Class: Panomosity::Optimizer

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/panomosity/optimizer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

#calculate_average, #calculate_average_and_std, #remove_outliers

Constructor Details

#initialize(panorama) ⇒ Optimizer

Returns a new instance of Optimizer.



9
10
11
12
13
14
15
# File 'lib/panomosity/optimizer.rb', line 9

def initialize(panorama)
  @panorama = panorama
  @images = @panorama.images
  @control_points = @panorama.control_points
  @optimisation_variables = @panorama.optimisation_variables
  @logger = @panorama.logger
end

Instance Attribute Details

#control_pointsObject

Returns the value of attribute control_points.



7
8
9
# File 'lib/panomosity/optimizer.rb', line 7

def control_points
  @control_points
end

#imagesObject

Returns the value of attribute images.



7
8
9
# File 'lib/panomosity/optimizer.rb', line 7

def images
  @images
end

#loggerObject

Returns the value of attribute logger.



7
8
9
# File 'lib/panomosity/optimizer.rb', line 7

def logger
  @logger
end

#optimisation_variablesObject

Returns the value of attribute optimisation_variables.



7
8
9
# File 'lib/panomosity/optimizer.rb', line 7

def optimisation_variables
  @optimisation_variables
end

#panoramaObject

Returns the value of attribute panorama.



7
8
9
# File 'lib/panomosity/optimizer.rb', line 7

def panorama
  @panorama
end

Instance Method Details

#calculate_average_distanceObject



141
142
143
144
145
146
# File 'lib/panomosity/optimizer.rb', line 141

def calculate_average_distance
  panorama.calculate_neighborhoods
  horizontal_distances = GeneralizedNeighborhood.horizontal[0..4].map(&:dist_avg)
  vertical_distances = GeneralizedNeighborhood.vertical[0..4].map(&:dist_avg)
  calculate_average(values: horizontal_distances + vertical_distances)
end

#calculate_estimated_rollObject



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/panomosity/optimizer.rb', line 154

def calculate_estimated_roll
  return
  # setting to 0
  images.each do |image|
    image.r = 0.0
  end
  panorama.calculate_neighborhoods
  cps = GeneralizedNeighborhood.horizontal.first.control_points
  avg, std = *calculate_average_and_std(values: cps.map(&:roll))
  # 0.1 degrees of std
  while std > 0.1
    cps.select!{|c| (avg - c.roll).abs <= std}
    avg, std = *calculate_average_and_std(values: cps.map(&:roll))
  end
  avg
rescue
  logger.debug 'estimating roll failed'
  nil
end

#recalculate_average_distance(roll:) ⇒ Object



148
149
150
151
152
# File 'lib/panomosity/optimizer.rb', line 148

def recalculate_average_distance(roll:)
  panorama.images.each { |i| i.r = roll }
  panorama.control_points = ControlPoint.calculate_distances(panorama.images, panorama.variable)
  calculate_average_distance
end

#runObject



17
18
19
20
21
22
23
24
25
26
# File 'lib/panomosity/optimizer.rb', line 17

def run
  variables_to_optimize = optimisation_variables.map { |v| v.attributes.keys }.flatten.uniq.sort
  if variables_to_optimize == [:d, :e]
    run_position_optimizer
  elsif variables_to_optimize == [:r]
    run_roll_optimizer
  else
    logger.error 'no optimization strategy found'
  end
end

#run_position_optimizer(xh_avg: nil, yh_avg: nil, xv_avg: nil, yv_avg: nil) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/panomosity/optimizer.rb', line 28

def run_position_optimizer(xh_avg: nil, yh_avg: nil, xv_avg: nil, yv_avg: nil)
  if xh_avg || yh_avg || xv_avg || yv_avg
    logger.info "applying custom values of xh_avg: #{xh_avg}, yh_avg: #{yh_avg}, xv_avg: #{xv_avg}, yv_avg: #{yv_avg}"
  end

  panorama.calculate_neighborhoods unless xh_avg && yh_avg && xv_avg && yv_avg

  ds = images.map(&:d).uniq.sort
  es = images.map(&:e).uniq.sort

  # get the average error for the best neighborhood group
  x_avg = xh_avg || GeneralizedNeighborhood.horizontal.first.x_avg
  y_avg = yv_avg || GeneralizedNeighborhood.vertical.first.y_avg

  # start horizontally
  d_map = {}
  ds.each_with_index do |d, i|
    d_map[d] = d + -x_avg * i
  end
  logger.debug "created d_map #{d_map}"

  # vertical
  e_map = {}
  es.each_with_index do |e, i|
    e_map[e] = e + -y_avg * i
  end
  logger.debug "created e_map #{e_map}"

  # add in the other offset
  x_avg = xv_avg || GeneralizedNeighborhood.vertical.first.x_avg
  y_avg = yh_avg || GeneralizedNeighborhood.horizontal.first.y_avg

  de_map = {}
  d_map.each_with_index do |(dk,dv),di|
    e_map.each_with_index do |(ek,ev),ei|
      de_map["#{dk},#{ek}"] = {}
      de_map["#{dk},#{ek}"][:d] = dv + -x_avg * ei
      de_map["#{dk},#{ek}"][:e] = ev + -y_avg * di
    end
  end
  logger.debug "created de_map #{de_map}"

  logger.debug 'updating image attributes'
  images.each do |image|
    d = image.d
    e = image.e
    image.d = de_map["#{d},#{e}"][:d]
    image.e = de_map["#{d},#{e}"][:e]
  end
end

#run_roll_optimizer(apply_roll: nil) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/panomosity/optimizer.rb', line 79

def run_roll_optimizer(apply_roll: nil)
  r = images.map(&:r).first
  original_roll = r
  logger.debug "current roll #{r}"

  if apply_roll
    logger.info "apply rolling custom roll #{apply_roll}"
    images.each do |image|
      image.r = apply_roll
    end
    return
  end

  roll = calculate_estimated_roll
  if roll
    logger.debug "using calculated horizontal roll #{roll}"
    images.each do |image|
      image.r = roll
    end
    return
  end

  # we grab the top 5 neighborhood groups and get the average distance for them and average that
  dist_avg = calculate_average_distance

  r -= 0.01
  logger.debug "current roll #{r}"
  new_dist_avg = recalculate_average_distance(roll: r)
  logger.debug "avg: #{dist_avg} new_avg: #{new_dist_avg}"

  operation_map = { :- => 'subtracting', :+ => 'adding' }
  if new_dist_avg < dist_avg
    operation = :-
    logger.debug "found that #{operation_map[operation]} roll will decrease distances, resetting roll..."
  else
    operation = :+
    logger.debug "found that #{operation_map[operation]} roll will decrease distances, resetting roll..."
    r = original_roll
    r += 0.01
    logger.debug "current roll #{r}"
    new_dist_avg = recalculate_average_distance(roll: r)
  end

  logger.debug "avg: #{dist_avg} new_avg: #{new_dist_avg}"
  if new_dist_avg > dist_avg
    logger.debug "found that #{operation_map[operation]} roll will also increase distances, leaving roll unchanged "
    r = original_roll
  end

  while new_dist_avg <= dist_avg
    r = r.send(operation, 0.01)
    logger.debug "current roll #{r}"
    dist_avg = new_dist_avg
    new_dist_avg = recalculate_average_distance(roll: r)
    logger.debug "avg: #{dist_avg} new_avg: #{new_dist_avg}"
  end

  images.each do |image|
    image.r = r
  end
end