http://www.perlmonks.org?node_id=214294

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

I have written a script to open and process all the pixels of a *.jpg file. My problem is that I need a way to convert the different RGB values to colors, such as redish, blueish, greenish ect. After doing this i would also like perl to color a box with the color it said the pixel was. Any ideas/help/suggestions would be appreciated. Here is my code so far:
use GD; open IMA,"many.jpg" or die "NO file, $!"; binmode IMA; $image=new GD::Image->newFromJpeg(IMA); ($x,$y)=$image->getBounds(); for $ix (0..$x-1) { for $iy (0..$y-1) { ($r,$g,$b)=$image->rgb($im->getPixel($ix,$iy)); if($r ne $or) { printf "%2x%2x%2x\n",$r,$g,$b; print "$r, $g, $b\n"; #print "$image\n"; } $or = $r; } } close IMA;

Replies are listed 'Best First'.
Re: Convert RGB to an actual color
by Zaxo (Archbishop) on Nov 20, 2002 at 05:12 UTC

    I read this to say you want to obtain color names for nearby rgb values. Is that correct? Assuming so, GD can give you a great deal of help. I'll go over how to produce a new copy of an image from a restricted set of colors, and the names will fall right out.

    Declare we will make use of GD objects and define a function to return the index of a GD object's palette color from an rgb triple, allocating a new color if necessary:

    use GD; sub getindex { my $image = shift; my $index = $image->colorExact(@_); $index == -1 ? $image->colorAllocate(@_) : $index; }
    Here lets read all the named colors from /usr/X11/lib/X11/rgb.txt and provide them to a reference GD image's color table:
    my $reference_image = new GD::Image(100,100); my @colornames; { my $goodline = qr/^\d/; open my $rgbdat, '<', '/usr/X11/lib/X11/rgb.txt' or die $!; while (<$rgbdat>) { /$goodline/ or next; my @dat = split " ", $_, 4; my $color = pop @dat; $colornames[getindex($reference_image, @dat)] = $color; } close $rgbdat or die $!; }
    Now read the jpeg file into a GD::Image
    my $image = GD::Image->newFromJpeg('many.jpg') or die $!; my @size = $image->Bounds();
    Start a new GD::Image to be built with only named colors my $new_image = GD::Image->new @size; and fill it in
    { my ($ix, $iy, @rgb); # preallocate and reuse for $ix (0 .. $size[0]-1) { for $iy (0 .. $size[1]-1) { @rgb = $image->rgb(image->getPixel($ix,$iy))); @rgb = $reference_image->rgb( $reference_image->colorClose +stHWB( @rgb )); $new_image->setPixel($ix, $iy, getindex($new_image, @rgb)) +; } } }
    The new image's colors are obtained by grabbing the pixel's rgb color, finding the closest reference color's rgb values, and setting the corresponding pixel in the new image, allocating the named color to the new palette if necessary.

    We can define a function to return the name of the nearest color, given an rgb pair, but as this code is written there is a closure with $reference_image and @colornames. It would be better to put the reference structures in a module, or case them up in constants with initialization in a BEGIN{} block.

    sub getcolor { return "puke" unless @_ == 3; $colornames[$reference_image->colorClosestHWB(@_)] }

    You'll notice that GD provided all the heavy lifting for this. Have fun!

    After Compline,
    Zaxo

Re: Convert RGB to an actual color
by hawtin (Prior) on Nov 19, 2002 at 23:37 UTC

    I don't understand your question. Do you mean that you want to match the colour in a given location to the "closest" colour in a fixed set. If so you need to decide which colour space to use and calculate the distance. For RGB you will get good results if you assume the "distance" between two colours is the geometric distance, to compare $c1 and $c2:

    $distance = ($c1->red - $c2->red)*($c1->red - $c2->red) + ($c1->green - $c2->green)*($c1->green - $c2->green) + ($c1->blue - $c2->blue)*($c1->blue - $c2->blue);

    (of course there is no point in taking the square root if you are comparing distances). If you want to be overly clever you can first convert to another colourspace (HSV would probably be good).

    Or you could be wanting to classify the whole picture (i.e. take the average colour work out the closest node and the direction of the difference to call it say "a pinkish green" colour?

    Or maybe I have misunderstood you completely?

    Update: Or are you thinking about dithering the image (replacing the full set of colours with some picked from a restricted set) in which case you need to look at a good text on "error diffusion" and Floyd-Steinberg.

•Re: Convert RGB to an actual color
by merlyn (Sage) on Nov 20, 2002 at 00:14 UTC
Re: Convert RGB to an actual color
by pg (Canon) on Nov 20, 2002 at 02:07 UTC
    You got the color of each pixel already, so now you want to draw the picture by yourself.

    One choice available is Perl Tk. In Tk, there is one widget called Canvas, and it supports all the operation like draw a line, draw a circle, draw a point ...(If you have the draw point function, you can draw anything else, is it not true ;-).

    I have a suggestion, as you got all the pixels already, you can even play with it, flip it, rotate it, turn black into white, whatever you want.

    But one thing you have to bear in mind is that, the GD module posterizes the JPEG files.
Re: Convert RGB to an actual color
by Anonymous Monk on Nov 20, 2002 at 15:55 UTC
    Sorry for not being clear in my problem statement. I am running *in2k and perl 5.05. I want to read in all of the pixels from a particular white/gray image and then determine how opaque the image is from pure white. I don’t know if this is even possible with perl, but perl is the only lang. that I like to use. I have the reading of the image part working, (though I must have something wrong with my hex conversion) I just need a little help figuring out how to get the average color and then determining opacity from that. Thanks for all of your help already!
Re: Convert RGB to an actual color
by Anonymous Monk on Nov 21, 2002 at 23:41 UTC
    (1) If you want meaningful names ... There's a file rgb.txt floating around the net with standard names in RGB space. It was originally distributed by the X Windows consortium, found in /usr/lib/X11/rgb.txt if you have one. If you initialize your GD palette with those, and remember the color#->name map after each, you can then use colorClosest() to find the closest name. Warning: You'll need to use the TrueColor option otherwise the 897 colors will overflow your 256 color palette.

    You're in luck, CPAN is having a special on rgb.txt: "Color::Rgb - Simple rgb.txt parsing class". The CPAN TGZ includes Rgb.pm and rgb.txt.

    (2) Or if you want geometric generalizations and want to code it yourself, Color::Object handles HSV/HSL/RGB conversions, and can give you something not unlike angles in colorspace that you could round, interpret, and convert back.

    For background on colorspace stuff and all sorts of graphics processing, check out the Graphic Gems series and source archive and the successor journal jgt. They speak C over there, but it's easily transliterated.

    -- bill n1vux