About the abnormal vectors arond a statistic masking

29 views
Skip to first unread message

Wenyue ZHANG

unread,
Nov 5, 2024, 3:59:22 AM11/5/24
to openpiv-users
Dear PIV community,

I am having a try on the openPIV, during which I have some questions about the masking technics.

About the making technics in the OpenPIV, it is introduced as:
1. masked image regions are set to zero or completely black. frame_a[image_mask] = 0 
2. PIV analysis in a completely black interrogation windows result in a zero peak and marked as invalid.

I tried to mask two small areas in the images of tutourial 1 (https://openpiv.readthedocs.io/en/latest/src/tutorial1.html),
by adding the following codes befor applying the pyprocess.extended_search_area_piv() function:
#==============================
#masking
from skimage.draw import polygon
from skimage.draw import disk

mask = 0*frame_a.copy()+1

rr, cc = polygon(
    [150,200,200,150],
    [100,100,250,250]
 )
mask[rr, cc] = 0

rr, cc = disk((150,450), 30)
mask[rr, cc] = 0

frame_a=frame_a*mask
frame_b=frame_b*mask

#plt.imshow(np.c_[frame_a,frame_b],cmap='gray')
#plt.show()
#==============================

It seems that around the boundaries of the statistic masks, some abnormal vectors occurred (thoes horizontal ones). The example is a simple 1D flow, but in some complex situations, the distortion around the masked region can be more serious.
masked.PNG

In my understanding, if we blacken a typical zone in the image, we create an unmoving object in the moving field, it may lead to a distortion in space (some particls moving into and coming out of a black hole).
In some other PIV software (for example, GeoPIV), they claim that they "preclude the masked zone so that they are ignored in the analysis".
I wounder whether the abnormal vectors are related to the "masking by blackening a certain zone in the image", and whether it is possible to "preclude certain part of the image before the analysis" in the OpenPIV.

Best regards,
Wenyue ZHANG


Alex Liberzon

unread,
Nov 5, 2024, 6:59:52 AM11/5/24
to openpiv-users
Please share your images/mask/result you obtain and the code you run. 

There are two type of masks in the OpenPIV - one that should be excluded from the final map and one that has invalid flags or completel black (no particle) regions, etc. There is an intrinsic difficulty to separate the two. One - static mask - is treated now as a NumPY masked array. We keep it along with the image, then regrid it every iteration on a new grid and eventually put their zeros. But when interpolation is done, there's a leak of data in/out of the mask due to the way of interpolation. 

For a given task, we should look together on your case and see what's the best way to continue - probably (that's what I do usually) is to impose the mask both as a mask (masked array) and impose it on the image itself as black region - this way I sometimes reduce the leakage. 

Alex

Wenyue ZHANG

unread,
Nov 5, 2024, 11:03:37 PM11/5/24
to openpiv-users
Thanks for the reply.

I have a trial to "impose the mask both as a mask (masked array) and impose it on the image itself as black region".
The code I used is shown below.
================================================================
from openpiv import tools, preprocess, pyprocess, validation, filters, scaling

import numpy as np
import matplotlib.pyplot as plt
#matplotlib inline

import imageio
import importlib_resources
import pathlib

path = importlib_resources.files('openpiv')


frame_a  = tools.imread( path / 'data/test1/exp1_001_a.bmp' )
frame_b  = tools.imread( path / 'data/test1/exp1_001_b.bmp' )

#fig,ax = plt.subplots(1,2,figsize=(12,10))
#ax[0].imshow(frame_a,cmap=plt.cm.gray);
#ax[1].imshow(frame_b,cmap=plt.cm.gray);
#plt.show()

winsize = 32 # pixels, interrogation window size in frame A
searchsize = 38  # pixels, search area size in frame B
overlap = 17 # pixels, 50% overlap
dt = 0.02 # sec, time interval between the two frames


#==============================
#masking
from skimage.draw import polygon
from skimage.draw import disk

mask = 0*frame_a.copy()+1

rr, cc = polygon(
    [150,200,200,150],
    [100,100,250,250]
 )
mask[rr, cc] = 0

rr, cc = disk((150,450), 50)

