leocharre has asked for the wisdom of the Perl Monks concerning the following question:

I want to take an image, limit the colors, and then find out what they are.

use Image::Magick; my $img = Image::Magick->new; $img->Read('./img.jpg' ); $img->Quantize( colors => 18 ); $img->Set( type => 'Palette' ); $img->Write('./out.png'); # I can check that it has been reduced... my $out = Image::Magick->new; $out->Read('./out.png'); $out->Get( colors ) == 18 or die;

How do I get what the colors actually are? In hex, rgb, anything will do..

Like the output of identify -verbose would do?

I want an array or some other structure that holds the colors. Right now I'm making a call to cli 'identify' but it's demeaning.

I'm willing to use something other than IM

Replies are listed 'Best First'.
Re: Reading an image palette
by BrowserUk (Patriarch) on Sep 26, 2008 at 21:48 UTC

    GD will do that for you:

    #! perl -slw use strict; use GD; my $img = GD::Image->new( $ARGV[ 0 ] ) or die $!; my $colors = $img->colorsTotal or die "$ARGV[ 0] is a truecolor image (no palette)"; for ( 0 .. $colors ) { printf "%5d: R:%2x G:%2x B:%2x\n", $_, $img->rgb( $_ ); }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Thank you, this is wonderful!

      I'm using it as:

      sub palette_rgb_gd { my $abs_path = _abs_path($_[0]) or return; my @colors; require GD; my $img = GD::Image->new( $abs_path ) or die $!; my $colors = $img->colorsTotal or carp("$abs_path is a truecolor image (no palette)?"); for ( 0 .. $colors ) { my $rgb = sprintf '%02x%02x%02x', $img->rgb( $_ ); #debug( $rgb); #printf "%5d: R:%2x G:%2x B:%2x\n", $_, $img->rgb( $_ ); push @colors , $rgb; } return @colors; }
      This is way better than what I was doing before!
      sub identify_im { my $abs_path = _abs_path($_[0]) or return; my %HASH; my $cmd = "identify -verbose '$abs_path'"; my @_i = split(/\n/, `$cmd`); if( $? ){ carp("problem with : $cmd, $abs_path, $?") and return; } @_i and scalar @_i or carp("no output with : $cmd, $abs_path") and +return; my $lastkey; for my $_line (@_i){ $_line=~s/^\s+|\s+$//g; $_line=~s/([^\:]+)\://; my($k,$v) = ($1,$_line); if ($k=~/^Histogram$|^Colormap$/){ $HASH{$k}={}; $lastkey =$k; } elsif($lastkey=~/^Histogram$|^Colormap$/ and $k=~/^\d+$/ ){ $HASH{$lastkey}->{$k} = $v; } else { $HASH{$k}= $v; $lastkey = $k; } } return \%HASH; } sub palette_rgb_im { my $abs_path = _abs_path($_[0]) or return; my $h = identify_im($abs_path) or return; my $colors = $h->{Colormap} || $h->{Histogram} ; $colors or carp("No Colormap or Historgram in $abs_path") and retur +n; my @colors; for ( keys %$colors ){ my $_line = $colors->{$_}; $_line=~/\(\s*([0-9]+),\s*([0-9]+),\s*([0-9]+)\)/ or die("cant m +atch into $_line"); my ($r,$g,$b) = ($1,$2,$3); debug( "$r $g $b"); #push @colors, "$r$g$b"; my $rgb = sprintf '%2x%2x%2x', $r, $g , $b; push @colors, $rgb; } return @colors; }
Re: Reading an image palette
by tod222 (Pilgrim) on Sep 26, 2008 at 21:02 UTC
    As you note, the command-line tool identify -verbose prints the colormap.

    I'm not sure if there's an API to return that information, but it's likely that the folks on the magick-users mailing list will know. You can also search the mailing list archive available at that link.

Re: Reading an image palette
by zentara (Cardinal) on Sep 27, 2008 at 15:04 UTC
      Hmm.. Yeah.. I'm not getting a return value on Identify().. At all.
        What do you want returned? Identify prints it's output to the terminal. If you want to capture the output for regexing, run the c command thru backticks or a piped open.
        #my $return = `identify -verbose $file`; #print $return; my $pid = open(my $fh,"identify -verbose $file |"); while(<$fh>){print }

        I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: Reading an image palette
by zentara (Cardinal) on Oct 05, 2008 at 19:24 UTC
    I knew that IM could do it... it's just such a big program things are hard to find and make work. Anyways, this gives output similar to BrowserUk's GD script.

    This is only quickly tested, I may have the shift order wrong, etc. The color count dosn't seem to agree with GD. Please test yourself to verify worthiness.

    #!/usr/bin/perl use warnings; use strict; use Image::Magick; my $file = shift or die "Need a file $!\n"; my $img = Image::Magick->new; $img->ReadImage($file); $img->Quantize( colors => 18 ); $img->Set( type => 'Palette' ); #$img->Write("$0.png"); # if you want a pixel by pixel list of every color # a huge slow output #$img->Set(magick => 'txt'); #$img->Write("$0.txt"); #histogram #returned values are an array of #red, green, blue, opacity, and count values. my $tot = 0; my (@colors) = $img->Histogram(); #print join "\n\n", @colors; # probably some array slice would be better than multi-shifts, # any code improvement's welcome while (1){ if (scalar @colors == 0){last} my $r = shift @colors; my $g = shift @colors; my $b = shift @colors; my $o = shift @colors; my $count = shift @colors; $tot++; print "$count ($r,$g,$b) at $o opacity\n"; } print "\n$tot total colors\n";

    I'm not really a human, but I play one on earth Remember How Lucky You Are
      As a followup as to why there is sometimes a discrepancy between the GD and IM output, it's because GD gets the actual palette, whearas the IM histogram method only gets the actual colors used. So you may have a palette with 17 colors, but only 15 may be used. I've also seen palettes with black(0,0,0) listed multiple times....that will screwup accurate color counts.

      I'm not really a human, but I play one on earth Remember How Lucky You Are