geo-trax 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. geo_trax-1.0.0/LICENSE +21 -0
  2. geo_trax-1.0.0/PKG-INFO +550 -0
  3. geo_trax-1.0.0/README.md +504 -0
  4. geo_trax-1.0.0/geo_trax.egg-info/PKG-INFO +550 -0
  5. geo_trax-1.0.0/geo_trax.egg-info/SOURCES.txt +41 -0
  6. geo_trax-1.0.0/geo_trax.egg-info/dependency_links.txt +1 -0
  7. geo_trax-1.0.0/geo_trax.egg-info/entry_points.txt +2 -0
  8. geo_trax-1.0.0/geo_trax.egg-info/requires.txt +18 -0
  9. geo_trax-1.0.0/geo_trax.egg-info/top_level.txt +1 -0
  10. geo_trax-1.0.0/geotrax/__init__.py +11 -0
  11. geo_trax-1.0.0/geotrax/__main__.py +9 -0
  12. geo_trax-1.0.0/geotrax/aggregate.py +205 -0
  13. geo_trax-1.0.0/geotrax/batch_process.py +424 -0
  14. geo_trax-1.0.0/geotrax/cfg/confident.yaml +474 -0
  15. geo_trax-1.0.0/geotrax/cfg/default.yaml +465 -0
  16. geo_trax-1.0.0/geotrax/cfg/lenient.yaml +482 -0
  17. geo_trax-1.0.0/geotrax/cfg/stable.yaml +475 -0
  18. geo_trax-1.0.0/geotrax/cli.py +100 -0
  19. geo_trax-1.0.0/geotrax/config.py +196 -0
  20. geo_trax-1.0.0/geotrax/extract.py +549 -0
  21. geo_trax-1.0.0/geotrax/georeference.py +916 -0
  22. geo_trax-1.0.0/geotrax/plot.py +842 -0
  23. geo_trax-1.0.0/geotrax/utils/__init__.py +32 -0
  24. geo_trax-1.0.0/geotrax/utils/cli_utils.py +32 -0
  25. geo_trax-1.0.0/geotrax/utils/config_utils.py +343 -0
  26. geo_trax-1.0.0/geotrax/utils/constants.py +15 -0
  27. geo_trax-1.0.0/geotrax/utils/data_utils.py +52 -0
  28. geo_trax-1.0.0/geotrax/utils/file_utils.py +206 -0
  29. geo_trax-1.0.0/geotrax/utils/logging_utils.py +110 -0
  30. geo_trax-1.0.0/geotrax/utils/registration.py +95 -0
  31. geo_trax-1.0.0/geotrax/visualize.py +657 -0
  32. geo_trax-1.0.0/pyproject.toml +112 -0
  33. geo_trax-1.0.0/setup.cfg +4 -0
  34. geo_trax-1.0.0/tests/test_batch_process.py +79 -0
  35. geo_trax-1.0.0/tests/test_cli.py +66 -0
  36. geo_trax-1.0.0/tests/test_config_utils.py +201 -0
  37. geo_trax-1.0.0/tests/test_data_utils.py +45 -0
  38. geo_trax-1.0.0/tests/test_extract.py +157 -0
  39. geo_trax-1.0.0/tests/test_file_utils.py +131 -0
  40. geo_trax-1.0.0/tests/test_georeference.py +269 -0
  41. geo_trax-1.0.0/tests/test_logging_utils.py +66 -0
  42. geo_trax-1.0.0/tests/test_plot.py +53 -0
  43. geo_trax-1.0.0/tests/test_visualize.py +78 -0
