panomosity 0.1.10 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec66a2f30f7e065a735367c5f5758ff9bb274c5c180a75de429135d137582874
4
- data.tar.gz: a59ce44c2c0a37b05ae323b46881345b8e0bdb213233abd16ae5a47780270ca9
3
+ metadata.gz: 1866a3bdb83866570192a3ca7f4c12632e28bd035b33c14ffa10b6ce68e5b0aa
4
+ data.tar.gz: 29251ea2be17bb88f0923d6394d25dff0ac3336e5471ed6523e2855785a05404
5
5
  SHA512:
6
- metadata.gz: 2bd782706baca58410c3e4c0e1cce4a4914c086874fe0976f4ebe490a489181ce5321d66692f42398064aef6116db563990c6331ba334700473906717541b704
7
- data.tar.gz: b8afce3fd3dec4ce71a7cf9bcbe3445c5ef4691d092784440560b725fc04e75db46346ba0ac4f99f65bb1622ea65b757e20d2c9be1c3fa53a78a5e6157d94a9d
6
+ metadata.gz: c5b0b268254fb2e5bb3a2e6b1d80852215945b2b692b67f5d1188688cab0b673f869b783ab299d4f4e84b08332a9f90ad2ee64d5ac10162d15514d0b547cb7f7
7
+ data.tar.gz: 192bf1ad022ff2b47dc34e060209fa6b6aa261b066606d51a4cfd9b3ab12f5fb603d695f108e00ba73947e870f3b8d4fb04969424f54ab1a310091b9433d0a69
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- panomosity (0.1.10)
4
+ panomosity (0.1.11)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -71,7 +71,7 @@ module Panomosity
71
71
  cp.pry = image1.e - image2.e + Math.cos(r) * (cp.y2 - cp.y1) - Math.sin(r) * (cp.x2 - cp.x1)
72
72
  cp.prdist = Math.sqrt(cp.prx ** 2 + cp.pry ** 2)
73
73
 
74
- cp.conn_type = image1.d == image2.d ? :vertical : :horizontal
74
+ cp.conn_type = image1.column == image2.column ? :vertical : :horizontal
75
75
  cp.i1 = image1
76
76
  cp.i2 = image2
77
77
  end
@@ -185,5 +185,9 @@ module Panomosity
185
185
  def detailed_info
186
186
  "#{to_s.sub(/\n/, '')} dist #{dist.round(4)} pixel_dist #{px.round(4)},#{py.round(4)},#{pdist.round(4)} pixel_r_dist #{prx.round(4)},#{pry.round(4)},#{prdist.round(4)} conn_type #{conn_type}"
187
187
  end
188
+
189
+ def attributes
190
+ @attributes
191
+ end
188
192
  end
189
193
  end
@@ -156,5 +156,15 @@ module Panomosity
156
156
  normalized_point = [point[0] / magnitude, point[1] / magnitude, point[2] / magnitude]
157
157
  normalized_point
158
158
  end
159
+
160
+ def column
161
+ return unless name =~ /c(\d+)_r(\d+)\.jpg/
162
+ name.scan(/c(\d+)_r(\d+)\.jpg/).flatten.map(&:to_i).first
163
+ end
164
+
165
+ def row
166
+ return unless name =~ /c(\d+)_r(\d+)\.jpg/
167
+ name.scan(/c(\d+)_r(\d+)\.jpg/).flatten.map(&:to_i).last
168
+ end
159
169
  end
160
170
  end
@@ -26,88 +26,117 @@ module Panomosity
26
26
  end
27
27
 
28
28
  def run_position_optimizer
29
+ panorama.calculate_neighborhoods(amount_ratio: 1.0, log: false)
30
+ panorama.calculate_neighborhood_groups(name: :horizontal, pairs: panorama.horizontal_pairs)
31
+ panorama.calculate_neighborhood_groups(name: :vertical, pairs: panorama.vertical_pairs)
32
+
29
33
  ds = images.map(&:d).uniq.sort
30
34
  es = images.map(&:e).uniq.sort
31
35
 
32
36
  # start horizontally
33
- xs = []
34
- es.each_with_index do |e, i_e|
35
- next if i_e == es.count - 1
36
- ds.each_with_index do |d, i_d|
37
- next if i_d == ds.count - 1
38
- image1 = images.find { |i| i.d == d && i.e == e }
39
- image2 = images.find { |i| i.d == ds[i_d+1] && i.e == e }
40
- id1, id2 = *[image1.id, image2.id].minmax
41
- cps = control_points.select { |cp| cp.conn_type == :horizontal && cp.n1 == id1 && cp.n2 == id2 }
42
- xs << cps.map(&:prx)
43
- end
44
- end
45
-
46
- xs.flatten!
47
- x_avg, x_std = *calculate_average_and_std(name: :x, values: xs, logger: logger)
48
- logger.debug 'filter first standard deviations'
49
- xs.select! { |x| (x - x_avg).abs < x_std }
50
- x_avg, x_std = *calculate_average_and_std(name: :x, values: xs, logger: logger)
51
- logger.debug 'filter second standard deviations'
52
- xs.select! { |x| (x - x_avg).abs < x_std }
53
- x_avg, _ = *calculate_average_and_std(name: :x, values: xs, logger: logger)
37
+ x_avg = panorama.horizontal_neighborhoods_group.first[:x_avg]
54
38
 
55
39
  d_map = {}
56
40
  ds.each_with_index do |d, i|
57
- if d == ds.first
58
- d_map[d] = d
59
- else
60
- d_map[d] = d + -x_avg * i
61
- end
41
+ d_map[d] = d + -x_avg * i
62
42
  end
63
43
  logger.debug "created d_map #{d_map}"
64
44
 
65
45
  # vertical
66
- ys = []
67
- ds.each_with_index do |d, i_d|
68
- next if i_d == ds.count - 1
69
- es.each_with_index do |e, i_e|
70
- next if i_e == es.count - 1
71
- image1 = images.find { |i| i.d == d && i.e == e }
72
- image2 = images.find { |i| i.d == d && i.e == es[i_e+1] }
73
- id1, id2 = *[image1.id, image2.id].minmax
74
- cps = control_points.select { |cp| cp.conn_type == :vertical && cp.n1 == id1 && cp.n2 == id2 }
75
- ys << cps.map(&:pry)
76
- end
77
- end
78
-
79
- ys.flatten!
80
- y_avg, y_std = *calculate_average_and_std(name: :y, values: ys, logger: logger)
81
- logger.debug 'filter first standard deviations'
82
- ys.select! { |y| (y - y_avg).abs < y_std }
83
- y_avg, y_std = *calculate_average_and_std(name: :y, values: ys, logger: logger)
84
- logger.debug 'filter second standard deviations'
85
- ys.select! { |y| (y - y_avg).abs < y_std }
86
- y_avg, _ = *calculate_average_and_std(name: :y, values: ys, logger: logger)
46
+ y_avg = panorama.vertical_neighborhoods_group.first[:y_avg]
87
47
 
