Cannot convert PIL image from Paint.NET to numpy array - python

I am getting a ValueError in numpy when performing operations on images. The problem seems to be that the images edited by Paint.NET are missing the RGB dimension when opened using PIL and converted to a numpy array.

If PIL is giving you a 861x1091 image when you are expecting an 861x1091x3 image, that is almost certainly because it is a palette image - see here for explanation.
The simplest thing to do, if you want a 3-channel RGB image rather than a single channel palette image is to convert it to RGB when you open it:
im = Image.open(path).convert('RGB')

Related

Matplotlib: How to save an image at full resolution?

I've created a mozaic image, such that a large picture consists of many tiny pictures. I can view this image just fine in the matplotlib viewer and I can zoom in to see all the tiny pictures. But when I save the image, no matter the extension, the image loses the zoom ability such that the tiny images become blurred when zooming in. Is there a way to save the image in full resolution?
I have the image in rgb in a numpy array so if there are other libraries more suitable that would work too.
This should work :
from PIL import Image
Image.fromarray(numpy_img).save("img_path.png")
I think you're being mislead by Windows's Photos Application here, which applies blur automatically if you zoom too much.
The image is being saved correctly with all pixel values by Matplotlib, you can check it by zooming again on the pixels of the loaded image.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
# save image
array = np.random.random((4000, 4000, 3))
plt.imsave("save.png", array)
# load image
img = mpimg.imread("save.png")
plt.imshow(img)
plt.show()
Another option is the small library that I wrote called numpngw. If the numpy array is, say, img (an array of 8-bit or 16-bit unsigned integers with shape (m, n, 3)), you could use:
from numpngw import write_png
write_png('output.png', img)
(If the array is floating point, you'll have to convert the values to unsigned integers. The PNG file format does not store floating point values.)
You can try imageio library:
https://imageio.readthedocs.io/en/stable/userapi.html#imageio.imwrite
from imageio import imwrite
imwrite("image.png", array)

Python Pillow: how to produce 3-channel image from 1-channel image?

A Python package that I'm trying to use only works with 3-channel images. If I have a grayscale PNG image, Pillow's Image.open() naturally reads it as a single-layer image. How can I use Pillow to transform the 1-channel image into a 3-channel RGB image?
The simplest method to convert a single-channel greyscale image into a 3-channel RGB image with PIL is probably like this:
RGB = Image.open('image.png').convert('RGB')
Further discussion and explanation is available here.

cvtColor "code" for 16-bit grayscale images

I have unsigned 16-bit grayscale tiff images as numpy arrays. I want to do some image processing on these images using OpenCV. I am converting the numpy array to Mat format using cv2.cvtColor(src, code). As far as the documentation goes, I am having a hard time finding the right code argument to correctly convert 16-bit grayscale images without losing any information.
Previously, I read the images directly using cv2.imread(src, cv2.IMREAD_UNCHANGED). However, I don't have the original image files now, only the pickled numpy array. I am looking for the code in cvtColor which does a similar thing as cv2.IMREAD_UNCHANGED
Your question is tough to follow. All you appear to have are some files containing pickled Numpy arrays, correct?
If so, you don't need any imread(), you just need to unpickle the files and you will have Numpy arrays of the type that OpenCV uses to hold images. Check their dtype is np.uint16 and their shape appears correct.

Problems in displaying uint16 np.array as image

I have a uint16 3-dim numpy array reppresenting an RGB image, the array is created from a TIF image.
The problem is that when I import the original image in QGIS for example is displayed correctly, but if I try to display within python (with plt.imshow) the result is different (in this case more green):
QGIS image:
Plot image:
I think it is somehow related to the way matplotlib manages uint16 but even if I try to divide by 255 and convert to uint8 I can't get good results.
Going by your comment, the image isn't encoded using an RGB colour space, since the R, G and B channels have a value range of [0-255] assuming 8 bits per channel.
I'm not sure exactly which colour space the image is using, but TIFF files generally use CMYK which is optimised for printing.
Other common colour spaces to try include YCbCr (YUV) and HSL, however there are lots of variations of these that have been created over the years as display hardware and video streaming technologies have advanced.
To convert the entire image to an RGB colour space, I'd recommend the opencv-python pip package. The package is well documented, but as an example, here's how you would convert a numpy array img from YUV to RGB:
img_bgr = cv.cvtColor(img, cv.COLOR_YUV2RGB)
When using plt.imshow there's the colormap parameter you can play with, try adding cmap="gray" so for example
plt.imshow(image, cmap="gray")
source:
https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.imshow.html
If I try to normalize the image I get good results:
for every channel:
image[i,:,:] = image[i,:,:] / image[i,:,:].max()
However, some images appear darker than others:
different images

overlay an image and show lighter pixel at each pixel location

I have two black and white images that I would like to merge with the final image showing the lighter/ white pixel at each pixel location in both images. I tried the following code but it did not work.
background=Image.open('ABC.jpg').convert("RGBA")
overlay=Image.open('DEF.jpg').convert("RGBA")
background_width=1936
background_height=1863
background_width,background_height = background.size
overlay_resize= overlay.resize((background_width,background_height),Image.ANTIALIAS)
background.paste(overlay_resize, None, overlay_resize)
overlay=background.save("overlay.jpg")
fn=np.maximum(background,overlay)
fn1=PIL.Image.fromarray(fn)
plt.imshow(fnl)
plt.show()
The error message I get is cannot handle this data type. Any help or advice anyone could give would be great.
I think you are over-complicating things. You just need to read in both images and make them greyscale numpy arrays, then choose the lighter of the two pixels at each location.
So starting with these two images:
#!/usr/local/bin/python3
import numpy as np
from PIL import Image
# Open two input images and convert to greyscale numpy arrays
bg=np.array(Image.open('a.png').convert('L'))
fg=np.array(Image.open('b.png').convert('L'))
# Choose lighter pixel at each location
result=np.maximum(bg,fg)
# Save
Image.fromarray(result).save('result.png')
You will get this:
Keywords: numpy, Python, image, image processing, compose, blend, blend mode, lighten, lighter, Photoshop, equivalent, darken, overlay.

Categories