| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Image::Compare, a module based on the great Imager, used to determine if |
|
2
|
|
|
|
|
|
|
# two images differ greatly from one another. |
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Image::Compare; |
|
5
|
|
|
|
|
|
|
|
|
6
|
8
|
|
|
8
|
|
109573
|
use strict; |
|
|
8
|
|
|
|
|
8
|
|
|
|
8
|
|
|
|
|
188
|
|
|
7
|
8
|
|
|
8
|
|
22
|
use warnings; |
|
|
8
|
|
|
|
|
8
|
|
|
|
8
|
|
|
|
|
160
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
8
|
|
|
8
|
|
5834
|
use Imager; |
|
|
8
|
|
|
|
|
244245
|
|
|
|
8
|
|
|
|
|
42
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# This is the base class for all comparators, and will also do the work of |
|
12
|
|
|
|
|
|
|
# loading all supplied implementations. |
|
13
|
8
|
|
|
8
|
|
4156
|
use Image::Compare::Comparator; |
|
|
8
|
|
|
|
|
18
|
|
|
|
8
|
|
|
|
|
26
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our %class_map; |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
my $loaded_lwp; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
BEGIN { |
|
20
|
8
|
|
|
8
|
|
14
|
$loaded_lwp = 0; |
|
21
|
8
|
|
|
|
|
357
|
eval "require LWP;"; |
|
22
|
8
|
50
|
|
|
|
252036
|
unless ($@) { $loaded_lwp = 1; } |
|
|
8
|
|
|
|
|
327
|
|
|
23
|
|
|
|
|
|
|
} |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our $VERSION = "1.02"; |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# If people don't want to deal with OO, we export the main "work" method |
|
28
|
|
|
|
|
|
|
# so they can call it in a simpler way. We'll see below where we handle this. |
|
29
|
8
|
|
|
8
|
|
50
|
use base qw/Exporter/; |
|
|
8
|
|
|
|
|
8
|
|
|
|
8
|
|
|
|
|
5848
|
|
|
30
|
|
|
|
|
|
|
our @EXPORT_OK = qw/compare/; |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
## Public methods begin here |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# The constructor method. |
|
35
|
|
|
|
|
|
|
# Takes a hash of arguments: (all are optional) |
|
36
|
|
|
|
|
|
|
# image1 => |
|
37
|
|
|
|
|
|
|
# Data representing the first image, either as an Imager object, file |
|
38
|
|
|
|
|
|
|
# name or a URL. |
|
39
|
|
|
|
|
|
|
# type1 => Type of image provided. |
|
40
|
|
|
|
|
|
|
# image2 => Like image1. |
|
41
|
|
|
|
|
|
|
# type2 => Like type1. |
|
42
|
|
|
|
|
|
|
# method => |
|
43
|
|
|
|
|
|
|
# Either the numeric constant representing the comparator, or an |
|
44
|
|
|
|
|
|
|
# instance of a comparator. |
|
45
|
|
|
|
|
|
|
# args => Arguments to pass to the comparator. |
|
46
|
|
|
|
|
|
|
# See the documentation on the relevant option setters for more details |
|
47
|
|
|
|
|
|
|
sub new { |
|
48
|
20
|
|
|
20
|
1
|
2097
|
my $proto = shift; |
|
49
|
20
|
|
|
|
|
51
|
my %args = @_; |
|
50
|
20
|
|
33
|
|
|
101
|
my $class = ref($proto) || $proto; # Bite me, Randal. |
|
51
|
20
|
|
|
|
|
28
|
my $self = {}; |
|
52
|
20
|
|
|
|
|
27
|
bless($self, $class); |
|
53
|
|
|
|
|
|
|
# These are default values |
|
54
|
20
|
100
|
|
|
|
52
|
if ($args{image1}) { |
|
55
|
|
|
|
|
|
|
$self->set_image1( |
|
56
|
|
|
|
|
|
|
img => $args{image1}, |
|
57
|
|
|
|
|
|
|
type => $args{type1} |
|
58
|
19
|
|
|
|
|
78
|
); |
|
59
|
|
|
|
|
|
|
} |
|
60
|
20
|
100
|
|
|
|
53
|
if ($args{image2}) { |
|
61
|
|
|
|
|
|
|
$self->set_image2( |
|
62
|
|
|
|
|
|
|
img => $args{image2}, |
|
63
|
|
|
|
|
|
|
type => $args{type2} |
|
64
|
19
|
|
|
|
|
62
|
); |
|
65
|
|
|
|
|
|
|
} |
|
66
|
20
|
100
|
|
|
|
50
|
if ($args{method}) { |
|
67
|
|
|
|
|
|
|
$self->set_method( |
|
68
|
|
|
|
|
|
|
method => $args{method}, |
|
69
|
|
|
|
|
|
|
args => $args{args} |
|
70
|
19
|
|
|
|
|
55
|
); |
|
71
|
|
|
|
|
|
|
} |
|
72
|
20
|
100
|
|
|
|
55
|
if ($args{mask}) { |
|
73
|
5
|
|
|
|
|
9
|
$self->set_mask(mask => $args{mask}); |
|
74
|
|
|
|
|
|
|
} |
|
75
|
20
|
|
|
|
|
37
|
return $self; |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# The next two just use the input to fetch image data and store it as an |
|
79
|
|
|
|
|
|
|
# Imager object. Currently supported image types: |
|
80
|
|
|
|
|
|
|
# Imager object |
|
81
|
|
|
|
|
|
|
# File name |
|
82
|
|
|
|
|
|
|
# URL |
|
83
|
|
|
|
|
|
|
sub set_image1 { |
|
84
|
20
|
|
|
20
|
1
|
118
|
my $self = shift; |
|
85
|
20
|
|
|
|
|
77
|
my %args = @_; |
|
86
|
20
|
|
|
|
|
52
|
$self->{_IMG1} = _get_image($args{img}, $args{type}); |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub set_image2 { |
|
90
|
19
|
|
|
19
|
1
|
20
|
my $self = shift; |
|
91
|
19
|
|
|
|
|
38
|
my %args = @_; |
|
92
|
19
|
|
|
|
|
37
|
$self->{_IMG2} = _get_image($args{img}, $args{type}); |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# Get back the Imager objects created by the preceding two methods. |
|
96
|
|
|
|
|
|
|
sub get_image1 { |
|
97
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
98
|
0
|
|
|
|
|
0
|
return $self->{_IMG1}; |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
sub get_image2 { |
|
101
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
102
|
0
|
|
|
|
|
0
|
return $self->{_IMG2}; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# How to set the matching mask parameter for this compaison instance. |
|
106
|
|
|
|
|
|
|
sub set_mask { |
|
107
|
5
|
|
|
5
|
1
|
5
|
my $self = shift; |
|
108
|
5
|
|
|
|
|
5
|
my %args = @_; |
|
109
|
5
|
|
|
|
|
9
|
$self->{_MASK} = $args{mask}; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
sub get_mask { |
|
112
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
113
|
0
|
|
|
|
|
0
|
return $self->{_MASK}; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# Given input as defined above, returns an Imager object representing the |
|
117
|
|
|
|
|
|
|
# image. |
|
118
|
|
|
|
|
|
|
sub _get_image { |
|
119
|
39
|
|
|
39
|
|
35
|
my($img, $type) = @_; |
|
120
|
39
|
50
|
|
|
|
90
|
unless ($img) { |
|
121
|
0
|
|
|
|
|
0
|
die "Missing 'img' parameter"; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# If we've been given an Imager object, we need only store it. |
|
125
|
39
|
50
|
|
|
|
71
|
if (ref($img)) { |
|
126
|
39
|
50
|
|
|
|
120
|
if ($img->isa('Imager')) { |
|
127
|
39
|
|
|
|
|
156
|
return $img; |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
# If it wasn't an Imager, but it's still some kind of reference, then |
|
130
|
|
|
|
|
|
|
# we have to give up. |
|
131
|
0
|
|
|
|
|
0
|
die "Unrecognized input type: '" . ref($img) . "'"; |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Otherwse, we need to construct an Imager object, and to do that, we |
|
135
|
|
|
|
|
|
|
# need to build up an arguments hash for the Imager constructor. |
|
136
|
0
|
|
|
|
|
0
|
my %args; |
|
137
|
0
|
0
|
|
|
|
0
|
if ($type) { |
|
138
|
|
|
|
|
|
|
# Provide the type argument to image, if it was provided. |
|
139
|
0
|
|
|
|
|
0
|
$args{type} = $type; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
# This is the base error message. |
|
142
|
0
|
|
|
|
|
0
|
my $errmsg = "Unable to read image data from "; |
|
143
|
|
|
|
|
|
|
# If $img looks like a URL, and if we were able to load LWP, then we might |
|
144
|
|
|
|
|
|
|
# be able to fetch an image via a URL. |
|
145
|
0
|
0
|
0
|
|
|
0
|
if ($loaded_lwp && ($img =~ /^https?:\/\//)) { |
|
146
|
0
|
|
|
|
|
0
|
$errmsg .= "URL '$img'"; |
|
147
|
0
|
|
|
|
|
0
|
my $ua = LWP::UserAgent->new(); |
|
148
|
0
|
|
|
|
|
0
|
$ua->agent("Image::Compare/v$VERSION "); |
|
149
|
0
|
|
|
|
|
0
|
my $res = $ua->request(HTTP::Request->new(GET => $img)); |
|
150
|
0
|
|
|
|
|
0
|
$args{data} = $res->content(); |
|
151
|
0
|
0
|
|
|
|
0
|
if (!$type) { |
|
152
|
0
|
|
|
|
|
0
|
$args{type} = $res->content_type(); |
|
153
|
0
|
|
|
|
|
0
|
$args{type} =~ s!^image/!!; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
else { |
|
157
|
|
|
|
|
|
|
# Otherwise, we have to think it's a file path. |
|
158
|
0
|
|
|
|
|
0
|
$errmsg .= "file '$img'"; |
|
159
|
0
|
|
|
|
|
0
|
$args{file} = $img; |
|
160
|
|
|
|
|
|
|
} |
|
161
|
0
|
|
|
|
|
0
|
my $newimg = Imager->new(); |
|
162
|
0
|
0
|
|
|
|
0
|
$newimg->read(%args) || die($errmsg . ": '" . $newimg->{ERRSTR} . "'"); |
|
163
|
0
|
|
|
|
|
0
|
return $newimg; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Sets the comparison method. Either takes the numeric constant that |
|
167
|
|
|
|
|
|
|
# identifies the method and any arguments needed by the method, or an instance |
|
168
|
|
|
|
|
|
|
# of the comparator. See the documentation for Image::Compare::Comparator or |
|
169
|
|
|
|
|
|
|
# it subclasses for more details. |
|
170
|
|
|
|
|
|
|
sub set_method { |
|
171
|
20
|
|
|
20
|
1
|
3241
|
my $self = shift; |
|
172
|
20
|
|
|
|
|
45
|
my %args = @_; |
|
173
|
20
|
50
|
|
|
|
42
|
unless ($args{method}) { |
|
174
|
0
|
|
|
|
|
0
|
die "Missing required argument 'method'"; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
20
|
50
|
|
|
|
38
|
if (ref($args{method})) { |
|
177
|
0
|
0
|
|
|
|
0
|
if ($args{method}->isa('Image::Compare::Comparator')) { |
|
178
|
0
|
|
|
|
|
0
|
$self->{_CMP} = $args{method}; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
else { |
|
181
|
|
|
|
|
|
|
die ( |
|
182
|
|
|
|
|
|
|
"Unrecognized type for 'method' argument: '" . |
|
183
|
0
|
|
|
|
|
0
|
ref($args{method}) . "'" |
|
184
|
|
|
|
|
|
|
); |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
else { |
|
188
|
20
|
50
|
|
|
|
54
|
unless ($class_map{$args{method}}) { |
|
189
|
0
|
|
|
|
|
0
|
die "Unrecognized method identifier: '$args{method}'"; |
|
190
|
|
|
|
|
|
|
} |
|
191
|
20
|
|
|
|
|
160
|
$self->{_CMP} = $class_map{$args{method}}->new($args{args}); |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# Returns information describing the comparison method set into this instance |
|
196
|
|
|
|
|
|
|
# of an Image::Compare. |
|
197
|
|
|
|
|
|
|
sub get_method { |
|
198
|
1
|
|
|
1
|
1
|
623
|
my $self = shift; |
|
199
|
1
|
50
|
|
|
|
7
|
unless ($self->{_CMP}) { |
|
200
|
0
|
0
|
|
|
|
0
|
return wantarray ? () : undef; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
1
|
|
|
|
|
8
|
return $self->{_CMP}->get_representation(); |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Compares two images and returns a result. |
|
206
|
|
|
|
|
|
|
sub compare { |
|
207
|
18
|
|
|
18
|
1
|
18768
|
my $self; |
|
208
|
|
|
|
|
|
|
# This can be called as an instance method |
|
209
|
18
|
100
|
|
|
|
50
|
if (ref($_[0]) eq 'Image::Compare') { |
|
210
|
2
|
|
|
|
|
3
|
$self = shift; |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
else { |
|
213
|
|
|
|
|
|
|
# Or, as a class method, if you swing that way... |
|
214
|
16
|
50
|
|
|
|
39
|
if ($_[0] eq 'Image::Compare') { |
|
215
|
0
|
|
|
|
|
0
|
shift; |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
# Or just as a plain method. In either case, we just need to construct |
|
218
|
|
|
|
|
|
|
# a $self so we can get on with life. |
|
219
|
16
|
|
|
|
|
59
|
$self = Image::Compare->new(@_); |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
# Sanity checking |
|
222
|
18
|
|
|
|
|
57
|
for my $ref ( |
|
223
|
|
|
|
|
|
|
['IMG1', 'Image 1'], ['IMG2', 'Image 2'], ['CMP', 'Comparison method'], |
|
224
|
|
|
|
|
|
|
) { |
|
225
|
54
|
50
|
|
|
|
165
|
die "$ref->[1] not specified" unless $self->{"_$ref->[0]"}; |
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# Give the images to the comparator and let them compare them. |
|
229
|
|
|
|
|
|
|
# The comparator will raise an exception if anything's wrong. |
|
230
|
|
|
|
|
|
|
return $self->{_CMP}->compare_images( |
|
231
|
18
|
|
|
|
|
71
|
@{$self}{qw/_IMG1 _IMG2 _MASK/} |
|
|
18
|
|
|
|
|
85
|
|
|
232
|
|
|
|
|
|
|
); |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
1; |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
__END__ |