Prepare 4.1.1 patch release#4554
Merged
Merged
Conversation
(cherry picked from commit 666d015)
### Background COLMAP's Caspar integration uses a small MSVC compatibility header for generated sources. This works for regular C/C++ compilation, but the same header was also force-included for CUDA sources through `-Xcompiler /FI`. That is too broad for NVCC builds on Windows: the compatibility header pulls in C++ standard library / MSVC headers during CUDA compilation, which can trigger MSVC/UCRT header redefinition errors with newer CUDA + Visual Studio toolchains. The CUDA sources only need a much smaller compatibility shim for MSVC, currently just the missing `uint` typedef used by generated Caspar CUDA code. This PR separates the CUDA pre-include path from the regular C/C++ forced include path and uses NVCC's native `--pre-include` option for CUDA files. ### Fix Keep the existing forced MSVC compact header for C/C++ sources, but use NVCC's native --pre-include with a CUDA-only compatibility header for Caspar CUDA sources. This avoids pulling C++ standard library headers into NVCC's MSVC host pass and fixes UCRT/MSVC header redefinition failures across supported CUDA + Visual Studio toolchains. ### Validation Tested on Windows with CUDA 12.5 and MSVC 19.44: - `caspar_lib_core` builds successfully in Release - `INSTALL` target completes successfully - Installed `COLMAP.bat help` runs successfully and reports CUDA support (cherry picked from commit 44e9f74)
## Problem Building with `-DCASPAR_ENABLED=ON` fails during Caspar kernel compilation when the target CUDA architecture is below compute capability 7.0. Caspar's Symforce-generated kernels use `cooperative_groups::labeled_partition` and `atomicAdd_block`, which require SM >= 7.0. The failure surfaces as low-level `nvcc` errors deep in the kernel build rather than a clear message, and is especially easy to hit in containerized / no-GPU builds where `CMAKE_CUDA_ARCHITECTURES` defaults to `native` and `nvcc` falls back to an older default arch. ## Fix Add a configure-time guard in `cmake/FindDependencies.cmake`, right after the arch list is finalized (i.e. after the `native` default is applied): - **FATAL_ERROR** when `CASPAR_ENABLED` and any resolved arch entry is `< 70`. The numeric check handles list entries and `-real`/`-virtual` suffixes. - **WARNING** for `native`/`all`/`all-major`, which cannot be resolved statically (the containerized/no-GPU case). This fails early with an actionable message instead of a cryptic kernel-compile error. ## Verification Tested the regex/comparison logic with `cmake -P`: | archs | result | |---|---| | `52`, `60;75`, `75;61` | FATAL | | `70`, `86`, `86-real`, `75;80;86;89;75-virtual` | pass | | `native`, `all`, `all-major` | warn | Fixes #4494 (cherry picked from commit a0477f9)
Fixes: #4490 ## Problem The SVG toolbar/menu icons do not render in the distributed Windows binaries produced by CI — they show up blank. The GUI loads all icons via `QIcon(":/media/*.svg")` (embedded Qt resources). For Qt to rasterize an SVG at runtime it needs two plugins deployed alongside the executable: - `iconengines/qsvgicon.dll` (icon engine used by `QIcon` for `.svg`) - `imageformats/qsvg.dll` Inspecting the artifact from a recent `main` run confirmed: - `bin/Qt6Svg.dll` ✅ present - `plugins/imageformats/` had only `qgif/qico/qjpeg` — **`qsvg.dll` missing** - `plugins/iconengines/` — **directory did not exist at all** ## Root cause A vcpkg triplet mismatch in `build-windows.yml`. The export contains `qtbase:x64-windows`, `qtbase:x64-windows-release`, and `qtsvg:x64-windows-release` — i.e. `qtsvg` is installed **only** for `x64-windows-release`. But the packaging step copied plugins from `x64-windows`: ``` cp -r vcpkg_export/colmap/installed/x64-windows/Qt6/plugins install ``` That grabs qtbase's base plugins but silently misses both qtsvg plugins. The DLLs were already copied from `x64-windows-release`, which is why `Qt6Svg.dll` was present but orphaned. ## Fix - Copy plugins from `x64-windows-release` — the triplet COLMAP is actually built against and where qtsvg lives (consistent with the DLL copy). This is a strict superset of what was copied before, plus the missing SVG plugins. - Add a fail-fast guard asserting both SVG plugins exist after the copy. The bug was invisible at build time (blank icons only appear at runtime), so the check prevents a silent regression. No C++ or `vcpkg.json` changes were needed — `qtsvg` was already a `gui` dependency; the defect was purely in packaging. (cherry picked from commit ef755a9)
(cherry picked from commit 52ff5a2)
Starting the COLMAP GUI (with no mapping requested) reads the entire database into a `DatabaseCache`, emitting logs like: ``` incremental_pipeline.cc:278] Loading database database_cache.cc:72] Loading rigs... database_cache.cc:90] Loading cameras... ... ``` The `MainWindow` constructor called `CreateControllers()`, which eagerly constructed an `IncrementalPipeline` whose constructor loads the database. This also fired on every New/Open Project and Import (via `ReconstructionReset()`). The work is redundant since the pipeline is rebuilt anyway in `ReconstructionStart()` right before running. Construct the mapper controller lazily — only when a reconstruction is actually started: - The constructor now just initializes control states via `UpdateMapperControls()` (already null-safe). - `ReconstructionReset()` calls the new `ResetMapperController()`, which tears down any running controller **without** building a new one (no DB read). - `CreateMapperController()` (renamed from the inaccurate plural `CreateControllers`) remains the single place that builds the pipeline, invoked from `ReconstructionStart()`. - Shared `Stop`/`Wait`/`reset` teardown factored into `StopMapperController()`. - `mapper_controller_` can now be null before the first start, so the relevant checks in `ReconstructionStart()`/`ReconstructionStep()` are null-guarded. After this change, launching the GUI and opening/importing projects no longer read the database — that only happens when mapping actually starts. - `ninja colmap_ui` builds cleanly. (cherry picked from commit 97bcd8f)
The example in the class comment passed the address of a local DatabaseCache/Reconstruction, but the constructor and BeginReconstruction now take std::shared_ptr (IncrementalMapper(std::shared_ptr<const DatabaseCache>) and BeginReconstruction(const std::shared_ptr< Reconstruction>&)). Update the example to construct and pass shared_ptrs so it matches the current API. Fixes #2656. (cherry picked from commit e2c17fa)
…4526) ## Problem Reported in #4398. There are two intentional but different quaternion layouts in the codebase, and the Python side does not document the difference: - The rig config JSON `cam_from_rig_rotation` is read in **`[w, x, y, z]`** order — see the `ReadRigConfig` header docs ("The rotation is expected in the order [w, x, y, z]") and `Eigen::Quaterniond(w, x, y, z)` in `rig.cc`. - `pycolmap.Rotation3d.quat` / `Rigid3d.rotation.quat` expose the quaternion in **`[x, y, z, w]`** order (Eigen coefficient order), as their own docstrings state. Because the `read_rig_config` binding docstring was just "Read the rig configuration from a .json file.", a Python user naturally assumes the JSON quaternion and `.quat` share the same layout and compares them directly, getting a surprising mismatch (the reporter's `assert np.allclose(...)` fails with the components swapped). ## Change Documentation only — no behavior change. Spell out both conventions in the `read_rig_config` docstring so the difference is discoverable via `help(pycolmap.read_rig_config)`. Addresses #4398 --------- Co-authored-by: Johannes Schönberger <jsch@meta.com> (cherry picked from commit f27a3e7)
Adds a Fedora section to doc/install.rst, covering the full set of build issues encountered on a fresh Fedora 43 system: package name differences (CGAL, OpenImageIO), CUDA toolkit setup via NVIDIA's official Fedora repo, the suitesparse-static packaging quirk, and the Ceres/FindGlog.cmake target-collision fix mentioned in #3347. --------- Co-authored-by: Johannes Schönberger <jsch@meta.com> (cherry picked from commit 6617573)
## Motivation When double-clicking an image in the 3D model viewer whose file cannot be found on disk, the image viewer window never appeared at all — the user only got a console error, even though the metadata was available. This makes the GUI more forgiving when working with reconstructions whose source images are no longer present. ## Changes - **Image viewer (double-click an image):** show the viewer with an empty image pane when the file cannot be read, so the metadata table (image/frame/rig id, camera, pose, point counts, name) remains visible. Previously `ShowPixmap` — which shows and raises the widget — was only called after a successful read, so a failed read returned early and the window never opened. The log is downgraded from error to warning. - **Image viewer table:** fix `ResizeTable` including the height of the *hidden* horizontal header, which left an empty strip below the last row that looked like an extra empty row. - **Point viewer (select a 3D point):** downgrade the missing-image log from error to warning. The loop already skips images that cannot be read, so a missing source image is expected rather than an error. (cherry picked from commit 89426d2)
## Summary - `exe/sfm.cc`: "follwoing" → "following" in the `pose_prior_mapper` `--overwrite_priors_covariance` help text. - `doc/faq.rst`: "conditiions" → "conditions". (cherry picked from commit bd7c30b)
…SAC (#4553) ## Problem Feature matching became ~4-6x slower after the OpenMP parallelization of `RANSAC`/`LORANSAC` introduced in #4169, as reported in #4482. ## Root cause The parallelization used an **unnamed** `#pragma omp critical` to guard updates to the shared best model/support. An unnamed `omp critical` maps to a *single process-global lock* shared by every unnamed critical region, and it is acquired **even when not inside a parallel region**. Geometric verification (`two_view_geometry.cc`) runs many `RANSAC`/`LORANSAC` calls concurrently across the matcher's `std::thread` pool, each with the default `num_threads == 1` (i.e. serial — the `#pragma omp parallel ... if (num_threads > 1)` region is never entered). Despite running serially, every model evaluation still acquired the one global critical lock, so all concurrent verification threads serialized on it in the hottest loop. ## Fix Replace the `#pragma omp critical` with a per-call `std::mutex`, locked via `std::unique_lock(..., std::defer_lock)` only when `num_threads > 1`: - **Serial path** (matching's default): no lock is taken at all → no cross-thread contention. - **Parallel path** (`num_threads > 1`): a call-local mutex, contended only among that call's own team threads → correctness preserved. ## Verification - `ransac_test` (6/6) and `loransac_test` (3/3) pass, including the `Parallel*` tests. - A standalone reproduction of the pattern confirmed the unnamed `omp critical` is ~100-150x slower than the per-call mutex under concurrent serial callers. - Audited the entire repository: these two were the only unnamed `omp critical` sites in first-party COLMAP code; all other `omp parallel` regions are either `num_threads(1)` or one-shot MVS worksharing loops and are unaffected. Fixes #4482 (cherry picked from commit 0af598c)
The Python binding of GPSTransform::Ellipsoid was exposed under the misspelled name GPSTransfromEllipsoid (Transfrom -> Transform). Rename it to GPSTransformEllipsoid. BREAKING CHANGE: the old misspelled name pycolmap.GPSTransfromEllipsoid is removed with no backwards-compatible alias. Update any code referencing it to pycolmap.GPSTransformEllipsoid. (cherry picked from commit 17f8ea1)
sarlinpe
approved these changes
Jul 17, 2026
ahojnnes
enabled auto-merge (squash)
July 17, 2026 17:21
ahojnnes
added a commit
that referenced
this pull request
Jul 18, 2026
Adds the `CHANGELOG.rst` entry for the upcoming **4.1.1** patch release. Documents the fixes being backported to `release/4.1` in #4554 (Improvements, Bug Fixes, and one Breaking Change — the `GPSTransfromEllipsoid` → `GPSTransformEllipsoid` pycolmap enum rename). Changelog-only; no code changes. The release date in the entry is provisional and can be adjusted when 4.1.1 is tagged.
Dawars
pushed a commit
to Dawars/colmap
that referenced
this pull request
Jul 22, 2026
Adds the `CHANGELOG.rst` entry for the upcoming **4.1.1** patch release. Documents the fixes being backported to `release/4.1` in colmap#4554 (Improvements, Bug Fixes, and one Breaking Change — the `GPSTransfromEllipsoid` → `GPSTransformEllipsoid` pycolmap enum rename). Changelog-only; no code changes. The release date in the entry is provisional and can be adjusted when 4.1.1 is tagged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prepares the 4.1.1 patch release by cherry-picking important bug/build fixes that landed on
mainsince4.1.0, plus one previously-unmerged fix. Features, refactors, algorithm changes, benchmarks, docs-site infra, and dependency upgrades frommainare intentionally excluded.Included fixes
Bug / correctness
max_image_sizeis provided (Fix: rescale already-undistorted images when max_image_size is provided. #4512)Build / packaging
GUI
Docs
read_rig_configbinding (Document quaternion order (wxyz vs xyzw) in read_rig_config binding #4526)IncrementalMapperdoc comment (Fix stale usage example in IncrementalMapper doc comment #4531)install.rst(doc: add Fedora build instructions to install.rst #4509)Previously unmerged
GPSTransfromEllipsoid→GPSTransformEllipsoid(Fix misspelled pycolmap enum name GPSTransfromEllipsoid #4551)#4551 removes
pycolmap.GPSTransfromEllipsoid(present since ≤4.1.0) with no backward-compatible alias. Downstream code must switch topycolmap.GPSTransformEllipsoid. This is unusual for a patch release and must be highlighted in the 4.1.1 release notes.Notes
UpdateMapperControls()/mapper_type_was removed and replaced with the 4.1.0 baseline's existing control-state handling; the net behavior (no database read at GUI startup / project reset) is preserved.-xreferences retained).Still TODO before tagging