panomosity 0.1.10 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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