# Copyright (c) OpenMMLab. All rights reserved. import argparse import glob import os import os.path as osp import sys import warnings from multiprocessing import Lock, Pool import mmcv import numpy as np def extract_frame(vid_item): """Generate optical flow using dense flow. Args: vid_item (list): Video item containing video full path, video (short) path, video id. Returns: bool: Whether generate optical flow successfully. """ full_path, vid_path, vid_id, method, task, report_file = vid_item if '/' in vid_path: act_name = osp.basename(osp.dirname(vid_path)) out_full_path = osp.join(args.out_dir, act_name) else: out_full_path = args.out_dir run_success = -1 if task == 'rgb': if args.use_opencv: # Not like using denseflow, # Use OpenCV will not make a sub directory with the video name try: video_name = osp.splitext(osp.basename(vid_path))[0] out_full_path = osp.join(out_full_path, video_name) vr = mmcv.VideoReader(full_path) for i, vr_frame in enumerate(vr): if vr_frame is not None: w, h, _ = np.shape(vr_frame) if args.new_short == 0: if args.new_width == 0 or args.new_height == 0: # Keep original shape out_img = vr_frame else: out_img = mmcv.imresize( vr_frame, (args.new_width, args.new_height)) else: if min(h, w) == h: new_h = args.new_short new_w = int((new_h / h) * w) else: new_w = args.new_short new_h = int((new_w / w) * h) out_img = mmcv.imresize(vr_frame, (new_h, new_w)) mmcv.imwrite(out_img, f'{out_full_path}/img_{i + 1:05d}.jpg') else: warnings.warn( 'Length inconsistent!' f'Early stop with {i + 1} out of {len(vr)} frames.' ) break run_success = 0 except Exception: run_success = -1 else: if args.new_short == 0: cmd = osp.join( f"denseflow '{full_path}' -b=20 -s=0 -o='{out_full_path}'" f' -nw={args.new_width} -nh={args.new_height} -v') else: cmd = osp.join( f"denseflow '{full_path}' -b=20 -s=0 -o='{out_full_path}'" f' -ns={args.new_short} -v') run_success = os.system(cmd) elif task == 'flow': if args.input_frames: if args.new_short == 0: cmd = osp.join( f"denseflow '{full_path}' -a={method} -b=20 -s=1 -o='{out_full_path}'" # noqa: E501 f' -nw={args.new_width} --nh={args.new_height} -v --if') else: cmd = osp.join( f"denseflow '{full_path}' -a={method} -b=20 -s=1 -o='{out_full_path}'" # noqa: E501 f' -ns={args.new_short} -v --if') else: if args.new_short == 0: cmd = osp.join( f"denseflow '{full_path}' -a={method} -b=20 -s=1 -o='{out_full_path}'" # noqa: E501 f' -nw={args.new_width} --nh={args.new_height} -v') else: cmd = osp.join( f"denseflow '{full_path}' -a={method} -b=20 -s=1 -o='{out_full_path}'" # noqa: E501 f' -ns={args.new_short} -v') run_success = os.system(cmd) else: if args.new_short == 0: cmd_rgb = osp.join( f"denseflow '{full_path}' -b=20 -s=0 -o='{out_full_path}'" f' -nw={args.new_width} -nh={args.new_height} -v') cmd_flow = osp.join( f"denseflow '{full_path}' -a={method} -b=20 -s=1 -o='{out_full_path}'" # noqa: E501 f' -nw={args.new_width} -nh={args.new_height} -v') else: cmd_rgb = osp.join( f"denseflow '{full_path}' -b=20 -s=0 -o='{out_full_path}'" f' -ns={args.new_short} -v') cmd_flow = osp.join( f"denseflow '{full_path}' -a={method} -b=20 -s=1 -o='{out_full_path}'" # noqa: E501 f' -ns={args.new_short} -v') run_success_rgb = os.system(cmd_rgb) run_success_flow = os.system(cmd_flow) if run_success_flow == 0 and run_success_rgb == 0: run_success = 0 if run_success == 0: print(f'{task} {vid_id} {vid_path} {method} done') sys.stdout.flush() lock.acquire() with open(report_file, 'a') as f: line = full_path + '\n' f.write(line) lock.release() else: print(f'{task} {vid_id} {vid_path} {method} got something wrong') sys.stdout.flush() return True def parse_args(): parser = argparse.ArgumentParser(description='extract optical flows') parser.add_argument('src_dir', type=str, help='source video directory') parser.add_argument('out_dir', type=str, help='output rawframe directory') parser.add_argument( '--task', type=str, default='flow', choices=['rgb', 'flow', 'both'], help='which type of frames to be extracted') parser.add_argument( '--level', type=int, choices=[1, 2], default=2, help='directory level of data') parser.add_argument( '--num-worker', type=int, default=8, help='number of workers to build rawframes') parser.add_argument( '--flow-type', type=str, default=None, choices=[None, 'tvl1', 'warp_tvl1', 'farn', 'brox'], help='flow type to be generated') parser.add_argument( '--out-format', type=str, default='jpg', choices=['jpg', 'h5', 'png'], help='output format') parser.add_argument( '--ext', type=str, default='avi', choices=['avi', 'mp4', 'webm'], help='video file extensions') parser.add_argument( '--mixed-ext', action='store_true', help='process video files with mixed extensions') parser.add_argument( '--new-width', type=int, default=0, help='resize image width') parser.add_argument( '--new-height', type=int, default=0, help='resize image height') parser.add_argument( '--new-short', type=int, default=0, help='resize image short side length keeping ratio') parser.add_argument('--num-gpu', type=int, default=8, help='number of GPU') parser.add_argument( '--resume', action='store_true', default=False, help='resume optical flow extraction instead of overwriting') parser.add_argument( '--use-opencv', action='store_true', help='Whether to use opencv to extract rgb frames') parser.add_argument( '--input-frames', action='store_true', help='Whether to extract flow frames based on rgb frames') parser.add_argument( '--report-file', type=str, default='build_report.txt', help='report to record files which have been successfully processed') args = parser.parse_args() return args def init(lock_): global lock lock = lock_ if __name__ == '__main__': args = parse_args() if not osp.isdir(args.out_dir): print(f'Creating folder: {args.out_dir}') os.makedirs(args.out_dir) if args.level == 2: classes = os.listdir(args.src_dir) for classname in classes: new_dir = osp.join(args.out_dir, classname) if not osp.isdir(new_dir): print(f'Creating folder: {new_dir}') os.makedirs(new_dir) if args.input_frames: print('Reading rgb frames from folder: ', args.src_dir) fullpath_list = glob.glob(args.src_dir + '/*' * args.level) print('Total number of rgb frame folders found: ', len(fullpath_list)) else: print('Reading videos from folder: ', args.src_dir) if args.mixed_ext: print('Extension of videos is mixed') fullpath_list = glob.glob(args.src_dir + '/*' * args.level) else: print('Extension of videos: ', args.ext) fullpath_list = glob.glob(args.src_dir + '/*' * args.level + '.' + args.ext) print('Total number of videos found: ', len(fullpath_list)) if args.resume: done_fullpath_list = [] with open(args.report_file) as f: for line in f: if line == '\n': continue done_full_path = line.strip().split()[0] done_fullpath_list.append(done_full_path) done_fullpath_list = set(done_fullpath_list) fullpath_list = list(set(fullpath_list).difference(done_fullpath_list)) if args.level == 2: vid_list = list( map( lambda p: osp.join( osp.basename(osp.dirname(p)), osp.basename(p)), fullpath_list)) elif args.level == 1: vid_list = list(map(osp.basename, fullpath_list)) lock = Lock() pool = Pool(args.num_worker, initializer=init, initargs=(lock, )) pool.map( extract_frame, zip(fullpath_list, vid_list, range(len(vid_list)), len(vid_list) * [args.flow_type], len(vid_list) * [args.task], len(vid_list) * [args.report_file])) pool.close() pool.join()