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__ |