mapillary-tools 0.13.3a1__py3-none-any.whl → 0.14.0__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 +287 -22
- mapillary_tools/authenticate.py +326 -64
- mapillary_tools/blackvue_parser.py +195 -0
- mapillary_tools/camm/camm_builder.py +55 -97
- mapillary_tools/camm/camm_parser.py +429 -181
- mapillary_tools/commands/__main__.py +17 -8
- mapillary_tools/commands/authenticate.py +8 -1
- mapillary_tools/commands/process.py +27 -51
- mapillary_tools/commands/process_and_upload.py +19 -5
- mapillary_tools/commands/sample_video.py +2 -3
- mapillary_tools/commands/upload.py +44 -13
- mapillary_tools/commands/video_process_and_upload.py +19 -5
- mapillary_tools/config.py +65 -26
- mapillary_tools/constants.py +141 -18
- mapillary_tools/exceptions.py +37 -34
- mapillary_tools/exif_read.py +221 -116
- mapillary_tools/exif_write.py +10 -8
- mapillary_tools/exiftool_read.py +33 -42
- mapillary_tools/exiftool_read_video.py +97 -47
- mapillary_tools/exiftool_runner.py +57 -0
- mapillary_tools/ffmpeg.py +417 -242
- mapillary_tools/geo.py +158 -118
- mapillary_tools/geotag/__init__.py +0 -1
- mapillary_tools/geotag/base.py +147 -0
- mapillary_tools/geotag/factory.py +307 -0
- mapillary_tools/geotag/geotag_images_from_exif.py +14 -131
- mapillary_tools/geotag/geotag_images_from_exiftool.py +136 -85
- mapillary_tools/geotag/geotag_images_from_gpx.py +60 -124
- mapillary_tools/geotag/geotag_images_from_gpx_file.py +13 -126
- mapillary_tools/geotag/geotag_images_from_nmea_file.py +4 -5
- mapillary_tools/geotag/geotag_images_from_video.py +88 -51
- mapillary_tools/geotag/geotag_videos_from_exiftool.py +123 -0
- mapillary_tools/geotag/geotag_videos_from_gpx.py +52 -0
- mapillary_tools/geotag/geotag_videos_from_video.py +20 -185
- 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 +182 -0
- mapillary_tools/geotag/utils.py +52 -16
- 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 +160 -0
- mapillary_tools/{geotag → gpmf}/gpmf_parser.py +205 -182
- mapillary_tools/{geotag → gpmf}/gps_filter.py +5 -3
- mapillary_tools/history.py +134 -20
- mapillary_tools/mp4/construct_mp4_parser.py +17 -10
- mapillary_tools/mp4/io_utils.py +0 -1
- mapillary_tools/mp4/mp4_sample_parser.py +36 -28
- mapillary_tools/mp4/simple_mp4_builder.py +10 -9
- mapillary_tools/mp4/simple_mp4_parser.py +13 -22
- mapillary_tools/process_geotag_properties.py +184 -414
- mapillary_tools/process_sequence_properties.py +594 -225
- mapillary_tools/sample_video.py +20 -26
- mapillary_tools/serializer/description.py +587 -0
- mapillary_tools/serializer/gpx.py +132 -0
- mapillary_tools/telemetry.py +26 -13
- mapillary_tools/types.py +98 -611
- mapillary_tools/upload.py +408 -416
- mapillary_tools/upload_api_v4.py +172 -174
- mapillary_tools/uploader.py +804 -284
- mapillary_tools/utils.py +49 -18
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info}/METADATA +93 -35
- mapillary_tools-0.14.0.dist-info/RECORD +75 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info}/WHEEL +1 -1
- mapillary_tools/geotag/blackvue_parser.py +0 -118
- mapillary_tools/geotag/geotag_from_generic.py +0 -22
- mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -93
- mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -145
- mapillary_tools/video_data_extraction/cli_options.py +0 -22
- mapillary_tools/video_data_extraction/extract_video_data.py +0 -176
- mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -75
- mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -34
- mapillary_tools/video_data_extraction/extractors/camm_parser.py +0 -38
- mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +0 -71
- mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -53
- mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -52
- mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -43
- 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.13.3a1.dist-info/RECORD +0 -75
- /mapillary_tools/{geotag → gpmf}/gpmf_gps_filter.py +0 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info/licenses}/LICENSE +0 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info}/top_level.txt +0 -0
mapillary_tools/utils.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import hashlib
|
|
2
4
|
import os
|
|
3
5
|
import typing as T
|
|
6
|
+
from multiprocessing import Pool
|
|
4
7
|
from pathlib import Path
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
# Use "hashlib._Hash" instead of hashlib._Hash because:
|
|
8
11
|
# AttributeError: module 'hashlib' has no attribute '_Hash'
|
|
9
|
-
def md5sum_fp(
|
|
10
|
-
fp: T.IO[bytes], md5: T.Optional["hashlib._Hash"] = None
|
|
11
|
-
) -> "hashlib._Hash":
|
|
12
|
+
def md5sum_fp(fp: T.IO[bytes], md5: "hashlib._Hash | None" = None) -> "hashlib._Hash":
|
|
12
13
|
if md5 is None:
|
|
13
14
|
md5 = hashlib.md5()
|
|
14
15
|
while True:
|
|
@@ -90,12 +91,12 @@ def filter_video_samples(
|
|
|
90
91
|
|
|
91
92
|
|
|
92
93
|
def find_all_image_samples(
|
|
93
|
-
image_paths: T.
|
|
94
|
-
) ->
|
|
94
|
+
image_paths: T.Iterable[Path], video_paths: T.Iterable[Path]
|
|
95
|
+
) -> dict[Path, list[Path]]:
|
|
95
96
|
# TODO: not work with the same filenames, e.g. foo/hello.mp4 and bar/hello.mp4
|
|
96
97
|
video_basenames = {path.name: path for path in video_paths}
|
|
97
98
|
|
|
98
|
-
image_samples_by_video_path:
|
|
99
|
+
image_samples_by_video_path: dict[Path, list[Path]] = {}
|
|
99
100
|
for image_path in image_paths:
|
|
100
101
|
# If you want to walk an arbitrary filesystem path upwards,
|
|
101
102
|
# it is recommended to first call Path.resolve() so as to resolve symlinks and eliminate “..” components.
|
|
@@ -112,7 +113,7 @@ def find_all_image_samples(
|
|
|
112
113
|
|
|
113
114
|
|
|
114
115
|
def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]:
|
|
115
|
-
resolved_paths:
|
|
116
|
+
resolved_paths: set[Path] = set()
|
|
116
117
|
for p in paths:
|
|
117
118
|
resolved = p.resolve()
|
|
118
119
|
if resolved not in resolved_paths:
|
|
@@ -121,10 +122,10 @@ def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]:
|
|
|
121
122
|
|
|
122
123
|
|
|
123
124
|
def find_images(
|
|
124
|
-
import_paths: T.
|
|
125
|
+
import_paths: T.Iterable[Path],
|
|
125
126
|
skip_subfolders: bool = False,
|
|
126
|
-
) ->
|
|
127
|
-
image_paths:
|
|
127
|
+
) -> list[Path]:
|
|
128
|
+
image_paths: list[Path] = []
|
|
128
129
|
for path in import_paths:
|
|
129
130
|
if path.is_dir():
|
|
130
131
|
image_paths.extend(
|
|
@@ -139,10 +140,10 @@ def find_images(
|
|
|
139
140
|
|
|
140
141
|
|
|
141
142
|
def find_videos(
|
|
142
|
-
import_paths: T.
|
|
143
|
+
import_paths: T.Iterable[Path],
|
|
143
144
|
skip_subfolders: bool = False,
|
|
144
|
-
) ->
|
|
145
|
-
video_paths:
|
|
145
|
+
) -> list[Path]:
|
|
146
|
+
video_paths: list[Path] = []
|
|
146
147
|
for path in import_paths:
|
|
147
148
|
if path.is_dir():
|
|
148
149
|
video_paths.extend(
|
|
@@ -157,10 +158,10 @@ def find_videos(
|
|
|
157
158
|
|
|
158
159
|
|
|
159
160
|
def find_zipfiles(
|
|
160
|
-
import_paths: T.
|
|
161
|
+
import_paths: T.Iterable[Path],
|
|
161
162
|
skip_subfolders: bool = False,
|
|
162
|
-
) ->
|
|
163
|
-
zip_paths:
|
|
163
|
+
) -> list[Path]:
|
|
164
|
+
zip_paths: list[Path] = []
|
|
164
165
|
for path in import_paths:
|
|
165
166
|
if path.is_dir():
|
|
166
167
|
zip_paths.extend(
|
|
@@ -174,8 +175,8 @@ def find_zipfiles(
|
|
|
174
175
|
return list(deduplicate_paths(zip_paths))
|
|
175
176
|
|
|
176
177
|
|
|
177
|
-
def find_xml_files(import_paths: T.
|
|
178
|
-
xml_paths:
|
|
178
|
+
def find_xml_files(import_paths: T.Iterable[Path]) -> list[Path]:
|
|
179
|
+
xml_paths: list[Path] = []
|
|
179
180
|
for path in import_paths:
|
|
180
181
|
if path.is_dir():
|
|
181
182
|
# XML could be hidden in hidden folders
|
|
@@ -194,3 +195,33 @@ def find_xml_files(import_paths: T.Sequence[Path]) -> T.List[Path]:
|
|
|
194
195
|
|
|
195
196
|
def get_file_size(path: Path) -> int:
|
|
196
197
|
return os.path.getsize(path)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_file_size_quietly(path: Path) -> int:
|
|
201
|
+
try:
|
|
202
|
+
return get_file_size(path)
|
|
203
|
+
except Exception:
|
|
204
|
+
return 0
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
TMapIn = T.TypeVar("TMapIn")
|
|
208
|
+
TMapOut = T.TypeVar("TMapOut")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def mp_map_maybe(
|
|
212
|
+
func: T.Callable[[TMapIn], TMapOut],
|
|
213
|
+
iterable: T.Iterable[TMapIn],
|
|
214
|
+
num_processes: int | None = None,
|
|
215
|
+
) -> T.Generator[TMapOut, None, None]:
|
|
216
|
+
if num_processes is None:
|
|
217
|
+
pool_num_processes = None
|
|
218
|
+
disable_multiprocessing = False
|
|
219
|
+
else:
|
|
220
|
+
pool_num_processes = max(num_processes, 1)
|
|
221
|
+
disable_multiprocessing = num_processes <= 0
|
|
222
|
+
|
|
223
|
+
if disable_multiprocessing:
|
|
224
|
+
yield from map(func, iterable)
|
|
225
|
+
else:
|
|
226
|
+
with Pool(processes=pool_num_processes) as pool:
|
|
227
|
+
yield from pool.imap(func, iterable)
|
|
@@ -1,31 +1,38 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: mapillary_tools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
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
|
-
|
|
21
|
-
Dynamic: author
|
|
22
|
-
Dynamic: description
|
|
23
|
-
Dynamic: description-content-type
|
|
24
|
-
Dynamic: home-page
|
|
25
|
-
Dynamic: license
|
|
26
|
-
Dynamic: requires-dist
|
|
27
|
-
Dynamic: requires-python
|
|
28
|
-
Dynamic: summary
|
|
34
|
+
Requires-Dist: typing-extensions>=4.12.2
|
|
35
|
+
Dynamic: license-file
|
|
29
36
|
|
|
30
37
|
<p align="center">
|
|
31
38
|
<a href="https://github.com/mapillary/mapillary_tools/">
|
|
@@ -57,18 +64,40 @@ mapillary_tools --help
|
|
|
57
64
|
<!--ts-->
|
|
58
65
|
|
|
59
66
|
- [Supported File Formats](#supported-file-formats)
|
|
67
|
+
- [Image Formats](#image-formats)
|
|
68
|
+
- [Video Formats](#video-formats)
|
|
60
69
|
- [Installation](#installation)
|
|
70
|
+
- [Standalone Executable](#standalone-executable)
|
|
71
|
+
- [Installing via pip](#installing-via-pip)
|
|
72
|
+
- [Installing on Android Devices](#installing-on-android-devices)
|
|
61
73
|
- [Usage](#usage)
|
|
62
74
|
- [Process and Upload](#process-and-upload)
|
|
63
75
|
- [Process](#process)
|
|
64
76
|
- [Upload](#upload)
|
|
65
77
|
- [Advanced Usage](#advanced-usage)
|
|
66
78
|
- [Local Video Processing](#local-video-processing)
|
|
79
|
+
- [Install FFmpeg](#install-ffmpeg)
|
|
80
|
+
- [Video Processing](#video-processing)
|
|
67
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)
|
|
68
89
|
- [Authenticate](#authenticate)
|
|
90
|
+
- [Examples](#examples-1)
|
|
69
91
|
- [Image Description](#image-description)
|
|
70
92
|
- [Zip Images](#zip-images)
|
|
71
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)
|
|
72
101
|
|
|
73
102
|
<!--te-->
|
|
74
103
|
|
|
@@ -494,29 +523,49 @@ git clone git@github.com:mapillary/mapillary_tools.git
|
|
|
494
523
|
cd mapillary_tools
|
|
495
524
|
```
|
|
496
525
|
|
|
497
|
-
|
|
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:
|
|
498
531
|
|
|
499
532
|
```sh
|
|
500
|
-
|
|
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
|
|
501
538
|
```
|
|
502
539
|
|
|
503
|
-
|
|
540
|
+
### Option 2: Using pip with virtual environment
|
|
541
|
+
|
|
542
|
+
Set up a virtual environment (recommended):
|
|
504
543
|
|
|
505
544
|
```sh
|
|
506
|
-
|
|
507
|
-
|
|
545
|
+
python -m venv .venv
|
|
546
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
508
547
|
```
|
|
509
548
|
|
|
510
|
-
|
|
549
|
+
Install the project in development mode:
|
|
511
550
|
|
|
512
551
|
```sh
|
|
513
|
-
|
|
552
|
+
# Install the project and all dependencies in editable mode
|
|
553
|
+
pip install -e .
|
|
554
|
+
|
|
555
|
+
# Install development dependencies
|
|
556
|
+
pip install --group dev
|
|
514
557
|
```
|
|
515
558
|
|
|
559
|
+
## Running the code
|
|
560
|
+
|
|
516
561
|
Run the code from the repository:
|
|
517
562
|
|
|
518
563
|
```sh
|
|
519
|
-
|
|
564
|
+
# If you have mapillary_tools installed in editable mode
|
|
565
|
+
mapillary_tools --version
|
|
566
|
+
|
|
567
|
+
# Alternatively
|
|
568
|
+
python -m mapillary_tools.commands --version
|
|
520
569
|
```
|
|
521
570
|
|
|
522
571
|
## Tests
|
|
@@ -524,19 +573,28 @@ python3 -m mapillary_tools.commands --version
|
|
|
524
573
|
Run tests:
|
|
525
574
|
|
|
526
575
|
```sh
|
|
527
|
-
#
|
|
528
|
-
|
|
529
|
-
#
|
|
530
|
-
|
|
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
|
|
531
580
|
```
|
|
532
581
|
|
|
533
|
-
|
|
582
|
+
## Code Quality
|
|
583
|
+
|
|
584
|
+
Run code formatting and linting:
|
|
534
585
|
|
|
535
586
|
```sh
|
|
536
|
-
#
|
|
537
|
-
|
|
538
|
-
|
|
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
|
|
539
594
|
usort format mapillary_tools tests
|
|
595
|
+
|
|
596
|
+
# Type checking with mypy
|
|
597
|
+
mypy mapillary_tools
|
|
540
598
|
```
|
|
541
599
|
|
|
542
600
|
## Release and Build
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
mapillary_tools/__init__.py,sha256=_3t9ZCm4vuKINk3jDtMZj_Wcw_4NcgFIpwhwW-DVWec,19
|
|
2
|
+
mapillary_tools/api_v4.py,sha256=gDhFa2Jz6fGbPrtSrBjtPIXRXOxqWaXvcVCJT8KyRww,11788
|
|
3
|
+
mapillary_tools/authenticate.py,sha256=OKS4jDFMo7XCzk9jEeHyZZUy-q56GoUZytJ5ta4QI8E,11219
|
|
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=UuEuSajD3GvV73D5QRnjFlC6eDzQZbtQShaE2l98x18,6080
|
|
7
|
+
mapillary_tools/exceptions.py,sha256=uxTgBEfXgGxT0XNGRIAZ5mjtdqsCYfP6gnaXAK_ewBM,2483
|
|
8
|
+
mapillary_tools/exif_read.py,sha256=gagyRv06RkZPRHQhyV-5-uQzxHgxOsVqUX2aC8QmCyM,32145
|
|
9
|
+
mapillary_tools/exif_write.py,sha256=I9GSs8KRd28QyKPVNHuxesSVbBvahHqKBS3nw9HoLsg,8830
|
|
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=mWaESfDf_zHmyvnt5aVFro4FGrjiULNsuZ6HfGUWvSA,11009
|
|
15
|
+
mapillary_tools/history.py,sha256=LP6e0zEYVBwRGUbFaGoE_AaBIEdpB4XrZsg9qwJVvRI,5344
|
|
16
|
+
mapillary_tools/ipc.py,sha256=DwWQb9hNshx0bg0Fo5NjY0mXjs-FkbR6tIQmjMgMtmg,1089
|
|
17
|
+
mapillary_tools/process_geotag_properties.py,sha256=uT7dMpCujpBIysKd26sQRsIh8sVAkNv2DnzZQmEOmt0,14263
|
|
18
|
+
mapillary_tools/process_sequence_properties.py,sha256=n4VjQHrgVjksIr3WoBviRhrQIBBDHGXMClolfyz6tu4,24057
|
|
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=fOJ_cdPspVeGbqiATP4cLTcvR6tx3eIUlQxzADh863Q,23815
|
|
23
|
+
mapillary_tools/upload_api_v4.py,sha256=ZpqWZ0WFiNCx3JUinLW7xKYkf4Tc-6oe3nE1qkXm898,7539
|
|
24
|
+
mapillary_tools/uploader.py,sha256=lVauLejQd8cL7BdvR8z_i28t5-yxDVVEt_xlM43KUgs,32064
|
|
25
|
+
mapillary_tools/utils.py,sha256=eqHVmaLDjEDYeLR-4b3gmg4_afoVi1RynVn8zQMFlt4,6773
|
|
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=-tm3p90JMAIQ9o86_MRCA40_WffOYGacTGWxBNzzEVA,5026
|
|
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=p2c_QGPJWrSaXyqjwC1Qh3yzV0Zy2V3wVIfTJx3cwYE,2819
|
|
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=MuuLb9gcGWs7LHywyn8cGsml4eupdVQjji9jj_QCujk,10218
|
|
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=EyhJjpP1L_DPRCX926aKeTvuxFxD-eWVCWgELp0-pzY,6313
|
|
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=_xmTQ6nrvdwkadMmIxrhw6rvGTGwQVmrdSkYLhhV1SM,5786
|
|
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=jS8fOgSByqieXA6C5ZqobaI0bK17U_YAkiWEUK_8j4E,17286
|
|
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=FB-DawgHVW5M2e81Uo921bY7DQv9fogXF3QMilpmEhQ,18473
|
|
69
|
+
mapillary_tools/serializer/gpx.py,sha256=_xx6gHjaWHrlXaUpB5GGBrbRKzbExFyIzWWAH-CvksI,4383
|
|
70
|
+
mapillary_tools-0.14.0.dist-info/licenses/LICENSE,sha256=l2D8cKfFmmJq_wcVq_JElPJrlvWQOzNWx7gMLINucxc,1292
|
|
71
|
+
mapillary_tools-0.14.0.dist-info/METADATA,sha256=QfSr8vy3dnl6HBeZjtYWgYoYpbJohbipNXmIio8lnO8,22200
|
|
72
|
+
mapillary_tools-0.14.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
73
|
+
mapillary_tools-0.14.0.dist-info/entry_points.txt,sha256=A3f3LP-BO_P-U8Y29QfpT4jx6Mjk3sXjTi2Yew4bvj8,75
|
|
74
|
+
mapillary_tools-0.14.0.dist-info/top_level.txt,sha256=FbDkMgOrt1S70ho1WSBrOwzKOSkJFDwwqFOoY5-527s,16
|
|
75
|
+
mapillary_tools-0.14.0.dist-info/RECORD,,
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
import pathlib
|
|
4
|
-
import re
|
|
5
|
-
import typing as T
|
|
6
|
-
|
|
7
|
-
import pynmea2
|
|
8
|
-
|
|
9
|
-
from .. import geo
|
|
10
|
-
from ..mp4 import simple_mp4_parser as sparser
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
LOG = logging.getLogger(__name__)
|
|
14
|
-
# An example: [1623057074211]$GPVTG,,T,,M,0.078,N,0.144,K,D*28[1623057075215]
|
|
15
|
-
NMEA_LINE_REGEX = re.compile(
|
|
16
|
-
rb"""
|
|
17
|
-
^\s*
|
|
18
|
-
\[(\d+)\] # timestamp
|
|
19
|
-
\s*
|
|
20
|
-
(\$\w{5}.*) # nmea line
|
|
21
|
-
\s*
|
|
22
|
-
(\[\d+\])? # strange timestamp
|
|
23
|
-
\s*$
|
|
24
|
-
""",
|
|
25
|
-
re.X,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _parse_gps_box(gps_data: bytes) -> T.Generator[geo.Point, None, None]:
|
|
30
|
-
for line_bytes in gps_data.splitlines():
|
|
31
|
-
match = NMEA_LINE_REGEX.match(line_bytes)
|
|
32
|
-
if match is None:
|
|
33
|
-
continue
|
|
34
|
-
nmea_line_bytes = match.group(2)
|
|
35
|
-
if nmea_line_bytes.startswith(b"$GPGGA"):
|
|
36
|
-
try:
|
|
37
|
-
nmea_line = nmea_line_bytes.decode("utf8")
|
|
38
|
-
except UnicodeDecodeError:
|
|
39
|
-
continue
|
|
40
|
-
try:
|
|
41
|
-
nmea = pynmea2.parse(nmea_line)
|
|
42
|
-
except pynmea2.nmea.ParseError:
|
|
43
|
-
continue
|
|
44
|
-
if not nmea.is_valid:
|
|
45
|
-
continue
|
|
46
|
-
epoch_ms = int(match.group(1))
|
|
47
|
-
yield geo.Point(
|
|
48
|
-
time=epoch_ms,
|
|
49
|
-
lat=nmea.latitude,
|
|
50
|
-
lon=nmea.longitude,
|
|
51
|
-
alt=nmea.altitude,
|
|
52
|
-
angle=None,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def extract_camera_model(fp: T.BinaryIO) -> str:
|
|
57
|
-
try:
|
|
58
|
-
cprt_bytes = sparser.parse_mp4_data_first(fp, [b"free", b"cprt"])
|
|
59
|
-
except sparser.ParsingError:
|
|
60
|
-
return ""
|
|
61
|
-
|
|
62
|
-
if cprt_bytes is None:
|
|
63
|
-
return ""
|
|
64
|
-
|
|
65
|
-
# examples: b' {"model":"DR900X Plus","ver":0.918,"lang":"English","direct":1,"psn":"","temp":34,"GPS":1}\x00'
|
|
66
|
-
# b' Pittasoft Co., Ltd.;DR900S-1CH;1.008;English;1;D90SS1HAE00661;T69;\x00'
|
|
67
|
-
cprt_bytes = cprt_bytes.strip().strip(b"\x00")
|
|
68
|
-
|
|
69
|
-
try:
|
|
70
|
-
cprt_str = cprt_bytes.decode("utf8")
|
|
71
|
-
except UnicodeDecodeError:
|
|
72
|
-
return ""
|
|
73
|
-
|
|
74
|
-
try:
|
|
75
|
-
cprt_json = json.loads(cprt_str)
|
|
76
|
-
except json.JSONDecodeError:
|
|
77
|
-
cprt_json = None
|
|
78
|
-
|
|
79
|
-
if cprt_json is not None:
|
|
80
|
-
return str(cprt_json.get("model", "")).strip()
|
|
81
|
-
|
|
82
|
-
fields = cprt_str.split(";")
|
|
83
|
-
if 2 <= len(fields):
|
|
84
|
-
model = fields[1]
|
|
85
|
-
if model:
|
|
86
|
-
return model.strip()
|
|
87
|
-
else:
|
|
88
|
-
return ""
|
|
89
|
-
else:
|
|
90
|
-
return ""
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def extract_points(fp: T.BinaryIO) -> T.Optional[T.List[geo.Point]]:
|
|
94
|
-
gps_data = sparser.parse_mp4_data_first(fp, [b"free", b"gps "])
|
|
95
|
-
if gps_data is None:
|
|
96
|
-
return None
|
|
97
|
-
|
|
98
|
-
points = list(_parse_gps_box(gps_data))
|
|
99
|
-
if not points:
|
|
100
|
-
return points
|
|
101
|
-
|
|
102
|
-
points.sort(key=lambda p: p.time)
|
|
103
|
-
|
|
104
|
-
first_point_time = points[0].time
|
|
105
|
-
for p in points:
|
|
106
|
-
p.time = (p.time - first_point_time) / 1000
|
|
107
|
-
|
|
108
|
-
return points
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def parse_gps_points(path: pathlib.Path) -> T.List[geo.Point]:
|
|
112
|
-
with path.open("rb") as fp:
|
|
113
|
-
points = extract_points(fp)
|
|
114
|
-
|
|
115
|
-
if points is None:
|
|
116
|
-
return []
|
|
117
|
-
|
|
118
|
-
return points
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import abc
|
|
2
|
-
import typing as T
|
|
3
|
-
|
|
4
|
-
from .. import types
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class GeotagImagesFromGeneric(abc.ABC):
|
|
8
|
-
def __init__(self) -> None:
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
@abc.abstractmethod
|
|
12
|
-
def to_description(self) -> T.List[types.ImageMetadataOrError]:
|
|
13
|
-
raise NotImplementedError
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class GeotagVideosFromGeneric(abc.ABC):
|
|
17
|
-
def __init__(self) -> None:
|
|
18
|
-
pass
|
|
19
|
-
|
|
20
|
-
@abc.abstractmethod
|
|
21
|
-
def to_description(self) -> T.List[types.VideoMetadataOrError]:
|
|
22
|
-
raise NotImplementedError
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import typing as T
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
from .. import exiftool_read, types, utils
|
|
6
|
-
from . import (
|
|
7
|
-
geotag_images_from_exiftool,
|
|
8
|
-
geotag_images_from_video,
|
|
9
|
-
geotag_videos_from_exiftool_video,
|
|
10
|
-
)
|
|
11
|
-
from .geotag_from_generic import GeotagImagesFromGeneric
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
LOG = logging.getLogger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class GeotagImagesFromExifToolBothImageAndVideo(GeotagImagesFromGeneric):
|
|
18
|
-
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
image_paths: T.Sequence[Path],
|
|
21
|
-
xml_path: Path,
|
|
22
|
-
offset_time: float = 0.0,
|
|
23
|
-
num_processes: T.Optional[int] = None,
|
|
24
|
-
):
|
|
25
|
-
self.image_paths = image_paths
|
|
26
|
-
self.xml_path = xml_path
|
|
27
|
-
self.offset_time = offset_time
|
|
28
|
-
self.num_processes = num_processes
|
|
29
|
-
super().__init__()
|
|
30
|
-
|
|
31
|
-
def to_description(self) -> T.List[types.ImageMetadataOrError]:
|
|
32
|
-
# will return this list
|
|
33
|
-
final_image_metadatas: T.List[types.ImageMetadataOrError] = []
|
|
34
|
-
|
|
35
|
-
# find the images that can be geotagged from EXIF
|
|
36
|
-
image_metadatas_from_exiftool = (
|
|
37
|
-
geotag_images_from_exiftool.GeotagImagesFromExifTool(
|
|
38
|
-
self.image_paths,
|
|
39
|
-
self.xml_path,
|
|
40
|
-
num_processes=self.num_processes,
|
|
41
|
-
).to_description()
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
# find all video paths in self.xml_path
|
|
45
|
-
rdf_description_by_path = exiftool_read.index_rdf_description_by_path(
|
|
46
|
-
[self.xml_path]
|
|
47
|
-
)
|
|
48
|
-
video_paths = utils.find_videos(
|
|
49
|
-
[Path(pathstr) for pathstr in rdf_description_by_path.keys()],
|
|
50
|
-
skip_subfolders=True,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
# will try to geotag these error metadatas from video later
|
|
54
|
-
error_metadata_by_image_path = {}
|
|
55
|
-
for image_metadata in image_metadatas_from_exiftool:
|
|
56
|
-
if isinstance(image_metadata, types.ErrorMetadata):
|
|
57
|
-
error_metadata_by_image_path[image_metadata.filename] = image_metadata
|
|
58
|
-
else:
|
|
59
|
-
final_image_metadatas.append(image_metadata)
|
|
60
|
-
|
|
61
|
-
maybe_image_samples = list(error_metadata_by_image_path.keys())
|
|
62
|
-
|
|
63
|
-
# find all video paths that have sample images
|
|
64
|
-
video_paths_with_image_samples = list(
|
|
65
|
-
utils.find_all_image_samples(maybe_image_samples, video_paths).keys()
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
video_metadatas = (
|
|
69
|
-
geotag_videos_from_exiftool_video.GeotagVideosFromExifToolVideo(
|
|
70
|
-
video_paths_with_image_samples,
|
|
71
|
-
self.xml_path,
|
|
72
|
-
num_processes=self.num_processes,
|
|
73
|
-
).to_description()
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
image_metadatas_from_video = geotag_images_from_video.GeotagImagesFromVideo(
|
|
77
|
-
maybe_image_samples,
|
|
78
|
-
video_metadatas,
|
|
79
|
-
offset_time=self.offset_time,
|
|
80
|
-
num_processes=self.num_processes,
|
|
81
|
-
).to_description()
|
|
82
|
-
final_image_metadatas.extend(image_metadatas_from_video)
|
|
83
|
-
|
|
84
|
-
# add back error metadatas that can not be geotagged at all
|
|
85
|
-
actual_image_sample_paths = set(
|
|
86
|
-
image_metadata.filename for image_metadata in image_metadatas_from_video
|
|
87
|
-
)
|
|
88
|
-
for path, error_metadata in error_metadata_by_image_path.items():
|
|
89
|
-
if path not in actual_image_sample_paths:
|
|
90
|
-
final_image_metadatas.append(error_metadata)
|
|
91
|
-
|
|
92
|
-
assert len(final_image_metadatas) <= len(self.image_paths)
|
|
93
|
-
return final_image_metadatas
|