mapillary-tools 0.10.2a0__py3-none-any.whl → 0.10.3a1__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 (31) hide show
  1. mapillary_tools/__init__.py +1 -1
  2. mapillary_tools/commands/process.py +4 -3
  3. mapillary_tools/exceptions.py +4 -0
  4. mapillary_tools/exif_read.py +543 -65
  5. mapillary_tools/exiftool_read.py +406 -0
  6. mapillary_tools/exiftool_read_video.py +360 -0
  7. mapillary_tools/geo.py +10 -2
  8. mapillary_tools/geotag/geotag_from_generic.py +13 -2
  9. mapillary_tools/geotag/{geotag_from_exif.py → geotag_images_from_exif.py} +51 -67
  10. mapillary_tools/geotag/geotag_images_from_exiftool.py +123 -0
  11. mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +81 -0
  12. mapillary_tools/geotag/{geotag_from_gpx.py → geotag_images_from_gpx.py} +16 -13
  13. mapillary_tools/geotag/{geotag_from_gpx_file.py → geotag_images_from_gpx_file.py} +52 -36
  14. mapillary_tools/geotag/{geotag_from_nmea_file.py → geotag_images_from_nmea_file.py} +4 -5
  15. mapillary_tools/geotag/geotag_images_from_video.py +87 -0
  16. mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +105 -0
  17. mapillary_tools/geotag/geotag_videos_from_video.py +175 -0
  18. mapillary_tools/process_geotag_properties.py +65 -31
  19. mapillary_tools/sample_video.py +19 -6
  20. mapillary_tools/types.py +2 -0
  21. mapillary_tools/utils.py +24 -2
  22. {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/METADATA +1 -1
  23. {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/RECORD +27 -24
  24. mapillary_tools/geotag/geotag_from_blackvue.py +0 -93
  25. mapillary_tools/geotag/geotag_from_camm.py +0 -94
  26. mapillary_tools/geotag/geotag_from_gopro.py +0 -96
  27. mapillary_tools/geotag/geotag_from_video.py +0 -145
  28. {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/LICENSE +0 -0
  29. {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/WHEEL +0 -0
  30. {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/entry_points.txt +0 -0
  31. {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/top_level.txt +0 -0
@@ -1,94 +0,0 @@
1
- import logging
2
- import typing as T
3
- from pathlib import Path
4
-
5
- from tqdm import tqdm
6
-
7
- from .. import exceptions, geo, types, utils
8
- from . import camm_parser, utils as geotag_utils
9
- from .geotag_from_generic import GeotagFromGeneric
10
- from .geotag_from_gpx import GeotagFromGPXWithProgress
11
-
12
-
13
- LOG = logging.getLogger(__name__)
14
-
15
-
16
- class GeotagFromCAMM(GeotagFromGeneric):
17
- def __init__(
18
- self,
19
- image_paths: T.Sequence[Path],
20
- video_paths: T.Sequence[Path],
21
- offset_time: float = 0.0,
22
- ):
23
- self.image_paths = image_paths
24
- self.video_paths = video_paths
25
- self.offset_time = offset_time
26
- super().__init__()
27
-
28
- def to_description(self) -> T.List[types.ImageMetadataOrError]:
29
- all_metadatas: T.List[types.ImageMetadataOrError] = []
30
- for video_path in self.video_paths:
31
- LOG.debug("Processing CAMM video: %s", video_path)
32
-
33
- sample_image_paths = list(
34
- utils.filter_video_samples(self.image_paths, video_path)
35
- )
36
- LOG.debug(
37
- "Found %d sample images from video %s",
38
- len(sample_image_paths),
39
- video_path,
40
- )
41
-
42
- if not sample_image_paths:
43
- continue
44
-
45
- points = camm_parser.parse_gpx(video_path)
46
-
47
- # bypass empty points to raise MapillaryGPXEmptyError
48
- if points and geotag_utils.is_video_stationary(
49
- geo.get_max_distance_from_start([(p.lat, p.lon) for p in points])
50
- ):
51
- LOG.warning(
52
- "Fail %d sample images due to stationary video %s",
53
- len(sample_image_paths),
54
- video_path,
55
- )
56
- for image_path in sample_image_paths:
57
- err_metadata = types.describe_error_metadata(
58
- exceptions.MapillaryStationaryVideoError(
59
- "Stationary CAMM video"
60
- ),
61
- image_path,
62
- filetype=types.FileType.IMAGE,
63
- )
64
- all_metadatas.append(err_metadata)
65
- continue
66
-
67
- with tqdm(
68
- total=len(sample_image_paths),
69
- desc=f"Interpolating {video_path.name}",
70
- unit="images",
71
- disable=LOG.getEffectiveLevel() <= logging.DEBUG,
72
- ) as pbar:
73
- geotag = GeotagFromGPXWithProgress(
74
- sample_image_paths,
75
- points,
76
- use_gpx_start_time=False,
77
- use_image_start_time=True,
78
- offset_time=self.offset_time,
79
- progress_bar=pbar,
80
- )
81
- this_metadatas = geotag.to_description()
82
- all_metadatas.extend(this_metadatas)
83
-
84
- # update make and model
85
- with video_path.open("rb") as fp:
86
- make, model = camm_parser.extract_camera_make_and_model(fp)
87
- LOG.debug('Found camera make "%s" and model "%s"', make, model)
88
-
89
- for metadata in this_metadatas:
90
- if isinstance(metadata, types.ImageMetadata):
91
- metadata.MAPDeviceMake = make
92
- metadata.MAPDeviceModel = model
93
-
94
- return all_metadatas
@@ -1,96 +0,0 @@
1
- import logging
2
- import typing as T
3
- from pathlib import Path
4
-
5
- from tqdm import tqdm
6
-
7
- from .. import exceptions, geo, types, utils
8
- from . import gpmf_gps_filter, gpmf_parser, utils as geotag_utils
9
- from .geotag_from_generic import GeotagFromGeneric
10
- from .geotag_from_gpx import GeotagFromGPXWithProgress
11
-
12
-
13
- LOG = logging.getLogger(__name__)
14
-
15
-
16
- class GeotagFromGoPro(GeotagFromGeneric):
17
- def __init__(
18
- self,
19
- image_paths: T.Sequence[Path],
20
- video_paths: T.Sequence[Path],
21
- offset_time: float = 0.0,
22
- ):
23
- self.image_paths = image_paths
24
- self.video_paths = video_paths
25
- self.offset_time = offset_time
26
- super().__init__()
27
-
28
- def to_description(self) -> T.List[types.ImageMetadataOrError]:
29
- all_metadatas: T.List[types.ImageMetadataOrError] = []
30
-
31
- for video_path in self.video_paths:
32
- LOG.debug("Processing GoPro video: %s", video_path)
33
-
34
- sample_image_paths = list(
35
- utils.filter_video_samples(self.image_paths, video_path)
36
- )
37
- LOG.debug(
38
- "Found %d sample images from video %s",
39
- len(sample_image_paths),
40
- video_path,
41
- )
42
-
43
- if not sample_image_paths:
44
- continue
45
-
46
- points = gpmf_gps_filter.filter_noisy_points(
47
- gpmf_parser.parse_gpx(video_path)
48
- )
49
-
50
- # bypass empty points to raise MapillaryGPXEmptyError
51
- if points and geotag_utils.is_video_stationary(
52
- geo.get_max_distance_from_start([(p.lat, p.lon) for p in points])
53
- ):
54
- LOG.warning(
55
- "Fail %d sample images due to stationary video %s",
56
- len(sample_image_paths),
57
- video_path,
58
- )
59
- for image_path in sample_image_paths:
60
- err_metadata = types.describe_error_metadata(
61
- exceptions.MapillaryStationaryVideoError(
62
- "Stationary GoPro video"
63
- ),
64
- image_path,
65
- filetype=types.FileType.IMAGE,
66
- )
67
- all_metadatas.append(err_metadata)
68
- continue
69
-
70
- with tqdm(
71
- total=len(sample_image_paths),
72
- desc=f"Interpolating {video_path.name}",
73
- unit="images",
74
- disable=LOG.getEffectiveLevel() <= logging.DEBUG,
75
- ) as pbar:
76
- geotag = GeotagFromGPXWithProgress(
77
- sample_image_paths,
78
- points,
79
- use_gpx_start_time=False,
80
- use_image_start_time=True,
81
- offset_time=self.offset_time,
82
- progress_bar=pbar,
83
- )
84
- this_metadatas = geotag.to_description()
85
- all_metadatas.extend(this_metadatas)
86
-
87
- # update make and model
88
- with video_path.open("rb") as fp:
89
- make, model = "GoPro", gpmf_parser.extract_camera_model(fp)
90
- LOG.debug('Found camera make "%s" and model "%s"', make, model)
91
- for metadata in this_metadatas:
92
- if isinstance(metadata, types.ImageMetadata):
93
- metadata.MAPDeviceMake = make
94
- metadata.MAPDeviceModel = model
95
-
96
- return all_metadatas
@@ -1,145 +0,0 @@
1
- import io
2
- import logging
3
- import typing as T
4
- from multiprocessing import Pool
5
- from pathlib import Path
6
-
7
- from tqdm import tqdm
8
-
9
- from .. import exceptions, geo, types
10
- from . import (
11
- blackvue_parser,
12
- camm_parser,
13
- gpmf_gps_filter,
14
- gpmf_parser,
15
- simple_mp4_parser as parser,
16
- utils as video_utils,
17
- )
18
-
19
- LOG = logging.getLogger(__name__)
20
-
21
-
22
- class GeotagFromVideo:
23
- def __init__(
24
- self,
25
- video_paths: T.Sequence[Path],
26
- filetypes: T.Optional[T.Set[types.FileType]] = None,
27
- ):
28
- self.video_paths = video_paths
29
- self.filetypes = filetypes
30
-
31
- def to_descriptions(self) -> T.List[types.VideoMetadataOrError]:
32
- with Pool() as pool:
33
- video_metadatas = pool.imap(
34
- self._geotag_video,
35
- self.video_paths,
36
- )
37
- return list(
38
- tqdm(
39
- video_metadatas,
40
- desc="Extracting GPS tracks from videos",
41
- unit="videos",
42
- disable=LOG.getEffectiveLevel() <= logging.DEBUG,
43
- total=len(self.video_paths),
44
- )
45
- )
46
-
47
- def _geotag_video(
48
- self,
49
- video_path: Path,
50
- ) -> types.VideoMetadataOrError:
51
- return GeotagFromVideo.geotag_video(video_path, self.filetypes)
52
-
53
- @staticmethod
54
- def geotag_video(
55
- video_path: Path,
56
- filetypes: T.Optional[T.Set[types.FileType]] = None,
57
- ) -> types.VideoMetadataOrError:
58
- video_metadata = None
59
- if filetypes is None or types.FileType.CAMM in filetypes:
60
- with video_path.open("rb") as fp:
61
- try:
62
- points = camm_parser.extract_points(fp)
63
- except parser.ParsingError:
64
- points = None
65
-
66
- if points is not None:
67
- fp.seek(0, io.SEEK_SET)
68
- make, model = camm_parser.extract_camera_make_and_model(fp)
69
- video_metadata = types.VideoMetadata(
70
- video_path, None, types.FileType.CAMM, points, make, model
71
- )
72
-
73
- if filetypes is None or types.FileType.GOPRO in filetypes:
74
- with video_path.open("rb") as fp:
75
- try:
76
- points_with_fix = gpmf_parser.extract_points(fp)
77
- except parser.ParsingError:
78
- points_with_fix = None
79
-
80
- if points_with_fix is not None:
81
- fp.seek(0, io.SEEK_SET)
82
- make, model = "GoPro", gpmf_parser.extract_camera_model(fp)
83
- video_metadata = types.VideoMetadata(
84
- video_path,
85
- None,
86
- types.FileType.GOPRO,
87
- T.cast(T.List[geo.Point], points_with_fix),
88
- make,
89
- model,
90
- )
91
- video_metadata.points = T.cast(
92
- T.List[geo.Point],
93
- gpmf_gps_filter.filter_noisy_points(
94
- T.cast(
95
- T.List[gpmf_parser.PointWithFix],
96
- video_metadata.points,
97
- )
98
- ),
99
- )
100
-
101
- if filetypes is None or types.FileType.BLACKVUE in filetypes:
102
- with video_path.open("rb") as fp:
103
- try:
104
- points = blackvue_parser.extract_points(fp)
105
- except parser.ParsingError:
106
- points = None
107
-
108
- if points is not None:
109
- fp.seek(0, io.SEEK_SET)
110
- make, model = "BlackVue", blackvue_parser.extract_camera_model(fp)
111
- video_metadata = types.VideoMetadata(
112
- video_path, None, types.FileType.BLACKVUE, points, make, model
113
- )
114
-
115
- if video_metadata is None:
116
- return types.describe_error_metadata(
117
- exceptions.MapillaryVideoError("No GPS data found from the video"),
118
- video_path,
119
- filetype=None,
120
- )
121
-
122
- if not video_metadata.points:
123
- return types.describe_error_metadata(
124
- exceptions.MapillaryGPXEmptyError("Empty GPS data found"),
125
- video_metadata.filename,
126
- filetype=video_metadata.filetype,
127
- )
128
-
129
- stationary = video_utils.is_video_stationary(
130
- geo.get_max_distance_from_start(
131
- [(p.lat, p.lon) for p in video_metadata.points]
132
- )
133
- )
134
- if stationary:
135
- return types.describe_error_metadata(
136
- exceptions.MapillaryStationaryVideoError("Stationary video"),
137
- video_metadata.filename,
138
- filetype=video_metadata.filetype,
139
- )
140
-
141
- if not isinstance(video_metadata, types.ErrorMetadata):
142
- LOG.debug("Calculating MD5 checksum for %s", str(video_metadata.filename))
143
- video_metadata.update_md5sum()
144
-
145
- return video_metadata