a b/combinedDeepLearningActiveContour/functions/smoothn.m
1
function [z,s,exitflag] = smoothn(varargin)
2
3
%SMOOTHN Robust spline smoothing for 1-D to N-D data.
4
%   SMOOTHN provides a fast, automatized and robust discretized spline
5
%   smoothing for data of arbitrary dimension.
6
%
7
%   Z = SMOOTHN(Y) automatically smoothes the uniformly-sampled array Y. Y
8
%   can be any N-D noisy array (time series, images, 3D data,...). Non
9
%   finite data (NaN or Inf) are treated as missing values.
10
%
11
%   Z = SMOOTHN(Y,S) smoothes the array Y using the smoothing parameter S.
12
%   S must be a real positive scalar. The larger S is, the smoother the
13
%   output will be. If the smoothing parameter S is omitted (see previous
14
%   option) or empty (i.e. S = []), it is automatically determined by
15
%   minimizing the generalized cross-validation (GCV) score.
16
%
17
%   Z = SMOOTHN(Y,W) or Z = SMOOTHN(Y,W,S) smoothes Y using a weighting
18
%   array W of positive values, that must have the same size as Y. Note
19
%   that a nil weight corresponds to a missing value.
20
%
21
%   If you want to smooth a vector field or multicomponent data, Y must be
22
%   a cell array. For example, if you need to smooth a 3-D vectorial flow
23
%   (Vx,Vy,Vz), use Y = {Vx,Vy,Vz}. The output Z is also a cell array which
24
%   contains the smoothed components. See examples 5 to 8 below.
25
%
26
%   [Z,S] = SMOOTHN(...) also returns the calculated value for the
27
%   smoothness parameter S so that you can fine-tune the smoothing
28
%   subsequently if needed.
29
%
30
%
31
%   1) ROBUST smoothing
32
%   -------------------
33
%   Z = SMOOTHN(...,'robust') carries out a robust smoothing that minimizes
34
%   the influence of outlying data.
35
%
36
%   An iteration process is used with the 'ROBUST' option, or in the
37
%   presence of weighted and/or missing values. Z = SMOOTHN(...,OPTIONS)
38
%   smoothes with the termination parameters specified in the structure
39
%   OPTIONS. OPTIONS is a structure of optional parameters that change the
40
%   default smoothing properties. It must be last input argument.
41
%   ---
42
%   The structure OPTIONS can contain the following fields:
43
%       -----------------
44
%       OPTIONS.TolZ:       Termination tolerance on Z (default = 1e-3),
45
%                           OPTIONS.TolZ must be in ]0,1[
46
%       OPTIONS.MaxIter:    Maximum number of iterations allowed
47
%                           (default = 100)
48
%       OPTIONS.Initial:    Initial value for the iterative process
49
%                           (default = original data, Y)
50
%       OPTIONS.Weight:     Weight function for robust smoothing:
51
%                           'bisquare' (default), 'talworth' or 'cauchy'
52
%       -----------------
53
%
54
%   [Z,S,EXITFLAG] = SMOOTHN(...) returns a boolean value EXITFLAG that
55
%   describes the exit condition of SMOOTHN:
56
%       1       SMOOTHN converged.
57
%       0       Maximum number of iterations was reached.
58
%
59
%
60
%   2) Different spacing increments
61
%   -------------------------------
62
%   SMOOTHN, by default, assumes that the spacing increments are constant
63
%   and equal in all the directions (i.e. dx = dy = dz = ...). This means
64
%   that the smoothness parameter is also similar for each direction. If
65
%   the increments differ from one direction to the other, it can be useful
66
%   to adapt these smoothness parameters. You can thus use the following
67
%   field in OPTIONS:
68
%       OPTIONS.Spacing' = [d1 d2 d3...],
69
%   where dI represents the spacing between points in the Ith dimension.
70
%
71
%   Important note: d1 is the spacing increment for the first
72
%   non-singleton dimension (i.e. the vertical direction for matrices).
73
%
74
%
75
%   3) REFERENCES (please refer to the two following papers)
76
%   ------------- 
77
%   1) Garcia D, Robust smoothing of gridded data in one and higher
78
%   dimensions with missing values. Computational Statistics & Data
79
%   Analysis, 2010;54:1167-1178. 
80
%   <a
81
%   href="matlab:web('http://www.biomecardio.com/pageshtm/publi/csda10.pdf')">PDF download</a>
82
%   2) Garcia D, A fast all-in-one method for automated post-processing of
83
%   PIV data. Exp Fluids, 2011;50:1247-1259.
84
%   <a
85
%   href="matlab:web('http://www.biomecardio.com/pageshtm/publi/media11.pdf')">PDF download</a>
86
%
87
%
88
%   EXAMPLES:
89
%   --------
90
%   %--- Example #1: smooth a curve ---
91
%   x = linspace(0,100,2^8);
92
%   y = cos(x/10)+(x/50).^2 + randn(size(x))/10;
93
%   y([70 75 80]) = [5.5 5 6];
94
%   z = smoothn(y); % Regular smoothing
95
%   zr = smoothn(y,'robust'); % Robust smoothing
96
%   subplot(121), plot(x,y,'r.',x,z,'k','LineWidth',2)
97
%   axis square, title('Regular smoothing')
98
%   subplot(122), plot(x,y,'r.',x,zr,'k','LineWidth',2)
99
%   axis square, title('Robust smoothing')
100
%
101
%   %--- Example #2: smooth a surface ---
102
%   xp = 0:.02:1;
103
%   [x,y] = meshgrid(xp);
104
%   f = exp(x+y) + sin((x-2*y)*3);
105
%   fn = f + randn(size(f))*0.5;
106
%   fs = smoothn(fn);
107
%   subplot(121), surf(xp,xp,fn), zlim([0 8]), axis square
108
%   subplot(122), surf(xp,xp,fs), zlim([0 8]), axis square
109
%
110
%   %--- Example #3: smooth an image with missing data ---
111
%   n = 256;
112
%   y0 = peaks(n);
113
%   y = y0 + randn(size(y0))*2;
114
%   I = randperm(n^2);
115
%   y(I(1:n^2*0.5)) = NaN; % lose 1/2 of data
116
%   y(40:90,140:190) = NaN; % create a hole
117
%   z = smoothn(y); % smooth data
118
%   subplot(2,2,1:2), imagesc(y), axis equal off
119
%   title('Noisy corrupt data')
120
%   subplot(223), imagesc(z), axis equal off
121
%   title('Recovered data ...')
122
%   subplot(224), imagesc(y0), axis equal off
123
%   title('... compared with the actual data')
124
%
125
%   %--- Example #4: smooth volumetric data ---
126
%   [x,y,z] = meshgrid(-2:.2:2);
127
%   xslice = [-0.8,1]; yslice = 2; zslice = [-2,0];
128
%   vn = x.*exp(-x.^2-y.^2-z.^2) + randn(size(x))*0.06;
129
%   subplot(121), slice(x,y,z,vn,xslice,yslice,zslice,'cubic')
130
%   title('Noisy data')
131
%   v = smoothn(vn);
132
%   subplot(122), slice(x,y,z,v,xslice,yslice,zslice,'cubic')
133
%   title('Smoothed data')
134
%
135
%   %--- Example #5: smooth a cardioid ---
136
%   t = linspace(0,2*pi,1000);
137
%   x = 2*cos(t).*(1-cos(t)) + randn(size(t))*0.1;
138
%   y = 2*sin(t).*(1-cos(t)) + randn(size(t))*0.1;
139
%   z = smoothn({x,y});
140
%   plot(x,y,'r.',z{1},z{2},'k','linewidth',2)
141
%   axis equal tight
142
%
143
%   %--- Example #6: smooth a 3-D parametric curve ---
144
%   t = linspace(0,6*pi,1000);
145
%   x = sin(t) + 0.1*randn(size(t));
146
%   y = cos(t) + 0.1*randn(size(t));
147
%   z = t + 0.1*randn(size(t));
148
%   u = smoothn({x,y,z});
149
%   plot3(x,y,z,'r.',u{1},u{2},u{3},'k','linewidth',2)
150
%   axis tight square
151
%
152
%   %--- Example #7: smooth a 2-D vector field ---
153
%   [x,y] = meshgrid(linspace(0,1,24));
154
%   Vx = cos(2*pi*x+pi/2).*cos(2*pi*y);
155
%   Vy = sin(2*pi*x+pi/2).*sin(2*pi*y);
156
%   Vx = Vx + sqrt(0.05)*randn(24,24); % adding Gaussian noise
157
%   Vy = Vy + sqrt(0.05)*randn(24,24); % adding Gaussian noise
158
%   I = randperm(numel(Vx));
159
%   Vx(I(1:30)) = (rand(30,1)-0.5)*5; % adding outliers
160
%   Vy(I(1:30)) = (rand(30,1)-0.5)*5; % adding outliers
161
%   Vx(I(31:60)) = NaN; % missing values
162
%   Vy(I(31:60)) = NaN; % missing values
163
%   Vs = smoothn({Vx,Vy},'robust'); % automatic smoothing
164
%   subplot(121), quiver(x,y,Vx,Vy,2.5), axis square
165
%   title('Noisy velocity field')
166
%   subplot(122), quiver(x,y,Vs{1},Vs{2}), axis square
167
%   title('Smoothed velocity field')
168
%
169
%   %--- Example #8: smooth a 3-D vector field ---
170
%   load wind % original 3-D flow
171
%   % -- Create noisy data
172
%   % Gaussian noise
173
%   un = u + randn(size(u))*8;
174
%   vn = v + randn(size(v))*8;
175
%   wn = w + randn(size(w))*8;
176
%   % Add some outliers
177
%   I = randperm(numel(u)) < numel(u)*0.025;
178
%   un(I) = (rand(1,nnz(I))-0.5)*200;
179
%   vn(I) = (rand(1,nnz(I))-0.5)*200;
180
%   wn(I) = (rand(1,nnz(I))-0.5)*200;
181
%   % -- Visualize the noisy flow (see CONEPLOT documentation)
182
%   figure, title('Noisy 3-D vectorial flow')
183
%   xmin = min(x(:)); xmax = max(x(:));
184
%   ymin = min(y(:)); ymax = max(y(:));
185
%   zmin = min(z(:));
186
%   daspect([2,2,1])
187
%   xrange = linspace(xmin,xmax,8);
188
%   yrange = linspace(ymin,ymax,8);
189
%   zrange = 3:4:15;
190
%   [cx cy cz] = meshgrid(xrange,yrange,zrange);
191
%   k = 0.1;
192
%   hcones = coneplot(x,y,z,un*k,vn*k,wn*k,cx,cy,cz,0);
193
%   set(hcones,'FaceColor','red','EdgeColor','none')
194
%   hold on
195
%   wind_speed = sqrt(un.^2 + vn.^2 + wn.^2);
196
%   hsurfaces = slice(x,y,z,wind_speed,[xmin,xmax],ymax,zmin);
197
%   set(hsurfaces,'FaceColor','interp','EdgeColor','none')
198
%   hold off
199
%   axis tight; view(30,40); axis off
200
%   camproj perspective; camzoom(1.5)
201
%   camlight right; lighting phong
202
%   set(hsurfaces,'AmbientStrength',.6)
203
%   set(hcones,'DiffuseStrength',.8)
204
%   % -- Smooth the noisy flow
205
%   Vs = smoothn({un,vn,wn},'robust');
206
%   % -- Display the result
207
%   figure, title('3-D flow smoothed by SMOOTHN')
208
%   daspect([2,2,1])
209
%   hcones = coneplot(x,y,z,Vs{1}*k,Vs{2}*k,Vs{3}*k,cx,cy,cz,0);
210
%   set(hcones,'FaceColor','red','EdgeColor','none')
211
%   hold on
212
%   wind_speed = sqrt(Vs{1}.^2 + Vs{2}.^2 + Vs{3}.^2);
213
%   hsurfaces = slice(x,y,z,wind_speed,[xmin,xmax],ymax,zmin);
214
%   set(hsurfaces,'FaceColor','interp','EdgeColor','none')
215
%   hold off
216
%   axis tight; view(30,40); axis off
217
%   camproj perspective; camzoom(1.5)
218
%   camlight right; lighting phong
219
%   set(hsurfaces,'AmbientStrength',.6)
220
%   set(hcones,'DiffuseStrength',.8)
221
%
222
%   See also SMOOTH1Q, DCTN, IDCTN.
223
%
224
%   -- Damien Garcia -- 2009/03, revised 2014/10
225
%   website: <a
226
%   href="matlab:web('http://www.biomecardio.com')">www.BiomeCardio.com</a>
227
228
%% Check input arguments
229
error(nargchk(1,5,nargin));
230
OPTIONS = struct;
231
NArgIn = nargin;
232
if isstruct(varargin{end}) % SMOOTHN(...,OPTIONS)
233
    OPTIONS = varargin{end};
