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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9429ab38b6928e5b0e731cdedbeeab3e95afba20549ee2acb01c3ee0afda0bf
4
- data.tar.gz: 2aa31fef07b269239cfd09bd4e877f5d897caf7f09eef1c13079564b293e4fa1
3
+ metadata.gz: ec66a2f30f7e065a735367c5f5758ff9bb274c5c180a75de429135d137582874
4
+ data.tar.gz: a59ce44c2c0a37b05ae323b46881345b8e0bdb213233abd16ae5a47780270ca9
5
5
  SHA512:
6
- metadata.gz: 9c594323f23c9e778737b6a309cce0b006f9e64c500f978075ab9da6e7fec5448df780a90fd4332b0562ccffb597261397e31fdaaa2bae7315009c15a60e6a1f
7
- data.tar.gz: 0a1e815356032d427326cecce3ee4668dbbad3b806499854088bf27c061919f075015d5ebf08c379294f3c63a723b765180461720453c62fee31c05d16b70510
6
+ metadata.gz: 2bd782706baca58410c3e4c0e1cce4a4914c086874fe0976f4ebe490a489181ce5321d66692f42398064aef6116db563990c6331ba334700473906717541b704
7
+ data.tar.gz: b8afce3fd3dec4ce71a7cf9bcbe3445c5ef4691d092784440560b725fc04e75db46346ba0ac4f99f65bb1622ea65b757e20d2c9be1c3fa53a78a5e6157d94a9d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- panomosity (0.1.9)
4
+ panomosity (0.1.10)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -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 == 'v'
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
@@ -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 "#{cp.to_s.sub(/\n/, '')} dist #{cp.dist.round(4)} pixel_dist #{cp.px.round(4)},#{cp.py.round(4)},#{cp.pdist.round(4)} pixel_r_dist #{cp.prx.round(4)},#{cp.pry.round(4)},#{cp.prdist.round(4)} conn_type #{cp.conn_type}"
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
@@ -1,3 +1,3 @@
1
1
  module Panomosity
2
- VERSION = '0.1.9'
2
+ VERSION = '0.1.10'
3
3
  end
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.9
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