openpiv
Performs Particle Image Velocimetry (PIV) analysis with OpenPIV
Install / Use
npx skills add K-Dense-AI/scientific-agent-skills --skill openpivInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Development & EngineeringSupported Platforms
Our assessment of openpiv
openpiv scores 97/100 on our quality scale, 148th of 4,648 Development & Engineering skills we index (top 4%).
Its SKILL.md is 17 KB long, well organised into 48 sections with 19 code examples: a thorough specification that gives an agent plenty to work with.
With 46,441 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated about 2 months ago, so openpiv is actively maintained.
- Our last check on 2026-10-23 found the source still online.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
Safety scan
No issues foundOur scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful.
AI review by kimi-k2.7-code on 2026-09-26. Automated pattern scan on 2026-09-26. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.
openpiv compared with similar skills
All 4 of these similar skills score higher than openpiv; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| openpiv (this skill)by K-Dense-AI | 97 | 46.4k | 48d ago | SKILL.md |
| taste-skillby Leonxlnx | 100 | 112.0k | 1d ago | CLAUDE.md |
| Agent-Reachby Panniantong | 100 | 110.3k | 1d ago | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 79.1k | today | CLAUDE.md |
| ai-job-searchby ai-job-search-dev | 100 | 70.5k | today | CLAUDE.md |
Frequently asked questions
- How do I install openpiv?
- Run
npx skills add K-Dense-AI/scientific-agent-skills --skill openpiv. The install tabs above show the steps for each supported agent. - Which AI agents does openpiv work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is openpiv safe to use?
- Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful. It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is openpiv still maintained?
- The repository was last updated about 2 months ago, so openpiv is actively maintained.
Skill content
View source on GitHubname: openpiv description: Particle Image Velocimetry (PIV) analysis with OpenPIV. Use when extracting velocity fields from PIV image pairs, analyzing fluid dynamics or flow visualization experiments, cross-correlating interrogation windows, validating and replacing spurious PIV vectors, or computing vorticity, strain rate, and turbulence statistics from measured velocity fields. license: BSD-3-Clause compatibility: Requires Python 3.10+ with openpiv installed (uv pip install openpiv). numpy, scipy, scikit-image, and matplotlib arrive as dependencies. No network access needed after install. allowed-tools: Read Write Edit Bash metadata: version: "1.1" skill-author: OpenPIV Team tested-against: "openpiv 0.25.4"
OpenPIV
Overview
OpenPIV (Open Particle Image Velocimetry) analyzes fluid flow from PIV image pairs. It covers preprocessing, cross-correlation, vector validation, outlier replacement, smoothing, and scaling to physical units.
Everything below is verified against openpiv 0.25.4. The API moves between releases — check
inspect.signature() before trusting a snippet against a different version.
When to use
Use this skill when working with experimental PIV or flow-visualization image pairs: measuring 2D velocity fields, tuning interrogation-window parameters, validating vectors, or deriving vorticity, strain rate, and turbulence statistics. For simulating flow rather than measuring it, use a CFD skill instead.
Quick Start
Install OpenPIV:
uv pip install openpiv
# Pin it when the analysis needs to be reproducible -- this is the version every
# snippet below was checked against.
uv pip install "openpiv==0.25.4"
Run PIV analysis on an image pair:
import numpy as np
from openpiv import tools, pyprocess, validation, filters, scaling
frame_a = tools.imread("image_a.bmp")
frame_b = tools.imread("image_b.bmp")
# Cross-correlate. Returns (u, v, s2n) whenever sig2noise_method is not None.
u, v, s2n = pyprocess.extended_search_area_piv(
frame_a.astype(np.int32),
frame_b.astype(np.int32),
window_size=32,
overlap=12,
dt=0.02,
search_area_size=38,
correlation_method="linear", # required for search_area_size > window_size
sig2noise_method="peak2peak",
)
x, y = pyprocess.get_coordinates(
image_size=frame_a.shape,
search_area_size=38,
overlap=12,
)
# flags is a boolean array: True marks a spurious vector.
flags = validation.sig2noise_val(s2n, threshold=1.05)
u, v = filters.replace_outliers(u, v, flags, method="localmean", max_iter=3, kernel_size=2)
# Scale to physical units, then flip to image coordinates for plotting.
x, y, u, v = scaling.uniform(x, y, u, v, scaling_factor=96.52)
x, y, u, v = tools.transform_coordinates(x, y, u, v)
tools.save("vectors.txt", x, y, u, v, flags)
Or use the bundled CLI, which wraps exactly that pipeline:
python skills/openpiv/scripts/runner.py \
--image frame_a.bmp --image frame_b.bmp --output_dir results --verbose
Core Concepts
PIV Fundamentals
Particle Image Velocimetry is an optical method for measuring fluid velocity by tracking illuminated tracer particles between two images.
Process flow:
- Capture an image pair (
frame_a,frame_b) separated by a known timedt. - Divide the images into interrogation windows.
- Cross-correlate matching windows to find peak displacement.
- Validate vectors (signal-to-noise, global range, local median).
- Replace spurious vectors with interpolated values.
- Scale pixel displacements to physical units.
Interrogation Window Parameters
window_size — correlation window in pixels (typically 16–128). Larger windows give better
correlation but coarser spatial resolution.
overlap — pixels shared between adjacent windows (typically 50–75% of window_size). Higher
overlap raises vector density and cost, but adjacent vectors become correlated rather than
independent.
search_area_size — the window searched in the second frame. Must be ≥ window_size; a few
pixels larger accommodates larger displacements. Pair an extended search area with
correlation_method="linear" — the default "circular" relies on FFT wrap-around and aliases large
displacements into small ones. See references/advanced_algorithms.md.
Rules of thumb: keep the largest displacement under about a quarter of window_size, and aim for
5–10 particles per window.
Signal-to-Noise Ratio
s2n measures how distinct the correlation peak is. sig2noise_method controls how it is computed —
"peak2mean" (the function default) or "peak2peak". The two are on different scales, so a
threshold tuned for one is meaningless for the other. Typical peak2peak thresholds are 1.05–1.3.
flags = validation.sig2noise_val(s2n, threshold=1.05)
# flags is bool: True == spurious. `~flags` selects the good vectors.
Common Operations
Dynamic Masking
Masking lives in openpiv.preprocess, not in an openpiv.masking module. It returns an
(image, mask) tuple and expects a float image.
from openpiv import preprocess
# method="edges" for dark, sharp-edged objects; "intensity" for high-contrast objects.
frame_a_masked, mask_a = preprocess.dynamic_masking(
frame_a.astype(np.float64), method="intensity", filter_size=7, threshold=0.005
)
frame_b_masked, mask_b = preprocess.dynamic_masking(
frame_b.astype(np.float64), method="intensity", filter_size=7, threshold=0.005
)
Feed the returned image into the correlation step — it already has the masked region zeroed. Do
not multiply the original frame by mask: masking is already applied, and for method="edges" the
mask comes back as uint8 0/255 rather than boolean, so multiplying rescales the image by 255.
Multi-Pass Processing
Multi-pass (window deformation) lives in openpiv.windef, driven by a PIVSettings dataclass.
pyprocess has no multi-pass entry point.
import numpy as np
from openpiv import scaling, windef
settings = windef.PIVSettings()
settings.windowsizes = (64, 32, 16) # one entry per pass, decreasing (this is also the default)
settings.overlap = (32, 16, 8) # same length as windowsizes
settings.num_iterations = 3 # number of passes to actually run
settings.sig2noise_threshold = 1.05
x, y, u, v, flags = windef.simple_multipass(
frame_a.astype(np.int32), frame_b.astype(np.int32), settings
)
# Output is in PIXELS PER FRAME -- convert yourself. scaling.uniform only divides
# by scaling_factor, so apply dt separately.
dt = 0.02
x, y, u, v = scaling.uniform(x, y, u, v, scaling_factor=96.52)
u, v = u / dt, v / dt
simple_multipass already validates, replaces outliers, fills remaining NaNs with zeros, and calls
transform_coordinates — do not repeat those steps.
Units trap: PIVSettings has dt and scaling_factor fields, but windef never uses either —
first_pass calls extended_search_area_piv without dt, so the whole multi-pass chain works in
pixels per frame. Setting settings.dt = 0.02 changes nothing about the returned values. Convert
after the fact, as above.
For control over individual passes, windef.first_pass and windef.multipass_img_deform are the
lower-level building blocks.
Validation and Post-Processing
Validation Methods
Every validator returns a boolean array where True marks a spurious vector.
# Signal-to-noise
flags = validation.sig2noise_val(s2n, threshold=1.05)
# Global range -- takes (min, max) TUPLES, positionally or as u_thresholds/v_thresholds.
flags = validation.global_val(u, v, (-300, 300), (-300, 300))
# Local median -- u_threshold and v_threshold are REQUIRED; size is the neighbourhood half-width.
flags = validation.local_median_val(u, v, u_threshold=30.0, v_threshold=30.0, size=1)
# Combine with boolean OR (not np.maximum -- these are bool arrays).
flags = (
validation.sig2noise_val(s2n, threshold=1.05)
| validation.global_val(u, v, (-300, 300), (-300, 300))
| validation.local_median_val(u, v, u_threshold=30.0, v_threshold=30.0)
)
Set these thresholds in the units of u and v, not in pixels per frame.
extended_search_area_piv divides by dt, so with dt=0.02 a 3 px/frame displacement arrives as
150 px/s. The thresholds above suit that case; the (-30, 30) figure that PIV literature and
PIVSettings.min_max_u_disp use is a px/frame limit, and applying it to px/s output rejects the
entire field. Either validate before scaling, or scale the thresholds by 1/dt too.
Outlier Replacement
u, v = filters.replace_outliers(
u, v, flags, method="localmean", max_iter=3, tol=1e-3, kernel_size=2
)
method accepts "localmean", "disk", or "distance" — and only those three. An unrecognized
name is not rejected; it falls through to an all-zero kernel and silently returns a useless field.
Note that replacement fills the flagged
positions with interpolated values — if you then overwrite them with NaN, the replacement was
wasted. Choose one or the other:
# Keep flagged vectors out of the analysis entirely, instead of interpolating them.
u = np.where(flags, np.nan, u)
v = np.where(flags, np.nan, v)
Smoothing
Smoothing is openpiv.smoothn.smoothn; there is no openpiv.smooth module. It returns a tuple
whose first element is the smoothed field, and it does not accept NaN input.
from openpiv.smoothn import smoothn
u_smooth, *_ = smoothn(np.nan_to_num(u), s=0.5) # s: larger == smoother
v_smooth, *_ = smoothn(np.nan_to_num(v), s=0.5)
u_smooth = np.asarray(u_smooth)
Visualization
Vector Field Plotting
display_vector_field reads a saved vectors file and calls plt.show() internally, so select a
non-interactive backend for batch runs.
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from openpiv import tools
fig, ax = plt.subplots(figsize=(8, 8))
tools.display_vector_field(
"vectors.txt",
ax=ax,
scaling_factor=96.52, # same factor used in scaling.uniform, to map back onto the image
scale=50,
width=0.0035,
on_img=True,
image_name="frame_a.bmp",
)
fig.savefig("vector_field.png", dpi=150, bbox_inches="tight")
plt.close(fig)
Custom Visualization
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
mag = np.sqrt(u**2 + v**2)
for ax, field, title, cmap in [
(axes[0], mag, "Velocity Magnitude", "viridis"),
(axes[1], u, "U Velocity", "RdBu_r"),
(axes[2], v, "V Velocity", "RdBu_r"),
]:
im = ax.imshow(field, cmap=cmap)
ax.set_title(title)
plt.colorbar(im, ax=ax)
fig.tight_layout()
fig.savefig("velocity_components.png")
plt.close(fig)
Analysis Functions
scripts/analyze.py bundles these against a params.npz written by runner.py. It infers the
physical grid spacing from the saved coordinates, so the derivatives come out per unit length:
import sys
sys.path.insert(0, "skills/openpiv/scripts")
from analyze import PIVAnalyzer
piv = PIVAnalyzer("results/params.npz")
vorticity = piv.compute_vorticity() # dv/dx - du/dy
exx, eyy, exy = piv.compute_strain()
stats = piv.compute_statistics() # u_mean, v_mean, rms_u, rms_v, tke
piv.plot_vector_field(save_path="quiver.png")
The standalone forms, if you would rather compute them inline:
Vorticity
def compute_vorticity(u, v, dx=1.0, dy=None):
"""Out-of-plane vorticity dv/dx - du/dy. Pass the physical grid spacing, not 1.0."""
dy = dx if dy is None else dy
return np.gradient(v, dx, axis=1) - np.gradient(u, dy, axis=0)
The grid spacing is (window_size - overlap) / scaling_factor in physical units, so leaving dx=1.0
yields vorticity per grid cell, not per unit length.
Sign convention: runner.py ends with transform_coordinates, which relabels the grid into a
right-handed y-up frame but leaves
Truncated for display — read the full file on GitHub.
Related Skills
taste-skill
112.0kAgent skills that give coding agents taste: art direction, reference images and a browser check instead of generic AI pages
Agent-Reach
110.3kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
79.1kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ai-job-search
70.5kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
