Class: Cicada::Correction

Inherits:
Object
  • Object
show all
Defined in:
lib/cicada/correction/correction.rb

Overview

Stores data for a standard 3d high-resolution colocalization correction, including positions for a number of objects used for correction, and local quadratic fits of aberration near these objects. Can correct 2d positions based upon this data.

Constant Summary collapse

XML_STRINGS =

Strings used in XML elements and attributes when writing a correction to an XML format

{ correction_element: "correction",
correction_point_element: "point", 
n_points_attr: "n",
ref_channel_attr: "reference_channel",
corr_channel_attr: "correction_channel",
x_pos_attr: "x_position",
y_pos_attr: "y_position",
z_pos_attr: "z_position",
x_param_element: "x_dimension_parameters",
y_param_element: "y_dimension_parameters",
z_param_element: "z_dimension_parameters",
binary_data_element: "serialized_form",
encoding_attr: "encoding",
encoding_name: "base64"}
NUM_CORR_PARAM =

Number of parameters used for correction (6, as this is the number of parameters for a 2d quadratic fit)

6

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(c_x, c_y, c_z, distance_cutoffs, image_objects, reference_channel, correction_channel) ⇒ Correction

Constructs a new correction based on the supplied data

Parameters:

  • c_x (Array<MVector>)

    an array of mutable vectors each of which contains the parameters for the interpolating function centered at an image object used for correction in the x direction

  • c_y (Array<MVector>)

    an array of mutable vectors each of which contains the parameters for the interpolating function centered at an image object used for correction in the y direction

  • c_z (Array<MVector>)

    an array of mutable vectors each of which contains the parameters for the interpolating function centered at an image object used for correction in the z direction

  • distance_cutoffs (MVector)

    a mutable vector containing the distance to the farthest point used to generate the interpolating function for each image object

  • image_objects (Array<ImageObject>)

    the image objects used for correction

  • reference_channel (Integer)

    the reference channel relative to which others are corrected

  • correction_channel (Integer)

    the channel being corrected



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/cicada/correction/correction.rb', line 84

def initialize(c_x, c_y, c_z, distance_cutoffs, image_objects, reference_channel, correction_channel)

  @correction_x = c_x
  @correction_y = c_y
  @correction_z = c_z
  @reference_channel = reference_channel
  @correction_channel = correction_channel
  @distance_cutoffs = distance_cutoffs

  n_dims = 3

  @positions_for_correction = MMatrix.build(image_objects.size, n_dims) do |r, c|

    image_objects[r].getPositionForChannel(reference_channel).toArray[c]

  end

end

Instance Attribute Details

#correction_channelObject

Returns the value of attribute correction_channel.



67
68
69
# File 'lib/cicada/correction/correction.rb', line 67

def correction_channel
  @correction_channel
end

#correction_xObject

Returns the value of attribute correction_x.



67
68
69
# File 'lib/cicada/correction/correction.rb', line 67

def correction_x
  @correction_x
end

#correction_yObject

Returns the value of attribute correction_y.



67
68
69
# File 'lib/cicada/correction/correction.rb', line 67

def correction_y
  @correction_y
end

#correction_zObject

Returns the value of attribute correction_z.



67
68
69
# File 'lib/cicada/correction/correction.rb', line 67

def correction_z
  @correction_z
end

#distance_cutoffsObject

Returns the value of attribute distance_cutoffs.



67
68
69
# File 'lib/cicada/correction/correction.rb', line 67

def distance_cutoffs
  @distance_cutoffs
end

#positions_for_correctionObject

Returns the value of attribute positions_for_correction.



67
68
69
# File 'lib/cicada/correction/correction.rb', line 67

def positions_for_correction
  @positions_for_correction
end

#reference_channelObject

Returns the value of attribute reference_channel.



67
68
69
# File 'lib/cicada/correction/correction.rb', line 67

def reference_channel
  @reference_channel
end

#treObject

Returns the value of attribute tre.



67
68
69
# File 'lib/cicada/correction/correction.rb', line 67

def tre
  @tre
end

Class Method Details

.read_from_file(fn) ⇒ Correction

Reads a correction from a specified file containing an XML-encoded correction.

Parameters:

  • fn (String)

    the filename from which to read the correction

Returns:

  • (Correction)

    the correction contained in the file.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/cicada/correction/correction.rb', line 228

def self.read_from_file(fn)

  return nil unless File.exist?(fn)
  
  xml_str = ""

  File.open(fn) do |f|

    xml_str = f.read

  end

  read_from_xml(xml_str)

end

.read_from_xml(xml_str) ⇒ Correction

