I am working on a project that involves calculating a velocity vector field using the Particle Image Velocimetry (PIV) technique from two grayscale images. The goal is to implement a function piv() that processes pairs of images to derive velocity vectors (U, V) at defined interrogation points (X, Y). Each interrogation point corresponds to an interrogation window in the first image and a search window in the second image.
The function piv() should:
Load two grayscale images from specified file paths.
Divide each image into small interrogation windows (size_interr_window) and find matching search windows (size_search_window).
Calculate the velocity vector (U, V) for each interrogation point based on the maximum cross-correlation between corresponding windows.
Handle cases where valid search windows cannot be found by setting the velocity vectors to zero.
-
I have implemented the piv() function based on the provided specifications:
-
I ensured that both images are loaded correctly and converted to grayscale if necessary.
-
I iterated through each interrogation point to extract the interrogation and search windows.
-
I used signal.correlate2d() to compute the cross-correlation between the interrogation and search windows and found the maximum correlation index to determine the velocity vector.
When a valid search window was not found, I explicitly set the velocity vectors (U, V) to zero for those points.
However, during testing with various images and window sizes: -
I consistently encountered assertion errors indicating that some velocity vectors were not zero, despite attempting to handle invalid cases by setting them explicitly to zero.
-
I used np.allclose() to verify if the velocity arrays were sufficiently close to zero, but the assertion checks failed.
Request for Help:
I am seeking assistance in:
Reviewing my implementation of the piv() function to identify any logical errors or oversights that may lead to incorrect velocity vector calculations.
Providing suggestions or alternative approaches to ensure that all velocity vectors are correctly computed and validated.
Offering insights on how to improve handling of cases where valid search windows cannot be found, ensuring robustness in the velocity field calculation.
Your guidance and expertise in solving these issues would be greatly appreciated. Thank you for your assistance!
Here is the code:
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
def loaddata(image_path1, image_path2):
# Load image data
image1 = plt.imread(image_path1)
image2 = plt.imread(image_path2)
# Reduce to grayscale if necessary
if image1.ndim == 3:
image1 = np.mean(image1, axis=2)
if image2.ndim == 3:
image2 = np.mean(image2, axis=2)
# Check if images have same dimensions
shape1 = image1.shape
shape2 = image2.shape
if shape1 != shape2:
raise ValueError(f'Input images have different dimensions: {shape1}, {shape2}')
return image1, image2
def velocity_vector(interr_window, search_window):
# Compute cross-correlation between search_window and interr_window
correl = signal.correlate2d(search_window, interr_window, mode='same')
max_index = np.unravel_index(np.argmax(correl), correl.shape)
center_index = (np.array(search_window.shape) - 1) // 2
shift = np.array(max_index) - center_index
return shift
def piv(image_path1, image_path2, size_interr_window=20, size_search_window=None):
# Ensure size_search_window is at least size_interr_window
if size_search_window is None:
size_search_window = size_interr_window
if size_search_window < size_interr_window:
raise ValueError('size_search_window must be at least size_interr_window!')
# Load image data
image1, image2 = loaddata(image_path1, image_path2)
# Calculate number of interrogation points
rows = image1.shape[0] // size_interr_window
cols = image1.shape[1] // size_interr_window
# Initialize output arrays for coordinates and velocities
X, Y = np.meshgrid(np.arange(cols) * size_interr_window + size_interr_window // 2,
np.arange(rows) * size_interr_window + size_interr_window // 2)
U = np.zeros_like(X)
V = np.zeros_like(Y)
# Check dimensions of output arrays
assert U.shape == (rows, cols)
assert V.shape == (rows, cols)
# Iterate over all interrogation points
for i in range(rows):
for j in range(cols):
inter_x_center = i * size_interr_window + size_interr_window // 2
inter_y_center = j * size_interr_window + size_interr_window // 2
inter_x_start = max(0, inter_x_center - size_interr_window // 2)
inter_x_end = min(image1.shape[0], inter_x_center + size_interr_window // 2)
inter_y_start = max(0, inter_y_center - size_interr_window // 2)
inter_y_end = min(image1.shape[1], inter_y_center + size_interr_window // 2)
interr_window = image1[inter_x_start:inter_x_end, inter_y_start:inter_y_end]
search_x_start = max(0, inter_x_center - size_search_window // 2)
search_x_end = min(image2.shape[0], inter_x_center + size_search_window // 2)
search_y_start = max(0, inter_y_center - size_search_window // 2)
search_y_end = min(image2.shape[1], inter_y_center + size_search_window // 2)
search_window = image2[search_x_start:search_x_end, search_y_start:search_y_end]
if search_window.shape[0] < size_interr_window or search_window.shape[1] < size_interr_window:
U[i, j] = 0
V[i, j] = 0
print(f'Interrogation point ({i},{j}): Invalid velocity vector, setting U,V to 0')
continue
v = velocity_vector(interr_window, search_window)
U[i, j] = v[0]
V[i, j] = v[1]
return X, Y, U, V
if __name__ == "__main__":
# Test 1: Same images should have zero velocity vectors
_res0 = piv('img/A045a.tif', 'img/A045a.tif', 20)
assert np.allclose(_res0[2], 0) and np.allclose(_res0[3], 0)
# Test 2: Different images with specific window sizes
_res1 = piv('img/B_010.TIF', 'img/B_014.TIF', size_search_window=76, size_interr_window=32)
assert _res1[2][2, 15] * _res1[2][-2, 15] < 0
print("Tests passed successfully!")
Ali Rezaie is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.