Conversation
mapillary#828 converts CAMM type 6 timestamps from GPS time to Unix time on read for Labpano, keyed on an exact make match, and writes Unix time back out. Review of what shipped found five problems. 1. Double conversion on write. camm_builder copies the source make into the CAMM track it writes, but wrote Unix time into the GPS time field, so our own upload of a 2024 Labpano video parsed back as 2034. denormalize_gps_epochs() now converts back to GPS time for makes that record it, which is also what the camera itself wrote and what v0.14.7 and earlier wrote. 2. Exact make match. "Labpano Technology Co.,Ltd" and similar variants were read ten years off, silently now that the elst widens to 64 bits instead of overflowing. The make now matches as a substring, and the mvhd creation_time decides ahead of the make when it can: GPS time sits one GPS epoch before it, Unix time sits close to it, ten years apart. That also reads the Unix-time outputs main has written since mapillary#828 correctly, whatever their make. 3. Silent GPX mis-sync. GPXVideoExtractor now raises MapillaryOutsideGPXTrackError when the GPX track, once synced, does not overlap the video's GPS track at all, like image geotagging already does. This replaces the warning mapillary#828 added. The error is made picklable, since video geotagging raises it in a worker process. 4. Mixed type 5 and type 6 samples. The native extractor dropped the type 5 points when a track had both; it now merges them in time order, and the GPX offset anchors on the first point that carries a timestamp rather than on the first point. 5. ExifTool 18 s behind native. ExifTool adds the GPS epoch offset without the leap seconds. Native is right: on a PanoX V2 file the first GPS fix plus the mvhd duration ends 0.02 s from the creation time, where ExifTool's reading ends 18 s after the file was created. The ExifTool reader now subtracts the leap seconds for CAMM tracks from makes that record GPS time. Two integration tests paired the 2019 GoPro sample video with a 2025 GPX file and asserted success, which is the mis-sync in 3. They now shift the GPX to the video's time, and a new test asserts that the unrelated pairing fails.
This was referenced Sep 23, 2026
Addresses review of the CAMM epoch follow-ups: - Write Unix time whatever the make, as main has since mapillary#828, instead of converting back to GPS time for Labpano. This is not what released versions wrote: v0.14.7 passed a Labpano camera's GPS time through unconverted, 1389017092.966 for the first sample of a PanoX V2 capture from 2024-01-11, where this writes the Unix time 1704981874.966, one GPS epoch less 18 leap seconds later. Our output copies the source creation time, which the reader already uses to tell Unix time from GPS time. Removes denormalize_gps_epochs and unix_to_gps_epoch. - Decide the epoch of a CAMM track by the median of its timestamps, so that one stray sample cannot flip the whole track. - Sync GPX to video time 0 rather than to the first video GPS point, which put the track early by that point's time when the video's GPS started late. - Replace the overlap check with a gap check: silent on overlap, a warning naming both files for a gap up to a day, and an error only beyond that, where no clock or time zone mistake can explain it. The error is now a geotagging error, so the next geotag source gets its turn. - ExifTool leap correction: match the CAMM format case-insensitively, log when it applies, and test it against real ExifTool output of a file we write. - Document the cost of mixing CAMM type 5 and type 6 samples.
Addresses further review of the CAMM epoch follow-ups: - Check the GPX track against the whole video, from video time 0 to the mvhd duration, rather than to the video's last GPS point. A track that misses the video now raises MapillaryOutsideGPXTrackError whatever the gap, since it has no position for any moment of the video. This catches the most common mistake, naive GPX timestamps read in the wrong time zone: main syncs a GPX 2 h off silently, and the previous commit only warned. A track that covers only part of the video warns. When the duration cannot be read, the previous commit's rule applies: a gap warns up to 24 h and raises beyond. - Without a conclusive creation time, do not read a CAMM timestamp as GPS time if that would put it in the future. Unix time read as GPS time lands ten years late, so our output of a Labpano source without a creation time no longer reads back ten years late, for recordings less than ten years old when read. - Drop CAMM GPS points whose timestamps are more than 30 days from the median of the track, and warn. A stray was converted with the track, so it ended up years off and corrupted the timestamps interpolated next to it. - Test that images outside a GPX track fall through to the next geotag source. The previous commit made MapillaryOutsideGPXTrackError a geotagging error, and images raise it too: with --geotag_source gpx --geotag_source exif, an image outside the GPX track now gets its EXIF location instead of failing. With GPX as the only or last source, nothing changes.
Addresses review of the previous commit: - The GPX time check ran only for a non-zero offset. When the video's GPS has no timestamps, as on the Ricoh Theta X, the offset is 0 and nothing was checked, and neither was a GPX that syncs to exactly video time 0 but covers only part of the video. Check whenever there are GPX and video GPS points. A GPX synced to 0 starts with the video, so the check can only warn that it ends before the video does. - Report a gap in seconds, minutes, hours or days, whichever fits, with significant digits: a GPX starting 0.089 s after the video ended was reported as missing it "by 0 seconds (0.0 days)". The hint now starts with checking that the GPX file belongs to the video, which fits a gap of any size, such as the GPX of the next clip. - Say what is known about the part of a video outside a GPX track: the track has no position for it. The warning and docstrings said that positions there are extrapolated. The video sampler drops frames outside its track instead, and what happens after upload cannot be checked here.
caglarpir
force-pushed
the
fix-camm-epoch-followups
branch
from
September 23, 2026 14:47
b5f4b94 to
d761dac
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #828. Review of what merged found five problems. The first one ships wrong data.
camm_builderwrites the timestamp it holds into the CAMM GPS time field, which since #828 is Unix time for every make, and copies the source make (Labpano) into the track. main's reader converts every Labpano track from GPS time, so our upload of a 2024 Labpano video parses back ten years late.mvhdcreation time when it can: GPS time sits one GPS epoch before it, Unix time sits close to it, and the two readings are ten years apart. Our output copies the source creation time, so it reads back as Unix time. Without a conclusive creation time the make decides, but a timestamp that GPS time would put in the future is read as Unix time, which covers our upload of a Labpano source that has no creation time. Writes are unchanged from main: Unix time for every make. Released versions wrote something else for a Labpano source: they passed the camera's GPS time through. For the first sample of a PanoX V2 capture from 2024-01-11, v0.14.7 writes 1389017092.966, and main and this branch write 1704981874.966.make == "labpano"misses variants likeLabpano Technology Co.,Ltd. Because theelstnow widens to 64 bits instead of overflowing, those files come out ten years off silently.mvhdduration. A track that misses the video has no position for any moment of it, so it raisesMapillaryOutsideGPXTrackErrorwhatever the gap: an epoch mix-up, a GPX file from another day, or naive GPX timestamps read in the wrong time zone. The message names both files and gives the gap in the unit that fits, with a hint to check that the GPX file belongs to the video, then the camera clock and the time zone of the GPX timestamps. A track that covers only part of the video warns with how much of it is left uncovered, allowing 2 s for a once-a-second logger started and stopped with the camera. The check also runs when the video's GPS has no timestamps, as on the Ricoh Theta X: the GPX is then aligned to video time 0, so only an uncovered end can show. If the duration cannot be read, the video is known only up to its last GPS sample, so a gap warns up to 24 h and raises beyond. The offset now projects to video time 0.metahandler, which ExifTool reports asMetaFormat=camm, matched case-insensitively. The tracks mapillary_tools writes are under acammhandler (OtherFormat=camm) and hold Unix time, so they are read as is. The correction is logged at debug level.Behaviour change
--video_geotag_source gpxwith a GPX file that does not overlap the video at all now fails that file withMapillaryOutsideGPXTrackError, andprocessexits with 6 unless--skip_process_errorsis set. Before, it exited with 0 and synced the video to a track that has no position for any moment of it. This includes a GPX in local time instead of UTC, and a camera clock off by more than the length of the recording. Other files in the same run are not affected.MapillaryOutsideGPXTrackErrorsubclassesMapillaryGeoTaggingError), so the next geotag source gets its turn. A GPX track that misses the capture time says nothing about the file itself. This also applies to images: with--geotag_source gpx --geotag_source exif, an image outside the GPX track now gets its EXIF location instead of failing. With GPX as the only or last source, the error is final, as before. When every source fails, the error reported is the last source's, as with other geotagging errors.Two integration tests paired the 2019 GoPro sample video with a 2025 GPX file and asserted success, which is exactly this mis-sync. Both now shift the GPX to the video's time. A new test asserts that the unrelated pairing fails.
Verification on real files
14 Labpano captures: PanoX V2 ×4, Pilot One ×9, Pilot Era ×1.
process: main and this branch produce identical output for all 14 files, with no errors and no warnings.processoutput is the same on all 25 before and after each of the last two commits. The only error, a stationary Ricoh Theta X video, happens on both.mvhdcreation time cleared. Both the previous commit and this branch read the source correctly. After a dry-runprocess_and_upload, the previous commit reads the upload back as 2034-01-15, and this branch as 2024-01-11, the same as the source.processwith a GPX geotag source on the 12 s GoPro sample video. A GPX covering the video exits 0 without warnings. A GPX starting 6 s into the video warns and exits 0. A GPX in a time zone 2 h off exits 6 withMapillaryOutsideGPXTrackError; main exits 0 without a warning. A GPX starting 0.089 s after the video ends exits 6, and the error says it misses the video by 0.089 seconds, where the previous commit said 0 seconds (0.0 days).processon a sample image with a GPX from another year: with--geotag_source gpx --geotag_source exif, it gets its EXIF location. With GPX alone, it exits 6.process_and_uploadon a PanoX V2 capture from 2024: parsed back natively, the upload matches the source to the millisecond (2024-01-18 10:40:43.6 to 10:43:56.6). With this branch, ExifTool reads the same times from the source (MetaFormat=camm, leap corrected) and from the upload (OtherFormat=camm, read as is).main and this branch write the same CAMM bytes, so the only question is who reads them:
#828 is not in any release yet, so no released version misreads these uploads.
Native vs ExifTool timing. The table shows the last GPS sample minus the
mvhdcreation time:The native reading puts the last GPS sample within 3 s of the creation time on every capture. ExifTool's reading would put it 15–18 s after the file was created.
Known limitation
For a Labpano source without an
mvhdcreation time, the check that keeps our upload in Unix time compares against the reading machine's clock. It holds for any recording less than ten years old when read, so a 2024 upload of such a source read after 2034 would come back ten years late. All 25 captures above have a creation time.A video whose GPS has no timestamps cannot be synced to a GPX by time, so the GPX is aligned to video time 0, as on main. If the video's GPS starts later, as on one of the Ricoh Theta X captures above, the track lands early by that much. The check then warns about the uncovered end, but cannot tell a misaligned track from a short one.
Tests
tests/unit/test_gps_epoch.pycovers:extract(), reading the duration, and falling through to the next geotag sourceMetaFormat) and mapillary_tools (OtherFormat) tags, plus a real ExifTool run over a file we write. That run is skipped where ExifTool is not installed._check_time_gap×23, the clock the make fallback reads ×8,_records_gps_time×6,_video_duration×3).DID NOT RAISE, and +18 s.ruff check,ruff format --check,usort diffandmypyare clean.#828's description
#828's description was written for its first commit (25e74ce), and parts of it no longer matched what merged. It has been updated to match 2e00e71: CAMM type 6 is not uniformly GPS time,
unix_to_gps_epoch()andget_gps_epoch_time()were removed, nothing converts on write, and it now lists both warnings.After this PR, CAMM type 6 changes epoch in one place: on read, in
camm_parser, decided by the creation time and then the make. Writes are Unix time for every make.