mork 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.ruby-version +1 -1
- data/README.md +130 -24
- data/lib/mork/extensions.rb +26 -0
- data/lib/mork/grid.rb +47 -325
- data/lib/mork/grid_const.rb +33 -35
- data/lib/mork/grid_omr.rb +94 -0
- data/lib/mork/grid_pdf.rb +107 -0
- data/lib/mork/mimage.rb +14 -2
- data/lib/mork/npatch.rb +0 -1
- data/lib/mork/{sheet.rb → sheet_omr.rb} +87 -89
- data/lib/mork/sheet_pdf.rb +60 -66
- data/lib/mork/version.rb +1 -1
- data/lib/mork.rb +2 -6
- data/spec/mork/extensions_spec.rb +10 -0
- data/spec/mork/grid_omr_spec.rb +122 -0
- data/spec/mork/grid_spec.rb +22 -138
- data/spec/mork/sheet_omr_spec.rb +165 -0
- data/spec/mork/sheet_pdf_spec.rb +53 -43
- data/spec/samples/grid01.yml +3 -4
- data/spec/samples/grid02.yml +57 -0
- data/spec/samples/grid160.yml +57 -0
- data/spec/samples/grid_omr_01.yml +28 -0
- data/spec/samples/sample_color.pdf +0 -0
- data/spec/samples/sample_gray.jpg +0 -0
- data/spec/samples/sheet666.jpg +0 -0
- data/spec/samples/sheet666.pdf +12566 -0
- data/spec/spec_helper.rb +3 -20
- metadata +50 -28
- data/spec/mork/sheet_spec.rb +0 -178
data/lib/mork/grid_const.rb
CHANGED
@@ -4,24 +4,24 @@ module Mork
|
|
4
4
|
# default units are millimiters
|
5
5
|
page_size: {
|
6
6
|
# this is A4
|
7
|
-
width:
|
8
|
-
height:
|
7
|
+
width: 210,
|
8
|
+
height: 297
|
9
9
|
}, # page end
|
10
10
|
regmarks: {
|
11
|
-
margin:
|
12
|
-
radius:
|
13
|
-
search:
|
14
|
-
offset:
|
11
|
+
margin: 10,
|
12
|
+
radius: 2.5,
|
13
|
+
search: 10,
|
14
|
+
offset: 2
|
15
15
|
}, # regmarks end
|
16
16
|
header: {
|
17
17
|
name: {
|
18
18
|
top: 5,
|
19
|
-
left:
|
20
|
-
width:
|
21
|
-
size:
|
19
|
+
left: 7.5,
|
20
|
+
width: 170,
|
21
|
+
size: 14,
|
22
22
|
},
|
23
23
|
title: {
|
24
|
-
top:
|
24
|
+
top: 15,
|
25
25
|
left: 7.5,
|
26
26
|
width: 180,
|
27
27
|
size: 12
|
@@ -34,7 +34,7 @@ module Mork
|
|
34
34
|
},
|
35
35
|
signature: {
|
36
36
|
top: 30,
|
37
|
-
left:
|
37
|
+
left: 7.5,
|
38
38
|
width: 120,
|
39
39
|
height: 15,
|
40
40
|
size: 7,
|
@@ -43,43 +43,41 @@ module Mork
|
|
43
43
|
}, # header end
|
44
44
|
items: {
|
45
45
|
columns: 4,
|
46
|
-
column_width:
|
46
|
+
column_width: 44,
|
47
47
|
rows: 30,
|
48
48
|
# from the top-left registration mark
|
49
49
|
# to the center of the first choice cell
|
50
50
|
first_x: 10.5,
|
51
51
|
first_y: 55.5,
|
52
52
|
# between choices
|
53
|
-
x_spacing: 7
|
53
|
+
x_spacing: 7,
|
54
54
|
# between rows
|
55
|
-
y_spacing: 7
|
55
|
+
y_spacing: 7,
|
56
56
|
# darkened area
|
57
|
-
cell_width: 6
|
58
|
-
cell_height: 5
|
57
|
+
cell_width: 6,
|
58
|
+
cell_height: 5,
|
59
59
|
# the maximum number of choices per question
|
60
|
-
max_cells:
|
61
|
-
# font size for the question number
|
62
|
-
|
60
|
+
max_cells: 5,
|
61
|
+
# font size for the question number and choice letters
|
62
|
+
font_size: 9,
|
63
63
|
# distance between right side of q num and left side of first choice cell
|
64
|
-
number_width:
|
64
|
+
number_width: 8,
|
65
65
|
# width of question number text box
|
66
|
-
number_margin:
|
67
|
-
# font size for the choice letter
|
68
|
-
letter_size: 8
|
66
|
+
number_margin: 2,
|
69
67
|
}, # items end
|
70
|
-
|
71
|
-
bits:
|
72
|
-
left:
|
73
|
-
width:
|
74
|
-
height:
|
75
|
-
spacing:
|
76
|
-
}, #
|
68
|
+
barcode: {
|
69
|
+
bits: 40,
|
70
|
+
left: 15,
|
71
|
+
width: 3,
|
72
|
+
height: 2.5,
|
73
|
+
spacing: 4
|
74
|
+
}, # barcode end
|
77
75
|
control: {
|
78
|
-
top:
|
79
|
-
left:
|
80
|
-
width:
|
81
|
-
size:
|
82
|
-
margin:
|
76
|
+
top: 40,
|
77
|
+
left: 123,
|
78
|
+
width: 50,
|
79
|
+
size: 9,
|
80
|
+
margin: 2.5
|
83
81
|
} # control end
|
84
82
|
}
|
85
83
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'mork/grid'
|
2
|
+
|
3
|
+
module Mork
|
4
|
+
class GridOMR < Grid
|
5
|
+
def initialize(page_width, page_height, options=nil)
|
6
|
+
super options
|
7
|
+
@px = page_width.to_f
|
8
|
+
@py = page_height.to_f
|
9
|
+
end
|
10
|
+
|
11
|
+
def barcode_bit_areas(code = 2**barcode_bits-1)
|
12
|
+
barcode_bits.times.collect { |b| barcode_bit_area b }
|
13
|
+
end
|
14
|
+
|
15
|
+
# ====================================================
|
16
|
+
# = Returning {x, y, w, h} hashes for area locations =
|
17
|
+
# ====================================================
|
18
|
+
def choice_cell_area(q, c)
|
19
|
+
{
|
20
|
+
x: (cx * cell_x(q,c)).round,
|
21
|
+
y: (cy * cell_y(q) ).round,
|
22
|
+
w: (cx * cell_width ).round,
|
23
|
+
h: (cy * cell_height).round
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def calibration_cell_areas
|
28
|
+
rows.times.collect do |q|
|
29
|
+
{
|
30
|
+
x: (cx * cal_cell_x ).round,
|
31
|
+
y: (cy * cell_y(q) ).round,
|
32
|
+
w: (cx * cell_width ).round,
|
33
|
+
h: (cy * cell_height).round
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def barcode_bit_area(bit)
|
39
|
+
{
|
40
|
+
x: (cx * barcode_bit_x(bit)).round,
|
41
|
+
y: (cy * barcode_y ).round,
|
42
|
+
w: (cx * barcode_width ).round,
|
43
|
+
h: (cy * barcode_height ).round
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
# the 4 values needed to locate a single registration mark
|
48
|
+
#
|
49
|
+
def rm_search_area(corner, i)
|
50
|
+
{
|
51
|
+
x: (ppu_x * rmx(corner, i)).round,
|
52
|
+
y: (ppu_y * rmy(corner, i)).round,
|
53
|
+
w: (ppu_x * (reg_search + reg_radius * i)).round,
|
54
|
+
h: (ppu_y * (reg_search + reg_radius * i)).round
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
# a safe distance to determine
|
59
|
+
def rm_edgy_x() (ppu_x * reg_radius).round + 5 end
|
60
|
+
def rm_edgy_y() (ppu_y * reg_radius).round + 5 end
|
61
|
+
# areas on the sheet that are certainly white/black
|
62
|
+
def paper_white_area() barcode_bit_area -1 end
|
63
|
+
def ink_black_area() barcode_bit_area 0 end
|
64
|
+
def max_choices_per_question() @params[:items][:max_cells].to_i end
|
65
|
+
def rm_max_search_area_side() (ppu_x * page_width / 4).round end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def cx() @px / reg_frame_width end
|
70
|
+
def cy() @py / reg_frame_height end
|
71
|
+
def ppu_x() @px / page_width end
|
72
|
+
def ppu_y() @py / page_height end
|
73
|
+
|
74
|
+
# finding the width of the registration area based on iteration
|
75
|
+
def rmx(corner, i)
|
76
|
+
case corner
|
77
|
+
when :tl; reg_off
|
78
|
+
when :tr; page_width - reg_search - reg_off - reg_radius * i
|
79
|
+
when :br; page_width - reg_search - reg_off - reg_radius * i
|
80
|
+
when :bl; reg_off
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# finding the height of the registration area based on iteration
|
85
|
+
def rmy(corner, i)
|
86
|
+
case corner
|
87
|
+
when :tl; reg_off
|
88
|
+
when :tr; reg_off
|
89
|
+
when :br; page_height - reg_search - reg_off - reg_radius * i
|
90
|
+
when :bl; page_height - reg_search - reg_off - reg_radius * i
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'mork/grid'
|
2
|
+
|
3
|
+
module Mork
|
4
|
+
# GridPDF gets coordinates and measurements from a Grid and
|
5
|
+
# provides SheetPDF with the properly computed values
|
6
|
+
class GridPDF < Grid
|
7
|
+
def initialize(options=nil)
|
8
|
+
super options
|
9
|
+
end
|
10
|
+
|
11
|
+
def reg_marks
|
12
|
+
r = reg_radius.mm
|
13
|
+
[
|
14
|
+
{ p: [0, 0 ], r: r },
|
15
|
+
{ p: [0, reg_frame_height.mm], r: r },
|
16
|
+
{ p: [reg_frame_width.mm, reg_frame_height.mm], r: r },
|
17
|
+
{ p: [reg_frame_width.mm, 0 ], r: r }
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
def barcode_bit_areas_for(code)
|
22
|
+
black = barcode_bits.times.reject { |x| (code>>x)[0]==0 }
|
23
|
+
black.collect { |x| barcode_area x+1 }
|
24
|
+
end
|
25
|
+
|
26
|
+
def calibration_cell_areas
|
27
|
+
rows.times.collect do |q|
|
28
|
+
{
|
29
|
+
p: [(reg_frame_width-cell_spacing).mm, (reg_frame_height - cell_y(q)).mm],
|
30
|
+
w: cell_width.mm,
|
31
|
+
h: cell_height.mm
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Coordinates at which to place calibration cell labels (usually an ‘X’)
|
37
|
+
def calibration_letter_xy(q)
|
38
|
+
[
|
39
|
+
(reg_frame_width-cell_spacing).mm + 2.mm,
|
40
|
+
item_text_y(q)
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Coordinates at which to place item numbers
|
45
|
+
def qnum_xy(q)
|
46
|
+
[
|
47
|
+
cell_x(q, 0).mm - qnum_width - @params[:items][:number_margin].to_f.mm,
|
48
|
+
item_text_y(q)
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Coordinates at which to place choice labels
|
53
|
+
def choice_letter_xy(q, c)
|
54
|
+
[
|
55
|
+
cell_x(q, c).mm + 2.mm,
|
56
|
+
item_text_y(q)
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
def choice_cell_area(q, c)
|
61
|
+
{
|
62
|
+
p: [cell_x(q, c).mm, (reg_frame_height - cell_y(q)).mm],
|
63
|
+
w: cell_width.mm,
|
64
|
+
h: cell_height.mm
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def page_size() [page_width.mm, page_height.mm] end
|
69
|
+
def margins() reg_margin.mm end
|
70
|
+
def ink_black_area() barcode_area(0) end
|
71
|
+
def qnum_width() @params[:items][:number_width].to_f.mm end
|
72
|
+
def item_font_size() @params[:items][:font_size].to_f end
|
73
|
+
def header_width(k) @params[:header][k][:width].to_f.mm end
|
74
|
+
def header_height(k) @params[:header][k][:height].to_f.mm end
|
75
|
+
def header_size(k) @params[:header][k][:size].to_f end
|
76
|
+
def header_boxed?(k) @params[:header][k][:box] == true end
|
77
|
+
|
78
|
+
def header_xy(k)
|
79
|
+
[
|
80
|
+
@params[:header][k][:left].to_f.mm,
|
81
|
+
(reg_frame_height - @params[:header][k][:top].to_f).mm
|
82
|
+
]
|
83
|
+
end
|
84
|
+
|
85
|
+
def header_padding(k)
|
86
|
+
[
|
87
|
+
1.mm,
|
88
|
+
header_height(k) - 1.mm
|
89
|
+
]
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def item_text_y(q)
|
96
|
+
(reg_frame_height - cell_y(q) - cell_height/4).mm
|
97
|
+
end
|
98
|
+
|
99
|
+
def barcode_area(i)
|
100
|
+
{
|
101
|
+
p: [barcode_bit_x(i).mm, (reg_frame_height - barcode_y).mm],
|
102
|
+
w: barcode_width.mm,
|
103
|
+
h: barcode_height.mm * 2
|
104
|
+
}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/mork/mimage.rb
CHANGED
@@ -39,7 +39,7 @@ module Mork
|
|
39
39
|
# =============
|
40
40
|
# = Highlight =
|
41
41
|
# =============
|
42
|
-
def
|
42
|
+
def highlight_cells!(cells, roundedness=nil)
|
43
43
|
cells = [cells] if cells.is_a? Hash
|
44
44
|
roundedness ||= [cells[0][:h], cells[0][:w]].min / 2
|
45
45
|
cells.each do |c|
|
@@ -51,12 +51,24 @@ module Mork
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
def highlight_rect!(areas)
|
55
|
+
areas = [areas] if areas.is_a? Hash
|
56
|
+
areas.each do |c|
|
57
|
+
out = Magick::Draw.new
|
58
|
+
out.fill_opacity 0
|
59
|
+
out.stroke 'yellow'
|
60
|
+
out.stroke_width 3
|
61
|
+
out.rectangle c[:x], c[:y], c[:x]+c[:w], c[:y]+c[:h]
|
62
|
+
out.draw @image
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
54
66
|
def join!(p)
|
55
67
|
poly = Magick::Draw.new
|
56
68
|
poly.fill_opacity 0
|
57
69
|
poly.stroke 'green'
|
58
70
|
poly.stroke_width 3
|
59
|
-
poly.polygon p[0], p[
|
71
|
+
poly.polygon p[0][:x], p[0][:y], p[1][:x], p[1][:y], p[2][:x], p[2][:y], p[3][:x], p[3][:y]
|
60
72
|
poly.draw @image
|
61
73
|
end
|
62
74
|
|
data/lib/mork/npatch.rb
CHANGED
@@ -1,19 +1,25 @@
|
|
1
|
+
require 'mork/grid_omr'
|
2
|
+
require 'mork/mimage'
|
3
|
+
require 'mork/mimage_list'
|
4
|
+
require 'mork/npatch'
|
5
|
+
|
1
6
|
module Mork
|
2
|
-
class
|
3
|
-
def initialize(im,
|
4
|
-
@raw
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
class SheetOMR
|
8
|
+
def initialize(im, grom=nil)
|
9
|
+
@raw = case im
|
10
|
+
when String
|
11
|
+
Mimage.new im
|
12
|
+
when Mork::Mimage
|
13
|
+
im
|
14
|
+
else
|
15
|
+
raise "A new sheet requires either a Mimage or the name of the source image file, but it was a: #{im.class}"
|
16
|
+
end
|
17
|
+
@grom = case grom
|
18
|
+
when String, Hash, NilClass
|
19
|
+
GridOMR.new @raw.width, @raw.height, grom
|
20
|
+
else
|
21
|
+
raise 'Invalid argument in SheetOMR initialization'
|
22
|
+
end
|
17
23
|
@rm = {}
|
18
24
|
@rmsa = {}
|
19
25
|
@ok_reg = register @raw
|
@@ -23,25 +29,21 @@ module Mork
|
|
23
29
|
@ok_reg
|
24
30
|
end
|
25
31
|
|
26
|
-
|
27
|
-
@rm
|
28
|
-
end
|
29
|
-
|
30
|
-
# code
|
32
|
+
# barcode
|
31
33
|
#
|
32
|
-
# returns the sheet
|
33
|
-
def
|
34
|
+
# returns the sheet barcode as an integer
|
35
|
+
def barcode
|
34
36
|
return if not_registered
|
35
|
-
|
37
|
+
barcode_string.to_i(2)
|
36
38
|
end
|
37
39
|
|
38
|
-
#
|
40
|
+
# barcode_string
|
39
41
|
#
|
40
|
-
# returns the sheet
|
42
|
+
# returns the sheet barcode as a string of 0s and 1s. The string is barcode_bits
|
41
43
|
# bits long, with most significant bits to the left
|
42
|
-
def
|
44
|
+
def barcode_string
|
43
45
|
return if not_registered
|
44
|
-
cs =
|
46
|
+
cs = @grom.barcode_bits.times.inject("") { |c, v| c << barcode_bit_value(v) }
|
45
47
|
cs.reverse
|
46
48
|
end
|
47
49
|
|
@@ -51,7 +53,6 @@ module Mork
|
|
51
53
|
# false otherwise
|
52
54
|
def marked?(q, c)
|
53
55
|
return if not_registered
|
54
|
-
# puts "SHADE: #{shade_of(q, c)}, Q: #{q}, C: #{c}, THR: #{choice_threshold}"
|
55
56
|
shade_of(q, c) < choice_threshold
|
56
57
|
end
|
57
58
|
|
@@ -67,7 +68,7 @@ module Mork
|
|
67
68
|
return if not_registered
|
68
69
|
question_range(r).collect do |q|
|
69
70
|
cho = []
|
70
|
-
(0...@
|
71
|
+
(0...@grom.max_choices_per_question).each do |c|
|
71
72
|
cho << c if marked?(q, c)
|
72
73
|
end
|
73
74
|
cho
|
@@ -77,7 +78,7 @@ module Mork
|
|
77
78
|
def mark_logical_array(r = nil)
|
78
79
|
return if not_registered
|
79
80
|
question_range(r).collect do |q|
|
80
|
-
(0...@
|
81
|
+
(0...@grom.max_choices_per_question).collect {|c| marked?(q, c)}
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
@@ -90,55 +91,36 @@ module Mork
|
|
90
91
|
@crop.outline! array_of cells
|
91
92
|
end
|
92
93
|
|
93
|
-
def highlight_ctrl
|
94
|
-
return if not_registered
|
95
|
-
@crop.highlight! [@grid.ctrl_area_dark, @grid.ctrl_area_light]
|
96
|
-
end
|
97
|
-
|
98
94
|
def highlight_all
|
99
95
|
return if not_registered
|
100
|
-
cells = (0...@
|
101
|
-
@crop.
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@crop.
|
96
|
+
cells = (0...@grom.max_questions).collect { |i| (0...@grom.max_choices_per_question).to_a }
|
97
|
+
@crop.highlight_cells! array_of cells
|
98
|
+
@crop.highlight_cells! @grom.calibration_cell_areas
|
99
|
+
@crop.highlight_rect! [@grom.ink_black_area, @grom.paper_white_area]
|
100
|
+
@crop.highlight_rect! @grom.barcode_bit_areas
|
101
|
+
# @grom.barcode_bits.times do |bit|
|
102
|
+
# @crop.highlight_rect! @grom.barcode_bit_area bit
|
103
|
+
# end
|
107
104
|
end
|
108
105
|
|
109
|
-
def
|
106
|
+
def highlight_marked
|
110
107
|
return if not_registered
|
111
|
-
@
|
112
|
-
@crop.highlight! @grid.code_bit_area bit
|
113
|
-
end
|
108
|
+
@crop.highlight_cells! array_of mark_array
|
114
109
|
end
|
115
110
|
|
116
|
-
def
|
111
|
+
def highlight_barcode
|
117
112
|
return if not_registered
|
118
|
-
@
|
119
|
-
if
|
120
|
-
@crop.
|
113
|
+
@grom.barcode_bits.times do |bit|
|
114
|
+
if barcode_string.reverse[bit] == '1'
|
115
|
+
@crop.highlight_rect! @grom.barcode_bit_area bit+1
|
121
116
|
end
|
122
117
|
end
|
123
118
|
end
|
124
119
|
|
125
|
-
def highlight_dark_calibration_bit
|
126
|
-
return if not_registered
|
127
|
-
@crop.highlight!(@grid.cal_area_black)
|
128
|
-
end
|
129
|
-
|
130
|
-
def highlight_light_calibration_bit
|
131
|
-
return if not_registered
|
132
|
-
@crop.highlight!(@grid.cal_area_white)
|
133
|
-
end
|
134
|
-
|
135
120
|
def highlight_reg_area
|
136
|
-
@raw.
|
137
|
-
@raw.highlight! @rmsa[:tr]
|
138
|
-
@raw.highlight! @rmsa[:br]
|
139
|
-
@raw.highlight! @rmsa[:bl]
|
121
|
+
@raw.highlight_rect! [@rmsa[:tl], @rmsa[:tr], @rmsa[:br], @rmsa[:bl]]
|
140
122
|
return if not_registered
|
141
|
-
@raw.join!
|
123
|
+
@raw.join! [@rm[:tl],@rm[:tr],@rm[:br],@rm[:bl]]
|
142
124
|
end
|
143
125
|
|
144
126
|
def write(fname)
|
@@ -150,13 +132,20 @@ module Mork
|
|
150
132
|
@raw.write(fname)
|
151
133
|
end
|
152
134
|
|
135
|
+
# =================================
|
136
|
+
# = compute shading with NPatches =
|
137
|
+
# =================================
|
138
|
+
def shade_of(q, c)
|
139
|
+
naverage @grom.choice_cell_area(q, c)
|
140
|
+
end
|
141
|
+
|
153
142
|
private
|
154
143
|
|
155
144
|
def array_of(cells)
|
156
145
|
out = []
|
157
146
|
cells.each_with_index do |q, i|
|
158
147
|
q.each do |c|
|
159
|
-
out << @
|
148
|
+
out << @grom.choice_cell_area(i, c)
|
160
149
|
end
|
161
150
|
end
|
162
151
|
out
|
@@ -164,7 +153,7 @@ module Mork
|
|
164
153
|
|
165
154
|
def question_range(r)
|
166
155
|
if r.nil?
|
167
|
-
(0...@
|
156
|
+
(0...@grom.max_questions)
|
168
157
|
elsif r.is_a? Fixnum
|
169
158
|
(0...r)
|
170
159
|
elsif r.is_a? Array
|
@@ -174,35 +163,44 @@ module Mork
|
|
174
163
|
end
|
175
164
|
end
|
176
165
|
|
177
|
-
|
178
|
-
|
179
|
-
# =================================
|
180
|
-
def shade_of(q, c)
|
181
|
-
naverage @grid.choice_cell_area(q, c)
|
182
|
-
end
|
183
|
-
|
184
|
-
def shade_of_code_bit(i)
|
185
|
-
naverage @grid.code_bit_area(i)
|
166
|
+
def barcode_bit_value(i)
|
167
|
+
shade_of_barcode_bit(i) < barcode_threshold ? "1" : "0"
|
186
168
|
end
|
187
169
|
|
188
|
-
def
|
189
|
-
|
170
|
+
def shade_of_barcode_bit(i)
|
171
|
+
naverage @grom.barcode_bit_area i+1
|
190
172
|
end
|
191
173
|
|
174
|
+
def barcode_threshold
|
175
|
+
@barcode_threshold ||= (paper_white + ink_black) / 2
|
176
|
+
end
|
177
|
+
|
192
178
|
def choice_threshold
|
193
|
-
@choice_threshold ||=
|
194
|
-
|
179
|
+
@choice_threshold ||= ccmeans.mean - ccmeans.stdev * 7
|
180
|
+
end
|
181
|
+
|
182
|
+
def ccmeans
|
183
|
+
@calcmeans ||= @grom.calibration_cell_areas.collect { |c| naverage c }
|
184
|
+
end
|
185
|
+
|
186
|
+
def paper_white
|
187
|
+
@paper_white ||= naverage @grom.paper_white_area
|
188
|
+
end
|
189
|
+
|
190
|
+
def ink_black
|
191
|
+
@ink_black ||= naverage @grom.ink_black_area
|
195
192
|
end
|
196
193
|
|
197
|
-
def
|
198
|
-
|
199
|
-
naverage(@grid.cal_area_white)) / 2
|
194
|
+
def shade_of_blank_cells
|
195
|
+
# @grom.
|
200
196
|
end
|
201
197
|
|
202
198
|
# ================
|
203
199
|
# = Registration =
|
204
200
|
# ================
|
205
201
|
|
202
|
+
# this method uses a 'stretch' strategy, i.e. where the image after
|
203
|
+
# registration has the same size in pixels as the original scanned file
|
206
204
|
def register(img)
|
207
205
|
# find the XY coordinates of the 4 registration marks
|
208
206
|
@rm[:tl] = reg_centroid_on(img, :tl)
|
@@ -224,19 +222,19 @@ module Mork
|
|
224
222
|
# in the XY coordinates of the entire image
|
225
223
|
def reg_centroid_on(img, corner)
|
226
224
|
1000.times do |i|
|
227
|
-
@rmsa[corner] = @
|
225
|
+
@rmsa[corner] = @grom.rm_search_area(corner, i)
|
228
226
|
cx, cy = NPatch.new(img.crop(@rmsa[corner])).dark_centroid
|
229
227
|
if cx.nil?
|
230
228
|
status = :insufficient_contrast
|
231
|
-
elsif (cx < @
|
232
|
-
(cy < @
|
233
|
-
(cy > @rmsa[corner][:h] - @
|
234
|
-
(cx > @rmsa[corner][:w] - @
|
229
|
+
elsif (cx < @grom.rm_edgy_x) or
|
230
|
+
(cy < @grom.rm_edgy_y) or
|
231
|
+
(cy > @rmsa[corner][:h] - @grom.rm_edgy_y) or
|
232
|
+
(cx > @rmsa[corner][:w] - @grom.rm_edgy_x)
|
235
233
|
status = :edgy
|
236
234
|
else
|
237
235
|
return {status: :ok, x: cx + @rmsa[corner][:x], y: cy + @rmsa[corner][:y]}
|
238
236
|
end
|
239
|
-
return {status: status, x: nil, y: nil} if @rmsa[corner][:w] > @
|
237
|
+
return {status: status, x: nil, y: nil} if @rmsa[corner][:w] > @grom.rm_max_search_area_side
|
240
238
|
end
|
241
239
|
end
|
242
240
|
|