extract_video_with_gps.py 3.89 KB
Newer Older
Clément Pinard's avatar
Clément Pinard committed
1
2
3
4
5
6
import edit_exif
from subprocess import Popen, PIPE
from path import Path
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
import pandas as pd
from tqdm import tqdm
Clement Pinard's avatar
Clement Pinard committed
7
8
import ffmpeg
import anafi_metadata as am
Clément Pinard's avatar
Clément Pinard committed
9
10
11
12
13
14
15
16
17
18


def extract_images(folder_path, file_path, fps):

    print("exporting to images with ffmpeg ...")
    if fps is not None:
        fps_arg = ["-vf", "fps={}".format(fps)]
    else:
        fps_arg = []

Clément Pinard's avatar
Clément Pinard committed
19
    ffmpeg = Popen(["ffmpeg", "-y", "-i", str(file_path), "-qscale:v", "2"] + fps_arg + [str(folder_path/"%05d.jpg")],
Clément Pinard's avatar
Clément Pinard committed
20
21
                   stdout=PIPE, stderr=PIPE)
    ffmpeg.wait()
Clement Pinard's avatar
Clement Pinard committed
22
    return sorted(folder_path.files("*.jpg"))
Clément Pinard's avatar
Clément Pinard committed
23
24
25


def extract_metadata(folder_path, file_path, native_wrapper):
Clément Pinard's avatar
Clément Pinard committed
26
    output_file = folder_path/"metadata.csv"
Clément Pinard's avatar
Clément Pinard committed
27
28
29
30
    print("extracting metadata with vmeta_extract...")
    vmeta_extract = Popen([native_wrapper, "vmeta-extract", str(file_path), "--csv", str(output_file)],
                          stdout=PIPE, stderr=PIPE)
    vmeta_extract.wait()
Clement Pinard's avatar
Clement Pinard committed
31
    return output_file
Clément Pinard's avatar
Clément Pinard committed
32
33


Clement Pinard's avatar
Clement Pinard committed
34
def add_gps_to_exif(metadata, image_paths, fps):
Clément Pinard's avatar
Clément Pinard committed
35
36
37
38
39
    metadata = metadata.set_index("time")
    metadata.index = pd.to_datetime(metadata.index, unit="us")

    if fps is not None:
        metadata = metadata.resample("{:.3f}S".format(1/fps)).first()
Clement Pinard's avatar
Clement Pinard committed
40
        metadata.to_csv(image_paths[0].parent / "metadata_{}fps.csv".format(fps), sep=" ")
Clément Pinard's avatar
Clément Pinard committed
41
42

    print("Modifying gps EXIF for colmap...")
Clement Pinard's avatar
Clement Pinard committed
43
    for img_path, row in tqdm(zip(image_paths, metadata.iterrows()), total=len(image_paths)):
Clément Pinard's avatar
Clément Pinard committed
44
        if row[1]["location_valid"] == 1:
Clement Pinard's avatar
Clement Pinard committed
45
            edit_exif.set_gps_location(img_path,
Clément Pinard's avatar
Clément Pinard committed
46
47
48
                                       row[1]["location_latitude"],
                                       row[1]["location_longitude"],
                                       row[1]["location_altitude"])
Clément Pinard's avatar
Clément Pinard committed
49
50


Clement Pinard's avatar
Clement Pinard committed
51
52
53
54
55
56
57
58
def save_images_path_list(output_folder, origin, images_path_list):
    relative_path_lists = [origin.relpathto(img) + '\n' for img in images_path_list]
    with open(output_folder/'images.txt', 'w') as f:
        f.writelines(relative_path_lists)


def workflow(root, output_folder, video_path, args):
    print("Generating images with gps for video {}".format(str(video_path)))
Clément Pinard's avatar
Clément Pinard committed
59
    output_folder /= video_path.stem
Clément Pinard's avatar
Clément Pinard committed
60
61
62
    if args.fps is not None:
        output_folder += "_{}fps".format(args.fps)
    output_folder.mkdir_p()
Clement Pinard's avatar
Clement Pinard committed
63
64
65
    images_path_list = ffmpeg.extract_images(output_folder, video_path, args.fps)
    metadata = am.extract_metadata(output_folder, video_path, args.nw)
    add_gps_to_exif(metadata, images_path_list, args.fps)
Clement Pinard's avatar
Clement Pinard committed
66
    save_images_path_list(output_folder, args.origin or root, images_path_list)
Clément Pinard's avatar
Clément Pinard committed
67
68
69
70
71


parser = ArgumentParser(description='image extractor from parrot video',
                        formatter_class=ArgumentDefaultsHelpFormatter)

Clement Pinard's avatar
Clement Pinard committed
72
73
parser.add_argument('--input', metavar='PATH', default="~/Images/scan manoir/anafi/video",
                    help='path to video folder or video file', type=Path)
Clément Pinard's avatar
Clément Pinard committed
74
75
parser.add_argument('--fps', metavar='F', default=None, type=int,
                    help='fps')
Clement Pinard's avatar
Clement Pinard committed
76
77
78
parser.add_argument('--output_folder', metavar='DIR', default=None, type=Path)
parser.add_argument('--origin', metavar='DIR', default=None, type=Path,
                    help='folder relative to which the images path list will be generated')
Clément Pinard's avatar
Clément Pinard committed
79
80
81
82
parser.add_argument('--nw', default='',
                    help="native-wrapper.sh file location")

if __name__ == '__main__':
Clement Pinard's avatar
Clement Pinard committed
83
    file_exts = ['.mp4', '.MP4']
Clément Pinard's avatar
Clément Pinard committed
84
    args = parser.parse_args()
Clement Pinard's avatar
Clement Pinard committed
85
86
87
88
89
90
91
92
    if args.input.isfile() and args.input.ext in file_exts:
        root = args.input.parent
        videos = [args.input]
    elif args.input.isdir():
        root = args.input
        videos = sum([args.input.walkfiles('*{}'.format(ext)) for ext in file_exts], [])
        print("Found {} videos".format(len(videos)))

Clément Pinard's avatar
Clément Pinard committed
93
    if args.output_folder is None:
Clement Pinard's avatar
Clement Pinard committed
94
95
96
        args.output_folder = root
    for video_path in videos:
        workflow(root, args.output_folder/(video_path.parent.relpath(root)), video_path, args)