mask[rr, cc] = 0

#rr, cc = disk((150,450), 50)
#mask[rr, cc] = 0

#"to impose it on the image itself as black region"
frame_a=frame_a*mask
frame_b=frame_b*mask

fig,ax = plt.subplots(1,2,figsize=(12,10))
ax[0].imshow(frame_a,cmap=plt.cm.gray);
ax[1].imshow(frame_b,cmap=plt.cm.gray);
plt.show()

# convert mask to a boolean mask
mask=1-mask
mask = np.where(mask, True, False)

#==============================


u0, v0, sig2noise = pyprocess.extended_search_area_piv(
    frame_a.astype(np.int32),
    frame_b.astype(np.int32),
    window_size=winsize,
    overlap=overlap,
    dt=dt,
    search_area_size=searchsize,
    sig2noise_method='peak2peak',
)

x, y = pyprocess.get_coordinates(
    image_size=frame_a.shape,
    search_area_size=searchsize,
    overlap=overlap,
)

#plt.hist(sig2noise.flatten())
#plt.show(block=False)

invalid_mask = validation.sig2noise_val(
    sig2noise,
    threshold = 1.05,
)

u1, v1 = filters.replace_outliers(
    u0, v0,
    invalid_mask,
    method='localmean',
    max_iter=3,
    kernel_size=3,
)

# convert x,y to mm
# convert u,v to mm/sec

x, y, u2, v2 = scaling.uniform(
    x, y, u1, v1,
    scaling_factor = 96.52,  # 96.52 pixels/millimeter
)

# 0,0 shall be bottom left, positive rotation rate is counterclockwise
x, y, u3, v3 = tools.transform_coordinates(x, y, u2, v2)

# using the grid mask to create masked arrays
grid_mask = preprocess.prepare_mask_on_grid(x,y,mask)
invalid_mask=invalid_mask | grid_mask

# "to impose the mask both as a mask (masked array)"
masked_u = np.ma.masked_array(u3, mask=invalid_mask)
masked_v = np.ma.masked_array(v3, mask=invalid_mask)

tools.save('exp1_001.txt' , x, y, masked_u, masked_v, invalid_mask)

fig, ax = plt.subplots(figsize=(8,8))
tools.display_vector_field(
    pathlib.Path('exp1_001.txt'),
    ax=ax, scaling_factor=96.52,
    scale=50, # scale defines here the arrow length
    width=0.0035, # width is the thickness of the arrow
    on_img=True, # overlay on the image
    image_name= str(path / 'data'/'test1'/'exp1_001_a.bmp'),
);

================================================================

The two images to be analyzed are:
images.PNG

A rectangule and a circle regions are masked:
images_masked.PNG

The outcome before the masking should be like this:
original.PNG

The outcome after masking is:
masked.PNG

It seems that the red vectors are masked out, but the abnormal horizontal vectors still exist.
I think these are the vectors out of the regions of our masks.
I guess that since some particles disappared (blocked by the masks),
the cross-correlation algorithom cannot correctly match the search window around the masks,
but match to some "fake peak" around.

I do not know whether there is a way to eliminate these abnormal vectors.
What I can think is to eliminate them manually in the final results.

Best regards,
Wenyue Zhang
2024年11月5日火曜日 20:59:52 UTC+9 alex.l...@gmail.com:

Alex Liberzon

unread,
Nov 6, 2024, 9:56:32 AM11/6/24
to openpiv-users
Hi, 

The vectors of this type is easy to remove  - they're arriving indeed from the sharp masking. Solution no. 1 - make mask with blurry edges, before applying the mask, use gaussian filter on the mask. 

Solution no. 2 - the vectors are easy to remove using global value filter - they're too long. if they're not specificlly too long, it's easy to remove them with the median filter - they're different from the neighbours


Wenyue ZHANG

unread,
Nov 7, 2024, 12:04:10 AM11/7/24
to openpiv-users
Thanks a lot for the help!
The problem is now properly solved.

2024年11月6日水曜日 23:56:32 UTC+9 alex.l...@gmail.com:
Reply all
Reply to author
Forward
0 new messages