ruby_marks 0.0.1.dev
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +4 -0
- data/lib/ruby_marks/clock_mark.rb +82 -0
- data/lib/ruby_marks/document.rb +145 -0
- data/lib/ruby_marks/rgb.rb +21 -0
- data/lib/ruby_marks/version.rb +3 -0
- data/lib/ruby_marks.rb +17 -0
- data/test/ruby_marks/clock_mark_test.rb +38 -0
- data/test/ruby_marks/document_test.rb +88 -0
- data/test/ruby_marks/rgb_test.rb +15 -0
- data/test/test_helper.rb +6 -0
- metadata +75 -0
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
module RubyMarks
|
3
|
+
|
4
|
+
class ClockMark
|
5
|
+
|
6
|
+
attr_accessor :document, :position, :coordinates
|
7
|
+
|
8
|
+
def initialize(params={})
|
9
|
+
params.each do |k, v|
|
10
|
+
self.send("#{k}=", v) if self.respond_to?("#{k}=")
|
11
|
+
end
|
12
|
+
@coordinates = {x1: 0, x2: 0, y1: 0, y2: 0}
|
13
|
+
self.calc_coordinates
|
14
|
+
end
|
15
|
+
|
16
|
+
def calc_coordinates
|
17
|
+
@coordinates.tap do |coordinates|
|
18
|
+
if self.document
|
19
|
+
x = position[:x]
|
20
|
+
y = position[:y]
|
21
|
+
|
22
|
+
coordinates[:x1] = x
|
23
|
+
loop do
|
24
|
+
coordinates[:x1] -= 1
|
25
|
+
color = self.document.file.pixel_color(coordinates[:x1], y)
|
26
|
+
color = RubyMarks::RGB.to_hex(color.red, color.green, color.blue)
|
27
|
+
|
28
|
+
break if color != "#000000" || coordinates[:x1] <= 0
|
29
|
+
end
|
30
|
+
|
31
|
+
coordinates[:x2] = x
|
32
|
+
loop do
|
33
|
+
coordinates[:x2] += 1
|
34
|
+
color = self.document.file.pixel_color(coordinates[:x2], y)
|
35
|
+
color = RubyMarks::RGB.to_hex(color.red, color.green, color.blue)
|
36
|
+
|
37
|
+
break if color != "#000000" || coordinates[:x2] >= self.document.file.page.width
|
38
|
+
end
|
39
|
+
|
40
|
+
coordinates[:y1] = y
|
41
|
+
loop do
|
42
|
+
coordinates[:y1] -= 1
|
43
|
+
color = self.document.file.pixel_color(x, coordinates[:y1])
|
44
|
+
color = RubyMarks::RGB.to_hex(color.red, color.green, color.blue)
|
45
|
+
|
46
|
+
break if color != "#000000" || coordinates[:y1] <= 0
|
47
|
+
end
|
48
|
+
|
49
|
+
coordinates[:y2] = y
|
50
|
+
loop do
|
51
|
+
coordinates[:y2] += 1
|
52
|
+
color = self.document.file.pixel_color(x, coordinates[:y2])
|
53
|
+
color = RubyMarks::RGB.to_hex(color.red, color.green, color.blue)
|
54
|
+
|
55
|
+
break if color != "#000000" || coordinates[:y2] >= self.document.file.page.height
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def width
|
62
|
+
coordinates[:x2] - coordinates[:x1]
|
63
|
+
end
|
64
|
+
|
65
|
+
def height
|
66
|
+
coordinates[:y2] - coordinates[:y1]
|
67
|
+
end
|
68
|
+
|
69
|
+
def horizontal_middle_position
|
70
|
+
coordinates[:x1] + self.width / 2
|
71
|
+
end
|
72
|
+
|
73
|
+
def vertical_middle_position
|
74
|
+
coordinates[:y1] + self.height / 2
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_s
|
78
|
+
self.coordinates
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
module RubyMarks
|
3
|
+
|
4
|
+
# Represents a scanned document
|
5
|
+
class Document
|
6
|
+
|
7
|
+
attr_reader :file
|
8
|
+
|
9
|
+
attr_accessor :current_position, :clock_marks
|
10
|
+
|
11
|
+
def initialize(file)
|
12
|
+
@file = Magick::Image.read(file).first
|
13
|
+
@current_position = {x: 0, y: 0}
|
14
|
+
@clock_marks = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def filename
|
18
|
+
@file.filename
|
19
|
+
end
|
20
|
+
|
21
|
+
def move_to(x, y)
|
22
|
+
@current_position = {x: @current_position[:x] + x, y: @current_position[:y] + y}
|
23
|
+
end
|
24
|
+
|
25
|
+
def marked?
|
26
|
+
if self.current_position
|
27
|
+
area_x = 8
|
28
|
+
area_y = 8
|
29
|
+
|
30
|
+
x_pos = current_position[:x]-area_x..current_position[:x]+area_x
|
31
|
+
y_pos = current_position[:y]-area_y..current_position[:y]+area_y
|
32
|
+
|
33
|
+
colors = []
|
34
|
+
|
35
|
+
y_pos.each do |y|
|
36
|
+
x_pos.each do |x|
|
37
|
+
color = @file.pixel_color(x, y)
|
38
|
+
color = RubyMarks::RGB.to_hex(color.red, color.green, color.blue)
|
39
|
+
color = (color == "#000000") ? "." : " "
|
40
|
+
colors << color
|
41
|
+
end
|
42
|
+
end
|
43
|
+
black_intensity = colors.count(".") * 100 / colors.size
|
44
|
+
return black_intensity >= 40 ? true : false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def unmarked?
|
49
|
+
!marked?
|
50
|
+
end
|
51
|
+
|
52
|
+
def scan
|
53
|
+
result = {}
|
54
|
+
result.tap do |result|
|
55
|
+
position_before = @current_position
|
56
|
+
|
57
|
+
scan_clock_marks unless clock_marks.any?
|
58
|
+
groups = [87, 310, 535, 760, 985]
|
59
|
+
marks = %w{A B C D E}
|
60
|
+
clock_marks.each_with_index do |clock_mark, index|
|
61
|
+
group_hash = {}
|
62
|
+
groups.each_with_index do |group, index|
|
63
|
+
@current_position = {x: clock_mark.coordinates[:x2], y: clock_mark.vertical_middle_position}
|
64
|
+
move_to(group, 0)
|
65
|
+
markeds = []
|
66
|
+
marks.each do |mark|
|
67
|
+
markeds << mark if marked?
|
68
|
+
move_to(25, 0)
|
69
|
+
end
|
70
|
+
group_hash["group_#{index+1}".to_sym] = markeds if markeds.any?
|
71
|
+
end
|
72
|
+
result["clock_#{index+1}".to_sym] = group_hash if group_hash.any?
|
73
|
+
end
|
74
|
+
@current_position = position_before
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def flag_position
|
79
|
+
file = @file.dup
|
80
|
+
|
81
|
+
file.tap do |file|
|
82
|
+
if current_position
|
83
|
+
add_mark file
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def flag_all_marks
|
89
|
+
file = @file.dup
|
90
|
+
|
91
|
+
file.tap do |file|
|
92
|
+
position_before = @current_position
|
93
|
+
|
94
|
+
scan_clock_marks unless clock_marks.any?
|
95
|
+
groups = [87, 310, 535, 760, 985]
|
96
|
+
marks = %w{A B C D E}
|
97
|
+
clock_marks.each do |clock_mark|
|
98
|
+
groups.each do |group|
|
99
|
+
@current_position = {x: clock_mark.coordinates[:x2], y: clock_mark.vertical_middle_position}
|
100
|
+
move_to(group, 0)
|
101
|
+
marks.each do |mark|
|
102
|
+
add_mark file
|
103
|
+
move_to(25, 0)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
@current_position = position_before
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def scan_clock_marks
|
113
|
+
@clock_marks = []
|
114
|
+
x = 62
|
115
|
+
in_clock = false
|
116
|
+
total_height = @file && @file.page.height || 0
|
117
|
+
@clock_marks.tap do |clock_marks|
|
118
|
+
total_height.times do |y|
|
119
|
+
clock = {}
|
120
|
+
color = @file.pixel_color(x, y)
|
121
|
+
color = RubyMarks::RGB.to_hex(color.red, color.green, color.blue)
|
122
|
+
if !in_clock && color == "#000000"
|
123
|
+
in_clock = true
|
124
|
+
clock_marks << RubyMarks::ClockMark.new(document: self, position: {x: x, y: y+3})
|
125
|
+
elsif in_clock && color != "#000000"
|
126
|
+
in_clock = false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
def add_mark(file)
|
134
|
+
flag = Magick::Draw.new
|
135
|
+
file.annotate(flag, 0, 0, current_position[:x] - 12, current_position[:y] + 15, "+") do
|
136
|
+
self.pointsize = 41
|
137
|
+
self.stroke = '#000000'
|
138
|
+
self.fill = '#C00000'
|
139
|
+
self.font_weight = Magick::BoldWeight
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
module RubyMarks
|
3
|
+
|
4
|
+
class RGB
|
5
|
+
|
6
|
+
def self.to_hex(red, green, blue)
|
7
|
+
red = get_hex_from_color(red)
|
8
|
+
green = get_hex_from_color(green)
|
9
|
+
blue = get_hex_from_color(blue)
|
10
|
+
"##{red}#{green}#{blue}".upcase
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def self.get_hex_from_color(color)
|
15
|
+
color = color.to_s(16)[0..1]
|
16
|
+
color.size < 2 ? "0#{color}" : color
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/ruby_marks.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'RMagick'
|
3
|
+
require 'ruby_marks/document'
|
4
|
+
require 'ruby_marks/clock_mark'
|
5
|
+
require 'ruby_marks/rgb'
|
6
|
+
require 'ruby_marks/version'
|
7
|
+
|
8
|
+
|
9
|
+
magick_version = `convert -version`
|
10
|
+
|
11
|
+
if magick_version =~ /Q16/
|
12
|
+
puts %{
|
13
|
+
*** IMPORTANT: You are running the ImageMagick under 16bits quantum depth. This configuration is used
|
14
|
+
in very specific cases and can cause RMagick work a bit slow. See more details in this forum post
|
15
|
+
http://rubyforge.org/forum/forum.php?thread_id=10975&forum_id=1618 ***
|
16
|
+
}
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RubyMarks::ClockMarkTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@file = 'assets/sheet_demo1.png'
|
7
|
+
@document = RubyMarks::Document.new(@file)
|
8
|
+
@positions = {}
|
9
|
+
@positions[:first_clock_position] = {x: 62, y: 794}
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_should_get_clock_coordinates_by_a_given_position
|
13
|
+
clock = RubyMarks::ClockMark.new(document: @document, position: @positions[:first_clock_position])
|
14
|
+
expected_coordinates = {:x1=>48, :x2=>75, :y1=>790, :y2=>802}
|
15
|
+
assert_equal expected_coordinates, clock.calc_coordinates
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_obtain_the_clock_mark_width
|
19
|
+
clock = RubyMarks::ClockMark.new(document: @document, position: @positions[:first_clock_position])
|
20
|
+
assert_equal 27, clock.width
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_obtain_the_clock_mark_height
|
24
|
+
clock = RubyMarks::ClockMark.new(document: @document, position: @positions[:first_clock_position])
|
25
|
+
assert_equal 12, clock.height
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_obtain_the_horizontal_middle_position
|
29
|
+
clock = RubyMarks::ClockMark.new(document: @document, position: @positions[:first_clock_position])
|
30
|
+
assert_equal 61, clock.horizontal_middle_position
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_should_obtain_the_vertical_middle_position
|
34
|
+
clock = RubyMarks::ClockMark.new(document: @document, position: @positions[:first_clock_position])
|
35
|
+
assert_equal 796, clock.vertical_middle_position
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RubyMarks::DocumentTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@file = 'assets/sheet_demo1.png'
|
7
|
+
@document = RubyMarks::Document.new(@file)
|
8
|
+
@positions = {}
|
9
|
+
@positions[:marked_position] = {x: 161, y: 794}
|
10
|
+
@positions[:unmarked_position] = {x: 161, y: 994}
|
11
|
+
@positions[:first_clock_position] = {x: 62, y: 794}
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_should_initialize_a_document_with_a_valid_file
|
15
|
+
assert_equal @file, @document.filename
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_return_a_file_with_a_position_flagged
|
19
|
+
@document.current_position = @positions[:first_clock_position]
|
20
|
+
flagged_document = @document.flag_position
|
21
|
+
assert_equal Magick::Image, flagged_document.class
|
22
|
+
|
23
|
+
# temp_filename = "temp_sheet_demo1.png"
|
24
|
+
# File.delete(temp_filename) if File.exist?(temp_filename)
|
25
|
+
# flagged_document.write(temp_filename)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_recognize_marked_position
|
29
|
+
@document.current_position = @positions[:marked_position]
|
30
|
+
assert @document.marked?, "The position wasn't recognized as marked"
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_should_recognize_not_marked_position
|
34
|
+
@document.current_position = @positions[:unmarked_position]
|
35
|
+
assert @document.unmarked?, "The position wasn't recognized as unmarked"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_recognize_the_document_clock_marks
|
39
|
+
@document.scan_clock_marks
|
40
|
+
assert_equal 20, @document.clock_marks.count
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_should_return_the_document_with_all_marks_flagged
|
44
|
+
flagged_document = @document.flag_all_marks
|
45
|
+
assert_equal Magick::Image, flagged_document.class
|
46
|
+
|
47
|
+
# temp_filename = "temp_sheet_demo2.png"
|
48
|
+
# File.delete(temp_filename) if File.exist?(temp_filename)
|
49
|
+
# flagged_document.write(temp_filename)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_move_the_current_position_in_10_and_20_pixels
|
53
|
+
@document.current_position = @positions[:marked_position]
|
54
|
+
expected_position = {x: 171, y: 814}
|
55
|
+
|
56
|
+
assert_equal expected_position, @document.move_to(10, 20)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_should_scan_the_document_and_get_a_hash_of_marked_marks
|
60
|
+
expected_hash = {
|
61
|
+
clock_1: {
|
62
|
+
group_1: ['A'],
|
63
|
+
group_2: ['A']
|
64
|
+
},
|
65
|
+
clock_2: {
|
66
|
+
group_1: ['B'],
|
67
|
+
group_2: ['B']
|
68
|
+
},
|
69
|
+
clock_3: {
|
70
|
+
group_1: ['C'],
|
71
|
+
group_2: ['C'],
|
72
|
+
group_3: ['D']
|
73
|
+
},
|
74
|
+
clock_4: {
|
75
|
+
group_1: ['D'],
|
76
|
+
group_2: ['D'],
|
77
|
+
group_3: ['D']
|
78
|
+
},
|
79
|
+
clock_5: {
|
80
|
+
group_1: ['E'],
|
81
|
+
group_2: ['E']
|
82
|
+
}
|
83
|
+
}
|
84
|
+
p @document.scan
|
85
|
+
assert_equal expected_hash, @document.scan
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class RubyMarks::RGBTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_should_return_the_white_color_in_hexa_receiving_8bits
|
6
|
+
color = RubyMarks::RGB.to_hex(255, 255, 255)
|
7
|
+
assert_equal "#FFFFFF", color
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_return_the_white_color_in_hexa_receiving_16bits
|
11
|
+
color = RubyMarks::RGB.to_hex(65535, 65535, 65535)
|
12
|
+
assert_equal "#FFFFFF", color
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby_marks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.dev
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- André Rodrigues
|
9
|
+
- Ronaldo Araujo
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-09-17 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rmagick
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
description: A simple ORM tool
|
32
|
+
email: andrerpbts@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- README.md
|
38
|
+
- lib/ruby_marks/clock_mark.rb
|
39
|
+
- lib/ruby_marks/document.rb
|
40
|
+
- lib/ruby_marks/rgb.rb
|
41
|
+
- lib/ruby_marks/version.rb
|
42
|
+
- lib/ruby_marks.rb
|
43
|
+
- test/ruby_marks/clock_mark_test.rb
|
44
|
+
- test/ruby_marks/document_test.rb
|
45
|
+
- test/ruby_marks/rgb_test.rb
|
46
|
+
- test/test_helper.rb
|
47
|
+
homepage: https://github.com/andrerpbts/ruby_marks.git
|
48
|
+
licenses: []
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ! '>'
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.3.1
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project: ruby_marks
|
67
|
+
rubygems_version: 1.8.24
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Ruby Marks ORM
|
71
|
+
test_files:
|
72
|
+
- test/ruby_marks/clock_mark_test.rb
|
73
|
+
- test/ruby_marks/document_test.rb
|
74
|
+
- test/ruby_marks/rgb_test.rb
|
75
|
+
- test/test_helper.rb
|