【OpenCV+pyqt5】视频抽帧裁剪与图片转视频

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

【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()

20210413更新

  • 1.播放条可以拖动,调整播放位置,增加视频信息展示
  • 2.抽帧逻辑优化,大视频可以快速抽帧
  • 3.增加抽帧数量的选项和保存图片类型
  • 4.优化界面,增加图标
    在这里插入图片描述

可执行文件下载
https://download.csdn.net/download/wangxiaobei2017/16651184


20210519更新

  1. 增加视频合并功能,选择视频文件夹,对所有视频进行统一读取并整合成一个视频;
  2. 视频裁剪增加倍速功能

视频合并

  • 考虑到对较长视频进行裁剪后需要重新合并成一段视频,于是增加该功能,主要逻辑就是依次读取文件夹内的视频,保存到新视频内,可定义新视频的帧率,新视频的分辨率与文件夹内最后视频保持一致;详细代码如下:
    def video_link(self,save_fps = 20,video_w=1280,video_h=1024):
        if len(self.video_file_name) < 1:
            loggingdata("video_link ERROR : no video file!!!")
            return -1
        # 多段视频拼接在一起
        self.frame_now = 0
        tmp_video_link =  cv2.VideoWriter(self.video_save_path, cv2.VideoWriter_fourcc('M','P','E','G'), save_fps, (video_w,video_h))
        for video_name in os.listdir(self.video_file_name):
            if video_name.split('.')[-1]not in ['mp4','avi','h264']:
                loggingdata("can not open this video {}".format(video_name))
                continue
            cap = cv2.VideoCapture(os.path.join(self.video_file_name,video_name))
            # print(os.path.join(self.video_file_name,video_name))
            while self.stop_flag:
                success, origin_img = cap.read()
                if not success or len(origin_img) < 2:
                    break
                # print(self.frame_now)
                self.frame_now += 1
                tmp_video_link.write(cv2.resize(origin_img,(video_w,video_h)))
        tmp_video_link.release()

裁剪视频增加倍速

  • 裁剪视频过程中会遇到需要快放和慢放,快放的逻辑就是剔除多余帧,慢放就是增加重复帧,具体代码如下:
       while self.stop_flag and 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
           if self.frame_now > cut_frame_time[0] and self.frame_now < cut_frame_time[1]:
               if video_speed >= 1:
                   if self.frame_now%int(video_speed) == 0:
                       cut_video.write(origin_img)
               else:
                   for i in range(int(1/video_speed)):
                       cut_video.write(origin_img)

在这里插入图片描述
在这里插入图片描述

可执行文件下载 版本:20210519
https://download.csdn.net/download/wangxiaobei2017/18862562

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
好的,下面我来详细解释一下如何使用PyQt5实现视频读取、车牌定位和车牌识别模块。 1. 视频读取模块: 使用PyQt5可以很方便地实现界面设计和与用户交互,但是视频读取和处理需要使用OpenCV库。具体实现方法如下: ```python import sys import cv2 from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QApplication, QDialog, QLabel, QPushButton class VideoThread(QThread): change_pixmap_signal = pyqtSignal(QImage) def __init__(self): super().__init__() self._run_flag = True def run(self): cap = cv2.VideoCapture(0) while self._run_flag: ret, cv_img = cap.read() if ret: qt_img = self.convert_cv_qt(cv_img) self.change_pixmap_signal.emit(qt_img) def stop(self): """Sets run flag to False and waits for thread to finish""" self._run_flag = False self.wait() @staticmethod def convert_cv_qt(cv_img): """Convert from an opencv image to QPixmap""" rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB) h, w, ch = rgb_image.shape bytes_per_line = ch * w convert_to_qt_format = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap = QPixmap.fromImage(convert_to_qt_format) return pixmap.toImage() class VideoPlayer(QDialog): def __init__(self): super().__init__() # Create label that will show the video self.image_label = QLabel(self) self.image_label.resize(640, 480) # Create button to start and stop video self.button = QPushButton('Start', self) self.button.clicked.connect(self.start_video) # Create thread for video processing self.thread = VideoThread() self.thread.change_pixmap_signal.connect(self.update_image) def start_video(self): if self.thread.isRunning(): self.thread.stop() self.button.setText('Start') else: self.thread.start() self.button.setText('Stop') def update_image(self, image): """Updates the image_label with a new video frame""" self.image_label.setPixmap(QPixmap.fromImage(image)) if __name__ == '__main__': app = QApplication(sys.argv) player = VideoPlayer() player.show() sys.exit(app.exec_()) ``` 2. 车牌定位模块: 车牌定位模块需要通过识别按钮,从视频流中拉取一帧图像送入车牌检测模型进行检测,并将检测的bounding box进行裁剪,保存。具体实现方法如下: ```python import sys import cv2 from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QApplication, QDialog, QLabel, QPushButton class VideoThread(QThread): change_pixmap_signal = pyqtSignal(QImage) def __init__(self): super().__init__() self._run_flag = True def run(self): cap = cv2.VideoCapture(0) while self._run_flag: ret, cv_img = cap.read() if ret: qt_img = self.convert_cv_qt(cv_img) self.change_pixmap_signal.emit(qt_img) def stop(self): """Sets run flag to False and waits for thread to finish""" self._run_flag = False self.wait() @staticmethod def convert_cv_qt(cv_img): """Convert from an opencv image to QPixmap""" rgb_image = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB) h, w, ch = rgb_image.shape bytes_per_line = ch * w convert_to_qt_format = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap = QPixmap.fromImage(convert_to_qt_format) return pixmap.toImage() class VideoPlayer(QDialog): def __init__(self): super().__init__() # Create label that will show the video self.image_label = QLabel(self) self.image_label.resize(640, 480) # Create button to start and stop video self.button = QPushButton('Start', self) self.button.clicked.connect(self.start_video) # Create thread for video processing self.thread = VideoThread() self.thread.change_pixmap_signal.connect(self.update_image) def start_video(self): if self.thread.isRunning(): self.thread.stop() self.button.setText('Start') else: self.thread.start() self.button.setText('Stop') def update_image(self, image): """Updates the image_label with a new video frame""" self.image_label.setPixmap(QPixmap.fromImage(image)) if __name__ == '__main__': app = QApplication(sys.argv) player = VideoPlayer() player.show() sys.exit(app.exec_()) ``` 3. 车牌识别模块: 车牌识别模块需要接收车牌定位模块输入的车牌信息的图片,并采用百度飞桨的OCR识别获得车牌信息。具体实现方法如下: ```python import paddlehub as hub class OCR(object): def __init__(self): self.module = hub.Module(name="chinese_ocr_db_crnn_server") def recognize(self, img_path): result = self.module.recognize_text(images=[cv2.imread(img_path)]) return result[0]['data'] ``` 以上便是使用PyQt5实现视频读取、车牌定位和车牌识别模块的基本实现方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangxiaobei2017

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值