') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - WebODM/OpenSfM: Open source Structure-from-Motion pipeline · GitHub
Skip to content

Latest commit

History

4,282 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

OpenSfM

OpenSfM

CondaDocker Ubuntu 20.04Docker Ubuntu 24.04

Coverage

Discord

🧟 Intro

This repository continues the original OpenSfM project, which is no longer in active development. We were maintainers and contributors of the original OpenSfM, and we will do our best to keep it alive and serve the community and our users (OpenDroneMap, WebODM and many others).

This 1.0 release focuses on the needs of those two biggest users — OpenDroneMap and WebODM — hence the strong emphasis on GIS / geo workflows. See the release notes for the full feature list.

🔭 Overview

OpenSfM is an open-source Structure-from-Motion (SfM) library written in Python with performance-critical code in C++. It reconstructs camera poses and sparse 3D points from unordered image collections, and goes all the way to dense point clouds, meshes, and georeferenced 2D maps (DSM, orthophoto) — with GPU acceleration throughout.

🧩 Core pipeline

Feature detection (SIFT, HAHOG, DSP-SIFT, AKAZE, SURF, ORB), GPU (OpenCL) matching — both online-trained binary-quantized descriptors and classic FLANN — with geometric verification, track building, and incremental + direct aerotriangulation reconstruction. Robust Ceres-based bundle adjustment, switching to a stochastic solver for very large scenes. Pair selection by GPS, capture time, file order, or image similarity (BoW / VLAD). See pipeline commands and the configuration reference.

SfM Reconstruction

📐 Camera models

Perspective, Brown, fisheye (OpenCV model and custom 62 / 624 parameters), spherical / equirectangular, and dual — with rolling-shutter correction. Lab-calibrated intrinsics can be injected and frozen, and multi-camera rigs are fully supported and can be auto-calibrated. See camera models and rig models.

🧭 Geolocation & georeferencing

GPS positions (with per-image X/Y/Z standard deviation) from EXIF or imported from a text file; ground control points and checkpoints (with per-point standard deviation) in any CRS. Horizontal + vertical coordinate systems via EPSG codes, compound EPSG, or PROJ strings, with geoids fetched on demand from the PROJ CDN, and adaptive datum-shift compensation. See georeferencing & GIS outputs and ground control points.

🍇 Dense reconstruction

Multi-view depth estimation via GPU PatchMatch (OpenCL), sparse-voxel-octree TSDF fusion with optional photometric refinement, and a Surface Nets (dual-contouring) mesh. Exports the dense cloud as PLY / LAS / LAZ, the mesh as PLY, and Potree-style octree tiles for streaming web viewers. See dense reconstruction & 2D maps.

Dense Reconstruction

🧇 2D maps — DSM & orthophoto

Direct, TSDF-based Digital Surface Model and orthophoto rendering, with hole filling, an edge-sharpening shock filter, and robust multi-view color baking. Accurately georeferenced to the output CRS (3rd-degree polynomial fit, TPS fallback) and exported as GeoTIFF. See 2D maps.

DSM and Ortho Extraction

🪜 Scalability

Out-of-core submodel splitting / merging for large scenes, rig constraints for multi-camera setups, stochastic bundle adjustment, and configurable multi-processing. See large datasets.

📦 Exports

COLMAP, Bundler, OpenMVS, PMVS, VisualSFM, PLY, LAS/LAZ, GeoJSON, and GeoTIFF — see the exporters.

🩺 Quality report

SfM metrics, GPS/GCP and checkpoint error tables, and DSM/ortho previews, localized in metric or imperial units and in five languages (en/fr/es/de/it), exported as a PDF. See quality report and an example report.

🥽 Visualisation

A built-in JavaScript viewer for interactive 3D preview and pipeline debugging, a web point-cloud viewer fed by the Potree octree tiles, and a Rerun export of the scene with its GPS/GCP data.

Rerun Export

🤝 Compatibility — Runs on Linux, macOS (Apple Silicon), and Windows. See the quickstart to get started.

🫶 Credits — OpenSfM was created by Pau Gargallo and bootstrapped by Mapillarians — check out this blog post with more demos.

🛫 Getting Started

Install using conda lock files (see building instructions):

Linux:

conda create --name opensfm --file conda-linux-64.lock --yes
conda activate opensfm && pip install -e .

macOS (Apple Silicon):

conda create --name opensfm --file conda-osx-arm64.lock --yes
conda activate opensfm && pip install -e .

Then reconstruct a dataset:

conda activate opensfm
./bin/opensfm_run_all path/to/dataset # Linux/macOS
bin\opensfm_run_all.bat path\to\dataset # Windows

Workflow presets — ready-made config.yaml files tuned for common capture types live in configs/ (aerial, terrestrial, object). Copy one into your dataset to start from sensible defaults: cp configs/aerial.yaml path/to/dataset/config.yaml. See workflow presets.

⏱️ Benchmarking

A built-in harness measures the impact of a change on speed and quality across commits. It builds any commit in an isolated git worktree + conda env, runs the pipeline on your datasets, and produces an HTML report that diffs the run against a reference commit (green = better, red = worse).

# Baseline, then your branch compared against it
python -m benchmark.run --config benchmark/benchmark_example.json --commit master
python -m benchmark.run --config benchmark/benchmark_example.json --commit my-feature --reference master

Add --dense to include the dense stages, or --resume to recover an interrupted run. See the benchmarking guide for the full workflow — resuming, report-only regeneration, and partial re-runs.

📚 Documentation

Getting Started

User Guide

Reference

Mathematical Notes

Development

⚖️ License

OpenSfM is BSD-style licensed, as found in the LICENSE file.

Example data in the README is under Creative Commons CC-BY 4.0 License by Wingtra AG, 8045 Zürich, Switzerland.

About

Open source Structure-from-Motion pipeline

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages