This core of this program takes an set of three images, two references and one test, and classifies the test image as more similar to reference A or reference B. It then uses a loop to poll for new images (from an HTTP server) in real time and run commands when the images changes between the two states. A fun application of this is to monitor a webcam and turn the screensaver on and off depending on whether or not you're sitting in front of your computer.
First, a module to summarize an image. This samples pixels at regular intervals and stores the values in an array or arrays:
# ImgSum.pm
package ImgSum;
use strict;
use warnings;
use Image::Magick;
sub summary {
my ($imgf, $res) = @_;
my $img = new Image::Magick;
$img->Read($imgf);
my ($w, $h) = $img->Get(qw/rows columns/);
my @sum;
my $i = 0;
for (my $y = 0; $y < $h; $y += $res)
{
for (my $x = 0; $x < $w; $x += $res)
{
my $px = $img->Get("pixel[$x,$y]");
my $rgb = [split/,/, $px];
$sum[$i++] = $rgb;
}
}
return \@sum;
}
1;
Next, a script to summarize an image file and store it for later use:
#!/usr/bin/perl
# sum.pl
use strict;
use warnings;
use ImgSum;
use Data::Dumper;
use Storable;
my ($img, $res, $out) = @ARGV;
# $res is the sampling resolution
my $sum = ImgSum::summary($img, $res);
store($sum, $out);
The next module takes two image summaries and calculates the average difference between them. It's an incredibly simple algorithm that has numerous shortcomings, but it works in simple situations.
# SumDif.pm
package SumDiff;
use strict;
use warnings;
use Data::Dumper;
sub mean {
my $tt = 0;
my $s = 0;
while (defined($_ = shift))
{
$tt += $_;
$s++;
}
return $tt/$s;
}
sub diffmap {
my ($s1, $s2) = @_;
my @diff;
foreach my $i(0..$#$s1)
{
my $c1 = $s1->[$i];
my $c2 = $s2->[$i];
my @d = map { abs($c2->[$_] - $c1->[$_]) } (0..$#$c1);
$diff[$i] = mean(@d);
}
return \@diff;
}
sub diffavg {
return mean(@{diffmap(@_)});
}
1;
Finally, watch.pl downloads images and compares them with two references and runs a command whenever the current image changes from being more like one reference to being more like the other. You can use a program like spook to serve images captured from a webcam.
#!/usr/bin/perl
# watch.pl
use strict;
use warnings;
use ImgSum;
use LWP::Simple;
use SumDiff;
use Storable;
my @refs = map {retrieve($_)} ('sum-r0.dat', 'sum-r1.dat');
sub min {
my $mni = 0;
for my $i(0..$#_)
{
if ($_[$i] < $_[$mni])
{
$mni = $i
}
}
return $mni;
}
sub getclass {
my $test = shift;
my @diffs;
for my $i(0..$#refs)
{
my $diff = SumDiff::diffavg($refs[$i], $test);
$diffs[$i] = $diff;
print "Difference to $i: $diff\n";
}
return min(@diffs);
}
my $lc = 1;
while (sleep 2)
{
my $stat = getstore('http://localhost:45005/webcam.jpg', 'current.
+jpg');
print "HTTP status: $stat\n";
my $sum = ImgSum::summary('current.jpg', 4);
my $cl = getclass($sum);
print "Likely class: $cl\n";
if ($lc != $cl)
{
system('xscreensaver-command ' .
($cl?'-deactivate':'-activate'));
$lc = $cl;
}
}
To use it:
( take a picture of the empty room, save it as ref0.jpg )
$ ./sum.pl ref0.jpg 4 sum-r0.dat
( take a picture of yourself, call it ref1.jpg )
$ ./sum.pl ref1.jpg 4 sum-r1.dat
$ ./watch.pl
Now walk in front of and away from the camera slowly and your screensaver will turn on and off.