当前位置:   article > 正文

pyQT 视频播放器(三) 实现视频截图、获取每一帧数据_如何获取视频每帧的观看量

如何获取视频每帧的观看量

pyQT 视频播放器(三) 实现视频截图、获取每一帧数据

背景

“PyQt5 实现视频播放器(二) ,详细版本 ,适合新手入门“
中已经想写的介绍了如何使用pyQT 自带的一些控件,实现简单的视频播放功能(例如播放、暂停、进度条跳转、声音控制、全屏播放等),通过评论区的交流,发现大家除了这些简单功能外,还有一个比较强烈的需求就是视频截图,所以下面就来实现一下视频截图功能

方法调研

在方法调研的前分享一下自己在遇到一个新问题的时候的“搜索方法”,希望能够起到抛砖引玉的作用。(有了正确的搜索方式,对于开发能少走弯路,提高效率)

  1. 问题定义:这一步也容易也难,特别是工作之后会有更加深刻的体验,简单来说就是如何将你的需求/问题与实际程序实现逻辑进行对应,比如说想实现视频进度条展示,那么对应到具体的pyqt的开发中可能就是,如何使用qt的slider来展示qmediaplayer 播放的媒体进度。这就要求你能对需求/问题进行分解、对使用的程序逻辑/程序组件功能有充分的了解。有点啰嗦了,回归正题,问题定义为:
    qt实现视频截图”、如何获取qt视频播放中的每一帧数据、“qt mediaplayer 截图 ”,
    会用英文更好: get frame data using QMediaPlayer
  2. 搜索: pyQT作为QT的一个python版本接口使用方式,整体的资料虽然不如QT的多,但是QT的使用方式都能够在pyQT上使用,因此我在搜索的时候基本上都是不区分pyqt和QT的,比如这次搜索的时候就是查询qt 如何获取,参考的代码也都是QT的源码,改成python的时候只需要注意import 的位置、以及c++写法到python写法的切换即可。
  3. 筛选:搜索到的方案比较多,大致可以归纳为如下几种:
    (1)使用“截屏”的方式来完成截图,我稍微尝试了一下发现对于qmediaplayer输出的widget无法截取到画面图像,另外即使截取到了也只是UI界面上大小原始原始图像的大小,因此放弃这个方案
    (2)qt + ffmpeg或qt + vlc或qt+ opencv 等进行视频解码播放,功能强大,能够比较方便的进行功能较复杂的图像处理需求,例如使用opencv可以对图像进行复杂的操作(识别、检测、画框)
    (3)QMediaPlayer + QAbstractVideoSurface,比较方便,转换的是QImage,可以获取每一帧数据。
    方案筛选上,由于我们只需要完成截图功能,而且之前用的就是Qmediaplayer,选方案二,能够最快速的实现这个功能。

详细代码说明

采用 QMediaPlayer + QAbstractVideoSurface 这个方案重点需要了解一下QAbstractVideoSurface 这个类,结合查询到的资料以及Assistant中的说明:QAbstractVideoSurface class is a base class for video presentation surfaces.
其中 [pure virtual] 的有两个函数:
(1)supportedPixelFormats() # 支持的视频解码后的数据格式
(2)present(const QVideoFrame &frame) # 获取视频解码的数据frame,进行展示
因此我们只需要新写一个类,继承这个抽象的QAbstractVideoSurface 类,然后重写里面的这个两个纯虚函方法,就能从present 输入的Frame中获取每一帧的数据。
如下:

from PyQt5.QtMultimedia import QAbstractVideoSurface, QVideoFrame, QAbstractVideoBuffer
from PyQt5.QtCore import pyqtSignal, QDateTime
from PyQt5.QtGui import QImage
class myVideoSurface(QAbstractVideoSurface):

    FinishGrab = pyqtSignal()  # 截图完成信号

    def __init__(self, parent=None):
        super(QAbstractVideoSurface, self).__init__(parent)

    def supportedPixelFormats(self, type=None):
        support_format = [
            QVideoFrame.Format_ARGB32,
            QVideoFrame.Format_ARGB32_Premultiplied,
            QVideoFrame.Format_ARGB8565_Premultiplied,
            QVideoFrame.Format_AYUV444,
            QVideoFrame.Format_AYUV444_Premultiplied,
            QVideoFrame.Format_BGR24,
            QVideoFrame.Format_BGR32,
            QVideoFrame.Format_BGR555,
            QVideoFrame.Format_BGR565,
            QVideoFrame.Format_BGRA32,
            QVideoFrame.Format_BGRA32_Premultiplied,
            QVideoFrame.Format_BGRA5658_Premultiplied,
            QVideoFrame.Format_CameraRaw,
            QVideoFrame.Format_IMC1,
            QVideoFrame.Format_IMC2,
            QVideoFrame.Format_IMC3,
            QVideoFrame.Format_IMC4,
            QVideoFrame.Format_Jpeg,
            QVideoFrame.Format_NV12,
            QVideoFrame.Format_NV21,
            QVideoFrame.Format_RGB24,
            QVideoFrame.Format_RGB32,
            QVideoFrame.Format_RGB555,
            QVideoFrame.Format_RGB565,
            QVideoFrame.Format_User,
            QVideoFrame.Format_UYVY,
            QVideoFrame.Format_Y16,
            QVideoFrame.Format_Y8 ,
            QVideoFrame.Format_YUV420P,
            QVideoFrame.Format_YUV444,
            QVideoFrame.Format_YUYV,
            QVideoFrame.Format_YV12,
        ]
        return support_format

    def present(self, frame: 'QVideoFrame'):
        print("width:{},heigth:{},format:{},start_time:{},endtime{}".format(
            frame.width(), frame.height(), frame.pixelFormat(), frame.startTime(), frame.endTime()
        ))
        if frame.isValid():
            frame.map(QAbstractVideoBuffer.ReadOnly)
            img = QImage(frame.bits(), frame.width(), frame.height(),
                         QVideoFrame.imageFormatFromPixelFormat(frame .pixelFormat()))
            grab_jpg = './'+QDateTime.currentDateTime().toString("yyyy-MM-dd hh-mm-ss-zzz")+'.jpg'
            save_state = img.save(grab_jpg)
            print("截图状态:"+str(save_state))
            frame.unmap()
            self.FinishGrab.emit()
            return True
        else:
            return False
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

其中 supportedPixelFormats 中的支持的格式只要后面present中能够支持即可,由于我们并不是真的展示,在present中只是转成了QImage,然后完成截图。在这里有两种方案:
(1)使用present中每一帧数据进行展示(这里已经转成QImage图像数据了,接着想怎么画图都可以,然后图像展示就可以),这个方案的话就是图像转换比较耗时,可能会影响到播放的流畅度,如果不是每一帧图像都需要处理的话,不建议用这种方式
(2)播放然后采用“PyQt5 实现视频播放器(二) ,详细版本 ,适合新手入门“
中介绍的widget进行播放,只有需要截图的将player的输出切换到videosurface进行截图,截完图在切换回去,这种方式播放就是在截图的时候由于切换了player的输出因此会导致截图的时候视频界面会就“黑“一下(此时没有输出),
这里考虑到截图功能不是每一帧都需要截(由界面上得按钮触发),因此采用了方案(2)

最终效果

首先在原先 “PyQt5 实现视频播放器(二) ,详细版本 ,适合新手入门“中的界面基础上增加了一个截图按钮:
视频播放界面(包含截图功能)
在视频播放的时候,点击截图按钮,会自动截取当前时刻的图片,由于只是demo(抛砖引玉的作用),因此图片会保存到demo所在的当前目录下面(保存的代码在present函数中):
截取的图片的存放目录

某张具体的截图如下:(获取到720x480的原始尺寸大小)
截取的图片

总结:

(1)基本完成视频截图的功能,如果需要复杂的功能可以在这个上面继续扩展(例如截图的列表展示、截图的帧号等)
(2)截图的时候已经能够获取每一帧的数据,那么对应的“画框”、图像检测、分类等等都可以在这个上面进行扩展
(3)完整的演示demo 已经打包上传到csdn上:
[https://download.csdn.net/download/u012552296/84553765](demo中有视频和解码器,可以完整运行完整个demo,windows10 环境验证正常)(https://download.csdn.net/download/u012552296/84553765)
[https://download.csdn.net/download/u012552296/16184221] (https://download.csdn.net/download/u012552296/16184221)

参考资料

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
[1]How to save a frame using QMediaPlayer?
https://stackoverflow.com/questions/37724602/how-to-save-a-frame-using-qmediaplayer
[1]原来Qt从视频中获取每一帧数据如此简单: https://blog.csdn.net/jxbinwd/article/details/81034339

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号