mapillary-tools 0.12.1__py3-none-any.whl → 0.13.1__py3-none-any.whl
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.
- mapillary_tools/__init__.py +1 -1
- mapillary_tools/api_v4.py +94 -4
- mapillary_tools/{geotag → camm}/camm_builder.py +73 -61
- mapillary_tools/camm/camm_parser.py +561 -0
- mapillary_tools/commands/__init__.py +0 -1
- mapillary_tools/commands/__main__.py +0 -6
- mapillary_tools/commands/process.py +0 -50
- mapillary_tools/commands/upload.py +1 -26
- mapillary_tools/constants.py +2 -2
- mapillary_tools/exiftool_read_video.py +13 -11
- mapillary_tools/ffmpeg.py +2 -2
- mapillary_tools/geo.py +0 -54
- mapillary_tools/geotag/blackvue_parser.py +4 -4
- mapillary_tools/geotag/geotag_images_from_exif.py +2 -1
- mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -1
- mapillary_tools/geotag/geotag_images_from_gpx_file.py +7 -1
- mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +5 -3
- mapillary_tools/geotag/geotag_videos_from_video.py +13 -14
- mapillary_tools/geotag/gpmf_gps_filter.py +9 -10
- mapillary_tools/geotag/gpmf_parser.py +346 -83
- mapillary_tools/mp4/__init__.py +0 -0
- mapillary_tools/{geotag → mp4}/construct_mp4_parser.py +32 -16
- mapillary_tools/mp4/mp4_sample_parser.py +322 -0
- mapillary_tools/{geotag → mp4}/simple_mp4_builder.py +64 -38
- mapillary_tools/process_geotag_properties.py +25 -19
- mapillary_tools/process_sequence_properties.py +6 -6
- mapillary_tools/sample_video.py +17 -16
- mapillary_tools/telemetry.py +71 -0
- mapillary_tools/types.py +18 -0
- mapillary_tools/upload.py +74 -233
- mapillary_tools/upload_api_v4.py +8 -9
- mapillary_tools/utils.py +9 -16
- mapillary_tools/video_data_extraction/cli_options.py +0 -1
- mapillary_tools/video_data_extraction/extract_video_data.py +13 -31
- mapillary_tools/video_data_extraction/extractors/base_parser.py +13 -11
- mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +5 -4
- mapillary_tools/video_data_extraction/extractors/camm_parser.py +13 -16
- mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +4 -9
- mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +9 -11
- mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +6 -11
- mapillary_tools/video_data_extraction/extractors/gopro_parser.py +11 -4
- mapillary_tools/video_data_extraction/extractors/gpx_parser.py +90 -11
- mapillary_tools/video_data_extraction/extractors/nmea_parser.py +3 -3
- mapillary_tools/video_data_extraction/video_data_parser_factory.py +13 -20
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/METADATA +10 -3
- mapillary_tools-0.13.1.dist-info/RECORD +75 -0
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/WHEEL +1 -1
- mapillary_tools/commands/upload_blackvue.py +0 -33
- mapillary_tools/commands/upload_camm.py +0 -33
- mapillary_tools/commands/upload_zip.py +0 -33
- mapillary_tools/geotag/camm_parser.py +0 -306
- mapillary_tools/geotag/mp4_sample_parser.py +0 -426
- mapillary_tools/process_import_meta_properties.py +0 -76
- mapillary_tools-0.12.1.dist-info/RECORD +0 -77
- /mapillary_tools/{geotag → mp4}/io_utils.py +0 -0
- /mapillary_tools/{geotag → mp4}/simple_mp4_parser.py +0 -0
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/LICENSE +0 -0
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/top_level.txt +0 -0
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import io
|
|
3
|
-
import typing as T
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
from . import construct_mp4_parser as cparser, simple_mp4_parser as parser
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class RawSample(T.NamedTuple):
|
|
10
|
-
# 1-based index
|
|
11
|
-
description_idx: int
|
|
12
|
-
# sample offset
|
|
13
|
-
offset: int
|
|
14
|
-
# sample size
|
|
15
|
-
size: int
|
|
16
|
-
# sample_delta read from stts entries,
|
|
17
|
-
# i.e. STTS(n) in the forumula DT(n+1) = DT(n) + STTS(n)
|
|
18
|
-
timedelta: int
|
|
19
|
-
# sample composition offset,
|
|
20
|
-
# i.e. CTTS(n) in the forumula CT(n) = DT(n) + CTTS(n).
|
|
21
|
-
composition_offset: int
|
|
22
|
-
# if it is a sync sample
|
|
23
|
-
is_sync: bool
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# TODO: can not inherit RawSample?
|
|
27
|
-
class Sample(T.NamedTuple):
|
|
28
|
-
# copied from RawSample
|
|
29
|
-
|
|
30
|
-
# 1-based index
|
|
31
|
-
description_idx: int
|
|
32
|
-
# sample offset
|
|
33
|
-
offset: int
|
|
34
|
-
# sample size
|
|
35
|
-
size: int
|
|
36
|
-
# sample delta in seconds read from stts entries,
|
|
37
|
-
# i.e. (STTS(n) / timescale) in the forumula DT(n+1) = DT(n) + STTS(n)
|
|
38
|
-
timedelta: float
|
|
39
|
-
# sample composition offset in seconds,
|
|
40
|
-
# i.e. (CTTS(n) / timescale) in the forumula CT(n) = DT(n) + CTTS(n).
|
|
41
|
-
composition_offset: float
|
|
42
|
-
# if it is a sync sample
|
|
43
|
-
is_sync: bool
|
|
44
|
-
|
|
45
|
-
# extended fields below
|
|
46
|
-
|
|
47
|
-
# accumulated sample_delta in seconds,
|
|
48
|
-
# i.e. (DT(n) / timescale) in the forumula DT(n+1) = DT(n) + STTS(n)
|
|
49
|
-
time_offset: T.Union[int, float]
|
|
50
|
-
# accumulated composition offset in seconds,
|
|
51
|
-
# i.e. (CT(n) / timescale) in the forumula CT(n) = DT(n) + CTTS(n).
|
|
52
|
-
composition_time_offset: T.Union[int, float]
|
|
53
|
-
# reference to the sample description
|
|
54
|
-
description: T.Dict
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def _extract_raw_samples(
|
|
58
|
-
sizes: T.Sequence[int],
|
|
59
|
-
chunk_entries: T.Sequence[T.Dict],
|
|
60
|
-
chunk_offsets: T.Sequence[int],
|
|
61
|
-
timedeltas: T.Sequence[int],
|
|
62
|
-
composition_offsets: T.Optional[T.Sequence[int]],
|
|
63
|
-
syncs: T.Optional[T.Set[int]],
|
|
64
|
-
) -> T.Generator[RawSample, None, None]:
|
|
65
|
-
if not sizes:
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
if not chunk_entries:
|
|
69
|
-
return
|
|
70
|
-
|
|
71
|
-
assert len(sizes) <= len(
|
|
72
|
-
timedeltas
|
|
73
|
-
), f"got less ({len(timedeltas)}) sample time deltas (stts) than expected ({len(sizes)})"
|
|
74
|
-
|
|
75
|
-
sample_idx = 0
|
|
76
|
-
chunk_idx = 0
|
|
77
|
-
|
|
78
|
-
# iterate compressed chunks
|
|
79
|
-
for entry_idx, entry in enumerate(chunk_entries):
|
|
80
|
-
if entry_idx + 1 < len(chunk_entries):
|
|
81
|
-
nbr_chunks = (
|
|
82
|
-
chunk_entries[entry_idx + 1]["first_chunk"] - entry["first_chunk"]
|
|
83
|
-
)
|
|
84
|
-
else:
|
|
85
|
-
nbr_chunks = 1
|
|
86
|
-
|
|
87
|
-
# iterate chunks
|
|
88
|
-
for _ in range(nbr_chunks):
|
|
89
|
-
sample_offset = chunk_offsets[chunk_idx]
|
|
90
|
-
# iterate samples in this chunk
|
|
91
|
-
for _ in range(entry["samples_per_chunk"]):
|
|
92
|
-
is_sync = syncs is None or (sample_idx + 1) in syncs
|
|
93
|
-
composition_offset = (
|
|
94
|
-
composition_offsets[sample_idx]
|
|
95
|
-
if composition_offsets is not None
|
|
96
|
-
else 0
|
|
97
|
-
)
|
|
98
|
-
yield RawSample(
|
|
99
|
-
description_idx=entry["sample_description_index"],
|
|
100
|
-
offset=sample_offset,
|
|
101
|
-
size=sizes[sample_idx],
|
|
102
|
-
timedelta=timedeltas[sample_idx],
|
|
103
|
-
composition_offset=composition_offset,
|
|
104
|
-
is_sync=is_sync,
|
|
105
|
-
)
|
|
106
|
-
sample_offset += sizes[sample_idx]
|
|
107
|
-
sample_idx += 1
|
|
108
|
-
chunk_idx += 1
|
|
109
|
-
|
|
110
|
-
# below handles the single-entry case:
|
|
111
|
-
# If all the chunks have the same number of samples per chunk
|
|
112
|
-
# and use the same sample description, this table has one entry.
|
|
113
|
-
|
|
114
|
-
# iterate chunks
|
|
115
|
-
while sample_idx < len(sizes):
|
|
116
|
-
sample_offset = chunk_offsets[chunk_idx]
|
|
117
|
-
# iterate samples in this chunk
|
|
118
|
-
for _ in range(chunk_entries[-1]["samples_per_chunk"]):
|
|
119
|
-
is_sync = syncs is None or (sample_idx + 1) in syncs
|
|
120
|
-
composition_offset = (
|
|
121
|
-
composition_offsets[sample_idx]
|
|
122
|
-
if composition_offsets is not None
|
|
123
|
-
else 0
|
|
124
|
-
)
|
|
125
|
-
yield RawSample(
|
|
126
|
-
description_idx=chunk_entries[-1]["sample_description_index"],
|
|
127
|
-
offset=sample_offset,
|
|
128
|
-
size=sizes[sample_idx],
|
|
129
|
-
timedelta=timedeltas[sample_idx],
|
|
130
|
-
composition_offset=composition_offset,
|
|
131
|
-
is_sync=is_sync,
|
|
132
|
-
)
|
|
133
|
-
sample_offset += sizes[sample_idx]
|
|
134
|
-
sample_idx += 1
|
|
135
|
-
chunk_idx += 1
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def _extract_samples(
|
|
139
|
-
raw_samples: T.Iterator[RawSample],
|
|
140
|
-
descriptions: T.List,
|
|
141
|
-
) -> T.Generator[Sample, None, None]:
|
|
142
|
-
acc_delta = 0
|
|
143
|
-
for raw_sample in raw_samples:
|
|
144
|
-
yield Sample(
|
|
145
|
-
description_idx=raw_sample.description_idx,
|
|
146
|
-
offset=raw_sample.offset,
|
|
147
|
-
size=raw_sample.size,
|
|
148
|
-
timedelta=raw_sample.timedelta,
|
|
149
|
-
composition_offset=raw_sample.composition_offset,
|
|
150
|
-
is_sync=raw_sample.is_sync,
|
|
151
|
-
description=descriptions[raw_sample.description_idx - 1],
|
|
152
|
-
time_offset=acc_delta,
|
|
153
|
-
# CT(n) = DT(n) + CTTS(n)
|
|
154
|
-
composition_time_offset=(acc_delta + raw_sample.composition_offset),
|
|
155
|
-
)
|
|
156
|
-
acc_delta += raw_sample.timedelta
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _apply_timescale(sample: Sample, media_timescale: int) -> Sample:
|
|
160
|
-
return Sample(
|
|
161
|
-
description_idx=sample.description_idx,
|
|
162
|
-
offset=sample.offset,
|
|
163
|
-
size=sample.size,
|
|
164
|
-
timedelta=sample.timedelta / media_timescale,
|
|
165
|
-
composition_offset=sample.composition_offset / media_timescale,
|
|
166
|
-
is_sync=sample.is_sync,
|
|
167
|
-
description=sample.description,
|
|
168
|
-
time_offset=sample.time_offset / media_timescale,
|
|
169
|
-
composition_time_offset=sample.composition_time_offset / media_timescale,
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def parse_raw_samples_from_stbl(
|
|
174
|
-
stbl: T.BinaryIO,
|
|
175
|
-
maxsize: int = -1,
|
|
176
|
-
) -> T.Tuple[T.List[T.Dict], T.Generator[RawSample, None, None]]:
|
|
177
|
-
"""
|
|
178
|
-
DEPRECATED: use parse_raw_samples_from_stbl_bytes instead
|
|
179
|
-
"""
|
|
180
|
-
|
|
181
|
-
descriptions = []
|
|
182
|
-
sizes = []
|
|
183
|
-
chunk_offsets = []
|
|
184
|
-
chunk_entries = []
|
|
185
|
-
timedeltas: T.List[int] = []
|
|
186
|
-
composition_offsets: T.Optional[T.List[int]] = None
|
|
187
|
-
syncs: T.Optional[T.Set[int]] = None
|
|
188
|
-
|
|
189
|
-
for h, s in parser.parse_boxes(stbl, maxsize=maxsize, extend_eof=False):
|
|
190
|
-
if h.type == b"stsd":
|
|
191
|
-
box = cparser.SampleDescriptionBox.parse(s.read(h.maxsize))
|
|
192
|
-
descriptions = list(box.entries)
|
|
193
|
-
elif h.type == b"stsz":
|
|
194
|
-
box = cparser.SampleSizeBox.parse(s.read(h.maxsize))
|
|
195
|
-
if box.sample_size == 0:
|
|
196
|
-
sizes = list(box.entries)
|
|
197
|
-
else:
|
|
198
|
-
sizes = [box.sample_size for _ in range(box.sample_count)]
|
|
199
|
-
elif h.type == b"stco":
|
|
200
|
-
box = cparser.ChunkOffsetBox.parse(s.read(h.maxsize))
|
|
201
|
-
chunk_offsets = list(box.entries)
|
|
202
|
-
elif h.type == b"co64":
|
|
203
|
-
box = cparser.ChunkLargeOffsetBox.parse(s.read(h.maxsize))
|
|
204
|
-
chunk_offsets = list(box.entries)
|
|
205
|
-
elif h.type == b"stsc":
|
|
206
|
-
box = cparser.SampleToChunkBox.parse(s.read(h.maxsize))
|
|
207
|
-
chunk_entries = list(box.entries)
|
|
208
|
-
elif h.type == b"stts":
|
|
209
|
-
timedeltas = []
|
|
210
|
-
box = cparser.TimeToSampleBox.parse(s.read(h.maxsize))
|
|
211
|
-
for entry in box.entries:
|
|
212
|
-
for _ in range(entry.sample_count):
|
|
213
|
-
timedeltas.append(entry.sample_delta)
|
|
214
|
-
elif h.type == b"ctts":
|
|
215
|
-
composition_offsets = []
|
|
216
|
-
box = cparser.CompositionTimeToSampleBox.parse(s.read(h.maxsize))
|
|
217
|
-
for entry in box.entries:
|
|
218
|
-
for _ in range(entry.sample_count):
|
|
219
|
-
composition_offsets.append(entry.sample_offset)
|
|
220
|
-
elif h.type == b"stss":
|
|
221
|
-
box = cparser.SyncSampleBox.parse(s.read(h.maxsize))
|
|
222
|
-
syncs = set(box.entries)
|
|
223
|
-
|
|
224
|
-
# some stbl have less timedeltas than the sample count i.e. len(sizes),
|
|
225
|
-
# in this case append 0's to timedeltas
|
|
226
|
-
while len(timedeltas) < len(sizes):
|
|
227
|
-
timedeltas.append(0)
|
|
228
|
-
if composition_offsets is not None:
|
|
229
|
-
while len(composition_offsets) < len(sizes):
|
|
230
|
-
composition_offsets.append(0)
|
|
231
|
-
|
|
232
|
-
raw_samples = _extract_raw_samples(
|
|
233
|
-
sizes, chunk_entries, chunk_offsets, timedeltas, composition_offsets, syncs
|
|
234
|
-
)
|
|
235
|
-
return descriptions, raw_samples
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
STBLBoxlistConstruct = cparser.Box64ConstructBuilder(
|
|
239
|
-
T.cast(cparser.SwitchMapType, cparser.CMAP[b"stbl"])
|
|
240
|
-
).BoxList
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
def parse_raw_samples_from_stbl_bytes(
|
|
244
|
-
stbl: bytes,
|
|
245
|
-
) -> T.Tuple[T.List[T.Dict], T.Generator[RawSample, None, None]]:
|
|
246
|
-
descriptions = []
|
|
247
|
-
sizes = []
|
|
248
|
-
chunk_offsets = []
|
|
249
|
-
chunk_entries = []
|
|
250
|
-
timedeltas: T.List[int] = []
|
|
251
|
-
composition_offsets: T.Optional[T.List[int]] = None
|
|
252
|
-
syncs: T.Optional[T.Set[int]] = None
|
|
253
|
-
|
|
254
|
-
stbl_boxes = T.cast(T.Sequence[cparser.BoxDict], STBLBoxlistConstruct.parse(stbl))
|
|
255
|
-
|
|
256
|
-
for box in stbl_boxes:
|
|
257
|
-
data: T.Dict = T.cast(T.Dict, box["data"])
|
|
258
|
-
|
|
259
|
-
if box["type"] == b"stsd":
|
|
260
|
-
descriptions = list(data["entries"])
|
|
261
|
-
elif box["type"] == b"stsz":
|
|
262
|
-
if data["sample_size"] == 0:
|
|
263
|
-
sizes = list(data["entries"])
|
|
264
|
-
else:
|
|
265
|
-
sizes = [data["sample_size"] for _ in range(data["sample_count"])]
|
|
266
|
-
elif box["type"] == b"stco":
|
|
267
|
-
chunk_offsets = list(data["entries"])
|
|
268
|
-
elif box["type"] == b"co64":
|
|
269
|
-
chunk_offsets = list(data["entries"])
|
|
270
|
-
elif box["type"] == b"stsc":
|
|
271
|
-
chunk_entries = list(data["entries"])
|
|
272
|
-
elif box["type"] == b"stts":
|
|
273
|
-
timedeltas = []
|
|
274
|
-
for entry in data["entries"]:
|
|
275
|
-
for _ in range(entry["sample_count"]):
|
|
276
|
-
timedeltas.append(entry["sample_delta"])
|
|
277
|
-
elif box["type"] == b"ctts":
|
|
278
|
-
composition_offsets = []
|
|
279
|
-
for entry in data["entries"]:
|
|
280
|
-
for _ in range(entry["sample_count"]):
|
|
281
|
-
composition_offsets.append(entry["sample_offset"])
|
|
282
|
-
elif box["type"] == b"stss":
|
|
283
|
-
syncs = set(data["entries"])
|
|
284
|
-
|
|
285
|
-
# some stbl have less timedeltas than the sample count i.e. len(sizes),
|
|
286
|
-
# in this case append 0's to timedeltas
|
|
287
|
-
while len(timedeltas) < len(sizes):
|
|
288
|
-
timedeltas.append(0)
|
|
289
|
-
if composition_offsets is not None:
|
|
290
|
-
while len(composition_offsets) < len(sizes):
|
|
291
|
-
composition_offsets.append(0)
|
|
292
|
-
|
|
293
|
-
raw_samples = _extract_raw_samples(
|
|
294
|
-
sizes, chunk_entries, chunk_offsets, timedeltas, composition_offsets, syncs
|
|
295
|
-
)
|
|
296
|
-
return descriptions, raw_samples
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
def parse_descriptions_from_trak(trak: T.BinaryIO, maxsize: int = -1) -> T.List[T.Dict]:
|
|
300
|
-
data = parser.parse_box_data_first(
|
|
301
|
-
trak, [b"mdia", b"minf", b"stbl", b"stsd"], maxsize=maxsize
|
|
302
|
-
)
|
|
303
|
-
if data is None:
|
|
304
|
-
return []
|
|
305
|
-
box = cparser.SampleDescriptionBox.parse(data)
|
|
306
|
-
return list(box.entries)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
def parse_samples_from_trak(
|
|
310
|
-
trak: T.BinaryIO,
|
|
311
|
-
maxsize: int = -1,
|
|
312
|
-
) -> T.Generator[Sample, None, None]:
|
|
313
|
-
trak_start_offset = trak.tell()
|
|
314
|
-
|
|
315
|
-
trak.seek(trak_start_offset, io.SEEK_SET)
|
|
316
|
-
mdhd_box = parser.parse_box_data_firstx(trak, [b"mdia", b"mdhd"], maxsize=maxsize)
|
|
317
|
-
mdhd = T.cast(T.Dict, cparser.MediaHeaderBox.parse(mdhd_box))
|
|
318
|
-
|
|
319
|
-
trak.seek(trak_start_offset, io.SEEK_SET)
|
|
320
|
-
h, s = parser.parse_box_path_firstx(
|
|
321
|
-
trak, [b"mdia", b"minf", b"stbl"], maxsize=maxsize
|
|
322
|
-
)
|
|
323
|
-
descriptions, raw_samples = parse_raw_samples_from_stbl(s, maxsize=h.maxsize)
|
|
324
|
-
|
|
325
|
-
yield from (
|
|
326
|
-
_apply_timescale(s, mdhd["timescale"])
|
|
327
|
-
for s in _extract_samples(raw_samples, descriptions)
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
STSDBoxListConstruct = cparser.Box64ConstructBuilder(
|
|
332
|
-
# pyre-ignore[6]: pyre does not support recursive type SwitchMapType
|
|
333
|
-
{b"stsd": cparser.CMAP[b"stsd"]}
|
|
334
|
-
).BoxList
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
class TrackBoxParser:
|
|
338
|
-
trak_boxes: T.Sequence[cparser.BoxDict]
|
|
339
|
-
stbl_data: bytes
|
|
340
|
-
|
|
341
|
-
def __init__(self, trak_boxes: T.Sequence[cparser.BoxDict]):
|
|
342
|
-
self.trak_boxes = trak_boxes
|
|
343
|
-
stbl = cparser.find_box_at_pathx(self.trak_boxes, [b"mdia", b"minf", b"stbl"])
|
|
344
|
-
self.stbl_data = T.cast(bytes, stbl["data"])
|
|
345
|
-
|
|
346
|
-
def tkhd(self) -> T.Dict:
|
|
347
|
-
return T.cast(
|
|
348
|
-
T.Dict, cparser.find_box_at_pathx(self.trak_boxes, [b"tkhd"])["data"]
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
def is_video_track(self) -> bool:
|
|
352
|
-
hdlr = cparser.find_box_at_pathx(self.trak_boxes, [b"mdia", b"hdlr"])
|
|
353
|
-
return T.cast(T.Dict[str, T.Any], hdlr["data"])["handler_type"] == b"vide"
|
|
354
|
-
|
|
355
|
-
def parse_sample_description(self) -> T.Dict:
|
|
356
|
-
boxes = STSDBoxListConstruct.parse(self.stbl_data)
|
|
357
|
-
stsd = cparser.find_box_at_pathx(
|
|
358
|
-
T.cast(T.Sequence[cparser.BoxDict], boxes), [b"stsd"]
|
|
359
|
-
)
|
|
360
|
-
return T.cast(T.Dict, stsd["data"])
|
|
361
|
-
|
|
362
|
-
def parse_raw_samples(self) -> T.Generator[RawSample, None, None]:
|
|
363
|
-
_, raw_samples = parse_raw_samples_from_stbl_bytes(self.stbl_data)
|
|
364
|
-
yield from raw_samples
|
|
365
|
-
|
|
366
|
-
def parse_samples(self) -> T.Generator[Sample, None, None]:
|
|
367
|
-
descriptions, raw_samples = parse_raw_samples_from_stbl_bytes(self.stbl_data)
|
|
368
|
-
mdhd = T.cast(
|
|
369
|
-
T.Dict,
|
|
370
|
-
cparser.find_box_at_pathx(self.trak_boxes, [b"mdia", b"mdhd"])["data"],
|
|
371
|
-
)
|
|
372
|
-
yield from (
|
|
373
|
-
_apply_timescale(s, mdhd["timescale"])
|
|
374
|
-
for s in _extract_samples(raw_samples, descriptions)
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
class MovieBoxParser:
|
|
379
|
-
moov_boxes: T.Sequence[cparser.BoxDict]
|
|
380
|
-
|
|
381
|
-
def __init__(self, moov: bytes):
|
|
382
|
-
self.moov_boxes = T.cast(
|
|
383
|
-
T.Sequence[cparser.BoxDict],
|
|
384
|
-
cparser.MOOVWithoutSTBLBuilderConstruct.BoxList.parse(moov),
|
|
385
|
-
)
|
|
386
|
-
|
|
387
|
-
@classmethod
|
|
388
|
-
def parse_file(cls, video_path: Path) -> "MovieBoxParser":
|
|
389
|
-
with video_path.open("rb") as fp:
|
|
390
|
-
moov = parser.parse_box_data_firstx(fp, [b"moov"])
|
|
391
|
-
return MovieBoxParser(moov)
|
|
392
|
-
|
|
393
|
-
def mvhd(self):
|
|
394
|
-
mvhd = cparser.find_box_at_pathx(self.moov_boxes, [b"mvhd"])
|
|
395
|
-
return mvhd["data"]
|
|
396
|
-
|
|
397
|
-
def parse_tracks(self) -> T.Generator[TrackBoxParser, None, None]:
|
|
398
|
-
for box in self.moov_boxes:
|
|
399
|
-
if box["type"] == b"trak":
|
|
400
|
-
yield TrackBoxParser(T.cast(T.Sequence[cparser.BoxDict], box["data"]))
|
|
401
|
-
|
|
402
|
-
def parse_track_at(self, stream_idx: int) -> TrackBoxParser:
|
|
403
|
-
"""
|
|
404
|
-
stream_idx should be the stream_index specifier. See http://ffmpeg.org/ffmpeg.html#Stream-specifiers-1
|
|
405
|
-
> Stream numbering is based on the order of the streams as detected by libavformat
|
|
406
|
-
"""
|
|
407
|
-
trak_boxes = [box for box in self.moov_boxes if box["type"] == b"trak"]
|
|
408
|
-
if not (0 <= stream_idx < len(trak_boxes)):
|
|
409
|
-
raise IndexError(
|
|
410
|
-
"unable to read stream at %d from the track list (length %d)",
|
|
411
|
-
stream_idx,
|
|
412
|
-
len(trak_boxes),
|
|
413
|
-
)
|
|
414
|
-
return TrackBoxParser(
|
|
415
|
-
T.cast(T.Sequence[cparser.BoxDict], trak_boxes[stream_idx]["data"])
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
_DT_1904 = datetime.datetime.utcfromtimestamp(0).replace(year=1904)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
def to_datetime(seconds_since_1904: int) -> datetime.datetime:
|
|
423
|
-
"""
|
|
424
|
-
Convert seconds since midnight, Jan. 1, 1904, in UTC time
|
|
425
|
-
"""
|
|
426
|
-
return _DT_1904 + datetime.timedelta(seconds=seconds_since_1904)
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import typing as T
|
|
3
|
-
|
|
4
|
-
from . import types
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
LOG = logging.getLogger(__name__)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def format_orientation(orientation: int) -> int:
|
|
11
|
-
"""
|
|
12
|
-
Convert orientation from clockwise degrees to exif tag
|
|
13
|
-
|
|
14
|
-
# see http://sylvana.net/jpegcrop/exif_orientation.html
|
|
15
|
-
"""
|
|
16
|
-
mapping: T.Mapping[int, int] = {
|
|
17
|
-
0: 1,
|
|
18
|
-
90: 8,
|
|
19
|
-
180: 3,
|
|
20
|
-
270: 6,
|
|
21
|
-
}
|
|
22
|
-
if orientation not in mapping:
|
|
23
|
-
raise ValueError("Orientation value has to be 0, 90, 180, or 270")
|
|
24
|
-
|
|
25
|
-
return mapping[orientation]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def process_import_meta_properties(
|
|
29
|
-
metadatas: T.List[types.MetadataOrError],
|
|
30
|
-
orientation=None,
|
|
31
|
-
device_make=None,
|
|
32
|
-
device_model=None,
|
|
33
|
-
GPS_accuracy=None,
|
|
34
|
-
add_file_name=False,
|
|
35
|
-
add_import_date=False,
|
|
36
|
-
custom_meta_data=None,
|
|
37
|
-
camera_uuid=None,
|
|
38
|
-
) -> T.List[types.MetadataOrError]:
|
|
39
|
-
if add_file_name:
|
|
40
|
-
LOG.warning("The option --add_file_name is not needed any more since v0.10.0")
|
|
41
|
-
|
|
42
|
-
if add_import_date:
|
|
43
|
-
LOG.warning("The option --add_import_date is not needed any more since v0.10.0")
|
|
44
|
-
|
|
45
|
-
if custom_meta_data:
|
|
46
|
-
LOG.warning(
|
|
47
|
-
"The option --custom_meta_data is not needed any more since v0.10.0"
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
for metadata in metadatas:
|
|
51
|
-
if isinstance(metadata, types.ErrorMetadata):
|
|
52
|
-
continue
|
|
53
|
-
|
|
54
|
-
if device_make is not None:
|
|
55
|
-
if isinstance(metadata, types.VideoMetadata):
|
|
56
|
-
metadata.make = device_make
|
|
57
|
-
else:
|
|
58
|
-
metadata.MAPDeviceMake = device_make
|
|
59
|
-
|
|
60
|
-
if device_model is not None:
|
|
61
|
-
if isinstance(metadata, types.VideoMetadata):
|
|
62
|
-
metadata.model = device_model
|
|
63
|
-
elif isinstance(metadata, types.ImageMetadata):
|
|
64
|
-
metadata.MAPDeviceModel = device_model
|
|
65
|
-
|
|
66
|
-
if isinstance(metadata, types.ImageMetadata):
|
|
67
|
-
if orientation is not None:
|
|
68
|
-
metadata.MAPOrientation = format_orientation(orientation)
|
|
69
|
-
|
|
70
|
-
if GPS_accuracy is not None:
|
|
71
|
-
metadata.MAPGPSAccuracyMeters = float(GPS_accuracy)
|
|
72
|
-
|
|
73
|
-
if camera_uuid is not None:
|
|
74
|
-
metadata.MAPCameraUUID = camera_uuid
|
|
75
|
-
|
|
76
|
-
return metadatas
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
mapillary_tools/__init__.py,sha256=Y_EQVeH08TULmSWJnga0Czi_46VOIkJR_BSsDzpxqNo,19
|
|
2
|
-
mapillary_tools/api_v4.py,sha256=EX0x-mGjKJIM3KYHsrMo5ZcGVb1_1A2j01eP7gc17vo,1685
|
|
3
|
-
mapillary_tools/authenticate.py,sha256=LCFcs6LqZmXaYkTUEKgGfmqytWdh5v_L3KXB48ojOZ4,3090
|
|
4
|
-
mapillary_tools/config.py,sha256=jCjaK4jJaTY4AV4qf_b_tcxn5LA_uPsEWlGIdm2zw6g,2103
|
|
5
|
-
mapillary_tools/constants.py,sha256=q5-4LG35aIrdUTaBh8v5WDGVR4o4fHYPCv0WFnKJLLg,2450
|
|
6
|
-
mapillary_tools/exceptions.py,sha256=Mh1tgVEFTSMnYEzrl9x7b95fW9Z3SPVD_YMEl7r8I0I,2693
|
|
7
|
-
mapillary_tools/exif_read.py,sha256=F60A0-T8XSBHvFKgVIrUz_ZWKQrTFWrtj3c6siB0IMg,28707
|
|
8
|
-
mapillary_tools/exif_write.py,sha256=3PawLnBOY8Z86TYiA_F4LxRhe5Ui6CTNhxYm9yeJNX8,8786
|
|
9
|
-
mapillary_tools/exiftool_read.py,sha256=Mg027me1IzblKb9IyGaLgU6goFqk_QiOt0Ppq-CeECg,16288
|
|
10
|
-
mapillary_tools/exiftool_read_video.py,sha256=BA4Mo0rdC-mM9jIAtFQbNJ2v4TkDqDiZzKfh5DcnwDE,14430
|
|
11
|
-
mapillary_tools/ffmpeg.py,sha256=M56MrJSbwf5SJs15KwHZC8moxjx6SrB6gM0h-JMPq1U,15788
|
|
12
|
-
mapillary_tools/geo.py,sha256=yAopdbA20R9RpG6hfyrXo6qP09Xpb915f8-BtYAgoiA,11211
|
|
13
|
-
mapillary_tools/history.py,sha256=l2z3YdYRyBGEOvcqcLExTV-0LUAX3iBq2OdBzLNMHLM,1766
|
|
14
|
-
mapillary_tools/ipc.py,sha256=DwWQb9hNshx0bg0Fo5NjY0mXjs-FkbR6tIQmjMgMtmg,1089
|
|
15
|
-
mapillary_tools/process_geotag_properties.py,sha256=39gpT96Rzfu9hH1uGKDsYrFR8MLU1PNX_ytdlsL7yvI,22813
|
|
16
|
-
mapillary_tools/process_import_meta_properties.py,sha256=pEqUp1pLzSNUoSEkxRGk1RVTtkE7FMagPo-yEagS3bk,2166
|
|
17
|
-
mapillary_tools/process_sequence_properties.py,sha256=PLCSYJDNwr3rpUfh8kLNtUrUozYbRByR1g3xh3GCVCY,11559
|
|
18
|
-
mapillary_tools/sample_video.py,sha256=3zji4zHt4hT_iWP0sV1XpNikFLfC-OGpk_p1NpSMPec,14385
|
|
19
|
-
mapillary_tools/types.py,sha256=wNdb516UR039voIMN8_MtNPEyLWgzCb8dGj-nR74Pds,21490
|
|
20
|
-
mapillary_tools/upload.py,sha256=Lm7juEYnMmpDLarTU-QGoxMvet1sS3eM3PqTE956LnY,29727
|
|
21
|
-
mapillary_tools/upload_api_v4.py,sha256=-JlrPg5lCAo959jAbL8lYl72gTgQ3o-MawFuAQJ8Uj0,8596
|
|
22
|
-
mapillary_tools/uploader.py,sha256=VieDKi51wdXTIhN7x_mcuQeHESUyFlF5cgB-TAnF4g0,14093
|
|
23
|
-
mapillary_tools/utils.py,sha256=eg0TdJNjOJSYMB0NCxzOzDAbQc67NFOT919ZJxvSNRs,6257
|
|
24
|
-
mapillary_tools/commands/__init__.py,sha256=wz89o8syKkWwoOCp3WBgnFWc41TBQRzRi62TlOTmVNQ,188
|
|
25
|
-
mapillary_tools/commands/__main__.py,sha256=m9MRLyQJfgiki3eRBaagWUwNBKEbEiYtAaf2K3JsQmM,4940
|
|
26
|
-
mapillary_tools/commands/authenticate.py,sha256=4aVvAQal_mqtm2NEMBt5aKLahi0iRdO8b7WSBf6jokA,1136
|
|
27
|
-
mapillary_tools/commands/process.py,sha256=PyfIcpsS7vnLElr02ol1sQ_9Ma6J6uLusIuiyrAiF-c,12808
|
|
28
|
-
mapillary_tools/commands/process_and_upload.py,sha256=osJv1TVHYAG5E-EsA0nB1F3RkKXMadLR2t1EGK0Ifqw,654
|
|
29
|
-
mapillary_tools/commands/sample_video.py,sha256=bTJmlDsajkC-QJ_ZO_scdD4R664zs-r_dh-x2PlOgyY,3281
|
|
30
|
-
mapillary_tools/commands/upload.py,sha256=GyLJ0wa2OO6fxr-8MoEelZsb2sOv3B2B2OhYtwFjzQc,2663
|
|
31
|
-
mapillary_tools/commands/upload_blackvue.py,sha256=9RbCbz0-BquOIcp5JycnVbIAPUIqctwAm50LcLZw3N8,923
|
|
32
|
-
mapillary_tools/commands/upload_camm.py,sha256=2zlBkZYbR2Hi4P02cHtBIymrqkudn5RrhFWwsCG7lG8,907
|
|
33
|
-
mapillary_tools/commands/upload_zip.py,sha256=p8EZZktXLm8IipAbxrmz7E0RcfcLcxeGhEtxZzq5rSw,897
|
|
34
|
-
mapillary_tools/commands/video_process.py,sha256=-wQeeIwWXPmy81HQHam5A0huMLRHknkEFa_V1OwElU4,890
|
|
35
|
-
mapillary_tools/commands/video_process_and_upload.py,sha256=llV0dHBS31qPZp-Fs1GCM0yYezEA_VF_tfYcp-Z8NkY,701
|
|
36
|
-
mapillary_tools/commands/zip.py,sha256=DVQuMLpbstwiy5o4pU_fBvM6eORuFjLeySd80AhHKU0,991
|
|
37
|
-
mapillary_tools/geotag/__init__.py,sha256=ohud7lLsqO1b9ddCF0SjrNOcUcRdQzNVR43RkcVVLAc,33
|
|
38
|
-
mapillary_tools/geotag/blackvue_parser.py,sha256=1yrIs9Hp_-tNrClLQn2gxzCVWpkWTa6OlpLXgI4it70,2983
|
|
39
|
-
mapillary_tools/geotag/camm_builder.py,sha256=AgY-LuuZy2w0Ni_GKI7Z_39Q19BfcghzfuHa5CSOK8o,8224
|
|
40
|
-
mapillary_tools/geotag/camm_parser.py,sha256=zRQcPNgD_oibLp06aW_cwk4H3MvWqHDg-RiKVFiftTA,8830
|
|
41
|
-
mapillary_tools/geotag/construct_mp4_parser.py,sha256=waaJl1wtQ2gbd6nIl9dj3V2nxiPYOn7bU8vopYeIWwE,16713
|
|
42
|
-
mapillary_tools/geotag/geotag_from_generic.py,sha256=bCYfIbkv4qIlkKttAjSGl9t4i_QAtzIiCZoth1hMVI4,480
|
|
43
|
-
mapillary_tools/geotag/geotag_images_from_exif.py,sha256=Wslwj3oNJdJjpSPV5JZ_aHEGF8O62LNQO7CBPczBOs0,4762
|
|
44
|
-
mapillary_tools/geotag/geotag_images_from_exiftool.py,sha256=a-c4H8VIyPdJkfUIvJho0phR0QU0zN8-lSyiCz0wc4s,3981
|
|
45
|
-
mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py,sha256=GbNtPxE-FanzrPUI3FS-dadT5d0lyH_AcAAr4H6GL-o,3386
|
|
46
|
-
mapillary_tools/geotag/geotag_images_from_gpx.py,sha256=S9Pw6FvP5kRSpHUnKUYKXmw0CHa9V92UmrS_MJfbjS4,9053
|
|
47
|
-
mapillary_tools/geotag/geotag_images_from_gpx_file.py,sha256=zEbC0kVf_iw9ioIyJLL-gYN_QvAOEdAoEczCBkizl38,5122
|
|
48
|
-
mapillary_tools/geotag/geotag_images_from_nmea_file.py,sha256=dDdHnJInQ_WN3ZRf-w44NSBElDLPs7XYBiimvE2iCNo,1651
|
|
49
|
-
mapillary_tools/geotag/geotag_images_from_video.py,sha256=XsaWOFChGItl-j1UbKM4hNjUqN29pVNbMpGT_BvI-o8,3306
|
|
50
|
-
mapillary_tools/geotag/geotag_videos_from_exiftool_video.py,sha256=5ezj-9uZszJiI9XZqW9b_XDYfX5RjlXnOyf7atCh47o,5143
|
|
51
|
-
mapillary_tools/geotag/geotag_videos_from_video.py,sha256=HPVJqFeFDvvrFD0vfCimeaOyHHddcWlneAT0LjLUnZo,7098
|
|
52
|
-
mapillary_tools/geotag/gpmf_gps_filter.py,sha256=7GHr05CLLF6lDGpMS3yAbZENh_vEfS0ezrfd_VFg3-c,2800
|
|
53
|
-
mapillary_tools/geotag/gpmf_parser.py,sha256=GDV2yFhmsII4RdzbnPCQwd-Quk6NdMTJ0BcM0QH8ERU,15284
|
|
54
|
-
mapillary_tools/geotag/gps_filter.py,sha256=4CPL8glxdzPWIbfGPPgyqMLyiZFt-djce5vhiFPuZB8,3766
|
|
55
|
-
mapillary_tools/geotag/io_utils.py,sha256=wc3-F1TnxZjTwB7-oea5yRmRQ_0T3Zbz8oBkW9JL8d4,5454
|
|
56
|
-
mapillary_tools/geotag/mp4_sample_parser.py,sha256=UxBqs0vFTA4qVth1ELkeBGElmW465_Vn2vKNApHmp7M,15141
|
|
57
|
-
mapillary_tools/geotag/simple_mp4_builder.py,sha256=-0ZFe0rnFYNcRxxBLrrqytl5GPnKToUH8TPEYjZkjZs,11981
|
|
58
|
-
mapillary_tools/geotag/simple_mp4_parser.py,sha256=eji6JZa497wK8CY8hQt21fjgtnd0nzuyBx7MPEKST74,6671
|
|
59
|
-
mapillary_tools/geotag/utils.py,sha256=Orl35Df4ypxj4v6Lu1Mhk9d2XZxa8yNffz1s1C0JZsQ,651
|
|
60
|
-
mapillary_tools/video_data_extraction/cli_options.py,sha256=g5kqzvkGJm6f5T4Vhum4JLL5qS6vE4iagQbEPtGJcug,563
|
|
61
|
-
mapillary_tools/video_data_extraction/extract_video_data.py,sha256=2MJk4ekb3fhRLRThCrUsPOuTvHkPzyTTMTzlnfvuGD8,6603
|
|
62
|
-
mapillary_tools/video_data_extraction/video_data_parser_factory.py,sha256=EDl4x_M0HvE4Pgwas679w70udLgeKuz6u-4Gv1BZQgE,1539
|
|
63
|
-
mapillary_tools/video_data_extraction/extractors/base_parser.py,sha256=qNjS4LRDekD1E2Guqb2J5XstGVV9X2eN54CDlzXQGCw,1996
|
|
64
|
-
mapillary_tools/video_data_extraction/extractors/blackvue_parser.py,sha256=zro4SUgqnVgEIrkjDf5yb6MJKOfl6ayPrr3FLoVBWO0,1143
|
|
65
|
-
mapillary_tools/video_data_extraction/extractors/camm_parser.py,sha256=u7Yp7CYM0zdcRlpgkHQSB72x_uL2j-_AmwnGSREJPv0,1368
|
|
66
|
-
mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py,sha256=mtdsoncnsspCtt5rMET2gxcEzvyOCyNv754RNhMEUNQ,2436
|
|
67
|
-
mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py,sha256=unLa5uZB1N5Vtl8bk19hayphRhXzHlSKgBYjVUv1hlQ,1848
|
|
68
|
-
mapillary_tools/video_data_extraction/extractors/generic_video_parser.py,sha256=mw_byv66za_5Mb5TrWbD5UU2Ts6mAMZqLgKB9umWGsY,1955
|
|
69
|
-
mapillary_tools/video_data_extraction/extractors/gopro_parser.py,sha256=xNXGuR7a-oDaE_WB2xK7sDpAGFwyWIsBUC8fSMKtfgM,1214
|
|
70
|
-
mapillary_tools/video_data_extraction/extractors/gpx_parser.py,sha256=cqNGtlWFuuxBTf5BliCuJ14avxu-8Pz5LvXnTpTum60,815
|
|
71
|
-
mapillary_tools/video_data_extraction/extractors/nmea_parser.py,sha256=QFMw_yf4uNP8z7tTybHMKHHMG6cS92bae-MJPHq6EJk,731
|
|
72
|
-
mapillary_tools-0.12.1.dist-info/LICENSE,sha256=l2D8cKfFmmJq_wcVq_JElPJrlvWQOzNWx7gMLINucxc,1292
|
|
73
|
-
mapillary_tools-0.12.1.dist-info/METADATA,sha256=nlA9PJrgEzB0LxKovlrER7YD-arXOyo7P6Zzr8PWt1Y,19638
|
|
74
|
-
mapillary_tools-0.12.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
75
|
-
mapillary_tools-0.12.1.dist-info/entry_points.txt,sha256=A3f3LP-BO_P-U8Y29QfpT4jx6Mjk3sXjTi2Yew4bvj8,75
|
|
76
|
-
mapillary_tools-0.12.1.dist-info/top_level.txt,sha256=FbDkMgOrt1S70ho1WSBrOwzKOSkJFDwwqFOoY5-527s,16
|
|
77
|
-
mapillary_tools-0.12.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|