initial commit

This commit is contained in:
2026-08-07 15:56:42 +09:00
commit 91ad25aca9
1012 changed files with 159314 additions and 0 deletions
+174
View File
@@ -0,0 +1,174 @@
% [PSD, freq] = power_spectral_density(img, varargin)
% Computes the power spectral density of the provided 3D image.
% Can handle non-cube arrays but assumes the voxel is isotropic
%
% Inputs:
% img input image (2D or 3D)
%
% Parameters:
% thickring Normally the pixels get assigned to the closest integer pixel ring in Fourier domain.
% With thickring the thickness of the rings is increased by
% thickring, so each ring gets more pixels and more statistics
% auto_binning apply binning if dimensions are significanlty different along each axis
% mask bool array equal to false for ignored pixels of the fft space
%
% Outputs:
% PSD PSD curve values
% freq normalized spatial frequencies to 1
%
% Example of use:
% img = randn(512,512,512);
% utils.power_spectral_density(img, 'thickring', 3);
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  Except where otherwise noted, this work is licensed under a          |
%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            |
%|  International (CC BY-NC-SA 4.0) license.                             |
%|                                                                       |
%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    |
%|                                                                       |
%|      Author: CXS group, PSI  |
%*-----------------------------------------------------------------------*
% You may use this code with the following provisions:
%
% If the code is fully or partially redistributed, or rewritten in another
% computing language this notice should be included in the redistribution.
%
% If this code, or subfunctions or parts of it, is used for research in a
% publication or if it is fully or partially rewritten for another
% computing language the authors and institution should be acknowledged
% in written form in the publication: Data processing was carried out
% using the cSAXS matlab package developed by the CXS group,
% Paul Scherrer Institut, Switzerland.
% Variations on the latter text can be incorporated upon discussion with
% the CXS group if needed to more specifically reflect the use of the package
% for the published work.
%
% A publication that focuses on describing features, or parameters, that
% are already existing in the code should be first discussed with the
% authors.
%
% This code and subroutines are part of a continuous development, they
% are provided as they are without guarantees or liability on part
% of PSI or the authors. It is the user responsibility to ensure its
% proper use and the correctness of the results.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [PSD, freq] = power_spectral_density(img, air, varargin)
import math.isint
import utils.*
%%%%%%%%%%%%%%%%%%%%% PROCESS PARAMETERS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parser = inputParser;
parser.addParameter('thickring', 3 , @isnumeric ) % thick ring in Fourier domain
parser.addParameter('auto_binning', true , @islogical ) % bin FRC before calculating rings, it makes calculations faster
parser.addParameter('max_rings', 200 , @isnumeric ) % maximal number of rings if autobinning is used
parser.addParameter('mask', true, @islogical ) % bool array, equal to false for ignored pixels of the fft space
parser.addParameter('windowautopos', true, @islogical ) % automatically position plotted window
parser.addParameter('figure_id', 101, @isint) % call figure(figure_id)
parser.parse(varargin{:})
param = parser.Results;
disp('Calculating PSD');
% remove masked values from consideration (i.e. for laminography)
Fimg = abs(bsxfun(@times,fftn(img) , param.mask+eps)).^2;
[ny,nx,nz] = size(img);
nmin = min(size(img));
% avoid edge artefacts
img = img .* tukeywin(size(img,1),0.5) .* tukeywin(size(img,2),0.5)' .* reshape(tukeywin(size(img,3),0.5),1,1,[]);
thickring = param.thickring;
if param.auto_binning
% bin the correlation values to speed up the following calculations
% find optimal binning to make the volumes roughly cubic
bin = ceil(thickring/4) * floor(size(img)/ nmin);
% avoid too large number of rings
bin = max(bin, floor(nmin ./ param.max_rings));
if any(bin > 1)
fprintf('Autobinning %ix%ix%i \n', bin)
thickring = ceil(thickring / min(bin));
% fftshift and crop the arrays to make their size dividable by binning number
if ismatrix(img); bin(3) = 1; end
% force the binning to be centered
subgrid = {fftshift(ceil(bin(1)/2):(floor(ny/bin(1))*bin(1)-floor(bin(1)/2)-1)), ...
fftshift(ceil(bin(2)/2):(floor(nx/bin(2))*bin(2)-floor(bin(2)/2)-1)), ...
fftshift(ceil(bin(3)/2):(floor(nz/bin(3))*bin(3)-floor(bin(3)/2)-1))};
if ismatrix(img); subgrid(3) = [] ; end
% binning makes the shell / ring calculations much faster
Fimg = ifftshift(utils.binning_3D(Fimg(subgrid{:}), bin));
end
else
bin = 1;
end
[ny,nx,nz] = size(Fimg);
nmax = max([nx ny nz]);
nmin = min(size(img));
% empirically tested that thickring should be >=3 along the smallest axis to avoid FRC undesampling
thickring = max(thickring, ceil(nmax/nmin));
param.thickring = thickring;
rnyquist = floor(nmax/2);
freq = [0:rnyquist];
x = ifftshift([-fix(nx/2):ceil(nx/2)-1])*floor(nmax/2)/floor(nx/2);
y = ifftshift([-fix(ny/2):ceil(ny/2)-1])*floor(nmax/2)/floor(ny/2);
if nz ~= 1
z = ifftshift([-fix(nz/2):ceil(nz/2)-1])*floor(nmax/2)/floor(nz/2);
else
z = 0;
end
[X,Y,Z] = meshgrid(single(x),single(y),single(z));
index = (sqrt(X.^2+Y.^2+Z.^2));
clear X Y Z
Nr = length(freq);
for ii = 1:Nr
r = freq(ii);
progressbar(ii,Nr)
% calculate always thickring, min ring thickness is given by the smallest axis
ind = index>=r-thickring/2 & index<=r+thickring/2 ;
ind = find(ind); % find seems to be faster then indexing
auxFimg = Fimg(ind);
C(ii) = sum(auxFimg);
n(ii) = numel(ind); % Number of points
end
n = n*prod(bin); % account for larger number of elements in the binned voxels
PSD = abs(C) ./ n;
freq = freq/freq(end);
figure(param.figure_id)
hold all
plot(freq, PSD)
hold off
set(gca, 'yscale', 'log')
ylabel('Power spectral density')
xlabel('Spatial frequency/Nyquist')
grid on
end