← Về thư mục
📄 / / profiles / r-and-d / raw_evidence / scientific-agent-skills / skills / openpiv / references / advanced_algorithms.md

Advanced OpenPIV Algorithms and Settings

Everything here is checked against openpiv 0.25.4. Confirm with inspect.signature() against other releases — names and defaults have moved between versions.

Correlation methods

pyprocess.extended_search_area_piv(..., correlation_method=...):

Value Behaviour
"circular" (default) FFT correlation with no zero-padding. Fastest and lowest memory. Wrap-around means a displacement past half the window aliases back as a small one in the opposite direction.
"linear" FFT correlation zero-padded to 2*window_size. No wrap-around, so large displacements survive, at roughly 2–4× the cost.

Only those two exist in 0.25.4. "direct" appears in the docstrings but the branch is missing — it prints correlation method direct is not implemented and then raises UnboundLocalError: cannot access local variable 'corr'. Do not offer it as an option.

Use "linear" whenever search_area_size > window_size. "circular" accepts an extended search area without complaining but keeps relying on wrap-around, and on OpenPIV's own test1 pair at window_size=32, search_area_size=38 it produced a peak |u| of 255 px/s against 87 px/s for "linear" — the difference is aliased vectors, not physics.

normalized_correlation=True normalizes intensities per window before correlating, making peak heights comparable across windows of differing brightness — useful under uneven illumination. It also shifts the s2n scale, so re-tune the threshold after switching it on.

use_vectorized=True swaps the per-window loop for the batched vectorized_correlation_to_displacements path. Same results, faster on large fields, higher peak memory since all correlation maps exist at once.

Subpixel peak fitting

subpixel_method selects how the integer correlation peak is refined:

Value Notes
"gaussian" (default) Three-point Gaussian fit per axis. Standard choice; biased toward integer values ("peak locking") when particle images are under 2 px.
"parabolic" Three-point parabolic fit. Cheaper, slightly less accurate for Gaussian particle images.
"centroid" Intensity-weighted centroid. More robust for wide or saturated peaks.

Peak locking is a particle-imaging problem, not a fitting problem: aim for 2–3 px particle image diameter rather than switching estimators.

Signal-to-noise measures

pyprocess.sig2noise_ratio(correlation, sig2noise_method="peak2peak", width=2):

The two scales are not interchangeable. A threshold copied from one to the other silently rejects everything or nothing.

Multi-pass window deformation (openpiv.windef)

windef.simple_multipass(frame_a, frame_b, settings) runs the full loop:

  1. windef.first_pass — coarse correlation on windowsizes[0].
  2. validation.typical_validation — applies every enabled check in settings at once.
  3. filters.replace_outliers — fills flagged vectors.
  4. windef.multipass_img_deform for iterations 1 .. num_iterations-1 — deforms the interrogation windows using the previous pass as a predictor, then re-correlates on the next smaller window.
  5. Remaining NaNs filled with zeros; transform_coordinates applied.

It returns (x, y, u, v, flags) in pixels per frame. settings.dt and settings.scaling_factor exist on the dataclass but the windef chain applies neither — first_pass calls extended_search_area_piv without passing dt. Convert afterwards:

x, y, u, v = scaling.uniform(x, y, u, v, scaling_factor=96.52)
u, v = u / dt, v / dt      # scaling.uniform does not divide by dt

The upside of working in px/frame is that the validation defaults (min_max_u_disp=(-30, 30), median_threshold=3) are stated in px/frame and therefore mean what the PIV literature says they mean. The same numbers applied to extended_search_area_piv output, which has been divided by dt, would reject the whole field.

deformation_method="symmetric" (the default) deforms both frames toward the midpoint, which halves the interpolation bias of deforming only the second frame. interpolation_order (default 3) is the spline order used for that deformation.

Window deformation is what makes multi-pass worth the cost: it handles velocity gradients within a window, which a fixed-window single pass cannot.

PIVSettings reference

windef.PIVSettings() is a dataclass; set attributes on an instance.

Input and region

Field Default Meaning
filepath_images, save_path, frame_pattern_a, frame_pattern_b OpenPIV's bundled data/test1 Batch-mode paths; irrelevant when calling simple_multipass with arrays
roi "full" "full" or (y1, y2, x1, x2) crop
invert False Invert intensities, for dark particles on a bright background

Masking

