Using PIVlab from the command line
Everything you do in the PIVlab window can also be done in a MATLAB script with
the functions of the pivlab package. They use exactly the same code as the GUI,
so a script and the GUI give identical results for identical settings. Every function works
with only its required inputs and then uses the PIVlab defaults.
The workflow in ten lines
imgs = pivlab.readImages("Example_data/Jet_*.jpg", "pairwise"); % select and pair images
imgs = pivlab.preprocess(imgs); % CLAHE + intensity stretch
res = pivlab.analyze(imgs); % PIV analysis
res = pivlab.filter(res); % remove outliers, interpolate
res = pivlab.toMetric(res, DeltaT=0.001, PxPerMeter=1234); % m and m/s
[res, vort] = pivlab.derive(res, "vorticity"); % derived quantities
m = pivlab.temporal(res, "mean"); % temporal mean
pivlab.display(m, Overlay="magnitude"); % figure like in the GUI
pivlab.saveSession(res, "analysis.mat"); % open it in the GUI later
The PIVlab folder must be on the MATLAB path (addpath it, or start PIVlab once).
Type help pivlab for an overview and help pivlab.analyze (etc.) for all
options of a step. Complete examples are in Example_scripts/PIVlab_api_*.m.
The functions
| Function | What it does | GUI equivalent |
|---|---|---|
pivlab.readImages(source, sequencing) | File pattern, folder or file list; sequencing "pairwise", "timeresolved" or "reference". Multi-page TIFFs, .b16 and pco.panda double images work as in the GUI. | Import images |
pivlab.preprocess(imgs, ...) | CLAHE, high-pass, intensity capping, Wiener filter, intensity limits, background removal (Background="mean" / "min"), region of interest (Roi), mask (Mask) and camera calibration (Camera, see below). | Image settings |
pivlab.getImage(imgs, k) | One image, pre-processed exactly as it goes into the analysis. | Apply and preview |
pivlab.analyze(imgs, ...) | FFT window deformation (default), ensemble, DCC or optical flow; window sizes, passes, robustness, uncertainty, parallel processing. | PIV settings + Analyze |
pivlab.filter(res, ...) | Standard deviation filter, normalized median test, velocity limits, correlation / notch / image-based filters, second-peak substitution, interpolation. | Vector validation |
pivlab.toMetric(res, ...) | Calibration with DeltaT and PxPerMeter (or ReferenceDistance), origin and axis directions. | Calibration |
pivlab.derive(res, quantity) | Vorticity, magnitude, u, v, divergence, Q criterion, shear, strain, direction, LIC, correlation, uncertainty; optional spatial / temporal smoothing. | Derive parameters |
pivlab.temporal(res, op) | Mean, standard deviation, sum, TKE (also phase averages with Frames={...}). | Temporal statistics |
pivlab.display(res, ...) | Vectors on the particle image or on a transparent colour map with colour bar, all options of the plot settings. | Plot appearance |
pivlab.defaults(), pivlab.loadSettings(file) | Default settings, or the settings of a settings file or session (detected automatically). | Load settings |
pivlab.loadSession(file), pivlab.saveSession(res, file) | Results of a session; save results as a session that opens in the GUI. | Load / save session |
pivlab.stereo(imgs, boards, board, ...) | Stereo-PIV of two cameras: stereo calibration, disparity correction, PIV of both cameras, U, V and W (see below). | Stereo-PIV panel + Analyze |
The result struct
res holds all image pairs; the third dimension of the arrays is the pair.
| Field | Content |
|---|---|
res.x, res.y | vector positions (px, or m after toMetric) |
res.u, res.v | velocities in res.units (filtered after pivlab.filter, smoothed after a smoothed pivlab.derive) |
res.u_raw, res.v_raw | unfiltered PIV result |
res.typevector | 1 = valid, 2 = removed/interpolated, 3 = second correlation peak, 0 = masked |
res.correlation_map | correlation coefficient of every vector |
res.derived | maps computed by pivlab.derive, e.g. res.derived.vorticity |
res.px | the same data in pixel units, as PIVlab stores it internally |
You can change values in res.u / res.v by hand, e.g.
res.u(res.u > 10) = NaN;. The next pivlab.* function takes the changed
values over (all other values stay exactly as computed). pivlab.filter always starts
again from the unfiltered data (res.u_raw), so change res.u_raw if the
filter should use your values. Adding or removing vectors or frames is not possible; use the
Pairs option of pivlab.analyze or Frames of
pivlab.temporal.
Useful options
Verbose=falseswitches the progress messages ofpreprocess,analyze,filter,toMetricandsaveSessionoff, e.g. in batch scripts over many folders. Warnings are still shown.Roi="none",Mask="none"(pivlab.preprocess) andVelocityLimits="none"(pivlab.filter) switch off a value that came in throughSettings.- Filter limits (
VelocityLimits,NotchLimits) are in the units the result has at that moment: px/frame beforepivlab.toMetric, m/s after it. A warning appears when more than half of the vectors are removed.VelocityLimitsFreehandtakes an outline[u v](one row per point) like the freehand velocity limits of the GUI; sessions and settings bring it along. pivlab.analyzewarns when you set an option that the chosen algorithm does not use (e.g.Passeswith"dcc",Parallelwith"ensemble").
Settings
Name=value arguments change single settings. To reuse a complete configuration, pass a settings
struct: from pivlab.defaults (then edit it), or from a file saved in the GUI.
s = pivlab.loadSettings("my_session.mat"); % session or settings file
imgs = pivlab.preprocess(pivlab.readImages("D:\run1\*.tif", "pairwise"), Settings=s);
res = pivlab.analyze(imgs, Settings=s, Parallel=true);
res = pivlab.filter(res, Settings=s);
res = pivlab.toMetric(res, Settings=s); % calibration of the session
Camera calibration (lens undistortion, rectification)
Make the camera calibration in the PIVlab window
(Camera calibration & rectification): there you see
the detected markers and the corrected images. A script then uses the result with the
Camera option of pivlab.preprocess:
imgs = pivlab.readImages("D:\run1\*.tif", "pairwise");
% undistortion and rectification exactly as in a session
imgs = pivlab.preprocess(imgs, Camera="my_session.mat");
% or: undistortion from a file saved with "Save camera parameters"
imgs = pivlab.preprocess(imgs, Camera="camera_calibration.mat");
| Option | Values |
|---|---|
Camera | A PIVlab session: lens undistortion and rectification exactly as they were
used in the session. A camera calibration file saved with Save camera parameters:
lens undistortion only. A cameraParameters object (Computer Vision Toolbox).
"none" switches the undistortion off. |
CameraView | "valid" (cut away black borders), "same"
(same size as the input image) or "full" (include black borders). Default: as in the
session, else "valid". |
Rectification | false switches the rectification of a session off;
a 2-D transformation (e.g. projtform2d) rectifies the undistorted images with it.
Default: as in the session. |
All following steps use the corrected images: Roi and Mask refer to them,
imgs.imageSize is their size, and pivlab.getImage shows them.
pivlab.loadSession restores the camera calibration of a session, and
pivlab.saveSession saves it, so the GUI opens the session with the same corrected images.
Stereo-PIV
pivlab.stereo does what the Stereo-PIV panel and
Analyze do in the GUI. It needs the images and the lens calibration of both
cameras, one image of the ChArUco board in the light sheet per camera, and the board definition:
imgs{1} = pivlab.preprocess(pivlab.readImages("D:\run1\cam1\*.tif", "pairwise"), Camera="lens_cam1.mat");
imgs{2} = pivlab.preprocess(pivlab.readImages("D:\run1\cam2\*.tif", "pairwise"), Camera="lens_cam2.mat");
board = struct('PatternDims',[23 24], 'CheckerSize',10, 'MarkerSize',7, ...
'MarkerFamily',"DICT_4X4_1000", 'OriginCheckerColor',"black"); % sizes in mm
res = pivlab.stereo(imgs, {"board_cam1.tif", "board_cam2.tif"}, board);
res = pivlab.filter(res); % validates both cameras, then U, V, W again
res = pivlab.toMetric(res, DeltaT=0.001); % the scale comes from the stereo calibration
pivlab.display(res, Overlay="w");
- The result is a normal result with the out-of-plane component in addition:
res.w(andres.px.w), positive away from camera 1.res.stereoholds the stereo calibration, the disparity (with the report of the Stereo-PIV panel inres.stereo.disparity.report), the 2D results of both cameras and the reconstruction residual. - As Analyze in the GUI,
pivlab.stereodoes not validate: callpivlab.filter.VelocityLimitsact on U, V of the 3D result andNotchLimitson the 3D magnitude; the removed vectors are removed in both cameras. pivlab.derive(res, "w")gives the w component,"magnitude"the 3D magnitude.pivlab.temporalincludes W.pivlab.displayshowsOverlay="w"andOverlay="residual"(how well both cameras fit one 3D velocity, px).Settings=pivlab.loadSettings("stereo_session.mat")uses the pre-processing, PIV and Stereo-PIV panel settings of a GUI session.pivlab.loadSessionreads stereo sessions of the GUI,pivlab.saveSessionwrites sessions that the GUI opens in stereo mode.
A complete example: Example_scripts/PIVlab_api_stereo.m, with the synthetic stereo
recording of a free jet in Example_data/stereo_free_jet.
Why pre-processing does not create new images
pivlab.preprocess only stores the settings (and computes the background image if
requested). The filters are applied to every pair inside pivlab.analyze, exactly
like the GUI does it, so long image series need no extra memory or disk space.
pivlab.getImage shows a pre-processed image.
Parallel=true).