Number of colors in an image

W

Will McGugan

Laszlo said:
Hello,

How can I determine the number of colors used in an image? I tried to
search on Google but I could figure out. I read the PIL handbook but I
do not see how to do it. Can anyone help?

You may get a better response on how to do it in Python, but the
following C++ code should be easy to translate if necessary. All it
requires is a get pixel function.

int CImage::CountUsedColours() const
{
if ( !IsValid() ) { Error( errImageInvalid ); return 0; }

const int TableSize= ( 256 * 256 * 256 ) / 8;

byte CountTable[ TableSize ];
memset( &CountTable[0], 0, TableSize );

for( int Y= 0; Y < GetHeight(); Y++ )
{
byte* pColour= (byte*)GetLinePointer( Y );
int WidthCount= GetWidth();

while( WidthCount-- )
{
const CColour colPix= GetPixel( pColour );
const int Offset= (int)colPix.R << 16 | (int)colPix.G << 8 |
(int)colPix.B;
CountTable[ Offset >> 3 ] |= 1 << ( Offset & 7 );
pColour+= GetBPP();
}

}

int ColourCount= 0;

for( int n= 0; n < TableSize; n++ )
{
const int CountBits= CountTable[n];

ColourCount+= ( CountBits & 1 );
ColourCount+= ( CountBits >> 1 ) & 1;
ColourCount+= ( CountBits >> 2 ) & 1;
ColourCount+= ( CountBits >> 3 ) & 1;
ColourCount+= ( CountBits >> 4 ) & 1;
ColourCount+= ( CountBits >> 5 ) & 1;
ColourCount+= ( CountBits >> 6 ) & 1;
ColourCount+= ( CountBits >> 7 );
}

OK();
return ColourCount;
}


Regards,

Will McGugan
 
L

Laszlo Zsolt Nagy

Hello Will,

You may get a better response on how to do it in Python, but the
following C++ code should be easy to translate if necessary. All it
requires is a get pixel function.
int CImage::CountUsedColours() const .....


Will McGugan

Yes, this can be efficient in C++. I'm not sure but possible it is not
so efficient in Python. Also it would be far better to have direct
access to the pixels (and not using a method/function). Now I'm
thinking about ImageMagick and something like this:

f = os.popen('identify -verbose ' + fullpath + ' | grep -e "Colors:"')
s = f.read()
index = s.find(':')
color_count = long( (s[index+1:]).strip() )

Probably ImageMagick uses direct access to pixels but it still take
seconds to run. It is not that portable but I'll stay at this version.

:)

Thank you very much.

--
Best regards,
Laszlo

mailto:[email protected]
web:http://designasign.biz
 
D

David Bolen

Laszlo Zsolt Nagy said:
How can I determine the number of colors used in an image? I tried to
search on Google but I could figure out. I read the PIL handbook but I
do not see how to do it. Can anyone help?

I saw your later post about using an external ImageMagick, but just in
case you do want to try it with PIL, one approach is to get the raw
data from PIL for each band, combining them into a tuple for the color
code, and then constructing a dictionary to count the colors.

For example, for an RGB image:

import sys
from PIL import Image
from itertools import izip, repeat

if __name__ == "__main__":

im = Image.open(sys.argv[1])
r = im.getdata(0)
g = im.getdata(1)
b = im.getdata(2)

# Use a dictionary to count colors by constructing it with an
# iterator of tuples of (pixel,1). The inner izip makes each
# pixel tuple out of the individual band values, and the outer
# izip combines it with the infinitely repeating 1 to generate
# the values for the dictionary constructor.
colors = dict(izip(izip(r,g,b), repeat(1)))

print 'Number of colors =', len(colors)

For a greyscale, or single banded image, it should be faster just to
use the built-in PIL "histogram" method and take the length of the
resulting list. You could also generalize the above to handle CMYK
too by using getbands() to dynamically work with as many bands as
available.

The use of the itertools functions helps to keep memory usage down
(but does require Python 2.3+) in terms of intermediate object
creation, but it'll still be fairly wasteful since the getband()
methods return a full list of integer values for the image pixels, and
the dictionary will have to have a tuple and value for each unique
color entry.

On my older PIII-450 desktop with Windows, this processes a 1416x1028
RGB image with 269750 colors in under 12 seconds. (A 3.2GHz P4
running FreeBSD does the same image in about 2s). It appears to be
about 5-6 times faster than a simple nested for loop processing each
pixel (via PILs getpixel()). Of course, it's nowhere near nowhere
near what some good C/C++ code could do if it had direct access to the
image data.

-- David
 
C

Christos TZOTZIOY Georgiou

# Use a dictionary to count colors by constructing it with an
# iterator of tuples of (pixel,1). The inner izip makes each
# pixel tuple out of the individual band values, and the outer
# izip combines it with the infinitely repeating 1 to generate
# the values for the dictionary constructor.
colors = dict(izip(izip(r,g,b), repeat(1)))

print 'Number of colors =', len(colors)

A set seems more appropriate in this case:

color_count = len(set(izip(r, g, b))) # untested code
For a greyscale, or single banded image, it should be faster just to
use the built-in PIL "histogram" method and take the length of the
resulting list.

More like the count of non-zero elements in the histogram; I believe the
length of the resulting list will be constant (ie 256).
 
L

Laszlo Zsolt Nagy

Hello David,

Friday, November 26, 2004, 11:18:04 PM, you wrote:

For example, for an RGB image:
import sys
from PIL import Image
from itertools import izip, repeat
if __name__ == "__main__":
im = Image.open(sys.argv[1])
r = im.getdata(0)
g = im.getdata(1)
b = im.getdata(2)
print 'Number of colors =', len(colors)

Thank you, this was very helpful. I'll try to use this one instead of
ImageMagick. (The identify program does not work in some cases...)

--
Best regards,
Laszlo

mailto:[email protected]
web:http://designasign.biz
 
T

Tim Hoffman

Have a look at the histogram function of PIL


Tim said:
Hello David,

Friday, November 26, 2004, 11:18:04 PM, you wrote:


For example, for an RGB image:

import sys
from PIL import Image
from itertools import izip, repeat

if __name__ == "__main__":

im = Image.open(sys.argv[1])
r = im.getdata(0)
g = im.getdata(1)
b = im.getdata(2)
print 'Number of colors =', len(colors)


Thank you, this was very helpful. I'll try to use this one instead of
ImageMagick. (The identify program does not work in some cases...)
 
D

David Bolen

Christos "TZOTZIOY" Georgiou said:
A set seems more appropriate in this case:

color_count = len(set(izip(r, g, b))) # untested code

Well, while potentially premature optimization, I was trying for
performance in this case. In Python 2.3, the sets module is coded in
Python, and just wraps a dictionary, and when handed an iterable, ends
up looping (in Python) with individual dictionary key assignments.
Although I didn't specifically test sets, when I did a loop like that
myself, it was 5-6 times slower than directly building the dictionary.

That might change in 2.4 with the built-in set - it's still a wrapper
around dict but knows it's just directly setting items to a true value
so can avoid dealing with the tuples that dict does (not to mention I
don't have to build the extra tuple).

Although I expect the direct support in PIL 1.1.5 that Fredrik posted
about will be best.
More like the count of non-zero elements in the histogram; I believe the
length of the resulting list will be constant (ie 256).

Oops, definitely yes.

-- David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top