panomosity 0.1.4 → 0.1.5

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: 1e32fb89b5812b7f726cf6c322423d4e816aa103bde59bd5deef4bd6fb2279d4
4
- data.tar.gz: 2aae7a094ad7bccd73af5d6042a799e29cd4ce1bd0e1bdd81d187b24e82b33f5
3
+ metadata.gz: d7d65793e972cbf9d8367f1530b60910163841a24a5517bbfea254a21e0a583c
4
+ data.tar.gz: cac1ed82d1567b8b0ede45b4697dd273d9383d1f951ee5eace80b95734d7e1f5
5
5
  SHA512:
6
- metadata.gz: d7d24392ffec1de2c6fb59b3ed8c4e03a59e91529ecc3a2a7c5d4bcca26383ea2a089dfd0f5e044fe2604c57b802324f6f4859532211b212808ea5a97aadc918
7
- data.tar.gz: f41fae931324dc129bb4f9b8499f86d0d209dd9bcf9c396b768e988d9ab8abe62eec1032601335da697a7df69ebb9f26ae5c271b1fd040b66a6b3e8d6a021611
6
+ metadata.gz: 8476d6c4ef2615c878b18232b929828ac03db88d133c654b9b04104db64ef370e5c13feb5f47573f8513397f612cc48a8c19d3fa551b01e9586c9332bdee68e9
7
+ data.tar.gz: f411883b85d56232f9b38f67161c80e32d682f56c842d87b1d0b31352c157748065922751f1ce99315e9cf67151c2ff173e09def1c6da8b08f3ca1295aa43d5e
data/.gitignore CHANGED
@@ -12,3 +12,6 @@
12
12
 
13
13
  # RubyMine
14
14
  /.idea/
15
+
16
+ # Gem build
17
+ panomosity*.gem
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- panomosity (0.1.3)
4
+ panomosity (0.1.5)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -36,10 +36,14 @@ module Panomosity
36
36
  options[:without_cropping] = wc
37
37
  end
38
38
 
39
- parser.on('--remove-equal-signs', 'Do not crop when running "crop_centers" (usually when the original run failed)') do |eq|
39
+ parser.on('--remove-equal-signs', 'Remove equal signs when running "convert_equaled_image_parameters" (necessary when parsing the PTO file using Panotools)') do |eq|
40
40
  options[:remove_equal_signs] = eq
41
41
  end
42
42
 
43
+ parser.on('--max-removal FRAC', Float, 'Max fraction of control points to be removed when running "clean_control_points" that are statistical outliers') do |mr|
44
+ options[:max_removal] = mr
45
+ end
46
+
43
47
  parser.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
44
48
  options[:verbose] = v
45
49
  end
@@ -47,7 +47,9 @@ module Panomosity
47
47
  point1 = image1.to_cartesian(cp.x1, cp.y1)
48
48
  point2 = image2.to_cartesian(cp.x2, cp.y2)
49
49
 
50
- angle = Math.acos(point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2])
50
+ product = point1[0] * point2[0] + point1[1] * point2[1] + point1[2] * point2[2]
51
+ product = 1.0 if product > 1.0
52
+ angle = Math.acos(product)
51
53
  radius = (panorama_variable.w / 2.0) / Math.tan((panorama_variable.v * Math::PI / 180) / 2)
52
54
 
53
55
  distance = angle * radius
@@ -59,9 +61,9 @@ module Panomosity
59
61
  x2 = (image2.w / 2.0) - cp.x2 + image2.d
60
62
  y2 = (image2.h / 2.0) - cp.y2 + image2.e
61
63
 
62
- pixel_distance = Math.sqrt((x1 - x2)**2 + (y1 - y2)**2)
63
64
  cp.px = x1 - x2
64
65
  cp.py = y1 - y2
66
+ pixel_distance = Math.sqrt(cp.px**2 + cp.py**2)
65
67
  cp.pdist = pixel_distance
