Reading Alpha of a PNG Pixel. Fast way via pure Python? - python

I am having an issue with an embedded 64bit Python instance not liking PIL. Before i start exhausting more methods to get a compiled image editor to read the pixels for me (such as ImageMagick) i am hoping perhaps anyone here can think of a purely Python solution that will be comparable in speeds to the compiled counterparts.
Now i am aware that the compiled friends will always be much faster, but i am hoping that because i "just" want to read the alpha of a group of pixels, that perhaps a fast enough pure Python solution can be conjured up. Anyone have any bright ideas?
Though, i have tried PyPNG and that is far too slow, so i'm not expecting any magic solutions. None the less, i had to ask.
Thanks to any replies!
And just for reference, the images i'll be reading will be on average around 512*512 to 2048*2048, and i'll be reading anywhere from one to all of the pixels alpha (multiplied by a few million times, but the values can be stored so reading twice isn't done).

Getting data out of a PNG requires unpacking data and decompressing it. These are likely going to be too slow in Python for your application. One possibility is to start with PyPNG and get rid of anything in it that you don't need. For example, it is probably storing all of the data it reads from the PNG, and some of the slow speed you see may be due to the memory allocations.

When you say PyPNG is too slow, how slow is it? To put it another way, how fast would be fast enough? PyPNG doesn't do anything stupid to make itself slow, but it is written in Python.
Make sure you're using read() to read the image row by row, and make sure you're using row[3::4] to extract the alpha channel. Extracting the alpha channel by using slice notation is no slower than reading the whole image.
I've added some notes to the PyPNG documentation about its speed.

Related

Dynamic font sizing/layout in Python/PIL

I have a problem where I need to programmatically lay out text and output a raster image. My initial approach is based around Python and PIL (or Pillow), however I am reasonably language agnostic (as long as it runs on Linux).
I have a list of several thousand long strings, roughly a paragraph each. The naive approach is to use Python's textwrap and PIL's font.getsize() and iterate to find the optimal size, but this seems inefficient to me - there are a lot of strings, and this is potentially running on a Rasperry Pi.
I feel that this is probably a solved problem, but I haven't been able to find a decent solution - I'm not tied to Python/PIL if another stack has a better solution (something in LaTeX? Even matplotlib or something?).
Flexibility to achieve more complex layouts would be a bonus, as well - for example, down the track I would like to treat one part of text as a special case, by increasing the font size and flowing the other text around it.
Any pointers or ideas greatly appreciated.
I would use cairo (2d graphics) and pango ("pretty" text formatting/layout) libraries (they both have binding for python):
http://cairographics.org/tutorial/
http://zetcode.com/gui/pygtk/pangoII/
http://cairographics.org/pycairo_pango/

Python: Manipulating a 16-bit .tiff image in PIL &/or pygame: convert to 8-bit somehow?

Hello all,
I am working on a program which determines the average colony size of yeast from a photograph, and it is working fine with the .bmp images I tested it on. The program uses pygame, and might use PIL later.
However, the camera/software combo we use in my lab will only save 16-bit grayscale tiff's, and pygame does not seem to be able to recognize 16-bit tiff's, only 8-bit. I have been reading up for the last few hours on easy ways around this, but even the Python Imaging Library does not seem to be able to work with 16-bit .tiff's, I've tried and I get "IOError: cannot identify image file".
import Image
img = Image.open("01 WT mm.tif")
My ultimate goal is to have this program be user-friendly and easy to install, so I'm trying to avoid adding additional modules or requiring people to install ImageMagick or something.
Does anyone know a simple workaround to this problem using freeware or pure python? I don't know too much about images: bit-depth manipulation is out of my scope. But I am fairly sure that I don't need all 16 bits, and that probably only around 8 actually have real data anyway. In fact, I once used ImageMagick to try to convert them, and this resulted in an all-white image: I've since read that I should use the command "-auto-levels" because the data does not actually encompass the 16-bit range.
I greatly appreciate your help, and apologize for my lack of knowledge.
P.S.: Does anyone have any tips on how to make my Python program easy for non-programmers to install? Is there a way, for example, to somehow bundle it with Python and pygame so it's only one install? Can this be done for both Windows and Mac? Thank you.
EDIT: I tried to open it in GIMP, and got 3 errors:
1) Incorrect count for field "DateTime" (27, expecting 20); tag trimmed
2) Sorry, can not handle images with 12-bit samples
3) Unsupported layout, no RGBA loader
What does this mean and how do I fit it?
py2exe is the way to go for packaging up your application if you are on a windows system.
Regarding the 16bit tiff issue:
This example http://ubuntuforums.org/showthread.php?t=1483265 shows how to convert for display using PIL.
Now for the unasked portion question: When doing image analysis, you want to maintain the highest dynamic range possible for as long as possible in your image manipulations - you lose less information that way. As you may or may not be aware, PIL provides you with many filters/transforms that would allow you enhance the contrast of an image, even out light levels, or perform edge detection. A future direction you might want to consider is displaying the original image (scaled to 8 bit of course) along side a scaled image that has been processed for edge detection.
Check out http://code.google.com/p/pyimp/wiki/screenshots for some more examples and sample code.
I would look at pylibtiff, which has a pure python tiff reader.
For bundling, your best bet is probably py2exe and py2app.
This is actually a 2 part question:
1) 16 bit image data mangling for Python - I usually use GDAL + Numpy. This might be a bit too much for your requirements, you can use PIL + Numpy instead.
2) Release engineering Python apps can get messy. Depending on how complex your app is you can get away with py2deb, py2app and py2exe. Learning distutils will help too.

