class Qrio::Qr
Attributes
candidates[R]
finder_patterns[R]
matches[R]
qr[R]
qr_bounds[R]
Public Class Methods
load(filename)
click to toggle source
# 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
new()
click to toggle source
# File lib/qrio/qr.rb, line 6 def initialize initialize_storage end
Public Instance Methods
add_candidate(new_candidate, direction)
click to toggle source
# 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()
click to toggle source
extract the qr into a smaller matrix and rotate to standard orientation
# 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()
click to toggle source
# 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()
click to toggle source
# 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)
click to toggle source
# File lib/qrio/qr.rb, line 63 def find_candidate(offset, origin, segment, direction) FinderPatternSlice.build_matching(offset, origin, segment, direction) end
find_intersections()
click to toggle source
find intersections of horizontal and vertical slices, these are (likely) finder patterns
# 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)
click to toggle source
# 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)
click to toggle source
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]
# 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)
click to toggle source
# 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()
click to toggle source
# 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
Private Instance Methods
initialize_storage()
click to toggle source
# File lib/qrio/qr.rb, line 189 def initialize_storage @candidates = { :horizontal => [], :vertical => [], } @matches = { :horizontal => [], :vertical => [], } @finder_patterns = [] @input_matrix = @extracted_matrix = nil end