ruby_marks 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 37a5334e1a3c2ea8f16932a3e7a6717169c44992
4
+ data.tar.gz: 8b3c73f1b8ad991af9a8d31d138c30d630faa616
5
+ SHA512:
6
+ metadata.gz: a82005142fcfd71856f789ba472bf8c40ec10fac8e5896d28443f5adc147c7f1f3d5683add03fd16f786f87a32642e473d782c4c518b1bd92434f2b6177c7045
7
+ data.tar.gz: 4daa86c2fc1a53c61c355436b14ce56694bf760e1a38605f637a9bc0e0e6fed9d7fe087965b2318455a7315a247000e96a3377c980fa81a07839e9b366934a3b
@@ -0,0 +1,53 @@
1
+ module RubyMarks
2
+
3
+ class FloodScan
4
+
5
+ def scan(image, node, width, height)
6
+ target = Magick::Pixel.new(65535, 65535, 65535, 0)
7
+ replacement = Magick::Pixel.new(0, 0, 0, 0)
8
+ queue = Array.new
9
+ queue.push(node)
10
+ vx = Hash.new { |hash, key| hash[key] = [] }
11
+ vy = Hash.new { |hash, key| hash[key] = [] }
12
+ steps = 0
13
+ until queue.empty?
14
+ node = queue.shift
15
+ x = node.x
16
+ y = node.y
17
+ span_up = false;
18
+ span_down = false;
19
+ while x > 0 && image.get_pixels(x - 1, y, 1, 1)[0] == target
20
+ x -= 1
21
+ end
22
+ while x < image.columns && image.get_pixels(x, y, 1, 1)[0] == target
23
+ image.store_pixels(x, y, 1, 1, [replacement])
24
+ if !span_up && y > 0 && image.get_pixels(x, y - 1, 1, 1)[0] == target
25
+ queue.push(Magick::Point.new(x, y - 1))
26
+ span_up = true
27
+ elsif span_up && y > 0 && image.get_pixels(x, y - 1, 1, 1)[0] != target
28
+ span_up = false
29
+ end
30
+ if !span_down && y < image.rows - 1 && image.get_pixels(x, y + 1, 1, 1)[0] == target
31
+ queue.push(Magick::Point.new(x, y + 1))
32
+ span_down = true
33
+ elsif span_down && y < image.rows - 1 && image.get_pixels(x, y + 1, 1, 1)[0] != target
34
+ span_down = false
35
+ end
36
+ vx[y] << x
37
+ vy[x] << y
38
+ x += 1
39
+ steps += 1
40
+ end
41
+ queue.push(Magick::Point.new(x, y - 1)) if queue.empty? && steps < 100
42
+ end
43
+ vx = vx.find_mesure(width, 5).max_frequency
44
+ vy = vy.find_mesure(height, 5).max_frequency
45
+ if vx && vy
46
+ {x1: vx[0][0], y1: vy[0][0], x2: vx[0][1], y2: vy[0][1]}
47
+ else
48
+ {}
49
+ end
50
+ end
51
+ end
52
+
53
+ end
File without changes
File without changes
@@ -119,26 +119,48 @@ module RubyMarks
119
119
  end
120
120
 
121
121
 
122
- def detect_groups
123
- file_str = RubyMarks::ImageUtils.export_file_to_str(@file)
124
- original_file_str = RubyMarks::ImageUtils.export_file_to_str(@original_file)
125
- incorrect_bubble_line_found = Hash.new { |hash, key| hash[key] = [] }
126
- bubbles_adjusted = []
127
- incorrect_expected_lines = false
122
+ def detect_groups
123
+ if @config.scan_mode == :grid
124
+ scaner = RubyMarks::FloodScan.new
125
+ @groups.each_pair do |label, group|
126
+ group_center = RubyMarks::ImageUtils.image_center(group.expected_coordinates)
127
+ x = group_center[:x]
128
+ y = group_center[:y]
129
+ width = RubyMarks::ImageUtils.calc_width(group.expected_coordinates[:x1], group.expected_coordinates[:x2])
130
+ height = RubyMarks::ImageUtils.calc_height(group.expected_coordinates[:y1], group.expected_coordinates[:y2])
131
+
132
+ block = scaner.scan(@file.dup, Magick::Point.new(x, y), width, height)
133
+ if block
134
+ group.coordinates = {x1: block[:x1], x2: block[:x2], y1: block[:y1], y2: block[:y2]}
135
+ marks_blocks = find_marks_grid(group)
136
+ marks_blocks.each do |mark|
137
+ mark_width = RubyMarks::ImageUtils.calc_width(mark[:x1], mark[:x2])
138
+ mark_height = RubyMarks::ImageUtils.calc_height(mark[:y1], mark[:y2])
139
+ mark_file = @original_file.crop(mark[:x1], mark[:y1], mark_width, mark_height)
140
+ o_mark = RubyMarks::Mark.new group: group,
141
+ coordinates: {x1: mark[:x1], y1: mark[:y1], x2: mark[:x2], y2: mark[:y2]},
142
+ image_str: RubyMarks::ImageUtils.export_file_to_str(mark_file),
143
+ line: mark[:line]
144
+ group.marks[mark[:line]] << o_mark
145
+ end
146
+ end
147
+ end
148
+ else
149
+ file_str = RubyMarks::ImageUtils.export_file_to_str(@file)
150
+ original_file_str = RubyMarks::ImageUtils.export_file_to_str(@original_file)
151
+ incorrect_bubble_line_found = Hash.new { |hash, key| hash[key] = [] }
152
+ bubbles_adjusted = []
153
+ incorrect_expected_lines = false
128
154
 
129
- @groups.each_pair do |label, group|
130
- next unless group.expected_coordinates.any?
155
+ @groups.each_pair do |label, group|
156
+ next unless group.expected_coordinates.any?
131
157
 
132
- line = 0
133
- group_center = RubyMarks::ImageUtils.image_center(group.expected_coordinates)
158
+ line = 0
159
+ group_center = RubyMarks::ImageUtils.image_center(group.expected_coordinates)
134
160
 
135
- block = find_block_marks(file_str, group_center[:x], group_center[:y], group)
136
- if block
137
- group.coordinates = {x1: block[:x1], x2: block[:x2], y1: block[:y1], y2: block[:y2]}
138
-
139
- if @config.scan_mode == :grid
140
- marks_blocks = find_marks_grid(group)
141
- else
161
+ block = find_block_marks(file_str, group_center[:x], group_center[:y], group)
162
+ if block
163
+ group.coordinates = {x1: block[:x1], x2: block[:x2], y1: block[:y1], y2: block[:y2]}
142
164
  marks_blocks = find_marks(original_file_str, group)
143
165
  marks_blocks.sort!{ |a,b| a[:y1] <=> b[:y1] }
144
166
  mark_ant = nil
@@ -237,33 +259,32 @@ module RubyMarks
237
259
  end
238
260
 
239
261
  end
240
- end
241
262
 
242
- marks_blocks.each do |mark|
243
- mark_width = RubyMarks::ImageUtils.calc_width(mark[:x1], mark[:x2])
244
- mark_height = RubyMarks::ImageUtils.calc_height(mark[:y1], mark[:y2])
245
- mark_file = @original_file.crop(mark[:x1], mark[:y1], mark_width, mark_height)
246
- o_mark = RubyMarks::Mark.new group: group,
247
- coordinates: {x1: mark[:x1], y1: mark[:y1], x2: mark[:x2], y2: mark[:y2]},
248
- image_str: RubyMarks::ImageUtils.export_file_to_str(mark_file),
249
- line: mark[:line]
250
- group.marks[mark[:line]] << o_mark
251
- end
263
+ marks_blocks.each do |mark|
264
+ mark_width = RubyMarks::ImageUtils.calc_width(mark[:x1], mark[:x2])
265
+ mark_height = RubyMarks::ImageUtils.calc_height(mark[:y1], mark[:y2])
266
+ mark_file = @original_file.crop(mark[:x1], mark[:y1], mark_width, mark_height)
267
+ o_mark = RubyMarks::Mark.new group: group,
268
+ coordinates: {x1: mark[:x1], y1: mark[:y1], x2: mark[:x2], y2: mark[:y2]},
269
+ image_str: RubyMarks::ImageUtils.export_file_to_str(mark_file),
270
+ line: mark[:line]
271
+ group.marks[mark[:line]] << o_mark
272
+ end
252
273
 
