v0.2.0 — reproducible source-built engine (COLMAP 4.0.4 + OpenMVS 2.4.0)
Added
- First full end-to-end run on the CPU image. The complete chain — COLMAP
sparse →image_undistorter→ OpenMVSDensifyPointCloud→ReconstructMesh
→RefineMesh→TextureMesh— now runs to completion on the CPU/arm64 image
against a real 70-image dataset, producing a textured OBJ. This closes the main
open 0.2.0 validation item (the engine had never been run through on a dataset). - Both images unified on COLMAP 4.0.4 + OpenMVS v2.4.0. The CUDA/production
Dockerfileand the CPUDockerfile.cpunow build the identical engine from
the identical pinned sources and recipe — the only differences are the CUDA
base image and the three-D*CUDA*flags. No image builds COLMAP 3.x any more. - OpenMVS bumped to v2.4.0 (both images). v2.3.0's
DensifyPointCloud
corrupts the heap and aborts on arm64 (it falls back off SSE); v2.4.0 swaps the
FLANN nearest-neighbour code for nanoflann and runs the full dense+mesh chain
cleanly. 2.4.0 needs two libs newer than Ubuntu 22.04 ships — nanoflann ≥1.5
and CGAL ≥6.0 — both header-only, so both Dockerfiles vendor pinned releases
(NANOFLANN_VERSION,CGAL_VERSION) rather than bumping the base off 22.04
(which would lose PDAL, dropped from 24.04). Two small source patches keep it
building against jammy's OpenCV (disable the hard libjxl requirement; map the
one OpenCV-4.7-only JXL write constant to the JPEG one — we emit no JPEG-XL). matcher=vocab_treenow works. Image-retrieval matching for large sets:
each image bakes in a SHA256-pinned vocabulary tree in the format its COLMAP
expects (FAISS for COLMAP 4). Previously the option was selectable but always
aborted with the opaque "Cannot process dataset".
Fixed
- CPU pipeline could not start: opaque "Cannot process dataset". A chain of
failures, each masking the next, all surfacing only as NodeODM's generic error:run.shapplied the defaultuse-gpu=trueeven on the CUDA-less CPU image,
so COLMAP's SIFT aborted ("Cannot use Sift GPU without CUDA or OpenGL"). It
now probes for a usable GPU (nvidia-smi -L) and falls back to CPU with a
loud warning when none is present.- COLMAP's CPU SIFT extractor OOM-killed itself fanning out over all cores on
large images; the CPU SIFT/match thread count is now capped
(EFFIGIES_CPU_THREADS, default 4). - COLMAP's CPU FLANN matcher segfaulted holding a full match block in memory;
the exhaustive block size is capped on the CPU path
(EFFIGIES_CPU_MATCH_BLOCK, default 10). InterfaceCOLMAPwas fed the rawsparse/0model instead of an undistorted
workspace;sparse_colmap.shnow runscolmap image_undistorterand
InterfaceCOLMAPreads$WORK/densewith the correct--image-folder.dense_openmvs.shpassed--cuda-deviceunconditionally; the CPU OpenMVS
build rejects it. The flag is now probed and passed only on CUDA builds.
- Source-built, pinned Dockerfile. COLMAP (
4.0.4) and OpenMVS (v2.4.0)
are now compiled from upstream source with CUDA, replacing the distro packages;
Eigen/CGAL/Boost/OpenCV come from Ubuntu 22.04. Versions are declared as build
ARGs and a build-timewhichgate fails the build loudly ifcolmap,
DensifyPointCloud,ReconstructMesh,RefineMesh,TextureMesh,
InterfaceCOLMAPorpdalis missing. - Georeferenced point cloud output. New
helpers/pointcloud_to_laz.pyapplies
the georef similarity toscene_dense.plyand writes
odm_georeferenced_model.lazvia PDAL (full projected coordinates, LAS
scale/offset for precision), and optionally builds an EPT tileset
(entwine_pointcloud/) for the Potree viewer whenentwine/untwineis
present.map_outputs.pymaps the LAZ + EPT into the WebODM paths, with the raw
PLY kept as a documented fallback. - CPU test image (
Dockerfile.cpu) — same pinned engine built without CUDA,
for local integration testing on machines without an NVIDIA GPU (e.g. Apple
Silicon). Plusdocs/DEPLOYMENT.md(local CPU + GPU host recipes, WebODM node
wiring) and a.dockerignore. - Unit tests for the point-cloud transform matrix (
tests/test_pointcloud.py) and
the NodeODM options translation (tests/test_options.py); the local runner and
CI now execute everytests/test_*.py.
Fixed
- Options were incompatible with NodeODM.
options.jsonwas a flat list, but
NodeODM (libs/odmInfo.js) expects an argparse-style descriptor object keyed by
--flag; it was serving every option asname="0".."12"with the wrong types.
helpers/optionsToJson.pynow translates our list into NodeODM's schema (enum
choices,<class 'int'>/float, bool viadefault, validmetavardomains),
so WebODM builds the correct task UI. (Found by actually running the node.) - Options shim could not find
options.json. It resolved the path with
abspath(__file__), which does not follow the NodeODM symlink — it looked in
/opt/NodeODM. Now prefersODM_PATHand falls back torealpath. mesh-decimatedomain changed tofloat: 0 <= x <= 1so it passes NodeODM's
checkDomainvalidation on task submission.
Notes
- NodeODM hard-skips a
--gcpUI option (WebODM handles GCP upload natively), so
thegcpoption is intentionally not shown;run.shstill auto-detects
gcp_list.txt. The node is verified to build, run, serve all options and be
reachable on the WebODM network; a full processing run on a dataset is the
remaining 0.2.0 validation.
Planned
See ROADMAP.md. Still open for 0.2.0: a full end-to-end processing
run on a real dataset, a verified VCGlib commit pin, and confirming the
InterfaceCOLMAP/InterfaceOpenSfM binary names across OpenMVS builds. Beyond
that: multi-view GCP triangulation (0.3.0).