panomosity 0.1.4 → 0.1.5
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/.gitignore +3 -0
- data/Gemfile.lock +1 -1
- data/lib/panomosity.rb +5 -1
- data/lib/panomosity/control_point.rb +4 -2
- data/lib/panomosity/runner.rb +129 -25
- 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: d7d65793e972cbf9d8367f1530b60910163841a24a5517bbfea254a21e0a583c
|
4
|
+
data.tar.gz: cac1ed82d1567b8b0ede45b4697dd273d9383d1f951ee5eace80b95734d7e1f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8476d6c4ef2615c878b18232b929828ac03db88d133c654b9b04104db64ef370e5c13feb5f47573f8513397f612cc48a8c19d3fa551b01e9586c9332bdee68e9
|
7
|
+
data.tar.gz: f411883b85d56232f9b38f67161c80e32d682f56c842d87b1d0b31352c157748065922751f1ce99315e9cf67151c2ff173e09def1c6da8b08f3ca1295aa43d5e
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/lib/panomosity.rb
CHANGED
@@ -36,10 +36,14 @@ module Panomosity
|
|
36
36
|
options[:without_cropping] = wc
|
37
37
|
end
|
38
38
|
|
39
|
-
parser.on('--remove-equal-signs', '
|
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
|
-
|
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
|
data/lib/panomosity/runner.rb
CHANGED
@@ -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)
|
235
|
-
|
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
|
-
|
241
|
-
|
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)
|
246
|
-
|
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
|
-
|
252
|
-
|
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
|
-
|
304
|
-
control_point
|
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
|
-
|
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
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
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
|
-
|
577
|
-
logger.info "converting all rolls to #{
|
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 =
|
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
|
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.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-
|
11
|
+
date: 2018-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|