88
48
  e_map = {}
89
49
  es.each_with_index do |e, i|
90
- if e == es.first
91
- e_map[e] = e
92
- else
93
- e_map[e] = e + -y_avg * i
94
- end
50
+ e_map[e] = e + -y_avg * i
95
51
  end
96
52
  logger.debug "created e_map #{e_map}"
97
53
 
54
+ x_avg = panorama.vertical_neighborhoods_group.first[:x_avg]
55
+ y_avg = panorama.horizontal_neighborhoods_group.first[:y_avg]
56
+
57
+ de_map = {}
58
+ d_map.each_with_index do |(dk,dv),di|
59
+ e_map.each_with_index do |(ek,ev),ei|
60
+ de_map["#{dk},#{ek}"] = {}
61
+ de_map["#{dk},#{ek}"][:d] = dv + -x_avg * ei
62
+ de_map["#{dk},#{ek}"][:e] = ev + -y_avg * di
63
+ end
64
+ end
65
+ logger.debug "created de_map #{de_map}"
66
+
98
67
  logger.debug 'updating image attributes'
99
68
  images.each do |image|
100
- image.d = d_map[image.d]
101
- image.e = e_map[image.e]
69
+ d = image.d
70
+ e = image.e
71
+ image.d = de_map["#{d},#{e}"][:d]
72
+ image.e = de_map["#{d},#{e}"][:e]
102
73
  end
103
74
  end
104
75
 
105
76
  def run_roll_optimizer
106
77
  r = images.map(&:r).first
107
- amount = (0.2 * (images.count)).floor
108
- pairs = control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, cps| [calculate_average_and_std(values: cps.map(&:prdist)).first, -cps.count] }[0..(amount-1)]
109
- pairs.select{|_,cps| cps.first.conn_type == :horizontal}.each do |pair, cps|
110
- logger.debug "#{pair} #{cps.map(&:detailed_info).join("\n")}"
78
+ original_roll = r
79
+ logger.debug "current roll #{r}"
80
+ panorama.calculate_neighborhoods(amount_ratio: 1.0, log: false)
81
+ panorama.calculate_neighborhood_groups(name: :horizontal, pairs: panorama.horizontal_pairs)
82
+ panorama.calculate_neighborhood_groups(name: :vertical, pairs: panorama.vertical_pairs)
83
+
84
+ # we grab the top 5 neighborhood groups and get the average distance for them and average that
85
+ horizontal_distances = panorama.horizontal_neighborhoods_group[0..5].map{|g| g[:prdist_avg]}
86
+ vertical_distances = panorama.vertical_neighborhoods_group[0..5].map{|g| g[:prdist_avg]}
87
+ dist_avg = calculate_average_and_std(values: horizontal_distances + vertical_distances).first
88
+
89
+ r -= 0.05
90
+ logger.debug "current roll #{r}"
91
+ panorama.images.each { |i| i.r = r }
92
+ panorama.control_points = ControlPoint.calculate_distances(panorama.images, panorama.variable)
93
+ panorama.calculate_neighborhoods(amount_ratio: 1.0, log: false)
94
+ panorama.calculate_neighborhood_groups(name: :horizontal, pairs: panorama.horizontal_pairs)
95
+ panorama.calculate_neighborhood_groups(name: :vertical, pairs: panorama.vertical_pairs)
96
+
97
+ horizontal_distances = panorama.horizontal_neighborhoods_group[0..5].map{|g| g[:prdist_avg]}
98
+ vertical_distances = panorama.vertical_neighborhoods_group[0..5].map{|g| g[:prdist_avg]}
99
+ new_dist_avg = calculate_average_and_std(values: horizontal_distances + vertical_distances).first
100
+ logger.debug "avg: #{dist_avg} new_avg: #{new_dist_avg}"
101
+
102
+ if new_dist_avg < dist_avg
103
+ logger.debug 'found that subtracting roll will decrease distances, resetting roll...'
104
+ operation = :-
105
+ else
106
+ logger.debug 'found that adding roll will decrease distances, resetting roll...'
107
+ operation = :+
108
+ r = original_roll
109
+ r += 0.05
110
+ logger.debug "current roll #{r}"
111
+ panorama.images.each { |i| i.r = r }
112
+ panorama.control_points = ControlPoint.calculate_distances(panorama.images, panorama.variable)
113
+ panorama.calculate_neighborhoods(amount_ratio: 1.0, log: false)
114
+ panorama.calculate_neighborhood_groups(name: :horizontal, pairs: panorama.horizontal_pairs)
115
+ panorama.calculate_neighborhood_groups(name: :vertical, pairs: panorama.vertical_pairs)
116
+
117
+ horizontal_distances = panorama.horizontal_neighborhoods_group[0..5].map{|g| g[:prdist_avg]}
118
+ vertical_distances = panorama.vertical_neighborhoods_group[0..5].map{|g| g[:prdist_avg]}
119
+ new_dist_avg = calculate_average_and_std(values: horizontal_distances + vertical_distances).first
120
+ end
121
+
122
+ while new_dist_avg <= dist_avg
123
+ r = r.send(operation, 0.05)
124
+ logger.debug "current roll #{r}"
125
+ dist_avg = new_dist_avg
126
+ panorama.images.each { |i| i.r = r }
127
+ panorama.control_points = ControlPoint.calculate_distances(panorama.images, panorama.variable)
128
+ panorama.calculate_neighborhoods(amount_ratio: 1.0, log: false)
129
+ panorama.calculate_neighborhood_groups(name: :horizontal, pairs: panorama.horizontal_pairs)
130
+ panorama.calculate_neighborhood_groups(name: :vertical, pairs: panorama.vertical_pairs)
131
+
132
+ horizontal_distances = panorama.horizontal_neighborhoods_group[0..5].map{|g| g[:prdist_avg]}
133
+ vertical_distances = panorama.vertical_neighborhoods_group[0..5].map{|g| g[:prdist_avg]}
134
+ new_dist_avg = calculate_average_and_std(values: horizontal_distances + vertical_distances).first
135
+ logger.debug "avg: #{dist_avg} new_avg: #{new_dist_avg}"
136
+ end
137
+
138
+ images.each do |image|
139
+ image.r = r
111
140
  end
112
141
  end
113
142
  end
@@ -1,6 +1,9 @@
1
1
  module Panomosity
2
2
  class Panorama
3
- attr_accessor :images, :control_points, :variable, :optimisation_variables, :logger
3
+ include Panomosity::Utils
4
+
5
+ attr_accessor :images, :control_points, :variable, :optimisation_variables, :logger, :horizontal_pairs,
6
+ :vertical_pairs, :horizontal_neighborhoods_group, :vertical_neighborhoods_group
4
7
 
5
8
  def initialize(input, logger = nil)
6
9
  @input = input
@@ -20,5 +23,466 @@ module Panomosity
20
23
  end
21
24
  end
22
25
  end
26
+
27
+ def calculate_neighborhoods(distance: 30, amount_ratio: 0.2, log: true)
28
+ pairs = control_points.group_by { |cp| [cp.n1, cp.n2] }
29
+ amount = (amount_ratio * (images.count)).floor
30
+
31
+ # separate out into horizontal and vertical pairs
32
+ horizontal_pairs = pairs.select { |_,cps| cps.first.conn_type == :horizontal }
33
+ vertical_pairs = pairs.select { |_,cps| cps.first.conn_type == :vertical }
34
+
35
+ # sort pairs by average distance first and number of control points descending second
36
+ horizontal_pairs = horizontal_pairs.sort_by { |_, cps| [calculate_average_and_std(values: cps.map(&:prdist)).first, -cps.count] }
37
+ vertical_pairs = vertical_pairs.sort_by { |_, cps| [calculate_average_and_std(values: cps.map(&:prdist)).first, -cps.count] }
38
+
39
+ # select a set amount
40
+ horizontal_pairs = horizontal_pairs[0..(amount-1)]
41
+ vertical_pairs = vertical_pairs[0..(amount-1)]
42
+
43
+ # looks for the highest concentration of points with similar distances within the neighborhood (30px) of the average
44
+ # group cps that are close in distance to each other
45
+ # start with horizontal pairs
46
+ @horizontal_pairs = calculate_neighborhood_info(pairs: horizontal_pairs, distance: distance)
47
+ log_detailed_neighborhood_info(name: :horizontal, pairs: @horizontal_pairs) if log
48
+
49
+ # vertical pairs
50
+ @vertical_pairs = calculate_neighborhood_info(pairs: vertical_pairs, distance: distance)
51
+ log_detailed_neighborhood_info(name: :vertical, pairs: @vertical_pairs) if log
52
+
53
+ { horizontal_pairs: @horizontal_pairs, vertical_pairs: @vertical_pairs }
54
+ end
55
+
56
+ def calculate_neighborhood_info(pairs: [], distance: 30)
57
+ pairs.map do |pair, cps|
58
+ neighborhoods = cps.map do |cp|
59
+ neighborhood = cps.select { |c| c.x1.between?(cp.x1 - distance, cp.x1 + distance) && c.y1.between?(cp.y1 - distance, cp.y1 + distance) }
60
+ if neighborhood.count > 1
61
+ prdist_avg, prdist_std = *calculate_average_and_std(values: neighborhood.map(&:prdist))
62
+ prx_avg, prx_std = *calculate_average_and_std(values: neighborhood.map(&:prx))
63
+ pry_avg, pry_std = *calculate_average_and_std(values: neighborhood.map(&:pry))
64
+
65
+ # add in control points that have similar distances (within std)
66
+ neighborhood_within_std = cps.select { |c| c.prdist.between?(cp.prdist - prdist_std, cp.prdist + prdist_std) }
67
+ {
68
+ cp: cp,
69
+ prdist_avg: prdist_avg,
70
+ prdist_std: prdist_std,
71
+ prx_avg: prx_avg,
72
+ prx_std: prx_std,
73
+ pry_avg: pry_avg,
74
+ pry_std: pry_std,
75
+ neighborhood: neighborhood,
76
+ neighborhood_within_std: neighborhood_within_std
77
+ }
78
+ else
79
+ nil
80
+ end
81
+ end.compact
82
+ # gets all control points for neighborhoods with a good std of distance
83
+ control_points_within_a_std = neighborhoods.select { |n| n[:neighborhood_within_std].count >= 3 }.flatten
84
+
85
+ best_neighborhood = neighborhoods.sort_by { |n| -n[:neighborhood].count }.first
86
+ {
87
+ pair: pair,
88
+ control_points: cps,
89
+ neighborhoods: neighborhoods,
90
+ best_neighborhood: best_neighborhood,
91
+ control_points_within_a_std: control_points_within_a_std
92
+ }
93
+ end
94
+ end
95
+
96
+ # get all neighborhoods based on how many control points are within the std of the distance
97
+ def calculate_neighborhood_groups(name: :horizontal, pairs: [])
98
+ neighborhood_default = 3
99
+ total_neighborhoods = pairs.map { |p| p[:neighborhoods].select { |n| n[:neighborhood_within_std].count >= neighborhood_default }}.flatten
100
+
101
+ if total_neighborhoods.map { |n| n[:prdist_std] }.empty?
102
+ logger.warn 'total_neighborhoods came up empty, neighborhood default count to 2'
103
+ neighborhood_default = 2
104
+ total_neighborhoods = pairs.map { |p| p[:neighborhoods].select { |n| n[:neighborhood_within_std].count >= neighborhood_default }}.flatten
105
+ raise 'still could not find neighborhoods' if total_neighborhoods.map { |n| n[:prdist_std] }.empty?
106
+ end
107
+
108
+ logger.debug "twice reducing #{name} neighborhood std outliers"
109
+ avg, std = *calculate_average_and_std(values: total_neighborhoods.map { |n| n[:prdist_std] })
110
+ total_neighborhoods.select! { |n| (avg - n[:prdist_std]).abs < std }
111
+ avg, std = *calculate_average_and_std(values: total_neighborhoods.map { |n| n[:prdist_std] })
112
+ total_neighborhoods.select! { |n| (avg - n[:prdist_std]).abs < std }
113
+ neighborhood_group = total_neighborhoods.map do |neighborhood|
114
+ ns_total = total_neighborhoods.select { |n| (n[:prdist_avg] - neighborhood[:prdist_avg]).abs < neighborhood[:prdist_std] }
115
+ cps_total = ns_total.map { |n| n[:neighborhood_within_std].count }.reduce(:+)
116
+ x_avg, _ = *calculate_average_and_std(values: ns_total.map { |n| n[:neighborhood_within_std] }.flatten.map(&:px))
117
+ y_avg, _ = *calculate_average_and_std(values: ns_total.map { |n| n[:neighborhood_within_std] }.flatten.map(&:py))
118
+ {
119
+ neighborhood: neighborhood,
120
+ total_neighboorhoods: ns_total,
121
+ total_control_points: cps_total,
122
+ prdist_avg: neighborhood[:prdist_avg],
123
+ prdist_std: neighborhood[:prdist_std],
124
+ x_avg: x_avg,
125
+ y_avg: y_avg
126
+ }
127
+ end
128
+ neighborhood_group.sort_by { |n| -n[:total_control_points] }[0..5].each do |ng|
129
+ logger.debug "#{ng[:prdist_avg]} #{ng[:prdist_std]} #{ng[:total_control_points]} x#{ng[:x_avg]} y#{ng[:y_avg]}"
130
+ end
131
+
132
+ if name == :horizontal
133
+ @horizontal_neighborhoods_group = neighborhood_group.sort_by { |n| -n[:total_control_points] }
134
+ else
135
+ @vertical_neighborhoods_group = neighborhood_group.sort_by { |n| -n[:total_control_points] }
136
+ end
137
+ end
138
+
139
+ def log_detailed_neighborhood_info(name: :horizontal, pairs: [])
140
+ logger.debug "showing #{name} pair information"
141
+ pair = pairs.sort_by{|pair| pair[:best_neighborhood] ? -pair[:best_neighborhood][:neighborhood].count : 0}.first
142
+ pairs.each do |p|
143
+ logger.debug "#{name} pair #{p[:pair]} found #{p[:best_neighborhood] ? p[:best_neighborhood][:neighborhood].count : 0} control points"
144
+ p[:neighborhoods].each do |neighborhood|
145
+ logger.debug "neighborhood centered at #{neighborhood[:cp].x1},#{neighborhood[:cp].y1}: #{neighborhood[:neighborhood].count} control points"
146
+ logger.debug "neighborhood centered at #{neighborhood[:cp].x1},#{neighborhood[:cp].y1}: prdist #{neighborhood[:prdist_avg]},#{neighborhood[:prdist_std]} prx #{neighborhood[:prx_avg]},#{neighborhood[:prx_std]} pry #{neighborhood[:pry_avg]},#{neighborhood[:pry_std]}"
147
+ neighborhood[:neighborhood].each do |point|
148
+ logger.debug point.detailed_info
149
+ end
150
+ end
151
+ end
152
+ pairs.each do |p|
153
+ logger.debug "#{name} pair #{p[:pair]} found #{p[:best_neighborhood] ? p[:best_neighborhood][:neighborhood].count : 0} control points"
154
+ end
155
+ #logger.debug "#{name} pair #{pair[:pair]} found #{pair[:best_neighborhood] ? pair[:best_neighborhood][:neighborhood].count : 0} control points"
156
+ end
157
+
158
+ def clean_control_points(options = {})
159
+ bad_control_points = []
160
+ min_control_points = 5
161
+ control_points.group_by { |cp| [cp.n1, cp.n2] }.select { |_, cps| cps.count > min_control_points }.each do |pair, cps|
162
+ logger.debug "cleaning pair #{pair.first} <> #{pair.last}"
163
+ average_x, x_std = *calculate_average_and_std(name: :x, values: cps.map(&:px), logger: logger)
164
+ average_y, y_std = *calculate_average_and_std(name: :y, values: cps.map(&:py), logger: logger)
165
+
166
+ max_removal = ((options[:max_removal] || 0.2) * cps.count).floor
167
+ min_cps = 8
168
+ max_iterations = 10
169
+ iterations = 0
170
+ bad_cps = cps.select { |cp| (cp.px - average_x).abs >= x_std || (cp.py - average_y).abs >= y_std }
171
+ while bad_cps.count > max_removal && (cps.count - bad_cps.count) >= min_cps && iterations <= max_iterations
172
+ x_std *= 1.1
173
+ y_std *= 1.1
174
+ iterations += 1
175
+ bad_cps = cps.select { |cp| (cp.px - average_x).abs >= x_std || (cp.py - average_y).abs >= y_std }
176
+ end
177
+
178
+ logger.info "found #{bad_cps.count} outliers"
179
+ bad_control_points << bad_cps if bad_cps.count <= max_removal
180
+ end
181
+ bad_control_points.flatten!
182
+ end
183
+
184
+ def clean_control_points_neighborhoods(options = {})
185
+ calculate_neighborhoods(amount_ratio: 1.0)
186
+ control_points = (@horizontal_pairs.map { |pair| pair[:control_points_within_a_std].map { |n| n[:neighborhood_within_std] } } +
187
+ @vertical_pairs.map { |pair| pair[:control_points_within_a_std].map { |n| n[:neighborhood_within_std] } }).flatten.uniq { |cp| cp.raw }
188
+ bad_control_points = @control_points.reject { |cp| control_points.map(&:raw).include?(cp.raw) }
189
+ end
190
+
191
+ def fix_unconnected_image_pairs
192
+ horizontal_control_points, vertical_control_points = *control_points.partition { |cp| cp.conn_type == :horizontal }
193
+ control_points_of_pair = horizontal_control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| members.count }.last.last
194
+ logger.debug "found horizontal pair #{control_points_of_pair.first.n1} <> #{control_points_of_pair.first.n2} with #{control_points_of_pair.count} connections"
195
+ average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist), logger: logger)
196
+ horizontal_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < distance_std }
197
+ logger.info "removed #{control_points_of_pair.count - horizontal_control_points_of_pair.count} outliers"
198
+ # For logging
199
+ calculate_average_and_std(name: :distance, values: horizontal_control_points_of_pair.map(&:pdist), logger: logger)
200
+
201
+ control_points_of_pair = vertical_control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| members.count }.last.last
202
+ logger.debug "found vertical pair #{control_points_of_pair.first.n1} <> #{control_points_of_pair.first.n2} with #{control_points_of_pair.count} connections"
203
+ average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist), logger: logger)
204
+ vertical_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < distance_std }
205
+ logger.info "removed #{control_points_of_pair.count - vertical_control_points_of_pair.count} outliers"
206
+ # For logging
207
+ calculate_average_and_std(name: :distance, values: vertical_control_points_of_pair.map(&:pdist), logger: logger)
208
+
209
+ logger.info 'finding unconnected image pairs'
210
+ unconnected_image_pairs = find_unconnected_image_pairs
211
+
212
+ logger.info unconnected_image_pairs.map { |i| { type: i[:type], pair: i[:pair].map(&:id) } }
213
+
214
+ logger.info 'finding control points with unrealistic distances (<1)'
215
+ bad_control_points = control_points.select { |cp| cp.pdist <= 1.0 }
216
+ logger.info 'adding pairs that have do not have enough control points (<3)'
217
+ changing_control_points_pairs = control_points.group_by { |cp| [cp.n1, cp.n2] }.select { |_, cps| cps.count < 3 }
218
+ changed_pairs = []
219
+
220
+ logger.info 'writing new control points'
221
+ control_point_lines_started = false
222
+ @lines = @input.each_line.map do |line|
223
+ cp = ControlPoint.parse_line(line)
224
+ if cp.nil?
225
+ # Control point lines ended
226
+ if control_point_lines_started
227
+ control_point_lines_started = false
228
+ unconnected_image_pairs.map do |pair|
229
+ if pair[:type] == :horizontal
230
+ control_point = horizontal_control_points_of_pair.first
231
+ else
232
+ control_point = vertical_control_points_of_pair.first
233
+ end
234
+
235
+ x_diff = control_point.x2 - control_point.x1
236
+ y_diff = control_point.y2 - control_point.y1
237
+
238
+ x1 = x_diff <= 0 ? -x_diff + 15 : 0
239
+ y1 = y_diff <= 0 ? -y_diff + 15 : 0
240
+
241
+ control_point[:n] = pair[:pair].first.id
242
+ control_point[:N] = pair[:pair].last.id
243
+ control_point[:x] = x1
244
+ control_point[:X] = x1 + x_diff
245
+ control_point[:y] = y1
246
+ control_point[:Y] = y1 + y_diff
247
+
248
+ logger.debug "adding control points connecting #{control_point.n1} <> #{control_point.n2}"
249
+ i = images.first
250
+ 3.times.map do
251
+ if control_point.conn_type == :horizontal
252
+ control_point[:x] += 5
253
+ control_point[:X] += 5
254
+ control_point[:y] += i.h * 0.25
255
+ control_point[:Y] += i.h * 0.25
256
+ else
257
+ control_point[:x] += i.w * 0.25
258
+ control_point[:X] += i.w * 0.25
259
+ control_point[:y] += 5
260
+ control_point[:Y] += 5
261
+ end
262
+ control_point.to_s
263
+ end.join
264
+ end + [line]
265
+ else
266
+ next line
267
+ end
268
+ else
269
+ control_point_lines_started = true
270
+ bad_control_point = bad_control_points.find { |cp| cp.raw == line }
271
+ changing_control_point_pair = changing_control_points_pairs.find { |_, cps| cps.find { |cp| cp.raw == line } }
272
+
273
+ if bad_control_point
274
+ if bad_control_point.conn_type == :horizontal
275
+ control_point = horizontal_control_points_of_pair.first
276
+ else
277
+ control_point = vertical_control_points_of_pair.first
278
+ end
279
+
280
+ x_diff = control_point.x2 - control_point.x1
281
+ y_diff = control_point.y2 - control_point.y1
282
+
283
+ x1 = x_diff <= 0 ? -x_diff + 15 : 0
284
+ y1 = y_diff <= 0 ? -y_diff + 15 : 0
285
+
286
+ control_point[:n] = bad_control_point[:n]
287
+ control_point[:N] = bad_control_point[:N]
288
+ control_point[:x] = x1
289
+ control_point[:X] = x1 + x_diff
290
+ control_point[:y] = y1
291
+ control_point[:Y] = y1 + y_diff
292
+
293
+ logger.debug "replacing unrealistic control point connecting #{control_point.n1} <> #{control_point.n2}"
294
+ i = images.first
295
+ 3.times.map do
296
+ if control_point.conn_type == :horizontal
297
+ control_point[:x] += 5
298
+ control_point[:X] += 5
299
+ control_point[:y] += i.h * 0.25
300
+ control_point[:Y] += i.h * 0.25
301
+ else
302
+ control_point[:x] += i.w * 0.25
303
+ control_point[:X] += i.w * 0.25
304
+ control_point[:y] += 5
305
+ control_point[:Y] += 5
306
+ end
307
+ control_point.to_s
308
+ end.join
309
+ elsif changing_control_point_pair && !changed_pairs.include?(changing_control_point_pair.first)
310
+ changed_pairs << changing_control_point_pair.first
311
+ bad_control_point = changing_control_point_pair.last.first
312
+ if bad_control_point.conn_type == :horizontal
313
+ control_point = horizontal_control_points_of_pair.first
314
+ else
315
+ control_point = vertical_control_points_of_pair.first
316
+ end
317
+
318
+ x_diff = control_point.x2 - control_point.x1
319
+ y_diff = control_point.y2 - control_point.y1
320
+
321
+ x1 = x_diff <= 0 ? -x_diff + 15 : 0
322
+ y1 = y_diff <= 0 ? -y_diff + 15 : 0
323
+
324
+ control_point[:n] = bad_control_point[:n]
325
+ control_point[:N] = bad_control_point[:N]
326
+ control_point[:x] = x1
327
+ control_point[:X] = x1 + x_diff
328
+ control_point[:y] = y1
329
+ control_point[:Y] = y1 + y_diff
330
+
331
+ logger.debug "adding control points connecting #{control_point.n1} <> #{control_point.n2}"
332
+ i = images.first
333
+ 3.times.map do
334
+ if control_point.conn_type == :horizontal
335
+ control_point[:x] += 5
336
+ control_point[:X] += 5
337
+ control_point[:y] += i.h * 0.25
338
+ control_point[:Y] += i.h * 0.25
339
+ else
340
+ control_point[:x] += i.w * 0.25
341
+ control_point[:X] += i.w * 0.25
342
+ control_point[:y] += 5
343
+ control_point[:Y] += 5
344
+ end
345
+ control_point.to_s
346
+ end.join
347
+ else
348
+ next line
349
+ end
350
+ end
351
+ end.compact.flatten
352
+ end
353
+
354
+ def fix_unconnected_image_pairs_neighborhoods
355
+ calculate_neighborhoods(amount_ratio: 1.0)
356
+ calculate_neighborhood_groups(name: :horizontal, pairs: @horizontal_pairs)
357
+ calculate_neighborhood_groups(name: :vertical, pairs: @vertical_pairs)
358
+
359
+ logger.info 'finding unconnected image pairs'
360
+ unconnected_image_pairs = find_unconnected_image_pairs
361
+ logger.info unconnected_image_pairs.map { |i| { type: i[:type], pair: i[:pair].map(&:id) } }
362
+
363
+ logger.info 'finding control points with unrealistic distances (<1)'
364
+ bad_control_points = control_points.select { |cp| cp.pdist <= 1.0 }
365
+ logger.info 'adding pairs that have do not have enough control points (<3)'
366
+ changing_control_points_pairs = control_points.group_by { |cp| [cp.n1, cp.n2] }.select { |_, cps| cps.count < 3 }
367
+ changed_pairs = []
368
+
369
+ logger.info 'writing new control points'
370
+ control_point_lines_started = false
371
+ @lines = @input.each_line.map do |line|
372
+ cp = ControlPoint.parse_line(line)
373
+ if cp.nil?
374
+ # Control point lines ended
375
+ if control_point_lines_started
376
+ control_point_lines_started = false
377
+ unconnected_image_pairs.map do |pair|
378
+ generate_control_points(pair: pair, message: 'adding control points connecting')
379
+ end + [line]
380
+ else
381
+ next line
382
+ end
383
+ else
384
+ control_point_lines_started = true
385
+ bad_control_point = bad_control_points.find { |cp| cp.raw == line }
386
+ changing_control_point_pair = changing_control_points_pairs.find { |_, cps| cps.find { |cp| cp.raw == line } }
387
+
388
+ if bad_control_point
389
+ generate_control_points(bad_control_point: bad_control_point, message: 'replacing unrealistic control point connecting')
390
+ elsif changing_control_point_pair && !changed_pairs.include?(changing_control_point_pair.first)
391
+ changed_pairs << changing_control_point_pair.first
392
+ bad_control_point = changing_control_point_pair.last.first
393
+ generate_control_points(bad_control_point: bad_control_point, message: 'adding control points connecting')
394
+ else
395
+ next line
396
+ end
397
+ end
398
+ end.compact.flatten
399
+ end
400
+
401
+ def find_unconnected_image_pairs
402
+ ds = images.map(&:d).uniq.sort
403
+ es = images.map(&:e).uniq.sort
404
+
405
+ unconnected_image_pairs = []
406
+ # horizontal connection checking
407
+ es.each do |e|
408
+ ds.each_with_index do |d, index|
409
+ next if index == (ds.count - 1)
410
+ image_1 = images.find { |i| i.e == e && i.d == d }
411
+ image_2 = images.find { |i| i.e == e && i.d == ds[index+1] }
412
+ connected = control_points.any? { |cp| (cp.n1 == image_1.id && cp.n2 == image_2.id) || (cp.n1 == image_2.id && cp.n2 == image_1.id) }
413
+ unconnected_image_pairs << { type: :horizontal, pair: [image_1, image_2].sort_by(&:id) } unless connected
414
+ end
415
+ end
416
+
417
+ # vertical connection checking
418
+ ds.each do |d|
419
+ es.each_with_index do |e, index|
420
+ next if index == (es.count - 1)
421
+ image_1 = images.find { |i| i.d == d && i.e == e }
422
+ image_2 = images.find { |i| i.d == d && i.e == es[index+1] }
423
+ connected = control_points.any? { |cp| (cp.n1 == image_1.id && cp.n2 == image_2.id) || (cp.n1 == image_2.id && cp.n2 == image_1.id) }
424
+ unconnected_image_pairs << { type: :vertical, pair: [image_1, image_2].sort_by(&:id) } unless connected
425
+ end
426
+ end
427
+
428
+ unconnected_image_pairs
429
+ end
430
+
431
+ def generate_control_points(pair: nil, bad_control_point: nil, message: '')
432
+ if pair
433
+ if pair[:type] == :horizontal
434
+ group = @horizontal_neighborhoods_group.first
435
+ else
436
+ group = @vertical_neighborhoods_group.first
437
+ end
438
+ else
439
+ if bad_control_point.conn_type == :horizontal
440
+ group = @horizontal_neighborhoods_group.first
441
+ else
442
+ group = @vertical_neighborhoods_group.first
443
+ end
444
+ end
445
+
446
+ control_point = ControlPoint.new(group[:neighborhood][:cp].attributes)
447
+
448
+ if pair
449
+ control_point[:n] = pair[:pair].first.id
450
+ control_point[:N] = pair[:pair].last.id
451
+ else
452
+ control_point[:n] = bad_control_point[:n]
453
+ control_point[:N] = bad_control_point[:N]
454
+ end
455
+
456
+ image_1 = images.find { |i| i.id == control_point[:n] }
457
+ image_2 = images.find { |i| i.id == control_point[:N] }
458
+
459
+ x_diff = group[:x_avg] + (image_2.d - image_1.d)
460
+ y_diff = group[:y_avg] + (image_2.e - image_1.e)
461
+
462
+ x1 = x_diff <= 0 ? -x_diff + 15 : 0
463
+ y1 = y_diff <= 0 ? -y_diff + 15 : 0
464
+
465
+ control_point[:x] = x1
466
+ control_point[:X] = x1 + x_diff
467
+ control_point[:y] = y1
468
+ control_point[:Y] = y1 + y_diff
469
+
470
+ logger.debug "#{message} #{control_point.n1} <> #{control_point.n2}"
471
+ i = images.first
472
+ 3.times.map do
473
+ if control_point.conn_type == :horizontal
474
+ control_point[:x] += 5
475
+ control_point[:X] += 5
476
+ control_point[:y] += i.h * 0.25
477
+ control_point[:Y] += i.h * 0.25
478
+ else
479
+ control_point[:x] += i.w * 0.25
480
+ control_point[:X] += i.w * 0.25
481
+ control_point[:y] += 5
482
+ control_point[:Y] += 5
483
+ end
484
+ control_point.to_s
485
+ end.join
486
+ end
23
487
  end
