sqed 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.
- checksums.yaml +7 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/.travis.yml +18 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +36 -0
- data/Rakefile +9 -0
- data/lib/sqed.rb +111 -0
- data/lib/sqed/boundaries.rb +79 -0
- data/lib/sqed/boundary_finder.rb +150 -0
- data/lib/sqed/boundary_finder/color_line_finder.rb +83 -0
- data/lib/sqed/boundary_finder/cross_finder.rb +23 -0
- data/lib/sqed/boundary_finder/stage_finder.rb +139 -0
- data/lib/sqed/extractor.rb +45 -0
- data/lib/sqed/parser.rb +11 -0
- data/lib/sqed/parser/barcode_parser.rb +27 -0
- data/lib/sqed/parser/ocr_parser.rb +52 -0
- data/lib/sqed/result.rb +15 -0
- data/lib/sqed/version.rb +3 -0
- data/lib/sqed_config.rb +112 -0
- data/spec/lib/sqed/boundaries_spec.rb +35 -0
- data/spec/lib/sqed/boundary_finder/color_line_finder_spec.rb +167 -0
- data/spec/lib/sqed/boundary_finder/cross_finder_spec.rb +28 -0
- data/spec/lib/sqed/boundary_finder/stage_finder_spec.rb +9 -0
- data/spec/lib/sqed/boundary_finder_spec.rb +108 -0
- data/spec/lib/sqed/extractor_spec.rb +82 -0
- data/spec/lib/sqed/parser_spec.rb +6 -0
- data/spec/lib/sqed/result_spec.rb +17 -0
- data/spec/lib/sqed_spec.rb +200 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/files/2Dbarcode.png +0 -0
- data/spec/support/files/CrossyBlackLinesSpecimen.jpg +0 -0
- data/spec/support/files/CrossyGreenLinesSpecimen.jpg +0 -0
- data/spec/support/files/Quadrant_2_3.jpg +0 -0
- data/spec/support/files/black_stage_green_line_specimen.jpg +0 -0
- data/spec/support/files/boundary_cross_green.jpg +0 -0
- data/spec/support/files/boundary_left_t_yellow.jpg +0 -0
- data/spec/support/files/boundary_offset_cross_red.jpg +0 -0
- data/spec/support/files/boundary_right_t_green.jpg +0 -0
- data/spec/support/files/greenlineimage.jpg +0 -0
- data/spec/support/files/label_images/black_stage_green_line_specimen_label.jpg +0 -0
- data/spec/support/files/test0.jpg +0 -0
- data/spec/support/files/test1.jpg +0 -0
- data/spec/support/files/test2.jpg +0 -0
- data/spec/support/files/test3.jpg +0 -0
- data/spec/support/files/test4.jpg +0 -0
- data/spec/support/files/test4OLD.jpg +0 -0
- data/spec/support/files/test_barcode.JPG +0 -0
- data/spec/support/files/test_ocr0.jpg +0 -0
- data/spec/support/files/types_21.jpg +0 -0
- data/spec/support/files/types_8.jpg +0 -0
- data/spec/support/image_helpers.rb +78 -0
- data/sqed.gemspec +31 -0
- metadata +244 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
|
3
|
+
# This was "green" line finder attempting to be agnostic; now it is reworked to be color-specific line finder
|
4
|
+
#
|
5
|
+
class Sqed::BoundaryFinder::ColorLineFinder < Sqed::BoundaryFinder
|
6
|
+
|
7
|
+
def initialize(image: image, layout: layout, boundary_color: :green)
|
8
|
+
super(image: image, layout: layout)
|
9
|
+
raise 'No layout provided.' if @layout.nil?
|
10
|
+
@boundary_color = boundary_color
|
11
|
+
find_bands
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def find_bands
|
17
|
+
case @layout # boundaries.coordinates are referenced from stage image
|
18
|
+
|
19
|
+
when :vertical_split # can vertical and horizontal split be re-used to do cross cases?
|
20
|
+
t = Sqed::BoundaryFinder.color_boundary_finder(image: img, boundary_color: @boundary_color) #detect vertical division, green line
|
21
|
+
return if t.nil?
|
22
|
+
boundaries.coordinates[0] = [0, 0, t[0], img.rows] # left section of image
|
23
|
+
boundaries.coordinates[1] = [t[2], 0, img.columns - t[2], img.rows] # right section of image
|
24
|
+
boundaries.complete = true
|
25
|
+
|
26
|
+
when :horizontal_split
|
27
|
+
t = Sqed::BoundaryFinder.color_boundary_finder(image: img, scan: :columns, boundary_color: @boundary_color) # set to detect horizontal division, (green line)
|
28
|
+
return if t.nil?
|
29
|
+
boundaries.coordinates[0] = [0, 0, img.columns, t[0]] # upper section of image
|
30
|
+
boundaries.coordinates[1] = [0, t[2], img.columns, img.rows - t[2]] # lower section of image
|
31
|
+
boundaries.complete = true
|
32
|
+
# boundaries.coordinates[2] = [0, 0, img.columns, t[1]] # upper section of image
|
33
|
+
# boundaries.coordinates[3] = [0, t[1], img.columns, img.rows - t[1]] # lower section of image
|
34
|
+
|
35
|
+
when :right_t # only 3 zones expected, with horizontal division in right-side of vertical division
|
36
|
+
t = Sqed::BoundaryFinder.color_boundary_finder(image: img, boundary_color: @boundary_color) #defaults to detect vertical division, green line
|
37
|
+
return if t.nil?
|
38
|
+
boundaries.coordinates[0] = [0, 0, t[0], img.rows] # left section of image
|
39
|
+
boundaries.coordinates[1] = [t[2], 0, img.columns - t[2], img.rows] # left section of image
|
40
|
+
|
41
|
+
# now subdivide right side
|
42
|
+
irt = img.crop(*boundaries.coordinates[1], true)
|
43
|
+
rt = Sqed::BoundaryFinder.color_boundary_finder(image: irt, scan: :columns, boundary_color: @boundary_color) # set to detect horizontal division, (green line)
|
44
|
+
return if rt.nil?
|
45
|
+
boundaries.coordinates[1] = [t[2], 0, img.columns - t[2], rt[0]] # upper section of image
|
46
|
+
boundaries.coordinates[2] = [t[2], rt[2], img.columns - t[2], img.rows - rt[2]] # lower section of image
|
47
|
+
boundaries.complete = true
|
48
|
+
# will return 1, 2, or 3
|
49
|
+
|
50
|
+
when :offset_cross # 4 zones expected, with horizontal division in right- and left- sides of vertical division
|
51
|
+
t = Sqed::BoundaryFinder.color_boundary_finder(image: img, boundary_color: @boundary_color) # defaults to detect vertical division, green line
|
52
|
+
raise if t.nil?
|
53
|
+
boundaries.coordinates[0] = [0, 0, t[0], img.rows] # left section of image
|
54
|
+
boundaries.coordinates[1] = [t[2], 0, img.columns - t[2], img.rows] # right section of image
|
55
|
+
|
56
|
+
# now subdivide left side
|
57
|
+
ilt = img.crop(*boundaries.coordinates[0], true)
|
58
|
+
|
59
|
+
lt = Sqed::BoundaryFinder.color_boundary_finder(image: ilt, scan: :columns, boundary_color: @boundary_color) # set to detect horizontal division, (green line)
|
60
|
+
if !lt.nil?
|
61
|
+
boundaries.coordinates[0] = [0, 0, t[0], lt[0]] # upper section of image
|
62
|
+
boundaries.coordinates[3] = [0, lt[2], t[0], img.rows - lt[2]] # lower section of image
|
63
|
+
end
|
64
|
+
|
65
|
+
# now subdivide right side
|
66
|
+
irt = img.crop(*boundaries.coordinates[1], true)
|
67
|
+
rt = Sqed::BoundaryFinder.color_boundary_finder(image: irt, scan: :columns, boundary_color: @boundary_color) # set to detect horizontal division, (green line)
|
68
|
+
return if rt.nil?
|
69
|
+
|
70
|
+
boundaries.coordinates[1] = [t[2], 0, img.columns - t[2], rt[0]] # upper section of image
|
71
|
+
boundaries.coordinates[2] = [t[2], rt[2], img.columns - t[2], img.rows - rt[2]] # lower section of image
|
72
|
+
# will return 1, 2, 3, or 4 //// does not handle staggered vertical boundary case
|
73
|
+
boundaries.complete = true
|
74
|
+
|
75
|
+
else
|
76
|
+
boundaries.coordinates[0] = [0, 0, img.columns, img.rows] # totality of image as default
|
77
|
+
return # return original image boundary if no method implemented
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
|
3
|
+
# Return four equal quadrants, no parsing through the image
|
4
|
+
#
|
5
|
+
class Sqed::BoundaryFinder::CrossFinder < Sqed::BoundaryFinder
|
6
|
+
|
7
|
+
def initialize(image: image)
|
8
|
+
@image = image
|
9
|
+
find_edges
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_edges
|
13
|
+
width = @image.columns / 2
|
14
|
+
height = @image.rows / 2
|
15
|
+
|
16
|
+
boundaries.coordinates[0] = [0, 0, width, height]
|
17
|
+
boundaries.coordinates[1] = [width, 0, width, height]
|
18
|
+
boundaries.coordinates[2] = [width, height, width, height]
|
19
|
+
boundaries.coordinates[3] = [0, height, width, height]
|
20
|
+
boundaries.complete = true
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
|
3
|
+
# Some of this code was originally inspired by Emmanuel Oga's gist https://gist.github.com/EmmanuelOga/2476153.
|
4
|
+
#
|
5
|
+
class Sqed::BoundaryFinder::StageFinder < Sqed::BoundaryFinder
|
6
|
+
|
7
|
+
# The proc containing the border finding algorithim
|
8
|
+
attr_reader :is_border
|
9
|
+
|
10
|
+
# assume white-ish image on dark-ish background
|
11
|
+
|
12
|
+
# How small we accept a cropped picture to be. E.G. if it was 100x100 and
|
13
|
+
# ratio 0.1, min output should be 10x10
|
14
|
+
MIN_CROP_RATIO = 0.1
|
15
|
+
|
16
|
+
attr_reader :x0, :y0, :x1, :y1, :min_width, :min_height, :rows, :columns
|
17
|
+
|
18
|
+
def initialize(image: image, is_border_proc: nil, min_ratio: MIN_CROP_RATIO)
|
19
|
+
super(image: image, layout: :internal_box)
|
20
|
+
|
21
|
+
@min_ratio = min_ratio
|
22
|
+
|
23
|
+
# Initial co-ordinates
|
24
|
+
@x0, @y0 = 0, 0
|
25
|
+
@x1, @y1 = img.columns, img.rows
|
26
|
+
@min_width, @min_height = img.columns * @min_ratio, img.rows * @min_ratio # minimum resultant area
|
27
|
+
@columns, @rows = img.columns, img.rows
|
28
|
+
|
29
|
+
# We need a border finder proc. Provide one if none was given.
|
30
|
+
@is_border = is_border_proc || self.class.default_border_finder(img) # if no proc specified, use default below
|
31
|
+
|
32
|
+
@x00 = @x0
|
33
|
+
@y00 = @y0
|
34
|
+
@height0 = height
|
35
|
+
@width0 = width
|
36
|
+
find_edges
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Returns a Proc that, given a set of pixels (an edge of the image) decides
|
42
|
+
# whether that edge is a border or not.
|
43
|
+
#
|
44
|
+
# (img, samples = 5, threshold = 0.95, fuzz_factor = 0.5) # initially
|
45
|
+
# (img, samples = 50, threshold = 0.9, fuzz_factor = 0.1) # semi-working on synthetic images 08-dec-2014 (x)
|
46
|
+
# (img, samples = 20, threshold = 0.8, fuzz_factor = 0.2) # WORKS with synthetic images and changes to x0, y0, width, height
|
47
|
+
#
|
48
|
+
# appears to assume sharp transition will occur in 5 pixels x/y
|
49
|
+
#
|
50
|
+
# how is threshold defined?
|
51
|
+
# works for 0.5, >0.137; 0.60, >0.14 0.65, >0.146; 0.70, >0.1875; 0.75, >0.1875; 0.8, >0.237; 0.85, >0.24; 0.90, >0.28; 0.95, >0.25
|
52
|
+
# fails for 0.75, (0.18, 0.17,0.16,0.15); 0.70, 0.18;
|
53
|
+
#
|
54
|
+
def self.default_border_finder(img, samples = 5, threshold = 0.75, fuzz_factor = 0.40) # working on non-synthetic images 04-dec-2014
|
55
|
+
fuzz = ((::QuantumRange + 1) * fuzz_factor).to_i
|
56
|
+
# Returns true if the edge is a border (border meaning outer region to be cropped)
|
57
|
+
lambda do |edge|
|
58
|
+
border, non_border = 0.0, 0.0 # maybe should be called outer, inner
|
59
|
+
|
60
|
+
pixels = (0...samples).map { |n| edge[n * edge.length / samples] }
|
61
|
+
pixels.combination(2).each do |a, b|
|
62
|
+
if a.fcmp(b, fuzz) then
|
63
|
+
border += 1
|
64
|
+
else
|
65
|
+
non_border += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
bratio = border.to_f / (border + non_border)
|
69
|
+
if bratio > threshold
|
70
|
+
return true
|
71
|
+
else
|
72
|
+
return false
|
73
|
+
end
|
74
|
+
border.to_f / (border + non_border) > threshold # number of matching string of pixels/(2 x total pixels - a.k.a. samples?)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_edges
|
79
|
+
# handle this exception
|
80
|
+
return unless is_border # return if no process defined or set for @is_border
|
81
|
+
|
82
|
+
u = x1 - 1 # rightmost pixel (kind of)
|
83
|
+
# increment from left to right
|
84
|
+
x0.upto(u) do |x|
|
85
|
+
if width_croppable? && is_border[vline(x)] then
|
86
|
+
@x0 = x + 1
|
87
|
+
else
|
88
|
+
break
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# increment from left to right
|
92
|
+
(u).downto(x0) { |x| width_croppable? && is_border[vline(x)] ? @x1 = x - 1 : break }
|
93
|
+
|
94
|
+
u = y1 - 1
|
95
|
+
0.upto(u) do |y|
|
96
|
+
if height_croppable? && is_border[hline y] then
|
97
|
+
@y0 = y + 1
|
98
|
+
else
|
99
|
+
break
|
100
|
+
end
|
101
|
+
end
|
102
|
+
(u).downto(y0) { |y| height_croppable? && is_border[hline y] ? @y1 = y - 1 : break }
|
103
|
+
u = 0
|
104
|
+
|
105
|
+
delta_x = 0 #width/50 # 2% of cropped image to make up for trapezoidal distortion
|
106
|
+
delta_y = 0 #height/50 # 2% of cropped image to make up for trapezoidal distortion <- NOT 3%
|
107
|
+
|
108
|
+
# TODO: add conditions
|
109
|
+
boundaries.complete = true
|
110
|
+
boundaries.coordinates[0] = [x0 + delta_x, y0 + delta_y, width - 2*delta_x, height - 2*delta_y]
|
111
|
+
end
|
112
|
+
|
113
|
+
def width_croppable?
|
114
|
+
width > min_width
|
115
|
+
end
|
116
|
+
|
117
|
+
def height_croppable?
|
118
|
+
height > min_height
|
119
|
+
end
|
120
|
+
|
121
|
+
def vline(x)
|
122
|
+
img.get_pixels x, @y00, 1, @height0 - 1
|
123
|
+
end
|
124
|
+
|
125
|
+
def hline(y)
|
126
|
+
img.get_pixels @x00, y, @width0 - 1, 1
|
127
|
+
end
|
128
|
+
|
129
|
+
# actually + 1 (starting at zero?)
|
130
|
+
def width
|
131
|
+
@x1 - @x0
|
132
|
+
end
|
133
|
+
|
134
|
+
# actually + 1 (starting at zero?)
|
135
|
+
def height
|
136
|
+
@y1 - @y0
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
|
3
|
+
# An Extractor takes Boundries object and a layout pattern and returns a Sqed::Result
|
4
|
+
#
|
5
|
+
class Sqed::Extractor
|
6
|
+
|
7
|
+
attr_accessor :boundaries, :layout, :image
|
8
|
+
|
9
|
+
def initialize(boundaries: boundaries, layout: layout, image: image)
|
10
|
+
raise if boundaries.nil? || !boundaries.class == Sqed::Boundaries
|
11
|
+
raise if layout.nil? || !layout.class == Hash
|
12
|
+
|
13
|
+
@layout = layout
|
14
|
+
@boundaries = boundaries
|
15
|
+
@image = image
|
16
|
+
end
|
17
|
+
|
18
|
+
def result
|
19
|
+
r = Sqed::Result.new()
|
20
|
+
|
21
|
+
# assign the images to the result
|
22
|
+
boundaries.each do |section, coords|
|
23
|
+
r.send("#{LAYOUT_SECTION_TYPES[section]}=", extract_image(coords))
|
24
|
+
end
|
25
|
+
|
26
|
+
# assign the metadata to the result
|
27
|
+
layout.keys.each do |section_index, section_type|
|
28
|
+
# only extract data if a parser exists
|
29
|
+
if parser = SECTION_PARSERS[section_type]
|
30
|
+
r.send("#{section_type}=", parser.new(image: r.send(section_type + "_image").text) )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
r
|
35
|
+
end
|
36
|
+
|
37
|
+
# coords are x1, y1, x2, y2
|
38
|
+
def extract_image(coords)
|
39
|
+
# crop takes x, y, width, height
|
40
|
+
# @image.crop(coords[0], coords[1], coords[2] - coords[0], coords[3] - coords[1] )
|
41
|
+
bp = 0
|
42
|
+
@image.crop(coords[0], coords[1], coords[2], coords[3], true)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/sqed/parser.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Given an image, return an ordered array of detectable barcodes
|
2
|
+
|
3
|
+
class Sqed::Parser::BarcodeParser < Sqed::Parser
|
4
|
+
attr_accessor :barcodes
|
5
|
+
|
6
|
+
def initialize(image)
|
7
|
+
super
|
8
|
+
@barcodes = bar_codes
|
9
|
+
end
|
10
|
+
|
11
|
+
def bar_codes
|
12
|
+
# process the images, spit out the barcodes
|
13
|
+
# return ZXing.decode_all(@image) #['ABC 123', 'DEF 456']
|
14
|
+
# a = `/usr/local/Cellar/zbar/0.10_1/bin/zbarimg ~/src/sqed/spec/support/files/test_barcode.JPG`
|
15
|
+
# b = a.split("\n")
|
16
|
+
f = 'SessionID_BarcodeImage.JPG'
|
17
|
+
i = @image[:image]
|
18
|
+
if i.nil?
|
19
|
+
i = @image
|
20
|
+
end
|
21
|
+
i.write("tmp/#{f}")
|
22
|
+
c = `/usr/local/Cellar/zbar/0.10_1/bin/zbarimg #{f}`
|
23
|
+
d = c.split("\n")
|
24
|
+
return d
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Given a single image return all text in that image.
|
4
|
+
#
|
5
|
+
# For past reference http://misteroleg.wordpress.com/2012/12/19/ocr-using-tesseract-and-imagemagick-as-pre-processing-task/
|
6
|
+
#
|
7
|
+
require 'rtesseract'
|
8
|
+
|
9
|
+
class Sqed::Parser::OcrParser < Sqed::Parser
|
10
|
+
attr_accessor :text
|
11
|
+
|
12
|
+
def text
|
13
|
+
img = @image #.white_threshold(245)
|
14
|
+
|
15
|
+
# @jrflood: this is where you will have to do some research, tuning images so that they can be better ocr-ed,
|
16
|
+
# all of these methods are from RMagick.
|
17
|
+
# get potential border pixel color (based on quadrant?)
|
18
|
+
new_color = img.pixel_color(1, 1)
|
19
|
+
# img = img.scale(2)
|
20
|
+
# img.write('foo0.jpg.jpg')
|
21
|
+
# img = img.enhance
|
22
|
+
# img = img.enhance
|
23
|
+
# img = img.enhance
|
24
|
+
# img = img.enhance
|
25
|
+
# img.write('foo1.jpg')
|
26
|
+
# img = img.quantize(8, Magick::GRAYColorspace)
|
27
|
+
# img.write('foo1.jpg')
|
28
|
+
# img = img.sharpen(1.0, 0.2)
|
29
|
+
# img.write('foo2.jpg')
|
30
|
+
# border_color = img.pixel_color(img.columns - 1, img.rows - 1)
|
31
|
+
# img = img.color_floodfill(img.columns - 1, img.rows - 1, new_color)
|
32
|
+
# img.write('tmp/foo4.jpg')
|
33
|
+
# img = img.quantize(2, Magick::GRAYColorspace)
|
34
|
+
# #img = img.threshold(0.5)
|
35
|
+
# img.write('foo4.jpg') # for debugging purposes, this is the image that is sent to OCR
|
36
|
+
# img = img.equalize #(32, Magick::GRAYColorspace)
|
37
|
+
# img.write('foo5.jpg') # for debugging purposes, this is the image that is sent to OCR
|
38
|
+
# #img.write('foo3.jpg') # for debugging purposes, this is the image that is sent to OCR
|
39
|
+
#
|
40
|
+
# img.write('foo.jpg') # for debugging purposes, this is the image that is sent to OCR
|
41
|
+
|
42
|
+
r = RTesseract.new(img, lang: 'eng', psm: 3)
|
43
|
+
|
44
|
+
|
45
|
+
# img = img.white_threshold(245)
|
46
|
+
|
47
|
+
@text = r.to_s
|
48
|
+
end
|
49
|
+
|
50
|
+
# Need to provide tuning methods here, i.e. image transormations that facilitate OCR
|
51
|
+
|
52
|
+
end
|
data/lib/sqed/result.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
# A Sqed::Result is a wrapper for the results of the
|
3
|
+
# full process of data extraction from an image.
|
4
|
+
#
|
5
|
+
#
|
6
|
+
#
|
7
|
+
class Sqed::Result
|
8
|
+
|
9
|
+
SqedConfig::LAYOUT_SECTION_TYPES.each do |k|
|
10
|
+
attr_accessor k
|
11
|
+
attr_accessor "#{k}_image".to_sym
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
data/lib/sqed/version.rb
ADDED
data/lib/sqed_config.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require_relative "sqed/parser"
|
4
|
+
require_relative "sqed/parser/ocr_parser"
|
5
|
+
require_relative "sqed/parser/barcode_parser"
|
6
|
+
|
7
|
+
require_relative "sqed/boundaries"
|
8
|
+
require_relative "sqed/boundary_finder"
|
9
|
+
require_relative "sqed/boundary_finder/cross_finder"
|
10
|
+
require_relative "sqed/boundary_finder/stage_finder"
|
11
|
+
require_relative "sqed/boundary_finder/color_line_finder"
|
12
|
+
|
13
|
+
# Sqed constants, including patterns for extraction etc.
|
14
|
+
#
|
15
|
+
module SqedConfig
|
16
|
+
|
17
|
+
# Layouts refer to the arrangement of the divided stage.
|
18
|
+
# Windows are enumerated from the top left, moving around the border
|
19
|
+
# in a clockwise position. For example:
|
20
|
+
# 0 | 1
|
21
|
+
# ----|---- :equal_cross //probably obviated by offset_cross
|
22
|
+
# 3 | 2
|
23
|
+
#
|
24
|
+
# | 1
|
25
|
+
# 0 |---- :right_t
|
26
|
+
# | 2
|
27
|
+
#
|
28
|
+
# should be an arbitrary cross layout
|
29
|
+
# 0 | 1
|
30
|
+
# |
|
31
|
+
# --------- :offset_cross //does not match current code
|
32
|
+
# 3 | 2
|
33
|
+
#
|
34
|
+
# 0 | 1
|
35
|
+
# |____
|
36
|
+
# ----| :offset_cross // matches current code
|
37
|
+
# 3 | 2
|
38
|
+
#
|
39
|
+
# 0
|
40
|
+
# -------- :horizontal_split
|
41
|
+
# 1
|
42
|
+
#
|
43
|
+
# |
|
44
|
+
# 0 | 1 :vertical_split
|
45
|
+
# |
|
46
|
+
#
|
47
|
+
# -----
|
48
|
+
# | 0 | :internal_box
|
49
|
+
# -----
|
50
|
+
#
|
51
|
+
|
52
|
+
# Hash values are used to stub out
|
53
|
+
# the Sqed::Boundaries instance.
|
54
|
+
#
|
55
|
+
LAYOUTS = {
|
56
|
+
cross: [0,1,2,3],
|
57
|
+
offset_cross: [0,1,2,3],
|
58
|
+
horizontal_split: [0,1],
|
59
|
+
vertical_split: [0,1],
|
60
|
+
right_t: [0,1,2],
|
61
|
+
left_t: [0,1,2],
|
62
|
+
internal_box: [0]
|
63
|
+
}
|
64
|
+
|
65
|
+
# Each element of the layout is a "section".
|
66
|
+
LAYOUT_SECTION_TYPES = [
|
67
|
+
:stage, # the image contains the full stage
|
68
|
+
:specimen, # the specimen only, no metadata should be present
|
69
|
+
:annotated_specimen, # a specimen is present, and metadata is too
|
70
|
+
:determination_labels, # the section contains text that determines the specimen
|
71
|
+
:labels, # the section contains collecting event and non-determination labels
|
72
|
+
:identifier, # the section contains an identifier (e.g. barcode or unique number)
|
73
|
+
:image_registration # the section contains only image registration information
|
74
|
+
]
|
75
|
+
|
76
|
+
# Links section types to data parsers
|
77
|
+
SECTION_PARSERS = {
|
78
|
+
labels: Sqed::Parser::OcrParser,
|
79
|
+
identifier: Sqed::Parser::BarcodeParser,
|
80
|
+
deterimination_labels: Sqed::Parser::OcrParser
|
81
|
+
}
|
82
|
+
|
83
|
+
EXTRACTION_PATTERNS = {
|
84
|
+
right_t: {
|
85
|
+
boundary_finder: Sqed::BoundaryFinder::ColorLineFinder,
|
86
|
+
layout: :right_t,
|
87
|
+
metadata_map: {0 => :annotated_specimen, 1 => :identifiers, 2 =>:image_registration }
|
88
|
+
},
|
89
|
+
offset_cross: {
|
90
|
+
boundary_finder: Sqed::BoundaryFinder::ColorLineFinder,
|
91
|
+
layout: :offset_cross,
|
92
|
+
metadata_map: {0 => :annotated_specimen, 1 => :identifiers, 2 =>:image_registration }
|
93
|
+
},
|
94
|
+
standard_cross: {
|
95
|
+
boundary_finder: Sqed::BoundaryFinder::CrossFinder,
|
96
|
+
layout: :cross,
|
97
|
+
metadata_map: {0 => :labels, 1 => :specimen, 2 => :identifier, 3 => :specimen_determinations }
|
98
|
+
},
|
99
|
+
stage: {
|
100
|
+
boundary_finder: Sqed::BoundaryFinder::StageFinder,
|
101
|
+
layout: :internal_box,
|
102
|
+
metadata_map: {0 => :stage}
|
103
|
+
}
|
104
|
+
# etc. ...
|
105
|
+
}
|
106
|
+
|
107
|
+
DEFAULT_TMP_DIR = "/tmp"
|
108
|
+
|
109
|
+
def self.index_for_section_type(pattern, section_type)
|
110
|
+
EXTRACTION_PATTERNS[pattern][:metadata_map].invert[section_type]
|
111
|
+
end
|
112
|
+
end
|