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



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/cicada/correction/correction.rb', line 79

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.



62
63
64
# File 'lib/cicada/correction/correction.rb', line 62

def correction_channel
  @correction_channel
end

#correction_xObject

Returns the value of attribute correction_x.



62
63
64
# File 'lib/cicada/correction/correction.rb', line 62

def correction_x
  @correction_x
end

#correction_yObject

Returns the value of attribute correction_y.



62
63
64
# File 'lib/cicada/correction/correction.rb', line 62

def correction_y
  @correction_y
end

#correction_zObject

Returns the value of attribute correction_z.



62
63
64
# File 'lib/cicada/correction/correction.rb', line 62

def correction_z
  @correction_z
end

#distance_cutoffsObject

Returns the value of attribute distance_cutoffs.



62
63
64
# File 'lib/cicada/correction/correction.rb', line 62

def distance_cutoffs
  @distance_cutoffs
end

#positions_for_correctionObject

Returns the value of attribute positions_for_correction.



62
63
64
# File 'lib/cicada/correction/correction.rb', line 62

def positions_for_correction
  @positions_for_correction
end

#reference_channelObject

Returns the value of attribute reference_channel.



62
63
64
# File 'lib/cicada/correction/correction.rb', line 62

def reference_channel
  @reference_channel
end

#treObject

Returns the value of attribute tre.



62
63
64
# File 'lib/cicada/correction/correction.rb', line 62

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.



183
184
185
186
187
188
189
190
191
192
# File 'lib/cicada/correction/correction.rb', line 183

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.



202
203
204
205
206
# File 'lib/cicada/correction/correction.rb', line 202

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.



219
220
221
222
223
224
# File 'lib/cicada/correction/correction.rb', line 219

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



233
234
235
236
237
238
239
# File 'lib/cicada/correction/correction.rb', line 233

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.



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/cicada/correction/correction.rb', line 292

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:



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/cicada/correction/correction.rb', line 251

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



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

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



151
152
153
154
155
156
# File 'lib/cicada/correction/correction.rb', line 151

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



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

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



99
100
101
102
103
# File 'lib/cicada/correction/correction.rb', line 99

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



163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/cicada/correction/correction.rb', line 163

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