253
- incorrect_expected_lines = group.incorrect_expected_lines
274
+ incorrect_expected_lines = group.incorrect_expected_lines
254
275
 
255
- group.marks.each_pair do |line, marks|
256
- if marks.count != group.marks_options.count
257
- incorrect_bubble_line_found[group.label.to_sym] << line
258
- end
259
- end
276
+ group.marks.each_pair do |line, marks|
277
+ if marks.count != group.marks_options.count
278
+ incorrect_bubble_line_found[group.label.to_sym] << line
279
+ end
280
+ end
281
+ end
282
+ end
283
+ @groups_detected = true
284
+ if incorrect_bubble_line_found.any? || bubbles_adjusted.any? || incorrect_expected_lines
285
+ raise_watcher :incorrect_group_watcher, incorrect_expected_lines, incorrect_bubble_line_found, bubbles_adjusted.flatten
260
286
  end
261
- end
262
- @groups_detected = true
263
- if incorrect_bubble_line_found.any? || bubbles_adjusted.any? || incorrect_expected_lines
264
- raise_watcher :incorrect_group_watcher, incorrect_expected_lines, incorrect_bubble_line_found, bubbles_adjusted.flatten
265
287
  end
266
-
267
288
  end
268
289
 
269
290
 
@@ -281,27 +302,35 @@ module RubyMarks
281
302
  found_blocks << block
282
303
 
283
304
  block[:width] = RubyMarks::ImageUtils.calc_width(block[:x1], block[:x2])
284
- block[:height] = RubyMarks::ImageUtils.calc_height(block[:y1], block[:y2])
305
+ block[:height] = RubyMarks::ImageUtils.calc_height(block[:y1], block[:y2])
285
306
 
286
307
  if @config.scan_mode == :grid
287
- if block[:width] >= expected_width + group.block_width_tolerance
288
- ajust_width = block[:width] - expected_width
289
- if @config.auto_ajust_block_width == :left
290
- block[:x2] = (block[:x2] - ajust_width) + @config.edge_level
291
- block[:width] = expected_width + @config.edge_level
292
- elsif @config.auto_ajust_block_width == :right
293
- block[:x1] = (block[:x1] + ajust_width) - @config.edge_level
294
- block[:width] = expected_width + @config.edge_level
308
+ unless block[:width] <= (expected_width + group.block_width_tolerance) && block[:width] >= (expected_width - group.block_width_tolerance)
309
+ if block[:width] > expected_width + group.block_width_tolerance
310
+ ajust_width = block[:width] - expected_width
311
+ if @config.auto_ajust_block_width == :left
312
+ block[:x2] = (block[:x2] - ajust_width) + @config.edge_level
313
+ block[:width] = expected_width + @config.edge_level
314
+ elsif @config.auto_ajust_block_width == :right
315
+ block[:x1] = (block[:x1] + ajust_width) - @config.edge_level
316
+ block[:width] = expected_width + @config.edge_level
317
+ end
318
+ else
319
+ block[:width] = 0
295
320
  end
296
321
  end
297
- if block[:height] > expected_height
298
- ajust_width = block[:height] - expected_height
299
- if @config.auto_ajust_block_height == :top
300
- block[:y2] = (block[:y2] - ajust_height) + @config.edge_level
301
- block[:height] = expected_height + @config.edge_level
302
- elsif @config.auto_ajust_block_height == :bottom
303
- block[:y1] = (block[:y1] + ajust_height) - @config.edge_level
304
- block[:height] = expected_height + @config.edge_level
322
+ unless block[:height] <= (expected_height + group.block_height_tolerance) && block[:height] >= (expected_height - group.block_height_tolerance)
323
+ if block[:height] > expected_height + group.block_height_tolerance
324
+ ajust_width = block[:height] - expected_height
325
+ if @config.auto_ajust_block_height == :top
326
+ block[:y2] = (block[:y2] - ajust_height) + @config.edge_level
327
+ block[:height] = expected_height + @config.edge_level
328
+ elsif @config.auto_ajust_block_height == :bottom
329
+ block[:y1] = (block[:y1] + ajust_height) - @config.edge_level
330
+ block[:height] = expected_height + @config.edge_level
331
+ end
332
+ else
333
+ block[:height] = 0
305
334
  end
