mapillary-tools 0.10.6a1__tar.gz → 0.11.0b2__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.
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/PKG-INFO +100 -2
- mapillary_tools-0.10.6a1/mapillary_tools.egg-info/PKG-INFO → mapillary_tools-0.11.0b2/README.md +87 -11
- mapillary_tools-0.11.0b2/mapillary_tools/__init__.py +1 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/api_v4.py +2 -9
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/__main__.py +5 -3
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/process.py +9 -1
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/constants.py +1 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/exceptions.py +4 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/exif_read.py +46 -10
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/exiftool_read.py +4 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/ffmpeg.py +3 -8
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/construct_mp4_parser.py +2 -8
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/gpmf_parser.py +1 -7
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/process_geotag_properties.py +115 -51
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/process_sequence_properties.py +3 -3
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/types.py +1 -6
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/uploader.py +2 -9
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/cli_options.py +22 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extract_video_data.py +190 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/base_parser.py +73 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +33 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/camm_parser.py +41 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +56 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +55 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +57 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/gopro_parser.py +36 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/gpx_parser.py +29 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/extractors/nmea_parser.py +24 -0
- mapillary_tools-0.11.0b2/mapillary_tools/video_data_extraction/video_data_parser_factory.py +46 -0
- mapillary_tools-0.10.6a1/README.md → mapillary_tools-0.11.0b2/mapillary_tools.egg-info/PKG-INFO +109 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools.egg-info/SOURCES.txt +12 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/setup.py +8 -2
- mapillary_tools-0.10.6a1/mapillary_tools/__init__.py +0 -1
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/LICENSE +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/MANIFEST.in +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/authenticate.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/__init__.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/authenticate.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/process_and_upload.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/sample_video.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/upload.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/upload_blackvue.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/upload_camm.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/upload_zip.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/video_process.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/video_process_and_upload.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/commands/zip.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/config.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/exif_write.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/exiftool_read_video.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geo.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/__init__.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/blackvue_parser.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/camm_builder.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/camm_parser.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_from_generic.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_images_from_exif.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_images_from_exiftool.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_images_from_gpx.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_images_from_gpx_file.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_images_from_nmea_file.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_images_from_video.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/geotag_videos_from_video.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/gpmf_gps_filter.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/gps_filter.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/io_utils.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/mp4_sample_parser.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/simple_mp4_builder.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/simple_mp4_parser.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/utils.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/history.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/ipc.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/process_import_meta_properties.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/sample_video.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/upload.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/upload_api_v4.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/utils.py +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools.egg-info/dependency_links.txt +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools.egg-info/entry_points.txt +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools.egg-info/requires.txt +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools.egg-info/top_level.txt +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/requirements.txt +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/schema/image_description_schema.json +0 -0
- {mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/setup.cfg +0 -0
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mapillary_tools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0b2
|
|
4
4
|
Summary: Mapillary Image/Video Import Pipeline
|
|
5
5
|
Home-page: https://github.com/mapillary/mapillary_tools
|
|
6
6
|
Author: Mapillary
|
|
7
7
|
License: BSD
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
+
Requires-Dist: appdirs<2.0.0,>=1.4.4
|
|
12
|
+
Requires-Dist: construct<3.0.0,>=2.10.0
|
|
13
|
+
Requires-Dist: exifread==2.3.2
|
|
14
|
+
Requires-Dist: piexif==1.1.3
|
|
15
|
+
Requires-Dist: gpxpy<1.6.0,>=1.5.0
|
|
16
|
+
Requires-Dist: pynmea2==1.12.0
|
|
17
|
+
Requires-Dist: requests<3.0.0,>=2.20.0
|
|
18
|
+
Requires-Dist: tqdm<5.0,>=4.0
|
|
19
|
+
Requires-Dist: typing_extensions
|
|
20
|
+
Requires-Dist: jsonschema~=4.17.3
|
|
21
|
+
Requires-Dist: dataclasses; python_version <= "3.6"
|
|
11
22
|
|
|
12
23
|
<p align="center">
|
|
13
24
|
<a href="https://github.com/mapillary/mapillary_tools/">
|
|
@@ -303,6 +314,93 @@ mapillary_tools video_process MY_VIDEO_DIR \
|
|
|
303
314
|
--video_sample_distance -1 --video_sample_interval 2
|
|
304
315
|
```
|
|
305
316
|
|
|
317
|
+
## New video geotagging features (experimental)
|
|
318
|
+
|
|
319
|
+
As experimental features, mapillary_tools can now:
|
|
320
|
+
* Geotag videos from tracks recorded in GPX and NMEA files
|
|
321
|
+
* Invoke `exiftool` internally, if available on the system. `exiftool` can extract geolocation data from a wide
|
|
322
|
+
range of video formats, allowing us to support more cameras with less trouble for the end user.
|
|
323
|
+
* Try several geotagging sources sequentially, until proper data is found.
|
|
324
|
+
|
|
325
|
+
These features apply to the `process` command, which analyzes the video for direct upload instead of sampling it
|
|
326
|
+
into images. They are experimental and will be subject to change in future releases.
|
|
327
|
+
|
|
328
|
+
### Usage
|
|
329
|
+
|
|
330
|
+
The new video processing is triggered with the `--video_geotag_source SOURCE` option. It can be specified multiple times:
|
|
331
|
+
In this case, each source will be tried in turn, until one returns good quality data.
|
|
332
|
+
|
|
333
|
+
`SOURCE` can be:
|
|
334
|
+
1. the plain name of the source - one of `video, camm, gopro, blackvue, gpx, nmea, exiftool_xml, exiftool_runtime`
|
|
335
|
+
2. a JSON object that includes optional parameters
|
|
336
|
+
```json5
|
|
337
|
+
{
|
|
338
|
+
"source": "SOURCE",
|
|
339
|
+
"pattern": "FILENAME_PATTERN" // optional
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
PATTERN specifies how the data source file is named, starting from the video filename:
|
|
344
|
+
* `%f`: the full video filename
|
|
345
|
+
* `%g`: the video filename without extension
|
|
346
|
+
* `%e`: the video filename extension
|
|
347
|
+
|
|
348
|
+
Supported sources and their default pattern are:
|
|
349
|
+
|
|
350
|
+
* `video`: parse the video, in order, as `camm, gopro, blackvue`. Pattern: `%f`
|
|
351
|
+
* `camm`: parse the video looking for a CAMM track. Pattern: `%f`
|
|
352
|
+
* `gopro`: parse the video looking for geolocation in GoPro format. Pattern: `%f`
|
|
353
|
+
* `blackvue`: parse the video looking for geolocation in BlackVue format. Pattern: `%f`
|
|
354
|
+
* `gpx`: external GPX file. Pattern: `%g.gpx`
|
|
355
|
+
* `nmea`: external NMEA file. Pattern: `%g.nmea`
|
|
356
|
+
* `exiftool_xml`: external XML file generated by exiftool. Pattern: `%g.xml`
|
|
357
|
+
* `exiftool_runtime`: execute exiftool on the video file. Pattern: `%f`
|
|
358
|
+
|
|
359
|
+
Notes:
|
|
360
|
+
* `exiftool_runtime` only works if exiftool is installed on the system. You can find it at https://exiftool.org/ or through
|
|
361
|
+
your software manager. If exiftool is installed, but is not in the default execution path, it is
|
|
362
|
+
possible to specify its location by setting the environment variable `MAPILLARY_TOOLS_EXIFTOOL_PATH`.
|
|
363
|
+
* Pattern are case-sensitive or not depending on the filesystem - in Windows, `%g.gpx` will match both `basename.gpx`
|
|
364
|
+
and `basename.GPX`, in MacOs, Linux or other Unix systems no.
|
|
365
|
+
* If both `--video_geotag_source` and `--geotag_source` are specified, `--video_geotag_source` will apply to video files
|
|
366
|
+
and `--geotag_source` to image files.
|
|
367
|
+
|
|
368
|
+
### Examples
|
|
369
|
+
|
|
370
|
+
#### Generic supported videos
|
|
371
|
+
|
|
372
|
+
Process all videos in a directory, trying to parse them as CAMM, GoPro or BlackVue:
|
|
373
|
+
```sh
|
|
374
|
+
mapillary_tools process --video_geotag_source video VIDEO_DIR/
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
#### External GPX
|
|
378
|
+
|
|
379
|
+
Process all videos in a directory, taking geolocation data from GPX files. A video named `foo.mp4` will be associated
|
|
380
|
+
with a GPX file called `foo.gpx`.
|
|
381
|
+
```sh
|
|
382
|
+
mapillary_tools process --video_geotag_source gpx VIDEO_DIR/
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
#### Insta360 stitched videos
|
|
386
|
+
|
|
387
|
+
The videos to process have been stitched by Insta360 Studio; the geolocation data is in the original
|
|
388
|
+
videos in the parent directory, and there may be GPX files alongside the stitched video.
|
|
389
|
+
First look for GPX, then fallback to running exiftool against the original videos.
|
|
390
|
+
```sh
|
|
391
|
+
mapillary_tools process \
|
|
392
|
+
--video_geotag_source gpx \
|
|
393
|
+
--video_geotag_source '{"source": "exiftool_runtime", "pattern": "../%g.insv"}' \
|
|
394
|
+
VIDEO_DIR/
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Limitations of `--video_geotag_source`
|
|
398
|
+
|
|
399
|
+
**External geolocation sources will be aligned with the start of video, there is
|
|
400
|
+
currently no way of applying offsets or scaling the time.** This means, for instance, that GPX tracks must begin precisely
|
|
401
|
+
at the instant of the video, and that timelapse videos are supported only for sources `camm, gopro, blackvue`.
|
|
402
|
+
|
|
403
|
+
|
|
306
404
|
## Authenticate
|
|
307
405
|
|
|
308
406
|
The command `authenticate` will update the user credentials stored in the config file.
|
mapillary_tools-0.10.6a1/mapillary_tools.egg-info/PKG-INFO → mapillary_tools-0.11.0b2/README.md
RENAMED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: mapillary-tools
|
|
3
|
-
Version: 0.10.6a1
|
|
4
|
-
Summary: Mapillary Image/Video Import Pipeline
|
|
5
|
-
Home-page: https://github.com/mapillary/mapillary_tools
|
|
6
|
-
Author: Mapillary
|
|
7
|
-
License: BSD
|
|
8
|
-
Requires-Python: >=3.6
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
|
|
12
1
|
<p align="center">
|
|
13
2
|
<a href="https://github.com/mapillary/mapillary_tools/">
|
|
14
3
|
<img src="https://raw.githubusercontent.com/mapillary/mapillary_tools/main/docs/images/logo.png">
|
|
@@ -303,6 +292,93 @@ mapillary_tools video_process MY_VIDEO_DIR \
|
|
|
303
292
|
--video_sample_distance -1 --video_sample_interval 2
|
|
304
293
|
```
|
|
305
294
|
|
|
295
|
+
## New video geotagging features (experimental)
|
|
296
|
+
|
|
297
|
+
As experimental features, mapillary_tools can now:
|
|
298
|
+
* Geotag videos from tracks recorded in GPX and NMEA files
|
|
299
|
+
* Invoke `exiftool` internally, if available on the system. `exiftool` can extract geolocation data from a wide
|
|
300
|
+
range of video formats, allowing us to support more cameras with less trouble for the end user.
|
|
301
|
+
* Try several geotagging sources sequentially, until proper data is found.
|
|
302
|
+
|
|
303
|
+
These features apply to the `process` command, which analyzes the video for direct upload instead of sampling it
|
|
304
|
+
into images. They are experimental and will be subject to change in future releases.
|
|
305
|
+
|
|
306
|
+
### Usage
|
|
307
|
+
|
|
308
|
+
The new video processing is triggered with the `--video_geotag_source SOURCE` option. It can be specified multiple times:
|
|
309
|
+
In this case, each source will be tried in turn, until one returns good quality data.
|
|
310
|
+
|
|
311
|
+
`SOURCE` can be:
|
|
312
|
+
1. the plain name of the source - one of `video, camm, gopro, blackvue, gpx, nmea, exiftool_xml, exiftool_runtime`
|
|
313
|
+
2. a JSON object that includes optional parameters
|
|
314
|
+
```json5
|
|
315
|
+
{
|
|
316
|
+
"source": "SOURCE",
|
|
317
|
+
"pattern": "FILENAME_PATTERN" // optional
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
PATTERN specifies how the data source file is named, starting from the video filename:
|
|
322
|
+
* `%f`: the full video filename
|
|
323
|
+
* `%g`: the video filename without extension
|
|
324
|
+
* `%e`: the video filename extension
|
|
325
|
+
|
|
326
|
+
Supported sources and their default pattern are:
|
|
327
|
+
|
|
328
|
+
* `video`: parse the video, in order, as `camm, gopro, blackvue`. Pattern: `%f`
|
|
329
|
+
* `camm`: parse the video looking for a CAMM track. Pattern: `%f`
|
|
330
|
+
* `gopro`: parse the video looking for geolocation in GoPro format. Pattern: `%f`
|
|
331
|
+
* `blackvue`: parse the video looking for geolocation in BlackVue format. Pattern: `%f`
|
|
332
|
+
* `gpx`: external GPX file. Pattern: `%g.gpx`
|
|
333
|
+
* `nmea`: external NMEA file. Pattern: `%g.nmea`
|
|
334
|
+
* `exiftool_xml`: external XML file generated by exiftool. Pattern: `%g.xml`
|
|
335
|
+
* `exiftool_runtime`: execute exiftool on the video file. Pattern: `%f`
|
|
336
|
+
|
|
337
|
+
Notes:
|
|
338
|
+
* `exiftool_runtime` only works if exiftool is installed on the system. You can find it at https://exiftool.org/ or through
|
|
339
|
+
your software manager. If exiftool is installed, but is not in the default execution path, it is
|
|
340
|
+
possible to specify its location by setting the environment variable `MAPILLARY_TOOLS_EXIFTOOL_PATH`.
|
|
341
|
+
* Pattern are case-sensitive or not depending on the filesystem - in Windows, `%g.gpx` will match both `basename.gpx`
|
|
342
|
+
and `basename.GPX`, in MacOs, Linux or other Unix systems no.
|
|
343
|
+
* If both `--video_geotag_source` and `--geotag_source` are specified, `--video_geotag_source` will apply to video files
|
|
344
|
+
and `--geotag_source` to image files.
|
|
345
|
+
|
|
346
|
+
### Examples
|
|
347
|
+
|
|
348
|
+
#### Generic supported videos
|
|
349
|
+
|
|
350
|
+
Process all videos in a directory, trying to parse them as CAMM, GoPro or BlackVue:
|
|
351
|
+
```sh
|
|
352
|
+
mapillary_tools process --video_geotag_source video VIDEO_DIR/
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### External GPX
|
|
356
|
+
|
|
357
|
+
Process all videos in a directory, taking geolocation data from GPX files. A video named `foo.mp4` will be associated
|
|
358
|
+
with a GPX file called `foo.gpx`.
|
|
359
|
+
```sh
|
|
360
|
+
mapillary_tools process --video_geotag_source gpx VIDEO_DIR/
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### Insta360 stitched videos
|
|
364
|
+
|
|
365
|
+
The videos to process have been stitched by Insta360 Studio; the geolocation data is in the original
|
|
366
|
+
videos in the parent directory, and there may be GPX files alongside the stitched video.
|
|
367
|
+
First look for GPX, then fallback to running exiftool against the original videos.
|
|
368
|
+
```sh
|
|
369
|
+
mapillary_tools process \
|
|
370
|
+
--video_geotag_source gpx \
|
|
371
|
+
--video_geotag_source '{"source": "exiftool_runtime", "pattern": "../%g.insv"}' \
|
|
372
|
+
VIDEO_DIR/
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Limitations of `--video_geotag_source`
|
|
376
|
+
|
|
377
|
+
**External geolocation sources will be aligned with the start of video, there is
|
|
378
|
+
currently no way of applying offsets or scaling the time.** This means, for instance, that GPX tracks must begin precisely
|
|
379
|
+
at the instant of the video, and that timelapse videos are supported only for sources `camm, gopro, blackvue`.
|
|
380
|
+
|
|
381
|
+
|
|
306
382
|
## Authenticate
|
|
307
383
|
|
|
308
384
|
The command `authenticate` will update the user credentials stored in the config file.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.11.0b2"
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import sys
|
|
3
2
|
import typing as T
|
|
4
|
-
from typing import Union
|
|
5
3
|
|
|
6
4
|
import requests
|
|
7
5
|
|
|
8
|
-
if sys.version_info >= (3, 8):
|
|
9
|
-
from typing import Literal # pylint: disable=no-name-in-module
|
|
10
|
-
else:
|
|
11
|
-
from typing_extensions import Literal
|
|
12
|
-
|
|
13
6
|
MAPILLARY_CLIENT_TOKEN = os.getenv(
|
|
14
7
|
"MAPILLARY_CLIENT_TOKEN", "MLY|5675152195860640|6b02c72e6e3c801e5603ab0495623282"
|
|
15
8
|
)
|
|
@@ -31,7 +24,7 @@ def get_upload_token(email: str, password: str) -> requests.Response:
|
|
|
31
24
|
|
|
32
25
|
|
|
33
26
|
def fetch_organization(
|
|
34
|
-
user_access_token: str, organization_id: Union[int, str]
|
|
27
|
+
user_access_token: str, organization_id: T.Union[int, str]
|
|
35
28
|
) -> requests.Response:
|
|
36
29
|
resp = requests.get(
|
|
37
30
|
f"{MAPILLARY_GRAPH_API_ENDPOINT}/{organization_id}",
|
|
@@ -47,7 +40,7 @@ def fetch_organization(
|
|
|
47
40
|
return resp
|
|
48
41
|
|
|
49
42
|
|
|
50
|
-
ActionType = Literal[
|
|
43
|
+
ActionType = T.Literal[
|
|
51
44
|
"upload_started_upload", "upload_finished_upload", "upload_failed_upload"
|
|
52
45
|
]
|
|
53
46
|
|
|
@@ -63,14 +63,14 @@ def add_general_arguments(parser, command):
|
|
|
63
63
|
elif command in ["upload"]:
|
|
64
64
|
parser.add_argument(
|
|
65
65
|
"import_path",
|
|
66
|
-
help="
|
|
66
|
+
help="Paths to your images or videos.",
|
|
67
67
|
nargs="+",
|
|
68
68
|
type=Path,
|
|
69
69
|
)
|
|
70
70
|
elif command in ["process", "process_and_upload"]:
|
|
71
71
|
parser.add_argument(
|
|
72
72
|
"import_path",
|
|
73
|
-
help="
|
|
73
|
+
help="Paths to your images or videos.",
|
|
74
74
|
nargs="+",
|
|
75
75
|
type=Path,
|
|
76
76
|
)
|
|
@@ -167,7 +167,9 @@ def main():
|
|
|
167
167
|
try:
|
|
168
168
|
args.func(argvars)
|
|
169
169
|
except exceptions.MapillaryUserError as exc:
|
|
170
|
-
LOG.error(
|
|
170
|
+
LOG.error(
|
|
171
|
+
"%s: %s", exc.__class__.__name__, exc, exc_info=log_level == logging.DEBUG
|
|
172
|
+
)
|
|
171
173
|
sys.exit(exc.exit_code)
|
|
172
174
|
|
|
173
175
|
|
|
@@ -171,6 +171,13 @@ class Command:
|
|
|
171
171
|
required=False,
|
|
172
172
|
type=Path,
|
|
173
173
|
)
|
|
174
|
+
group_geotagging.add_argument(
|
|
175
|
+
"--video_geotag_source",
|
|
176
|
+
help="Name of the video data extractor and optional arguments. Can be specified multiple times. See the documentation for details. [Experimental, subject to change]",
|
|
177
|
+
action="append",
|
|
178
|
+
default=[],
|
|
179
|
+
required=False,
|
|
180
|
+
)
|
|
174
181
|
group_geotagging.add_argument(
|
|
175
182
|
"--interpolation_use_gpx_start_time",
|
|
176
183
|
help=f"If supplied, the first image will use the first GPX point time for interpolation, which means the image location will be interpolated to the first GPX point too. Only works for geotagging from {', '.join(geotag_gpx_based_sources)}.",
|
|
@@ -261,13 +268,14 @@ class Command:
|
|
|
261
268
|
vars_args["duplicate_angle"] = 360
|
|
262
269
|
|
|
263
270
|
metadatas = process_geotag_properties(
|
|
271
|
+
vars_args=vars_args,
|
|
264
272
|
**(
|
|
265
273
|
{
|
|
266
274
|
k: v
|
|
267
275
|
for k, v in vars_args.items()
|
|
268
276
|
if k in inspect.getfullargspec(process_geotag_properties).args
|
|
269
277
|
}
|
|
270
|
-
)
|
|
278
|
+
),
|
|
271
279
|
)
|
|
272
280
|
|
|
273
281
|
metadatas = process_import_meta_properties(
|
|
@@ -18,6 +18,7 @@ VIDEO_SAMPLE_DISTANCE = float(os.getenv(_ENV_PREFIX + "VIDEO_SAMPLE_DISTANCE", 3
|
|
|
18
18
|
VIDEO_DURATION_RATIO = float(os.getenv(_ENV_PREFIX + "VIDEO_DURATION_RATIO", 1))
|
|
19
19
|
FFPROBE_PATH: str = os.getenv(_ENV_PREFIX + "FFPROBE_PATH", "ffprobe")
|
|
20
20
|
FFMPEG_PATH: str = os.getenv(_ENV_PREFIX + "FFMPEG_PATH", "ffmpeg")
|
|
21
|
+
EXIFTOOL_PATH: str = os.getenv(_ENV_PREFIX + "EXIFTOOL_PATH", "exiftool")
|
|
21
22
|
IMAGE_DESCRIPTION_FILENAME = os.getenv(
|
|
22
23
|
_ENV_PREFIX + "IMAGE_DESCRIPTION_FILENAME", "mapillary_image_description.json"
|
|
23
24
|
)
|
|
@@ -34,6 +34,10 @@ class MapillaryFFmpegNotFoundError(MapillaryUserError):
|
|
|
34
34
|
help = "https://github.com/mapillary/mapillary_tools#video-support"
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
class MapillaryExiftoolNotFoundError(MapillaryUserError):
|
|
38
|
+
exit_code = 8
|
|
39
|
+
|
|
40
|
+
|
|
37
41
|
class MapillaryDescriptionError(Exception):
|
|
38
42
|
pass
|
|
39
43
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import datetime
|
|
3
3
|
import logging
|
|
4
|
+
import re
|
|
4
5
|
import typing as T
|
|
5
6
|
import xml.etree.ElementTree as et
|
|
7
|
+
from fractions import Fraction
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
|
|
8
10
|
import exifread
|
|
@@ -21,6 +23,8 @@ XMP_NAMESPACES = {
|
|
|
21
23
|
# https://github.com/ianare/exif-py/issues/167
|
|
22
24
|
EXIFREAD_LOG = logging.getLogger("exifread")
|
|
23
25
|
EXIFREAD_LOG.setLevel(logging.ERROR)
|
|
26
|
+
SIGN_BY_DIRECTION = {None: 1, "N": 1, "S": -1, "E": 1, "W": -1}
|
|
27
|
+
ADOBE_FORMAT_REGEX = re.compile(r"(\d+),(\d{1,3}\.?\d*)([NSWE])")
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
def eval_frac(value: Ratio) -> float:
|
|
@@ -47,6 +51,38 @@ def gps_to_decimal(values: T.Tuple[Ratio, Ratio, Ratio]) -> T.Optional[float]:
|
|
|
47
51
|
return degrees + minutes / 60 + seconds / 3600
|
|
48
52
|
|
|
49
53
|
|
|
54
|
+
def _parse_coord_numeric(coord: str, ref: T.Optional[str]) -> T.Optional[float]:
|
|
55
|
+
try:
|
|
56
|
+
return float(coord) * SIGN_BY_DIRECTION[ref]
|
|
57
|
+
except (ValueError, KeyError):
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _parse_coord_adobe(coord: str) -> T.Optional[float]:
|
|
62
|
+
"""
|
|
63
|
+
Parse Adobe coordinate format: <degrees,fractionalminutes[NSEW]>
|
|
64
|
+
"""
|
|
65
|
+
matches = ADOBE_FORMAT_REGEX.match(coord)
|
|
66
|
+
if matches:
|
|
67
|
+
deg = Ratio(int(matches.group(1)), 1)
|
|
68
|
+
min_frac = Fraction.from_float(float(matches.group(2)))
|
|
69
|
+
min = Ratio(min_frac.numerator, min_frac.denominator)
|
|
70
|
+
sec = Ratio(0, 1)
|
|
71
|
+
converted = gps_to_decimal((deg, min, sec))
|
|
72
|
+
if converted is not None:
|
|
73
|
+
return converted * SIGN_BY_DIRECTION[matches.group(3)]
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _parse_coord(coord: T.Optional[str], ref: T.Optional[str]) -> T.Optional[float]:
|
|
78
|
+
if coord is None:
|
|
79
|
+
return None
|
|
80
|
+
parsed = _parse_coord_numeric(coord, ref)
|
|
81
|
+
if parsed is None:
|
|
82
|
+
parsed = _parse_coord_adobe(coord)
|
|
83
|
+
return parsed
|
|
84
|
+
|
|
85
|
+
|
|
50
86
|
def _parse_iso(dtstr: str) -> T.Optional[datetime.datetime]:
|
|
51
87
|
try:
|
|
52
88
|
return datetime.datetime.fromisoformat(dtstr)
|
|
@@ -378,22 +414,22 @@ class ExifReadFromXMP(ExifReadABC):
|
|
|
378
414
|
)
|
|
379
415
|
|
|
380
416
|
def extract_lon_lat(self) -> T.Optional[T.Tuple[float, float]]:
|
|
381
|
-
|
|
417
|
+
lat_ref = self._extract_alternative_fields(["exif:GPSLatitudeRef"], str)
|
|
418
|
+
lat_str: T.Optional[str] = self._extract_alternative_fields(
|
|
419
|
+
["exif:GPSLatitude"], str
|
|
420
|
+
)
|
|
421
|
+
lat: T.Optional[float] = _parse_coord(lat_str, lat_ref)
|
|
382
422
|
if lat is None:
|
|
383
423
|
return None
|
|
384
424
|
|
|
385
|
-
|
|
425
|
+
lon_ref = self._extract_alternative_fields(["exif:GPSLongitudeRef"], str)
|
|
426
|
+
lon_str: T.Optional[str] = self._extract_alternative_fields(
|
|
427
|
+
["exif:GPSLongitude"], str
|
|
428
|
+
)
|
|
429
|
+
lon = _parse_coord(lon_str, lon_ref)
|
|
386
430
|
if lon is None:
|
|
387
431
|
return None
|
|
388
432
|
|
|
389
|
-
ref = self._extract_alternative_fields(["exif:GPSLongitudeRef"], str)
|
|
390
|
-
if ref and ref.upper() == "W":
|
|
391
|
-
lon = -1 * lon
|
|
392
|
-
|
|
393
|
-
ref = self._extract_alternative_fields(["exif:GPSLatitudeRef"], str)
|
|
394
|
-
if ref and ref.upper() == "S":
|
|
395
|
-
lat = -1 * lat
|
|
396
|
-
|
|
397
433
|
return lon, lat
|
|
398
434
|
|
|
399
435
|
def extract_make(self) -> T.Optional[str]:
|
|
@@ -310,6 +310,10 @@ class ExifToolRead(exif_read.ExifReadABC):
|
|
|
310
310
|
if lon_lat is not None:
|
|
311
311
|
return lon_lat
|
|
312
312
|
|
|
313
|
+
lon_lat = self._extract_lon_lat("XMP-exif:GPSLongitude", "XMP-exif:GPSLatitude")
|
|
314
|
+
if lon_lat is not None:
|
|
315
|
+
return lon_lat
|
|
316
|
+
|
|
313
317
|
return None
|
|
314
318
|
|
|
315
319
|
def _extract_lon_lat(
|
|
@@ -11,22 +11,17 @@ import tempfile
|
|
|
11
11
|
import typing as T
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
if sys.version_info >= (3, 8):
|
|
15
|
-
from typing import TypedDict # pylint: disable=no-name-in-module
|
|
16
|
-
else:
|
|
17
|
-
from typing_extensions import TypedDict
|
|
18
|
-
|
|
19
14
|
LOG = logging.getLogger(__name__)
|
|
20
15
|
FRAME_EXT = ".jpg"
|
|
21
16
|
NA_STREAM_IDX = "NA"
|
|
22
17
|
|
|
23
18
|
|
|
24
|
-
class StreamTag(TypedDict):
|
|
19
|
+
class StreamTag(T.TypedDict):
|
|
25
20
|
creation_time: str
|
|
26
21
|
language: str
|
|
27
22
|
|
|
28
23
|
|
|
29
|
-
class Stream(TypedDict):
|
|
24
|
+
class Stream(T.TypedDict):
|
|
30
25
|
codec_name: str
|
|
31
26
|
codec_tag_string: str
|
|
32
27
|
codec_type: str
|
|
@@ -37,7 +32,7 @@ class Stream(TypedDict):
|
|
|
37
32
|
width: int
|
|
38
33
|
|
|
39
34
|
|
|
40
|
-
class ProbeOutput(TypedDict):
|
|
35
|
+
class ProbeOutput(T.TypedDict):
|
|
41
36
|
streams: T.List[Stream]
|
|
42
37
|
|
|
43
38
|
|
{mapillary_tools-0.10.6a1 → mapillary_tools-0.11.0b2}/mapillary_tools/geotag/construct_mp4_parser.py
RENAMED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
# pyre-ignore-all-errors[5, 16, 21, 58]
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
3
|
import typing as T
|
|
5
4
|
|
|
6
|
-
if sys.version_info >= (3, 8):
|
|
7
|
-
from typing import Literal, TypedDict # pylint: disable=no-name-in-module
|
|
8
|
-
else:
|
|
9
|
-
from typing_extensions import Literal, TypedDict
|
|
10
|
-
|
|
11
5
|
import construct as C
|
|
12
6
|
|
|
13
7
|
|
|
14
|
-
BoxType = Literal[
|
|
8
|
+
BoxType = T.Literal[
|
|
15
9
|
b"@mak",
|
|
16
10
|
b"@mod",
|
|
17
11
|
b"co64",
|
|
@@ -46,7 +40,7 @@ BoxType = Literal[
|
|
|
46
40
|
]
|
|
47
41
|
|
|
48
42
|
|
|
49
|
-
class BoxDict(TypedDict, total=True):
|
|
43
|
+
class BoxDict(T.TypedDict, total=True):
|
|
50
44
|
type: BoxType
|
|
51
45
|
data: T.Union[T.Sequence["BoxDict"], T.Dict[str, T.Any], bytes]
|
|
52
46
|
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import pathlib
|
|
3
|
-
import sys
|
|
4
3
|
import typing as T
|
|
5
4
|
|
|
6
|
-
if sys.version_info >= (3, 8):
|
|
7
|
-
from typing import TypedDict # pylint: disable=no-name-in-module
|
|
8
|
-
else:
|
|
9
|
-
from typing_extensions import TypedDict
|
|
10
|
-
|
|
11
5
|
import construct as C
|
|
12
6
|
|
|
13
7
|
from .. import geo
|
|
@@ -35,7 +29,7 @@ NOTE:
|
|
|
35
29
|
"""
|
|
36
30
|
|
|
37
31
|
|
|
38
|
-
class KLVDict(TypedDict):
|
|
32
|
+
class KLVDict(T.TypedDict):
|
|
39
33
|
key: bytes
|
|
40
34
|
type: bytes
|
|
41
35
|
structure_size: int
|