python cv2实现视频抽帧

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、具体实现如下


前言

基于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



你可能感兴趣的:(python,音视频,opencv)