PyMultiDIC
PyMultiDIC is a Python-first multi-view digital image correlation workflow. It
wraps the full solving process as public Python API calls under
pymultidic.<function_name> while keeping native C++ acceleration for the
expensive Ncorr-style 2D DIC, CPU-only COLMAP SfM, and 3D reconstruction
stages.
The project can be used in two ways:
- Install the released package with
pip install pymultidicand call the API. - Build the repository locally, including the native C++ components under
native/, then run the same API from source.
The full user manual is available here: docs/pymultidic_usage_en.md.
Example Results
The bundled case/CylinderDIC example was solved through the Python API. A
small set of representative result files is stored under
docs/results/cylinderdic.
3D morphology cloud map
Total 3D displacement cloud map
Displacement component cloud maps
| Ux | Uy | Uz |
|---|---|---|
Additional copied outputs:
- docs/results/cylinderdic/sparse_scene.png
- docs/results/cylinderdic/camera_observations_2d.png
- docs/results/cylinderdic/camera_observations_3d.png
- docs/results/cylinderdic/recon3d_002.ply
- docs/results/cylinderdic/recon3d_report.json
- docs/results/cylinderdic/pipeline_report.json
The published example report was regenerated with the embedded
native_colmap sparse-source backend and the native_recon3d backend. The
current report registers all 12 cameras in one SfM model, starts from 14403
native sparse points, removes 36 spatial sparse-point outliers during product
export, and reconstructs 12926 valid 3D displacement points for frame
002.bmp. The SfM mean reprojection error is 0.109651 px.
Install From PyPI
pip install pymultidic
PyMultiDIC 2.x publishes native CPython 3.12 wheels for Windows x86_64 and
Linux x86_64 (manylinux_2_39, glibc 2.39 or newer). Other Python versions,
older Linux distributions, and macOS do not currently have supported binary
wheels.
Then call the package from Python:
import pymultidic
config = pymultidic.load_config("configs/MDIC.yaml")
report = pymultidic.run_pipeline(
config,
steps=["validate", "sfm", "scale", "mask", "dic2d", "recon3d", "visualize3d"],
)
You can also call the API without a YAML file. In direct-input mode,
case_root is required and the remaining paths and numerical parameters use
PyMultiDIC defaults unless overridden:
import pymultidic
report = pymultidic.run_pipeline(
case_root="case/CylinderDIC",
project_name="CylinderDIC",
steps=["validate", "sfm", "scale", "mask", "dic2d", "recon3d", "visualize3d"],
subset_radius=25,
subset_spacing=6,
min_corrcoef=0.6,
)
If an MDICConfig object is supplied, it is used as the base configuration.
Explicit keyword arguments still override matching fields for that call:
config = pymultidic.load_config("configs/MDIC.yaml")
pymultidic.run_sfm(config, colmap_workspace="colmap_native")
Local Native C++ Build
Use this route when developing the repository, changing files under native/,
or validating native builds before publishing wheels. The only supported local
source build path is the top-level native/CMakeLists.txt
entry point:
native_ncorr/libnative_ncorr.ancorr_clinative_recon3dpybind11 extensionnative_colmappybind11 extension
WSL / Linux example:
sudo apt-get update
sudo apt-get install -y \
build-essential cmake ninja-build python3-dev python3-pip python3-venv \
libboost-graph-dev libeigen3-dev libceres-dev \
libsqlite3-dev libgoogle-glog-dev libmetis-dev libsuitesparse-dev
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -U pybind11 scikit-build-core
cmake -S native -B build/wsl-native -G Ninja \
-DPYBIND11_FINDPYTHON=ON \
-DPython_EXECUTABLE=$(which python) \
-Dpybind11_DIR=$(python -m pybind11 --cmakedir)
cmake --build build/wsl-native
Expected WSL/Linux outputs:
build/wsl-native/ncorr/libnative_ncorr.a
build/wsl-native/ncorr/ncorr_cli
build/wsl-native/recon3d/native_recon3d*.so
build/wsl-native/colmap/native_colmap*.so
Windows example from a Developer PowerShell with CMake and Ninja available:
python -m pip install -U pybind11 scikit-build-core cmake ninja
cmake -S native -B build/windows-native -G Ninja -DPYBIND11_FINDPYTHON=ON
cmake --build build/windows-native
The locally validated Windows build for the embedded COLMAP source port uses a
dedicated build/native-colmap-port tree. From this repository root:
cmd /c ""C:\01project\ncorr\.tools\vsbt\VC\Auxiliary\Build\vcvars64.bat" >nul && python -m cmake -S native -B build\native-colmap-port -G Ninja -DCMAKE_MAKE_PROGRAM=C:\Users\LBD\AppData\Roaming\Python\Python313\Scripts\ninja.exe -DCMAKE_BUILD_TYPE=Release -DPYBIND11_FINDPYTHON=OFF -Dpybind11_DIR=C:\Users\LBD\AppData\Roaming\Python\Python313\site-packages\pybind11\share\cmake\pybind11"
cmd /c ""C:\01project\ncorr\.tools\vsbt\VC\Auxiliary\Build\vcvars64.bat" >nul && C:\Users\LBD\AppData\Roaming\Python\Python313\Scripts\ninja.exe -C build\native-colmap-port native_colmap -j 2"
Expected Windows outputs include:
build/windows-native/ncorr/ncorr_cli.exe
build/windows-native/recon3d/native_recon3d*.pyd
build/windows-native/colmap/native_colmap*.pyd
build/native-colmap-port/colmap/native_colmap*.pyd
After the native build, run the full example from the repository root:
python run.py --config configs/MDIC.yaml
run.py automatically prefers local build extensions, including
build/native-colmap-port/colmap, before falling back to installed modules, so
stale editable installs do not shadow the current checkout.
SfM always uses the embedded native_colmap extension in
native/colmap/src. It runs the trimmed COLMAP CorrespondenceGraph,
IncrementalMapper, IncrementalTriangulator, and local/global Ceres bundle
adjustment sources. There is no runtime backend selection or executable
fallback. The native mapper tries multiple initial-image pairs, scores complete
models by registration count, per-camera observation counts, camera-center
outliers, reprojection error, and 2D coverage, then exports only the selected
model. Exported sparse points are additionally filtered by 3D spatial
distribution before observations and figures are written. Model summaries,
candidate diagnostics, sparse outlier filter statistics, and native backend
capabilities are included in sfm_report.json and pipeline_report.json.
The maintained boundary is the narrow native_colmap API plus COLMAP text and
Multi-DIC product files. Unused source trees are not shipped.
Public API
Core API functions:
pymultidic.load_config(config_path, workspace_root=None)pymultidic.build_config(config=None, *, case_root=None, ...)pymultidic.validate_project(config=None, **kwargs)pymultidic.run_validate(config=None, **kwargs)pymultidic.run_sfm(config=None, **kwargs)pymultidic.run_scale(config=None, **kwargs)pymultidic.run_mask(config=None, **kwargs)pymultidic.run_dic2d(config=None, **kwargs)pymultidic.run_recon3d(config=None, **kwargs)pymultidic.run_visualize3d(config=None, **kwargs)pymultidic.run_step(config_or_step=None, step=None, **kwargs)pymultidic.run_pipeline(config=None, steps=None, stop_on_error=True, **kwargs)
See docs/pymultidic_usage_en.md for the full function-by-function parameter reference, return values, and examples.
Workflow
flowchart TD
A["Case folder<br/>camera images and calibration images"] --> B["validate<br/>check inputs and output folders"]
B --> C["sfm<br/>camera geometry, sparse points, observations"]
C --> D["scale<br/>checkerboard world-scale correction"]
C --> E["mask<br/>ROI masks from user masks or automatic logic"]
D --> F["dic2d<br/>native ncorr per-camera 2D DIC"]
E --> F
F --> G["recon3d<br/>triangulated 3D displacement and pair surfaces"]
G --> H["visualize3d<br/>morphology and displacement cloud maps"]
H --> I["reports, npz, ply, png results"]
Manual step-by-step control:
import pymultidic
config = pymultidic.build_config(
case_root="case/CylinderDIC",
project_name="CylinderDIC",
subset_radius=25,
subset_spacing=6,
min_corrcoef=0.6,
)
for step in ["validate", "sfm", "scale", "mask", "dic2d", "recon3d", "visualize3d"]:
report = pymultidic.run_step(config, step)
if not report.get("ok"):
raise RuntimeError(f"{step} failed: {report}")
Output Layout
By default, results are written under <case_root>/<output_root>. For the
bundled example this is case/CylinderDIC/results.
Common output folders:
logs/: JSON reports for each step and the full pipeline.sfm/colmap/: camera models, sparse points, observations, and COLMAP files.scale/: checkerboard scale correction outputs.masks/: ROI masks, overlays, and debug images.dic2d/: per-camera/per-frame DIC2D.npzoutputs.recon3d/: global 3D reconstruction.npzand.plyfiles.recon3d/pairs/<frame>/: MultiDIC-style pair surface meshes.recon3d/post/<frame>/: pair-surface post-processing results.figures/: 3D visualization outputs.figures/surface_clouds/: morphology, total displacement, and Ux/Uy/Uz cloud maps.
Programmatic access to visualization outputs:
vis_report = pymultidic.run_visualize3d(config)
outputs = vis_report["outputs"]
print(outputs["surface_cloud_morphology"])
print(outputs["surface_cloud_displacement_total"])
print(outputs["surface_cloud_displacement_ux"])
print(outputs["surface_cloud_displacement_uy"])
print(outputs["surface_cloud_displacement_uz"])
Reference Source
reference_code_lib/ is kept as local reference source code. The formal
PyMultiDIC implementation lives in the repository root, the pymultidic/
package, the multidic/ implementation modules, and the native C++ projects
under native/.
Release files for pymultidic 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pymultidic-2.0.0.tar.gz | 8.5 MB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pymultidic-2.0.0-cp312-cp312-win_amd64.whl | CPython 3.12 | CPython 3.12 | Windows x86-64 | Details |
| pymultidic-2.0.0-cp312-cp312-manylinux_2_39_x86_64.whl | CPython 3.12 | CPython 3.12 | Linux glibc 2.39+ x86-64 | Details |
Total release size: 28.3 MB
Release files / pymultidic-2.0.0.tar.gz
| Download URL | pymultidic-2.0.0.tar.gz |
|---|---|
| Size | 8.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0adc0b25d6f5354333c9a46c1f75d6f5eeadbdce891ce47b5cdf116b38e4a802
|
|
BLAKE2b-256 checksum How to use checksums |
528a208f1c8af811a92fa548f63c737882731bc7397d94b9ccac1d1495016fe5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 13, 2026.
Transparency logRelease files / pymultidic-2.0.0-cp312-cp312-win_amd64.whl
| Download URL | pymultidic-2.0.0-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 9.2 MB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
8afd86361c21d944ec1fe8cc824a4ad4ee8ed6af0cf988f5396d545d9f7f997e
|
|
BLAKE2b-256 checksum How to use checksums |
049117c8ab49fed32af3c4d47949e76de48d3ffe4b475a1f1de9b01197c7bb21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 13, 2026.
Transparency logRelease files / pymultidic-2.0.0-cp312-cp312-manylinux_2_39_x86_64.whl
| Download URL | pymultidic-2.0.0-cp312-cp312-manylinux_2_39_x86_64.whl |
|---|---|
| Size | 10.5 MB |
| Tags | CPython 3.12 Linux glibc 2.39+ x86-64 |
|
SHA-256 checksum How to use checksums |
9ff01469d896a68c3c71b2b09379e84aebdbfedb08b5e88461a14f2b794edbca
|
|
BLAKE2b-256 checksum How to use checksums |
86be71b6e63e008f0526509288872c7fcae2f242d6bc927c12a8cd5662e093e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 13, 2026.
Transparency log