sqed 0.5.8 → 0.7.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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +4 -2
- data/lib/sqed/boundaries.rb +1 -1
- data/lib/sqed/boundary_finder/color_line_finder.rb +124 -39
- data/lib/sqed/boundary_finder.rb +13 -10
- data/lib/sqed/error.rb +2 -0
- data/lib/sqed/extractor.rb +3 -3
- data/lib/sqed/parser.rb +2 -1
- data/lib/sqed/version.rb +1 -1
- data/lib/sqed.rb +11 -6
- data/lib/sqed_config.rb +53 -15
- data/spec/lib/sqed_config_spec.rb +2 -4
- data/spec/lib/sqed_spec.rb +8 -10
- data/spec/lib/stage_handling/horizontal_offset_cross_spec.rb +3 -1
- data/spec/lib/stage_handling/inverted_t_stage_spec.rb +61 -0
- data/spec/lib/stage_handling/lep_stage2_spec.rb +64 -0
- data/spec/lib/stage_handling/seven_slot_spec.rb +91 -16
- data/spec/lib/stage_handling/t_stage_spec.rb +61 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/files/stage_images/inhs_7_slot3.jpg +0 -0
- data/spec/support/files/stage_images/inverted_t_stage.png +0 -0
- data/spec/support/files/stage_images/lep_stage2.jpg +0 -0
- data/spec/support/files/stage_images/lep_stage3.jpg +0 -0
- data/spec/support/files/stage_images/t_stage.png +0 -0
- data/spec/support/image_helpers.rb +25 -8
- data/sqed.gemspec +4 -4
- metadata +32 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a10699c845a53421c6ea0446eac442be0470448d0c55c876bb849c2ec4a928e
|
4
|
+
data.tar.gz: b15da56bbbd2146e8683c7c19bcf3df8712c9bdb43c4015f173992e111b52520
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f7b7798e7e21145b3d46da2f68ff8b5bd83f341a7926d894d3f7253e5551eafa8297a26bd5fb0053707acca91c2fec2db42638bc27b54c13d58986b8ab6a051
|
7
|
+
data.tar.gz: b5ba7f2552f5db764f9436c77b874e7c094369d554a6df44ac15342a40177a7a80531fe1c3d87d2a34b725deeef21adec53fab1a311e0de7bf55e4a3ced64194
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3.0.2
|
data/.travis.yml
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
language: ruby
|
4
4
|
dist: bionic
|
5
5
|
rvm:
|
6
|
-
- 2.
|
6
|
+
- 2.7.1
|
7
7
|
#before_install:
|
8
8
|
# - sudo add-apt-repository -y ppa:moti-p/cc
|
9
9
|
# - sudo apt-get update
|
@@ -12,9 +12,11 @@ rvm:
|
|
12
12
|
before_install:
|
13
13
|
- sudo apt-get update -qq
|
14
14
|
- sudo apt-get install -qq tesseract-ocr tesseract-ocr-nld
|
15
|
+
- sudo sed -i 's/name="disk" value="1GiB"/name="disk" value="8GiB"/' /etc/ImageMagick-6/policy.xml
|
16
|
+
- identify -list resource | grep Disk | grep 8GiB # Check ImageMagick setting is actually working.
|
15
17
|
branches:
|
16
18
|
only:
|
17
|
-
-
|
19
|
+
- main
|
18
20
|
notifications:
|
19
21
|
email:
|
20
22
|
- diapriid@gmail.com
|
data/lib/sqed/boundaries.rb
CHANGED
@@ -24,7 +24,7 @@ class Sqed
|
|
24
24
|
attr_accessor :complete
|
25
25
|
|
26
26
|
def initialize(layout = nil)
|
27
|
-
raise 'unrecognized layout' if layout && !SqedConfig::LAYOUTS.include?(layout)
|
27
|
+
raise Sqed::Error, 'unrecognized layout' if layout && !SqedConfig::LAYOUTS.include?(layout)
|
28
28
|
@complete = false
|
29
29
|
|
30
30
|
@layout = layout
|
@@ -15,7 +15,7 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
15
15
|
|
16
16
|
super(image: image, layout: layout, use_thumbnail: use_thumbnail)
|
17
17
|
|
18
|
-
raise 'No layout provided.' if @layout.nil?
|
18
|
+
raise Sqed::Error, 'No layout provided.' if @layout.nil?
|
19
19
|
|
20
20
|
# !@#? why this
|
21
21
|
@boundary_color = boundary_color
|
@@ -29,34 +29,39 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
|
+
# boundaries.coordinates are referenced from stage image
|
33
|
+
#
|
34
|
+
# A reminder that boundaries are x1,y1, width, height
|
32
35
|
def find_bands
|
33
|
-
case layout # boundaries.coordinates are referenced from stage image
|
34
36
|
|
35
|
-
|
37
|
+
# see config/sqed_config.rb
|
38
|
+
case layout
|
39
|
+
|
40
|
+
when :cross # 4 windows, with perfectly intersected horizontal and vertical division
|
36
41
|
v = self.class.new(image: @image, layout: :vertical_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
37
|
-
h = self.class.new(image: @image, layout: :horizontal_split, boundary_color: boundary_color, use_thumbnail: false).boundaries
|
42
|
+
h = self.class.new(image: @image, layout: :horizontal_split, boundary_color: boundary_color, use_thumbnail: false).boundaries
|
38
43
|
|
39
44
|
return if v.nil? || h.nil?
|
40
45
|
|
41
|
-
boundaries.set(0, [0,0, v.width_for(0), h.height_for(0) ])
|
42
|
-
boundaries.set(1, [ v.x_for(1), 0, v.width_for(1), h.height_for(0) ])
|
43
|
-
boundaries.set(2, [ v.x_for(1), h.y_for(1), v.width_for(1), h.height_for(1) ])
|
44
|
-
boundaries.set(3, [0, h.y_for(1), v.width_for(0), h.height_for(1) ])
|
46
|
+
boundaries.set(0, [0,0, v.width_for(0), h.height_for(0) ])
|
47
|
+
boundaries.set(1, [ v.x_for(1), 0, v.width_for(1), h.height_for(0) ])
|
48
|
+
boundaries.set(2, [ v.x_for(1), h.y_for(1), v.width_for(1), h.height_for(1) ])
|
49
|
+
boundaries.set(3, [0, h.y_for(1), v.width_for(0), h.height_for(1) ])
|
45
50
|
|
46
51
|
# No specs for this yet
|
47
52
|
when :horizontal_offset_cross
|
48
53
|
horizontal = self.class.new(image: @image, layout: :horizontal_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
49
54
|
|
50
|
-
itop = image.crop(*horizontal.for(0), true)
|
55
|
+
itop = image.crop(*horizontal.for(0), true)
|
51
56
|
ibottom = image.crop(*horizontal.for(1), true)
|
52
57
|
|
53
58
|
top = self.class.new(image: itop, layout: :vertical_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
54
59
|
bottom = self.class.new(image: ibottom, layout: :vertical_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
55
60
|
|
56
|
-
boundaries.set(0, [0, 0, top.width_for(0), top.height_for(0) ])
|
57
|
-
boundaries.set(1, [top.x_for(1), 0, top.width_for(1), top.height_for(1) ])
|
58
|
-
boundaries.set(2, [bottom.x_for(1), horizontal.y_for(1), bottom.width_for(1), bottom.height_for(1) ])
|
59
|
-
boundaries.set(3, [0, horizontal.y_for(1), bottom.width_for(0), bottom.height_for(0) ])
|
61
|
+
boundaries.set(0, [0, 0, top.width_for(0), top.height_for(0) ])
|
62
|
+
boundaries.set(1, [top.x_for(1), 0, top.width_for(1), top.height_for(1) ])
|
63
|
+
boundaries.set(2, [bottom.x_for(1), horizontal.y_for(1), bottom.width_for(1), bottom.height_for(1) ])
|
64
|
+
boundaries.set(3, [0, horizontal.y_for(1), bottom.width_for(0), bottom.height_for(0) ])
|
60
65
|
|
61
66
|
when :lep_stage
|
62
67
|
top_bottom_split = Sqed::BoundaryFinder.color_boundary_finder(image: image, scan: :columns, boundary_color: boundary_color) # detect vertical division [array]
|
@@ -64,9 +69,9 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
64
69
|
|
65
70
|
boundaries.set(6, [0, top_bottom_split[2], left_right_split[0], image.rows - top_bottom_split[2]] )
|
66
71
|
|
67
|
-
left_top_image = image.crop( 0, 0, left_right_split[0], top_bottom_split[0], true)
|
72
|
+
left_top_image = image.crop( 0, 0, left_right_split[0], top_bottom_split[0], true)
|
68
73
|
|
69
|
-
left_top_split =
|
74
|
+
left_top_split =
|
70
75
|
SqedUtils.corrected_frequency(
|
71
76
|
Sqed::BoundaryFinder.color_boundary_finder(image: left_top_image, boundary_color: boundary_color),
|
72
77
|
max_width: left_top_image.columns,
|
@@ -74,8 +79,8 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
74
79
|
)
|
75
80
|
|
76
81
|
boundaries.set(0, [0, 0, left_top_split[1], top_bottom_split[0]] ) # keep as 1 for safety
|
77
|
-
|
78
|
-
boundaries.set(1, [left_top_split[2], 0, left_top_image.columns - left_top_split[2], top_bottom_split[0]] )
|
82
|
+
|
83
|
+
boundaries.set(1, [left_top_split[2], 0, left_top_image.columns - left_top_split[2], top_bottom_split[0]] )
|
79
84
|
boundaries.set(2, [left_right_split[2], 0, image.columns - left_right_split[0], top_bottom_split[0]] )
|
80
85
|
|
81
86
|
bottom_right_image = image.crop(left_right_split[2], top_bottom_split[2], image.columns - left_right_split[2], image.rows - top_bottom_split[2], true)
|
@@ -89,7 +94,7 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
89
94
|
|
90
95
|
boundaries.set(3, [left_right_split[2] + bottom_right_split[2], top_bottom_split[2], image.columns - bottom_right_split[2], image.rows - top_bottom_split[2] ] )
|
91
96
|
|
92
|
-
bottom_right_left_image = image.crop(left_right_split[2], top_bottom_split[2], bottom_right_split[0], image.rows - top_bottom_split[2], true)
|
97
|
+
bottom_right_left_image = image.crop(left_right_split[2], top_bottom_split[2], bottom_right_split[0], image.rows - top_bottom_split[2], true)
|
93
98
|
|
94
99
|
bottom_right_left_split =
|
95
100
|
SqedUtils.corrected_frequency(
|
@@ -101,25 +106,104 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
101
106
|
boundaries.set(4, [left_right_split[2], top_bottom_split[2], bottom_right_image.columns - bottom_right_left_image.columns, bottom_right_left_split[0] ] )
|
102
107
|
|
103
108
|
boundaries.set(5, [
|
104
|
-
left_right_split[2],
|
109
|
+
left_right_split[2],
|
105
110
|
top_bottom_split[2] + bottom_right_left_split[2],
|
106
111
|
bottom_right_image.columns - bottom_right_left_image.columns,
|
107
|
-
bottom_right_left_image.rows - bottom_right_left_split[2]
|
112
|
+
bottom_right_left_image.rows - bottom_right_left_split[2]
|
108
113
|
])
|
109
114
|
|
115
|
+
when :lep_stage2
|
116
|
+
top_bottom_split = Sqed::BoundaryFinder.color_boundary_finder(image: image, scan: :columns, boundary_color: boundary_color) # detect vertical division [array]
|
117
|
+
left_right_split = Sqed::BoundaryFinder.color_boundary_finder(image: image, sample_subdivision_size: 2, boundary_color: boundary_color) # detect horizontal division [array]
|
118
|
+
|
119
|
+
boundaries.set(6, [0, top_bottom_split[2], left_right_split[0], image.rows - top_bottom_split[2]] ) # OK
|
120
|
+
|
121
|
+
left_top_image = image.crop( 0, 0, left_right_split[0], top_bottom_split[0], true)
|
122
|
+
|
123
|
+
left_top_split =
|
124
|
+
SqedUtils.corrected_frequency(
|
125
|
+
Sqed::BoundaryFinder.color_boundary_finder(image: left_top_image, boundary_color: boundary_color),
|
126
|
+
max_width: left_top_image.columns,
|
127
|
+
width_factor: 1.8
|
128
|
+
)
|
129
|
+
|
130
|
+
boundaries.set(0, [0, 0, left_top_split[1], top_bottom_split[0]] ) # OK
|
131
|
+
|
132
|
+
boundaries.set(1, [left_top_split[2], 0, left_top_image.columns - left_top_split[2], top_bottom_split[0]] )
|
133
|
+
boundaries.set(2, [left_right_split[2], 0, image.columns - left_right_split[0], top_bottom_split[0]] )
|
134
|
+
|
135
|
+
bottom_right_image = image.crop(left_right_split[2], top_bottom_split[2], image.columns - left_right_split[2], image.rows - top_bottom_split[2], true)
|
136
|
+
|
137
|
+
bottom_right_split =
|
138
|
+
SqedUtils.corrected_frequency(
|
139
|
+
Sqed::BoundaryFinder.color_boundary_finder(image: bottom_right_image, boundary_color: boundary_color, scan: :rows),
|
140
|
+
max_width: bottom_right_image.columns,
|
141
|
+
width_factor: 1.8
|
142
|
+
)
|
143
|
+
|
144
|
+
boundaries.set(3, [
|
145
|
+
left_right_split[2] + bottom_right_split[2],
|
146
|
+
top_bottom_split[2],
|
147
|
+
image.columns - bottom_right_split[2],
|
148
|
+
top_bottom_split[0] ])
|
149
|
+
|
150
|
+
bottom_right_left_image = image.crop(left_right_split[2], top_bottom_split[2], bottom_right_split[0], image.rows - top_bottom_split[2], true)
|
151
|
+
|
152
|
+
bottom_right_left_top_bottom_split =
|
153
|
+
SqedUtils.corrected_frequency(
|
154
|
+
Sqed::BoundaryFinder.color_boundary_finder(image: bottom_right_left_image, scan: :columns, boundary_color: boundary_color),
|
155
|
+
max_width: bottom_right_left_image.columns,
|
156
|
+
width_factor: 1.8
|
157
|
+
)
|
158
|
+
|
159
|
+
boundaries.set(4, [
|
160
|
+
left_right_split[2],
|
161
|
+
top_bottom_split[2] + bottom_right_left_top_bottom_split[2],
|
162
|
+
bottom_right_left_image.columns,
|
163
|
+
bottom_right_left_top_bottom_split[2]
|
164
|
+
])
|
165
|
+
|
166
|
+
|
167
|
+
boundaries.set(5, [
|
168
|
+
left_right_split[2],
|
169
|
+
top_bottom_split[2],
|
170
|
+
bottom_right_left_image.columns,
|
171
|
+
bottom_right_left_top_bottom_split[0],
|
172
|
+
])
|
173
|
+
|
174
|
+
when :t
|
175
|
+
horizontal = self.class.new(image: @image, layout: :horizontal_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
176
|
+
|
177
|
+
bottom = image.crop(*horizontal.for(1), true)
|
178
|
+
btm_split = self.class.new(image: bottom, layout: :vertical_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
179
|
+
|
180
|
+
boundaries.set(0, horizontal.for(0))
|
181
|
+
boundaries.set(1, [ btm_split.x_for(1), horizontal.height_for(0), btm_split.width_for(1), btm_split.height_for(1) ] )
|
182
|
+
boundaries.set(2, [ 0, horizontal.height_for(0), btm_split.width_for(0), btm_split.height_for(0) ] )
|
183
|
+
|
184
|
+
when :inverted_t
|
185
|
+
horizontal = self.class.new(image: @image, layout: :horizontal_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
186
|
+
|
187
|
+
top = image.crop(*horizontal.for(0), true)
|
188
|
+
top_split = self.class.new(image: top, layout: :vertical_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
189
|
+
|
190
|
+
boundaries.set(0, [ 0,0, top_split.width_for(0), top_split.height_for(0)] )
|
191
|
+
boundaries.set(1, [ top_split.width_for(0), 0, top_split.width_for(1), top_split.height_for(1)] )
|
192
|
+
boundaries.set(2, horizontal.for(1))
|
193
|
+
|
110
194
|
when :right_t
|
111
195
|
vertical = self.class.new(image: @image, layout: :vertical_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
112
196
|
|
113
197
|
irt = image.crop(*vertical.for(1), true)
|
114
198
|
right = self.class.new(image: irt, layout: :horizontal_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries
|
115
199
|
|
116
|
-
boundaries.set(0, vertical.for(0))
|
117
|
-
boundaries.set(1, [ vertical.x_for(1), 0, right.width_for(0), right.height_for(0) ] )
|
118
|
-
boundaries.set(2, [ vertical.x_for(1), right.y_for(1), right.width_for(1), right.height_for(1)] )
|
200
|
+
boundaries.set(0, vertical.for(0))
|
201
|
+
boundaries.set(1, [ vertical.x_for(1), 0, right.width_for(0), right.height_for(0) ] )
|
202
|
+
boundaries.set(2, [ vertical.x_for(1), right.y_for(1), right.width_for(1), right.height_for(1)] )
|
119
203
|
|
120
204
|
when :seven_slot
|
121
205
|
top_bottom_split = Sqed::BoundaryFinder.color_boundary_finder(image: image, scan: :columns, boundary_color: boundary_color) # detect vertical division [array]
|
122
|
-
left_right_split = Sqed::BoundaryFinder.color_boundary_finder(image: image, sample_subdivision_size:
|
206
|
+
left_right_split = Sqed::BoundaryFinder.color_boundary_finder(image: image, sample_subdivision_size: 1, boundary_color: boundary_color) # detect horizontal division [array]
|
123
207
|
|
124
208
|
boundaries.set(0, [0, 0, left_right_split[0], top_bottom_split[0]])
|
125
209
|
boundaries.set(6, [0, top_bottom_split[2], left_right_split[0], image.rows - top_bottom_split[2]] )
|
@@ -128,18 +212,22 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
128
212
|
right_bottom_image = image.crop(left_right_split[2], top_bottom_split[2], image.columns - left_right_split[2], image.rows - top_bottom_split[2], true) # sections 3,4,5
|
129
213
|
|
130
214
|
right_top_split = ::SqedUtils.corrected_frequency(
|
131
|
-
Sqed::BoundaryFinder.color_boundary_finder(
|
132
|
-
|
133
|
-
|
215
|
+
Sqed::BoundaryFinder.color_boundary_finder(
|
216
|
+
image: right_top_image, boundary_color: boundary_color, scan: :rows),
|
217
|
+
width_factor: 1.8,
|
218
|
+
max_width: right_top_image.columns
|
134
219
|
) # vertical line b/w 1 & 2, use "corrected_frequency" to account for color bleed from previous crop
|
135
220
|
|
136
221
|
boundaries.set(1, [left_right_split[2], 0, right_top_split[0], top_bottom_split[0]] )
|
137
222
|
boundaries.set(2, [left_right_split[2] + right_top_split[2], 0, right_top_image.columns - right_top_split[2], top_bottom_split[0]])
|
138
223
|
|
139
224
|
right_bottom_split = SqedUtils.corrected_frequency(
|
140
|
-
Sqed::BoundaryFinder.color_boundary_finder(
|
141
|
-
|
142
|
-
|
225
|
+
Sqed::BoundaryFinder.color_boundary_finder(
|
226
|
+
image: right_bottom_image,
|
227
|
+
scan: :columns,
|
228
|
+
sample_subdivision_size: 2, boundary_color: boundary_color),
|
229
|
+
width_factor: 1.8,
|
230
|
+
max_width: right_bottom_image.rows) # horizontal line b/w (5,3) & 4, use "corrected_frequency" to account for color bleed from previous crop
|
143
231
|
|
144
232
|
bottom_right_top_image = right_bottom_image.crop(0,0, image.columns - left_right_split[2], right_bottom_split[0], true) # 3,5
|
145
233
|
|
@@ -151,18 +239,18 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
151
239
|
when :vertical_offset_cross # 4 zones expected, with (varying) horizontal division in left- and right- sides of vertical division
|
152
240
|
vertical = self.class.new(image: @image, layout: :vertical_split, boundary_color: boundary_color, use_thumbnail: false).boundaries
|
153
241
|
|
154
|
-
ilt = image.crop(*vertical.for(0), true)
|
242
|
+
ilt = image.crop(*vertical.for(0), true)
|
155
243
|
irt = image.crop(*vertical.for(1), true)
|
156
244
|
|
157
245
|
left = self.class.new(image: ilt, layout: :horizontal_split, boundary_color: boundary_color, use_thumbnail: false).boundaries # fails
|
158
246
|
right = self.class.new(image: irt, layout: :horizontal_split, boundary_color: boundary_color, use_thumbnail: false ).boundaries # OK
|
159
247
|
|
160
|
-
boundaries.set(0, [0, 0, left.width_for(0), left.height_for(0) ])
|
161
|
-
boundaries.set(1, [vertical.x_for(1), 0, right.width_for(0), right.height_for(0) ])
|
162
|
-
boundaries.set(2, [vertical.x_for(1), right.y_for(1), right.width_for(1), right.height_for(1) ])
|
163
|
-
boundaries.set(3, [0, left.y_for(1), left.width_for(1), left.height_for(1) ])
|
248
|
+
boundaries.set(0, [0, 0, left.width_for(0), left.height_for(0) ])
|
249
|
+
boundaries.set(1, [vertical.x_for(1), 0, right.width_for(0), right.height_for(0) ])
|
250
|
+
boundaries.set(2, [vertical.x_for(1), right.y_for(1), right.width_for(1), right.height_for(1) ])
|
251
|
+
boundaries.set(3, [0, left.y_for(1), left.width_for(1), left.height_for(1) ])
|
164
252
|
|
165
|
-
when :vertical_split
|
253
|
+
when :vertical_split
|
166
254
|
t = Sqed::BoundaryFinder.color_boundary_finder(image: image, boundary_color: boundary_color) #detect vertical division
|
167
255
|
return if t.nil?
|
168
256
|
boundaries.set(0, [0, 0, t[0], image.rows]) # left section of image
|
@@ -175,10 +263,7 @@ class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
|
175
263
|
boundaries.set(0, [0, 0, image.columns, t[0]]) # upper section of image
|
176
264
|
boundaries.set(1, [0, t[2], image.columns, image.rows - t[2]]) # lower section of image
|
177
265
|
|
178
|
-
|
179
|
-
|
180
266
|
else # no @layout provided !?
|
181
|
-
|
182
267
|
boundaries.set(0, [0, 0, image.columns, image.rows]) # totality of image as default
|
183
268
|
end
|
184
269
|
|
data/lib/sqed/boundary_finder.rb
CHANGED
@@ -6,6 +6,7 @@ class Sqed
|
|
6
6
|
#
|
7
7
|
class BoundaryFinder
|
8
8
|
|
9
|
+
# Problemantic (e.g. seven slot) seem to resolve at ~360
|
9
10
|
THUMB_SIZE = 100
|
10
11
|
COLOR_DELTA = 1.3 # color (e.g. red) must be this much be *COLOR_DELTA > than other values (e.g. blue/green)
|
11
12
|
|
@@ -18,7 +19,8 @@ class Sqed
|
|
18
19
|
# A Sqed::Boundaries instance, stores the coordinates of all of the layout sections
|
19
20
|
attr_reader :boundaries
|
20
21
|
|
21
|
-
#
|
22
|
+
# @return Boolean
|
23
|
+
# Whether to compress the original image to a thumbnail when finding boundaries at certain steps of the processing
|
22
24
|
attr_reader :use_thumbnail
|
23
25
|
|
24
26
|
# when we compute using a derived thumbnail we temporarily store the full size image here
|
@@ -31,8 +33,8 @@ class Sqed
|
|
31
33
|
@layout = opts[:layout]
|
32
34
|
@image = opts[:image]
|
33
35
|
|
34
|
-
raise 'No layout provided.' if layout.nil?
|
35
|
-
raise 'No image provided.' if image.nil? || image.class.name != 'Magick::Image'
|
36
|
+
raise Sqed::Error, 'No layout provided.' if layout.nil?
|
37
|
+
raise Sqed::Error, 'No image provided.' if image.nil? || image.class.name != 'Magick::Image'
|
36
38
|
|
37
39
|
true
|
38
40
|
end
|
@@ -135,7 +137,7 @@ class Sqed
|
|
135
137
|
while attempts < 5 do
|
136
138
|
samples_to_take = (image_width / sample_subdivision_size).to_i - 1
|
137
139
|
border_hits = sample_border(image, boundary_color, samples_to_take, sample_subdivision_size, scan)
|
138
|
-
|
140
|
+
|
139
141
|
break if border_hits.select{|k,v| v > 1}.size > 2 || sample_subdivision_size == 1
|
140
142
|
|
141
143
|
sample_subdivision_size = (sample_subdivision_size.to_f / 2.0).to_i
|
@@ -164,7 +166,7 @@ class Sqed
|
|
164
166
|
elsif scan == :columns
|
165
167
|
j = image.crop(s * sample_subdivision_size, 0, 1, image.rows, true)
|
166
168
|
else
|
167
|
-
raise
|
169
|
+
raise Sqed::Error
|
168
170
|
end
|
169
171
|
|
170
172
|
j.each_pixel do |pixel, c, r|
|
@@ -207,7 +209,7 @@ class Sqed
|
|
207
209
|
# the start, mid, endpoint position of all (pixel) positions that have a count greater than the cutoff
|
208
210
|
def self.frequency_stats(frequency_hash, sample_cutoff = 0)
|
209
211
|
|
210
|
-
return nil if sample_cutoff.nil? ||
|
212
|
+
return nil if sample_cutoff.nil? || sample_cutoff < 1
|
211
213
|
hit_ranges = []
|
212
214
|
|
213
215
|
frequency_hash.each do |position, count|
|
@@ -236,6 +238,11 @@ class Sqed
|
|
236
238
|
[hit_ranges.first, hit_ranges[(hit_ranges.length / 2).to_i], hit_ranges.last]
|
237
239
|
end
|
238
240
|
|
241
|
+
def self.max_difference(array)
|
242
|
+
array.max - array.min
|
243
|
+
end
|
244
|
+
|
245
|
+
# Usused
|
239
246
|
|
240
247
|
# Returns an Integer, the maximum of the pairwise differences of the values in the array
|
241
248
|
# For example, given
|
@@ -248,10 +255,6 @@ class Sqed
|
|
248
255
|
(0..array.length - 2).map{|i| (array[i] - array[i + 1]).abs }.max
|
249
256
|
end
|
250
257
|
|
251
|
-
def self.max_difference(array)
|
252
|
-
array.max - array.min
|
253
|
-
end
|
254
|
-
|
255
258
|
def self.derivative_signs(array)
|
256
259
|
(0..array.length - 2).map { |i| (array[i + 1] - array[i]) <=> 0 }
|
257
260
|
end
|
data/lib/sqed/error.rb
ADDED
data/lib/sqed/extractor.rb
CHANGED
@@ -23,9 +23,9 @@ class Sqed
|
|
23
23
|
@boundaries = opts[:boundaries]
|
24
24
|
@image = opts[:image]
|
25
25
|
|
26
|
-
raise Error, 'boundaries not provided or provided boundary is not a Sqed::Boundaries' if boundaries.nil? || !boundaries.class == Sqed::Boundaries
|
27
|
-
raise Error, 'metadata_map not provided or metadata_map not a Hash' if metadata_map.nil? || !metadata_map.class == Hash
|
28
|
-
raise Error, 'image not provided' if image.nil? || !image.class.name == 'Magick::Image'
|
26
|
+
raise Sqed::Error, 'boundaries not provided or provided boundary is not a Sqed::Boundaries' if boundaries.nil? || !boundaries.class.name == 'Sqed::Boundaries'
|
27
|
+
raise Sqed::Error, 'metadata_map not provided or metadata_map not a Hash' if metadata_map.nil? || !metadata_map.class.name == 'Hash'
|
28
|
+
raise Sqed::Error, 'image not provided' if image.nil? || !image.class.name == 'Magick::Image'
|
29
29
|
end
|
30
30
|
|
31
31
|
def result
|
data/lib/sqed/parser.rb
CHANGED
@@ -9,8 +9,9 @@ class Sqed::Parser
|
|
9
9
|
attr_accessor :extracted_text
|
10
10
|
|
11
11
|
def initialize(image)
|
12
|
+
raise Sqed::Error, 'no image passed to parser' if image.nil?
|
13
|
+
raise Sqed::Error, 'image is not a Magick::Image' if !(image.class.name == 'Magick::Image')
|
12
14
|
@image = image
|
13
|
-
raise 'no image provided to parser' if @image && !(@image.class.name == 'Magick::Image')
|
14
15
|
end
|
15
16
|
|
16
17
|
end
|
data/lib/sqed/version.rb
CHANGED
data/lib/sqed.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
3
|
recent_ruby = RUBY_VERSION >= '2.4.1'
|
4
|
-
raise 'IMPORTANT: sqed gem requires ruby >= 2.4.1' unless recent_ruby
|
4
|
+
raise Sqed::Error, 'IMPORTANT: sqed gem requires ruby >= 2.4.1' unless recent_ruby
|
5
5
|
|
6
6
|
require 'rmagick'
|
7
7
|
require 'sqed_utils'
|
@@ -17,10 +17,12 @@ require 'sqed_utils'
|
|
17
17
|
#
|
18
18
|
class Sqed
|
19
19
|
|
20
|
+
require_relative 'sqed/error'
|
20
21
|
require_relative 'sqed_config'
|
21
22
|
require_relative 'sqed/extractor'
|
22
23
|
require_relative 'sqed/result'
|
23
24
|
|
25
|
+
|
24
26
|
# initial image which is an instance of ImageMagick::Image, containing background and stage, or just stage
|
25
27
|
attr_accessor :image
|
26
28
|
|
@@ -68,6 +70,7 @@ class Sqed
|
|
68
70
|
# Provide a metadata map, overrides metadata taken from pattern
|
69
71
|
attr_accessor :metadata_map
|
70
72
|
|
73
|
+
# @return [Sqed::BoundaryFinder::<Klass>]
|
71
74
|
# Provide a boundary_finder, overrides metadata taken from pattern
|
72
75
|
attr_accessor :boundary_finder
|
73
76
|
|
@@ -165,6 +168,8 @@ class Sqed
|
|
165
168
|
configure_layout(opts)
|
166
169
|
configure_metadata_map(opts)
|
167
170
|
|
171
|
+
raise Sqed::Error, 'boundary_finder not provided' if @boundary_finder.nil?
|
172
|
+
|
168
173
|
@has_border = opts[:has_border]
|
169
174
|
@has_border = true if @has_border.nil?
|
170
175
|
|
@@ -178,18 +183,18 @@ class Sqed
|
|
178
183
|
def configure_from_pattern(value)
|
179
184
|
return if value.nil?
|
180
185
|
value = value.to_sym
|
181
|
-
raise "provided extraction pattern '#{value}' not defined" if !SqedConfig::EXTRACTION_PATTERNS.keys.include?(value)
|
186
|
+
raise Sqed::Error, "provided extraction pattern '#{value}' not defined" if !SqedConfig::EXTRACTION_PATTERNS.keys.include?(value)
|
182
187
|
@pattern = value
|
183
188
|
a = SqedConfig::EXTRACTION_PATTERNS[pattern]
|
184
189
|
@boundary_finder = a[:boundary_finder]
|
185
190
|
@layout = a[:layout]
|
186
191
|
@metadata_map = a[:metadata_map]
|
192
|
+
|
187
193
|
true
|
188
194
|
end
|
189
195
|
|
190
196
|
def configure_boundary_finder(opts)
|
191
|
-
@boundary_finder = opts[:boundary_finder]
|
192
|
-
@boundary_finder ||= Sqed::BoundaryFinder::CrossFinder
|
197
|
+
@boundary_finder = opts[:boundary_finder] if !opts[:boundary_finder].nil?
|
193
198
|
end
|
194
199
|
|
195
200
|
def configure_layout(opts)
|
@@ -212,7 +217,7 @@ class Sqed
|
|
212
217
|
end
|
213
218
|
|
214
219
|
def get_section_boundaries
|
215
|
-
boundary_finder.new(section_params).boundaries
|
220
|
+
boundary_finder.new(**section_params).boundaries
|
216
221
|
end
|
217
222
|
|
218
223
|
# @return [Hash]
|
@@ -232,7 +237,7 @@ class Sqed
|
|
232
237
|
if boundary.populated?
|
233
238
|
@stage_boundary.set(0, boundary.for(0))
|
234
239
|
else
|
235
|
-
raise 'error detecting stage'
|
240
|
+
raise Sqed::Error, 'error detecting stage'
|
236
241
|
end
|
237
242
|
else
|
238
243
|
@stage_boundary.set(0, [0, 0, image.columns, image.rows])
|
data/lib/sqed_config.rb
CHANGED
@@ -17,33 +17,40 @@ require_relative 'sqed/boundary_finder/color_line_finder'
|
|
17
17
|
module SqedConfig
|
18
18
|
|
19
19
|
# Layouts refer to the arrangement of the divided stage.
|
20
|
-
# Windows are enumerated from the top left, moving around the border
|
20
|
+
# Windows are enumerated from the top left, moving around the border
|
21
21
|
# in a clockwise position. For example:
|
22
22
|
#
|
23
23
|
# 0 | 1
|
24
24
|
# ----|---- :cross (any cross pattern)
|
25
25
|
# |
|
26
26
|
# 3 | 2
|
27
|
-
#
|
27
|
+
#
|
28
28
|
#
|
29
29
|
# 0 | 1
|
30
30
|
# |
|
31
|
-
# --------- :horizontal_offset_cross
|
31
|
+
# --------- :horizontal_offset_cross
|
32
32
|
# 3 | 2
|
33
|
-
#
|
33
|
+
#
|
34
34
|
#
|
35
35
|
# 0
|
36
36
|
# -------- :horizontal_split
|
37
37
|
# 1
|
38
|
-
#
|
38
|
+
#
|
39
39
|
#
|
40
40
|
# 0 | 1 | 2
|
41
41
|
# ------------
|
42
42
|
# | 5 | :lep_stage
|
43
|
-
# 6 |---- 3
|
43
|
+
# 6 |---- 3
|
44
44
|
# | 4 |
|
45
45
|
#
|
46
46
|
#
|
47
|
+
# 0 | 1 | 2
|
48
|
+
# --------------
|
49
|
+
# | 5 | :lep_stage2
|
50
|
+
# 6 |---- 3
|
51
|
+
# | 4 |
|
52
|
+
#
|
53
|
+
#
|
47
54
|
# | 1
|
48
55
|
# 0 |---- :right_t
|
49
56
|
# | 2
|
@@ -55,28 +62,41 @@ module SqedConfig
|
|
55
62
|
# 6 |--------
|
56
63
|
# | 4
|
57
64
|
#
|
58
|
-
#
|
65
|
+
#
|
59
66
|
# 0 | 1
|
60
67
|
# |____
|
61
|
-
# ----| :vertical_offset_cross
|
68
|
+
# ----| :vertical_offset_cross
|
62
69
|
# 3 | 2
|
63
|
-
#
|
64
70
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
71
|
+
#
|
72
|
+
# |
|
73
|
+
# 0 | 1 :vertical_split
|
74
|
+
# |
|
68
75
|
#
|
69
76
|
# -----
|
70
|
-
# | 0 |
|
77
|
+
# | 0 | :internal_box
|
71
78
|
# -----
|
72
|
-
#
|
79
|
+
#
|
80
|
+
# 0 | 1 :inverted_t
|
81
|
+
# -----
|
82
|
+
# 2
|
83
|
+
#
|
84
|
+
# 0 :t
|
85
|
+
# -----
|
86
|
+
# 2 | 1
|
87
|
+
#
|
88
|
+
#
|
89
|
+
#
|
73
90
|
# Hash values are used to stub out
|
74
91
|
# the Sqed::Boundaries instance.
|
75
92
|
LAYOUTS = {
|
93
|
+
t: [0,1,2],
|
94
|
+
inverted_t: [0, 1, 2],
|
76
95
|
cross: [0, 1, 2, 3],
|
77
96
|
horizontal_offset_cross: [0, 1, 2, 3],
|
78
97
|
horizontal_split: [0, 1],
|
79
98
|
lep_stage: [0, 1, 2, 3, 4, 5, 6],
|
99
|
+
lep_stage2: [0, 1, 2, 3, 4, 5, 6],
|
80
100
|
right_t: [0, 1, 2],
|
81
101
|
seven_slot: [0, 1, 2, 3, 4, 5, 6],
|
82
102
|
vertical_offset_cross: [0, 1, 2, 3],
|
@@ -93,7 +113,7 @@ module SqedConfig
|
|
93
113
|
:identifier, # the section contains an identifier (e.g. barcode or unique number)
|
94
114
|
:image_registration, # the section contains only image registration information,
|
95
115
|
:labels, # the section contains collecting event and other non-determination labels
|
96
|
-
:nothing, # section is empty
|
116
|
+
:nothing, # section is empty
|
97
117
|
:other_labels, # the section that contains text that misc.
|
98
118
|
:specimen, # the specimen only, no metadata should be present
|
99
119
|
:stage, # the image contains the full stage
|
@@ -115,6 +135,18 @@ module SqedConfig
|
|
115
135
|
}.freeze
|
116
136
|
|
117
137
|
EXTRACTION_PATTERNS = {
|
138
|
+
t: {
|
139
|
+
boundary_finder: Sqed::BoundaryFinder::ColorLineFinder,
|
140
|
+
layout: :t,
|
141
|
+
metadata_map: { 0 => :annotated_specimen, 1 => :identifier, 2 => :image_registration }
|
142
|
+
},
|
143
|
+
|
144
|
+
inverted_t: {
|
145
|
+
boundary_finder: Sqed::BoundaryFinder::ColorLineFinder,
|
146
|
+
layout: :inverted_t,
|
147
|
+
metadata_map: { 0 => :identifier, 1 => :image_registration, 2 => :annotated_specimen }
|
148
|
+
},
|
149
|
+
|
118
150
|
cross: {
|
119
151
|
boundary_finder: Sqed::BoundaryFinder::ColorLineFinder,
|
120
152
|
layout: :cross,
|
@@ -139,6 +171,12 @@ module SqedConfig
|
|
139
171
|
metadata_map: { 0 => :curator_metadata, 1 => :collecting_event_labels, 2 => :image_registration, 3 => :identifier, 4 => :other_labels, 5 => :determination_labels, 6 => :specimen }
|
140
172
|
},
|
141
173
|
|
174
|
+
lep_stage2: {
|
175
|
+
boundary_finder: Sqed::BoundaryFinder::ColorLineFinder,
|
176
|
+
layout: :lep_stage2,
|
177
|
+
metadata_map: { 0 => :curator_metadata, 1 => :collecting_event_labels, 2 => :image_registration, 3 => :identifier, 4 => :other_labels, 5 => :determination_labels, 6 => :specimen }
|
178
|
+
},
|
179
|
+
|
142
180
|
right_t: {
|
143
181
|
boundary_finder: Sqed::BoundaryFinder::ColorLineFinder,
|
144
182
|
layout: :right_t,
|
@@ -7,13 +7,11 @@ describe SqedConfig do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
specify 'layouts' do
|
10
|
-
expect(SqedConfig.metadata[:layouts].keys).to contain_exactly(:cross, :horizontal_offset_cross, :horizontal_split, :lep_stage, :right_t, :seven_slot, :vertical_offset_cross, :vertical_split)
|
10
|
+
expect(SqedConfig.metadata[:layouts].keys).to contain_exactly(:t, :inverted_t, :cross, :horizontal_offset_cross, :horizontal_split, :lep_stage, :lep_stage2, :right_t, :seven_slot, :vertical_offset_cross, :vertical_split)
|
11
11
|
end
|
12
12
|
|
13
13
|
specify 'layouts are in patterns' do
|
14
|
-
expect(SqedConfig.metadata[:layouts].keys + [:stage]).to contain_exactly(*SqedConfig::EXTRACTION_PATTERNS.keys)
|
14
|
+
expect(SqedConfig.metadata[:layouts].keys + [:stage]).to contain_exactly(*SqedConfig::EXTRACTION_PATTERNS.keys)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
17
|
end
|