mork 0.0.5 → 0.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 74b53c108c7a1ae34a090aac3b9380805326ee01
4
- data.tar.gz: f34f58ad865ffc0995cf51a21e0c2c163b4f8e27
3
+ metadata.gz: 4e92c32ca59c988cea80c3c77500fd756e53410d
4
+ data.tar.gz: 0334a33ef6ce0f4166ba19d57d9b82909f915c05
5
5
  SHA512:
6
- metadata.gz: 55f9f4dff569c87008aa9e15a4029172258d0c3998746700188f440695ece40948a0b529d21a3512dd632537cfc123ee8730e8bfb99b7dd31a6c708150f742a4
7
- data.tar.gz: 68b53cf1ab3b9adc014b8da070da4e8d16b8706eb3754f735fa596b8025f6b720b803d8a225efacd1db2d9922b1ba98ae1cba2712b714de694c67b9d1c5f6d6c
6
+ metadata.gz: 7f7dd80d9be2d3a2ccd58ec2e1a077401269baeebcb2d84d0105b325f351bad0bef130607502d5e5eecb86af0004e49e12d6d8410251ac55013147d5261924af
7
+ data.tar.gz: 96b17f9ea0bf9a307d46a1f8cecbc6e39858f0c6303ea3d23690e8bfccb8602eccf5a08b5690046edef8bfb345a49b87994629586cbf90e41e7387df270541ca
data/config/grids.yml CHANGED
@@ -27,7 +27,7 @@ default:
27
27
  height: 10
28
28
  size: 7
29
29
  box: true
30
- responses:
30
+ items:
31
31
  columns: 4
32
32
  column_width: 50.0
33
33
  rows: 40
data/lib/mork/grid.rb CHANGED
@@ -9,14 +9,24 @@ module Mork
9
9
  def initialize(options = {})
10
10
  case options.class.to_s
11
11
  when "Hash"
12
- @params = DGRID.merge! options
12
+ @params = DGRID.merge symbolize options
13
13
  when "String"
14
- @params = DGRID.merge! YAML.load_file(options)
14
+ @params = DGRID.merge symbolize YAML.load_file(options)
15
15
  else
16
16
  raise "Invalid options parameter: #{options.class.inspect}"
17
17
  end
18
18
  end
19
19
 
20
+ # symbolize(object)
21
+ #
22
+ # recursively turn hash keys into symbols. pasted from
23
+ # http://stackoverflow.com/questions/800122/best-way-to-convert-strings-to-symbols-in-hash
24
+ def symbolize(obj)
25
+ return obj.inject({}){|memo,(k,v)| memo[k.to_sym] = symbolize(v); memo} if obj.is_a? Hash
26
+ return obj.inject([]){|memo,v | memo << symbolize(v); memo} if obj.is_a? Array
27
+ return obj
28
+ end
29
+
20
30
  def options
21
31
  @params
22
32
  end
@@ -31,7 +41,7 @@ module Mork
31
41
  end
32
42
 
33
43
  def max_choices_per_question
34
- @params[:responses][:max_cells]
44
+ @params[:items][:max_cells].to_i
35
45
  end
36
46
 
37
47
  def code_bits
@@ -41,43 +51,52 @@ module Mork
41
51
  # ====================================================
42
52
  # = Returning {x, y, w, h} hashes for area locations =
43
53
  # ====================================================
44
- def reg_mark_tl_search_area
45
- reg_mark_search_area 0, 0
54
+ # {} = rm_search_area(x, y)
55
+ #
56
+ # the 4 values needed to locate a single registration mark
57
+ def rm_search_area(corner, i)
58
+ {
59
+ x: (ppu_x * rmx(corner, i)).round,
60
+ y: (ppu_y * rmy(corner, i)).round,
61
+ w: (ppu_x * (reg_search + reg_step * i)).round,
62
+ h: (ppu_y * (reg_search + reg_step * i)).round
63
+ }
46
64
  end
47
65
 
48
- def reg_mark_tr_search_area
49
- reg_mark_search_area page_width - reg_search, 0
66
+ def rm_edgy_x
67
+ (ppu_x * reg_radius).round + 5
50
68
  end
51
-
52
- def reg_mark_br_search_area
53
- reg_mark_search_area page_width - reg_search, page_height - reg_search
69
+
70
+ def rm_edgy_y
71
+ (ppu_y * reg_radius).round + 5
54
72
  end
55
73
 
56
- def reg_mark_bl_search_area
57
- reg_mark_search_area 0, page_height - reg_search
74
+ def rm_max_search_area_side
75
+ (ppu_x * page_width / 4).round
58
76
  end
59
77
 
60
78
  def choice_cell_area(q, c)
61
79
  cell_area cell_x(q, c), cell_y(q)
62
80
  end
63
81
 
64
- def dark_control_cell_area
65
- cell_area ctrl_cell_left, ctrl_cell_y
82
+ def ctrl_area_dark
83
+ cell_area ctrl_cell_x, ctrl_cell_y
66
84
  end
67
85
 
68
- def light_control_cell_area
69
- cell_area ctrl_cell_left + cell_spacing, ctrl_cell_y
86
+ def ctrl_area_light
87
+ cell_area ctrl_cell_x + cell_spacing, ctrl_cell_y
70
88
  end
