Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
527 commits
Select commit Hold shift + click to select a range
2f4efa4
fix: try to fix read issue on remotes
Apr 25, 2026
5ea10eb
feat: read DJI calibrated focal (for drone who provide it)
Apr 25, 2026
33ffd0e
feat: BIG BIG update on calib' DB
Apr 25, 2026
52814f2
feat: add sensefly cameras
Apr 25, 2026
4d1d2b8
feat: support per-image GPS std dev. (DJI and Ebee)
Apr 25, 2026
d0f4692
fix: fixed ebee X calib' data
Apr 26, 2026
661ccf6
feat: properly handle no-GPS images separately
Apr 27, 2026
a0ee236
feat: observations pool
Apr 27, 2026
f037ef5
feat: add report-only flag in case of failed runs
Apr 28, 2026
f94b9f1
feat: IRLS solver
Apr 28, 2026
a07f794
fix: adjust annealing
Apr 28, 2026
1281d6d
fxi: IRLS tweak : removed loss and threshold on projection since IRLS…
Apr 28, 2026
cfb3312
feat: per-shot projection groups
Apr 30, 2026
810b995
fix: fix bootstrap
Apr 30, 2026
04e1c81
feat: parallelize over groups now that we have a lot of them !
Apr 30, 2026
5fbe375
feat: add relative_altitude support in C++
Apr 30, 2026
28c4d32
feat: add GSD in the report
Apr 30, 2026
506fc24
feat: add csv2 parsing ++ mmaped tracksmanager file and absl use
Apr 30, 2026
9fd9550
fix: unify memory log output
May 1, 2026
1296175
fix: load the tracks manager by mmaping directly
May 1, 2026
6b2e136
refactor: full C++ grow loop
May 1, 2026
7bf5de0
feat: full C++ triangulation and fremoved Python dead code
May 6, 2026
8f818f3
fix: fixed tests
May 6, 2026
a340e39
feat: specific GPS density ratio
May 10, 2026
dbc3fc3
fix: fixed no-GPS dataset handling
May 11, 2026
860e979
versioning: v0.8 intermediate release before v1.0
May 11, 2026
cfa9cdc
refactor: first sync : move all the C++ code under lib
May 6, 2026
67cbd5f
feat: first sync with the app : conda lock-based build, markdown doc
May 8, 2026
053af9e
fix: handle worktrees correctly
May 11, 2026
fb77c18
feat: add Python and C++ code coverage
May 11, 2026
f2d82c4
feat: moar unit tests
May 11, 2026
31d3130
feat: even moar test coverage
May 11, 2026
ac3117f
feat: add moar Python tests
May 11, 2026
7f395f6
refactor: unify build destination
May 11, 2026
ac71878
refactor: let's remove empty files just for the sake of C++ instancia…
May 11, 2026
e54c19b
feat: big doc update
May 11, 2026
2afbbb7
fix: fixed flaky tests
May 11, 2026
a8b6c81
fix: flaky mac
May 11, 2026
2485bcb
fix: flaky mac again
May 11, 2026
8b1c735
fix: updated README
May 11, 2026
1ef91db
feat: moar cameras : fc6310 series, hasselblad, and two sony lenses
May 12, 2026
8e7a0c2
fix: fix GCP export (not used for now)
May 12, 2026
2235fcd
feat: multithreaded EXIF extraction
May 12, 2026
cced3f5
Update CONTRIBUTING.md
YanNoun May 12, 2026
dac0bbd
Update coverage badge [skip ci]
github-actions[bot] May 12, 2026
3f4bfcc
fix: update Mac conda. Always use python and not python3 since conda-…
May 20, 2026
28b471a
Fixing windows compilation
Jun 3, 2026
baad6d8
Fixing CI test failures
Jun 3, 2026
952f62a
Patching dockefile Ubuntu20 to also use lock files
Jun 3, 2026
061fca3
Patching .github workflow to use conda.lock
Jun 3, 2026
90e92a8
Merge pull request #3 from OpenSfM/fix-windows-build
YanNoun Jun 4, 2026
36b61ce
Update coverage badge [skip ci]
github-actions[bot] Jun 4, 2026
53bfc86
Sync master
pierotofy Jun 4, 2026
30c47e5
Remove stray folder
pierotofy Jun 4, 2026
1c456e1
Merge pull request #2 from pierotofy/dspsift
DodgySpaniard Jun 6, 2026
ffe32e4
Update coverage badge [skip ci]
github-actions[bot] Jun 6, 2026
9a907a8
feat: online binary projection trained batched OpenCL matching
May 13, 2026
f5547e5
feat: // binarisation
May 13, 2026
79eaf2f
feat: custom lowes for hamming
May 13, 2026
07d7714
feat: robust GCPs and per-GCP report
May 20, 2026
a83167e
feat: fancier report
May 20, 2026
a8e6a5d
feat: added overlap computation and stats
May 20, 2026
c3b2f7f
feat: restructured summary with more data
May 20, 2026
4c47f06
feat: unify colors
May 21, 2026
e914430
feat: add checkpoint support in BA and report
May 22, 2026
150bf4a
fix: fix orientation of topview
May 22, 2026
48aa7c2
fix: sigma not relevant for CPs
May 22, 2026
af1fdf5
fix: better naming
May 22, 2026
99bcb8d
feat: add report customisation
May 24, 2026
1448765
feat: custom accent color
May 24, 2026
9aec15c
feat: full support of CRS in JSON GCP file and vertical CRS in report
Jun 2, 2026
75a5a29
fix: proper return of vertical CRS
Jun 3, 2026
c714615
feat: add geolocation extraction from CSV to exif_overrides. As autom…
Jun 3, 2026
06384d5
feat: add support for local/CDN PROJ grids, and robustify geoloc extr…
Jun 3, 2026
10ebbd8
feat: not robust foot print for now
Jun 3, 2026
8b00a0c
feat: add GCP conversion command
Jun 3, 2026
128bad1
feat: do not run over all landmarks to clean them
Jun 4, 2026
fff0918
feat: robust initialisation
Jun 5, 2026
3891cca
feat: large datasets support
Jun 5, 2026
f150d8f
fix: reload features if exist in case of corruptef files
Jun 9, 2026
4c71e8c
features : hello CLAUDE
Jun 9, 2026
8287801
feat: metric + language localisation
May 20, 2026
4f52c21
fix: cap points for topview rendering
Jun 9, 2026
a5dfe68
feat: we let the stochastic bundle like this. We need to re-attach th…
Jun 9, 2026
8e4a9be
fix: fixed tests
Jun 9, 2026
178a426
fix: fixed moar
Jun 9, 2026
e19d604
fix: fix opencl matching test
Jun 10, 2026
294ba68
fix: use fractional constant for conversion
Jun 10, 2026
8fe72f2
Merge pull request #4 from OpenSfM/feat-checkpoint
YanNoun Jun 10, 2026
1efe211
Update coverage badge [skip ci]
github-actions[bot] Jun 10, 2026
0ecd86a
fix: can't repro on mac, but still bumping a bit the tol expecting it…
Jun 10, 2026
c759368
Update coverage badge [skip ci]
github-actions[bot] Jun 10, 2026
8d16ff8
fix: fix stats on large projects (> 5K)
Jun 10, 2026
35f0fdd
fix: fixed footprint computation by projecting on the direction vector
Jun 10, 2026
0ec3605
fix: properly skip outliers with the refactored loops
Jun 10, 2026
1a49864
Check for python3 as fallback
pierotofy Jun 11, 2026
af01b06
Fix compilation when Ceres is installed outside of system path
pierotofy Jun 11, 2026
f40b317
Fix GIL release in ReconstructFromTracksManager
pierotofy Jun 11, 2026
488828d
fix: adress PR remarks
Jun 11, 2026
22de806
Merge pull request #5 from OpenSfM/fix-stats-large-projects
YanNoun Jun 17, 2026
d22fe12
feat: DIF training
Jun 9, 2026
100b625
Update coverage badge [skip ci]
github-actions[bot] Jun 17, 2026
c0cd17b
feat: add cache clear for not OOMing during training
Jun 17, 2026
60f070d
feat: 28 aerial 80 pairs 4M pairs of training on HAHOG
Jun 17, 2026
6eb22cc
fix: fix unique lowes ratio
Jun 17, 2026
91dec58
Merge pull request #7 from OpenSfM/feat-dif-training
YanNoun Jun 18, 2026
0686b6f
Update coverage badge [skip ci]
github-actions[bot] Jun 18, 2026
43c0704
Add search for conda python first
YanNoun Jun 18, 2026
f209b9e
Merge pull request #6 from pierotofy/buildfix
YanNoun Jun 18, 2026
210f80e
Update coverage badge [skip ci]
github-actions[bot] Jun 18, 2026
b133854
fix: README update
Jun 18, 2026
83c83cb
Update coverage badge [skip ci]
github-actions[bot] Jun 18, 2026
4a1d78a
fix: fine-tweaked for less smoothing + leftovers
May 10, 2026
5ad797b
feat: photometric TSDF refinement 1st shot
May 10, 2026
e10510a
feat: finer dense locked-in
May 11, 2026
0cae625
feat: we need specific config for terrestrial
May 11, 2026
09844fd
refactor: clean legacy fuser and proto/dead code
May 11, 2026
e134e24
feat: DSM as K-Mode running detection. Also cleaned the rest of the l…
May 12, 2026
17e63ef
feat: it's full of crap but it works, we'll clean the mess later
May 22, 2026
a753c0e
feat: multi-resolution
May 22, 2026
749029d
feat: less artifacts on MR
May 23, 2026
c46f62e
feat: decimate and adaptive
May 23, 2026
c821b9b
feat: working refinement (colors out of the equation)
May 23, 2026
d722527
feat: degeneracy check + cons 05
May 24, 2026
d22bc57
fix: refinement still doesn't do much
May 25, 2026
ee29afc
fix: use clean depth prior for color bake
May 26, 2026
f533ae7
feat: multi-scale DSM
May 26, 2026
0e5b96a
feat: rasterisation based DSM
May 26, 2026
67fa411
feat: integrated point cloud fusion + DSM + ortho
May 26, 2026
46fe68c
feat: two-pass carve.
May 26, 2026
c1832f2
feat: DSM fix + logging
May 27, 2026
837e6c5
fix: faster
May 27, 2026
a99f2c0
feat: finer
Jun 7, 2026
f583c44
feat: CLAUDE and timebomb
Jun 7, 2026
870ee4c
feat: clean
Jun 7, 2026
472c959
feat: bake revamp
Jun 7, 2026
ba279dd
feat: working refine
Jun 7, 2026
1bbbf67
feat: finer locked
Jun 8, 2026
540488c
refactor: cleaned legacy DSM
Jun 8, 2026
2952d50
feat: revamp, dual-contouring-based, DSM and ortho
Jun 10, 2026
4c4813f
feat: smoother ortho
Jun 10, 2026
1467768
feat: fill hole filling
Jun 10, 2026
9831df2
feat: ortho validated
Jun 11, 2026
65dea34
feat: fill more holes
Jun 11, 2026
e20d104
feat: dense locked
Jun 17, 2026
991c799
feat: LAS/LAZ export, fixed OOM on octree building
Jun 17, 2026
2af8213
feat: fixed memory grow, oblique painting in empty regions and re-use…
Jun 18, 2026
4f085e6
feat: original-surface attach term + early stop RMS tracking-bsed
Jun 18, 2026
0328906
feat: refine + patchmath optim
Jun 19, 2026
a75f236
feat: cluster split optim
Jun 19, 2026
e560fa1
feat + fix: remove LUMA as it doesn't improve. Fix > max views ortho …
Jun 22, 2026
65793f7
refactor + fix : much better clustering + 4-command big split
Jun 22, 2026
b98681d
feat: fix ortho and DSM speckle
Jun 23, 2026
e5b8e13
feat: auto voxel size
Jun 23, 2026
95523ac
feat: DSM/Ortho locked
Jun 24, 2026
d137d57
fix: cosmetics
Jun 24, 2026
8d0a2a0
feat: on the fly delete of RAW
Jun 24, 2026
5abc4b1
fix: always save clusters
Jun 24, 2026
36aeef2
fix: remove old code
Jun 24, 2026
ea68332
fix: fixed tests
Jun 24, 2026
1edd3d7
Rolling shutter correction for aerial datasets
pierotofy Jun 24, 2026
39120d6
Update description
pierotofy Jun 24, 2026
7dff856
fix: add missing configs
Jun 24, 2026
471b6c6
feat: clamp oblique views + fix clean crazy bug
Jun 25, 2026
18603ff
feat: geo-referenced outputs
Jun 25, 2026
6dabcfa
feat: README update
Jun 25, 2026
649b391
feat: add mesh export (no watertight, no hole-filled)
Jun 25, 2026
ee96c24
feat: add example report
Jun 25, 2026
797e1d1
feat: add DSM/Ortho in the report
Jun 25, 2026
3cbb822
fix: fix the doc
Jun 25, 2026
a576543
feat: update doc
Jun 25, 2026
b0db24c
feat: moar docs
Jun 25, 2026
fef97ba
feat: add missing bits
Jun 25, 2026
7e20e83
feat: handle projection accurately
Jun 25, 2026
d75932a
fix: fix conda mac
Jun 25, 2026
61f0ee1
fix: fixed tests
Jun 25, 2026
244988d
fix: that test needs a revamp ...
Jun 25, 2026
77bbaf5
Rename rolling shutter command
pierotofy Jun 26, 2026
30ee558
Merge pull request #8 from pierotofy/rscorrect
DodgySpaniard Jun 26, 2026
b783935
Update coverage badge [skip ci]
github-actions[bot] Jun 26, 2026
abc0e5f
feat: doc update
Jun 27, 2026
3a78d7c
feat: release log
Jun 28, 2026
9f96e8c
feat: finally chunk-wise dense generation, so much faster.
Jun 28, 2026
6c998e2
feat: object specific configs + dense timings
Jun 28, 2026
8d31aa9
feat: configs doc
Jun 28, 2026
24116d7
feat: dense benchmark + exif_overrides
Jun 28, 2026
625f2e2
feat: image equalisation
Jun 29, 2026
3bf007e
feat: auto-boundary
Jun 29, 2026
1764177
feat: discord link
Jun 29, 2026
b75b49e
feat: equalize in benchmark
Jun 29, 2026
0a3043c
feat: local staging for benchmark
Jun 29, 2026
70e1fef
feat: dense locked final
Jun 29, 2026
3a313d6
feat: doc update
Jun 29, 2026
8ed7f4e
feat: moar doc
Jun 29, 2026
78b7884
feat: 3D error and CE90 LE90 stats
pierotofy Jun 29, 2026
0f58ac6
Fix test
pierotofy Jun 29, 2026
61c94d9
Fix bold font mismatch
pierotofy Jun 29, 2026
18a392d
Comment fix
pierotofy Jun 29, 2026
8a637ba
feat: slower on Macs
Jun 29, 2026
ec7be71
fix: FUCK MACOS AND YOUR BUGGY OPENCL (I know it's deprecated ...) AN…
Jun 29, 2026
95982f2
fix: removed flaky test
Jun 29, 2026
ec37b96
fix: flaky forever
Jun 29, 2026
0351ef8
feat: one last bit of issues
Jun 29, 2026
dfa8f1c
Merge pull request #9 from OpenSfM/feat-new-dense
YanNoun Jun 29, 2026
174c86b
Update coverage badge [skip ci]
github-actions[bot] Jun 29, 2026
287daa6
Merge branch 'master' of https://github.com/OpenSfM/OpenSfM into td
pierotofy Jun 30, 2026
e3f2cc1
Random sampling for point selection
pierotofy Jun 30, 2026
96eac46
Merge pull request #10 from pierotofy/td
YanNoun Jun 30, 2026
63c6dba
Update coverage badge [skip ci]
github-actions[bot] Jun 30, 2026
09e4a6f
fix: remove per track albedo which has a very weird gauge freedom, le…
Jun 30, 2026
341ff2a
Merge pull request #11 from OpenSfM/fix-equalize-drift
YanNoun Jun 30, 2026
851ebdc
Update coverage badge [skip ci]
github-actions[bot] Jun 30, 2026
c4400a9
Merge 1.0
pierotofy Jun 30, 2026
f9757b6
Remove old submodule
pierotofy Jul 1, 2026
39960fe
Fix bundle adjuster constant aspect ratio constraint
pierotofy Jul 1, 2026
d6bcd77
Fix type annotation
pierotofy Jul 1, 2026
0448afc
Remove LRU cache module, adjust report styling
pierotofy Jul 1, 2026
137f30a
Less verbose
pierotofy Jul 1, 2026
a67d17f
Less verbose logging, fix export_geocoords
pierotofy Jul 1, 2026
feb2535
Report fixes
pierotofy Jul 1, 2026
ca26a8c
Report export working
pierotofy Jul 1, 2026
6b943ab
Fix topview orientation
pierotofy Jul 1, 2026
3b230d7
Invert cmap
pierotofy Jul 1, 2026
6f1ac72
Quieter logs
pierotofy Jul 2, 2026
d8e78ef
Quieter yet
pierotofy Jul 2, 2026
563ff9b
Disable pydense for now
pierotofy Jul 2, 2026
08a9b22
SVO fuser optional
pierotofy Jul 2, 2026
3647086
Use TCMALLOC minimal, prioritize linking
pierotofy Jul 3, 2026
918d613
Bring GCP observations and coordinates stats
pierotofy Jul 3, 2026
40d3989
Report units
pierotofy Jul 3, 2026
94c6315
Ceres --> ceres
pierotofy Jul 3, 2026
90bc246
Remove findceres
pierotofy Jul 3, 2026
a6040c0
Add Findceres
pierotofy Jul 3, 2026
583fc61
Remove FindCeres
pierotofy Jul 3, 2026
d7fed44
Add Findceres
pierotofy Jul 3, 2026
3cf5811
Require ceres
pierotofy Jul 3, 2026
deda78e
Dont use LD_PRELOAD
pierotofy Jul 4, 2026
dd4adb7
Disable OpenCL on Windows
pierotofy Jul 4, 2026
48d1323
Remove old rs_correct command
pierotofy Jul 4, 2026
245124c
Merge branch '380' of https://github.com/WebODM/OpenSfM into 380
pierotofy Jul 4, 2026
d441588
Fix import
pierotofy Jul 4, 2026
227a306
Feat: CUDA port of hamming matcher
pierotofy Jul 6, 2026
7904b3e
Adjust logging
pierotofy Jul 6, 2026
83806d9
Update ellipsoid constant
pierotofy Jul 6, 2026
a1ebcbb
Uniform logging for matching
pierotofy Jul 6, 2026
b40e441
Make image read resiliant to corruption
pierotofy Jul 7, 2026
c52747f
Cap upper/lower bounds for Cx,Cy
pierotofy Jul 7, 2026
b383b5d
Include miniglog headers
pierotofy Jul 7, 2026
e3b3e99
Remove fast float dependency, fix miniglog includes
pierotofy Jul 7, 2026
4b6ea8b
Quieter log
pierotofy Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
BasedOnStyle: Google
InsertBraces: true
18 changes: 18 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[run]
data_file = coverage_data/.coverage
branch = True
relative_files = True
source =
opensfm
omit =
opensfm/test/*

[report]
omit =
opensfm/test/*

[xml]
output = coverage_data/coverage-python.xml

[html]
directory = coverage_data/htmlcov
7 changes: 7 additions & 0 deletions .dir-locals.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
;;; Directory Local Variables -*- no-byte-compile: t; -*-
;;; For more information see (info "(emacs) Directory Variables")

((c++-mode . ((c-basic-offset . 2)
(tab-width . 2)))
(c++-ts-mode . ((c-ts-mode-indent-offset . 2)
(tab-width . 2))))
5 changes: 4 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Pick what to include
!bin
!configs
!data/berlin/config.yaml
!data/berlin/gcp_list.txt
!data/berlin/images
Expand All @@ -11,8 +12,10 @@
!doc
!LICENSE
!opensfm
!pyproject.toml
!conda.yml
!conda-*.lock
!README.md
!requirements.txt
!setup.cfg
!setup.py
!viewer
Expand Down
25 changes: 25 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
name: Bug report
about: Create a bug report so we can improve
title: ''
labels: ''
assignees: YanNoun

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Please give us a reproduction step (command). If there's NO REPRO steps, WE'LL CLOSE this issue immediately.

**Screenshots or Log**
Please attach some screenshot or log that show what's happening.

**Platform**
Linux (which distrib' and version) ?
WIndows ?
Mac ?

**Additional context**
Add any other context about the problem here.
80 changes: 80 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# OpenSfM AI Coding Instructions

## 1. Architecture & "Big Picture"
OpenSfM is a Structure-from-Motion library with a hybrid architecture:
- **Core Logic (C++)**: Heavy computational tasks (geometry, bundle adjustment, features) are implemented in C++ under `opensfm/src/`. These are exposed to Python as compiled modules (e.g., `pygeometry`, `pymap`, `pfm`) using **PyBind11**.
- **Pipeline Layout (Python)**: The application logic, pipeline orchestration, and user interface are in Python (`opensfm/` package).
- **Data Abstraction**: The `DataSet` class (`opensfm/dataset.py`) is the central definition for filesystem interactions. All pipeline stages read/write to a hardcoded directory structure (`images/`, `config.yaml`, `reconstruction.json`) managed by this class.
- **State Management**: The `Reconstruction` class (`opensfm/types.py`) wraps the C++ `pymap.Map` object. This pattern (Python class holding a C++ handle) is pervasive.

## 2. Key Developer Workflows

### Building
The project uses `scikit-build-core` to compile C++ extensions.
- **Full Build**: `pip install -e .[test]` (Builds C++ extensions and tests, and installs the package in editable mode).
- **Rebuild C++**: Re-running `pip install -e .` is usually required after changing `.cc` files.
- **Dependencies**: Managed via `pyproject.toml` for managing Python dependencies. Outer envinvironment (C++ toolchain, libraries) is set up through `conda` and the `conda.yml` file. Any change to `conda.yml` requires recreating the conda environment with `conda env create --file conda.yml --yes`, then activating it with `conda activate opensfm`. Note that before running the build commands, the conda environment must be activated once.

### Testing
- **Framework**:
- Python : `pytest` is used, and tests are in files `test_YYY.py` under `opensfm/test`.
- C++ : `gtest` is used. For a given library XXX, tests are put under `opensfm/src/XXX/test/` as `YYY_test.cc`.
- **Style**: we favor many tests with few asserts and ideally one function to be tested. Only a few (1-2) larger integration tests per file. We also strive to test using toy examples to check for function correctness and not just check return semantics.
- **Location**: `opensfm/test/` for Python tests. C++ tests are in `./cmake_build`.
- **Synthetic Data**: Python tests heavily rely on synthetic scenes generated in `opensfm/test/conftest.py`. Look for fixtures like `scene_synthetic` or `scene_synthetic_cube`.
- **Run Tests**: `pytest opensfm/test/` for Python tests. C++ tests can be run via `ctest` with `cd cmake_build && ctest --output-on-failure && cd ..`. Don't forget to activate the conda environment before running tests, and run `export LD_PRELOAD=$CONDA_PREFIX/lib/libtcmalloc.so` before running.

### Running the Pipeline
- **Entry Point**: `bin/opensfm` (shell script) -> `bin/opensfm_main.py`.
- **Commands**: Each CLI command (e.g., `detect_features`, `reconstruct`) is implemented as a module in `opensfm/commands/`.
- **Example**: `bin/opensfm reconstruct path/to/dataset` invokes the `run()` method in `opensfm/commands/reconstruct.py`.

## 3. Coding Conventions & Patterns

### Python/C++ Interop
- **Wrappers**: Do not use C++ objects directly if a Python wrapper exists (e.g., use `opensfm.types.Reconstruction` instead of `opensfm.pymap.Map` where possible).
- **Extensions**: C++ extensions are imported from the root package (e.g., `from opensfm import pygeometry`).


### Code Philosophy
- **Readability**: Prioritize clear, simple and maintainable code. Avoid complicated constructs : you're an seasoned engineer that knows about real-world trade-offs, not a rookie trying to expose its design patterns knowledge. You'll use frameworks and design pattern when they're strictly justified at the present moment, not for an hypothetical future. Use descriptive variable and function names, and break down complex functions into smaller, focused ones.
- **Documentation**: Document all public functions and classes with docstrings. For complex algorithms, include comments explaining the rationale and key steps.
- **Testing**: Write tests for new features and bug fixes. Use the existing test suite as a reference for style and structure.
- **C++ Style**: Follow the existing C++ style in the codebase. Use `clang-format` for consistent formatting. Key points:
* Stick to use STL and Eigen for data structures. Avoid using OpenCV datastructures in C++, except for image resizing.
* Use SoA structuring for performance critical data like point clouds.
* Strive to minimize heap allocation and fragmentation by using stack-allocated buffer. In case of heap-allocated, re-use buffers, and avoid tiny buffers in long for loops.
- **Design**: For the data, try to re-use core lib's data structures. In addition, it is of upmost importance to be able to manipulate large reconstructions efficiently. We strive to handle large amounts of data, so minimize copy operations, use Data-Oriented Design principles, and leverage GPU acceleration where possible (e.g., for map rendering).

### Type Hinting
- **Strictness**: The codebase uses `pyre-strict`. Ensure all new code has complete type annotations. A comment `# pyre-strict` is often present at the top of files.

### Configuration
- **Definition**: Default parameters are in `opensfm/config.py` (dataclass `OpenSfMConfig`).
- **Overrides**: Parameters are overridden by `config.yaml` in the dataset directory.
- **Access**: Access config values via `data.config['param_name']` (where `data` is a `DataSet` instance).

## 4. Essential Files
- `opensfm/dataset.py`: **READ THIS FIRST** when dealing with file I/O. Defines where every file lives.
- `opensfm/types.py`: Defines key data structures (`Reconstruction`, `Shot`, `Camera`).
- `opensfm/config.py`: Documentation for all tunable parameters.
- `opensfm/src/map/map.cc`: The backing C++ implementation for `Reconstruction`.
- `opensfm/commands/`: Implementation of individual pipeline steps.

## 5. Common Pitfalls
- **Direct File Access**: Avoid manual `open()` calls. Use `dataset.load_*` and `dataset.save_*` methods to ensure consistency with the expected directory structure.
- **Geometry Types**: Be careful with rotation representations (angle-axis vs matrices). `pygeometry` exposes helper functions; check `opensfm/src/geometry/` for implementation details if behavior is unclear.
- **Skipping Conda**: when running or building the library, don't forget to activate the conda environment at least once.

## 6. General Algorithmic Notes
- **Multi-Platforms**: The codebase is designed to run on Windows, Linux, and macOS. Avoid platform-specific code unless absolutely necessary, and use cross-platform libraries (e.g., Qt for GUI, STL/Eigen for C++).

Always refer to instructions/memory.instructions.md for project-specific commands, style guides, and recent progress updates.

If and only if the prompt iS MEMUPDATE, summarize the current state of the project in instructions/memory.instructions.md, including recent progress, lessons learned, and known technical debt. This information should be concise and focused on actionable insights for developers.

## 7. For coding agents:
- **STRICTLY FORBIDDEN**:
- Do not perform any Git operations (commits, pushes, pulls, merges, rebases, etc.). This is a shared repository and all Git operations must be performed by human developers to ensure proper code review and collaboration.
- **BEST PRACTICES**:
- You will favor using bash tools such as sed or awk to perform complex code replacements, and avoid doing complex string manipulation in Python with ad-hoc scripts that open and write to files directly. This is to ensure that code changes are made in a clear and auditable way for humans, and to leverage the power of existing command-line tools for code transformation.
154 changes: 154 additions & 0 deletions .github/scripts/generate_coverage_badge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""Generate a repository-local SVG badge from Cobertura XML reports."""

import argparse
import json
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional
from xml.etree import ElementTree
from xml.sax.saxutils import escape


@dataclass(frozen=True)
class CoverageTotals:
"""Coverage counters extracted from a Cobertura XML report."""

name: str
lines_covered: int
lines_valid: int
branches_covered: int
branches_valid: int

def line_rate(self) -> Optional[float]:
if self.lines_valid == 0:
return None
return (self.lines_covered / self.lines_valid) * 100.0

def branch_rate(self) -> Optional[float]:
if self.branches_valid == 0:
return None
return (self.branches_covered / self.branches_valid) * 100.0

def to_dict(self) -> Dict[str, object]:
return {
"name": self.name,
"lines_covered": self.lines_covered,
"lines_valid": self.lines_valid,
"line_rate": self.line_rate(),
"branches_covered": self.branches_covered,
"branches_valid": self.branches_valid,
"branch_rate": self.branch_rate(),
}


def _parse_count(attributes: Dict[str, str], key: str) -> int:
raw_value = attributes.get(key)
if raw_value is None:
return 0
return int(float(raw_value))


def _load_report(report_path: Path) -> CoverageTotals:
root = ElementTree.parse(report_path).getroot()
return CoverageTotals(
name=report_path.stem,
lines_covered=_parse_count(root.attrib, "lines-covered"),
lines_valid=_parse_count(root.attrib, "lines-valid"),
branches_covered=_parse_count(root.attrib, "branches-covered"),
branches_valid=_parse_count(root.attrib, "branches-valid"),
)


def _merge_reports(reports: List[CoverageTotals]) -> CoverageTotals:
return CoverageTotals(
name="total",
lines_covered=sum(report.lines_covered for report in reports),
lines_valid=sum(report.lines_valid for report in reports),
branches_covered=sum(report.branches_covered for report in reports),
branches_valid=sum(report.branches_valid for report in reports),
)


def _format_percentage(value: Optional[float]) -> str:
if value is None:
return "n/a"
formatted = f"{value:.1f}".rstrip("0").rstrip(".")
return f"{formatted}%"


def _badge_color(rate: Optional[float]) -> str:
if rate is None:
return "#9f9f9f"
if rate < 50.0:
return "#e05d44"
if rate < 70.0:
return "#fe7d37"
if rate < 85.0:
return "#dfb317"
if rate < 95.0:
return "#a4a61d"
return "#4c1"


def _segment_width(text: str) -> int:
return max(10, len(text) * 7 + 10)


def _build_badge_svg(label: str, value: str, color: str) -> str:
label_width = _segment_width(label)
value_width = _segment_width(value)
total_width = label_width + value_width
label_center = label_width * 5
value_center = (label_width + (value_width / 2.0)) * 10
aria_label = escape(f"{label}: {value}")
label_text = escape(label)
value_text = escape(value)
return f"""<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{total_width}\" height=\"20\" role=\"img\" aria-label=\"{aria_label}\">\n <linearGradient id=\"s\" x2=\"0\" y2=\"100%\">\n <stop offset=\"0\" stop-color=\"#bbb\" stop-opacity=\".1\"/>\n <stop offset=\"1\" stop-opacity=\".1\"/>\n </linearGradient>\n <clipPath id=\"r\">\n <rect width=\"{total_width}\" height=\"20\" rx=\"3\" fill=\"#fff\"/>\n </clipPath>\n <g clip-path=\"url(#r)\">\n <rect width=\"{label_width}\" height=\"20\" fill=\"#555\"/>\n <rect x=\"{label_width}\" width=\"{value_width}\" height=\"20\" fill=\"{color}\"/>\n <rect width=\"{total_width}\" height=\"20\" fill=\"url(#s)\"/>\n </g>\n <g fill=\"#fff\" text-anchor=\"middle\" font-family=\"Verdana,Geneva,DejaVu Sans,sans-serif\" text-rendering=\"geometricPrecision\" font-size=\"110\">\n <text x=\"{label_center}\" y=\"140\" transform=\"scale(.1)\" fill=\"#010101\" fill-opacity=\".3\">{label_text}</text>\n <text x=\"{label_center}\" y=\"130\" transform=\"scale(.1)\">{label_text}</text>\n <text x=\"{value_center}\" y=\"140\" transform=\"scale(.1)\" fill=\"#010101\" fill-opacity=\".3\">{value_text}</text>\n <text x=\"{value_center}\" y=\"130\" transform=\"scale(.1)\">{value_text}</text>\n </g>\n</svg>\n"""


def _build_summary(reports: List[CoverageTotals], total: CoverageTotals) -> Dict[str, object]:
return {
"reports": [report.to_dict() for report in reports],
"total": total.to_dict(),
"badge": {
"label": "coverage",
"message": _format_percentage(total.line_rate()),
"color": _badge_color(total.line_rate()),
},
}


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("reports", nargs="+", type=Path,
help="Cobertura XML coverage reports")
parser.add_argument("--output", required=True, type=Path,
help="Path to the SVG badge to write")
parser.add_argument(
"--summary-json",
required=True,
type=Path,
help="Path to a JSON summary file written alongside the badge",
)
args = parser.parse_args()

reports = [_load_report(path) for path in args.reports]
total = _merge_reports(reports)
badge_value = _format_percentage(total.line_rate())
badge_svg = _build_badge_svg(
"coverage", badge_value, _badge_color(total.line_rate()))

args.output.parent.mkdir(parents=True, exist_ok=True)
args.summary_json.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(badge_svg, encoding="utf-8")
args.summary_json.write_text(
json.dumps(_build_summary(reports, total),
indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
return 0


if __name__ == "__main__":
raise SystemExit(main())
43 changes: 43 additions & 0 deletions .github/workflows/conda.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Conda

on: [push, pull_request]

jobs:

build-test:
name: Build conda environment and run tests

strategy:
matrix:
include:
- os: ubuntu-24.04
lockfile: conda-linux-64.lock
- os: ubuntu-22.04
lockfile: conda-linux-64.lock
- os: macos-latest
lockfile: conda-osx-arm64.lock

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
with:
submodules: true

- name: Setup Miniconda
uses: conda-incubator/setup-miniconda@v3
with:
environment-file: ${{ matrix.lockfile }}
activate-environment: opensfm

- name: Build OpenSfM
shell: bash -l {0}
run: pip install -e .[test]

- name: Run C++ tests
shell: bash -l {0}
run: cd build && ctest

- name: Run Python tests
shell: bash -l {0}
run: export LD_PRELOAD=$CONDA_PREFIX/lib/libtcmalloc.so && python -m pytest
Loading
Loading