66
68
  cp.conn_type = image1.d == image2.d ? :vertical : :horizontal
67
69
  end
@@ -6,6 +6,7 @@ module Panomosity
6
6
 
7
7
  AVAILABLE_COMMANDS = %w(
8
8
  check_position_changes
9
+ clean_control_points
9
10
  convert_equaled_image_parameters
10
11
  convert_horizontal_lines
11
12
  convert_translation_parameters
@@ -83,6 +84,52 @@ module Panomosity
83
84
  save_file
84
85
  end
85
86
 
87
+ def clean_control_points
88
+ logger.info 'cleaning control points'
89
+ # Since this is very exact, having many outliers in control points distances will cause errors
90
+ images = Image.parse(@input_file)
91
+ panorama_variable = PanoramaVariable.parse(@input_file).first
92
+ ControlPoint.parse(@input_file)
93
+ control_points = ControlPoint.calculate_distances(images, panorama_variable)
94
+
95
+ bad_control_points = []
96
+ min_control_points = 5
97
+ control_points.group_by { |cp| [cp.n1, cp.n2] }.select { |_, cps| cps.count > min_control_points }.each do |pair, cps|
98
+ 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))
101
+
102
+ max_removal = ((@options[:max_removal] || 0.2) * cps.count).floor
103
+ min_cps = 10
104
+ max_iterations = 10
105
+ iterations = 0
106
+ bad_cps = cps.select { |cp| (cp.px - average_x).abs >= x_std || (cp.py - average_y).abs >= y_std }
107
+ while bad_cps.count > max_removal && (cps.count - bad_cps.count) >= min_cps && iterations <= max_iterations
108
+ x_std *= 1.1
109
+ y_std *= 1.1
110
+ iterations += 1
111
+ bad_cps = cps.select { |cp| (cp.px - average_x).abs >= x_std || (cp.py - average_y).abs >= y_std }
112
+ end
113
+
114
+ logger.info "found #{bad_cps.count} outliers"
115
+ bad_control_points << bad_cps if bad_cps.count <= max_removal
116
+ end
117
+ bad_control_points.flatten!
118
+
119
+ logger.info "removing #{bad_control_points.count} control points"
120
+ @lines = @input_file.each_line.map do |line|
121
+ control_point = ControlPoint.parse_line(line)
122
+ # skip this control point if we found it
123
+ if control_point && bad_control_points.find { |bad_cp| bad_cp.raw == control_point.raw }
124
+ next
125
+ else
126
+ next line
127
+ end
128
+ end.compact
129
+
130
+ save_file
131
+ end
132
+
86
133
  def convert_equaled_image_parameters
87
134
  logger.info 'converting equaled image parameters'
88
135
  images = Image.parse(@input_file)
@@ -231,25 +278,19 @@ module Panomosity
231
278
  horizontal_control_points, vertical_control_points = *control_points.partition { |cp| cp.conn_type == :horizontal }
232
279
  control_points_of_pair = horizontal_control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| members.count }.last.last
233
280
  logger.debug "found horizontal pair #{control_points_of_pair.first.n1} <> #{control_points_of_pair.first.n2} with #{control_points_of_pair.count} connections"
234
- average_distance = control_points_of_pair.map(&:pdist).reduce(:+).to_f / control_points_of_pair.count
235
- logger.debug "average distance #{average_distance}"
236
- dist_std = Math.sqrt(control_points_of_pair.map { |cp| (cp.pdist - average_distance) ** 2 }.reduce(:+) / (control_points_of_pair.count - 1))
237
- logger.debug "dist std: #{dist_std}"
238
- horizontal_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < dist_std }
281
+ average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist))
282
+ horizontal_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < distance_std }
239
283
  logger.info "removed #{control_points_of_pair.count - horizontal_control_points_of_pair.count} outliers"
