diff --git a/Addons/ContrastRatio.m b/Addons/ContrastRatio.m new file mode 100644 index 0000000000000000000000000000000000000000..b92c1220e7c27d6d0b18191881c8456a0772461b --- /dev/null +++ b/Addons/ContrastRatio.m @@ -0,0 +1,59 @@ +function medianCR = ContrastRatio(MatOut) + + + +%Size of patchs +SZ = 64; +SX = 64; + +PWTD = CropZeros(MatOut); +[Nz, Nx] = size(PWTD); + + +PWTD = PWTD(1:floor(Nz/SZ)*SZ,1+Nx-floor(Nx/SX)*SX:end); +[Nz, Nx] = size(PWTD); + +N11 = SZ*ones(1,Nz/SZ); +N22 = SX*ones(1,Nx/SX); +PWTD2 = mat2cell(PWTD,N11,N22); +PWTD2 = reshape(PWTD2,Nz/SZ*Nx/SX,[]); + +%Define reference Patch +%R21 = PWTD(zz:zz+SZ-1,xx:xx+SX-1); % fixed position +patchs=zeros(1,length(PWTD2)); +for i=1:length(PWTD2) +R11 = PWTD2{i}; +if IsPatchValid(R11) +patchs(i) = mean(R11(:).^2); +else + patchs(i)=0; +end +end + +patchs(patchs == 0 ) = NaN; +[A,I] = min(patchs); +R21 = PWTD2{I}; +RefPower = mean(R21(:).^2); + +% Calc CR +CR=zeros(1,length(PWTD2)); +for i=1:length(PWTD2) +R11 = PWTD2{i}; +CR(i) = 10*log10(mean(R11(:).^2) / RefPower); % = mean(R21(:).^2) +end +CR = CR(isfinite(CR)); +CR = CR(CR>0); + +medianCR = median(CR,'all'); + +% show boxplot +% listis= boxplot(CR, 'Whisker', 2); +% CRis=get(listis(6)).YData; +% ylabel('CR [dB]') +% fprintf('CR: %f\n',[CRis(1)]); + +% show reference patch +% figure +% imagesc(R21) + +end \ No newline at end of file diff --git a/Addons/CropZeros.m b/Addons/CropZeros.m new file mode 100644 index 0000000000000000000000000000000000000000..ff833d022b76c0c2a9902d1ef6aec7aae5598539 --- /dev/null +++ b/Addons/CropZeros.m @@ -0,0 +1,44 @@ +function OutMat = CropZeros(InMat) + % CropZeros removes border rows and columns if they have too many zeros. + % + % Input: + % InMat - The input matrix. + % + % Output: + % OutMat - The cropped matrix. + + % Define the threshold for zero ratio + zeroThreshold = 0.95; + + % Get matrix dimensions + [rows, cols] = size(InMat); + + % Initialize start and end indices for rows and columns + rowStart = 1; + rowEnd = rows; + colStart = 1; + colEnd = cols; + + % Check the top rows for zero ratio + while rowStart <= rowEnd && nnz(InMat(rowStart, :) == 0) / cols > zeroThreshold + rowStart = rowStart + 1; + end + + % Check the bottom rows for zero ratio + while rowStart <= rowEnd && nnz(InMat(rowEnd, :) == 0) / cols > zeroThreshold + rowEnd = rowEnd - 1; + end + + % Check the left columns for zero ratio + while colStart <= colEnd && nnz(InMat(:, colStart) == 0) / rows > zeroThreshold + colStart = colStart + 1; + end + + % Check the right columns for zero ratio + while colStart <= colEnd && nnz(InMat(:, colEnd) == 0) / rows > zeroThreshold + colEnd = colEnd - 1; + end + + % Crop the matrix based on the computed indices + OutMat = InMat(rowStart:rowEnd, colStart:colEnd); +end diff --git a/Addons/IsPatchValid.m b/Addons/IsPatchValid.m new file mode 100644 index 0000000000000000000000000000000000000000..63858ca401e4966941442b1136c7125e72ad4405 --- /dev/null +++ b/Addons/IsPatchValid.m @@ -0,0 +1,25 @@ +function isValid = IsPatchValid(InMat) + % IsPatchValid checks if all quadrants of InMat contain at least one non-zero element. + % + % Input: + % InMat - The input matrix. + % + % Output: + % isValid - Returns true if all quadrants have at least one non-zero element; false otherwise. + + % Get the dimensions of the matrix + [rows, cols] = size(InMat); + + % Find the midpoint indices for rows and columns + midRow = ceil(rows / 2); + midCol = ceil(cols / 2); + + % Define the four quadrants + Q1 = InMat(1:midRow, 1:midCol); % Top-left quadrant + Q2 = InMat(1:midRow, midCol+1:end); % Top-right quadrant + Q3 = InMat(midRow+1:end, 1:midCol); % Bottom-left quadrant + Q4 = InMat(midRow+1:end, midCol+1:end); % Bottom-right quadrant + + % Check if each quadrant contains at least one non-zero element + isValid = any(Q1(:)) && any(Q2(:)) && any(Q3(:)) && any(Q4(:)); +end diff --git a/Addons/WriteTif.m b/Addons/WriteTif.m new file mode 100644 index 0000000000000000000000000000000000000000..1de411a1481479a3f3bfa93e89817a69fd074ca7 --- /dev/null +++ b/Addons/WriteTif.m @@ -0,0 +1,78 @@ +function WriteTif(MatIn,OutPutColorMap,tif_filename,varargin) +%% function WriteTif(MatIn,OutPutColorMap,tif_filename,varargin) +% created by Arthur Chavignon 18/12/19 +% example WriteTif(MatOutConv(:,:,1:10:end),hot(256),'tiftest.tif') + +if ~strcmp(tif_filename(end-3:end),'.tif') + error('OutPutName should end by .tif ') +end + +tmp = strcmpi(varargin,'overwrite'); +if any(tmp),ForceOverWirte= varargin{find(tmp)+1};else, ForceOverWirte=0;end + +if exist(tif_filename,'file')==2 + if ForceOverWirte==1 + choice = 'Overwrite'; + else + % Ask the user whether to overwrite the existing file: + choice = questdlg(['The file ',tif_filename,' already exists. Overwrite it?'], ... + 'The file already exists.','Overwrite','Cancel','Cancel'); + end + % Overwriting basically means deleting and starting from scratch: + + if strcmp(choice,'Overwrite') + delete(tif_filename) + else + clear tif_filename firstframe DelayTime DitherOption LoopCount frame + error('The tiffing has been canceled.') + end +end + +c0 = [0 max(MatIn(:))]; +tmp = strcmpi(varargin,'caxis'); +if any(tmp) + InputCaxis = varargin{find(tmp)+1}; + if numel(InputCaxis)==1 + c0 = c0.*InputCaxis; + elseif numel(InputCaxis)==2 + c0 = InputCaxis; + end +end + +%% Convert into digits +if ~isempty(OutPutColorMap) + Ndigit = max(size(OutPutColorMap)); + + MatInSat = MatIn; + MatInSat(MatIn>c0(2)) = c0(2); + MatInSat(MatInSat<c0(1)) = c0(1); + MatInNorm = [MatInSat-c0(1)]/diff(c0); + + MatInNorm = round(MatInNorm*Ndigit); + MatInNorm(MatInNorm>Ndigit)=Ndigit; +else + % if MatIn is already RGB + MatInNorm = MatIn; +end +% Write RGB data + +for ii=1:size(MatInNorm,3) + if ~isempty(OutPutColorMap) + RGB = ind2rgb(MatInNorm(:,:,ii),OutPutColorMap); % convert to RGB + else + RGB = squeeze(MatInNorm(:,:,ii,:)); + end + imwrite(RGB, tif_filename,'WriteMode','append','Compression','lzw','Description','ArthurChavignon'); +end + +tmp = strcmpi(varargin,'VoxelSizeMm'); +if any(tmp),VoxelSizeMm= varargin{find(tmp)+1}; + t_tif = Tiff(tif_filename,'r+'); + VoxSize_cm = VoxelSizeMm*1e-3; + setTag(t_tif,'XResolution',1/VoxSize_cm(1)) + setTag(t_tif,'YResolution',1/VoxSize_cm(2)) + setTag(t_tif,'ResolutionUnit',Tiff.ResolutionUnit.Centimeter) + close(t_tif); +end + +return \ No newline at end of file diff --git a/Addons/blockproc3.m b/Addons/blockproc3.m new file mode 100644 index 0000000000000000000000000000000000000000..b48f99233a9e6e410cc5b91138cf98f97c14c665 --- /dev/null +++ b/Addons/blockproc3.m @@ -0,0 +1,308 @@ +function im2 = blockproc3(im, blksz, fun, border, numworkers) +% BLOCKPROC3 Block processing for a 3D image +% +% Sometimes images are too large to be processed in one block. Matlab +% provides function blockproc(), that works on 2D images only. This +% blockproc3() function, on the other hand, works also with 3D images. +% +% IM2 = blockproc3(IM, BLKSZ, FUN) +% +% IM is a 2D or 3D-array with the input grayscale image. +% +% IM2 is the ouput image, processed by blocks, with the same size as IM. +% +% BLKSZ is a 2- or 3-vector with the size of the blocks the image will be +% split into. The last block in every row, column and slice will be +% smaller, if the image cannot accommodate it. +% +% FUN is a function handle. This is the processing applied to every +% block. +% +% IM2 = blockproc3(IM, BLKSZ, FUN, BORDER) +% +% BORDER is a 2- or 3-vector with the size of the border around each +% block. Basically, each block size will be grown by this amount, the +% block processed, and then the border cut off from the solution. This is +% useful to avoid "seams" in IM2. +% +% Example: +% +% fun = @(x) deconvblind(x, ones(20, 20, 10)); +% im2 = blockproc3d(im, [256 256 128], fun, [30 30 10]); +% +% IM2 = blockproc3(..., NUMWORKERS) +% +% NUMWORKERS is an integer to activate parallel processing. If the +% Parallel Computing Toolbox is available and NUMWORKERS>1, each image +% block will be processed as a parallel job, using up to NUMWORKERS cores +% at the same time. By default, NUMWORKERS=1, and parallel processing is +% disabled. +% +% Note that in parallel model, FUN gets wrapped in parallel jobs. Thus, +% execution can be interrupted by pressing CTRL+C even when FUN is a C++ +% MEX file. +% +% Parallel processing works by first splitting the image into blocks with +% borders, assigning processing of each block to a separate job, then +% processing each block, and finally trimming the borders and +% reassembling the results into the output image. +% +% This will increase the amount of necessary memory, but this function is +% relatively efficient in the sense that only blocks being processed take +% up extra memory (blocks waiting in the job queue don't). +% +% The speed improvement may not be as good as expected (e.g. 4 processor +% will make the processing only 2x faster), as Matlab function can be +% already more or less optimised to make use of several processors. +% +% To use this option, it is important to NOT have created a pool of +% workers in Matlab with "matlabpool open", as this would block all local +% resources, and not leave any cores free for processing the image +% blocks. A simple example: +% +% % median filtering using a 19x19x19 voxel neighbourhood, processing +% % the image by blocks, using parallel computations +% matlabpool open +% sz = [19 19 19]; +% fun = @(x) medfilt3(x, sz); +% im2 = blockproc3(im, [128 128 64], fun, (sz+1)/2, true); +% matlabpool close +% +% +% See also: blockproc, scimat_blockproc3. + +% Author: Ramon Casero <rcasero@gmail.com> +% Copyright © 2011 University of Oxford +% Version: 0.4.0 +% +% University of Oxford means the Chancellor, Masters and Scholars of +% the University of Oxford, having an administrative office at +% Wellington Square, Oxford OX1 2JD, UK. +% +% This file is part of Gerardus. +% +% This program is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. The offer of this +% program under the terms of the License is subject to the License +% being interpreted in accordance with English Law and subject to any +% action against the University of Oxford being under the jurisdiction +% of the English Courts. +% +% You should have received a copy of the GNU General Public License +% along with this program. If not, see <http://www.gnu.org/licenses/>. + +% check arguments +narginchk(3, 5); +nargoutchk(0, 1); + +% defaults +if isempty(blksz) + blksz = size(im); +end +if (nargin < 4) || isempty(border) + border = [0 0 0]; +end +if (nargin < 5) || isempty(numworkers) + numworkers = 1; +end + +% for convenience, we need the size vector to have 3 components, even for a +% 2D image +if (length(blksz) < 3) + blksz(3) = 1; +end + +% image size +imsz = size(im); + +% block limits without the extra borders... + +% ... starting points +r0 = 1:blksz(1):imsz(1); +c0 = 1:blksz(2):imsz(2); +s0 = 1:blksz(3):imsz(3); + +% ... end points +rx = r0 + blksz(1) - 1; +cx = c0 + blksz(2) - 1; +sx = s0 + blksz(3) - 1; +rx = min(rx, imsz(1)); +cx = min(cx, imsz(2)); +sx = min(sx, imsz(3)); + +% block limits with the extra borders... + +% ... starting points +br0 = max(r0 - border(1), 1); +bc0 = max(c0 - border(2), 1); +bs0 = max(s0 - border(3), 1); + +% ... end points +brx = min(rx + border(1), imsz(1)); +bcx = min(cx + border(2), imsz(2)); +bsx = min(sx + border(3), imsz(3)); + +% number of blocks in each dimension +NR = length(r0); +NC = length(c0); +NS = length(s0); + +% init output +im2 = im; + + +if (numworkers > 1) % parallel processing + + % build a cluster from the local profile + c = parcluster('local'); + + if (c.NumWorkers < numworkers) + warning(['Number of workers selected by user is too large. Only ' ... + num2str(c.NumWorkers) ' cores available']) + else + % save number of maximum number of workers to undo the change after + % the function exits + numworkers0 = c.NumWorkers; + + % change profile's maximum number of workers to the number selected + % by the user + c.NumWorkers = numworkers; + + % when the function exits for whatever reason, reset the number of + % available workers + numworkersCleanupTrigger = onCleanup(@() resetNumworkers(numworkers0)); + end + + % number of blocks + numblocks = NR * NC * NS; + + % init cell array to contain the job descriptors and job cleanup triggers + job = cell(1, numblocks); + jobCleanupTrigger = cell(1, numblocks); + + % process all input blocks (loops are in inverted order, so that + % linear indices follow 1, 2, 3, 4...) + for B = 1:numblocks + + % get r, c, s indices for current block + [I, J, K] = ind2sub([NR, NC, NS], B); + + % create a new job for this block + job{B} = createJob(c); + + % link the job to cleanup, so that if the user interrupts with + % CTRL+C, the job is cancelled and doesn't keep consuming resources + % + % note that we need to create a vector of onCleanup objects, + % because onCleanup gets called when the object is destroyed. and + % this will happen if we overwrite the same variable + jobCleanupTrigger{B} = onCleanup(@() cleanup(job{B})); + + % add processing of current block as a task + createTask(job{B}, fun, 1, ... + {im(br0(I):brx(I), ... + bc0(J):bcx(J), ... + bs0(K):bsx(K) ... + )}); + + % run job + submit(job{B}); + + end + + % wait on all the jobs to finish, and extract the outputs + for B = 1:numblocks + + % get r, c, s indices for current block + [I, J, K] = ind2sub([NR, NC, NS], B); + + % wait until job finishes + wait(job{B}); + + % extract the output of this job + aux = fetchOutputs(job{B}); + + % assign result to output removing the borders + im2(... + r0(I):rx(I), ... + c0(J):cx(J), ... + s0(K):sx(K) ... + ) ... + = aux{1}(... + r0(I)-br0(I)+1:rx(I)-br0(I)+1, ... + c0(J)-bc0(J)+1:cx(J)-bc0(J)+1, ... + s0(K)-bs0(K)+1:sx(K)-bs0(K)+1 ... + ); + + end + +else % single processor (we save memory by not creating a cell vector with all the blocks) + + % iterate all image blocks + for I = 1:NR + for J = 1:NC + for K = 1:NS + + % process current image block + aux = fun(im(... + br0(I):brx(I), ... + bc0(J):bcx(J), ... + bs0(K):bsx(K) ... + )); + + + % assign result to output removing the borders + im2(... + r0(I):rx(I), ... + c0(J):cx(J), ... + s0(K):sx(K) ... + ) ... + = aux(... + (r0(I)-br0(I)+1):(end-(brx(I)-rx(I))), ... + (c0(J)-bc0(J)+1):(end-(bcx(J)-cx(J))), ... + (s0(K)-bs0(K)+1):(end-(bsx(K)-sx(K))) ... + ); + + end + end + end + +end + +end + +% cleanup(): function called when we exit, in charge of cancelling one of +% the current jobs. This can be due to two reasons: 1) the task finished +% successfully or 2) the user interrupted with CTRL+C +function cleanup(job) + +switch job.State + case {'running', 'queued'} + % cancel job so that it doesn't keep taking up resources + job.cancel; + case 'finished' + % DEBUG: +% disp('my_cleanup called because job finished'); +end + +end % function cleanup() + +% resetNumworkers(): function called when we exit, in charge of leaving the +% cluster profile with the same number of maximum workers it had when we +% entered the function +function resetNumworkers(numworkers0) + + % get cluster from the local profile + c = parcluster('local'); + + % reset maximum number of workers + c.NumWorkers = numworkers0; + +end