image_compare 1.0.0.pre.dev

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a67b91873ee2ca3ec939803ef3ff7c7e116ce75e48d4643340aae67f48c59225
4
+ data.tar.gz: 2795771468140989921e1ec9afffa2f9616f5363df7106e050ebcb36871659b0
5
+ SHA512:
6
+ metadata.gz: 39a0d7d0bc330345bc4a9624bbbd721f7206db5d5c0d5900bfaa78bb0eec90dd9c03598d683c7f165a78549a8d311edaa7e206b5773abe96672479b25a322851
7
+ data.tar.gz: 3fc93372828b8354867e63ac3e80cd2998fdd025ef9e3601a239d952f7ec0bfbdff3cd148bd03c4feb1a2c9cddf0be73c39da65ad55276639f3ac392886af1fa
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Change log
2
+
3
+ ## main
4
+ - Add Color mode to generate "normal" diff images ([@gsguma][], [@cristianofmc][])
5
+
6
+ [@gsguma]: https://github.com/gsguma
7
+ [@cristianofmc]: https://github.com/cristianofmc
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 instantink
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ [![Gem Version](https://badge.fury.io/rb/image_compare.svg)](https://rubygems.org/gems/image_compare)
2
+ [![Build](https://github.com/instantink/image_compare/workflows/Build/badge.svg)](https://github.com/instantink/image_compare/actions)
3
+
4
+ # ImageCompare
5
+
6
+ Compare PNG images in pure Ruby (uses [ChunkyPNG](https://github.com/wvanbergen/chunky_png)) using different algorithms.
7
+ This is an utility library for image regression testing.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'image_compare'
15
+ ```
16
+
17
+ Or adding to your project:
18
+
19
+ ```ruby
20
+ # my-cool-gem.gemspec
21
+ Gem::Specification.new do |spec|
22
+ # ...
23
+ spec.add_dependency 'image_compare'
24
+ # ...
25
+ end
26
+ ```
27
+
28
+ Additionally, you may want to install [oily_png](https://github.com/wvanbergen/oily_png) to improve performance when using MRI. Just install it globally or add to your Gemfile.
29
+
30
+ ## Modes
31
+
32
+ ImageCompare supports different ways (_modes_) of comparing images.
33
+
34
+ Source images used in examples:
35
+
36
+ <img src='https://raw.githubusercontent.com/instantink/image_compare/master/spec/fixtures/a.png' width='300' />
37
+ <img src='https://raw.githubusercontent.com/instantink/image_compare/master/spec/fixtures/b.png' width='300' />
38
+
39
+ ### Base (RGB) mode
40
+
41
+ Compare pixels by values, resulting score is a ratio of unequal pixels.
42
+ Resulting diff represents per-channel difference.
43
+
44
+ <img src='https://raw.githubusercontent.com/instantink/image_compare/master/spec/fixtures/rgb_diff.png' width='300' />
45
+
46
+ ### Grayscale mode
47
+
48
+ Compare pixels as grayscale (by brightness and alpha), resulting score is a ratio of unequal pixels (with respect to provided tolerance).
49
+
50
+ Resulting diff contains grayscale version of the first image with different pixels highlighted in red and red bounding box.
51
+
52
+ <img src='https://raw.githubusercontent.com/instantink/image_compare/master/spec/fixtures/grayscale_diff.png' width='300' />
53
+
54
+ ### Delta
55
+
56
+ Compare pixels using [Delta E](https://en.wikipedia.org/wiki/Color_difference) distance.
57
+ Resulting diff contains grayscale version of the first image with different pixels highlighted in red (with respect to diff score).
58
+
59
+ <img src='https://raw.githubusercontent.com/instantink/image_compare/master/spec/fixtures/delta_diff.png' width='300' />
60
+
61
+ ## Usage
62
+
63
+ ```ruby
64
+ # create new matcher with default threshold equals to 0
65
+ # and base (RGB) mode
66
+ cmp = ImageCompare::Matcher.new
67
+ cmp.mode #=> ImageCompare::Modes::RGB
68
+
69
+ # create matcher with specific threshold
70
+ cmp = ImageCompare::Matcher.new threshold: 0.05
71
+ cmp.threshold #=> 0.05
72
+
73
+ # or with a lower threshold (in case you want to test that there is some difference)
74
+ cmp = ImageCompare::Matcher.new lower_threshold: 0.01
75
+ cmp.lower_threshold #=> 0.01
76
+
77
+ # create zero-tolerance grayscale matcher
78
+ cmp = ImageCompare::Matcher.new mode: :grayscale, tolerance: 0
79
+ cmp.mode #=> ImageCompare::Modes::Grayscale
80
+
81
+ res = cmp.compare(path_1, path_2)
82
+ res #=> ImageCompare::Result
83
+ res.match? #=> true
84
+ res.score #=> 0.0
85
+
86
+ # Return diff image object
87
+ res.difference_image #=> ImageCompare::Image
88
+ res.difference_image.save(new_path)
89
+
90
+ # without explicit matcher
91
+ res = ImageCompare.compare(path_1, path_2, options)
92
+
93
+ # equals to
94
+ res = ImageCompare::Matcher.new(options).compare(path_1, path_2)
95
+ ```
96
+
97
+ ## Excluding rectangle
98
+
99
+ <img src='https://raw.githubusercontent.com/instantink/image_compare/master/spec/fixtures/a.png' width='300' />
100
+ <img src='https://raw.githubusercontent.com/instantink/image_compare/master/spec/fixtures/a1.png' width='300' />
101
+
102
+ You can exclude rectangle from comparing by passing `:exclude_rect` to `compare`.
103
+ E.g., if `path_1` and `path_2` contain images above
104
+ ```ruby
105
+ ImageCompare.compare(path_1, path_2, exclude_rect: [200, 150, 275, 200]).match? # => true
106
+ ```
107
+ `[200, 150, 275, 200]` is array of two vertices of rectangle -- (200, 150) is left-top vertex and (275, 200) is right-bottom.
108
+
109
+ ## Including rectangle
110
+
111
+ You can set bounds of comparing by passing `:include_rect` to `compare` with array similar to previous example
112
+
113
+ ## Contributing
114
+
115
+ Bug reports and pull requests are welcome on GitHub at https://github.com/instantink/image_compare.
116
+
117
+ ## License
118
+
119
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'image_compare'
5
+ require 'pry'
6
+
7
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chunky_png'
4
+
5
+ begin
6
+ require 'oily_png' unless RUBY_PLATFORM == 'java'
7
+ rescue LoadError => _e
8
+ nil
9
+ end
10
+
11
+ module ImageCompare
12
+ module ColorMethods
13
+ include ChunkyPNG::Color
14
+
15
+ def brightness(a)
16
+ 0.3 * r(a) + 0.59 * g(a) + 0.11 * b(a)
17
+ end
18
+
19
+ def red
20
+ rgb(255, 0, 0)
21
+ end
22
+
23
+ def green
24
+ rgb(0, 255, 0)
25
+ end
26
+
27
+ def blue
28
+ rgb(0, 0, 255)
29
+ end
30
+
31
+ def yellow
32
+ rgb(255, 255, 51)
33
+ end
34
+
35
+ def orange
36
+ rgb(255, 128, 0)
37
+ end
38
+
39
+ def transparent
40
+ rgba(255, 255, 255, 255)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'image_compare/color_methods'
4
+
5
+ module ImageCompare
6
+ class Image < ChunkyPNG::Image
7
+ include ColorMethods
8
+
9
+ def each_pixel
10
+ height.times do |y|
11
+ current_row = row(y) || []
12
+ current_row.each_with_index do |pixel, x|
13
+ yield(pixel, x, y)
14
+ end
15
+ end
16
+ end
17
+
18
+ def compare_each_pixel(image, area: nil)
19
+ area = bounding_rect if area.nil?
20
+ (area.top..area.bot).each do |y|
21
+ current_row = row(y) || []
22
+ range = (area.left..area.right)
23
+ next if image.row(y).slice(range) == current_row.slice(range)
24
+ (area.left..area.right).each do |x|
25
+ yield(self[x, y], image[x, y], x, y)
26
+ end
27
+ end
28
+ end
29
+
30
+ def to_grayscale
31
+ each_pixel do |pixel, x, y|
32
+ self[x, y] = grayscale(brightness(pixel).round)
33
+ end
34
+ self
35
+ end
36
+
37
+ def with_alpha(value)
38
+ each_pixel do |pixel, x, y|
39
+ self[x, y] = rgba(r(pixel), g(pixel), b(pixel), value)
40
+ end
41
+ self
42
+ end
43
+
44
+ def sizes_match?(image)
45
+ [width, height] == [image.width, image.height]
46
+ end
47
+
48
+ def inspect
49
+ "Image:#{object_id}<#{width}x#{height}>"
50
+ end
51
+
52
+ def highlight_rectangle(rect, color = :red)
53
+ raise ArgumentError, "Undefined color: #{color}" unless respond_to?(color)
54
+ return self if rect.nil?
55
+ rect(*rect.bounds, send(color))
56
+ self
57
+ end
58
+
59
+ def bounding_rect
60
+ Rectangle.new(0, 0, width - 1, height - 1)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ class Matcher
5
+ require 'image_compare/image'
6
+ require 'image_compare/result'
7
+ require 'image_compare/modes'
8
+
9
+ MODES = {
10
+ rgb: 'RGB',
11
+ delta: 'Delta',
12
+ grayscale: 'Grayscale',
13
+ color: 'Color'
14
+ }.freeze
15
+
16
+ attr_reader :mode
17
+
18
+ def initialize(**options)
19
+ mode_type = options.delete(:mode) || :rgb
20
+ raise ArgumentError, "Undefined mode: #{mode_type}" unless MODES.key?(mode_type)
21
+ @mode = Modes.const_get(MODES[mode_type]).new(**options)
22
+ end
23
+
24
+ def compare(a, b)
25
+ a = Image.from_file(a) unless a.is_a?(Image)
26
+ b = Image.from_file(b) unless b.is_a?(Image)
27
+
28
+ unless a.sizes_match?(b)
29
+ raise SizesMismatchError,
30
+ "Size mismatch: first image size: #{a.width}x#{a.height}, second image size: #{b.width}x#{b.height}"
31
+ end
32
+
33
+ image_area = Rectangle.new(0, 0, a.width - 1, a.height - 1)
34
+
35
+ unless mode.exclude_rect.nil?
36
+ unless image_area.contains?(mode.exclude_rect)
37
+ raise ArgumentError, 'Bounds must be in image'
38
+ end
39
+ end
40
+
41
+ unless mode.include_rect.nil?
42
+ unless image_area.contains?(mode.include_rect)
43
+ raise ArgumentError, 'Bounds must be in image'
44
+ end
45
+ unless mode.exclude_rect.nil?
46
+ unless mode.include_rect.contains?(mode.exclude_rect)
47
+ raise ArgumentError, 'Included area must contain excluded'
48
+ end
49
+ end
50
+ end
51
+
52
+ mode.compare(a, b)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ module Modes
5
+ class Base
6
+ require 'image_compare/rectangle'
7
+ include ColorMethods
8
+
9
+ attr_reader :result, :threshold, :lower_threshold, :bounds, :exclude_rect, :include_rect
10
+
11
+ def initialize(threshold: 0.0, lower_threshold: 0.0, exclude_rect: nil, include_rect: nil)
12
+ @include_rect = Rectangle.new(*include_rect) unless include_rect.nil?
13
+ @exclude_rect = Rectangle.new(*exclude_rect) unless exclude_rect.nil?
14
+ @threshold = threshold
15
+ @lower_threshold = lower_threshold
16
+ @result = Result.new(self, threshold: threshold, lower_threshold: lower_threshold)
17
+ end
18
+
19
+ def compare(a, b)
20
+ result.image = a
21
+ @include_rect ||= a.bounding_rect
22
+ @bounds = Rectangle.new(*include_rect.bounds)
23
+
24
+ b.compare_each_pixel(a, area: include_rect) do |b_pixel, a_pixel, x, y|
25
+ next if pixels_equal?(b_pixel, a_pixel)
26
+ next if !exclude_rect.nil? && exclude_rect.contains_point?(x, y)
27
+ update_result(b_pixel, a_pixel, x, y)
28
+ end
29
+
30
+ result.score = score
31
+ result
32
+ end
33
+
34
+ def diff(bg, diff)
35
+ diff_image = background(bg).highlight_rectangle(exclude_rect, :blue)
36
+ diff.each do |pixels_pair|
37
+ pixels_diff(diff_image, *pixels_pair)
38
+ end
39
+ create_diff_image(bg, diff_image)
40
+ .highlight_rectangle(bounds)
41
+ .highlight_rectangle(include_rect, :green)
42
+ end
43
+
44
+ def score
45
+ result.diff.length.to_f / area
46
+ end
47
+
48
+ def update_result(*_args, x, y)
49
+ update_bounds(x, y)
50
+ end
51
+
52
+ def update_bounds(x, y)
53
+ bounds.left = [x, bounds.left].max
54
+ bounds.top = [y, bounds.top].max
55
+ bounds.right = [x, bounds.right].min
56
+ bounds.bot = [y, bounds.bot].min
57
+ end
58
+
59
+ def area
60
+ area = include_rect.area
61
+ return area if exclude_rect.nil?
62
+ area - exclude_rect.area
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ module Modes
5
+ require 'image_compare/modes/base'
6
+
7
+ class Color < Base
8
+ include ColorMethods
9
+
10
+ DEFAULT_TOLERANCE = 16
11
+
12
+ attr_reader :tolerance
13
+
14
+ def initialize(**options)
15
+ @tolerance = options.delete(:tolerance) || DEFAULT_TOLERANCE
16
+ super(**options)
17
+ end
18
+
19
+ def diff(bg, diff)
20
+ diff_image = bg.highlight_rectangle(exclude_rect, :blue)
21
+
22
+ if area_in_exclude_rect?
23
+ diff_image
24
+ else
25
+ diff_image.highlight_rectangle(bounds)
26
+ end
27
+ end
28
+
29
+ def area_in_exclude_rect?
30
+ return false if exclude_rect.nil?
31
+
32
+ diff_area = {
33
+ left: bounds.bounds[0],
34
+ top: bounds.bounds[1],
35
+ right: bounds.bounds[2],
36
+ bot: bounds.bounds[3]
37
+ }
38
+
39
+ exclude_area = {
40
+ left: exclude_rect.bounds[0],
41
+ top: exclude_rect.bounds[1],
42
+ right: exclude_rect.bounds[2],
43
+ bot: exclude_rect.bounds[3]
44
+ }
45
+
46
+ diff_area[:left] <= exclude_area[:left] &&
47
+ diff_area[:top] <= exclude_area[:top] &&
48
+ diff_area[:right] >= exclude_area[:right] &&
49
+ diff_area[:bot] >= exclude_area[:bot]
50
+ end
51
+
52
+ def pixels_equal?(a, b)
53
+ alpha = color_similar?(a(a), a(b))
54
+ brightness = color_similar?(brightness(a), brightness(b))
55
+ brightness && alpha
56
+ end
57
+
58
+ def update_result(a, b, x, y)
59
+ super
60
+ @result.diff << [a, b, x, y]
61
+ end
62
+
63
+ def color_similar?(a, b)
64
+ d = (a - b).abs
65
+ d <= tolerance
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ module Modes
5
+ require 'image_compare/modes/base'
6
+
7
+ class Delta < Base
8
+ attr_reader :tolerance
9
+
10
+ def initialize(**options)
11
+ @tolerance = options.delete(:tolerance) || 0.01
12
+ @delta_score = 0.0
13
+ super(**options)
14
+ end
15
+
16
+ private
17
+
18
+ def pixels_equal?(a, b)
19
+ a == b
20
+ end
21
+
22
+ def update_result(a, b, x, y)
23
+ d = euclid(a, b) / (MAX * Math.sqrt(3))
24
+ return if d <= tolerance
25
+ @result.diff << [a, b, x, y, d]
26
+ @delta_score += d
27
+ super
28
+ end
29
+
30
+ def background(bg)
31
+ Image.new(bg.width, bg.height, WHITE).with_alpha(0)
32
+ end
33
+
34
+ def euclid(a, b)
35
+ Math.sqrt(
36
+ (r(a) - r(b))**2 +
37
+ (g(a) - g(b))**2 +
38
+ (b(a) - b(b))**2
39
+ )
40
+ end
41
+
42
+ def create_diff_image(bg, diff_image)
43
+ bg.to_grayscale.compose!(diff_image, 0, 0)
44
+ end
45
+
46
+ def pixels_diff(d, *_args, x, y, a)
47
+ d[x, y] = rgba(MAX, 0, 0, (a * MAX).round)
48
+ end
49
+
50
+ def score
51
+ @delta_score / area
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ module Modes
5
+ require 'image_compare/modes/base'
6
+
7
+ class Grayscale < Base
8
+ DEFAULT_TOLERANCE = 16
9
+
10
+ attr_reader :tolerance
11
+
12
+ def initialize(**options)
13
+ @tolerance = options.delete(:tolerance) || DEFAULT_TOLERANCE
14
+ super(**options)
15
+ end
16
+
17
+ def pixels_equal?(a, b)
18
+ alpha = color_similar?(a(a), a(b))
19
+ brightness = color_similar?(brightness(a), brightness(b))
20
+ brightness && alpha
21
+ end
22
+
23
+ def update_result(a, b, x, y)
24
+ super
25
+ @result.diff << [a, b, x, y]
26
+ end
27
+
28
+ def background(bg)
29
+ bg.to_grayscale
30
+ end
31
+
32
+ def pixels_diff(d, _a, _b, x, y)
33
+ d[x, y] = rgb(255, 0, 0)
34
+ end
35
+
36
+ def create_diff_image(_bg, diff_image)
37
+ diff_image
38
+ end
39
+
40
+ def color_similar?(a, b)
41
+ d = (a - b).abs
42
+ d <= tolerance
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ module Modes
5
+ require 'image_compare/modes/base'
6
+
7
+ class RGB < Base
8
+ def pixels_equal?(a, b)
9
+ a == b
10
+ end
11
+
12
+ def update_result(a, b, x, y)
13
+ super
14
+ @result.diff << [a, b, x, y]
15
+ end
16
+
17
+ def background(bg)
18
+ Image.new(bg.width, bg.height, BLACK)
19
+ end
20
+
21
+ def create_diff_image(_bg, diff_image)
22
+ diff_image
23
+ end
24
+
25
+ def pixels_diff(d, a, b, x, y)
26
+ d[x, y] = rgb(
27
+ (r(a) - r(b)).abs,
28
+ (g(a) - g(b)).abs,
29
+ (b(a) - b(b)).abs
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ module Modes
5
+ require 'image_compare/modes/rgb'
6
+ require 'image_compare/modes/grayscale'
7
+ require 'image_compare/modes/delta'
8
+ require 'image_compare/modes/color'
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ class Rectangle
5
+ attr_accessor :left, :top, :right, :bot
6
+
7
+ def initialize(l, t, r, b)
8
+ @left = l
9
+ @top = t
10
+ @right = r
11
+ @bot = b
12
+ end
13
+
14
+ def area
15
+ (right - left + 1) * (bot - top + 1)
16
+ end
17
+
18
+ def contains?(rect)
19
+ (left <= rect.left) &&
20
+ (right >= rect.right) &&
21
+ (top <= rect.top) &&
22
+ (bot >= rect.bot)
23
+ end
24
+
25
+ def bounds
26
+ [left, top, right, bot]
27
+ end
28
+
29
+ def contains_point?(x, y)
30
+ x.between?(left, right) && y.between?(top, bot)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ class Result
5
+ attr_accessor :score, :image
6
+ attr_reader :diff, :mode, :threshold, :lower_threshold
7
+
8
+ def initialize(mode, threshold:, lower_threshold:)
9
+ @score = 0.0
10
+ @diff = []
11
+ @threshold = threshold
12
+ @lower_threshold = lower_threshold
13
+ @mode = mode
14
+ end
15
+
16
+ def difference_image
17
+ @diff_image ||= mode.diff(image, diff)
18
+ end
19
+
20
+ def match?
21
+ score <= threshold && score >= lower_threshold
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageCompare
4
+ VERSION = '1.0.0-dev'
5
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'image_compare/version'
4
+
5
+ module ImageCompare
6
+ class SizesMismatchError < StandardError
7
+ end
8
+
9
+ require 'image_compare/matcher'
10
+ require 'image_compare/color_methods'
11
+
12
+ def self.compare(path_a, path_b, **options)
13
+ Matcher.new(**options).compare(path_a, path_b)
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_compare
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre.dev
5
+ platform: ruby
6
+ authors:
7
+ - cristianofmc
8
+ - gsguma
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2023-03-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chunky_png
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '13.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '13.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '3.9'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '3.9'
56
+ description: Image comparison lib built on top of ChunkyPNG
57
+ email:
58
+ - cristiano.fmc@hotmail.com
59
+ - giltonguma@gmail.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - CHANGELOG.md
65
+ - LICENSE.txt
66
+ - README.md
67
+ - bin/console
68
+ - bin/setup
69
+ - lib/image_compare.rb
70
+ - lib/image_compare/color_methods.rb
71
+ - lib/image_compare/image.rb
72
+ - lib/image_compare/matcher.rb
73
+ - lib/image_compare/modes.rb
74
+ - lib/image_compare/modes/base.rb
75
+ - lib/image_compare/modes/color.rb
76
+ - lib/image_compare/modes/delta.rb
77
+ - lib/image_compare/modes/grayscale.rb
78
+ - lib/image_compare/modes/rgb.rb
79
+ - lib/image_compare/rectangle.rb
80
+ - lib/image_compare/result.rb
81
+ - lib/image_compare/version.rb
82
+ homepage: https://github.com/instantink/image_compare
83
+ licenses:
84
+ - MIT
85
+ metadata:
86
+ bug_tracker_uri: https://github.com/instantink/image_compare/issues
87
+ changelog_uri: https://github.com/instantink/image_compare/blob/master/CHANGELOG.md
88
+ documentation_uri: https://github.com/instantink/image_compare
89
+ homepage_uri: https://github.com/instantink/image_compare
90
+ source_code_uri: https://github.com/instantink/image_compare
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: 3.2.0
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">"
103
+ - !ruby/object:Gem::Version
104
+ version: 1.3.1
105
+ requirements: []
106
+ rubygems_version: 3.4.1
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Image comparison lib
110
+ test_files: []