ruby_marks 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -7,18 +7,116 @@ module RubyMarks
|
|
7
7
|
x2.to_i - x1.to_i + 1
|
8
8
|
end
|
9
9
|
|
10
|
+
|
10
11
|
def self.calc_height(y1, y2)
|
11
12
|
y2.to_i - y1.to_i + 1
|
12
13
|
end
|
13
14
|
|
15
|
+
|
14
16
|
def self.calc_middle_horizontal(x, width)
|
15
17
|
x.to_i + width.to_i / 2
|
16
18
|
end
|
17
19
|
|
20
|
+
|
18
21
|
def self.calc_middle_vertical(y, height)
|
19
22
|
y.to_i + height.to_i / 2
|
20
23
|
end
|
21
|
-
|
24
|
+
|
25
|
+
|
26
|
+
def self.image_center(coordinates)
|
27
|
+
width = self.calc_width(coordinates[:x1], coordinates[:x2])
|
28
|
+
height = self.calc_height(coordinates[:y1], coordinates[:y2])
|
29
|
+
|
30
|
+
x = self.calc_middle_horizontal(coordinates[:x1], width)
|
31
|
+
y = self.calc_middle_vertical(coordinates[:y1], height)
|
32
|
+
return {x: x, y: y}
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def self.flood_scan(image, x, y, character=" ", coordinates={})
|
37
|
+
coordinates = {x1: 0, y1: 0, x2: image[0].size, y2: image.size} unless coordinates.any?
|
38
|
+
result_mask = Hash.new { |hash, key| hash[key] = [] }
|
39
|
+
result_mask.tap do |result_mask|
|
40
|
+
process_queue = Hash.new { |hash, key| hash[key] = [] }
|
41
|
+
process_line = true
|
42
|
+
|
43
|
+
loop do
|
44
|
+
|
45
|
+
break if y > coordinates[:y2] - 1 || y < coordinates[:y1]
|
46
|
+
reset_process = false
|
47
|
+
|
48
|
+
if process_line
|
49
|
+
current_x = x
|
50
|
+
loop do
|
51
|
+
position = image[y][current_x]
|
52
|
+
|
53
|
+
break if position != character || current_x - 1 <= coordinates[:x1]
|
54
|
+
process_queue[y] << current_x unless process_queue[y].include?(current_x) || result_mask[y].include?(current_x)
|
55
|
+
result_mask[y] << current_x unless result_mask[y].include?(current_x)
|
56
|
+
current_x = current_x - 1
|
57
|
+
end
|
58
|
+
|
59
|
+
current_x = x.to_i
|
60
|
+
loop do
|
61
|
+
position = image[y][current_x]
|
62
|
+
|
63
|
+
break if position != character || current_x + 1 >= coordinates[:x2]
|
64
|
+
process_queue[y] << current_x unless process_queue[y].include?(current_x) || result_mask[y].include?(current_x)
|
65
|
+
result_mask[y] << current_x unless result_mask[y].include?(current_x)
|
66
|
+
current_x = current_x + 1
|
67
|
+
end
|
68
|
+
|
69
|
+
result_mask[y] = result_mask[y].sort
|
70
|
+
process_queue[y] = process_queue[y].sort
|
71
|
+
end
|
72
|
+
|
73
|
+
process_line = true
|
74
|
+
|
75
|
+
process_queue[y].each do |element|
|
76
|
+
if y - 1 >= coordinates[:y1]
|
77
|
+
position = image[y-1][element]
|
78
|
+
|
79
|
+
if position == character && !result_mask[y-1].include?(element)
|
80
|
+
x = element
|
81
|
+
y = y - 1
|
82
|
+
reset_process = true
|
83
|
+
break
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
next if reset_process
|
89
|
+
|
90
|
+
process_queue[y].each do |element|
|
91
|
+
if y + 1 <= coordinates[:y2]
|
92
|
+
position = image[y+1] && image[y+1][element]
|
93
|
+
|
94
|
+
if position && position == character && !result_mask[y+1].include?(element)
|
95
|
+
x = element
|
96
|
+
y = y + 1
|
97
|
+
reset_process = true
|
98
|
+
break
|
99
|
+
else
|
100
|
+
process_queue[y].delete(element)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
next if reset_process
|
107
|
+
|
108
|
+
process_queue.each do |k,v|
|
109
|
+
process_queue.delete(k) if v.empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
break if process_queue.empty?
|
113
|
+
|
114
|
+
process_line = false
|
115
|
+
y = process_queue.first[0] if process_queue.first.is_a?(Array)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
22
120
|
|
23
121
|
def self.to_hex(red, green, blue)
|
24
122
|
red = get_hex_from_color(red)
|
@@ -27,6 +125,16 @@ module RubyMarks
|
|
27
125
|
"##{red}#{green}#{blue}".upcase
|
28
126
|
end
|
29
127
|
|
128
|
+
|
129
|
+
def self.export_file_to_str(file)
|
130
|
+
image = file.export_pixels_to_str
|
131
|
+
image = image.gsub!(Regexp.new('\xFF\xFF\xFF', nil, 'n'), " ,") if image
|
132
|
+
image = image.gsub!(Regexp.new('\x00\x00\x00', nil, 'n'), ".,") if image
|
133
|
+
image = image.split(',') if image
|
134
|
+
image = image.each_slice(file.page.width).to_a if image
|
135
|
+
end
|
136
|
+
|
137
|
+
|
30
138
|
private
|
31
139
|
def self.get_hex_from_color(color)
|
32
140
|
color = color.to_s(16)[0..1]
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module RubyMarks
|
2
|
+
|
3
|
+
class Mark
|
4
|
+
|
5
|
+
attr_accessor :coordinates, :group, :position, :line, :image_str, :distance_from_previous
|
6
|
+
|
7
|
+
def initialize(params={})
|
8
|
+
params.each do |k, v|
|
9
|
+
self.send("#{k}=", v) if self.respond_to?("#{k}=")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def marked?
|
14
|
+
if @image_str
|
15
|
+
colors = []
|
16
|
+
|
17
|
+
@image_str.each do |y|
|
18
|
+
y.each do |x|
|
19
|
+
colors << x
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
intensity = colors.count(".") * 100 / colors.size
|
24
|
+
return intensity >= @group.recognizer.config.intensity_percentual
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def unmarked?
|
29
|
+
!marked?
|
30
|
+
end
|
31
|
+
|
32
|
+
def value
|
33
|
+
if @group
|
34
|
+
position = @group.marks[line].index(self)
|
35
|
+
|
36
|
+
values = @group.marks_options
|
37
|
+
return position && values[position]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|