zoukankan      html  css  js  c++  java
  • 【OpenCV+pyqt5】视频抽帧相关操作

    【OpenCV+pyqt5】视频抽帧相关操作

    • 本文利用OpenCV对视频进行读取,并进行抽帧,可指定时间段和抽帧间隔
    • 对视频进行裁剪,裁剪设定时间段内的视频
    • 对指定文件夹下的图像进行视频转换

    pyqt5搭建界面

    界面功能简介

    • 界面比较简单,左侧显示视频,右侧提供操作按钮
    • 视频下方有进度条(暂时不能调整进度),和后面的时间
    • 右侧操作区有三个功能切换,右下方显示操作进度条

    功能测试

    • 视频抽帧有全部抽帧和时间段抽帧
    • 视频裁剪根据时间段进行裁剪
    • 图片转换成视频,遍历文件夹下的所有图片,根据帧率合成指定视频名称
    • 以上三种功能测试均通过

    OpenCV功能详解

    读取视频并显示视频信息

    • 利用cap = cv2.VideoCapture(video_name)进行指定视频文件的读取
    def _open_video_(self):
        self.video_name = os.path.split(self.video_path_name)[-1]
        if self.video_name.split('.')[-1]not in ['mp4','avi','h264']:
            loggingdata('video name error, {}'.format(self.video_path_name))
            return -1
        self.frame_now = 0
        self.cap       = cv2.VideoCapture(self.video_path_name)
        self.frame_num = int(self.cap.get(7))
        self.FPS       = int(self.cap.get(5))
        self.frame_w   = int(self.cap.get(3))
        self.frame_h   = int(self.cap.get(4))
    
    • 通过以下命令获取视频信息
    cap.get(0)	CV_CAP_PROP_POS_MSEC      视频文件的当前位置(播放)以毫秒为单位
    cap.get(1)	CV_CAP_PROP_POS_FRAMES    基于以0开始的被捕获或解码的帧索引
    cap.get(2)	CV_CAP_PROP_POS_AVI_RATIO 视频文件的相对位置(播放):0 = 电影开始,1 = 影片的结尾。
    cap.get(3)	CV_CAP_PROP_FRAME_WIDTH   在视频流的帧的宽度
    cap.get(4)	CV_CAP_PROP_FRAME_HEIGHT  在视频流的帧的高度
    cap.get(5)	CV_CAP_PROP_FPS           帧速率
    cap.get(6)	CV_CAP_PROP_FOURCC        编解码的4字-字符代码
    cap.get(7)	CV_CAP_PROP_FRAME_COUNT   视频文件中的帧数
    

    时间转换函数

    • 通过界面输入时间00:00:15转换成秒,【注】这里默认输入多组时间段
    def _time2second(self,start_end_time):
         将输入的时间转换成秒 ['00:10:40','00:10:47'] - > [600,900]
      second_time = []
      for span_time in start_end_time:
          start_time = span_time[0].split(':')
          print(start_time)
          start_time = int(start_time[0])*3600 + int(start_time[1])*60 + int(start_time[2])
          end_time = span_time[1].split(':')
          end_time = int(end_time[0])*3600 + int(end_time[1])*60 + int(end_time[2])
          # print(end_time)
          if end_time < start_time:
              return -1
          second_time.append([start_time,end_time])
      return second_time
    

    根据获得的视频进行抽帧

    • 抽帧的逻辑是:循环读取视频,当前帧如果在指定范围内,则进行保存,超过范围则退出;显然这种方式再截取较长视频的尾部会很慢。
    • 另外一种逻辑就是利用cap.set(cv2.CAP_PROP_POS_FRAMES,start_frame)指定读取位置,这种方式较快
    • 但实验发现.h264利用第二种不能实现定位,每次还是从视频起点开始读,所以本例采用前者进行抽帧;
    def video2frame(self,method = 0,frame_span = 1,ex_frame_time = [0,10]):
        # 1. 视频抽帧
        # method = 0 整体抽帧 method = 1 时间段抽帧
        loggingdata('---------------start video2frame---------------------')
        self._open_video_()
        ex_frame_time = [x*self.FPS for x in ex_frame_time] if method == 1 else [0,self.frame_num]
        while True:
            if method == 1 and self.frame_now >= ex_frame_time[1]:
                loggingdata('video2frame save picture number is {},save frame form {} to {}'.format(
                    ex_frame_time[1],ex_frame_time[0],self.frame_now))
                break
            success, origin_img = self.cap.read()
            if not success or len(origin_img) < 2:
                break
            self.frame_now +=1
            if self.frame_now%frame_span != 0:
                continue
            if self.frame_now < ex_frame_time[1] and self.frame_now >= ex_frame_time[0]:
                cv2.imwrite(os.path.join(self.img_save_path,self.video_name.split('.')[0])+"_%.6d.jpg"%self.frame_now,origin_img)
        loggingdata('==============save image stop==================')
    

    视频裁剪

    • 裁剪逻辑:继承抽帧逻辑,将符合裁剪区域内的图像写入cut_video中,帧率默认原视频,名称在原名称上增加裁剪起始帧
    def cut_video(self,save_fps = 20,cut_frame_time = [0,10]):
        self._open_video_()
        save_fps = self.FPS
        cut_frame_time = [x*self.FPS for x in cut_frame_time]
        cut_video = cv2.VideoWriter(os.path.join(self.cut_video_save_path,self.video_name.split('.')[0])+
            '_'+str(cut_frame_time[0]) + '.mp4', cv2.VideoWriter_fourcc('M','P','E','G'), save_fps, (self.frame_w,self.frame_h))
        while self.frame_now < cut_frame_time[1]:
            success, origin_img = self.cap.read()
            if not success or len(origin_img) < 2 or self.frame_now >= cut_frame_time[1]:
                break
            self.frame_now +=1
            # print(self.frame_now)
            if self.frame_now > cut_frame_time[0] and self.frame_now < cut_frame_time[1]:
                cut_video.write(origin_img)
        print('cut end')
        cut_video.release()
    
    

    图片转视频

    • 和视频裁剪类似,只是将前半段的视频读取替换成图片
    def img2video(self,save_fps = 20):
      img_h = 0
      img_w = 0
      for img_name in os.listdir(self.img_files):
          if img_name.split('.')[-1] not in ['bmp','jpg','jpeg','png']:
              continue
          tmp_img = cv2.imread(os.path.join(self.img_files,img_name))
          img_h = tmp_img.shape[0]
          img_w = tmp_img.shape[1]
          break
      img2video = cv2.VideoWriter(self.video_save_path, cv2.VideoWriter_fourcc('M','P','E','G'), save_fps, (img_w,img_h))
      for img_name in os.listdir(self.img_files):
          if img_name.split('.')[-1] not in ['bmp','jpg','jpeg','png']:
              continue
          tmp_img = cv2.imread(os.path.join(self.img_files,img_name))
          img2video.write(tmp_img)
      img2video.release()
    

    暂存问题

      1. 读取h264时获取的视频总帧数出现异常-192153584101141,导致不能正常显示视频进度条与处理进度
      1. 图片转视频,后续需要优化,将遍历图片显示在界面

    https://www.cnblogs.com/wangxiaobei2019/p/14784678.html

  • 相关阅读:
    mysql复习相关
    OpenStack三种类型的NAT转换
    openstack资料相关
    [转]Web 调试工具之 Advanced REST client
    [转]Aspose.Words.dll 将 Word 转换成 html
    [Android] 开发第十天
    [win10]遇坑指南
    [转]Explorer.exe的命令行参数
    [Android] 开发第九天
    [Android] 开发第八天
  • 原文地址:https://www.cnblogs.com/wangxiaobei2019/p/14236676.html
Copyright © 2011-2022 走看看