cicada 0.9.4-java → 0.9.5-java
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cicada.rb +0 -3
- data/lib/cicada/aberration_map.rb +2 -20
- data/lib/cicada/cicada_main.rb +44 -234
- data/lib/cicada/correction/correction.rb +1 -94
- data/lib/cicada/correction/in_situ_correction.rb +67 -0
- data/lib/cicada/correction/position_corrector.rb +14 -164
- data/lib/cicada/correction/unable_to_correct_error.rb +30 -0
- data/lib/cicada/file_interaction.rb +5 -98
- data/lib/cicada/fitting/p3d_fitter.rb +3 -45
- data/lib/cicada/mutable_matrix.rb +0 -21
- data/lib/cicada/version.rb +1 -5
- data/spec/cicada/cicada_main_spec.rb +62 -0
- data/spec/cicada/correction/position_corrector_spec.rb +3 -4
- data/spec/spec_helper.rb +3 -5
- metadata +28 -37
@@ -32,11 +32,6 @@ require 'facets/enumerable/ewise'
|
|
32
32
|
|
33
33
|
module Cicada
|
34
34
|
|
35
|
-
##
|
36
|
-
# An error indicating that a position cannot be corrected (probably due to incomplete
|
37
|
-
# coverage in the correction dataset).
|
38
|
-
class UnableToCorrectError < StandardError; end
|
39
|
-
|
40
35
|
##
|
41
36
|
# Stores data for a standard 3d high-resolution colocalization correction, including
|
42
37
|
# positions for a number of objects used for correction, and local quadratic fits of
|
@@ -82,22 +77,16 @@ module Cicada
|
|
82
77
|
# @param [Integer] correction_channel the channel being corrected
|
83
78
|
#
|
84
79
|
def initialize(c_x, c_y, c_z, distance_cutoffs, image_objects, reference_channel, correction_channel)
|
85
|
-
|
86
80
|
@correction_x = c_x
|
87
81
|
@correction_y = c_y
|
88
82
|
@correction_z = c_z
|
89
83
|
@reference_channel = reference_channel
|
90
84
|
@correction_channel = correction_channel
|
91
85
|
@distance_cutoffs = distance_cutoffs
|
92
|
-
|
93
86
|
n_dims = 3
|
94
|
-
|
95
87
|
@positions_for_correction = MMatrix.build(image_objects.size, n_dims) do |r, c|
|
96
|
-
|
97
88
|
image_objects[r].getPositionForChannel(reference_channel).toArray[c]
|
98
|
-
|
99
89
|
end
|
100
|
-
|
101
90
|
end
|
102
91
|
|
103
92
|
##
|
@@ -108,16 +97,11 @@ module Cicada
|
|
108
97
|
# @return [void]
|
109
98
|
#
|
110
99
|
def write_to_file(fn)
|
111
|
-
|
112
100
|
File.open(fn, 'w') do |f|
|
113
|
-
|
114
101
|
f.puts(write_to_xml)
|
115
|
-
|
116
102
|
end
|
117
|
-
|
118
103
|
end
|
119
104
|
|
120
|
-
|
121
105
|
##
|
122
106
|
# Writes all the points used for correction to XML within a supplied correction
|
123
107
|
# XML element
|
@@ -127,16 +111,11 @@ module Cicada
|
|
127
111
|
# @return [void]
|
128
112
|
#
|
129
113
|
def write_all_correction_point_xml(correction_element)
|
130
|
-
|
131
114
|
@distance_cutoffs.each_with_index do |e,i|
|
132
|
-
|
133
115
|
write_correction_point_xml(correction_element, i)
|
134
|
-
|
135
116
|
end
|
136
|
-
|
137
117
|
end
|
138
118
|
|
139
|
-
|
140
119
|
##
|
141
120
|
# Writes a single point used for correction to XML within a supplied correction
|
142
121
|
# XML element
|
@@ -147,26 +126,18 @@ module Cicada
|
|
147
126
|
# @return [void]
|
148
127
|
#
|
149
128
|
def write_correction_point_xml(correction_element, i)
|
150
|
-
|
151
129
|
cp = correction_element.add_element XML_STRINGS[:correction_point_element]
|
152
|
-
|
153
130
|
cp.attributes[XML_STRINGS[:x_pos_attr]]= @positions_for_correction[i,0]
|
154
131
|
cp.attributes[XML_STRINGS[:y_pos_attr]]= @positions_for_correction[i,1]
|
155
132
|
cp.attributes[XML_STRINGS[:z_pos_attr]]= @positions_for_correction[i,2]
|
156
|
-
|
157
133
|
point_dims_to_corr = {XML_STRINGS[:x_param_element] => @correction_x,
|
158
134
|
XML_STRINGS[:y_param_element] => @correction_y,
|
159
135
|
XML_STRINGS[:z_param_element] => @correction_z}
|
160
136
|
|
161
|
-
|
162
137
|
point_dims_to_corr.each do |dim_el, corr_txt|
|
163
|
-
|
164
138
|
p = cp.add_element dim_el
|
165
|
-
|
166
139
|
p.text = corr_txt[i].to_a.join(", ")
|
167
|
-
|
168
140
|
end
|
169
|
-
|
170
141
|
end
|
171
142
|
|
172
143
|
##
|
@@ -178,15 +149,10 @@ module Cicada
|
|
178
149
|
# @return [void]
|
179
150
|
#
|
180
151
|
def write_correction_binary_data_element(correction_element)
|
181
|
-
|
182
152
|
bd = correction_element.add_element XML_STRINGS[:binary_data_element]
|
183
|
-
|
184
153
|
bd.attributes[XML_STRINGS[:encoding_attr]]= XML_STRINGS[:encoding_name]
|
185
|
-
|
186
154
|
bin_data = Base64.encode64(Marshal.dump(self))
|
187
|
-
|
188
155
|
bd.text = bin_data
|
189
|
-
|
190
156
|
end
|
191
157
|
|
192
158
|
##
|
@@ -195,27 +161,16 @@ module Cicada
|
|
195
161
|
# @return [String] the correction data encoded as XML
|
196
162
|
#
|
197
163
|
def write_to_xml
|
198
|
-
|
199
164
|
doc = REXML::Document.new
|
200
|
-
|
201
165
|
ce = doc.add_element XML_STRINGS[:correction_element]
|
202
|
-
|
203
166
|
ce.attributes[XML_STRINGS[:n_points_attr]] = @distance_cutoffs.size
|
204
|
-
|
205
167
|
ce.attributes[XML_STRINGS[:ref_channel_attr]] = @reference_channel
|
206
|
-
|
207
168
|
ce.attributes[XML_STRINGS[:corr_channel_attr]] = @correction_channel
|
208
|
-
|
209
169
|
write_all_correction_point_xml(ce)
|
210
|
-
|
211
170
|
write_correction_binary_data_element(ce)
|
212
|
-
|
213
171
|
doc_string = ""
|
214
|
-
|
215
172
|
doc.write doc_string, 2
|
216
|
-
|
217
173
|
doc_string
|
218
|
-
|
219
174
|
end
|
220
175
|
|
221
176
|
##
|
@@ -226,19 +181,14 @@ module Cicada
|
|
226
181
|
# @return [Correction] the correction contained in the file.
|
227
182
|
#
|
228
183
|
def self.read_from_file(fn)
|
229
|
-
|
230
184
|
return nil unless File.exist?(fn)
|
231
185
|
|
232
186
|
xml_str = ""
|
233
|
-
|
234
187
|
File.open(fn) do |f|
|
235
|
-
|
236
188
|
xml_str = f.read
|
237
|
-
|
238
189
|
end
|
239
190
|
|
240
191
|
read_from_xml(xml_str)
|
241
|
-
|
242
192
|
end
|
243
193
|
|
244
194
|
|
@@ -250,13 +200,9 @@ module Cicada
|
|
250
200
|
# @return [Correction] the correction contained in the string.
|
251
201
|
#
|
252
202
|
def self.read_from_xml(xml_str)
|
253
|
-
|
254
203
|
doc = REXML::Document.new xml_str
|
255
|
-
|
256
204
|
bin_el = doc.root.elements[1, XML_STRINGS[:binary_data_element]]
|
257
|
-
|
258
205
|
Marshal.load(Base64.decode64(bin_el.text))
|
259
|
-
|
260
206
|
end
|
261
207
|
|
262
208
|
|
@@ -271,15 +217,10 @@ module Cicada
|
|
271
217
|
# @return [Vector] the distance from the specified point to each image object.
|
272
218
|
#
|
273
219
|
def calculate_normalized_dists_to_centroids(x,y)
|
274
|
-
|
275
220
|
dists_to_centroids = @positions_for_correction.column(0).map { |x0| (x0-x)**2 }
|
276
|
-
|
277
221
|
dists_to_centroids += @positions_for_correction.column(1).map { |y0| (y0-y)**2 }
|
278
|
-
|
279
222
|
dists_to_centroids = dists_to_centroids.map { |e| Math.sqrt(e) }
|
280
|
-
|
281
223
|
dists_to_centroids.map2(@distance_cutoffs) { |e1, e2| e1/e2 }
|
282
|
-
|
283
224
|
end
|
284
225
|
|
285
226
|
##
|
@@ -290,17 +231,11 @@ module Cicada
|
|
290
231
|
# @return [Vector] the weights for each local fit used for correction
|
291
232
|
#
|
292
233
|
def calculate_weights(x, y)
|
293
|
-
|
294
234
|
dist_ratio = calculate_normalized_dists_to_centroids(x,y)
|
295
|
-
|
296
235
|
dist_ratio_mask = MVector.zero(dist_ratio.size)
|
297
|
-
|
298
236
|
dist_ratio_mask = dist_ratio.map { |e| e <= 1 ? 1 : 0 }
|
299
|
-
|
300
237
|
weights = dist_ratio.map { |e| -3*e**2 + 1 + 2*e**3 }
|
301
|
-
|
302
238
|
weights.map2(dist_ratio_mask) { |e1, e2| e1*e2 }
|
303
|
-
|
304
239
|
end
|
305
240
|
|
306
241
|
##
|
@@ -314,41 +249,28 @@ module Cicada
|
|
314
249
|
# of the selected image objects used for correction; and the weights of the fits
|
315
250
|
#
|
316
251
|
def find_points_for_correction(x,y)
|
317
|
-
|
318
252
|
weights = calculate_weights(x,y)
|
319
|
-
|
320
253
|
count_weights = weights.count { |e| e > 0 }
|
321
|
-
|
322
254
|
raise UnableToCorrectError, "Incomplete coverage in correction dataset at (x,y) = (#{x}, #{y})." if count_weights == 0
|
323
255
|
|
324
256
|
cx = MMatrix.zero(count_weights, @correction_x[0].size)
|
325
257
|
cy = MMatrix.zero(count_weights, @correction_y[0].size)
|
326
258
|
cz = MMatrix.zero(count_weights, @correction_z[0].size)
|
327
|
-
|
328
259
|
x_vec = MVector.zero(count_weights)
|
329
260
|
y_vec = MVector.zero(count_weights)
|
330
|
-
|
331
261
|
kept_weights = MVector.zero(count_weights)
|
332
|
-
|
333
262
|
kept_counter = 0
|
334
263
|
|
335
264
|
weights.each_with_index do |w, i|
|
336
|
-
|
337
265
|
if w > 0 then
|
338
|
-
|
339
266
|
cx.replace_row(kept_counter, @correction_x[i])
|
340
267
|
cy.replace_row(kept_counter, @correction_y[i])
|
341
|
-
cz.replace_row(kept_counter, @correction_z[i])
|
342
|
-
|
268
|
+
cz.replace_row(kept_counter, @correction_z[i])
|
343
269
|
x_vec[kept_counter] = x - positions_for_correction[i,0]
|
344
270
|
y_vec[kept_counter] = y - positions_for_correction[i,1]
|
345
|
-
|
346
271
|
kept_weights[kept_counter] = weights[i]
|
347
|
-
|
348
272
|
kept_counter += 1
|
349
|
-
|
350
273
|
end
|
351
|
-
|
352
274
|
end
|
353
275
|
|
354
276
|
OpenStruct.new(cx: cx,
|
@@ -357,7 +279,6 @@ module Cicada
|
|
357
279
|
x_vec: x_vec,
|
358
280
|
y_vec: y_vec,
|
359
281
|
weights: kept_weights)
|
360
|
-
|
361
282
|
end
|
362
283
|
|
363
284
|
##
|
@@ -369,41 +290,27 @@ module Cicada
|
|
369
290
|
# for the specified position.
|
370
291
|
#
|
371
292
|
def correct_position(x, y)
|
372
|
-
|
373
293
|
points = find_points_for_correction(x,y)
|
374
|
-
|
375
294
|
x_corr = 0.0
|
376
295
|
y_corr = 0.0
|
377
296
|
z_corr = 0.0
|
378
|
-
|
379
297
|
all_correction_parameters = MMatrix.columns([MVector.unit(points.x_vec.size),
|
380
298
|
points.x_vec,
|
381
299
|
points.y_vec,
|
382
300
|
points.x_vec.map { |e| e**2 },
|
383
301
|
points.y_vec.map { |e| e**2 },
|
384
302
|
points.x_vec.map2(points.y_vec) { |e1, e2| e1*e2 }])
|
385
|
-
|
386
|
-
|
387
303
|
all_correction_parameters.row_size.times do |i|
|
388
|
-
|
389
304
|
x_corr += all_correction_parameters.row(i).inner_product(points.cx.row(i))*points.weights[i]
|
390
305
|
y_corr += all_correction_parameters.row(i).inner_product(points.cy.row(i))*points.weights[i]
|
391
306
|
z_corr += all_correction_parameters.row(i).inner_product(points.cz.row(i))*points.weights[i]
|
392
|
-
|
393
307
|
end
|
394
308
|
|
395
309
|
sum_weights = points.weights.reduce(0.0) { |a,e| a + e }
|
396
|
-
|
397
310
|
x_corr /= sum_weights
|
398
311
|
y_corr /= sum_weights
|
399
312
|
z_corr /= sum_weights
|
400
|
-
|
401
313
|
MVector[x_corr, y_corr, z_corr]
|
402
|
-
|
403
314
|
end
|
404
|
-
|
405
315
|
end
|
406
|
-
|
407
316
|
end
|
408
|
-
|
409
|
-
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2013 Colin J. Fuller
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the Software), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
# SOFTWARE.
|
21
|
+
#++
|
22
|
+
|
23
|
+
require 'rimageanalysistools/fitting/bisquare_linear_fit'
|
24
|
+
|
25
|
+
module Cicada
|
26
|
+
class InSituCorrection
|
27
|
+
attr_accessor :reference_channel, :in_situ_corr_second_channel, :correction_channel, :parameters, :corr_parameters
|
28
|
+
|
29
|
+
def initialize(ref_ch, in_situ_ch, corr_ch, iobjs, disable_intercept)
|
30
|
+
self.reference_channel = ref_ch
|
31
|
+
self.in_situ_corr_second_channel = in_situ_ch
|
32
|
+
self.correction_channel = corr_ch
|
33
|
+
@disable_intercept = disable_intercept
|
34
|
+
|
35
|
+
calculate_in_situ_corr(iobjs)
|
36
|
+
end
|
37
|
+
|
38
|
+
def disable_intercept?
|
39
|
+
@disable_intercept
|
40
|
+
end
|
41
|
+
|
42
|
+
def calculate_in_situ_corr(iobjs)
|
43
|
+
corr_diffs = Matrix.rows(iobjs.map { |iobj| iobj.getCorrectedVectorDifferenceBetweenChannels(reference_channel, in_situ_corr_second_channel).toArray })
|
44
|
+
expt_diffs = Matrix.rows(iobjs.map { |iobj| iobj.getCorrectedVectorDifferenceBetweenChannels(reference_channel, correction_channel).toArray })
|
45
|
+
|
46
|
+
bslf = BisquareLinearFit.new
|
47
|
+
bslf.disableIntercept if disable_intercept?
|
48
|
+
all_parameters = 0.upto(corr_diffs.column_size - 1).collect do |i|
|
49
|
+
bslf.fit_rb(corr_diffs.column(i), expt_diffs.column(i)).toArray
|
50
|
+
end
|
51
|
+
|
52
|
+
self.corr_parameters = all_parameters.transpose
|
53
|
+
end
|
54
|
+
|
55
|
+
def apply(iobjs)
|
56
|
+
corrected_differences = iobjs.map do |iobj|
|
57
|
+
corr_diff = iobj.getCorrectedVectorDifferenceBetweenChannels(reference_channel, in_situ_corr_second_channel).toArray.to_a
|
58
|
+
expt_diff = iobj.getCorrectedVectorDifferenceBetweenChannels(reference_channel, correction_channel).toArray.to_a
|
59
|
+
correction = (corr_diff.ewise * corr_parameters[0]).ewise + corr_parameters[1]
|
60
|
+
Vector.elements(expt_diff.ewise - correction, false)
|
61
|
+
end
|
62
|
+
|
63
|
+
corrected_differences
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -25,17 +25,15 @@
|
|
25
25
|
#++
|
26
26
|
|
27
27
|
require 'cicada/mutable_matrix'
|
28
|
-
|
29
28
|
require 'cicada/correction/correction'
|
29
|
+
require 'cicada/correction/in_situ_correction'
|
30
30
|
|
31
31
|
require 'ostruct'
|
32
32
|
require 'logger'
|
33
33
|
|
34
34
|
require 'pqueue'
|
35
|
-
|
36
35
|
require 'facets/enumerable/ewise'
|
37
36
|
require 'facets/math/mean'
|
38
|
-
|
39
37
|
require 'rimageanalysistools/fitting/bisquare_linear_fit'
|
40
38
|
require 'rimageanalysistools/thread_queue'
|
41
39
|
|
@@ -73,7 +71,6 @@ module Cicada
|
|
73
71
|
@logger = Logger.new(STDOUT)
|
74
72
|
end
|
75
73
|
|
76
|
-
|
77
74
|
##
|
78
75
|
# Creates a RealVector (org.apache.commons.math3.linear.RealVector) that is a copy of
|
79
76
|
# the contents of the supplied vector.
|
@@ -83,20 +80,13 @@ module Cicada
|
|
83
80
|
# @return [RealVector] the commons math RealVector containing the same elements
|
84
81
|
#
|
85
82
|
def self.convert_to_realvector(vec)
|
86
|
-
|
87
83
|
conv = ArrayRealVector.new(vec.size, 0.0)
|
88
|
-
|
89
84
|
vec.each_with_index do |e, i|
|
90
|
-
|
91
85
|
conv.setEntry(i, e)
|
92
|
-
|
93
86
|
end
|
94
|
-
|
95
87
|
conv
|
96
|
-
|
97
88
|
end
|
98
89
|
|
99
|
-
|
100
90
|
##
|
101
91
|
# Generates a correction from a specified array of image objects.
|
102
92
|
#
|
@@ -105,86 +95,58 @@ module Cicada
|
|
105
95
|
# @return [Correction] the correction generated from the input objects
|
106
96
|
#
|
107
97
|
def generate_correction(iobjs)
|
108
|
-
|
109
98
|
#TODO refactor into smaller chunks
|
110
|
-
|
111
99
|
ref_ch = parameters[:reference_channel].to_i
|
112
100
|
corr_ch = parameters[:channel_to_correct].to_i
|
113
|
-
|
114
101
|
unless parameters[:determine_correction] then
|
115
|
-
|
116
102
|
return Correction.read_from_file(FileInteraction.correction_filename(parameters))
|
117
|
-
|
118
103
|
end
|
119
104
|
|
120
105
|
correction_x = []
|
121
106
|
correction_y = []
|
122
107
|
correction_z = []
|
123
|
-
|
124
108
|
distance_cutoffs = MVector.zero(iobjs.size)
|
125
109
|
|
126
110
|
iobjs.each_with_index do |obj, ind|
|
127
|
-
|
128
111
|
obj_pos = obj.getPositionForChannel(ref_ch)
|
129
|
-
|
130
|
-
distances_to_objects = iobjs.map { |obj2| obj2.getPositionForChannel(ref_ch).subtract(obj_pos).getNorm }
|
131
|
-
|
112
|
+
distances_to_objects = iobjs.map { |obj2| obj2.getPositionForChannel(ref_ch).subtract(obj_pos).getNorm }
|
132
113
|
pq = PQueue.new
|
133
|
-
|
134
114
|
np = @parameters[:num_points].to_i
|
135
115
|
|
136
116
|
distances_to_objects.each do |d|
|
137
|
-
|
138
117
|
if pq.size < np + 1 then
|
139
|
-
|
140
118
|
pq.push d
|
141
|
-
|
142
119
|
elsif d < pq.top then
|
143
|
-
|
144
120
|
pq.pop
|
145
121
|
pq.push d
|
146
|
-
|
147
122
|
end
|
148
|
-
|
149
123
|
end
|
150
124
|
|
151
|
-
|
152
125
|
first_exclude = pq.pop
|
153
|
-
|
154
126
|
last_dist = pq.pop
|
155
|
-
|
156
127
|
distance_cutoff = (last_dist + first_exclude)/2.0
|
157
|
-
|
158
128
|
distance_cutoffs[ind] = distance_cutoff
|
159
129
|
|
160
130
|
objs_ind_to_fit = (0...iobjs.size).select { |i| distances_to_objects[i] < distance_cutoff }
|
161
|
-
|
162
131
|
objs_to_fit = iobjs.values_at(*objs_ind_to_fit)
|
163
132
|
|
164
133
|
diffs_to_fit = MMatrix[*objs_to_fit.map { |e| e.getVectorDifferenceBetweenChannels(ref_ch, corr_ch).toArray }]
|
165
134
|
x_to_fit = objs_to_fit.map { |e| e.getPositionForChannel(ref_ch).getEntry(0) }
|
166
135
|
y_to_fit = objs_to_fit.map { |e| e.getPositionForChannel(ref_ch).getEntry(1) }
|
167
|
-
|
168
136
|
x = Vector[*x_to_fit.map { |e| e - obj_pos.getEntry(0) }]
|
169
137
|
y = Vector[*y_to_fit.map { |e| e - obj_pos.getEntry(1) }]
|
170
138
|
|
171
139
|
correction_parameters = Matrix.columns([MVector.unit(objs_to_fit.size), x, y, x.map { |e| e**2 }, y.map { |e| e**2 }, x.map2(y) { |ex, ey| ex*ey }])
|
172
|
-
|
173
140
|
cpt = correction_parameters.transpose
|
174
|
-
|
175
141
|
cpt_cp = cpt * correction_parameters
|
176
|
-
|
177
142
|
cpt_cp_lup = cpt_cp.lup
|
178
143
|
|
179
144
|
correction_x << cpt_cp_lup.solve(cpt * diffs_to_fit.column(0))
|
180
145
|
correction_y << cpt_cp_lup.solve(cpt * diffs_to_fit.column(1))
|
181
146
|
correction_z << cpt_cp_lup.solve(cpt * diffs_to_fit.column(2))
|
182
|
-
|
183
|
-
|
184
147
|
end
|
185
148
|
|
186
149
|
Correction.new(correction_x, correction_y, correction_z, distance_cutoffs, iobjs, ref_ch, corr_ch)
|
187
|
-
|
188
150
|
end
|
189
151
|
|
190
152
|
##
|
@@ -196,9 +158,7 @@ module Cicada
|
|
196
158
|
# @return [Vector] the vector scaled to physical units (by parameter naming convention, in nm)
|
197
159
|
#
|
198
160
|
def apply_scale(vec)
|
199
|
-
|
200
161
|
vec.map2(@pixel_to_distance_conversions) { |e1, e2| e1*e2 }
|
201
|
-
|
202
162
|
end
|
203
163
|
|
204
164
|
##
|
@@ -211,46 +171,28 @@ module Cicada
|
|
211
171
|
# wavelengths for each image object provided.
|
212
172
|
#
|
213
173
|
def apply_correction(c, iobjs)
|
214
|
-
|
215
174
|
ref_ch = @parameters[:reference_channel].to_i
|
216
175
|
corr_ch = @parameters[:channel_to_correct].to_i
|
217
|
-
|
218
176
|
vec_diffs = iobjs.map { |e| e.getVectorDifferenceBetweenChannels(ref_ch, corr_ch) }
|
219
|
-
|
220
177
|
vec_diffs.map! { |e| apply_scale(Vector[*e.toArray]) }
|
221
|
-
|
222
178
|
corrected_vec_diffs = []
|
223
179
|
|
224
|
-
if
|
225
|
-
|
180
|
+
if c.nil? then
|
181
|
+
corrected_vec_diffs = vec_diffs
|
182
|
+
else
|
226
183
|
iobjs.each do |iobj|
|
227
|
-
|
228
184
|
begin
|
229
|
-
|
230
185
|
corrected_vec_diffs << correct_single_object(c, iobj, ref_ch, corr_ch)
|
231
|
-
|
232
186
|
iobj.setCorrectionSuccessful(true)
|
233
|
-
|
234
187
|
rescue UnableToCorrectError => e
|
235
|
-
|
236
188
|
iobj.setCorrectionSuccessful(false)
|
237
|
-
|
238
189
|
end
|
239
|
-
|
240
190
|
end
|
241
|
-
|
242
|
-
corrected_vec_diffs.map! { |e| apply_scale(e) }
|
243
|
-
|
244
|
-
else
|
245
|
-
|
246
|
-
corrected_vec_diffs = vec_diffs
|
247
|
-
|
191
|
+
corrected_vec_diffs.map! { |e| apply_scale(e) }
|
248
192
|
end
|
249
|
-
|
193
|
+
|
250
194
|
print_distance_components(vec_diffs, corrected_vec_diffs)
|
251
|
-
|
252
195
|
corrected_vec_diffs.map { |e| e.norm }
|
253
|
-
|
254
196
|
end
|
255
197
|
|
256
198
|
##
|
@@ -262,32 +204,23 @@ module Cicada
|
|
262
204
|
# @return [void]
|
263
205
|
#
|
264
206
|
def print_distance_components(vec_diffs, corrected_vec_diffs)
|
265
|
-
|
266
207
|
mean_uncorr_vec = [0.0, 0.0, 0.0]
|
267
|
-
|
268
208
|
vec_diffs.each do |e|
|
269
|
-
|
270
209
|
mean_uncorr_vec = mean_uncorr_vec.ewise + e.to_a
|
271
|
-
|
272
210
|
end
|
273
211
|
|
274
212
|
mean_corr_vec = [0.0, 0.0, 0.0]
|
275
|
-
|
276
213
|
corrected_vec_diffs.each do |e|
|
277
|
-
|
278
214
|
mean_corr_vec = mean_corr_vec.ewise + e.to_a
|
279
|
-
|
280
215
|
end
|
281
216
|
|
282
217
|
mean_uncorr_vec.map! { |e| e / vec_diffs.length }
|
283
|
-
|
284
218
|
mean_corr_vec.map! { |e| e / corrected_vec_diffs.length }
|
285
219
|
|
286
220
|
self.logger.info("mean components uncorrected: [#{mean_uncorr_vec.join(', ')}]")
|
287
221
|
self.logger.info("mean distance uncorrected: #{Vector[*mean_uncorr_vec].norm}")
|
288
222
|
self.logger.info("mean components corrected: [#{mean_corr_vec.join(', ')}]")
|
289
223
|
self.logger.info("mean distance corrected: #{Vector[*mean_corr_vec].norm}")
|
290
|
-
|
291
224
|
end
|
292
225
|
|
293
226
|
##
|
@@ -301,19 +234,13 @@ module Cicada
|
|
301
234
|
# @return [Vector] the corrected (x,y,z) vector difference between the two channels
|
302
235
|
#
|
303
236
|
def correct_single_object(c, iobj, ref_ch, corr_ch)
|
304
|
-
|
305
237
|
corr = c.correct_position(iobj.getPositionForChannel(ref_ch).getEntry(0), iobj.getPositionForChannel(corr_ch).getEntry(1))
|
306
|
-
|
307
238
|
if parameters[:invert_z_axis] then
|
308
|
-
|
309
239
|
corr.setEntry(2, -1.0*corr.getEntry(2))
|
310
|
-
|
311
240
|
end
|
312
241
|
|
313
242
|
iobj.applyCorrectionVectorToChannel(corr_ch, PositionCorrector.convert_to_realvector(corr))
|
314
|
-
|
315
243
|
Vector.elements(iobj.getCorrectedVectorDifferenceBetweenChannels(ref_ch, corr_ch).toArray)
|
316
|
-
|
317
244
|
end
|
318
245
|
|
319
246
|
##
|
@@ -322,11 +249,8 @@ module Cicada
|
|
322
249
|
# @return @see #generate_in_situ_correction_from_iobjs
|
323
250
|
#
|
324
251
|
def generate_in_situ_correction
|
325
|
-
|
326
252
|
iobjs_for_in_situ_corr = FileInteraction.read_in_situ_corr_data(@parameters)
|
327
|
-
|
328
253
|
generate_in_situ_correction_from_iobjs(iobjs_for_in_situ_corr)
|
329
|
-
|
330
254
|
end
|
331
255
|
|
332
256
|
##
|
@@ -335,64 +259,28 @@ module Cicada
|
|
335
259
|
# @param [Array<ImageObject>] an array containing the image objects from which the in situ
|
336
260
|
# correction will be generated
|
337
261
|
#
|
338
|
-
# @return [
|
339
|
-
#
|
340
|
-
# dimension. The intercept will be zero if disabled in the parameter file.
|
262
|
+
# @return [InSituCorrection] an InSituCorrection object containing the necessary information
|
263
|
+
# to perform the correction.
|
341
264
|
#
|
342
265
|
def generate_in_situ_correction_from_iobjs(iobjs_for_in_situ_corr)
|
343
|
-
|
344
266
|
ref_ch = @parameters[:reference_channel].to_i
|
345
267
|
corr_ch = @parameters[:channel_to_correct].to_i
|
346
268
|
cicada_ch = @parameters[:in_situ_aberr_corr_channel]
|
347
269
|
|
348
|
-
|
349
|
-
expt_diffs = Matrix.rows(iobjs_for_in_situ_corr.map { |iobj| iobj.getCorrectedVectorDifferenceBetweenChannels(ref_ch, corr_ch).toArray })
|
350
|
-
|
351
|
-
bslf = BisquareLinearFit.new
|
352
|
-
|
353
|
-
bslf.disableIntercept if @parameters[:disable_in_situ_corr_constant_offset]
|
354
|
-
|
355
|
-
all_parameters = 0.upto(corr_diffs.column_size - 1).collect do |i|
|
356
|
-
|
357
|
-
bslf.fit_rb(corr_diffs.column(i), expt_diffs.column(i)).toArray
|
358
|
-
|
359
|
-
end
|
360
|
-
|
361
|
-
all_parameters
|
362
|
-
|
270
|
+
InSituCorrection.new(ref_ch, cicada_ch, corr_ch, iobjs_for_in_situ_corr, @parameters[:disable_in_situ_corr_constant_offset])
|
363
271
|
end
|
364
272
|
|
365
273
|
##
|
366
274
|
# Applies an in situ aberration correction to an array of image objects.
|
367
275
|
#
|
368
276
|
# @param [Enumerable<ImageObject>] iobjs the objects to be corrected
|
369
|
-
# @param [
|
370
|
-
#
|
371
|
-
#
|
277
|
+
# @param [InSituCorrection] isc the in situ correction object.
|
278
|
+
#
|
372
279
|
# @return [Array< Array <Numeric> >] an array of the corrected vector distance between
|
373
280
|
# wavelengths for each image object being corrected.
|
374
281
|
#
|
375
|
-
def apply_in_situ_correction(iobjs,
|
376
|
-
|
377
|
-
corr_params = corr_params.transpose
|
378
|
-
|
379
|
-
ref_ch = @parameters[:reference_channel].to_i
|
380
|
-
corr_ch = @parameters[:channel_to_correct].to_i
|
381
|
-
cicada_ch = @parameters[:in_situ_aberr_corr_channel]
|
382
|
-
|
383
|
-
corrected_differences = iobjs.map do |iobj|
|
384
|
-
|
385
|
-
corr_diff = iobj.getCorrectedVectorDifferenceBetweenChannels(ref_ch, cicada_ch).toArray.to_a
|
386
|
-
expt_diff = iobj.getCorrectedVectorDifferenceBetweenChannels(ref_ch, corr_ch).toArray.to_a
|
387
|
-
|
388
|
-
correction = (corr_diff.ewise * corr_params[0]).ewise + corr_params[1]
|
389
|
-
|
390
|
-
Vector.elements(expt_diff.ewise - correction, false)
|
391
|
-
|
392
|
-
end
|
393
|
-
|
394
|
-
corrected_differences
|
395
|
-
|
282
|
+
def apply_in_situ_correction(iobjs, isc)
|
283
|
+
isc.apply(iobjs)
|
396
284
|
end
|
397
285
|
|
398
286
|
##
|
@@ -404,96 +292,58 @@ module Cicada
|
|
404
292
|
# @return [Float] the (3d) TRE
|
405
293
|
#
|
406
294
|
def determine_tre(iobjs)
|
407
|
-
|
408
295
|
ref_ch = @parameters[:reference_channel].to_i
|
409
296
|
corr_ch = @parameters[:channel_to_correct].to_i
|
410
|
-
|
411
297
|
results = []
|
412
|
-
|
413
298
|
max_threads = 1
|
414
|
-
|
415
299
|
if @parameters[:max_threads]
|
416
300
|
max_threads = @parameters[:max_threads].to_i
|
417
301
|
end
|
418
302
|
|
419
303
|
tq = Executors.newFixedThreadPool(max_threads)
|
420
|
-
|
421
304
|
mut = Mutex.new
|
422
305
|
|
423
306
|
iobjs.each_with_index do |iobj, i|
|
424
|
-
|
425
307
|
RImageAnalysisTools::ThreadQueue.new_scope_with_vars(iobj, iobjs, i) do |obj, objs, ii|
|
426
|
-
|
427
308
|
tq.submit do
|
428
|
-
|
429
309
|
self.logger.debug("Calculating TRE. Progress: #{ii} of #{objs.length}") if ii.modulo(10) == 0
|
430
|
-
|
431
310
|
temp_objs = objs.select { |e| e != obj }
|
432
|
-
|
433
311
|
c = generate_correction(temp_objs)
|
434
|
-
|
435
312
|
pos = obj.getPositionForChannel(ref_ch)
|
436
|
-
|
437
313
|
result = OpenStruct.new
|
438
|
-
|
439
314
|
begin
|
440
|
-
|
441
315
|
corr = c.correct_position(pos.getEntry(0), pos.getEntry(1))
|
442
|
-
|
443
316
|
result.success = true
|
444
|
-
|
445
317
|
tre_vec = Vector[*obj.getVectorDifferenceBetweenChannels(ref_ch, corr_ch).toArray] - corr
|
446
|
-
|
447
318
|
tre_vec = tre_vec.map2(@pixel_to_distance_conversions) { |e1, e2| e1*e2 }
|
448
|
-
|
449
319
|
result.tre = tre_vec.norm
|
450
|
-
|
451
320
|
result.tre_xy = Math.hypot(tre_vec[0], tre_vec[1])
|
452
|
-
|
453
321
|
rescue UnableToCorrectError => e
|
454
|
-
|
455
322
|
result.success = false
|
456
|
-
|
457
323
|
end
|
458
324
|
|
459
325
|
mut.synchronize do
|
460
|
-
|
461
326
|
results << result
|
462
|
-
|
463
327
|
end
|
464
328
|
|
465
329
|
result
|
466
|
-
|
467
330
|
end
|
468
|
-
|
469
331
|
end
|
470
|
-
|
471
332
|
end
|
472
333
|
|
473
334
|
tq.shutdown
|
474
|
-
|
475
335
|
until tq.isTerminated do
|
476
|
-
|
477
336
|
sleep 0.4
|
478
|
-
|
479
337
|
end
|
480
338
|
|
481
339
|
tre_values = results
|
482
|
-
|
483
340
|
tre_values.select! { |e| e.success }
|
484
|
-
|
485
341
|
tre_3d = Math.mean(tre_values) { |e| e.tre }
|
486
|
-
|
487
342
|
tre_2d = Math.mean(tre_values) { |e| e.tre_xy }
|
488
|
-
|
489
343
|
self.logger.info("TRE: #{tre_3d}")
|
490
344
|
self.logger.info("X-Y TRE: #{tre_2d}")
|
491
345
|
|
492
346
|
tre_3d
|
493
|
-
|
494
347
|
end
|
495
|
-
|
496
348
|
end
|
497
|
-
|
498
349
|
end
|
499
|
-
|