Newbie question about tuples and list comprehensions

Peter Otten __peter__ at web.de
Thu Jun 26 04:11:49 EDT 2008


idiolect wrote:

> On Jun 25, 7:26 pm, Terry Reedy <tjre... at udel.edu> wrote:
>> idiolect wrote:
>> > Hi all - Sorry to plague you with another newbie question from a
>> > lurker.  Hopefully, this will be simple.
>>
>> > I have a list full of RGB pixel values read from an image.  I want to
>> > test each RGB band value per pixel, and set it to something else if it
>> > meets or falls below a certain threshold - i.e., a Red value of 0
>> > would be changed to 50.
>>
>> > I've built my list by using a Python Image Library statement akin to
>> > the following:
>>
>> > data = list(image.getdata())
>>
>> > Which produces a very long list that looks like [(0,150,175),
>> > (50,175,225),...].  I'm trying to figure out a fast and pythonic way
>> > to perform my operation.  The closest I've come so far to a succinct
>> > statement is a list comprehension along the syntax of:
>>
>> Why are you trying to do this with a list comprehension?  Learn the
>> basics first.  Perhaps you have read too many of the recent threads
>> presenting diverting challenges for bored experienced programmers.  Some
>> of these were definitely not Pythonic code for real use.
>>
>> First question: do you really want to create a new 'very long list' or
>> modify list 'data' in place.  Let's assume the latter.
>>
>> for i,tup in enumerate(data):
>>      data[i] = replace(tup)
>>
>> where replace(tup) is an expression or function that produces a tuple
>> meeting your criteria.  Simplest is
>> (max(tup[0],Rthresh), max(tup[1],Gthresh), max(tup[2],Bthresh)).
>>
>> If nearly all your pixels are ok, add the following before the
>> assignment so you only make replacements when actually needed:
>> if tup[0] < Rthresh or tup[1] < Gthresh or tup[2] < Bthresh:
>>
>> Terry Jan Reedy
> 
> A giant thank-you to all who've posted in response to my query - these
> are all much better approaches to my problem.  I think I got hooked on
> using a list comprehension as it seemed the most concise approach vs.
> other techniques after a bunch of Google searches, but all of you have
> pointed out more efficient methods.  I appreciate your willingness to
> indulge a n00b who hasn't thought his problem through, apparently.
> I'll try all of these approaches out over the next day and see what
> works best, although I suspect you've all posted sufficient solutions.
> 
> Can't wait to try these suggestions out - cheers, idiolect

I'd like to emphasize Roger Miller's point with a small example script:

#!/usr/bin/env python
import sys
import Image

def transform_image_getdata(old, new):
    image = Image.open(old)

    image.putdata([(max(50, r), min(g*2, 255), 0) for r, g, b in
image.getdata()])

    image.save(new)

def transform_image_point(old, new):
    image = Image.open(old)

    transform_red = [max(50, i) for i in range(256)]
    transform_green = [min(i*2, 255) for i in range(256)]
    transform_blue = [0 for i in range(256)]

    image.point(transform_red + transform_green + transform_blue).save(new)


if __name__ == "__main__":
    import os, time
    def measure(f):
        def g(*args, **kw):
            start = time.time()
            try:
                return f(*args, **kw)
            finally:
                print f.__name__, time.time() - start
        return g

    for name in sys.argv[1:]:
        a, b = os.path.splitext(name)
        measure(transform_image_getdata)(name, a + ".getdata" + b)
        measure(transform_image_point)(name, a + ".point" + b)

Run it:

$ ./transform_image.py tmp.jpg
transform_image_getdata 27.5557489395
transform_image_point 0.458500862122

For the example Image.point() is about 60 times faster than getdata(). It's
the way to go if you can handle the color channels individually.

Peter



More information about the Python-list mailing list