imatcher 0.1.1 → 0.1.2
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.
- checksums.yaml +4 -4
- data/examples/performance.rb +42 -0
- data/lib/imatcher/image.rb +4 -0
- data/lib/imatcher/matcher.rb +3 -3
- data/lib/imatcher/modes/base.rb +11 -13
- data/lib/imatcher/modes/delta.rb +7 -3
- data/lib/imatcher/modes/grayscale.rb +1 -0
- data/lib/imatcher/modes/rgb.rb +2 -1
- data/lib/imatcher/version.rb +1 -1
- data/spec/fixtures/a.png +0 -0
- data/spec/fixtures/b.png +0 -0
- data/spec/fixtures/darker.png +0 -0
- data/spec/fixtures/delta_diff.png +0 -0
- data/spec/fixtures/grayscale_diff.png +0 -0
- data/spec/fixtures/rgb_diff.png +0 -0
- data/spec/integrations/delta_spec.rb +13 -5
- data/spec/integrations/grayscale_spec.rb +10 -10
- data/spec/integrations/rgb_spec.rb +3 -3
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cdc4ac5a873087643b65c5d5b196cb389c64bf96
|
4
|
+
data.tar.gz: 2c2641e6e1421b5627731cdcc9e912b289539dc2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 347587af896ae12f1647c897c2c8749aecc0546f8d50eb8a143a2d4dc7bd1193f24ca85ee204bf31c2064fd5b290f9b84ba8e7d9ca813a3745e45e9b2f95ffd1
|
7
|
+
data.tar.gz: fab3b1e8e9501e5cdb5db0c460a8bc414d4d0189af99c38d585759a49d561bac66124a3b08bc3d876942db5c3956dcf80e9f25316cef8c5a8fd60b546818119f
|
@@ -0,0 +1,42 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'benchmark/ips'
|
3
|
+
require 'imatcher'
|
4
|
+
|
5
|
+
a = Imatcher::Image.from_file(File.expand_path('../../spec/fixtures/a.png', __FILE__))
|
6
|
+
b = Imatcher::Image.from_file(File.expand_path('../../spec/fixtures/a.png', __FILE__))
|
7
|
+
|
8
|
+
rgb = Imatcher::Matcher.new
|
9
|
+
grayscale = Imatcher::Matcher.new mode: :grayscale
|
10
|
+
delta = Imatcher::Matcher.new mode: :delta
|
11
|
+
|
12
|
+
Benchmark.ips do |x|
|
13
|
+
x.report 'RGB' do
|
14
|
+
rgb.compare(a, b)
|
15
|
+
end
|
16
|
+
|
17
|
+
x.report 'Grayscale' do
|
18
|
+
grayscale.compare(a, b)
|
19
|
+
end
|
20
|
+
|
21
|
+
x.report 'Delta E' do
|
22
|
+
delta.compare(a, b)
|
23
|
+
end
|
24
|
+
|
25
|
+
x.compare!
|
26
|
+
end
|
27
|
+
|
28
|
+
Benchmark.ips do |x|
|
29
|
+
x.report 'RGB' do
|
30
|
+
rgb.compare(a, b).difference_image
|
31
|
+
end
|
32
|
+
|
33
|
+
x.report 'Grayscale' do
|
34
|
+
grayscale.compare(a, b).difference_image
|
35
|
+
end
|
36
|
+
|
37
|
+
x.report 'Delta E' do
|
38
|
+
delta.compare(a, b).difference_image
|
39
|
+
end
|
40
|
+
|
41
|
+
x.compare!
|
42
|
+
end
|
data/lib/imatcher/image.rb
CHANGED
data/lib/imatcher/matcher.rb
CHANGED
@@ -18,9 +18,9 @@ module Imatcher
|
|
18
18
|
@mode = Modes.const_get(MODES[mode_type]).new(options)
|
19
19
|
end
|
20
20
|
|
21
|
-
def compare(
|
22
|
-
a = Image.from_file(
|
23
|
-
b = Image.from_file(
|
21
|
+
def compare(a, b)
|
22
|
+
a = Image.from_file(a) unless a.is_a?(Image)
|
23
|
+
b = Image.from_file(b) unless b.is_a?(Image)
|
24
24
|
raise SizesMismatchError,
|
25
25
|
"Size mismatch: first image size: " \
|
26
26
|
"#{a.width}x#{a.height}, " \
|
data/lib/imatcher/modes/base.rb
CHANGED
@@ -3,7 +3,7 @@ module Imatcher
|
|
3
3
|
class Base # :nodoc:
|
4
4
|
include ColorMethods
|
5
5
|
|
6
|
-
attr_reader :result, :threshold
|
6
|
+
attr_reader :result, :threshold, :bounds
|
7
7
|
|
8
8
|
def initialize(threshold: 0.0)
|
9
9
|
@threshold = threshold
|
@@ -12,6 +12,7 @@ module Imatcher
|
|
12
12
|
|
13
13
|
def compare(a, b)
|
14
14
|
result.image = a
|
15
|
+
@bounds = [0, 0, result.image.width - 1, result.image.height - 1]
|
15
16
|
|
16
17
|
b.compare_each_pixel(a) do |b_pixel, a_pixel, x, y|
|
17
18
|
next if pixels_equal?(b_pixel, a_pixel)
|
@@ -24,28 +25,25 @@ module Imatcher
|
|
24
25
|
|
25
26
|
def diff(bg, diff)
|
26
27
|
diff_image = background(bg)
|
27
|
-
diff_image.render_bounds(*calculate_bounds(diff))
|
28
28
|
diff.each do |pixels_pair|
|
29
29
|
pixels_diff(diff_image, *pixels_pair)
|
30
30
|
end
|
31
|
-
create_diff_image(bg, diff_image)
|
31
|
+
create_diff_image(bg, diff_image).render_bounds(*bounds)
|
32
32
|
end
|
33
33
|
|
34
34
|
def score
|
35
35
|
@result.diff.length.to_f / @result.image.pixels.length
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
diff.each do |pixels_pair|
|
42
|
-
xmin = pixels_pair[2] if pixels_pair[2] < xmin
|
43
|
-
xmax = pixels_pair[2] if pixels_pair[2] > xmax
|
44
|
-
ymin = pixels_pair[3] if pixels_pair[3] < ymin
|
45
|
-
ymax = pixels_pair[3] if pixels_pair[3] > ymax
|
46
|
-
end
|
38
|
+
def update_result(*_args, x, y)
|
39
|
+
update_bounds(x, y)
|
40
|
+
end
|
47
41
|
|
48
|
-
|
42
|
+
def update_bounds(x, y)
|
43
|
+
bounds[0] = [x, bounds[0]].max
|
44
|
+
bounds[1] = [y, bounds[1]].max
|
45
|
+
bounds[2] = [x, bounds[2]].min
|
46
|
+
bounds[3] = [y, bounds[3]].min
|
49
47
|
end
|
50
48
|
end
|
51
49
|
end
|
data/lib/imatcher/modes/delta.rb
CHANGED
@@ -4,10 +4,12 @@ module Imatcher
|
|
4
4
|
|
5
5
|
# Compare pixels using Delta E distance.
|
6
6
|
class Delta < Base
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
attr_reader :tolerance
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@tolerance = options.delete(:tolerance) || 0.01
|
10
11
|
@delta_score = 0.0
|
12
|
+
super(options)
|
11
13
|
end
|
12
14
|
|
13
15
|
private
|
@@ -18,8 +20,10 @@ module Imatcher
|
|
18
20
|
|
19
21
|
def update_result(a, b, x, y)
|
20
22
|
d = euclid(a, b) / (MAX * Math.sqrt(3))
|
23
|
+
return if d <= tolerance
|
21
24
|
@result.diff << [a, b, x, y, d]
|
22
25
|
@delta_score += d
|
26
|
+
super
|
23
27
|
end
|
24
28
|
|
25
29
|
def background(bg)
|
data/lib/imatcher/modes/rgb.rb
CHANGED
@@ -10,6 +10,7 @@ module Imatcher
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def update_result(a, b, x, y)
|
13
|
+
super
|
13
14
|
@result.diff << [a, b, x, y]
|
14
15
|
end
|
15
16
|
|
@@ -18,7 +19,7 @@ module Imatcher
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def create_diff_image(bg, diff_image)
|
21
|
-
|
22
|
+
diff_image
|
22
23
|
end
|
23
24
|
|
24
25
|
def pixels_diff(d, a, b, x, y)
|
data/lib/imatcher/version.rb
CHANGED
data/spec/fixtures/a.png
CHANGED
Binary file
|
data/spec/fixtures/b.png
CHANGED
Binary file
|
data/spec/fixtures/darker.png
CHANGED
Binary file
|
Binary file
|
Binary file
|
data/spec/fixtures/rgb_diff.png
CHANGED
Binary file
|
@@ -7,9 +7,9 @@ describe Imatcher::Modes::Delta do
|
|
7
7
|
|
8
8
|
let(:options) { { mode: :delta } }
|
9
9
|
|
10
|
-
context "with
|
11
|
-
it "score around 0.
|
12
|
-
expect(subject.score).to be_within(0.
|
10
|
+
context "with darker image" do
|
11
|
+
it "score around 0.075" do
|
12
|
+
expect(subject.score).to be_within(0.005).of(0.075)
|
13
13
|
end
|
14
14
|
|
15
15
|
context "with custom threshold" do
|
@@ -32,12 +32,20 @@ describe Imatcher::Modes::Delta do
|
|
32
32
|
context "with different images" do
|
33
33
|
let(:path_2) { image_path "b" }
|
34
34
|
|
35
|
-
it "score around 0.
|
36
|
-
expect(subject.score).to be_within(0.
|
35
|
+
it "score around 0.0046" do
|
36
|
+
expect(subject.score).to be_within(0.0001).of(0.0046)
|
37
37
|
end
|
38
38
|
|
39
39
|
it "creates correct difference image" do
|
40
40
|
expect(subject.difference_image).to eq(Imatcher::Image.from_file(image_path("delta_diff")))
|
41
41
|
end
|
42
|
+
|
43
|
+
context "with high tolerance" do
|
44
|
+
let(:options) { { mode: :delta, tolerance: 0.1 } }
|
45
|
+
|
46
|
+
it "score around 0.0038" do
|
47
|
+
expect(subject.score).to be_within(0.0001).of(0.0038)
|
48
|
+
end
|
49
|
+
end
|
42
50
|
end
|
43
51
|
end
|
@@ -8,16 +8,16 @@ describe Imatcher::Modes::Grayscale do
|
|
8
8
|
let(:options) { { mode: :grayscale } }
|
9
9
|
|
10
10
|
context "darker image" do
|
11
|
-
it "score around 0.
|
12
|
-
expect(subject.score).to be_within(0.05).of(0.
|
11
|
+
it "score around 0.95" do
|
12
|
+
expect(subject.score).to be_within(0.05).of(0.95)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
context "different images" do
|
17
17
|
let(:path_2) { image_path "b" }
|
18
18
|
|
19
|
-
it "score around 0.
|
20
|
-
expect(subject.score).to be_within(0.
|
19
|
+
it "score around 0.005" do
|
20
|
+
expect(subject.score).to be_within(0.001).of(0.005)
|
21
21
|
end
|
22
22
|
|
23
23
|
it "creates correct difference image" do
|
@@ -37,8 +37,8 @@ describe Imatcher::Modes::Grayscale do
|
|
37
37
|
context "different image" do
|
38
38
|
let(:path_2) { image_path "b" }
|
39
39
|
|
40
|
-
it "score around 0.
|
41
|
-
expect(subject.score).to be_within(0.
|
40
|
+
it "score around 0.016" do
|
41
|
+
expect(subject.score).to be_within(0.001).of(0.016)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -55,16 +55,16 @@ describe Imatcher::Modes::Grayscale do
|
|
55
55
|
let(:options) { { mode: :grayscale, tolerance: 8 } }
|
56
56
|
|
57
57
|
context "darker image" do
|
58
|
-
it "score around 0.
|
59
|
-
expect(subject.score).to be_within(0.005).of(0.
|
58
|
+
it "score around 0.96" do
|
59
|
+
expect(subject.score).to be_within(0.005).of(0.96)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
context "different image" do
|
64
64
|
let(:path_2) { image_path "b" }
|
65
65
|
|
66
|
-
it "score around 0.
|
67
|
-
expect(subject.score).to be_within(0.0005).of(0.
|
66
|
+
it "score around 0.006" do
|
67
|
+
expect(subject.score).to be_within(0.0005).of(0.006)
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
@@ -6,7 +6,7 @@ describe Imatcher::Modes::RGB do
|
|
6
6
|
subject { Imatcher.compare(path_1, path_2, options) }
|
7
7
|
let(:options) { {} }
|
8
8
|
|
9
|
-
context "with
|
9
|
+
context "with darker" do
|
10
10
|
it "score equals to 1" do
|
11
11
|
expect(subject.score).to eq 1
|
12
12
|
end
|
@@ -15,8 +15,8 @@ describe Imatcher::Modes::RGB do
|
|
15
15
|
context "with different images" do
|
16
16
|
let(:path_2) { image_path "b" }
|
17
17
|
|
18
|
-
it "score around 0.
|
19
|
-
expect(subject.score).to be_within(0.
|
18
|
+
it "score around 0.016" do
|
19
|
+
expect(subject.score).to be_within(0.001).of(0.016)
|
20
20
|
end
|
21
21
|
|
22
22
|
it "creates correct difference image" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: imatcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
@@ -97,6 +97,7 @@ files:
|
|
97
97
|
- Rakefile
|
98
98
|
- bin/console
|
99
99
|
- bin/setup
|
100
|
+
- examples/performance.rb
|
100
101
|
- imatcher.gemspec
|
101
102
|
- lib/imatcher.rb
|
102
103
|
- lib/imatcher/color_methods.rb
|