306
335
  end
307
336
  end
@@ -311,7 +340,6 @@ module RubyMarks
311
340
 
312
341
  return block if block_width_with_tolerance >= expected_width &&
313
342
  block_height_with_tolerance >= expected_height
314
-
315
343
  end
316
344
  end
317
345
 
@@ -327,16 +355,16 @@ module RubyMarks
327
355
  block_width = RubyMarks::ImageUtils.calc_width(block[:x1], block[:x2])
328
356
  block_height = RubyMarks::ImageUtils.calc_height(block[:y1], block[:y2])
329
357
  lines = group.expected_lines
330
- columns = @config.default_marks_options.size
331
- distance_lin = @config.default_mark_height
332
- distance_col = @config.default_mark_width
358
+ columns = group.marks_options.size
359
+ distance_lin = (block_height / lines).ceil
360
+ distance_col = (block_width / columns).ceil
333
361
  lines.times do |lin|
334
362
  columns.times do |col|
335
363
 
336
- blocks << { :x1 => @config.edge_level + block[:x1] + (col * distance_col) + ((col % 2 == 1) ? 0 : 1),
337
- :y1 => @config.edge_level + block[:y1] + (lin * distance_lin) + ((lin % 2 == 1) ? 0 : 1),
338
- :x2 => @config.edge_level + block[:x1] + (col * distance_col) + distance_col + ((col % 2 == 1) ? 0 : 1),
339
- :y2 => @config.edge_level + block[:y1] + (lin * distance_lin) + distance_lin + ((lin % 2 == 1) ? 0 : 1),
364
+ blocks << { :x1 => block[:x1] + (col * distance_col),
365
+ :y1 => block[:y1] + (lin * distance_lin),
366
+ :x2 => block[:x1] + (col * distance_col) + distance_col,
367
+ :y2 => block[:y1] + (lin * distance_lin) + distance_lin,
340
368
  :line => lin + 1 }
341
369
  end
342
370
  end
@@ -419,7 +447,7 @@ module RubyMarks
419
447
  end
420
448
  rescue Timeout::Error
421
449
  raise_watcher :timed_out_watcher
422
- return false
450
+ return file
423
451
  end
424
452
 
425
453
  @groups.each_pair do |label, group|
@@ -496,7 +524,7 @@ module RubyMarks
496
524
  def add_mark(file, position, mark=nil)
497
525
  dr = Magick::Draw.new
498
526
  if @config.scan_mode == :grid
499
- dr.annotate(file, 0, 0, position[:x]-9, position[:y]+5, mark.intensity.ceil.to_s) do
527
+ dr.annotate(file, 0, 0, position[:x]-9, position[:y]+5, mark.intensity ? mark.intensity.ceil.to_s : '+' ) do
500
528
  self.pointsize = 15
501
529
  self.fill = RubyMarks::COLORS[2]
502
530
  end
@@ -1,3 +1,3 @@
1
1
  module RubyMarks
2
- VERSION = "0.3.5".freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
data/lib/ruby_marks.rb CHANGED
@@ -68,9 +68,36 @@ module RubyMarks
68
68
  ]
69
69
  end
70
70
 
