panomosity 0.1.32 → 0.1.37
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 +2 -2
- data/lib/panomosity.rb +39 -0
- data/lib/panomosity/control_point.rb +2 -2
- data/lib/panomosity/generalized_neighborhood.rb +262 -0
- data/lib/panomosity/image.rb +7 -1
- data/lib/panomosity/measure.rb +56 -0
- data/lib/panomosity/optimisation_variable.rb +3 -3
- data/lib/panomosity/optimizer.rb +10 -15
- data/lib/panomosity/pair.rb +51 -5
- data/lib/panomosity/panorama.rb +79 -40
- data/lib/panomosity/panorama_variable.rb +4 -0
- data/lib/panomosity/runner.rb +72 -4
- data/lib/panomosity/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 55fb5d3e10b18279691fb06a4cc567ca0a1bc01098d23d1247a9469e75ffc5e4
|
4
|
+
data.tar.gz: f5088d9459ecbeaa7f892d14f9a9deb12c33fd5017dd11625555e29d9fd95817
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe93c2b8c9acfe777523d9533ca0391a7e63964a4a7facb153e7f7f8fb34ccf8616af7b998965c724f4779bc02d94e73356ac8f3f0208b604bbac1e1e0920c8e
|
7
|
+
data.tar.gz: 1e69b3b9c79df369d31330188ef7ab7c3d10e6aa9d2e625cbce1fe4e3d327b27ea1d9c5df7df03d27b2cbdc4fd17a2b84ce4832efd7ef04f61ac4fbaa38b6d73
|
data/Gemfile.lock
CHANGED
data/lib/panomosity.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'panomosity/control_point'
|
2
|
+
require 'panomosity/generalized_neighborhood'
|
2
3
|
require 'panomosity/image'
|
4
|
+
require 'panomosity/measure'
|
3
5
|
require 'panomosity/neighborhood'
|
4
6
|
require 'panomosity/neighborhood_group'
|
5
7
|
require 'panomosity/optimisation_variable'
|
@@ -13,6 +15,7 @@ require 'panomosity/version'
|
|
13
15
|
require 'pathname'
|
14
16
|
require 'fileutils'
|
15
17
|
require 'optparse'
|
18
|
+
require 'json'
|
16
19
|
|
17
20
|
module Panomosity
|
18
21
|
def self.parse(arguments)
|
@@ -70,6 +73,42 @@ module Panomosity
|
|
70
73
|
options[:report_type] = type
|
71
74
|
end
|
72
75
|
|
76
|
+
parser.on('--darwin', 'Sets a flag to indicate the operating system') do |type|
|
77
|
+
options[:darwin] = type
|
78
|
+
end
|
79
|
+
|
80
|
+
parser.on('--version', 'Show the installed version') do
|
81
|
+
puts VERSION
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
|
85
|
+
parser.on('--regional-distance-similarities-count COUNT', Integer, 'Set the minimum amount of regional control point counts for determining similar neighborhoods (default: 3)') do |count|
|
86
|
+
options[:regional_distance_similarities_count] = count
|
87
|
+
end
|
88
|
+
|
89
|
+
parser.on('--max-reduction-attempts COUNT', Integer, 'Set the max reduction attempts when removing neighborhood outliers (default: 2)') do |count|
|
90
|
+
options[:max_reduction_attempts] = count
|
91
|
+
end
|
92
|
+
|
93
|
+
desc = <<~DESC
|
94
|
+
Set distances to use when determining neighborhood region size in pairs
|
95
|
+
Use JSON e.g. '{"x1": 150, "x2": 30}'
|
96
|
+
Defaults:
|
97
|
+
Vertical pair is x is 10% of image width and y is 100px
|
98
|
+
Horizontal pair is x is 100px and y is 10% of image height
|
99
|
+
DESC
|
100
|
+
parser.on('--distances [DISTANCE_JSON]', desc) do |distances|
|
101
|
+
options[:distances] = JSON.parse(distances) rescue nil
|
102
|
+
end
|
103
|
+
|
104
|
+
parser.on('--distances-horizontal [DISTANCE_JSON]', 'Same as above but only affects horizontal image pairs') do |distances|
|
105
|
+
options[:regional_distance_similarities_count] = JSON.parse(distances) rescue nil
|
106
|
+
end
|
107
|
+
|
108
|
+
parser.on('--distances-vertical [DISTANCE_JSON]', 'Same as above but only affects vertical image pairs') do |distances|
|
109
|
+
options[:regional_distance_similarities_count] = JSON.parse(distances) rescue nil
|
110
|
+
end
|
111
|
+
|
73
112
|
parser.on('-h', '--help', 'Display this screen') do
|
74
113
|
puts parser
|
75
114
|
exit
|
@@ -196,8 +196,8 @@ module Panomosity
|
|
196
196
|
"#{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}"
|
197
197
|
end
|
198
198
|
|
199
|
-
def attributes
|
200
|
-
@attributes
|
199
|
+
def attributes(raw: false)
|
200
|
+
@attributes.keep_if { |k, _| raw || !%i(raw i1 i2).include?(k) }
|
201
201
|
end
|
202
202
|
end
|
203
203
|
end
|
@@ -0,0 +1,262 @@
|
|
1
|
+
require 'panomosity/utils'
|
2
|
+
|
3
|
+
module Panomosity
|
4
|
+
class GeneralizedNeighborhood
|
5
|
+
include Panomosity::Utils
|
6
|
+
|
7
|
+
CONTROL_POINT_ATTRIBUTES = [:dist_avg, :dist_std, :x_avg, :x_std, :y_avg, :y_std]
|
8
|
+
ATTRIBUTES = [:center, :scope, :control_points, :count] + CONTROL_POINT_ATTRIBUTES
|
9
|
+
DEFAULT_DISTANCE = 100
|
10
|
+
|
11
|
+
attr_reader :measure, :options
|
12
|
+
attr_accessor *ATTRIBUTES
|
13
|
+
|
14
|
+
class << self
|
15
|
+
include Panomosity::Utils
|
16
|
+
|
17
|
+
attr_accessor :options, :neighborhoods, :horizontal_similar_neighborhoods, :vertical_similar_neighborhoods, :horizontal_neighborhoods_by_similar_neighborhood, :vertical_neighborhoods_by_similar_neighborhood
|
18
|
+
|
19
|
+
def logger
|
20
|
+
@logger ||= Panomosity.logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def attributes
|
24
|
+
{ name: name }
|
25
|
+
end
|
26
|
+
|
27
|
+
def horizontal
|
28
|
+
horizontal_neighborhoods_by_similar_neighborhood
|
29
|
+
end
|
30
|
+
|
31
|
+
def vertical
|
32
|
+
vertical_neighborhoods_by_similar_neighborhood
|
33
|
+
end
|
34
|
+
|
35
|
+
def calculate_all(panorama:, options: {})
|
36
|
+
@neighborhoods = []
|
37
|
+
@options = options
|
38
|
+
|
39
|
+
Pair.create_pairs_from_panorama(panorama)
|
40
|
+
calculate_from_pairs
|
41
|
+
calculate_from_neighborhoods(type: :horizontal)
|
42
|
+
calculate_from_neighborhoods(type: :vertical)
|
43
|
+
|
44
|
+
@neighborhoods
|
45
|
+
end
|
46
|
+
|
47
|
+
def calculate_from_pairs
|
48
|
+
logger.debug 'calculating neighborhoods from pairs'
|
49
|
+
Pair.all.each do |pair|
|
50
|
+
control_points = pair.control_points.select(&:not_generated?)
|
51
|
+
control_points.each do |control_point|
|
52
|
+
base_params = { center: control_point, scope: pair, options: options }
|
53
|
+
position_params = { measure: { type: :position } }
|
54
|
+
neighborhood = calculate_neighborhood(**base_params.merge(position_params))
|
55
|
+
pair.generalized_neighborhoods << neighborhood
|
56
|
+
distance_params = { measure: { type: :distance, distances: { x1: neighborhood.dist_std } } }
|
57
|
+
distance_neighborhood = calculate_neighborhood(**base_params.merge(distance_params))
|
58
|
+
neighborhood.reference = distance_neighborhood
|
59
|
+
pair.generalized_neighborhoods << distance_neighborhood
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def calculate_from_neighborhoods(type:)
|
65
|
+
count = options[:regional_distance_similarities_count] || 3
|
66
|
+
attempts = options[:max_reduction_attempts] || 2
|
67
|
+
|
68
|
+
# calculates similar neighborhoods based on the regional control point distances by pair
|
69
|
+
calculate_similar_neighborhoods(type: type, count: count)
|
70
|
+
if neighborhoods.empty?
|
71
|
+
logger.warn 'total neighborhoods came up empty, neighborhood default count to 2'
|
72
|
+
calculate_similar_neighborhoods(type: type, count: 2)
|
73
|
+
raise 'still could not find neighborhoods' if neighborhoods.empty?
|
74
|
+
end
|
75
|
+
|
76
|
+
std_outlier_reduction(type: type, max_reduction_attempts: attempts)
|
77
|
+
calculate_neighborhoods_by_similar_neighborhood(type: type)
|
78
|
+
end
|
79
|
+
|
80
|
+
def calculate_neighborhood(center:, scope:, options:, measure: {})
|
81
|
+
neighborhood = new(center: center, scope: scope, options: options)
|
82
|
+
neighborhood.update_measure(measure)
|
83
|
+
@neighborhoods << neighborhood.calculate
|
84
|
+
neighborhood
|
85
|
+
end
|
86
|
+
|
87
|
+
def similar_neighborhoods(type: :horizontal)
|
88
|
+
type == :horizontal ? @horizontal_similar_neighborhoods : @vertical_similar_neighborhoods
|
89
|
+
end
|
90
|
+
|
91
|
+
def neighborhoods_by_similar_neighborhood(type: :horizontal)
|
92
|
+
type == :horizontal ? @horizontal_neighborhoods_by_similar_neighborhood : @vertical_neighborhoods_by_similar_neighborhood
|
93
|
+
end
|
94
|
+
|
95
|
+
def calculate_similar_neighborhoods(type: :horizontal, count: 3)
|
96
|
+
similar_neighborhoods = neighborhoods.select(&:measure_position?).select(&:"#{type}?").select do |neighborhood|
|
97
|
+
neighborhood.scope.similar_neighborhoods << neighborhood if neighborhood.reference.count >= count
|
98
|
+
end
|
99
|
+
self.send(:"#{type}_similar_neighborhoods=", similar_neighborhoods)
|
100
|
+
end
|
101
|
+
|
102
|
+
def std_outlier_reduction(type: :horizontal, max_reduction_attempts: 2, reduction_attempts: 0)
|
103
|
+
return if reduction_attempts >= max_reduction_attempts
|
104
|
+
logger.debug "twice reducing #{type} neighborhood std outliers"
|
105
|
+
avg, std = *calculate_average_and_std(values: similar_neighborhoods(type: type).map(&:dist_std))
|
106
|
+
similar_neighborhoods(type: type).select! { |n| (avg - n.dist_std).abs <= std }
|
107
|
+
std_outlier_reduction(type: type, max_reduction_attempts: max_reduction_attempts, reduction_attempts: reduction_attempts + 1)
|
108
|
+
end
|
109
|
+
|
110
|
+
def calculate_neighborhoods_by_similar_neighborhood(type: :horizontal)
|
111
|
+
instance_variable_set("@#{type}_neighborhoods_by_similar_neighborhood", [])
|
112
|
+
similar_neighborhoods(type: type).each do |neighborhood|
|
113
|
+
base_params = { center: neighborhood, scope: self, options: options }
|
114
|
+
distance_params = { measure: { type: :distance, distances: { x1: neighborhood.dist_std } } }
|
115
|
+
neighborhoods_by_similar_neighborhood(type: type) << calculate_neighborhood(**base_params.merge(distance_params))
|
116
|
+
end
|
117
|
+
|
118
|
+
neighborhoods_by_similar_neighborhood(type: type).sort_by! { |n| -n.count }
|
119
|
+
neighborhoods_by_similar_neighborhood(type: type).max_by(5) { |n| n.count }.each do |n|
|
120
|
+
logger.debug "#{n.dist_avg} #{n.dist_std} #{n.count} x#{n.x_avg} y#{n.y_avg}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def initialize(center:, scope:, options: {})
|
126
|
+
@center = center
|
127
|
+
@scope = scope
|
128
|
+
@options = options
|
129
|
+
@measure = Measure.new
|
130
|
+
end
|
131
|
+
|
132
|
+
def id
|
133
|
+
@id
|
134
|
+
end
|
135
|
+
|
136
|
+
def id=(id)
|
137
|
+
@id = id
|
138
|
+
end
|
139
|
+
|
140
|
+
def reference
|
141
|
+
@reference
|
142
|
+
end
|
143
|
+
|
144
|
+
def reference=(reference)
|
145
|
+
@reference = reference
|
146
|
+
end
|
147
|
+
|
148
|
+
def pair_scope?
|
149
|
+
scope.class.name == 'Panomosity::Pair'
|
150
|
+
end
|
151
|
+
|
152
|
+
def neighborhood_scope?
|
153
|
+
scope.class.name == self.class.name
|
154
|
+
end
|
155
|
+
|
156
|
+
def measure_position?
|
157
|
+
measure.position?
|
158
|
+
end
|
159
|
+
|
160
|
+
def measure_distance?
|
161
|
+
measure.distance?
|
162
|
+
end
|
163
|
+
|
164
|
+
def horizontal?
|
165
|
+
pair_scope? ? scope.horizontal? : center.horizontal?
|
166
|
+
end
|
167
|
+
|
168
|
+
def vertical?
|
169
|
+
pair_scope? ? scope.vertical? : center.vertical?
|
170
|
+
end
|
171
|
+
|
172
|
+
def type
|
173
|
+
horizontal? ? :horizontal : :vertical
|
174
|
+
end
|
175
|
+
|
176
|
+
def update_measure(params)
|
177
|
+
measure.update(params)
|
178
|
+
set_distance_defaults
|
179
|
+
set_measure_defaults
|
180
|
+
end
|
181
|
+
|
182
|
+
def distances_from_options(type: :horizontal)
|
183
|
+
if type == :both
|
184
|
+
options.fetch(:distances, {})
|
185
|
+
else
|
186
|
+
options[:distances]&.fetch(type, {}) || {}
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def set_distance_defaults
|
191
|
+
return unless measure_position?
|
192
|
+
|
193
|
+
measure.update_distances(distances_from_options(type: :both))
|
194
|
+
|
195
|
+
if scope.horizontal?
|
196
|
+
measure.update_distances(x2: (scope.first_image.h * 0.1).round)
|
197
|
+
measure.update_distances(distances_from_options(type: :horizontal))
|
198
|
+
else
|
199
|
+
measure.update_distances(x1: (scope.first_image.w * 0.1).round)
|
200
|
+
measure.update_distances(distances_from_options(type: :vertical))
|
201
|
+
end
|
202
|
+
|
203
|
+
measure.distances[:x1] ||= DEFAULT_DISTANCE
|
204
|
+
measure.distances[:x2] ||= DEFAULT_DISTANCE
|
205
|
+
end
|
206
|
+
|
207
|
+
def set_measure_defaults
|
208
|
+
center_values = if measure_position?
|
209
|
+
{ x1: center.x1, x2: center.y1 }
|
210
|
+
elsif pair_scope?
|
211
|
+
{ x1: center.pdist }
|
212
|
+
else
|
213
|
+
{ x1: center.dist_avg }
|
214
|
+
end
|
215
|
+
|
216
|
+
measure.update(center: center_values)
|
217
|
+
end
|
218
|
+
|
219
|
+
def calculate
|
220
|
+
if pair_scope?
|
221
|
+
elements = scope.control_points.select(&:not_generated?)
|
222
|
+
|
223
|
+
@control_points = if measure_position?
|
224
|
+
elements.select { |cp| measure.includes?(cp.x1, cp.y1) }
|
225
|
+
else
|
226
|
+
elements.select { |cp| measure.includes?(cp.pdist) }
|
227
|
+
end
|
228
|
+
else
|
229
|
+
@neighborhoods = scope.similar_neighborhoods(type: center.type).select { |n| measure.includes?(n.dist_avg) }
|
230
|
+
distance_neighborhoods = @neighborhoods.map(&:reference)
|
231
|
+
@control_points = distance_neighborhoods.map(&:control_points).flatten.uniq(&:raw)
|
232
|
+
end
|
233
|
+
|
234
|
+
@dist_avg, @dist_std = *calculate_average_and_std(values: control_points.map(&:pdist), ignore_empty: true)
|
235
|
+
@x_avg, @x_std = *calculate_average_and_std(values: control_points.map(&:px), ignore_empty: true)
|
236
|
+
@y_avg, @y_std = *calculate_average_and_std(values: control_points.map(&:py), ignore_empty: true)
|
237
|
+
@count = control_points.count
|
238
|
+
|
239
|
+
self
|
240
|
+
end
|
241
|
+
|
242
|
+
def attributes
|
243
|
+
attributes = CONTROL_POINT_ATTRIBUTES.reduce({}) { |h, k| h.merge!({ k => self.send(k) }) }
|
244
|
+
measure_attributes = measure.attributes.reduce({}) do |hash, (key, value)|
|
245
|
+
hash["measure_#{key}"] = value
|
246
|
+
hash
|
247
|
+
end
|
248
|
+
if pair_scope?
|
249
|
+
center_id = center[:id]
|
250
|
+
scope_id = [scope.control_points.first.n1, scope.control_points.first.n2]
|
251
|
+
scope_name = 'pair'
|
252
|
+
else
|
253
|
+
center_id = center.id
|
254
|
+
scope_id = nil
|
255
|
+
scope_name = 'neighborhood'
|
256
|
+
end
|
257
|
+
attributes.merge!(measure_attributes)
|
258
|
+
attributes.merge!(id: id, center: center_id, scope_id: scope_id, scope_name: scope_name, type: type, control_points: control_points.map{|c| c[:id]})
|
259
|
+
attributes
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
data/lib/panomosity/image.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# Example of an image line
|
2
2
|
# i w2448 h3264 f0 v=0 Ra=0 Rb=0 Rc=0 Rd=0 Re=0 Eev6.433 Er1 Eb1 r0 p0 y0 TrX0.88957152 TrY0.79560269 TrZ1 Tpy0 Tpp0 j0 a=0 b=0 c=0 d=0 e=0 g0 t0 Va=0 Vb=0 Vc=0 Vd=0 Vx=0 Vy=0 Vm5 n"WZ8ppTx9PtcxASB3hbeeuS6Z"\n
|
3
3
|
|
4
4
|
module Panomosity
|
@@ -167,5 +167,11 @@ module Panomosity
|
|
167
167
|
return unless name =~ /c(\d+)_r(\d+)\.jpg/
|
168
168
|
name.scan(/c(\d+)_r(\d+)\.jpg/).flatten.map(&:to_i).last
|
169
169
|
end
|
170
|
+
|
171
|
+
def attributes
|
172
|
+
attributes = @attributes.keep_if { |k, _| !%i(raw).include?(k) }
|
173
|
+
attributes.merge!(column: column || 0, row: row || 0)
|
174
|
+
attributes
|
175
|
+
end
|
170
176
|
end
|
171
177
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'panomosity/utils'
|
2
|
+
|
3
|
+
module Panomosity
|
4
|
+
class Measure
|
5
|
+
include Panomosity::Utils
|
6
|
+
|
7
|
+
ATTRIBUTES = [:type, :center, :distances]
|
8
|
+
DEFAULT_DISTANCE = 100
|
9
|
+
|
10
|
+
attr_reader :options
|
11
|
+
attr_accessor *ATTRIBUTES
|
12
|
+
|
13
|
+
def initialize(attributes = {})
|
14
|
+
@attributes = attributes
|
15
|
+
@attributes[:center] ||= {}
|
16
|
+
@attributes[:distances] ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def position?
|
20
|
+
type == :position
|
21
|
+
end
|
22
|
+
|
23
|
+
def distance?
|
24
|
+
type == :distance
|
25
|
+
end
|
26
|
+
|
27
|
+
def type
|
28
|
+
@attributes[:type]
|
29
|
+
end
|
30
|
+
|
31
|
+
def center
|
32
|
+
@attributes[:center]
|
33
|
+
end
|
34
|
+
|
35
|
+
def distances
|
36
|
+
@attributes[:distances]
|
37
|
+
end
|
38
|
+
|
39
|
+
def update(attributes = {})
|
40
|
+
|
41
|
+
@attributes.merge!(attributes)
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_distances(values = {})
|
45
|
+
@attributes[:distances].merge!(values)
|
46
|
+
end
|
47
|
+
|
48
|
+
def includes?(*values)
|
49
|
+
center.values.zip(values, distances.values).all? { |center, value, distance| (center - value).abs <= distance }
|
50
|
+
end
|
51
|
+
|
52
|
+
def attributes
|
53
|
+
{ type: type, center: center.values, distances: distances.values }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -59,12 +59,12 @@ module Panomosity
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def to_s
|
62
|
-
line_values = @@attributes.map { |attribute| "#{attribute}#{self.send(attribute)}" }
|
63
|
-
"v #{line_values.join(' ')}\n"
|
62
|
+
line_values = @@attributes.map { |attribute| "#{attribute}#{self.send(attribute)}" if self.send(attribute) }
|
63
|
+
"v #{line_values.compact.join(' ')}\n"
|
64
64
|
end
|
65
65
|
|
66
66
|
def attributes
|
67
|
-
@attributes.keep_if { |k,_| !%i(raw).include?(k) }
|
67
|
+
@attributes.keep_if { |k, _| !%i(raw).include?(k) }
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
data/lib/panomosity/optimizer.rb
CHANGED
@@ -30,17 +30,14 @@ module Panomosity
|
|
30
30
|
logger.info "applying custom values of xh_avg: #{xh_avg}, yh_avg: #{yh_avg}, xv_avg: #{xv_avg}, yv_avg: #{yv_avg}"
|
31
31
|
end
|
32
32
|
|
33
|
-
unless xh_avg && yh_avg && xv_avg && yv_avg
|
34
|
-
Pair.calculate_neighborhoods(panorama)
|
35
|
-
Pair.calculate_neighborhood_groups
|
36
|
-
end
|
33
|
+
panorama.calculate_neighborhoods unless xh_avg && yh_avg && xv_avg && yv_avg
|
37
34
|
|
38
35
|
ds = images.map(&:d).uniq.sort
|
39
36
|
es = images.map(&:e).uniq.sort
|
40
37
|
|
41
38
|
# get the average error for the best neighborhood group
|
42
|
-
x_avg = xh_avg ||
|
43
|
-
y_avg = yv_avg ||
|
39
|
+
x_avg = xh_avg || GeneralizedNeighborhood.horizontal.first.x_avg
|
40
|
+
y_avg = yv_avg || GeneralizedNeighborhood.vertical.first.y_avg
|
44
41
|
|
45
42
|
# start horizontally
|
46
43
|
d_map = {}
|
@@ -57,8 +54,8 @@ module Panomosity
|
|
57
54
|
logger.debug "created e_map #{e_map}"
|
58
55
|
|
59
56
|
# add in the other offset
|
60
|
-
x_avg = xv_avg ||
|
61
|
-
y_avg = yh_avg ||
|
57
|
+
x_avg = xv_avg || GeneralizedNeighborhood.vertical.first.x_avg
|
58
|
+
y_avg = yh_avg || GeneralizedNeighborhood.horizontal.first.y_avg
|
62
59
|
|
63
60
|
de_map = {}
|
64
61
|
d_map.each_with_index do |(dk,dv),di|
|
@@ -142,10 +139,9 @@ module Panomosity
|
|
142
139
|
end
|
143
140
|
|
144
141
|
def calculate_average_distance
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
vertical_distances = NeighborhoodGroup.vertical[0..4].map(&:prdist_avg)
|
142
|
+
panorama.calculate_neighborhoods
|
143
|
+
horizontal_distances = GeneralizedNeighborhood.horizontal[0..4].map(&:dist_avg)
|
144
|
+
vertical_distances = GeneralizedNeighborhood.vertical[0..4].map(&:dist_avg)
|
149
145
|
calculate_average(values: horizontal_distances + vertical_distances)
|
150
146
|
end
|
151
147
|
|
@@ -161,9 +157,8 @@ module Panomosity
|
|
161
157
|
images.each do |image|
|
162
158
|
image.r = 0.0
|
163
159
|
end
|
164
|
-
|
165
|
-
|
166
|
-
cps = NeighborhoodGroup.horizontal.first.control_points
|
160
|
+
panorama.calculate_neighborhoods
|
161
|
+
cps = GeneralizedNeighborhood.horizontal.first.control_points
|
167
162
|
avg, std = *calculate_average_and_std(values: cps.map(&:roll))
|
168
163
|
# 0.1 degrees of std
|
169
164
|
while std > 0.1
|
data/lib/panomosity/pair.rb
CHANGED
@@ -5,7 +5,7 @@ module Panomosity
|
|
5
5
|
include Panomosity::Utils
|
6
6
|
extend Panomosity::Utils
|
7
7
|
|
8
|
-
attr_accessor :pair, :control_points, :neighborhoods, :type
|
8
|
+
attr_accessor :pair, :control_points, :neighborhoods, :type, :generalized_neighborhoods, :similar_neighborhoods
|
9
9
|
|
10
10
|
class << self
|
11
11
|
attr_accessor :panorama, :logger
|
@@ -22,8 +22,8 @@ module Panomosity
|
|
22
22
|
@pairs
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
@pairs.map
|
25
|
+
def select_control_points_with_regional_distance_similarities
|
26
|
+
@pairs.map(&:select_control_points_with_regional_distance_similarities).flatten.uniq(&:raw)
|
27
27
|
end
|
28
28
|
|
29
29
|
def unconnected
|
@@ -44,6 +44,9 @@ module Panomosity
|
|
44
44
|
rows = images.map(&:row).uniq.sort
|
45
45
|
|
46
46
|
@pairs = []
|
47
|
+
@horizontal_pairs = []
|
48
|
+
@vertical_pairs = []
|
49
|
+
|
47
50
|
# horizontal pair creation
|
48
51
|
rows.each do |row|
|
49
52
|
columns.each do |column|
|
@@ -52,7 +55,9 @@ module Panomosity
|
|
52
55
|
image_2 = images.find { |i| i.row == row && i.column == column.next }
|
53
56
|
next if @panorama.calibration? && (image_1.nil? || image_2.nil?)
|
54
57
|
control_points = @panorama.control_points.select { |cp| [cp.n1, cp.n2].sort == [image_1.id, image_2.id].sort }
|
55
|
-
|
58
|
+
pair = Pair.new([image_1, image_2].sort_by(&:id), control_points: control_points, type: :horizontal)
|
59
|
+
@pairs << pair
|
60
|
+
@horizontal_pairs << pair
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
@@ -64,7 +69,9 @@ module Panomosity
|
|
64
69
|
image_2 = images.find { |i| i.column == column && i.row == row.next }
|
65
70
|
next if @panorama.calibration? && (image_1.nil? || image_2.nil?)
|
66
71
|
control_points = @panorama.control_points.select { |cp| [cp.n1, cp.n2].sort == [image_1.id, image_2.id].sort }
|
67
|
-
|
72
|
+
pair = Pair.new([image_1, image_2].sort_by(&:id), control_points: control_points, type: :vertical)
|
73
|
+
@pairs << pair
|
74
|
+
@vertical_pairs << pair
|
68
75
|
end
|
69
76
|
end
|
70
77
|
end
|
@@ -150,6 +157,8 @@ module Panomosity
|
|
150
157
|
@pair = pair
|
151
158
|
@control_points = control_points
|
152
159
|
@neighborhoods = []
|
160
|
+
@generalized_neighborhoods = []
|
161
|
+
@similar_neighborhoods = []
|
153
162
|
@type = type
|
154
163
|
end
|
155
164
|
|
@@ -157,6 +166,10 @@ module Panomosity
|
|
157
166
|
pair.map(&:id).to_s.gsub(' ', '')
|
158
167
|
end
|
159
168
|
|
169
|
+
def ==(other)
|
170
|
+
to_s == other.to_s
|
171
|
+
end
|
172
|
+
|
160
173
|
def info
|
161
174
|
"#{to_s}(#{type}) image_1 d,e: #{pair.first.d},#{pair.first.e} | image_2 d,e: #{pair.last.d},#{pair.last.e}"
|
162
175
|
end
|
@@ -218,6 +231,27 @@ module Panomosity
|
|
218
231
|
end
|
219
232
|
end
|
220
233
|
|
234
|
+
# Distance neighborhoods
|
235
|
+
def similar_control_points
|
236
|
+
@similar_control_points ||= similar_neighborhoods.map(&:reference).map(&:control_points).flatten.uniq(&:raw)
|
237
|
+
end
|
238
|
+
|
239
|
+
def select_control_points_with_regional_distance_similarities
|
240
|
+
# Keep all our control points if we have less than 10
|
241
|
+
if control_points.count >= 10
|
242
|
+
ratio = similar_control_points.count.to_f / control_points.count
|
243
|
+
if ratio < 0.2
|
244
|
+
Panomosity.logger.warn "#{to_s} keeping less than 20% (#{(ratio * 100).round(4)}%) of #{control_points.count} control points. Reverting and keeping all control points"
|
245
|
+
control_points
|
246
|
+
else
|
247
|
+
similar_control_points
|
248
|
+
end
|
249
|
+
else
|
250
|
+
Panomosity.logger.debug "Skipping pair #{to_s} since it has fewer than 10 control points"
|
251
|
+
control_points
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
221
255
|
def best_neighborhood
|
222
256
|
@best_neighborhood ||= @neighborhoods.max_by { |n| n.control_points.count }
|
223
257
|
end
|
@@ -225,5 +259,17 @@ module Panomosity
|
|
225
259
|
def control_points_of_best_neighborhood
|
226
260
|
best_neighborhood ? best_neighborhood.control_points : []
|
227
261
|
end
|
262
|
+
|
263
|
+
def attributes
|
264
|
+
x_avg, x_std = *calculate_average_and_std(values: control_points.map(&:px), ignore_empty: true)
|
265
|
+
y_avg, y_std = *calculate_average_and_std(values: control_points.map(&:py), ignore_empty: true)
|
266
|
+
dist_avg, dist_std = *calculate_average_and_std(values: control_points.map(&:pdist), ignore_empty: true)
|
267
|
+
i1 = control_points.first.n1
|
268
|
+
i2 = control_points.first.n2
|
269
|
+
{
|
270
|
+
id: [i1, i2], n: i1, N: i2, count: control_points.count, type: type,
|
271
|
+
x_avg: x_avg, x_std: x_std, y_avg: y_avg, y_std: y_std, dist_avg: dist_avg, dist_std: dist_std
|
272
|
+
}
|
273
|
+
end
|
228
274
|
end
|
229
275
|
end
|
data/lib/panomosity/panorama.rb
CHANGED
@@ -18,22 +18,25 @@ module Panomosity
|
|
18
18
|
@logger = Panomosity.logger
|
19
19
|
end
|
20
20
|
|
21
|
+
def calculate_neighborhoods
|
22
|
+
GeneralizedNeighborhood.calculate_all(panorama: self, options: options)
|
23
|
+
end
|
24
|
+
|
21
25
|
def clean_control_points
|
22
|
-
if calibration?
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
control_points_to_keep = Pair.good_control_points_to_keep(count: 2)
|
26
|
+
options.merge!(distances: { x1: 30, x2: 30 }) if calibration? && !options[:distances].nil?
|
27
|
+
options.merge!(regional_distance_similarities_count: 2) unless options[:regional_distance_similarities_count]
|
28
|
+
calculate_neighborhoods
|
29
|
+
|
30
|
+
control_points_to_keep = Pair.select_control_points_with_regional_distance_similarities
|
28
31
|
bad_control_points = control_points.reject { |cp| control_points_to_keep.map(&:raw).include?(cp.raw) }
|
29
32
|
# far_control_points = control_points.select { |cp| cp.prdist > 50 }
|
30
33
|
control_points_to_clean = bad_control_points.uniq(&:raw)
|
31
34
|
|
32
35
|
# log warnings
|
33
36
|
control_point_ratio = control_points_to_clean.count.to_f / control_points.count
|
34
|
-
logger.warn "Removing more than 30% (#{(control_point_ratio*100).round(4)}%) of control points. May potentially cause issues." if control_point_ratio >= 0.3
|
37
|
+
logger.warn "Removing more than 30% (#{(control_point_ratio * 100).round(4)}%) of control points. May potentially cause issues." if control_point_ratio >= 0.3
|
35
38
|
control_point_pair_ratio = Pair.without_enough_control_points(ignore_connected: true).count.to_f / Pair.all.count
|
36
|
-
logger.warn "More than 50% (#{(control_point_pair_ratio*100).round(4)}%) of pairs have fewer than 3 control points. May potentially cause issues." if control_point_pair_ratio >= 0.5
|
39
|
+
logger.warn "More than 50% (#{(control_point_pair_ratio * 100).round(4)}%) of pairs have fewer than 3 control points. May potentially cause issues." if control_point_pair_ratio >= 0.5
|
37
40
|
|
38
41
|
control_points_to_clean
|
39
42
|
end
|
@@ -41,12 +44,8 @@ module Panomosity
|
|
41
44
|
def fix_unconnected_image_pairs
|
42
45
|
logger.info 'finding unconnected image pairs'
|
43
46
|
|
44
|
-
if calibration?
|
45
|
-
|
46
|
-
else
|
47
|
-
Pair.calculate_neighborhoods(self, distance: 100)
|
48
|
-
end
|
49
|
-
Pair.calculate_neighborhood_groups
|
47
|
+
options.merge!(distances: { x1: 30, x2: 30 }) if calibration? && !options[:distances].nil?
|
48
|
+
calculate_neighborhoods
|
50
49
|
|
51
50
|
unconnected_image_pairs = Pair.unconnected
|
52
51
|
logger.debug unconnected_image_pairs.map { |i| { type: i.type, pair: i.pair.map(&:id) } }
|
@@ -92,19 +91,19 @@ module Panomosity
|
|
92
91
|
def generate_control_points(pair: nil, bad_control_point: nil, message: '')
|
93
92
|
if pair
|
94
93
|
if pair.horizontal?
|
95
|
-
group =
|
94
|
+
group = GeneralizedNeighborhood.horizontal.first
|
96
95
|
else
|
97
|
-
group =
|
96
|
+
group = GeneralizedNeighborhood.vertical.first
|
98
97
|
end
|
99
98
|
else
|
100
99
|
if bad_control_point.conn_type == :horizontal
|
101
|
-
group =
|
100
|
+
group = GeneralizedNeighborhood.horizontal.first
|
102
101
|
else
|
103
|
-
group =
|
102
|
+
group = GeneralizedNeighborhood.vertical.first
|
104
103
|
end
|
105
104
|
end
|
106
105
|
|
107
|
-
control_point = ControlPoint.new(group.center.center.attributes)
|
106
|
+
control_point = ControlPoint.new(group.center.center.attributes(raw: true))
|
108
107
|
|
109
108
|
if pair
|
110
109
|
control_point[:n] = pair.first_image.id
|
@@ -153,8 +152,7 @@ module Panomosity
|
|
153
152
|
end
|
154
153
|
|
155
154
|
def diagnose
|
156
|
-
|
157
|
-
Pair.calculate_neighborhood_groups
|
155
|
+
calculate_neighborhoods
|
158
156
|
|
159
157
|
recommendations = []
|
160
158
|
messages = []
|
@@ -184,7 +182,7 @@ module Panomosity
|
|
184
182
|
end
|
185
183
|
|
186
184
|
# neighborhood group tests
|
187
|
-
group_count =
|
185
|
+
group_count = GeneralizedNeighborhood.horizontal.count
|
188
186
|
if group_count < 5
|
189
187
|
message = <<~MESSAGE
|
190
188
|
Total number of horizontal neighborhood groups is #{group_count} which is very low.
|
@@ -194,7 +192,7 @@ module Panomosity
|
|
194
192
|
messages << message
|
195
193
|
end
|
196
194
|
|
197
|
-
group_std_avg = calculate_average(values:
|
195
|
+
group_std_avg = calculate_average(values: GeneralizedNeighborhood.horizontal[0..4].map(&:dist_std))
|
198
196
|
if group_std_avg > 1.0
|
199
197
|
message = <<~MESSAGE
|
200
198
|
The standard deviation of distances in the top 5 horizontal neighborhood groups is #{group_std_avg} which is high.
|
@@ -207,7 +205,7 @@ module Panomosity
|
|
207
205
|
messages << message
|
208
206
|
end
|
209
207
|
|
210
|
-
group_control_points =
|
208
|
+
group_control_points = GeneralizedNeighborhood.horizontal.first.control_points.count
|
211
209
|
total_control_points = Pair.horizontal.map(&:control_points).flatten.uniq(&:raw).count
|
212
210
|
group_control_point_ratio = group_control_points.to_f / total_control_points
|
213
211
|
if group_control_point_ratio < 0.2
|
@@ -222,7 +220,7 @@ module Panomosity
|
|
222
220
|
recommendations << 'horizontal'
|
223
221
|
end
|
224
222
|
|
225
|
-
group_count =
|
223
|
+
group_count = GeneralizedNeighborhood.vertical.count
|
226
224
|
if group_count < 5
|
227
225
|
message = <<~MESSAGE
|
228
226
|
Total number of vertical neighborhood groups is #{group_count} which is very low.
|
@@ -232,7 +230,7 @@ module Panomosity
|
|
232
230
|
messages << message
|
233
231
|
end
|
234
232
|
|
235
|
-
group_std_avg = calculate_average(values:
|
233
|
+
group_std_avg = calculate_average(values: GeneralizedNeighborhood.vertical[0..4].map(&:dist_std))
|
236
234
|
if group_std_avg > 1.0
|
237
235
|
message = <<~MESSAGE
|
238
236
|
The standard deviation of distances in the top 5 vertical neighborhood groups is #{group_std_avg} which is high.
|
@@ -245,7 +243,7 @@ module Panomosity
|
|
245
243
|
messages << message
|
246
244
|
end
|
247
245
|
|
248
|
-
group_control_points =
|
246
|
+
group_control_points = GeneralizedNeighborhood.vertical.first.control_points.count
|
249
247
|
total_control_points = Pair.vertical.map(&:control_points).flatten.uniq(&:raw).count
|
250
248
|
group_control_point_ratio = group_control_points.to_f / total_control_points
|
251
249
|
if group_control_point_ratio < 0.2
|
@@ -274,11 +272,24 @@ module Panomosity
|
|
274
272
|
delta_d: delta_d,
|
275
273
|
delta_e: delta_e,
|
276
274
|
roll: roll,
|
277
|
-
horizontal:
|
278
|
-
vertical:
|
275
|
+
horizontal: GeneralizedNeighborhood.horizontal.first.attributes,
|
276
|
+
vertical: GeneralizedNeighborhood.vertical.first.attributes
|
279
277
|
}
|
280
278
|
}
|
279
|
+
rescue => error
|
280
|
+
message = "Got error #{error.message} when calculating neighborhoods. Recommending fallback"
|
281
281
|
|
282
|
+
logger.error message
|
283
|
+
error.backtrace.each { |line| logger.error line }
|
284
|
+
|
285
|
+
recommendations = %w(horizontal vertical)
|
286
|
+
|
287
|
+
diagnostic_report = {
|
288
|
+
messages: messages,
|
289
|
+
recommendations: recommendations,
|
290
|
+
data: {}
|
291
|
+
}
|
292
|
+
ensure
|
282
293
|
File.open('diagnostic_report.json', 'w+') { |f| f.puts diagnostic_report.to_json }
|
283
294
|
|
284
295
|
if recommendations.empty?
|
@@ -301,17 +312,13 @@ module Panomosity
|
|
301
312
|
calibration_report = JSON.parse(File.read(filename))
|
302
313
|
|
303
314
|
if @options[:report_type] == 'position'
|
304
|
-
if calibration?
|
305
|
-
|
306
|
-
else
|
307
|
-
Pair.calculate_neighborhoods(self, distance: 100)
|
308
|
-
end
|
309
|
-
Pair.calculate_neighborhood_groups
|
315
|
+
options.merge!(distances: { x1: 30, x2: 30 }) if calibration? && !options[:distances].nil?
|
316
|
+
calculate_neighborhoods
|
310
317
|
|
311
|
-
xh_avg =
|
312
|
-
yh_avg =
|
313
|
-
xv_avg =
|
314
|
-
yv_avg =
|
318
|
+
xh_avg = GeneralizedNeighborhood.horizontal.first.x_avg
|
319
|
+
yh_avg = GeneralizedNeighborhood.horizontal.first.y_avg
|
320
|
+
xv_avg = GeneralizedNeighborhood.vertical.first.x_avg
|
321
|
+
yv_avg = GeneralizedNeighborhood.vertical.first.y_avg
|
315
322
|
calibration_report['position'] = {
|
316
323
|
xh_avg: xh_avg,
|
317
324
|
yh_avg: yh_avg,
|
@@ -327,7 +334,39 @@ module Panomosity
|
|
327
334
|
end
|
328
335
|
|
329
336
|
def calibration?
|
330
|
-
!!@input.split(/\n/).find { |line| '#panomosity calibration true' }
|
337
|
+
!!@input.split(/\n/).find { |line| line == '#panomosity calibration true' }
|
338
|
+
end
|
339
|
+
|
340
|
+
def save_file(filename)
|
341
|
+
logger.info "saving file #{filename}"
|
342
|
+
|
343
|
+
lines = @input.each_line.map do |line|
|
344
|
+
objects = [images, variable, control_points, optimisation_variables].flatten
|
345
|
+
object = objects.find { |object| object.raw == line }
|
346
|
+
object&.to_s || line
|
347
|
+
end.compact
|
348
|
+
|
349
|
+
File.open(filename, 'w') { |f| lines.each { |line| f.puts line } }
|
350
|
+
end
|
351
|
+
|
352
|
+
def attributes
|
353
|
+
calculate_neighborhoods
|
354
|
+
control_points = self.control_points.dup
|
355
|
+
control_points.each_with_index { |cp, i| cp[:id] = i }
|
356
|
+
neighborhoods = GeneralizedNeighborhood.neighborhoods.dup
|
357
|
+
neighborhoods.each_with_index { |n, i| n.id = i }
|
358
|
+
types = %i(horizontal vertical)
|
359
|
+
similar_neighborhoods = types.map { |type| GeneralizedNeighborhood.similar_neighborhoods(type: type) }.flatten
|
360
|
+
neighborhoods_by_similar_neighborhood = types.map { |type| GeneralizedNeighborhood.neighborhoods_by_similar_neighborhood(type: type) }.flatten
|
361
|
+
{
|
362
|
+
images: images.map(&:attributes),
|
363
|
+
variable: variable.attributes,
|
364
|
+
control_points: control_points.map(&:attributes),
|
365
|
+
optimisation_variables: optimisation_variables.map(&:attributes),
|
366
|
+
pairs: Pair.all.map(&:attributes),
|
367
|
+
similar_neighborhoods: similar_neighborhoods.map(&:attributes),
|
368
|
+
neighborhoods_by_similar_neighborhood: neighborhoods_by_similar_neighborhood.map(&:attributes)
|
369
|
+
}
|
331
370
|
end
|
332
371
|
end
|
333
372
|
end
|
data/lib/panomosity/runner.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'logger'
|
2
2
|
require 'json'
|
3
|
+
require 'csv'
|
3
4
|
|
4
5
|
module Panomosity
|
5
6
|
class Runner
|
@@ -18,12 +19,15 @@ module Panomosity
|
|
18
19
|
create_calibration_report
|
19
20
|
crop_centers
|
20
21
|
diagnose
|
22
|
+
export_control_point_csv
|
21
23
|
fix_conversion_errors
|
22
24
|
fix_unconnected_image_pairs
|
23
25
|
generate_border_line_control_points
|
24
26
|
get_columns_and_rows
|
25
27
|
get_control_point_info
|
26
28
|
get_neighborhood_info
|
29
|
+
get_panorama_attributes
|
30
|
+
import_control_point_csv
|
27
31
|
merge_image_parameters
|
28
32
|
nona_grid
|
29
33
|
optimize
|
@@ -73,7 +77,7 @@ module Panomosity
|
|
73
77
|
optimizer = Optimizer.new(panorama)
|
74
78
|
calibration_report = JSON.parse(@report_file)
|
75
79
|
|
76
|
-
if calibration_report.fetch('position')
|
80
|
+
if calibration_report.fetch('position', nil)
|
77
81
|
logger.info 'calibration_report.json included position, applying position values'
|
78
82
|
optimizer.run_position_optimizer(xh_avg: calibration_report['position']['xh_avg'],
|
79
83
|
yh_avg: calibration_report['position']['yh_avg'],
|
@@ -81,7 +85,7 @@ module Panomosity
|
|
81
85
|
yv_avg: calibration_report['position']['yv_avg'])
|
82
86
|
end
|
83
87
|
|
84
|
-
if calibration_report.fetch('roll')
|
88
|
+
if calibration_report.fetch('roll', nil)
|
85
89
|
logger.info 'calibration_report.json included roll, applying roll values'
|
86
90
|
optimizer.run_roll_optimizer(apply_roll: calibration_report.fetch('roll'))
|
87
91
|
end
|
@@ -289,6 +293,25 @@ module Panomosity
|
|
289
293
|
panorama.diagnose
|
290
294
|
end
|
291
295
|
|
296
|
+
def export_control_point_csv
|
297
|
+
logger.info 'exporting control point csv'
|
298
|
+
panorama = Panorama.new(@input_file, @options)
|
299
|
+
panorama.calculate_neighborhoods
|
300
|
+
|
301
|
+
filename = 'control_points.csv'
|
302
|
+
headers = %w(image_1_name image_2_name image_1_id image_2_id type width height d1 e1 d2 e2 x1 y1 x2 y2 rx ry r)
|
303
|
+
CSV.open(filename, 'w+') do |csv|
|
304
|
+
csv << headers
|
305
|
+
panorama.control_points.each do |cp|
|
306
|
+
pair = Pair.all.find { |p| p.control_points.include?(cp) }
|
307
|
+
csv << [cp.i1.name, cp.i2.name, cp.i1.id, cp.i2.id, pair.type, cp.i1.w, cp.i1.h, cp.i1.d, cp.i1.e,
|
308
|
+
cp.i2.d, cp.i2.e, cp.x1, cp.y1, cp.x2, cp.y2, cp.px, cp.py, cp.pdist]
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
logger.info 'Done. Check for control_points.csv'
|
313
|
+
end
|
314
|
+
|
292
315
|
def fix_conversion_errors
|
293
316
|
logger.info 'fixing conversion errors'
|
294
317
|
@lines = @input_file.each_line.map do |line|
|
@@ -459,6 +482,41 @@ module Panomosity
|
|
459
482
|
panorama.get_neighborhood_info
|
460
483
|
end
|
461
484
|
|
485
|
+
def get_panorama_attributes
|
486
|
+
# for printing out the JSON
|
487
|
+
logger.level = Logger::UNKNOWN
|
488
|
+
logger.info 'getting panorama attributes'
|
489
|
+
panorama = Panorama.new(@input_file, @options)
|
490
|
+
puts panorama.attributes.to_json
|
491
|
+
end
|
492
|
+
|
493
|
+
def import_control_point_csv
|
494
|
+
logger.info 'importing control point csv'
|
495
|
+
panorama = Panorama.new(@input_file, @options)
|
496
|
+
|
497
|
+
filename = @csv || 'control_points.csv'
|
498
|
+
csv = CSV.read(filename)
|
499
|
+
headers = csv.first
|
500
|
+
csv = csv[1..(csv.length - 1)]
|
501
|
+
data = csv.map { |s| Hash[headers.zip(s)] }
|
502
|
+
|
503
|
+
@lines = @input_file.each_line.map do |line|
|
504
|
+
cp = panorama.control_points.find { |c| c.raw == line }
|
505
|
+
if cp
|
506
|
+
kept_cp = data.find do |d|
|
507
|
+
cp.i1.id == d['image_1_id'].to_i && cp.i2.id == d['image_2_id'].to_i &&
|
508
|
+
cp.x1.round(4) == d['x1'].to_f.round(4) && cp.x2.round(4) == d['x2'].to_f.round(4) &&
|
509
|
+
cp.y1.round(4) == d['y1'].to_f.round(4) && cp.y2.round(4) == d['y2'].to_f.round(4)
|
510
|
+
end
|
511
|
+
cp.to_s if kept_cp
|
512
|
+
else
|
513
|
+
next line
|
514
|
+
end
|
515
|
+
end.compact
|
516
|
+
|
517
|
+
save_file
|
518
|
+
end
|
519
|
+
|
462
520
|
def merge_image_parameters
|
463
521
|
logger.info 'merging image parameters'
|
464
522
|
control_points = ControlPoint.parse(@compare_file)
|
@@ -501,7 +559,7 @@ module Panomosity
|
|
501
559
|
images = Image.parse(@input_file)
|
502
560
|
res = @options[:res] || 'low'
|
503
561
|
columns = images.map(&:column).uniq.sort
|
504
|
-
columns.
|
562
|
+
commands = columns.map do |column|
|
505
563
|
@output = "project_nona_c#{column}.pto"
|
506
564
|
@output_file = File.new(@output, 'w')
|
507
565
|
logger.debug "creating file #{@output}"
|
@@ -515,9 +573,19 @@ module Panomosity
|
|
515
573
|
end.compact
|
516
574
|
save_file
|
517
575
|
logger.debug "running nona #{@output}"
|
518
|
-
output =
|
576
|
+
output = "nona --save-intermediate-images --intermediate-suffix=intermediate -v -m TIFF_m --seam=blend #{@output} -o #{res}_res_stitch_section_c#{column.to_s.rjust(5, '0')}_"
|
519
577
|
logger.debug output
|
578
|
+
output
|
579
|
+
end
|
580
|
+
|
581
|
+
logger.debug 'parallelizing'
|
582
|
+
if @options[:darwin]
|
583
|
+
parallel_command = "cat <<'EOF' | parallel \n#{commands.join("\n")}\nEOF"
|
584
|
+
else
|
585
|
+
parallel_command = "cat <<'EOF' | parallel -j $(fgrep -c processor /proc/cpuinfo) \n#{commands.join("\n")}\nEOF"
|
520
586
|
end
|
587
|
+
|
588
|
+
`#{parallel_command}`
|
521
589
|
end
|
522
590
|
|
523
591
|
def optimize
|
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.37
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oliver Garcia
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -77,7 +77,9 @@ files:
|
|
77
77
|
- exe/panomosity
|
78
78
|
- lib/panomosity.rb
|
79
79
|
- lib/panomosity/control_point.rb
|
80
|
+
- lib/panomosity/generalized_neighborhood.rb
|
80
81
|
- lib/panomosity/image.rb
|
82
|
+
- lib/panomosity/measure.rb
|
81
83
|
- lib/panomosity/neighborhood.rb
|
82
84
|
- lib/panomosity/neighborhood_group.rb
|
83
85
|
- lib/panomosity/optimisation_variable.rb
|
@@ -108,8 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
110
|
- !ruby/object:Gem::Version
|
109
111
|
version: '0'
|
110
112
|
requirements: []
|
111
|
-
|
112
|
-
rubygems_version: 2.7.3
|
113
|
+
rubygems_version: 3.0.8
|
113
114
|
signing_key:
|
114
115
|
specification_version: 4
|
115
116
|
summary: Wrapper for the PTO file parsing needed for PanoTools.
|