240
- new_average_distance = horizontal_control_points_of_pair.map(&:pdist).reduce(:+).to_f / horizontal_control_points_of_pair.count
241
- logger.info "new average #{new_average_distance}"
284
+ # For logging
285
+ calculate_average_and_std(name: :distance, values: horizontal_control_points_of_pair.map(&:pdist))
242
286
 
243
287
  control_points_of_pair = vertical_control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| members.count }.last.last
244
288
  logger.debug "found vertical pair #{control_points_of_pair.first.n1} <> #{control_points_of_pair.first.n2} with #{control_points_of_pair.count} connections"
245
- average_distance = control_points_of_pair.map(&:pdist).reduce(:+).to_f / control_points_of_pair.count
246
- logger.debug "average distance #{average_distance}"
247
- dist_std = Math.sqrt(control_points_of_pair.map { |cp| (cp.pdist - average_distance) ** 2 }.reduce(:+) / (control_points_of_pair.count - 1))
248
- logger.debug "dist std: #{dist_std}"
249
- vertical_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < dist_std }
289
+ average_distance, distance_std = *calculate_average_and_std(name: :distance, values: control_points_of_pair.map(&:pdist))
290
+ vertical_control_points_of_pair = control_points_of_pair.select { |cp| (cp.pdist - average_distance).abs < distance_std }
250
291
  logger.info "removed #{control_points_of_pair.count - vertical_control_points_of_pair.count} outliers"
251
- new_average_distance = vertical_control_points_of_pair.map(&:pdist).reduce(:+).to_f / vertical_control_points_of_pair.count
252
- logger.info "new average #{new_average_distance}"
292
+ # For logging
293
+ calculate_average_and_std(name: :distance, values: vertical_control_points_of_pair.map(&:pdist))
253
294
 
254
295
  logger.info 'finding unconnected image pairs'
255
296
  ds = images.map(&:d).uniq.sort
@@ -298,10 +339,35 @@ module Panomosity
298
339
  control_point = vertical_control_points_of_pair.first
299
340
  end
300
341
 
342
+ x_diff = control_point.x2 - control_point.x1
343
+ y_diff = control_point.y2 - control_point.y1
344
+
345
+ x1 = x_diff <= 0 ? -x_diff + 15 : 0
346
+ y1 = y_diff <= 0 ? -y_diff + 15 : 0
347
+
301
348
  control_point[:n] = pair[:pair].first.id
302
349
  control_point[:N] = pair[:pair].last.id
303
- logger.debug "adding control point connecting #{control_point.n1} <> #{control_point.n2}"
304
- control_point.to_s
350
+ control_point[:x] = x1
351
+ control_point[:X] = x1 + x_diff
352
+ control_point[:y] = y1
353
+ control_point[:Y] = y1 + y_diff
354
+
355
+ logger.debug "adding control points connecting #{control_point.n1} <> #{control_point.n2}"
356
+ i = images.first
357
+ 3.times.map do
358
+ if control_point.conn_type == :horizontal
359
+ control_point[:x] += 5
360
+ control_point[:X] += 5
361
+ control_point[:y] += i.h * 0.25
362
+ control_point[:Y] += i.h * 0.25
363
+ else
364
+ control_point[:x] += i.w * 0.25
365
+ control_point[:X] += i.w * 0.25
366
+ control_point[:y] += 5
367
+ control_point[:Y] += 5
368
+ end
369
+ control_point.to_s
370
+ end.join
305
371
  end + [line]
306
372
  else
307
373
  next line
@@ -315,10 +381,36 @@ module Panomosity
315
381
  else
316
382
  control_point = vertical_control_points_of_pair.first
317
383
  end
384
+
385
+ x_diff = control_point.x2 - control_point.x1
386
+ y_diff = control_point.y2 - control_point.y1
387
+
388
+ x1 = x_diff <= 0 ? -x_diff + 15 : 0
389
+ y1 = y_diff <= 0 ? -y_diff + 15 : 0
390
+
318
391
  control_point[:n] = fake_control_point[:n]
