panomosity 0.1.9 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
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