add new comparison method rgb_difference that resembles arithmetical difference per... master
authorAndreas Koenig <andk@cpan.org>
Sun, 18 Oct 2020 16:11:55 +0000 (18:11 +0200)
committerTony Cook <tony@develop-help.com>
Wed, 10 Mar 2021 01:01:33 +0000 (12:01 +1100)
- the difference of two identical images is a black image
- any pixel in the resulting image that is not black indicates where there is a difference
- transparency is ignored

Imager.pm
Imager.xs
filters.im
imager.h
lib/Imager/Filters.pod
lib/Imager/Preprocess.pm
t/400-filter/010-filters.t

index 1204a63..d7504ac 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -3942,6 +3942,26 @@ sub difference {
   return $result;
 }
 
+sub rgb_difference {
+  my ($self, %opts) = @_;
+
+  $self->_valid_image("rgb_difference")
+    or return;
+
+  defined $opts{other}
+    or return $self->_set_error("No 'other' parameter supplied");
+  unless ($opts{other}->_valid_image("rgb_difference")) {
+    $self->_set_error($opts{other}->errstr . " (other image)");
+    return;
+  }
+
+  my $result = Imager->new;
+  $result->{IMG} = i_rgbdiff_image($self->{IMG}, $opts{other}{IMG})
+    or return $self->_set_error($self->_error_as_msg());
+
+  return $result;
+}
+
 # destructive border - image is shrunk by one pixel all around
 
 sub border {
@@ -4980,6 +5000,9 @@ register_reader() - L<Imager::Files/register_reader()>
 
 register_writer() - L<Imager::Files/register_writer()>
 
+rgb_difference() - L<Imager::Filters/rgb_difference()> - produce a difference
+images from two input images.
+
 rotate() - L<Imager::Transformations/rotate()>
 
 rubthrough() - L<Imager::Transformations/rubthrough()> - draw an image
index 9e49298..e9363db 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -3051,6 +3051,11 @@ i_diff_image(im, im2, mindist=0)
     Imager::ImgRaw     im2
             im_double     mindist
 
+Imager::ImgRaw
+i_rgbdiff_image(im, im2)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     im2
+
 undef_int
 i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, segs)
     Imager::ImgRaw     im