24
488
  end
@@ -90,34 +90,8 @@ module Panomosity
90
90
  def clean_control_points
91
91
  logger.info 'cleaning control points'
92
92
  # Since this is very exact, having many outliers in control points distances will cause errors
93
- images = Image.parse(@input_file)
94
- panorama_variable = PanoramaVariable.parse(@input_file).first
95
- ControlPoint.parse(@input_file)
96
- control_points = ControlPoint.calculate_distances(images, panorama_variable)
97
-
98
- bad_control_points = []
99
- min_control_points = 5
100
- control_points.group_by { |cp| [cp.n1, cp.n2] }.select { |_, cps| cps.count > min_control_points }.each do |pair, cps|
101
- logger.debug "cleaning pair #{pair.first} <> #{pair.last}"
102
- average_x, x_std = *calculate_average_and_std(name: :x, values: cps.map(&:px), logger: logger)
103
- average_y, y_std = *calculate_average_and_std(name: :y, values: cps.map(&:py), logger: logger)
104
-
105
- max_removal = ((@options[:max_removal] || 0.2) * cps.count).floor
106
- min_cps = 8
107
- max_iterations = 10
108
- iterations = 0
109
- bad_cps = cps.select { |cp| (cp.px - average_x).abs >= x_std || (cp.py - average_y).abs >= y_std }
110
- while bad_cps.count > max_removal && (cps.count - bad_cps.count) >= min_cps && iterations <= max_iterations
111
- x_std *= 1.1
112
- y_std *= 1.1
113
- iterations += 1
114
- bad_cps = cps.select { |cp| (cp.px - average_x).abs >= x_std || (cp.py - average_y).abs >= y_std }
115
- end
116
-
117
- logger.info "found #{bad_cps.count} outliers"
118
- bad_control_points << bad_cps if bad_cps.count <= max_removal
119
- end
120
- bad_control_points.flatten!
93
+ panorama = Panorama.new(@input_file, logger)
94
+ bad_control_points = panorama.clean_control_points_neighborhoods(@options)
121
95
 
