Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Image Gurus: Optimize my Python PNG transparency function

I need to replace all the white(ish) pixels in a PNG image with alpha transparency.

I'm using Python in AppEngine and so do not have access to libraries like PIL, imagemagick etc. AppEngine does have an image library, but is pitched mainly at image resizing.

I found the excellent little pyPNG module and managed to knock up a little function that does what I need:

make_transparent.py

pseudo-code for the main loop would be something like:

for each pixel:
    if pixel looks "quite white":
        set pixel values to transparent
    otherwise:
        keep existing pixel values

and (assuming 8bit values) "quite white" would be:

where each r,g,b value is greater than "240" 
AND each r,g,b value is within "20" of each other

This is the first time I've worked with raw pixel data in this way, and although works, it also performs extremely poorly. It seems like there must be a more efficient way of processing the data without iterating over each pixel in this manner? (Matrices?)

I was hoping someone with more experience in dealing with these things might be able to point out some of my more obvious mistakes/improvements in my algorithm.

Thanks!

like image 529
Chris Farmiloe Avatar asked Nov 05 '22 12:11

Chris Farmiloe


1 Answers

This still visits every pixel, but may be faster:

new_pixels = []
for row in pixels:
    new_row = array('B', row)
    i = 0
    while i < len(new_row):
        r = new_row[i]
        g = new_row[i + 1]
        b = new_row[i + 2]
        if r>threshold and g>threshold and b>threshold:
            m = int((r+g+b)/3)
            if nearly_eq(r,m,tolerance) and nearly_eq(g,m,tolerance) and nearly_eq(b,m,tolerance):
                new_row[i + 3] = 0
        i += 4
    new_pixels.append(new_row)

It avoids the slicen generator, which will be copying the entire row of pixels for every pixel (less one pixel each time).

It also pre-allocates the output row by directly copying the input row, and then only writes the alpha value of pixels which have changed.

Even faster would be to not allocate a new set of pixels at all, and just write directly over the pixels in the source image (assuming you don't need the source image for anything else).

like image 156
Saxon Druce Avatar answered Nov 11 '22 04:11

Saxon Druce