Synethetic DIC Data Generator
Introduction
It is useful to have reference data with known deformation and strain fields when evaluating the accuracy of DIC (Digital Image Correlation) software. For this purpose, I wrote a small speckle pattern image generator called DicGen. This free open source software, which is the subject of this post, produces synthetic 2D speckle images in which the deformation is known exactly.
In my previous post I described a new 2D DIC pipeline written in Python, and how I drove it with an LLM agent on 1,000 frames from a laboratory tensile test. That post ended with a validation claim: on a known dataset, the measured displacement was within 0.0066 px of the truth. In this post I will discuss how I generated that data.
Exemplar video, generated by DicGen, showing the speckle patterns during a 30 percent stretch in the horizontal x-direction, and a 10 percent contraction in the vertical y-direction.
Quick start
Requires Python 3.10+ with numpy, scipy and pillow. Writing the sequence.mp4
movie additionally needs the external ffmpeg binary on PATH (e.g.
apt install ffmpeg or brew install ffmpeg); without it the run still succeeds and
simply skips the movie, so you can also install it later.
# from the repository root
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt # numpy, scipy, pillow
pip install -e . # installs the `dicgen` console scriptMinimal invocation — a 5-image, 512×512 sequence with a total 2% stretch in x:
python -m dicgen --output-dir out/demo -n 5 --width 512 --height 512 --strain-x 0.02 --seed 0It prints a concise progress log and a summary such as:
dicgen 1.0.0: preparing out/demo
Created output directory out/demo
Speckle: 2153 dots, radii [3, 6] px, coverage 0.4989 (target 0.5, 2 calibration pass(es))
[1/5] image0001.png: t = 0.000000, valid area 100.00 %, grey levels 1-137
[2/5] image0002.png: t = 0.250000, valid area 100.00 %, grey levels 1-137
[3/5] image0003.png: t = 0.500000, valid area 100.00 %, grey levels 1-137
[4/5] image0004.png: t = 0.750000, valid area 100.00 %, grey levels 1-137
[5/5] image0005.png: t = 1.000000, valid area 100.00 %, grey levels 2-137
Movie sequence.mp4: 5 frames, 10 fps, 0.10 MB
Done in 0.43 s: 14 files in out/demo
Coverage 0.4989 (target 0.5), 2153 dots, mean valid area 100.00 %, max |u| 5.110 px, noise sigma 2 grey levels
Generation summary
------------------
output directory : out/demo
images : 5 x 512x512 px
files written : 14
dot coverage : 0.4989 (target 0.5, 2153 dots)
mean valid area : 100.00 %
max |u| : 5.1100 px
noise sigma : 2 grey levels (multiplicative 0)
seed : 0
movie : sequence.mp4 (10 fps)
elapsed : 0.43 sBoth entry points are equivalent: python -m dicgen … and the installed console script
dicgen …. The dataset verifier is a separate command: python -m dicgen.verify --dataset OUT
(or dicgen-verify --dataset OUT).
Generate synthetic DIC images with an LLM agent
DicGen comes with a SKILL.md file that makes it easy to generate DIC
datasets using an LLM agent. For example, give the agent this prompt:
I want you to create a synthetic DIC dataset in
/tmp/data. I want 20 images of size 400×400, with an applied engineering strain of 20% in the x-direction and −5% in the y-direction.
This prompt generates the requested DIC images as expected.
CLI reference
The full, always-current list of options is python -m dicgen --help (or dicgen --help). The most important options and their exact defaults:
| Option | Meaning | Default |
|---|---|---|
--output-dir | Directory for the images and ground truth (created if missing) | required |
--width | Image width in pixels | 1024 |
--height | Image height in pixels | 1024 |
-n, --num-images | Total number of images, including the undeformed reference (image 0) | 11 |
--strain-x | Total engineering strain in x over the whole sequence (0.02 = 2%) | 0.0 |
--strain-y | Total engineering strain in y over the whole sequence | 0.0 |
--rotation | Total rotation over the whole sequence, in degrees | 0.0 |
--translation-x | Total rigid-body translation in x, in pixels | 0.0 |
--translation-y | Total rigid-body translation in y, in pixels | 0.0 |
--deformation-mode | affine or sinusoid (adds a non-homogeneous field) | affine |
--sin-amplitude-x / --sin-amplitude-y | sinusoid mode: displacement amplitude in x / y, in pixels | 0.0 |
--sin-wavelength-x / --sin-wavelength-y | sinusoid mode: wavelength in x / y, in pixels | None → image width / height |
--dot-radius-min / --dot-radius-max | Range of speckle dot radii, in pixels | 3.0 / 6.0 |
--dot-coverage | Target fraction of the image area covered by dots (0 < c < 0.95) | 0.5 |
--dot-profile | taper or gaussian analytic dot profile | taper |
--dot-softness | taper profile: edge softness in pixels (sub-pixel anti-aliasing) | 0.7 |
--dot-amplitude-min / --dot-amplitude-max | Range of dot darkening relative to the background, in grey levels | 70.0 / 120.0 |
--background | Background grey level of the speckle pattern | 128.0 |
--min-dot-distance | Minimum distance between dot centres, in pixels (0 = unconstrained) | 0.0 |
--coverage-tolerance | Stop calibrating the dot count once the measured coverage is this close to the target | 0.01 |
--sampling | analytic (exact continuous field) or bicubic (spline resample of the reference) | analytic |
--interpolation-order | Spline order used by --sampling bicubic (1–5) | 3 |
--noise-sigma | Std-dev of additive Gaussian grey-level noise (0 = noise free) | 2.0 |
--multiplicative-noise-sigma | Relative sigma of the optional multiplicative speckle noise (0 = off) | 0.0 |
--seed | RNG seed; the run is fully reproducible | 0 |
--save-ground-truth / --no-ground-truth | Write / do not write the ground-truth CSVs | on |
--save-masks / --no-masks | Write / do not write the validity masks | on |
--save-displacement-maps | Write qa/ colour maps of |u| and arrow overlays | off |
--save-displacement-npy | Write displacement_field.npy ((n, 2, height, width) float32) | off |
--save-movie / --no-movie | Write / do not write sequence.mp4 from the image sequence (needs ffmpeg) | on |
--movie-fps | Playback rate of sequence.mp4, in frames per second | 10.0 |
--gt-grid NX NY | Sample grid of ground_truth_grid.csv, in points | 16 16 |
--overwrite | Delete everything inside --output-dir, including files not generated by dicgen | off |
--dry-run | Validate arguments, report what would be deleted, write nothing | off |
--log-level {DEBUG,INFO,WARNING,ERROR} | Logging verbosity | INFO |
-q, --quiet / -v, --verbose | Only warnings+errors / per-image debug logging | off |
--output-dir and --overwrite semantics
- If the directory does not exist it is created, including parents.
- If it exists, everything that this tool could have generated is deleted before new
files are written (so stale images from a longer previous run disappear). Recognised
content:
image####.png,ground_truth.csv,ground_truth_grid.csv,metadata.json,displacement_field.npy,sequence.mp4, and themasks/andqa/sub-directories when they contain only tool-generated files. - Anything else (“foreign content” — an unrelated file, a symlink, a directory with an
unknown name) is left untouched and the run is refused with a clear message that
lists it, unless
--overwriteis passed. With--overwrite, every entry inside the directory is removed (symlinks are unlinked, never followed). - The output directory itself is never deleted, and nothing outside it is ever touched.
As a safety net the tool refuses the filesystem root, the home directory, and the
current working directory as
--output-dir.
--dry-run
Validates all arguments, inspects the output directory and prints exactly what would be deleted, then exits without creating or deleting anything (the summary reports “dry run: nothing was written”).
Exit codes and validation
Arguments are validated up front and errors are actionable. Examples: n >= 2,
width/height >= 8, --dot-radius-max <= 25% of the shorter side,
0 < --dot-coverage < 0.95, --background - --dot-amplitude-max >= 0,
--interpolation-order in [1, 5], --gt-grid at least 2 2, and
--noise-sigma <= 128. Exit codes: 0 success, 2 argument/validation error
(including refused foreign content), 3 an OS/runtime error.
Jorgen Bergstrom