Reads a correction from an XML string.

Parameters:

  • xml_str (String)

    the XML string containing the information

Returns:

  • (Correction)

    the correction contained in the string.



252
253
254
255
256
257
258
259
260
# File 'lib/cicada/correction/correction.rb', line 252

def self.read_from_xml(xml_str)

  doc = REXML::Document.new xml_str

  bin_el = doc.root.elements[1, XML_STRINGS[:binary_data_element]]

  Marshal.load(Base64.decode64(bin_el.text))

end

Instance Method Details

#calculate_normalized_dists_to_centroids(x, y) ⇒ Vector

Calculates the 2d distances from a specified 2d point to the centroid of each of the image objects used for the correction.

Parameters:

  • x (Numeric)

    the x coordinate of the point

  • y (Numeric)

    the y coordinate of the point

Returns:

  • (Vector)

    the distance from the specified point to each image object.



273
274
275
276
277
278
279
280
281
282
283
# File 'lib/cicada/correction/correction.rb', line 273

def calculate_normalized_dists_to_centroids(x,y)

  dists_to_centroids = @positions_for_correction.column(0).map { |x0| (x0-x)**2 }

  dists_to_centroids += @positions_for_correction.column(1).map { |y0| (y0-y)**2 }

  dists_to_centroids = dists_to_centroids.map { |e| Math.sqrt(e) }

  dists_to_centroids.map2(@distance_cutoffs) { |e1, e2| e1/e2 }

end

#calculate_weights(x, y) ⇒ Vector

Calculates the weight of each local quadratic fit for correcting a specified point.

Parameters:

  • x (Numeric)

    the x coordinate of the point

  • y (Numeric)

    the y coordinate of the point

Returns:

  • (Vector)

    the weights for each local fit used for correction



292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/cicada/correction/correction.rb', line 292

def calculate_weights(x, y) 

  dist_ratio = calculate_normalized_dists_to_centroids(x,y)

  dist_ratio_mask = MVector.zero(dist_ratio.size)

  dist_ratio_mask = dist_ratio.map { |e| e <= 1 ? 1 : 0 }

  weights = dist_ratio.map { |e| -3*e**2 + 1 + 2*e**3 }

  weights.map2(dist_ratio_mask) { |e1, e2| e1*e2 }

end

#correct_position(x, y) ⇒ MVector

Calculates the correction for a specified position.

Parameters:

  • x (Numeric)

    the x coordinate of the point

  • y (Numeric)

    the y coordinate of the point

Returns:

  • (MVector)

    a mutable vector containing the correction in the x, y, and z dimensions for the specified position.



371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/cicada/correction/correction.rb', line 371

def correct_position(x, y)
 
  points = find_points_for_correction(x,y)

  x_corr = 0.0
  y_corr = 0.0
  z_corr = 0.0

  all_correction_parameters = MMatrix.columns([MVector.unit(points.x_vec.size), 
                                              points.x_vec, 
                                              points.y_vec, 
                                              points.x_vec.map { |e| e**2 }, 
                                              points.y_vec.map { |e| e**2 }, 
                                              points.x_vec.map2(points.y_vec) { |e1, e2| e1*e2 }])

 
  all_correction_parameters.row_size.times do |i|

    x_corr += all_correction_parameters.row(i).inner_product(points.cx.row(i))*points.weights[i]
    y_corr += all_correction_parameters.row(i).inner_product(points.cy.row(i))*points.weights[i]
    z_corr += all_correction_parameters.row(i).inner_product(points.cz.row(i))*points.weights[i]

  end

  sum_weights = points.weights.reduce(0.0) { |a,e| a + e }

  x_corr /= sum_weights
  y_corr /= sum_weights
  z_corr /= sum_weights

  MVector[x_corr, y_corr, z_corr]

end

#find_points_for_correction(x, y) ⇒ OpenStruct, ...

Selects the local fits and their associated image objects that are to be used for correcting a specified point (i.e. those fits with nonzero weight).

Parameters:

  • x (Numeric)

    the x coordinate of the point

  • y (Numeric)

    the y coordinate of the point

