--- a +++ b/combinedDeepLearningActiveContour/functions/smoothn.m @@ -0,0 +1,839 @@ +function [z,s,exitflag] = smoothn(varargin) + +%SMOOTHN Robust spline smoothing for 1-D to N-D data. +% SMOOTHN provides a fast, automatized and robust discretized spline +% smoothing for data of arbitrary dimension. +% +% Z = SMOOTHN(Y) automatically smoothes the uniformly-sampled array Y. Y +% can be any N-D noisy array (time series, images, 3D data,...). Non +% finite data (NaN or Inf) are treated as missing values. +% +% Z = SMOOTHN(Y,S) smoothes the array Y using the smoothing parameter S. +% S must be a real positive scalar. The larger S is, the smoother the +% output will be. If the smoothing parameter S is omitted (see previous +% option) or empty (i.e. S = []), it is automatically determined by +% minimizing the generalized cross-validation (GCV) score. +% +% Z = SMOOTHN(Y,W) or Z = SMOOTHN(Y,W,S) smoothes Y using a weighting +% array W of positive values, that must have the same size as Y. Note +% that a nil weight corresponds to a missing value. +% +% If you want to smooth a vector field or multicomponent data, Y must be +% a cell array. For example, if you need to smooth a 3-D vectorial flow +% (Vx,Vy,Vz), use Y = {Vx,Vy,Vz}. The output Z is also a cell array which +% contains the smoothed components. See examples 5 to 8 below. +% +% [Z,S] = SMOOTHN(...) also returns the calculated value for the +% smoothness parameter S so that you can fine-tune the smoothing +% subsequently if needed. +% +% +% 1) ROBUST smoothing +% ------------------- +% Z = SMOOTHN(...,'robust') carries out a robust smoothing that minimizes +% the influence of outlying data. +% +% An iteration process is used with the 'ROBUST' option, or in the +% presence of weighted and/or missing values. Z = SMOOTHN(...,OPTIONS) +% smoothes with the termination parameters specified in the structure +% OPTIONS. OPTIONS is a structure of optional parameters that change the +% default smoothing properties. It must be last input argument. +% --- +% The structure OPTIONS can contain the following fields: +% ----------------- +% OPTIONS.TolZ: Termination tolerance on Z (default = 1e-3), +% OPTIONS.TolZ must be in ]0,1[ +% OPTIONS.MaxIter: Maximum number of iterations allowed +% (default = 100) +% OPTIONS.Initial: Initial value for the iterative process +% (default = original data, Y) +% OPTIONS.Weight: Weight function for robust smoothing: +% 'bisquare' (default), 'talworth' or 'cauchy' +% ----------------- +% +% [Z,S,EXITFLAG] = SMOOTHN(...) returns a boolean value EXITFLAG that +% describes the exit condition of SMOOTHN: +% 1 SMOOTHN converged. +% 0 Maximum number of iterations was reached. +% +% +% 2) Different spacing increments +% ------------------------------- +% SMOOTHN, by default, assumes that the spacing increments are constant +% and equal in all the directions (i.e. dx = dy = dz = ...). This means +% that the smoothness parameter is also similar for each direction. If +% the increments differ from one direction to the other, it can be useful +% to adapt these smoothness parameters. You can thus use the following +% field in OPTIONS: +% OPTIONS.Spacing' = [d1 d2 d3...], +% where dI represents the spacing between points in the Ith dimension. +% +% Important note: d1 is the spacing increment for the first +% non-singleton dimension (i.e. the vertical direction for matrices). +% +% +% 3) REFERENCES (please refer to the two following papers) +% ------------- +% 1) Garcia D, Robust smoothing of gridded data in one and higher +% dimensions with missing values. Computational Statistics & Data +% Analysis, 2010;54:1167-1178. +% <a +% href="matlab:web('http://www.biomecardio.com/pageshtm/publi/csda10.pdf')">PDF download</a> +% 2) Garcia D, A fast all-in-one method for automated post-processing of +% PIV data. Exp Fluids, 2011;50:1247-1259. +% <a +% href="matlab:web('http://www.biomecardio.com/pageshtm/publi/media11.pdf')">PDF download</a> +% +% +% EXAMPLES: +% -------- +% %--- Example #1: smooth a curve --- +% x = linspace(0,100,2^8); +% y = cos(x/10)+(x/50).^2 + randn(size(x))/10; +% y([70 75 80]) = [5.5 5 6]; +% z = smoothn(y); % Regular smoothing +% zr = smoothn(y,'robust'); % Robust smoothing +% subplot(121), plot(x,y,'r.',x,z,'k','LineWidth',2) +% axis square, title('Regular smoothing') +% subplot(122), plot(x,y,'r.',x,zr,'k','LineWidth',2) +% axis square, title('Robust smoothing') +% +% %--- Example #2: smooth a surface --- +% xp = 0:.02:1; +% [x,y] = meshgrid(xp); +% f = exp(x+y) + sin((x-2*y)*3); +% fn = f + randn(size(f))*0.5; +% fs = smoothn(fn); +% subplot(121), surf(xp,xp,fn), zlim([0 8]), axis square +% subplot(122), surf(xp,xp,fs), zlim([0 8]), axis square +% +% %--- Example #3: smooth an image with missing data --- +% n = 256; +% y0 = peaks(n); +% y = y0 + randn(size(y0))*2; +% I = randperm(n^2); +% y(I(1:n^2*0.5)) = NaN; % lose 1/2 of data +% y(40:90,140:190) = NaN; % create a hole +% z = smoothn(y); % smooth data +% subplot(2,2,1:2), imagesc(y), axis equal off +% title('Noisy corrupt data') +% subplot(223), imagesc(z), axis equal off +% title('Recovered data ...') +% subplot(224), imagesc(y0), axis equal off +% title('... compared with the actual data') +% +% %--- Example #4: smooth volumetric data --- +% [x,y,z] = meshgrid(-2:.2:2); +% xslice = [-0.8,1]; yslice = 2; zslice = [-2,0]; +% vn = x.*exp(-x.^2-y.^2-z.^2) + randn(size(x))*0.06; +% subplot(121), slice(x,y,z,vn,xslice,yslice,zslice,'cubic') +% title('Noisy data') +% v = smoothn(vn); +% subplot(122), slice(x,y,z,v,xslice,yslice,zslice,'cubic') +% title('Smoothed data') +% +% %--- Example #5: smooth a cardioid --- +% t = linspace(0,2*pi,1000); +% x = 2*cos(t).*(1-cos(t)) + randn(size(t))*0.1; +% y = 2*sin(t).*(1-cos(t)) + randn(size(t))*0.1; +% z = smoothn({x,y}); +% plot(x,y,'r.',z{1},z{2},'k','linewidth',2) +% axis equal tight +% +% %--- Example #6: smooth a 3-D parametric curve --- +% t = linspace(0,6*pi,1000); +% x = sin(t) + 0.1*randn(size(t)); +% y = cos(t) + 0.1*randn(size(t)); +% z = t + 0.1*randn(size(t)); +% u = smoothn({x,y,z}); +% plot3(x,y,z,'r.',u{1},u{2},u{3},'k','linewidth',2) +% axis tight square +% +% %--- Example #7: smooth a 2-D vector field --- +% [x,y] = meshgrid(linspace(0,1,24)); +% Vx = cos(2*pi*x+pi/2).*cos(2*pi*y); +% Vy = sin(2*pi*x+pi/2).*sin(2*pi*y); +% Vx = Vx + sqrt(0.05)*randn(24,24); % adding Gaussian noise +% Vy = Vy + sqrt(0.05)*randn(24,24); % adding Gaussian noise +% I = randperm(numel(Vx)); +% Vx(I(1:30)) = (rand(30,1)-0.5)*5; % adding outliers +% Vy(I(1:30)) = (rand(30,1)-0.5)*5; % adding outliers +% Vx(I(31:60)) = NaN; % missing values +% Vy(I(31:60)) = NaN; % missing values +% Vs = smoothn({Vx,Vy},'robust'); % automatic smoothing +% subplot(121), quiver(x,y,Vx,Vy,2.5), axis square +% title('Noisy velocity field') +% subplot(122), quiver(x,y,Vs{1},Vs{2}), axis square +% title('Smoothed velocity field') +% +% %--- Example #8: smooth a 3-D vector field --- +% load wind % original 3-D flow +% % -- Create noisy data +% % Gaussian noise +% un = u + randn(size(u))*8; +% vn = v + randn(size(v))*8; +% wn = w + randn(size(w))*8; +% % Add some outliers +% I = randperm(numel(u)) < numel(u)*0.025; +% un(I) = (rand(1,nnz(I))-0.5)*200; +% vn(I) = (rand(1,nnz(I))-0.5)*200; +% wn(I) = (rand(1,nnz(I))-0.5)*200; +% % -- Visualize the noisy flow (see CONEPLOT documentation) +% figure, title('Noisy 3-D vectorial flow') +% xmin = min(x(:)); xmax = max(x(:)); +% ymin = min(y(:)); ymax = max(y(:)); +% zmin = min(z(:)); +% daspect([2,2,1]) +% xrange = linspace(xmin,xmax,8); +% yrange = linspace(ymin,ymax,8); +% zrange = 3:4:15; +% [cx cy cz] = meshgrid(xrange,yrange,zrange); +% k = 0.1; +% hcones = coneplot(x,y,z,un*k,vn*k,wn*k,cx,cy,cz,0); +% set(hcones,'FaceColor','red','EdgeColor','none') +% hold on +% wind_speed = sqrt(un.^2 + vn.^2 + wn.^2); +% hsurfaces = slice(x,y,z,wind_speed,[xmin,xmax],ymax,zmin); +% set(hsurfaces,'FaceColor','interp','EdgeColor','none') +% hold off +% axis tight; view(30,40); axis off +% camproj perspective; camzoom(1.5) +% camlight right; lighting phong +% set(hsurfaces,'AmbientStrength',.6) +% set(hcones,'DiffuseStrength',.8) +% % -- Smooth the noisy flow +% Vs = smoothn({un,vn,wn},'robust'); +% % -- Display the result +% figure, title('3-D flow smoothed by SMOOTHN') +% daspect([2,2,1]) +% hcones = coneplot(x,y,z,Vs{1}*k,Vs{2}*k,Vs{3}*k,cx,cy,cz,0); +% set(hcones,'FaceColor','red','EdgeColor','none') +% hold on +% wind_speed = sqrt(Vs{1}.^2 + Vs{2}.^2 + Vs{3}.^2); +% hsurfaces = slice(x,y,z,wind_speed,[xmin,xmax],ymax,zmin); +% set(hsurfaces,'FaceColor','interp','EdgeColor','none') +% hold off +% axis tight; view(30,40); axis off +% camproj perspective; camzoom(1.5) +% camlight right; lighting phong +% set(hsurfaces,'AmbientStrength',.6) +% set(hcones,'DiffuseStrength',.8) +% +% See also SMOOTH1Q, DCTN, IDCTN. +% +% -- Damien Garcia -- 2009/03, revised 2014/10 +% website: <a +% href="matlab:web('http://www.biomecardio.com')">www.BiomeCardio.com</a> + +%% Check input arguments +error(nargchk(1,5,nargin)); +OPTIONS = struct; +NArgIn = nargin; +if isstruct(varargin{end}) % SMOOTHN(...,OPTIONS) + OPTIONS = varargin{end}; + NArgIn = NArgIn-1; +end +idx = cellfun(@ischar,varargin); +if any(idx) % SMOOTHN(...,'robust',...) + assert(sum(idx)==1 && strcmpi(varargin{idx},'robust'),... + 'Wrong syntax. Read the help text for instructions.') + isrobust = true; + assert(find(idx)==NArgIn,... + 'Wrong syntax. Read the help text for instructions.') + NArgIn = NArgIn-1; +else + isrobust = false; +end + +%% Test & prepare the variables +%--- +% y = array to be smoothed +y = varargin{1}; +if ~iscell(y), y = {y}; end +sizy = size(y{1}); +ny = numel(y); % number of y components +for i = 1:ny + assert(isequal(sizy,size(y{i})),... + 'Data arrays in Y must have the same size.') + y{i} = double(y{i}); +end +noe = prod(sizy); % number of elements +if noe==1, z = y{1}; s = []; exitflag = true; return, end +%--- +% Smoothness parameter and weights +W = ones(sizy); +s = []; +if NArgIn==2 + if isempty(varargin{2}) || isscalar(varargin{2}) % smoothn(y,s) + s = varargin{2}; % smoothness parameter + else % smoothn(y,W) + W = varargin{2}; % weight array + end +elseif NArgIn==3 % smoothn(y,W,s) + W = varargin{2}; % weight array + s = varargin{3}; % smoothness parameter +end +assert(isnumeric(W),'W must be a numeric array') +assert(isnumeric(s),'S must be a numeric scalar') +assert(isequal(size(W),sizy),... + 'Arrays for data and weights (Y and W) must have same size.') +assert(isempty(s) || (isscalar(s) && s>0),... + 'The smoothing parameter S must be a scalar >0') +%--- +% Field names in the structure OPTIONS +OptionNames = fieldnames(OPTIONS); +idx = ismember(OptionNames,... + {'TolZ','MaxIter','Initial','Weight','Spacing','Order'}); +assert(all(idx),... + ['''' OptionNames{find(~idx,1)} ''' is not a valid field name for OPTIONS.']) +%--- +% "Maximal number of iterations" criterion +if ~ismember('MaxIter',OptionNames) + OPTIONS.MaxIter = 100; % default value for MaxIter +end +assert(isnumeric(OPTIONS.MaxIter) && isscalar(OPTIONS.MaxIter) &&... + OPTIONS.MaxIter>=1 && OPTIONS.MaxIter==round(OPTIONS.MaxIter),... + 'OPTIONS.MaxIter must be an integer >=1') +%--- +% "Tolerance on smoothed output" criterion +if ~ismember('TolZ',OptionNames) + OPTIONS.TolZ = 1e-3; % default value for TolZ +end +assert(isnumeric(OPTIONS.TolZ) && isscalar(OPTIONS.TolZ) &&... + OPTIONS.TolZ>0 && OPTIONS.TolZ<1,'OPTIONS.TolZ must be in ]0,1[') +%--- +% "Initial Guess" criterion +if ~ismember('Initial',OptionNames) + isinitial = false; +else + isinitial = true; + z0 = OPTIONS.Initial; + if ~iscell(z0), z0 = {z0}; end + nz0 = numel(z0); % number of y components + assert(nz0==ny,... + 'OPTIONS.Initial must contain a valid initial guess for Z') + for i = 1:nz0 + assert(isequal(sizy,size(z0{i})),... + 'OPTIONS.Initial must contain a valid initial guess for Z') + z0{i} = double(z0{i}); + end +end +%--- +% "Weight function" criterion (for robust smoothing) +if ~ismember('Weight',OptionNames) + OPTIONS.Weight = 'bisquare'; % default weighting function +else + assert(ischar(OPTIONS.Weight),... + 'A valid weight function (OPTIONS.Weight) must be chosen') + assert(any(ismember(lower(OPTIONS.Weight),... + {'bisquare','talworth','cauchy'})),... + 'The weight function must be ''bisquare'', ''cauchy'' or '' talworth''.') +end +%--- +% "Order" criterion (by default m = 2) +% Note: m = 0 is of course not recommended! +if ~ismember('Order',OptionNames) + m = 2; % order +else + m = OPTIONS.Order; + if ~ismember(m,0:2); + error('MATLAB:smoothn:IncorrectOrder',... + 'The order (OPTIONS.order) must be 0, 1 or 2.') + end +end +%--- +% "Spacing" criterion +d = ndims(y{1}); +if ~ismember('Spacing',OptionNames) + dI = ones(1,d); % spacing increment +else + dI = OPTIONS.Spacing; + assert(isnumeric(dI) && length(dI)==d,... + 'A valid spacing (OPTIONS.Spacing) must be chosen') +end +dI = dI/max(dI); +%--- +% Weights. Zero weights are assigned to not finite values (Inf or NaN), +% (Inf/NaN values = missing data). +IsFinite = isfinite(y{1}); +for i = 2:ny, IsFinite = IsFinite & isfinite(y{i}); end +nof = nnz(IsFinite); % number of finite elements +W = W.*IsFinite; +assert(all(W(:)>=0),'Weights must all be >=0') +W = W/max(W(:)); +%--- +% Weighted or missing data? +isweighted = any(W(:)<1); +%--- +% Automatic smoothing? +isauto = isempty(s); + + +%% Create the Lambda tensor +%--- +% Lambda contains the eingenvalues of the difference matrix used in this +% penalized least squares process (see CSDA paper for details) +d = ndims(y{1}); +Lambda = zeros(sizy); +for i = 1:d + siz0 = ones(1,d); + siz0(i) = sizy(i); + Lambda = bsxfun(@plus,Lambda,... + (2-2*cos(pi*(reshape(1:sizy(i),siz0)-1)/sizy(i)))/dI(i)^2); +end +if ~isauto, Gamma = 1./(1+s*Lambda.^m); end + +%% Upper and lower bound for the smoothness parameter +% The average leverage (h) is by definition in [0 1]. Weak smoothing occurs +% if h is close to 1, while over-smoothing appears when h is near 0. Upper +% and lower bounds for h are given to avoid under- or over-smoothing. See +% equation relating h to the smoothness parameter for m = 2 (Equation #12 +% in the referenced CSDA paper). +N = sum(sizy~=1); % tensor rank of the y-array +hMin = 1e-6; hMax = 0.99; +if m==0 % Not recommended. For mathematical purpose only. + sMinBnd = 1/hMax^(1/N)-1; + sMaxBnd = 1/hMin^(1/N)-1; +elseif m==1 + sMinBnd = (1/hMax^(2/N)-1)/4; + sMaxBnd = (1/hMin^(2/N)-1)/4; +elseif m==2 + sMinBnd = (((1+sqrt(1+8*hMax^(2/N)))/4/hMax^(2/N))^2-1)/16; + sMaxBnd = (((1+sqrt(1+8*hMin^(2/N)))/4/hMin^(2/N))^2-1)/16; +end + +%% Initialize before iterating +%--- +Wtot = W; +%--- Initial conditions for z +if isweighted + %--- With weighted/missing data + % An initial guess is provided to ensure faster convergence. For that + % purpose, a nearest neighbor interpolation followed by a coarse + % smoothing are performed. + %--- + if isinitial % an initial guess (z0) has been already given + z = z0; + else + z = InitialGuess(y,IsFinite); + end +else + z = cell(size(y)); + for i = 1:ny, z{i} = zeros(sizy); end +end +%--- +z0 = z; +for i = 1:ny + y{i}(~IsFinite) = 0; % arbitrary values for missing y-data +end +%--- +tol = 1; +RobustIterativeProcess = true; +RobustStep = 1; +nit = 0; +DCTy = cell(1,ny); +vec = @(x) x(:); +%--- Error on p. Smoothness parameter s = 10^p +errp = 0.1; +opt = optimset('TolX',errp); +%--- Relaxation factor RF: to speedup convergence +RF = 1 + 0.75*isweighted; + +%% Main iterative process +%--- +while RobustIterativeProcess + %--- "amount" of weights (see the function GCVscore) + aow = sum(Wtot(:))/noe; % 0 < aow <= 1 + %--- + while tol>OPTIONS.TolZ && nit<OPTIONS.MaxIter + nit = nit+1; + for i = 1:ny + DCTy{i} = dctn(Wtot.*(y{i}-z{i})+z{i}); + end + if isauto && ~rem(log2(nit),1) + %--- + % The generalized cross-validation (GCV) method is used. + % We seek the smoothing parameter S that minimizes the GCV + % score i.e. S = Argmin(GCVscore). + % Because this process is time-consuming, it is performed from + % time to time (when the step number - nit - is a power of 2) + %--- + fminbnd(@gcv,log10(sMinBnd),log10(sMaxBnd),opt); + end + for i = 1:ny + z{i} = RF*idctn(Gamma.*DCTy{i}) + (1-RF)*z{i}; + end + + % if no weighted/missing data => tol=0 (no iteration) + tol = isweighted*norm(vec([z0{:}]-[z{:}]))/norm(vec([z{:}])); + + z0 = z; % re-initialization + end + exitflag = nit<OPTIONS.MaxIter; + + if isrobust %-- Robust Smoothing: iteratively re-weighted process + %--- average leverage + h = 1; + for k = 1:N + if m==0 % not recommended - only for numerical purpose + h0 = 1/(1+s/dI(k)^(2^m)); + elseif m==1 + h0 = 1/sqrt(1+4*s/dI(k)^(2^m)); + elseif m==2 + h0 = sqrt(1+16*s/dI(k)^(2^m)); + h0 = sqrt(1+h0)/sqrt(2)/h0; + else + error('m must be 0, 1 or 2.') + end + h = h*h0; + end + %--- take robust weights into account + Wtot = W.*RobustWeights(y,z,IsFinite,h,OPTIONS.Weight); + %--- re-initialize for another iterative weighted process + isweighted = true; tol = 1; nit = 0; + %--- + RobustStep = RobustStep+1; + RobustIterativeProcess = RobustStep<4; % 3 robust steps are enough. + else + RobustIterativeProcess = false; % stop the whole process + end +end + +%% Warning messages +%--- +if isauto + if abs(log10(s)-log10(sMinBnd))<errp + warning('MATLAB:smoothn:SLowerBound',... + ['S = ' num2str(s,'%.3e') ': the lower bound for S ',... + 'has been reached. Put S as an input variable if required.']) + elseif abs(log10(s)-log10(sMaxBnd))<errp + warning('MATLAB:smoothn:SUpperBound',... + ['S = ' num2str(s,'%.3e') ': the upper bound for S ',... + 'has been reached. Put S as an input variable if required.']) + end +end +if nargout<3 && ~exitflag + warning('MATLAB:smoothn:MaxIter',... + ['Maximum number of iterations (' int2str(OPTIONS.MaxIter) ') has been exceeded. ',... + 'Increase MaxIter option (OPTIONS.MaxIter) or decrease TolZ (OPTIONS.TolZ) value.']) +end + +if numel(z)==1, z = z{:}; end + + +%% GCV score +%--- +function GCVscore = gcv(p) + % Search the smoothing parameter s that minimizes the GCV score + %--- + s = 10^p; + Gamma = 1./(1+s*Lambda.^m); + %--- RSS = Residual sum-of-squares + RSS = 0; + if aow>0.9 % aow = 1 means that all of the data are equally weighted + % very much faster: does not require any inverse DCT + for kk = 1:ny + RSS = RSS + norm(DCTy{kk}(:).*(Gamma(:)-1))^2; + end + else + % take account of the weights to calculate RSS: + for kk = 1:ny + yhat = idctn(Gamma.*DCTy{kk}); + RSS = RSS + norm(sqrt(Wtot(IsFinite)).*... + (y{kk}(IsFinite)-yhat(IsFinite)))^2; + end + end + %--- + TrH = sum(Gamma(:)); + GCVscore = RSS/nof/(1-TrH/noe)^2; +end + +end + +function W = RobustWeights(y,z,I,h,wstr) + % One seeks the weights for robust smoothing... + ABS = @(x) sqrt(sum(abs(x).^2,2)); + r = cellfun(@minus,y,z,'UniformOutput',0); % residuals + r = cellfun(@(x) x(:),r,'UniformOutput',0); + rI = cell2mat(cellfun(@(x) x(I),r,'UniformOutput',0)); + MMED = median(rI); % marginal median + AD = ABS(bsxfun(@minus,rI,MMED)); % absolute deviation + MAD = median(AD); % median absolute deviation + + %-- Studentized residuals + u = ABS(cell2mat(r))/(1.4826*MAD)/sqrt(1-h); + u = reshape(u,size(I)); + + switch lower(wstr) + case 'cauchy' + c = 2.385; W = 1./(1+(u/c).^2); % Cauchy weights + case 'talworth' + c = 2.795; W = u<c; % Talworth weights + case 'bisquare' + c = 4.685; W = (1-(u/c).^2).^2.*((u/c)<1); % bisquare weights + otherwise + error('MATLAB:smoothn:IncorrectWeights',... + 'A valid weighting function must be chosen') + end + W(isnan(W)) = 0; + +% NOTE: +% ---- +% The RobustWeights subfunction looks complicated since we work with cell +% arrays. For better clarity, here is how it would look like without the +% use of cells. Much more readable, isn't it? +% +% function W = RobustWeights(y,z,I,h) +% % weights for robust smoothing. +% r = y-z; % residuals +% MAD = median(abs(r(I)-median(r(I)))); % median absolute deviation +% u = abs(r/(1.4826*MAD)/sqrt(1-h)); % studentized residuals +% c = 4.685; W = (1-(u/c).^2).^2.*((u/c)<1); % bisquare weights +% W(isnan(W)) = 0; +% end + +end + +%% Initial Guess with weighted/missing data +function z = InitialGuess(y,I) + ny = numel(y); + %-- nearest neighbor interpolation (in case of missing values) + if any(~I(:)) + z = cell(size(y)); + if license('test','image_toolbox') + for i = 1:ny + [z{i},L] = bwdist(I); + z{i} = y{i}; + z{i}(~I) = y{i}(L(~I)); + end + else + % If BWDIST does not exist, NaN values are all replaced with the + % same scalar. The initial guess is not optimal and a warning + % message thus appears. + z = y; + for i = 1:ny + z{i}(~I) = mean(y{i}(I)); + end + warning('MATLAB:smoothn:InitialGuess',... + ['BWDIST (Image Processing Toolbox) does not exist. ',... + 'The initial guess may not be optimal; additional',... + ' iterations can thus be required to ensure complete',... + ' convergence. Increase ''MaxIter'' criterion if necessary.']) + end + else + z = y; + end + %-- coarse fast smoothing using one-tenth of the DCT coefficients + siz = size(z{1}); + z = cellfun(@(x) dctn(x),z,'UniformOutput',0); + for k = 1:ndims(z{1}) + for i = 1:ny + z{i}(ceil(siz(k)/10)+1:end,:) = 0; + z{i} = reshape(z{i},circshift(siz,[0 1-k])); + z{i} = shiftdim(z{i},1); + end + end + z = cellfun(@(x) idctn(x),z,'UniformOutput',0); +end + +%% DCTN +function y = dctn(y) + +%DCTN N-D discrete cosine transform. +% Y = DCTN(X) returns the discrete cosine transform of X. The array Y is +% the same size as X and contains the discrete cosine transform +% coefficients. This transform can be inverted using IDCTN. +% +% Reference +% --------- +% Narasimha M. et al, On the computation of the discrete cosine +% transform, IEEE Trans Comm, 26, 6, 1978, pp 934-936. +% +% Example +% ------- +% RGB = imread('autumn.tif'); +% I = rgb2gray(RGB); +% J = dctn(I); +% imshow(log(abs(J)),[]), colormap(jet), colorbar +% +% The commands below set values less than magnitude 10 in the DCT matrix +% to zero, then reconstruct the image using the inverse DCT. +% +% J(abs(J)<10) = 0; +% K = idctn(J); +% figure, imshow(I) +% figure, imshow(K,[0 255]) +% +% -- Damien Garcia -- 2008/06, revised 2011/11 +% -- www.BiomeCardio.com -- + +y = double(y); +sizy = size(y); +y = squeeze(y); +dimy = ndims(y); + +% Some modifications are required if Y is a vector +if isvector(y) + dimy = 1; + if size(y,1)==1, y = y.'; end +end + +% Weighting vectors +w = cell(1,dimy); +for dim = 1:dimy + n = (dimy==1)*numel(y) + (dimy>1)*sizy(dim); + w{dim} = exp(1i*(0:n-1)'*pi/2/n); +end + +% --- DCT algorithm --- +if ~isreal(y) + y = complex(dctn(real(y)),dctn(imag(y))); +else + for dim = 1:dimy + siz = size(y); + n = siz(1); + y = y([1:2:n 2*floor(n/2):-2:2],:); + y = reshape(y,n,[]); + y = y*sqrt(2*n); + y = ifft(y,[],1); + y = bsxfun(@times,y,w{dim}); + y = real(y); + y(1,:) = y(1,:)/sqrt(2); + y = reshape(y,siz); + y = shiftdim(y,1); + end +end + +y = reshape(y,sizy); + +end + +%% IDCTN +function y = idctn(y) + +%IDCTN N-D inverse discrete cosine transform. +% X = IDCTN(Y) inverts the N-D DCT transform, returning the original +% array if Y was obtained using Y = DCTN(X). +% +% Reference +% --------- +% Narasimha M. et al, On the computation of the discrete cosine +% transform, IEEE Trans Comm, 26, 6, 1978, pp 934-936. +% +% Example +% ------- +% RGB = imread('autumn.tif'); +% I = rgb2gray(RGB); +% J = dctn(I); +% imshow(log(abs(J)),[]), colormap(jet), colorbar +% +% The commands below set values less than magnitude 10 in the DCT matrix +% to zero, then reconstruct the image using the inverse DCT. +% +% J(abs(J)<10) = 0; +% K = idctn(J); +% figure, imshow(I) +% figure, imshow(K,[0 255]) +% +% See also DCTN, IDSTN, IDCT, IDCT2, IDCT3. +% +% -- Damien Garcia -- 2009/04, revised 2011/11 +% -- www.BiomeCardio.com -- + +y = double(y); +sizy = size(y); +y = squeeze(y); +dimy = ndims(y); + +% Some modifications are required if Y is a vector +if isvector(y) + dimy = 1; + if size(y,1)==1 + y = y.'; + end +end + +% Weighing vectors +w = cell(1,dimy); +for dim = 1:dimy + n = (dimy==1)*numel(y) + (dimy>1)*sizy(dim); + w{dim} = exp(1i*(0:n-1)'*pi/2/n); +end + +% --- IDCT algorithm --- +if ~isreal(y) + y = complex(idctn(real(y)),idctn(imag(y))); +else + for dim = 1:dimy + siz = size(y); + n = siz(1); + y = reshape(y,n,[]); + y = bsxfun(@times,y,w{dim}); + y(1,:) = y(1,:)/sqrt(2); + y = ifft(y,[],1); + y = real(y*sqrt(2*n)); + I = (1:n)*0.5+0.5; + I(2:2:end) = n-I(1:2:end-1)+1; + y = y(I,:); + y = reshape(y,siz); + y = shiftdim(y,1); + end +end + +y = reshape(y,sizy); + +end + + +%% ----------------------------------------------- +% Simplified SMOOTHN function for better clarity. +% ----------------------------------------------- +% The following function works with scalar and complex N-D arrays. + +%{ +function z = ezsmoothn(y) + +sizy = size(y); +n = prod(sizy); +N = sum(sizy~=1); + +Lambda = zeros(sizy); +d = ndims(y); +for i = 1:d + siz0 = ones(1,d); + siz0(i) = sizy(i); + Lambda = bsxfun(@plus,Lambda,... + 2-2*cos(pi*(reshape(1:sizy(i),siz0)-1)/sizy(i))); +end + +W = ones(sizy); +zz = y; +for k = 1:4 + tol = Inf; + while tol>1e-3 + DCTy = dctn(W.*(y-zz)+zz); + fminbnd(@GCVscore,-10,30); + tol = norm(zz(:)-z(:))/norm(z(:)); + zz = z; + end + tmp = sqrt(1+16*s); + h = (sqrt(1+tmp)/sqrt(2)/tmp)^N; + W = bisquare(y-z,h); +end + function GCVs = GCVscore(p) + s = 10^p; + Gamma = 1./(1+s*Lambda.^2); + z = idctn(Gamma.*DCTy); + RSS = norm(sqrt(W(:)).*(y(:)-z(:)))^2; + TrH = sum(Gamma(:)); + GCVs = RSS/n/(1-TrH/n)^2; + end +end + +function W = bisquare(r,h) +MAD = median(abs(r(:)-median(r(:)))); +u = abs(r/(1.4826*MAD)/sqrt(1-h)); +W = (1-(u/4.685).^2).^2.*((u/4.685)<1); +end + +%}