opensfm/video.py (102 lines of code) (raw):
import datetime
import os
from subprocess import Popen, PIPE
import cv2
import dateutil.parser
from opensfm import context
from opensfm import geotag_from_gpx
from opensfm import io
def video_orientation(video_file) -> int:
# Rotation
# pyre-fixme[16]: Optional type has no attribute `read`.
rotation = Popen(
["exiftool", "-Rotation", "-b", video_file], stdout=PIPE
).stdout.read()
if rotation:
rotation = float(rotation)
if rotation == 0:
orientation = 1
elif rotation == 90:
orientation = 6
elif rotation == 180:
orientation = 3
elif rotation == 270:
orientation = 8
else:
raise RuntimeError(f"rotation {rotation} has no valid orientation!")
else:
orientation = 1
return orientation
def import_video_with_gpx(
video_file,
gpx_file,
output_path: str,
dx,
dt=None,
start_time=None,
visual: bool=False,
image_description=None,
):
points = geotag_from_gpx.get_lat_lon_time(gpx_file)
orientation = video_orientation(video_file)
if start_time:
video_start_time = dateutil.parser.parse(start_time)
else:
try:
# pyre-fixme[16]: Optional type has no attribute `read`.
exifdate = Popen(
["exiftool", "-CreateDate", "-b", video_file], stdout=PIPE
).stdout.read()
video_start_time = datetime.datetime.strptime(exifdate, "%Y:%m:%d %H:%M:%S")
except Exception:
print("Video recording timestamp not found. Using first GPS point time.")
video_start_time = points[0][0]
try:
duration = Popen(
["exiftool", "-MediaDuration", "-b", video_file], stdout=PIPE
).stdout.read()
video_duration = float(duration)
video_end_time = video_start_time + datetime.timedelta(
seconds=video_duration
)
except Exception:
print("Video end time not found. Using last GPS point time.")
video_end_time = points[-1][0]
print("GPS track starts at: {}".format(points[0][0]))
print("Video starts at: {}".format(video_start_time))
# Extract video frames.
io.mkdir_p(output_path)
key_points = geotag_from_gpx.sample_gpx(points, dx, dt)
cap = cv2.VideoCapture(video_file)
image_files = []
for p in key_points:
dt = (p[0] - video_start_time).total_seconds()
if dt > 0:
CAP_PROP_POS_MSEC = (
cv2.CAP_PROP_POS_MSEC
if context.OPENCV3
else cv2.cv.CV_CAP_PROP_POS_MSEC
)
cap.set(CAP_PROP_POS_MSEC, int(dt * 1000))
ret, frame = cap.read()
if ret:
print("Grabbing frame for time {}".format(p[0]))
filepath = os.path.join(
output_path, p[0].strftime("%Y_%m_%d_%H_%M_%S_%f")[:-3] + ".jpg"
)
cv2.imwrite(filepath, frame)
geotag_from_gpx.add_exif_using_timestamp(
filepath, points, timestamp=p[0], orientation=orientation
)
# Display the resulting frame
if visual:
# Display the resulting frame
max_display_size = 800
resize_ratio = float(max_display_size) / max(
frame.shape[0], frame.shape[1]
)
frame = cv2.resize(
frame, dsize=(0, 0), fx=resize_ratio, fy=resize_ratio
)
cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == 27:
break
image_files.append(filepath)
# When everything done, release the capture
cap.release()
if visual:
cv2.destroyAllWindows()
return image_files