Returns:

  • (OpenStruct, #cx, #cy, #cz, #x_vec, #y_vec, #weights)

    an OpenStruct containing the selected fits for the x dimension, y dimension, and z dimension; the x and y positions of the selected image objects used for correction; and the weights of the fits

Raises:



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/cicada/correction/correction.rb', line 316

def find_points_for_correction(x,y)
  
  weights = calculate_weights(x,y)

  count_weights = weights.count { |e| e > 0 }

  raise UnableToCorrectError, "Incomplete coverage in correction dataset at (x,y) = (#{x}, #{y})." if count_weights == 0

  cx = MMatrix.zero(count_weights, @correction_x[0].size)
  cy = MMatrix.zero(count_weights, @correction_y[0].size)
  cz = MMatrix.zero(count_weights, @correction_z[0].size)
  
  x_vec = MVector.zero(count_weights)
  y_vec = MVector.zero(count_weights)

  kept_weights = MVector.zero(count_weights)

  kept_counter = 0

  weights.each_with_index do |w, i|

    if w > 0 then

      cx.replace_row(kept_counter, @correction_x[i])
      cy.replace_row(kept_counter, @correction_y[i])
      cz.replace_row(kept_counter, @correction_z[i])
      
      x_vec[kept_counter] = x - positions_for_correction[i,0]
      y_vec[kept_counter] = y - positions_for_correction[i,1]

      kept_weights[kept_counter] = weights[i]

      kept_counter += 1

    end

  end

  OpenStruct.new(cx: cx, 
                 cy: cy, 
                 cz: cz, 
                 x_vec: x_vec, 
                 y_vec: y_vec,
                 weights: kept_weights)

end

#write_all_correction_point_xml(correction_element) ⇒ void

This method returns an undefined value.

Writes all the points used for correction to XML within a supplied correction

XML element

Parameters:

  • correction_element (REXML::Element)

    the XML element representing the correction



129
130
131
132
133
134
135
136
137
# File 'lib/cicada/correction/correction.rb', line 129

def write_all_correction_point_xml(correction_element)

  @distance_cutoffs.each_with_index do |e,i|

    write_correction_point_xml(correction_element, i)

  end

end

#write_correction_binary_data_element(correction_element) ⇒ void

This method returns an undefined value.

Writes the internal binary representation of the correction into

an XML element.

Parameters:

  • correction_element (REXML::Element)

    the XML element representing the correction



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/cicada/correction/correction.rb', line 180

def write_correction_binary_data_element(correction_element)

  bd = correction_element.add_element XML_STRINGS[:binary_data_element]

  bd.attributes[XML_STRINGS[:encoding_attr]]= XML_STRINGS[:encoding_name]

  bin_data = Base64.encode64(Marshal.dump(self))

  bd.text = bin_data

end

#write_correction_point_xml(correction_element, i) ⇒ void

This method returns an undefined value.

Writes a single point used for correction to XML within a supplied correction

XML element

Parameters:

  • correction_element (REXML::Element)

    the XML element representing the correction

  • i (Integer)

    the index of the point to write



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/cicada/correction/correction.rb', line 149

def write_correction_point_xml(correction_element, i)

  cp = correction_element.add_element XML_STRINGS[:correction_point_element]

  cp.attributes[XML_STRINGS[:x_pos_attr]]= @positions_for_correction[i,0]
  cp.attributes[XML_STRINGS[:y_pos_attr]]= @positions_for_correction[i,1]
  cp.attributes[XML_STRINGS[:z_pos_attr]]= @positions_for_correction[i,2]

  point_dims_to_corr = {XML_STRINGS[:x_param_element] => @correction_x,
                        XML_STRINGS[:y_param_element] => @correction_y,
                        XML_STRINGS[:z_param_element] => @correction_z}


  point_dims_to_corr.each do |dim_el, corr_txt|

    p = cp.add_element dim_el

    p.text = corr_txt[i].to_a.join(", ")

  end

end

#write_to_file(fn) ⇒ void

This method returns an undefined value.

Writes the correction to a specified file in XML format.

Parameters:

  • fn (String)

    the filename to which to write the correction



110
111
112
113
114
115
116
117
118
# File 'lib/cicada/correction/correction.rb', line 110

def write_to_file(fn)
  
  File.open(fn, 'w') do |f|

    f.puts(write_to_xml)

  end

end

#write_to_xmlString

Writes the correction to a string in XML format

Returns:

  • (String)

    the correction data encoded as XML



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/cicada/correction/correction.rb', line 197

def write_to_xml
  
  doc = REXML::Document.new

  ce = doc.add_element XML_STRINGS[:correction_element]

  ce.attributes[XML_STRINGS[:n_points_attr]] = @distance_cutoffs.size

  ce.attributes[XML_STRINGS[:ref_channel_attr]] = @reference_channel

  ce.attributes[XML_STRINGS[:corr_channel_attr]] = @correction_channel
  
  write_all_correction_point_xml(ce)

  write_correction_binary_data_element(ce)

  doc_string = ""

  doc.write doc_string, 2

  doc_string

end