Class: Qrio::Qr
Instance Attribute Summary collapse
-
#candidates ⇒ Object
readonly
Returns the value of attribute candidates.
-
#finder_patterns ⇒ Object
readonly
Returns the value of attribute finder_patterns.
-
#matches ⇒ Object
readonly
Returns the value of attribute matches.
-
#qr ⇒ Object
readonly
Returns the value of attribute qr.
-
#qr_bounds ⇒ Object
readonly
Returns the value of attribute qr_bounds.
Class Method Summary collapse
Instance Method Summary collapse
- #add_candidate(new_candidate, direction) ⇒ Object
-
#build_normalized_qr ⇒ Object
extract the qr into a smaller matrix and rotate to standard orientation.
- #filter_candidates ⇒ Object
- #find_alignment_pattern ⇒ Object
- #find_candidate(offset, origin, segment, direction) ⇒ Object
-
#find_intersections ⇒ Object
find intersections of horizontal and vertical slices, these are (likely) finder patterns.
-
#initialize ⇒ Qr
constructor
A new instance of Qr.
- #load_image(filename) ⇒ Object
-
#rle(vector) ⇒ Object
transform a vector of bits in to a run-length encoded vector of widths example: [1, 1, 1, 1, 0, 0, 1, 1, 1] => [4, 2, 3].
- #scan(direction) ⇒ Object
- #set_qr_bounds ⇒ Object
Methods included from ImageDumper
#color, #save_image, #save_to_image
Constructor Details
#initialize ⇒ Qr
Returns a new instance of Qr.
6 7 8 |
# File 'lib/qrio/qr.rb', line 6 def initialize initialize_storage end |
Instance Attribute Details
#candidates ⇒ Object (readonly)
Returns the value of attribute candidates.
3 4 5 |
# File 'lib/qrio/qr.rb', line 3 def candidates @candidates end |
#finder_patterns ⇒ Object (readonly)
Returns the value of attribute finder_patterns.
3 4 5 |
# File 'lib/qrio/qr.rb', line 3 def finder_patterns @finder_patterns end |
#matches ⇒ Object (readonly)
Returns the value of attribute matches.
3 4 5 |
# File 'lib/qrio/qr.rb', line 3 def matches @matches end |
#qr ⇒ Object (readonly)
Returns the value of attribute qr.
3 4 5 |
# File 'lib/qrio/qr.rb', line 3 def qr @qr end |
#qr_bounds ⇒ Object (readonly)
Returns the value of attribute qr_bounds.
3 4 5 |
# File 'lib/qrio/qr.rb', line 3 def qr_bounds @qr_bounds end |
Class Method Details
.load(filename) ⇒ Object
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/qrio/qr.rb', line 10 def self.load(filename) instance = new instance.load_image(filename) instance.scan(:horizontal) instance.scan(:vertical) instance.filter_candidates instance.find_intersections instance.set_qr_bounds instance.build_normalized_qr instance.find_alignment_pattern instance end |
Instance Method Details
#add_candidate(new_candidate, direction) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/qrio/qr.rb', line 67 def add_candidate(new_candidate, direction) added = false @candidates[direction].each_with_index do |existing, index| if new_candidate.adjacent?(existing) @candidates[direction][index] = existing.union(new_candidate) added = true end end @candidates[direction] << new_candidate unless added end |
#build_normalized_qr ⇒ Object
extract the qr into a smaller matrix and rotate to standard orientation
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/qrio/qr.rb', line 128 def build_normalized_qr return false if @sampling_grid.nil? original_orientation = @sampling_grid.orientation @sampling_grid.normalize @extracted_matrix = @sampling_grid.matrix bits = [] @sampling_grid.extracted_pixels do |x, y| bits << @extracted_matrix[x, y] end @qr = QrMatrix.new(bits, @sampling_grid.logical_width, @sampling_grid.logical_height) @translated_matches = { :horizontal => [], :vertical => [] } @translated_finder_patterns = [] @translated_neighbors = [] @matches[:horizontal].each do |m| m = m.translate(*@qr_bounds.top_left) if original_orientation > 0 (4 - original_orientation).times do m = m.rotate(@qr_bounds.width, @qr_bounds.height) end end @translated_matches[:horizontal] << m end @matches[:vertical].each do |m| m = m.translate(*@qr_bounds.top_left) if original_orientation > 0 (4 - original_orientation).times do m = m.rotate(@qr_bounds.width, @qr_bounds.height) end end @translated_matches[:vertical] << m end end |
#filter_candidates ⇒ Object
80 81 82 83 84 85 86 |
# File 'lib/qrio/qr.rb', line 80 def filter_candidates [:horizontal, :vertical].each do |direction| @candidates[direction].uniq.each do |candidate| @matches[direction] << candidate if candidate.matches_aspect_ratio? end end end |
#find_alignment_pattern ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/qrio/qr.rb', line 170 def find_alignment_pattern test_x = @sampling_grid.top_right.center.first - (@sampling_grid.block_width * 3) test_y = @sampling_grid.bottom_left.center.last - (@sampling_grid.block_height * 3) test_x = (test_x - @sampling_grid.block_width / 2.0).round test_y = (test_y - @sampling_grid.block_height / 2.0).round # TODO : this is where the AP *should* be, given no image skew. # starting here, find center of nearest blob that "looks" like an AP. # offset from predicted will be used in sampling grid to account for skew @alignment_pattern = Qrio::Region.new( test_x, test_y, (test_x + @sampling_grid.block_width).round, (test_y + @sampling_grid.block_height).round ) end |
#find_candidate(offset, origin, segment, direction) ⇒ Object
63 64 65 |
# File 'lib/qrio/qr.rb', line 63 def find_candidate(offset, origin, segment, direction) FinderPatternSlice.build_matching(offset, origin, segment, direction) end |
#find_intersections ⇒ Object
find intersections of horizontal and vertical slices, these are (likely) finder patterns
112 113 114 115 116 117 118 |
# File 'lib/qrio/qr.rb', line 112 def find_intersections @matches[:horizontal].each do |h| @matches[:vertical].each do |v| @finder_patterns << h.union(v) if h.intersects?(v) end end end |
#load_image(filename) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/qrio/qr.rb', line 27 def load_image(filename) initialize_storage image_type = File.extname(filename).upcase.gsub('.', '') image_loader_class = "#{ image_type }ImageLoader" image_loader_class = ImageLoader.const_get(image_loader_class) rescue nil if image_loader_class.nil? raise "Image type '#{ image_type }' not supported" end @input_matrix = image_loader_class.load(filename) end |
#rle(vector) ⇒ Object
transform a vector of bits in to a run-length encoded vector of widths example: [1, 1, 1, 1, 0, 0, 1, 1, 1] => [4, 2, 3]
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/qrio/qr.rb', line 90 def rle(vector) v = vector.dup pattern = [] length = 1 last = v.shift v.each do |current| if current === last length += 1 else pattern << length length = 1 last = current end end pattern << length end |
#scan(direction) ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/qrio/qr.rb', line 41 def scan(direction) vectors = direction == :horizontal ? @input_matrix.rows : @input_matrix.columns vectors.each_with_index do |vector, offset| pattern = rle(vector) if pattern.length >= 5 origin = 0 segment = pattern.slice!(0,4) while next_length = pattern.shift segment << next_length if candidate = find_candidate(offset, origin, segment, direction) add_candidate(candidate, direction) end origin += segment.shift end end end end |
#set_qr_bounds ⇒ Object
120 121 122 123 124 125 |
# File 'lib/qrio/qr.rb', line 120 def set_qr_bounds if @finder_patterns.length >= 3 @sampling_grid = SamplingGrid.new(@input_matrix, @finder_patterns) @qr_bounds = @sampling_grid.bounds end end |