cicada 0.9.0-java

Sign up to get free protection for your applications and to get access to all the features.
data/bin/cicada ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # /* ***** BEGIN LICENSE BLOCK *****
5
+ # *
6
+ # * Copyright (c) 2013 Colin J. Fuller
7
+ # *
8
+ # * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # * of this software and associated documentation files (the Software), to deal
10
+ # * in the Software without restriction, including without limitation the rights
11
+ # * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # * copies of the Software, and to permit persons to whom the Software is
13
+ # * furnished to do so, subject to the following conditions:
14
+ # *
15
+ # * The above copyright notice and this permission notice shall be included in
16
+ # * all copies or substantial portions of the Software.
17
+ # *
18
+ # * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # * SOFTWARE.
25
+ # *
26
+ # * ***** END LICENSE BLOCK ***** */
27
+ #++
28
+
29
+ require 'cicada'
30
+
31
+ ##
32
+ # Start the analysis using the parameter file
33
+ # specified on the command line.
34
+ #
35
+ Cicada::CicadaMain.run_from_parameter_file(ARGV[0])
36
+
37
+
data/lib/cicada.rb ADDED
@@ -0,0 +1,31 @@
1
+ #--
2
+ # /* ***** BEGIN LICENSE BLOCK *****
3
+ # *
4
+ # * Copyright (c) 2013 Colin J. Fuller
5
+ # *
6
+ # * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # * of this software and associated documentation files (the Software), to deal
8
+ # * in the Software without restriction, including without limitation the rights
9
+ # * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # * copies of the Software, and to permit persons to whom the Software is
11
+ # * furnished to do so, subject to the following conditions:
12
+ # *
13
+ # * The above copyright notice and this permission notice shall be included in
14
+ # * all copies or substantial portions of the Software.
15
+ # *
16
+ # * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # * SOFTWARE.
23
+ # *
24
+ # * ***** END LICENSE BLOCK ***** */
25
+ #++
26
+
27
+ require 'rimageanalysistools'
28
+
29
+ require 'cicada/cicada_main'
30
+
31
+
@@ -0,0 +1,674 @@
1
+ #--
2
+ # /* ***** BEGIN LICENSE BLOCK *****
3
+ # *
4
+ # * Copyright (c) 2012 Colin J. Fuller
5
+ # *
6
+ # * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # * of this software and associated documentation files (the Software), to deal
8
+ # * in the Software without restriction, including without limitation the rights
9
+ # * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # * copies of the Software, and to permit persons to whom the Software is
11
+ # * furnished to do so, subject to the following conditions:
12
+ # *
13
+ # * The above copyright notice and this permission notice shall be included in
14
+ # * all copies or substantial portions of the Software.
15
+ # *
16
+ # * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # * SOFTWARE.
23
+ # *
24
+ # * ***** END LICENSE BLOCK ***** */
25
+ #++
26
+
27
+ require 'cicada/file_interaction'
28
+ require 'cicada/correction/correction'
29
+ require 'cicada/correction/position_corrector'
30
+ require 'cicada/fitting/p3d_fitter'
31
+ require 'ostruct'
32
+ require 'logger'
33
+
34
+
35
+ require 'rimageanalysistools'
36
+ require 'rimageanalysistools/image_shortcuts'
37
+ require 'rimageanalysistools/create_parameters'
38
+
39
+ require 'facets/math/sum'
40
+
41
+ require 'edu/stanford/cfuller/imageanalysistools/resources/common_methods'
42
+
43
+ java_import Java::edu.stanford.cfuller.imageanalysistools.filter.ImageSubtractionFilter
44
+ java_import Java::edu.stanford.cfuller.imageanalysistools.image.Histogram
45
+ java_import Java::edu.stanford.cfuller.imageanalysistools.fitting.GaussianImageObject
46
+ java_import Java::edu.stanford.cfuller.imageanalysistools.meta.parameters.ParameterDictionary
47
+
48
+ java_import Java::java.util.concurrent.Executors
49
+
50
+
51
+ module Cicada
52
+
53
+
54
+ ##
55
+ # This class is the main entry point for running 3D high-resolution colocalization and CICADA.
56
+ #
57
+ class CicadaMain
58
+
59
+ include IATScripting #TODO: find a better way to get at these methods.
60
+
61
+ # parameters required by the methods in this class
62
+ REQUIRED_PARAMETERS = [:dirname, :basename, :im_border_size, :half_z_size, :determine_correction, :pixelsize_nm, :z_sectionsize_nm, :num_wavelengths, :photons_per_greylevel]
63
+
64
+ # parmeters used but not required in this class or only required for optional functionality
65
+ OPTIONAL_PARAMETERS = [:precomputed_position_data, :max_threads, :darkcurrent_image, :residual_cutoff, :max_greylevel_cutoff, :distance_cutoff, :fit_error_cutoff, :determine_correction, :determine_tre, :output_positions_to_directory, :in_situ_aberr_corr_basename, :in_situ_aberr_corr_channel, :log_to_file, :log_detailed_messages]
66
+
67
+ attr_accessor :parameters, :failures, :logger
68
+
69
+ FAILURE_REASONS = {r2: "R^2 value", edge: "Too close to image edge", sat: "Saturated pixels", sep: "Separation between channels too large", err: "Fit error too large"}
70
+
71
+ ##
72
+ # Sets up the analysis from a parameter dictionary.
73
+ #
74
+ # @param [ParameterDictionary, Hash] p a parameter dictionary or other object with hash-like behavior
75
+ # containing all the parameters for the analysis.
76
+ #
77
+ def initialize(p)
78
+
79
+ @parameters = p
80
+
81
+ @parameters = RImageAnalysisTools.create_parameter_dictionary(p) unless @parameters.is_a? ParameterDictionary
82
+
83
+ @failures = {r2: 0, edge: 0, sat: 0, sep: 0, err: 0}
84
+
85
+ if @parameters[:darkcurrent_image] then
86
+
87
+ @dark_image = FileInteraction.load_image(@parameters[:darkcurrent_image])
88
+
89
+ end
90
+
91
+ set_up_logging
92
+
93
+ end
94
+
95
+
96
+ ##
97
+ # Load the position data from disk if this is requested in the specified parameters. If this
98
+ # has not been requested or if the position data file does not exist, returns nil.
99
+ #
100
+ # @return [Array<ImageObject>] the image objects, complete with their fitted positions, or nil if
101
+ # this should be recalculated or if the file cannot be found.
102
+ #
103
+ def load_position_data
104
+
105
+ if @parameters[:precomputed_position_data] and FileInteraction.position_file_exists?(@parameters) then
106
+
107
+ return FileInteraction.read_position_data(@parameters)
108
+
109
+ end
110
+
111
+ nil
112
+
113
+ end
114
+
115
+
116
+ ##
117
+ # Loads the image and mask from an image and mask pair and darkcurrent corrects the
118
+ # image if specified in the parameters.
119
+ #
120
+ # @param [OpenStruct, #image_fn, #mask_fn, #image=, #mask=] im_set An object that
121
+ # specified the filename of image and mask and can store the loaded image
122
+ # and mask. Should respond to #image_fn, #mask_fn, #image=, and #mask= for
123
+ # getting the filenames and setting the loaded images, respectively.
124
+ # @return [void]
125
+ #
126
+ def load_and_dark_correct_image(im_set)
127
+
128
+ im_set.image = FileInteraction.load_image(im_set.image_fn)
129
+ im_set.mask = FileInteraction.load_image(im_set.mask_fn)
130
+
131
+ if (@dark_image) then
132
+
133
+ im_set.image = im_set.image.writableInstance
134
+
135
+ isf = ImageSubtractionFilter.new
136
+
137
+ isf.setSubtractPlanarImage(true)
138
+
139
+ isf.setReferenceImage(@dark_image)
140
+ isf.apply(im_set.image)
141
+
142
+ end
143
+
144
+ end
145
+
146
+ ##
147
+ # Submits a single object to a thread queue for fitting.
148
+ #
149
+ # @param [ImageObject] obj the image object to fit
150
+ # @param [ExecutorService] queue the thread queue
151
+ #
152
+ # @return [void]
153
+ #
154
+ def submit_single_object(obj, queue)
155
+
156
+ queue.submit do
157
+
158
+ @logger.debug { "Processing object #{obj.getLabel}" }
159
+
160
+ obj.fitPosition(@parameters)
161
+
162
+ end
163
+
164
+ end
165
+
166
+ ##
167
+ # Fits all the image objects in a single supplied image.
168
+ #
169
+ # Does not check whether the fitting was successful.
170
+ # @param [OpenStruct, #image, #mask] im_set An object that references the image
171
+ # and the mask from which the objects will be fit. Should respond to #image and #mask.
172
+ # @return [Array<ImageObject>] an array containing all the image objects in the image
173
+ # (one per unique greylevel in the mask).
174
+ #
175
+ def fit_objects_in_single_image(im_set)
176
+
177
+ objs = []
178
+
179
+ load_and_dark_correct_image(im_set)
180
+
181
+ unless im_set.image and im_set.mask then
182
+
183
+ logger.error { "Unable to process image #{im_set.image_fn}." }
184
+
185
+ return objs
186
+
187
+ end
188
+
189
+ h = Histogram.new(im_set.mask)
190
+
191
+ max_threads = 1
192
+
193
+ if @parameters[:max_threads] then
194
+ max_threads = @parameters[:max_threads].to_i
195
+ end
196
+
197
+ thread_queue = Executors.newFixedThreadPool(max_threads)
198
+
199
+ 1.upto(h.getMaxValue) do |i|
200
+
201
+ obj = GaussianImageObject.new(i, image_shallow_copy(im_set.mask), image_shallow_copy(im_set.image), ParameterDictionary.new(@parameters))
202
+
203
+ obj.setImageID(im_set.image_fn)
204
+
205
+ objs << obj
206
+
207
+ end
208
+
209
+ objs.each do |obj|
210
+
211
+ submit_single_object(obj, thread_queue)
212
+
213
+ end
214
+
215
+ thread_queue.shutdown
216
+
217
+ until thread_queue.isTerminated do
218
+ sleep 0.4
219
+ end
220
+
221
+ objs
222
+
223
+ end
224
+
225
+
226
+ ##
227
+ # Checks whether the fitting was successful for a given object according to several criteria:
228
+ # whether the fitting finished without error, whether the R^2 value of the fit is above the
229
+ # cutoff, whether the object is too close to the image edges, whether the camera is saturated
230
+ # in the object, whether the separation between channels is above some cutoff, and whether the
231
+ # calculated fitting error is too large. Cutoffs for all these criteria are specified in the
232
+ # parameters file.
233
+ #
234
+ # @param [ImageObject] to_check the ImageObject to check for fitting success
235
+ # @return [Boolean] whether the fitting was successful by all criteria.
236
+ #
237
+ def check_fit(to_check)
238
+
239
+ checks = [:check_r2, :check_edges, :check_saturation, :check_separation, :check_error]
240
+
241
+ to_check.finishedFitting and checks.all? { |c| self.send(c, to_check) }
242
+
243
+ end
244
+
245
+
246
+ ##
247
+ # Checks whether the fit R^2 value is below the specified cutoff.
248
+ #
249
+ # @param (see #check_fit)
250
+ # @return [Boolean] whether the fitting was successful by this criterion.
251
+ #
252
+ def check_r2(to_check)
253
+
254
+ return true unless @parameters[:residual_cutoff]
255
+
256
+ to_check.getFitR2ByChannel.each do |r2|
257
+
258
+ if r2 < @parameters[:residual_cutoff].to_f then
259
+
260
+ @failures[:r2] += 1
261
+
262
+ @logger.debug { "check failed for object #{to_check.getLabel} R^2 = #{r2}" }
263
+
264
+ return false
265
+
266
+ end
267
+
268
+ end
269
+
270
+ true
271
+
272
+ end
273
+
274
+ ##
275
+ # Checks whether the fitted position is too close to the image edges.
276
+ # @param (see #check_fit)
277
+ # @return (see #check_r2)
278
+ #
279
+ def check_edges(to_check)
280
+
281
+ eps = 0.1
282
+
283
+ border_size = @parameters[:im_border_size].to_f
284
+ z_size = @parameters[:half_z_size].to_f
285
+
286
+ range_x = border_size...(to_check.getParent.getDimensionSizes[:x] - border_size)
287
+ range_y = border_size...(to_check.getParent.getDimensionSizes[:y] - border_size)
288
+ range_z = z_size...(to_check.getParent.getDimensionSizes[:z] - z_size)
289
+
290
+ to_check.getFitParametersByChannel.each do |fp|
291
+
292
+ x = fp.getPosition(ImageCoordinate::X)
293
+ y = fp.getPosition(ImageCoordinate::Y)
294
+ z = fp.getPosition(ImageCoordinate::Z)
295
+
296
+ ok = (range_x.include?(x) and range_y.include?(y) and range_z.include?(z))
297
+
298
+ unless ok then
299
+
300
+ @failures[:edge] += 1
301
+
302
+ @logger.debug { "check failed for object #{to_check.getLabel} position: #{x}, #{y}, #{z}" }
303
+
304
+ return false
305
+
306
+ end
307
+
308
+ end
309
+
310
+ true
311
+
312
+ end
313
+
314
+
315
+ ##
316
+ # Checks whether the camera has saturated in the object.
317
+ # @param (see #check_fit)
318
+ # @return (see #check_r2)
319
+ #
320
+ def check_saturation(to_check)
321
+
322
+ if @parameters[:max_greylevel_cutoff] then
323
+
324
+ to_check.boxImages
325
+
326
+ cutoff = @parameters[:max_greylevel_cutoff].to_f
327
+
328
+ to_check.getParent.each do |ic|
329
+
330
+ if to_check.getParent[ic] > cutoff then
331
+
332
+ to_check.unboxImages
333
+ @failures[:sat] += 1
334
+
335
+ @logger.debug { "check failed for object #{to_check.getLabel} greylevel: #{to_check.getParent[ic]}" }
336
+
337
+ return false
338
+
339
+ end
340
+
341
+ end
342
+
343
+ end
344
+
345
+ true
346
+
347
+ end
348
+
349
+ ##
350
+ # Checks whether the separation between channels is too large.
351
+ #
352
+ # Note that this check can significantly skew the distance measurements if the cutoff is too small.
353
+ # This remains here because occasionally closely spaced objects are fit as a single object and produce
354
+ # ridiculous values.
355
+ #
356
+ # @param (see #check_fit)
357
+ # @return (see #check_r2)
358
+ #
359
+ def check_separation(to_check)
360
+
361
+ if @parameters[:distance_cutoff] then
362
+
363
+ size_c = to_check.getFitParametersByChannel.size
364
+
365
+ xy_pixelsize_2 = @parameters[:pixelsize_nm].to_f**2
366
+
367
+ z_sectionsize_2 = @parameters[:z_sectionsize_nm].to_f**2
368
+
369
+ 0.upto(size_c-1) do |ci|
370
+ 0.upto(size_c-1) do |cj|
371
+
372
+ fp1 = to_check.getFitParametersByChannel.get(ci)
373
+ fp2 = to_check.getFitParametersByChannel.get(cj)
374
+
375
+ ijdist = xy_pixelsize_2 * (fp1.getPosition(ImageCoordinate::X) - fp2.getPosition(ImageCoordinate::X))**2 +
376
+ xy_pixelsize_2 * (fp1.getPosition(ImageCoordinate::Y) - fp2.getPosition(ImageCoordinate::Y))**2 +
377
+ z_sectionsize_2 * (fp1.getPosition(ImageCoordinate::Z) - fp2.getPosition(ImageCoordinate::Z))**2
378
+
379
+ ijdist = ijdist**0.5
380
+
381
+ if (ijdist > @parameters[:distance_cutoff].to_f) then
382
+
383
+ @failures[:sep] += 1
384
+ @logger.debug { "check failed for object #{to_check.getLabel} with distance: #{ijdist}" }
385
+
386
+ return false
387
+
388
+ end
389
+
390
+ end
391
+ end
392
+
393
+ end
394
+
395
+ true
396
+
397
+ end
398
+
399
+
400
+ ##
401
+ # Checks whether the caluclated fitting error (summed in quadrature over all wavelengths)
402
+ # is larger than a specified cutoff.
403
+ #
404
+ # @param (see #check_fit)
405
+ # @return (see #check_r2)
406
+ #
407
+ def check_error(to_check)
408
+
409
+ if @parameters[:fit_error_cutoff] then
410
+
411
+ total_error = 0
412
+
413
+ to_check.getFitErrorByChannel.each do |d|
414
+
415
+ total_error += d**2
416
+
417
+ end
418
+
419
+ total_error = total_error**0.5
420
+
421
+ if total_error > @parameters[:fit_error_cutoff].to_f or total_error.nan? then
422
+
423
+ @failures[:err] += 1
424
+
425
+ @logger.debug { "check failed for object #{to_check.getLabel} with total fitting error: #{total_error}" }
426
+
427
+ return false
428
+
429
+ end
430
+
431
+ end
432
+
433
+ true
434
+
435
+ end
436
+
437
+ ##
438
+ # Sets up a logger to either standard output or a file with appropriate detail level
439
+ # as specified in the parameters
440
+ #
441
+ # @return (void)
442
+ #
443
+ def set_up_logging
444
+
445
+ if @parameters[:log_to_file] then
446
+
447
+ @logger = Logger.new(@parameters[:log_to_file])
448
+
449
+ else
450
+
451
+ @logger = Logger.new(STDOUT)
452
+
453
+ end
454
+
455
+ if @parameters[:log_detailed_messages] then
456
+
457
+ @logger.sev_threshold = Logger::DEBUG
458
+
459
+ else
460
+
461
+ @logger.sev_threshold = Logger::INFO
462
+
463
+ end
464
+
465
+ end
466
+
467
+ ##
468
+ # Loads previously existing image objects for the current images or fits them anew
469
+ # if they don't exist or this is requested in the parameters.
470
+ #
471
+ # @return [Array<ImageObject>] The image objects that have been loaded or fit. Only
472
+ # successfully fit objects that have passed all checks are included.
473
+ #
474
+ def load_or_fit_image_objects
475
+
476
+ image_objects = load_position_data
477
+
478
+ unless image_objects then
479
+
480
+ image_objects = []
481
+
482
+ to_process = FileInteraction.list_files(@parameters)
483
+
484
+ to_process.each do |im_set|
485
+
486
+ objs = fit_objects_in_single_image(im_set)
487
+
488
+ objs.each do |o|
489
+
490
+ if check_fit(o) then
491
+
492
+ image_objects << o
493
+
494
+ end
495
+
496
+ o.nullifyImages
497
+
498
+ end
499
+
500
+ end
501
+
502
+ log_fitting_failures
503
+
504
+ end
505
+
506
+ image_objects
507
+
508
+ end
509
+
510
+
511
+ ##
512
+ # Formats the fitting failures using their description strings and logs them
513
+ #
514
+ # @return [void]
515
+ #
516
+ def log_fitting_failures
517
+
518
+ @logger.info { "fitting failures by type:" }
519
+
520
+ @failures.each_key do |k|
521
+
522
+ @logger.info { FAILURE_REASONS[k] + ": " + @failures[k].to_s }
523
+
524
+ end
525
+
526
+ end
527
+
528
+
529
+ ##
530
+ # Runs the analysis.
531
+ #
532
+ # @return [void]
533
+ #
534
+ def go
535
+
536
+ image_objects = load_or_fit_image_objects
537
+
538
+ FileInteraction.write_position_data(image_objects, @parameters)
539
+
540
+ pc = PositionCorrector.new(@parameters)
541
+
542
+ c = pc.generate_correction(image_objects)
543
+
544
+ tre = 0.0
545
+
546
+ if @parameters[:determine_tre] and @parameters[:determine_correction] then
547
+
548
+ puts "calculating tre"
549
+
550
+ tre = pc.determine_tre(image_objects)
551
+
552
+ c.tre= tre
553
+
554
+ else
555
+
556
+ tre = c.tre
557
+
558
+ end
559
+
560
+
561
+ c.write_to_file(FileInteraction.correction_filename(@parameters))
562
+
563
+
564
+
565
+ diffs = pc.apply_correction(c, image_objects)
566
+
567
+ corrected_image_objects = []
568
+
569
+ image_objects.each do |iobj|
570
+
571
+ if iobj.getCorrectionSuccessful then
572
+
573
+ corrected_image_objects << iobj
574
+
575
+ end
576
+
577
+ end
578
+
579
+ FileInteraction.write_position_data(corrected_image_objects, @parameters)
580
+
581
+
582
+ image_objects = corrected_image_objects
583
+
584
+ df= P3DFitter.new(@parameters)
585
+
586
+ fitparams = df.fit(image_objects, diffs)
587
+
588
+ @logger.info { "p3d fit parameters: #{fitparams.join(', ')}" }
589
+
590
+ if @parameters[:in_situ_aberr_corr_basename] and @parameters[:in_situ_aberr_corr_channel] then
591
+
592
+ slopes = pc.determine_in_situ_aberration_correction
593
+
594
+ vector_diffs = pc.apply_in_situ_aberration_correction(image_objects, slopes)
595
+
596
+ scalar_diffs = get_scalar_diffs_from_vector(vector_diffs)
597
+
598
+ corr_fit_params = df.fit(image_objects, scalar_diffs)
599
+
600
+ FileInteraction.write_differences(diffs, @parameters)
601
+
602
+ if corr_fit_params then
603
+
604
+ @logger.info { "p3d fit parameters after in situ correction: #{fitparams.join(', ') }" }
605
+
606
+ else
607
+
608
+ @logger.info { "unable to fit after in situ correction" }
609
+
610
+ end
611
+
612
+ end
613
+
614
+ end
615
+
616
+ ##
617
+ # Converts an array of vectors to an array of scalars by taking their 2-norm.
618
+ #
619
+ # @param [Enumerable< Enumerable<Numeric> >] vector_diffs an array of arrays (vectors, etc.)
620
+ # each of which will be normed.
621
+ #
622
+ # @return [Array] an array of the norms of the vectors provided.
623
+ #
624
+ def get_scalar_diffs_from_vector(vector_diffs)
625
+
626
+ vector_diffs.map do |vd|
627
+
628
+ Math.sqrt(Math.sum(vd) { |e| e**2 })
629
+
630
+ end
631
+
632
+ end
633
+
634
+ ##
635
+ # Runs analysis using a specified parameter file.
636
+ #
637
+ # @param [String] fn the filename of the parameter file
638
+ #
639
+ # @return [void]
640
+ #
641
+ def self.run_from_parameter_file(fn)
642
+
643
+ java_import Java::edu.stanford.cfuller.imageanalysistools.meta.AnalysisMetadataParserFactory
644
+
645
+ parser = AnalysisMetadataParserFactory.createParserForFile(fn)
646
+
647
+ p = parser.parseFileToParameterDictionary(fn)
648
+
649
+ c = new(p)
650
+
651
+ c.go
652
+
653
+ end
654
+
655
+
656
+ end
657
+
658
+ end
659
+
660
+ ##
661
+ # If this file is run from the command line, start the analysis using the parameter file
662
+ # specified on the command line.
663
+ #
664
+ if __FILE__ == $0 then
665
+
666
+ Cicada::CicadaMain.run_from_parameter_file(ARGV[0])
667
+
668
+ end
669
+
670
+
671
+
672
+
673
+
674
+