views:

299

answers:

3

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?

+1  A: 

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.

Wayne Werner
I believe that "L" stands for Luminance, as in a gray-scale image. CMYK stands for Cyan-Magenta-Yellow-Key, where Key is black (see this [Wikipedia article](http://en.wikipedia.org/wiki/Cmyk)).
martineau
I'm not totally sure, but from what I have tested, when the 'L' mode is used the image is saved as a binary image (black and white), where each pixel occupies one byte, instead of 1 bit as in the '1' mode.
Alceu Costa
+2  A: 

The specific documentation is at http://www.pythonware.com/library/pil/handbook/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 (3x8-bit pixels, true colour)
  • RGBA (4x8-bit pixels, true colour with transparency mask)
  • CMYK (4x8-bit pixels, colour separation)
  • YCbCr (3x8-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).

katrielalex
+1  A: 

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.

martineau
The image that I was reading was grayscale. So, each pixel is represented 16 bit unsigned integer value. The documentation that katrielalex found was useful. However, PIL support to save those 16 bit grayscale image is very limited in PIL. I think that I will have to stick with matlab for this task...
Alceu Costa
Since 16 bits is way more shades of gray than your eye can distinguish, you might be able to quickly convert it on-the-fly (in Python) to a more common 8-bit gray-scale image with no visual loss.
martineau