forked from UTFOIL/Vectorization-Public
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vectorize_V200.m
3993 lines (2716 loc) · 198 KB
/
vectorize_V200.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
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
362
363
364
365
366
367
368
369
370
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
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
function [ time_stamp, ROI_names ] = vectorize_V200( varargin )
%% Vectorize_V200 - Samuel Alexander Mihelic - Novemeber 8th, 2018
% VECTORIZE( ) prompts the user at the command window for all required inputs. It first asks
% whether to vectorize a new batch of images or continue with a previous batch. A batch is a
% group of images that the VECTORIZE function organizes together with two properties:
%
% 1) The same set of input parameters applies to every image in a batch.
%
% 2) The images in a batch are processed in parallel at each step of the vectorization.
% (see Methods below for descriptions of the four steps in the vectorization algorithm).
%
% If the user continues with a previous batch, VECTORIZE prompts the user to select a previous
% batch folder with data to recycle.
%
% Alternatively, if the user starts a new batch, VECTORIZE prompts the user to select a folder
% with some image file(s) to be vectorized. It makes a new batch folder in a location
% specified by the user.
%
% In either case, VECTORIZE prompts the user for a few logistical inputs: which vectorization
% step(s) to execute, what previous data or settings (if any) to recycle, which visual(s) to
% output (if any), and whether or not to open a graphical curator interface. It also prompts the
% user for workflow-specific parameters: It displays imported parameters for review, and prompts
% the user for any missing required parameters. VECTORIZE writes any outputs to the batch
% folder with a time stamp of the form YYMMDD_HHmmss.
%
% Conventions: Greater values in the IMAGE_MATRIX correspond to greater vascular signal
% The IMAGE_MATRIX dimensions correspond to the physical dimensions y, x, and z
% (1,x,z) is the top border of the x-y image at height z
% (y,1,z) is the left border of the x-y image at height z
% (y,x,1) is the x-y image nearest to the objective
%
% Supported input image file types: .tif
%
% For in-line function calls that do not require manual interfacing (e.g. for writing wrapper
% functions or for keeping a concise record of VECTORIZE function calls in a script file), see the
% Optional Input, Logistical Parameters, and Workflow Specific Parameters Sections.
%
% Note: For more organizational/navigational control over this document in MATLAB:
% 1) open the Preferences Window (HOME>>PREFERENCES)
% 2) enable Code Folding for Sections (MATLAB>>Editor/Debugger>>Code Folding)
% 3) fold all of the sections in this document (ctrl,+)
%
%% ------------------------------------------- Overview -------------------------------------------
%
% The purpose of the vectorization algorithm is to convert a grayscale, 3D image of vasculature (see
% Inputs section) to a vectorized model of the vascular components. The output model consists of a
% list of many spherical objects with 3D-position (x,y,z), radius (r), and a contrast metric (c, see
% Methods section). These objects are vectors because each object is a 5-tuples of real numbers:
% [x,y,z,r,c]. The output vectors can then be rendered as a 2- or 3-dimensional image at any
% requested resolution, or it could be analyzed for statistical properties such as volume fraction
% or bifurcation density. With these objects in hand, many analyses are greatly simplified. Many
% such demonstrations and visualizations are automatically output (see Outputs section).
%
%% ---------------------------------------- Optional Input ----------------------------------------
%
% VECTORIZE( IMAGE_MATRIX ) vectorizes the numerical array IMAGE_MATRIX. A batch folder is made
% in the OutputDirectory specified by the user. The user is prompted for the other logistical
% and workflow specific parameters as in the VECTORIZE( ) call.
%
% Supported IMAGE_MATRIX variable types: 3D array of doubles
%
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% VECTORIZE( IMAGE_MATRICES ) vectorizes each IMAGE_MATRIX in the cell vector IMAGE_MATRICES. The
% outputs in the batch folder are numbered by the input order of the images in the cell vector.
%
% Supported IMAGE_MATRICES variable types: Cell vector of 3D array of doubles
%
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% VECTORIZE( FILE_NAME ) vectorizes the IMAGE_MATRIX(-CES) specified by the path(s) in FILE_NAME.
%
% Supported FILE_NAME variable types: character vectors
%
% FILE_NAME is an absolute or relative paths to current working folder. Wild card commands (i.e.
% '*' or '**' ) in the FILE_NAME are also supported. For example:
%
% VECTORIZE( '*.tif' ) vectorizes all .tif files in the current directory.
%
% VECTORIZE([ '**', filesep, '*.tif' ]) vectorizes all .tif files in the current directory or any
% subdirectory thereof.
%
% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
%
% VECTORIZE( FILE_NAMES ) vectorizes the IMAGE_MATRICES specified by the cell vector of FILE_NAMES.
%
% Supported FILE_NAMES variable types: Cell vector of character vectors
%
%% -------------------------------------- Logistical Parameters -----------------------------------
%
% VECTORIZE( ..., NAME, VALUE )
% uses the following NAME/VALUE pair assignments for logistical inputs:
%
% ------- NAME ------- -------------------------------- VALUE --------------------------------
%
% 'OutputDirectory' Char or string specifying the folder that contains the output batch
% folder. The default is to prompt the user at the command window.
%
% 'NewBatch' 'prompt' - Prompts the user at the command window.
% 'yes' - Makes a new batch folder in the OutputDirectory folder.
% This is the only option if the user provides the Optional
% Input.
% 'no' - Writes into an existing batch folder in the OutputDirectory.
%
% 'PreviousBatch' 'prompt' (default) - Prompts the user to input a previous batch folder.
% 'none' - Does not import any existing data or settings. If
% NewBatch is 'no', this is not an option.
% 'yyMMdd-HHmmss' - Imports from the batch_yyMMdd-HHmmss folder in the
% OutputDirectory.
% 'recent' - Imports from the most recent batch_* folder in the
% OutputDirectory.
%
% 'PreviousWorkflow' 'prompt' (default) - Prompts the user to input a previous settings file.
% 'none' - Does not import any existing data or settings. If
% NewBatch is 'no', this is not an option.
% 'yyMMdd-HHmmss' - Imports from the workflow_yyMMdd-HHmmss folder in
% the batch folder.
% 'recent' - Imports from the most recent workflow_settings_*
% folder in the batch folder.
%
% 'StartWorkflow' Note: These are listed in order: Energy is the first process.
%
% 'prompt' (default) - Prompts the user at the command window.
% 'none' - Does not run any workflow steps. The user may run
% curatation or visual steps.
% 'next' - Starts the vectorization process at the next step for
% the selected PreviousWorkflow.
% 'energy' - Starts the vectorization process at the Energy step.
% This is the only option if NewBatch is 'yes'.
% 'vertices' - Starts the vectorization process at the Vertices step.
% 'edges' - Starts the vectorization process at the Edges step.
% 'network' - Starts the vectorization process at the Network step.
%
% 'FinalWorkflow' 'prompt'(default) - Prompts the user at the command window.
% 'none' - Does not run any vectorization processes.
% This is the only option if StartWorkflow is 'none'
% 'one' - Ends the vectorization process at the StartWorkflow step.
% 'energy' - Ends the vectorization process at the Energy step.
% 'vertices' - Ends the vectorization process at the Vertices step.
% 'edges' - Ends the vectorization process at the Edges step.
% 'network' - Ends the vectorization process at the Network step.
%
% 'Visual' 'none' - Does not write visual outputs.
% 'original' - Writes visuals for just the input images.
% 'energy' - Writes visuals for just the Energy step.
% 'vertices' - Writes visuals for just the Vertices step.
% 'edges' - Writes visuals for just the Edges step.
% 'network' - Writes visuals for just the Network step.
% { ... } - Writes visuals for just the ... steps.
% 'productive' (default) - Writes visuals for just the workflow steps.
% being executed at this vectorization call.
% 'all' - Writes visuals for all vectorization steps.
%
% 'SpecialOutput' 'none' - Does not create any special network outputs.
% 'histograms' - (defualt) shows strand, length statistic histograms
% 'depth-stats' - Shows depth-resolved statistics.
% 'flow-field' - Writes x, y, and z component .tif's of flow field.
% 'depth' - Shows vectors over raw with color-coded depth.
% 'strands' - Shows vectors over raw with color-coded strands.
% 'directions' - Shows vectors over raw with color-coded direcions.
% '3D-strands' - Shows 3D volume rendering with color-coded strands.
% 'casX' - Creates .casX equivalent representation of strands.
% Format .casX is due to LPPD in Chicago.
% { ... } - Creates ... special network outputs.
% 'all' - Creates all special network outputs.
%
% 'VertexCuration' 'auto' - All non-overlapping vertices are passed to edges.
% Chooses least energy vector upon volume conflict.
% 'manual' (default) - Prompts user with a graphical curation interface.
% 'machine-manual' - Applies neural network categorization and then
% prompts user with a graphical interface.
% 'machine-auto' - Applies neural network categorization and then
% all non-overlapping vertices are passed to edges.
% Chooses best vector according to the neural network
% upon volume conflict.
%
% 'EdgeCuration' 'auto' - All edges are passed to network.
% 'manual' (default) - Prompts user with a graphical curation interface.
% 'machine-manual' - Applies neural network categorization and then
% prompts user with a graphical interface.
% 'machine-auto' - Applies neural network categorization. All edges
% are passed to network.
%
% 'NetworkPath' 'prompt
% 'built-in' (default) - Built-in network ...
% 'train' - Trains new network from all curation files found
% in the training folder in the vectorization base
% directory.
% 'yyMMdd-HHmmss' - Imports network trained at yyMMdd-HHmmss from the
% network folder in the vectorization base
% directory.
% 'recent' - Imports network trained most recently from the
% network folder in the vectorization base
% directory.
%
% 'Forgetful' 'none' (default) - No intermediate data files will be deleted
% 'original' - Deletes intermediate copies of the input images
% 'energy' - Deletes intermediate Energy data files
% 'both' - Deletes both intermediate data files when done
%
% 'Presumptive' false (default) - Prompts user for all required workflow-specific inputs
% true - Assumes previous settings or default if no previous
%% ------------------------------- Workflow-Specific Parameters -----------------------------------
%
% VECTORIZE( ..., NAME, VALUE )
% uses the NAME/VALUE pair assignments listed below to input workflow-specific parameters. Each
% parameter is listed below under the first workflow step that it modifies.
%
% Resolving conflicts between NAME/VALUE pairs and imported parameters from the PreviousWorkflow:
%
% If a NAME/VALUE pais is provided for a parameter that modifies a workflow step that is
% upstream of the StartWorkflow, then that value is ignored and the value from the
% PreviousWorkflow value will remain. A warning is produced. This must occur because the
% relevant workflow has already been executed and is not scheduled to run again, therefore the
% parameters that modify it will not change.
%
% If a NAME/VALUE pair is provided for a parameter that only modifies workflows that are equal
% to or downstream of the StartWorkflow, then that value will overwrite any value that may
% have been retrieved from the PreviousWorkflow. The overwriting is displayed in the commaind
% window.
%
%% - - - - - - - - - - - - - - - - - Energy - - - - - - - - - - - - -
% ---------------- NAME ---------------- ------------------------- VALUE -------------------------
%
% 'microns_per_voxel' Real, positive, three-element vector specifying the voxel
% size in microns in y, x, and z dimensions, respectively.
% Default: [ 1, 1, 1 ]
%
% 'radius_of_smallest_vessel_in_microns' Real, positive scalar specifying the radius of the
% smallest vessel to be detected in microns. Default: 1.5
%
% 'radius_of_largest_vessel_in_microns' Real, positive scalar specifying the radius of the largest
% vessel to be detected in microns. Default: 50
%
% 'approximating_PSF' Logical scalar specifying whether to approximate the PSF
% using "Nonlinear Magic: Multiphoton Microscopy in the
% Biosciences" (Zipfel, W.R. et al.). Default: true
%
% 'sample_index_of_refraction' Real, positive scalar specifying the index of refraction
% of the sample. This parameter is only used if
% approximating the PSF. Default: 1.33
%
% 'numerical_aperture' Real, positive scalar specifying the numerical aperture of
% the microscope objective. Default: 0.95
%
% 'excitation_wavelength_in_microns' Real, positive scalar specifying the excitation wavelength
% of the laser in microns. Default: 1.3
%
% 'scales_per_octave' Real, positive scalar specifying the number of vessel
% sizes to detected per doubling of the radius cubed.
% Default: 1.5
%
% 'max_voxels_per_node_energy' Real, positive scalar specifying Default: 1e5
%
% 'gaussian_to_ideal_ratio' Real scalar between 0 and 1 inclusive specifying the
% standard deviation of the Gaussian kernel per the total
% object length for objects that are much larger than the
% PSF. Default: 1
%
% 'spherical_to_annular_ratio' Real scalar between 0 and 1 inclusive specifying the
% weighting factor of the spherical pulse over the combined
% weights of spherical and annular pulses. This parameter is
% only used if gaussian_to_ideal_ratio is strictly less than
% unity. Default: 1
%
%
%% - - - - - - - - - - - - - - - - Edges - - - - - - - - - - - - -
% ---------------- NAME ---------------- ------------------------- VALUE -------------------------
%
% 'max_edge_length_per_origin_radius' Real, positive scalar specifying the maximum length of an
% edge trace per the radius of the seed vertex. Default: 30
%
% 'number_of_edges_per_vertex' Real, positive integer specifying the maximum number of
% edge traces per seed vertex. Default: 4
%
%% - - - - - - - - - - - - - - - - Network - - - - - - - - - - - - -
% ---------------- NAME ---------------- ------------------------- VALUE -------------------------
%
% 'sigma_strand_smoothing' Real, non-negative integer specifying the standard
% deviation of the Gaussian smoothing kernel per the radius
% of the strand at every position along the strand vector.
% Default: 1
%
%% ------------------------------------------- Methods --------------------------------------------
%
% Vectorization is accomplished in four steps: (1) energy image formation, (2) vertex extraction,
% (3) edge extraction, and (4) network extraction. The raw output is a superposition
% of possible models until it is segmented in some way. The objects are assigned a contrast metric
% based on the values from the energy image, and thresholding them on this value provides direct
% control over the sensitivity and specificity of the vectorization. Alternatively, the graphical
% curation interface provides a platform for manual segmentation such as local threshold selection
% or point-and-click object selection.
%
% 1: Energy Image Formation
% Multi-scale (at many pre-defined sizes) gradient and curvature information from the original
% 3D image is combined to form a 4-dimensional, multi-scale, centerline-enhanced, image, known
% as the energy image. Ideally, the voxels with the lowest energy value the energy image will
% be the most likely to be centerline voxels for vessels in the pre-defined size range. This
% can be visually verified by inspecting the energy*.tif visual output in the visual output
% directory. The energy image should be very negative right at the vessel centerlines and close
% to zero or positive elsewhere. The value of the size image at a given voxel shows the index
% of the pre-defined sizes that is most likely assuming a vessel is centered at that voxel.
%
% 2: Vertex Extraction
% Vertices are extracted as local minima in the 4D energy image (with associated x, y, z,
% radius, and energy value). This method was inspired by the first part of the SIFT algorithm
% (David Lowe, International Journal of Computer Vision, 2004)). Ideally, local minima of the
% energy image correspond to voxels that are locally the most likely to be along a vessel
% centerline. The size coordinate is also required to be at a local energy minimum. In theory,
% the vertices are ordered from most to least likely to exist by the energy values at their
% locations.
%
% 3: Edge Extraction
% Edges are extracted as voxel to voxel random walks through the (min. projected to 3D) energy
% image. Therefore edges are lists of spherical objects like vertices. Edge trajectories seek
% lower energy values and are forced to connect exactly two vertices. The trajectories between
% vertices are in theory ordered from most to least likely to exist by their mean energy values.
%
% 4: Network Extraction
% Strands are defined as the sequences of non-branching edges (single random color in the
% colored strands image). Strands are found by counting the number of edges in the adjacency
% matrix of the vertices. Strands are the connected components of the adjacency matrix that
% only includes vertices with two edges. With the strands of the network in hand, we
% equivalently know where the bifurcations in the network are. Network information unlocks many
% doors. For instance, we can smooth the positions and sizes of the extracted vectors along
% their strands and approximate local blood flow fields.
%
%% ------------------------------------------- Outputs --------------------------------------------
%
% The main output is the vectorized model of the vascular network depicted in the 3D input image.
% The vectorized model is composed of a list of objects that we can think of as 5 dimensional
% vectors (3D position, size, and energy). These objects, when thresholded by an appropriate
% contrast value and rendered as volume-filled spheres are a 3-dimensional mask of the vascular
% volume in the input image at a sensitivity determined by the threshold.
%
% TIME_STAMP = VECTORIZE( ... )
% returns the TIME_STAMP that was assigned to any new data or settings output
%
% [ TIME_STAMP, ROI_NAMES ] = VECTORIZE( ... )
% also returns the FILE_NAMES that were used as input or the names assigned to the IMAGE_MATRICES in
% the order that they were passed to VECTORIZE.
%
%% version notes:
%
% V02: in which the directory structure is overhauled and the calling of functions, and the saving
% of files and settings are all standardized.
%
% V10: in which:
%
% 1) there is no downsampling (all calculations are done at the same resolution). This allows easy
% laplacian comparisons across octaves, easy zeroing of spaces across all octaves, and easy
% traversal through the 4D when placing edges. This comes at the cost of increased storage and
% time. Some of this storage will be won back by not needing to overlap the octaves at their edges
% in scale space. More storage can be won back by eliminating the need to interpolate. This would
% require smart sampling of the gaussian kernels (perhaps in the Fourier space after analytic
% transformation) and proper anisotropic weighting of the direction (and symmetry) feature(s) based
% on their orientations. This increase in storage and time is not a primary concern.
%
% 2) without the octave breaks, we can compare all the laplacians on even playing field and
% eliminate vertices that lie within a radius of another vertex whose laplacian is more negative.
%
% 3) the edges are placed in the following way: for each vertex, and each direction, a neighborhood
% of laplacian image is loaded around that vertex. The sphere representing the vertex itself is
% zero'd out, except for the cone representing the current direction. A 26 neighbor strel that
% spans all the scales is placed at the center of the vertex and the lowest laplacian valued pixel
% is selected as the first mini_vertex of the edge. The sterel is then centered at that mini-vertex
% and the previous strel is zero'd out. The next however many mini-vertices in the edge are
% selected by succesively choosing the lowest laplacian from the partially zerod strels. If at any
% point, there are no negative laplacians to choose from, then this edge is terminated. If at any
% point, this edge finds itself at the same pixel and scale as another vertex, then that vertex is
% the terminal vertex of this edge.
%
% 4) laying the infrastructure to input vessel sizes in microns instead of in pixels and to
% incorporate deconvolution of the PSF during the blurring stages.
%
% 5) fixing a bug that put the image size in the settings folder, which would be incompatible with
% the intended use of the settings folder to apply to multiple ROI's equally. Instead, we can read
% the image size from the h5 file like this:
%
% original_file = [ directories{ 4, ROI_index }, interpolate_handle ];
%
% original_info = h5info( original_file );
%
% size_of_image = uint16( original_info.Datasets.Dataspace.Size );
%
% V12 in which phase II is removed. also fixed a naming error in the blurring settings
% "size_per_sigma" -> "sigma_per_size" SAM 4/22/18
%
% V13 in which the Laplacian is not necessarilly calculated, but an enery field is created to lie
% over the 4D image whose exact formula is an input variable but generally depends on the curvature
% values which are fleetingly calculated in the get_energy function. The output of the get energy
% function is the energy field (which could be the laplacian) and is fed to the get_vertices and
% get_edges functions.
%
% Also removing interpolation as an workflow option. So the image size is retrieved from the
% original file hereafter and the voxel aspect ratio is declared in the blurring step.
%
% note, we should make the energy function two 3D images instead of a 4D image. We should min
% project the energy function across the size dimension but remember the scale coordinate where the
% min occurs. Both the vertices and edges end up doing a min projection anyway, so doing it up front
% would save computation a bit and a lot of storage. Will have to adapt the get_vertices and
% get_edges functions along with get_energy to enact this change. (not yet implemented 5/5/18 SAM).
%
% SAM 4/25/18
%
% V14 in wich the derivatives are computed in the fourier domain instead of by finite differencing
% in the voxel array space. Blurring and energy calculation are thus merged into one step. SAM
% 5/5/18
%
% V15 in which the energy is min projected across the scale dimension before saving into h5 file SAM
% 5/7/18
%
% V16 in which the clean up phase is added. We attempt to assign existence probabilities to the
% different model components so that we may easily threshold them later. SAM 5/11/18
%
% V162 in which the transition matrix is just multiplied and there is no connection test or true
% eigenvalue solving SAM 5/16/18
%
% V161 in which the transitino matrix is decomposed into connected components and eigenvalues are
% found for each connected component SAM 5/16/18
%
% V170 in which only the top two edges are kept from each vertex in the transition matrix and then
% the strongly connected components of that transition matrix (viewed as an adjacency matrix) are
% called strands. SAM 5/22/18
%
% V180, Vertices are chosen such that no vertex center is inside of a vertex of lower energy. SAM
% 5/29/18 Also, The edges are chosen such that no edge will conflict with a beginning or ending
% vertex of a lower energy edge. SAM 5/31/18 (retro-dated from 7/11/18 following the choose edges
% version notes)
%
% V190: SAM 7/18/18
%
% Manual curation is added (to the vertices section for starters).
%
% vector positions and sizes are smoothed along the axis of the strand that owns them (amount of
% smoothing is an input).
%
% Bifurcation Vertices are added to the visual ouptuts of the network section
%
% A 3D vector field of blood flow direction is estimated by the spatial derivatives with respect to
% the strand index (this vector is painted inside of the volume of the associated spherical object
% and edges that are better contrast overwrite edges of lesser contrast).
%
% A bug was fixed in the function get_network (previously our definition of strand objects as being
% a series of vectors was not achieved by the code. Some strands contained bifurcations before.)
%
% The input, output, and methods sections at the top of this script were updated. (The methods
% section could be expanded and the outputs section is incomplete as it does not include edges,
% strands, junctions, bifurcations, or flow field direction.
%
% To-Do:
%
% add manual curation to the edges and network sections as needed.
%
% Edge curation could rate every edge by its rank in the following way: lower score is better: every
% edge is assigned a number of points equal to the ordinate of the choice of this edge with respect
% to either of its vertices added together. The lowest/best score would thus be a 2, meaning that
% both vertices choose this edge first. If the edge is chosen as first by one vertex and second by
% the other, it would get a score of three. The user could manipulate the edges under this metric,
% for instance, they could set all edges with scores 4 or greater to false.
%
% Or, edges could be rated by ( max_energy - min_energy ), and lower values would be favored.
%
% For the creation of the flow field make two improvements: (1) input the contrast metrics on a per
% vector basis instead of a per edge basis to make the resulting image more accurate. (2) use the
% vessel direction to make a cylindrical object (not a spherical object) for the filling of the flow
% field directions.
%
% SAM 7/18/18
%
% V200: Attempting to create an end-user version of the vectorization software for a general
% neuroscientist that requires minimal directory structure and is intuitive to use. Moving from a
% script to a function. SAM 11/8/18
%% 0: Input Parser
% time stamp
time_stamp = char( datetime('now', 'TimeZone', 'local', 'Format', 'yyMMdd-HHmmss' ));
q = inputParser ;
q.FunctionName = 'vectorize' ;
q.KeepUnmatched = true ;
%% ---------------------------------------- Optional Input ----------------------------------------
% IF optional input is provided (the total number of inputs will be odd).
if mod( length( varargin ), 2 ) % odd number of inputs
p = inputParser ;
p.FunctionName = 'vectorize' ;
p.KeepUnmatched = true ;
addRequired( p, 'OptionalInput' )
parse( p, varargin{ : });
NewBatch_values = { 'yes' };
NewBatch_default = 'yes' ;
optional_input_provided = true ;
else % ELSE optional input not provided
p = inputParser ;
p.FunctionName = 'vectorize' ;
p.KeepUnmatched = true ;
NewBatch_values = { 'prompt', 'yes', 'no' };
NewBatch_default = 'prompt' ;
optional_input_provided = false ;
end % IF no optional input provided
function [ validation_flag, ROI_names ] = validate_the_OptionalInput( x )
validation_flag = true;
% IF cell array
if iscell( x )
input_index_range = 1 : numel( x );
validation_type = cell( size( x ));
for input_index = input_index_range
validation_type{ input_index } = validate_one_OptionalInput( x{ input_index }, ' cell array entry ' );
end % FOR input index
else % ELSE not a cell
validation_type = { validate_one_OptionalInput( x, '' )}; % cell scalar
input_index_range = 1 ;
x = { x };
end % IF cell array
number_of_inputs_total = 0 ;
number_of_matrices = 0 ;
ROI_names = { };
% matrix_file_name = [ current_directory, filesep, 'ans_' ];
for input_index = input_index_range
switch validation_type{ input_index }
case 'path'
input_file_listing = dir( x{ input_index });
number_of_input_files = length( input_file_listing );
if number_of_input_files == 0
error([ 'No files matched the input file: ', x{ input_index }])
end
input_file_cell = struct2cell( input_file_listing );
input_file_names = input_file_cell( 1, : );
input_file_names_temp = input_file_names ;
for file_index = 1 : number_of_input_files
number_of_inputs_total = number_of_inputs_total + 1 ;
input_image_path = [ input_file_cell{ 2, file_index }, filesep, input_file_names{ file_index }]; % TIF file path
ROI_names{ number_of_inputs_total } = [ '_', input_file_names{ file_index }( 1 : end - 4 )];
file_extension = input_file_names{ file_index }( end - 3 : end );
if ~ strcmp( file_extension, '.tif' )
error([ 'File path (', input_image_path, '), in OptionalInput pointed to file without ".tif" extension. Input file type must be .tif' ])
end
if any( strcmp( input_file_names_temp{ file_index }, input_file_names_temp([ 1 : file_index - 1, file_index + 1 : end ])))
error( 'Multiple inputs with the same name were referenced in .tif files in the OptionalInput. Input file names must be unique.' )
end
original_data = tif2mat( input_image_path );
size_of_image = size( original_data );
path_to_original_data = fullfile( root_directory, [ 'batch_', time_stamp ], 'data', [ 'original', ROI_names{ number_of_inputs_total }]); % h5 file path
original_data_type = class( original_data );
h5create( path_to_original_data, '/d', size_of_image, 'Datatype', original_data_type );
mat2h5( path_to_original_data, original_data );
end
case 'matrix'
number_of_inputs_total = number_of_inputs_total + 1 ;
number_of_matrices = number_of_matrices + 1 ;
ROI_names{ number_of_inputs_total } = [ '_ans_', num2str( number_of_matrices )];
path_to_original_data = fullfile( root_directory, [ 'batch_', time_stamp ], 'data', [ 'original', ROI_names{ number_of_inputs_total }]); % h5 file path
h5create( path_to_original_data, '/d', size( x{ input_index }), 'Datatype', class( x{ input_index }));
mat2h5( path_to_original_data, x{ input_index });
end
end % FOR ROI index
function [ validation_type ] = validate_one_OptionalInput( x, phrase )
if ischar( x ) || isstring( x )
validation_type = 'path' ;
else % is not char or string
if isnumeric( x )
if length( size( x )) == 3, validation_type = 'matrix' ;
else, error([ 'OptionalInput', phrase, ' was numeric, but was not three-dimensional' ])
end
else, error([ 'OptionalInput', phrase, 'was neither type char, string, nor numeric' ])
end
end % is char or string
end % FUNCTION VALIDATE_ONE_OPTIONALINPUT
end % FUNCTION VALIDATE_THE_OPTIONALINPUT
%% -------------------------------------- Logistical Parameters -----------------------------------
%% - - - - - - - - - - - - - - output directory - - - - - - - - - - -
addParameter( p, 'OutputDirectory', '', @( x ) isstring( x ) || ischar( x ));
parse( p, varargin{ : });
root_directory = p.Results.OutputDirectory ;
if isempty( root_directory )
root_directory = input( 'Please enter an absolute path to an output directory [current directory]:', 's' );
% default text response
if isempty( root_directory ), root_directory = pwd ; end
end
root_directory = [ root_directory, filesep ];
disp([ 'OutputDirectory: ', root_directory ]);
%% - - - - - - - - - - - - - - - new batch - - - - - - - - - - - -
batch_listing = dir([ root_directory, 'batch_*-*' ]);
there_is_no_previous_batch = isempty( batch_listing );
addParameter( p, 'NewBatch', NewBatch_default, @( x ) any( validatestring( x, NewBatch_values, 'vectorize', 'NewBatch' )));
% if ~ isempty( varargin
parse( p, varargin{ : });
new_batch = p.Results.NewBatch ;
if strcmp( validatestring( new_batch, NewBatch_values ), 'prompt' )
if there_is_no_previous_batch
disp( 'No previous batch folders found in the chosen output directory. Creating a new one...' )
new_batch = 'yes' ;
else % there is a previous batch
disp( 'Previous batch folder(s) were found in the chosen output directory.' )
disp( 'You may import one of these or create a new folder.' )
new_batch = input( 'Would you like to create a new batch folder in the OutputDirectory [no]?: ', 's' );
end
% default text response
if isempty( new_batch ), new_batch = 'no' ; end
NewBatch_values = { 'yes', 'no' };
end
batch_listing_cell = struct2cell( batch_listing );
batch_listing_time_stamps = cellfun( @( x ) x( 7 : 19 ), batch_listing_cell( 1, : ), 'UniformOutput', false );
new_batch = validatestring( new_batch, NewBatch_values );
if strcmp( new_batch, 'yes' )
new_batch = true ;
% PreviousBatch_values = [{ 'none', 'prompt', 'recent' }, batch_listing_time_stamps ];
% PreviousBatch_default = 'prompt' ;
PreviousBatch_values = { 'none' };
PreviousBatch_default = 'none' ;
% This is the folder that keeps the vectorization progress of one or more vectorization attempts
% of a set of batch-processed ROI's
batch_handle = [ 'batch_', time_stamp ];
[ batch_directory, ...
data_directory, ...
visual_data_directory, ...
vector_directory, ...
visual_vector_directory, ...
curation_directory, ...
settings_directory ] = get_directories( root_directory, batch_handle );
disp([ 'Creating a new batch folder (for parallel image processing) in the OutputDirectory: ' batch_directory ])
[ ~, ~ ] = mkdir( batch_directory );
[ ~, ~ ] = mkdir( data_directory );
[ ~, ~ ] = mkdir( visual_data_directory );
[ ~, ~ ] = mkdir( vector_directory );
[ ~, ~ ] = mkdir( visual_vector_directory );
[ ~, ~ ] = mkdir( curation_directory );
if ~ optional_input_provided
[ file, path ] = uigetfile( '*.tif', 'Please select one or more input .tif file.', 'MultiSelect', 'on');
if isnumeric( path ) || isnumeric( path ), error( 'No paths were collected by the folder selection interface. Please retry selection.' ); end
optional_input = fullfile( path, file );
else % OptionalInput provided
optional_input = p.Results.OptionalInput ;
end % IF Optional Input Not Provided
disp( 'Attempting to write an h5 files for each input into the "Data" subdirectory of the OutputDirectory... ' )
[ ~, ROI_names ] = validate_the_OptionalInput( optional_input );
number_of_ROIs = length( ROI_names );
ROI_index_range = 1 : number_of_ROIs ;
if number_of_ROIs > 1
names_list = '' ;
for ROI_index = ROI_index_range( 1 : end - 1 )
names_list = [ names_list, ROI_names{ ROI_index }, ', ' ];
end
names_list = [ names_list, 'and ', ROI_names{ end }( 2 : end ), '.' ];
disp([ 'Input names were set to: ', names_list ])
else % one single ROI
disp([ 'Input name was set to: ', ROI_names{ 1 }( 2 : end ), '.' ])
if exist( 'path', 'var' ), disp([ 'Input file was located at path: ', path ]), end
end
% This is where all the settings for this batch will be saved.
[ ~, ~ ] = mkdir( settings_directory );
% This file holds the directory tree that is specific to this batch.
batch_settings = [ settings_directory, 'batch' ];
save( batch_settings , ...
'optional_input', ...
'ROI_names' , ...
... 'data_directory', ...
... 'visual_data_directory', ...
... 'vector_directory', ...
... 'visual_vector_directory', ...
... 'curation_directory', ...
... 'settings_directory', ...
'ROI_index_range' );
else % ELSE old batch
there_exists_a_previous_batch = ~ isempty( batch_listing );
if there_exists_a_previous_batch
new_batch = false ;
PreviousBatch_values = [{ 'prompt', 'recent' }, batch_listing_time_stamps ];
PreviousBatch_default = 'prompt' ;
disp( 'Importing settings and data from a previous batch folder in the OutputDirectory.' )
else % ELSE no previous batch exists
error( 'No previous batches were found in the OutputDirectory. Previous batch folders need to be in the OutputDirectory to import data or settings.' )
end % IF previous batch exists
end % IF new batch
%% - - - - - - - - - - - - - previous batch - - - - - - - - - - - -
addParameter( p, 'PreviousBatch', PreviousBatch_default, @( x ) any( validatestring( x, PreviousBatch_values, 'vectorize', 'PreviousBatch' )));
parse( p, varargin{ : });
previous_batch = validatestring( p.Results.PreviousBatch, PreviousBatch_values );
if strcmp( previous_batch, 'prompt' )
number_of_previous_batches = length( batch_listing_time_stamps );
disp( 'Previous batch time-stamps:' )
for batch_index = 1 : number_of_previous_batches
disp([ ' ', batch_listing_time_stamps{ batch_index }])
end
previous_batch = input( 'Please select a PreviousBatch from the OutputDirectory [recent]: ', 's' );
% default text response
if isempty( previous_batch )
previous_batch = 'recent' ;
else % validate response against possible choices
previous_batch = validatestring( previous_batch, [{ 'recent' }, batch_listing_time_stamps ]);
end
end
switch previous_batch
case 'none'
previous_batch_handle = 'none' ;
case 'recent'
most_recent_previous_batch_time_stamp = batch_listing( end ).name( 7 : 19 );
previous_batch_handle = [ 'batch_', most_recent_previous_batch_time_stamp ];
otherwise % specific time-stamp referenced by user
previous_batch_handle = [ 'batch_', previous_batch ];
end
if strcmp( previous_batch_handle, 'none' )
disp( 'Not loading any previous data or settings from a previous batch' )
previous_batch_directory = '' ;
PreviousWorkflow_values = { 'none' };
PreviousWorkflow_default = 'none' ;
else
% load an old batch to continue working from
disp([ 'Loading previous settings and data from ', previous_batch_handle, ' in the OutputDirectory...' ])
previous_batch_directory = [ root_directory, previous_batch_handle, filesep ];
previous_settings_directory = [ previous_batch_directory, 'settings', filesep ];
previous_batch_settings = [ previous_settings_directory, 'batch' ];
load( previous_batch_settings )
[ ~, ...
data_directory, ...
visual_data_directory, ...
vector_directory, ...
visual_vector_directory, ...
curation_directory, ...
settings_directory ] = get_directories( root_directory, previous_batch_handle );
% find most recent workflow time stamp in the most recent batch directory
workflow_listing = dir([ previous_batch_directory, 'settings', filesep, 'workflow_*.mat' ]);
there_exists_a_previous_workflow = ~ isempty( workflow_listing );
if there_exists_a_previous_workflow
workflow_listing_cell = struct2cell( workflow_listing );
workflow_listing_time_stamps = cellfun( @( x ) x( 10 : 22 ), workflow_listing_cell( 1, : ), 'UniformOutput', false );
PreviousWorkflow_values = [{ 'prompt', 'recent' }, workflow_listing_time_stamps ];
PreviousWorkflow_default = 'prompt' ;
else % ELSE no previous workflow exists
error('No previous workflows were found in the PreviousBatch directory. Previous workflow .mat files need to be in the PreviousBatch directory to use previous data or settings.')
end % IF previous workflow exists
end
%% - - - - - - - - - - - - - previous workflow - - - - - - - - - - -
addParameter( p, 'PreviousWorkflow', PreviousWorkflow_default, @( x ) any( validatestring( x, PreviousWorkflow_values, 'vectorize', 'PreviousWorkflow' )));
parse( p, varargin{ : });
previous_workflow = validatestring( p.Results.PreviousWorkflow, PreviousWorkflow_values );
if strcmp( previous_workflow, 'prompt' )
number_of_previous_workflows = length( workflow_listing_time_stamps );
disp( 'Previous workflow time-stamps:' )
for workflow_index = 1 : number_of_previous_workflows
disp([ ' ', workflow_listing_time_stamps{ workflow_index }])
end
previous_workflow = input( 'Please select a PreviousWorkflow .mat file from the PreviousBatch directory [recent]: ', 's' );
% default text response
if isempty( previous_workflow )
previous_workflow = 'recent' ;
else % validate response against possible choices
previous_workflow = validatestring( previous_workflow, [{ 'recent' }, workflow_listing_time_stamps ]);
end
end
switch previous_workflow
case 'recent'
most_recent_previous_workflow_time_stamp = workflow_listing( end ).name( 10 : 22 );
previous_workflow_handle = [ 'workflow_', most_recent_previous_workflow_time_stamp ];
otherwise % specific time-stamp referenced by user
previous_workflow_handle = [ 'workflow_', previous_workflow ];
end
% load or instantiate the production_times variable
if ~ strcmp( previous_workflow, 'none' )
disp([ 'Loading previous settings and data from ', previous_workflow_handle, '...' ])
% load an old workflow schedule to get the production times to handle the old data
path_to_previous_workflow_settings = [ previous_settings_directory, previous_workflow_handle ];
load( path_to_previous_workflow_settings, 'production_times', 'attempted_production_times' )
else
% initialize the production times variable
production_times = cell( 1, 5 );
production_times{ 1 } = time_stamp ;
production_times( 2 : 5 ) = { 'yyMMdd-HHmmss' };
attempted_production_times = production_times ;