71
+ class Array
72
+ def to_ranges
73
+ compact.sort.uniq.inject([]) do |r,x|
74
+ r.empty? || r.last.last.succ != x ? r << [x,x] : r[0..-2] << [r.last.first, x]
75
+ end
76
+ end
77
+
78
+ def max_frequency
79
+ group_by{ |w| w }
80
+ .map{ |w, v| [w, v.size] }
81
+ .max { |a, b| a[1] <=> b[1] }
82
+ end
83
+ end
84
+
85
+ class Hash
86
+ def find_mesure(measure, tolerance)
87
+ ax = []
88
+ each do |k, v|
89
+ max = v.max { |a, b| a <=> b }
90
+ min = v.min { |a, b| a <=> b }
91
+ ax << [min, max] if max - min >= measure - tolerance && max - min <= measure + tolerance
92
+ end
93
+ ax
94
+ end
95
+ end
96
+
71
97
  require 'ruby_marks/config'
72
98
  require 'ruby_marks/group'
73
99
  require 'ruby_marks/image_utils'
74
100
  require 'ruby_marks/mark'
75
101
  require 'ruby_marks/recognizer'
76
- require 'ruby_marks/watcher'
102
+ require 'ruby_marks/watcher'
103
+ require 'ruby_marks/flood_scan'
@@ -5,15 +5,13 @@ class RubyMarks::RecognizerGridTest < Test::Unit::TestCase
5
5
  def setup
6
6
  @file = 'assets/sheet_demo_grid.png'
7
7
  @recognizer = RubyMarks::Recognizer.new
8
- @positions = {}
9
- @positions[:marked_position] = {x: 161, y: 794}
10
- @positions[:unmarked_position] = {x: 161, y: 994}
11
8
 
12
9
  @recognizer.configure do |config|
13
10
 
14
11
  config.scan_mode = :grid
12
+ config.edge_level = 4
15
13
  config.default_expected_lines = 5
16
- config.intensity_percentual = 50
14
+ config.intensity_percentual = 45
17
15
  config.default_mark_height = 25
18
16
  config.default_mark_width = 25
19
17
  config.auto_ajust_block_width = :right
@@ -24,24 +22,26 @@ class RubyMarks::RecognizerGridTest < Test::Unit::TestCase
24
22
  end
25
23
 
26
24
  config.define_group :dois do |group|
27
- group.expected_coordinates = {x1: 350, y1: 200, x2: 475, y2: 350}
25
+ group.expected_coordinates = {x1: 350, y1: 235, x2: 475, y2: 360}
28
26
  end
29
27
 
30
28
  config.define_group :tres do |group|
31
- group.expected_coordinates = {x1: 570, y1: 200, x2: 695, y2: 350}
29
+ group.expected_coordinates = {x1: 570, y1: 235, x2: 695, y2: 360}
32
30
  end
33
31
 
34
32
  config.define_group :quatro do |group|
35
- group.expected_coordinates = {x1: 790, y1: 200, x2: 915, y2: 350}
33
+ group.expected_coordinates = {x1: 790, y1: 235, x2: 915, y2: 360}
36
34
  end
37
35
 
38
36
  config.define_group :cinco do |group|
39
- group.expected_coordinates = {x1: 1010, y1: 200, x2: 1135, y2: 350}
37
+ group.expected_coordinates = {x1: 1010, y1: 235, x2: 1135, y2: 360}
40
38
  end
41
39
  end
42
40
 
43
41
  @recognizer.file = @file
44
42
 
43
+ file = @recognizer.file
44
+ file.write("sheet_demo_grid3.png")
45
45
  end
46
46
 
47
47
  def test_should_return_the_recognizer_with_all_marks_flagged
@@ -98,4 +98,5 @@ class RubyMarks::RecognizerGridTest < Test::Unit::TestCase
98
98
  result.delete_if { |group, lines| lines.empty? }
99
99
  assert_equal expected_hash, result
100
100
  end
101
+
101
102
  end
@@ -1,7 +1,7 @@
1
1
  require "test_helper"
2
2
 
3
3
  class RubyMarks::RecognizerTest < Test::Unit::TestCase
4
-
4
+ =begin
5
5
  def setup
6
6
  @file = 'assets/sheet_demo1.png'
7
7
  @recognizer = RubyMarks::Recognizer.new
