ruby_marks 0.1.5 → 0.2.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.
- data/README.md +168 -139
- data/lib/ruby_marks/config.rb +14 -35
- data/lib/ruby_marks/group.rb +43 -13
- data/lib/ruby_marks/image_utils.rb +109 -1
- data/lib/ruby_marks/mark.rb +42 -0
- data/lib/ruby_marks/recognizer.rb +304 -315
- data/lib/ruby_marks/scan_area.rb +11 -0
- data/lib/ruby_marks/version.rb +1 -1
- data/lib/ruby_marks.rb +28 -19
- data/test/ruby_marks/image_utils_test.rb +17 -1
- data/test/ruby_marks/recognizer_test.rb +51 -55
- metadata +10 -10
- data/lib/ruby_marks/clock_mark.rb +0 -65
- data/test/ruby_marks/clock_mark_test.rb +0 -44
- data/test/ruby_marks/group_test.rb +0 -26
@@ -3,9 +3,9 @@ module RubyMarks
|
|
3
3
|
|
4
4
|
class Recognizer
|
5
5
|
|
6
|
-
attr_reader :file, :raised_watchers, :groups, :watchers, :file_str
|
6
|
+
attr_reader :file, :raised_watchers, :groups, :watchers, :file_str, :original_file_str
|
7
|
+
attr_accessor :config
|
7
8
|
|
8
|
-
attr_accessor :current_position, :clock_marks, :config
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
self.reset_document
|
@@ -13,14 +13,23 @@ module RubyMarks
|
|
13
13
|
self.create_config
|
14
14
|
end
|
15
15
|
|
16
|
+
|
16
17
|
def file=(file)
|
17
18
|
self.reset_document
|
18
19
|
@file = nil
|
19
20
|
@file_str = nil
|
20
21
|
@file = Magick::Image.read(file).first
|
21
|
-
@file = @file.
|
22
|
+
@file = @file.quantize(256, Magick::GRAYColorspace)
|
23
|
+
@file = @file.threshold(@config.calculated_threshold_level)
|
24
|
+
@original_file = @file
|
25
|
+
@file = @file.edge(@config.edge_level)
|
26
|
+
|
27
|
+
@groups.each_pair do |label, group|
|
28
|
+
group.marks = Hash.new { |hash, key| hash[key] = [] }
|
29
|
+
end
|
22
30
|
end
|
23
31
|
|
32
|
+
|
24
33
|
def reset_document
|
25
34
|
@current_position = {x: 0, y: 0}
|
26
35
|
@clock_marks = []
|
@@ -28,28 +37,34 @@ module RubyMarks
|
|
28
37
|
@watchers = {}
|
29
38
|
end
|
30
39
|
|
40
|
+
|
31
41
|
def create_config
|
32
42
|
@config ||= RubyMarks::Config.new(self)
|
33
43
|
end
|
34
44
|
|
45
|
+
|
35
46
|
def filename
|
36
47
|
@file && @file.filename
|
37
48
|
end
|
38
49
|
|
50
|
+
|
39
51
|
def configure(&block)
|
40
52
|
self.create_config
|
41
53
|
@config.configure(&block)
|
42
54
|
end
|
43
55
|
|
56
|
+
|
44
57
|
def add_group(group)
|
45
58
|
@groups[group.label] = group if group
|
46
59
|
end
|
47
60
|
|
61
|
+
|
48
62
|
def add_watcher(watcher_name, &block)
|
49
63
|
watcher = RubyMarks::Watcher.new(watcher_name, self, &block)
|
50
64
|
@watchers[watcher.name] = watcher if watcher
|
51
65
|
end
|
52
66
|
|
67
|
+
|
53
68
|
def raise_watcher(name, *args)
|
54
69
|
watcher = @watchers[name]
|
55
70
|
if watcher
|
@@ -59,406 +74,380 @@ module RubyMarks
|
|
59
74
|
end
|
60
75
|
end
|
61
76
|
|
62
|
-
def move_to(x, y)
|
63
|
-
@current_position = {x: @current_position[:x] + x, y: @current_position[:y] + y}
|
64
|
-
end
|
65
77
|
|
66
|
-
def
|
78
|
+
def scan
|
67
79
|
raise IOError, "There's a invalid or missing file" if @file.nil?
|
68
|
-
|
69
|
-
self.export_file_to_str if self.file_str.nil?
|
70
80
|
|
71
|
-
|
81
|
+
unmarked_group_found = false
|
82
|
+
multiple_marked_found = false
|
72
83
|
|
73
|
-
|
74
|
-
|
84
|
+
result = Hash.new { |hash, key| hash[key] = [] }
|
85
|
+
result.tap do |result|
|
86
|
+
|
87
|
+
self.detect_groups
|
88
|
+
|
89
|
+
@groups.each_pair do |label, group|
|
90
|
+
marks = Hash.new { |hash, key| hash[key] = [] }
|
91
|
+
group.marks.each_pair do |line, value|
|
92
|
+
value.each do |mark|
|
93
|
+
marks[line] << mark.value if mark.marked?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
if marks.any?
|
98
|
+
result[group.label.to_sym] = marks
|
99
|
+
multiple_marked_found = true if marks.size > 1
|
100
|
+
else
|
101
|
+
unmarked_group_found = true
|
102
|
+
end
|
103
|
+
end
|
75
104
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
105
|
+
raise_watcher :scan_unmarked_watcher, result if unmarked_group_found
|
106
|
+
raise_watcher :scan_multiple_marked_watcher, result if multiple_marked_found
|
107
|
+
raise_watcher :scan_mark_watcher, result, unmarked_group_found, multiple_marked_found if unmarked_group_found || multiple_marked_found
|
108
|
+
end
|
109
|
+
end
|
81
110
|
|
82
|
-
stack = flood_scan(current_x, current_y)
|
83
111
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
112
|
+
def detect_groups
|
113
|
+
file_str = RubyMarks::ImageUtils.export_file_to_str(@file)
|
114
|
+
original_file_str = RubyMarks::ImageUtils.export_file_to_str(@original_file)
|
115
|
+
incorrect_bubble_line_found = false
|
116
|
+
bubbles_adjusted = false
|
117
|
+
incorrect_expected_lines = false
|
118
|
+
@groups.each_pair do |label, group|
|
119
|
+
next unless group.expected_coordinates.any?
|
90
120
|
|
91
|
-
|
92
|
-
y_elements.sort!.uniq!
|
121
|
+
group_center = RubyMarks::ImageUtils.image_center(group.expected_coordinates)
|
93
122
|
|
94
|
-
|
95
|
-
x2 = x_elements.last || 0
|
96
|
-
y1 = y_elements.first || 0
|
97
|
-
y2 = y_elements.last || 0
|
123
|
+
block = find_block_marks(file_str, group_center[:x], group_center[:y], group.expected_coordinates)
|
98
124
|
|
99
|
-
|
100
|
-
|
125
|
+
if block
|
126
|
+
group.coordinates = {x1: block[:x1], x2: block[:x2], y1: block[:y1], y2: block[:y2]}
|
101
127
|
|
102
|
-
|
103
|
-
|
104
|
-
|
128
|
+
marks_blocks = find_marks(original_file_str, group)
|
129
|
+
positions = []
|
130
|
+
marks_blocks.each do |mark|
|
131
|
+
line = 0
|
132
|
+
mark_width = RubyMarks::ImageUtils.calc_width(mark[:x1], mark[:x2])
|
133
|
+
mark_height = RubyMarks::ImageUtils.calc_height(mark[:y1], mark[:y2])
|
134
|
+
|
135
|
+
if mark_width >= group.mark_width_with_down_tolerance &&
|
136
|
+
mark_width <= group.mark_width_with_up_tolerance &&
|
137
|
+
mark_height >= group.mark_height_with_down_tolerance &&
|
138
|
+
mark_height <= group.mark_height_with_up_tolerance
|
139
|
+
|
140
|
+
group.marks.each_pair do |key, marks_array|
|
141
|
+
mark_positions = mark[:y1]-10..mark[:y1]+10
|
142
|
+
|
143
|
+
marks_array.each do |m|
|
144
|
+
if mark_positions.include?(m.coordinates[:y1])
|
145
|
+
line = key
|
146
|
+
break
|
147
|
+
end
|
148
|
+
end
|
105
149
|
|
106
|
-
if
|
107
|
-
x2 = x1 + expected_width
|
108
|
-
else
|
109
|
-
x1 = x2 - expected_width
|
110
|
-
end
|
111
|
-
current_width = RubyMarks::ImageUtils.calc_width(x1, x2)
|
150
|
+
break if line > 0
|
112
151
|
end
|
113
152
|
|
153
|
+
line = group.marks.size + 1 if line == 0
|
114
154
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
if distance_y1 <= distance_y2
|
120
|
-
y2 = y1 + expected_height
|
121
|
-
else
|
122
|
-
y1 = y2 - expected_height
|
123
|
-
end
|
124
|
-
current_height = RubyMarks::ImageUtils.calc_height(y1, y2)
|
155
|
+
conflict_marks = group.marks[line].select do |el|
|
156
|
+
el.coordinates[:x2] >= mark[:x1] && el.coordinates[:x2] <= mark[:x2] ||
|
157
|
+
el.coordinates[:x1] >= mark[:x1] && el.coordinates[:x1] <= mark[:x2]
|
125
158
|
end
|
126
159
|
|
127
|
-
if
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
colors = []
|
132
|
-
|
133
|
-
x_pos = x1..x2
|
134
|
-
y_pos = y1..y2
|
135
|
-
|
136
|
-
y_pos.each do |y|
|
137
|
-
x_pos.each do |x|
|
138
|
-
color = self.file_str[y][x]
|
139
|
-
colors << color
|
140
|
-
end
|
160
|
+
if conflict_marks.any?
|
161
|
+
conflict_marks.each do |conflict_mark|
|
162
|
+
group.marks[line].delete(conflict_mark)
|
141
163
|
end
|
164
|
+
else
|
165
|
+
mark_file = @original_file.crop(mark[:x1], mark[:y1], mark_width, mark_height)
|
166
|
+
|
167
|
+
mark = RubyMarks::Mark.new group: group,
|
168
|
+
coordinates: {x1: mark[:x1], y1: mark[:y1], x2: mark[:x2], y2: mark[:y2]},
|
169
|
+
image_str: RubyMarks::ImageUtils.export_file_to_str(mark_file),
|
170
|
+
line: line
|
142
171
|
|
143
|
-
|
144
|
-
|
172
|
+
group.marks[line] << mark
|
173
|
+
group.marks[line].sort! { |a, b| a.coordinates[:x1] <=> b.coordinates[:x1] }
|
145
174
|
end
|
146
175
|
end
|
147
|
-
|
148
176
|
end
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
|
155
|
-
def unmarked?(x_pos, y_pos)
|
156
|
-
!marked?(x_pos, y_pos)
|
157
|
-
end
|
158
|
-
|
159
|
-
def scan
|
160
|
-
raise IOError, "There's a invalid or missing file" if @file.nil?
|
177
|
+
|
178
|
+
first_position = 0
|
179
|
+
elements_position_count = 0
|
180
|
+
group.marks.each_pair do |line, marks|
|
161
181
|
|
162
|
-
|
182
|
+
if marks.count == group.marks_options.count &&
|
183
|
+
marks.first && marks.first.coordinates
|
163
184
|
|
164
|
-
|
165
|
-
multiple_marked_found = false
|
185
|
+
first_position += marks.first.coordinates[:x1]
|
166
186
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
scan_clock_marks unless clock_marks.any?
|
171
|
-
|
172
|
-
return false if self.config.expected_clocks_count > 0 && @clock_marks.count != self.config.expected_clocks_count
|
173
|
-
clock_marks.each_with_index do |clock_mark, index|
|
174
|
-
group_hash = {}
|
175
|
-
@groups.each do |key, group|
|
176
|
-
if group.belongs_to_clock?(index + 1)
|
177
|
-
@current_position = {x: clock_mark.coordinates[:x2], y: clock_mark.vertical_middle_position}
|
178
|
-
move_to(group.x_distance_from_clock, 0)
|
179
|
-
markeds = []
|
180
|
-
group.marks_options.each do |mark|
|
181
|
-
markeds << mark if marked?(group.mark_width, group.mark_height)
|
182
|
-
move_to(group.distance_between_marks, 0)
|
183
|
-
end
|
184
|
-
if markeds.any?
|
185
|
-
group_hash["group_#{key}".to_sym] = markeds
|
186
|
-
multiple_marked_found = true if markeds.size > 1
|
187
|
-
else
|
188
|
-
unmarked_group_found = true
|
189
|
-
end
|
187
|
+
elements_position_count += 1
|
188
|
+
else
|
189
|
+
incorrect_bubble_line_found = true
|
190
190
|
end
|
191
191
|
end
|
192
|
-
result["clock_#{index+1}".to_sym] = group_hash if group_hash.any?
|
193
|
-
end
|
194
|
-
@current_position = position_before
|
195
|
-
raise_watcher :scan_unmarked_watcher, result if unmarked_group_found
|
196
|
-
raise_watcher :scan_multiple_marked_watcher, result if multiple_marked_found
|
197
|
-
raise_watcher :scan_mark_watcher, result, unmarked_group_found, multiple_marked_found if unmarked_group_found || multiple_marked_found
|
198
|
-
end
|
199
|
-
end
|
200
192
|
|
201
|
-
|
202
|
-
|
203
|
-
|
193
|
+
if @config.adjust_inconsistent_bubbles && elements_position_count > 0
|
194
|
+
first_position = first_position / elements_position_count
|
195
|
+
distance = group.distance_between_marks * (group.marks_options.count - 1)
|
196
|
+
last_position = first_position + distance
|
197
|
+
|
198
|
+
group.marks.each_pair do |line, marks|
|
199
|
+
loop do
|
200
|
+
reprocess = false
|
201
|
+
marks.each_with_index do |current_mark, index|
|
202
|
+
if current_mark.coordinates[:x1] < first_position - 10 ||
|
203
|
+
current_mark.coordinates[:x1] > last_position + 10
|
204
|
+
|
205
|
+
group.marks[line].delete(current_mark)
|
206
|
+
reprocess = true
|
207
|
+
bubbles_adjusted = true
|
208
|
+
break
|
209
|
+
|
210
|
+
else
|
211
|
+
|
212
|
+
if index == 0
|
213
|
+
first_mark_position = first_position-5..first_position+5
|
214
|
+
unless first_mark_position.include?(current_mark.coordinates[:x1])
|
215
|
+
new_mark_x1 = first_position
|
216
|
+
new_mark_x2 = new_mark_x1 + group.mark_width
|
217
|
+
new_mark_y1 = current_mark.coordinates[:y1]
|
218
|
+
new_mark_y2 = new_mark_y1 + group.mark_height
|
219
|
+
reprocess = true
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
unless reprocess
|
224
|
+
next_mark = marks[index + 1]
|
225
|
+
distance = 0
|
226
|
+
distance = next_mark.coordinates[:x1] - current_mark.coordinates[:x1] if next_mark
|
227
|
+
|
228
|
+
if distance > group.distance_between_marks + 10 ||
|
229
|
+
next_mark.nil? && index + 1 < group.marks_options.count
|
230
|
+
new_mark_x1 = current_mark.coordinates[:x1] + group.distance_between_marks
|
231
|
+
new_mark_x2 = new_mark_x1 + group.mark_width
|
232
|
+
new_mark_y1 = current_mark.coordinates[:y1]
|
233
|
+
new_mark_y2 = new_mark_y1 + group.mark_height
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
if new_mark_x1 && new_mark_x2 && new_mark_y1 && new_mark_y2
|
238
|
+
mark_width = RubyMarks::ImageUtils.calc_width(new_mark_x1, new_mark_x2)
|
239
|
+
mark_height = RubyMarks::ImageUtils.calc_height(new_mark_y1, new_mark_y2)
|
240
|
+
|
241
|
+
mark_file = @original_file.crop(new_mark_x1, new_mark_y1, mark_width, mark_height)
|
242
|
+
|
243
|
+
current_mark = RubyMarks::Mark.new group: group,
|
244
|
+
coordinates: {x1: new_mark_x1, y1: new_mark_y1, x2: new_mark_x2, y2: new_mark_y2},
|
245
|
+
image_str: RubyMarks::ImageUtils.export_file_to_str(mark_file),
|
246
|
+
line: line
|
247
|
+
|
248
|
+
group.marks[line] << current_mark
|
249
|
+
group.marks[line].sort! { |a, b| a.coordinates[:x1] <=> b.coordinates[:x1] }
|
250
|
+
reprocess = true
|
251
|
+
bubbles_adjusted = true
|
252
|
+
break
|
253
|
+
end
|
254
|
+
end
|
255
|
+
break if reprocess
|
256
|
+
end
|
257
|
+
break unless reprocess
|
258
|
+
end
|
204
259
|
|
205
|
-
|
260
|
+
incorrect_expected_lines = true if group.incorrect_expected_lines
|
261
|
+
end
|
262
|
+
end
|
206
263
|
|
207
|
-
file.tap do |file|
|
208
|
-
if current_position
|
209
|
-
add_mark file
|
210
264
|
end
|
265
|
+
end
|
266
|
+
|
267
|
+
if incorrect_bubble_line_found || bubbles_adjusted || incorrect_expected_lines
|
268
|
+
raise_watcher :incorrect_group_watcher, incorrect_expected_lines, incorrect_bubble_line_found, bubbles_adjusted
|
211
269
|
end
|
212
270
|
end
|
213
271
|
|
214
|
-
def flag_all_marks
|
215
272
|
|
216
|
-
|
217
|
-
|
218
|
-
|
273
|
+
def find_block_marks(image, x, y, expected_coordinates)
|
274
|
+
found_blocks = []
|
275
|
+
expected_width = RubyMarks::ImageUtils.calc_width(expected_coordinates[:x1], expected_coordinates[:x2])
|
276
|
+
expected_height = RubyMarks::ImageUtils.calc_height(expected_coordinates[:y1], expected_coordinates[:y2])
|
277
|
+
block = nil
|
278
|
+
while x <= expected_coordinates[:x2] && y <= expected_coordinates[:y2]
|
279
|
+
if image[y] && image[y][x] == " "
|
280
|
+
block = find_in_blocks(found_blocks, x, y)
|
281
|
+
unless block
|
282
|
+
block = find_block(image, x, y)
|
283
|
+
found_blocks << block
|
284
|
+
|
285
|
+
block[:width] = RubyMarks::ImageUtils.calc_width(block[:x1], block[:x2])
|
286
|
+
block[:height] = RubyMarks::ImageUtils.calc_height(block[:y1], block[:y2])
|
219
287
|
|
220
|
-
|
288
|
+
block_width_with_tolerance = block[:width] + 100
|
289
|
+
block_height_with_tolerance = block[:height] + 100
|
221
290
|
|
222
|
-
file.tap do |file|
|
223
|
-
position_before = @current_position
|
224
|
-
|
225
|
-
dr = Magick::Draw.new
|
226
|
-
dr.fill(RubyMarks::COLORS[4])
|
227
|
-
dr.line(@config.clock_marks_scan_x, 0, @config.clock_marks_scan_x, file.page.height)
|
228
|
-
dr.draw(file)
|
229
291
|
|
230
|
-
|
231
|
-
|
232
|
-
clock_marks.each_with_index do |clock, index|
|
233
|
-
dr = Magick::Draw.new
|
234
|
-
dr.fill(RubyMarks::COLORS[5])
|
235
|
-
dr.rectangle(clock.coordinates[:x1], clock.coordinates[:y1], clock.coordinates[:x2], clock.coordinates[:y2])
|
236
|
-
dr.draw(file)
|
237
|
-
end
|
292
|
+
return block if block_width_with_tolerance >= expected_width &&
|
293
|
+
block_height_with_tolerance >= expected_height
|
238
294
|
|
239
|
-
clock_marks.each_with_index do |clock_mark, index|
|
240
|
-
@groups.each do |key, group|
|
241
|
-
if group.belongs_to_clock?(index + 1)
|
242
|
-
@current_position = {x: clock_mark.coordinates[:x2], y: clock_mark.vertical_middle_position}
|
243
|
-
move_to(group.x_distance_from_clock, 0)
|
244
|
-
group.marks_options.each do |mark|
|
245
|
-
add_mark file
|
246
|
-
move_to(group.distance_between_marks, 0)
|
247
|
-
end
|
248
|
-
end
|
249
295
|
end
|
250
296
|
end
|
251
297
|
|
252
|
-
|
298
|
+
x += 1
|
299
|
+
y += 1
|
253
300
|
end
|
254
301
|
end
|
255
302
|
|
256
|
-
def scan_clock_marks
|
257
|
-
|
258
|
-
raise IOError, "There's a invalid or missing file" if @file.nil?
|
259
|
-
|
260
|
-
self.export_file_to_str if self.file_str.nil?
|
261
|
-
|
262
|
-
@clock_marks = []
|
263
|
-
x = @config.clock_marks_scan_x
|
264
|
-
total_width = @file && @file.page.width || 0
|
265
|
-
total_height = @file && @file.page.height || 0
|
266
|
-
|
267
|
-
@clock_marks.tap do |clock_marks|
|
268
|
-
current_y = 0
|
269
|
-
loop do
|
270
|
-
|
271
|
-
break if current_y >= total_height
|
272
303
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
304
|
+
def find_marks(image, group)
|
305
|
+
block = group.coordinates
|
306
|
+
y = block[:y1]
|
307
|
+
blocks = []
|
308
|
+
blocks.tap do |blocks|
|
309
|
+
while y < block[:y2]
|
310
|
+
x = block[:x1]
|
311
|
+
while x < block[:x2] do
|
312
|
+
if image[y][x] == " "
|
313
|
+
x += 1
|
314
|
+
next
|
283
315
|
end
|
284
316
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
if x_elements.size > 50
|
290
|
-
current_y = last_y + 1
|
291
|
-
next
|
292
|
-
end
|
317
|
+
result = find_in_blocks(blocks, x, y)
|
318
|
+
unless result
|
319
|
+
result = find_block(image, x, y, ".", block)
|
293
320
|
|
294
|
-
|
295
|
-
|
321
|
+
mark_width = RubyMarks::ImageUtils.calc_width(result[:x1], result[:x2])
|
322
|
+
mark_height = RubyMarks::ImageUtils.calc_height(result[:y1], result[:y2])
|
296
323
|
|
297
324
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
current_width = RubyMarks::ImageUtils.calc_width(x_elements.first, x_elements.last)
|
306
|
-
middle = RubyMarks::ImageUtils.calc_middle_horizontal(x_elements.first, current_width)
|
307
|
-
|
308
|
-
x_elements.delete_if do |el|
|
309
|
-
col <= middle && el <= col || col >= middle && el >= col
|
310
|
-
end
|
311
|
-
|
312
|
-
stack_modified = true
|
325
|
+
if mark_width > group.mark_width_with_up_tolerance
|
326
|
+
distance_x1 = x - result[:x1]
|
327
|
+
distance_x2 = result[:x2] - x
|
328
|
+
if distance_x1 <= distance_x2
|
329
|
+
result[:x2] = result[:x1] + group.mark_width_with_up_tolerance - 2
|
330
|
+
else
|
331
|
+
result[:x1] = result[:x2] - group.mark_width_with_up_tolerance + 2
|
313
332
|
end
|
314
|
-
end
|
315
|
-
|
316
|
-
y_elements.each do |row|
|
317
|
-
if stack[row].count < self.config.clock_width_with_down_tolerance
|
318
|
-
current_height = RubyMarks::ImageUtils.calc_height(y_elements.first, y_elements.last)
|
319
|
-
middle = RubyMarks::ImageUtils.calc_middle_vertical(y_elements.first, current_height)
|
333
|
+
end
|
320
334
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
335
|
+
if mark_height > group.mark_height_with_up_tolerance
|
336
|
+
distance_y1 = y - result[:y1]
|
337
|
+
distance_y2 = result[:y2] - y
|
338
|
+
if distance_y1 <= distance_y2
|
339
|
+
result[:y2] = result[:y1] + group.mark_height_with_up_tolerance - 2
|
340
|
+
else
|
341
|
+
result[:y1] = result[:y2] - group.mark_height_with_up_tolerance + 2
|
342
|
+
end
|
327
343
|
end
|
328
344
|
|
329
|
-
|
345
|
+
blocks << result
|
330
346
|
end
|
331
|
-
|
332
|
-
x1 = x_elements.first || 0
|
333
|
-
x2 = x_elements.last || 0
|
334
|
-
y1 = y_elements.first || 0
|
335
|
-
y2 = y_elements.last || 0
|
347
|
+
x += 1
|
336
348
|
end
|
349
|
+
y += 1
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
337
353
|
|
338
|
-
clock = RubyMarks::ClockMark.new(recognizer: self, coordinates: {x1: x1, x2: x2, y1: y1, y2: y2})
|
339
354
|
|
340
|
-
|
341
|
-
|
342
|
-
current_y = last_y
|
343
|
-
end
|
355
|
+
def flag_position(position)
|
356
|
+
raise IOError, "There's a invalid or missing file" if @file.nil?
|
344
357
|
|
345
|
-
|
346
|
-
|
347
|
-
|
358
|
+
file = @original_file.dup
|
359
|
+
|
360
|
+
file.tap do |file|
|
361
|
+
add_mark file, position
|
348
362
|
end
|
349
363
|
end
|
350
364
|
|
351
|
-
def flood_scan(x, y)
|
352
|
-
|
353
|
-
result_mask = Hash.new { |hash, key| hash[key] = [] }
|
354
|
-
result_mask.tap do |result_mask|
|
355
|
-
process_queue = Hash.new { |hash, key| hash[key] = [] }
|
356
|
-
process_line = true
|
357
|
-
|
358
|
-
loop do
|
359
|
-
|
360
|
-
break if y > self.file_str.size - 1
|
361
|
-
reset_process = false
|
362
|
-
|
363
|
-
if process_line
|
364
|
-
current_x = x.to_i
|
365
|
-
loop do
|
366
|
-
position = self.file_str[y][current_x]
|
367
|
-
|
368
|
-
break if position != "." || current_x - 1 <= 0
|
369
|
-
process_queue[y] << current_x unless process_queue[y].include?(current_x) || result_mask[y].include?(current_x)
|
370
|
-
result_mask[y] << current_x unless result_mask[y].include?(current_x)
|
371
|
-
current_x = current_x - 1
|
372
|
-
end
|
373
365
|
|
374
|
-
|
375
|
-
|
376
|
-
|
366
|
+
def flag_all_marks
|
367
|
+
raise IOError, "There's a invalid or missing file" if @file.nil?
|
368
|
+
|
369
|
+
file = @original_file.dup
|
377
370
|
|
378
|
-
|
379
|
-
process_queue[y] << current_x unless process_queue[y].include?(current_x) || result_mask[y].include?(current_x)
|
380
|
-
result_mask[y] << current_x unless result_mask[y].include?(current_x)
|
381
|
-
current_x = current_x + 1
|
382
|
-
end
|
371
|
+
file.tap do |file|
|
383
372
|
|
384
|
-
|
385
|
-
process_queue[y] = process_queue[y].sort
|
386
|
-
end
|
373
|
+
self.detect_groups
|
387
374
|
|
388
|
-
|
389
|
-
process_queue = Hash.new { |hash, key| hash[key] = [] }
|
390
|
-
y = y + 1
|
391
|
-
next
|
392
|
-
end
|
375
|
+
@groups.each_pair do |label, group|
|
393
376
|
|
394
|
-
|
377
|
+
dr = Magick::Draw.new
|
378
|
+
dr.stroke_width = 5
|
379
|
+
dr.stroke(RubyMarks::COLORS[3])
|
380
|
+
dr.line(group.expected_coordinates[:x1], group.expected_coordinates[:y1], group.expected_coordinates[:x2], group.expected_coordinates[:y1])
|
381
|
+
dr.line(group.expected_coordinates[:x2], group.expected_coordinates[:y1], group.expected_coordinates[:x2], group.expected_coordinates[:y2])
|
382
|
+
dr.line(group.expected_coordinates[:x2], group.expected_coordinates[:y2], group.expected_coordinates[:x1], group.expected_coordinates[:y2])
|
383
|
+
dr.line(group.expected_coordinates[:x1], group.expected_coordinates[:y2], group.expected_coordinates[:x1], group.expected_coordinates[:y1])
|
384
|
+
dr.draw(file)
|
395
385
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
end
|
406
|
-
end
|
386
|
+
if group.coordinates
|
387
|
+
dr = Magick::Draw.new
|
388
|
+
dr.stroke_width = 5
|
389
|
+
dr.stroke(RubyMarks::COLORS[5])
|
390
|
+
dr.line(group.coordinates[:x1], group.coordinates[:y1], group.coordinates[:x2], group.coordinates[:y1])
|
391
|
+
dr.line(group.coordinates[:x2], group.coordinates[:y1], group.coordinates[:x2], group.coordinates[:y2])
|
392
|
+
dr.line(group.coordinates[:x2], group.coordinates[:y2], group.coordinates[:x1], group.coordinates[:y2])
|
393
|
+
dr.line(group.coordinates[:x1], group.coordinates[:y2], group.coordinates[:x1], group.coordinates[:y1])
|
394
|
+
dr.draw(file)
|
407
395
|
end
|
408
396
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
position = self.file_str[y+1][element]
|
414
|
-
|
415
|
-
if position == "." && !result_mask[y+1].include?(element)
|
416
|
-
x = element
|
417
|
-
y = y + 1
|
418
|
-
reset_process = true
|
419
|
-
break
|
420
|
-
else
|
421
|
-
process_queue[y].delete(element)
|
422
|
-
end
|
397
|
+
marks = Hash.new { |hash, key| hash[key] = [] }
|
398
|
+
group.marks.each_pair do |line, value|
|
399
|
+
value.each do |mark|
|
400
|
+
add_mark file, RubyMarks::ImageUtils.image_center(mark.coordinates)
|
423
401
|
end
|
424
402
|
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
425
406
|
|
426
|
-
next if reset_process
|
427
407
|
|
428
|
-
|
429
|
-
|
430
|
-
|
408
|
+
private
|
409
|
+
def find_block(image, x, y, character=" ", coordinates={})
|
410
|
+
stack = RubyMarks::ImageUtils.flood_scan(image, x, y, character, coordinates)
|
411
|
+
|
412
|
+
x_elements = []
|
413
|
+
y_elements = []
|
414
|
+
stack.each do |k,v|
|
415
|
+
stack[k].inject(x_elements, :<<)
|
416
|
+
y_elements << k
|
417
|
+
end
|
431
418
|
|
432
|
-
|
419
|
+
x_elements.sort!.uniq!
|
420
|
+
y_elements.sort!.uniq!
|
433
421
|
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
422
|
+
x1 = x_elements.first || 0
|
423
|
+
x2 = x_elements.last || 0
|
424
|
+
y1 = y_elements.first || 0
|
425
|
+
y2 = y_elements.last || 0
|
426
|
+
|
427
|
+
{x1: x1, x2: x2, y1: y1, y2: y2}
|
438
428
|
end
|
439
429
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
430
|
+
|
431
|
+
def find_in_blocks(blocks, x, y)
|
432
|
+
blocks.find do |result|
|
433
|
+
result[:x1] <= x && result[:x2] >= x &&
|
434
|
+
result[:y1] <= y && result[:y2] >= y
|
435
|
+
end
|
446
436
|
end
|
447
437
|
|
448
|
-
|
449
|
-
def add_mark(file)
|
438
|
+
def add_mark(file, position)
|
450
439
|
dr = Magick::Draw.new
|
451
|
-
dr.annotate(file, 0, 0,
|
440
|
+
dr.annotate(file, 0, 0, position[:x]-9, position[:y]+11, "+") do
|
452
441
|
self.pointsize = 30
|
453
442
|
self.fill = '#900000'
|
454
443
|
end
|
455
444
|
|
456
445
|
dr = Magick::Draw.new
|
457
446
|
dr.fill = '#FF0000'
|
458
|
-
dr.point(
|
459
|
-
dr.point(
|
460
|
-
dr.point(
|
461
|
-
dr.point(
|
447
|
+
dr.point(position[:x], position[:y])
|
448
|
+
dr.point(position[:x], position[:y] + 1)
|
449
|
+
dr.point(position[:x] + 1, position[:y])
|
450
|
+
dr.point(position[:x] + 1, position[:y] + 1)
|
462
451
|
dr.draw(file)
|
463
452
|
end
|
464
453
|
|