mapillary-tools 0.14.0a2__tar.gz → 0.14.1__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.14.0a2 → mapillary_tools-0.14.1}/PKG-INFO +91 -34
- mapillary_tools-0.14.0a2/mapillary_tools.egg-info/PKG-INFO → mapillary_tools-0.14.1/README.md +67 -46
- mapillary_tools-0.14.1/mapillary_tools/__init__.py +1 -0
- mapillary_tools-0.14.1/mapillary_tools/api_v4.py +176 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/authenticate.py +54 -46
- mapillary_tools-0.14.1/mapillary_tools/blackvue_parser.py +195 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/__main__.py +15 -16
- mapillary_tools-0.14.1/mapillary_tools/commands/upload.py +88 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/config.py +38 -17
- mapillary_tools-0.14.1/mapillary_tools/constants.py +175 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/exceptions.py +4 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/exif_read.py +2 -1
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/exif_write.py +3 -1
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/exiftool_read_video.py +52 -15
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/exiftool_runner.py +4 -24
- mapillary_tools-0.14.1/mapillary_tools/ffmpeg.py +626 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geo.py +16 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/base.py +8 -4
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/factory.py +106 -89
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/geotag_images_from_exiftool.py +27 -20
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/geotag_images_from_gpx.py +7 -6
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/geotag_images_from_video.py +35 -0
- mapillary_tools-0.14.1/mapillary_tools/geotag/geotag_videos_from_exiftool.py +144 -0
- mapillary_tools-0.14.1/mapillary_tools/geotag/geotag_videos_from_gpx.py +52 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/options.py +25 -3
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/utils.py +9 -12
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/video_extractors/base.py +1 -1
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/video_extractors/exiftool.py +1 -1
- mapillary_tools-0.14.1/mapillary_tools/geotag/video_extractors/gpx.py +117 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/video_extractors/native.py +34 -31
- mapillary_tools-0.14.1/mapillary_tools/history.py +182 -0
- mapillary_tools-0.14.1/mapillary_tools/http.py +211 -0
- mapillary_tools-0.14.1/mapillary_tools/mp4/__init__.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/mp4/construct_mp4_parser.py +8 -2
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/process_geotag_properties.py +47 -35
- mapillary_tools-0.14.1/mapillary_tools/process_sequence_properties.py +714 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/sample_video.py +8 -8
- mapillary_tools-0.14.1/mapillary_tools/serializer/description.py +587 -0
- mapillary_tools-0.14.1/mapillary_tools/serializer/gpx.py +132 -0
- mapillary_tools-0.14.1/mapillary_tools/types.py +208 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/upload.py +327 -352
- mapillary_tools-0.14.1/mapillary_tools/upload_api_v4.py +248 -0
- mapillary_tools-0.14.1/mapillary_tools/uploader.py +1162 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/utils.py +57 -5
- mapillary_tools-0.14.0a2/README.md → mapillary_tools-0.14.1/mapillary_tools.egg-info/PKG-INFO +103 -16
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools.egg-info/SOURCES.txt +5 -5
- mapillary_tools-0.14.1/mapillary_tools.egg-info/requires.txt +11 -0
- mapillary_tools-0.14.1/pyproject.toml +85 -0
- mapillary_tools-0.14.1/setup.cfg +4 -0
- mapillary_tools-0.14.0a2/MANIFEST.in +0 -2
- mapillary_tools-0.14.0a2/mapillary_tools/__init__.py +0 -1
- mapillary_tools-0.14.0a2/mapillary_tools/api_v4.py +0 -372
- mapillary_tools-0.14.0a2/mapillary_tools/blackvue_parser.py +0 -138
- mapillary_tools-0.14.0a2/mapillary_tools/commands/upload.py +0 -59
- mapillary_tools-0.14.0a2/mapillary_tools/constants.py +0 -91
- mapillary_tools-0.14.0a2/mapillary_tools/ffmpeg.py +0 -452
- mapillary_tools-0.14.0a2/mapillary_tools/geotag/geotag_videos_from_exiftool.py +0 -97
- mapillary_tools-0.14.0a2/mapillary_tools/geotag/geotag_videos_from_gpx.py +0 -39
- mapillary_tools-0.14.0a2/mapillary_tools/geotag/video_extractors/gpx.py +0 -126
- mapillary_tools-0.14.0a2/mapillary_tools/history.py +0 -62
- mapillary_tools-0.14.0a2/mapillary_tools/process_sequence_properties.py +0 -699
- mapillary_tools-0.14.0a2/mapillary_tools/types.py +0 -774
- mapillary_tools-0.14.0a2/mapillary_tools/upload_api_v4.py +0 -195
- mapillary_tools-0.14.0a2/mapillary_tools/uploader.py +0 -581
- mapillary_tools-0.14.0a2/mapillary_tools.egg-info/requires.txt +0 -10
- mapillary_tools-0.14.0a2/requirements.txt +0 -10
- mapillary_tools-0.14.0a2/schema/image_description_schema.json +0 -189
- mapillary_tools-0.14.0a2/setup.cfg +0 -8
- mapillary_tools-0.14.0a2/setup.py +0 -60
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/LICENSE +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/camm/camm_builder.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/camm/camm_parser.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/__init__.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/authenticate.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/process.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/process_and_upload.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/sample_video.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/video_process.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/video_process_and_upload.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/commands/zip.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/exiftool_read.py +0 -0
- {mapillary_tools-0.14.0a2/mapillary_tools/mp4 → mapillary_tools-0.14.1/mapillary_tools/geotag}/__init__.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/geotag_images_from_exif.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/geotag_images_from_gpx_file.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/geotag_images_from_nmea_file.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/geotag_videos_from_video.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/image_extractors/base.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/image_extractors/exif.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/geotag/image_extractors/exiftool.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/gpmf/gpmf_gps_filter.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/gpmf/gpmf_parser.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/gpmf/gps_filter.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/ipc.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/mp4/io_utils.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/mp4/mp4_sample_parser.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/mp4/simple_mp4_builder.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/mp4/simple_mp4_parser.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools/telemetry.py +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools.egg-info/dependency_links.txt +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools.egg-info/entry_points.txt +0 -0
- {mapillary_tools-0.14.0a2 → mapillary_tools-0.14.1}/mapillary_tools.egg-info/top_level.txt +0 -0
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapillary_tools
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.1
|
|
4
4
|
Summary: Mapillary Image/Video Import Pipeline
|
|
5
|
-
|
|
6
|
-
Author: Mapillary
|
|
5
|
+
Author-email: Mapillary <support@mapillary.com>
|
|
7
6
|
License: BSD
|
|
8
|
-
|
|
7
|
+
Project-URL: Homepage, https://github.com/mapillary/mapillary_tools
|
|
8
|
+
Project-URL: Repository, https://github.com/mapillary/mapillary_tools
|
|
9
|
+
Project-URL: Issues, https://github.com/mapillary/mapillary_tools/issues
|
|
10
|
+
Keywords: mapillary,gis,computer vision,street view
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Requires-Python: >=3.9
|
|
9
22
|
Description-Content-Type: text/markdown
|
|
10
23
|
License-File: LICENSE
|
|
11
24
|
Requires-Dist: appdirs<2.0.0,>=1.4.4
|
|
12
|
-
Requires-Dist: construct
|
|
13
|
-
Requires-Dist: exifread
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: construct~=2.10.0
|
|
26
|
+
Requires-Dist: exifread~=3.0
|
|
27
|
+
Requires-Dist: gpxpy~=1.6.0
|
|
28
|
+
Requires-Dist: humanize>=4.12.3
|
|
29
|
+
Requires-Dist: jsonschema~=4.17.0
|
|
30
|
+
Requires-Dist: piexif~=1.1
|
|
16
31
|
Requires-Dist: pynmea2<2.0.0,>=1.12.0
|
|
17
32
|
Requires-Dist: requests[socks]<3.0.0,>=2.20.0
|
|
18
33
|
Requires-Dist: tqdm<5.0,>=4.0
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist: jsonschema~=4.17.3
|
|
21
|
-
Dynamic: author
|
|
22
|
-
Dynamic: description
|
|
23
|
-
Dynamic: description-content-type
|
|
24
|
-
Dynamic: home-page
|
|
25
|
-
Dynamic: license
|
|
34
|
+
Requires-Dist: typing-extensions>=4.12.2
|
|
26
35
|
Dynamic: license-file
|
|
27
|
-
Dynamic: requires-dist
|
|
28
|
-
Dynamic: requires-python
|
|
29
|
-
Dynamic: summary
|
|
30
36
|
|
|
31
37
|
<p align="center">
|
|
32
38
|
<a href="https://github.com/mapillary/mapillary_tools/">
|
|
@@ -58,18 +64,40 @@ mapillary_tools --help
|
|
|
58
64
|
<!--ts-->
|
|
59
65
|
|
|
60
66
|
- [Supported File Formats](#supported-file-formats)
|
|
67
|
+
- [Image Formats](#image-formats)
|
|
68
|
+
- [Video Formats](#video-formats)
|
|
61
69
|
- [Installation](#installation)
|
|
70
|
+
- [Standalone Executable](#standalone-executable)
|
|
71
|
+
- [Installing via pip](#installing-via-pip)
|
|
72
|
+
- [Installing on Android Devices](#installing-on-android-devices)
|
|
62
73
|
- [Usage](#usage)
|
|
63
74
|
- [Process and Upload](#process-and-upload)
|
|
64
75
|
- [Process](#process)
|
|
65
76
|
- [Upload](#upload)
|
|
66
77
|
- [Advanced Usage](#advanced-usage)
|
|
67
78
|
- [Local Video Processing](#local-video-processing)
|
|
79
|
+
- [Install FFmpeg](#install-ffmpeg)
|
|
80
|
+
- [Video Processing](#video-processing)
|
|
68
81
|
- [Geotagging with GPX](#geotagging-with-gpx)
|
|
82
|
+
- [New video geotagging features (experimental)](#new-video-geotagging-features-experimental)
|
|
83
|
+
- [Usage](#usage-1)
|
|
84
|
+
- [Examples](#examples)
|
|
85
|
+
- [Generic supported videos](#generic-supported-videos)
|
|
86
|
+
- [External GPX](#external-gpx)
|
|
87
|
+
- [Insta360 stitched videos](#insta360-stitched-videos)
|
|
88
|
+
- [Limitations of `--video_geotag_source`](#limitations-of---video_geotag_source)
|
|
69
89
|
- [Authenticate](#authenticate)
|
|
90
|
+
- [Examples](#examples-1)
|
|
70
91
|
- [Image Description](#image-description)
|
|
71
92
|
- [Zip Images](#zip-images)
|
|
72
93
|
- [Development](#development)
|
|
94
|
+
- [Setup](#setup)
|
|
95
|
+
- [Option 1: Using uv (Recommended)](#option-1-using-uv-recommended)
|
|
96
|
+
- [Option 2: Using pip with virtual environment](#option-2-using-pip-with-virtual-environment)
|
|
97
|
+
- [Running the code](#running-the-code)
|
|
98
|
+
- [Tests](#tests)
|
|
99
|
+
- [Code Quality](#code-quality)
|
|
100
|
+
- [Release and Build](#release-and-build)
|
|
73
101
|
|
|
74
102
|
<!--te-->
|
|
75
103
|
|
|
@@ -495,29 +523,49 @@ git clone git@github.com:mapillary/mapillary_tools.git
|
|
|
495
523
|
cd mapillary_tools
|
|
496
524
|
```
|
|
497
525
|
|
|
498
|
-
|
|
526
|
+
### Option 1: Using uv (Recommended)
|
|
527
|
+
|
|
528
|
+
Use [uv](https://docs.astral.sh/uv/) - a fast Python package manager.
|
|
529
|
+
|
|
530
|
+
Install the project in development mode with all dependencies:
|
|
499
531
|
|
|
500
532
|
```sh
|
|
501
|
-
|
|
533
|
+
# Install the project and development dependencies
|
|
534
|
+
uv sync --group dev
|
|
535
|
+
|
|
536
|
+
# Activate the virtual environment
|
|
537
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
502
538
|
```
|
|
503
539
|
|
|
504
|
-
|
|
540
|
+
### Option 2: Using pip with virtual environment
|
|
541
|
+
|
|
542
|
+
Set up a virtual environment (recommended):
|
|
505
543
|
|
|
506
544
|
```sh
|
|
507
|
-
|
|
508
|
-
|
|
545
|
+
python -m venv .venv
|
|
546
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
509
547
|
```
|
|
510
548
|
|
|
511
|
-
|
|
549
|
+
Install the project in development mode:
|
|
512
550
|
|
|
513
551
|
```sh
|
|
514
|
-
|
|
552
|
+
# Install the project and all dependencies in editable mode
|
|
553
|
+
pip install -e .
|
|
554
|
+
|
|
555
|
+
# Install development dependencies
|
|
556
|
+
pip install --group dev
|
|
515
557
|
```
|
|
516
558
|
|
|
559
|
+
## Running the code
|
|
560
|
+
|
|
517
561
|
Run the code from the repository:
|
|
518
562
|
|
|
519
563
|
```sh
|
|
520
|
-
|
|
564
|
+
# If you have mapillary_tools installed in editable mode
|
|
565
|
+
mapillary_tools --version
|
|
566
|
+
|
|
567
|
+
# Alternatively
|
|
568
|
+
python -m mapillary_tools.commands --version
|
|
521
569
|
```
|
|
522
570
|
|
|
523
571
|
## Tests
|
|
@@ -525,19 +573,28 @@ python3 -m mapillary_tools.commands --version
|
|
|
525
573
|
Run tests:
|
|
526
574
|
|
|
527
575
|
```sh
|
|
528
|
-
#
|
|
529
|
-
|
|
530
|
-
#
|
|
531
|
-
|
|
576
|
+
# Test all cases
|
|
577
|
+
pytest -s -vv tests
|
|
578
|
+
# Or test a single case specifically
|
|
579
|
+
pytest -s -vv tests/unit/test_camm_parser.py::test_build_and_parse
|
|
532
580
|
```
|
|
533
581
|
|
|
534
|
-
|
|
582
|
+
## Code Quality
|
|
583
|
+
|
|
584
|
+
Run code formatting and linting:
|
|
535
585
|
|
|
536
586
|
```sh
|
|
537
|
-
#
|
|
538
|
-
|
|
539
|
-
|
|
587
|
+
# Format code with ruff
|
|
588
|
+
ruff format mapillary_tools tests
|
|
589
|
+
|
|
590
|
+
# Lint code with ruff
|
|
591
|
+
ruff check mapillary_tools tests
|
|
592
|
+
|
|
593
|
+
# Sort imports with usort
|
|
540
594
|
usort format mapillary_tools tests
|
|
595
|
+
|
|
596
|
+
# Type checking with mypy
|
|
597
|
+
mypy mapillary_tools
|
|
541
598
|
```
|
|
542
599
|
|
|
543
600
|
## Release and Build
|
mapillary_tools-0.14.0a2/mapillary_tools.egg-info/PKG-INFO → mapillary_tools-0.14.1/README.md
RENAMED
|
@@ -1,33 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mapillary_tools
|
|
3
|
-
Version: 0.14.0a2
|
|
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.8
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
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<2.0.0,>=1.12.0
|
|
17
|
-
Requires-Dist: requests[socks]<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
|
-
Dynamic: author
|
|
22
|
-
Dynamic: description
|
|
23
|
-
Dynamic: description-content-type
|
|
24
|
-
Dynamic: home-page
|
|
25
|
-
Dynamic: license
|
|
26
|
-
Dynamic: license-file
|
|
27
|
-
Dynamic: requires-dist
|
|
28
|
-
Dynamic: requires-python
|
|
29
|
-
Dynamic: summary
|
|
30
|
-
|
|
31
1
|
<p align="center">
|
|
32
2
|
<a href="https://github.com/mapillary/mapillary_tools/">
|
|
33
3
|
<img src="https://raw.githubusercontent.com/mapillary/mapillary_tools/main/docs/images/logo.png">
|
|
@@ -58,18 +28,40 @@ mapillary_tools --help
|
|
|
58
28
|
<!--ts-->
|
|
59
29
|
|
|
60
30
|
- [Supported File Formats](#supported-file-formats)
|
|
31
|
+
- [Image Formats](#image-formats)
|
|
32
|
+
- [Video Formats](#video-formats)
|
|
61
33
|
- [Installation](#installation)
|
|
34
|
+
- [Standalone Executable](#standalone-executable)
|
|
35
|
+
- [Installing via pip](#installing-via-pip)
|
|
36
|
+
- [Installing on Android Devices](#installing-on-android-devices)
|
|
62
37
|
- [Usage](#usage)
|
|
63
38
|
- [Process and Upload](#process-and-upload)
|
|
64
39
|
- [Process](#process)
|
|
65
40
|
- [Upload](#upload)
|
|
66
41
|
- [Advanced Usage](#advanced-usage)
|
|
67
42
|
- [Local Video Processing](#local-video-processing)
|
|
43
|
+
- [Install FFmpeg](#install-ffmpeg)
|
|
44
|
+
- [Video Processing](#video-processing)
|
|
68
45
|
- [Geotagging with GPX](#geotagging-with-gpx)
|
|
46
|
+
- [New video geotagging features (experimental)](#new-video-geotagging-features-experimental)
|
|
47
|
+
- [Usage](#usage-1)
|
|
48
|
+
- [Examples](#examples)
|
|
49
|
+
- [Generic supported videos](#generic-supported-videos)
|
|
50
|
+
- [External GPX](#external-gpx)
|
|
51
|
+
- [Insta360 stitched videos](#insta360-stitched-videos)
|
|
52
|
+
- [Limitations of `--video_geotag_source`](#limitations-of---video_geotag_source)
|
|
69
53
|
- [Authenticate](#authenticate)
|
|
54
|
+
- [Examples](#examples-1)
|
|
70
55
|
- [Image Description](#image-description)
|
|
71
56
|
- [Zip Images](#zip-images)
|
|
72
57
|
- [Development](#development)
|
|
58
|
+
- [Setup](#setup)
|
|
59
|
+
- [Option 1: Using uv (Recommended)](#option-1-using-uv-recommended)
|
|
60
|
+
- [Option 2: Using pip with virtual environment](#option-2-using-pip-with-virtual-environment)
|
|
61
|
+
- [Running the code](#running-the-code)
|
|
62
|
+
- [Tests](#tests)
|
|
63
|
+
- [Code Quality](#code-quality)
|
|
64
|
+
- [Release and Build](#release-and-build)
|
|
73
65
|
|
|
74
66
|
<!--te-->
|
|
75
67
|
|
|
@@ -495,29 +487,49 @@ git clone git@github.com:mapillary/mapillary_tools.git
|
|
|
495
487
|
cd mapillary_tools
|
|
496
488
|
```
|
|
497
489
|
|
|
498
|
-
|
|
490
|
+
### Option 1: Using uv (Recommended)
|
|
491
|
+
|
|
492
|
+
Use [uv](https://docs.astral.sh/uv/) - a fast Python package manager.
|
|
493
|
+
|
|
494
|
+
Install the project in development mode with all dependencies:
|
|
499
495
|
|
|
500
496
|
```sh
|
|
501
|
-
|
|
497
|
+
# Install the project and development dependencies
|
|
498
|
+
uv sync --group dev
|
|
499
|
+
|
|
500
|
+
# Activate the virtual environment
|
|
501
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
502
502
|
```
|
|
503
503
|
|
|
504
|
-
|
|
504
|
+
### Option 2: Using pip with virtual environment
|
|
505
|
+
|
|
506
|
+
Set up a virtual environment (recommended):
|
|
505
507
|
|
|
506
508
|
```sh
|
|
507
|
-
|
|
508
|
-
|
|
509
|
+
python -m venv .venv
|
|
510
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
509
511
|
```
|
|
510
512
|
|
|
511
|
-
|
|
513
|
+
Install the project in development mode:
|
|
512
514
|
|
|
513
515
|
```sh
|
|
514
|
-
|
|
516
|
+
# Install the project and all dependencies in editable mode
|
|
517
|
+
pip install -e .
|
|
518
|
+
|
|
519
|
+
# Install development dependencies
|
|
520
|
+
pip install --group dev
|
|
515
521
|
```
|
|
516
522
|
|
|
523
|
+
## Running the code
|
|
524
|
+
|
|
517
525
|
Run the code from the repository:
|
|
518
526
|
|
|
519
527
|
```sh
|
|
520
|
-
|
|
528
|
+
# If you have mapillary_tools installed in editable mode
|
|
529
|
+
mapillary_tools --version
|
|
530
|
+
|
|
531
|
+
# Alternatively
|
|
532
|
+
python -m mapillary_tools.commands --version
|
|
521
533
|
```
|
|
522
534
|
|
|
523
535
|
## Tests
|
|
@@ -525,19 +537,28 @@ python3 -m mapillary_tools.commands --version
|
|
|
525
537
|
Run tests:
|
|
526
538
|
|
|
527
539
|
```sh
|
|
528
|
-
#
|
|
529
|
-
|
|
530
|
-
#
|
|
531
|
-
|
|
540
|
+
# Test all cases
|
|
541
|
+
pytest -s -vv tests
|
|
542
|
+
# Or test a single case specifically
|
|
543
|
+
pytest -s -vv tests/unit/test_camm_parser.py::test_build_and_parse
|
|
532
544
|
```
|
|
533
545
|
|
|
534
|
-
|
|
546
|
+
## Code Quality
|
|
547
|
+
|
|
548
|
+
Run code formatting and linting:
|
|
535
549
|
|
|
536
550
|
```sh
|
|
537
|
-
#
|
|
538
|
-
|
|
539
|
-
|
|
551
|
+
# Format code with ruff
|
|
552
|
+
ruff format mapillary_tools tests
|
|
553
|
+
|
|
554
|
+
# Lint code with ruff
|
|
555
|
+
ruff check mapillary_tools tests
|
|
556
|
+
|
|
557
|
+
# Sort imports with usort
|
|
540
558
|
usort format mapillary_tools tests
|
|
559
|
+
|
|
560
|
+
# Type checking with mypy
|
|
561
|
+
mypy mapillary_tools
|
|
541
562
|
```
|
|
542
563
|
|
|
543
564
|
## Release and Build
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.14.1"
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import typing as T
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from . import http
|
|
11
|
+
|
|
12
|
+
LOG = logging.getLogger(__name__)
|
|
13
|
+
MAPILLARY_CLIENT_TOKEN = os.getenv(
|
|
14
|
+
"MAPILLARY_CLIENT_TOKEN", "MLY|5675152195860640|6b02c72e6e3c801e5603ab0495623282"
|
|
15
|
+
)
|
|
16
|
+
MAPILLARY_GRAPH_API_ENDPOINT = os.getenv(
|
|
17
|
+
"MAPILLARY_GRAPH_API_ENDPOINT", "https://graph.mapillary.com"
|
|
18
|
+
)
|
|
19
|
+
REQUESTS_TIMEOUT: float = 60 # 1 minutes
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HTTPContentError(Exception):
|
|
23
|
+
"""
|
|
24
|
+
Raised when the HTTP response is ok (200) but the content is not as expected
|
|
25
|
+
e.g. not JSON or not a valid response.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, message: str, response: requests.Response):
|
|
29
|
+
self.response = response
|
|
30
|
+
super().__init__(message)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ClusterFileType(enum.Enum):
|
|
34
|
+
ZIP = "zip"
|
|
35
|
+
BLACKVUE = "mly_blackvue_video"
|
|
36
|
+
CAMM = "mly_camm_video"
|
|
37
|
+
MLY_BUNDLE_MANIFEST = "mly_bundle_manifest"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def create_user_session(user_access_token: str) -> requests.Session:
|
|
41
|
+
session = http.Session()
|
|
42
|
+
session.headers["Authorization"] = f"OAuth {user_access_token}"
|
|
43
|
+
return session
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def create_client_session(disable_logging: bool = False) -> requests.Session:
|
|
47
|
+
session = http.Session()
|
|
48
|
+
session.headers["Authorization"] = f"OAuth {MAPILLARY_CLIENT_TOKEN}"
|
|
49
|
+
if disable_logging:
|
|
50
|
+
session.disable_logging_request = True
|
|
51
|
+
session.disable_logging_response = True
|
|
52
|
+
return session
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def is_auth_error(resp: requests.Response) -> bool:
|
|
56
|
+
if resp.status_code in [401, 403]:
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
if resp.status_code in [400]:
|
|
60
|
+
try:
|
|
61
|
+
error_body = resp.json()
|
|
62
|
+
except Exception:
|
|
63
|
+
error_body = {}
|
|
64
|
+
|
|
65
|
+
type = error_body.get("debug_info", {}).get("type")
|
|
66
|
+
if type in ["NotAuthorizedError"]:
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def extract_auth_error_message(resp: requests.Response) -> str:
|
|
73
|
+
assert is_auth_error(resp), "has to be an auth error"
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
error_body = resp.json()
|
|
77
|
+
except Exception:
|
|
78
|
+
error_body = {}
|
|
79
|
+
|
|
80
|
+
# from Graph APIs
|
|
81
|
+
message = error_body.get("error", {}).get("message")
|
|
82
|
+
if message is not None:
|
|
83
|
+
return str(message)
|
|
84
|
+
|
|
85
|
+
# from upload service
|
|
86
|
+
message = error_body.get("debug_info", {}).get("message")
|
|
87
|
+
if message is not None:
|
|
88
|
+
return str(message)
|
|
89
|
+
|
|
90
|
+
return resp.text
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_upload_token(
|
|
94
|
+
client_session: requests.Session, email: str, password: str
|
|
95
|
+
) -> requests.Response:
|
|
96
|
+
url = f"{MAPILLARY_GRAPH_API_ENDPOINT}/login"
|
|
97
|
+
json_data = {"email": email, "password": password, "locale": "en_US"}
|
|
98
|
+
|
|
99
|
+
resp = client_session.post(url, json=json_data, timeout=REQUESTS_TIMEOUT)
|
|
100
|
+
resp.raise_for_status()
|
|
101
|
+
|
|
102
|
+
return resp
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def fetch_organization(
|
|
106
|
+
user_session: requests.Session, organization_id: int | str
|
|
107
|
+
) -> requests.Response:
|
|
108
|
+
url = f"{MAPILLARY_GRAPH_API_ENDPOINT}/{organization_id}"
|
|
109
|
+
params = {"fields": ",".join(["slug", "description", "name"])}
|
|
110
|
+
|
|
111
|
+
resp = user_session.get(url, params=params, timeout=REQUESTS_TIMEOUT)
|
|
112
|
+
resp.raise_for_status()
|
|
113
|
+
|
|
114
|
+
return resp
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def fetch_user_or_me(
|
|
118
|
+
user_session: requests.Session, user_id: int | str | None = None
|
|
119
|
+
) -> requests.Response:
|
|
120
|
+
if user_id is None:
|
|
121
|
+
url = f"{MAPILLARY_GRAPH_API_ENDPOINT}/me"
|
|
122
|
+
else:
|
|
123
|
+
url = f"{MAPILLARY_GRAPH_API_ENDPOINT}/{user_id}"
|
|
124
|
+
params = {"fields": ",".join(["id", "username"])}
|
|
125
|
+
|
|
126
|
+
resp = user_session.get(url, params=params, timeout=REQUESTS_TIMEOUT)
|
|
127
|
+
resp.raise_for_status()
|
|
128
|
+
|
|
129
|
+
return resp
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
ActionType = T.Literal[
|
|
133
|
+
"upload_started_upload", "upload_finished_upload", "upload_failed_upload"
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def log_event(
|
|
138
|
+
client_session: requests.Session, action_type: ActionType, properties: dict
|
|
139
|
+
) -> requests.Response:
|
|
140
|
+
url = f"{MAPILLARY_GRAPH_API_ENDPOINT}/logging"
|
|
141
|
+
json_data = {"action_type": action_type, "properties": properties}
|
|
142
|
+
|
|
143
|
+
resp = client_session.post(url, json=json_data, timeout=REQUESTS_TIMEOUT)
|
|
144
|
+
resp.raise_for_status()
|
|
145
|
+
|
|
146
|
+
return resp
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def finish_upload(
|
|
150
|
+
user_session: requests.Session,
|
|
151
|
+
file_handle: str,
|
|
152
|
+
cluster_filetype: ClusterFileType,
|
|
153
|
+
organization_id: int | str | None = None,
|
|
154
|
+
) -> requests.Response:
|
|
155
|
+
url = f"{MAPILLARY_GRAPH_API_ENDPOINT}/finish_upload"
|
|
156
|
+
json_data: dict[str, str | int] = {
|
|
157
|
+
"file_handle": file_handle,
|
|
158
|
+
"file_type": cluster_filetype.value,
|
|
159
|
+
}
|
|
160
|
+
if organization_id is not None:
|
|
161
|
+
json_data["organization_id"] = organization_id
|
|
162
|
+
|
|
163
|
+
resp = user_session.post(url, json=json_data, timeout=REQUESTS_TIMEOUT)
|
|
164
|
+
resp.raise_for_status()
|
|
165
|
+
|
|
166
|
+
return resp
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def jsonify_response(resp: requests.Response) -> T.Any:
|
|
170
|
+
"""
|
|
171
|
+
Convert the response to JSON, raising HTTPContentError if the response is not JSON.
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
return resp.json()
|
|
175
|
+
except requests.JSONDecodeError as ex:
|
|
176
|
+
raise HTTPContentError("Invalid JSON response", resp) from ex
|