Search code examples
pythonpython-3.xpython-imaging-librarydata-science

How to display greyscaled Image properly using Pilow?


I'm trying to open an Image using PIL in python. The code I'm running works fine with other images (and greyscale images I produced myself). But this one image file doesn't display properly. It makes the Image pixels either true White or true Black instead of a regular greyscaled pixels.

I received this image, with other 3 very similar ones. But weirdly, only this one is giving me this unusual show()

I've already tried converting the Image to rgb. with the method convert, but the output is still a pure black and white image.

I've opened the image in aseprite (image editor) and saved again with another name and the copy doesn't reproduce the error.

Noticed as well that this image's bit density is 16 (different from all other images I've tested). And I couldn't find any other image with this density.

The image that gave me this trouble follows:

image_not_readable_using_show_method

(I believe is downloadable)

For some reason, the display function in python notebooks can reveal the image correctly.

I can reproduce this malfunction with no other images.

To get the same malfunction I'm facing just download the image and open it with:

from PIL import Image

image = Image.open('image.png')
image.show()

Solution

  • This should really be a comment, not an answer, but it’s too large... As I wrote in a comment, it seems that Pillow has some issues with handling “deep” images. Here’s an extremely ugly (and somewhat ham-fisted, as I am not good with numpy nor with Pillow) work-around, mostly to demonstrate the issue. I am sure there are more elegant ways to write this:

    import numpy as np
    from PIL import Image
    
    image = Image.open('lCNSM.png')
    
    # The mode is 'I' = 32-bit signed
    print(f'{image.mode=}')
    
    # Convert to numpy array so we can do math on it    
    a32 = np.array(image)
    
    # min=0, max=65535, indicating that only part of the
    # 32-bit range is actually used
    print(a32.dtype, a32.min(), a32.max())
    
    # Scale down do 8 bits (via float...). Can numpy do bit shifts?
    # I’m too lazy to find out. =)
    a8 = (a32.astype(np.float64) * (1/256)).astype(np.uint8)
    
    # Convert to image and show!
    im8 = Image.fromarray(a8)
    im8.show()