Skip to content

Commit

Permalink
add new comparison method rgb_difference that resembles arithmetical …
Browse files Browse the repository at this point in the history
…difference per channel

- 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
  • Loading branch information
andk committed Dec 26, 2020
1 parent 91aa1a9 commit a2e46d1
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 2 deletions.
23 changes: 23 additions & 0 deletions Imager.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions Imager.xs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
98 changes: 97 additions & 1 deletion filters.im
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -1524,6 +1524,102 @@ 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 == 1 || outchans == 3)
++outchans;

xsize = i_min(im1->xsize, im2->xsize);
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));
i_color *line2 = mymalloc(xsize * sizeof(*line1));
i_color empty;
i_img_dim x, y;
int 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);
if (outchans != diffchans) {
/* give the output an alpha channel since it doesn't have one */
for (x = 0; x < xsize; ++x)
line2[x].channel[diffchans] = 255;
}
for (x = 0; x < xsize; ++x) {
int diff = 0;
for (ch = 0; ch < diffchans; ++ch) {
line2[x].channel[ch] = i_abs(line1[x].channel[ch] - line2[x].channel[ch]);
}
}
i_plin(out, 0, xsize, y, line2);
}
myfree(line1);
myfree(line2);
}
else {
i_fcolor *line1 = mymalloc(xsize * sizeof(*line1));
i_fcolor *line2 = mymalloc(xsize * sizeof(*line2));
i_fcolor empty;
i_img_dim x, y;
int ch;

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);
if (outchans != diffchans) {
/* give the output an alpha channel since it doesn't have one */
for (x = 0; x < xsize; ++x)
line2[x].channel[diffchans] = 1.0;
}
for (x = 0; x < xsize; ++x) {
int diff = 0;
for (ch = 0; ch < diffchans; ++ch) {
if (line1[x].channel[ch] != line2[x].channel[ch]) {
line2[x].channel[ch] = i_abs(line1[x].channel[ch] - line2[x].channel[ch]);
}
}
}
i_plinf(out, 0, xsize, y, line2);
}
myfree(line1);
myfree(line2);
}

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);
Expand Down
1 change: 1 addition & 0 deletions imager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
29 changes: 29 additions & 0 deletions lib/Imager/Filters.pod
Original file line number Diff line number Diff line change
Expand Up @@ -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>.
Expand Down
9 changes: 8 additions & 1 deletion t/400-filter/010-filters.t
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -430,6 +430,13 @@ 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");

my $diff3 = $im1->rgb_difference(other => $im2);
my $cmp3 = Imager->new(xsize => 3, ysize => 2, channels => 4);
$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");
}

{
Expand Down

0 comments on commit a2e46d1

Please sign in to comment.