python-pillow / Pillow

Python Imaging Library (Fork)
https://python-pillow.org
Other
12.23k stars 2.23k forks source link

Save 16-bit PGM Image? #8226

Closed SvarunSoda closed 1 month ago

SvarunSoda commented 3 months ago

I am attempting to convert an image in PNG format to a 16-bit PGM format and save it using Python's PIL library. I'm using Python 3.12.4 in all examples shown.


Using the following test.png image:

test

Attempting a simple script like this with the Image.save() function:

from PIL import Image

image = Image.open("test.png")
image.save("test.pgm")

Can save resave the image as PGM, however it's always saved as an 8-bit image, as shown by GIMP:

gimp


Attempting to specify the amount of pixel bits via the optional bits argument as such:

from PIL import Image

image = Image.open("test.png")
image.save("test.pgm", bits = 16)

Also results in an 8-bit PGM image being saved.


Attempting to manually create a numpy array of the np.uint16 type & then creating an image from it using the Image.fromarray() function as such:

from PIL import Image
import numpy as np

image = Image.open("test.png")
imageData = np.array(image.getdata(), dtype = np.uint16)
newImage = Image.fromarray(imageData)
newImage.save("test.pgm", bits = 16)

Results in:

OSError: cannot write mode I;16 as PPM

I also noticed that this appears to break the image, as saving with np.uint32 saves a blank image with a very narrow horizontal size and a very large vertical size.


What is the proper way to save 16-bit PGM images using the Python PIL library?

Thanks for reading my post, any guidance is appreciated.

radarhere commented 3 months ago

Hi. The image that you have provided is an RGBA image. It has 8-bits per channel, so I think it makes perfect sense for it to be saved in 8-bit mode by default.

Converting it to I mode

from PIL import Image

image = Image.open("test.png")
image.convert("I").save("test.pgm")

will save a 16-bit image.

via the optional bits argument as such:

Looking at the source code, that isn't a Pillow feature for the PGM format. I'm guessing you saw this used for a PNG image somewhere, and presumed that you could use the same argument when saving a PGM image? That is not the case - Pillow often has format-specific arguments for saving.

I also noticed that this appears to break the image, as saving with np.uint32 saves a blank image with a very narrow horizontal size and a very large vertical size.

im.getdata() returns a "flattened" object, so I expect that's where the size information is being lost. Instead of np.array(image.getdata()), you can pass a Pillow image directly to NumPy with np.array(image)

OSError: cannot write mode I;16 as PPM

I've created https://github.com/python-pillow/Pillow/pull/8231 to fix this.

radarhere commented 3 months ago

@SvarunSoda was that answer helpful?