mapillary-tools 0.13.1a1__tar.gz → 0.13.3a1__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.13.1a1/mapillary_tools.egg-info → mapillary_tools-0.13.3a1}/PKG-INFO +1 -1
- mapillary_tools-0.13.3a1/mapillary_tools/__init__.py +1 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/constants.py +1 -1
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_images_from_gpx_file.py +7 -1
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/upload_api_v4.py +68 -61
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/uploader.py +0 -2
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extractors/camm_parser.py +8 -12
- mapillary_tools-0.13.3a1/mapillary_tools/video_data_extraction/extractors/gpx_parser.py +108 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1/mapillary_tools.egg-info}/PKG-INFO +1 -1
- mapillary_tools-0.13.1a1/mapillary_tools/__init__.py +0 -1
- mapillary_tools-0.13.1a1/mapillary_tools/video_data_extraction/extractors/gpx_parser.py +0 -71
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/LICENSE +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/MANIFEST.in +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/README.md +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/api_v4.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/authenticate.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/camm/camm_builder.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/camm/camm_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/__init__.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/__main__.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/authenticate.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/process.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/process_and_upload.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/sample_video.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/upload.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/video_process.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/video_process_and_upload.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/zip.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/config.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/exceptions.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/exif_read.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/exif_write.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/exiftool_read.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/exiftool_read_video.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/ffmpeg.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geo.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/__init__.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/blackvue_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_from_generic.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_images_from_exif.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_images_from_exiftool.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_images_from_gpx.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_images_from_nmea_file.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_images_from_video.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_videos_from_video.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/gpmf_gps_filter.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/gpmf_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/gps_filter.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/utils.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/history.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/ipc.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/__init__.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/construct_mp4_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/io_utils.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/mp4_sample_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/simple_mp4_builder.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/simple_mp4_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/process_geotag_properties.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/process_sequence_properties.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/sample_video.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/telemetry.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/types.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/upload.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/utils.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/cli_options.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extract_video_data.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/extractors/nmea_parser.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/video_data_extraction/video_data_parser_factory.py +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools.egg-info/SOURCES.txt +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools.egg-info/dependency_links.txt +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools.egg-info/entry_points.txt +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools.egg-info/requires.txt +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools.egg-info/top_level.txt +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/requirements.txt +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/schema/image_description_schema.json +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/setup.cfg +0 -0
- {mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/setup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.13.3a1"
|
|
@@ -45,6 +45,6 @@ GOPRO_GPS_PRECISION = float(os.getenv(_ENV_PREFIX + "GOPRO_GPS_PRECISION", 15))
|
|
|
45
45
|
# Max number of images per sequence
|
|
46
46
|
MAX_SEQUENCE_LENGTH = int(os.getenv(_ENV_PREFIX + "MAX_SEQUENCE_LENGTH", 1000))
|
|
47
47
|
# Max file size per sequence (sum of image filesizes in the sequence)
|
|
48
|
-
MAX_SEQUENCE_FILESIZE: str = os.getenv(_ENV_PREFIX + "MAX_SEQUENCE_FILESIZE", "
|
|
48
|
+
MAX_SEQUENCE_FILESIZE: str = os.getenv(_ENV_PREFIX + "MAX_SEQUENCE_FILESIZE", "110G")
|
|
49
49
|
# Max number of pixels per sequence (sum of image pixels in the sequence)
|
|
50
50
|
MAX_SEQUENCE_PIXELS: str = os.getenv(_ENV_PREFIX + "MAX_SEQUENCE_PIXELS", "6G")
|
|
@@ -25,7 +25,13 @@ class GeotagImagesFromGPXFile(GeotagImagesFromGeneric):
|
|
|
25
25
|
num_processes: T.Optional[int] = None,
|
|
26
26
|
):
|
|
27
27
|
super().__init__()
|
|
28
|
-
|
|
28
|
+
try:
|
|
29
|
+
tracks = parse_gpx(source_path)
|
|
30
|
+
except Exception as ex:
|
|
31
|
+
raise RuntimeError(
|
|
32
|
+
f"Error parsing GPX {source_path}: {ex.__class__.__name__}: {ex}"
|
|
33
|
+
)
|
|
34
|
+
|
|
29
35
|
if 1 < len(tracks):
|
|
30
36
|
LOG.warning(
|
|
31
37
|
"Found %s tracks in the GPX file %s. Will merge points in all the tracks as a single track for interpolation",
|
|
@@ -5,6 +5,7 @@ import logging
|
|
|
5
5
|
import os
|
|
6
6
|
import random
|
|
7
7
|
import typing as T
|
|
8
|
+
import uuid
|
|
8
9
|
|
|
9
10
|
import requests
|
|
10
11
|
|
|
@@ -55,31 +56,31 @@ def _truncate_end(s: _S) -> _S:
|
|
|
55
56
|
|
|
56
57
|
class UploadService:
|
|
57
58
|
user_access_token: str
|
|
58
|
-
entity_size: int
|
|
59
59
|
session_key: str
|
|
60
60
|
callbacks: T.List[T.Callable[[bytes, T.Optional[requests.Response]], None]]
|
|
61
61
|
cluster_filetype: ClusterFileType
|
|
62
62
|
organization_id: T.Optional[T.Union[str, int]]
|
|
63
63
|
chunk_size: int
|
|
64
64
|
|
|
65
|
+
MIME_BY_CLUSTER_TYPE: T.Dict[ClusterFileType, str] = {
|
|
66
|
+
ClusterFileType.ZIP: "application/zip",
|
|
67
|
+
ClusterFileType.BLACKVUE: "video/mp4",
|
|
68
|
+
ClusterFileType.CAMM: "video/mp4",
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
def __init__(
|
|
66
72
|
self,
|
|
67
73
|
user_access_token: str,
|
|
68
74
|
session_key: str,
|
|
69
|
-
entity_size: int,
|
|
70
75
|
organization_id: T.Optional[T.Union[str, int]] = None,
|
|
71
76
|
cluster_filetype: ClusterFileType = ClusterFileType.ZIP,
|
|
72
77
|
chunk_size: int = DEFAULT_CHUNK_SIZE,
|
|
73
78
|
):
|
|
74
|
-
if entity_size <= 0:
|
|
75
|
-
raise ValueError(f"Expect positive entity size but got {entity_size}")
|
|
76
|
-
|
|
77
79
|
if chunk_size <= 0:
|
|
78
80
|
raise ValueError("Expect positive chunk size")
|
|
79
81
|
|
|
80
82
|
self.user_access_token = user_access_token
|
|
81
83
|
self.session_key = session_key
|
|
82
|
-
self.entity_size = entity_size
|
|
83
84
|
self.organization_id = organization_id
|
|
84
85
|
# validate the input
|
|
85
86
|
self.cluster_filetype = ClusterFileType(cluster_filetype)
|
|
@@ -107,55 +108,66 @@ class UploadService:
|
|
|
107
108
|
data: T.IO[bytes],
|
|
108
109
|
offset: T.Optional[int] = None,
|
|
109
110
|
) -> str:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
entity_type_map: T.Dict[ClusterFileType, str] = {
|
|
114
|
-
ClusterFileType.ZIP: "application/zip",
|
|
115
|
-
ClusterFileType.BLACKVUE: "video/mp4",
|
|
116
|
-
ClusterFileType.CAMM: "video/mp4",
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
entity_type = entity_type_map[self.cluster_filetype]
|
|
120
|
-
|
|
121
|
-
data.seek(offset, io.SEEK_CUR)
|
|
111
|
+
chunks = self._chunkize_byte_stream(data)
|
|
112
|
+
return self.upload_chunks(chunks, offset=offset)
|
|
122
113
|
|
|
114
|
+
def _chunkize_byte_stream(
|
|
115
|
+
self, stream: T.IO[bytes]
|
|
116
|
+
) -> T.Generator[bytes, None, None]:
|
|
123
117
|
while True:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
118
|
+
data = stream.read(self.chunk_size)
|
|
119
|
+
if not data:
|
|
120
|
+
break
|
|
121
|
+
yield data
|
|
122
|
+
|
|
123
|
+
def _offset_chunks(
|
|
124
|
+
self, chunks: T.Iterable[bytes], offset: int
|
|
125
|
+
) -> T.Generator[bytes, None, None]:
|
|
126
|
+
assert offset >= 0, f"Expect non-negative offset but got {offset}"
|
|
127
|
+
|
|
128
|
+
for chunk in chunks:
|
|
129
|
+
if offset:
|
|
130
|
+
if offset < len(chunk):
|
|
131
|
+
yield chunk[offset:]
|
|
132
|
+
offset = 0
|
|
133
|
+
else:
|
|
134
|
+
offset -= len(chunk)
|
|
135
|
+
else:
|
|
136
|
+
yield chunk
|
|
137
|
+
|
|
138
|
+
def _attach_callbacks(
|
|
139
|
+
self, chunks: T.Iterable[bytes]
|
|
140
|
+
) -> T.Generator[bytes, None, None]:
|
|
141
|
+
for chunk in chunks:
|
|
142
|
+
yield chunk
|
|
148
143
|
for callback in self.callbacks:
|
|
149
|
-
callback(chunk,
|
|
150
|
-
# we can assert that offset == self.fetch_offset(session_key)
|
|
151
|
-
# otherwise, server will throw
|
|
144
|
+
callback(chunk, None)
|
|
152
145
|
|
|
153
|
-
|
|
154
|
-
|
|
146
|
+
def upload_chunks(
|
|
147
|
+
self,
|
|
148
|
+
chunks: T.Iterable[bytes],
|
|
149
|
+
offset: T.Optional[int] = None,
|
|
150
|
+
) -> str:
|
|
151
|
+
if offset is None:
|
|
152
|
+
offset = self.fetch_offset()
|
|
155
153
|
|
|
156
|
-
|
|
157
|
-
|
|
154
|
+
chunks = self._attach_callbacks(self._offset_chunks(chunks, offset))
|
|
155
|
+
|
|
156
|
+
headers = {
|
|
157
|
+
"Authorization": f"OAuth {self.user_access_token}",
|
|
158
|
+
"Offset": f"{offset}",
|
|
159
|
+
"X-Entity-Name": self.session_key,
|
|
160
|
+
"X-Entity-Type": self.MIME_BY_CLUSTER_TYPE[self.cluster_filetype],
|
|
161
|
+
}
|
|
162
|
+
url = f"{MAPILLARY_UPLOAD_ENDPOINT}/{self.session_key}"
|
|
163
|
+
LOG.debug("POST %s HEADERS %s", url, json.dumps(_sanitize_headers(headers)))
|
|
164
|
+
resp = request_post(
|
|
165
|
+
url,
|
|
166
|
+
headers=headers,
|
|
167
|
+
data=chunks,
|
|
168
|
+
timeout=UPLOAD_REQUESTS_TIMEOUT,
|
|
158
169
|
)
|
|
170
|
+
LOG.debug("HTTP response %s: %s", resp.status_code, _truncate_end(resp.content))
|
|
159
171
|
|
|
160
172
|
payload = resp.json()
|
|
161
173
|
try:
|
|
@@ -209,35 +221,30 @@ class FakeUploadService(UploadService):
|
|
|
209
221
|
)
|
|
210
222
|
self._error_ratio = 0.1
|
|
211
223
|
|
|
212
|
-
def
|
|
224
|
+
def upload_chunks(
|
|
213
225
|
self,
|
|
214
|
-
|
|
226
|
+
chunks: T.Iterable[bytes],
|
|
215
227
|
offset: T.Optional[int] = None,
|
|
216
228
|
) -> str:
|
|
217
229
|
if offset is None:
|
|
218
230
|
offset = self.fetch_offset()
|
|
231
|
+
|
|
232
|
+
chunks = self._attach_callbacks(self._offset_chunks(chunks, offset))
|
|
233
|
+
|
|
219
234
|
os.makedirs(self._upload_path, exist_ok=True)
|
|
220
235
|
filename = os.path.join(self._upload_path, self.session_key)
|
|
221
236
|
with open(filename, "ab") as fp:
|
|
222
|
-
|
|
223
|
-
while True:
|
|
224
|
-
chunk = data.read(self.chunk_size)
|
|
225
|
-
if not chunk:
|
|
226
|
-
break
|
|
227
|
-
# fail here means nothing uploaded
|
|
237
|
+
for chunk in chunks:
|
|
228
238
|
if random.random() <= self._error_ratio:
|
|
229
239
|
raise requests.ConnectionError(
|
|
230
240
|
f"TEST ONLY: Failed to upload with error ratio {self._error_ratio}"
|
|
231
241
|
)
|
|
232
242
|
fp.write(chunk)
|
|
233
|
-
# fail here means patially uploaded
|
|
234
243
|
if random.random() <= self._error_ratio:
|
|
235
244
|
raise requests.ConnectionError(
|
|
236
245
|
f"TEST ONLY: Partially uploaded with error ratio {self._error_ratio}"
|
|
237
246
|
)
|
|
238
|
-
|
|
239
|
-
callback(chunk, None)
|
|
240
|
-
return self.session_key
|
|
247
|
+
return uuid.uuid4().hex
|
|
241
248
|
|
|
242
249
|
def finish(self, _: str) -> str:
|
|
243
250
|
return "0"
|
|
@@ -195,7 +195,6 @@ class Uploader:
|
|
|
195
195
|
upload_api_v4.FakeUploadService(
|
|
196
196
|
user_access_token=self.user_items["user_upload_token"],
|
|
197
197
|
session_key=session_key,
|
|
198
|
-
entity_size=entity_size,
|
|
199
198
|
organization_id=self.user_items.get("MAPOrganizationKey"),
|
|
200
199
|
cluster_filetype=cluster_filetype,
|
|
201
200
|
chunk_size=self.chunk_size,
|
|
@@ -205,7 +204,6 @@ class Uploader:
|
|
|
205
204
|
upload_service = upload_api_v4.UploadService(
|
|
206
205
|
user_access_token=self.user_items["user_upload_token"],
|
|
207
206
|
session_key=session_key,
|
|
208
|
-
entity_size=entity_size,
|
|
209
207
|
organization_id=self.user_items.get("MAPOrganizationKey"),
|
|
210
208
|
cluster_filetype=cluster_filetype,
|
|
211
209
|
chunk_size=self.chunk_size,
|
|
@@ -13,8 +13,12 @@ class CammParser(BaseParser):
|
|
|
13
13
|
parser_label = "camm"
|
|
14
14
|
|
|
15
15
|
@functools.cached_property
|
|
16
|
-
def
|
|
17
|
-
|
|
16
|
+
def _camera_info(self) -> T.Tuple[str, str]:
|
|
17
|
+
source_path = self.geotag_source_path
|
|
18
|
+
if not source_path:
|
|
19
|
+
return "", ""
|
|
20
|
+
|
|
21
|
+
with source_path.open("rb") as fp:
|
|
18
22
|
return camm_parser.extract_camera_make_and_model(fp)
|
|
19
23
|
|
|
20
24
|
def extract_points(self) -> T.Sequence[geo.Point]:
|
|
@@ -28,15 +32,7 @@ class CammParser(BaseParser):
|
|
|
28
32
|
return []
|
|
29
33
|
|
|
30
34
|
def extract_make(self) -> T.Optional[str]:
|
|
31
|
-
|
|
32
|
-
if not source_path:
|
|
33
|
-
return None
|
|
34
|
-
with source_path.open("rb") as _fp:
|
|
35
|
-
return self.__camera_info[0] or None
|
|
35
|
+
return self._camera_info[0] or None
|
|
36
36
|
|
|
37
37
|
def extract_model(self) -> T.Optional[str]:
|
|
38
|
-
|
|
39
|
-
if not source_path:
|
|
40
|
-
return None
|
|
41
|
-
with source_path.open("rb") as _fp:
|
|
42
|
-
return self.__camera_info[1] or None
|
|
38
|
+
return self._camera_info[1] or None
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import logging
|
|
3
|
+
import typing as T
|
|
4
|
+
|
|
5
|
+
from ... import geo, telemetry
|
|
6
|
+
from ...geotag import geotag_images_from_gpx_file
|
|
7
|
+
from .base_parser import BaseParser
|
|
8
|
+
from .generic_video_parser import GenericVideoParser
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
LOG = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GpxParser(BaseParser):
|
|
15
|
+
default_source_pattern = "%g.gpx"
|
|
16
|
+
parser_label = "gpx"
|
|
17
|
+
|
|
18
|
+
def extract_points(self) -> T.Sequence[geo.Point]:
|
|
19
|
+
path = self.geotag_source_path
|
|
20
|
+
if not path:
|
|
21
|
+
return []
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
gpx_tracks = geotag_images_from_gpx_file.parse_gpx(path)
|
|
25
|
+
except Exception as ex:
|
|
26
|
+
raise RuntimeError(
|
|
27
|
+
f"Error parsing GPX {path}: {ex.__class__.__name__}: {ex}"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if 1 < len(gpx_tracks):
|
|
31
|
+
LOG.warning(
|
|
32
|
+
"Found %s tracks in the GPX file %s. Will merge points in all the tracks as a single track for interpolation",
|
|
33
|
+
len(gpx_tracks),
|
|
34
|
+
self.videoPath,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
gpx_points: T.Sequence[geo.Point] = sum(gpx_tracks, [])
|
|
38
|
+
if not gpx_points:
|
|
39
|
+
return gpx_points
|
|
40
|
+
|
|
41
|
+
offset = self._synx_gpx_by_first_gps_timestamp(gpx_points)
|
|
42
|
+
|
|
43
|
+
self._rebase_times(gpx_points, offset=offset)
|
|
44
|
+
|
|
45
|
+
return gpx_points
|
|
46
|
+
|
|
47
|
+
def _synx_gpx_by_first_gps_timestamp(
|
|
48
|
+
self, gpx_points: T.Sequence[geo.Point]
|
|
49
|
+
) -> float:
|
|
50
|
+
offset: float = 0.0
|
|
51
|
+
|
|
52
|
+
if not gpx_points:
|
|
53
|
+
return offset
|
|
54
|
+
|
|
55
|
+
first_gpx_dt = datetime.datetime.fromtimestamp(
|
|
56
|
+
gpx_points[0].time, tz=datetime.timezone.utc
|
|
57
|
+
)
|
|
58
|
+
LOG.info("First GPX timestamp: %s", first_gpx_dt)
|
|
59
|
+
|
|
60
|
+
# Extract first GPS timestamp (if found) for synchronization
|
|
61
|
+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
|
|
62
|
+
parser = GenericVideoParser(self.videoPath, self.options, {})
|
|
63
|
+
gps_points = parser.extract_points()
|
|
64
|
+
|
|
65
|
+
if not gps_points:
|
|
66
|
+
LOG.warning(
|
|
67
|
+
"Skip GPX synchronization because no GPS found in video %s",
|
|
68
|
+
self.videoPath,
|
|
69
|
+
)
|
|
70
|
+
return offset
|
|
71
|
+
|
|
72
|
+
first_gps_point = gps_points[0]
|
|
73
|
+
if isinstance(first_gps_point, telemetry.GPSPoint):
|
|
74
|
+
if first_gps_point.epoch_time is not None:
|
|
75
|
+
first_gps_dt = datetime.datetime.fromtimestamp(
|
|
76
|
+
first_gps_point.epoch_time, tz=datetime.timezone.utc
|
|
77
|
+
)
|
|
78
|
+
LOG.info("First GPS timestamp: %s", first_gps_dt)
|
|
79
|
+
offset = gpx_points[0].time - first_gps_point.epoch_time
|
|
80
|
+
if offset:
|
|
81
|
+
LOG.warning(
|
|
82
|
+
"Found offset between GPX %s and video GPS timestamps %s: %s seconds",
|
|
83
|
+
first_gpx_dt,
|
|
84
|
+
first_gps_dt,
|
|
85
|
+
offset,
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
LOG.info(
|
|
89
|
+
"GPX and GPS are perfectly synchronized (all starts from %s)",
|
|
90
|
+
first_gpx_dt,
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
LOG.warning(
|
|
94
|
+
"Skip GPX synchronization because no GPS epoch time found in video %s",
|
|
95
|
+
self.videoPath,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return offset
|
|
99
|
+
|
|
100
|
+
def extract_make(self) -> T.Optional[str]:
|
|
101
|
+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
|
|
102
|
+
parser = GenericVideoParser(self.videoPath, self.options, {})
|
|
103
|
+
return parser.extract_make()
|
|
104
|
+
|
|
105
|
+
def extract_model(self) -> T.Optional[str]:
|
|
106
|
+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
|
|
107
|
+
parser = GenericVideoParser(self.videoPath, self.options, {})
|
|
108
|
+
return parser.extract_model()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = "0.13.1a1"
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import logging
|
|
3
|
-
import typing as T
|
|
4
|
-
|
|
5
|
-
from ... import geo, telemetry
|
|
6
|
-
from ...geotag import geotag_images_from_gpx_file
|
|
7
|
-
from .base_parser import BaseParser
|
|
8
|
-
from .generic_video_parser import GenericVideoParser
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
LOG = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class GpxParser(BaseParser):
|
|
15
|
-
default_source_pattern = "%g.gpx"
|
|
16
|
-
parser_label = "gpx"
|
|
17
|
-
|
|
18
|
-
def extract_points(self) -> T.Sequence[geo.Point]:
|
|
19
|
-
path = self.geotag_source_path
|
|
20
|
-
if not path:
|
|
21
|
-
return []
|
|
22
|
-
|
|
23
|
-
gpx_tracks = geotag_images_from_gpx_file.parse_gpx(path)
|
|
24
|
-
if 1 < len(gpx_tracks):
|
|
25
|
-
LOG.warning(
|
|
26
|
-
"Found %s tracks in the GPX file %s. Will merge points in all the tracks as a single track for interpolation",
|
|
27
|
-
len(gpx_tracks),
|
|
28
|
-
self.videoPath,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
gpx_points: T.Sequence[geo.Point] = sum(gpx_tracks, [])
|
|
32
|
-
if not gpx_points:
|
|
33
|
-
return gpx_points
|
|
34
|
-
|
|
35
|
-
first_gpx_dt = datetime.datetime.fromtimestamp(
|
|
36
|
-
gpx_points[0].time, tz=datetime.timezone.utc
|
|
37
|
-
)
|
|
38
|
-
LOG.info("First GPX timestamp: %s", first_gpx_dt)
|
|
39
|
-
|
|
40
|
-
# Extract first GPS timestamp (if found) for synchronization
|
|
41
|
-
offset: float = 0.0
|
|
42
|
-
parser = GenericVideoParser(self.videoPath, self.options, self.parserOptions)
|
|
43
|
-
gps_points = parser.extract_points()
|
|
44
|
-
if gps_points:
|
|
45
|
-
first_gps_point = gps_points[0]
|
|
46
|
-
if isinstance(first_gps_point, telemetry.GPSPoint):
|
|
47
|
-
if first_gps_point.epoch_time is not None:
|
|
48
|
-
first_gps_dt = datetime.datetime.fromtimestamp(
|
|
49
|
-
first_gps_point.epoch_time, tz=datetime.timezone.utc
|
|
50
|
-
)
|
|
51
|
-
LOG.info("First GPS timestamp: %s", first_gps_dt)
|
|
52
|
-
offset = gpx_points[0].time - first_gps_point.epoch_time
|
|
53
|
-
if offset:
|
|
54
|
-
LOG.warning(
|
|
55
|
-
"Found offset between GPX %s and video GPS timestamps %s: %s seconds",
|
|
56
|
-
first_gpx_dt,
|
|
57
|
-
first_gps_dt,
|
|
58
|
-
offset,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
self._rebase_times(gpx_points, offset=offset)
|
|
62
|
-
|
|
63
|
-
return gpx_points
|
|
64
|
-
|
|
65
|
-
def extract_make(self) -> T.Optional[str]:
|
|
66
|
-
parser = GenericVideoParser(self.videoPath, self.options, self.parserOptions)
|
|
67
|
-
return parser.extract_make()
|
|
68
|
-
|
|
69
|
-
def extract_model(self) -> T.Optional[str]:
|
|
70
|
-
parser = GenericVideoParser(self.videoPath, self.options, self.parserOptions)
|
|
71
|
-
return parser.extract_model()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/authenticate.py
RENAMED
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/process_and_upload.py
RENAMED
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/sample_video.py
RENAMED
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/commands/video_process.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/exiftool_read_video.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/blackvue_parser.py
RENAMED
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/geotag_from_generic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/geotag/gpmf_gps_filter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/construct_mp4_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/mp4_sample_parser.py
RENAMED
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/simple_mp4_builder.py
RENAMED
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/mp4/simple_mp4_parser.py
RENAMED
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/process_geotag_properties.py
RENAMED
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools/process_sequence_properties.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{mapillary_tools-0.13.1a1 → mapillary_tools-0.13.3a1}/mapillary_tools.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|