Skip to content

Commit 7b53966

Browse files
committed
I finalized the function validation.local_norm_median_val() (I added y=x.copy() to rfunc(x) there) and I wrote a test for it. It's ready for pull request.
1 parent 8f7bfbd commit 7b53966

3 files changed

Lines changed: 62 additions & 3 deletions

File tree

186 KB
Loading

openpiv/test/test_validation.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,62 @@ def test_local_median_validation(u_threshold=3, N=3):
7676
assert flag[N, N]
7777

7878

79+
def test_local_norm_median_validation():
80+
""" test normalized local median
81+
82+
Args:
83+
threshold (int, optional): _description_.
84+
N (int, optional): _description_.
85+
"""
86+
87+
# The idea is the following. Let's create an array of the size of one filtering kernel.
88+
# Calculate normalized median filter by hand and using local_norm_median_val(). See if
89+
# the results are comparable. As outlined in the description of local_norm_median_val(),
90+
# follow the paper J. Westerweel, F. Scarano, "Universal outlier detection for PIV data",
91+
# Experiments in fluids, 39(6), p.1096-1100, 2005, equation 2, to perform calculations
92+
# by hand.
93+
94+
# Case study:
95+
u = np.asarray([[1,2,3],[4,5,6],[7,8,9]])
96+
v = np.asarray([[2,3,4],[5,6,7],[8,9,10]])
97+
ε = 0.1
98+
thresh = 2
99+
100+
# Calculations by hand (according to equation 2 in the referenced article).
101+
u0 = 5
102+
v0 = 6
103+
# Median formula for even number of observations (the center is excluded)
104+
# that are placed in the ascending order (just like the given u and v arrays):
105+
# Median = [(n/2)th term + ((n/2) + 1)th term]/2 (https://www.cuemath.com/data/median/).
106+
um = 5 # = (4 + 6) / 2
107+
vm = 6 # = (5 + 7) / 2
108+
# Arrays of residuals given by the formula rui = |ui-um| and rvi = |vi-vm|
109+
rui = np.asarray([[4,3,2],[1,0,1],[2,3,4]])
110+
rvi = np.asarray([[4,3,2],[1,0,1],[2,3,4]])
111+
# Median residuals values (center excluded) must be calculated for ascending arrays:
112+
ruiascend = [1, 1, 2, 2, 3, 3, 4, 4]
113+
rviascend = [1, 1, 2, 2, 3, 3, 4, 4]
114+
rum = 2.5 # = (2 + 3) / 2
115+
rvm = 2.5 # = (2 + 3) / 2
116+
# Now implement equation 2 from the referenced article:
117+
r0ast_u = np.abs(u0 - um) / (rum + ε)
118+
r0ast_v = np.abs(v0 - vm) / (rvm + ε)
119+
# The method of comparison to the threshold is given at the very end of the referenced
120+
# article in the MATLAB code:
121+
byHand = (r0ast_u**2 + r0ast_v**2)**0.5 > thresh
122+
# Here, we calculated byHand for the velocity vector (u0,v0) coordinates of which are
123+
# located at u[1,1] and v[1,1].
124+
125+
# Calculations using local_norm_median_val() function.
126+
byFunc = validation.local_norm_median_val(u, v, ε, thresh)
127+
# Here by func returns a boolean matrix containing either True or False for each velocity
128+
# vector (ui,vi).
129+
130+
# Compare the two results:
131+
assert byHand == byFunc[1,1]
132+
133+
134+
79135
def test_global_val():
80136
""" tests global validation
81137
"""

openpiv/validation.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,10 +312,13 @@ def rfunc(x):
312312
This function must return a scalar: https://stackoverflow.com/a/14060024/10073233
313313
"""
314314
# copied from here: https://stackoverflow.com/a/60166608/10073233
315-
np.put(x, x.size//2, np.nan) # put NaN in the middle to avoid using
315+
y = x.copy() # need this step, because np.put() below changes the array in place,
316+
# and we can end up with a situation when the entire filtering kernel
317+
# is comprised of NaNs resulting in NumPy RuntimeWarning: All-NaN slice encountered
318+
np.put(y, y.size//2, np.nan) # put NaN in the middle to avoid using
316319
# the middle in the calculations
317-
xm = np.nanmedian(x) # Um for the current filtering window
318-
rm = np.nanmedian(np.abs(np.subtract(x,xm))) # median of |ui-um| or |vi-vm|
320+
ym = np.nanmedian(y) # Um for the current filtering window
321+
rm = np.nanmedian(np.abs(np.subtract(y,ym))) # median of |ui-um| or |vi-vm|
319322
return rm
320323

321324
rm_u = generic_filter(masked_u,

0 commit comments

Comments
 (0)