ruby_marks 0.3.5 → 0.4.0

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 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