Skip to content

Commit 8f7bfbd

Browse files
committed
I completed the normalized Westerweel's function.
1 parent 2d9b16b commit 8f7bfbd

1 file changed

Lines changed: 64 additions & 7 deletions

File tree

openpiv/validation.py

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,7 @@ def local_norm_median_val(
232232
u: np.ndarray,
233233
v: np.ndarray,
234234
ε: float,
235-
u_threshold: float,
236-
v_threshold: float,
235+
threshold: float,
237236
size: int=1
238237
)->np.ndarray:
239238
"""This function is adapted from OpenPIV's implementation of
@@ -245,6 +244,8 @@ def local_norm_median_val(
245244
article J. Westerweel, F. Scarano, "Universal outlier detection for PIV data",
246245
Experiments in fluids, 39(6), p.1096-1100, 2005.
247246
For the list of parameters, see the referenced article, equation 2 on p.1097.
247+
The current function implements equation 2 from the referenced article in a
248+
manner shown in the MATLAB script at the end of the article, on p.1100.
248249
249250
This validation method tests for the spatial consistency of the data.
250251
Vectors are classified as outliers and replaced with Nan (Not a Number) if
@@ -267,11 +268,8 @@ def local_norm_median_val(
267268
ε : float
268269
minimum normalization level (see the referenced article, eqn.2)
269270
270-
u_threshold : float
271-
the threshold value for component u
272-
273-
v_threshold : float
274-
the threshold value for component v
271+
threshold : float
272+
the threshold to determine whether the vector is valid or not
275273
276274
size: int
277275
the representative size of the kernel of the median filter, the
@@ -286,6 +284,65 @@ def local_norm_median_val(
286284
a boolean array; true elements corresponds to outliers
287285
288286
"""
287+
if np.ma.is_masked(u):
288+
masked_u = np.where(~u.mask, u.data, np.nan)
289+
masked_v = np.where(~v.mask, v.data, np.nan)
290+
else:
291+
masked_u = u
292+
masked_v = v
293+
294+
um = generic_filter(masked_u,
295+
np.nanmedian,
296+
mode='constant',
297+
cval=np.nan,
298+
size=(2*size+1, 2*size+1)
299+
)
300+
vm = generic_filter(masked_v,
301+
np.nanmedian,
302+
mode='constant',
303+
cval=np.nan,
304+
size=(2*size+1, 2*size+1)
305+
)
306+
307+
def rfunc(x):
308+
"""
309+
Implementation of r from the cited article (see the description of
310+
the function above). x is the array within the filtering kernel.
311+
I.e., every element of x is a velocity vector ui or vi.
312+
This function must return a scalar: https://stackoverflow.com/a/14060024/10073233
313+
"""
314+
# 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
316+
# 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|
319+
return rm
320+
321+
rm_u = generic_filter(masked_u,
322+
rfunc,
323+
mode='constant',
324+
cval=np.nan,
325+
size=(2*size+1, 2*size+1)
326+
)
327+
rm_v = generic_filter(masked_v,
328+
rfunc,
329+
mode='constant',
330+
cval=np.nan,
331+
size=(2*size+1, 2*size+1)
332+
)
333+
334+
r0ast_u = np.divide(np.abs(np.subtract(masked_u,um)), np.add(rm_u,ε)) # r0ast stands for r_0^* -
335+
# see formula 2 in the
336+
# referenced article
337+
# (see description of the function)
338+
r0ast_v = np.divide(np.abs(np.subtract(masked_v,vm)), np.add(rm_v,ε)) # r0ast stands for r_0^* -
339+
# see formula 2 in the
340+
# referenced article
341+
# (see description of the function)
342+
343+
ind = (np.sqrt(np.add(np.square(r0ast_u),np.square(r0ast_v)))) > threshold
344+
345+
return ind
289346

290347

291348
def typical_validation(

0 commit comments

Comments
 (0)