mapillary-tools 0.14.0a1__py3-none-any.whl → 0.14.0a2__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.
Files changed (67) hide show
  1. mapillary_tools/__init__.py +1 -1
  2. mapillary_tools/api_v4.py +4 -4
  3. mapillary_tools/camm/camm_parser.py +5 -5
  4. mapillary_tools/commands/__main__.py +1 -2
  5. mapillary_tools/config.py +7 -5
  6. mapillary_tools/constants.py +1 -2
  7. mapillary_tools/exceptions.py +1 -1
  8. mapillary_tools/exif_read.py +65 -65
  9. mapillary_tools/exif_write.py +7 -7
  10. mapillary_tools/exiftool_read.py +23 -46
  11. mapillary_tools/exiftool_read_video.py +36 -34
  12. mapillary_tools/ffmpeg.py +24 -23
  13. mapillary_tools/geo.py +4 -21
  14. mapillary_tools/geotag/{geotag_from_generic.py → base.py} +32 -48
  15. mapillary_tools/geotag/factory.py +27 -34
  16. mapillary_tools/geotag/geotag_images_from_exif.py +15 -51
  17. mapillary_tools/geotag/geotag_images_from_exiftool.py +107 -59
  18. mapillary_tools/geotag/geotag_images_from_gpx.py +20 -10
  19. mapillary_tools/geotag/geotag_images_from_gpx_file.py +2 -34
  20. mapillary_tools/geotag/geotag_images_from_nmea_file.py +0 -3
  21. mapillary_tools/geotag/geotag_images_from_video.py +16 -14
  22. mapillary_tools/geotag/geotag_videos_from_exiftool.py +97 -0
  23. mapillary_tools/geotag/geotag_videos_from_gpx.py +14 -115
  24. mapillary_tools/geotag/geotag_videos_from_video.py +14 -147
  25. mapillary_tools/geotag/image_extractors/base.py +18 -0
  26. mapillary_tools/geotag/image_extractors/exif.py +60 -0
  27. mapillary_tools/geotag/image_extractors/exiftool.py +18 -0
  28. mapillary_tools/geotag/options.py +1 -0
  29. mapillary_tools/geotag/utils.py +62 -0
  30. mapillary_tools/geotag/video_extractors/base.py +18 -0
  31. mapillary_tools/geotag/video_extractors/exiftool.py +70 -0
  32. mapillary_tools/{video_data_extraction/extractors/gpx_parser.py → geotag/video_extractors/gpx.py} +57 -39
  33. mapillary_tools/geotag/video_extractors/native.py +157 -0
  34. mapillary_tools/gpmf/gpmf_parser.py +16 -16
  35. mapillary_tools/gpmf/gps_filter.py +5 -3
  36. mapillary_tools/history.py +4 -2
  37. mapillary_tools/mp4/construct_mp4_parser.py +9 -8
  38. mapillary_tools/mp4/mp4_sample_parser.py +27 -27
  39. mapillary_tools/mp4/simple_mp4_builder.py +10 -9
  40. mapillary_tools/mp4/simple_mp4_parser.py +13 -12
  41. mapillary_tools/process_geotag_properties.py +5 -7
  42. mapillary_tools/process_sequence_properties.py +40 -38
  43. mapillary_tools/sample_video.py +8 -8
  44. mapillary_tools/telemetry.py +6 -5
  45. mapillary_tools/types.py +33 -38
  46. mapillary_tools/utils.py +16 -18
  47. {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/METADATA +1 -1
  48. mapillary_tools-0.14.0a2.dist-info/RECORD +72 -0
  49. mapillary_tools/geotag/__init__.py +0 -1
  50. mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -77
  51. mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -151
  52. mapillary_tools/video_data_extraction/cli_options.py +0 -22
  53. mapillary_tools/video_data_extraction/extract_video_data.py +0 -157
  54. mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -75
  55. mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -49
  56. mapillary_tools/video_data_extraction/extractors/camm_parser.py +0 -62
  57. mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +0 -74
  58. mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -52
  59. mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -52
  60. mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -58
  61. mapillary_tools/video_data_extraction/extractors/nmea_parser.py +0 -24
  62. mapillary_tools/video_data_extraction/video_data_parser_factory.py +0 -39
  63. mapillary_tools-0.14.0a1.dist-info/RECORD +0 -78
  64. {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/WHEEL +0 -0
  65. {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/entry_points.txt +0 -0
  66. {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/licenses/LICENSE +0 -0
  67. {mapillary_tools-0.14.0a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/top_level.txt +0 -0
@@ -19,7 +19,7 @@ LOG = logging.getLogger(__name__)
19
19
 
20
20
  def _normalize_path(
21
21
  video_import_path: Path, skip_subfolders: bool
22
- ) -> T.Tuple[Path, T.List[Path]]:
22
+ ) -> tuple[Path, list[Path]]:
23
23
  if video_import_path.is_dir():
24
24
  video_list = utils.find_videos(
25
25
  [video_import_path], skip_subfolders=skip_subfolders
@@ -51,7 +51,7 @@ def sample_video(
51
51
  video_sample_distance=constants.VIDEO_SAMPLE_DISTANCE,
52
52
  video_sample_interval=constants.VIDEO_SAMPLE_INTERVAL,
53
53
  video_duration_ratio=constants.VIDEO_DURATION_RATIO,
54
- video_start_time: T.Optional[str] = None,
54
+ video_start_time: str | None = None,
55
55
  skip_sample_errors: bool = False,
56
56
  rerun: bool = False,
57
57
  ) -> None:
@@ -62,7 +62,7 @@ def sample_video(
62
62
  f"Expect either non-negative video_sample_distance or positive video_sample_interval but got {video_sample_distance} and {video_sample_interval} respectively"
63
63
  )
64
64
 
65
- video_start_time_dt: T.Optional[datetime.datetime] = None
65
+ video_start_time_dt: datetime.datetime | None = None
66
66
  if video_start_time is not None:
67
67
  try:
68
68
  video_start_time_dt = types.map_capture_time_to_datetime(video_start_time)
@@ -179,7 +179,7 @@ def _sample_single_video_by_interval(
179
179
  sample_dir: Path,
180
180
  sample_interval: float,
181
181
  duration_ratio: float,
182
- start_time: T.Optional[datetime.datetime] = None,
182
+ start_time: datetime.datetime | None = None,
183
183
  ) -> None:
184
184
  ffmpeg = ffmpeglib.FFMPEG(constants.FFMPEG_PATH, constants.FFPROBE_PATH)
185
185
 
@@ -219,7 +219,7 @@ def _sample_video_stream_by_distance(
219
219
  points: T.Sequence[geo.Point],
220
220
  video_track_parser: mp4_sample_parser.TrackBoxParser,
221
221
  sample_distance: float,
222
- ) -> T.Dict[int, T.Tuple[mp4_sample_parser.Sample, geo.Point]]:
222
+ ) -> dict[int, tuple[mp4_sample_parser.Sample, geo.Point]]:
223
223
  """
224
224
  Locate video frames along the track (points), then resample them by the minimal sample_distance, and return the sparse frames.
225
225
  """
@@ -275,7 +275,7 @@ def _sample_single_video_by_distance(
275
275
  video_path: Path,
276
276
  sample_dir: Path,
277
277
  sample_distance: float,
278
- start_time: T.Optional[datetime.datetime] = None,
278
+ start_time: datetime.datetime | None = None,
279
279
  ) -> None:
280
280
  ffmpeg = ffmpeglib.FFMPEG(constants.FFMPEG_PATH, constants.FFPROBE_PATH)
281
281
 
@@ -290,9 +290,9 @@ def _sample_single_video_by_distance(
290
290
 
291
291
  LOG.info("Extracting video metdata")
292
292
 
293
- video_metadatas = geotag_videos_from_video.GeotagVideosFromVideo(
293
+ video_metadatas = geotag_videos_from_video.GeotagVideosFromVideo().to_description(
294
294
  [video_path]
295
- ).to_description()
295
+ )
296
296
  assert len(video_metadatas) == 1, "expect 1 video metadata"
297
297
  video_metadata = video_metadatas[0]
298
298
  if isinstance(video_metadata, types.ErrorMetadata):
@@ -1,5 +1,6 @@
1
+ from __future__ import annotations
2
+
1
3
  import dataclasses
2
- import typing as T
3
4
  from enum import Enum, unique
4
5
 
5
6
  from .geo import Point
@@ -26,10 +27,10 @@ class TimestampedMeasurement:
26
27
 
27
28
  @dataclasses.dataclass
28
29
  class GPSPoint(TimestampedMeasurement, Point):
29
- epoch_time: T.Optional[float]
30
- fix: T.Optional[GPSFix]
31
- precision: T.Optional[float]
32
- ground_speed: T.Optional[float]
30
+ epoch_time: float | None
31
+ fix: GPSFix | None
32
+ precision: float | None
33
+ ground_speed: float | None
33
34
 
34
35
 
35
36
  @dataclasses.dataclass
mapillary_tools/types.py CHANGED
@@ -53,23 +53,20 @@ NATIVE_VIDEO_FILETYPES = {
53
53
  @dataclasses.dataclass
54
54
  class ImageMetadata(geo.Point):
55
55
  filename: Path
56
- # if None or absent, it will be calculated
57
- md5sum: T.Optional[str] = None
58
- # filetype: is always FileType.IMAGE
59
- width: T.Optional[int] = None
60
- height: T.Optional[int] = None
61
- MAPSequenceUUID: T.Optional[str] = None
62
- MAPDeviceMake: T.Optional[str] = None
63
- MAPDeviceModel: T.Optional[str] = None
64
- MAPGPSAccuracyMeters: T.Optional[float] = None
65
- MAPCameraUUID: T.Optional[str] = None
66
- MAPOrientation: T.Optional[int] = None
67
- # deprecated since v0.10.0; keep here for compatibility
68
- MAPMetaTags: T.Optional[T.Dict] = None
69
- MAPFilename: T.Optional[str] = None
70
- filesize: T.Optional[int] = None
71
-
72
- def update_md5sum(self, image_data: T.Optional[T.BinaryIO] = None) -> None:
56
+ md5sum: str | None = None
57
+ width: int | None = None
58
+ height: int | None = None
59
+ MAPSequenceUUID: str | None = None
60
+ MAPDeviceMake: str | None = None
61
+ MAPDeviceModel: str | None = None
62
+ MAPGPSAccuracyMeters: float | None = None
63
+ MAPCameraUUID: str | None = None
64
+ MAPOrientation: int | None = None
65
+ MAPMetaTags: dict | None = None
66
+ MAPFilename: str | None = None
67
+ filesize: int | None = None
68
+
69
+ def update_md5sum(self, image_data: T.BinaryIO | None = None) -> None:
73
70
  if self.md5sum is None:
74
71
  if image_data is None:
75
72
  with self.filename.open("rb") as fp:
@@ -87,13 +84,12 @@ class ImageMetadata(geo.Point):
87
84
  @dataclasses.dataclass
88
85
  class VideoMetadata:
89
86
  filename: Path
90
- # if None or absent, it will be calculated
91
87
  filetype: FileType
92
88
  points: T.Sequence[geo.Point]
93
- md5sum: T.Optional[str] = None
94
- make: T.Optional[str] = None
95
- model: T.Optional[str] = None
96
- filesize: T.Optional[int] = None
89
+ md5sum: str | None = None
90
+ make: str | None = None
91
+ model: str | None = None
92
+ filesize: int | None = None
97
93
 
98
94
  def update_md5sum(self) -> None:
99
95
  if self.md5sum is None:
@@ -144,7 +140,7 @@ def combine_filetype_filters(
144
140
 
145
141
 
146
142
  class UserItem(TypedDict, total=False):
147
- MAPOrganizationKey: T.Union[int, str]
143
+ MAPOrganizationKey: int | str
148
144
  # Not in use. Keep here for back-compatibility
149
145
  MAPSettingsUsername: str
150
146
  MAPSettingsUserKey: str
@@ -183,23 +179,22 @@ class ImageDescription(_SequenceOnly, _Image, MetaProperties, total=True):
183
179
  # filename is required
184
180
  filename: str
185
181
  # if None or absent, it will be calculated
186
- md5sum: T.Optional[str]
182
+ md5sum: str | None
187
183
  filetype: Literal["image"]
188
- filesize: T.Optional[int]
184
+ filesize: int | None
189
185
 
190
186
 
191
187
  class _VideoDescriptionRequired(TypedDict, total=True):
192
188
  filename: str
193
- # if None or absent, it will be calculated
194
- md5sum: T.Optional[str]
189
+ md5sum: str | None
195
190
  filetype: str
196
- MAPGPSTrack: T.List[T.Sequence[T.Union[float, int, None]]]
191
+ MAPGPSTrack: list[T.Sequence[float | int | None]]
197
192
 
198
193
 
199
194
  class VideoDescription(_VideoDescriptionRequired, total=False):
200
195
  MAPDeviceMake: str
201
196
  MAPDeviceModel: str
202
- filesize: T.Optional[int]
197
+ filesize: int | None
203
198
 
204
199
 
205
200
  class _ErrorDescription(TypedDict, total=False):
@@ -207,7 +202,7 @@ class _ErrorDescription(TypedDict, total=False):
207
202
  type: str
208
203
  message: str
209
204
  # vars is optional
210
- vars: T.Dict
205
+ vars: dict
211
206
 
212
207
 
213
208
  class _ImageDescriptionErrorRequired(TypedDict, total=True):
@@ -238,7 +233,7 @@ def separate_errors(
238
233
 
239
234
 
240
235
  def _describe_error_desc(
241
- exc: Exception, filename: Path, filetype: T.Optional[FileType]
236
+ exc: Exception, filename: Path, filetype: FileType | None
242
237
  ) -> ImageDescriptionError:
243
238
  err: _ErrorDescription = {
244
239
  "type": exc.__class__.__name__,
@@ -397,7 +392,7 @@ VideoDescriptionSchema = {
397
392
  }
398
393
 
399
394
 
400
- def merge_schema(*schemas: T.Dict) -> T.Dict:
395
+ def merge_schema(*schemas: dict) -> dict:
401
396
  for s in schemas:
402
397
  assert s.get("type") == "object", "must be all object schemas"
403
398
  properties = {}
@@ -507,7 +502,7 @@ def validate_video_desc(desc: T.Any) -> None:
507
502
  raise exceptions.MapillaryMetadataValidationError(ex.message) from ex
508
503
 
509
504
 
510
- def datetime_to_map_capture_time(time: T.Union[datetime.datetime, int, float]) -> str:
505
+ def datetime_to_map_capture_time(time: datetime.datetime | int | float) -> str:
511
506
  if isinstance(time, (float, int)):
512
507
  dt = datetime.datetime.fromtimestamp(time, datetime.timezone.utc)
513
508
  # otherwise it will be assumed to be in local time
@@ -608,7 +603,7 @@ def from_desc(desc):
608
603
 
609
604
 
610
605
  def _from_image_desc(desc) -> ImageMetadata:
611
- kwargs: T.Dict = {}
606
+ kwargs: dict = {}
612
607
  for k, v in desc.items():
613
608
  if k not in [
614
609
  "filename",
@@ -638,7 +633,7 @@ def _from_image_desc(desc) -> ImageMetadata:
638
633
  )
639
634
 
640
635
 
641
- def _encode_point(p: geo.Point) -> T.Sequence[T.Union[float, int, None]]:
636
+ def _encode_point(p: geo.Point) -> T.Sequence[float | int | None]:
642
637
  entry = [
643
638
  int(p.time * 1000),
644
639
  round(p.lon, _COORDINATES_PRECISION),
@@ -743,10 +738,10 @@ def desc_file_to_exif(
743
738
 
744
739
 
745
740
  def group_and_sort_images(
746
- metadatas: T.Sequence[ImageMetadata],
747
- ) -> T.Dict[str, T.List[ImageMetadata]]:
741
+ metadatas: T.Iterable[ImageMetadata],
742
+ ) -> dict[str, list[ImageMetadata]]:
748
743
  # group metadatas by uuid
749
- sequences_by_uuid: T.Dict[str, T.List[ImageMetadata]] = {}
744
+ sequences_by_uuid: dict[str, list[ImageMetadata]] = {}
750
745
  missing_sequence_uuid = str(uuid.uuid4())
751
746
  for metadata in metadatas:
752
747
  if metadata.MAPSequenceUUID is None:
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.Sequence[Path], video_paths: T.Sequence[Path]
97
- ) -> T.Dict[Path, T.List[Path]]:
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: T.Dict[Path, T.List[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: T.Set[Path] = set()
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.Sequence[Path],
125
+ import_paths: T.Iterable[Path],
128
126
  skip_subfolders: bool = False,
129
- ) -> T.List[Path]:
130
- image_paths: T.List[Path] = []
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.Sequence[Path],
143
+ import_paths: T.Iterable[Path],
146
144
  skip_subfolders: bool = False,
147
- ) -> T.List[Path]:
148
- video_paths: T.List[Path] = []
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.Sequence[Path],
161
+ import_paths: T.Iterable[Path],
164
162
  skip_subfolders: bool = False,
165
- ) -> T.List[Path]:
166
- zip_paths: T.List[Path] = []
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.Sequence[Path]) -> T.List[Path]:
181
- xml_paths: T.List[Path] = []
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapillary_tools
3
- Version: 0.14.0a1
3
+ Version: 0.14.0a2
4
4
  Summary: Mapillary Image/Video Import Pipeline
5
5
  Home-page: https://github.com/mapillary/mapillary_tools
6
6
  Author: Mapillary
@@ -0,0 +1,72 @@
1
+ mapillary_tools/__init__.py,sha256=AdfWnUWJogoEJm_EqofQxlG8NYL78h0B0M3Igr2xQpE,21
2
+ mapillary_tools/api_v4.py,sha256=uvz48tb2HaM1wfomaUR0V2HdId39HoxnQAc1WWgptvo,10091
3
+ mapillary_tools/authenticate.py,sha256=KG3p_7D4D7nUvrGRLGp56_ogLy7JUHzkGSpBw8V70LY,11091
4
+ mapillary_tools/blackvue_parser.py,sha256=Gmz3M1cvBmB2W3Pme6x3w2yuixDpKiTOkQe82iPXr2s,3613
5
+ mapillary_tools/config.py,sha256=uoNW5PnB1a2TW959KmNSYgJ1Y4_trn0xxPHJQ6T-cbI,2633
6
+ mapillary_tools/constants.py,sha256=BVnAlqz2XJfodNXsJfsGIV0L4P_3--oOgw1pR38D-Ls,3770
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=WXpw7CI7B4daJHRWzzVXAyVgDrFN8nPxQ3UPcVQqRJM,14760
12
+ mapillary_tools/exiftool_runner.py,sha256=J04V0Y70kTiGE2q2m5TKWfORd0hmYNmAsmrZxb34S3s,2316
13
+ mapillary_tools/ffmpeg.py,sha256=tRgdChGHAhHXjZPivDuVImubrJWfSDZDBZ7mSzkeABc,15698
14
+ mapillary_tools/geo.py,sha256=MSfkjSAgTAeuBqncQ8-QXvDuJ7ar7bTt5P_e9HDIgVs,10604
15
+ mapillary_tools/history.py,sha256=8ng2WHXadzbE5gnsoik6gbmRoEXTuGPp997RBiiWy08,1649
16
+ mapillary_tools/ipc.py,sha256=DwWQb9hNshx0bg0Fo5NjY0mXjs-FkbR6tIQmjMgMtmg,1089
17
+ mapillary_tools/process_geotag_properties.py,sha256=f0Up7yNzgLn32VBFMzoMT6SCsdzFUFYmloOsVHmK5k0,13920
18
+ mapillary_tools/process_sequence_properties.py,sha256=LSYyCH1nmEcYWWZKF1bOYh4hqjwXdz4FY9TqQQfW7AU,23418
19
+ mapillary_tools/sample_video.py,sha256=MwTr2bY5RFnOFThNPg3YiyXCXzWOlTXKq7EOri_W3Vw,13880
20
+ mapillary_tools/telemetry.py,sha256=lL6qQbtOZft4DZZrCNK3njlwHT_30zLyYS_YRN5pgHY,1568
21
+ mapillary_tools/types.py,sha256=0O4fzbwwoT6Znp2Ag1ptjzBKGb3E_RhGyqUUZu6gkPY,23078
22
+ mapillary_tools/upload.py,sha256=exQeEh3jpys94k-3QDT-4xoeJ_lAHbQBoy749hbPfnA,24878
23
+ mapillary_tools/upload_api_v4.py,sha256=dTEpQDnV11TyPf5hefd-FVeEfw7WEVYyZcIUJttUEp0,6417
24
+ mapillary_tools/uploader.py,sha256=xPEszuXlgcgCklGfsgL8qN1ozsFuU4rLT4jocMD2dVI,18694
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/base.py,sha256=JdLBXHlC22gqmZkyfAykv1osrzitWoCK8RFHdCd5qlc,4864
39
+ mapillary_tools/geotag/factory.py,sha256=2HIOnUtfxsgWn2GF0PbbgHq3Tzyssrl4T39YDMtN42U,9564
40
+ mapillary_tools/geotag/geotag_images_from_exif.py,sha256=a5fDOp4tlEoVNwrOCJBrvSPzu1LbyxL4RNrNDD0tPP0,608
41
+ mapillary_tools/geotag/geotag_images_from_exiftool.py,sha256=YxMFwv3A35Hh9tcGX1B1Si_m6TNep54SL5XFAQadQh0,5302
42
+ mapillary_tools/geotag/geotag_images_from_gpx.py,sha256=b_4xKVUt0HLIjNN-v7ZVddXDZ9cdxAfecB5Dj2X6XQE,6353
43
+ mapillary_tools/geotag/geotag_images_from_gpx_file.py,sha256=HYQkwak32YBDuRrTNiIZOmE3iImCRc22HWWb485WRS4,1121
44
+ mapillary_tools/geotag/geotag_images_from_nmea_file.py,sha256=J8xj6ch9bMPRubJtsRGeb3sb9LyB0ZYy65NPEOVkUe8,1597
45
+ mapillary_tools/geotag/geotag_images_from_video.py,sha256=5_qVgLt_DKl7oQrr9kWxi4iM143_HPAx5Lau-3fB7t4,3136
46
+ mapillary_tools/geotag/geotag_videos_from_exiftool.py,sha256=7chO_tb7ECsBlOkFhiXQ4fhomVPkfBOWtdfzXGpsJes,3168
47
+ mapillary_tools/geotag/geotag_videos_from_gpx.py,sha256=k4a1imUVLg2yDqggCwTSqjvY7jXQd3E-vniBuJj2d4M,1017
48
+ mapillary_tools/geotag/geotag_videos_from_video.py,sha256=T8XS4lVF2Wz4eDXNi5Vt076M5dxjxJXibVrWhqVvErs,863
49
+ mapillary_tools/geotag/options.py,sha256=7Fz0QoCMyZZbhkKvY4HPNWYfnKdQSWE2oRXb7jYZhmA,4336
50
+ mapillary_tools/geotag/utils.py,sha256=0DenLVCLP-GZdk0rCFP0V1tjMGK4XAvYro3I_1RKgCA,1733
51
+ mapillary_tools/geotag/image_extractors/base.py,sha256=XoNrLCbJtd-MN-snbhv6zyr6zBfJRoJkntV0ptrh6qg,358
52
+ mapillary_tools/geotag/image_extractors/exif.py,sha256=cCUegbFqWxjAP4oOP1nZmwoJISWeKgjGO8h_t7nucHs,2079
53
+ mapillary_tools/geotag/image_extractors/exiftool.py,sha256=zokJmf-D2rPvASRJs3dZzEu7j82igpMOr4SE6Z1nsVg,497
54
+ mapillary_tools/geotag/video_extractors/base.py,sha256=_3B37T-m2HGy77avAF8i_kEatIyQqFfBu49qVnx_0PU,357
55
+ mapillary_tools/geotag/video_extractors/exiftool.py,sha256=CB49qZqgmxECma-N5757aEn37xXRbCNRcT4o-Ip7dCw,2312
56
+ mapillary_tools/geotag/video_extractors/gpx.py,sha256=Oycdn74CNiq1j8dAq7O9YiVNTun2mFWRH9VTUn_viRk,4114
57
+ mapillary_tools/geotag/video_extractors/native.py,sha256=XChOyDvrf88Vu8ED-FTT_hzax2FllokanszSW4Q2MEA,5702
58
+ mapillary_tools/gpmf/gpmf_gps_filter.py,sha256=7cg8wEjC1DrujKY76FZguXsaPqTRkG9-t32OeuOJQIc,2755
59
+ mapillary_tools/gpmf/gpmf_parser.py,sha256=uOVXkwJxC3Y2YfTdzUDpt7Bh0pdVqa5u0WUuv7pEJEs,24670
60
+ mapillary_tools/gpmf/gps_filter.py,sha256=zhXkuvr0Dd1bxGTYBwvk6P7xasY_RLuWjHaX7CdBayc,3796
61
+ mapillary_tools/mp4/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ mapillary_tools/mp4/construct_mp4_parser.py,sha256=ArJ5C8iWJhHNPFkN048DXh6EwQ8LMfnTMwoNlKV9NoI,17191
63
+ mapillary_tools/mp4/io_utils.py,sha256=KZaJTlgFS27Oh3pcA5MKXYFoCifqgFaEZJyU6lb1jc4,5416
64
+ mapillary_tools/mp4/mp4_sample_parser.py,sha256=0ILTq8M6mXFTI3agKgljpvO9uYa7HXGUGZpdHT8a6ac,11547
65
+ mapillary_tools/mp4/simple_mp4_builder.py,sha256=9TUGk1hzI6mQFN1P30jwHL3dCYz3Zz7rsm8UBvMAqMc,12734
66
+ mapillary_tools/mp4/simple_mp4_parser.py,sha256=g3vvPhBoNu7anhVzC5_XQCV7IwfRWro1vJ6d6GyDkHE,6315
67
+ mapillary_tools-0.14.0a2.dist-info/licenses/LICENSE,sha256=l2D8cKfFmmJq_wcVq_JElPJrlvWQOzNWx7gMLINucxc,1292
68
+ mapillary_tools-0.14.0a2.dist-info/METADATA,sha256=UDVbKn5kqizwZmE70erGYDOnSz8sg9CuTd81GHSWtDI,19782
69
+ mapillary_tools-0.14.0a2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
70
+ mapillary_tools-0.14.0a2.dist-info/entry_points.txt,sha256=A3f3LP-BO_P-U8Y29QfpT4jx6Mjk3sXjTi2Yew4bvj8,75
71
+ mapillary_tools-0.14.0a2.dist-info/top_level.txt,sha256=FbDkMgOrt1S70ho1WSBrOwzKOSkJFDwwqFOoY5-527s,16
72
+ mapillary_tools-0.14.0a2.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- from .. import geo # noqa: F401
@@ -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]