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

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).

Related

Writing int16 numpy array as an proper image

I am trying to write(save) a int16 numpy array as an image using openCV. Find the numpy file of an image in the link below: https://drive.google.com/file/d/1nEq_CeNmSgacARa2ADr_f_qVaSfJSZZX/view?usp=sharing
The image I saved in bmp or png or tiff format is look like this:
Uint16
I converted the numpy array to uint8 and the image become very dark and the maximum value of the image is just 34 as shown below:
uint8
Please let me know how to properly save and visualize this int16 format image.
Note: plt.imshow of int16 numpy array showing proper visual. matplotlib_imshow
I have saved the image properly using the following syntax
from skimage.util import img_as_ubyte
img=np.load('brain.npy')
img1=img/img.max()
img2=img_as_ubyte(img1)
cv2.imwrite('brain_u8.png', img2)
correct_output

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().

How to convert dtype(uint16) data into a 16bit png image?

I'm trying to encrypt and decrypt an image using RSA algo. For that, I need to read the image as greyscale and then apply the keys and save the uint16 type array into a png or any image format which supports 16bit data. Then I need to read that 16bit data convert it into an array and do the decryption. Now, previously I tried to save the image as .tif and when I read it with
img = sk.imread('image.tiff', plugin = 'tifffile')
it treats the image as RGB, which is not what I want. Now I want to save the uint16 type array to a 16bit png image which will take values between 0 to 65536 and then read it again as a uint16 type data. I tried to save the values to a 16bit png file using
img16 = img.astype(np.uint16)
imgOut = Image.fromarray(img16)
imgOut.save('en.png')
This gives me this error: OSError: cannot write mode I;16 as PNG
I have also tried imgOut = Image.fromarray(img16, 'I') but this yeilds not enough image data
Please help me to save the 16bit data into a .png image. Thank you.
There are a couple of possibilities...
First, using imageio to write a 16-bit PNG:
import imageio
import numpy as np
# Construct 16-bit gradient greyscale image
im = np.arange(65536,dtype=np.uint16).reshape(256,256)
# Save as PNG with imageio
imageio.imwrite('result.png',im)
You can then read the image back from disk and change the first pixel to mid-grey (32768) like this:
# Now read image back from disk into Numpy array
im2 = imageio.imread('result.png')
# Change first pixel to mid-grey
im2[0][0] = 32768
Or, if you don't like imageio, you can use PIL/Pillow and save a 16-bit TIFF:
from PIL import Image
import numpy as np
# Construct 16-bit gradient greyscale image
im = np.arange(65536,dtype=np.uint16).reshape(256,256)
# Save as TIFF with PIL/Pillow
Image.fromarray(im).save('result.tif')
You can then read back the image from disk and change the first pixel to mid-grey like this:
# Read image back from disk into PIL Image
im2 = Image.open('result.tif')
# Convert PIL Image to Numpy array
im2 = np.array(im2)
# Make first pixel mid-grey
im2[0][0] = 32768
Keywords: Image, image processing, Python, Numpy, PIL, Pillow, imageio, TIF, TIFF, PNG, 16 bit, 16-bit, short, unsigned short, save, write.

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

Read binary .pgm files with python and numpy

I need to read 64x64 pgm plain format image files and put the resulting values in a numpy matrix. I can do it only if I use Opencv and PIL functions to open the image, but the final numpy matrix yielded has 3-channel and the values returned are 0 and 255, instead of 0 and 1 (the image is binary). I also tried to use genfromtxt but it can't put the values in a numpy matrix.
I only want a 1 channel numpy matrix with 0 and 1's from the pgm image. How can I do that with python?
If PIL opens your image files as RGB but you want them in binary, I think your only choice is to convert after opening.
im = Image.open('imagefile').convert('1')

Categories

Resources