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
@@ -0,0 +1,30 @@
|
|
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
|
+
module Cicada
|
24
|
+
##
|
25
|
+
# An error indicating that a position cannot be corrected (possibly due to incomplete
|
26
|
+
# coverage in the correction dataset).
|
27
|
+
class UnableToCorrectError < StandardError
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -55,28 +55,18 @@ module Cicada
|
|
55
55
|
# image objects.
|
56
56
|
#
|
57
57
|
def self.serialize_image_objects(image_objects)
|
58
|
-
|
59
58
|
doc = REXML::Document.new
|
60
|
-
|
61
|
-
|
62
59
|
doc.add_element "root"
|
63
60
|
|
64
61
|
image_objects.each do |iobj|
|
65
|
-
|
66
62
|
in_doc = REXML::Document.new iobj.writeToXMLString
|
67
|
-
|
68
63
|
in_doc.root.elements[1, "serialized_form"].text = Base64.encode64(Marshal.dump(iobj))
|
69
|
-
|
70
64
|
doc.root.add in_doc.elements[1,"image_object"]
|
71
|
-
|
72
65
|
end
|
73
66
|
|
74
67
|
output = ""
|
75
|
-
|
76
68
|
doc.write(output, 2)
|
77
|
-
|
78
69
|
output
|
79
|
-
|
80
70
|
end
|
81
71
|
|
82
72
|
##
|
@@ -89,15 +79,7 @@ module Cicada
|
|
89
79
|
# @return [ImageObject] the encoded ImageObject
|
90
80
|
#
|
91
81
|
def self.image_object_from_bytes(bin_data)
|
92
|
-
|
93
82
|
Marshal.load(bin_data)
|
94
|
-
|
95
|
-
#j_bytes = bin_data.to_java_bytes
|
96
|
-
|
97
|
-
#oi = Java::java.io.ObjectInputStream.new(Java::java.io.ByteArrayInputStream.new(j_bytes))
|
98
|
-
|
99
|
-
#oi.readObject
|
100
|
-
|
101
83
|
end
|
102
84
|
|
103
85
|
##
|
@@ -109,23 +91,16 @@ module Cicada
|
|
109
91
|
# @return [Array<ImageObject>] the image objects encoded in the string
|
110
92
|
#
|
111
93
|
def self.unserialize_image_objects(data)
|
112
|
-
|
113
94
|
objs = []
|
114
|
-
|
115
95
|
doc = REXML::Document.new data
|
116
96
|
|
117
97
|
doc.elements.each("*/image_object/serialized_form") do |el|
|
118
|
-
|
119
98
|
bin_data = Base64.decode64(el.text)
|
120
|
-
|
121
99
|
objs << image_object_from_bytes(bin_data)
|
122
|
-
|
123
100
|
end
|
124
101
|
|
125
102
|
objs
|
126
|
-
|
127
103
|
end
|
128
|
-
|
129
104
|
end
|
130
105
|
|
131
106
|
|
@@ -133,7 +108,6 @@ module Cicada
|
|
133
108
|
# A collection of methods for interacting with input and output files for cicada.
|
134
109
|
#
|
135
110
|
class FileInteraction
|
136
|
-
|
137
111
|
# parameters required by the methods in this class
|
138
112
|
REQUIRED_PARAMETERS = [:dirname_set, :basename_set, :mask_relative_dirname, :mask_extra_extension, :data_directory, :correction_date, :output_positions_to_directory]
|
139
113
|
|
@@ -155,7 +129,6 @@ module Cicada
|
|
155
129
|
# separator used in the parameter file for multiple files, directories, etc.
|
156
130
|
MULTI_NAME_SEP = ","
|
157
131
|
|
158
|
-
|
159
132
|
##
|
160
133
|
# Loads an image from the specified file.
|
161
134
|
#
|
@@ -164,12 +137,9 @@ module Cicada
|
|
164
137
|
# @return [ReadOnlyImage] the image at the specified filename
|
165
138
|
#
|
166
139
|
def self.load_image(image_fn)
|
167
|
-
|
168
140
|
RImageAnalysisTools.get_image(image_fn)
|
169
|
-
|
170
141
|
end
|
171
142
|
|
172
|
-
|
173
143
|
##
|
174
144
|
# Gets the filename to which / from which image object positions will be written /
|
175
145
|
# read from a parameter dictionary.
|
@@ -182,7 +152,6 @@ module Cicada
|
|
182
152
|
File.expand_path(p[:basename_set].split(MULTI_NAME_SEP)[0] + POS_XML_EXTENSION, dir)
|
183
153
|
end
|
184
154
|
|
185
|
-
|
186
155
|
##
|
187
156
|
# Gets the filename to which human-friendly-formatted object positions will be written.
|
188
157
|
#
|
@@ -195,7 +164,6 @@ module Cicada
|
|
195
164
|
File.expand_path(p[:basename_set].split(MULTI_NAME_SEP)[0] + POS_HUMAN_EXTENSION, dir)
|
196
165
|
end
|
197
166
|
|
198
|
-
|
199
167
|
##
|
200
168
|
# Gets the filename of data to use for in situ correction from a parameter dictionary.
|
201
169
|
#
|
@@ -205,7 +173,7 @@ module Cicada
|
|
205
173
|
# @return [String] the absolute path to the in situ correction data file
|
206
174
|
#
|
207
175
|
def self.in_situ_corr_data_filename(p)
|
208
|
-
dir = [:data_directory]
|
176
|
+
dir = p[:data_directory]
|
209
177
|
File.expand_path(p[:in_situ_aberr_corr_basename_set].split(MULTI_NAME_SEP)[0] + POS_XML_EXTENSION, dir)
|
210
178
|
end
|
211
179
|
|
@@ -229,15 +197,11 @@ module Cicada
|
|
229
197
|
# @return [Array<ImageObject>] the image objects contained in the file.
|
230
198
|
#
|
231
199
|
def self.unserialize_position_data_file(fn)
|
232
|
-
|
233
200
|
data_str = nil
|
234
|
-
|
235
201
|
File.open(fn) do |f|
|
236
202
|
data_str = f.read
|
237
203
|
end
|
238
|
-
|
239
204
|
Serialization.unserialize_image_objects(data_str)
|
240
|
-
|
241
205
|
end
|
242
206
|
|
243
207
|
##
|
@@ -248,11 +212,8 @@ module Cicada
|
|
248
212
|
# @return [Array<ImageObject>] the image objects associated with the analysis
|
249
213
|
#
|
250
214
|
def self.read_position_data(p)
|
251
|
-
|
252
215
|
fn = FileInteraction.position_data_filename(p)
|
253
|
-
|
254
216
|
FileInteraction.unserialize_position_data_file(fn)
|
255
|
-
|
256
217
|
end
|
257
218
|
|
258
219
|
##
|
@@ -265,11 +226,8 @@ module Cicada
|
|
265
226
|
# @return [Array<ImageObject>] the image objects for in situ correction associated with the analysis
|
266
227
|
#
|
267
228
|
def self.read_in_situ_corr_data(p)
|
268
|
-
|
269
229
|
fn = FileInteraction.in_situ_corr_data_filename(p)
|
270
|
-
|
271
230
|
FileInteraction.unserialize_position_data_file(fn)
|
272
|
-
|
273
231
|
end
|
274
232
|
|
275
233
|
##
|
@@ -281,35 +239,22 @@ module Cicada
|
|
281
239
|
# which return each image's filename and its paired mask's filename respectively.
|
282
240
|
#
|
283
241
|
def self.list_files(p)
|
284
|
-
|
285
242
|
dirnames = p[:dirname_set].split(MULTI_NAME_SEP)
|
286
243
|
basenames = p[:basename_set].split(MULTI_NAME_SEP)
|
287
|
-
|
288
244
|
image_sets = []
|
289
245
|
|
290
246
|
dirnames.each do |d|
|
291
|
-
|
292
247
|
mask_dirname = File.join(d, p[:mask_relative_dirname])
|
293
|
-
|
294
248
|
Dir.foreach(d) do |f|
|
295
|
-
|
296
|
-
if basenames.any? { |e| f.match(e) } then
|
297
|
-
|
249
|
+
if basenames.any? { |e| f.match(e) } then
|
298
250
|
im = File.expand_path(f, d)
|
299
251
|
msk = File.expand_path(f + p[:mask_extra_extension], mask_dirname)
|
300
|
-
|
301
|
-
current = OpenStruct.new(image_fn: im, mask_fn: msk)
|
302
|
-
|
252
|
+
current = OpenStruct.new(image_fn: im, mask_fn: msk)
|
303
253
|
image_sets << current
|
304
|
-
|
305
254
|
end
|
306
|
-
|
307
255
|
end
|
308
|
-
|
309
|
-
end
|
310
|
-
|
256
|
+
end
|
311
257
|
image_sets
|
312
|
-
|
313
258
|
end
|
314
259
|
|
315
260
|
##
|
@@ -321,15 +266,10 @@ module Cicada
|
|
321
266
|
# @return [void]
|
322
267
|
#
|
323
268
|
def self.write_position_data(image_objects, p)
|
324
|
-
|
325
269
|
fn = position_data_filename(p)
|
326
|
-
|
327
270
|
write_position_data_file(image_objects,fn)
|
328
|
-
|
329
271
|
fn2 = human_friendly_position_data_filename(p)
|
330
|
-
|
331
|
-
write_human_friendly_position_data_file(image_objects, fn2)
|
332
|
-
|
272
|
+
write_human_friendly_position_data_file(image_objects, fn2)
|
333
273
|
end
|
334
274
|
|
335
275
|
##
|
@@ -341,13 +281,9 @@ module Cicada
|
|
341
281
|
# @return [void]
|
342
282
|
#
|
343
283
|
def self.write_position_data_file(image_objects, fn)
|
344
|
-
|
345
284
|
File.open(fn, 'w') do |f|
|
346
|
-
|
347
285
|
f.write(Serialization.serialize_image_objects(image_objects))
|
348
|
-
|
349
286
|
end
|
350
|
-
|
351
287
|
end
|
352
288
|
|
353
289
|
##
|
@@ -357,39 +293,25 @@ module Cicada
|
|
357
293
|
# @see write_position_data_file
|
358
294
|
#
|
359
295
|
def self.write_human_friendly_position_data_file(image_objects, fn)
|
360
|
-
|
361
296
|
CSV.open(fn, 'wb') do |csv|
|
362
|
-
|
363
297
|
obj = image_objects[0]
|
364
|
-
|
365
298
|
n_channels = obj.getFitParametersByChannel.size
|
366
|
-
|
367
299
|
headers = ["object_id"]
|
368
300
|
n_channels.times do |i|
|
369
301
|
headers.concat(["pos#{i}_x", "pos#{i}_y", "pos#{i}_z"])
|
370
302
|
end
|
371
|
-
|
372
303
|
csv << headers
|
373
304
|
|
374
305
|
image_objects.each do |im_obj|
|
375
|
-
|
376
306
|
row = [im_obj.getLabel]
|
377
|
-
|
378
307
|
n_channels.times do |i|
|
379
|
-
|
380
308
|
row.concat(im_obj.getPositionForChannel(i).toArray)
|
381
|
-
|
382
309
|
end
|
383
|
-
|
384
310
|
csv << row
|
385
|
-
|
386
311
|
end
|
387
|
-
|
388
312
|
end
|
389
|
-
|
390
313
|
end
|
391
314
|
|
392
|
-
|
393
315
|
##
|
394
316
|
# Gets the filename for storing/reading the correction based upon the supplied parameter dictionary.
|
395
317
|
#
|
@@ -398,12 +320,9 @@ module Cicada
|
|
398
320
|
# @return [String] the filename for the correction file.
|
399
321
|
#
|
400
322
|
def self.correction_filename(p)
|
401
|
-
|
402
323
|
dir = p[:data_directory]
|
403
324
|
fn = p[:correction_date]
|
404
|
-
|
405
325
|
File.expand_path(fn + CORR_XML_EXTENSION, dir)
|
406
|
-
|
407
326
|
end
|
408
327
|
|
409
328
|
##
|
@@ -415,25 +334,13 @@ module Cicada
|
|
415
334
|
# @return [void]
|
416
335
|
#
|
417
336
|
def self.write_differences(diffs, p)
|
418
|
-
|
419
337
|
dirname = p[:output_positions_to_directory]
|
420
|
-
|
421
338
|
fn = File.expand_path(p[:basename_set] + DIFFS_TXT_EXTENSION, dirname)
|
422
|
-
|
423
339
|
File.open(fn, 'w') do |f|
|
424
|
-
|
425
340
|
diffs.each do |d|
|
426
|
-
|
427
341
|
f.puts(d.to_s)
|
428
|
-
|
429
342
|
end
|
430
|
-
|
431
343
|
end
|
432
|
-
|
433
344
|
end
|
434
|
-
|
435
345
|
end
|
436
|
-
|
437
346
|
end
|
438
|
-
|
439
|
-
|
@@ -23,7 +23,6 @@
|
|
23
23
|
# * ***** END LICENSE BLOCK ***** */
|
24
24
|
|
25
25
|
require 'rimageanalysistools'
|
26
|
-
|
27
26
|
require 'facets/math/mean'
|
28
27
|
require 'facets/math/std'
|
29
28
|
|
@@ -34,7 +33,6 @@ module Cicada
|
|
34
33
|
# to some distribution.
|
35
34
|
#
|
36
35
|
class DistributionFitter
|
37
|
-
|
38
36
|
attr_accessor :parameters
|
39
37
|
|
40
38
|
##
|
@@ -44,9 +42,7 @@ module Cicada
|
|
44
42
|
# @param [ParameterDictionary, Hash] params a hash-like object containing the parameters
|
45
43
|
#
|
46
44
|
def initialize(params)
|
47
|
-
|
48
45
|
@parameters = params
|
49
|
-
|
50
46
|
end
|
51
47
|
|
52
48
|
##
|
@@ -63,7 +59,6 @@ module Cicada
|
|
63
59
|
def fit(objects, diffs)
|
64
60
|
nil
|
65
61
|
end
|
66
|
-
|
67
62
|
end
|
68
63
|
|
69
64
|
##
|
@@ -74,24 +69,20 @@ module Cicada
|
|
74
69
|
#
|
75
70
|
#
|
76
71
|
class P3DObjectiveFunction
|
77
|
-
|
78
72
|
include Java::edu.stanford.cfuller.imageanalysistools.fitting.ObjectiveFunction
|
79
73
|
|
80
74
|
##
|
81
75
|
# Constructs an empty P3DObjectiveFunction.
|
82
76
|
#
|
83
77
|
def initialize
|
84
|
-
|
85
78
|
@r = nil
|
86
79
|
@s = nil
|
87
80
|
@min_prob = nil
|
88
81
|
@use_min_prob = false
|
89
82
|
@should_fit_s = true
|
90
|
-
|
91
83
|
end
|
92
84
|
|
93
85
|
attr_accessor :r, :use_min_prob, :should_fit_s
|
94
|
-
|
95
86
|
attr_reader :s, :min_prob
|
96
87
|
|
97
88
|
##
|
@@ -117,10 +108,8 @@ module Cicada
|
|
117
108
|
# @return [void]
|
118
109
|
#
|
119
110
|
def min_prob=(min_prob)
|
120
|
-
|
121
111
|
@min_prob = min_prob
|
122
112
|
@use_min_prob = true
|
123
|
-
|
124
113
|
end
|
125
114
|
|
126
115
|
##
|
@@ -133,9 +122,7 @@ module Cicada
|
|
133
122
|
# @return [Float] the probability density at the given point
|
134
123
|
#
|
135
124
|
def p3d(r, m, s)
|
136
|
-
|
137
125
|
(Math.sqrt(2.0/Math::PI)*r/(2*m*s))*(Math.exp(-1 * (m-r)**2/(2*s**2)) - Math.exp( -1 * (m+r)**2/(2*s**2)))
|
138
|
-
|
139
126
|
end
|
140
127
|
|
141
128
|
##
|
@@ -148,47 +135,32 @@ module Cicada
|
|
148
135
|
# @return [Float] the negative log-likelihood of the data.
|
149
136
|
#
|
150
137
|
def evaluate(point)
|
151
|
-
|
152
138
|
point = point.toArray unless point.is_a? Array
|
153
|
-
|
154
139
|
m = point[0]
|
155
140
|
s = point[1]
|
156
141
|
s = @s unless @should_fit_s
|
157
|
-
|
158
142
|
return Float::MAX if (m < 0 or s < 0)
|
159
143
|
|
160
144
|
r.reduce(0.0) do |sum, ri|
|
161
|
-
|
162
145
|
temp_neg_log_p = -1.0*Math.log( p3d(ri, m, s))
|
163
|
-
|
164
|
-
if (@use_min_prob and temp_neg_log_p > @min_prob) then
|
165
|
-
|
146
|
+
if (@use_min_prob and temp_neg_log_p > @min_prob) then
|
166
147
|
sum + @min_prob
|
167
|
-
|
168
148
|
else
|
169
|
-
|
170
149
|
sum + temp_neg_log_p
|
171
|
-
|
172
150
|
end
|
173
|
-
|
174
|
-
end
|
175
|
-
|
151
|
+
end
|
176
152
|
end
|
177
|
-
|
178
153
|
end
|
179
|
-
|
180
154
|
|
181
155
|
##
|
182
156
|
# A distribution fitter that fits data to a P3D distribution.
|
183
157
|
#
|
184
158
|
class P3DFitter < DistributionFitter
|
185
|
-
|
186
159
|
# parameters required by the methods in this class
|
187
160
|
REQUIRED_PARAMETERS = []
|
188
161
|
|
189
162
|
# parmeters used but not required in this class or only required for optional functionality
|
190
163
|
OPTIONAL_PARAMETERS = [:robust_p3d_fit_cutoff]
|
191
|
-
|
192
164
|
|
193
165
|
##
|
194
166
|
# Fits the P3D mean- and standard-deviation-like parameters to the data.
|
@@ -199,36 +171,22 @@ module Cicada
|
|
199
171
|
# @return [Array] a two-element array containing the mean- and standard-deviation-like parameters.
|
200
172
|
#
|
201
173
|
def fit(objects, diffs)
|
202
|
-
|
203
174
|
of = P3DObjectiveFunction.new
|
204
|
-
|
205
175
|
of.r = diffs
|
206
|
-
|
207
176
|
tol = 1e-12
|
208
|
-
|
209
177
|
nmm = Java::edu.stanford.cfuller.imageanalysistools.fitting.NelderMeadMinimizer.new(tol)
|
210
|
-
|
211
178
|
initial_mean = Math.mean(diffs)
|
212
|
-
|
213
179
|
initial_width = Math.std(diffs)
|
214
|
-
|
215
180
|
starting_point = Java::org.apache.commons.math3.linear.ArrayRealVector.new(2, 0.0)
|
216
|
-
|
217
181
|
starting_point.setEntry(0, initial_mean)
|
218
182
|
starting_point.setEntry(1, initial_width)
|
219
|
-
|
220
|
-
if @parameters[:robust_p3d_fit_cutoff] then
|
221
|
-
|
183
|
+
if @parameters[:robust_p3d_fit_cutoff] then
|
222
184
|
of.min_prob= @parmaeters[:robust_p3d_fit_cutoff].to_f
|
223
|
-
|
224
185
|
end
|
225
186
|
|
226
187
|
nmm.optimize(of, starting_point).toArray.to_a
|
227
|
-
|
228
188
|
end
|
229
|
-
|
230
189
|
end
|
231
|
-
|
232
190
|
end
|
233
191
|
|
234
192
|
|