mapillary-tools 0.13.1__tar.gz → 0.13.2__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 (85) hide show
  1. {mapillary_tools-0.13.1/mapillary_tools.egg-info → mapillary_tools-0.13.2}/PKG-INFO +1 -1
  2. mapillary_tools-0.13.2/mapillary_tools/__init__.py +1 -0
  3. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/camm/camm_builder.py +62 -14
  4. mapillary_tools-0.13.2/mapillary_tools/camm/camm_parser.py +342 -0
  5. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/telemetry.py +8 -20
  6. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/upload.py +3 -2
  7. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2/mapillary_tools.egg-info}/PKG-INFO +1 -1
  8. mapillary_tools-0.13.1/mapillary_tools/__init__.py +0 -1
  9. mapillary_tools-0.13.1/mapillary_tools/camm/camm_parser.py +0 -561
  10. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/LICENSE +0 -0
  11. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/MANIFEST.in +0 -0
  12. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/README.md +0 -0
  13. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/api_v4.py +0 -0
  14. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/authenticate.py +0 -0
  15. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/__init__.py +0 -0
  16. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/__main__.py +0 -0
  17. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/authenticate.py +0 -0
  18. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/process.py +0 -0
  19. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/process_and_upload.py +0 -0
  20. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/sample_video.py +0 -0
  21. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/upload.py +0 -0
  22. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/video_process.py +0 -0
  23. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/video_process_and_upload.py +0 -0
  24. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/commands/zip.py +0 -0
  25. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/config.py +0 -0
  26. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/constants.py +0 -0
  27. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/exceptions.py +0 -0
  28. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/exif_read.py +0 -0
  29. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/exif_write.py +0 -0
  30. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/exiftool_read.py +0 -0
  31. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/exiftool_read_video.py +0 -0
  32. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/ffmpeg.py +0 -0
  33. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geo.py +0 -0
  34. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/__init__.py +0 -0
  35. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/blackvue_parser.py +0 -0
  36. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_from_generic.py +0 -0
  37. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_images_from_exif.py +0 -0
  38. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_images_from_exiftool.py +0 -0
  39. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -0
  40. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_images_from_gpx.py +0 -0
  41. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_images_from_gpx_file.py +0 -0
  42. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_images_from_nmea_file.py +0 -0
  43. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_images_from_video.py +0 -0
  44. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -0
  45. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/geotag_videos_from_video.py +0 -0
  46. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/gpmf_gps_filter.py +0 -0
  47. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/gpmf_parser.py +0 -0
  48. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/gps_filter.py +0 -0
  49. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/geotag/utils.py +0 -0
  50. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/history.py +0 -0
  51. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/ipc.py +0 -0
  52. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/mp4/__init__.py +0 -0
  53. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/mp4/construct_mp4_parser.py +0 -0
  54. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/mp4/io_utils.py +0 -0
  55. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/mp4/mp4_sample_parser.py +0 -0
  56. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/mp4/simple_mp4_builder.py +0 -0
  57. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/mp4/simple_mp4_parser.py +0 -0
  58. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/process_geotag_properties.py +0 -0
  59. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/process_sequence_properties.py +0 -0
  60. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/sample_video.py +0 -0
  61. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/types.py +0 -0
  62. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/upload_api_v4.py +0 -0
  63. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/uploader.py +0 -0
  64. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/utils.py +0 -0
  65. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/cli_options.py +0 -0
  66. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extract_video_data.py +0 -0
  67. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -0
  68. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -0
  69. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/camm_parser.py +0 -0
  70. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +0 -0
  71. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -0
  72. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -0
  73. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -0
  74. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/gpx_parser.py +0 -0
  75. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/extractors/nmea_parser.py +0 -0
  76. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools/video_data_extraction/video_data_parser_factory.py +0 -0
  77. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools.egg-info/SOURCES.txt +0 -0
  78. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools.egg-info/dependency_links.txt +0 -0
  79. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools.egg-info/entry_points.txt +0 -0
  80. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools.egg-info/requires.txt +0 -0
  81. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/mapillary_tools.egg-info/top_level.txt +0 -0
  82. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/requirements.txt +0 -0
  83. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/schema/image_description_schema.json +0 -0
  84. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/setup.cfg +0 -0
  85. {mapillary_tools-0.13.1 → mapillary_tools-0.13.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapillary_tools
3
- Version: 0.13.1
3
+ Version: 0.13.2
4
4
  Summary: Mapillary Image/Video Import Pipeline
5
5
  Home-page: https://github.com/mapillary/mapillary_tools
6
6
  Author: Mapillary
@@ -0,0 +1 @@
1
+ VERSION = "0.13.2"
@@ -1,7 +1,7 @@
1
1
  import io
2
2
  import typing as T
3
3
 
4
- from .. import geo, types
4
+ from .. import geo, telemetry, types
5
5
  from ..mp4 import (
6
6
  construct_mp4_parser as cparser,
7
7
  mp4_sample_parser as sample_parser,
@@ -11,11 +11,62 @@ from ..mp4 import (
11
11
  from . import camm_parser
12
12
 
13
13
 
14
- def _build_camm_sample(measurement: camm_parser.TelemetryMeasurement) -> bytes:
15
- for sample_entry_cls in camm_parser.SAMPLE_ENTRY_CLS_BY_CAMM_TYPE.values():
16
- if sample_entry_cls.serializable(measurement):
17
- return sample_entry_cls.serialize(measurement)
18
- raise ValueError(f"Unsupported measurement type {type(measurement)}")
14
+ TelemetryMeasurement = T.Union[
15
+ geo.Point,
16
+ telemetry.TelemetryMeasurement,
17
+ ]
18
+
19
+
20
+ def _build_camm_sample(measurement: TelemetryMeasurement) -> bytes:
21
+ if isinstance(measurement, geo.Point):
22
+ return camm_parser.CAMMSampleData.build(
23
+ {
24
+ "type": camm_parser.CAMMType.MIN_GPS.value,
25
+ "data": [
26
+ measurement.lat,
27
+ measurement.lon,
28
+ -1.0 if measurement.alt is None else measurement.alt,
29
+ ],
30
+ }
31
+ )
32
+ elif isinstance(measurement, telemetry.AccelerationData):
33
+ # Accelerometer reading in meters/second^2 along XYZ axes of the camera.
34
+ return camm_parser.CAMMSampleData.build(
35
+ {
36
+ "type": camm_parser.CAMMType.ACCELERATION.value,
37
+ "data": [
38
+ measurement.x,
39
+ measurement.y,
40
+ measurement.z,
41
+ ],
42
+ }
43
+ )
44
+ elif isinstance(measurement, telemetry.GyroscopeData):
45
+ # Gyroscope signal in radians/seconds around XYZ axes of the camera. Rotation is positive in the counterclockwise direction.
46
+ return camm_parser.CAMMSampleData.build(
47
+ {
48
+ "type": camm_parser.CAMMType.GYRO.value,
49
+ "data": [
50
+ measurement.x,
51
+ measurement.y,
52
+ measurement.z,
53
+ ],
54
+ }
55
+ )
56
+ elif isinstance(measurement, telemetry.MagnetometerData):
57
+ # Ambient magnetic field.
58
+ return camm_parser.CAMMSampleData.build(
59
+ {
60
+ "type": camm_parser.CAMMType.MAGNETIC_FIELD.value,
61
+ "data": [
62
+ measurement.x,
63
+ measurement.y,
64
+ measurement.z,
65
+ ],
66
+ }
67
+ )
68
+ else:
69
+ raise ValueError(f"unexpected measurement type {type(measurement)}")
19
70
 
20
71
 
21
72
  def _create_edit_list_from_points(
@@ -70,19 +121,16 @@ def _create_edit_list_from_points(
70
121
 
71
122
  def _multiplex(
72
123
  points: T.Sequence[geo.Point],
73
- measurements: T.Optional[T.List[camm_parser.TelemetryMeasurement]] = None,
74
- ) -> T.List[camm_parser.TelemetryMeasurement]:
75
- mutiplexed: T.List[camm_parser.TelemetryMeasurement] = [
76
- *points,
77
- *(measurements or []),
78
- ]
124
+ measurements: T.Optional[T.List[telemetry.TelemetryMeasurement]] = None,
125
+ ) -> T.List[TelemetryMeasurement]:
126
+ mutiplexed: T.List[TelemetryMeasurement] = [*points, *(measurements or [])]
79
127
  mutiplexed.sort(key=lambda m: m.time)
80
128
 
81
129
  return mutiplexed
82
130
 
83
131
 
84
132
  def convert_telemetry_to_raw_samples(
85
- measurements: T.Sequence[camm_parser.TelemetryMeasurement],
133
+ measurements: T.Sequence[TelemetryMeasurement],
86
134
  timescale: int,
87
135
  ) -> T.Generator[sample_parser.RawSample, None, None]:
88
136
  for idx, measurement in enumerate(measurements):
@@ -235,7 +283,7 @@ def create_camm_trak(
235
283
 
236
284
  def camm_sample_generator2(
237
285
  video_metadata: types.VideoMetadata,
238
- telemetry_measurements: T.Optional[T.List[camm_parser.TelemetryMeasurement]] = None,
286
+ telemetry_measurements: T.Optional[T.List[telemetry.TelemetryMeasurement]] = None,
239
287
  ):
240
288
  def _f(
241
289
  fp: T.BinaryIO,
@@ -0,0 +1,342 @@
1
+ # pyre-ignore-all-errors[5, 11, 16, 21, 24, 58]
2
+
3
+ import dataclasses
4
+ import io
5
+ import logging
6
+ import pathlib
7
+ import typing as T
8
+ from enum import Enum
9
+
10
+ import construct as C
11
+
12
+ from .. import geo, telemetry
13
+ from ..mp4 import simple_mp4_parser as sparser
14
+ from ..mp4.mp4_sample_parser import MovieBoxParser, Sample, TrackBoxParser
15
+
16
+
17
+ LOG = logging.getLogger(__name__)
18
+
19
+
20
+ TelemetryMeasurement = T.Union[
21
+ geo.Point,
22
+ telemetry.AccelerationData,
23
+ telemetry.GyroscopeData,
24
+ telemetry.MagnetometerData,
25
+ ]
26
+
27
+
28
+ # Camera Motion Metadata Spec https://developers.google.com/streetview/publish/camm-spec
29
+ class CAMMType(Enum):
30
+ ANGLE_AXIS = 0
31
+ EXPOSURE_TIME = 1
32
+ GYRO = 2
33
+ ACCELERATION = 3
34
+ POSITION = 4
35
+ MIN_GPS = 5
36
+ GPS = 6
37
+ MAGNETIC_FIELD = 7
38
+
39
+
40
+ # All fields are little-endian
41
+ Float = C.Float32l
42
+ Double = C.Float64l
43
+
44
+ _SWITCH: T.Dict[int, C.Struct] = {
45
+ # angle_axis
46
+ CAMMType.ANGLE_AXIS.value: Float[3],
47
+ CAMMType.EXPOSURE_TIME.value: C.Struct(
48
+ "pixel_exposure_time" / C.Int32sl,
49
+ "rolling_shutter_skew_time" / C.Int32sl,
50
+ ),
51
+ # gyro
52
+ CAMMType.GYRO.value: Float[3],
53
+ # acceleration
54
+ CAMMType.ACCELERATION.value: Float[3],
55
+ # position
56
+ CAMMType.POSITION.value: Float[3],
57
+ # lat, lon, alt
58
+ CAMMType.MIN_GPS.value: Double[3],
59
+ CAMMType.GPS.value: C.Struct(
60
+ "time_gps_epoch" / Double,
61
+ "gps_fix_type" / C.Int32sl,
62
+ "latitude" / Double,
63
+ "longitude" / Double,
64
+ "altitude" / Float,
65
+ "horizontal_accuracy" / Float,
66
+ "vertical_accuracy" / Float,
67
+ "velocity_east" / Float,
68
+ "velocity_north" / Float,
69
+ "velocity_up" / Float,
70
+ "speed_accuracy" / Float,
71
+ ),
72
+ # magnetic_field
73
+ CAMMType.MAGNETIC_FIELD.value: Float[3],
74
+ }
75
+
76
+ CAMMSampleData = C.Struct(
77
+ C.Padding(2),
78
+ "type" / C.Int16ul,
79
+ "data"
80
+ / C.Switch(
81
+ C.this.type,
82
+ _SWITCH,
83
+ ),
84
+ )
85
+
86
+
87
+ def _parse_telemetry_from_sample(
88
+ fp: T.BinaryIO, sample: Sample
89
+ ) -> T.Optional[TelemetryMeasurement]:
90
+ fp.seek(sample.raw_sample.offset, io.SEEK_SET)
91
+ data = fp.read(sample.raw_sample.size)
92
+ box = CAMMSampleData.parse(data)
93
+ if box.type == CAMMType.MIN_GPS.value:
94
+ return geo.Point(
95
+ time=sample.exact_time,
96
+ lat=box.data[0],
97
+ lon=box.data[1],
98
+ alt=box.data[2],
99
+ angle=None,
100
+ )
101
+ elif box.type == CAMMType.GPS.value:
102
+ # Not using box.data.time_gps_epoch as the point timestamp
103
+ # because it is from another clock
104
+ return geo.Point(
105
+ time=sample.exact_time,
106
+ lat=box.data.latitude,
107
+ lon=box.data.longitude,
108
+ alt=box.data.altitude,
109
+ angle=None,
110
+ )
111
+ elif box.type == CAMMType.ACCELERATION.value:
112
+ return telemetry.AccelerationData(
113
+ time=sample.exact_time,
114
+ x=box.data[0],
115
+ y=box.data[1],
116
+ z=box.data[2],
117
+ )
118
+ elif box.type == CAMMType.GYRO.value:
119
+ return telemetry.GyroscopeData(
120
+ time=sample.exact_time,
121
+ x=box.data[0],
122
+ y=box.data[1],
123
+ z=box.data[2],
124
+ )
125
+ elif box.type == CAMMType.MAGNETIC_FIELD.value:
126
+ return telemetry.MagnetometerData(
127
+ time=sample.exact_time,
128
+ x=box.data[0],
129
+ y=box.data[1],
130
+ z=box.data[2],
131
+ )
132
+ return None
133
+
134
+
135
+ def _filter_telemetry_by_elst_segments(
136
+ measurements: T.Iterable[TelemetryMeasurement],
137
+ elst: T.Sequence[T.Tuple[float, float]],
138
+ ) -> T.Generator[TelemetryMeasurement, None, None]:
139
+ empty_elst = [entry for entry in elst if entry[0] == -1]
140
+ if empty_elst:
141
+ offset = empty_elst[-1][1]
142
+ else:
143
+ offset = 0
144
+
145
+ elst = [entry for entry in elst if entry[0] != -1]
146
+
147
+ if not elst:
148
+ for m in measurements:
149
+ yield dataclasses.replace(m, time=m.time + offset)
150
+ return
151
+
152
+ elst.sort(key=lambda entry: entry[0])
153
+ elst_idx = 0
154
+ for m in measurements:
155
+ if len(elst) <= elst_idx:
156
+ break
157
+ media_time, duration = elst[elst_idx]
158
+ if m.time < media_time:
159
+ pass
160
+ elif m.time <= media_time + duration:
161
+ yield dataclasses.replace(m, time=m.time + offset)
162
+ else:
163
+ elst_idx += 1
164
+
165
+
166
+ def elst_entry_to_seconds(
167
+ entry: T.Dict, movie_timescale: int, media_timescale: int
168
+ ) -> T.Tuple[float, float]:
169
+ assert movie_timescale > 0, "expected positive movie_timescale"
170
+ assert media_timescale > 0, "expected positive media_timescale"
171
+ media_time, duration = entry["media_time"], entry["segment_duration"]
172
+ if media_time != -1:
173
+ media_time = media_time / media_timescale
174
+ duration = duration / movie_timescale
175
+ return (media_time, duration)
176
+
177
+
178
+ def _is_camm_description(description: T.Dict) -> bool:
179
+ return description["format"] == b"camm"
180
+
181
+
182
+ def _contains_camm_description(track: TrackBoxParser) -> bool:
183
+ descriptions = track.extract_sample_descriptions()
184
+ return any(_is_camm_description(d) for d in descriptions)
185
+
186
+
187
+ def _filter_telemetry_by_track_elst(
188
+ moov: MovieBoxParser,
189
+ track: TrackBoxParser,
190
+ measurements: T.Iterable[TelemetryMeasurement],
191
+ ) -> T.List[TelemetryMeasurement]:
192
+ elst_boxdata = track.extract_elst_boxdata()
193
+
194
+ if elst_boxdata is not None:
195
+ elst_entries = elst_boxdata["entries"]
196
+ if elst_entries:
197
+ # media_timescale
198
+ mdhd_boxdata = track.extract_mdhd_boxdata()
199
+ media_timescale = mdhd_boxdata["timescale"]
200
+
201
+ # movie_timescale
202
+ mvhd_boxdata = moov.extract_mvhd_boxdata()
203
+ movie_timescale = mvhd_boxdata["timescale"]
204
+
205
+ segments = [
206
+ elst_entry_to_seconds(
207
+ entry,
208
+ movie_timescale=movie_timescale,
209
+ media_timescale=media_timescale,
210
+ )
211
+ for entry in elst_entries
212
+ ]
213
+
214
+ return list(_filter_telemetry_by_elst_segments(measurements, segments))
215
+
216
+ return list(measurements)
217
+
218
+
219
+ def extract_points(fp: T.BinaryIO) -> T.Optional[T.List[geo.Point]]:
220
+ """
221
+ Return a list of points (could be empty) if it is a valid CAMM video,
222
+ otherwise None
223
+ """
224
+
225
+ moov = MovieBoxParser.parse_stream(fp)
226
+
227
+ for track in moov.extract_tracks():
228
+ if _contains_camm_description(track):
229
+ maybe_measurements = (
230
+ _parse_telemetry_from_sample(fp, sample)
231
+ for sample in track.extract_samples()
232
+ if _is_camm_description(sample.description)
233
+ )
234
+ points = [m for m in maybe_measurements if isinstance(m, geo.Point)]
235
+
236
+ return T.cast(
237
+ T.List[geo.Point], _filter_telemetry_by_track_elst(moov, track, points)
238
+ )
239
+
240
+ return None
241
+
242
+
243
+ def extract_telemetry_data(fp: T.BinaryIO) -> T.Optional[T.List[TelemetryMeasurement]]:
244
+ moov = MovieBoxParser.parse_stream(fp)
245
+
246
+ for track in moov.extract_tracks():
247
+ if _contains_camm_description(track):
248
+ maybe_measurements = (
249
+ _parse_telemetry_from_sample(fp, sample)
250
+ for sample in track.extract_samples()
251
+ if _is_camm_description(sample.description)
252
+ )
253
+ measurements = [m for m in maybe_measurements if m is not None]
254
+
255
+ measurements = _filter_telemetry_by_track_elst(moov, track, measurements)
256
+
257
+ return measurements
258
+
259
+ return None
260
+
261
+
262
+ def parse_gpx(path: pathlib.Path) -> T.List[geo.Point]:
263
+ with path.open("rb") as fp:
264
+ points = extract_points(fp)
265
+ if points is None:
266
+ return []
267
+ return points
268
+
269
+
270
+ MakeOrModel = C.Struct(
271
+ "size" / C.Int16ub,
272
+ C.Padding(2),
273
+ "data" / C.FixedSized(C.this.size, C.GreedyBytes),
274
+ )
275
+
276
+
277
+ def _decode_quietly(data: bytes, h: sparser.Header) -> str:
278
+ try:
279
+ return data.decode("utf-8")
280
+ except UnicodeDecodeError:
281
+ LOG.warning("Failed to decode %s: %s", h, data[:512])
282
+ return ""
283
+
284
+
285
+ def _parse_quietly(data: bytes, h: sparser.Header) -> bytes:
286
+ try:
287
+ parsed = MakeOrModel.parse(data)
288
+ except C.ConstructError:
289
+ LOG.warning("Failed to parse %s: %s", h, data[:512])
290
+ return b""
291
+ return parsed["data"]
292
+
293
+
294
+ def extract_camera_make_and_model(fp: T.BinaryIO) -> T.Tuple[str, str]:
295
+ header_and_stream = sparser.parse_path(
296
+ fp,
297
+ [
298
+ b"moov",
299
+ b"udta",
300
+ [
301
+ # Insta360 Titan
302
+ b"\xa9mak",
303
+ b"\xa9mod",
304
+ # RICHO THETA V
305
+ b"@mod",
306
+ b"@mak",
307
+ # RICHO THETA V
308
+ b"manu",
309
+ b"modl",
310
+ ],
311
+ ],
312
+ )
313
+
314
+ make: T.Optional[str] = None
315
+ model: T.Optional[str] = None
316
+
317
+ try:
318
+ for h, s in header_and_stream:
319
+ data = s.read(h.maxsize)
320
+ if h.type == b"\xa9mak":
321
+ make_data = _parse_quietly(data, h)
322
+ make_data = make_data.rstrip(b"\x00")
323
+ make = _decode_quietly(make_data, h)
324
+ elif h.type == b"\xa9mod":
325
+ model_data = _parse_quietly(data, h)
326
+ model_data = model_data.rstrip(b"\x00")
327
+ model = _decode_quietly(model_data, h)
328
+ elif h.type in [b"@mak", b"manu"]:
329
+ make = _decode_quietly(data, h)
330
+ elif h.type in [b"@mod", b"modl"]:
331
+ model = _decode_quietly(data, h)
332
+ # quit when both found
333
+ if make and model:
334
+ break
335
+ except sparser.ParsingError:
336
+ pass
337
+
338
+ if make:
339
+ make = make.strip()
340
+ if model:
341
+ model = model.strip()
342
+ return make or "", model or ""
@@ -12,6 +12,14 @@ class GPSFix(Enum):
12
12
  FIX_3D = 3
13
13
 
14
14
 
15
+ @dataclasses.dataclass
16
+ class GPSPoint(Point):
17
+ epoch_time: T.Optional[float]
18
+ fix: T.Optional[GPSFix]
19
+ precision: T.Optional[float]
20
+ ground_speed: T.Optional[float]
21
+
22
+
15
23
  @dataclasses.dataclass(order=True)
16
24
  class TelemetryMeasurement:
17
25
  """Base class for all telemetry measurements.
@@ -24,26 +32,6 @@ class TelemetryMeasurement:
24
32
  time: float
25
33
 
26
34
 
27
- @dataclasses.dataclass
28
- class GPSPoint(TelemetryMeasurement, Point):
29
- epoch_time: T.Optional[float]
30
- fix: T.Optional[GPSFix]
31
- precision: T.Optional[float]
32
- ground_speed: T.Optional[float]
33
-
34
-
35
- @dataclasses.dataclass
36
- class CAMMGPSPoint(TelemetryMeasurement, Point):
37
- time_gps_epoch: float
38
- gps_fix_type: int
39
- horizontal_accuracy: float
40
- vertical_accuracy: float
41
- velocity_east: float
42
- velocity_north: float
43
- velocity_up: float
44
- speed_accuracy: float
45
-
46
-
47
35
  @dataclasses.dataclass(order=True)
48
36
  class GyroscopeData(TelemetryMeasurement):
49
37
  """Gyroscope signal in radians/seconds around XYZ axes of the camera."""
@@ -18,13 +18,14 @@ from . import (
18
18
  exceptions,
19
19
  history,
20
20
  ipc,
21
+ telemetry,
21
22
  types,
22
23
  upload_api_v4,
23
24
  uploader,
24
25
  utils,
25
26
  VERSION,
26
27
  )
27
- from .camm import camm_builder, camm_parser
28
+ from .camm import camm_builder
28
29
  from .geotag import gpmf_parser
29
30
  from .mp4 import simple_mp4_builder
30
31
  from .types import FileType
@@ -615,7 +616,7 @@ def upload(
615
616
  assert isinstance(video_metadata.md5sum, str), "md5sum should be updated"
616
617
 
617
618
  # extract telemetry measurements from GoPro videos
618
- telemetry_measurements: T.List[camm_parser.TelemetryMeasurement] = []
619
+ telemetry_measurements: T.List[telemetry.TelemetryMeasurement] = []
619
620
  if MAPILLARY__EXPERIMENTAL_ENABLE_IMU == "YES":
620
621
  if video_metadata.filetype is FileType.GOPRO:
621
622
  with video_metadata.filename.open("rb") as fp:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapillary_tools
3
- Version: 0.13.1
3
+ Version: 0.13.2
4
4
  Summary: Mapillary Image/Video Import Pipeline
5
5
  Home-page: https://github.com/mapillary/mapillary_tools
6
6
  Author: Mapillary
@@ -1 +0,0 @@
1
- VERSION = "0.13.1"