mork 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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