panomosity 0.1.9 → 0.1.10
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 +11 -0
- data/lib/panomosity/optimisation_variable.rb +7 -1
- data/lib/panomosity/optimizer.rb +114 -0
- data/lib/panomosity/panorama.rb +24 -0
- data/lib/panomosity/runner.rb +30 -17
- data/lib/panomosity/utils.rb +11 -0
- data/lib/panomosity/version.rb +1 -1
- data/lib/panomosity.rb +3 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec66a2f30f7e065a735367c5f5758ff9bb274c5c180a75de429135d137582874
|
4
|
+
data.tar.gz: a59ce44c2c0a37b05ae323b46881345b8e0bdb213233abd16ae5a47780270ca9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bd782706baca58410c3e4c0e1cce4a4914c086874fe0976f4ebe490a489181ce5321d66692f42398064aef6116db563990c6331ba334700473906717541b704
|
7
|
+
data.tar.gz: b8afce3fd3dec4ce71a7cf9bcbe3445c5ef4691d092784440560b725fc04e75db46346ba0ac4f99f65bb1622ea65b757e20d2c9be1c3fa53a78a5e6157d94a9d
|
data/Gemfile.lock
CHANGED
@@ -174,5 +174,16 @@ module Panomosity
|
|
174
174
|
y1.floor == o.y1.floor &&
|
175
175
|
y2.floor == o.y2.floor
|
176
176
|
end
|
177
|
+
|
178
|
+
def recalculate_pixel_distance
|
179
|
+
r = i1.r * Math::PI / 180
|
180
|
+
self.prx = i1.d - i2.d + Math.cos(r) * (x2 - x1) - Math.sin(r) * (y2 - y1)
|
181
|
+
self.pry = i1.e - i2.e + Math.cos(r) * (y2 - y1) - Math.sin(r) * (x2 - x1)
|
182
|
+
self.prdist = Math.sqrt(prx ** 2 + pry ** 2)
|
183
|
+
end
|
184
|
+
|
185
|
+
def detailed_info
|
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
|
+
end
|
177
188
|
end
|
178
189
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Panomosity
|
2
2
|
class OptimisationVariable
|
3
|
+
START_LINE = 'v'
|
3
4
|
@@attributes = %i(w h f v Ra Rb Rc Rd Re Eev Er Eb r p y TrX TrY TrZ Tpy Tpp j a b c d e g t Va Vb Vc Vd Vx Vy Vm n)
|
4
5
|
|
5
6
|
def self.parse(pto_file)
|
@@ -12,7 +13,8 @@ module Panomosity
|
|
12
13
|
|
13
14
|
def self.parse_line(line)
|
14
15
|
parts = line.split(' ')
|
15
|
-
if parts.first ==
|
16
|
+
if parts.first == START_LINE
|
17
|
+
parts = parts[1..(parts.count-1)]
|
16
18
|
data = parts.each_with_object({}) do |part, hash|
|
17
19
|
attribute = @@attributes.find { |attr| part[0] == attr.to_s }
|
18
20
|
next unless attribute
|
@@ -60,5 +62,9 @@ module Panomosity
|
|
60
62
|
line_values = @@attributes.map { |attribute| "#{attribute}#{self.send(attribute)}" }
|
61
63
|
"v #{line_values.join(' ')}\n"
|
62
64
|
end
|
65
|
+
|
66
|
+
def attributes
|
67
|
+
@attributes.keep_if { |k,_| !%i(raw).include?(k) }
|
68
|
+
end
|
63
69
|
end
|
64
70
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'panomosity/utils'
|
2
|
+
|
3
|
+
module Panomosity
|
4
|
+
class Optimizer
|
5
|
+
include Panomosity::Utils
|
6
|
+
|
7
|
+
attr_accessor :panorama, :images, :control_points, :optimisation_variables, :logger
|
8
|
+
|
9
|
+
def initialize(panorama)
|
10
|
+
@panorama = panorama
|
11
|
+
@images = @panorama.images
|
12
|
+
@control_points = @panorama.control_points
|
13
|
+
@optimisation_variables = @panorama.optimisation_variables
|
14
|
+
@logger = @panorama.logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
variables_to_optimize = optimisation_variables.map { |v| v.attributes.keys }.flatten.uniq.sort
|
19
|
+
if variables_to_optimize == [:d, :e]
|
20
|
+
run_position_optimizer
|
21
|
+
elsif variables_to_optimize == [:r]
|
22
|
+
run_roll_optimizer
|
23
|
+
else
|
24
|
+
logger.error 'no optimization strategy found'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_position_optimizer
|
29
|
+
ds = images.map(&:d).uniq.sort
|
30
|
+
es = images.map(&:e).uniq.sort
|
31
|
+
|
32
|
+
# start horizontally
|
33
|
+
xs = []
|
34
|
+
es.each_with_index do |e, i_e|
|
35
|
+
next if i_e == es.count - 1
|
36
|
+
ds.each_with_index do |d, i_d|
|
37
|
+
next if i_d == ds.count - 1
|
38
|
+
image1 = images.find { |i| i.d == d && i.e == e }
|
39
|
+
image2 = images.find { |i| i.d == ds[i_d+1] && i.e == e }
|
40
|
+
id1, id2 = *[image1.id, image2.id].minmax
|
41
|
+
cps = control_points.select { |cp| cp.conn_type == :horizontal && cp.n1 == id1 && cp.n2 == id2 }
|
42
|
+
xs << cps.map(&:prx)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
xs.flatten!
|
47
|
+
x_avg, x_std = *calculate_average_and_std(name: :x, values: xs, logger: logger)
|
48
|
+
logger.debug 'filter first standard deviations'
|
49
|
+
xs.select! { |x| (x - x_avg).abs < x_std }
|
50
|
+
x_avg, x_std = *calculate_average_and_std(name: :x, values: xs, logger: logger)
|
51
|
+
logger.debug 'filter second standard deviations'
|
52
|
+
xs.select! { |x| (x - x_avg).abs < x_std }
|
53
|
+
x_avg, _ = *calculate_average_and_std(name: :x, values: xs, logger: logger)
|
54
|
+
|
55
|
+
d_map = {}
|
56
|
+
ds.each_with_index do |d, i|
|
57
|
+
if d == ds.first
|
58
|
+
d_map[d] = d
|
59
|
+
else
|
60
|
+
d_map[d] = d + -x_avg * i
|
61
|
+
end
|
62
|
+
end
|
63
|
+
logger.debug "created d_map #{d_map}"
|
64
|
+
|
65
|
+
# vertical
|
66
|
+
ys = []
|
67
|
+
ds.each_with_index do |d, i_d|
|
68
|
+
next if i_d == ds.count - 1
|
69
|
+
es.each_with_index do |e, i_e|
|
70
|
+
next if i_e == es.count - 1
|
71
|
+
image1 = images.find { |i| i.d == d && i.e == e }
|
72
|
+
image2 = images.find { |i| i.d == d && i.e == es[i_e+1] }
|
73
|
+
id1, id2 = *[image1.id, image2.id].minmax
|
74
|
+
cps = control_points.select { |cp| cp.conn_type == :vertical && cp.n1 == id1 && cp.n2 == id2 }
|
75
|
+
ys << cps.map(&:pry)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
ys.flatten!
|
80
|
+
y_avg, y_std = *calculate_average_and_std(name: :y, values: ys, logger: logger)
|
81
|
+
logger.debug 'filter first standard deviations'
|
82
|
+
ys.select! { |y| (y - y_avg).abs < y_std }
|
83
|
+
y_avg, y_std = *calculate_average_and_std(name: :y, values: ys, logger: logger)
|
84
|
+
logger.debug 'filter second standard deviations'
|
85
|
+
ys.select! { |y| (y - y_avg).abs < y_std }
|
86
|
+
y_avg, _ = *calculate_average_and_std(name: :y, values: ys, logger: logger)
|
87
|
+
|
88
|
+
e_map = {}
|
89
|
+
es.each_with_index do |e, i|
|
90
|
+
if e == es.first
|
91
|
+
e_map[e] = e
|
92
|
+
else
|
93
|
+
e_map[e] = e + -y_avg * i
|
94
|
+
end
|
95
|
+
end
|
96
|
+
logger.debug "created e_map #{e_map}"
|
97
|
+
|
98
|
+
logger.debug 'updating image attributes'
|
99
|
+
images.each do |image|
|
100
|
+
image.d = d_map[image.d]
|
101
|
+
image.e = e_map[image.e]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def run_roll_optimizer
|
106
|
+
r = images.map(&:r).first
|
107
|
+
amount = (0.2 * (images.count)).floor
|
108
|
+
pairs = control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, cps| [calculate_average_and_std(values: cps.map(&:prdist)).first, -cps.count] }[0..(amount-1)]
|
109
|
+
pairs.select{|_,cps| cps.first.conn_type == :horizontal}.each do |pair, cps|
|
110
|
+
logger.debug "#{pair} #{cps.map(&:detailed_info).join("\n")}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Panomosity
|
2
|
+
class Panorama
|
3
|
+
attr_accessor :images, :control_points, :variable, :optimisation_variables, :logger
|
4
|
+
|
5
|
+
def initialize(input, logger = nil)
|
6
|
+
@input = input
|
7
|
+
@images = Image.parse(@input)
|
8
|
+
@variable = PanoramaVariable.parse(@input).first
|
9
|
+
ControlPoint.parse(@input)
|
10
|
+
@control_points = ControlPoint.calculate_distances(@images, @variable)
|
11
|
+
@optimisation_variables = OptimisationVariable.parse(@input)
|
12
|
+
|
13
|
+
if logger
|
14
|
+
@logger = logger
|
15
|
+
else
|
16
|
+
@logger = Logger.new(STDOUT)
|
17
|
+
@logger.level = Logger::DEBUG
|
18
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
19
|
+
"[#{datetime}][#{severity}] #{msg}\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/panomosity/runner.rb
CHANGED
@@ -2,6 +2,8 @@ require 'logger'
|
|
2
2
|
|
3
3
|
module Panomosity
|
4
4
|
class Runner
|
5
|
+
include Panomosity::Utils
|
6
|
+
|
5
7
|
attr_reader :logger
|
6
8
|
|
7
9
|
AVAILABLE_COMMANDS = %w(
|
@@ -16,6 +18,7 @@ module Panomosity
|
|
16
18
|
generate_border_line_control_points
|
17
19
|
get_detailed_control_point_info
|
18
20
|
merge_image_parameters
|
21
|
+
optimize
|
19
22
|
prepare_for_pr0ntools
|
20
23
|
remove_anchor_variables
|
21
24
|
standardize_roll
|
@@ -96,8 +99,8 @@ module Panomosity
|
|
96
99
|
min_control_points = 5
|
97
100
|
control_points.group_by { |cp| [cp.n1, cp.n2] }.select { |_, cps| cps.count > min_control_points }.each do |pair, cps|
|
98
101
|
logger.debug "cleaning pair #{pair.first} <> #{pair.last}"
|
99
|
-
average_x, x_std = *calculate_average_and_std(name: :x, values: cps.map(&:px))
|
100
|
-
average_y, y_std = *calculate_average_and_std(name: :y, values: cps.map(&:py))
|
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)
|
101
104
|
|
102
105
|
max_removal = ((@options[:max_removal] || 0.2) * cps.count).floor
|
103
106
|
min_cps = 8
|
@@ -283,19 +286,19 @@ module Panomosity
|
|
283
286
|
horizontal_control_points, vertical_control_points = *control_points.partition { |cp| cp.conn_type == :horizontal }
|
284
287
|
control_points_of_pair = horizontal_control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| members.count }.last.last
|
285
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"
|
286
|
-
average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist))
|
289
|
+
average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist), logger: logger)
|
287
290
|
horizontal_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < distance_std }
|
288
291
|
logger.info "removed #{control_points_of_pair.count - horizontal_control_points_of_pair.count} outliers"
|
289
292
|
# For logging
|
290
|
-
calculate_average_and_std(name: :distance, values: horizontal_control_points_of_pair.map(&:pdist))
|
293
|
+
calculate_average_and_std(name: :distance, values: horizontal_control_points_of_pair.map(&:pdist), logger: logger)
|
291
294
|
|
292
295
|
control_points_of_pair = vertical_control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| members.count }.last.last
|
293
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"
|
294
|
-
average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist))
|
297
|
+
average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist), logger: logger)
|
295
298
|
vertical_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < distance_std }
|
296
299
|
logger.info "removed #{control_points_of_pair.count - vertical_control_points_of_pair.count} outliers"
|
297
300
|
# For logging
|
298
|
-
calculate_average_and_std(name: :distance, values: vertical_control_points_of_pair.map(&:pdist))
|
301
|
+
calculate_average_and_std(name: :distance, values: vertical_control_points_of_pair.map(&:pdist), logger: logger)
|
299
302
|
|
300
303
|
logger.info 'finding unconnected image pairs'
|
301
304
|
ds = images.map(&:d).uniq.sort
|
@@ -600,7 +603,7 @@ module Panomosity
|
|
600
603
|
control_points = ControlPoint.calculate_distances(images, panorama_variable)
|
601
604
|
|
602
605
|
control_points.each do |cp|
|
603
|
-
logger.debug
|
606
|
+
logger.debug cp.detailed_info
|
604
607
|
end
|
605
608
|
end
|
606
609
|
|
@@ -623,6 +626,24 @@ module Panomosity
|
|
623
626
|
save_file
|
624
627
|
end
|
625
628
|
|
629
|
+
def optimize
|
630
|
+
logger.info 'optimizing'
|
631
|
+
panorama = Panorama.new(@input_file, logger)
|
632
|
+
optimizer = Optimizer.new(panorama)
|
633
|
+
optimizer.run
|
634
|
+
|
635
|
+
@lines = @input_file.each_line.map do |line|
|
636
|
+
image = optimizer.images.find { |i| i.raw == line }
|
637
|
+
if image
|
638
|
+
image.to_s
|
639
|
+
else
|
640
|
+
next line
|
641
|
+
end
|
642
|
+
end.compact
|
643
|
+
|
644
|
+
save_file
|
645
|
+
end
|
646
|
+
|
626
647
|
def prepare_for_pr0ntools
|
627
648
|
logger.info 'preparing for pr0ntools'
|
628
649
|
|
@@ -689,10 +710,10 @@ module Panomosity
|
|
689
710
|
image_ids = pairs.map { |image_ids, _| image_ids }.flatten.uniq
|
690
711
|
rolls = images.select { |image| image_ids.include?(image.id) }.map(&:r)
|
691
712
|
|
692
|
-
average_roll, roll_std = *calculate_average_and_std(name: :roll, values: rolls)
|
713
|
+
average_roll, roll_std = *calculate_average_and_std(name: :roll, values: rolls, logger: logger)
|
693
714
|
new_rolls = rolls.select { |r| (r - average_roll).abs < roll_std }
|
694
715
|
logger.info "removed #{rolls.count - new_rolls.count} outliers"
|
695
|
-
average_roll, _ = *calculate_average_and_std(name: :roll, values: new_rolls)
|
716
|
+
average_roll, _ = *calculate_average_and_std(name: :roll, values: new_rolls, logger: logger)
|
696
717
|
logger.info "converting all rolls to #{average_roll}"
|
697
718
|
|
698
719
|
@lines = @input_file.each_line.map do |line|
|
@@ -725,13 +746,5 @@ module Panomosity
|
|
725
746
|
|
726
747
|
@output_file.close
|
727
748
|
end
|
728
|
-
|
729
|
-
def calculate_average_and_std(name: 'value', values: [])
|
730
|
-
average_value = values.reduce(:+).to_f / values.count
|
731
|
-
logger.debug "average #{name}: #{average_value}"
|
732
|
-
value_std = Math.sqrt(values.map { |v| (v - average_value) ** 2 }.reduce(:+) / (values.count - 1))
|
733
|
-
logger.debug "#{name} std: #{value_std}"
|
734
|
-
[average_value, value_std]
|
735
|
-
end
|
736
749
|
end
|
737
750
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Panomosity
|
2
|
+
module Utils
|
3
|
+
def calculate_average_and_std(name: 'value', values: [], logger: nil)
|
4
|
+
average_value = values.reduce(:+).to_f / values.count
|
5
|
+
logger.debug "average #{name}: #{average_value}" if logger
|
6
|
+
value_std = Math.sqrt(values.map { |v| (v - average_value) ** 2 }.reduce(:+) / (values.count - 1))
|
7
|
+
logger.debug "#{name} std: #{value_std}" if logger
|
8
|
+
[average_value, value_std]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/panomosity/version.rb
CHANGED
data/lib/panomosity.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'panomosity/control_point'
|
2
2
|
require 'panomosity/image'
|
3
3
|
require 'panomosity/optimisation_variable'
|
4
|
+
require 'panomosity/optimizer'
|
5
|
+
require 'panomosity/panorama'
|
4
6
|
require 'panomosity/panorama_variable'
|
5
7
|
require 'panomosity/runner'
|
8
|
+
require 'panomosity/utils'
|
6
9
|
require 'panomosity/version'
|
7
10
|
require 'pathname'
|
8
11
|
require 'fileutils'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oliver Garcia
|
@@ -78,8 +78,11 @@ files:
|
|
78
78
|
- lib/panomosity/control_point.rb
|
79
79
|
- lib/panomosity/image.rb
|
80
80
|
- lib/panomosity/optimisation_variable.rb
|
81
|
+
- lib/panomosity/optimizer.rb
|
82
|
+
- lib/panomosity/panorama.rb
|
81
83
|
- lib/panomosity/panorama_variable.rb
|
82
84
|
- lib/panomosity/runner.rb
|
85
|
+
- lib/panomosity/utils.rb
|
83
86
|
- lib/panomosity/version.rb
|
84
87
|
- panomosity.gemspec
|
85
88
|
homepage: https://github.com/elevatesystems/panomosity
|