This function is supposed to take an integer that contains an rgb value, extract the value of each channel, and then return that calculated luminance of the pixel.

As it stands, the performance of this function is not fast enough. Looking at it, I can't think of anything else that I can do to speed it up.

Also, I was thinking if there's an algorithm to calculate the luminance straight from the integer instead of converting to rgb first, and whether that would be faster.

Anyway, here it is:

def luminance(pixel):
    """Calculate and return the luminance of a pixel."""
    r = pixel & 255
    g = (pixel & (255 << 8)) >> 8
    b = pixel >> 16

    return 0.299 * r + 0.587 * g + 0.114 * b

Edit: What ever improvements you may have, however little they are, don't hesitate to suggest them. This function is called hundreds of thousands (sometimes even millions) of times by the script.

Recommended Answers

All 5 Replies

Write it in C.

Edit:
Well, for starters,

g = (pixel >> 8) & 255;

It's not necessary to right-shift a value immediately before you multiply it by a constant. So:

def luminance(pixel):
    """Calculate and return the luminance of a pixel."""

    return 0.299 * (pixel & 0xFF) + 0.002293 * (pixel & 0xFF00) + .00000174 * (pixel & 0xFF0000)

I assume removing the doc comment doesn't help, but maybe you shouldn't.

Also, there's the option of inlining the code, so that you don't incur the cost of a function call (which seems to me like it would be a problem in Python).

No seriously, write this performance-intensive part in C.

I don't want to write it in C because the script is to be used to demonstrate how something can be done in Python.

I really only want the fastest speed that python can offer (as I said, it's just for demonstration), so if this is as fast as it gets, I will be happy to settle.

commented: I owe you one. +9

Rashakil Fol's approach shaves off a few precious fractional microseconds ...

# calculate the luminance of a color pixel
# (psyco does not help to improve speed)

def luminance1(pixel):
    """calculate and return the luminance of a denary pixel value"""
    r = pixel & 255
    g = (pixel & (255 << 8)) >> 8
    b = pixel >> 16
    return 0.299 * r + 0.587 * g + 0.114 * b

def luminance2(pixel):
    """calculate and return the luminance of a denary pixel value"""
    r = pixel & 255
    g = (pixel >> 8) & 255
    b = pixel >> 16
    return 0.299 * r + 0.587 * g + 0.114 * b

def luminance3(pix):
    """calculate and return the luminance of a denary pixel value"""
    return 0.299*(pix&255)+0.587*((pix>>8)&255)+0.114*(pix>>16)

def luminance4(pix):
    """calculate and return the luminance of a denary pixel value"""
    return 0.299*(pix&0xFF)+0.00229297*(pix&0xFF00)+0.0000017395*(pix&0xFF0000)


color = "0xFF6734"
pix = int(color, 16)
lum1 = luminance1(pix)
print( "1) color pixel = %d (%X) has luminance %f" % (pix, pix, lum1) )
lum2 = luminance2(pix)
print( "2) color pixel = %d (%X) has luminance %f" % (pix, pix, lum2) )
lum3 = luminance3(pix)
print( "3) color pixel = %d (%X) has luminance %f" % (pix, pix, lum3) )
lum4 = luminance4(pix)
print( "4) color pixel = %d (%X) has luminance %f" % (pix, pix, lum4) )

print('-'*60)

import timeit

def use_timeit(stmt, setup, passes=1000000):
    """
    use module timeit to time a statement stmt
    (this can be a function call too)
    setup gives information where too find variables/functions
    for time consuming functions use fewer passes to save time
    for instance -->
    stmt='myfunction(x)'
    setup='from __main__ import myfunction, x'
    """
    t = timeit.Timer(stmt=stmt, setup=setup)
    # doing 10 passes * 100000 gives the time in microseconds/pass
    elapsed = (1000000//passes * t.timeit(number=passes))
    print( "Function '%s' takes %0.3f micro-seconds/pass" % (stmt, elapsed) )

stmt = 'luminance1(pix)'
setup = 'from __main__ import luminance1, pix'
use_timeit(stmt, setup, passes=1000000)

stmt = 'luminance2(pix)'
setup = 'from __main__ import luminance2, pix'
use_timeit(stmt, setup, passes=1000000)

stmt = 'luminance3(pix)'
setup = 'from __main__ import luminance3, pix'
use_timeit(stmt, setup, passes=1000000)

stmt = 'luminance4(pix)'
setup = 'from __main__ import luminance4, pix'
use_timeit(stmt, setup, passes=1000000)

"""
typical result on my computer -->
1) color pixel = 16738100 (FF6734) has luminance 105.079000
2) color pixel = 16738100 (FF6734) has luminance 105.079000
3) color pixel = 16738100 (FF6734) has luminance 105.079000
4) color pixel = 16738100 (FF6734) has luminance 105.079000
------------------------------------------------------------
Function 'luminance1(pix)' takes 1.647 micro-seconds/pass
Function 'luminance2(pix)' takes 1.613 micro-seconds/pass
Function 'luminance3(pix)' takes 1.600 micro-seconds/pass
Function 'luminance4(pix)' takes 1.317 micro-seconds/pass
"""
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.