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.
- mapillary_tools/__init__.py +1 -1
- mapillary_tools/commands/process.py +4 -3
- mapillary_tools/exceptions.py +4 -0
- mapillary_tools/exif_read.py +543 -65
- mapillary_tools/exiftool_read.py +406 -0
- mapillary_tools/exiftool_read_video.py +360 -0
- mapillary_tools/geo.py +10 -2
- mapillary_tools/geotag/geotag_from_generic.py +13 -2
- mapillary_tools/geotag/{geotag_from_exif.py → geotag_images_from_exif.py} +51 -67
- mapillary_tools/geotag/geotag_images_from_exiftool.py +123 -0
- mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +81 -0
- mapillary_tools/geotag/{geotag_from_gpx.py → geotag_images_from_gpx.py} +16 -13
- mapillary_tools/geotag/{geotag_from_gpx_file.py → geotag_images_from_gpx_file.py} +52 -36
- mapillary_tools/geotag/{geotag_from_nmea_file.py → geotag_images_from_nmea_file.py} +4 -5
- mapillary_tools/geotag/geotag_images_from_video.py +87 -0
- mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +105 -0
- mapillary_tools/geotag/geotag_videos_from_video.py +175 -0
- mapillary_tools/process_geotag_properties.py +65 -31
- mapillary_tools/sample_video.py +19 -6
- mapillary_tools/types.py +2 -0
- mapillary_tools/utils.py +24 -2
- {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/METADATA +1 -1
- {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/RECORD +27 -24
- mapillary_tools/geotag/geotag_from_blackvue.py +0 -93
- mapillary_tools/geotag/geotag_from_camm.py +0 -94
- mapillary_tools/geotag/geotag_from_gopro.py +0 -96
- mapillary_tools/geotag/geotag_from_video.py +0 -145
- {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/LICENSE +0 -0
- {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/WHEEL +0 -0
- {mapillary_tools-0.10.2a0.dist-info → mapillary_tools-0.10.3a1.dist-info}/entry_points.txt +0 -0
- {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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|