mork 0.0.9 → 0.0.10
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/.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
|
|