% 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); % write the content for kk=1:size(folders_arr,1) %write filenames fileID = fopen(output_collected,'a'); fprintf(fileID, [ folders_arr{kk,1} '\t' ... folders_arr{kk,2} '\t' ... folders_arr{kk,3} '\t' ... folders_arr{kk,4} '\t' ... folders_arr{kk,5} '\t' ... folders_arr{kk,6} '\t' ]); fclose(fileID); %write statistics dlmwrite( output_collected , all_vals_mat(kk,:), '-append', 'delimiter','\t') %collected data end