How to read a raw image using PIL?

Question:

I have a raw image where each pixel corresponds to a 16 bits unsigned integer. I am trying to read using the PIL Image.fromstring() function as in the following code:

if __name__ == "__main__":
    if (len(sys.argv) != 4):
        print 'Error: missing input argument'
        sys.exit()

    file = open(sys.argv[1], 'rb')
    rawData = file.read()
    file.close()

    imgSize = (int(sys.argv[2]), int(sys.argv[3]))

    # Use the PIL raw decoder to read the data.
    #   - the 'F;16' informs the raw decoder that we are reading a little endian, unsigned integer 16 bit data.
    img = Image.fromstring('L', imgSize, rawData, 'raw', 'F;16')

    im.save('out.png')

The PIL documentation informs that the first argument of the fromstring() function is ‘mode’. However, looking at the documentation and googling I wasn’t able to find details about what that argument really means (I believe that it is related to the color space or something like that). Does anyone knows where I can find a more detailed reference about the fromstring() function and what the mode argument means?

Asked By: Alceu Costa

||

Answers:

Image.frombuffer(mode, size, data) => image

(New in PIL 1.1.4). Creates an image memory from pixel data in a string or buffer object, using the standard "raw" decoder. For some modes, the image memory will share memory with the original buffer (this means that changes to the original buffer object are reflected in the image). Not all modes can share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". For other modes, this function behaves like a corresponding call to the fromstring function.

I’m not sure what "L" stands for, but "RGBA" stands for Red-Green-Blue-Alpha, so I presume RGBX is equivalent to RGB (edit: upon testing this isn’t the case)? CMYK is Cyan-Magenta-Yellow-Kelvin, which is another type of colorspace. Of course I assume that if you know about PIL you also know about colorspaces. If not, Wikipedia has a great article.

As for what it really means (if that’s not enough): pixel values will be encoded differently for each colorspace. In regular RGB you have 3 bytes per pixel – 0-254, 0-254, 0-254. For Alpha you add another byte to each pixel. If you decode an RGB image as RGBA, you’ll end out reading the R pixel to the right of the first pixel as your alpha, which means you’ll get the G pixel as your R value. This will be magnified depending on how large your image, but it will really make your colors go wonky. Similarly, trying to read a CMYK encoded image as RGB (or RGBA) will make your image look very much not like it’s supposed to. For instance, try this with an image:

i = Image.open('image.png')
imgSize = i.size
rawData = i.tostring()
img = Image.fromstring('L', imgSize, rawData)
img.save('lmode.png')
img = Image.fromstring('RGB', imgSize, rawData)
img.save('rgbmode.png')
img = Image.fromstring('RGBX', imgSize, rawData)
img.save('rgbxmode.jfif')
img = Image.fromstring('RGBA', imgSize, rawData)
img.save('rgbamode.png')
img = Image.fromstring('CMYK', imgSize, rawData)
img.save('rgbamode.tiff')

And you’ll see what the different modes do – try it with a variety of input images: png with alpha, png without alpha, bmp, gif, and jpeg. It’s kinda a fun experiment, actually.

Answered By: Wayne Werner

The specific documentation is at http://effbot.org/imagingbook/concepts.htm:

Mode

The mode of an image defines the type
and depth of a pixel in the image. The
current release supports the following
standard modes:

  • 1 (1-bit pixels, black and white, stored with one pixel per byte)
  • L (8-bit pixels, black and white)
  • P (8-bit pixels, mapped to any other mode using a colour palette)
  • RGB (3×8-bit pixels, true colour)
  • RGBA (4×8-bit pixels, true colour with transparency mask)
  • CMYK (4×8-bit pixels, colour separation)
  • YCbCr (3×8-bit pixels, colour video format)
  • I (32-bit signed integer pixels)
  • F (32-bit floating point pixels)

PIL also provides limited support for
a few special modes, including LA (L
with alpha), RGBX (true colour with
padding) and RGBa (true colour with
premultiplied alpha).

Answered By: Katriel

If all else fails, you can always read the source code. For PIL, the downloads are here.

You never said exactly what format the pixel data in the 16 bits unsigned integers was in, but I’d guess it’s something like RRRRRGGGGGGBBBBBB, (5-bits Red, 6-bits Green, 5-bits Blue), or RRRRRGGGGGBBBBBA (5-bits Red, 5-bits Green, 5-bits Blue, 1-bit Alpha or Transparency). I didn’t see support for those formats after a very quick peek at the some of the sources myself, but can’t say one way or the other for sure.

On the same web page where the PIL downloads are, they mention that one can send questions to the Python Image SIG mailing list and provide a link for it. That might be a better source than asking here.

Hope this helps.

Answered By: martineau

This is an old question, but this might help someone in the future. One of the problems with the original code snippet is that in Image.fromstring('L', imgSize, rawData, 'raw', 'F;16'), the F;16 part works for 'F' mode.

This works for me:

image = Image.fromstring('F', imgSize, rawData, 'raw', 'F;16')
image.convert('L').save('out.png')
Answered By: matiasg