Freeimage plugin mirrors RGB arrays if saving in 16-bit - python

I am working with 2D floating-point numpy arrays and saving them as .png files with high precision (see this question for how I came to this point). To do this I use the freeimage plugin, as in that linked question.
This creates a weird behaviour where the images are flipped (both left-right and up-down) if saved to 16-bit. This behaviour happens only for RGB or RGBA images, not for greyscale images. Here is some example code:
from skimage import io, img_as_uint, img_as_ubyte
im = np.random.uniform(size=(256, 256))
im[:128, :128] = 1
im = img_as_ubyte(im)
io.use_plugin('freeimage')
io.imsave('test_1.png', im)
creates the following picture:
when I try to save this in 16 bit, I get the same result (albeit taking 99kb instead of 50, so I know the bitdepth is working).
Now do the same as an RGB image:
im = np.random.uniform(size=(256, 256, 3))
im[:128, :128] = 1
im = img_as_ubyte(im)
io.use_plugin('freeimage')
io.imsave('test_1.png', im)
The 8-bit result is:
but doing the following
im = img_as_uint(im)
io.use_plugin('freeimage')
io.imsave('test_1.png', im)
gives me
This happens if the array contains an alpha level too.
It can be fixed by including
im = np.fliplr(np.flipud(im))
before saving. However, it seems to me this is pretty weird behaviour and not very desirable. Any idea why this is happening or whether it is intended? As far as I could see it's not documented.

Related

Reading and saving tif images with python

I am trying to read this tiff image with python. I have tried PIL to and save this image. The process goes smoothly, but the output image seems to be plain dark. Here is the code I used.
from PIL import Image
im = Image.open('file.tif')
imarray = np.array(im)
data = Image.fromarray(imarray)
data.save('x.tif')
Please let me know if I have done anything wrong, or if there is any other working way to read and save tif images. I mainly need it as NumPy array for processing purposes.
The problem is simply that the image is dark. If you open it with PIL, and convert to a Numpy array, you can see the maximum brightness is 2455, which on a 16-bit image with possible range 0..65535, means it is only 2455/65535, or 3.7% bright.
from PIL import Image
# Open image
im = Image.open('5 atm_gain 80_C001H001S0001000025.tif')
# Make into Numpy array
na = np.array(im)
print(na.max()) # prints 2455
So, you need to normalise your image or scale up the brightnesses. A VERY CRUDE method is to multiply by 50, for example:
Image.fromarray(na*50).show()
But really, you should use a proper normalisation, like PIL.ImageOps.autocontrast() or OpenCV normalize().

Save 16-bit numpy arrays as 16-bit PNG image

I'm trying to save a 16-bit numpy array as a 16-bit PNG but what I obtain is only a black picture. I put here a minimum example of what I'm talking aboout.
im = np.random.randint(low=1, high=6536, size=65536).reshape(256,256) #sample numpy array to save as image
plt.imshow(im, cmap=plt.cm.gray)
Given the above numpy array this is the image I see with matplotlib, but when then I save the image as 16-bit png I obtain the picture below:
import imageio
imageio.imwrite('result.png', im)
Image saved:
where some light grey spots are visible but the image is substantially black. Anyway when I read back the image and visualize it again with matplotlib I see the same starting image. I also tried other libraries instead of imageio (like PIL or PyPNG) but with the same result.
I know that 16-bit image values range from 0 to 65535 and in the array numpy array here there only values from 1 to 6536, but I need to save numpy arrays images similar to this, i.e. where the maximum value represented in the image isn't the maximum representable value. I think that some sort of nornalization is involved in the saving process. I need to save the array exactly as I see them in matplotlib at their maximum resolution and without compression or shrinkage in their values (so division by 255 or conversion to 8-bit array are not suitable).
It looks like imageio.imwrite will do the right thing if you convert the data type of the array to numpy.uint16 before writing the PNG file:
imageio.imwrite('result.png', im.astype(np.uint16))
When I do that, result.png is a 16 bit gray-scale PNG file.
If you want the image to have the full grayscale range from black to white, you'll have to scale the values to the range [0, 65535]. E.g. something like:
im2 = (65535*(im - im.min())/im.ptp()).astype(np.uint16)
Then you can save that array with
imageio.imwrite('result2.png', im2)
For writing a NumPy array to a PNG file, an alternative is numpngw (a package that I created). For example,
from numpngw import write_png
im2 = (65535*(im - im.min())/im.ptp()).astype(np.uint16)
write_png('result2.png', im2)
If you are already using imageio, there is probably no signficant advantage to using numpngw. It is, however, a much lighter dependency than imageio--it depends only on NumPy (no dependence on PIL/Pillow and no dependence on libpng).

Image.open() gives a plain white image

I am trying to edit this image:
However, when I run
im = Image.open(filename)
im.show()
it outputs a completely plain white image of the same size. Why is Image.open() not working? How can I fix this? Is there another library I can use to get non-255 pixel values (the correct pixel array)?
Thanks,
Vinny
Image.open actually seems to work fine, as does getpixel, putpixel and save, so you can still load, edit and save the image.
The problem seems to be that the temp file the image is saved in for show is just plain white, so the image viewer shows just a white image. Your original image is 16 bit grayscale, but the temp image is saved as an 8 bit grayscale.
My current theory is that there might actually be a bug in show where a 16 bit grayscale image is just "converted" to 8 bit grayscale by capping all pixel values to 255, resulting in an all-white temp image since all the pixels values in the original are above 30,000.
If you set a pixel to a value below 255 before calling show, that pixel shows correctly. Thus, assuming you want to enhance the contrast in the picture, you can open the picture, map the values to a range from 0 to 255 (e.g. using numpy), and then use show.
from PIL import Image
import numpy as np
arr = np.array(Image.open("Rt5Ov.png"))
arr = (arr - arr.min()) * 255 // (arr.max() - arr.min())
img = Image.fromarray(arr.astype("uint8"))
img.show()
But as said before, since save seems to work as it should, you could also keep the 16 bit grayscale depth and just save the edited image instead of using show.
you can use openCV library for loading images.
import cv2
img = cv2.imread('image file')
plt.show(img)

Python - how to convert a 24-bit PNG image to 32-bit using Open-cv or PIL

I want to convert a 24-bit PNG image to 32-bit so that it can be displayed on the LED matrix. Here is the code which I have used, but it converted 24-bit to 48-bit
import cv2
import numpy as np
i = cv2.imread("bbb.png")
img = np.array(i, dtype = np.uint16)
img *= 256
cv2.imwrite('test.png', img)
I looked at the christmas.png image in the code you linked to, and it appears to be a 624x8 pixel image with a palette and an 8-bit alpha channel.
Assuming the sample image works, you can make one with the same characteristics by taking a PNG image and adding a fully opaque alpha channel like this:
#!/usr/local/bin/python3
from PIL import Image
# Load the image and convert to 32-bit RGBA
im = Image.open("image.png").convert('RGBA')
# Save result
im.save("result.png")
I generated a gradient image and applied that processing and got this, so maybe you can try that:
I think you have confused the color bit-depth with the size of the input image/array. From the links posted in the comments, there is no mention of 32 as a bit depth. The script at that tutorial link uses an image with 3-channel, 8-bit color (red, green, and blue code values each represented as numbers from 0-255). The input image must have the same height as the array, but can be a different width to allow scrolling.
For more on bit-depth: https://en.wikipedia.org/wiki/Color_depth

RGB Values Being Returned by PIL don't match RGB color

I'm attempting to make a reasonably simple code that will be able to read the size of an image and return all the RGB values. I'm using PIL on Python 2.7, and my code goes like this:
import os, sys
from PIL import Image
img = Image.open('C:/image.png')
pixels = img.load()
print(pixels[0, 1])
now this code was actually gotten off of this site as a way to read a gif file. I'm trying to get the code to print out an RGB tuple (in this case (55, 55, 55)) but all it gives me is a small sequence of unrelated numbers, usually containing 34.
I have tried many other examples of code, whether from here or not, but it doesn't seem to work. Is it something wrong with the .png format? Do I need to further code in the rgb part? I'm happy for any help.
My guess is that your image file is using pre-multiplied alpha values. The 8 values you see are pretty close to 55*34/255 (where 34 is the alpha channel value).
PIL uses the mode "RGBa" (with a little a) to indicate when it's using premultiplied alpha. You may be able to tell PIL to covert the to normal "RGBA", where the pixels will have roughly the values you expect:
img = Image.open('C:/image.png').convert("RGBA")
Note that if your image isn't supposed to be partly transparent at all, you may have larger issues going on. We can't help you with that without knowing more about your image.

Categories

Resources