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 +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
|