234
    NArgIn = NArgIn-1;
235
end
236
idx = cellfun(@ischar,varargin);
237
if any(idx) % SMOOTHN(...,'robust',...)
238
    assert(sum(idx)==1 && strcmpi(varargin{idx},'robust'),...
239
        'Wrong syntax. Read the help text for instructions.')
240
    isrobust = true;
241
    assert(find(idx)==NArgIn,...
242
        'Wrong syntax. Read the help text for instructions.')
243
    NArgIn = NArgIn-1;
244
else
245
    isrobust = false;
246
end
247
248
%% Test & prepare the variables
249
%---
250
% y = array to be smoothed
251
y = varargin{1};
252
if ~iscell(y), y = {y}; end
253
sizy = size(y{1});
254
ny = numel(y); % number of y components
255
for i = 1:ny
256
    assert(isequal(sizy,size(y{i})),...
257
        'Data arrays in Y must have the same size.')
258
    y{i} = double(y{i});
259
end
260
noe = prod(sizy); % number of elements
261
if noe==1, z = y{1}; s = []; exitflag = true; return, end
262
%---
263
% Smoothness parameter and weights
264
W = ones(sizy);
265
s = [];
266
if NArgIn==2
267
    if isempty(varargin{2}) || isscalar(varargin{2}) % smoothn(y,s)
