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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/panomosity/control_point.rb +5 -1
- data/lib/panomosity/image.rb +10 -0
- data/lib/panomosity/optimizer.rb +87 -58
- data/lib/panomosity/panorama.rb +465 -1
- data/lib/panomosity/runner.rb +6 -222
- data/lib/panomosity/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1866a3bdb83866570192a3ca7f4c12632e28bd035b33c14ffa10b6ce68e5b0aa
|
4
|
+
data.tar.gz: 29251ea2be17bb88f0923d6394d25dff0ac3336e5471ed6523e2855785a05404
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5b0b268254fb2e5bb3a2e6b1d80852215945b2b692b67f5d1188688cab0b673f869b783ab299d4f4e84b08332a9f90ad2ee64d5ac10162d15514d0b547cb7f7
|
7
|
+
data.tar.gz: 192bf1ad022ff2b47dc34e060209fa6b6aa261b066606d51a4cfd9b3ab12f5fb603d695f108e00ba73947e870f3b8d4fb04969424f54ab1a310091b9433d0a69
|
data/Gemfile.lock
CHANGED
@@ -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.
|
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
|
data/lib/panomosity/image.rb
CHANGED
@@ -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
|
data/lib/panomosity/optimizer.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
101
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
data/lib/panomosity/panorama.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
module Panomosity
|
2
2
|
class Panorama
|
3
|
-
|
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
|
data/lib/panomosity/runner.rb
CHANGED
@@ -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
|
-
|
94
|
-
|
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
|
-
|
205
|
-
|
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
|
-
|
282
|
-
|
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
|
|
data/lib/panomosity/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2018-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|