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

  Any suggestions for a Module/Code snippet to count the number of pixels in a giff/png/whatever? Well, most likely gif or png. FYI: the application I'm writing this for is one which will go through a collection of images, all of which are cut-outs from a main image, and compare the number of pixels in said image to the total number of pixels in the origional image, and hence allow me to estimate the percent cover of the cut-out piece from the whole.

  And yes, for those of you who have followed my previous quandries on perlmonks, this is to estimate the percentage cover of sessile marine invertebrates from digital photo quadrats.

Replies are listed 'Best First'.
Re: pixel counting
by sauoq (Abbot) on Jun 08, 2003 at 21:18 UTC

    Check out Image::Info. That module handles lots of image formats and provides a dim() function. Use it in list context to get both X and Y dimensions and then multiply for the number of pixels. This code is untested, but it should be as easy as:

    use Image::Info; my $info = image_info($image_file); my ($x,$y) = dim($info); print "Total pixels: ", $x * $y, "\n";

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: pixel counting
by BrowserUk (Patriarch) on Jun 08, 2003 at 21:24 UTC

    I'm just seeking clarification of your problem.

    Are the images your after counting the pixels in, non-rectangular in shape?

    Will compression have been applied to them?

    GIFs and PNGs are usually rectangular in shape, and the size is embedded in the header and easily recoverable. The only way (that I am aware, though I'm open to correction?) that a non rectangular piece of an image could be represented in either format is by designating one of the colour table entries to be the "transparent background colour". So, to determine the number of pixels that constitute the visible part of the image means counting those not index by that colour table entry. However, that get further complicated if compression has been applied to the image.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


      Yup - quite nonrectangular - the initial images will look somewhat like this - although larger and more diverse (and if ya wanna know what all of that stuff is, msg me and I'll be happy to talk about it)- and I would like to be able to select and cut out the portion of the photo that pertain to each species, dump them in a seperate file (I was thinking transparent gif or png, so the only pixels are those that are from the species), then I'd like to run a script that counts the total number of pixels taken up by the selection.

      So, in really ugly psuedocode
      my $referencepixels=countpixels($referencefile); @data=ls($datadir) foreach my $file (@data){ my $cover=countpixels($file)/$referencepixels; $datahash{$file}=$cover; } output_to_datafile(%datahash);

        This is untested; just going from memory and a quick look at the Image::Magick API Reference. Basically it should read in the image, figure out which colour is transparent, then loop through every pixel and increment a counter when the pixel isn’t transparent. Again, it’s untested. I’m unsure if the pixel index starts at 1 (as I used) or 0. I’m also not totally sure on the $image->Get('transparent') call -- read the API for the exact method -- it’s been a while since I've used it. This should give you a good jumping off point though.

        use Image::Magick; my $image = new Image::Magick; $image->Read('foo.png'); my $count; # The number of non-tra +nsparent pixels. my $transparent = $image->Get('transparent'); # Index of the transpar +ent colour. my $height = $image->Get('height'); # Image height. my $width = $image->Get('width'); # Image width. for my $x ( 1 .. $height ) { for my $y ( 1 .. $width ) { $count++ if $image->Get("pixel[$x,$y]") != $transparent; } }

        If I get what you're trying to do, it might be beneficial to store each species as a mask. If you save it as a black and white bitmap counting becomes really fast, since you can read the file as one long binary string and just count the 1’s.

        Another possibility (again I’m assuming you're generating these masks by hand) is to use a format that allows multiple layers. Storing the image as a photoshop PSD or something similar would allow you to keep all the masks as named channels or layers. The Gimp might be a great tool here as it allows Perl extensions and has said layers. This way the masks stay embedded right in the original image for easy archiving, and you could even store the result of those counts in the image comments. With a little Perl magic, Gimp could become a biology workdesk :).

        That's what I figured. Have you decided yet how (ie. what tools. I can see the process will probably need to be manual) you are to use to create the cut-outs?


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


Re: pixel counting
by abell (Chaplain) on Jun 09, 2003 at 08:40 UTC

    If the border is formed by segments and you can code it as the sequence of edges
    ( (P0x, P0y), (P1x, P1y), ... , (P0x, P0y) ),
    then there is a simple way to compute the inside area:

    my @border = ( [2,1], [1,4], [4,5], [5,2], [2,1] ); my $area = 0; for ( 0 .. $#border-1 ) { $area += ( $border[$_+1][0] - $border[$_][0] ) * ( $border[$_][1] + $border[$_+1][1] ) / 2; } print "$area\n";
    This is simply the integral of the segments (the area of the underlying trapeziums), with a positive sign when going right and negative when going left. In order for it to give the correct (positive) result, the points of the border must be stored clockwise. If there are holes in your image, you have to compute their area separately and subtract it from the area of the outer image (or you could use other tricks, like inserting a "cut" from the outer border to the inner one and counting it in both directions).

    Cheers

    Antonio

    Update: Slight change in code and comment to make them better fit together

    The stupider the astronaut, the easier it is to win the trip to Vega - A. Tucket
Re: pixel counting
by AidanLee (Chaplain) on Jun 09, 2003 at 14:58 UTC
    This doesn't really answer your question, but is there any particular reason you aren't using GIS? It's a software package that was made for this kind of work.