index 7c23a5a..2f6aa16 100644 (file)
@@ -1447,7 +1447,7 @@ i_diff_image(i_img *im1, i_img *im2, double mindist) {
   ysize = i_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(xsize * sizeof(*line1)); /* checked 17feb2005 tonyc */
     i_color *line2 = mymalloc(xsize * sizeof(*line1)); /* checked 17feb2005 tonyc */
@@ -1524,6 +1524,61 @@ i_diff_image(i_img *im1, i_img *im2, double mindist) {
   return out;
 }
 
+/*
+=item i_rgbdiff_image(im1, im2)
+
+Creates a new image that is black, except where the pixel in im2 is
+different from im1, where it is the arithmetical difference to im2 per
+color.
+
+=cut
+*/
+
+i_img *
+i_rgbdiff_image(i_img *im1, i_img *im2) {
+  i_img *out;
+  int outchans, diffchans;
+  i_img_dim xsize, ysize;
+  dIMCTXim(im1);
+
+  i_clear_error();
+  if (im1->channels != im2->channels) {
+    i_push_error(0, "different number of channels");
+    return NULL;
+  }
+
+  outchans = diffchans = im1->channels;
+  if (outchans == 2 || outchans == 4)
+    --outchans;
+
+  xsize = i_min(im1->xsize, im2->xsize);
+  ysize = i_min(im1->ysize, im2->ysize);
+
+  out = i_sametype_chans(im1, xsize, ysize, outchans);
+
+#code im1->bits == i_8_bits && im2->bits == i_8_bits
+    IM_COLOR *line1 = mymalloc(xsize * sizeof(*line1));
+    IM_COLOR *line2 = mymalloc(xsize * sizeof(*line1));
+    i_img_dim x, y;
+    int ch;
+
+    for (y = 0; y < ysize; ++y) {
+      IM_GLIN(im1, 0, xsize, y, line1);
+      IM_GLIN(im2, 0, xsize, y, line2);
+      for (x = 0; x < xsize; ++x) {
+        for (ch = 0; ch < outchans; ++ch) {
+          line2[x].channel[ch] = IM_ABS(line1[x].channel[ch] - line2[x].channel[ch]);
+        }
+      }
+      IM_PLIN(out, 0, xsize, y, line2);
+    }
+    myfree(line1);
+    myfree(line2);
+#/code
+
+  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);
index 6a21765..c9472c8 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -293,6 +293,7 @@ void i_turbnoise(i_img *im,double xo,double yo,double scale);
 void i_gradgen(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *ival, int dmeasure);
 int i_nearest_color(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *ival, int dmeasure);
 i_img *i_diff_image(i_img *im, i_img *im2, double mindist);
+i_img *i_rgbdiff_image(i_img *im, i_img *im2);
 int
 i_fountain(i_img *im, double xa, double ya, double xb, double yb, 
            i_fountain_type type, i_fountain_repeat repeat, 
index 4cb271d..8d4fb4d 100644 (file)
@@ -808,6 +808,35 @@ For large sample images this is scaled down to the range 0 .. 1.
 
 =back
 
+=item rgb_difference()
+
+You can create a new image that is the difference between 2 other images.
+
+  my $diff = $img->rgb_difference(other=>$other_img);
+
+For each pixel in $img that is different to the pixel in $other_img,
+the arithmetic difference for the value of the pixel in $img from
+$other_img per color is given. Transparency is ignored.
+
+This can be used for measuring image differences ("How much are they
+different?").
+
+Note that $img and $other_img must have the same number of channels.
+The width and height of $diff will be the minimum of each of the width
+and height of $img and $other_img.
+
+Parameters:
+
+=over
+
+=item *
+
+C<other> - the other image object to compare against
+
+=back
+
+=back
+
 =head1 AUTHOR
 
 Arnar M. Hrafnkelsson, Tony Cook <tonyc@cpan.org>.
index 40eaaaa..dd9bb96 100644 (file)
@@ -8,7 +8,7 @@ use Text::ParseWords;
 our @EXPORT = qw(preprocess);
 our @ISA = qw(Exporter);
 
-our $VERSION = "1.002";
+our $VERSION = "1.003";
 
 sub preprocess {
   unshift @ARGV, grep /^-/, shellwords($ENV{IMAGER_PREPROCESS_OPTS})
@@ -139,6 +139,7 @@ sub byte_samples {
     s/\bIM_LIMIT\(/IM_LIMIT_8(/g;
     s/\bIM_RENDER_LINE\(/i_render_line(/g;
     s/\bIM_FILL_COMBINE_F\b/i_fill_combine_f/g;
+    s/\bIM_ABS\b/abs/g;
   }
   
   @lines;
@@ -168,6 +169,7 @@ sub double_samples {
     s/\bIM_LIMIT\(/IM_LIMIT_double(/g;
     s/\bIM_RENDER_LINE\(/i_render_linef(/g;
     s/\bIM_FILL_COMBINE_F\b/i_fill_combinef_f/g;
+    s/\bIM_ABS\b/fabs/g;
   }
 
   @lines;
@@ -281,6 +283,10 @@ object.
 
 =item *
 
+IM_ABS(sample) - calculate the absolute value of an IM_WORK_T value.
+
+=item *
+
 IM_SAMPLE_MAX - maximum value for a sample
 
 =item *
index 1ec6e19..93ac338 100644 (file)
@@ -1,7 +1,7 @@
 #!perl -w
 use strict;
 use Imager qw(:handy);
-use Test::More tests => 136;
+use Test::More tests => 137;
 
 -d "testout" or mkdir "testout";
 
@@ -430,6 +430,19 @@ is($name, "test gradient", "check the name matches");
   my $cmp2 = Imager->new(xsize => 3, ysize => 2, channels => 4);
   $cmp2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
   is_image($diff2, $cmp2, "difference() - check image with mindist 1");
+
+  $im1 = Imager->new(xsize => 3, ysize => 2, channels => 4);
+  $im2 = $im1->copy;
+  $im1->setpixel(x => 1, 'y' => 0, color => 'FF00FF80');
+  $im2->setpixel(x => 1, 'y' => 0, color => 'FF01FF84');
+  $im1->setpixel(x => 2, 'y' => 0, color => 'FF00FF80');
+  $im2->setpixel(x => 2, 'y' => 0, color => 'FF02FF84');
+  my $diff3 = $im1->rgb_difference(other => $im2);
+  my $cmp3 = Imager->new(xsize => 3, ysize => 2, channels => 3);
+  $cmp3->box(filled => 1, color => '#000000');
+  $cmp3->setpixel(x => 1, 'y' => 0, color => '#000100');
+  $cmp3->setpixel(x => 2, 'y' => 0, color => '#000200');
+  is_image($diff3, $cmp3, "rgb_difference() - check image");
 }
 
 {