image_match 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +30 -0
- data/README.md +135 -0
- data/Rakefile +7 -0
- data/image_match.gemspec +26 -0
- data/lib/image_match.rb +252 -0
- data/lib/image_match/version.rb +3 -0
- data/samples/taking_screenshot_and_match/1416131348_match_result.png +0 -0
- data/samples/taking_screenshot_and_match/google-logo.jpg +0 -0
- data/samples/taking_screenshot_and_match/sample.rb +25 -0
- data/samples/taking_screenshot_and_match/screenshot.png +0 -0
- data/spec/image_match_spec.rb +111 -0
- data/spec/images/box.jpg +0 -0
- data/spec/images/box_in_scene.jpg +0 -0
- data/spec/images/lena-eyes.jpg +0 -0
- data/spec/images/lena-noise.jpg +0 -0
- data/spec/images/lena.jpg +0 -0
- data/spec/spec_helper.rb +3 -0
- metadata +145 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
The BSD License
|
2
|
+
|
3
|
+
Copyright (c) 2014 Hidetomo Suzuki
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use of this software in source and binary forms, with or without modification, are
|
7
|
+
permitted provided that the following conditions are met:
|
8
|
+
|
9
|
+
* Redistributions of source code must retain the above
|
10
|
+
copyright notice, this list of conditions and the
|
11
|
+
following disclaimer.
|
12
|
+
|
13
|
+
* Redistributions in binary form must reproduce the above
|
14
|
+
copyright notice, this list of conditions and the
|
15
|
+
following disclaimer in the documentation and/or other
|
16
|
+
materials provided with the distribution.
|
17
|
+
|
18
|
+
* Neither the name of Hidetomo Suzuki. nor the names of its
|
19
|
+
contributors may be used to endorse or promote products
|
20
|
+
derived from this software without specific prior
|
21
|
+
written permission of Hidetomo Suzuki.
|
22
|
+
|
23
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
24
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
25
|
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
26
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
27
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
28
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
29
|
+
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
30
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
# ImageMatch
|
2
|
+
|
3
|
+
An simple image match library for view test.
|
4
|
+
|
5
|
+
* Ruby 1.9.3 and OpenCV 2.4.10 are supported.
|
6
|
+
|
7
|
+
## Requirement
|
8
|
+
|
9
|
+
* OpenCV <http://opencv.org/>
|
10
|
+
* [Download](http://sourceforge.net/projects/opencvlibrary/)
|
11
|
+
* [Install guide](http://docs.opencv.org/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.html#table-of-content-introduction)
|
12
|
+
|
13
|
+
## Installation of this library
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'image_match'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install image_match
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
### Flow
|
32
|
+
|
33
|
+
Basic flow is following.
|
34
|
+
|
35
|
+
1. Get comparison source and destination image file.
|
36
|
+
2. Compare them with this library.
|
37
|
+
|
38
|
+
### Interfaces
|
39
|
+
|
40
|
+
Following 3 functions are prepared.
|
41
|
+
|
42
|
+
1. perfect_match
|
43
|
+
Calc match score between image1 and image2.
|
44
|
+
This function requires same size image as image1 and image2
|
45
|
+
This returns true if match score is higher than limit_similarity.
|
46
|
+
When you set true to is_output, you can check matching result with image.
|
47
|
+
The image will be created at your current directory.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
perfect_match(image1_filename, image2_filename, limit_similarity=0.9, is_output=false)
|
51
|
+
```
|
52
|
+
|
53
|
+
2. perfect_match_template
|
54
|
+
Try to find template image in scene image.
|
55
|
+
This function requires that template image's size is smaller than image2.
|
56
|
+
This returns true if match score is higher than limit_similarity.
|
57
|
+
When you set true to is_output, you can check matching result with image.
|
58
|
+
The output image will be created at your current directory.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
perfect_match_template(scene_filename, template_filename, limit_similarity=0.9, is_output=false)
|
62
|
+
```
|
63
|
+
|
64
|
+
3. fuzzy_match_template
|
65
|
+
Try to find template image in scene image.
|
66
|
+
This function requires that template image's size is smaller(or equal) than image2.
|
67
|
+
This function ignore image size, color and image detail.
|
68
|
+
When you set true to is_output, you can check matching result with image.
|
69
|
+
The output image will be created at your current directory.
|
70
|
+
Note that ome times this is useful, but accuracy is not so high.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
fuzzy_match_template(scene_filename, template_filename, is_output=false)
|
74
|
+
```
|
75
|
+
|
76
|
+
### Sample Code
|
77
|
+
|
78
|
+
A sample to take screen shot on http://google.com/ and compare screen shot and prepared logo image.
|
79
|
+
You can check all files related this sample on samples directory(currently sample is only one).
|
80
|
+
|
81
|
+
#### Gemfile
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
source 'https://rubygems.org'
|
85
|
+
|
86
|
+
gem 'capybara'
|
87
|
+
gem 'poltergeist'
|
88
|
+
gem 'image_match'
|
89
|
+
```
|
90
|
+
|
91
|
+
#### Main
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
require 'capybara'
|
95
|
+
require 'capybara/poltergeist'
|
96
|
+
require 'image_match'
|
97
|
+
include ImageMatch
|
98
|
+
|
99
|
+
# Get current google web page image
|
100
|
+
url = 'http://google.com/'
|
101
|
+
Capybara.javascript_driver = :poltergeist
|
102
|
+
Capybara.register_driver :poltergeist do |app|
|
103
|
+
Capybara::Poltergeist::Driver.new app, js_errors: false
|
104
|
+
end
|
105
|
+
Capybara.default_selector = :xpath
|
106
|
+
|
107
|
+
session = Capybara::Session.new(:poltergeist)
|
108
|
+
session.driver.headers = {'User-Agent' => "Mozilla/5.0 (Macintosh; Intel Mac OS X)"}
|
109
|
+
session.visit(url)
|
110
|
+
|
111
|
+
session.save_screenshot('screenshot.png', full: true)
|
112
|
+
|
113
|
+
# Compare logo (output match result image)
|
114
|
+
if perfect_match_template('./screenshot.png', './google-logo.jpg', 0.9, true)
|
115
|
+
puts "Same!"
|
116
|
+
else
|
117
|
+
puts "Different..."
|
118
|
+
end
|
119
|
+
|
120
|
+
```
|
121
|
+
|
122
|
+
## Contributing
|
123
|
+
|
124
|
+
1. Fork it ( https://github.com/[my-github-username]/image_match/fork )
|
125
|
+
2. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
126
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
127
|
+
4. Push to the branch (`git push origin feature/my-new-feature`)
|
128
|
+
5. Create a new Pull Request to develop branch
|
129
|
+
Note that develop branch is newest version(not release version).
|
130
|
+
|
131
|
+
## LICENSE:
|
132
|
+
|
133
|
+
The BSD Liscense
|
134
|
+
|
135
|
+
see LICENSE.txt
|
data/Rakefile
ADDED
data/image_match.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'image_match/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "image_match"
|
8
|
+
spec.version = ImageMatch::VERSION
|
9
|
+
spec.authors = ["Hidetomo Suzuki"]
|
10
|
+
spec.email = ["zuqqhi2@gmail.com"]
|
11
|
+
spec.summary = %q{Check web page or widget design with image file automatically}
|
12
|
+
spec.description = %q{1.Make page or widget image file. 2.Get current page or widget image on the web. 3.Compare them with this gem.}
|
13
|
+
spec.homepage = "https://github.com/zuqqhi2/image_diff"
|
14
|
+
spec.license = "The BSD Liscense"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
|
25
|
+
spec.add_dependency "ruby-opencv", "~> 0.0.13"
|
26
|
+
end
|
data/lib/image_match.rb
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
require "opencv"
|
2
|
+
include OpenCV
|
3
|
+
|
4
|
+
require "image_match/version"
|
5
|
+
|
6
|
+
module ImageMatch
|
7
|
+
#====================================================================
|
8
|
+
# Private functions
|
9
|
+
#====================================================================
|
10
|
+
|
11
|
+
def compare_surf_descriptors(d1, d2, best, length)
|
12
|
+
raise ArgumentError unless (length % 4) == 0
|
13
|
+
total_cost = 0
|
14
|
+
0.step(length - 1, 4) { |i|
|
15
|
+
t0 = d1[i] - d2[i]
|
16
|
+
t1 = d1[i + 1] - d2[i + 1]
|
17
|
+
t2 = d1[i + 2] - d2[i + 2]
|
18
|
+
t3 = d1[i + 3] - d2[i + 3]
|
19
|
+
total_cost += t0 * t0 + t1 * t1 + t2 * t2 + t3 * t3
|
20
|
+
break if total_cost > best
|
21
|
+
}
|
22
|
+
total_cost
|
23
|
+
end
|
24
|
+
|
25
|
+
def naive_nearest_neighbor(vec, laplacian, model_keypoints, model_descriptors)
|
26
|
+
length = model_descriptors[0].size
|
27
|
+
neighbor = nil
|
28
|
+
dist1 = 1e6
|
29
|
+
dist2 = 1e6
|
30
|
+
|
31
|
+
model_descriptors.size.times { |i|
|
32
|
+
kp = model_keypoints[i]
|
33
|
+
mvec = model_descriptors[i]
|
34
|
+
next if laplacian != kp.laplacian
|
35
|
+
|
36
|
+
d = compare_surf_descriptors(vec, mvec, dist2, length)
|
37
|
+
if d < dist1
|
38
|
+
dist2 = dist1
|
39
|
+
dist1 = d
|
40
|
+
neighbor = i
|
41
|
+
elsif d < dist2
|
42
|
+
dist2 = d
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
return (dist1 < 0.6 * dist2) ? neighbor : nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_pairs(template_keypoints, template_descriptors, scene_keypoints, scene_descriptors)
|
50
|
+
ptpairs = []
|
51
|
+
template_descriptors.size.times { |i|
|
52
|
+
kp = template_keypoints[i]
|
53
|
+
descriptor = template_descriptors[i]
|
54
|
+
nearest_neighbor = naive_nearest_neighbor(descriptor, kp.laplacian, scene_keypoints, scene_descriptors)
|
55
|
+
unless nearest_neighbor.nil?
|
56
|
+
ptpairs << i
|
57
|
+
ptpairs << nearest_neighbor
|
58
|
+
end
|
59
|
+
}
|
60
|
+
ptpairs
|
61
|
+
end
|
62
|
+
|
63
|
+
def locate_planar_template(template_keypoints, template_descriptors, scene_keypoints, scene_descriptors, src_corners)
|
64
|
+
ptpairs = find_pairs(template_keypoints, template_descriptors, scene_keypoints, scene_descriptors)
|
65
|
+
n = ptpairs.size / 2
|
66
|
+
|
67
|
+
return nil if n < 4
|
68
|
+
|
69
|
+
pt1 = []
|
70
|
+
pt2 = []
|
71
|
+
n.times { |i|
|
72
|
+
pt1 << template_keypoints[ptpairs[i * 2]].pt
|
73
|
+
pt2 << scene_keypoints[ptpairs[i * 2 + 1]].pt
|
74
|
+
}
|
75
|
+
|
76
|
+
_pt1 = CvMat.new(1, n, CV_32F, 2)
|
77
|
+
_pt2 = CvMat.new(1, n, CV_32F, 2)
|
78
|
+
_pt1.set_data(pt1)
|
79
|
+
_pt2.set_data(pt2)
|
80
|
+
h = CvMat.find_homography(_pt1, _pt2, :ransac, 5)
|
81
|
+
|
82
|
+
dst_corners = []
|
83
|
+
4.times { |i|
|
84
|
+
x = src_corners[i].x
|
85
|
+
y = src_corners[i].y
|
86
|
+
z = 1.0 / (h[6][0] * x + h[7][0] * y + h[8][0])
|
87
|
+
x = (h[0][0] * x + h[1][0] * y + h[2][0]) * z
|
88
|
+
y = (h[3][0] * x + h[4][0] * y + h[5][0]) * z
|
89
|
+
dst_corners << CvPoint.new(x.to_i, y.to_i)
|
90
|
+
}
|
91
|
+
|
92
|
+
dst_corners
|
93
|
+
end
|
94
|
+
|
95
|
+
#====================================================================
|
96
|
+
# Public Interface
|
97
|
+
#====================================================================
|
98
|
+
|
99
|
+
##
|
100
|
+
#
|
101
|
+
# Calculate matching score of 1st input image and 2nd input image
|
102
|
+
# required the sizes are same.
|
103
|
+
#
|
104
|
+
# @param [String] image1_filename Compare target image1 file path
|
105
|
+
# @param [String] image2_filename Compare target image2 file path
|
106
|
+
# @param [Int] limit_similarity Accepting similarity (default is 90% matching)
|
107
|
+
# @return [Boolean] true if matching score is higher than limit_similarity
|
108
|
+
# false otherwise
|
109
|
+
#
|
110
|
+
def perfect_match(image1_filename, image2_filename, limit_similarity=0.9)
|
111
|
+
raise ArgumentError, 'File does not exists.' unless File.exist?(image1_filename) and File.exist?(image2_filename)
|
112
|
+
raise ArgumentError, 'limit_similarity must be 0.1 - 1.0.' unless limit_similarity >= 0.1 and limit_similarity <= 1.0
|
113
|
+
|
114
|
+
image1, image2 = nil, nil
|
115
|
+
begin
|
116
|
+
image1 = IplImage.load(image1_filename)
|
117
|
+
image2 = IplImage.load(image2_filename)
|
118
|
+
rescue
|
119
|
+
raise RuntimeError, 'Couldn\'t read image files correctly'
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
|
123
|
+
return false unless image1.width == image2.width and image1.height == image2.height
|
124
|
+
|
125
|
+
return perfect_match_template(image1_filename, image2_filename, limit_similarity)
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
#
|
130
|
+
# Calculate matching score of 1st input image and 2nd input image.
|
131
|
+
# The 2nd input image size must be smaller than 1st input image.
|
132
|
+
# This function is robust for brightness.
|
133
|
+
#
|
134
|
+
# @param [String] scene_filename Scene image file path
|
135
|
+
# @param [String] template_filename template image file path which you want find in scene image
|
136
|
+
# @param [Int] limit_similarity Accepting similarity (default is 90% matching)
|
137
|
+
# @param [Boolean] is_output if you set true, you can get match result with image (default is false)
|
138
|
+
# @return [Boolean] true if matching score is higher than limit_similarity
|
139
|
+
# false otherwise
|
140
|
+
#
|
141
|
+
def perfect_match_template(scene_filename, template_filename, limit_similarity=0.9, is_output=false)
|
142
|
+
raise ArgumentError, 'File does not exists.' unless File.exist?(scene_filename) and File.exist?(template_filename)
|
143
|
+
raise ArgumentError, 'limit_similarity must be 0.1 - 1.0.' unless limit_similarity >= 0.1 and limit_similarity <= 1.0
|
144
|
+
raise ArgumentError, 'is_output must be true or false.' unless is_output == false or is_output == true
|
145
|
+
|
146
|
+
scene, template = nil, nil
|
147
|
+
begin
|
148
|
+
scene = IplImage.load(scene_filename)
|
149
|
+
template = IplImage.load(template_filename)
|
150
|
+
rescue
|
151
|
+
raise RuntimeError, 'Couldn\'t read image files correctly'
|
152
|
+
return false
|
153
|
+
end
|
154
|
+
|
155
|
+
return false unless scene.width >= template.width and scene.height >= template.height
|
156
|
+
|
157
|
+
result = scene.match_template(template, :ccoeff_normed)
|
158
|
+
min_score, max_score, min_point, max_point = result.min_max_loc
|
159
|
+
|
160
|
+
if is_output
|
161
|
+
from = max_point
|
162
|
+
to = CvPoint.new(from.x + template.width, from.y + template.height)
|
163
|
+
scene.rectangle!(from, to, :color => CvColor::Red, :thickness => 3)
|
164
|
+
scene.save_image(Time.now.to_i.to_s + "_match_result.png")
|
165
|
+
end
|
166
|
+
|
167
|
+
return (max_score >= limit_similarity ? true : false)
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
#
|
172
|
+
# Try to find 2nd input image in 1st input image.
|
173
|
+
# This function ignores color, size and shape details.
|
174
|
+
# (I mean this func checks whether almost same or clearly different)
|
175
|
+
#
|
176
|
+
# Note that this function is useful I think,
|
177
|
+
# but sometimes it doesn't output correct result which you want.
|
178
|
+
# It depends on input images.
|
179
|
+
# TODO : improve accuracy
|
180
|
+
#
|
181
|
+
# @param [String] scene_filename Scene image file path
|
182
|
+
# @param [String] template_filename template image file path which you want find in scene image
|
183
|
+
# @param [Boolean] is_output if you set true, you can get match result with image (default is false)
|
184
|
+
# @return [Boolean] true if 1st input image seems to have 2nd input image
|
185
|
+
# false otherwise
|
186
|
+
#
|
187
|
+
def fuzzy_match_template(scene_filename, template_filename, is_output=false)
|
188
|
+
raise ArgumentError, 'File does not exists.' unless File.exist?(scene_filename) and File.exist?(template_filename)
|
189
|
+
raise ArgumentError, 'is_output must be true or false.' unless is_output == false or is_output == true
|
190
|
+
|
191
|
+
scene, template = nil, nil
|
192
|
+
begin
|
193
|
+
scene = IplImage.load(scene_filename, CV_LOAD_IMAGE_GRAYSCALE)
|
194
|
+
template = IplImage.load(template_filename, CV_LOAD_IMAGE_GRAYSCALE)
|
195
|
+
rescue
|
196
|
+
raise RuntimeError, 'Couldn\'t read image files correctly'
|
197
|
+
return false
|
198
|
+
end
|
199
|
+
|
200
|
+
return false unless scene.width >= template.width and scene.height >= template.height
|
201
|
+
|
202
|
+
param = CvSURFParams.new(1500)
|
203
|
+
template_keypoints, template_descriptors = template.extract_surf(param)
|
204
|
+
scene_keypoints, scene_descriptors = scene.extract_surf(param)
|
205
|
+
|
206
|
+
src_corners = [CvPoint.new(0, 0),
|
207
|
+
CvPoint.new(template.width, 0),
|
208
|
+
CvPoint.new(template.width, template.height),
|
209
|
+
CvPoint.new(0, template.height)]
|
210
|
+
dst_corners = locate_planar_template(template_keypoints,
|
211
|
+
template_descriptors,
|
212
|
+
scene_keypoints,
|
213
|
+
scene_descriptors,
|
214
|
+
src_corners)
|
215
|
+
|
216
|
+
|
217
|
+
if is_output
|
218
|
+
correspond = IplImage.new(scene.width, template.height + scene.height, CV_8U, 1);
|
219
|
+
correspond.set_roi(CvRect.new(0, 0, template.width, template.height))
|
220
|
+
template.copy(correspond)
|
221
|
+
correspond.set_roi(CvRect.new(0, template.height, scene.width, scene.height))
|
222
|
+
scene.copy(correspond)
|
223
|
+
correspond.reset_roi
|
224
|
+
correspond = correspond.GRAY2BGR
|
225
|
+
|
226
|
+
if dst_corners
|
227
|
+
4.times { |i|
|
228
|
+
r1 = dst_corners[i % 4]
|
229
|
+
r2 = dst_corners[(i + 1) % 4]
|
230
|
+
correspond.line!(CvPoint.new(r1.x, r1.y + template.height),
|
231
|
+
CvPoint.new(r2.x, r2.y + template.height),
|
232
|
+
:color => CvColor::Red,
|
233
|
+
:thickness => 2, :line_type => :aa)
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
ptpairs = find_pairs(template_keypoints, template_descriptors, scene_keypoints, scene_descriptors)
|
238
|
+
|
239
|
+
0.step(ptpairs.size - 1, 2) { |i|
|
240
|
+
r1 = template_keypoints[ptpairs[i]]
|
241
|
+
r2 = scene_keypoints[ptpairs[i + 1]]
|
242
|
+
correspond.line!(r1.pt, CvPoint.new(r2.pt.x, r2.pt.y + template.height),
|
243
|
+
:color => CvColor::Red, :line_type => :aa)
|
244
|
+
}
|
245
|
+
|
246
|
+
correspond.save_image(Time.now.to_i.to_s + "_match_result.png")
|
247
|
+
end
|
248
|
+
|
249
|
+
return (dst_corners ? true : false)
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
require 'capybara/poltergeist'
|
3
|
+
require 'image_match'
|
4
|
+
include ImageMatch
|
5
|
+
|
6
|
+
# Get current google web page image
|
7
|
+
url = 'http://google.com/'
|
8
|
+
Capybara.javascript_driver = :poltergeist
|
9
|
+
Capybara.register_driver :poltergeist do |app|
|
10
|
+
Capybara::Poltergeist::Driver.new app, js_errors: false
|
11
|
+
end
|
12
|
+
Capybara.default_selector = :xpath
|
13
|
+
|
14
|
+
session = Capybara::Session.new(:poltergeist)
|
15
|
+
session.driver.headers = {'User-Agent' => "Mozilla/5.0 (Macintosh; Intel Mac OS X)"}
|
16
|
+
session.visit(url)
|
17
|
+
|
18
|
+
session.save_screenshot('screenshot.png', full: true)
|
19
|
+
|
20
|
+
# Compare logo (output match result image)
|
21
|
+
if perfect_match_template('./screenshot.png', './google-logo.jpg', 0.9, true)
|
22
|
+
puts "Same!"
|
23
|
+
else
|
24
|
+
puts "Different..."
|
25
|
+
end
|
Binary file
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ImageMatch do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
@image_dir = File.expand_path(File.dirname(__FILE__)) + '/images'
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
describe 'perfect_match' do
|
11
|
+
it 'finds that input images are same' do
|
12
|
+
result = perfect_match(@image_dir + '/lena.jpg', @image_dir + '/lena.jpg')
|
13
|
+
expect(result).to eq true
|
14
|
+
|
15
|
+
result = perfect_match(@image_dir + '/lena.jpg', @image_dir + '/lena.jpg', 0.95)
|
16
|
+
expect(result).to eq true
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'finds that input images are not same' do
|
20
|
+
# Require similarity is too high with noise image
|
21
|
+
result = perfect_match(@image_dir + '/lena.jpg', @image_dir + '/lena-noise.jpg', 0.98)
|
22
|
+
expect(result).to eq false
|
23
|
+
|
24
|
+
# Size not match
|
25
|
+
result = perfect_match(@image_dir + '/lena.jpg', @image_dir + '/lena-eyes.jpg')
|
26
|
+
expect(result).to eq false
|
27
|
+
|
28
|
+
# File does not exists
|
29
|
+
expect{ perfect_match(@image_dir + '/lena.jpg', @image_dir + '/lena-eyes-2nd.jpg') }.to raise_error
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'perfect_match_template' do
|
34
|
+
it 'finds that 2nd input image is a part of 1st input image' do
|
35
|
+
# Same image
|
36
|
+
result = perfect_match_template(@image_dir + '/lena.jpg', @image_dir + '/lena.jpg')
|
37
|
+
expect(result).to eq true
|
38
|
+
|
39
|
+
# Template
|
40
|
+
result = perfect_match_template(@image_dir + '/lena.jpg', @image_dir + '/lena-eyes.jpg')
|
41
|
+
expect(result).to eq true
|
42
|
+
|
43
|
+
# Set similarity
|
44
|
+
result = perfect_match_template(@image_dir + '/lena.jpg', @image_dir + '/lena-eyes.jpg', 0.95)
|
45
|
+
expect(result).to eq true
|
46
|
+
|
47
|
+
# Set is_output
|
48
|
+
perfect_match_template(@image_dir + '/lena.jpg', @image_dir + '/lena-eyes.jpg', 0.95, true)
|
49
|
+
path = File.expand_path(File.dirname(__FILE__))
|
50
|
+
result = `ls #{path}/../*_match_result.png | wc -l`
|
51
|
+
expect(result.chomp).to eq "1"
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'finds that 1st input image doesn\'t contains 2nd input image' do
|
55
|
+
# No correlation images
|
56
|
+
result = perfect_match_template(@image_dir + '/lena.jpg', @image_dir + '/box.jpg')
|
57
|
+
expect(result).to eq false
|
58
|
+
|
59
|
+
# Require similarity is too high with noise image
|
60
|
+
result = perfect_match_template(@image_dir + '/lena.jpg', @image_dir + '/lena-noise.jpg', 0.98)
|
61
|
+
expect(result).to eq false
|
62
|
+
|
63
|
+
# Size not match
|
64
|
+
result = perfect_match_template(@image_dir + '/lena.jpg', @image_dir + '/box_in_scene.jpg')
|
65
|
+
expect(result).to eq false
|
66
|
+
|
67
|
+
# File does not exists
|
68
|
+
expect{ perfect_match_template(@image_dir + '/lena.jpg', @image_dir + '/lena-eyes-2nd.jpg') }.to raise_error
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'fuzzy_match_template' do
|
73
|
+
it 'finds that 2nd input image is a part of 1st input image' do
|
74
|
+
# Same
|
75
|
+
result = fuzzy_match_template(@image_dir + '/box_in_scene.jpg', @image_dir + '/box_in_scene.jpg')
|
76
|
+
expect(result).to eq true
|
77
|
+
|
78
|
+
# Template
|
79
|
+
result = fuzzy_match_template(@image_dir + '/box_in_scene.jpg', @image_dir + '/box.jpg')
|
80
|
+
expect(result).to eq true
|
81
|
+
|
82
|
+
# Set is_output
|
83
|
+
result = fuzzy_match_template(@image_dir + '/box_in_scene.jpg', @image_dir + '/box.jpg')
|
84
|
+
path = File.expand_path(File.dirname(__FILE__))
|
85
|
+
result = `ls #{path}/../*_match_result.png | wc -l`
|
86
|
+
expect(result.chomp).to eq '1'
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'finds that 1st input image doesn\'t contains 2nd input image' do
|
90
|
+
# No correlation images
|
91
|
+
result = fuzzy_match_template(@image_dir + '/box.jpg', @image_dir + '/lena-eyes.jpg', true)
|
92
|
+
expect(result).to eq false
|
93
|
+
|
94
|
+
# Size not match
|
95
|
+
result = fuzzy_match_template(@image_dir + '/lena.jpg', @image_dir + '/box_in_scene.jpg')
|
96
|
+
expect(result).to eq false
|
97
|
+
|
98
|
+
# File does not exists
|
99
|
+
expect{ fuzzy_match_template(@image_dir + '/lena.jpg', @image_dir + '/lena-eyes-2nd.jpg') }.to raise_error
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
after :all do
|
104
|
+
# Delete created files
|
105
|
+
path = File.expand_path(File.dirname(__FILE__))
|
106
|
+
result = `ls #{path}/../*_match_result.png | wc -l`
|
107
|
+
if result != '0'
|
108
|
+
`rm #{path}/../*_match_result.png`
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/spec/images/box.jpg
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: image_match
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Hidetomo Suzuki
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-11-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.7'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.7'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '10.0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '10.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: ruby-opencv
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.0.13
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.0.13
|
78
|
+
description: 1.Make page or widget image file. 2.Get current page or widget image
|
79
|
+
on the web. 3.Compare them with this gem.
|
80
|
+
email:
|
81
|
+
- zuqqhi2@gmail.com
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- .rspec
|
88
|
+
- .travis.yml
|
89
|
+
- Gemfile
|
90
|
+
- LICENSE.txt
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- image_match.gemspec
|
94
|
+
- lib/image_match.rb
|
95
|
+
- lib/image_match/version.rb
|
96
|
+
- samples/taking_screenshot_and_match/1416131348_match_result.png
|
97
|
+
- samples/taking_screenshot_and_match/google-logo.jpg
|
98
|
+
- samples/taking_screenshot_and_match/sample.rb
|
99
|
+
- samples/taking_screenshot_and_match/screenshot.png
|
100
|
+
- spec/image_match_spec.rb
|
101
|
+
- spec/images/box.jpg
|
102
|
+
- spec/images/box_in_scene.jpg
|
103
|
+
- spec/images/lena-eyes.jpg
|
104
|
+
- spec/images/lena-noise.jpg
|
105
|
+
- spec/images/lena.jpg
|
106
|
+
- spec/spec_helper.rb
|
107
|
+
homepage: https://github.com/zuqqhi2/image_diff
|
108
|
+
licenses:
|
109
|
+
- The BSD Liscense
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
hash: -702331356302588290
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
hash: -702331356302588290
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 1.8.23.2
|
135
|
+
signing_key:
|
136
|
+
specification_version: 3
|
137
|
+
summary: Check web page or widget design with image file automatically
|
138
|
+
test_files:
|
139
|
+
- spec/image_match_spec.rb
|
140
|
+
- spec/images/box.jpg
|
141
|
+
- spec/images/box_in_scene.jpg
|
142
|
+
- spec/images/lena-eyes.jpg
|
143
|
+
- spec/images/lena-noise.jpg
|
144
|
+
- spec/images/lena.jpg
|
145
|
+
- spec/spec_helper.rb
|