319
392
  control_point[:N] = fake_control_point[:N]
393
+ control_point[:x] = x1
394
+ control_point[:X] = x1 + x_diff
395
+ control_point[:y] = y1
396
+ control_point[:Y] = y1 + y_diff
397
+
320
398
  logger.debug "replacing unrealistic control point connecting #{control_point.n1} <> #{control_point.n2}"
321
- control_point.to_s
399
+ i = images.first
400
+ 3.times.map do
401
+ if control_point.conn_type == :horizontal
402
+ control_point[:x] += 5
403
+ control_point[:X] += 5
404
+ control_point[:y] += i.h * 0.25
405
+ control_point[:Y] += i.h * 0.25
406
+ else
407
+ control_point[:x] += i.w * 0.25
408
+ control_point[:X] += i.w * 0.25
409
+ control_point[:y] += 5
410
+ control_point[:Y] += 5
411
+ end
412
+ control_point.to_s
413
+ end.join
322
414
  else
323
415
  next line
324
416
  end
@@ -566,20 +658,24 @@ module Panomosity
566
658
  def standardize_roll
567
659
  logger.info 'standardizing roll'
568
660
  images = Image.parse(@input_file)
569
- rolls = images.map(&:r)
570
- average_roll = rolls.reduce(:+).to_f / rolls.count
571
- logger.debug "average roll: #{average_roll}"
572
- roll_std = Math.sqrt(rolls.map { |r| (r - average_roll) ** 2 }.reduce(:+) / (rolls.count - 1))
573
- logger.debug "roll std: #{roll_std}"
661
+ panorama_variable = PanoramaVariable.parse(@input_file).first
662
+ ControlPoint.parse(@input_file)
663
+ control_points = ControlPoint.calculate_distances(images, panorama_variable)
664
+ max_count = (images.count * 0.5).ceil - 1
665
+ pairs = control_points.group_by { |cp| [cp.n1, cp.n2] }.sort_by { |_, members| -members.count }[0..max_count]
666
+ image_ids = pairs.map { |image_ids, _| image_ids }.flatten.uniq
667
+ rolls = images.select { |image| image_ids.include?(image.id) }.map(&:r)
668
+
669
+ average_roll, roll_std = *calculate_average_and_std(name: :roll, values: rolls)
574
670
  new_rolls = rolls.select { |r| (r - average_roll).abs < roll_std }
575
671
  logger.info "removed #{rolls.count - new_rolls.count} outliers"
576
- new_average_roll = new_rolls.reduce(:+).to_f / new_rolls.count
577
- logger.info "converting all rolls to #{new_average_roll}"
672
+ average_roll, _ = *calculate_average_and_std(name: :roll, values: new_rolls)
673
+ logger.info "converting all rolls to #{average_roll}"
578
674
 
579
675
  @lines = @input_file.each_line.map do |line|
580
676
  image = images.find { |i| i.raw == line }
581
677
  if image
582
- image.r = new_average_roll
678
+ image.r = average_roll
583
679
  image.to_s
584
680
  else
585
681
  next line
@@ -606,5 +702,13 @@ module Panomosity
606
702
 
607
703
  @output_file.close
608
704
  end
705
+
706
+ def calculate_average_and_std(name:, values: [])
707
+ average_value = values.reduce(:+).to_f / values.count
708
+ logger.debug "average #{name}: #{average_value}"
709
+ value_std = Math.sqrt(values.map { |v| (v - average_value) ** 2 }.reduce(:+) / (values.count - 1))
710
+ logger.debug "#{name} std: #{value_std}"
711
+ [average_value, value_std]
712
+ end
609
713
  end
610
714
  end
@@ -1,3 +1,3 @@
1
1
  module Panomosity
2
- VERSION = '0.1.4'
2
+ VERSION = '0.1.5'
3
3
  end
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
4
+ version: 0.1.5
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-09-18 00:00:00.000000000 Z
11
+ date: 2018-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler