Warning, /utils/matlab/inpaint_nans.m is written in an unsupported language. File is not indexed.
view on githubraw file Latest commit add29e06 on 2018-01-31 20:35:05 UTC
36eeb8dc0c Bayl*0001 function B=inpaint_nans(A,method)
0002 % inpaint_nans: in-paints over nans in an array
0003 % usage: B=inpaint_nans(A)
0004 %
0005 % solves approximation to one of several pdes to
0006 % interpolate and extrapolate holes
0007 %
0008 % Written by John D'Errico (woodchips@rochester.rr.com)
0009 %
0010 % arguments (input):
0011 % A - nxm array with some NaNs to be filled in
0012 %
0013 % method - (OPTIONAL) scalar numeric flag - specifies
0014 % which approach (or physical metaphor to use
0015 % for the interpolation.) All methods are capable
0016 % of extrapolation, some are better than others.
0017 % There are also speed differences, as well as
0018 % accuracy differences for smooth surfaces.
0019 %
0020 % methods {0,1,2} use a simple plate metaphor
0021 % methods {3} use a better plate equation,
0022 % but will be slower
0023 % methods 4 use a spring metaphor
0024 %
0025 % method == 0 --> (DEFAULT) see method 1, but
0026 % this method does not build as large of a
0027 % linear system in the case of only a few
0028 % NaNs in a large array.
0029 % Extrapolation behavior is linear.
0030 %
0031 % method == 1 --> simple approach, applies del^2
0032 % over the entire array, then drops those parts
0033 % of the array which do not have any contact with
0034 % NaNs. Uses a least squares approach, but it
0035 % does not touch existing points.
0036 % In the case of small arrays, this method is
0037 % quite fast as it does very little extra work.
0038 % Extrapolation behavior is linear.
0039 %
0040 % method == 2 --> uses del^2, but solving a direct
0041 % linear system of equations for nan elements.
0042 % This method will be the fastest possible for
0043 % large systems since it uses the sparsest
0044 % possible system of equations. Not a least
0045 % squares approach, so it may be least robust
0046 % to noise on the boundaries of any holes.
0047 % This method will also be least able to
0048 % interpolate accurately for smooth surfaces.
0049 % Extrapolation behavior is linear.
0050 %
0051 % method == 3 --+ See method 0, but uses del^4 for
0052 % the interpolating operator. This may result
0053 % in more accurate interpolations, at some cost
0054 % in speed.
0055 %
0056 % method == 4 --+ Uses a spring metaphor. Assumes
0057 % springs (with a nominal length of zero)
0058 % connect each node with every neighbor
0059 % (horizontally, vertically and diagonally)
0060 % Since each node tries to be like its neighbors,
0061 % extrapolation is as a constant function where
0062 % this is consistent with the neighboring nodes.
0063 %
0064 %
0065 % arguments (output):
0066 % B - nxm array with NaNs replaced
0067
0068 % I always need to know which elements are NaN,
0069 % and what size the array is for any method
0070 [n,m]=size(A);
0071 nm=n*m;
0072 k=isnan(A(:));
0073
0074 % list the nodes which are known, and which will
0075 % be interpolated
0076 nan_list=find(k);
0077 known_list=find(~k);
0078
0079 % how many nans overall
0080 nan_count=length(nan_list);
0081
0082 % convert NaN indices to (r,c) form
0083 % nan_list==find(k) are the unrolled (linear) indices
0084 % (row,column) form
0085 [nr,nc]=ind2sub([n,m],nan_list);
0086
0087 % both forms of index in one array:
0088 % column 1 == unrolled index
0089 % column 2 == row index
0090 % column 3 == column index
0091 nan_list=[nan_list,nr,nc];
0092
0093 % supply default method
0094 if (nargin<2)|isempty(method)
0095 method = 0;
0096 elseif ~ismember(method,0:4)
0097 error 'If supplied, method must be one of: {0,1,2,3,4}.'
0098 end
0099
0100 % for different methods
0101 switch method
0102 case 0
0103 % The same as method == 1, except only work on those
0104 % elements which are NaN, or at least touch a NaN.
0105
0106 % horizontal and vertical neighbors only
0107 talks_to = [-1 0;0 -1;1 0;0 1];
0108 neighbors_list=identify_neighbors(n,m,nan_list,talks_to);
0109
0110 % list of all nodes we have identified
0111 all_list=[nan_list;neighbors_list];
0112
0113 % generate sparse array with second partials on row
0114 % variable for each element in either list, but only
0115 % for those nodes which have a row index > 1 or < n
0116 L = find((all_list(:,2) > 1) & (all_list(:,2) < n));
0117 nl=length(L);
0118 if nl>0
0119 fda=sparse(repmat(all_list(L,1),1,3), ...
0120 repmat(all_list(L,1),1,3)+repmat([-1 0 1],nl,1), ...
0121 repmat([1 -2 1],nl,1),nm,nm);
0122 else
0123 fda=spalloc(n*m,n*m,size(all_list,1)*5);
0124 end
0125
0126 % 2nd partials on column index
0127 L = find((all_list(:,3) > 1) & (all_list(:,3) < m));
0128 nl=length(L);
0129 if nl>0
0130 fda=fda+sparse(repmat(all_list(L,1),1,3), ...
0131 repmat(all_list(L,1),1,3)+repmat([-n 0 n],nl,1), ...
0132 repmat([1 -2 1],nl,1),nm,nm);
0133 end
0134
0135 % eliminate knowns
0136 rhs=-fda(:,known_list)*A(known_list);
0137 k=find(any(fda(:,nan_list(:,1)),2));
0138
0139 % and solve...
0140 B=A;
0141 B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k);
0142
0143 case 1
0144 % least squares approach with del^2. Build system
0145 % for every array element as an unknown, and then
0146 % eliminate those which are knowns.
0147
0148 % Build sparse matrix approximating del^2 for
0149 % every element in A.
0150 % Compute finite difference for second partials
0151 % on row variable first
0152 [i,j]=ndgrid(2:(n-1),1:m);
0153 ind=i(:)+(j(:)-1)*n;
0154 np=(n-2)*m;
0155 fda=sparse(repmat(ind,1,3),[ind-1,ind,ind+1], ...
0156 repmat([1 -2 1],np,1),n*m,n*m);
0157
0158 % now second partials on column variable
0159 [i,j]=ndgrid(1:n,2:(m-1));
0160 ind=i(:)+(j(:)-1)*n;
0161 np=n*(m-2);
0162 fda=fda+sparse(repmat(ind,1,3),[ind-n,ind,ind+n], ...
0163 repmat([1 -2 1],np,1),nm,nm);
0164
0165 % eliminate knowns
0166 rhs=-fda(:,known_list)*A(known_list);
0167 k=find(any(fda(:,nan_list),2));
0168
0169 % and solve...
0170 B=A;
0171 B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k);
0172
0173 case 2
0174 % Direct solve for del^2 BVP across holes
0175
0176 % generate sparse array with second partials on row
0177 % variable for each nan element, only for those nodes
0178 % which have a row index > 1 or < n
0179 L = find((nan_list(:,2) > 1) & (nan_list(:,2) < n));
0180 nl=length(L);
0181 if nl>0
0182 fda=sparse(repmat(nan_list(L,1),1,3), ...
0183 repmat(nan_list(L,1),1,3)+repmat([-1 0 1],nl,1), ...
0184 repmat([1 -2 1],nl,1),n*m,n*m);
0185 else
0186 fda=spalloc(n*m,n*m,size(nan_list,1)*5);
0187 end
0188
0189 % 2nd partials on column index
0190 L = find((nan_list(:,3) > 1) & (nan_list(:,3) < m));
0191 nl=length(L);
0192 if nl>0
0193 fda=fda+sparse(repmat(nan_list(L,1),1,3), ...
0194 repmat(nan_list(L,1),1,3)+repmat([-n 0 n],nl,1), ...
0195 repmat([1 -2 1],nl,1),n*m,n*m);
0196 end
0197
0198 % fix boundary conditions at extreme corners
0199 % of the array in case there were nans there
0200 if ismember(1,nan_list(:,1))
0201 fda(1,[1 2 n+1])=[-2 1 1];
0202 end
0203 if ismember(n,nan_list(:,1))
0204 fda(n,[n, n-1,n+n])=[-2 1 1];
0205 end
0206 if ismember(nm-n+1,nan_list(:,1))
0207 fda(nm-n+1,[nm-n+1,nm-n+2,nm-n])=[-2 1 1];
0208 end
0209 if ismember(nm,nan_list(:,1))
0210 fda(nm,[nm,nm-1,nm-n])=[-2 1 1];
0211 end
0212
0213 % eliminate knowns
0214 rhs=-fda(:,known_list)*A(known_list);
0215
0216 % and solve...
0217 B=A;
0218 k=nan_list(:,1);
0219 B(k)=fda(k,k)\rhs(k);
0220
0221 case 3
0222 % The same as method == 0, except uses del^4 as the
0223 % interpolating operator.
0224
0225 % del^4 template of neighbors
0226 talks_to = [-2 0;-1 -1;-1 0;-1 1;0 -2;0 -1; ...
0227 0 1;0 2;1 -1;1 0;1 1;2 0];
0228 neighbors_list=identify_neighbors(n,m,nan_list,talks_to);
0229
0230 % list of all nodes we have identified
0231 all_list=[nan_list;neighbors_list];
0232
0233 % generate sparse array with del^4, but only
0234 % for those nodes which have a row & column index
0235 % >= 3 or <= n-2
0236 L = find( (all_list(:,2) >= 3) & ...
0237 (all_list(:,2) <= (n-2)) & ...
0238 (all_list(:,3) >= 3) & ...
0239 (all_list(:,3) <= (m-2)));
0240 nl=length(L);
0241 if nl>0
0242 % do the entire template at once
0243 fda=sparse(repmat(all_list(L,1),1,13), ...
0244 repmat(all_list(L,1),1,13) + ...
0245 repmat([-2*n,-n-1,-n,-n+1,-2,-1,0,1,2,n-1,n,n+1,2*n],nl,1), ...
0246 repmat([1 2 -8 2 1 -8 20 -8 1 2 -8 2 1],nl,1),nm,nm);
0247 else
0248 fda=spalloc(n*m,n*m,size(all_list,1)*5);
0249 end
0250
0251 % on the boundaries, reduce the order around the edges
0252 L = find((((all_list(:,2) == 2) | ...
0253 (all_list(:,2) == (n-1))) & ...
0254 (all_list(:,3) >= 2) & ...
0255 (all_list(:,3) <= (m-1))) | ...
0256 (((all_list(:,3) == 2) | ...
0257 (all_list(:,3) == (m-1))) & ...
0258 (all_list(:,2) >= 2) & ...
0259 (all_list(:,2) <= (n-1))));
0260 nl=length(L);
0261 if nl>0
0262 fda=fda+sparse(repmat(all_list(L,1),1,5), ...
0263 repmat(all_list(L,1),1,5) + ...
0264 repmat([-n,-1,0,+1,n],nl,1), ...
0265 repmat([1 1 -4 1 1],nl,1),nm,nm);
0266 end
0267
0268 L = find( ((all_list(:,2) == 1) | ...
0269 (all_list(:,2) == n)) & ...
0270 (all_list(:,3) >= 2) & ...
0271 (all_list(:,3) <= (m-1)));
0272 nl=length(L);
0273 if nl>0
0274 fda=fda+sparse(repmat(all_list(L,1),1,3), ...
0275 repmat(all_list(L,1),1,3) + ...
0276 repmat([-n,0,n],nl,1), ...
0277 repmat([1 -2 1],nl,1),nm,nm);
0278 end
0279
0280 L = find( ((all_list(:,3) == 1) | ...
0281 (all_list(:,3) == m)) & ...
0282 (all_list(:,2) >= 2) & ...
0283 (all_list(:,2) <= (n-1)));
0284 nl=length(L);
0285 if nl>0
0286 fda=fda+sparse(repmat(all_list(L,1),1,3), ...
0287 repmat(all_list(L,1),1,3) + ...
0288 repmat([-1,0,1],nl,1), ...
0289 repmat([1 -2 1],nl,1),nm,nm);
0290 end
0291
0292 % eliminate knowns
0293 rhs=-fda(:,known_list)*A(known_list);
0294 k=find(any(fda(:,nan_list(:,1)),2));
0295
0296 % and solve...
0297 B=A;
0298 B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k);
0299
0300 case 4
0301 % Spring analogy
0302 % interpolating operator.
0303
0304 % list of all springs between a node and a horizontal
0305 % or vertical neighbor
0306 hv_list=[-1 -1 0;1 1 0;-n 0 -1;n 0 1];
0307 hv_springs=[];
0308 for i=1:4
0309 hvs=nan_list+repmat(hv_list(i,:),nan_count,1);
0310 k=(hvs(:,2)>=1) & (hvs(:,2)<=n) & (hvs(:,3)>=1) & (hvs(:,3)<=m);
0311 hv_springs=[hv_springs;[nan_list(k,1),hvs(k,1)]];
0312 end
0313
0314 % delete replicate springs
0315 hv_springs=unique(sort(hv_springs,2),'rows');
0316
0317 % build sparse matrix of connections, springs
0318 % connecting diagonal neighbors are weaker than
0319 % the horizontal and vertical springs
0320 nhv=size(hv_springs,1);
0321 springs=sparse(repmat((1:nhv)',1,2),hv_springs, ...
0322 repmat([1 -1],nhv,1),nhv,nm);
0323
0324 % eliminate knowns
0325 rhs=-springs(:,known_list)*A(known_list);
0326
0327 % and solve...
0328 B=A;
0329 B(nan_list(:,1))=springs(:,nan_list(:,1))\rhs;
0330
0331 end
0332
0333 % ====================================================
0334 % end of main function
0335 % ====================================================
0336 % ====================================================
0337 % begin subfunctions
0338 % ====================================================
0339 function neighbors_list=identify_neighbors(n,m,nan_list,talks_to)
0340 % identify_neighbors: identifies all the neighbors of
0341 % those nodes in nan_list, not including the nans
0342 % themselves
0343 %
0344 % arguments (input):
0345 % n,m - scalar - [n,m]=size(A), where A is the
0346 % array to be interpolated
0347 % nan_list - array - list of every nan element in A
0348 % nan_list(i,1) == linear index of i'th nan element
0349 % nan_list(i,2) == row index of i'th nan element
0350 % nan_list(i,3) == column index of i'th nan element
0351 % talks_to - px2 array - defines which nodes communicate
0352 % with each other, i.e., which nodes are neighbors.
0353 %
0354 % talks_to(i,1) - defines the offset in the row
0355 % dimension of a neighbor
0356 % talks_to(i,2) - defines the offset in the column
0357 % dimension of a neighbor
0358 %
0359 % For example, talks_to = [-1 0;0 -1;1 0;0 1]
0360 % means that each node talks only to its immediate
0361 % neighbors horizontally and vertically.
0362 %
0363 % arguments(output):
0364 % neighbors_list - array - list of all neighbors of
0365 % all the nodes in nan_list
0366
0367 if ~isempty(nan_list)
0368 % use the definition of a neighbor in talks_to
0369 nan_count=size(nan_list,1);
0370 talk_count=size(talks_to,1);
0371
0372 nn=zeros(nan_count*talk_count,2);
0373 j=[1,nan_count];
0374 for i=1:talk_count
0375 nn(j(1):j(2),:)=nan_list(:,2:3) + ...
0376 repmat(talks_to(i,:),nan_count,1);
0377 j=j+nan_count;
0378 end
0379
0380 % drop those nodes which fall outside the bounds of the
0381 % original array
0382 L = (nn(:,1)<1)|(nn(:,1)>n)|(nn(:,2)<1)|(nn(:,2)>m);
0383 nn(L,:)=[];
0384
0385 % form the same format 3 column array as nan_list
0386 neighbors_list=[sub2ind([n,m],nn(:,1),nn(:,2)),nn];
0387
0388 % delete replicates in the neighbors list
0389 neighbors_list=unique(neighbors_list,'rows');
0390
0391 % and delete those which are also in the list of NaNs.
0392 neighbors_list=setdiff(neighbors_list,nan_list,'rows');
0393
0394 else
0395 neighbors_list=[];
0396 end
0397
0398
0399
0400
0401
0402
0403
0404
0405
0406
0407
0408
0409
0410
0411
0412