mapillary-tools 0.14.0a1__py3-none-any.whl → 0.14.0b1__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 +5 -4
- mapillary_tools/authenticate.py +9 -9
- mapillary_tools/blackvue_parser.py +79 -22
- mapillary_tools/camm/camm_parser.py +5 -5
- mapillary_tools/commands/__main__.py +1 -2
- mapillary_tools/config.py +41 -18
- mapillary_tools/constants.py +3 -2
- mapillary_tools/exceptions.py +1 -1
- mapillary_tools/exif_read.py +65 -65
- mapillary_tools/exif_write.py +7 -7
- mapillary_tools/exiftool_read.py +23 -46
- mapillary_tools/exiftool_read_video.py +88 -49
- mapillary_tools/exiftool_runner.py +4 -24
- mapillary_tools/ffmpeg.py +417 -242
- mapillary_tools/geo.py +4 -21
- mapillary_tools/geotag/__init__.py +0 -1
- mapillary_tools/geotag/{geotag_from_generic.py → base.py} +34 -50
- mapillary_tools/geotag/factory.py +105 -103
- mapillary_tools/geotag/geotag_images_from_exif.py +15 -51
- mapillary_tools/geotag/geotag_images_from_exiftool.py +118 -63
- mapillary_tools/geotag/geotag_images_from_gpx.py +33 -16
- mapillary_tools/geotag/geotag_images_from_gpx_file.py +2 -34
- mapillary_tools/geotag/geotag_images_from_nmea_file.py +0 -3
- mapillary_tools/geotag/geotag_images_from_video.py +51 -14
- mapillary_tools/geotag/geotag_videos_from_exiftool.py +123 -0
- mapillary_tools/geotag/geotag_videos_from_gpx.py +35 -123
- mapillary_tools/geotag/geotag_videos_from_video.py +14 -147
- mapillary_tools/geotag/image_extractors/base.py +18 -0
- mapillary_tools/geotag/image_extractors/exif.py +60 -0
- mapillary_tools/geotag/image_extractors/exiftool.py +18 -0
- mapillary_tools/geotag/options.py +26 -3
- mapillary_tools/geotag/utils.py +62 -0
- mapillary_tools/geotag/video_extractors/base.py +18 -0
- mapillary_tools/geotag/video_extractors/exiftool.py +70 -0
- mapillary_tools/geotag/video_extractors/gpx.py +116 -0
- mapillary_tools/geotag/video_extractors/native.py +135 -0
- mapillary_tools/gpmf/gpmf_parser.py +16 -16
- mapillary_tools/gpmf/gps_filter.py +5 -3
- mapillary_tools/history.py +8 -3
- mapillary_tools/mp4/construct_mp4_parser.py +9 -8
- mapillary_tools/mp4/mp4_sample_parser.py +27 -27
- mapillary_tools/mp4/simple_mp4_builder.py +10 -9
- mapillary_tools/mp4/simple_mp4_parser.py +13 -12
- mapillary_tools/process_geotag_properties.py +21 -15
- mapillary_tools/process_sequence_properties.py +49 -49
- mapillary_tools/sample_video.py +15 -14
- mapillary_tools/serializer/description.py +587 -0
- mapillary_tools/serializer/gpx.py +132 -0
- mapillary_tools/telemetry.py +6 -5
- mapillary_tools/types.py +64 -635
- mapillary_tools/upload.py +176 -197
- mapillary_tools/upload_api_v4.py +94 -51
- mapillary_tools/uploader.py +284 -138
- mapillary_tools/utils.py +16 -18
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0b1.dist-info}/METADATA +87 -31
- mapillary_tools-0.14.0b1.dist-info/RECORD +75 -0
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0b1.dist-info}/WHEEL +1 -1
- mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -77
- mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -151
- mapillary_tools/video_data_extraction/cli_options.py +0 -22
- mapillary_tools/video_data_extraction/extract_video_data.py +0 -157
- mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -75
- mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -49
- mapillary_tools/video_data_extraction/extractors/camm_parser.py +0 -62
- mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +0 -74
- mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -52
- mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -52
- mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -58
- mapillary_tools/video_data_extraction/extractors/gpx_parser.py +0 -108
- mapillary_tools/video_data_extraction/extractors/nmea_parser.py +0 -24
- mapillary_tools/video_data_extraction/video_data_parser_factory.py +0 -39
- mapillary_tools-0.14.0a1.dist-info/RECORD +0 -78
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0b1.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0b1.dist-info}/licenses/LICENSE +0 -0
- {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0b1.dist-info}/top_level.txt +0 -0
mapillary_tools/utils.py
CHANGED
|
@@ -9,9 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
|
|
10
10
|
# Use "hashlib._Hash" instead of hashlib._Hash because:
|
|
11
11
|
# AttributeError: module 'hashlib' has no attribute '_Hash'
|
|
12
|
-
def md5sum_fp(
|
|
13
|
-
fp: T.IO[bytes], md5: T.Optional["hashlib._Hash"] = None
|
|
14
|
-
) -> "hashlib._Hash":
|
|
12
|
+
def md5sum_fp(fp: T.IO[bytes], md5: "hashlib._Hash | None" = None) -> "hashlib._Hash":
|
|
15
13
|
if md5 is None:
|
|
16
14
|
md5 = hashlib.md5()
|
|
17
15
|
while True:
|
|
@@ -93,12 +91,12 @@ def filter_video_samples(
|
|
|
93
91
|
|
|
94
92
|
|
|
95
93
|
def find_all_image_samples(
|
|
96
|
-
image_paths: T.
|
|
97
|
-
) ->
|
|
94
|
+
image_paths: T.Iterable[Path], video_paths: T.Iterable[Path]
|
|
95
|
+
) -> dict[Path, list[Path]]:
|
|
98
96
|
# TODO: not work with the same filenames, e.g. foo/hello.mp4 and bar/hello.mp4
|
|
99
97
|
video_basenames = {path.name: path for path in video_paths}
|
|
100
98
|
|
|
101
|
-
image_samples_by_video_path:
|
|
99
|
+
image_samples_by_video_path: dict[Path, list[Path]] = {}
|
|
102
100
|
for image_path in image_paths:
|
|
103
101
|
# If you want to walk an arbitrary filesystem path upwards,
|
|
104
102
|
# it is recommended to first call Path.resolve() so as to resolve symlinks and eliminate “..” components.
|
|
@@ -115,7 +113,7 @@ def find_all_image_samples(
|
|
|
115
113
|
|
|
116
114
|
|
|
117
115
|
def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]:
|
|
118
|
-
resolved_paths:
|
|
116
|
+
resolved_paths: set[Path] = set()
|
|
119
117
|
for p in paths:
|
|
120
118
|
resolved = p.resolve()
|
|
121
119
|
if resolved not in resolved_paths:
|
|
@@ -124,10 +122,10 @@ def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]:
|
|
|
124
122
|
|
|
125
123
|
|
|
126
124
|
def find_images(
|
|
127
|
-
import_paths: T.
|
|
125
|
+
import_paths: T.Iterable[Path],
|
|
128
126
|
skip_subfolders: bool = False,
|
|
129
|
-
) ->
|
|
130
|
-
image_paths:
|
|
127
|
+
) -> list[Path]:
|
|
128
|
+
image_paths: list[Path] = []
|
|
131
129
|
for path in import_paths:
|
|
132
130
|
if path.is_dir():
|
|
133
131
|
image_paths.extend(
|
|
@@ -142,10 +140,10 @@ def find_images(
|
|
|
142
140
|
|
|
143
141
|
|
|
144
142
|
def find_videos(
|
|
145
|
-
import_paths: T.
|
|
143
|
+
import_paths: T.Iterable[Path],
|
|
146
144
|
skip_subfolders: bool = False,
|
|
147
|
-
) ->
|
|
148
|
-
video_paths:
|
|
145
|
+
) -> list[Path]:
|
|
146
|
+
video_paths: list[Path] = []
|
|
149
147
|
for path in import_paths:
|
|
150
148
|
if path.is_dir():
|
|
151
149
|
video_paths.extend(
|
|
@@ -160,10 +158,10 @@ def find_videos(
|
|
|
160
158
|
|
|
161
159
|
|
|
162
160
|
def find_zipfiles(
|
|
163
|
-
import_paths: T.
|
|
161
|
+
import_paths: T.Iterable[Path],
|
|
164
162
|
skip_subfolders: bool = False,
|
|
165
|
-
) ->
|
|
166
|
-
zip_paths:
|
|
163
|
+
) -> list[Path]:
|
|
164
|
+
zip_paths: list[Path] = []
|
|
167
165
|
for path in import_paths:
|
|
168
166
|
if path.is_dir():
|
|
169
167
|
zip_paths.extend(
|
|
@@ -177,8 +175,8 @@ def find_zipfiles(
|
|
|
177
175
|
return list(deduplicate_paths(zip_paths))
|
|
178
176
|
|
|
179
177
|
|
|
180
|
-
def find_xml_files(import_paths: T.
|
|
181
|
-
xml_paths:
|
|
178
|
+
def find_xml_files(import_paths: T.Iterable[Path]) -> list[Path]:
|
|
179
|
+
xml_paths: list[Path] = []
|
|
182
180
|
for path in import_paths:
|
|
183
181
|
if path.is_dir():
|
|
184
182
|
# XML could be hidden in hidden folders
|
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapillary_tools
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.0b1
|
|
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
25
|
Requires-Dist: construct<3.0.0,>=2.10.0
|
|
13
26
|
Requires-Dist: exifread==2.3.2
|
|
14
|
-
Requires-Dist: piexif==1.1.3
|
|
15
27
|
Requires-Dist: gpxpy<1.6.0,>=1.5.0
|
|
28
|
+
Requires-Dist: jsonschema~=4.17.3
|
|
29
|
+
Requires-Dist: piexif==1.1.3
|
|
16
30
|
Requires-Dist: pynmea2<2.0.0,>=1.12.0
|
|
17
31
|
Requires-Dist: requests[socks]<3.0.0,>=2.20.0
|
|
18
32
|
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
|
|
33
|
+
Requires-Dist: typing-extensions>=4.12.2
|
|
26
34
|
Dynamic: license-file
|
|
27
|
-
Dynamic: requires-dist
|
|
28
|
-
Dynamic: requires-python
|
|
29
|
-
Dynamic: summary
|
|
30
35
|
|
|
31
36
|
<p align="center">
|
|
32
37
|
<a href="https://github.com/mapillary/mapillary_tools/">
|
|
@@ -58,18 +63,40 @@ mapillary_tools --help
|
|
|
58
63
|
<!--ts-->
|
|
59
64
|
|
|
60
65
|
- [Supported File Formats](#supported-file-formats)
|
|
66
|
+
- [Image Formats](#image-formats)
|
|
67
|
+
- [Video Formats](#video-formats)
|
|
61
68
|
- [Installation](#installation)
|
|
69
|
+
- [Standalone Executable](#standalone-executable)
|
|
70
|
+
- [Installing via pip](#installing-via-pip)
|
|
71
|
+
- [Installing on Android Devices](#installing-on-android-devices)
|
|
62
72
|
- [Usage](#usage)
|
|
63
73
|
- [Process and Upload](#process-and-upload)
|
|
64
74
|
- [Process](#process)
|
|
65
75
|
- [Upload](#upload)
|
|
66
76
|
- [Advanced Usage](#advanced-usage)
|
|
67
77
|
- [Local Video Processing](#local-video-processing)
|
|
78
|
+
- [Install FFmpeg](#install-ffmpeg)
|
|
79
|
+
- [Video Processing](#video-processing)
|
|
68
80
|
- [Geotagging with GPX](#geotagging-with-gpx)
|
|
81
|
+
- [New video geotagging features (experimental)](#new-video-geotagging-features-experimental)
|
|
82
|
+
- [Usage](#usage-1)
|
|
83
|
+
- [Examples](#examples)
|
|
84
|
+
- [Generic supported videos](#generic-supported-videos)
|
|
85
|
+
- [External GPX](#external-gpx)
|
|
86
|
+
- [Insta360 stitched videos](#insta360-stitched-videos)
|
|
87
|
+
- [Limitations of `--video_geotag_source`](#limitations-of---video_geotag_source)
|
|
69
88
|
- [Authenticate](#authenticate)
|
|
89
|
+
- [Examples](#examples-1)
|
|
70
90
|
- [Image Description](#image-description)
|
|
71
91
|
- [Zip Images](#zip-images)
|
|
72
92
|
- [Development](#development)
|
|
93
|
+
- [Setup](#setup)
|
|
94
|
+
- [Option 1: Using uv (Recommended)](#option-1-using-uv-recommended)
|
|
95
|
+
- [Option 2: Using pip with virtual environment](#option-2-using-pip-with-virtual-environment)
|
|
96
|
+
- [Running the code](#running-the-code)
|
|
97
|
+
- [Tests](#tests)
|
|
98
|
+
- [Code Quality](#code-quality)
|
|
99
|
+
- [Release and Build](#release-and-build)
|
|
73
100
|
|
|
74
101
|
<!--te-->
|
|
75
102
|
|
|
@@ -495,29 +522,49 @@ git clone git@github.com:mapillary/mapillary_tools.git
|
|
|
495
522
|
cd mapillary_tools
|
|
496
523
|
```
|
|
497
524
|
|
|
498
|
-
|
|
525
|
+
### Option 1: Using uv (Recommended)
|
|
526
|
+
|
|
527
|
+
Use [uv](https://docs.astral.sh/uv/) - a fast Python package manager.
|
|
528
|
+
|
|
529
|
+
Install the project in development mode with all dependencies:
|
|
499
530
|
|
|
500
531
|
```sh
|
|
501
|
-
|
|
532
|
+
# Install the project and development dependencies
|
|
533
|
+
uv sync --group dev
|
|
534
|
+
|
|
535
|
+
# Activate the virtual environment
|
|
536
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
502
537
|
```
|
|
503
538
|
|
|
504
|
-
|
|
539
|
+
### Option 2: Using pip with virtual environment
|
|
540
|
+
|
|
541
|
+
Set up a virtual environment (recommended):
|
|
505
542
|
|
|
506
543
|
```sh
|
|
507
|
-
|
|
508
|
-
|
|
544
|
+
python -m venv .venv
|
|
545
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
509
546
|
```
|
|
510
547
|
|
|
511
|
-
|
|
548
|
+
Install the project in development mode:
|
|
512
549
|
|
|
513
550
|
```sh
|
|
514
|
-
|
|
551
|
+
# Install the project and all dependencies in editable mode
|
|
552
|
+
pip install -e .
|
|
553
|
+
|
|
554
|
+
# Install development dependencies
|
|
555
|
+
pip install --group dev
|
|
515
556
|
```
|
|
516
557
|
|
|
558
|
+
## Running the code
|
|
559
|
+
|
|
517
560
|
Run the code from the repository:
|
|
518
561
|
|
|
519
562
|
```sh
|
|
520
|
-
|
|
563
|
+
# If you have mapillary_tools installed in editable mode
|
|
564
|
+
mapillary_tools --version
|
|
565
|
+
|
|
566
|
+
# Alternatively
|
|
567
|
+
python -m mapillary_tools.commands --version
|
|
521
568
|
```
|
|
522
569
|
|
|
523
570
|
## Tests
|
|
@@ -525,19 +572,28 @@ python3 -m mapillary_tools.commands --version
|
|
|
525
572
|
Run tests:
|
|
526
573
|
|
|
527
574
|
```sh
|
|
528
|
-
#
|
|
529
|
-
|
|
530
|
-
#
|
|
531
|
-
|
|
575
|
+
# Test all cases
|
|
576
|
+
pytest -s -vv tests
|
|
577
|
+
# Or test a single case specifically
|
|
578
|
+
pytest -s -vv tests/unit/test_camm_parser.py::test_build_and_parse
|
|
532
579
|
```
|
|
533
580
|
|
|
534
|
-
|
|
581
|
+
## Code Quality
|
|
582
|
+
|
|
583
|
+
Run code formatting and linting:
|
|
535
584
|
|
|
536
585
|
```sh
|
|
537
|
-
#
|
|
538
|
-
|
|
539
|
-
|
|
586
|
+
# Format code with ruff
|
|
587
|
+
ruff format mapillary_tools tests
|
|
588
|
+
|
|
589
|
+
# Lint code with ruff
|
|
590
|
+
ruff check mapillary_tools tests
|
|
591
|
+
|
|
592
|
+
# Sort imports with usort
|
|
540
593
|
usort format mapillary_tools tests
|
|
594
|
+
|
|
595
|
+
# Type checking with mypy
|
|
596
|
+
mypy mapillary_tools
|
|
541
597
|
```
|
|
542
598
|
|
|
543
599
|
## Release and Build
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
mapillary_tools/__init__.py,sha256=BXYNPTo5QIOW_UpAqv3arB1SDKpH8qZZAzzNEEAG4Io,21
|
|
2
|
+
mapillary_tools/api_v4.py,sha256=2Xb72FcddR0KEohH_zulRlHaGzb1BgqYt84r0IiHr4c,10139
|
|
3
|
+
mapillary_tools/authenticate.py,sha256=rO39j--CSGTXnYWOKOgkXKvK7l7p7Nnh1bnbihDyQ_U,11094
|
|
4
|
+
mapillary_tools/blackvue_parser.py,sha256=ea2JtU9MWU6yB0bQlF970_Of0bJVofSTRq1P30WKW-0,5623
|
|
5
|
+
mapillary_tools/config.py,sha256=97zsyPnZXGincEkM__c6UNWH25EMTtRKZGsp6vBpHy8,3326
|
|
6
|
+
mapillary_tools/constants.py,sha256=KvTizvc4PLQhU7RDB3Eabgr2B4cIvRfV4mCM05YeB9Y,3859
|
|
7
|
+
mapillary_tools/exceptions.py,sha256=P8vEh3pfOqyzJgOiQiRAcWsZTrcawjic7i4v3k335tM,2411
|
|
8
|
+
mapillary_tools/exif_read.py,sha256=gagyRv06RkZPRHQhyV-5-uQzxHgxOsVqUX2aC8QmCyM,32145
|
|
9
|
+
mapillary_tools/exif_write.py,sha256=Ue0ngLKCfuLQM7X4h5ETjSInUDcyEBWOR6v8ko7DJ1o,8769
|
|
10
|
+
mapillary_tools/exiftool_read.py,sha256=5uatYE9mgbg2d9NAnPfX22nSRBjXhJ9ayMqNMd4QwGM,15779
|
|
11
|
+
mapillary_tools/exiftool_read_video.py,sha256=23O_bjUOVq6j7i3xMz6fY-XIEsjinsCejK_0nrbFyOM,16084
|
|
12
|
+
mapillary_tools/exiftool_runner.py,sha256=g4gSyqeh3D6EnMJ-c3s-RnO2EP_jD354Qkaz0Y-4D04,1658
|
|
13
|
+
mapillary_tools/ffmpeg.py,sha256=akpvvsjAR-Iiv-hOrUoJvPM9vUU3JqMQ5HJL1_NgwB8,22908
|
|
14
|
+
mapillary_tools/geo.py,sha256=MSfkjSAgTAeuBqncQ8-QXvDuJ7ar7bTt5P_e9HDIgVs,10604
|
|
15
|
+
mapillary_tools/history.py,sha256=5Zk4iXSXny9kImzOKrnZmZ3sDjXtCcnJC5pzqHcf-Vk,1753
|
|
16
|
+
mapillary_tools/ipc.py,sha256=DwWQb9hNshx0bg0Fo5NjY0mXjs-FkbR6tIQmjMgMtmg,1089
|
|
17
|
+
mapillary_tools/process_geotag_properties.py,sha256=ND4Mz8NIIhbiD0aFk4Eo33WRpt3VYwCBbOBaxmP-rbU,14246
|
|
18
|
+
mapillary_tools/process_sequence_properties.py,sha256=XqddHPsj5EjIKbu8omqPcufWdEbrMS_MNf8se5DXL1M,23480
|
|
19
|
+
mapillary_tools/sample_video.py,sha256=gViQQ4CSHihzk42kGq_Rx4sr9sBPsGvPXXx2Bgj5grU,13980
|
|
20
|
+
mapillary_tools/telemetry.py,sha256=lL6qQbtOZft4DZZrCNK3njlwHT_30zLyYS_YRN5pgHY,1568
|
|
21
|
+
mapillary_tools/types.py,sha256=pIU2wcxiOUWT5Pd05pgNzY9EVEDlwoldtlF2IIYYvE0,5909
|
|
22
|
+
mapillary_tools/upload.py,sha256=s29bRwYuaZuTuy3TIBspgCwA8ueYN-RiDvOFkWI-kfY,24411
|
|
23
|
+
mapillary_tools/upload_api_v4.py,sha256=4aw0uMkKL-zhQ-jPv0GgDvnYSX1DA8zRk4TOFyU9EJA,7753
|
|
24
|
+
mapillary_tools/uploader.py,sha256=KUEwHdIZ8xN2JMUvUG--T_S_VgREJuvARqNWsnPpKsk,23202
|
|
25
|
+
mapillary_tools/utils.py,sha256=ft-TrqgwWq1a-HpQqnCWIortZbjjTOywllPi8HBA3FU,6642
|
|
26
|
+
mapillary_tools/camm/camm_builder.py,sha256=ub6Z9ijep8zAo1NOlU51Gxk95kQ2vfN58YgVCLmNMRk,9211
|
|
27
|
+
mapillary_tools/camm/camm_parser.py,sha256=aNHP65hNXYQBWBTfhaj_S5XYzmAHhjwcAfGhbm83__o,18043
|
|
28
|
+
mapillary_tools/commands/__init__.py,sha256=41CFrPLGlG3566uhxssEF3TGAtSpADFPPcDMHbViU0E,171
|
|
29
|
+
mapillary_tools/commands/__main__.py,sha256=5gwngScbvrSNF2S1CA5w_qZgxW_kFevu5NQ89U9u1ZE,5046
|
|
30
|
+
mapillary_tools/commands/authenticate.py,sha256=yqtpHMYzkyBrrchj6MARxB0ywUTfqCEOPkMbkyaO9Ks,1344
|
|
31
|
+
mapillary_tools/commands/process.py,sha256=Japc6_P0B_8HzoM8_82P3_YAiyBBaQZXS9TZF46pbMM,9771
|
|
32
|
+
mapillary_tools/commands/process_and_upload.py,sha256=-RB_86a5xKfQ7Ye79dh6yyJQpZg2xnJZAWOJsUNbUtQ,1041
|
|
33
|
+
mapillary_tools/commands/sample_video.py,sha256=jtnrZrsqqv5eYV1chNTas7RhfbeKBqbAUDUNRFjF01w,3253
|
|
34
|
+
mapillary_tools/commands/upload.py,sha256=YnA1iSvNcFeV7kMs1p4TdaPBUWUFYb7X-vFhQeSz0eI,2005
|
|
35
|
+
mapillary_tools/commands/video_process.py,sha256=-wQeeIwWXPmy81HQHam5A0huMLRHknkEFa_V1OwElU4,890
|
|
36
|
+
mapillary_tools/commands/video_process_and_upload.py,sha256=hOyq9L9TuD0JcqFSOOxdCdgsBA1iJ6fu1TtDbsUr8sI,1088
|
|
37
|
+
mapillary_tools/commands/zip.py,sha256=DVQuMLpbstwiy5o4pU_fBvM6eORuFjLeySd80AhHKU0,991
|
|
38
|
+
mapillary_tools/geotag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
mapillary_tools/geotag/base.py,sha256=19-3P5nbFehOxacTT042nQUITzGnXndgnW_mXLO5q9s,4948
|
|
40
|
+
mapillary_tools/geotag/factory.py,sha256=Yn8bf0_uA_ukF1Od_yrFIkBYnO0EFq-uvsVrbO2FUrM,9967
|
|
41
|
+
mapillary_tools/geotag/geotag_images_from_exif.py,sha256=a5fDOp4tlEoVNwrOCJBrvSPzu1LbyxL4RNrNDD0tPP0,608
|
|
42
|
+
mapillary_tools/geotag/geotag_images_from_exiftool.py,sha256=TTJb9tq1kYSPn_gwDDAcviueYwoUIbIEoLl-abAB8T8,5716
|
|
43
|
+
mapillary_tools/geotag/geotag_images_from_gpx.py,sha256=xh7lqQT-e3PMIisdRIkkfbWP-GrCRZF0KAo03V0ixEI,6401
|
|
44
|
+
mapillary_tools/geotag/geotag_images_from_gpx_file.py,sha256=HYQkwak32YBDuRrTNiIZOmE3iImCRc22HWWb485WRS4,1121
|
|
45
|
+
mapillary_tools/geotag/geotag_images_from_nmea_file.py,sha256=J8xj6ch9bMPRubJtsRGeb3sb9LyB0ZYy65NPEOVkUe8,1597
|
|
46
|
+
mapillary_tools/geotag/geotag_images_from_video.py,sha256=3NV3-NfSkxT0n_n8Ajqjab24x29H2vL98DpwJqnIvT8,4411
|
|
47
|
+
mapillary_tools/geotag/geotag_videos_from_exiftool.py,sha256=ge29zKj9jLQFLmmzb0IyE69wenuwJ3XGmMyANT0UIAI,4288
|
|
48
|
+
mapillary_tools/geotag/geotag_videos_from_gpx.py,sha256=IoV7asxl7dojF1lftvohm1jK_LboFg_mBz25GiV_CsY,1653
|
|
49
|
+
mapillary_tools/geotag/geotag_videos_from_video.py,sha256=T8XS4lVF2Wz4eDXNi5Vt076M5dxjxJXibVrWhqVvErs,863
|
|
50
|
+
mapillary_tools/geotag/options.py,sha256=0CSQUuhVE-WdAVQXWXjwMtxujW1yB6yxRkFmVKH8LkQ,5092
|
|
51
|
+
mapillary_tools/geotag/utils.py,sha256=0DenLVCLP-GZdk0rCFP0V1tjMGK4XAvYro3I_1RKgCA,1733
|
|
52
|
+
mapillary_tools/geotag/image_extractors/base.py,sha256=XoNrLCbJtd-MN-snbhv6zyr6zBfJRoJkntV0ptrh6qg,358
|
|
53
|
+
mapillary_tools/geotag/image_extractors/exif.py,sha256=cCUegbFqWxjAP4oOP1nZmwoJISWeKgjGO8h_t7nucHs,2079
|
|
54
|
+
mapillary_tools/geotag/image_extractors/exiftool.py,sha256=zokJmf-D2rPvASRJs3dZzEu7j82igpMOr4SE6Z1nsVg,497
|
|
55
|
+
mapillary_tools/geotag/video_extractors/base.py,sha256=KxyKxoh_mV-XBnJq3dSzNEt2rQqZAKjmS3GazsdYbnc,350
|
|
56
|
+
mapillary_tools/geotag/video_extractors/exiftool.py,sha256=mH9arsWBaArYhNMxnrRpoEa3YrMjSpCiZaz-baJRMCo,2305
|
|
57
|
+
mapillary_tools/geotag/video_extractors/gpx.py,sha256=jE3UQs6rTzIElPBvS7eKgR83JAZnbteXM1V5TbNCdyY,3753
|
|
58
|
+
mapillary_tools/geotag/video_extractors/native.py,sha256=4I0Vjo3xCaYuJktxUGHalEv-eW5h-4fHzgwhjImEuWs,4572
|
|
59
|
+
mapillary_tools/gpmf/gpmf_gps_filter.py,sha256=7cg8wEjC1DrujKY76FZguXsaPqTRkG9-t32OeuOJQIc,2755
|
|
60
|
+
mapillary_tools/gpmf/gpmf_parser.py,sha256=uOVXkwJxC3Y2YfTdzUDpt7Bh0pdVqa5u0WUuv7pEJEs,24670
|
|
61
|
+
mapillary_tools/gpmf/gps_filter.py,sha256=zhXkuvr0Dd1bxGTYBwvk6P7xasY_RLuWjHaX7CdBayc,3796
|
|
62
|
+
mapillary_tools/mp4/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
63
|
+
mapillary_tools/mp4/construct_mp4_parser.py,sha256=ArJ5C8iWJhHNPFkN048DXh6EwQ8LMfnTMwoNlKV9NoI,17191
|
|
64
|
+
mapillary_tools/mp4/io_utils.py,sha256=KZaJTlgFS27Oh3pcA5MKXYFoCifqgFaEZJyU6lb1jc4,5416
|
|
65
|
+
mapillary_tools/mp4/mp4_sample_parser.py,sha256=0ILTq8M6mXFTI3agKgljpvO9uYa7HXGUGZpdHT8a6ac,11547
|
|
66
|
+
mapillary_tools/mp4/simple_mp4_builder.py,sha256=9TUGk1hzI6mQFN1P30jwHL3dCYz3Zz7rsm8UBvMAqMc,12734
|
|
67
|
+
mapillary_tools/mp4/simple_mp4_parser.py,sha256=g3vvPhBoNu7anhVzC5_XQCV7IwfRWro1vJ6d6GyDkHE,6315
|
|
68
|
+
mapillary_tools/serializer/description.py,sha256=ZYg68FoA1oA6PPXRMx9RyxUCoCh3mKpG7cYTlcO3xKk,18368
|
|
69
|
+
mapillary_tools/serializer/gpx.py,sha256=fwnJ0KF8d8IWRoIGM70NrYDmS89akC3tyRnY3fYc-R8,4344
|
|
70
|
+
mapillary_tools-0.14.0b1.dist-info/licenses/LICENSE,sha256=l2D8cKfFmmJq_wcVq_JElPJrlvWQOzNWx7gMLINucxc,1292
|
|
71
|
+
mapillary_tools-0.14.0b1.dist-info/METADATA,sha256=jz6asGlxY7OFZbmqSHQ1_SxATrzJWMnPsElBl618HkE,22188
|
|
72
|
+
mapillary_tools-0.14.0b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
73
|
+
mapillary_tools-0.14.0b1.dist-info/entry_points.txt,sha256=A3f3LP-BO_P-U8Y29QfpT4jx6Mjk3sXjTi2Yew4bvj8,75
|
|
74
|
+
mapillary_tools-0.14.0b1.dist-info/top_level.txt,sha256=FbDkMgOrt1S70ho1WSBrOwzKOSkJFDwwqFOoY5-527s,16
|
|
75
|
+
mapillary_tools-0.14.0b1.dist-info/RECORD,,
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import typing as T
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
from .. import exiftool_read, types, utils
|
|
8
|
-
from . import (
|
|
9
|
-
geotag_images_from_exiftool,
|
|
10
|
-
geotag_images_from_video,
|
|
11
|
-
geotag_videos_from_exiftool_video,
|
|
12
|
-
)
|
|
13
|
-
from .geotag_from_generic import GeotagImagesFromGeneric
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
LOG = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class GeotagImagesFromExifToolBothImageAndVideo(GeotagImagesFromGeneric):
|
|
20
|
-
def __init__(
|
|
21
|
-
self,
|
|
22
|
-
image_paths: T.Sequence[Path],
|
|
23
|
-
xml_path: Path,
|
|
24
|
-
offset_time: float = 0.0,
|
|
25
|
-
num_processes: int | None = None,
|
|
26
|
-
):
|
|
27
|
-
super().__init__(image_paths, num_processes=num_processes)
|
|
28
|
-
self.xml_path = xml_path
|
|
29
|
-
self.offset_time = offset_time
|
|
30
|
-
|
|
31
|
-
def geotag_samples(self) -> list[types.ImageMetadataOrError]:
|
|
32
|
-
# Find all video paths in self.xml_path
|
|
33
|
-
rdf_description_by_path = exiftool_read.index_rdf_description_by_path(
|
|
34
|
-
[self.xml_path]
|
|
35
|
-
)
|
|
36
|
-
video_paths = utils.find_videos(
|
|
37
|
-
[Path(pathstr) for pathstr in rdf_description_by_path.keys()],
|
|
38
|
-
skip_subfolders=True,
|
|
39
|
-
)
|
|
40
|
-
# Find all video paths that have sample images
|
|
41
|
-
samples_by_video = utils.find_all_image_samples(self.image_paths, video_paths)
|
|
42
|
-
|
|
43
|
-
video_metadata_or_errors = (
|
|
44
|
-
geotag_videos_from_exiftool_video.GeotagVideosFromExifToolVideo(
|
|
45
|
-
list(samples_by_video.keys()),
|
|
46
|
-
self.xml_path,
|
|
47
|
-
num_processes=self.num_processes,
|
|
48
|
-
).to_description()
|
|
49
|
-
)
|
|
50
|
-
sample_paths = sum(samples_by_video.values(), [])
|
|
51
|
-
sample_metadata_or_errors = geotag_images_from_video.GeotagImagesFromVideo(
|
|
52
|
-
sample_paths,
|
|
53
|
-
video_metadata_or_errors,
|
|
54
|
-
offset_time=self.offset_time,
|
|
55
|
-
num_processes=self.num_processes,
|
|
56
|
-
).to_description()
|
|
57
|
-
|
|
58
|
-
return sample_metadata_or_errors
|
|
59
|
-
|
|
60
|
-
def to_description(self) -> list[types.ImageMetadataOrError]:
|
|
61
|
-
sample_metadata_or_errors = self.geotag_samples()
|
|
62
|
-
|
|
63
|
-
sample_paths = set(metadata.filename for metadata in sample_metadata_or_errors)
|
|
64
|
-
|
|
65
|
-
non_sample_paths = [
|
|
66
|
-
path for path in self.image_paths if path not in sample_paths
|
|
67
|
-
]
|
|
68
|
-
|
|
69
|
-
non_sample_metadata_or_errors = (
|
|
70
|
-
geotag_images_from_exiftool.GeotagImagesFromExifTool(
|
|
71
|
-
non_sample_paths,
|
|
72
|
-
self.xml_path,
|
|
73
|
-
num_processes=self.num_processes,
|
|
74
|
-
).to_description()
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
return sample_metadata_or_errors + non_sample_metadata_or_errors
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import typing as T
|
|
5
|
-
import xml.etree.ElementTree as ET
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
from .. import constants, exceptions, exiftool_read, geo, types, utils
|
|
9
|
-
from ..exiftool_read_video import ExifToolReadVideo
|
|
10
|
-
from ..exiftool_runner import ExiftoolRunner
|
|
11
|
-
from ..gpmf import gpmf_gps_filter
|
|
12
|
-
from ..telemetry import GPSPoint
|
|
13
|
-
from .geotag_from_generic import GenericVideoExtractor, GeotagVideosFromGeneric
|
|
14
|
-
|
|
15
|
-
LOG = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class VideoExifToolExtractor(GenericVideoExtractor):
|
|
19
|
-
def __init__(self, video_path: Path, element: ET.Element):
|
|
20
|
-
super().__init__(video_path)
|
|
21
|
-
self.element = element
|
|
22
|
-
|
|
23
|
-
def extract(self) -> types.VideoMetadataOrError:
|
|
24
|
-
exif = ExifToolReadVideo(ET.ElementTree(self.element))
|
|
25
|
-
|
|
26
|
-
make = exif.extract_make()
|
|
27
|
-
model = exif.extract_model()
|
|
28
|
-
|
|
29
|
-
is_gopro = make is not None and make.upper() in ["GOPRO"]
|
|
30
|
-
|
|
31
|
-
points = exif.extract_gps_track()
|
|
32
|
-
|
|
33
|
-
# ExifTool has no idea if GPS is not found or found but empty
|
|
34
|
-
if is_gopro:
|
|
35
|
-
if not points:
|
|
36
|
-
raise exceptions.MapillaryGPXEmptyError("Empty GPS data found")
|
|
37
|
-
|
|
38
|
-
# ExifTool (since 13.04) converts GPSSpeed for GoPro to km/h, so here we convert it back to m/s
|
|
39
|
-
for p in points:
|
|
40
|
-
if isinstance(p, GPSPoint) and p.ground_speed is not None:
|
|
41
|
-
p.ground_speed = p.ground_speed / 3.6
|
|
42
|
-
|
|
43
|
-
if isinstance(points[0], GPSPoint):
|
|
44
|
-
points = T.cast(
|
|
45
|
-
T.List[geo.Point],
|
|
46
|
-
gpmf_gps_filter.remove_noisy_points(
|
|
47
|
-
T.cast(T.List[GPSPoint], points)
|
|
48
|
-
),
|
|
49
|
-
)
|
|
50
|
-
if not points:
|
|
51
|
-
raise exceptions.MapillaryGPSNoiseError("GPS is too noisy")
|
|
52
|
-
|
|
53
|
-
if not points:
|
|
54
|
-
raise exceptions.MapillaryVideoGPSNotFoundError(
|
|
55
|
-
"No GPS data found from the video"
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
filetype = types.FileType.GOPRO if is_gopro else types.FileType.VIDEO
|
|
59
|
-
|
|
60
|
-
video_metadata = types.VideoMetadata(
|
|
61
|
-
self.video_path,
|
|
62
|
-
filesize=utils.get_file_size(self.video_path),
|
|
63
|
-
filetype=filetype,
|
|
64
|
-
points=points,
|
|
65
|
-
make=make,
|
|
66
|
-
model=model,
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
return video_metadata
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class GeotagVideosFromExifToolVideo(GeotagVideosFromGeneric):
|
|
73
|
-
def __init__(
|
|
74
|
-
self,
|
|
75
|
-
video_paths: T.Sequence[Path],
|
|
76
|
-
xml_path: Path,
|
|
77
|
-
num_processes: int | None = None,
|
|
78
|
-
):
|
|
79
|
-
super().__init__(video_paths, num_processes=num_processes)
|
|
80
|
-
self.xml_path = xml_path
|
|
81
|
-
|
|
82
|
-
def _generate_video_extractors(
|
|
83
|
-
self,
|
|
84
|
-
) -> T.Sequence[GenericVideoExtractor | types.ErrorMetadata]:
|
|
85
|
-
rdf_description_by_path = exiftool_read.index_rdf_description_by_path(
|
|
86
|
-
[self.xml_path]
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
results: list[VideoExifToolExtractor | types.ErrorMetadata] = []
|
|
90
|
-
|
|
91
|
-
for path in self.video_paths:
|
|
92
|
-
rdf_description = rdf_description_by_path.get(
|
|
93
|
-
exiftool_read.canonical_path(path)
|
|
94
|
-
)
|
|
95
|
-
if rdf_description is None:
|
|
96
|
-
exc = exceptions.MapillaryEXIFNotFoundError(
|
|
97
|
-
f"The {exiftool_read._DESCRIPTION_TAG} XML element for the video not found"
|
|
98
|
-
)
|
|
99
|
-
results.append(
|
|
100
|
-
types.describe_error_metadata(
|
|
101
|
-
exc, path, filetype=types.FileType.VIDEO
|
|
102
|
-
)
|
|
103
|
-
)
|
|
104
|
-
else:
|
|
105
|
-
results.append(VideoExifToolExtractor(path, rdf_description))
|
|
106
|
-
|
|
107
|
-
return results
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class GeotagVideosFromExifToolRunner(GeotagVideosFromGeneric):
|
|
111
|
-
def _generate_video_extractors(
|
|
112
|
-
self,
|
|
113
|
-
) -> T.Sequence[GenericVideoExtractor | types.ErrorMetadata]:
|
|
114
|
-
runner = ExiftoolRunner(constants.EXIFTOOL_PATH)
|
|
115
|
-
|
|
116
|
-
LOG.debug(
|
|
117
|
-
"Extracting XML from %d videos with exiftool command: %s",
|
|
118
|
-
len(self.video_paths),
|
|
119
|
-
" ".join(runner._build_args_read_stdin()),
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
try:
|
|
123
|
-
xml = runner.extract_xml(self.video_paths)
|
|
124
|
-
except FileNotFoundError as ex:
|
|
125
|
-
raise exceptions.MapillaryExiftoolNotFoundError(ex) from ex
|
|
126
|
-
|
|
127
|
-
rdf_description_by_path = (
|
|
128
|
-
exiftool_read.index_rdf_description_by_path_from_xml_element(
|
|
129
|
-
ET.fromstring(xml)
|
|
130
|
-
)
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
results: list[VideoExifToolExtractor | types.ErrorMetadata] = []
|
|
134
|
-
|
|
135
|
-
for path in self.video_paths:
|
|
136
|
-
rdf_description = rdf_description_by_path.get(
|
|
137
|
-
exiftool_read.canonical_path(path)
|
|
138
|
-
)
|
|
139
|
-
if rdf_description is None:
|
|
140
|
-
exc = exceptions.MapillaryEXIFNotFoundError(
|
|
141
|
-
f"The {exiftool_read._DESCRIPTION_TAG} XML element for the video not found"
|
|
142
|
-
)
|
|
143
|
-
results.append(
|
|
144
|
-
types.describe_error_metadata(
|
|
145
|
-
exc, path, filetype=types.FileType.VIDEO
|
|
146
|
-
)
|
|
147
|
-
)
|
|
148
|
-
else:
|
|
149
|
-
results.append(VideoExifToolExtractor(path, rdf_description))
|
|
150
|
-
|
|
151
|
-
return results
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import typing as T
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
known_parser_options = ["source", "pattern", "exiftool_path"]
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class CliParserOptions(T.TypedDict, total=False):
|
|
9
|
-
source: str
|
|
10
|
-
pattern: T.Optional[str]
|
|
11
|
-
exiftool_path: T.Optional[Path]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class CliOptions(T.TypedDict, total=False):
|
|
15
|
-
paths: T.Sequence[Path]
|
|
16
|
-
recursive: bool
|
|
17
|
-
geotag_sources_options: T.Sequence[CliParserOptions]
|
|
18
|
-
geotag_source_path: Path
|
|
19
|
-
exiftool_path: Path
|
|
20
|
-
num_processes: int
|
|
21
|
-
device_make: T.Optional[str]
|
|
22
|
-
device_model: T.Optional[str]
|