geo_trax-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Robert Fonod
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,550 @@
1
+ Metadata-Version: 2.4
2
+ Name: geo-trax
3
+ Version: 1.0.0
4
+ Summary: A Comprehensive Framework for Georeferenced Vehicle Trajectory Extraction from Drone Imagery
5
+ Author-email: Robert Fonod <robert.fonod@ieee.org>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/rfonod/geo-trax/
8
+ Project-URL: Source, https://github.com/rfonod/geo-trax/
9
+ Project-URL: Repository, https://github.com/rfonod/geo-trax/
10
+ Project-URL: Changelog, https://github.com/rfonod/geo-trax/releases
11
+ Project-URL: Issues, https://github.com/rfonod/geo-trax/issues/
12
+ Keywords: trajectory-extraction,trajectory-stabilization,georeferencing,object-detection,object-tracking,vehicle-tracking,computer-vision,deep-learning,yolo,drone,aerial-imagery,traffic-analysis
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Intended Audience :: Developers
22
+ Classifier: Intended Audience :: Science/Research
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Topic :: Software Development
25
+ Classifier: Topic :: Multimedia :: Video
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: ultralytics[extra]<9.0,>=8.4.64
30
+ Requires-Dist: huggingface_hub>=0.30
31
+ Requires-Dist: lapx>=0.5.2
32
+ Requires-Dist: stabilo>=1.2.3
33
+ Requires-Dist: geopandas>=1.0.0
34
+ Requires-Dist: matplotlib>=3.3.0
35
+ Requires-Dist: seaborn>=0.11.0
36
+ Requires-Dist: pysrt>=1.1.2
37
+ Requires-Dist: tifffile
38
+ Requires-Dist: comet_ml
39
+ Provides-Extra: dev
40
+ Requires-Dist: ultralytics[dev]; extra == "dev"
41
+ Requires-Dist: pytest>=6.0; extra == "dev"
42
+ Requires-Dist: ruff; extra == "dev"
43
+ Provides-Extra: export
44
+ Requires-Dist: ultralytics[export-base]; extra == "export"
45
+ Dynamic: license-file
46
+
47
+ # Geo-trax
48
+
49
+ [![GitHub Release](https://img.shields.io/github/v/release/rfonod/geo-trax?include_prereleases)](https://github.com/rfonod/geo-trax/releases) [![PyPI - Version](https://img.shields.io/pypi/v/geo-trax)](https://pypi.org/project/geo-trax/) [![PyPI - Total Downloads](https://img.shields.io/pepy/dt/geo-trax?label=total%20downloads)](https://pepy.tech/project/geo-trax) [![PyPI - Downloads per Month](https://img.shields.io/pypi/dm/geo-trax?color=%234c1)](https://pypi.org/project/geo-trax/) [![CI](https://github.com/rfonod/geo-trax/actions/workflows/ci.yml/badge.svg)](https://github.com/rfonod/geo-trax/actions/workflows/ci.yml) [![Python](https://img.shields.io/badge/python-3.9--3.13-blue)](https://www.python.org/) [![License](https://img.shields.io/github/license/rfonod/geo-trax)](https://github.com/rfonod/geo-trax/blob/main/LICENSE) [![GitHub Issues](https://img.shields.io/github/issues/rfonod/geo-trax)](https://github.com/rfonod/geo-trax/issues) [![Open Access](https://img.shields.io/badge/Journal-10.1016%2Fj.trc.2025.105205-blue)](https://doi.org/10.1016/j.trc.2025.105205) [![arXiv](https://img.shields.io/badge/arXiv-2411.02136-b31b1b.svg)](https://arxiv.org/abs/2411.02136) [![Archived Code](https://img.shields.io/badge/Zenodo-Software%20Archive-blue)](https://zenodo.org/doi/10.5281/zenodo.12119542) [![Hugging Face](https://img.shields.io/badge/🤗%20Model-rfonod%2Fgeo--trax-yellow)](https://huggingface.co/rfonod/geo-trax) [![Project Website](https://img.shields.io/badge/REAL%20Lab-Geo--trax-informational)](https://www.real-lab.ch/geo-trax) [![YouTube](https://img.shields.io/badge/YouTube-Video-red?logo=youtube&logoColor=red)](https://youtu.be/gOGivL9FFLk)
50
+
51
+ **Geo-trax** (GEO-referenced TRAjectory eXtraction) is a comprehensive pipeline that extracts high-accuracy, georeferenced vehicle trajectories from high-altitude drone imagery. Built for quasi-stationary aerial monitoring of urban traffic, it turns raw bird's-eye view (BEV) drone footage into precise, real-world vehicle trajectories. The framework combines YOLO detection, multi-object tracking, and video stabilization with a robust orthophoto-based georeferencing stage, producing GNSS-tagged, lane-resolved trajectories that are spatially and temporally consistent and ready for large-scale traffic analysis and simulation. It is optimized for urban intersections and arterial corridors, where high-fidelity, vehicle-level insights drive intelligent transportation systems and digital twin applications.
52
+
53
+ ![Geo-trax Output Visualization](https://raw.githubusercontent.com/rfonod/geo-trax/v1.0.0/assets/geo-trax_visualization.webp)
54
+
55
+ 🎬 An accelerated preview of Geo-trax's capabilities. Watch the full ~4 min 4K demo on [YouTube](https://youtu.be/gOGivL9FFLk).
56
+
57
+ ### Why Geo-trax
58
+
59
+ - 🛰️ **Real-world output**: georeferenced, lane-resolved trajectories (WGS84 + local CRS) with per-vehicle speed, acceleration, and estimated dimensions, straight from raw BEV drone video.
60
+ - 🎯 **Accurate detection**: [YOLOv8s vehicle detector](#detection-model) reaching **0.951 mAP@50**, trained on more than 19,000 annotated aerial images.
61
+ - 🚗 **Flexible tracking**: four vehicle classes and [six selectable multi-object trackers](#tracking) (BoT-SORT, ByteTrack, OC-SORT, and more).
62
+ - 🌀 **Drone-motion robust**: homography-based stabilization ([Stabilo](https://github.com/rfonod/stabilo)) plus orthophoto image registration for consistent, cross-flight coordinates.
63
+ - 📊 **Proven at scale**: powered the [Songdo Traffic](https://doi.org/10.5281/zenodo.13828383) dataset (roughly **700,000 trajectories** across **20 intersections**, fleet of **10 drones**; see [Real-World Deployment](#real-world-deployment-the-songdo-experiment)).
64
+ - ⚙️ **One command, one config**: `geotrax batch` runs the whole pipeline; a single YAML drives every stage, with [four tuned presets](#configuration) included.
65
+
66
+ ## Pipeline
67
+
68
+ ![Geo-trax pipeline diagram: raw drone video → detection → tracking → stabilization → georeferencing → dataset](assets/geo-trax_pipeline.svg)
69
+
70
+ 🔍 The core pipeline (solid box) produces stabilized, pixel-coordinate vehicle trajectories. Optional extensions add georeferencing via orthophoto image registration, vision dataset creation through frame (pre-)annotation for custom detector fine-tuning, and visualization, analysis, and probe vehicle validation tools, all applicable to both pixel-coordinate and georeferenced outputs.
71
+
72
+ ## Install
73
+
74
+ ```bash
75
+ python3.11 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
76
+ python -m pip install geo-trax
77
+ ```
78
+
79
+ Python 3.9 to 3.13. Also works with [uv](https://docs.astral.sh/uv/) (`uv pip install geo-trax`) and [conda](https://www.anaconda.com/docs/getting-started/miniconda/install). For development:
80
+
81
+ ```bash
82
+ git clone --depth 1 https://github.com/rfonod/geo-trax.git
83
+ cd geo-trax && python -m pip install -e '.[dev]'
84
+ ```
85
+
86
+ > [!NOTE]
87
+ > The default model auto-downloads from [🤗 Hugging Face](https://huggingface.co/rfonod/geo-trax) on first use (cached in `~/.cache/huggingface/hub`, overridable via `HF_HOME`). To use your own weights, set `--model` or `extraction.model` in the config to a local `.pt` path or `hf://<org>/<repo>/<path/to/file>.pt`.
88
+
89
+ <details>
90
+ <summary><b>Alternative Environments & Advanced Dev Install</b></summary>
91
+
92
+ **Create and activate a virtual environment** (any of the following):
93
+
94
+ ```bash
95
+ # venv (standard library)
96
+ python3.11 -m venv .venv
97
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
98
+
99
+ # uv (fastest drop-in for venv + pip)
100
+ uv venv --python 3.11
101
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
102
+
103
+ # Miniconda
104
+ conda create -n geo-trax python=3.11 -y
105
+ conda activate geo-trax
106
+ ```
107
+
108
+ **Install from PyPI** (runtime use). This installs the `geotrax` command-line interface together with the bundled configuration tree (`geotrax/cfg/`):
109
+
110
+ ```bash
111
+ python -m pip install geo-trax # pip
112
+ uv pip install geo-trax # uv (faster)
113
+ ```
114
+
115
+ **Install from local source** (recommended for development or model training). Clone (or fork) the repository, then install in editable mode (`-e`), which reflects code changes without reinstalling:
116
+
117
+ ```bash
118
+ git clone https://github.com/rfonod/geo-trax.git # add --depth 1 for the latest snapshot only
119
+ cd geo-trax
120
+ python -m pip install -e . # pip
121
+ # uv pip install -e . # uv (faster; requires the uv venv above)
122
+ # poetry install # Poetry (auto-manages its own virtualenv; skip the venv step)
123
+ ```
124
+
125
+ **Optional dependency groups** (development/testing tools, ONNX export):
126
+
127
+ ```bash
128
+ python -m pip install -e '.[dev]' # development + test tooling
129
+ python -m pip install -e '.[export]' # ONNX export dependencies
130
+ # uv pip install -e '.[dev]' # uv equivalents
131
+ # poetry install --extras dev # Poetry equivalents
132
+ # poetry install --extras export
133
+ ```
134
+
135
+ </details>
136
+
137
+ ## Quick Start
138
+
139
+ `data/U_video_cut.mp4` is a 5-second sample clip included for immediate testing. See [data/README.md](data/README.md) for matching orthophotos.
140
+
141
+ ```bash
142
+ # Pixel-coordinate trajectories (no orthophoto required)
143
+ geotrax batch data/U_video_cut.mp4 --no-geo --save
144
+
145
+ # Full pipeline: detection, tracking, stabilization, georeferencing, and visualization
146
+ geotrax batch data/U_video_cut.mp4 -orf data/orthophotos -mf data/master_frames --save --show-lanes
147
+
148
+ # Scale up: process a whole project tree, then merge multi-drone results into one dataset
149
+ geotrax batch path/to/PROCESSED/
150
+ geotrax aggregate path/to/PROCESSED/
151
+ ```
152
+
153
+ Run `geotrax -h` or `geotrax batch -h` for all options. The scale-up commands above run flag-free with the [recommended project structure](#real-world-deployment-the-songdo-experiment); any other layout works with explicit path flags.
154
+
155
+ <details>
156
+ <summary><b>📋 Full Feature Overview</b></summary>
157
+
158
+ - **Detection**: YOLOv8s on aerial BEV imagery; detects car (incl. vans), bus, truck, and motorcycle.
159
+ - **Tracking**: six multi-object trackers (BoT-SORT default); see [Tracking](#tracking) for a comparison.
160
+ - **Stabilization**: homography-based trajectory correction via [Stabilo](https://github.com/rfonod/stabilo) 🌀, tuned with [Stabilo-Optimize](https://github.com/rfonod/stabilo-optimize) 🎯.
161
+ - **Georeferencing**: frame-to-orthophoto registration; outputs lat/lon, local CRS, speed, acceleration, and lane assignment per vehicle.
162
+ - **Visualization**: track overlays on original, stabilized, or static-reference video, in three rendering modes.
163
+ - **Analysis**: trajectory maps, kinematic distributions, and class/dimension charts, per-video or aggregated across drones and sessions.
164
+ - **Scaling & tooling**: batch-processes directory trees and aggregates multi-drone data; includes standalone utilities for end-to-end data preparation, training, evaluation, and validation.
165
+
166
+ </details>
167
+
168
+ <details>
169
+ <summary><b>🚀 Planned Enhancements</b></summary>
170
+
171
+ - Comprehensive documentation in a dedicated `docs/` folder. A [`tools/README.md`](tools/README.md) index already covers the auxiliary scripts.
172
+ - Modularized, OOP-based pipeline with custom reference frame support and georeferencing leveraging Stabilo's image-matching backend.
173
+ - Per-class confidence thresholds and oriented bounding box visualization (using azimuth and dimension estimates).
174
+ - Trajectory interpolation and SAHI-based small-object detection.
175
+ - Batch inference, GPU-accelerated image registration, and multi-thread processing.
176
+ - Real-world map visualization (e.g., MovingPandas, contextily) and interactive web app.
177
+
178
+ </details>
179
+
180
+ <details>
181
+ <summary><b>🔗 Related Projects</b></summary>
182
+
183
+ Geo-trax integrates with and complements several specialized tools:
184
+
185
+ - **[Stabilo](https://github.com/rfonod/stabilo) 🌀**: Python library for video and trajectory stabilization using robust homography transformations. Supports various feature detectors, RANSAC algorithms, and user-defined masks. Used as Geo-trax's core stabilization engine.
186
+
187
+ - **[Stabilo-Optimize](https://github.com/rfonod/stabilo-optimize) 🎯**: benchmarking and hyperparameter optimization framework for Stabilo. Evaluates stabilization performance through ground truth-free assessment using random perturbations. Used to fine-tune Geo-trax stabilization parameters.
188
+
189
+ - **[HBB2OBB](https://github.com/rfonod/hbb2obb) 📦**: converts horizontal bounding boxes to oriented bounding boxes using SAM segmentation models. Can enhance Geo-trax outputs when object orientation is needed for downstream analysis.
190
+
191
+ </details>
192
+
193
+ ## Configuration
194
+
195
+ The entire pipeline is driven by a **single, self-contained YAML config**: one file for detection, tracking, stabilization, georeferencing, visualization, and plotting. Four presets ship with the package:
196
+
197
+ | Preset | Focus |
198
+ |--------|-------|
199
+ | [`default`](geotrax/cfg/default.yaml) | Balanced baseline |
200
+ | [`confident`](geotrax/cfg/confident.yaml) | Precision (fewer false positives) |
201
+ | [`lenient`](geotrax/cfg/lenient.yaml) | Recall (catches more vehicles) |
202
+ | [`stable`](geotrax/cfg/stable.yaml) | Stabilization quality |
203
+
204
+ ```bash
205
+ geotrax batch video.mp4 -c confident # use a bundled preset by name
206
+ geotrax batch video.mp4 -c ./my.yaml # use a custom config file
207
+ ```
208
+
209
+ <details>
210
+ <summary><b>⚙️ Inspect, copy, and customize configs</b></summary>
211
+
212
+ Manage the bundled configs with the `geotrax config` command:
213
+
214
+ ```bash
215
+ geotrax config show # list bundled presets and their location
216
+ geotrax config show default # print a preset's full contents
217
+ geotrax config copy # copy presets into the current directory as <name>_copy.yaml
218
+ geotrax config copy -o ~/myproj # copy into a specific directory
219
+ ```
220
+
221
+ Copy a preset, edit it, then pass it with `-c`:
222
+
223
+ ```bash
224
+ geotrax config copy
225
+ # edit default_copy.yaml ...
226
+ geotrax extract video.mp4 -c default_copy.yaml
227
+ ```
228
+
229
+ To switch the tracking algorithm, set `tracker.active` in the config (see [Tracking](#tracking)).
230
+
231
+ </details>
232
+
233
+ ## Detection Model
234
+
235
+ The default detector is **YOLOv8s** (HBB, 1920 × 1920 px, ~11 M parameters), trained on more than 19,000 annotated aerial images (~679k labeled vehicle instances) and fine-tuned on a curated, high-quality subset. It is hosted on [🤗 Hugging Face](https://huggingface.co/rfonod/geo-trax) and **downloads automatically on first use**. Results on the Songdo Vision test split (1,084 images; full results in [Table 3](https://doi.org/10.1016/j.trc.2025.105205)):
236
+
237
+ | ID | Label | Precision | Recall | mAP@50 | mAP@50-95 |
238
+ |---|---|---|---|---|---|
239
+ | 0 | Car (incl. vans) | 0.979 | 0.981 | 0.992 | 0.835 |
240
+ | 1 | Bus | 0.952 | 0.977 | 0.988 | 0.826 |
241
+ | 2 | Truck | 0.887 | 0.916 | 0.935 | 0.722 |
242
+ | 3 | Motorcycle | 0.827 | 0.866 | 0.888 | 0.463 |
243
+ | **All** | | **0.911** | **0.935** | **0.951** | **0.711** |
244
+
245
+ > Pedestrian and bicycle classes exist in the weights but are underrepresented, unevaluated, and filtered by default. See the [model card](https://huggingface.co/rfonod/geo-trax) for full details.
246
+
247
+ To use a different model, point `--model` (CLI) or `extraction.model` (config) to a local `.pt` path or `hf://<org>/<repo>/<file>.pt`; any [Ultralytics](https://github.com/ultralytics/ultralytics)-compatible model works.
248
+
249
+ ### Custom Model Training
250
+
251
+ Training and export scripts for custom YOLO detectors live in `train/`, with a SLURM wrapper for HPC clusters. See [train/README.md](train/README.md).
252
+
253
+ ## Tracking
254
+
255
+ Six multi-object trackers ship with [Ultralytics](https://github.com/ultralytics/ultralytics) `>=8.4.63`. Selection is config-driven: set `tracker.active`, no code changes needed. Default: **BoT-SORT**.
256
+
257
+ | Tracker | `tracker.active` | ReID | GMC¹ | Pros | Cons |
258
+ |---------|------------------|:----:|:----:|------|------|
259
+ | **BoT-SORT** (default) | `botsort` | opt | ✅ | Strong accuracy; motion + optional appearance | Slower; ReID adds compute |
260
+ | **ByteTrack** | `bytetrack` | ❌ | ❌ | Fastest; two-stage association | More ID switches under occlusion |
261
+ | **OC-SORT** | `ocsort` | ❌ | ❌ | Robust to non-linear motion; lightweight | Weaker on long occlusions |
262
+ | **Deep OC-SORT** | `deepocsort` | opt | opt | OC-SORT + appearance; dense scenes | Heaviest variant with ReID |
263
+ | **FastTracker** | `fasttrack` | ❌ | ❌ | Occlusion-aware ByteTrack variant | Newer; several knobs to tune |
264
+ | **TrackTrack** | `tracktrack` | opt | ✅ | Multi-cue cost; best ID retention | Most parameters; highest compute |
265
+
266
+ ¹ GMC (in-tracker camera-motion compensation) runs during tracking and is independent of Stabilo's post-hoc trajectory stabilization stage.
267
+
268
+ > 💡 Run `geotrax config show default` to print the full `tracker:` block, with every parameter for all six trackers documented inline. Run `geotrax config copy` to get an editable local copy. For a head-to-head comparison on your own data, see [`tools/compare_tracking.py`](tools/compare_tracking.py).
269
+
270
+ ## Usage
271
+
272
+ The `geotrax` CLI provides one subcommand per stage: `batch` (primary entry point), `extract`, `georeference`, `visualize`, `plot`, `aggregate`, and `config`. Run `geotrax -h` or `geotrax <subcommand> -h` for the full reference (`python -m geotrax` works identically).
273
+
274
+ ```bash
275
+ # Recursively process a directory (or a single video) without georeferencing
276
+ geotrax batch path/to/videos/ --no-geo
277
+
278
+ # Run an individual stage on its own
279
+ geotrax extract video.mp4 # detect, track, and stabilize
280
+ geotrax visualize video.mp4 --save # render an annotated video from existing results
281
+ geotrax plot video.mp4 # trajectory and distribution plots
282
+ ```
283
+
284
+ > [!TIP]
285
+ > See [data/README.md](data/README.md) for sample data and testing examples.
286
+
287
+ <details>
288
+ <summary><b>💡 More Examples & Advanced Usage</b></summary>
289
+
290
+ ```bash
291
+ # Use a custom config (bundled preset by name, or a path to your own file)
292
+ geotrax batch video.mp4 -c confident
293
+ geotrax batch video.mp4 -c path/to/custom_config.yaml
294
+
295
+ # Regenerate visualization without re-running extraction
296
+ geotrax batch video.mp4 --viz-only --save
297
+
298
+ # Render specific visualization modes (0: original, 1: stabilized, 2: reference frame)
299
+ geotrax visualize video.mp4 --save --viz-mode 0 2
300
+
301
+ # Show lane IDs, hide the speed overlay (requires georeferencing)
302
+ geotrax batch video.mp4 --viz-only --save --show-lanes --hide-speed
303
+
304
+ # Georeference an already-extracted video against orthophotos
305
+ geotrax georeference video.mp4 -orf path/to/orthophotos -mf path/to/master_frames
306
+
307
+ # Aggregated trajectory plots, excluding buses and trucks
308
+ geotrax batch path/to/PROCESSED/ --plot-only --plot-aggregate --plot-class-filter 1 2
309
+
310
+ # Merge multi-drone results for the same locations into a unified dataset
311
+ geotrax aggregate path/to/PROCESSED/
312
+ ```
313
+
314
+ > [!NOTE]
315
+ > **Why use master frames?** When georeferencing, geo-trax can route each video's homography through a shared *master frame* per location ID. A master frame is a high-quality, near-nadir BEV frame chosen once per location (see [`tools/find_master_frames.py`](tools/find_master_frames.py)), used instead of registering every video's reference frame directly to the orthophoto. The mapping is split into two homographies: `reference → master` (recomputed per video) and `master → orthophoto` (computed **once per location ID and cached**, validated by a hash of the master image). This gives two benefits:
316
+ > - **Speed**: the expensive cross-domain `master → orthophoto` registration runs once and is reused across every drone and flight at that location, instead of once per video.
317
+ > - **Consistency & robustness**: every video is matched against the *same* master frame. This same-modality BEV-to-BEV registration is far more reliable than a direct BEV-to-orthophoto match, so trajectories from different drones, altitudes, and viewpoints resolve into one coherent coordinate system.
318
+ >
319
+ > Master frames are enabled by default. Disable them with `--no-master`, or force re-computation of the cached `master → orthophoto` homography with `--recompute`.
320
+
321
+ </details>
322
+
323
+ <details>
324
+ <summary><b>📁 Output file formats</b></summary>
325
+
326
+ Suppose the input video is `video_file.mp4`. By default, outputs are written to a `results/` sub-folder next to the input; the folder and all filename postfixes are configurable via the `output:` section of the pipeline config (or `--output-folder` / `-of` for the folder).
327
+
328
+ - **video_file.txt** (`<stem><tracks_postfix>.txt`): Contains the extracted vehicle trajectories in the following format:
329
+
330
+ ```text
331
+ frame_id, vehicle_id, x_c(unstab), y_c(unstab), w(unstab), h(unstab), x_c(stab), y_c(stab), w(stab), h(stab), class_id, confidence, vehicle_length, vehicle_width
332
+ ```
333
+
334
+ where:
335
+ - `frame_id`: Frame number (0, 1, ...).
336
+ - `vehicle_id`: Unique vehicle identifier (1, 2, ...).
337
+ - `x_c(unstab)`, `y_c(unstab)`: Unstabilized vehicle centroid coordinates.
338
+ - `w(unstab)`, `h(unstab)`: Unstabilized vehicle bounding box width and height.
339
+ - `x_c(stab)`, `y_c(stab)`: Stabilized vehicle centroid coordinates.
340
+ - `w(stab)`, `h(stab)`: Stabilized vehicle bounding box width and height.
341
+ - `class_id`: Vehicle class identifier (0: car (incl. vans), 1: bus, 2: truck, 3: motorcycle)
342
+ - `confidence`: Detection confidence score (0-1).
343
+ - `vehicle_length`, `vehicle_width`: Estimated vehicle dimensions in pixels.
344
+
345
+ - **video_file_vid_transf.txt** (`<stem><stab_transform_postfix>.txt`): Contains the transformation matrix for each frame in the format:
346
+
347
+ ```text
348
+ frame_id, h11, h12, h13, h21, h22, h23, h31, h32, h33
349
+ ```
350
+
351
+ where:
352
+ - `frame_id`: Frame number of the stabilized frame (starts from `cut_frame_left + 1` since the reference frame itself has no transform).
353
+ - `hij`: Elements of the 3x3 homography matrix that maps each frame (`frame_id`) to the reference frame.
354
+
355
+ - **video_file.yaml**: Video metadata and the configuration settings used for processing `video_file.mp4`. (This file is saved in the same directory as the input video, not in the output folder.)
356
+
357
+ - **video_file_mode_X.mp4** (`<stem><visualization_postfix>_mode_<X>.mp4`): Annotated video in three rendering modes (X = 0 / 1 / 2):
358
+ - **Mode 0**: overlaid on the original (unstabilized) video
359
+ - **Mode 1**: overlaid on the stabilized video
360
+ - **Mode 2**: plotted on the static reference frame
361
+
362
+ Each version can display vehicle bounding boxes, IDs, class labels, confidence scores, and short trajectory trails that fade and vary in thickness to indicate the recency of the movement. If an input `video_file.csv` file is available in the same directory as the input video, i.e., the converted flight logs, vehicle speed and lane information can also be displayed.
363
+
364
+ - **video_file.csv** (`<stem><georeferenced_postfix>.csv`): Contains the georeferenced vehicle trajectories in a tabular format. This file includes both geographic and local coordinates, estimated real-world dimensions, kinematic data, road section, and lane information. The columns are:
365
+
366
+ ```text
367
+ Vehicle_ID, [Timestamp,] Frame_Number, Ortho_X, Ortho_Y, Local_X, Local_Y, Latitude, Longitude, Vehicle_Length, Vehicle_Width, Vehicle_Class, Vehicle_Speed, Vehicle_Acceleration, Road_Section, Lane_Number, Visibility
368
+ ```
369
+
370
+ where:
371
+ - `Vehicle_ID`: Unique vehicle identifier.
372
+ - `Timestamp`: Timestamp of the frame (YYYY-MM-DD HH:MM:SS.ms). Present only when a flight-log CSV with timestamps is available alongside the video.
373
+ - `Frame_Number`: Video frame index corresponding to this detection.
374
+ - `Ortho_X`, `Ortho_Y`: X and Y coordinates of the vehicle centroid in the orthophoto's pixel coordinate system.
375
+ - `Local_X`, `Local_Y`: X and Y coordinates of the vehicle centroid in a local projected coordinate system (e.g., EPSG:5186 for KGD2002 / Central Belt 2010 used in the Songdo experiment).
376
+ - `Latitude`, `Longitude`: Geographic coordinates of the vehicle centroid (WGS84).
377
+ - `Vehicle_Length`, `Vehicle_Width`: Estimated vehicle dimensions in meters.
378
+ - `Vehicle_Class`: Vehicle class identifier (0: car (incl. vans), 1: bus, 2: truck, 3: motorcycle).
379
+ - `Vehicle_Speed`: Estimated vehicle speed in km/h.
380
+ - `Vehicle_Acceleration`: Estimated vehicle acceleration in m/s$^2$.
381
+ - `Road_Section`: Identifier for the road segment the vehicle is on.
382
+ - `Lane_Number`: Identifier for the lane the vehicle is in.
383
+ - `Visibility`: Boolean indicating if the vehicle's bounding box is fully visible within the frame.
384
+
385
+ - **video_file_geo_transf.txt** (`<stem><geo_transform_postfix>.txt`): Contains the 3x3 georeferencing transformation matrix (homography) that maps points from the video's reference frame to the orthomap. The format is a comma-separated list of the 9 matrix elements:
386
+
387
+ ```text
388
+ h11, h12, h13, h21, h22, h23, h31, h32, h33
389
+ ```
390
+
391
+ **Note:** *All output files (except `video_file.yaml`) are saved in the configured output folder (default: `results/` sub-folder next to the input video). Trajectory and distribution plots are always written to a `plots/` sub-folder inside the output folder.*
392
+
393
+ </details>
394
+
395
+ ## Real-World Deployment: The Songdo Experiment
396
+
397
+ Geo-trax was validated in a large-scale urban traffic monitoring campaign in Songdo, South Korea, where it processed footage from a fleet of 10 drones to produce the [**Songdo Traffic**](https://doi.org/10.5281/zenodo.13828383) dataset. The detection model was trained on the companion [**Songdo Vision**](https://doi.org/10.5281/zenodo.13828407) dataset. Both are described in the [publication](#citation).
398
+
399
+ | Songdo campaign | |
400
+ |---|---|
401
+ | 📍 Location | Songdo International Business District, South Korea |
402
+ | 📅 Duration | 4 days (October 4 to 7, 2022) |
403
+ | 🚁 Fleet | 10 drones (DJI Mavic 3), 140 to 150 m altitude, 4K at 29.97 fps |
404
+ | 🔭 Coverage | 20 busy intersections |
405
+ | 🚗 Result | ~700,000 georeferenced vehicle trajectories |
406
+
407
+ 🎥 *Demo of Geo-trax applied to the Songdo experiment:* [https://youtu.be/gOGivL9FFLk](https://youtu.be/gOGivL9FFLk)
408
+
409
+ The blocks below document the project layout and data-wrangling workflow used in that campaign; they double as the recommended setup for your own multi-drone projects.
410
+
411
+ <details>
412
+ <summary><b>📂 Recommended project folder structure</b></summary>
413
+
414
+ The layout below mirrors the Songdo experiment and matches the pipeline's auto-detection defaults, letting `geotrax batch` run with no path flags. Two conventions do the heavy lifting:
415
+
416
+ - **A `PROCESSED/` folder anchors auto-detection.** When georeferencing or plotting needs orthophotos, master frames, or segmentations and no explicit path is given, Geo-trax walks *up* from the video until it finds `PROCESSED`, then looks for a sibling `ORTHOPHOTOS/` folder.
417
+ - **A location ID ties each video to its assets.** The location ID is the leading letters in the clip filename (`A1.mp4` → `A`), so `A1.mp4` automatically resolves to `ORTHOPHOTOS/A.png`, `ORTHOPHOTOS/master_frames/A.png`, and `ORTHOPHOTOS/segmentations/A.csv`.
418
+
419
+ ### Directory tree
420
+
421
+ ```text
422
+ <project>/ # project root (name arbitrary)
423
+ ├── RAW/ # untouched drone footage + flight logs (never modified)
424
+ │ └── 2022-10-07/D1/PM1/ # arbitrary nesting, e.g. date / drone / session
425
+ │ ├── DJI_0001.MP4 DJI_0001.SRT
426
+ │ └── DJI_0002.MP4 DJI_0002.SRT # drone splits a recording into segments (file-size limit)
427
+ ├── PROCESSED/ # pipeline input (auto-detect anchor)
428
+ │ └── 2022-10-07/D1/PM1/
429
+ │ ├── 0_merged.mp4 0_merged.srt # merged flight video + log (temporary, deletable)
430
+ │ ├── 0_merged.txt # cut list: start/end frames, one cut per line (temporary)
431
+ │ ├── A1.mp4 A1.csv # cut clip + flight log; 'A' = location ID, '1' = sequence
432
+ │ ├── A2.mp4 A2.csv # next clip at the same location
433
+ │ ├── A1.yaml # run metadata, saved next to the clip (not in results/)
434
+ │ └── results/ # pipeline outputs, written next to each clip
435
+ │ ├── A1.txt # pixel-coordinate tracks
436
+ │ ├── A1_vid_transf.txt # stabilization homographies
437
+ │ ├── A1_geo_transf.txt # georeferencing homography
438
+ │ ├── A1.csv # georeferenced trajectories + kinematics
439
+ │ ├── A1_mode_0.mp4 # video with overlaid boxes & trajectories (modes 0/1/2)
440
+ │ └── plots/ # various trajectory & distribution plots
441
+ ├── ORTHOPHOTOS/ # auto-detected sibling of PROCESSED / DATASET
442
+ │ ├── A.png # orthophoto cut-out, per location
443
+ │ ├── A.txt (or A.tif) # georeferencing parameters (or a georeferenced GeoTIFF)
444
+ │ ├── ortho_parameters.txt # (alternative) shared params + per-location A_center.txt
445
+ │ ├── master_frames/ # optional; consistent reference frame per location
446
+ │ │ ├── A.png # reference frame image
447
+ │ │ └── A.txt # cached master->ortho homography
448
+ │ └── segmentations/ # optional; per-location lane/road geometry
449
+ │ ├── A.csv # lane & road-section polygons
450
+ │ └── A.png # overlay image (used for plotting only)
451
+ └── DATASET/ # `geotrax aggregate` output (sibling of PROCESSED)
452
+ └── 2022-10-07_A/ # one intersection-day
453
+ ├── 2022-10-07_A_AM1.csv # one CSV per flight session (AM1-AM5, PM1-PM5),
454
+ └── 2022-10-07_A_PM1.csv # trajectories merged across drones for that session
455
+ ```
456
+
457
+ `RAW/` is kept immutable; everything downstream lives under `PROCESSED/`. The `master_frames/` and `segmentations/` sub-folders are optional; provide them only when you need cross-flight georeferencing consistency or lane-level analysis. `DATASET/` is created by `geotrax aggregate` and is also a valid auto-detection anchor for `ORTHOPHOTOS/`.
458
+
459
+ </details>
460
+
461
+ <details>
462
+ <summary><b>🏷️ Clip naming conventions</b></summary>
463
+
464
+ Only the **leading location letters** of a clip filename are required by the code (parsed by `determine_location_id`). The contextual metadata (date, drone, session) normally lives in the **folder path**, so each clip can be named compactly as location ID + sequence number:
465
+
466
+ ```text
467
+ 2022-10-07/D10/PM5/U1.mp4
468
+ │ │ │ └── clip: location ID 'U' + sequence number '1'
469
+ │ │ └────── flight session: AM1-AM5 (morning) / PM1-PM5 (afternoon)
470
+ │ └────────── drone ID (D1, D2, ...)
471
+ └───────────────────── capture date (ISO 8601, YYYY-MM-DD)
472
+ ```
473
+
474
+ These compact names are assigned automatically by the cutting step, not typed by hand: given a location map (a JSON file pairing each label with its `[lat, lon]` center), [`tools/cut_merged_videos_and_logs.py`](tools/cut_merged_videos_and_logs.py) labels every clip with the location nearest to its GPS centroid and appends a per-location sequence number (`U1`, `U2`, ...).
475
+
476
+ Because only the leading letters matter, the same context can instead be packed into a single self-contained filename when clips are detached from this tree. This is how the sample videos published on Zenodo are named, e.g. `U_D10_2022-10-07_PM5_60s.mp4` (location `U`, drone `D10`, date `2022-10-07`, session `PM5`). Here the per-location sequence number is replaced by a time marker showing where the clip falls within the session: `60s` denotes the first 60 seconds of that session at the location. Either way, the code still extracts location `U`.
477
+
478
+ | Clip filename | Location ID | Resolves to |
479
+ |---|---|---|
480
+ | `U1.mp4` | `U` | `ORTHOPHOTOS/U.png`, `master_frames/U.png`, `segmentations/U.csv` |
481
+ | `U2.mp4` | `U` | `ORTHOPHOTOS/U.png`, … |
482
+ | `U_D10_2022-10-07_PM5_60s.mp4` | `U` | `ORTHOPHOTOS/U.png`, … |
483
+
484
+ `geotrax aggregate` groups results by location (and date/session), merging clips from different drones that cover the same place into a unified dataset.
485
+
486
+ </details>
487
+
488
+ <details>
489
+ <summary><b>🛠️ From raw footage to trajectories</b></summary>
490
+
491
+ The `tools/` directory provides the wrangling scripts that take you from raw footage to pipeline-ready clips (see [`tools/README.md`](tools/README.md) for the full index):
492
+
493
+ 1. **Merge** the recorded video segments and their logs into one video + log per flight session → [`tools/merge_videos_and_logs.py`](tools/merge_videos_and_logs.py)
494
+ 2. **Cut** each merged flight into per-location clips: list the start/end frames of each stable hover in `0_merged.txt`, then split (converting the DJI SRT log to a per-clip CSV) → [`tools/cut_merged_videos_and_logs.py`](tools/cut_merged_videos_and_logs.py)
495
+ 3. **QA / repair** the cut logs → [`tools/find_cut_video_issues.py`](tools/find_cut_video_issues.py), [`tools/fix_timestamp_anomalies.py`](tools/fix_timestamp_anomalies.py), [`tools/interpolate_missing_timestamps.py`](tools/interpolate_missing_timestamps.py)
496
+ 4. **Build the georeferencing assets**: orthophoto cut-outs per location → [`tools/subset_orthophoto.py`](tools/subset_orthophoto.py); master frames → [`tools/find_master_frames.py`](tools/find_master_frames.py); lane segmentations are drawn manually, with overlays rendered via [`tools/viz_segmentations.py`](tools/viz_segmentations.py)
497
+ 5. **Run the pipeline**: `geotrax batch PROCESSED/ ...`; orthophotos, master frames, and segmentations are auto-detected from the sibling `ORTHOPHOTOS/` folder.
498
+ 6. **(Optional) Aggregate** results across drones and flights for the same location → `geotrax aggregate PROCESSED/`, which writes a unified dataset to a sibling `DATASET/` folder.
499
+
500
+ ### Lessons from the Songdo experiment
501
+
502
+ - Treat `RAW/` as read-only archival storage and derive everything under `PROCESSED/`; the wrangling steps are reproducible from the raw footage.
503
+ - The **master frame** is an intermediary coordinate system per location: aligning every flight to one shared reference frame keeps trajectories from different drones, altitudes, and viewpoints in a single consistent coordinate system.
504
+ - Coordinates were projected to a local CRS (EPSG:5186, KGD2002 / Central Belt 2010) alongside WGS84 lat/lon; set your own CRS in the `georef:` config section.
505
+ - Imagery was captured at ~140–150 m altitude in 4K, giving a ground sampling distance of ≈ 0.027 m/px (the default `extraction.gsd`). Re-tune the GSD for different altitudes or cameras.
506
+
507
+ </details>
508
+
509
+ ## Citation
510
+
511
+ If you use **Geo-trax** in your research or software, please cite:
512
+
513
+ 1. **Journal article** (preferred for any use of the framework):
514
+
515
+ ```bibtex
516
+ @article{fonod2025advanced,
517
+ title = {Advanced computer vision for extracting georeferenced vehicle trajectories from drone imagery},
518
+ author = {Fonod, Robert and Cho, Haechan and Yeo, Hwasoo and Geroliminis, Nikolas},
519
+ journal = {Transportation Research Part C: Emerging Technologies},
520
+ volume = {178},
521
+ pages = {105205},
522
+ year = {2025},
523
+ publisher = {Elsevier},
524
+ doi = {10.1016/j.trc.2025.105205},
525
+ url = {https://doi.org/10.1016/j.trc.2025.105205}
526
+ }
527
+ ```
528
+
529
+ 2. **Software archive** (when referencing or building on the code itself):
530
+
531
+ ```bibtex
532
+ @software{fonod2026geo-trax,
533
+ author = {Fonod, Robert},
534
+ title = {Geo-trax: A Comprehensive Framework for Georeferenced Vehicle Trajectory Extraction from Drone Imagery},
535
+ year = {2026},
536
+ month = jun,
537
+ version = {1.0.0},
538
+ doi = {10.5281/zenodo.12119542},
539
+ url = {https://github.com/rfonod/geo-trax},
540
+ license = {MIT}
541
+ }
542
+ ```
543
+
544
+ ## Contributions
545
+
546
+ Early code received key contributions from [Haechan Cho](https://github.com/cho-96) (georeferencing) and [Sohyeong Kim](https://github.com/shgold) (video/flight-log merging). Community contributions are welcome: open a [GitHub Issue](https://github.com/rfonod/geo-trax/issues) or submit a pull request.
547
+
548
+ ## License
549
+
550
+ This project is distributed under the MIT License. See the [LICENSE](LICENSE) for more details.