71
89
 
72
- def white_calibration_area
90
+ def cal_area_white
73
91
  code_cell_area -1
74
92
  end
75
93
 
76
- def black_calibration_area
94
+ def cal_area_black
77
95
  code_cell_area 0
78
96
  end
79
97
 
80
98
  def code_bit_area(i)
99
+ raise "Invalid code bit" if i >= code_bits
81
100
  code_cell_area i+1
82
101
  end
83
102
 
@@ -93,7 +112,7 @@ module Mork
93
112
  end
94
113
 
95
114
  def pdf_reg_marks
96
- r = @params[:regmarks][:radius].to_f.mm
115
+ r = reg_radius.mm
97
116
  [
98
117
  { p: [0, 0 ], r: r },
99
118
  { p: [0, reg_frame_height.mm], r: r },
@@ -145,21 +164,21 @@ module Mork
145
164
 
146
165
  def pdf_qnum_xy(q)
147
166
  [
148
- cell_x(q, 0).mm - pdf_qnum_width - @params[:responses][:number_margin].to_f.mm,
167
+ cell_x(q, 0).mm - pdf_qnum_width - @params[:items][:number_margin].to_f.mm,
149
168
  (reg_frame_height - cell_y(q)).mm - cell_height
150
169
  ]
151
170
  end
152
171
 
153
172
  def pdf_qnum_size
154
- @params[:responses][:number_size].to_f
173
+ @params[:items][:number_size].to_f
155
174
  end
156
175
 
157
176
  def pdf_chlett_size
158
- @params[:responses][:letter_size].to_f
177
+ @params[:items][:letter_size].to_f
159
178
  end
160
179
 
161
180
  def pdf_qnum_width
162
- @params[:responses][:number_width].to_f.mm
181
+ @params[:items][:number_width].to_f.mm
163
182
  end
164
183
 
165
184
  def pdf_header_xy(k)
@@ -192,7 +211,7 @@ module Mork
192
211
  @params[:header][k][:box] == true
193
212
  end
194
213
 
195
- def pdf_dark_control_cell_area
214
+ def pdf_ctrl_area_dark
196
215
  {
197
216
  p: [pdf_control_xy[0]+pdf_control_width+ctrl_margin.mm, pdf_control_xy[1]],
198
217
  w: cell_width.mm,
@@ -200,7 +219,7 @@ module Mork
200
219
  }
201
220
  end
202
221
 
203
- def pdf_light_control_cell_area
222
+ def pdf_ctrl_area_light
204
223
  {
205
224
  p: [cell_spacing.mm + pdf_control_xy[0]+pdf_control_width+@params[:control][:margin].to_f.mm, pdf_control_xy[1]],
206
225
  w: cell_width.mm,
@@ -209,7 +228,7 @@ module Mork
209
228
  end
210
229
 
211
230
  def pdf_dark_control_letter_xy
212
- xy = pdf_dark_control_cell_area[:p]
231
+ xy = pdf_ctrl_area_dark[:p]
213
232
  [
214
233
  xy[0] + 2.mm,
215
234
  xy[1] - 4.mm
@@ -217,7 +236,7 @@ module Mork
217
236
  end
218
237
 
219
238
  def pdf_light_control_letter_xy
220
- xy = pdf_light_control_cell_area[:p]
239
+ xy = pdf_ctrl_area_light[:p]
221
240
  [
222
241
  xy[0] + 2.mm,
223
242
  xy[1] - 4.mm
@@ -249,11 +268,13 @@ module Mork
249
268
  @py / reg_frame_height
250
269
  end
251
270
 
252
- def cw
271
+ def ppu_x
272
+ # horizontal pixels per unit of length (mm by def)
253
273
  @px / page_width
254
274
  end
255
275
 
256
- def ch
276
+ def ppu_y
277
+ # vertical pixels per unit of length (mm by def)
257
278
  @py / page_height
258
279
  end
259
280
 
@@ -269,24 +290,12 @@ module Mork
269
290
  }
270
291
  end
271
292
 
272
- # {} = reg_mark_search_area(x, y)
273
- #
274
- # the 4 values needed to locate a single registration mark
275
- def reg_mark_search_area(x, y)
276
- {
277
- x: (cw * x).round,
278
- y: (ch * y).round,
279
- w: (cw * reg_search).round,
280
- h: (ch * reg_search).round
281
- }
282
- end
283
-
284
293
  # cell_y(q)
285
294
  #
286
295
  # the distance from the registration frame to the top edge
287
296
  # of all choice cells in the q-th question
288
297
  def cell_y(q)
289
- first_y + response_spacing * (q % rows) - cell_height / 2
298
+ first_y + item_spacing * (q % rows) - cell_height / 2
290
299
  end
291
300
 
292
301
  # cell_x(q,c)
@@ -321,33 +330,53 @@ module Mork
321
330
  # = registration sides =
322
331
  # ======================
323
332
 
333
+ def rmx(corner, i)
334
+ case corner
335
+ when :tl; reg_off
336
+ when :tr; page_width - reg_search - reg_off - reg_step * i
337
+ when :br; page_width - reg_search - reg_off - reg_step * i
338
+ when :bl; reg_off
339
+ end
340
+ end
341
+
342
+ def rmy(corner, i)
343
+ case corner
344
+ when :tl; reg_off
345
+ when :tr; reg_off
346
+ when :br; page_height - reg_search - reg_off - reg_step * i
347
+ when :bl; page_height - reg_search - reg_off - reg_step * i
348
+ end
349
+ end
350
+
351
+ def reg_step
352
+ reg_radius
353
+ end
354
+
324
355
  # ===============================
325
356
  # = Simple parameter extraction =
326
357
  # ===============================
327
- def ctrl_cell_left() @params[:control][:left].to_f + @params[:control][:width].to_f + ctrl_margin end
358
+ def ctrl_cell_x() @params[:control][:left].to_f + @params[:control][:width].to_f + ctrl_margin end
328
359
  def ctrl_margin() @params[:control][:margin].to_f end
329
360
  def ctrl_cell_y() @params[:control][:top].to_f end
330
361
  def code_height() @params[:code][:height].to_f end
331
362
  def code_width() @params[:code][:width].to_f end
332
363
  def page_width() @params[:page_size][:width].to_f end
333
364
  def page_height() @params[:page_size][:height].to_f end
334
- def name_x() @params[:header][:name][:top].to_f end
335
- def name_y() @params[:header][:name][:left].to_f end
336
- def name_w() @params[:header][:name][:width].to_f end
337
- def name_size() @params[:header][:name][:size].to_f end
338
- def cell_width() @params[:responses][:cell_width].to_f end
339
- def cell_height() @params[:responses][:cell_height].to_f end
340
- def cell_spacing() @params[:responses][:x_spacing].to_f end
341
- def response_spacing() @params[:responses][:y_spacing].to_f end
342
- def column_width() @params[:responses][:column_width].to_f end
343
- def row_spacing() @params[:responses][:y_spacing].to_f end
344
- def first_x() @params[:responses][:first_x].to_f end
345
- def first_y() @params[:responses][:first_y].to_f end
346
- def rows() @params[:responses][:rows] end
347
- def columns() @params[:responses][:columns] end
365
+ def cell_width() @params[:items][:cell_width].to_f end
366
+ def cell_height() @params[:items][:cell_height].to_f end
367
+ def cell_spacing() @params[:items][:x_spacing].to_f end
368
+ def item_spacing() @params[:items][:y_spacing].to_f end
369
+ def column_width() @params[:items][:column_width].to_f end
370
+ def row_spacing() @params[:items][:y_spacing].to_f end
371
+ def first_x() @params[:items][:first_x].to_f end
372
+ def first_y() @params[:items][:first_y].to_f end
373
+ def rows() @params[:items][:rows] end
374
+ def columns() @params[:items][:columns] end
348
375
  def reg_margin() @params[:regmarks][:margin].to_f end
349
376
  def reg_search() @params[:regmarks][:search].to_f end
377
+ def reg_radius() @params[:regmarks][:radius].to_f end
350
378
  def reg_frame_width() page_width - reg_margin * 2 end
351
379
  def reg_frame_height() page_height - reg_margin * 2 end
380
+ def reg_off() @params[:regmarks][:offset].to_f end
352
381
  end
353
382
  end
@@ -4,13 +4,14 @@ module Mork
4
4
  # default units are millimiters
5
5
  page_size: {
6
6
  # this is A4
7
- width: 210.0,
8
- height: 297.0
7
+ width: 210,
8
+ height: 297
9
9
  }, # page end
10
10
  regmarks: {
11
11
  margin: 10,
12
12
  radius: 2.5,
13
- search: 10
13
+ search: 10,
14
+ offset: 3
14
15
  }, # regmarks end
15
16
  header: {
16
17
  name: {
@@ -40,7 +41,7 @@ module Mork
40
41
  box: true,
41
42
  }
42
43
  }, # header end
43
- responses: {
44
+ items: {
44
45
  columns: 4,
45
46
  column_width: 49,
46
47
  rows: 30,
@@ -65,7 +66,7 @@ module Mork
65
66
  number_margin: 2,
66
67
  # font size for the choice letter
67
68
  letter_size: 8
68
- }, # responses end
69
+ }, # items end
69
70
  code: {
70
71
  bits: 40,
71
72
  left: 15,
data/lib/mork/mimage.rb CHANGED
@@ -27,6 +27,15 @@ module Mork
27
27
  @image.composite! m, c[:x], c[:y], Magick::CopyYellowCompositeOp
28
28
  end
29
29
 
30
+ def join!(p)
31
+ poly = Magick::Draw.new
32
+ poly.fill_opacity 0
33
+ poly.stroke 'green'
34
+ poly.stroke_width 3
35
+ poly.polygon p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]
36
+ poly.draw @image
37
+ end
38
+
30
39
  # ============
31
40
  # = Cropping =
32
41
  # ============
data/lib/mork/npatch.rb CHANGED
@@ -15,13 +15,13 @@ module Mork
15
15
  end
16
16
 
17
17
  def dark_centroid
18
- return nil, nil unless sufficient_contrast?
18
+ sufficient_contrast? or return
19
19
  xp = patch.sum(1).to_a
20
20
  yp = patch.sum(0).to_a
21
21
  # find the intensity trough
22
22
  ctr_x = xp.find_index(xp.min)
23
23
  ctr_y = yp.find_index(yp.min)
24
- return nil, nil if edgy?(ctr_x, ctr_y)
24
+ # return :edgy if edgy?(ctr_x, ctr_y)
25
25
  return ctr_x, ctr_y
26
26
  end
27
27
 
@@ -44,6 +44,7 @@ module Mork
44
44
  end
45
45
 
46
46
  def edgy?(x, y)
47
+ puts "PATCH: (#{@width},#{@height}) EDGY: x=#{x}, y=#{y}"
47
48
  tol = 5
48
49
  (x < tol) or (y < tol) or (y > @height - tol) or (x > @width - tol)
49
50
  end
data/lib/mork/sheet.rb CHANGED
@@ -1,20 +1,28 @@
1
1
  module Mork
2
2
  class Sheet
3
3
  def initialize(im, grid=Grid.new)
4
- im = Mimage.new(im) if im.class == String
5
- raise "A new sheet requires either a Mimage or the name of the source image file" unless im.class == Mimage
4
+ @raw = case im.class.to_s
5
+ when "String"
6
+ Mimage.new im
7
+ when "Mimage"
8
+ im
9
+ else
10
+ raise "A new sheet requires either a Mimage or the name of the source image file"
11
+ end
6
12
  @grid = grid
7
13
  # send page size to the grid, so that all later measurements can be done within the
8
14
  # grid itself; this method assumes a 'stretch' strategy, i.e. where the image
9
15
  # after registration has the same size in pixels as the original scanned file
10
- @grid.set_page_size im.width, im.height
11
- @mimage = register(im)
16
+ @grid.set_page_size @raw.width, @raw.height
17
+ @rmsa = {}
18
+ @ok_reg = register @raw
12
19
  end
13
20
 
14
21
  # code
15
22
  #
16
23
  # returns the sheet code as an integer
17
24
  def code
25
+ return if not_registered
18
26
  code_string.to_i(2)
19
27
  end
20
28
 
@@ -23,6 +31,7 @@ module Mork
23
31
  # returns the sheet code as a string of 0s and 1s. The string is code_bits
24
32
  # bits long, with most significant bits to the left
25
33
  def code_string
34
+ return if not_registered
26
35
  cs = (0...@grid.code_bits).inject("") { |c, v| c << code_cell_value(v) }
27
36
  cs.reverse
28
37
  end
@@ -32,6 +41,7 @@ module Mork
32
41
  # returns true if the specified question/choice cell has been darkened
33
42
  # false otherwise
34
43
  def marked?(q, c)
44
+ return if not_registered
35
45
  shade_of(q, c) < choice_threshold
36
46
  end
37
47
 
@@ -44,6 +54,7 @@ module Mork
44
54
  # in which case the choices for the first n questions will be returned.
45
55
  # if called without arguments, all available choices will be evaluated
46
56
  def mark_array(r = nil)
57
+ return if not_registered
47
58
  question_range(r).collect do |q|
48
59
  cho = []
49
60
  (0...@grid.max_choices_per_question).each do |c|
@@ -54,6 +65,7 @@ module Mork
54
65
  end
55
66
 
56
67
  def mark_logical_array(r = nil)
68
+ return if not_registered
57
69
  question_range(r).collect do |q|
58
70
  (0...@grid.max_choices_per_question).collect {|c| marked?(q, c)}
59
71
  end
@@ -64,49 +76,65 @@ module Mork
64
76
  # ================
65
77
 
66
78
  def highlight_all
79
+ return if not_registered
67
80
  @grid.max_questions.times do |i|
68
81
  @grid.max_choices_per_question.times do |j|
69
82
  highlight_choice i, j
70
83
  end
71
84
  end
72
- @mimage.highlight! @grid.dark_control_cell_area
73
- @mimage.highlight! @grid.light_control_cell_area
85
+ @crop.highlight! @grid.ctrl_area_dark
86
+ @crop.highlight! @grid.ctrl_area_light
74
87
  end
75
88
 
76
89
  def highlight
90
+ return if not_registered
77
91
  mark_array.each_with_index do |qa, i|
78
92
  qa.each do |cho|
79
93
  highlight_choice i, cho
80
94
  end
81
95
  end
82
- @mimage.highlight! @grid.dark_control_cell_area
83
- @mimage.highlight! @grid.light_control_cell_area
96
+ @crop.highlight! @grid.ctrl_area_dark
97
+ @crop.highlight! @grid.ctrl_area_light
84
98
  end
85
99
 
86
100
  def highlight_choice(q, c)
87
- @mimage.highlight! @grid.choice_cell_area(q, c)
101
+ return if not_registered
102
+ @crop.highlight! @grid.choice_cell_area(q, c)
88
103
  end
89
104
 
90
105
  def highlight_code
106
+ return if not_registered
91
107
  @grid.code_bits.times do |bit|
92
- @mimage.highlight! @grid.code_bit_area bit
108
+ @crop.highlight! @grid.code_bit_area bit
93
109
  end
94
110
  end
95
111
 
96
112
  def highlight_code_bit(i)
97
- @mimage.highlight! @grid.code_bit_area(i)
113
+ @crop.highlight!(@grid.code_bit_area(i)) if @ok_reg
98
114
  end
99
115
 
100
116
  def highlight_dark_calibration_bit
101
- @mimage.highlight! @grid.black_calibration_area
117
+ @crop.highlight!(@grid.cal_area_black) if @ok_reg
102
118
  end
103
119
 
104
120
  def highlight_light_calibration_bit
105
- @mimage.highlight! @grid.white_calibration_area
121
+ @crop.highlight!(@grid.cal_area_white) if @ok_reg
122
+ end
123
+
124
+ def highlight_reg_area
125
+ @raw.highlight! @rmsa[:tl]
126
+ @raw.highlight! @rmsa[:tr]
127
+ @raw.highlight! @rmsa[:br]
128
+ @raw.highlight! @rmsa[:bl]
129
+ @raw.join!(@rm) if @ok_reg
106
130
  end
107
131
 
108
132
  def write(fname)
109
- @mimage.write(fname)
133
+ @crop.write(fname) if @ok_reg
134
+ end
135
+
136
+ def write_raw(fname)
137
+ @raw.write(fname)
110
138
  end
111
139
 
112
140
  private
@@ -137,13 +165,13 @@ module Mork
137
165
  end
138
166
 
139
167
  def choice_threshold
140
- @choice_threshold ||= (naverage(@grid.dark_control_cell_area) +
141
- naverage(@grid.light_control_cell_area)) / 2
168
+ @choice_threshold ||= (naverage(@grid.ctrl_area_dark) +
169
+ naverage(@grid.ctrl_area_light)) / 2
142
170
  end
143
171
 
144
172
  def code_threshold
145
- @code_threshold ||= (naverage(@grid.black_calibration_area) +
146
- naverage(@grid.white_calibration_area)) / 2
173
+ @code_threshold ||= (naverage(@grid.cal_area_black) +
174
+ naverage(@grid.cal_area_white)) / 2
147
175
  end
148
176
 
149
177
  # ================
@@ -151,30 +179,54 @@ module Mork
151
179
  # ================
152
180
 
153
181
  def register(img)
182
+ @rm = []
183
+ @reg_status = []
154
184
  # find the XY coordinates of the 4 registration marks
155
- x1, y1 = reg_centroid_on(img, @grid.reg_mark_tl_search_area)
156
- x2, y2 = reg_centroid_on(img, @grid.reg_mark_tr_search_area)
157
- x3, y3 = reg_centroid_on(img, @grid.reg_mark_br_search_area)
158
- x4, y4 = reg_centroid_on(img, @grid.reg_mark_bl_search_area)
185
+ @reg_status[0], @rm[0], @rm[1] = reg_centroid_on(img, :tl)
186
+ @reg_status[1], @rm[2], @rm[3] = reg_centroid_on(img, :tr)
187
+ @reg_status[2], @rm[4], @rm[5] = reg_centroid_on(img, :br)
188
+ @reg_status[3], @rm[6], @rm[7] = reg_centroid_on(img, :bl)
189
+ return false if @reg_status.any? { |c| c != :ok }
159
190
  # stretch the 4 points to fit the original size and return the resulting image
160
- img.stretch [
161
- x1, y1, 0, 0,
162
- x2, y2, img.width, 0,
163
- x3, y3, img.width, img.height,
164
- x4, y4, 0, img.height
191
+ @crop = img.stretch [
192
+ @rm[0], @rm[1], 0, 0,
193
+ @rm[2], @rm[3], img.width, 0,
194
+ @rm[4], @rm[5], img.width, img.height,
195
+ @rm[6], @rm[7], 0, img.height
165
196
  ]
197
+ true
166
198
  end
167
199
 
168
200
  # returns the centroid of the dark region within the given area
169
201
  # in the XY coordinates of the entire image
170
- def reg_centroid_on(img, c)
171
- cx, cy = NPatch.new(img.crop(c)).dark_centroid
172
- return nil, nil if cx.nil?
173
- return cx + c[:x], cy + c[:y]
202
+ def reg_centroid_on(img, corner)
203
+ 1000.times do |i|
204
+ @rmsa[corner] = @grid.rm_search_area(corner, i)
205
+ puts "RM: #{@rmsa[corner].inspect}"
206
+ cx, cy = NPatch.new(img.crop(@rmsa[corner])).dark_centroid
207
+ if cx.nil?
208
+ status = :insufficient_contrast
209
+ elsif (cx < @grid.rm_edgy_x) or
210
+ (cy < @grid.rm_edgy_y) or
211
+ (cy > @rmsa[corner][:h] - @grid.rm_edgy_y) or
212
+ (cx > @rmsa[corner][:w] - @grid.rm_edgy_x)
213
+ status = :edgy
214
+ else
215
+ return :ok, cx + @rmsa[corner][:x], cy + @rmsa[corner][:y]
216
+ end
217
+ return status if @rmsa[corner][:w] > @grid.rm_max_search_area_side
218
+ end
174
219
  end
175
220
 
176
221
  def naverage(where)
177
- NPatch.new(@mimage.crop where).average
222
+ NPatch.new(@crop.crop where).average
223
+ end
224
+
225
+ def not_registered
226
+ unless @ok_reg
227
+ puts "---=={ Unregistered image. Reason: '#{@reg_status.inspect}' }==---"
228
+ true
229
+ end
178
230
  end
179
231
  end
180
232
  end
@@ -73,12 +73,12 @@ module Mork
73
73
  font_size
74
74
  stroke_color "ff0000"
75
75
  # dark
76
- a = @grid.pdf_dark_control_cell_area
76
+ a = @grid.pdf_ctrl_area_dark
77
77
  rounded_rectangle a[:p], a[:w], a[:h], [a[:h], a[:w]].min / 2
78
78
  fill_color "ff0000"
79
79
  draw_text info[:labels][0], at: @grid.pdf_dark_control_letter_xy
80
80
  # light
81
- a = @grid.pdf_light_control_cell_area
81
+ a = @grid.pdf_ctrl_area_light
82
82
  rounded_rectangle a[:p], a[:w], a[:h], [a[:h], a[:w]].min / 2
83
83
  fill_color "ff0000"
84
84
  draw_text info[:labels][1], at: @grid.pdf_light_control_letter_xy
data/lib/mork/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mork
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -2,53 +2,190 @@ require 'spec_helper'
2
2
 
3
3
  module Mork
4
4
  describe Grid do
5
- let(:grid) { Grid.new }
6
-
7
- describe "#initialize" do
8
- it "returns a Grid object" do
9
- grid.should be_a(Grid)
5
+ context 'default grid', focus: true do
6
+ before(:all) do
7
+ @grid = Grid.new
8
+ @grid.set_page_size 1601, 2281 # the size of sample01.jpg
10
9
  end
11
- end
12
-
13
- describe "#cell_x" do
14
- context "for 1st-column questions" do
15
- it "returns the distance from the registration frame of the left edge of the 1st choice" do
16
- grid.send(:cell_x,0,0).should == 7.5
10
+
11
+ describe '#max_questions' do
12
+ it 'returns the maximum number of questions in a sheet' do
13
+ @grid.max_questions.should == 120
14
+ end
15
+ end
16
+
17
+ describe '#max_choices_per_question' do
18
+ it 'returns the maximum number of choice cells per question' do
19
+ @grid.max_choices_per_question.should == 5
20
+ end
21
+ end
22
+
23
+ describe '#code_bits' do
24
+ it 'returns the number of bits used to define the form code' do
25
+ @grid.code_bits.should == 40
17
26
  end
18
27
 
19
- it "returns the distance from the registration frame of the left edge of the 2nd choice" do
20
- grid.send(:cell_x,0,1).should == 14.5
28
+ it 'returns an integer' do
29
+ @grid.code_bits.should be_a Fixnum
30
+ end
31
+ end
32
+
33
+ describe '#rm_search_area' do
34
+ context 'on the first iteration' do
35
+ it 'returns an {:x, :y, :w, :h} hash of coordinates in pixels for the :tl reg_mark corner' do
36
+ c = @grid.rm_search_area :tl, 0
37
+ c.should == { x: 23, y: 23, w: 76, h: 77 }
38
+ end
39
+
40
+ it 'returns an {:x, :y, :w, :h} hash of coordinates in pixels for the :tr reg_mark corner' do
41
+ c = @grid.rm_search_area :tr, 0
42
+ c.should == { x: 1502, y: 23, w: 76, h: 77 }
43
+ end
44
+
45
+ it 'returns an {:x, :y, :w, :h} hash of coordinates in pixels for the :br reg_mark corner' do
46
+ c = @grid.rm_search_area :br, 0
47
+ c.should == { x: 1502, y: 2181, w: 76, h: 77 }
48
+ end
49
+
50
+ it 'returns an {:x, :y, :w, :h} hash of coordinates in pixels for the :bl reg_mark corner' do
51
+ c = @grid.rm_search_area :bl, 0
52
+ c.should == { x: 23, y: 2181, w: 76, h: 77 }
53
+ end
54
+ end
55
+
56
+ context 'on the third iteration' do
57
+ it 'returns an {:x, :y, :w, :h} hash of coordinates in pixels for the :tl reg_mark corner' do
58
+ c = @grid.rm_search_area :tl, 2
59
+ c.should == { x: 23, y: 23, w: 114, h: 115 }
60
+ end
61
+
62
+ it 'returns an {:x, :y, :w, :h} hash of coordinates in pixels for the :tr reg_mark corner' do
63
+ c = @grid.rm_search_area :tr, 2
64
+ c.should == { x: 1464, y: 23, w: 114, h: 115 }
65
+ end
66
+
67
+ it 'returns an {:x, :y, :w, :h} hash of coordinates in pixels for the :br reg_mark corner' do
68
+ c = @grid.rm_search_area :br, 2
69
+ c.should == { x: 1464, y: 2143, w: 114, h: 115 }
70
+ end
71
+
72
+ it 'returns an {:x, :y, :w, :h} hash of coordinates in pixels for the :bl reg_mark corner' do
73
+ c = @grid.rm_search_area :bl, 2
74
+ c.should == { x: 23, y: 2143, w: 114, h: 115 }
75
+ end
76
+ end
77
+ end
78
+
79
+ describe '#rm_edgy_x' do
80
+ it 'returns the minimum acceptable number of pixels from the regmark center to the edge of the rm_search_area' do
81
+ @grid.rm_edgy_x.should == 24
82
+ end
83
+ it 'returns an integer' do
84
+ @grid.rm_edgy_x.should be_a Fixnum
21
85
  end
22
86
  end
23
87
 
24
- context "for 4th-column questions" do
25
- it "returns the distance from the registration frame of the left edge of the 1st choice" do
26
- grid.send(:cell_x,120,0).should == 157.5
88
+ describe '#rm_edgy_y' do
89
+ it 'returns the minimum acceptable number of pixels from the regmark center to the edge of the rm_search_area' do
90
+ @grid.rm_edgy_y.should == 24
91
+ end
92
+ it 'returns an integer' do
93
+ @grid.rm_edgy_y.should be_a Fixnum
94
+ end
95
+ end
96
+
97
+ describe '#rm_max_search_area_side' do
98
+ it 'returns the maximum extent of the regmark search area, 1/4 of the raw image horizontal pixels' do
99
+ @grid.rm_max_search_area_side.should == 400
100
+ end
101
+ it 'returns an integer' do
102
+ @grid.rm_max_search_area_side.should be_a Fixnum
103
+ end
104
+ end
105
+
106
+ describe '#choice_cell_area' do
107
+ it 'returns the coordinates of the first choice cell' do
108
+ @grid.choice_cell_area(0,0).should == {x: 63, y: 436, w: 51, h: 41}
27
109
  end
28
110
 
29
- it "returns the distance from the registration frame of the left edge of the 2nd choice" do
30
- grid.send(:cell_x,120,1).should == 164.5
111
+ it 'returns the coordinates of the last choice cell' do
112
+ @grid.choice_cell_area(119,4).should == {x: 1538, y: 2108, w: 51, h: 41}
31
113
  end
32
114
  end
33
- end
34
-
35
- describe "#cell_y" do
36
- it "returns the distance from the registration frame of the top edge of the 1st row of cells" do
37
- grid.send(:cell_y,0).should == 33.5
115
+
116
+ describe '#ctrl_area_dark' do
117
+ it 'returns the coordinates of the control cell used to set the darkened threshold' do
118
+ @grid.ctrl_area_dark.should == {:x=>1479, :y=>329, :w=>51, :h=>41}
119
+ end
38
120
  end
39
121
 
40
- it "returns the distance from the registration frame of the 40th row of cells" do
41
- grid.send(:cell_y,39).should == 267.5
122
+ describe '#ctrl_area_light' do
123
+ it 'returns the coordinates of the control cell used to set the darkened threshold' do
124
+ @grid.ctrl_area_light.should == {:x=>1538, :y=>329, :w=>51, :h=>41}
125
+ end
42
126
  end
43
- end
44
-
45
- describe "#cell_xy" do
46
- it "returns the left and top distances from the registration frame of a sample cell" do
47
- x, y = grid.send(:cell_xy,54, 3)
48
- x.should == 78.5
49
- y.should == 117.5
127
+
128
+ describe '#cal_area_white' do
129
+ it 'returns the coordinates of the white area used for code calibration' do
130
+ @grid.cal_area_white.should == {:x=>93, :y=>2260, :w=>25, :h=>21}
131
+ end
50
132
  end
133
+
134
+ describe '#cal_area_black' do
135
+ it 'returns the coordinates of the code calibration bar' do
136
+ @grid.cal_area_black.should == {:x=>126, :y=>2260, :w=>25, :h=>21}
137
+ end
138
+ end
139
+
140
+ describe '#code_bit_area' do
141
+ it 'returns the coordinates of the first code bit area' do
142
+ @grid.code_bit_area(0).should == {:x=>160, :y=>2260, :w=>25, :h=>21}
143
+ end
144
+
145
+ it 'returns the coordinates of the last code bit area' do
146
+ @grid.code_bit_area(39).should == {:x=>1475, :y=>2260, :w=>25, :h=>21}
147
+ end
148
+
149
+ it 'fails if an invalid code bit is requested' do
150
+ lambda { @grid.code_bit_area(40) }.should raise_error
151
+ end
152
+ end
153
+
154
+
51
155
  end
156
+
157
+
158
+ # describe "#cell_x" do
159
+ # context "for 1st-column questions" do
160
+ # it "returns the distance from the registration frame of the left edge of the 1st choice" do
161
+ # grid.send(:cell_x,0,0).should == 7.5
162
+ # end
163
+ #
164
+ # it "returns the distance from the registration frame of the left edge of the 2nd choice" do
165
+ # grid.send(:cell_x,0,1).should == 14.5
166
+ # end
167
+ # end
168
+ #
169
+ # context "for 4th-column questions" do
170
+ # it "returns the distance from the registration frame of the left edge of the 1st choice" do
171
+ # grid.send(:cell_x,120,0).should == 157.5
172
+ # end
173
+ #
174
+ # it "returns the distance from the registration frame of the left edge of the 2nd choice" do
175
+ # grid.send(:cell_x,120,1).should == 164.5
176
+ # end
177
+ # end
178
+ # end
179
+ #
180
+ # describe "#cell_y" do
181
+ # it "returns the distance from the registration frame of the top edge of the 1st row of cells" do
182
+ # grid.send(:cell_y,0).should == 33.5
183
+ # end
184
+ #
185
+ # it "returns the distance from the registration frame of the 40th row of cells" do
186
+ # grid.send(:cell_y,39).should == 267.5
187
+ # end
188
+ # end
52
189
  end
53
190
  end
54
191
 
@@ -53,7 +53,7 @@ module Mork
53
53
  box: true,
54
54
  }
55
55
  }, # header end
56
- responses: {
56
+ items: {
57
57
  columns: 4,
58
58
  column_width: 49,
59
59
  rows: 30,
@@ -4,7 +4,7 @@ module Mork
4
4
  describe Sheet do
5
5
  context "describing marked choices" do
6
6
  before(:all) do
7
- @sheet = Sheet.new('spec/samples/sample02.jpg')
7
+ @sheet = Sheet.new('spec/samples/sample01.jpg', Grid.new('spec/samples/grid01.yml'))
8
8
  end
9
9
 
10
10
  describe "#marked?" do
File without changes
@@ -0,0 +1,58 @@
1
+ page_size:
2
+ width: 210
3
+ height: 297
4
+ regmarks:
5
+ margin: 10
6
+ radius: 2.5
7
+ search: 12
8
+ offset: 2
9
+ header:
10
+ name:
11
+ top: 5
12
+ left: 7.5
13
+ width: 170
14
+ size: 14
15
+ title:
16
+ top: 15
17
+ left: 7.5
18
+ width: 180
19
+ size: 12
20
+ code:
21
+ top: 5
22
+ left: 165
23
+ width: 20
24
+ size: 14
25
+ signature:
26
+ top: 30
27
+ left: 7.5
28
+ width: 120
29
+ height: 15
30
+ size: 7
31
+ box: true
32
+ items:
33
+ columns: 4
34
+ column_width: 49
35
+ rows: 30
36
+ first_x: 10.5
37
+ first_y: 55.5
38
+ x_spacing: 7.0
39
+ y_spacing: 7.0
40
+ cell_width: 6.0
41
+ cell_height: 5.0
42
+ max_cells: 5
43
+ number_size: 10
44
+ number_width: 8
45
+ number_margin: 2
46
+ letter_size: 8
47
+ code:
48
+ bits: 40
49
+ left: 15
50
+ width: 3.0
51
+ height: 2.5
52
+ spacing: 4
53
+ control:
54
+ top: 40
55
+ left: 123
56
+ width: 50
57
+ size: 9
58
+ margin: 2.5
Binary file
Binary file
Binary file
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,10 @@
1
1
  require 'mork'
2
2
 
3
+ RSpec.configure do |c|
4
+ # filter_run is short-form alias for filter_run_including
5
+ c.filter_run focus: true
6
+ end
7
+
3
8
  class SampleImager
4
9
  attr_reader :info, :reg_marks, :q_boxes, :code_string, :code_int
5
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mork
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Giuseppe Bertini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-04 00:00:00.000000000 Z
11
+ date: 2014-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: narray
@@ -183,16 +183,16 @@ files:
183
183
  - spec/mork/npatch_spec.rb
184
184
  - spec/mork/sheet_pdf_spec.rb
185
185
  - spec/mork/sheet_spec.rb
186
- - spec/samples/22161694.pdf
187
186
  - spec/samples/code_sample.pdf
188
187
  - spec/samples/code_sample.png
189
188
  - spec/samples/code_zero.pdf
189
+ - spec/samples/content01.yml
190
+ - spec/samples/grid01.yml
190
191
  - spec/samples/info.yml
191
- - spec/samples/qzc006.jpg
192
192
  - spec/samples/reg_mark.jpg
193
- - spec/samples/sample.pages
194
193
  - spec/samples/sample01.jpg
195
194
  - spec/samples/sample02.jpg
195
+ - spec/samples/sample03.jpg
196
196
  - spec/samples/sheet1.jpg
197
197
  - spec/samples/sheet1.pdf
198
198
  - spec/samples/two_pages.pdf
@@ -227,16 +227,16 @@ test_files:
227
227
  - spec/mork/npatch_spec.rb
228
228
  - spec/mork/sheet_pdf_spec.rb
229
229
  - spec/mork/sheet_spec.rb
230
- - spec/samples/22161694.pdf
231
230
  - spec/samples/code_sample.pdf
232
231
  - spec/samples/code_sample.png
233
232
  - spec/samples/code_zero.pdf
233
+ - spec/samples/content01.yml
234
+ - spec/samples/grid01.yml
234
235
  - spec/samples/info.yml
235
- - spec/samples/qzc006.jpg
236
236
  - spec/samples/reg_mark.jpg
237
- - spec/samples/sample.pages
238
237
  - spec/samples/sample01.jpg
239
238
  - spec/samples/sample02.jpg
239
+ - spec/samples/sample03.jpg
240
240
  - spec/samples/sheet1.jpg
241
241
  - spec/samples/sheet1.pdf
242
242
  - spec/samples/two_pages.pdf
Binary file
Binary file
Binary file