Skip to content

Commit bb74b3d

Browse files
authored
fix: enforce camera info to be extracted from video itself instead of gpx (#713)
* fix: enforce camera info to be extracted from video itself instead of gpx * refactor * refactor and logging
1 parent 4f0b470 commit bb74b3d

File tree

2 files changed

+61
-34
lines changed

2 files changed

+61
-34
lines changed

mapillary_tools/video_data_extraction/extractors/camm_parser.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ class CammParser(BaseParser):
1313
parser_label = "camm"
1414

1515
@functools.cached_property
16-
def __camera_info(self) -> T.Tuple[str, str]:
17-
with self.videoPath.open("rb") as fp:
16+
def _camera_info(self) -> T.Tuple[str, str]:
17+
source_path = self.geotag_source_path
18+
if not source_path:
19+
return "", ""
20+
21+
with source_path.open("rb") as fp:
1822
return camm_parser.extract_camera_make_and_model(fp)
1923

2024
def extract_points(self) -> T.Sequence[geo.Point]:
@@ -28,15 +32,7 @@ def extract_points(self) -> T.Sequence[geo.Point]:
2832
return []
2933

3034
def extract_make(self) -> T.Optional[str]:
31-
source_path = self.geotag_source_path
32-
if not source_path:
33-
return None
34-
with source_path.open("rb") as _fp:
35-
return self.__camera_info[0] or None
35+
return self._camera_info[0] or None
3636

3737
def extract_model(self) -> T.Optional[str]:
38-
source_path = self.geotag_source_path
39-
if not source_path:
40-
return None
41-
with source_path.open("rb") as _fp:
42-
return self.__camera_info[1] or None
38+
return self._camera_info[1] or None

mapillary_tools/video_data_extraction/extractors/gpx_parser.py

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,40 +38,71 @@ def extract_points(self) -> T.Sequence[geo.Point]:
3838
if not gpx_points:
3939
return gpx_points
4040

41+
offset = self._synx_gpx_by_first_gps_timestamp(gpx_points)
42+
43+
self._rebase_times(gpx_points, offset=offset)
44+
45+
return gpx_points
46+
47+
def _synx_gpx_by_first_gps_timestamp(
48+
self, gpx_points: T.Sequence[geo.Point]
49+
) -> float:
50+
offset: float = 0.0
51+
52+
if not gpx_points:
53+
return offset
54+
4155
first_gpx_dt = datetime.datetime.fromtimestamp(
4256
gpx_points[0].time, tz=datetime.timezone.utc
4357
)
4458
LOG.info("First GPX timestamp: %s", first_gpx_dt)
4559

4660
# Extract first GPS timestamp (if found) for synchronization
47-
offset: float = 0.0
48-
parser = GenericVideoParser(self.videoPath, self.options, self.parserOptions)
61+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
62+
parser = GenericVideoParser(self.videoPath, self.options, {})
4963
gps_points = parser.extract_points()
50-
if gps_points:
51-
first_gps_point = gps_points[0]
52-
if isinstance(first_gps_point, telemetry.GPSPoint):
53-
if first_gps_point.epoch_time is not None:
54-
first_gps_dt = datetime.datetime.fromtimestamp(
55-
first_gps_point.epoch_time, tz=datetime.timezone.utc
56-
)
57-
LOG.info("First GPS timestamp: %s", first_gps_dt)
58-
offset = gpx_points[0].time - first_gps_point.epoch_time
59-
if offset:
60-
LOG.warning(
61-
"Found offset between GPX %s and video GPS timestamps %s: %s seconds",
62-
first_gpx_dt,
63-
first_gps_dt,
64-
offset,
65-
)
6664

67-
self._rebase_times(gpx_points, offset=offset)
65+
if not gps_points:
66+
LOG.warning(
67+
"Skip GPX synchronization because no GPS found in video %s",
68+
self.videoPath,
69+
)
70+
return offset
6871

69-
return gpx_points
72+
first_gps_point = gps_points[0]
73+
if isinstance(first_gps_point, telemetry.GPSPoint):
74+
if first_gps_point.epoch_time is not None:
75+
first_gps_dt = datetime.datetime.fromtimestamp(
76+
first_gps_point.epoch_time, tz=datetime.timezone.utc
77+
)
78+
LOG.info("First GPS timestamp: %s", first_gps_dt)
79+
offset = gpx_points[0].time - first_gps_point.epoch_time
80+
if offset:
81+
LOG.warning(
82+
"Found offset between GPX %s and video GPS timestamps %s: %s seconds",
83+
first_gpx_dt,
84+
first_gps_dt,
85+
offset,
86+
)
87+
else:
88+
LOG.info(
89+
"GPX and GPS are perfectly synchronized (all starts from %s)",
90+
first_gpx_dt,
91+
)
92+
else:
93+
LOG.warning(
94+
"Skip GPX synchronization because no GPS epoch time found in video %s",
95+
self.videoPath,
96+
)
97+
98+
return offset
7099

71100
def extract_make(self) -> T.Optional[str]:
72-
parser = GenericVideoParser(self.videoPath, self.options, self.parserOptions)
101+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
102+
parser = GenericVideoParser(self.videoPath, self.options, {})
73103
return parser.extract_make()
74104

75105
def extract_model(self) -> T.Optional[str]:
76-
parser = GenericVideoParser(self.videoPath, self.options, self.parserOptions)
106+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
107+
parser = GenericVideoParser(self.videoPath, self.options, {})
77108
return parser.extract_model()

0 commit comments

Comments
 (0)