@@ -42,7 +42,6 @@ class RubyMarks::RecognizerTest < Test::Unit::TestCase
42
42
 
43
43
  end
44
44
 
45
- =begin
46
45
  def test_should_initialize_a_recognizer_with_a_valid_file
47
46
  assert_equal @file, @recognizer.filename
48
47
  end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_marks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
5
- prerelease:
4
+ version: 0.4.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - André Rodrigues
@@ -15,7 +14,6 @@ dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: rmagick
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
18
  - - '='
21
19
  - !ruby/object:Gem::Version
@@ -23,7 +21,6 @@ dependencies:
23
21
  type: :runtime
24
22
  prerelease: false
25
23
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
24
  requirements:
28
25
  - - '='
29
26
  - !ruby/object:Gem::Version
@@ -37,24 +34,26 @@ extensions: []
37
34
  extra_rdoc_files: []
38
35
  files:
39
36
  - README.md
40
- - lib/ruby_marks.rb
41
- - lib/ruby_marks/version.rb
42
- - lib/ruby_marks/support.rb
37
+ - lib/ruby_marks/image_utils.rb
43
38
  - lib/ruby_marks/mark.rb
44
- - lib/ruby_marks/group.rb
45
39
  - lib/ruby_marks/watcher.rb
46
40
  - lib/ruby_marks/recognizer.rb
41
+ - lib/ruby_marks/flood_scan.rb
42
+ - lib/ruby_marks/group.rb
43
+ - lib/ruby_marks/support.rb
47
44
  - lib/ruby_marks/config.rb
48
- - lib/ruby_marks/image_utils.rb
45
+ - lib/ruby_marks/version.rb
46
+ - lib/ruby_marks.rb
49
47
  - test/test_helper.rb
50
- - test/ruby_marks/recognizer_test.rb
51
- - test/ruby_marks/recognizer_grid_test.rb
52
48
  - test/ruby_marks/watcher_test.rb
53
49
  - test/ruby_marks/image_utils_test.rb
50
+ - test/ruby_marks/recognizer_grid_test.rb
51
+ - test/ruby_marks/recognizer_test.rb
54
52
  homepage: https://github.com/andrerpbts/ruby_marks.git
55
53
  licenses:
56
54
  - MIT
57
- post_install_message: ! "\n *** NOTE: You are running the ImageMagick under 16bits
55
+ metadata: {}
56
+ post_install_message: "\n *** NOTE: You are running the ImageMagick under 16bits
58
57
  quantum depth. This configuration is used\n in very specific cases and
59
58
  can cause RMagick work a bit slow. See more details in this forum post\n http://rubyforge.org/forum/forum.php?thread_id=10975&forum_id=1618
60
59
  ***\n\n We changed the way it recognizes the marks. It's not based on clocks
@@ -65,26 +64,24 @@ rdoc_options: []
65
64
  require_paths:
66
65
  - lib
67
66
  required_ruby_version: !ruby/object:Gem::Requirement
68
- none: false
69
67
  requirements:
70
- - - ! '>='
68
+ - - '>='
71
69
  - !ruby/object:Gem::Version
72
70
  version: '0'
73
71
  required_rubygems_version: !ruby/object:Gem::Requirement
74
- none: false
75
72
  requirements:
76
- - - ! '>='
73
+ - - '>='
77
74
  - !ruby/object:Gem::Version
78
75
  version: '0'
79
76
  requirements: []
80
77
  rubyforge_project: ruby_marks
81
- rubygems_version: 1.8.25
78
+ rubygems_version: 2.1.11
82
79
  signing_key:
83
- specification_version: 3
80
+ specification_version: 4
84
81
  summary: A simple OMR tool
85
82
  test_files:
86
83
  - test/test_helper.rb
87
- - test/ruby_marks/recognizer_test.rb
88
- - test/ruby_marks/recognizer_grid_test.rb
89
84
  - test/ruby_marks/watcher_test.rb
90
85
  - test/ruby_marks/image_utils_test.rb
86
+ - test/ruby_marks/recognizer_grid_test.rb
87
+ - test/ruby_marks/recognizer_test.rb