视频转存图片的代码实现如下,record_time为用于计时的装饰器,详见此文;video2pics为视频转存图片函数,读取视频的每一帧保存为jpg文件
import cv2
import time
import os
def record_time(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__}执行时间:{end-start:.3f}秒')
return result
return wrapper
@record_time
def video2pics(video_path, save_dir_path):
pics = cv2.VideoCapture(video_path)
c = 0
while pics.isOpened():
rval, frame = pics.read()
if rval:
save_path = os.path.join(save_dir_path, str(c)+'.jpg')
cv2.imwrite(save_path, frame)
c += 1
else:
break
pics.release()
if __name__ == '__main__':
video_path = r'D://Video/raw.mp4'
save_dir_path = r'D://pics'
video2pics(video_path,save_dir_path)
将一个1080p,帧率为25,时长5分钟的视频转存为7500张图片,此程序需花费433秒
在上个程序中,存储这么多图片,可能造成程序耗时长的一部分原因,而存图片是一个I/O输入/输出任务,因此考虑使用多线程。在操作系统中,进程是分配内存的基本单位,一个程序包含一个或多个进程,而线程是分配CPU的基本单位,一个进程通常有一个或多个线程,多个线程共享内存,针对密集的I/O操作,创建多个线程并发实现减少运行时间。
from concurrent.futures import ThreadPoolExecutor
def video2pics(video_path, save_dir_path, thread_nums=25):
pics = cv2.VideoCapture(video_path)
c = 0
with ThreadPoolExecutor(max_workers=thread_nums) as pool:
while pics.isOpened():
rval, frame = pics.read()
if rval:
save_path = os.path.join(save_dir_path, str(c)+'.jpg')
pool.submit(cv2.imwrite, save_path, frame)
c += 1
else:
break
pics.release()
新的video2pics创建了一个线程池,为cv2.imwrite方法创建线程。使用线程池的原因是因为如果每一个图片下载任务都使用一个线程的话线程创建、调度和释放会有较大开销,所以使用容器提前创建好若干线程,使用线程时从其中拿出一个线程,用完后不释放线程,而是放回线程池,重复利用。可以看到,使用25个线程,运行时长明显减少,多线程加速了视频转存图片的实现。
视频可以转存为图片,图片也可以合成视频,可自行设置需要的编码格式,帧率,分辨率。该过程暂时没想到如何使用多线程,之后如果解决再补上。
def pics2video(pics_dir_path, video_save_path, fps=25, size=(1920, 1080), pic_nums = 7500):
fourcc = cv2.VideoWriter_fourcc(*'I420')
out = cv2.VideoWriter(video_save_path, fourcc, fps, size)
for c in range(pic_nums):
pic_path = os.path.join(pics_dir_path, str(c)+'.jpg')
frame = cv2.imread(pic_path)
out.write(frame)
out.release()
if __name__ == '__main__':
save_dir_path = r'E://pics'
video_save_path = r'E://Video/new.avi'
pics2video(save_dir_path,video_save_path)