提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
基于python-opencv 实现对视频的图片抽取
#!/usr/bin/python3.6.10
# -*- coding: utf-8 -*-
# @Author :
# @Time : 2022/1/24 12:34
# @File : video_handel.py
import os
import re
from pathlib import Path
import base64
from threading import Thread
import cv2
import os
import numpy as np
import time
# from subprocess import Popen, PIPE, STARTUPINFO, STDOUT, STARTF_USESHOWWINDOW
# from PyQt5.QtCore import QThread
# from PyQt5.QtCore import pyqtSignal
# import re, time
# from pathlib import Path
class VideoBody(object):
def __init__(self, video_path, save_path):
self.video_path = video_path
self.cap = cv2.VideoCapture(video_path)
self.save_path = Path(save_path).as_posix()
self.flag = 0
self.get_video_attr() # 设置属性
self.is_exists_path() # 判断文件位置是否存在,不存在创建
def video_status(self):
"""
获取视频状态
:return:
"""
return self.cap.isOpened()
def get_video_attr(self):
"""
获取视频属性
:return:
"""
self.video_fps = int(self.cap.get(cv2.CAP_PROP_FPS))
self.video_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.video_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.video_count = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(f"视频总帧数: {self.video_count}")
def linspace_list(self, start, end, de):
"""
start: 起始帧
end: 结束帧
de: 等差项
等差数列抽帧
:return: 返回 等差列表
"""
li = []
start = start
while start <= end:
li.append(start)
start = start + de
return li
def cut_linspace(self, start, end, de):
"""
按照等差数列帧截取视频
:param start: 起始帧
:param end: 结束帧
:param de: 等差值
:return: 生成图片
"""
status = self.cap.isOpened()
count = 45
cut_list = self.linspace_list(start, end, de)
# if not os.path.exists(self.save_path):
# os.makedirs(self.save_path)
while status and count < self.video_count:
status, frame1 = self.cap.read()
if status:
if count in cut_list:
print(f"抽取第 {count}帧")
cv2.imwrite(os.path.join(self.save_path, str(count).zfill(6)) + '.jpg', frame1)
count += 1
self.cap.release()
def cut_frame(self, para=None):
"""
默认:整体视频抽帧;
para 存在 隔 para 帧抽
para: 每隔para帧抽一个
:return:
"""
status = self.cap.isOpened()
count = 0
while status and count < self.video_count:
status, frame1 = self.cap.read()
if status:
if para:
if count % para == 0: # 每隔 para帧 抽一个
print(f'隔 {para} 帧抽...')
cv2.imwrite(os.path.join(self.save_path, str(count).zfill(6))+'.jpg', frame1)
else:
if count % 100 == 0:
print('每帧抽...')
cv2.imwrite(os.path.join(self.save_path, str(count).zfill(6)) + '.jpg', frame1)
count += 1
else:
if count == 0:
print('视频错误..')
self.cap.release()
def set_sec_cut_single_frame(self, sec):
"""
截取某一时刻的 frame
:param sec:
:return:
"""
status = self.cap.isOpened()
count = 0
if status:
self.cap.set(cv2.CAP_PROP_POS_MSEC, sec*1000) # #截取图片的方法 在sec秒处截取一个
if status:
status, frame1 = self.cap.read()
if status:
print(str(self.save_path) + f'/{sec}.jpg')
cv2.imwrite(str(self.save_path) + f'/{sec}.jpg', frame1)
else:
print(f"取视频最大时长前一帧")
self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.video_count - 1) # #截取图片的方法 在sec秒处截取一个
status, frame1 = self.cap.read()
cv2.imwrite(str(self.save_path) + f'/{sec}.jpg', frame1)
pass
def cut_video(self, start_time=None, end_time=None):
"""
截取视频的片段 时间间隔为 end_time - start_time
:param start_time: 开始时间 s
:param end_time: 结束时间 s
:param save_path: 截取后视频保存路径
:return:
"""
video_duration = int(self.video_count / self.video_fps) # 视频总时长
print(f"视频总时长: {video_duration}")
start_frame = int(self.video_fps * start_time)
end_frame = int(self.video_fps * end_time)
count = 0
videoWriter = cv2.VideoWriter(os.path.join(self.save_path, 'test.avi'), cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'), self.video_fps,
(self.video_width, self.video_height))
status = self.video_status()
if start_time > video_duration:
raise Exception("设置的开始时长 超过了视频总时长")
if end_time > video_duration:
end_time = video_duration
end_frame = int(self.video_fps * end_time)
# raise Exception("设置的结束时长 超过了视频总时长")
if status:
self.cap.set(cv2.CAP_PROP_POS_FRAMES, float(start_frame)) # 设置在第 start_frame 帧处
while status and count < end_frame-start_frame:
status, frame1 = self.cap.read()
if status:
videoWriter.write(frame1)
count += 1
else:
if count == 0:
print('视频打开错误')
self.cap.release()
videoWriter.release()
pass
# def __iter__(self):
#
# return self
def is_exists_path(self):
if not os.path.exists(self.save_path):
print('创建文件夹')
os.makedirs(self.save_path)
def __next__(self):
status = self.video_status()
if status:
status, frame = self.cap.read()
self.flag += 1
return frame
else:
raise StopIteration
def __call__(self):
pass
def __del__(self):
self.cap.release()
print('end')
# 视频的读取与显示
class VideoThread(Thread):
def __init__(self, video_path, save_path):
super(VideoThread, self).__init__()
self.video_obj = VideoBody(video_path, save_path)
def run(self):
# self.video_obj.cut_linspace(75, 500, 20) # 按20等差数列抽帧
# self.video_obj.cut_frame(10) # 每个10帧抽一张
# self.video_obj.set_sec_cut_single_frame(75) # 截取第75s的图片
# self.video_obj.cut_video(start_time=20, end_time=80) # 截取 20~80s 之间的视频
#########以下是显示读取的视频###########
while True:
frame = next(self.video_obj)
# break
if isinstance(frame, np.ndarray):
frame = cv2.resize(frame,(600, 400))
cv2.imshow('', frame)
cv2.waitKey(1)
else:
break
if __name__ == '__main__':
# video_path = r"C:\Users\Desktop\0119_test_video\front.mp4"
video_path = "test2.avi"
save_path = r"C:\Users\Desktop\0119_test_video\test_front\1\2"
vt = VideoThread(video_path, save_path)
vt.start()
pass