Moving Standard Deviation issues with NaN values (i.e., Stdfilt). Alternatives and workarounds?
8 views (last 30 days)
Show older comments
Hi All,
I need to compute a 5x5 moving standard deviation in a matrix I. Also, I need to exclude the central pixel of my moving window from the std computation (see the code below).
The issue is when I have some NaN values, as the stdfilt output returns a sqaure of NaN.
Can you suggest a solution?
Thanks a lot in advance!
(Please see attached my code below)
I = magic(130);
Stdlfilt_neighborhoodSize = [5, 5];
mask_Stdlfilt = true(Stdlfilt_neighborhoodSize);
mask_Stdlfilt(ceil(Stdlfilt_neighborhoodSize(1)/2), ceil(Stdlfilt_neighborhoodSize(2)/2)) = false;
Std_I = stdfilt(I, mask_Stdlfilt);
0 Comments
Accepted Answer
William Rose
on 19 Dec 2023
Edited: William Rose
on 19 Dec 2023
[Edit: I attached the script twice. I am removing the earlier version.]
See attached script. Key steps, which occur inside a loop, are shown below. In the code below, img2 is the image, with NaNs. img2std is the array of standard deviations of img2. sn is the number of points in the st.dev mask. shw is the half-width of the st.dev. mask, rounded down. When the mask is 5x5, sn=25 and shw=2.
A=img2(i-shw:i+shw,j-shw:j+shw); % extract the chunk of the image
A=reshape(A,sn,1); % make A a vector
A((sn+1)/2)=[]; % delete the central element
img2std(i,j)=std(A(~isnan(A))); % stdev(A without NaNs)
The code above computes the st.dev. of A, with the central element and any NaNs removed.
This script generates and displays three images, shown below: the original "magic(130)" image, the original plus NaNs at a random 5% of the pixels, and the st.dev. across the image.
5 Comments
William Rose
on 20 Dec 2023
You are welcome.
I don't know why Method 2 gives st.devs. that are sometimes a tiny bit different than the values reported by the Methods 1 and 3. The max StDev is 7714, the mean StDev is 699, so differences of ~10^-12 are irrelevant.
Method 2 is interesting. The idea is: 1. Make an augmented image by adding a 1-pixel-wide strip of NaNs all around the original image. 2. Make a 3D array which is a 24-layer stack of copies of the augmented image. 3. Do a circular shift of each of the 24 layers, so that a drill-down through the stack will hit the 24 points of the 5x5 mask (minus the central point). 4. Compute the StDev of each 24-layer stack of pixels.
More Answers (2)
Sulaymon Eshkabilov
on 19 Dec 2023
NaN values can be substituted with (1) "some value" or (2) next to it value or (3) skip nan values element, e.g.:
A = randi([-3, 3], 10, 5);
B = 0./A;
A(isnan(B))= NaN
%% (2) Substitute NAN with some value
SOME_Value = 2;
A1 = A;
A1(isnan(A1))= SOME_Value
%% (2) with the next value
IDX_nan = find(isnan(A));
IDX_NOT_nan = find(~isnan(A));
A2 = A;
A2(IDX_nan) = A2(IDX_nan+1)
A2(isnan(A2)) = A2(find(isnan(A2))+1) % In case, there are two nans one after another
%% (3) Skip or omit NAN while computing std()
STD_A = std(A, "omitnan")
0 Comments
DGM
on 19 Dec 2023
Edited: DGM
on 19 Dec 2023
IPT stdfilt() doesn't behave as you describe in any version I've tested.
% a float array with a 10x10 NaN block
A = magic(130);
A(21:30,21:30) = NaN;
nansin = nnz(isnan(A)) % 10x10
% a 5x5 box filter with a 1px hole
fsz = [5, 5];
fk = true(fsz);
fk(ceil(fsz(1)/2), ceil(fsz(2)/2)) = false;
% the filtered result has no NaNs
B = stdfilt(A, fk);
nansout = nnz(isnan(B)) % no nans
% the 10x10 NaN block turns into a 14x14 block of zeros
% due to the original block size and the filter radius
% i.e. 10 + 2*floor(5/2) = 14
sqrt(nnz(B == 0)) % block width
% see the output region local to the defect
B(16:35,16:35)
Internally, imfilter() is used to do the work, and while the intermediate results from imfilter() will have NaNs, the final output winds up going through
% where do the NaNs turn into zeros?
intermediateresult = [0 100 225 NaN];
output = sqrt(max(intermediateresult,0)) % this is why
which will return 0 for NaNs in the intermediate result.
Either I'm misreading something, or there's something missing from this picture. Is there some other way in which you're using stdfilt() which is producing different results? Are your NaNs sparse, or are they located in large contiguous regions which can span the filter window? Is avoiding NaN throughput the goal, or is it important to instead preserve existing NaNs?
0 Comments
See Also
Categories
Find more on Orange in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!