268
        s = varargin{2}; % smoothness parameter
269
    else % smoothn(y,W)
270
        W = varargin{2}; % weight array
271
    end
272
elseif NArgIn==3 % smoothn(y,W,s)
273
        W = varargin{2}; % weight array
274
        s = varargin{3}; % smoothness parameter
275
end
276
assert(isnumeric(W),'W must be a numeric array')
277
assert(isnumeric(s),'S must be a numeric scalar')
278
assert(isequal(size(W),sizy),...
279
        'Arrays for data and weights (Y and W) must have same size.')
280
assert(isempty(s) || (isscalar(s) && s>0),...
281
    'The smoothing parameter S must be a scalar >0')
282
%---
283
% Field names in the structure OPTIONS
284
OptionNames = fieldnames(OPTIONS);
285
idx = ismember(OptionNames,...
286
    {'TolZ','MaxIter','Initial','Weight','Spacing','Order'});
287
assert(all(idx),...
288
    ['''' OptionNames{find(~idx,1)} ''' is not a valid field name for OPTIONS.'])
289
%---
290
% "Maximal number of iterations" criterion
291
if ~ismember('MaxIter',OptionNames)
292
    OPTIONS.MaxIter = 100; % default value for MaxIter
293
end
294
assert(isnumeric(OPTIONS.MaxIter) && isscalar(OPTIONS.MaxIter) &&...
295
    OPTIONS.MaxIter>=1 && OPTIONS.MaxIter==round(OPTIONS.MaxIter),...
296
    'OPTIONS.MaxIter must be an integer >=1')
297
%---
298
% "Tolerance on smoothed output" criterion
299
if ~ismember('TolZ',OptionNames)
300
    OPTIONS.TolZ = 1e-3; % default value for TolZ
301
end
302
assert(isnumeric(OPTIONS.TolZ) && isscalar(OPTIONS.TolZ) &&...
303
    OPTIONS.TolZ>0 && OPTIONS.TolZ<1,'OPTIONS.TolZ must be in ]0,1[')
304
%---
305
% "Initial Guess" criterion
306
if ~ismember('Initial',OptionNames)
307
    isinitial = false;
308
else
309
    isinitial = true;
310
    z0 = OPTIONS.Initial;
311
    if ~iscell(z0), z0 = {z0}; end
312
    nz0 = numel(z0); % number of y components
313
    assert(nz0==ny,...
314
        'OPTIONS.Initial must contain a valid initial guess for Z')
315
    for i = 1:nz0
316
        assert(isequal(sizy,size(z0{i})),...
317
                'OPTIONS.Initial must contain a valid initial guess for Z')
318
        z0{i} = double(z0{i});
319
    end
320
end
321
%---
322
% "Weight function" criterion (for robust smoothing)
323
if ~ismember('Weight',OptionNames)
324
    OPTIONS.Weight = 'bisquare'; % default weighting function
325
else
326
    assert(ischar(OPTIONS.Weight),...
327
        'A valid weight function (OPTIONS.Weight) must be chosen')
328
    assert(any(ismember(lower(OPTIONS.Weight),...
329
        {'bisquare','talworth','cauchy'})),...
330
        'The weight function must be ''bisquare'', ''cauchy'' or '' talworth''.')
331
end
332
%---
333
% "Order" criterion (by default m = 2)
334
% Note: m = 0 is of course not recommended!
335
if ~ismember('Order',OptionNames)
336
    m = 2; % order
337
else
338
    m = OPTIONS.Order;
339
    if ~ismember(m,0:2);
340
        error('MATLAB:smoothn:IncorrectOrder',...
341
            'The order (OPTIONS.order) must be 0, 1 or 2.')
342
    end    
343
end
344
%---
345
% "Spacing" criterion
346
d = ndims(y{1});
347
if ~ismember('Spacing',OptionNames)
348
    dI = ones(1,d); % spacing increment
349
else
350
    dI = OPTIONS.Spacing;
351
    assert(isnumeric(dI) && length(dI)==d,...
352
            'A valid spacing (OPTIONS.Spacing) must be chosen')
353
end
354
dI = dI/max(dI);
355
%---
356
% Weights. Zero weights are assigned to not finite values (Inf or NaN),
357
% (Inf/NaN values = missing data).
358
IsFinite = isfinite(y{1});
359
for i = 2:ny, IsFinite = IsFinite & isfinite(y{i}); end
360
nof = nnz(IsFinite); % number of finite elements
361
W = W.*IsFinite;
362
assert(all(W(:)>=0),'Weights must all be >=0')
363
W = W/max(W(:));
364
%---
365
% Weighted or missing data?
366
isweighted = any(W(:)<1);
367
%---
368
% Automatic smoothing?
369
isauto = isempty(s);
370
371
372
%% Create the Lambda tensor
373
%---
374
% Lambda contains the eingenvalues of the difference matrix used in this
375
% penalized least squares process (see CSDA paper for details)
376
d = ndims(y{1});
377
Lambda = zeros(sizy);
378
for i = 1:d
379
    siz0 = ones(1,d);
380
    siz0(i) = sizy(i);
381
    Lambda = bsxfun(@plus,Lambda,...
382
        (2-2*cos(pi*(reshape(1:sizy(i),siz0)-1)/sizy(i)))/dI(i)^2);
383
end
384
if ~isauto, Gamma = 1./(1+s*Lambda.^m); end
385
386
%% Upper and lower bound for the smoothness parameter
387
% The average leverage (h) is by definition in [0 1]. Weak smoothing occurs
388
% if h is close to 1, while over-smoothing appears when h is near 0. Upper
389
% and lower bounds for h are given to avoid under- or over-smoothing. See
390
% equation relating h to the smoothness parameter for m = 2 (Equation #12
391
% in the referenced CSDA paper).
392
N = sum(sizy~=1); % tensor rank of the y-array
393
hMin = 1e-6; hMax = 0.99;
394
if m==0 % Not recommended. For mathematical purpose only.
395
    sMinBnd = 1/hMax^(1/N)-1;
396
    sMaxBnd = 1/hMin^(1/N)-1;
397
elseif m==1
398
    sMinBnd = (1/hMax^(2/N)-1)/4;
399
    sMaxBnd = (1/hMin^(2/N)-1)/4;
400
elseif m==2
401
    sMinBnd = (((1+sqrt(1+8*hMax^(2/N)))/4/hMax^(2/N))^2-1)/16;
402
    sMaxBnd = (((1+sqrt(1+8*hMin^(2/N)))/4/hMin^(2/N))^2-1)/16;
403
end
404
405
%% Initialize before iterating
406
%---
407
Wtot = W;
408
%--- Initial conditions for z
409
if isweighted
410
    %--- With weighted/missing data
411
    % An initial guess is provided to ensure faster convergence. For that
412
    % purpose, a nearest neighbor interpolation followed by a coarse
413
    % smoothing are performed.
414
    %---
415
    if isinitial % an initial guess (z0) has been already given
416
        z = z0;
417
    else
418
        z = InitialGuess(y,IsFinite);
419
    end
420
else
421
    z = cell(size(y));
422
    for i = 1:ny, z{i} = zeros(sizy); end
423
end
424
%---
425
z0 = z;
426
for i = 1:ny
427
    y{i}(~IsFinite) = 0; % arbitrary values for missing y-data
428
end
429
%---
430
tol = 1;
431
RobustIterativeProcess = true;
432
RobustStep = 1;
433
nit = 0;
434
DCTy = cell(1,ny);
435
vec = @(x) x(:);
436
%--- Error on p. Smoothness parameter s = 10^p
437
errp = 0.1;
438
opt = optimset('TolX',errp);
439
%--- Relaxation factor RF: to speedup convergence
440
RF = 1 + 0.75*isweighted;
441
442
%% Main iterative process
443
%---
444
while RobustIterativeProcess
445
    %--- "amount" of weights (see the function GCVscore)
446
    aow = sum(Wtot(:))/noe; % 0 < aow <= 1
447
    %---
448
    while tol>OPTIONS.TolZ && nit<OPTIONS.MaxIter
449
        nit = nit+1;
450
        for i = 1:ny
451
            DCTy{i} = dctn(Wtot.*(y{i}-z{i})+z{i});
452
        end
453
        if isauto && ~rem(log2(nit),1)
454
            %---
455
            % The generalized cross-validation (GCV) method is used.
456
            % We seek the smoothing parameter S that minimizes the GCV
457
            % score i.e. S = Argmin(GCVscore).
458
            % Because this process is time-consuming, it is performed from
459
            % time to time (when the step number - nit - is a power of 2)
460
            %---
461
            fminbnd(@gcv,log10(sMinBnd),log10(sMaxBnd),opt);
462
        end
463
        for i = 1:ny
464
            z{i} = RF*idctn(Gamma.*DCTy{i}) + (1-RF)*z{i};
465
        end
466
        
467
        % if no weighted/missing data => tol=0 (no iteration)
468
        tol = isweighted*norm(vec([z0{:}]-[z{:}]))/norm(vec([z{:}]));
469
       
470
        z0 = z; % re-initialization
471
    end
472
    exitflag = nit<OPTIONS.MaxIter;
473
474
    if isrobust %-- Robust Smoothing: iteratively re-weighted process
475
        %--- average leverage
476
        h = 1;
477
        for k = 1:N
478
            if m==0 % not recommended - only for numerical purpose
479
                h0 = 1/(1+s/dI(k)^(2^m));
480
            elseif m==1
481
                h0 = 1/sqrt(1+4*s/dI(k)^(2^m));
482
            elseif m==2
483
                h0 = sqrt(1+16*s/dI(k)^(2^m));
484
                h0 = sqrt(1+h0)/sqrt(2)/h0;
485
            else
486
                error('m must be 0, 1 or 2.')
487
            end
488
            h = h*h0;
489
        end
490
        %--- take robust weights into account
491
        Wtot = W.*RobustWeights(y,z,IsFinite,h,OPTIONS.Weight);
492
        %--- re-initialize for another iterative weighted process
493
        isweighted = true; tol = 1; nit = 0; 
494
        %---
495
        RobustStep = RobustStep+1;
496
        RobustIterativeProcess = RobustStep<4; % 3 robust steps are enough.
497
    else
498
        RobustIterativeProcess = false; % stop the whole process
499
    end
500
end
501
502
%% Warning messages
503
%---
504
if isauto
505
    if abs(log10(s)-log10(sMinBnd))<errp
506
        warning('MATLAB:smoothn:SLowerBound',...
507
            ['S = ' num2str(s,'%.3e') ': the lower bound for S ',...
508
            'has been reached. Put S as an input variable if required.'])
509
    elseif abs(log10(s)-log10(sMaxBnd))<errp
510
        warning('MATLAB:smoothn:SUpperBound',...
511
            ['S = ' num2str(s,'%.3e') ': the upper bound for S ',...
512
            'has been reached. Put S as an input variable if required.'])
513
    end
514
end
515
if nargout<3 && ~exitflag
516
    warning('MATLAB:smoothn:MaxIter',...
517
        ['Maximum number of iterations (' int2str(OPTIONS.MaxIter) ') has been exceeded. ',...
518
        'Increase MaxIter option (OPTIONS.MaxIter) or decrease TolZ (OPTIONS.TolZ) value.'])
519
end
520
521
if numel(z)==1, z = z{:}; end
522
523
524
%% GCV score
525
%---
526
function GCVscore = gcv(p)
527
    % Search the smoothing parameter s that minimizes the GCV score
528
    %---
529
    s = 10^p;
530
    Gamma = 1./(1+s*Lambda.^m);
531
    %--- RSS = Residual sum-of-squares
532
    RSS = 0;
533
    if aow>0.9 % aow = 1 means that all of the data are equally weighted
534
        % very much faster: does not require any inverse DCT
535
        for kk = 1:ny
536
            RSS = RSS + norm(DCTy{kk}(:).*(Gamma(:)-1))^2;
537
        end
538
    else
539
        % take account of the weights to calculate RSS:
540
        for kk = 1:ny
541
            yhat = idctn(Gamma.*DCTy{kk});
542
            RSS = RSS + norm(sqrt(Wtot(IsFinite)).*...
543
                (y{kk}(IsFinite)-yhat(IsFinite)))^2;
544
        end
545
    end
546
    %---
547
    TrH = sum(Gamma(:));
548
    GCVscore = RSS/nof/(1-TrH/noe)^2;
549
end
550
551
end
552
553
function W = RobustWeights(y,z,I,h,wstr)
554
    % One seeks the weights for robust smoothing...
555
    ABS = @(x) sqrt(sum(abs(x).^2,2));
556
    r = cellfun(@minus,y,z,'UniformOutput',0); % residuals
557
    r = cellfun(@(x) x(:),r,'UniformOutput',0);
558
    rI = cell2mat(cellfun(@(x) x(I),r,'UniformOutput',0));
559
    MMED = median(rI); % marginal median
560
    AD = ABS(bsxfun(@minus,rI,MMED)); % absolute deviation
561
    MAD = median(AD); % median absolute deviation
562
 
563
    %-- Studentized residuals
564
    u = ABS(cell2mat(r))/(1.4826*MAD)/sqrt(1-h); 
565
    u = reshape(u,size(I));
566
    
567
    switch lower(wstr)
568
        case 'cauchy'
569
            c = 2.385; W = 1./(1+(u/c).^2); % Cauchy weights
570
        case 'talworth'
571
            c = 2.795; W = u<c; % Talworth weights
572
        case 'bisquare'
573
            c = 4.685; W = (1-(u/c).^2).^2.*((u/c)<1); % bisquare weights
574
        otherwise
575
            error('MATLAB:smoothn:IncorrectWeights',...
576
                'A valid weighting function must be chosen')
577
    end
578
    W(isnan(W)) = 0;
579
580
% NOTE:
581
% ----
582
% The RobustWeights subfunction looks complicated since we work with cell
583
% arrays. For better clarity, here is how it would look like without the
584
% use of cells. Much more readable, isn't it?
585
%
586
% function W = RobustWeights(y,z,I,h)
587
%     % weights for robust smoothing.
588
%     r = y-z; % residuals
589
%     MAD = median(abs(r(I)-median(r(I)))); % median absolute deviation
590
%     u = abs(r/(1.4826*MAD)/sqrt(1-h)); % studentized residuals
591
%     c = 4.685; W = (1-(u/c).^2).^2.*((u/c)<1); % bisquare weights
592
%     W(isnan(W)) = 0;
593
% end
594
595
end
596
597
%% Initial Guess with weighted/missing data
598
function z = InitialGuess(y,I)
599
    ny = numel(y);
600
    %-- nearest neighbor interpolation (in case of missing values)
601
    if any(~I(:))
602
        z = cell(size(y));        
603
        if license('test','image_toolbox')
604
            for i = 1:ny
605
                [z{i},L] = bwdist(I);
606
                z{i} = y{i};
607
                z{i}(~I) = y{i}(L(~I));
608
            end
609
        else
610
        % If BWDIST does not exist, NaN values are all replaced with the
611
        % same scalar. The initial guess is not optimal and a warning
612
        % message thus appears.
613
            z = y;
614
            for i = 1:ny
615
                z{i}(~I) = mean(y{i}(I));
616
            end
617
            warning('MATLAB:smoothn:InitialGuess',...
618
                ['BWDIST (Image Processing Toolbox) does not exist. ',...
619
                'The initial guess may not be optimal; additional',...
620
                ' iterations can thus be required to ensure complete',...
621
                ' convergence. Increase ''MaxIter'' criterion if necessary.'])    
622
        end
623
    else
624
        z = y;
625
    end
626
    %-- coarse fast smoothing using one-tenth of the DCT coefficients
627
    siz = size(z{1});
628
    z = cellfun(@(x) dctn(x),z,'UniformOutput',0);
629
    for k = 1:ndims(z{1})
630
        for i = 1:ny
631
            z{i}(ceil(siz(k)/10)+1:end,:) = 0;
632
            z{i} = reshape(z{i},circshift(siz,[0 1-k]));
633
            z{i} = shiftdim(z{i},1);
634
        end
635
    end
636
    z = cellfun(@(x) idctn(x),z,'UniformOutput',0);
637
end
638
639
%% DCTN
640
function y = dctn(y)
641
642
%DCTN N-D discrete cosine transform.
643
%   Y = DCTN(X) returns the discrete cosine transform of X. The array Y is
644
%   the same size as X and contains the discrete cosine transform
645
%   coefficients. This transform can be inverted using IDCTN.
646
%
647
%   Reference
648
%   ---------
649
%   Narasimha M. et al, On the computation of the discrete cosine
650
%   transform, IEEE Trans Comm, 26, 6, 1978, pp 934-936.
651
%
652
%   Example
653
%   -------
654
%       RGB = imread('autumn.tif');
655
%       I = rgb2gray(RGB);
656
%       J = dctn(I);
657
%       imshow(log(abs(J)),[]), colormap(jet), colorbar
658
%
659
%   The commands below set values less than magnitude 10 in the DCT matrix
660
%   to zero, then reconstruct the image using the inverse DCT.
661
%
662
%       J(abs(J)<10) = 0;
663
%       K = idctn(J);
664
%       figure, imshow(I)
665
%       figure, imshow(K,[0 255])
666
%
667
%   -- Damien Garcia -- 2008/06, revised 2011/11
668
%   -- www.BiomeCardio.com --
669
670
y = double(y);
671
sizy = size(y);
672
y = squeeze(y);
673
dimy = ndims(y);
674
675
% Some modifications are required if Y is a vector
676
if isvector(y)
677
    dimy = 1;
678
    if size(y,1)==1, y = y.'; end
679
end
680
681
% Weighting vectors
682
w = cell(1,dimy);
683
for dim = 1:dimy
684
    n = (dimy==1)*numel(y) + (dimy>1)*sizy(dim);
685
    w{dim} = exp(1i*(0:n-1)'*pi/2/n);
686
end
687
688
% --- DCT algorithm ---
689
if ~isreal(y)
690
    y = complex(dctn(real(y)),dctn(imag(y)));
691
else
692
    for dim = 1:dimy
693
        siz = size(y);
694
        n = siz(1);
695
        y = y([1:2:n 2*floor(n/2):-2:2],:);
696
        y = reshape(y,n,[]);
697
        y = y*sqrt(2*n);
698
        y = ifft(y,[],1);
699
        y = bsxfun(@times,y,w{dim});
700
        y = real(y);
701
        y(1,:) = y(1,:)/sqrt(2);
702
        y = reshape(y,siz);
703
        y = shiftdim(y,1);
704
    end
705
end
706
        
707
y = reshape(y,sizy);
708
709
end
710
711
%% IDCTN
712
function y = idctn(y)
713
714
%IDCTN N-D inverse discrete cosine transform.
715
%   X = IDCTN(Y) inverts the N-D DCT transform, returning the original
716
%   array if Y was obtained using Y = DCTN(X).
717
%
718
%   Reference
719
%   ---------
720
%   Narasimha M. et al, On the computation of the discrete cosine
721
%   transform, IEEE Trans Comm, 26, 6, 1978, pp 934-936.
722
%
723
%   Example
724
%   -------
725
%       RGB = imread('autumn.tif');
726
%       I = rgb2gray(RGB);
727
%       J = dctn(I);
728
%       imshow(log(abs(J)),[]), colormap(jet), colorbar
729
%
730
%   The commands below set values less than magnitude 10 in the DCT matrix
731
%   to zero, then reconstruct the image using the inverse DCT.
732
%
733
%       J(abs(J)<10) = 0;
734
%       K = idctn(J);
735
%       figure, imshow(I)
736
%       figure, imshow(K,[0 255])
737
%
738
%   See also DCTN, IDSTN, IDCT, IDCT2, IDCT3.
739
%
740
%   -- Damien Garcia -- 2009/04, revised 2011/11
741
%   -- www.BiomeCardio.com --
742
743
y = double(y);
744
sizy = size(y);
745
y = squeeze(y);
746
dimy = ndims(y);
747
748
% Some modifications are required if Y is a vector
749
if isvector(y)
750
    dimy = 1;
751
    if size(y,1)==1
752
        y = y.';
753
    end
754
end
755
756
% Weighing vectors
757
w = cell(1,dimy);
758
for dim = 1:dimy
759
    n = (dimy==1)*numel(y) + (dimy>1)*sizy(dim);
760
    w{dim} = exp(1i*(0:n-1)'*pi/2/n);
761
end
762
763
% --- IDCT algorithm ---
764
if ~isreal(y)
765
    y = complex(idctn(real(y)),idctn(imag(y)));
766
else
767
    for dim = 1:dimy
768
        siz = size(y);
769
        n = siz(1);
770
        y = reshape(y,n,[]);
771
        y = bsxfun(@times,y,w{dim});
772
        y(1,:) = y(1,:)/sqrt(2);
773
        y = ifft(y,[],1);
774
        y = real(y*sqrt(2*n));
775
        I = (1:n)*0.5+0.5;
776
        I(2:2:end) = n-I(1:2:end-1)+1;
777
        y = y(I,:);
778
        y = reshape(y,siz);
779
        y = shiftdim(y,1);            
780
    end
781
end
782
        
783
y = reshape(y,sizy);
784
785
end
786
787
788
%% -----------------------------------------------
789
%  Simplified SMOOTHN function for better clarity.
790
%  -----------------------------------------------
791
%  The following function works with scalar and complex N-D arrays.
792
793
%{
794
function z = ezsmoothn(y)
795
796
sizy = size(y);
797
n = prod(sizy);
798
N = sum(sizy~=1);
799
800
Lambda = zeros(sizy);
801
d = ndims(y);
802
for i = 1:d
803
    siz0 = ones(1,d);
804
    siz0(i) = sizy(i);
805
    Lambda = bsxfun(@plus,Lambda,...
806
        2-2*cos(pi*(reshape(1:sizy(i),siz0)-1)/sizy(i)));
807
end
808
809
W = ones(sizy);
810
zz = y;
811
for k = 1:4
812
    tol = Inf;
813
    while tol>1e-3
814
        DCTy = dctn(W.*(y-zz)+zz);
815
        fminbnd(@GCVscore,-10,30);
816
        tol = norm(zz(:)-z(:))/norm(z(:));
817
        zz = z;
818
    end
819
    tmp = sqrt(1+16*s);
820
    h = (sqrt(1+tmp)/sqrt(2)/tmp)^N;
821
    W = bisquare(y-z,h);
822
end
823
    function GCVs = GCVscore(p)
824
        s = 10^p;
825
        Gamma = 1./(1+s*Lambda.^2);
826
        z = idctn(Gamma.*DCTy);
827
        RSS = norm(sqrt(W(:)).*(y(:)-z(:)))^2;
828
        TrH = sum(Gamma(:));
829
        GCVs = RSS/n/(1-TrH/n)^2;
830
    end
831
end
832
833
function W = bisquare(r,h)
834
MAD = median(abs(r(:)-median(r(:))));
835
u = abs(r/(1.4826*MAD)/sqrt(1-h));
836
W = (1-(u/4.685).^2).^2.*((u/4.685)<1);
837
end
838
839
%}