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