line |
stmt |
code |
1
|
|
#!/usr/bin/perl -w |
2
|
|
# Copyright 2021 SUSE LLC |
3
|
|
# SPDX-License-Identifier: GPL-2.0-or-later |
4
|
|
# |
5
|
|
|
6
|
2
|
use Mojo::Base -strict, -signatures; |
|
2
|
|
|
2
|
|
7
|
2
|
use Mojo::JSON qw(encode_json); |
|
2
|
|
|
2
|
|
8
|
2
|
use Mojo::Log; |
|
2
|
|
|
2
|
|
9
|
2
|
use Mojo::Util qw(getopt); |
|
2
|
|
|
2
|
|
10
|
|
|
11
|
2
|
use FindBin '$Bin'; |
|
2
|
|
|
2
|
|
12
|
2
|
use lib "$Bin"; |
|
2
|
|
|
2
|
|
13
|
2
|
use cv; |
|
2
|
|
|
2
|
|
14
|
2
|
use needle; |
|
2
|
|
|
2
|
|
15
|
|
|
16
|
|
# define a basic needle package; normally `needle` should inherit from this package |
17
|
|
{ |
18
|
0
|
use base 'needle'; |
19
|
2
|
use Mojo::File qw(path); |
|
2
|
|
|
2
|
|
20
|
2
|
|
|
2
|
|
|
2
|
|
21
|
|
my $name = path($image_path)->basename; |
22
|
2
|
my $image = tinycv::read $image_path; |
|
2
|
|
|
2
|
|
|
2
|
|
|
2
|
|
|
2
|
|
23
|
2
|
my %area = (type => 'match', match => $match_level, margin => $margin, |
24
|
2
|
xpos => 0, ypos => 0, width => $image->xres, height => $image->yres, |
25
|
2
|
img => $image); |
26
|
|
my $self = {name => $name, png => $image_path, area => [\%area]}; |
27
|
|
bless $self, $classname; |
28
|
2
|
$self->register; |
29
|
2
|
return $self; |
30
|
2
|
} |
31
|
2
|
|
32
|
|
|
33
|
|
|
34
|
4
|
print "imgsearch [options] |
|
4
|
|
|
4
|
|
|
4
|
|
|
4
|
|
35
|
|
|
36
|
1
|
options: |
|
1
|
|
|
1
|
|
|
1
|
|
|
1
|
|
|
2
|
|
37
|
|
--needle-images specifies images to look for |
38
|
|
--haystack-images specifies images to look in |
39
|
2
|
--verbose enables debug output |
|
1
|
|
|
1
|
|
40
|
1
|
|
41
|
|
example: |
42
|
|
imagesearch \ |
43
|
|
--needle-images logo1.png logo2.png \ |
44
|
|
--haystack-images asset.png screenshot.png |
45
|
|
"; |
46
|
|
exit 0; |
47
|
|
} |
48
|
|
|
49
|
|
getopt 'needle-images=s@{1,}' => \my @needle_image_paths, |
50
|
|
'haystack-images=s@{1,}' => \my @image_paths, |
51
|
|
'match-level=f' => \my $match_level, |
52
|
1
|
'margin=f' => \my $margin, |
53
|
|
'threshold=f' => \my $threshold, |
54
|
|
'search-ratio=f' => \my $search_ratio, |
55
|
2
|
'v|verbose' => \my $verbose; |
56
|
|
|
57
|
|
print_usage unless @needle_image_paths && @image_paths; |
58
|
|
|
59
|
|
# stop opencv logging messages polluting stdout |
60
|
|
$ENV{'OPENCV_LOG_LEVEL'} ||= 'SILENT'; |
61
|
|
# initialize logging, tinycv and parameters |
62
|
|
my $log = Mojo::Log->new; |
63
|
2
|
$log->level($verbose ? 'debug' : 'info'); |
64
|
|
$log->debug('Loading tinycv'); |
65
|
|
cv::init; |
66
|
1
|
require tinycv; |
67
|
|
$match_level //= 80; # the similarity level required to consider a finding a match (unit: percent) |
68
|
1
|
$margin //= 1000000; # very high value to search within full image by default (unit: pixel) |
69
|
1
|
$search_ratio //= 0; # set to zero to disable unwanted computation of margin (which assumes an image width of 1024 px) |
70
|
1
|
$threshold //= 0.0; # subtracted from each area's match level; just keep at zero here |
71
|
1
|
$log->debug("Martch-level: $match_level, margin: $margin, threshold: $threshold, search ratio: $search_ratio"); |
72
|
1
|
|
73
|
1
|
# load needles |
74
|
1
|
$log->info('Loading needles'); |
75
|
1
|
my $needles = basic_needle::from_paths(\@needle_image_paths, $match_level, $margin); |
76
|
1
|
|
77
|
1
|
# search needles in images |
78
|
|
my %results = map { |
79
|
|
my $image_path = $_; |
80
|
1
|
$log->info("Searching $image_path"); |
81
|
1
|
my $image = tinycv::read $image_path; |
82
|
|
my ($match, $candidates) = $image->search($needles, $threshold, $search_ratio); |
83
|
|
($image_path => {match => $match, candidates => $candidates}); |
84
|
|
} @image_paths; |
85
|
1
|
|
|
2
|
|
86
|
2
|
print encode_json(\%results); |