Field Default Meaning
dynamic_masking_method None None, "edges", or "intensity"
dynamic_masking_threshold 0.005 Edge-strength threshold for "edges"
dynamic_masking_filter_size 7 Gaussian/median filter size in px
static_mask None Boolean array marking permanently excluded pixels

Correlation

Field Default
correlation_method "circular" (or "linear")
normalized_correlation False
windowsizes (64, 32, 16)
overlap (32, 16, 8)
num_iterations 3
subpixel_method "gaussian"
use_vectorized False
deformation_method "symmetric"
interpolation_order 3

windowsizes and overlap must be at least num_iterations long — each pass reads its own entry.

Scaling

Field Default Meaning
dt 1.0 Seconds between frames — ignored by the windef chain
scaling_factor 1.0 Pixels per physical unit — ignored by the windef chain

Validation — all consumed by validation.typical_validation

Field Default Meaning
sig2noise_method "peak2mean" See above
sig2noise_mask 2 width around the first peak for "peak2peak"
sig2noise_threshold 1.0 Reject below this
sig2noise_validate True Enable the s2n check
validation_first_pass True Also validate the coarse pass
min_max_u_disp, min_max_v_disp (-30, 30) Global range in px/frame
std_threshold 10 Reject beyond N standard deviations
median_threshold 3 Local-median residual threshold
median_size 1 Neighbourhood half-width for the median test
median_normalized False Normalize the median residual by local fluctuation

Replacement and smoothing

Field Default Meaning
replace_vectors True Run replace_outliers after validation
filter_method "localmean" "localmean", "disk", or "distance"
max_filter_iteration 4 Inpainting iterations
filter_kernel_size 2 Inpainting kernel size
smoothn False Apply smoothn between passes
smoothn_p 0.05 Smoothing strength when enabled

Smoothing between passes stabilizes the predictor for the next pass. It also propagates smoothing into the final result, so report it as part of the processing chain.

Output

Field Default
save_plot, show_plot, show_all_plots False
scale_plot 100
fmt "%.4e"

Volumetric PIV (openpiv.pyprocess3D)

from openpiv import pyprocess3D

u, v, w, s2n = pyprocess3D.extended_search_area_piv3D(
    vol_a, vol_b,
    window_size=(32, 32, 32),
    overlap=(16, 16, 16),
    dt=(1.0, 1.0, 1.0),
    search_area_size=(38, 38, 38),
    correlation_method="fft",       # note: "fft" here, not the 2D "circular"/"linear"
    subpixel_method="gaussian",
    sig2noise_method="peak2peak",
)

# Note the extra window_size argument -- this signature differs from pyprocess.get_coordinates.
x, y, z = pyprocess3D.get_coordinates(
    vol_a.shape, search_area_size=(38, 38, 38), window_size=(32, 32, 32), overlap=(16, 16, 16)
)

Inputs are 3D intensity volumes — this module correlates reconstructed volumes; it does not perform the tomographic reconstruction itself. dt is a per-axis tuple. Memory scales with the cube of window size, so 32³ windows on a large volume are already demanding.

Phase separation (openpiv.phase_separation)

For two-phase flows where large particles (droplets, bubbles) must be separated from tracers before correlation:

from openpiv import phase_separation

big, small = phase_separation.khalitov_longmire(
    image,
    big_particles_criteria={"min_size": 20, "min_brightness": 30},
    small_particles_criteria={"max_size": 20, "min_brightness": 5},
    blur_kernel_size=1,
    I_sat=230,
)

Criteria dicts accept min_size, max_size, min_brightness, and max_brightness. min_size is mandatory for the big-particle dict and max_size for the small-particle dict; unrecognized keys are ignored silently, so check spelling.

Also available: median_filter_method(image, kernel_size) (Kiger & Pan) and opening_method(image, kernel_size, iterations=1, thresh_factor=1.1) for simpler size-based separation. Run PIV separately on each returned phase — tracer statistics computed on an unseparated image are contaminated by the dispersed phase.

Choosing an approach

Situation Approach
Small displacements, uniform flow extended_search_area_piv, correlation_method="circular"
Displacements above ~1/4 window search_area_size > window_size with correlation_method="linear"
Strong velocity gradients, shear layers windef.simple_multipass with decreasing windowsizes
Uneven illumination normalized_correlation=True, plus background subtraction
Solid bodies, reflections, free surfaces preprocess.dynamic_masking or a static_mask
Two-phase flow phase_separation first, then PIV per phase
Volumetric data pyprocess3D.extended_search_area_piv3D