sqed 0.3.2 → 0.4.0
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/.ruby-version +1 -1
- data/Guardfile +66 -0
- data/lib/sqed.rb +120 -68
- data/lib/sqed/boundaries.rb +30 -25
- data/lib/sqed/boundary_finder.rb +221 -212
- data/lib/sqed/boundary_finder/color_line_finder.rb +50 -42
- data/lib/sqed/boundary_finder/cross_finder.rb +3 -3
- data/lib/sqed/boundary_finder/stage_finder.rb +8 -3
- data/lib/sqed/extractor.rb +23 -25
- data/lib/sqed/parser.rb +4 -7
- data/lib/sqed/parser/barcode_parser.rb +5 -5
- data/lib/sqed/parser/ocr_parser.rb +46 -46
- data/lib/sqed/result.rb +60 -57
- data/lib/sqed/version.rb +1 -1
- data/lib/sqed_config.rb +52 -56
- data/spec/lib/sqed/boundaries_spec.rb +1 -1
- data/spec/lib/sqed/boundary_finder/color_line_finder_spec.rb +24 -24
- data/spec/lib/sqed/boundary_finder/cross_finder_spec.rb +1 -1
- data/spec/lib/sqed/boundary_finder/stage_finder_spec.rb +1 -1
- data/spec/lib/sqed/boundary_finder_spec.rb +73 -45
- data/spec/lib/sqed/extractor_spec.rb +4 -4
- data/spec/lib/sqed/parser/ocr_spec.rb +2 -2
- data/spec/lib/sqed_spec.rb +39 -39
- data/spec/lib/stage_handling/seven_slot_spec.rb +45 -9
- data/spec/support/files/stage_images/inhs_7_slot2.jpg +0 -0
- data/spec/support/image_helpers.rb +10 -9
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97003b6e21e84313180a638df393052b18a7eb25
|
4
|
+
data.tar.gz: e125134f5ed864b06fd4b8280228261e7d1eac40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd0e7287ef11ba6fef8784be32c24898784125dde881e2b8082b15d1f19a5995c55e434924057f2bdd8ef27419a51b9e118351f20ebbff594b9a7d7e4f500a6b
|
7
|
+
data.tar.gz: 2e87fa5f7e6cd1bb0f5db13158e6c5e41e812a547cd2f90f44ce5104de8105d6595a60c86a2794ff5804615a5a5659da51c9daaed5ede8236661d0a85fb39c0b
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3
|
1
|
+
2.4.3
|
data/Guardfile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
guard :minitest do
|
19
|
+
# with Minitest::Unit
|
20
|
+
watch(%r{^test/(.*)\/?test_(.*)\.rb$})
|
21
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
22
|
+
watch(%r{^test/test_helper\.rb$}) { 'test' }
|
23
|
+
|
24
|
+
# with Minitest::Spec
|
25
|
+
# watch(%r{^spec/(.*)_spec\.rb$})
|
26
|
+
# watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
27
|
+
# watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
32
|
+
# rspec may be run, below are examples of the most common uses.
|
33
|
+
# * bundler: 'bundle exec rspec'
|
34
|
+
# * bundler binstubs: 'bin/rspec'
|
35
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
36
|
+
# installed the spring binstubs per the docs)
|
37
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
38
|
+
# * 'just' rspec: 'rspec'
|
39
|
+
|
40
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
41
|
+
require "guard/rspec/dsl"
|
42
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
43
|
+
|
44
|
+
# Feel free to open issues for suggestions and improvements
|
45
|
+
|
46
|
+
# RSpec files
|
47
|
+
rspec = dsl.rspec
|
48
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
49
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
50
|
+
watch(rspec.spec_files)
|
51
|
+
|
52
|
+
# Ruby files
|
53
|
+
ruby = dsl.ruby
|
54
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
55
|
+
|
56
|
+
# Turnip features and steps
|
57
|
+
# watch(%r{^spec/acceptance/(.+)\.feature$})
|
58
|
+
# watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
59
|
+
# Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
60
|
+
# end
|
61
|
+
end
|
62
|
+
|
63
|
+
# guard :rubocop do
|
64
|
+
# watch(%r{.+\.rb$})
|
65
|
+
# watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
66
|
+
# end
|
data/lib/sqed.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
recent_ruby = RUBY_VERSION >= '2.
|
4
|
-
raise
|
3
|
+
recent_ruby = RUBY_VERSION >= '2.4.1'
|
4
|
+
raise 'IMPORTANT: sqed gem requires ruby >= 2.4.1' unless recent_ruby
|
5
5
|
|
6
|
-
require
|
6
|
+
require 'rmagick'
|
7
7
|
|
8
8
|
# Instances take the following
|
9
|
-
# 1)
|
9
|
+
# 1) An :image @image
|
10
10
|
# 2) A target extraction pattern, or individually specified attributes
|
11
11
|
#
|
12
12
|
# Return a Sqed::Result
|
@@ -17,85 +17,78 @@ require "rmagick"
|
|
17
17
|
class Sqed
|
18
18
|
|
19
19
|
require_relative 'sqed_config'
|
20
|
-
require_relative
|
21
|
-
require_relative
|
20
|
+
require_relative 'sqed/extractor'
|
21
|
+
require_relative 'sqed/result'
|
22
22
|
|
23
23
|
# initial image which is an instance of ImageMagick::Image, containing background and stage, or just stage
|
24
24
|
attr_accessor :image
|
25
25
|
|
26
|
-
# !optional! A lookup macro that if provided sets boundary_finder, layout, and metadata_map.
|
26
|
+
# !optional! A lookup macro that if provided sets boundary_finder, layout, and metadata_map.
|
27
|
+
# These can be individually overwritten.
|
27
28
|
# Legal values are symbols taken from SqedConfig::EXTRACTION_PATTERNS.
|
28
|
-
# DO NOT pass pattern outside of a sqed instance!
|
29
29
|
#
|
30
|
-
# !!
|
30
|
+
# !! Patterns are not intended to be persisted in external databases (they may change names). !!
|
31
|
+
# To persist Sqed metadata in something like Postgres reference individual
|
32
|
+
# attributes (e.g. layout, metadata_map, boundary_finder).
|
33
|
+
#
|
34
|
+
# @return [Symbol] like `:seven_slot`, see Sqed::CONFIG for valid options,
|
35
|
+
# default value is `nil`
|
36
|
+
# not required if layout, metadata_map, and boundary_finder are provided
|
31
37
|
attr_accessor :pattern
|
32
38
|
|
33
|
-
#
|
39
|
+
# @return [Symbol] like `:cross`
|
40
|
+
# !! Provide a specific layout, passed as option :layout, overrides layout metadata taken from :pattern, defaults to `:cross`
|
34
41
|
attr_accessor :layout
|
35
42
|
|
36
43
|
# the image that is the cropped content for parsing
|
37
44
|
attr_accessor :stage_image
|
38
45
|
|
39
|
-
#
|
46
|
+
# @return [Sqed::Boundaries instance]
|
47
|
+
# stores the coordinates of the stage
|
40
48
|
attr_accessor :stage_boundary
|
41
49
|
|
42
|
-
#
|
50
|
+
# @return [Sqed::Boundaries instance]
|
51
|
+
# contains the coordinates of the internal stage sections
|
43
52
|
attr_accessor :boundaries
|
44
53
|
|
45
|
-
# Boolean
|
54
|
+
# @return [Boolean] defaults to `true`
|
55
|
+
# when true detects border on initialization
|
46
56
|
attr_accessor :has_border
|
47
57
|
|
48
|
-
#
|
58
|
+
# @return [Symbol] like `:red`, `:green`, `:blue`, defaults to `:green`
|
59
|
+
# describing the boundary color within the stage
|
49
60
|
attr_accessor :boundary_color
|
50
61
|
|
51
|
-
# Boolean
|
62
|
+
# @return [Boolean] defaults to `true` (faster, less accurate)
|
63
|
+
# if `true` do the boundary detection (not stage detection at present)
|
64
|
+
# against a thumbnail version of the passed image
|
52
65
|
attr_accessor :use_thumbnail
|
53
66
|
|
54
|
-
# Provide a metadata map, overrides metadata taken from pattern
|
55
|
-
# !! Always passed as an option :target_metadata_map, an persisted as :metadata_map
|
67
|
+
# Provide a metadata map, overrides metadata taken from pattern
|
56
68
|
attr_accessor :metadata_map
|
57
69
|
|
58
70
|
# Provide a boundary_finder, overrides metadata taken from pattern
|
59
71
|
attr_accessor :boundary_finder
|
60
72
|
|
61
|
-
def initialize(
|
62
|
-
raise 'extraction pattern not defined' if target_pattern && !SqedConfig::EXTRACTION_PATTERNS.keys.include?(target_pattern)
|
63
|
-
|
64
|
-
# data, and stubs for results
|
65
|
-
@image = target_image
|
66
|
-
@boundaries = nil
|
67
|
-
@stage_boundary = Sqed::Boundaries.new(:internal_box)
|
68
|
-
|
73
|
+
def initialize(**opts)
|
69
74
|
# extraction metadata
|
70
|
-
@
|
71
|
-
|
72
|
-
@has_border = has_border
|
73
|
-
@boundary_finder = boundary_finder.constantize if boundary_finder
|
74
|
-
@layout = target_layout
|
75
|
-
@layout ||= SqedConfig::EXTRACTION_PATTERNS[pattern][:layout] if pattern
|
75
|
+
@image = opts[:image]
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
@use_thumbnail = use_thumbnail
|
80
|
-
|
81
|
-
set_stage_boundary if @image
|
77
|
+
configure(opts)
|
78
|
+
stub_results
|
82
79
|
end
|
83
80
|
|
84
81
|
# @return [Hash]
|
85
|
-
# federate extraction options
|
82
|
+
# federate extraction options
|
86
83
|
def extraction_metadata
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
data[:boundary_color] = boundary_color
|
96
|
-
data[:has_border] = has_border
|
97
|
-
data[:use_thumbnail] = use_thumbnail
|
98
|
-
data
|
84
|
+
{
|
85
|
+
boundary_finder: boundary_finder,
|
86
|
+
layout: layout,
|
87
|
+
metadata_map: metadata_map,
|
88
|
+
boundary_color: boundary_color,
|
89
|
+
has_border: has_border,
|
90
|
+
use_thumbnail: use_thumbnail
|
91
|
+
}
|
99
92
|
end
|
100
93
|
|
101
94
|
# @return [ImageMagick::Image]
|
@@ -117,14 +110,13 @@ class Sqed
|
|
117
110
|
end
|
118
111
|
|
119
112
|
def stage_image
|
120
|
-
|
121
|
-
|
113
|
+
crop_image if @stage_image.nil?
|
114
|
+
@stage_image
|
122
115
|
end
|
123
116
|
|
124
|
-
#
|
117
|
+
# @return [Sqed::Boundaries instance, nil]
|
125
118
|
# a boundaries instance that has the original image (prior to cropping stage) coordinates
|
126
119
|
def native_boundaries
|
127
|
-
# check for @boundaries.complete first? OR handle partial detections ?!
|
128
120
|
if @boundaries.complete
|
129
121
|
@boundaries.offset(stage_boundary)
|
130
122
|
else
|
@@ -132,7 +124,7 @@ class Sqed
|
|
132
124
|
end
|
133
125
|
end
|
134
126
|
|
135
|
-
# return [Image]
|
127
|
+
# @return [Image]
|
136
128
|
# crops the stage if not done, then sets/returns @stage_image
|
137
129
|
def crop_image
|
138
130
|
if has_border
|
@@ -145,19 +137,20 @@ class Sqed
|
|
145
137
|
|
146
138
|
def result
|
147
139
|
return false if image.nil?
|
148
|
-
return false if pattern.nil? && (metadata_map.nil? && layout.nil? && boundary_finder.nil?)
|
149
140
|
|
150
141
|
extractor = Sqed::Extractor.new(
|
151
|
-
|
152
|
-
|
153
|
-
|
142
|
+
boundaries: boundaries,
|
143
|
+
metadata_map: metadata_map, # extraction_metadata[:metadata_map],
|
144
|
+
image: stage_image
|
145
|
+
)
|
146
|
+
|
154
147
|
extractor.result
|
155
148
|
end
|
156
149
|
|
157
150
|
# @return [Hash]
|
158
151
|
# an overview of data/metadata, for debugging purposes only
|
159
152
|
def attributes
|
160
|
-
{
|
153
|
+
{ image: image,
|
161
154
|
boundaries: boundaries,
|
162
155
|
stage_boundary: stage_boundary
|
163
156
|
}.merge!(extraction_metadata)
|
@@ -165,9 +158,76 @@ class Sqed
|
|
165
158
|
|
166
159
|
protected
|
167
160
|
|
161
|
+
def configure(opts)
|
162
|
+
configure_from_pattern(opts[:pattern])
|
163
|
+
configure_boundary_finder(opts)
|
164
|
+
configure_layout(opts)
|
165
|
+
configure_metadata_map(opts)
|
166
|
+
|
167
|
+
@has_border = opts[:has_border]
|
168
|
+
@has_border = true if @has_border.nil?
|
169
|
+
|
170
|
+
@boundary_color = opts[:boundary_color]
|
171
|
+
@boundary_color ||= :green
|
172
|
+
|
173
|
+
@use_thumbnail = opts[:use_thumbnail]
|
174
|
+
@use_thumbnail = true if @use_thumbnail.nil?
|
175
|
+
end
|
176
|
+
|
177
|
+
def configure_from_pattern(value)
|
178
|
+
return if value.nil?
|
179
|
+
value = value.to_sym
|
180
|
+
raise "provided extraction pattern '#{value}' not defined" if !SqedConfig::EXTRACTION_PATTERNS.keys.include?(value)
|
181
|
+
@pattern = value
|
182
|
+
a = SqedConfig::EXTRACTION_PATTERNS[pattern]
|
183
|
+
@boundary_finder = a[:boundary_finder]
|
184
|
+
@layout = a[:layout]
|
185
|
+
@metadata_map = a[:metadata_map]
|
186
|
+
true
|
187
|
+
end
|
188
|
+
|
189
|
+
def configure_boundary_finder(opts)
|
190
|
+
@boundary_finder = opts[:boundary_finder].constantize if !opts[:boundary_finder].nil?
|
191
|
+
@boundary_finder ||= Sqed::BoundaryFinder::CrossFinder
|
192
|
+
end
|
193
|
+
|
194
|
+
def configure_layout(opts)
|
195
|
+
@layout = opts[:layout]
|
196
|
+
if p = opts[:pattern]
|
197
|
+
@layout ||= SqedConfig::EXTRACTION_PATTERNS[p][:layout]
|
198
|
+
end
|
199
|
+
@layout ||= :cross
|
200
|
+
end
|
201
|
+
|
202
|
+
def configure_metadata_map(opts)
|
203
|
+
@metadata_map = opts[:metadata_map] unless opts[:metadata_map].nil?
|
204
|
+
end
|
205
|
+
|
206
|
+
# stubs for data and results
|
207
|
+
def stub_results
|
208
|
+
@boundaries = nil
|
209
|
+
@stage_boundary = Sqed::Boundaries.new(:internal_box)
|
210
|
+
set_stage_boundary if @image
|
211
|
+
end
|
212
|
+
|
213
|
+
def get_section_boundaries
|
214
|
+
boundary_finder.new(section_params).boundaries
|
215
|
+
end
|
216
|
+
|
217
|
+
# @return [Hash]
|
218
|
+
# variables for the isolated stage image
|
219
|
+
def section_params
|
220
|
+
{
|
221
|
+
image: stage_image,
|
222
|
+
use_thumbnail: use_thumbnail,
|
223
|
+
layout: layout,
|
224
|
+
boundary_color: boundary_color
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
168
228
|
def set_stage_boundary
|
169
229
|
if has_border
|
170
|
-
boundary = Sqed::BoundaryFinder::StageFinder.new(
|
230
|
+
boundary = Sqed::BoundaryFinder::StageFinder.new(image: image).boundaries
|
171
231
|
if boundary.populated?
|
172
232
|
@stage_boundary.set(0, boundary.for(0))
|
173
233
|
else
|
@@ -178,12 +238,4 @@ class Sqed
|
|
178
238
|
end
|
179
239
|
end
|
180
240
|
|
181
|
-
def get_section_boundaries
|
182
|
-
options = {target_image: stage_image, use_thumbnail: use_thumbnail}
|
183
|
-
options.merge!( target_layout: extraction_metadata[:target_layout] ) unless extraction_metadata[:boundary_finder].name == 'Sqed::BoundaryFinder::CrossFinder'
|
184
|
-
options.merge!( boundary_color: boundary_color) if extraction_metadata[:boundary_finder].name == 'Sqed::BoundaryFinder::ColorLineFinder'
|
185
|
-
|
186
|
-
extraction_metadata[:boundary_finder].new(options).boundaries
|
187
|
-
end
|
188
|
-
|
189
241
|
end
|
data/lib/sqed/boundaries.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
# An Sqed::Boundaries is a simple wrapper for a hash that contains the co-ordinates for each section of a layout.
|
2
1
|
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
class Sqed
|
3
|
+
|
4
|
+
# An Sqed::Boundaries is a simple wrapper for a hash that contains the co-ordinates for each section of a layout.
|
5
|
+
#
|
6
|
+
# Layouts are Hashes defined in EXTRACTION_PATTERNS[<pattern>][<layout>]
|
7
|
+
#
|
8
|
+
class Boundaries
|
6
9
|
include Enumerable
|
7
10
|
|
8
11
|
# stores a hash
|
@@ -12,11 +15,12 @@ class Sqed::Boundaries
|
|
12
15
|
# 0 => [10,10,40,40]
|
13
16
|
attr_reader :coordinates
|
14
17
|
|
15
|
-
# A symbol from Sqed::Config::LAYOUTS.keys
|
18
|
+
# A symbol from Sqed::Config::LAYOUTS.keys
|
16
19
|
# :right_t
|
17
20
|
attr_accessor :layout
|
18
21
|
|
19
|
-
# Boolean
|
22
|
+
# @return [Boolean] whether or not the last method to populate this object
|
23
|
+
# executed to completion
|
20
24
|
attr_accessor :complete
|
21
25
|
|
22
26
|
def initialize(layout = nil)
|
@@ -34,17 +38,19 @@ class Sqed::Boundaries
|
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
41
|
+
# @return [Sqed::Boundaries instance]
|
42
|
+
# the idea here is to create a deep copy of self, offsetting by boundary
|
43
|
+
# as we go
|
37
44
|
def offset(boundary)
|
38
|
-
b = Sqed::Boundaries.new
|
39
|
-
(0..
|
40
|
-
b.set(i,
|
41
|
-
[(
|
42
|
-
(
|
43
|
-
|
44
|
-
|
45
|
-
)
|
45
|
+
b = Sqed::Boundaries.new
|
46
|
+
(0..coordinates.length - 1).each do |i|
|
47
|
+
b.set(i,
|
48
|
+
[(x_for(i) + boundary.x_for(0)),
|
49
|
+
(y_for(i) + boundary.y_for(0)),
|
50
|
+
width_for(i),
|
51
|
+
height_for(i)])
|
46
52
|
end
|
47
|
-
b.complete =
|
53
|
+
b.complete = complete
|
48
54
|
b
|
49
55
|
end
|
50
56
|
|
@@ -84,25 +90,24 @@ class Sqed::Boundaries
|
|
84
90
|
end
|
85
91
|
|
86
92
|
def populated?
|
87
|
-
|
93
|
+
each do |index, coords|
|
88
94
|
coords.each do |c|
|
89
95
|
return false if c.nil?
|
90
96
|
end
|
91
97
|
end
|
92
|
-
true
|
98
|
+
true
|
93
99
|
end
|
94
100
|
|
95
101
|
def zoom(width_factor, height_factor)
|
96
102
|
coordinates.keys.each do |i|
|
97
|
-
set(i, [
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
103
|
+
set(i, [
|
104
|
+
(x_for(i).to_f * width_factor).to_i,
|
105
|
+
(y_for(i).to_f * height_factor).to_i,
|
106
|
+
(width_for(i).to_f * width_factor).to_i,
|
107
|
+
(height_for(i).to_f * height_factor).to_i
|
102
108
|
])
|
103
|
-
|
104
|
-
end
|
109
|
+
end
|
105
110
|
end
|
106
111
|
|
107
|
-
|
112
|
+
end
|
108
113
|
end
|