LloydRice has asked for the wisdom of the Perl Monks concerning the following question:
I have a question about the Imager package. This is ActiveState v5.12.4 with Win7Pro on a 64-bit Gateway SX-2841.
I would like to access the pixel values from a BMP image.
c:>type gp1.pl
use Imager;
my $image_source = "\\c\\perl\\graphics\\" . shift . ".bmp";
my $image = Imager->new;
$image->read(file=>$image_source)
or die "Cannot load $image: ", $image->errstr;
$width = $image->getwidth();
$height = $image->getheight();
print "Image dimensions: height = $height, width = $width\n";
$x = 10;
for $y ( 10 .. 15 )
{
$color = $image->getpixel( x=>$x, y=>$y, type=>'8bit' );
( $r, $g, $b, $a ) = $color->rgba();
print " shade = $r, $g, $b\n";
}
c:>gp1 d2044
Image dimensions: height = 348, width = 349
shade = 238, 238, 238
shade = 255, 255, 255
shade = 255, 255, 255
shade = 238, 238, 238
shade = 238, 238, 238
shade = 238, 238, 238
c:>
So far, so good. It is said that I can also use the getpixel invocation directly as the method reference to run rgba().
c:>type gp2.pl
use Imager;
my $image_source = "\\c\\perl\\graphics\\" . shift . ".bmp";
my $image = Imager->new;
$image->read(file=>$image_source)
or die "Cannot load $image: ", $image->errstr;
$width = $image->getwidth();
$height = $image->getheight();
print "Image dimensions: height = $height, width = $width\n";
$x = 10;
for $y ( 10 .. 15 )
{
( $r, $g, $b, $a ) =
$image->getpixel( x=>$x, y=>$y, type=>'8bit' )->rgba();
print " shade = $r, $g, $b\n";
}
c:>gp2 d2044
Image dimensions: height = 348, width = 349
shade = 238, 238, 238
shade = 255, 255, 255
shade = 255, 255, 255
shade = 238, 238, 238
shade = 238, 238, 238
shade = 238, 238, 238
Very good. But now it seems like a lot of work to call getpixel separately for each pixel. The 'draw' tutorial says that I can pass a reference to a list of pixel addresses to getpixel(). I should get back a list of references to a color method. I tried it this way.
c:>type gp3.pl
use Imager;
my $image_source = "\\c\\perl\\graphics\\" . shift . ".bmp";
my $image = Imager->new;
$image->read(file=>$image_source)
or die "Cannot load $image: ", $image->errstr;
$width = $image->getwidth();
$height = $image->getheight();
print "Image dimensions: height = $height, width = $width\n";
$x = 10;
$y = [ 10 .. 15 ];
@colors = $image->getpixel( x=>$x, y=>$y, type=>'8bit' );
for $color ( @colors )
{
( $r, $g, $b, $a ) = $color->rgba();
print " shade = $r, $g, $b\n";
}
c:>gp3 d2044
Image dimensions: height = 348, width = 349
Can't call method "rgba" on an undefined value at gp3.pl line 15.
No luck. It seems that the elements of @colors are undefined. I presume something is wrong with the getpixel() call.
OK. So maybe if one axis is a reference, then both have to be.
c:>type gp4.pl
use Imager;
my $image_source = "\\c\\perl\\graphics\\" . shift . ".bmp";
my $image = Imager->new;
$image->read(file=>$image_source)
or die "Cannot load $image: ", $image->errstr;
$width = $image->getwidth();
$height = $image->getheight();
print "Image dimensions: height = $height, width = $width\n";
$x = [ 10 ];
$y = [ 10 .. 15 ];
@colors = $image->getpixel( x=>$x, y=>$y, type=>'8bit' );
for $color ( @colors )
{
( $r, $g, $b, $a ) = $color->rgba();
print " shade = $r, $g, $b\n";
}
c:>gp4 d2044
Image dimensions: height = 348, width = 349
Can't call method "rgba" on an undefined value at gp4.pl line 15.
Still doesn't work. What am I missing?
Re: Using Imager getpixel
by Khen1950fx (Canon) on Dec 30, 2011 at 11:04 UTC
|
Two minor problems: First, the default is 8bit, so you don't need that. Second, you forgot to declare some of your variables, esp. $color, hence, the undefined value. I corrected that and did this, using your first script:
#!/usr/bin/perl
use strict;
use warnings;
use Imager;
my $image_source = shift @ARGV;
my $image = Imager->new;
$image->read( file => $image_source )
or die "Cannot load $image: ", $image->errstr;
my $width = $image->getwidth();
my $height = $image->getheight();
print "Image dimensions: height = $height, width = $width\n";
my $x = 10;
foreach my $y ( 10 .. 15 ) {
my $color = $image->getpixel( x => $x, y => $y );
my ( $r, $g, $b, $a ) = $color->rgba();
print " shade = $r, $g, $b\n";
}
| [reply] [d/l] [select] |
|
OK. I agree that '8bit' was the default, so it was redundant, but not wrong, to include it. But declaring $color does not seem to have been the answer for my third or fourth attempt. There does seem to be some sort of dependency between the use of scalar vs. reference on one or both axes. But this still does not clear up the mystery.
In the following, I have kept the startup portion of the code exactly as shown above by Khen1950fx, so I will not repeat it here. May we just concentrate on the getpixel call? First, I have located a portion of the image with distinct values in four adjacent pixels.
for my $x ( 14, 15 )
{
for my $y ( 14, 15 )
{
my @color = $image->getpixel( x => $x, y => $y );
my ( $r, $g, $b, $a ) = $color[0]->rgba();
print " shade( $x, $y ) = $r, $g, $b\n";
}
}
This version produces the output:
Image dimensions: height = 348, width = 349
shade( 14, 14 ) = 255, 255, 255
shade( 14, 15 ) = 221, 221, 221
shade( 15, 14 ) = 170, 170, 170
shade( 15, 15 ) = 51, 51, 51
So far, so good. But now if we replace both axes by referenced lists, we get something I did not expect:
my @color = $image->getpixel( x => [ 14, 15 ], y => [ 14, 15 ] );
for my $clr ( @color )
{
my ( $r, $g, $b, $a ) = $clr->rgba();
print " shade = $r, $g, $b\n";
}
We get, not the full square of scanned pixels, as I expected, but rather just the diagonal.
This would explain why both axes must have identical reference lists. If they differ, the result is undefined.
Image dimensions: height = 348, width = 349
shade = 255, 255, 255
shade = 51, 51, 51
No. That's still not quite it. Actually, the requirement is that both referenced lists do need to be the same length, but the reference is only to pixel pairs, respectively, not the full square (or rectangle), as I assumed it might work.
For example, this works:
my @color = $image->getpixel(
x => [ 13, 15, 17, 17, 20 ],
y => [ 17, 14, 18, 19, 15 ] );
for my $clr ( @color )
{
my ( $r, $g, $b, $a ) = $clr->rgba();
print " shade = $r, $g, $b\n";
}
The output is:
Image dimensions: height = 348, width = 349
shade = 204, 204, 204
shade = 170, 170, 170
shade = 136, 136, 136
shade = 187, 187, 187
shade = 153, 153, 153
And I ran the first code fragment above with the pixel address values such as to display the entire rectangle covered by this last example and the pixel values are exactly those reference pairwise here. In other words, if I had duplicated the pixel addresses for the final printout, I would have:
shade( 13, 17 ) = 204, 204, 204
shade( 15, 14 ) = 170, 170, 170
shade( 17, 18 ) = 136, 136, 136
shade( 17, 19 ) = 187, 187, 187
shade( 20, 15 ) = 153, 153, 153
Any time the pixel reference lists are of unequal length, you get an undefined result. Makes sense, now.
My only complaint then (Tony?), would be that this is not very clear in the 'draw' documentation.
| [reply] [d/l] [select] |
|
I'll improve it, both the handling of supplying a scalar and ref in x, y and diagnostics for different array lengths.
If you can work row-by-row instead of column-by-column, you might want to consider using getscanline() or getsamples() instead of getpixel().
| [reply] |
|
Re: Using Imager getpixel
by BrowserUk (Patriarch) on Dec 30, 2011 at 09:38 UTC
|
$y = [ 10 .. 15 ];
$x = [ (10) x $#$y ];
...
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
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".
| [reply] [d/l] [select] |
|
|