122
96
  logger.info "removing #{bad_control_points.count} control points"
123
97
  @lines = @input_file.each_line.map do |line|
@@ -201,10 +175,8 @@ module Panomosity
201
175
  unless @options[:without_cropping]
202
176
  images = Image.parse(@input_file)
203
177
  images.each do |image|
204
- geometry = `identify -verbose #{image.name} | grep Geometry`.strip
205
- _, width, height = *geometry.match(/(\d{2,5})x(\d{2,5})(\+|\-)\d{1,5}(\+|\-)\d{1,5}/)
206
- width_offset = (width.to_f * (1 - scale_factor) / 2).round
207
- height_offset = (height.to_f * (1 - scale_factor) / 2).round
178
+ width_offset = (image.w.to_f * (1 - scale_factor) / 2).round
179
+ height_offset = (image.h.to_f * (1 - scale_factor) / 2).round
208
180
  logger.debug "cropping #{image.name}"
209
181
  `convert #{image.name} -crop "#{percent}%x#{percent}%+#{width_offset}+#{height_offset}" #{image.name}`
210
182
  end
@@ -278,196 +250,8 @@ module Panomosity
278
250
 
279
251
  def fix_unconnected_image_pairs
280
252
  logger.info 'fixing unconnected image pairs'
281
- images = Image.parse(@input_file)
282
- panorama_variable = PanoramaVariable.parse(@input_file).first
283
- ControlPoint.parse(@input_file)
284
- control_points = ControlPoint.calculate_distances(images, panorama_variable)
285
-
286
- horizontal_control_points, vertical_control_points = *control_points.partition { |cp| cp.conn_type == :horizontal }
287
- control_points_of_pair = horizontal_control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| members.count }.last.last
288
- logger.debug "found horizontal pair #{control_points_of_pair.first.n1} <> #{control_points_of_pair.first.n2} with #{control_points_of_pair.count} connections"
289
- average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist), logger: logger)
290
- horizontal_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < distance_std }
291
- logger.info "removed #{control_points_of_pair.count - horizontal_control_points_of_pair.count} outliers"
292
- # For logging
293
- calculate_average_and_std(name: :distance, values: horizontal_control_points_of_pair.map(&:pdist), logger: logger)
294
-
295
- control_points_of_pair = vertical_control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| members.count }.last.last
296
- logger.debug "found vertical pair #{control_points_of_pair.first.n1} <> #{control_points_of_pair.first.n2} with #{control_points_of_pair.count} connections"
297
- average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist), logger: logger)
298
- vertical_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < distance_std }
299
- logger.info "removed #{control_points_of_pair.count - vertical_control_points_of_pair.count} outliers"
300
- # For logging
301
- calculate_average_and_std(name: :distance, values: vertical_control_points_of_pair.map(&:pdist), logger: logger)
302
-
303
- logger.info 'finding unconnected image pairs'
304
- ds = images.map(&:d).uniq.sort
305
- es = images.map(&:e).uniq.sort
306
-
307
- unconnected_image_pairs = []
308
- # horizontal connection checking
309
- es.each do |e|
310
- ds.each_with_index do |d, index|
311
- next if index == (ds.count - 1)
312
- image_1 = images.find { |i| i.e == e && i.d == d }
313
- image_2 = images.find { |i| i.e == e && i.d == ds[index+1] }
314
- connected = control_points.any? { |cp| (cp.n1 == image_1.id && cp.n2 == image_2.id) || (cp.n1 == image_2.id && cp.n2 == image_1.id) }
315
- unconnected_image_pairs << { type: :horizontal, pair: [image_1, image_2].sort_by(&:id) } unless connected
316
- end
317
- end
318
-
319
- # vertical connection checking
320
- ds.each do |d|
321
- es.each_with_index do |e, index|
322
- next if index == (es.count - 1)
323
- image_1 = images.find { |i| i.d == d && i.e == e }
324
- image_2 = images.find { |i| i.d == d && i.e == es[index+1] }
325
- connected = control_points.any? { |cp| (cp.n1 == image_1.id && cp.n2 == image_2.id) || (cp.n1 == image_2.id && cp.n2 == image_1.id) }
326
- unconnected_image_pairs << { type: :vertical, pair: [image_1, image_2].sort_by(&:id) } unless connected
327
- end
328
- end
329
-
330
- logger.info unconnected_image_pairs.map { |i| { type: i[:type], pair: i[:pair].map(&:id) } }
331
-
332
- logger.info 'finding control points with unrealistic distances (<1)'
333
- bad_control_points = control_points.select { |cp| cp.pdist <= 1.0 }
334
- logger.info 'adding pairs that have do not have enough control points (<3)'
335
- changing_control_points_pairs = control_points.group_by { |cp| [cp.n1, cp.n2] }.select { |_, cps| cps.count < 3 }
336
- changed_pairs = []
337
-
338
- logger.info 'writing new control points'
339
- control_point_lines_started = false
340
- @lines = @input_file.each_line.map do |line|
341
- cp = ControlPoint.parse_line(line)
342
- if cp.nil?
343
- # Control point lines ended
344
- if control_point_lines_started
345
- control_point_lines_started = false
346
- unconnected_image_pairs.map do |pair|
347
- if pair[:type] == :horizontal
348
- control_point = horizontal_control_points_of_pair.first
349
- else
350
- control_point = vertical_control_points_of_pair.first
351
- end
352
-
353
- x_diff = control_point.x2 - control_point.x1
354
- y_diff = control_point.y2 - control_point.y1
355
-
356
- x1 = x_diff <= 0 ? -x_diff + 15 : 0
357
- y1 = y_diff <= 0 ? -y_diff + 15 : 0
358
-
359
- control_point[:n] = pair[:pair].first.id
360
- control_point[:N] = pair[:pair].last.id
361
- control_point[:x] = x1
362
- control_point[:X] = x1 + x_diff
363
- control_point[:y] = y1
364
- control_point[:Y] = y1 + y_diff
365
-
366
- logger.debug "adding control points connecting #{control_point.n1} <> #{control_point.n2}"
367
- i = images.first
368
- 3.times.map do
369
- if control_point.conn_type == :horizontal
370
- control_point[:x] += 5
371
- control_point[:X] += 5
372
- control_point[:y] += i.h * 0.25
373
- control_point[:Y] += i.h * 0.25
374
- else
375
- control_point[:x] += i.w * 0.25
376
- control_point[:X] += i.w * 0.25
377
- control_point[:y] += 5
378
- control_point[:Y] += 5
379
- end
380
- control_point.to_s
381
- end.join
382
- end + [line]
383
- else
384
- next line
385
- end
386
- else
387
- control_point_lines_started = true
388
- bad_control_point = bad_control_points.find { |cp| cp.raw == line }
389
- changing_control_point_pair = changing_control_points_pairs.find { |_, cps| cps.find { |cp| cp.raw == line } }
390
-
391
- if bad_control_point
392
- if bad_control_point.conn_type == :horizontal
393
- control_point = horizontal_control_points_of_pair.first
394
- else
395
- control_point = vertical_control_points_of_pair.first
396
- end
397
-
398
- x_diff = control_point.x2 - control_point.x1
399
- y_diff = control_point.y2 - control_point.y1
400
-
401
- x1 = x_diff <= 0 ? -x_diff + 15 : 0
402
- y1 = y_diff <= 0 ? -y_diff + 15 : 0
403
-
404
- control_point[:n] = bad_control_point[:n]
405
- control_point[:N] = bad_control_point[:N]
406
- control_point[:x] = x1
407
- control_point[:X] = x1 + x_diff
408
- control_point[:y] = y1
409
- control_point[:Y] = y1 + y_diff
410
-
411
- logger.debug "replacing unrealistic control point connecting #{control_point.n1} <> #{control_point.n2}"
412
- i = images.first
413
- 3.times.map do
414
- if control_point.conn_type == :horizontal
415
- control_point[:x] += 5
416
- control_point[:X] += 5
417
- control_point[:y] += i.h * 0.25
418
- control_point[:Y] += i.h * 0.25
419
- else
420
- control_point[:x] += i.w * 0.25
421
- control_point[:X] += i.w * 0.25
422
- control_point[:y] += 5
423
- control_point[:Y] += 5
424
- end
425
- control_point.to_s
426
- end.join
427
- elsif changing_control_point_pair && !changed_pairs.include?(changing_control_point_pair.first)
428
- changed_pairs << changing_control_point_pair.first
429
- bad_control_point = changing_control_point_pair.last.first
430
- if bad_control_point.conn_type == :horizontal
431
- control_point = horizontal_control_points_of_pair.first
432
- else
433
- control_point = vertical_control_points_of_pair.first
434
- end
435
-
436
- x_diff = control_point.x2 - control_point.x1
437
- y_diff = control_point.y2 - control_point.y1
438
-
439
- x1 = x_diff <= 0 ? -x_diff + 15 : 0
440
- y1 = y_diff <= 0 ? -y_diff + 15 : 0
441
-
442
- control_point[:n] = bad_control_point[:n]
443
- control_point[:N] = bad_control_point[:N]
444
- control_point[:x] = x1
445
- control_point[:X] = x1 + x_diff
446
- control_point[:y] = y1
447
- control_point[:Y] = y1 + y_diff
448
-
449
- logger.debug "adding control points connecting #{control_point.n1} <> #{control_point.n2}"
450
- i = images.first
451
- 3.times.map do
452
- if control_point.conn_type == :horizontal
453
- control_point[:x] += 5
454
- control_point[:X] += 5
455
- control_point[:y] += i.h * 0.25
456
- control_point[:Y] += i.h * 0.25
457
- else
458
- control_point[:x] += i.w * 0.25
459
- control_point[:X] += i.w * 0.25
460
- control_point[:y] += 5
461
- control_point[:Y] += 5
462
- end
463
- control_point.to_s
464
- end.join
465
- else
466
- next line
467
- end
468
- end
469
- end.compact.flatten
470
-
253
+ panorama = Panorama.new(@input_file, logger)
254
+ @lines = panorama.fix_unconnected_image_pairs_neighborhoods
471
255
  save_file
472
256
  end
473
257
 
@@ -1,3 +1,3 @@
1
1
  module Panomosity
2
- VERSION = '0.1.10'
2
+ VERSION = '0.1.12'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: panomosity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oliver Garcia
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-25 00:00:00.000000000 Z
11
+ date: 2018-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler