Open MAJAQA opened 1 year ago
Hi @MAJAQA,
Your numpy array is int64, so libvips is saving it as an int32 TIFF image (the closest it can get).
You can see what's happening like this:
john@banana ~/try $ python
Python 3.11.4 (main, Jun 9 2023, 07:59:55) [GCC 12.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvips
>>> import numpy as np
>>> image = pyvips.Image.new_from_file("image_r.tif")
>>> cr = image[0]
>>> crl = cr.tolist()
>>> n = np.array(crl)
>>> n.dtype
dtype('int64')
>>>
numpy is just seeing a python array of integers, so it's wrapping it up as an array of int64.
A better way to go to numpy is like this:
>>> image = pyvips.Image.new_from_file("image_r.tif")
>>> cr = image[0]
>>> n = cr.numpy()
>>> n.dtype
dtype('uint8')
>>>
The numpy()
method on a pyvips image will make a numpy array directly from the libvips pixels, so there's no copy, it's much faster, and the numeric type is preserved. Now if you save, you'll get an 8-bit TIFF.
You could also improve this:
cr = i[0]
I guess you want to convert to mono? You'll find:
cr = i.colourspace("b-w")
Will usually give better results.
I used the single channel approach as I want to process the tones as integer values (0 to 255), so not mono, but gray levels.
Tried:
n = np.array(crl, dtype=np.int8)
then the output is 'identical' to the input, however I get deprecation warnings for all values > 128: ex.
C:\Users\maja\OneDrive - Veralto\TechTime\snowme\snowme_github_issue-posted.py:62: DeprecationWarning: NumPy will stop allowing conversion of out-of-bound Python integers to integer arrays. The conversion of 129 to int8 will fail in the future. For the old behavior, usually: np.array(value).astype(dtype) will give the desired result (the cast overflows). n = np.array(crl, dtype=np.int8)
needed to use 'uint8' then ok
I used the single channel approach as I want to process the tones as integer values (0 to 255), so not mono, but gray levels.
.colourspace("b-w")
will make a mono image. It's just a better version of what you have.
n = np.array(crl, dtype=np.int8)
I wouldn't do this, it'll be very slow and eat huge amounts of memory. Just use n = cr.numpy()
.
I 'can't' use the cr.numpy(), because that's the original image. I am converting the cr to the crl, which is the 'list' version of the image, so I can 'easily' search and change the image values. The whole idea behind this test is to try to 'simulate' how an image would look like after ripping (using certain screening technology) and printing it on a traditional flexo printing press.... Had to leave out my '#' code comments as that seems to break the 'code' inserting tool: ex. `code line 1
code line3 ` How can you prevent this? >> then I can add my part of test code to show what I am trying to do... Always open for suggestions...
The images I am using are 'small' so no real memory issues at the moment. Processing it is done so fast, I can't even see the amount of memory python takes in the windows task manager... Also, the final 'simulation' image needs to be shown on screen, so that's pretty 'low' resolution too, so I can scale the images if needed when memory use would become a problem.
btw. The images I use are single color (Black) 'representing' 1 plate (ink) of the printing press, so when I saw "b-w" I assumed it would be black and white (binary) only, but it indeed keeps all tone levels 0 to 255 and is a better approach if you would use full color images.
You write code in markdown, so three backticks for a many-line code block. You can put the name of the language to get syntax highlighting that exact thing, eg:
```python
a = b + 12
Renders as:
```python
a = b + 12
You can use a single backtick for inline code:
this is `some python` inline
Appears as:
this is some python
inline
The markdown docs are worth a look.
https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
The CheatSheet is very helpful indeed!
here is my embryo code ... left out the kDotSpiral spec since that's company confidential ...
#kMinDots4000 = [6, 9, 12, 16, 19, 22, 25, 31, 36, 43, 48, 54, 60]
kMinDots4000 = [15, 30, 60]
def SimulatePrintedFM (image_r, toneFM, mindotList):
#load the image into a n-channel stream
#i = py.Image.new_from_file(image_r, access="sequential")
i = py.Image.new_from_file(image_r)
imY = i.height
imW = i.width
print('image - bands={0} format={1} interpretation={2} coding={3}'.format(i.bands, i.format, i.interpretation, i.coding))
print(i)
#get the tones from the 1st image channel(=r from rgb)
#cr = i[0]
cr = i.colourspace("b-w")
#convert the pixel tones to a list of coordinates and tone values
crl = cr.tolist()
nr_rows = len(crl)
nr_cols = len(crl[0])
#print('nr_rows={0} nr_cols={1}'.format(nr_rows,nr_cols))
#print(crl)
# coord_list is the list of coordinates per tone in the highlights
coord_list = []
for s in range(toneFM,255):
clist = []
for y in range(nr_rows):
for x in range(nr_cols):
if crl[y][x] == s:
lCoordinates = [y,x]
clist.append(lCoordinates)
#print(clist)
coord_list.append(clist)
# for all mindots, make a print simulation version
for mDot in mindotList:
# iterate for all tones
tmp_crl = crl
tmp_coord_list = coord_list
MinDot_4000 = mDot
kMinDotResolutionPpmm = 4000/25.4
MinDot_scaled = round(MinDot_4000/(kMinDotResolutionPpmm/i.xres))
# need to round to make sure to at least show 1 pixel in output
print('image - res_ppmm={0} nr_pix_y={1} nr_pix_x={2} Mindot_scaled={3}'.format(i.xres,imY,imW,MinDot_scaled))
grainyTone=toneFM
ToneSeadStep=5/(255-toneFM)
ToneSeadFrequency=5*MinDot_scaled
for t in range(len(tmp_coord_list)):
step = 255-toneFM-t
pixs = len(tmp_coord_list[t])
#print(hlist[t])
print('steps={0} pixs={1}'.format(step,pixs))
s = step
# enable start of grainy dots at very beginning
c = random.randint(0,ToneSeadFrequency)
# gradually make the grainy dots darker
grainyTone -= ToneSeadStep
while s < pixs:
#print('hlist[{0}]={1}'.format(s,hlist[t][s]))
if c == ToneSeadFrequency:
c = 0
tone = int(grainyTone)
else:
tone = 255
# every 'step' pixels, make a white pixel, but every 'ToneSeadFrequency' add a bit darker one
# this should give the impression the mindot becomes visible at the very lowest tones >> visualises the printed 'graininess'
# when using subsampled image (72 dpi only) >> crl[hlist[t][s][0]][hlist[t][s][1]] = tone
# now adding real size mindot (scaled to image resolution)
for pix in range(MinDot_scaled):
x,y = tmp_coord_list[t][s][0] + kDotSpiral[pix][0], tmp_coord_list[t][s][1] +kDotSpiral[pix][1]
#print('pix={0} pos-x={1} pos-y={2}'.format(pix, x, y))
tmp_crl[x][y] = tone
# add random seed to eleminate patterns(ex. in linear wedges, fadings, ...)
s += int(random.randint(1,30)/10)
s += step
c += 1
#print(crl)
# convert the list to a numpy array to enable tiffsave to grayscale
n = np.array(tmp_crl, dtype=np.uint8)
ni = py.Image.new_from_array(n, interpretation='rgb')
#ni = py.Image.new_from_list(n)
image_w = os.path.splitext(image_r)[0] + '-snow-FM' + str(toneFM) + '-mindot' + str(mDot) + '.tif'
ni.tiffsave(image_w, compression='lzw', xres=i.xres, yres=i.yres)
Ah gotcha. I'd use numpy arrays rather than list()
, but I can see why you went that way.
@jcupitt, As a 'hobby' programmer, I consider this a compliment. Thanks for the tips, hope it's useful for other programmers too.
I having trouble to keep the tone depth of an rgb tiff image. What am I missing? The output tiff file appears darker over the whole tone range...
Input and output in this zip file: example.zip