Newer
Older
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
% Analyse laminin orientations in projected data of zebrafish optical cup
% in data by Karen Soans, Caren Norden group, MPI-CBG
% Note that the input to this script are derotated and max-projected ROIs
% from the original image stack, obtained within Fiji by the script
% extractDerotatedSurface.py by Noreen Walker, MPI-CBG.
% Here, orientations are inferred from small image patches, and the
% alignment between orientations (or their coherency) is calculated locally
% and globally across each region of interest.
%
% Karl Hoffmann, Max Planck Institute of Molecular Cell Biology and
% Genetics, Dresden, Germany
% last edit: 2022_09_05
%
% developed using MATLAB R2019, R2020, and R2021
%
%%% usage %%%
% It is assumed that regions of interest (ROIs) of the original image stack
% were extracted, derotated by extractDerotatedSurface.py, and each saved
% into a folder structure
% head_folder/
% <Stage>/
% <Embryo>/
% <RegionOfTheEye>/
% <DerotationConditions>/
% Roi<nn>_ZMAX_of_Resliced_of_Derotated_with2ndCrop.tif
% [the pilot data had only <head_folder>/<Stage>/<Embryo>/<EyeAndChannel>/<ROI>/ ]
% Then per-ROI observables are collected in the array output_collected
% together with their relevant settings and conditions.
%
% Orientations are expressed as vector with coordinates (u,v) where
% anti-podal vectors (u,v) and (-u,-v) represent the same orientation,
% or as magnitude magn and angle theta (from the interval [0,pi]) with
% (u,v) = +- magn * (cos theta, sin theta).
% CHANGELOG
% 2022_08_23 clean-up for publication
% 2021_05_25 adapt plotting to downsampling
% 2021_05_18 include downsampling (i) of the input grayscale image and
% (ii) on the level of orientions.
% 2021_03_26 smoothing now discouraged - replaced by downsampling (on
% grayscale or on orientations)
% 2021_03_16 curate computation of theta_for_hists
% 2021_03_12 switch to orientation estimation by the gradient method (with
% gradient estimate by a Scharr operator),
% introduced kernelSize as opposed to boxRadius
% 2021_01_20 adapted to the structure of 2021 data
%% settings for orientation analysis
% Scharr operator with kernel size 5 is default and highly recommended for
% extremely low error in orientation estimation, see Tabelle B.11 in
%
% Hanno Scharr (2000): Optimale Operatoren in der digitalen
% Bildverarbeitung, PhD Thesis at University Heidelberg, Germany,
% DOI=10.11588/heidok.00000962, http://www.ub.uni-heidelberg.de/archiv/962
%
% Alternative orientation estimation based on the tensor of inertia (tensor
% of second moments of image intensities) and on the Fourier transform are
% included for completeness
method = 'gradient'; %one of "fourier", "inertia", "gradient"
kernelSize = 5; %only relevant for method="gradient" - allowed values are 5 (recommended), 3 and 2
maxImageIntensity = 256; %only relevant for method="gradient" - necessary for scaling of the gradient
boxRadius = 10; %only relevant for method="inertia" and method="fourier" - orientation estimation is performed in square boxes of side length 2*boxRadius+1
forCenterOfMass = true; %only relevant for method="inertia" - compute second moments for center of the image patch or center of mass (image intensity)
subtractBackgroud = true; %only relevant for method="inertia"
minWaveLength = 1; %only relevant for method="fourier" - wavelength to consider for radial integration in the Fourier domain
maxWaveLength = 10; %only relevant for method="fourier"
%% settings for optional downsampling of the grayscale image and/or the extracted orientation field
downSamplFactorGrayscale = 1; %needs to be integer, 1=no downsampling
downSamplFactorOrient = 1; %needs to be integer, 1=no downsampling
downSamplTotal = downSamplFactorGrayscale * downSamplFactorOrient;
%% collect prepocessing settings in a "methodInfo"
switch method
case 'fourier'
methodInfo = [ method '_radius' num2str(boxRadius) '_WaveLengths' num2str(minWaveLength) 'to' num2str(maxWaveLength) ];
case 'inertia'
methodInfo = [ method '_radius' num2str(boxRadius) '_' repmat('NOT', 1-forCenterOfMass) 'forCenterOfMass' '_' repmat('NOT', 1-subtractBackgroud) 'subtractBackgroud' ];
case 'gradient'
methodInfo = [ method '_kernel' num2str(kernelSize) ];
boxRadius = kernelSize; % use boxRadius as means to store the value of kernelSize, p.e. for usage in image erosion
end
methodInfo = [methodInfo '_downsample' num2str(downSamplFactorGrayscale) 'x' num2str(downSamplFactorOrient) ];
%% settings for analysis of coherency, that is degree of local alignment betweem orientations in a box
boxRadius_LocalAlignment = 10; %this quantity counts in ORIGINAL pixels, defining a box of side length 1+2*boxRadius_LocalAlignment
boxRadius_LocalAlignment_downSampled = floor( boxRadius_LocalAlignment / downSamplTotal );
powerOfMagn_LocalAlignment = 1;
myStrengthExponent = 1.0; % for smoothing
mySigmaForDirectySmoothing = [1.0 1.0]; %kernel radius for smoothing
requiredFracOfOrientInfo = 0.9; %fraction of orientations that must lay inside the region
% of interest (or its foreground) for a box to be included into the statistics
%% setting data folder and preparing output collection
head_folder = '/Users/khoffman/Documents/DirectionalityDefects/processedData/2021_08_06_KarenSoans/';
genericFilename = 'ZMAX_of_Resliced_of_Derotated_with2ndCrop.tif'; % according to the output of extractDerotatedSurface.py,
% specify WITHOUT leading individual filename portions like 'ROI<number>_'
filesToAnalyse = dir([head_folder '/**/Roi*_' genericFilename ]);
% the levels below head_folder ougth to be <Stage>/<Embryo>/<RegionOfTheEye>/<DerotationConditions>/
% whereby <RegionOfTheEye> is one of the ROInames = ["hollow", "inBetween", "ridge", "rim", "back"];
% prepare matrix/array for collection of statistics
output_folder_stats = [head_folder 'collectedResults' '_' methodInfo ...
'_coherencyRadius' num2str(boxRadius_LocalAlignment) 'Power' num2str(powerOfMagn_LocalAlignment) '/'];
temp = strsplit(output_folder_stats, '/');
% within the folder, specify file to hold the statistics (its name = name of the deepest subfolder)
output_collected = [output_folder_stats temp{end-1} '.csv'];
%folder for the statistics across ROIs and plots
if ~exist(output_folder_stats, 'dir')
mkdir( output_folder_stats )
end
%% analysis file by file (ROI by ROI)
all_vals_mat = [];
folders_arr = {};
for numFile = 1:numel(filesToAnalyse)
input_folder = [filesToAnalyse(numFile).folder '/' ] ;
output_folder= input_folder;
%% load the grayscale image
input_fullPath = [input_folder filesToAnalyse(numFile).name] ;
[~, input_soleFilename, ~] = fileparts(input_fullPath);
leading_ROIinfo_inFilename = input_soleFilename( 1 : strfind(input_soleFilename, genericFilename(1:end-4))-1 ); %find name part that is individual to this file
[~, GrayImageStack] = loadGrayStack(input_fullPath);
assert(numel(size(GrayImageStack)) ==2) %2D data
NumSlices = 1;
% scale image intensities to a fixed range for comparable results by the gradient
if max(GrayImageStack(:))>255
warning('image intensities are being scaled down to <=255')
GrayImageStack = GrayImageStack / max(GrayImageStack(:)) ;
end
% optional: show loaded data
% imshow(GrayImageStack_orig / 255); title('original data')
GrayImageStack_orig = GrayImageStack;
%% optional downsampling of the grayscale image (downSamplFactorGrayscale = 1 means no downsampling)
% NOTE: Identification of the foreground still happens in the original image,
% but orientation analysis is performed in the downsampled grayscale data
size_y_orig = size(GrayImageStack_orig,1);
size_x_orig = size(GrayImageStack_orig,2);
assert( int8(downSamplFactorGrayscale) == downSamplFactorGrayscale, "must be integer")
GrayImageStack_blocks = 1/(downSamplFactorGrayscale^2) ...
* conv2(GrayImageStack, ones(downSamplFactorGrayscale), 'valid') ;
GrayImageStack_downSampl = GrayImageStack_blocks(1:downSamplFactorGrayscale:end, 1:downSamplFactorGrayscale:end);
GrayImageStack = GrayImageStack_downSampl ;
size_y_grayBlocks = size(GrayImageStack_downSampl,1);
size_x_grayBlocks = size(GrayImageStack_downSampl,2);
%% load info on preprocessing
% in particular rotations performed to check for anisotropy along the z-axis in the original imaging system
% that might result from the form of the point-spread function
input_preprocessingInfoPath = [input_folder leading_ROIinfo_inFilename 'logFileParameters.txt'];
fileID = fopen(input_preprocessingInfoPath);
preprocessingInfo_raw = fread(fileID,'*char')';
fclose(fileID);
% find text location where normal vector is represented
pattern = 'Smallest eigen vector (V0) = normal vector of plane: (nx,ny,nz)= ';
textlocation = strfind(preprocessingInfo_raw, pattern);
if isempty(textlocation)
warning('no info on normal vector found')
NormalVecOrig = [NaN, NaN, NaN];
else
NormalVecOrig_raw = strsplit( preprocessingInfo_raw(textlocation+length(pattern) : end)); %split at whitespace and newline
NormalVecOrig_raw = NormalVecOrig_raw(1:3);
NormalVecOrig = str2double(NormalVecOrig_raw);
assert( abs( 1- sum(NormalVecOrig.^2) ) < 1e-5, 'Unit length violated by normal vector of the plane that was read')
end
%NormalVecOrig is the normal vector of the plane in the original data,
% where the z-axis is the axis of anisotropy
Rotation_theta_x = atan2(NormalVecOrig(2) , NormalVecOrig(3)); % equals atan(NormalVecOrig(2) / NormalVecOrig(3))
Rotation_theta_y = - atan2(NormalVecOrig(1) , sqrt(1-NormalVecOrig(1)^2) ); % named 'theta_yprime' in the preprocessingInfo_raw
AnisotropyAxis = [ sin(Rotation_theta_y), ...
-cos(Rotation_theta_y) * sin(Rotation_theta_x), ...
cos(Rotation_theta_y) * cos(Rotation_theta_x) ] ;
%AnisotropyAxis is the axis of anisotropy in the rotated data,
% where the z-axis is the normal axis of the plane (and this dimension is omitted by projection)
%AnisotropyAxis is in the left-handed coordinate systems of Fiji (and MATLAB)
% therefore distinctino between "MATLABcoords" and righthanded "NORMALcoords"
AnisotropyAxis_inThePlane_MATLABcoords = AnisotropyAxis(1:2) / sqrt(sum( AnisotropyAxis(1:2).^2)) ;
%for the angle, take the same approach as for orientations theta: use the standard right-handed coordinate layout
AngleAnisotropy_inThePlane_NORMALcoords = atan2( -AnisotropyAxis(2), AnisotropyAxis(1) );
AngleAnisotropyProjection = acos(NormalVecOrig(3)) ; % or = acos(AnisotropyAxis(3));
% in 3D: angle between axis of anisotropy and direction of projection
%% orientation analysis
%% Prepare output arrays
theta = zeros( size(GrayImageStack) );
magn = zeros( size(GrayImageStack) );
%% Orientation analysis
time_needed_total=0;
for useSlice = 1:NumSlices
tic
switch method
case 'inertia'
[directy, ~] = ComputeDirecty(GrayImageStack(:,:,useSlice), boxRadius, forCenterOfMass, subtractBackgroud) ;
case 'fourier'
[directy, ~] = ComputeDirecty_FourierAndQ(GrayImageStack(:,:,useSlice), boxRadius, minWaveLength, maxWaveLength);
case 'gradient'
directy = ComputeDirectyGradient(GrayImageStack(:,:,useSlice), kernelSize );
otherwise
warning(['method "' method '" not known'])
end
theta(:,:,useSlice) = atan2( directy(:,:,2), directy(:,:,1) );
if strcmp(method,'gradient')
magn (:,:,useSlice) = directy(:,:,3) / maxImageIntensity;
else %'inertia', 'fourier'
magn (:,:,useSlice) = directy(:,:,3);
end
time_needed=toc;
time_needed_total = time_needed_total + time_needed;
disp(['... done in ', num2str(time_needed), ' seconds.'])
end
disp(['All slices done in ', num2str(time_needed_total), ' seconds.'])
%% save result arrays (together with their settings) to file
% as checkpoint, because orientation estimation with method='inertia' and 'fourier' can take up to hours
filename_intermediate_output = [input_fullPath(1:end-4) '_' methodInfo '.mat'] ;
save( filename_intermediate_output, ...
'GrayImageStack_orig', ... %the original data
'input_fullPath', ... % its source
'NormalVecOrig', ... % the normal vector of the plane in original data, from which the applied rotation follows
'method', 'boxRadius', 'forCenterOfMass', 'subtractBackgroud', ...
'minWaveLength', 'maxWaveLength', ... %the settings for orientation analysis
'theta', 'magn', ... %the results
'downSamplFactorGrayscale', 'downSamplFactorOrient' ) %information on downsampling
%load(filename_intermediate_output)
%% find foreground = image parts sufficiently off the boundary
% (determined without downsampling in the original image data of highest resolution)
% note that the actual data is surrounded by a dim region of polygonal
% shape due to the repeated cutting and projecting in Fiji
% fg_closed = approximate the foreground region (after morphological closing for smoother shape and convex hull operation
% as the shapes du to cutting/projecting are known to be convex polygons)
% fg_inner = all those pixels, for which the complete box used for orientation estimation was on the foreground fg_closed
% fg_innermost = all those pixels, for which all those pixels taken into
% account for local alignment had their respective box used for orientation estimation on the foreground fg_closed
foreground_orig = imgaussfilt(GrayImageStack_orig/255, 5, 'FilterDomain','spatial', 'Padding', 0.0 );
foreground_orig = imbinarize(foreground_orig/max(foreground_orig(:)), 'global'); %imbinarize expects input normalised in [0,1]
% imshow(foreground)
structureElement = strel('diamond',5);
fg_closed_orig = imclose(foreground_orig, structureElement);
%fg_closed_orig = imfill(fg_closed_orig, 'holes');
fg_closed_orig = bwconvhull(fg_closed_orig);
% compare
% figure(1); imshow(fg_closed_orig)
% figure(2); imshow(GrayImageStack_orig/255+0.5)
fg_inner_orig = imerode(fg_closed_orig, strel('square', 2*boxRadius+1) );
% figure(3); imshow(fg_inner_orig)
fg_innermost_orig = imerode(fg_inner_orig, strel('square', 2*boxRadius_LocalAlignment+1) );
% figure(4); imshow(fg_innermost_orig)
%% downsample the foreground information according to the downsampling of grayscale and orientation blocks
% two variants: count a block in the downsampled image as forground, when
% (A) any of the contained pixels is foreground, or when
% (B) all of the contained pixels are foreground (" .."strict")
%% foreground downsampled to the resoution of grayscale downsampling
fg_closed_blocks = 1/(downSamplFactorGrayscale^2) ...
* conv2(fg_closed_orig, ones(downSamplFactorGrayscale), 'valid') ;
fg_closed = (0 < fg_closed_blocks(1:downSamplFactorGrayscale:end, 1:downSamplFactorGrayscale:end) );
fg_closed_strict = (1 == fg_closed_blocks(1:downSamplFactorGrayscale:end, 1:downSamplFactorGrayscale:end) );
fg_inner_blocks = 1/(downSamplFactorGrayscale^2) ...
* conv2(fg_inner_orig, ones(downSamplFactorGrayscale), 'valid') ;
fg_inner = (0 < fg_inner_blocks(1:downSamplFactorGrayscale:end, 1:downSamplFactorGrayscale:end) );
fg_inner_strict = (1 == fg_inner_blocks(1:downSamplFactorGrayscale:end, 1:downSamplFactorGrayscale:end) );
fg_innermost_blocks = 1/(downSamplFactorGrayscale^2) ...
* conv2(fg_innermost_orig, ones(downSamplFactorGrayscale), 'valid') ;
fg_innermost = (0 < fg_inner_blocks(1:downSamplFactorGrayscale:end, 1:downSamplFactorGrayscale:end) );
fg_innermost_strict = (1 == fg_innermost_blocks(1:downSamplFactorGrayscale:end, 1:downSamplFactorGrayscale:end) );
%% foreground downsampled to the (coarser) resolution which the orientation downsampling will attain
% downsample the full-resolution foreground information like "fg_closed_orig"
% by the factor downSamplTotal = downSamplFactorGrayscale * downSamplFactorOrient;
fg_closed_blocks = 1/(downSamplTotal^2) ...
* conv2(fg_closed_orig, ones(downSamplTotal), 'valid') ;
fg_closed_downSampl = (0 < fg_closed_blocks(1:downSamplTotal:end, 1:downSamplTotal:end) );
fg_closed_downSampl_strict = (1 == fg_closed_blocks(1:downSamplTotal:end, 1:downSamplTotal:end) );
fg_inner_blocks = 1/(downSamplTotal^2) ...
* conv2(fg_inner_orig, ones(downSamplTotal), 'valid') ;
fg_inner_downSampl = (0 < fg_inner_blocks(1:downSamplTotal:end, 1:downSamplTotal:end) );
fg_inner_downSampl_strict = (1 == fg_inner_blocks(1:downSamplTotal:end, 1:downSamplTotal:end) );
fg_innermost_blocks = 1/(downSamplTotal^2) ...
* conv2(fg_inner_orig, ones(downSamplTotal), 'valid') ;
fg_innermost_downSampl = (0 < fg_innermost_blocks(1:downSamplTotal:end, 1:downSamplTotal:end) );
fg_innermost_downSampl_strict = (1 == fg_innermost_blocks(1:downSamplTotal:end, 1:downSamplTotal:end) );
%% smooth orientations with Gaussian kernel, and find versions restricted to the foreground
[theta_smoothed, magn_smoothed] = smoothDirecty_ThetaStrength(theta, magn, mySigmaForDirectySmoothing, myStrengthExponent);
% alternative versions restrict to points in the foreground (_fg)
% and those for which the image patch used for orientation estimation was fully within the foreground (_inner)
% and those for which all pixels considered for local alignment had their respective box for orientation estimation fully in the foreground (_innermost)
% (the last is NOT the same as "all pixels considered in the smoothing step")
magn_smoothed_fg = NaN (size(magn_smoothed));
magn_smoothed_fg(fg_closed) = magn_smoothed(fg_closed);
magn_smoothed_fg_inner = NaN (size(magn_smoothed));
magn_smoothed_fg_inner(fg_inner) = magn_smoothed(fg_inner);
magn_smoothed_fg_innermost = NaN (size(magn_smoothed));
magn_smoothed_fg_innermost(fg_innermost) = magn_smoothed(fg_innermost);
%% optional: use the smoothed orientation field for further analysis
% basically set theta = theta_smoothed but assert values in the interval [0, pi]
% NOTE: smoothing might interfere with the subsequent analysis of alignment
%{
theta = mod(theta_smoothed, pi) ;
magn = magn_smoothed;
%}
%% optional: downsampling of the orientation field (downSamplFactorOrient = 1 means no downsampling)
assert( int8(downSamplFactorOrient) == downSamplFactorOrient, "must be integer")
[theta_downSampl, magn_downSampl] = downSamplDirecty_ThetaStrength(theta, magn, downSamplFactorOrient); % skipRows=0 and skipCols=0 fits to the grayscale downsampling in this file, strength_exponent)
size_y_grayAndOrientBlocks = size(theta_downSampl,1);
size_x_grayAndOrientBlocks = size(theta_downSampl,2);
% also downsample the grayscale intensities (again) as they will be used as weights in the check for anisotropy
GrayImageStack_blocksCoarse = 1/(downSamplFactorOrient^2) ...
* conv2(GrayImageStack_downSampl, ones(downSamplFactorOrient), 'valid') ;
GrayImageStack_downSamplTwice = GrayImageStack_blocksCoarse(1:downSamplFactorOrient:end, 1:downSamplFactorOrient:end);
assert( all([size_y_grayAndOrientBlocks, size_x_grayAndOrientBlocks] == size(GrayImageStack_downSamplTwice) ) )
% use downsampled orientation field for further analysis
theta_orig = theta;
magn_orig = magn ;
% basically set theta = theta_downSampl but assert values in the interval [0, pi]
theta = mod(theta_downSampl, pi) ;
magn = magn_downSampl;
%% alignment as a function of space
%% compute local alignment in fixed box - potentially time consuming
% use boxRadius_LocalAlignment_downSampled that is adjusted to the downsampling performed beforehand
[localOrient, localCoherency] = ComputeLocalAlignment(theta,magn,boxRadius_LocalAlignment_downSampled, powerOfMagn_LocalAlignment);
%% save result arrays of local alignment (together with their settings) to file
% as checkpoint
filename_intermediate_output1 = [filename_intermediate_output(1:end-4) ...
'_coherencyRadius' num2str(boxRadius_LocalAlignment) 'Power' num2str(powerOfMagn_LocalAlignment) '.mat'];
save( filename_intermediate_output1, ...
'GrayImageStack_orig', ... %the original data
'input_fullPath', ... % its source
'NormalVecOrig', ... % the normal vector of the plane in original data, from which the applied rotation follows
'method', 'boxRadius', 'forCenterOfMass', 'subtractBackgroud', ...
'minWaveLength', 'maxWaveLength', ... %the settings for orientation analysis
'theta', 'magn', ... %the results
'downSamplFactorGrayscale', 'downSamplFactorOrient', ... %information on downsampling
'fg_closed', 'fg_inner', 'fg_innermost', ... % different definitions of foreground
'boxRadius_LocalAlignment', 'boxRadius_LocalAlignment_downSampled', 'powerOfMagn_LocalAlignment', ... %the settings for local alignment analysis
'localOrient', 'localCoherency' ) %the results
%load(filename_intermediate_output1)
%% local alignment restricted to foreground versions
% recall: this restricts to points in the foreground (_fg)
% and those for which the image patch used for orientation estimation was fully within the foreground (_inner)
% and those for which all pixels considered for local alignment had their respective box for orientation estimation fully in the foreground (_innermost)
localCoherency_fg = NaN (size(localCoherency));
localCoherency_fg(fg_closed_downSampl) = localCoherency(fg_closed_downSampl);
localCoherency_fg_inner = NaN (size(localCoherency));
localCoherency_fg_inner(fg_inner_downSampl) = localCoherency(fg_inner_downSampl);
localCoherency_fg_innermost = NaN (size(localCoherency));
localCoherency_fg_innermost(fg_innermost_downSampl) = localCoherency(fg_innermost_downSampl);
%% compute global alignment of ALL orientations present
% in the whole image, in the foreground (_fg), and of those for which the
% image patch used for orientation estimation was fully within the foreground (_inner)
[overallOrient, overallCoherency] = ComputeQTensor(theta, magn, powerOfMagn_LocalAlignment);
[overallOrient_fg, overallCoherency_fg] = ComputeQTensor(theta, magn.*fg_closed_downSampl, powerOfMagn_LocalAlignment);
[overallOrient_fg_inner, overallCoherency_fg_inner] = ComputeQTensor(theta, magn.*fg_inner_downSampl , powerOfMagn_LocalAlignment);
%% Alternatively, limit further analysis of local orientation to points
% for which the neighbourhood considered contains not too much pixels without orientation information
OrientInfoPresent = (~isnan(theta))&(~isnan(magn));
OrientInfoPresent(1:boxRadius, :) = 0;
OrientInfoPresent(end-boxRadius+1:end, :) = 0;
OrientInfoPresent(:, 1:boxRadius ) = 0;
OrientInfoPresent(:, end-boxRadius+1:end) = 0;
LocalOrientIsReliable = requiredFracOfOrientInfo * conv2( ones(size(theta)) , ones( 2*boxRadius_LocalAlignment_downSampled+1), 'same' ) ...
<= conv2( OrientInfoPresent , ones( 2*boxRadius_LocalAlignment_downSampled+1), 'same' ) ;
LocalOrientIsReliably_fg = requiredFracOfOrientInfo * conv2( ones(size(theta)) , ones( 2*boxRadius_LocalAlignment_downSampled+1), 'same' ) ...
<= conv2( OrientInfoPresent.*fg_closed_downSampl , ones( 2*boxRadius_LocalAlignment_downSampled+1), 'same' ) ;
[overallOrient_reliable, overallCoherency_reliable] = ComputeQTensor(theta, magn.*LocalOrientIsReliable, powerOfMagn_LocalAlignment);
[overallOrient_reliably_fg, overallCoherency_reliably_fg] = ComputeQTensor(theta, magn.*LocalOrientIsReliably_fg, powerOfMagn_LocalAlignment);
%% compute alignment of orientations with axis of anisotropy in the data
% alignment1 = linear = absolute value of scalar product between two vectors
% alignment2 = quadratic like the nematic order parameter, but with the (projected) axis of anisotropy as reference
% s = average over (3/2 cos^2 beta - 1/2)
% where beta = angle between orientation and (projected) axis of anisotropy, hence
% cos beta = scalar product of orientation with (projected) axis of anisotropy (when both are expressed as normalised vectors)
% values of s range from -0.5 (all detected orientations perpendicular to axis of anisotropy)
% to +1.0 (all detected orientations along the axis of anisotropy), but
% should be around zero (indicating little bias of the orientations by the anisotropy of imaging in the original z-axis)
alignment1 = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) )) / nansum(nansum( isfinite(theta) )) ;
alignment1_fg = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* fg_closed_downSampl )) / nansum(nansum( isfinite(theta) .* fg_closed_downSampl )) ;
alignment1_fg_inner = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* fg_inner_downSampl )) / nansum(nansum( isfinite(theta) .* fg_inner_downSampl )) ;
alignment2 = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 )) / nansum(nansum( isfinite(theta) )) ;
alignment2_fg = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* fg_closed_downSampl )) / nansum(nansum( isfinite(theta) .* fg_closed_downSampl )) ;
alignment2_fg_inner = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* fg_inner_downSampl )) / nansum(nansum( isfinite(theta) .* fg_inner_downSampl )) ;
% versions that are weighted by magnitude of the orientation information and/or image intensity at that pixel
alignment1_magn = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* magn )) / nansum(nansum( isfinite(theta) .* magn )) ;
alignment1_magn_fg = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* magn .* fg_closed_downSampl )) / nansum(nansum( isfinite(theta) .* magn .* fg_closed_downSampl )) ;
alignment1_magn_fg_inner = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* magn .* fg_inner_downSampl )) / nansum(nansum( isfinite(theta) .* magn .* fg_inner_downSampl )) ;
alignment2_magn = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* magn )) / nansum(nansum( isfinite(theta) .* magn )) ;
alignment2_magn_fg = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* magn .* fg_closed_downSampl )) / nansum(nansum( isfinite(theta) .* magn .* fg_closed_downSampl )) ;
alignment2_magn_fg_inner = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* magn .* fg_inner_downSampl )) / nansum(nansum( isfinite(theta) .* magn .* fg_inner_downSampl )) ;
alignment1_intens = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* GrayImageStack_downSamplTwice )) / nansum(nansum( isfinite(theta) .* GrayImageStack_downSamplTwice )) ;
alignment1_intens_fg = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* GrayImageStack_downSamplTwice .* fg_closed_downSampl )) / nansum(nansum( isfinite(theta) .* GrayImageStack_downSamplTwice .* fg_closed_downSampl )) ;
alignment1_intens_fg_inner = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* GrayImageStack_downSamplTwice .* fg_inner_downSampl )) / nansum(nansum( isfinite(theta) .* GrayImageStack_downSamplTwice .* fg_inner_downSampl )) ;
alignment2_intens = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* GrayImageStack_downSamplTwice )) / nansum(nansum( isfinite(theta) .* GrayImageStack_downSamplTwice )) ;
alignment2_intens_fg = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* GrayImageStack_downSamplTwice .* fg_closed_downSampl )) / nansum(nansum( isfinite(theta) .* GrayImageStack_downSamplTwice .* fg_closed_downSampl )) ;
alignment2_intens_fg_inner = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* GrayImageStack_downSamplTwice .* fg_inner_downSampl )) / nansum(nansum( isfinite(theta) .* GrayImageStack_downSamplTwice .* fg_inner_downSampl )) ;
alignment1_magn_intens = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* magn .* GrayImageStack_downSamplTwice )) / nansum(nansum( isfinite(theta) .* magn .* GrayImageStack_downSamplTwice )) ;
alignment1_magn_intens_fg = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* magn .* GrayImageStack_downSamplTwice .* fg_closed_downSampl )) / nansum(nansum( isfinite(theta) .* magn .* GrayImageStack_downSamplTwice .* fg_closed_downSampl )) ;
alignment1_magn_intens_fg_inner = -2/pi+ nansum(nansum( abs( cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ) .* magn .* GrayImageStack_downSamplTwice .* fg_inner_downSampl )) / nansum(nansum( isfinite(theta) .* magn .* GrayImageStack_downSamplTwice .* fg_inner_downSampl )) ;
alignment2_magn_intens = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* magn .* GrayImageStack_downSamplTwice )) / nansum(nansum( isfinite(theta) .* magn .* GrayImageStack_downSamplTwice )) ;
alignment2_magn_intens_fg = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* magn .* GrayImageStack_downSamplTwice .* fg_closed_downSampl )) / nansum(nansum( isfinite(theta) .* magn .* GrayImageStack_downSamplTwice .* fg_closed_downSampl )) ;
alignment2_magn_intens_fg_inner = -0.5 + nansum(nansum( (cos(theta).* cos(AngleAnisotropy_inThePlane_NORMALcoords) + sin(theta) .* sin(AngleAnisotropy_inThePlane_NORMALcoords) ).^2 .* magn .* GrayImageStack_downSamplTwice .* fg_inner_downSampl )) / nansum(nansum( isfinite(theta) .* magn .* GrayImageStack_downSamplTwice .* fg_inner_downSampl )) ;
%% collect results in structs with descriptions and values
%start with info on size of foreground
vals = struct();
descr = struct();
vals. size_x = size(theta,2);
descr.size_x = 'image size x';
vals. size_y = size(theta,1);
descr.size_y = 'image size y';
vals .pixels = sum(~isnan(theta(:)));
descr.pixels = 'pixels with orientation';
assert(all(unique(fg_closed) == [ 0; 1]))
vals .pixels_fg = sum(fg_closed(:));
descr.pixels_fg = 'foreground pixels';
assert(all(unique(fg_inner) == [ 0; 1]))
vals. pixels_fg_inner = sum(fg_inner(:));
descr.pixels_fg_inner = 'pixels for wich box was within foreground';
%% add results of coherencies in structs with descriptions and values
vals. overallCoherency = overallCoherency;
descr.overallCoherency = 'coherency - all orientations';
vals. overallTheta = atan2(overallOrient(2), overallOrient(1));
descr.overallTheta = 'theta - all orientations';
vals. overallCoherency_fg = overallCoherency_fg;
descr.overallCoherency_fg = 'coherency - all orientations on foreground';
vals. overallTheta_fg = atan2(overallOrient_fg(2), overallOrient_fg(1));
descr.overallTheta_fg = 'theta - all orientations on foreground';
vals. overallCoherency_fg_inner = overallCoherency_fg_inner;
descr.overallCoherency_fg_inner = 'coherency - all orientations for which box was within foreground';
vals. overallTheta_fg_inner = atan2(overallOrient_fg_inner(2), overallOrient_fg_inner(1));
descr.overallTheta_fg_inner = 'theta - all orientations for which box was within foreground';
vals. overallCoherency_reliab = overallCoherency_reliable;
descr.overallCoherency_reliab = 'coherency - all orientations for which box had mostly orientation information';
vals. overallTheta_reliab = atan2(overallOrient_reliable(2), overallOrient_reliable(1));
descr.overallTheta_reliab= 'theta - all orientations for which box box had mostly orientation information';
vals. overallCoherency_reliab_fg = overallCoherency_reliably_fg;
descr.overallCoherency_reliab_fg = 'coherency - all orientations for which box had mostly foreground and orientation information';
vals. overallTheta_reliab_fg = atan2(overallOrient_reliably_fg(2), overallOrient_reliably_fg(1));
descr.overallTheta_reliab_fg = 'theta - all orientations for which box had mostly foreground and orientation information';
vals. localCoherency = nanmean(localCoherency(:));
descr.localCoherency = 'mean local coherency (in boxes)';
vals. localCoherency_fg = nanmean(localCoherency_fg(:));
descr.localCoherency_fg = 'mean local coherency (in boxes) over foreground';
vals. localCoherency_fg_inner = nanmean(localCoherency_fg_inner(:));
descr.localCoherency_fg_inner = 'mean local coherency (in boxes) where box was within foreground';
vals. localCoherencyGaussian = nanmean(magn_smoothed(:));
descr.localCoherencyGaussian = 'mean local coherency (Gaussian)';
vals. localCoherencyGaussian_fg = nanmean(magn_smoothed_fg(:));
descr.localCoherencyGaussian_fg = 'mean local coherency (Gaussian) over foreground';
vals. localCoherencyGaussian_fg_inner = nanmean(magn_smoothed_fg_inner(:));
descr.localCoherencyGaussian_fg_inner = 'mean local coherency (Gaussian) where box was within foreground';
%% add info on anisotropy of data
vals. normalVec_x = NormalVecOrig(1);
descr.normalVec_x = 'normal vector in original data n_x';
vals. normalVec_y = NormalVecOrig(2);
descr.normalVec_y = 'normal vector in original data n_y';
vals. normalVec_z = NormalVecOrig(3);
descr.normalVec_z = 'normal vector in original data n_z';
vals. AngleAnisotropyProjection = AngleAnisotropyProjection;
descr.AngleAnisotropyProjection = 'angle between projection axis and anisotropy (radians)';
vals. orientationBias_linear = alignment1_fg_inner;
descr.orientationBias_linear = 'bias of orientations by anisotropy - linear, where box was within foreground';
vals. orientationBias_quadratic = alignment2_fg_inner;
descr.orientationBias_quadratic = 'bias of orientations by anisotropy - quadratic (as for nematic order parameter), where box was within foreground';
%% prepare writing of data
vals_mat = struct2array(vals);
all_vals_mat = [all_vals_mat; vals_mat] ;
folder_to_subfolders = strsplit(input_folder, filesep);
folders_arr = [ folders_arr ; ...
{input_folder} folder_to_subfolders(end-4), folder_to_subfolders(end-3), folder_to_subfolders(end-2), folder_to_subfolders(end-1), ...
leading_ROIinfo_inFilename];
%recall: the code assumes that folder_to_subfolders
% contains <Stage>/<Embryo>/<RegionOfTheEye>/<DerotationConditions>/
% and the different ROIs within one folder are distinguished by leading_ROIinfo_inFilename
%% write results to text file
filename_intermediate_output2 = [filename_intermediate_output(1:end-4) ...
'_coherencyRadius' num2str(boxRadius_LocalAlignment) 'Power' num2str(powerOfMagn_LocalAlignment) '.txt'];
dlmwrite( filename_intermediate_output2 , ['coherencies obtained from'], 'delimiter','') %header
dlmwrite( filename_intermediate_output2 , [filename_intermediate_output2] , '-append', 'delimiter','') % origin of data
dlmwrite( filename_intermediate_output2 , ['Parameters used'] , '-append', 'delimiter','')
dlmwrite( filename_intermediate_output2 , [num2str(boxRadius) , ' BoxRadius'], '-append', 'delimiter','') % parameters used
dlmwrite( filename_intermediate_output2 , [num2str(boxRadius_LocalAlignment) , ' BoxRadius for Local Alignment'], '-append', 'delimiter','') % parameters used
dlmwrite( filename_intermediate_output2 , [num2str(boxRadius_LocalAlignment_downSampled) , ' BoxRadius for Local Alignment in downsampled pixels'], '-append', 'delimiter','') % parameters used
dlmwrite( filename_intermediate_output2 , [num2str(powerOfMagn_LocalAlignment), ' Power of Magnitude for Local Alignment'], '-append', 'delimiter','') % parameters used
dlmwrite( filename_intermediate_output2 , [num2str(mySigmaForDirectySmoothing) , ' sigma for smoothing'], '-append', 'delimiter','') % parameters used
dlmwrite( filename_intermediate_output2 , [num2str(myStrengthExponent), ' Power of Magnitude for smoothing'], '-append', 'delimiter','') % parameters used
dlmwrite( filename_intermediate_output2 , [' '], '-append', 'delimiter','') % empty line
fieldnameslist = fieldnames(vals);
for kk = 1:numel(fieldnameslist)
dlmwrite( filename_intermediate_output2 , [num2str(vals.(fieldnameslist{kk})), ' ', descr.(fieldnameslist{kk})], '-append', 'delimiter','')
end
%% plotting
%% Color and other conventions for plotting
col_director = [0.4 0.4 1.0]; %NOT 'blue', as this is too dark
col_localOrient = [1.0 0.4 0.4];
show_localOrient = false; % show locally averaged orientation that corresponds to the computed alignment
col_anisotropy = [0.2 1 0.2] ;
show_anisotropy = true;
centerQuiverOnPixels = true; %show orientations centered over their location
savePlots = false;
useMagnForPlotting = false; % scale quiver plots by orientation magnitude
%spacing12 between plotted orientation [integer, =n plots only every n-th orientation]
spacing12 = ceil(4.0/downSamplTotal); % a reasonable start for ROIs of few hundred pixels
%spacing12 = 1
% {
%% plotting of orig image with orientation field
myData = GrayImageStack_orig(:,:,useSlice);
myData = myData/ max(GrayImageStack_orig(:)); %rescale for plotting
myData = 0.2+0.8* myData; %make the data more bright in plotting
assert(max(myData(:)) <= 1.0, 'Rescale data!')
theta_slice = theta(:,:,useSlice);
magn_slice = GrayImageStack_downSamplTwice .* magn (:,:,useSlice);
[x12, y12] = meshgrid( 0.5+downSamplTotal*(-0.5+(1:size(theta_slice,2)) ), ...
0.5+downSamplTotal*(-0.5+(1:size(theta_slice,1)) ) );
% rescale positions that are given as row/column in the arrays of data
% after downsampling twice, back to their positions in the original data
scaleToOrig_x = @(xInDownSamplTwice) 0.5+(xInDownSamplTwice-0.5)*downSamplTotal;
scaleToOrig_y = @(yInDownSamplTwice) 0.5+(yInDownSamplTwice-0.5)*downSamplTotal;
u12 = magn_slice.^useMagnForPlotting .* cos( theta_slice ) ;
v12 = magn_slice.^useMagnForPlotting .* -sin( theta_slice ) ; %negative sign to transform from theta in normal coord layout to coord layout used by MATLAB (y downwards in plotting that is started by imshow() )
u_localOrient = localOrient(:,:,1) .* localCoherency;
v_localOrient = localOrient(:,:,2) .* localCoherency;
%% plotting itself
Fig12 = figure('PaperOrientation', 'landscape', 'Position', [0, 0, 1200, 700]);
[axesHandles12, positions12] = tight_subplot(1,1,[.08 .05],[.05 .01],[.05 .02]) ; %gap, margin_height, margin_width)
fig12A = axesHandles12(1);
axes(fig12A) %make the existing axes current
OrigData12 = imshow(myData); % (myData * 2 - 3);
hold on
% add overlay to indicate the foreground - see https://de.mathworks.com/company/newsletters/articles/image-overlay-using-transparency.html
% show true-color all-red image on top
red = cat(3, ones(size(myData)), zeros(size(myData)), zeros(size(myData)));
foreground_plot = imshow(red);
% but set its transparency according to fg_closed_orig
set(foreground_plot, 'AlphaData', 0.1* fg_closed_orig)
% orientations plotted as head-less vectors
quiv_onOrigData1 = quiver(x12(1:spacing12:end,1:spacing12:end), y12(1:spacing12:end,1:spacing12:end), u12(1:spacing12:end,1:spacing12:end), v12(1:spacing12:end,1:spacing12:end));
if centerQuiverOnPixels
%duplicate quiver (with opposite sign) to have coherent appearance
quiv_onOrigData2 = quiver(x12(1:spacing12:end,1:spacing12:end), y12(1:spacing12:end,1:spacing12:end), -u12(1:spacing12:end,1:spacing12:end), -v12(1:spacing12:end,1:spacing12:end));
end
% modify quiver plot(s)
quiv_onOrigData1.ShowArrowHead = 'off';
quiv_onOrigData1.LineWidth = 1.5; %(default: 0.5)
quiv_onOrigData1.AutoScaleFactor = 1.0 ;
quiv_onOrigData1.Color = col_director;
if centerQuiverOnPixels
%give the duplicate quiver the same properties, especially the same color
quiv_onOrigData2.ShowArrowHead = quiv_onOrigData1.ShowArrowHead ;
quiv_onOrigData2.LineWidth = quiv_onOrigData1.LineWidth ;
quiv_onOrigData2.AutoScaleFactor = quiv_onOrigData1.AutoScaleFactor ;
quiv_onOrigData2.Color = quiv_onOrigData1.Color ;
end
if show_localOrient
quiv_localOrient1 = quiver(x12(1:spacing12:end,1:spacing12:end), y12(1:spacing12:end,1:spacing12:end), u_localOrient(1:spacing12:end,1:spacing12:end), -v_localOrient(1:spacing12:end,1:spacing12:end));
%Note negative sign on y-component to transform from theta in normal coord layout to coord layout used by MATLAB (y downwards in plotting that is started by imshow() )
if centerQuiverOnPixels
%duplicate quiver (with opposite sign) to have coherent appearance
quiv_localOrient2 = quiver(x12(1:spacing12:end,1:spacing12:end), y12(1:spacing12:end,1:spacing12:end), -u_localOrient(1:spacing12:end,1:spacing12:end), v_localOrient(1:spacing12:end,1:spacing12:end));
end
% modify quiver plot(s)
quiv_localOrient1.ShowArrowHead = 'off';
quiv_localOrient1.LineWidth = 0.5; %(default: 0.5)
quiv_localOrient1.AutoScaleFactor = 1.0 ;
quiv_localOrient1.Color = col_localOrient;
if centerQuiverOnPixels
%give the duplicate quiver the same properties, especially the same color
quiv_localOrient2.ShowArrowHead = quiv_localOrient1.ShowArrowHead ;
quiv_localOrient2.LineWidth = quiv_localOrient1.LineWidth ;
quiv_localOrient2.AutoScaleFactor = quiv_localOrient1.AutoScaleFactor ;
quiv_localOrient2.Color = quiv_localOrient1.Color ;
end
end
if show_anisotropy
% plot projection of the anisotropy outside of the plot area
quiv_anisotropy = quiver( (size_x_orig+15)*[1 1], 0.5*(size_y_orig)*[1 1], ...
[AnisotropyAxis_inThePlane_MATLABcoords(1), -AnisotropyAxis_inThePlane_MATLABcoords(1)], ...
[AnisotropyAxis_inThePlane_MATLABcoords(2), -AnisotropyAxis_inThePlane_MATLABcoords(2)] );
quiv_anisotropy.ShowArrowHead = 'off';
quiv_anisotropy.LineWidth = 5;
quiv_anisotropy.AutoScaleFactor = 10;
quiv_anisotropy.Color = col_anisotropy;
text( (size_x_orig+15), 0.5*(size_y_orig)-20, ...
'anisotropy axis (projected)')
text( (size_x_orig+15), 0.5*(size_y_orig)+20, [{'bias into anisotropy direction' , 'among orientations where box was within foreground', ['linear = ', num2str(alignment1_magn_intens_fg_inner)], ['quadratic = ', num2str(alignment2_magn_intens_fg_inner)] }])
text( (size_x_orig+15), 0.5*(size_y_orig)+60, ...
{'angle between', 'anisotropy and projection', ['= ' num2str(AngleAnisotropyProjection*180/pi) ' degree']} )
end
axis equal
title(['orig image with foreground highlighted, orientations'] )
drawnow
pause(0.1)
if show_anisotropy
%make more space for title and annotations on the right side
currAx = gca();
currAx.Position = currAx.Position - [0 0 0.05 0.05];
end
print(Fig12, [filename_intermediate_output(1:end-4) '_Orientations'],'-dpdf', '-r0', '-fillpage')
print(Fig12, [filename_intermediate_output(1:end-4) '_Orientations'],'-dpng', '-r0')
close all
%% plotting the distribution of orientations to check for impact of anisotropy in imaging
%% binning
% bin the data explicitly to allow for weights being considered
% bin into interval [0,pi], then duplicate data from theta to pi+theta to represent nematics
NbinsPolar = 128;
theta_for_hists = mod(theta, pi);
assert ( min(theta_for_hists(:))>= 0 & max(theta_for_hists(:))<=pi, 'theta falls out of range [0, pi]' )
EdgesPolar = linspace(0, pi, NbinsPolar+1);
% this ensures to have all data fall into the bins (NaN bins are only due to theta value NaN)
assert ( EdgesPolar(1) == 0 & EdgesPolar(end) == pi , 'Incorrect bins')
EdgesNematic = [EdgesPolar(1:end-1) pi+EdgesPolar];
whichBin = discretize(theta_for_hists, EdgesPolar);
Freq = zeros([1 numel(EdgesPolar)-1]);
Freq_fg = zeros([1 numel(EdgesPolar)-1]);
Freq_fg_inner = zeros([1 numel(EdgesPolar)-1]);
%Freq_fg_innermost = zeros([1 numel(EdgesPolar)-1]);
% _innermost does not add information, because it makes sure that coherency estimation is within the foreground
Freq_magn = zeros([1 numel(EdgesPolar)-1]);
Freq_magn_fg = zeros([1 numel(EdgesPolar)-1]);
Freq_magn_fg_inner = zeros([1 numel(EdgesPolar)-1]);
Freq_intens = zeros([1 numel(EdgesPolar)-1]);
Freq_intens_fg = zeros([1 numel(EdgesPolar)-1]);
Freq_intens_fg_inner = zeros([1 numel(EdgesPolar)-1]);
Freq_magn_intens = zeros([1 numel(EdgesPolar)-1]);
Freq_magn_intens_fg = zeros([1 numel(EdgesPolar)-1]);
Freq_magn_intens_fg_inner = zeros([1 numel(EdgesPolar)-1]);
for kk=1:numel(EdgesPolar)-1
Freq(kk) = sum( sum( (whichBin==kk) ));
Freq_fg(kk) = sum( sum( (whichBin==kk) .* fg_closed_downSampl));
Freq_fg_inner(kk) = sum( sum( (whichBin==kk) .* fg_inner_downSampl));
%Freq_fg_innermost(kk)= sum( sum( (whichBin==kk) .* fg_innermost_downSampl));
Freq_magn(kk) = nansum(nansum( (whichBin==kk) .* magn ));
Freq_magn_fg(kk) = nansum(nansum( (whichBin==kk) .* magn .* fg_closed_downSampl));
Freq_magn_fg_inner(kk) = nansum(nansum( (whichBin==kk) .* magn .* fg_inner_downSampl ));
Freq_intens(kk) = sum( sum( (whichBin==kk) .* GrayImageStack_downSamplTwice));
Freq_intens_fg(kk) = sum( sum( (whichBin==kk) .* GrayImageStack_downSamplTwice .* fg_closed_downSampl));
Freq_intens_fg_inner(kk) = sum( sum( (whichBin==kk) .* GrayImageStack_downSamplTwice .* fg_inner_downSampl ));
Freq_magn_intens(kk) = nansum(nansum( (whichBin==kk) .* magn .* GrayImageStack_downSamplTwice));
Freq_magn_intens_fg(kk) = nansum(nansum( (whichBin==kk) .* magn .* GrayImageStack_downSamplTwice .* fg_closed_downSampl));
Freq_magn_intens_fg_inner(kk) = nansum(nansum( (whichBin==kk) .* magn .* GrayImageStack_downSamplTwice .* fg_inner_downSampl ));
end
%% plotting of orientation distribution itself
colUse_All = 'black' ;
colUse_fg = [0 0.8 0]; %dark green
colUse_fg_inner = 'blue';
colUse_fg_innermost = 'red';
Fig13 = figure(13); %'PaperSize', [11 8.5]) %'Position', [100 100 300 500]);
Fig13.Position = [0, 0, 1200, 900] ;
Fig13A = polaraxes('Units', 'normalized', 'OuterPosition', [ 0 1/2 1/2 1/2]) ;
Fig13B = polaraxes('Units', 'normalized', 'OuterPosition', [ 1/2 1/2 1/2 1/2]) ;
Fig13C = polaraxes('Units', 'normalized', 'OuterPosition', [ 0 0 1/2 1/2]) ;
Fig13D = polaraxes('Units', 'normalized', 'OuterPosition', [ 1/2 0 1/2 1/2]) ;
subplot(Fig13A); %makes the existing subplot current
hold on
pHist = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq Freq ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_All);
pHist_fg = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_fg Freq_fg ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_fg);
pHist_fg_inner = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_fg_inner Freq_fg_inner ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_fg_inner);
% pHist_fg_innermost= polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_fg_innermost Freq_fg_innermost], ...
% 'DisplayStyle','stairs', 'EdgeColor', colUse_fg_innermost);
% add projection of the anisotropy
pAnisotropyAxis = polarplot( [AngleAnisotropy_inThePlane_NORMALcoords, AngleAnisotropy_inThePlane_NORMALcoords+pi], [0.8, 0.8]* max(Freq), 'Color', 'red' );
legend([pHist, pHist_fg, pHist_fg_inner, ... %, pHist_fg_innermost,...
pAnisotropyAxis], ...
{'all orientations', 'orientations in foreground', 'where box was within foreground', ... % 'where coherency box was within foreground', ...
'axis of anisotropy (projected)'}, ...
'Location', 'southwest')
text(-pi/4, max(Freq), [{'bias into anisotropy direction' , 'among orientations where box was within foreground', ['linear = ', num2str(alignment1_fg_inner)], ['quadratic = ', num2str(alignment2_fg_inner)] }])
text( pi/4, 1.5*max(Freq), ...
{'angle between', 'anisotropy and projection', ['= ' num2str(AngleAnisotropyProjection*180/pi) ' degree']} )
title('frequency of orientations')
subplot(Fig13B); %makes the existing subplot current
hold on
pHist_magn = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_magn Freq_magn ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_All);
pHist_magn_fg = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_magn_fg Freq_magn_fg ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_fg);
pHist_magn_fg_inner = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_magn_fg_inner Freq_magn_fg_inner ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_fg_inner);
pAnisotropyAxis = polarplot( [AngleAnisotropy_inThePlane_NORMALcoords, AngleAnisotropy_inThePlane_NORMALcoords+pi], [0.8, 0.8]* max(Freq_magn), 'Color', 'red' );
%legend([pHist_magn, pHist_magn_fg, pHist_magn_fg_inner, ...
% pAnisotropyAxis], ...
% {'all orientations', 'orientations in foreground', 'where box was within foreground', ...
% 'axis of anisotropy (projected)'}, ...
% 'Location', 'southwest')
text(-pi/4, max(Freq_magn), [{'bias into anisotropy direction' , 'among orientations where box was within foreground', ['linear = ', num2str(alignment1_magn_fg_inner)], ['quadratic = ', num2str(alignment2_magn_fg_inner)] }])
title('frequency of orientations - magnitude weighted')
subplot(Fig13C); %makes the existing subplot current
hold on
pHist_intens = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_intens Freq_intens ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_All);
pHist_intens_fg = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_intens_fg Freq_intens_fg ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_fg);
pHist_intens_fg_inner = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_intens_fg_inner Freq_intens_fg_inner ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_fg_inner);
pAnisotropyAxis = polarplot( [AngleAnisotropy_inThePlane_NORMALcoords, AngleAnisotropy_inThePlane_NORMALcoords+pi], [0.8, 0.8]* max(Freq_intens), 'Color', 'red' );
%legend([pHist_intens, pHist_intens_fg, pHist_intens_fg_inner, ...
% pAnisotropyAxis], ...
% {'all orientations', 'orientations in foreground', 'where box was within foreground', ...
% 'axis of anisotropy (projected)'}, ...
% 'Location', 'southwest')
text(-pi/4, max(Freq_intens), [{'bias into anisotropy direction' , 'among orientations where box was within foreground', ['linear = ', num2str(alignment1_intens_fg_inner)], ['quadratic = ', num2str(alignment2_intens_fg_inner)] }])
title('frequency of orientations - intensity weighted')
subplot(Fig13D); %makes the existing subplot current
hold on
pHist_magn_intens = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_magn_intens Freq_magn_intens ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_All);
pHist_magn_intens_fg = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_magn_intens_fg Freq_magn_intens_fg ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_fg);
pHist_magn_intens_fg_inner = polarhistogram('BinEdges',EdgesNematic,'BinCounts',[Freq_magn_intens_fg_inner Freq_magn_intens_fg_inner ], ...
'DisplayStyle','stairs', 'EdgeColor', colUse_fg_inner);
pAnisotropyAxis = polarplot( [AngleAnisotropy_inThePlane_NORMALcoords, AngleAnisotropy_inThePlane_NORMALcoords+pi], [0.8, 0.8]* max(Freq_magn_intens), 'Color', 'red' );
%legend([pHist_magn_intens, pHist_magn_intens_fg, pHist_magn_intens_fg_inner, ...
% pAnisotropyAxis], ...
% {'all orientations', 'orientations in foreground', 'where box was within foreground', ...
% 'axis of anisotropy (projected)'}, ...
% 'Location', 'southwest')
text(-pi/4, max(Freq_magn_intens), [{'bias into anisotropy direction' , 'among orientations where box was within foreground', ['linear = ', num2str(alignment1_magn_intens_fg_inner)], ['quadratic = ', num2str(alignment2_magn_intens_fg_inner)] }])
title('frequency of orientations - magnitude and intensity weighted')
drawnow
pause(0.1)
print(Fig13, [filename_intermediate_output(1:end-4) '_Anisotropy' ],'-dpdf', '-r0', '-fillpage')
print(Fig13, [filename_intermediate_output(1:end-4) '_Anisotropy' ],'-dpng', '-r0')
close all
%% plotting of local coherency and locally dominant orientation
% NOTE: this plot is only possible at the resolution of the orientation
% estimate, that is after the downsampling
[x14, y14] = meshgrid(1:size(localCoherency,2), 1:size(localCoherency,1) );
u_localOrient = localOrient(:,:,1) .* localCoherency;
v_localOrient = localOrient(:,:,2) .* localCoherency;
Fig14 = figure('PaperOrientation', 'landscape', 'Position', [0, 0, 1200, 700]);
[axesHandles14, positions14] = tight_subplot(1,1,[.08 .05],[.05 .01],[.05 .02]) ; %gap, margin_height, margin_width)
fig14A = axesHandles14(1);
axes(fig14A) %make the existing axes current
OrigData14 = imshow(localCoherency); % (myData * 2 - 3);
hold on
if true % if show_localOrient
quiv_localOrient1 = quiver(x14(1:spacing12:end,1:spacing12:end), y14(1:spacing12:end,1:spacing12:end), u_localOrient(1:spacing12:end,1:spacing12:end), -v_localOrient(1:spacing12:end,1:spacing12:end));
%Note negative sign on y-component to transform from theta in normal coord layout to coord layout used by MATLAB (y downwards in plotting that is started by imshow() )
if centerQuiverOnPixels
%duplicate quiver (with opposite sign) to have coherent appearance
quiv_localOrient2 = quiver(x14(1:spacing12:end,1:spacing12:end), y14(1:spacing12:end,1:spacing12:end), -u_localOrient(1:spacing12:end,1:spacing12:end), v_localOrient(1:spacing12:end,1:spacing12:end));
end
% modify quiver plot(s)
quiv_localOrient1.ShowArrowHead = 'off';
quiv_localOrient1.LineWidth = 0.5; %(default: 0.5)
quiv_localOrient1.AutoScaleFactor = 1.0 ;
quiv_localOrient1.Color = col_localOrient;
if centerQuiverOnPixels
%give the duplicate quiver the same properties, especially the same color
quiv_localOrient2.ShowArrowHead = quiv_localOrient1.ShowArrowHead ;
quiv_localOrient2.LineWidth = quiv_localOrient1.LineWidth ;
quiv_localOrient2.AutoScaleFactor = quiv_localOrient1.AutoScaleFactor ;
quiv_localOrient2.Color = quiv_localOrient1.Color ;
end
end
axis equal
%{
cbar = colorbar;
cbar.Label.String = 'Local Coherency';
%}
title(['locally dominant orientation (within radius = ' num2str(boxRadius_LocalAlignment) '(orig pixels) at power ' num2str(powerOfMagn_LocalAlignment) ')' ])
drawnow
pause(0.1)
print(Fig14, [filename_intermediate_output(1:end-4) '_coherencyRadius' num2str(boxRadius_LocalAlignment) 'Power' num2str(powerOfMagn_LocalAlignment) ],'-dpdf', '-r0', '-fillpage')
print(Fig14, [filename_intermediate_output(1:end-4) '_coherencyRadius' num2str(boxRadius_LocalAlignment) 'Power' num2str(powerOfMagn_LocalAlignment) ],'-dpng', '-r0')
close all
end
%% save collected coherency for each ROI to file output_collected as .mat and as .csv for subsequent statistical analysis
% basically write all_vals_mat with descr as its header, but precede by
% information on the respective path stored in folders_arr
%recall: the code assumes that folder_to_subfolders
% contains <Stage>/<Embryo>/<RegionOfTheEye>/<DerotationConditions>/
% and the different ROIs within one folder are distinguished by leading_ROIinfo_inFilename
% save as .mat file
save([ output_collected(1:end-4) '.mat'], 'descr', 'folders_arr', 'all_vals_mat')
% save as .csv
% write the header
fileID = fopen(output_collected,'w');
fprintf(fileID, ['full path' '\t' 'stage' '\t' 'embryo' '\t' 'region of the eye' '\t' ...
'derotation conditions' '\t' 'ROI'] );
fieldnameslist = fieldnames(descr);
for kk = 1:numel(fieldnameslist)
fprintf(fileID, [descr.(fieldnameslist{kk}) '\t'] );
end
fprintf(fileID, '\n');
fclose(fileID);