difference() method
authorTony Cook <tony@develop=help.com>
Sun, 31 Mar 2002 12:41:54 +0000 (12:41 +0000)
committerTony Cook <tony@develop=help.com>
Sun, 31 Mar 2002 12:41:54 +0000 (12:41 +0000)
Changes
Imager.pm
Imager.xs
filters.c
image.c
image.h
lib/Imager/Filters.pod
t/t61filters.t

diff --git a/Changes b/Changes
index c7b13be..a5cded8 100644 (file)
--- a/Changes
+++ b/Changes
@@ -625,6 +625,7 @@ Revision history for Perl extension Imager.
           prevent pointer vs int size warnings on 64-bit builds of perl.
         - add our own INT2PTR and PTR2IV definitions when perl doesn't
           supply them
+        - difference() method
 
 =================================================================
 
index 2b283a4..41d687f 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -2402,6 +2402,27 @@ sub map {
   return $self;
 }
 
+sub difference {
+  my ($self, %opts) = @_;
+
+  defined $opts{mindist} or $opts{mindist} = 0;
+
+  defined $opts{other}
+    or return $self->_set_error("No 'other' parameter supplied");
+  defined $opts{other}{IMG}
+    or return $self->_set_error("No image data in 'other' image");
+
+  $self->{IMG}
+    or return $self->_set_error("No image data");
+
+  my $result = Imager->new;
+  $result->{IMG} = i_diff_image($self->{IMG}, $opts{other}{IMG}, 
+                                $opts{mindist})
+    or return $self->_set_error($self->_error_as_msg());
+
+  return $result;
+}
+
 # destructive border - image is shrunk by one pixel all around
 
 sub border {
@@ -2516,6 +2537,7 @@ sub _set_error {
   else {
     $ERRSTR = $msg;
   }
+  return;
 }
 
 # Default guess for the type of an image from extension
index 6945022..3db907f 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -2773,6 +2773,11 @@ i_gradgen(im, ...)
         myfree(yo);
         myfree(ival);
 
+Imager::ImgRaw
+i_diff_image(im, im2, mindist=0)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     im2
+               int     mindist
 
 void
 i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, segs)
index 51993c7..e801488 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1197,6 +1197,100 @@ void i_unsharp_mask(i_img *im, double stddev, double scale) {
   i_img_exorcise(&copy);
 }
 
+/*
+=item i_diff_image(im1, im2, mindiff)
+
+Creates a new image that is transparent, except where the pixel in im2
+is different from im1, where it is the pixel from im2.
+
+The samples must differ by at least mindiff to be considered different.
+
+=cut
+*/
+
+i_img *
+i_diff_image(i_img *im1, i_img *im2, int mindiff) {
+  i_img *out;
+  int outchans, diffchans;
+  int xsize, ysize;
+  i_img temp;
+
+  i_clear_error();
+  if (im1->channels != im2->channels) {
+    i_push_error(0, "different number of channels");
+    return NULL;
+  }
+
+  outchans = diffchans = im1->channels;
+  if (outchans == 1 || outchans == 3)
+    ++outchans;
+
+  xsize = min(im1->xsize, im2->xsize);
+  ysize = min(im1->ysize, im2->ysize);
+
+  out = i_sametype_chans(im1, xsize, ysize, outchans);
+  
+  if (im1->bits == i_8_bits && im2->bits == i_8_bits) {
+    i_color *line1 = mymalloc(2 * xsize * sizeof(*line1));
+    i_color *line2 = line1 + xsize;
+    i_color empty;
+    int x, y, ch;
+
+    for (ch = 0; ch < MAXCHANNELS; ++ch)
+      empty.channel[ch] = 0;
+
+    for (y = 0; y < ysize; ++y) {
+      i_glin(im1, 0, xsize, y, line1);
+      i_glin(im2, 0, xsize, y, line2);
+      for (x = 0; x < xsize; ++x) {
+        int diff = 0;
+        for (ch = 0; ch < diffchans; ++ch) {
+          if (line1[x].channel[ch] != line2[x].channel[ch]
+              && abs(line1[x].channel[ch] - line2[x].channel[ch]) > mindiff) {
+            diff = 1;
+            break;
+          }
+        }
+        if (!diff)
+          line2[x] = empty;
+      }
+      i_plin(out, 0, xsize, y, line2);
+    }
+    myfree(line1);
+  }
+  else {
+    i_fcolor *line1 = mymalloc(2 * xsize * sizeof(*line1));
+    i_fcolor *line2 = line1 + xsize;
+    i_fcolor empty;
+    int x, y, ch;
+    double dist = mindiff / 255;
+
+    for (ch = 0; ch < MAXCHANNELS; ++ch)
+      empty.channel[ch] = 0;
+
+    for (y = 0; y < ysize; ++y) {
+      i_glinf(im1, 0, xsize, y, line1);
+      i_glinf(im2, 0, xsize, y, line2);
+      for (x = 0; x < xsize; ++x) {
+        int diff = 0;
+        for (ch = 0; ch < diffchans; ++ch) {
+          if (line1[x].channel[ch] != line2[x].channel[ch]
+              && abs(line1[x].channel[ch] - line2[x].channel[ch]) > dist) {
+            diff = 1;
+            break;
+          }
+        }
+        if (!diff)
+          line2[x] = empty;
+      }
+      i_plinf(out, 0, xsize, y, line2);
+    }
+    myfree(line1);
+  }
+
+  return out;
+}
+
 struct fount_state;
 static double linear_fount_f(double x, double y, struct fount_state *state);
 static double bilinear_fount_f(double x, double y, struct fount_state *state);
diff --git a/image.c b/image.c
index 3fab821..e3fa3a7 100644 (file)
--- a/image.c
+++ b/image.c
@@ -1071,6 +1071,32 @@ i_img *i_sametype(i_img *src, int xsize, int ysize) {
   }
 }
 
+/*
+=item i_sametype_chans(i_img *im, int xsize, int ysize, int channels)
+
+Returns an image of the same type (sample size).
+
+For paletted images the equivalent direct type is returned.
+
+=cut
+*/
+
+i_img *i_sametype_chans(i_img *src, int xsize, int ysize, int channels) {
+  if (src->bits == 8) {
+    return i_img_empty_ch(NULL, xsize, ysize, channels);
+  }
+  else if (src->bits == i_16_bits) {
+    return i_img_16_new(xsize, ysize, channels);
+  }
+  else if (src->bits == i_double_bits) {
+    return i_img_double_new(xsize, ysize, channels);
+  }
+  else {
+    i_push_error(0, "Unknown image bits");
+    return NULL;
+  }
+}
+
 /*
 =item i_transform(im, opx, opxl, opy, opyl, parm, parmlen)
 
diff --git a/image.h b/image.h
index 7e507fd..25e231e 100644 (file)
--- a/image.h
+++ b/image.h
@@ -58,6 +58,7 @@ void   i_img_destroy(i_img *im);
 void   i_img_info(i_img *im,int *info);
 
 extern i_img *i_sametype(i_img *im, int xsize, int ysize);
+extern i_img *i_sametype_chans(i_img *im, int xsize, int ysize, int channels);
 
 i_img *i_img_pal_new(int x, int y, int ch, int maxpal);
 
@@ -614,6 +615,7 @@ void i_radnoise(i_img *im,int xo,int yo,float rscale,float ascale);
 void i_turbnoise(i_img *im,float xo,float yo,float scale);
 void i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure);
 void i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure);
+i_img *i_diff_image(i_img *im, i_img *im2, int mindist);
 typedef enum {
   i_fst_linear,
   i_fst_curved,
index 134d00a..f749de2 100644 (file)
@@ -22,6 +22,7 @@ Imager::Filters - Entire Image Filtering Operations
   unload_plugin("dynfilt/dyntest.so")
     or die "unable to load plugin\n";
 
+  $out = $img->difference(other=>$other_img);
 
 =head1 DESCRIPTION
 
@@ -397,5 +398,21 @@ Note: This seems to test ok on the following systems:
 Linux, Solaris, HPUX, OpenBSD, FreeBSD, TRU64/OSF1, AIX.
 If you test this on other systems please let me know.
 
+=head2 Image Difference
+
+You can create a new image that is the difference between 2 other images.
+
+  my $diff = $img->difference(other=>$other_img);
+
+For each pixel in $img that is different to the pixel in $other_img,
+the pixel from $other_img is given, otherwise the pixel is transparent
+black.
+
+This can be used for debugging image differences ("Where are they
+different?"), and for optimizing animated GIFs.
+
+Note that $img and $other_img must have the same number of channels.
+The width and heigh of $diff will be the minimum of each of the width
+and height of $img and $other_img.
 
 =cut
index 8708f9b..e1a64e0 100644 (file)
@@ -9,7 +9,7 @@ $imbase->open(file=>'testout/t104.ppm') or die;
 my $im_other = Imager->new(xsize=>150, ysize=>150);
 $im_other->box(xmin=>30, ymin=>60, xmax=>120, ymax=>90, filled=>1);
 
-print "1..43\n";
+print "1..45\n";
 
 test($imbase, 1, {type=>'autolevels'}, 'testout/t61_autolev.ppm');
 
@@ -91,6 +91,19 @@ test($imbase, 42, {type=>'fountain',  xa=>75, ya=>75, xb=>90, yb=>15,
                     segments=>$f4, super_sample=>'grid',
                     ftype=>'linear', combine=>'color' },
      'testout/t61_regress_fount.ppm');
+my $im2 = $imbase->copy;
+$im2->box(xmin=>20, ymin=>20, xmax=>40, ymax=>40, color=>'FF0000', filled=>1);
+$im2->write(file=>'testout/t61_diff_base.ppm');
+my $im3 = Imager->new(xsize=>150, ysize=>150, channels=>3);
+$im3->box(xmin=>20, ymin=>20, xmax=>40, ymax=>40, color=>'FF0000', filled=>1);
+my $diff = $imbase->difference(other=>$im2);
+print $diff ? "ok 44\n" : "not ok 44\n";
+if ($im3 && $diff) {
+  print Imager::i_img_diff($im3->{IMG}, $diff->{IMG}) ? "not ok 45\n" : "ok 45\n";
+}
+else {
+  print "ok 45 # skip\n";
+}
 
 sub test {
   my ($in, $num, $params, $out) = @_;