How fast is Python Image Library's (PIL) ImageDraw Module, for instance as compared to OpenGL?

I have some .png images and I want to be able to quickly:
(a) Load a .png from a file.
(b) Draw some simple lines on top of the .png.
(c) Get the contents (bytes) of the resulting image to return as the result of an http request.
It sounds like PIL is a good candidate for doing this with relatively little code. However, I'm trying to understand how efficient it is, especially when I have, say, thousands of lines to draw in step (b). The alternative is using PyOpenGL, but before getting into that I wanted to understand if PIL was already fast enough.
I was going to ask if PIL used OpenGL under the covers. But that might be the wrong question, because my understanding is that to get the real speed benefit from PyOpenGL I'd want to submit my line vertexes as NumPy arrays. So presumably even if PIL uses OpenGL, I'm going to lose a lot of that benefit when I make an individual PIL call to draw each of my lines?
Anybody have concrete data for speed of PIL when drawing lots of primitives?
"Draw some simple lines on top of the .png" is not a computationally intensive task.
This doesn't seems to be a good candidate for the GPU since they are better suited for more complex tasks. You've got to realize that the image is initially loaded on the RAM, making it your job to send this data to the GPU memory and then retrieve it back. This operation consumes a few milliseconds, depending on the size of the image, that could be better used for CPU processing.
Your application would only benefit from the GPU if it had high arithmetic intensity.

Getting pixel data from a PythonMagick image?

Does anyone know a way get the pixel data from a PythonMagick.Image instance without having to write it to disk first?
For instance, I can read in an image using:
import PythonMagick
im = PythonMagick.Image('image.jp2')
I would now like to be able to get the uncompressed image data so that I can use it in something else like NumPy or matplotlib, but I can't seem to find any way to do this. I would just use matplotlib or PIL directly but the image format I'm reading in is JPEG 2000 which is only supported by PythonMagick as far as I know.
Any suggestions?
Disclaimer: I don't have PythonMagick built where I am right now and am no expert, so (1) any or all of the following may be wrong, (2) it will certainly be less specific than you'd like, and (3) if someone else knows better I hope they won't be put off by seeing an answer already here. Anyway:
From a quick look at the code, it looks as if you can read pixel values one by one using the pixelColor method on the Image class. This returns a PythonMagick.Color value, from which you can extract R,G,B components. The underlying C++ library supports reading out lots of pixels at a time using Image::writePixels, which is also present in PythonMagick.Image; but I think the proper use of that method depends on other things that aren't implemented in PythonMagick. That's a pity, because I bet it would have been much much more efficient than reading one pixel at a time.
Alternatively and probably better, it looks as if you can write the contents of the image to a PythonMagick.Blob object in memory, which basically does the same as writing to a file only without the file :-). You can choose what format it should write in, just as you do when writing to a file. There seems to be something called get_blob_data for extracting the contents of a Blob. Something like this:
im = PythonMagick.Image('image.jp2')
blob = PythonMagick.Blob()
im.write(blob, "png")
data = PythonMagick.get_blob_data(blob)
The resulting data is, I think, a Python string whose bytes are the binary representation of the image. (I'm assuming you're using Python 2.x, where the string type is 8-bit. I don't know whether PythonMagick works with 3.x.) I think there are some formats that are basically raw pixel data; try "RGB". You can then extract the contents via lots of struct.unpack or whatever.

Using Python to convert color formats?

I'm working on a Python tool to convert image data into these color formats:
RGB565
RGBA5551
RGBA4444.
What's the simplest way to achieve this?
I've used the Python Imaging Library (PIL) frequently. So I know how to load an image and obtain each pixel value in RGBA8888 format. And I know how to write all the conversion code manually from that point.
Is there an easier way? Perhaps some type of 'formatter' plugin for PIL?
Does PIL already support some of the formats I'm targeting? I can't ever figure out which formats PIL really supports without digging though all of the source code.
Or is there a better library than PIL to accomplish this in Python?
Any tips would be appreciated. Thanks!
Changing something from 8 to 5 bits is trivial. In 8 bits the value is between 0 and 255, in 5 bits it's between 0 and 31, so all you need to do is divide the value with 8. Or 4 in the case for green in RGB565 mode. Or 16 in RGBA4444 mode as it uses 4 bits per channel, etc.
Edit: Reading through your question again, I think there is a confusion (either with me or you). RGB555 and RGBA4444 etc are not really formats, like GIF or JPG, they are color spaces. That conversion is trivial (see above). What file format you want to save it in later is another question. Most file formats have limited support for color spaces. I think for example that JPEG always saves it in YCbCr (but I could be mistaken), GIF uses a palette (which in turn always is RGB888, I think) etc.
There's a module called Python Colormath which provides a lot of different conversions. Highly recommended.
Numpy is powerful indeed, but to get there and back to PIL requires two memory copies. Have you tried something along the following lines?
im = Image.open('yourimage.png')
im.putdata([yourfunction(r,g,b) for (r,g,b) in im.getdata()])
This is quite fast (especially when you can use a lookup table). I am not familiar with the colour spaces you mention, but as I understand you know the conversion so implementation of yourfunction(r,g,b) should be straight forward.
Also im.convert('RGBA', matrix) might be very powerful as it is super fast in applying a colour transformation through the supplied matrix. However I have never gotten that to do what I wanted it to do... :-/
There is also a module named Grapefruit that let you do conversions between quite a lot of color formats.
I ended up doing the conversions manually as Lennart Regebro suggested.
However, pure Python (iterating over each pixel) turned out to be too slow.
My final solution used PIL to load the image and numpy to operate on (convert) an array of pixels.

Categories