Edge interpolation in 2D-matrix bounded by NaNs

11 views (last 30 days)
I would like to obtain "smooth" envelope between numeric values and NaNs by interpolating "transition region" accordingly.
I have a 4x6 2D-matrix Z, which contains numeric elements and NaN entries. The numeric entries are bounded by NaNs i.e. both forms two distinctive regions.
a = 1:4; % vector in 1st dimension
b = 1:6; % vector in 2nd dimension
Z = [ 1 1 1 1 1 1;
5 5 5 5 5 5;
NaN 4 4 4 4 NaN;
NaN NaN 3 3 NaN NaN];
Then I Interpolate gridded data using griddedInterpolant
[X1, X2] = ndgrid(a,b);
Z_fun = griddedInterpolant(X1,X2,Z,'linear');
Z_fun.ExtrapolationMethod = 'linear'; % explicitly set extrapolation method
The test then gives me
x1_vec=1:0.1:4;
x2_vec=1:0.1:6;
[X1_vec, X2_vec] = ndgrid(x1_vec,x2_vec);
Z_int = Z_fun(X1_vec, X2_vec);
contourf(X1_vec,X2_vec,Z_int);
It is obvious that griddedInteropolant works only on domain defined by numeric entries in matrix Z, for example
Z_fun(2.5,1.6)
returns NaN. While this makes (some) sense to me, I wonder if I can nevertheless query for a point in small regions between numeric and NaN entries? I am actually interested only in points, which are closer to numeric entries than NaNs (e.g. 2.5,1.6), in order to obtain smoother transition between numeric and NaN values.

Accepted Answer

Matt J
Matt J on 20 Dec 2019
Edited: Matt J on 20 Dec 2019
The goal is to have a function that would linearly interpolate between succesive "edges" in Z matrix.
Consider using scattered interpolation instead,
a = 1:4; % vector in 1st dimension
b = 1:6; % vector in 2nd dimension
Z = [ 1 1 1 1 1 1;
5 5 5 5 5 5;
NaN 4 4 4 4 NaN;
NaN NaN 3 3 NaN NaN];
[A,B]=ndgrid(a,b);
I=~isnan(Z);
Z_fun=scatteredInterpolant(A(I),B(I),Z(I),'linear','none');
For example, Z_fun(2.5,1.6) should return some numeric value, but Z_fun(2.5,1.4) should (still) return NaN.
So far, so good:
>> Z_fun(2.5,1.6)
ans =
4.5000
>> Z_fun(2.5,1.4)
ans =
NaN
  1 Comment
Klemen D
Klemen D on 20 Dec 2019
Edited: Klemen D on 20 Dec 2019
Beautiful!
Testing above code with
x1_vec=1:0.01:4;
x2_vec=1:0.01:6;
[X1_vec, X2_vec] = ndgrid(x1_vec,x2_vec);
Z_int = Z_fun(X1_vec, X2_vec);
contourf(X1_vec,X2_vec,Z_int);
returns the desired result.Please, add this snippet to your solution.

Sign in to comment.

More Answers (1)

Image Analyst
Image Analyst on 20 Dec 2019
You can replace the nan's by a distance-weighted average of nearby non-nan values, like in a 3x3 window. Try this and see if it satisfies you:
Z = [ 1 1 1 1 1 1;
5 5 5 5 5 5;
NaN 4 4 4 4 NaN;
NaN NaN 3 3 NaN NaN]
% Count the number of non-nans in a 3-by-3 window
nanMap = isnan(Z)
counts = conv2(~nanMap, ones(3), 'same')
% Get the sums of the non-nan values in the 3-by-3 window
Z_sums = Z;
Z_sums(isnan(Z)) = 0;
% Make a "linear distance"-weighted kernel
kernel = ones(3) / sqrt(2);
kernel(2,:) = 1;
kernel(:, 2) = 1
% Use that kernel to get distance weighted sums at each point.
sums = conv2(Z_sums, kernel, 'same')
% Divide them to get the average of non-nans in a 3-by-3 window.
Z_means = sums ./ counts
% Replace nan values with the average values, leaving non-nan values alone.
Z_repaired = Z; % Initialize as Z so we can leave the non-nan values alone.
Z_repaired(nanMap) = Z_means(nanMap)
In the command window you'll see:
Z =
1 1 1 1 1 1
5 5 5 5 5 5
NaN 4 4 4 4 NaN
NaN NaN 3 3 NaN NaN
nanMap =
4×6 logical array
0 0 0 0 0 0
0 0 0 0 0 0
1 0 0 0 0 1
1 1 0 0 1 1
counts =
4 6 6 6 6 4
5 8 9 9 8 5
3 6 8 8 6 3
1 3 5 5 3 1
kernel =
0.70711 1 0.70711
1 1 1
0.70711 1 0.70711
sums =
10.536 15.071 15.071 15.071 15.071 10.536
14.536 24.243 27.071 27.071 24.243 14.536
12.536 22.192 29.192 29.192 22.192 12.536
2.8284 9.8284 15.657 15.657 9.8284 2.8284
Z_means =
2.6339 2.5118 2.5118 2.5118 2.5118 2.6339
2.9071 3.0303 3.0079 3.0079 3.0303 2.9071
4.1785 3.6987 3.649 3.649 3.6987 4.1785
2.8284 3.2761 3.1314 3.1314 3.2761 2.8284
Z_repaired =
1 1 1 1 1 1
5 5 5 5 5 5
4.1785 4 4 4 4 4.1785
2.8284 3.2761 3 3 3.2761 2.8284
  3 Comments
Image Analyst
Image Analyst on 20 Dec 2019
Try imresize(). And give an example of the output you'd want.
If you just want to put in some arbitrary locations, then you'd probably be best off just using bilinear or bicubic interpolation. I don't think there is any routine in MATLAB where you can put in 4 corner values, and an interior location and get the interpolated value at the interior location, though it's simple enough to do. See : Wikipedia: Bilinear interpolation in image processing
imresize() does interpolation but you have to give it a known output image size, so the values would be interpolated at regular, periodic, known locations, not arbitrary locations.
Klemen D
Klemen D on 20 Dec 2019
I have added a graphical representation of the situation.

Sign in to comment.

Categories

Find more on Interpolation in Help Center and File Exchange

Products


Release

R2019a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!