Python并行处理视频帧

11 篇文章 1 订阅
4 篇文章 0 订阅

参考链接:Speedy Computer Vision Pipelines using Parallelism

方案:

  • 使用Python多进程编程,将视频分成多个小段,可按照CPU核数num_processes = multiprocessing.cpu_count()等分帧数。

  • 通过cv2.CAP_PROP_FRAME_COUNT可以得到视频的总帧数(不准确),然后得到每一段视频的帧数大约为FRAMES_PER_SEG = cv2.CAP_PROP_FRAME_COUNT // multiprocessing.cpu_count()。

  • 通过cv2.CAP_PROP_POS_FRAMES可以设置每一小段视频开始读取的起始帧位置:video.set(cv2.CAP_PROP_POS_FRAMES, seg_count * FRAMES_PER_SEG).

  • 使用进程池multiprocessing.Pool(num_processes)

测试

测试视频:分辨率 800x450, 时长:00:01:56.40, FPS: 25, 总帧数:2908

  • 常规操作:将视频存储为逐帧图片
import os
import sys
import time
import cv2
import argparse
 
 
def process_video(input, output):
    if not os.path.exists(output):
        os.makedirs(output)
 
    vid = cv2.VideoCapture(input)
    proc_frames = 0
    ret, frame = vid.read()
    while ret:
        out_file = output + '/{:0>6d}.png'.format(proc_frames)
        cv2.imwrite(out_file, frame)
        ret, frame = vid.read()
        proc_frames += 1
    vid.release()
 
 
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--input", default="heheda.mp4", type=str)
    parser.add_argument("--output", default="images", type=str)
    args = parser.parse_args()
 
    start_time = time.time()
    process_video(args.input, args.output)
 
    print(
        "Method {}: Input:{}, Time taken: {}".format(
            sys.argv[0], args.input, time.time() - start_time
        )
    )

测试耗时: 47.633s

  • 多进程并行:将视频存储为逐帧图片
import os
import cv2
import sys
import time
import argparse
import multiprocessing as mp
 

def process_video(group_number):
    vid = cv2.VideoCapture(args.input)
    pos_frames = int(frame_jump_unit * group_number)
    vid.set(cv2.CAP_PROP_POS_FRAMES, pos_frames)
    proc_frames = 0
 
    if not os.path.exists(args.output):
        os.makedirs(args.output)
 
    if group_number != num_processes-1:
        not_last_seg = True
    else:
        not_last_seg = False
 
    ret, frame = vid.read()
    while ret:
        if not_last_seg and proc_frames == frame_jump_unit:
            break
        out_file = args.output + '/{:0>6d}.png'.format(proc_frames + pos_frames)
        cv2.imwrite(out_file, frame)
        ret, frame = vid.read()
        proc_frames += 1
    vid.release()
    return None
 
 
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--input", default="heheda.mp4", type=str)
    parser.add_argument("--output", default="images", type=str)
    args = parser.parse_args()
 
    start_time = time.time()
 
    num_processes = mp.cpu_count()
    vid = cv2.VideoCapture(args.input)
    frame_jump_unit = vid.get(cv2.CAP_PROP_FRAME_COUNT) // num_processes
    width = int(vid.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = vid.get(cv2.CAP_PROP_FPS)
    vid.release()
 
    p = mp.Pool(num_processes)
    p.map(process_video, range(num_processes))
 
    print(
        "Method {}: Input:{}, Output:{}, Time taken: {}".format(
            sys.argv[0], args.input, args.output, time.time() - start_time
        )
    )

测试耗时: 20.775s

  • 多进程并行:将视频处理后另存为视频
    代码来自github,将视频分段后并行处理各小段的视频帧,然后用ffmpeg进行视频拼接。
import cv2
import sys
import time
import argparse
import numpy as np
import subprocess as sp
import multiprocessing as mp


def process_video(group_number):
    cap = cv2.VideoCapture(args.input_file)
    cap.set(cv2.CAP_PROP_POS_FRAMES, frame_jump_unit * group_number)
    proc_frames = 0
    out = cv2.VideoWriter(
        "{}.{}".format(group_number, args.extension),
        cv2.VideoWriter_fourcc(*vid_fourcc[args.extension]),
        fps,
        (width, height),
    )

    while proc_frames < frame_jump_unit:
        ret, frame = cap.read()
        if ret == False:
            break
        out.write(cv2.filter2D(frame, -1, kernel))
        proc_frames += 1

    cap.release()
    out.release()
    return None


if __name__ == "__main__":
    kernel = np.ones((7,7),np.float32)/49
    parser = argparse.ArgumentParser()
    parser.add_argument("--input_file", default="Kiiara.mp4", type=str)
    parser.add_argument("--x264", default=False, type=bool)
    parser.add_argument("--extension", choices=["mp4", "avi", "mkv"], default="avi")
    parser.add_argument("--extra_flags", default="", type=str)
    args = parser.parse_args()
    start_time = time.time()
    
    num_processes = mp.cpu_count()
    vid_fourcc = {"avi": "MJPG", "mp4": "mp4v", "mkv": "mp4v"}
    cap = cv2.VideoCapture(args.input_file)
    frame_jump_unit = cap.get(cv2.CAP_PROP_FRAME_COUNT) // num_processes
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = cap.get(cv2.CAP_PROP_FPS)
    cap.release()

    p = mp.Pool(num_processes)
    p.map(process_video, range(num_processes))

    intermediate_files = ["{}.{}".format(i, args.extension) for i in range(num_processes)]
    with open("intermediate_files.txt", "w") as f:
        for t in intermediate_files:
            f.write("file {} \n".format(t))

    ffmpeg_command = "ffmpeg -y -loglevel error -f concat -safe 0 -i intermediate_files.txt {}".format(
        args.extra_flags
    ).strip()
    if args.x264 == False:
        ffmpeg_command += " -vcodec copy"
    else:
        ffmpeg_command += " -vcodec libx264"
    ffmpeg_command += " output_method2.{}".format(args.extension)
    t2 = time.time()
    sp.Popen(ffmpeg_command, shell=True).wait()
    t3 = time.time()

    from os import remove

    for f in intermediate_files:
        remove(f)
    remove("intermediate_files.txt")
    print(
        "Method {}: Input:{}, re-encode to x264:{}, output extension:{}, extra_flags:{}, Time taken: {}".format(
            sys.argv[0], args.input_file, args.x264, args.extension, args.extra_flags, t3 - start_time
        )
    )
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值