cicada 0.9.4-java → 0.9.5-java
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.
- 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
|
-
|