Contents

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 script

Minimal 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 0

It 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 s

Both 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:

OptionMeaningDefault
--output-dirDirectory for the images and ground truth (created if missing)required
--widthImage width in pixels1024
--heightImage height in pixels1024
-n, --num-imagesTotal number of images, including the undeformed reference (image 0)11
--strain-xTotal engineering strain in x over the whole sequence (0.02 = 2%)0.0
--strain-yTotal engineering strain in y over the whole sequence0.0
--rotationTotal rotation over the whole sequence, in degrees0.0
--translation-xTotal rigid-body translation in x, in pixels0.0
--translation-yTotal rigid-body translation in y, in pixels0.0
--deformation-modeaffine or sinusoid (adds a non-homogeneous field)affine
--sin-amplitude-x / --sin-amplitude-ysinusoid mode: displacement amplitude in x / y, in pixels0.0
--sin-wavelength-x / --sin-wavelength-ysinusoid mode: wavelength in x / y, in pixelsNone → image width / height
--dot-radius-min / --dot-radius-maxRange of speckle dot radii, in pixels3.0 / 6.0
--dot-coverageTarget fraction of the image area covered by dots (0 < c < 0.95)0.5
--dot-profiletaper or gaussian analytic dot profiletaper
--dot-softnesstaper profile: edge softness in pixels (sub-pixel anti-aliasing)0.7
--dot-amplitude-min / --dot-amplitude-maxRange of dot darkening relative to the background, in grey levels70.0 / 120.0
--backgroundBackground grey level of the speckle pattern128.0
--min-dot-distanceMinimum distance between dot centres, in pixels (0 = unconstrained)0.0
--coverage-toleranceStop calibrating the dot count once the measured coverage is this close to the target0.01
--samplinganalytic (exact continuous field) or bicubic (spline resample of the reference)analytic
--interpolation-orderSpline order used by --sampling bicubic (1–5)3
--noise-sigmaStd-dev of additive Gaussian grey-level noise (0 = noise free)2.0
--multiplicative-noise-sigmaRelative sigma of the optional multiplicative speckle noise (0 = off)0.0
--seedRNG seed; the run is fully reproducible0
--save-ground-truth / --no-ground-truthWrite / do not write the ground-truth CSVson
--save-masks / --no-masksWrite / do not write the validity maskson
--save-displacement-mapsWrite qa/ colour maps of |u| and arrow overlaysoff
--save-displacement-npyWrite displacement_field.npy ((n, 2, height, width) float32)off
--save-movie / --no-movieWrite / do not write sequence.mp4 from the image sequence (needs ffmpeg)on
--movie-fpsPlayback rate of sequence.mp4, in frames per second10.0
--gt-grid NX NYSample grid of ground_truth_grid.csv, in points16 16
--overwriteDelete everything inside --output-dir, including files not generated by dicgenoff
--dry-runValidate arguments, report what would be deleted, write nothingoff
--log-level {DEBUG,INFO,WARNING,ERROR}Logging verbosityINFO
-q, --quiet / -v, --verboseOnly warnings+errors / per-image debug loggingoff

--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 the masks/ and qa/ 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 --overwrite is 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.