mork 0.8.1 → 0.9.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 +4 -4
- data/.gitignore +3 -1
- data/README.md +107 -35
- data/lib/mork/coord.rb +1 -1
- data/lib/mork/extensions.rb +3 -0
- data/lib/mork/grid.rb +13 -6
- data/lib/mork/grid_const.rb +11 -9
- data/lib/mork/grid_omr.rb +15 -5
- data/lib/mork/grid_pdf.rb +1 -0
- data/lib/mork/magicko.rb +46 -28
- data/lib/mork/mimage.rb +113 -52
- data/lib/mork/mimage_list.rb +6 -5
- data/lib/mork/npatch.rb +16 -18
- data/lib/mork/sheet_omr.rb +210 -125
- data/lib/mork/sheet_pdf.rb +15 -8
- data/lib/mork/version.rb +1 -1
- data/mork.gemspec +2 -0
- data/mork.sublime-project +1 -1
- data/spec/mork/extensions_spec.rb +3 -3
- data/spec/mork/grid_omr_spec.rb +2 -1
- data/spec/mork/grid_spec.rb +34 -35
- data/spec/mork/magicko_spec.rb +2 -2
- data/spec/mork/mimage_spec.rb +22 -63
- data/spec/mork/sheet_omr_spec.rb +96 -201
- data/spec/mork/sheet_pdf_spec.rb +9 -10
- data/spec/out/{.gitignore → barcode/.gitignore} +0 -0
- data/spec/out/highlight/.gitignore +4 -0
- data/spec/out/mark/.gitignore +4 -0
- data/spec/out/outline/.gitignore +4 -0
- data/spec/out/pdf/.gitignore +4 -0
- data/spec/out/registration/.gitignore +4 -0
- data/spec/out/text/.gitignore +4 -0
- data/spec/samples/angolo.jpg +0 -0
- data/spec/samples/angolo2.jpg +0 -0
- data/spec/samples/angolo3.jpg +0 -0
- data/spec/samples/boxy.yml +0 -0
- data/spec/samples/grid.yml +0 -0
- data/spec/samples/grid160.yml +0 -0
- data/spec/samples/grid_omr_layout.yml +56 -0
- data/spec/samples/info.yml +15 -39
- data/spec/samples/jdoe/JohnDoe1.jpeg +0 -0
- data/spec/samples/jdoe/JohnDoe2.jpeg +0 -0
- data/spec/samples/jdoe/JohnDoe3.jpeg +0 -0
- data/spec/samples/jdoe/layout.yml +58 -0
- data/spec/samples/layout.yml +2 -2
- data/spec/samples/lucrezia/border1.pdf +0 -0
- data/spec/samples/lucrezia/border2.pdf +0 -0
- data/spec/samples/lucrezia/bw1.pdf +0 -0
- data/spec/samples/lucrezia/bw2.pdf +0 -0
- data/spec/samples/lucrezia/gray1.pdf +0 -0
- data/spec/samples/lucrezia/gray2.pdf +0 -0
- data/spec/samples/{syst/IMG_20150104_0004.jpg → marisol/marisol1.jpg} +0 -0
- data/spec/samples/{syst/IMG_20150104_0009.jpg → marisol/marisol2.jpg} +0 -0
- data/spec/samples/{syst/IMG_20150104_0011.jpg → marisol/marisol3.jpg} +0 -0
- data/spec/samples/reg_mark.jpg +0 -0
- data/spec/samples/rm00.jpeg +0 -0
- data/spec/samples/rm01.jpeg +0 -0
- data/spec/samples/rm02.jpeg +0 -0
- data/spec/samples/rm03.jpeg +0 -0
- data/spec/samples/rm04.jpeg +0 -0
- data/spec/samples/rm05.jpeg +0 -0
- data/spec/samples/syst/barr0.jpg +0 -0
- data/spec/samples/syst/barr1.jpg +0 -0
- data/spec/samples/syst/barr2.jpg +0 -0
- data/spec/samples/syst/bell0.jpg +0 -0
- data/spec/samples/syst/bell1.jpg +0 -0
- data/spec/samples/syst/bell2.jpg +0 -0
- data/spec/samples/syst/bila0.jpg +0 -0
- data/spec/samples/syst/bila1.jpg +0 -0
- data/spec/samples/syst/bila2.jpg +0 -0
- data/spec/samples/syst/bila3.jpg +0 -0
- data/spec/samples/syst/bila4.jpg +0 -0
- data/spec/samples/syst/bone0.jpg +0 -0
- data/spec/samples/syst/bone1.jpg +0 -0
- data/spec/samples/syst/bone2.jpg +0 -0
- data/spec/samples/syst/cost0.jpg +0 -0
- data/spec/samples/syst/cost1.jpg +0 -0
- data/spec/samples/syst/cost2.jpg +0 -0
- data/spec/samples/syst/cost3.jpg +0 -0
- data/spec/samples/syst/cost4.jpg +0 -0
- data/spec/samples/syst/dald0.jpg +0 -0
- data/spec/samples/syst/dald1.jpg +0 -0
- data/spec/samples/syst/dald2.jpg +0 -0
- data/spec/samples/syst/dald3.jpg +0 -0
- data/spec/samples/syst/dald4.jpg +0 -0
- data/spec/samples/syst/dign0.jpg +0 -0
- data/spec/samples/syst/dign1.jpg +0 -0
- data/spec/samples/syst/dign2.jpg +0 -0
- data/spec/samples/syst/dive0.jpg +0 -0
- data/spec/samples/syst/dive1.jpg +0 -0
- data/spec/samples/syst/dive2.jpg +0 -0
- data/spec/samples/syst/histo.m +0 -0
- data/spec/samples/{syst_grid.yml → syst/layout.yml} +0 -0
- data/spec/spec_helper.rb +29 -29
- metadata +49 -62
- data/auto.txt +0 -0
- data/spec/samples/out-1.jpg +0 -0
- data/spec/samples/qzc013.jpg +0 -0
- data/spec/samples/sample02.jpg +0 -0
- data/spec/samples/sample_gray.jpg +0 -0
- data/spec/samples/sheet.jpg +0 -0
- data/spec/samples/sheet666.jpg +0 -0
- data/spec/samples/slanted.jpg +0 -0
- data/spec/samples/slanted.yml +0 -54
- data/spec/samples/syst/IMG_20150104_0004.txt +0 -4955
- data/spec/samples/syst/IMG_20150104_0009.txt +0 -4955
- data/spec/samples/syst/IMG_20150104_0011.txt +0 -4955
- data/spec/samples/syst/SCN_0001.jpg +0 -0
- data/spec/samples/syst/SCN_0001.txt +0 -4955
- data/spec/samples/syst/barr0.txt +0 -4955
- data/spec/samples/syst/barr1.txt +0 -4955
- data/spec/samples/syst/barr2.txt +0 -4955
- data/spec/samples/syst/bell0.txt +0 -4955
- data/spec/samples/syst/bell1.txt +0 -4955
- data/spec/samples/syst/bell2.txt +0 -4955
- data/spec/samples/syst/bila0.txt +0 -4955
- data/spec/samples/syst/bila1.txt +0 -4955
- data/spec/samples/syst/bila2.txt +0 -4955
- data/spec/samples/syst/bila3.txt +0 -4955
- data/spec/samples/syst/bila4.txt +0 -4955
- data/spec/samples/syst/bone0.txt +0 -4955
- data/spec/samples/syst/bone1.txt +0 -4955
- data/spec/samples/syst/bone2.txt +0 -4955
- data/spec/samples/syst/cost0.txt +0 -4955
- data/spec/samples/syst/cost1.txt +0 -4955
- data/spec/samples/syst/cost2.txt +0 -4955
- data/spec/samples/syst/cost3.txt +0 -4955
- data/spec/samples/syst/cost4.txt +0 -4955
- data/spec/samples/syst/dald0.txt +0 -4955
- data/spec/samples/syst/dald1.txt +0 -4955
- data/spec/samples/syst/dald2.txt +0 -4955
- data/spec/samples/syst/dald3.txt +0 -4955
- data/spec/samples/syst/dald4.txt +0 -4955
- data/spec/samples/syst/dign0.txt +0 -4955
- data/spec/samples/syst/dign1.txt +0 -4955
- data/spec/samples/syst/dign2.txt +0 -4955
- data/spec/samples/syst/dive0.txt +0 -4955
- data/spec/samples/syst/dive1.txt +0 -4955
- data/spec/samples/syst/dive2.txt +0 -4955
- data/spec/samples/syst/out0000.jpg +0 -0
- data/spec/samples/syst/out0000.txt +0 -4955
- data/spec/samples/syst/out0001.jpg +0 -0
- data/spec/samples/syst/out0001.txt +0 -4955
- data/spec/samples/syst/out0002.jpg +0 -0
- data/spec/samples/syst/out0002.txt +0 -4955
- data/spec/samples/syst/qzc013.jpg +0 -0
- data/spec/samples/syst/qzc013.txt +0 -4955
- data/spec/samples/syst/sample_gray.jpg +0 -0
- data/spec/samples/syst/sample_gray.txt +0 -4955
- data/spec/samples/two_pages.pdf +0 -0
data/lib/mork/mimage.rb
CHANGED
|
@@ -2,8 +2,7 @@ require 'mork/npatch'
|
|
|
2
2
|
require 'mork/magicko'
|
|
3
3
|
|
|
4
4
|
module Mork
|
|
5
|
-
#
|
|
6
|
-
# Note that Mimage is NOT intended as public API, it should only be called by SheetOMR
|
|
5
|
+
# @private
|
|
7
6
|
class Mimage
|
|
8
7
|
attr_reader :rm
|
|
9
8
|
|
|
@@ -13,7 +12,6 @@ module Mork
|
|
|
13
12
|
@grom = grom.set_page_size @mack.width, @mack.height
|
|
14
13
|
@rm = {} # registration mark centers
|
|
15
14
|
@valid = register
|
|
16
|
-
# @writing = nil
|
|
17
15
|
end
|
|
18
16
|
|
|
19
17
|
def valid?
|
|
@@ -25,66 +23,91 @@ module Mork
|
|
|
25
23
|
tl: @rm[:tl][:status],
|
|
26
24
|
tr: @rm[:tr][:status],
|
|
27
25
|
br: @rm[:br][:status],
|
|
28
|
-
bl: @rm[:bl][:status]
|
|
29
|
-
write: @writing
|
|
26
|
+
bl: @rm[:bl][:status]
|
|
30
27
|
}
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
def marked
|
|
34
|
-
|
|
30
|
+
def marked
|
|
31
|
+
@logical_array_of_marked_cells ||= begin # memoization necessary?
|
|
32
|
+
itemator { |q, c| shade_of(q, c) < choice_threshold }
|
|
33
|
+
end
|
|
35
34
|
end
|
|
36
35
|
|
|
37
|
-
def
|
|
38
|
-
|
|
36
|
+
def marked_int
|
|
37
|
+
marked.map do |q|
|
|
38
|
+
[].tap do |choices|
|
|
39
|
+
q.each_with_index do |choice, idx|
|
|
40
|
+
choices << idx if choice
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
39
44
|
end
|
|
40
45
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
def barcode_bits
|
|
47
|
+
@barcode_bits ||= begin
|
|
48
|
+
@grom.barcode_bits.times.collect do |b|
|
|
49
|
+
reg_pixels.average(@grom.barcode_bit_area b+1) < barcode_threshold
|
|
50
|
+
end
|
|
51
|
+
end
|
|
44
52
|
end
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
def overlay(what, where)
|
|
55
|
+
areas = case where
|
|
56
|
+
when :barcode
|
|
57
|
+
@grom.barcode_areas barcode_bits
|
|
58
|
+
when :cal
|
|
59
|
+
@grom.calibration_cell_areas
|
|
60
|
+
when :marked
|
|
61
|
+
choice_cell_areas marked_int
|
|
62
|
+
when :all
|
|
63
|
+
all_choice_cell_areas
|
|
64
|
+
when :max
|
|
65
|
+
@grom.max_questions.times.map { |i| (0...@grom.max_choices_per_question).to_a }
|
|
66
|
+
when Array
|
|
67
|
+
choice_cell_areas where
|
|
68
|
+
else
|
|
69
|
+
raise ArgumentError, 'Invalid overlay argument “where”'
|
|
70
|
+
end
|
|
71
|
+
round = where != :barcode
|
|
72
|
+
@mack.send what, areas, round
|
|
52
73
|
end
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
# write the underlying MiniMagick::Image to disk;
|
|
76
|
+
# if no file name is given, image is processed in-place;
|
|
77
|
+
# if the 2nd arg is false, then stretching is not applied
|
|
78
|
+
def save(fname=nil, reg=true)
|
|
79
|
+
pp = reg ? @rm : nil
|
|
80
|
+
@mack.save fname, pp
|
|
57
81
|
end
|
|
58
82
|
|
|
59
|
-
def
|
|
60
|
-
@mack.
|
|
83
|
+
def save_registration(fname)
|
|
84
|
+
each_corner { |c| @mack.plus @rm[c][:x], @rm[c][:y], 30 }
|
|
85
|
+
each_corner { |c| @mack.outline [@grom.rm_crop_area(c)], false }
|
|
86
|
+
@mack.save fname, nil
|
|
61
87
|
end
|
|
62
88
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
89
|
+
# ============================================================#
|
|
90
|
+
private #
|
|
91
|
+
# ============================================================#
|
|
66
92
|
|
|
67
|
-
def
|
|
68
|
-
|
|
93
|
+
def itemator(items=@nitems)
|
|
94
|
+
items.map.with_index do |cho, q|
|
|
95
|
+
if cho.is_a? Fixnum
|
|
96
|
+
cho.times.map { |c| yield q, c }
|
|
97
|
+
else
|
|
98
|
+
cho.map { |c| yield q, c }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
69
101
|
end
|
|
70
102
|
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
cells = [cells] if cells.is_a? Hash
|
|
74
|
-
@mack.cross coordinates_of(cells)
|
|
103
|
+
def choice_cell_areas(cells)
|
|
104
|
+
itemator(cells) { |q,c| @grom.choice_cell_area q, c }.flatten
|
|
75
105
|
end
|
|
76
106
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
# if the 2nd arg is false, then stretching is not applied
|
|
80
|
-
def write(fname=nil, reg=true)
|
|
81
|
-
pp = reg ? @rm : nil
|
|
82
|
-
@mack.write fname, pp
|
|
107
|
+
def all_choice_cell_areas
|
|
108
|
+
@all_choice_cell_areas ||= choice_cell_areas(@nitems)
|
|
83
109
|
end
|
|
84
110
|
|
|
85
|
-
# ============================================================#
|
|
86
|
-
private #
|
|
87
|
-
# ============================================================#
|
|
88
111
|
def each_corner
|
|
89
112
|
[:tl, :tr, :br, :bl].each { |c| yield c }
|
|
90
113
|
end
|
|
@@ -95,17 +118,14 @@ module Mork
|
|
|
95
118
|
|
|
96
119
|
def choice_cell_averages
|
|
97
120
|
@choice_cell_averages ||= begin
|
|
98
|
-
|
|
99
|
-
cho.times.collect do |c|
|
|
100
|
-
reg_pixels.average @grom.choice_cell_area(q, c)
|
|
101
|
-
end
|
|
102
|
-
end
|
|
121
|
+
itemator { |q, c| reg_pixels.average @grom.choice_cell_area(q, c) }
|
|
103
122
|
end
|
|
104
123
|
end
|
|
105
124
|
|
|
106
125
|
# TODO: 0.75 should be a parameter
|
|
107
126
|
def choice_threshold
|
|
108
|
-
|
|
127
|
+
# puts "CT #{@grom.choice_threshold.inspect}"
|
|
128
|
+
@choice_threshold ||= (cal_cell_mean - darkest_cell_mean) * @grom.choice_threshold + darkest_cell_mean
|
|
109
129
|
end
|
|
110
130
|
|
|
111
131
|
def barcode_threshold
|
|
@@ -142,10 +162,6 @@ module Mork
|
|
|
142
162
|
# plus the stdev of the search area as quality control
|
|
143
163
|
def register
|
|
144
164
|
each_corner { |c| @rm[c] = rm_centroid_on c }
|
|
145
|
-
# puts "TL: #{@rm[:tl].inspect}"
|
|
146
|
-
# puts "TR: #{@rm[:tr].inspect}"
|
|
147
|
-
# puts "BR: #{@rm[:br].inspect}"
|
|
148
|
-
# puts "BL: #{@rm[:bl].inspect}"
|
|
149
165
|
@rm.all? { |k,v| v[:status] == :ok }
|
|
150
166
|
end
|
|
151
167
|
|
|
@@ -154,7 +170,6 @@ module Mork
|
|
|
154
170
|
def rm_centroid_on(corner)
|
|
155
171
|
c = @grom.rm_crop_area(corner)
|
|
156
172
|
p = @mack.rm_patch(c, @grom.rm_blur, @grom.rm_dilate)
|
|
157
|
-
# puts "REG #{@grom.rm_blur} - #{@grom.rm_dilate} - C #{c.inspect}"
|
|
158
173
|
n = NPatch.new(p, c.w, c.h)
|
|
159
174
|
cx, cy, sd = n.centroid
|
|
160
175
|
st = (cx < 2) or (cy < 2) or (cy > c.h-2) or (cx > c.w-2)
|
|
@@ -197,3 +212,49 @@ end
|
|
|
197
212
|
# @mack.raw_patch
|
|
198
213
|
# end
|
|
199
214
|
|
|
215
|
+
# def outline(cells)
|
|
216
|
+
# return if cells.empty?
|
|
217
|
+
# @mack.outline coordinates_of(cells)
|
|
218
|
+
# end
|
|
219
|
+
|
|
220
|
+
# # highlight_cells(cells, roundedness)
|
|
221
|
+
# #
|
|
222
|
+
# # partially transparent yellow on top of choice cells
|
|
223
|
+
# def highlight_cells(cells)
|
|
224
|
+
# return if cells.empty?
|
|
225
|
+
# @mack.highlight_cells coordinates_of(cells)
|
|
226
|
+
# end
|
|
227
|
+
|
|
228
|
+
# def highlight_all_choices
|
|
229
|
+
# cells = (0...@grom.max_questions).collect { |i| (0...@grom.max_choices_per_question).to_a }
|
|
230
|
+
# highlight_cells cells
|
|
231
|
+
# end
|
|
232
|
+
|
|
233
|
+
# def highlight_barcode(bitstring)
|
|
234
|
+
# @mack.highlight_rect @grom.barcode_bit_areas bitstring
|
|
235
|
+
# end
|
|
236
|
+
|
|
237
|
+
# def highlight_rm_centers
|
|
238
|
+
# each_corner { |c| @mack.plus @rm[c][:x], @rm[c][:y], 20 }
|
|
239
|
+
# end
|
|
240
|
+
|
|
241
|
+
# def highlight_rm_areas
|
|
242
|
+
# each_corner { |c| @mack.highlight_area @grom.rm_crop_area(c) }
|
|
243
|
+
# end
|
|
244
|
+
|
|
245
|
+
# def cross(cells)
|
|
246
|
+
# return if cells.empty?
|
|
247
|
+
# cells = [cells] if cells.is_a? Hash
|
|
248
|
+
# @mack.cross coordinates_of(cells)
|
|
249
|
+
# end
|
|
250
|
+
|
|
251
|
+
# def barcode_bit?(i)
|
|
252
|
+
# reg_pixels.average(@grom.barcode_bit_area i+1) < barcode_threshold
|
|
253
|
+
# end
|
|
254
|
+
|
|
255
|
+
# puts "TL: #{@rm[:tl].inspect}"
|
|
256
|
+
# puts "TR: #{@rm[:tr].inspect}"
|
|
257
|
+
# puts "BR: #{@rm[:br].inspect}"
|
|
258
|
+
# puts "BL: #{@rm[:bl].inspect}"
|
|
259
|
+
|
|
260
|
+
# puts "REG #{@grom.rm_blur} - #{@grom.rm_dilate} - C #{c.inspect}"
|
data/lib/mork/mimage_list.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# require 'RMagick'
|
|
2
2
|
|
|
3
3
|
module Mork
|
|
4
|
-
#
|
|
4
|
+
# @private
|
|
5
|
+
# The class MimageList, currently abandoned
|
|
5
6
|
class MimageList
|
|
6
7
|
def initialize(fname)
|
|
7
8
|
raise "Initializing a MimageList requires a string" unless fname.class == String
|
|
@@ -11,21 +12,21 @@ module Mork
|
|
|
11
12
|
@images = Magick::ImageList.new(fname)
|
|
12
13
|
end
|
|
13
14
|
end
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
def shift
|
|
16
17
|
Mimage.new @images.shift
|
|
17
18
|
end
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
def [] (i)
|
|
20
21
|
# puts "I: #{i}"
|
|
21
22
|
# puts @images[i].inspect
|
|
22
23
|
Mimage.new @images[i]
|
|
23
24
|
end
|
|
24
|
-
|
|
25
|
+
|
|
25
26
|
def each
|
|
26
27
|
@images.each do |i|
|
|
27
28
|
yield Mimage.new i
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
|
-
end
|
|
32
|
+
end
|
data/lib/mork/npatch.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'narray'
|
|
2
2
|
|
|
3
3
|
module Mork
|
|
4
|
+
# @private
|
|
4
5
|
# NPatch handles low-level computations on pixels by leveraging NArray
|
|
5
6
|
class NPatch
|
|
6
7
|
# NPatch.new(source, width, height) constructs an NPatch object
|
|
@@ -9,36 +10,28 @@ module Mork
|
|
|
9
10
|
def initialize(source, width, height)
|
|
10
11
|
@patch = NArray.float(width, height)
|
|
11
12
|
@patch[true] = source
|
|
12
|
-
@width = width
|
|
13
|
-
@height = height
|
|
13
|
+
# @width = width
|
|
14
|
+
# @height = height
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
def average(
|
|
17
|
-
@patch[
|
|
17
|
+
def average(coord)
|
|
18
|
+
@patch[coord.x_rng, coord.y_rng].mean
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
def stddev(
|
|
21
|
-
@patch[
|
|
21
|
+
def stddev(coord)
|
|
22
|
+
@patch[coord.x_rng, coord.y_rng].stddev
|
|
22
23
|
end
|
|
23
24
|
|
|
24
|
-
def length
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
25
|
+
# def length
|
|
26
|
+
# # is this only going to be used for testing purposes?
|
|
27
|
+
# @patch.length
|
|
28
|
+
# end
|
|
28
29
|
|
|
29
30
|
def centroid
|
|
30
31
|
xp = @patch.sum(1).to_a
|
|
31
32
|
yp = @patch.sum(0).to_a
|
|
32
33
|
return xp.find_index(xp.min), yp.find_index(yp.min), @patch.stddev
|
|
33
34
|
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def sufficient_contrast?(p)
|
|
38
|
-
# puts "Contrast: #{p.stddev}"
|
|
39
|
-
# tested with the few examples: spec/samples/rm0x.jpeg
|
|
40
|
-
p.stddev > 20
|
|
41
|
-
end
|
|
42
35
|
end
|
|
43
36
|
end
|
|
44
37
|
|
|
@@ -61,3 +54,8 @@ end
|
|
|
61
54
|
# p
|
|
62
55
|
# end
|
|
63
56
|
|
|
57
|
+
# def sufficient_contrast?(p)
|
|
58
|
+
# # puts "Contrast: #{p.stddev}"
|
|
59
|
+
# # tested with the few examples: spec/samples/rm0x.jpeg
|
|
60
|
+
# p.stddev > 20
|
|
61
|
+
# end
|