Skip to content

Reject GPX tracks that miss the video, as placed on its timeline - #848

Merged
caglarpir merged 1 commit into
mapillary:mainfrom
caglarpir:reject-gpx-outside-video
Oct 20, 2026
Merged

caglarpir merged 1 commit into
mapillary:mainfrom
caglarpir:reject-gpx-outside-video

Conversation

@caglarpir

@caglarpir caglarpir commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Split out of #842. Reworked on top of #841, #847 and #849.

Problem

A GPX track that does not overlap the video in time is synced anyway: the video gets a track with no position for any moment of it, and process exits 0. Main only warns when the offset is over a day, so a GPX in the wrong time zone, the GPX of the next clip, or a wrong --video_start_time goes through silently.

Changes

The track is checked against the video once it is placed on the video's timeline, so the check sees what the frames will see: placed by the video's GPS clock (a timelapse's included, #849), by --video_start_time, or by assuming that it starts with the video (#841), and after --interpolation_offset_time. The video runs from video time 0 to its mvhd duration.

  • A track that misses the video raises MapillaryOutsideGPXTrackError, whatever the gap. The message names both files and their times, gives the gap in GPS time, and suggests checking that the GPX belongs to the video, then the camera clock and the time zone of the GPX timestamps. This replaces main's warning for an offset over a day.
  • A track that covers only part of the video warns with how much of the video is left uncovered, allowing 2 s of GPS time for a once-a-second logger started and stopped with the camera.
  • If the duration cannot be read, a gap warns up to a day and raises beyond. A track assumed to start with the video has no clock to say that it misses, so it only warns.

MapillaryOutsideGPXTrackError is now a geotagging error, so the next geotag source gets its turn, for images too. When that source geotags the file, a warning says so, so a GPX that does not belong to the file does not go unnoticed: one warning per video, and one per GPX source for images. With GPX as the only or last source the error is final, as before. The error is made picklable, since video geotagging raises it in a worker process.

The module keeps one duration formatter, used by #841's messages too. It rounds rather than truncates: timestamps often fall a hair short of a whole unit, which humanize.naturaldelta reports as "4 seconds" for 4.999 s and "an hour" for 1 h 59 min.

Behaviour

process on the 12 s GoPro sample video and a sample image from the test data:

Run main this branch
Video, GPX covering it exit 0 exit 0
Video, GPX starting 6 s in exit 0, silent exit 0, warns
Video, GPX 2 h off exit 0, silent, synced 2 h off exit 6, MapillaryOutsideGPXTrackError
Video, GPX 2 h off, then native exit 0, synced 2 h off exit 0, native track, warns that the GPX misses the video
Video, GPX from 2025 exit 0, warns exit 6, MapillaryOutsideGPXTrackError
Image, GPX from another year exit 6 exit 6
Image, GPX from another year, then exif exit 6 exit 0, EXIF location, warns that 1 image falls outside the GPX track
Video or image, default sources exit 0 exit 0, same output

Verification on real files

  • 29 GPX and video pairs, including the 17 tracks exported from their own videos that Sync a GPX to the GPS clock of a timelapse, even one too noisy to publish #849 was verified on, the 12.5x GoPro MAX2 timelapse reported in Let a GPX override a video whose own GPS is unusable #831, and GPX files paired with GoPro MAX, HERO8 and noisy-GPS videos, some of them shifted to test the sync. The placement is the same as on main for 28. The 29th, a GPX that ends 1.2 minutes before a HERO8 video starts, now raises: main placed it entirely before the video.
  • The MAX2 timelapse is accepted: checked at 1x, the previous version of this PR rejected it as missing the video by 0.76 seconds.
  • 8 of the pairs now warn of a partial cover, each accurately: GPX tracks that start minutes in, such as a HERO7 track exported after its first 3.7 minutes of noisy fixes were dropped, a GPX recorded for only the end of a GoPro MAX video, and the MAX2 timelapse, whose GPX leaves 3.4 s of GPS time uncovered.
  • Default process on 45 sample videos from GoPro, BlackVue, Labpano, Insta360 and Ricoh cameras: output identical to main.

Tests

  • tests/unit/test_gpx_video_coverage.py, through extract() on CAMM videos and GPX files: covering, partial, missing, the gap in a readable unit, the error's times, no duration, an epoch mix-up, --interpolation_offset_time, pickling, timelapses (a GPX of the whole recording, half of it, and the gap in GPS time), --video_start_time, the assumed start, and falling through to the next source with a warning, for videos and images.
  • test_good_clock_days_away_is_still_used (Sync a GPX to the GPS clock of a timelapse, even one too noisy to publish #849) now expects the rejection.
  • Two GPX sync tests (Honour --geotag_source in distance-based sampling #841) used a GPX 8 hours before the sample video's GPS, which started at 23:44:42Z, not 15:44:47Z as their comment said.
  • Two integration tests paired the 2019 GoPro sample video with a 2025 GPX file and asserted success. They now shift the GPX to the video's time, and a new test asserts that the unrelated pairing fails.
  • video_process with a GPX two hours before --video_start_time still fails with exit 7, now with MapillaryOutsideGPXTrackError rather than finding no frames.
  • Unit: 913 passed, 18 skipped, 1 xfailed. Integration: 69 passed. Doctests pass. ruff check, ruff format --check, usort diff and mypy are clean.

Known limitations

  • When every source fails, the error reported is the last source's, as with other geotagging errors, so a GPX that missed the file is only logged at debug level.
  • The 2 s tolerance fits a once-a-second logger. A GPX that samples every 5 s can warn even when it covers the video.

Check a GPX track against the video once it is placed on the video's
timeline, by the video's GPS clock (including a timelapse's), by
--video_start_time, or by assuming that it starts with the video, and
after --interpolation_offset_time:

- A track that misses the video raises MapillaryOutsideGPXTrackError,
  whatever the gap: a GPX file from another day or another clip, an
  epoch mix-up, or naive GPX timestamps read in the wrong time zone.
  Before, the video was synced to a track with no position for any
  moment of it, and process exited 0, with a warning only for an offset
  over a day. The message names both files, their times and the gap,
  in GPS time.
- A track that covers only part of the video warns with how much of it
  is left uncovered, allowing 2 s of GPS time for a once-a-second
  logger started and stopped with the camera.
- If the duration cannot be read, a gap warns up to a day and raises
  beyond. A track assumed to start with the video only warns.

MapillaryOutsideGPXTrackError is now a geotagging error, so the next
geotag source gets its turn, for images too, and a warning says so when
that source geotags the file: one per video, and one per GPX source for
images. With GPX as the only or last source the error is final, as
before. The error is made picklable, since video geotagging raises it
in a worker process.

The GPX module keeps one duration formatter. It rounds instead of
truncating, since timestamps often fall a hair short of a whole unit,
which humanize reported as "4 seconds" for 4.999 and "an hour" for
1 h 59 min.

Tests:
- test_good_clock_days_away_is_still_used now expects the rejection.
- Two GPX sync tests used a GPX 8 hours before the sample video's GPS,
  which started at 23:44:42Z, not 15:44:47Z as their comment said.
- Two integration tests paired the 2019 GoPro sample video with a 2025
  GPX file and asserted success. They now shift the GPX to the video's
  time, and a new test asserts that the unrelated pairing fails.
- video_process with a GPX two hours before --video_start_time still
  fails with exit 7, now with MapillaryOutsideGPXTrackError.
@caglarpir
caglarpir force-pushed the reject-gpx-outside-video branch from f1123b8 to 9f04bed Compare October 20, 2026 11:57
@caglarpir caglarpir changed the title Reject GPX tracks that miss the video Reject GPX tracks that miss the video, as placed on its timeline Oct 20, 2026
@caglarpir
caglarpir merged commit bc991b7 into mapillary:main Oct 20, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant