1
- import datetime
2
1
import logging
3
2
import os
4
- import sys
5
- import tempfile
6
3
import typing as T
7
4
from pathlib import Path
8
5
9
6
from tqdm import tqdm
10
7
11
- from .. import constants , exceptions , ffmpeg as ffmpeglib , geo , types , utils
12
- from ..geo import get_max_distance_from_start , gps_distance , pairwise
13
- from . import utils as geotag_utils
8
+ from .. import constants , exceptions , geo , types , utils
9
+ from . import gpmf_parser , utils as geotag_utils
14
10
from .geotag_from_generic import GeotagFromGeneric
15
11
16
12
from .geotag_from_gpx import GeotagFromGPXWithProgress
17
- from .gpmf import interpolate_times , parse_bin
18
13
19
14
20
15
LOG = logging .getLogger (__name__ )
@@ -25,7 +20,6 @@ def __init__(
25
20
self ,
26
21
image_dir : str ,
27
22
source_path : str ,
28
- use_gpx_start_time : bool = False ,
29
23
offset_time : float = 0.0 ,
30
24
):
31
25
self .image_dir = image_dir
@@ -34,10 +28,43 @@ def __init__(
34
28
else :
35
29
# it is okay to not suffix with .mp4
36
30
self .videos = [source_path ]
37
- self .use_gpx_start_time = use_gpx_start_time
38
31
self .offset_time = offset_time
39
32
super ().__init__ ()
40
33
34
+ def _filter_noisy_points (
35
+ self , points : T .Sequence [gpmf_parser .PointWithFix ], video : Path
36
+ ) -> T .Sequence [gpmf_parser .PointWithFix ]:
37
+ num_points = len (points )
38
+ points = [
39
+ p
40
+ for p in points
41
+ if p .gps_fix is not None and p .gps_fix .value in constants .GOPRO_GPS_FIXES
42
+ ]
43
+ if len (points ) < num_points :
44
+ LOG .warning (
45
+ "Removed %d points with the GPS fix not in %s from %s" ,
46
+ num_points - len (points ),
47
+ constants .GOPRO_GPS_FIXES ,
48
+ video ,
49
+ )
50
+
51
+ num_points = len (points )
52
+ points = [
53
+ p
54
+ for p in points
55
+ if p .gps_precision is not None
56
+ and p .gps_precision <= constants .GOPRO_MAX_GPS_PRECISION
57
+ ]
58
+ if len (points ) < num_points :
59
+ LOG .warning (
60
+ "Removed %d points with DoP value higher than %d from %s" ,
61
+ num_points - len (points ),
62
+ constants .GOPRO_MAX_GPS_PRECISION ,
63
+ video ,
64
+ )
65
+
66
+ return points
67
+
41
68
def to_description (self ) -> T .List [types .ImageDescriptionFileOrError ]:
42
69
descs : T .List [types .ImageDescriptionFileOrError ] = []
43
70
@@ -55,11 +82,13 @@ def to_description(self) -> T.List[types.ImageDescriptionFileOrError]:
55
82
if not sample_images :
56
83
continue
57
84
58
- points = get_points_from_gpmf (Path (video ))
85
+ points = self ._filter_noisy_points (
86
+ gpmf_parser .parse_gpx (Path (video )), Path (video )
87
+ )
59
88
60
89
# bypass empty points to raise MapillaryGPXEmptyError
61
90
if points and geotag_utils .is_video_stationary (
62
- get_max_distance_from_start ([(p .lat , p .lon ) for p in points ])
91
+ geo . get_max_distance_from_start ([(p .lat , p .lon ) for p in points ])
63
92
):
64
93
LOG .warning (
65
94
"Fail %d sample images due to stationary video %s" ,
@@ -85,103 +114,11 @@ def to_description(self) -> T.List[types.ImageDescriptionFileOrError]:
85
114
self .image_dir ,
86
115
sample_images ,
87
116
points ,
88
- use_gpx_start_time = self .use_gpx_start_time ,
117
+ use_gpx_start_time = False ,
118
+ use_image_start_time = True ,
89
119
offset_time = self .offset_time ,
90
120
progress_bar = pbar ,
91
121
)
92
122
descs .extend (geotag .to_description ())
93
123
94
124
return descs
95
-
96
-
97
- def extract_and_parse_bin (path : Path ) -> T .List :
98
- ffmpeg = ffmpeglib .FFMPEG (constants .FFMPEG_PATH , constants .FFPROBE_PATH )
99
- probe = ffmpeg .probe_format_and_streams (path )
100
-
101
- format_name = probe ["format" ]["format_name" ].lower ()
102
- if "mp4" not in format_name :
103
- raise IOError ("File must be an mp4" )
104
-
105
- stream_id = None
106
- for stream in probe ["streams" ]:
107
- if (
108
- "codec_tag_string" in stream
109
- and "gpmd" in stream ["codec_tag_string" ].lower ()
110
- ):
111
- stream_id = stream ["index" ]
112
-
113
- if stream_id is None :
114
- raise IOError ("No GoPro metadata track found - was GPS turned on?" )
115
-
116
- # https://github.com/mapillary/mapillary_tools/issues/503
117
- if sys .platform == "win32" :
118
- delete = False
119
- else :
120
- delete = True
121
-
122
- with tempfile .NamedTemporaryFile (delete = delete ) as tmp :
123
- try :
124
- LOG .debug ("Extracting GoPro stream %s to %s" , stream_id , tmp .name )
125
- ffmpeg .extract_stream (path , Path (tmp .name ), stream_id )
126
- LOG .debug ("Parsing GoPro GPMF %s" , tmp .name )
127
- return parse_bin (tmp .name )
128
- finally :
129
- if not delete :
130
- try :
131
- os .remove (tmp .name )
132
- except FileNotFoundError :
133
- pass
134
-
135
-
136
- def get_points_from_gpmf (path : Path ) -> T .List [geo .Point ]:
137
- gpmf_data = extract_and_parse_bin (path )
138
-
139
- rows = len (gpmf_data )
140
-
141
- points : T .List [geo .Point ] = []
142
- for i , frame in enumerate (gpmf_data ):
143
- t = frame ["time" ]
144
-
145
- if i < rows - 1 :
146
- next_ts = gpmf_data [i + 1 ]["time" ]
147
- else :
148
- next_ts = t + datetime .timedelta (seconds = 1 )
149
-
150
- interpolate_times (frame , next_ts )
151
-
152
- for point in frame ["gps" ]:
153
- points .append (
154
- geo .Point (
155
- time = geo .as_unix_time (point ["time" ]),
156
- lat = point ["lat" ],
157
- lon = point ["lon" ],
158
- alt = point ["alt" ],
159
- angle = None ,
160
- )
161
- )
162
-
163
- return points
164
-
165
-
166
- if __name__ == "__main__" :
167
- import sys
168
-
169
- points = get_points_from_gpmf (Path (sys .argv [1 ]))
170
- gpx = geotag_utils .convert_points_to_gpx (points )
171
- print (gpx .to_xml ())
172
-
173
- LOG .setLevel (logging .INFO )
174
- handler = logging .StreamHandler (sys .stderr )
175
- handler .setLevel (logging .INFO )
176
- LOG .addHandler (handler )
177
- LOG .info (
178
- "Stationary: %s" ,
179
- geotag_utils .is_video_stationary (
180
- get_max_distance_from_start ([(p .lat , p .lon ) for p in points ])
181
- ),
182
- )
183
- distance = sum (
184
- gps_distance ((cur .lat , cur .lon ), (nex .lat , nex .lon ))
185
- for cur , nex in pairwise (points )
186
- )
187
- LOG .info ("Total distance: %f" , distance )
0 commit comments