Standalone analysis and data-wrangling scripts that support — but are not part of — the core
geotrax pipeline (see the project README and geotrax -h). They cover dataset
preparation, annotation, georeferencing setup, and evaluation. Run any tool directly:
python tools/<name>.py -h # full usage, arguments, and examplesRun tools from the repository root. Logs go to the platform log directory by default;
--log-path/-lpredirects them. Most tools accept-c/--cfgto load a pipeline config (seegeotrax config show).
A few of these scripts hardcode Songdo experiment-specific values (or rigidly assume its directory layout) in their code; the rest are reusable as-is even where the docstring cites Songdo as the motivating example. Each tool below is tagged:
| Tag | Meaning |
|---|---|
| 🟢 General | Reusable on any compatible dataset (a Songdo mention in the docstring is just context). |
| 🔵 Songdo | The code hardcodes Songdo specifics — drone/AV IDs, dates, GSD, session time windows, EPSG:5186, or the Songdo PROCESSED/ directory layout. Adapt before reuse elsewhere. |
| 🧪 Research | One-off analysis backing the paper; low general reuse. |
🔵/🧪 tools relate to the Songdo deployment described in Fonod et al. 2025 (the Songdo Traffic and Songdo Vision datasets).
These chains show how the tools compose around the geotrax stages:
- Footage → clips.
merge_videos_and_logs(per-session merge) →cut_merged_videos_and_logs(per-location clips) →find_cut_video_issues→fix_timestamp_anomalies(QA + auto-repair, which callrecut_video_and_log/interpolate_missing_timestamps) →geotrax extract. - Georeferencing setup.
subset_orthophoto(orthophoto cutouts) +find_master_frames(reference frames) +viz_segmentations(verify lane/section overlays) →geotrax georeference. - Detection training data.
sample_frames→ manual labelling and/orannotate_frames(pre-labels) → inspect withviz_annotations/find_max_annotations/compute_bb_center_error→ convert withfix_json_annotations/yolo_to_coco→train/. - Evaluation & QA. Trackers:
compare_tracking. Dimensions:analyze_bb_ratios,viz_dimension_estimation. Georeferencing accuracy:benchmark_ortho_matching,compare_av_detections_and_tune_filters. Final dataset:check_dataset,find_source_id.
| Script | Purpose | Tag |
|---|---|---|
merge_videos_and_logs.py |
Merge per-flight DJI videos + SRT logs into one merged pair per session | 🟢 |
cut_merged_videos_and_logs.py |
Cut merged video/SRT into per-location clips + per-cut CSV logs | 🟢 |
recut_video_and_log.py |
Re-cut a video + its CSV log by frame range (keyframe-aligned or exact) | 🟢 |
find_cut_video_issues.py |
Detect spatial, temporal, and camera anomalies in flight logs | 🔵 |
fix_timestamp_anomalies.py |
Auto-repair timestamp anomalies by recutting at the anomaly frame | 🔵 |
interpolate_missing_timestamps.py |
Fill NaN timestamps from the frame rate (given or inferred) | 🟢 |
| Script | Purpose | Tag |
|---|---|---|
sample_frames.py |
Randomly sample frames from drone videos for annotation (global or balanced) | 🟢 |
annotate_frames.py |
Run YOLO on images → YOLO-format labels (+ optional viz / masked images) | 🟢 |
yolo_to_coco.py |
Convert YOLO normalized labels → COCO JSON with absolute pixel coordinates | 🟢 |
fix_json_annotations.py |
Clean/convert COCO-like JSON (strip image data, normalize paths, HBB↔OBB) | 🟢 |
find_max_annotations.py |
List top-N YOLO label files by annotation count (also a shared helper) | 🟢 |
viz_annotations.py |
Draw YOLO bounding boxes on images — single file or batch directory | 🟢 |
| Script | Purpose | Tag |
|---|---|---|
find_master_frames.py |
Select optimal reference (master) frames for georeferencing from flight logs | 🟢 |
subset_orthophoto.py |
Extract square PNG subsets from a large GeoTIFF orthophoto per location | 🟢 |
viz_segmentations.py |
Overlay lane and road-section segmentation polygons on orthophotos | 🟢 |
benchmark_ortho_matching.py |
Benchmark orthophoto-matching reprojection error across resolutions | 🧪 |
| Script | Purpose | Tag |
|---|---|---|
compare_tracking.py |
Compare trackers via track-length distributions and KL divergence | 🟢 |
compute_bb_center_error.py |
Bounding-box center error between human labels and model predictions | 🟢 |
analyze_bb_ratios.py |
Length/width aspect-ratio statistics and histograms per vehicle class | 🧪 |
viz_dimension_estimation.py |
Step-by-step visualization of the azimuth-based dimension estimator | 🔵 |
compare_av_detections_and_tune_filters.py |
Compare extracted vs. RTK-GNSS AV trajectories; tune smoothing filters | 🧪 |
| Script | Purpose | Tag |
|---|---|---|
check_dataset.py |
Flag vehicles with speed/acceleration violations in an aggregated dataset | 🔵 |
find_source_id.py |
Trace an aggregated Vehicle_ID back to its source video and original ID | 🔵 |
Turn raw DJI drone footage and flight logs into clean, per-location clips for the pipeline.
🟢 General — Merges multiple per-flight DJI video files and their SRT flight logs from a
session directory into a single 0_merged.mp4 / 0_merged.srt pair, handling the DJI counter
reset and _trimmed conventions. Configurable video extension and output stem; can process one
session or sweep an entire tree. (Developed for Songdo, but assumes only DJI conventions.)
python tools/merge_videos_and_logs.py /path/to/RAW --output-dir /path/to/PROCESSED
python tools/merge_videos_and_logs.py /path/to/RAW --output-dir /path/to/PROCESSED --dry-run🟢 General — Cuts merged video + SRT files into per-location clips per a cuts specification,
converting SRT records into per-clip CSV flight logs. Location names come from a user-supplied
JSON map (not hardcoded); also --dry-run preview and --cleanup to delete merged sources.
python tools/cut_merged_videos_and_logs.py /path/to/PROCESSED \
--location-map /path/to/locations.json --cleanup🟢 General — Re-cuts a video and its companion CSV log to a frame range, via a cuts file or
direct --start/--end. Defaults to keyframe-aligned cuts for codec efficiency (--exact-cut
for exact frames), supports --rotate, and rebases CSV frame numbers to start at 0.
python tools/recut_video_and_log.py video.MP4 cuts.txt
python tools/recut_video_and_log.py video.MP4 --start 120 --end 540 --rotate 90 -o cut.MP4🔵 Songdo — Scans flight logs under a PROCESSED/ tree for spatial, temporal, and
camera-parameter anomalies (each with a tunable *-diff-threshold). The temporal check uses a
hardcoded Songdo session schedule (SESSION2TIME_WINDOW, AM1–PM5) and defaults to epsg:5186.
Writes stats and an anomalies CSV (consumed by fix_timestamp_anomalies.py) plus visualizations.
python tools/find_cut_video_issues.py /path/to/PROCESSED -s -f -viz -sv -tc🔵 Songdo — Reads the anomalies CSV from find_cut_video_issues.py and automatically
re-cuts each affected video + log at the anomaly frame (renaming originals with _original).
Delegates to recut_video_and_log.py and can re-run geotrax batch on the repaired clips.
python tools/fix_timestamp_anomalies.py flight_log_anomalies.csv \
--processed-folder /data/PROCESSED/🟢 General — Fills NaN values in a CSV timestamp column from the video frame rate —
given via --fps or inferred from the spacing of the existing timestamps — forward from the
previous valid timestamp or --backward from the next. Works for any constant frame rate
(missing values are anchored to original timestamps to avoid rounding drift). Writes a new
*_interpolated.CSV, leaving the original untouched.
python tools/interpolate_missing_timestamps.py flight_log.CSV # infer fps
python tools/interpolate_missing_timestamps.py flight_log.CSV --fps 30 --backwardBuild and convert the datasets used to train or evaluate the YOLO detection model.
🟢 General — Randomly samples frames from drone videos for manual annotation: global random
sampling or --balanced (equal frames per video). The --name-filter (default merged) is
overridable, metadata fields are arbitrary; filter by SRT/CSV metadata (e.g.
--srt-filter rel_alt:130:160) and skip takeoff/landing with --skip-start/--skip-end.
Output filenames encode the source relative path for traceability.
python tools/sample_frames.py /path/to/PROCESSED /path/to/frames -n 200 --balanced
python tools/sample_frames.py /path/to/PROCESSED /path/to/frames --srt-filter rel_alt:130:160🟢 General — Runs the configured YOLO model over a directory of images and writes YOLO-format
labels. Optionally saves box visualizations (--save-viz) and masked images (--save-masked).
Detection parameters (--conf, --iou, --imgsz, --augment, per-class conf) override config.
python tools/annotate_frames.py path/to/images/ --save-viz --conf 0.2 --augment
python tools/annotate_frames.py path/to/images/ --save-masked --margin 0.2 -z path/to/viz/🟢 General — Converts a directory of YOLO .txt labels (normalized) to COCO JSON with
absolute pixel coordinates. The class map comes inline (-cm 0=Car ...), from a YAML/JSON file
(-mf), or — by default — is extracted from the pipeline's configured YOLO model.
python tools/yolo_to_coco.py path/to/labels/ # class map from model (config)
python tools/yolo_to_coco.py path/to/labels/ -cm 0=Car 1=Bus 2=Truck 3=Motorcycle🟢 General — Batch-cleans COCO-like JSON annotation files: strip embedded image data
(--remove-image-data), normalize paths (--normalize-to-unix/-windows), and convert
between horizontal and oriented boxes (--to-obb/--to-hbb).
python tools/fix_json_annotations.py path/to/annotations/ --remove-image-data
python tools/fix_json_annotations.py path/to/annotations/ --normalize-to-unix --to-obb🟢 General — Lists the top-N YOLO label files by annotation count (-n), optionally filtered
to specific classes (--type). Also imported as a helper by viz_annotations.py to pick the
busiest frames in directory mode.
python tools/find_max_annotations.py /path/to/annotations/ -n 10
python tools/find_max_annotations.py /path/to/annotations/ -n 5 --type 0 1🟢 General — Draws YOLO bounding boxes on images for inspection. For a single image it draws
all boxes; for a directory it auto-selects the -n most-annotated frames (via
find_max_annotations). Resolve labels with -cn from a YAML/JSON file or inline id:name
pairs; filter classes with --type; --show and/or --save.
python tools/viz_annotations.py images/ --save -n 20 -cn names.yaml
python tools/viz_annotations.py image.jpg -cn 0:car 1:bus 2:truck --showPrepare the orthophotos, master frames, and segmentation overlays used by geotrax georeference.
🟢 General — Selects the best reference (master) frame per video by finding the frame closest
to the drone's mean hover position with the best detection coverage. Outputs
best_master_frames.csv, optional master-frame PNGs (-smf), and visualizations.
python tools/find_master_frames.py /path/to/PROCESSED -of /path/to/master_frames -s -smf -svExample --visualize output from the Songdo experiment with 20 drones, showing the selected master frame location per intersection relative to the initial frame position of each flight clip.
🟢 General — Extracts square PNG subsets from a large GeoTIFF orthophoto, centred on
coordinates from a user-supplied JSON location dictionary, downscaled by --scale-factor
(crop size and scale are overridable defaults). Writes pixel-coordinate text files alongside
each crop.
python tools/subset_orthophoto.py --orthophoto-filepath ortho.tif \
--ortho-cutout-folder output/ --location-dict-filepath locations.json🟢 General — Overlays lane and road-section segmentation polygons onto orthophotos. Reads any
CSV in the geo-trax segmentation format (Section, Lane, and corner-coordinate columns),
drawing red lane contours and blue section-ID labels. CSVs and output default to
<ortho_folder>/segmentations/.
python tools/viz_segmentations.py data/orthophotos/ -sf data/segmentations/ -o data/output/🧪 Research — Benchmarks orthophoto-matching accuracy by computing reprojection errors across
a range of resolutions (RootSIFT features + RANSAC/USAC_MAGSAC homography). Writes results.txt
with LaTeX-formatted statistics and ground-truth overlays. Built for the paper's figures.
python tools/benchmark_ortho_matching.py path/to/data -v
python tools/benchmark_ortho_matching.py path/to/data -mr 1000 -xr 10000 -rs 500 -oEvaluate and compare pipeline components during model development and tuning.
🟢 General — Compares tracker performance using trajectory-length distributions and pairwise
KL divergence. Expects a results_<tracker>/ subdirectory per tracker alongside each video.
Defaults to all six supported trackers; restrict with --trackers.
python tools/compare_tracking.py /path/to/videos/ --show
python tools/compare_tracking.py /path/to/videos/ --trackers botsort ocsort --saveExample output comparing BoT-SORT and ByteTrack on a sample flight session from the Songdo Traffic dataset.
🟢 General — Computes bounding-box centre error between human labels (../labels/) and model
predictions (../pre-labels/) via spatial matching. Reports per-class (or --class-agnostic)
statistics; --save writes error-distribution plots. Override paths with -ha/-pa.
python tools/compute_bb_center_error.py /path/to/images/
python tools/compute_bb_center_error.py /path/to/images/ --class-agnostic --saveExample output showing the bounding-box center error distribution between human annotations from the Songdo Vision v1 dataset and geo-trax model predictions (the released model's training/evaluation version; a later v2 annotation-quality release exists on Zenodo).
🧪 Research — Analyses length-to-width aspect ratios of vehicle bounding boxes per class,
computing descriptive statistics and optional histograms (--hist). Used to derive the tau_c
ratio thresholds for the azimuth-based dimension estimator.
python tools/analyze_bb_ratios.py data/ --hist
python tools/analyze_bb_ratios.py results/video.yaml # names the video one level upDimensions are recomputed with the pipeline's own estimator
(geotrax.extract.estimate_vehicle_dimensions), overriding only tau_c and theta_bar with
restrictive values so stationary vehicles drop out; every other parameter comes from -c. For a
step-by-step visualization of that estimator on one vehicle ID, use viz_dimension_estimation.py.
🔵 Songdo — Renders step-by-step visualizations of the azimuth-based dimension estimator for one vehicle ID: a trajectory plot with colour-coded boxes and a dimension-distribution histogram. Constants are tuned to the Songdo DJI Mavic 3 setup (140–150 m, 4K, EPSG:5186).
python tools/viz_dimension_estimation.py path/to/video.mp4 --id 42 --show
python tools/viz_dimension_estimation.py path/to/video.mp4 --id 42 --saveExample output from the Songdo experiment showing a vehicle trajectory with accepted (green) and rejected (red) bounding boxes (left) and the resulting dimension distribution with the final estimate (right).
🧪 Research — Compares extracted trajectories against RTK-GNSS ground truth from an AV test
vehicle: positional/speed errors, --coords local|global, and smoothing-filter tuning
(--tune, --filter). Hardcodes Songdo AV IDs, session dates, and EPSG:5186.
python tools/compare_av_detections_and_tune_filters.py data/ --show
python tools/compare_av_detections_and_tune_filters.py data/ --tune --save --filter savitzky_golayThe figure below illustrates the two complementary trajectory sources (drone-derived BEV and on-board probe vehicle) and how positional differences between them are computed. In the Songdo experiment, the probe vehicle was an AV equipped with high-precision RTK-GNSS sensors, provided by Stanford Center at the Incheon Global Campus (SCIGC).
Validate and debug the aggregated output of the full pipeline.
🔵 Songdo — Scans an aggregated CSV dataset for vehicles exceeding speed
(--speed-threshold, default 130 km/h) or acceleration (--acceleration-threshold, default
12 m/s²) limits, reporting the source video for each violation. Assumes the DATASET/ →
PROCESSED/ layout from the aggregation workflow.
python tools/check_dataset.py data.csv
python tools/check_dataset.py dataset/ --speed-threshold 100 --acceleration-threshold 10🔵 Songdo — Given a Vehicle_ID in an aggregated dataset CSV, traces it back to the original
source video and per-video ID using the PROCESSED/ structure and the ID-offset logic from
geotrax aggregate. Useful for verifying or debugging specific trajectories.
python tools/find_source_id.py 2022-10-04_A/2022-10-04_A_AM1.csv 5 \
--processed-folder /path/to/PROCESSED/- Run from the repository root.
viz_annotations.pydoesfrom find_max_annotations import find_max_annotations, which resolves only when both files sit onsys.pathtogether — i.e. when invoked aspython tools/viz_annotations.py …from the repo root. This dependency is one reason the directory is kept flat. - Shared conventions. Every tool has a structured docstring (Usage/Arguments/Options/Examples),
--quiet/-q, and--log-path/-lp; most accept-c/--cfg.