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.
@@ -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 @parameters[:correct_images] then
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 [Array< Array<Numeric> >] an array containing the x, y, and z corrections; each
339
- # correction is a 2-element array containing the slope and intercept for the fit in each
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
- corr_diffs = Matrix.rows(iobjs_for_in_situ_corr.map { |iobj| iobj.getCorrectedVectorDifferenceBetweenChannels(ref_ch, cicada_ch).toArray })
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 [Array< Array<Numeric> >] corr_params the in situ correction parameters (an array
370
- # for each dimension containing the correction's slope and intercept).
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, corr_params)
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
-