Class: Qrio::SamplingGrid

Inherits:
Object
  • Object
show all
Defined in:
lib/qrio/sampling_grid.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(matrix, finder_patterns) ⇒ SamplingGrid

Returns a new instance of SamplingGrid.



8
9
10
11
12
13
14
15
# File 'lib/qrio/sampling_grid.rb', line 8

def initialize(matrix, finder_patterns)
  @matrix          = matrix
  @finder_patterns = finder_patterns
  @angles          = []

  find_origin_corner
  detect_orientation
end

Instance Attribute Details

#anglesObject (readonly)

Returns the value of attribute angles.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def angles
  @angles
end

#block_heightObject (readonly)

Returns the value of attribute block_height.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def block_height
  @block_height
end

#block_widthObject (readonly)

Returns the value of attribute block_width.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def block_width
  @block_width
end

#bottom_leftObject (readonly)

Returns the value of attribute bottom_left.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def bottom_left
  @bottom_left
end

#boundsObject (readonly)

Returns the value of attribute bounds.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def bounds
  @bounds
end

#finder_patternsObject (readonly)

Returns the value of attribute finder_patterns.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def finder_patterns
  @finder_patterns
end

#matrixObject (readonly)

Returns the value of attribute matrix.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def matrix
  @matrix
end

#orientationObject (readonly)

Returns the value of attribute orientation.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def orientation
  @orientation
end

#origin_cornerObject (readonly)

Returns the value of attribute origin_corner.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def origin_corner
  @origin_corner
end

#provisional_versionObject (readonly)

Returns the value of attribute provisional_version.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def provisional_version
  @provisional_version
end

#top_rightObject (readonly)

Returns the value of attribute top_right.



3
4
5
# File 'lib/qrio/sampling_grid.rb', line 3

def top_right
  @top_right
end

Instance Method Details

#build_finder_pattern_neighborsObject



77
78
79
80
81
82
83
84
# File 'lib/qrio/sampling_grid.rb', line 77

def build_finder_pattern_neighbors
  @finder_patterns.each do |source|
    @finder_patterns.each do |destination|
      next if source.center == destination.center
      @angles << Neighbor.new(source, destination)
    end
  end
end

#detect_orientationObject

which way is the QR rotated?

0) normal - shared finder patterns in top left
1)        - shared finder patterns in top right
2)        - shared finder patterns in bottom right
3)        - shared finder patterns in bottom left


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/qrio/sampling_grid.rb', line 48

def detect_orientation
  # TODO : handle multiple possible matches
  other_corners = non_origin_finder_patterns

  dc = other_corners.map(&:distance).inject(0){|s,d| s + d } / 2.0
  threshold = dc / 2.0

  other_corners = other_corners.map(&:destination)

  set_block_dimensions(@origin_corner, *other_corners)
  @provisional_version = ((dc / @block_width).round - 10) / 4

  xs = other_corners.map{|fp| fp.center.first }
  ys = other_corners.map{|fp| fp.center.last }

  above = ys.select{|y| y < (@origin_corner.center.last - threshold) }
  left  = xs.select{|x| x < (@origin_corner.center.first - threshold) }

  @orientation = if above.any?
    left.any? ? 2 : 3
  else
    left.any? ? 1 : 0
  end
end

#extracted_pixelsObject



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/qrio/sampling_grid.rb', line 128

def extracted_pixels
  start_x = @origin_corner.center.first - (@block_width  * 3.0)
  start_y = @origin_corner.center.last  - (@block_height * 3.0)

  dest_x = @bottom_left.center.first - (@block_width * 3.0)
  dest_y = @top_right.center.last  - (@block_height * 3.0)

  # TODO : take bottom right alignment pattern into consideration
  dy = (dest_y - start_y) / logical_width
  dx = (dest_x - start_x) / logical_height

  logical_height.round.times do |row_index|
    row_start_x = start_x + row_index * dx
    row_start_y = start_y + (row_index * @block_height)

    logical_width.round.times do |col_index|
      x = row_start_x + (col_index * @block_width)
      y = row_start_y + (col_index * dy)

      yield x.round, y.round
    end
  end
end

#find_origin_corner(normalized = false) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/qrio/sampling_grid.rb', line 17

def find_origin_corner(normalized=false)
  build_finder_pattern_neighbors

  shared_corners = @finder_patterns.select do |fp|
    fp.neighbors.select(&:right_angle?).count > 1
  end

  # TODO : handle multiple possible matches
  if @origin_corner = shared_corners.first
    if normalized
      # we have correct orientation, identify fp positions
      @top_right = non_origin_finder_patterns.map(&:destination).detect{|fp| fp.center.last < @matrix.height / 2.0 }
      @bottom_left = non_origin_finder_patterns.map(&:destination).detect{|fp| fp.center.first < @matrix.width / 2.0 }
    else
      set_bounds
    end
  end
end

#logical_heightObject



95
96
97
# File 'lib/qrio/sampling_grid.rb', line 95

def logical_height
  @matrix.height / @block_height
end

#logical_widthObject



91
92
93
# File 'lib/qrio/sampling_grid.rb', line 91

def logical_width
  @matrix.width / @block_width
end

#non_origin_finder_patternsObject



73
74
75
# File 'lib/qrio/sampling_grid.rb', line 73

def non_origin_finder_patterns
  @origin_corner.neighbors.select(&:right_angle?)[0,2]
end

#normalizeObject



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/qrio/sampling_grid.rb', line 99

def normalize
  @matrix = @matrix.extract(*@bounds.to_point_size)
  translate(*@bounds.top_left)

  if @orientation > 0
    (4 - @orientation).times do
      rotate
    end
  end

  find_origin_corner(true)
end

#rotateObject



123
124
125
126
# File 'lib/qrio/sampling_grid.rb', line 123

def rotate
  @matrix = @matrix.rotate
  @finder_patterns.map!{|f| f.rotate(@bounds.width, @bounds.height) }
end

#set_block_dimensions(*finder_patterns) ⇒ Object



86
87
88
89
# File 'lib/qrio/sampling_grid.rb', line 86

def set_block_dimensions(*finder_patterns)
  @block_width  = finder_patterns.inject(0){|s,f| s + f.width } / 21.0
  @block_height = finder_patterns.inject(0){|s,f| s + f.height } / 21.0
end

#set_boundsObject



36
37
38
39
40
41
# File 'lib/qrio/sampling_grid.rb', line 36

def set_bounds
  @bounds = @origin_corner.dup
  @bounds.neighbors.select(&:right_angle?).each do |n|
    @bounds = @bounds.union(n.destination)
  end
end

#translate(x, y) ⇒ Object



112
113
114
115
116
117
118
119
120
121
# File 'lib/qrio/sampling_grid.rb', line 112

def translate(x, y)
  @angles = []
  other_corners = non_origin_finder_patterns.map(&:destination)

  @origin_corner = @origin_corner.translate(x, y)
  translated = [@origin_corner]
  translated += other_corners.map{|c| c.translate(x, y) }

  @finder_patterns = translated
end