利用QCamera等实现的摄像头每一帧数据的捕捉(只依赖Qt)

目的:

如果只是单纯地显示摄像头拍摄的画面,利用QCamera其实可以很简单地实现,但是如果为了获取摄像头的每一帧数据做进一步处理,而不是仅仅用于显示,在Qt中貌似没有很直接的方法,因此参考了网上一些大神的做法,做了一些整理。

参考博客:

https://blog.csdn.net/flfihpv259/article/details/68940906
https://blog.csdn.net/flfihpv259/article/details/68940875

描述:

从网上查找到的资料,大概可以理解为实现这样的目的需要用到Qt中的一个类QAbstractVideoSurface,查看Qt中QAbstractVideoSurface的描述:

The QAbstractVideoSurface class is a base class for video presentation surfaces.
The QAbstractVideoSurface class defines the standard interface that video producers use to inter-operate with video presentation surfaces. You can subclass this interface to receive video frames from sources like decoded media or cameras to perform your own processing.
大概的意思为:
QAbstractVideoSurface类是视频演示表面的基类。
QAbstractVideoSurface类定义视频制作者用于与视频演示表面交互操作的标准接口。您可以将此接口子类化,以接收来自解码媒体或相机等源的视频帧,以执行您自己的处理。

实现:

自定义一个类继承于QAbstractVideoSurface,如我定义了一个类QtCameraCapture,代码如下:
QtCameraCapture.h如下:

#ifndef QTCAMERACAPTURE_H
#define QTCAMERACAPTURE_H

#include 
#include 
#include 

class QtCameraCapture : public QAbstractVideoSurface
{
    Q_OBJECT
public:
    enum PixelFormat {
        Format_Invalid,
        Format_ARGB32,
        Format_ARGB32_Premultiplied,
        Format_RGB32,
        Format_RGB24,
        Format_RGB565,
        Format_RGB555,
        Format_ARGB8565_Premultiplied,
        Format_BGRA32,
        Format_BGRA32_Premultiplied,
        Format_BGR32,
        Format_BGR24,
        Format_BGR565,
        Format_BGR555,
        Format_BGRA5658_Premultiplied,

        Format_AYUV444,
        Format_AYUV444_Premultiplied,
        Format_YUV444,
        Format_YUV420P,
        Format_YV12,
        Format_UYVY,
        Format_YUYV,
        Format_NV12,
        Format_NV21,
        Format_IMC1,
        Format_IMC2,
        Format_IMC3,
        Format_IMC4,
        Format_Y8,
        Format_Y16,

        Format_Jpeg,

        Format_CameraRaw,
        Format_AdobeDng,

#ifndef Q_QDOC
        NPixelFormats,
#endif
        Format_User = 1000
    };

    Q_ENUM(PixelFormat)

    explicit QtCameraCapture(QObject *parent = 0);

    QList supportedPixelFormats(
            QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;

    bool present(const QVideoFrame &frame) override;

signals:
    void frameAvailable(QImage frame);

};

#endif // QTCAMERACAPTURE_H

QtCameraCapture.cpp如下:

#include "QtCameraCapture.h"

QtCameraCapture::QtCameraCapture(QObject *parent) : QAbstractVideoSurface(parent)
{

}

QList<QVideoFrame::PixelFormat> QtCameraCapture::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
    Q_UNUSED(handleType);
    return QList<QVideoFrame::PixelFormat>()
            << QVideoFrame::Format_ARGB32
            << QVideoFrame::Format_ARGB32_Premultiplied
            << QVideoFrame::Format_RGB32
            << QVideoFrame::Format_RGB24
            << QVideoFrame::Format_RGB565
            << QVideoFrame::Format_RGB555
            << QVideoFrame::Format_ARGB8565_Premultiplied
            << QVideoFrame::Format_BGRA32
            << QVideoFrame::Format_BGRA32_Premultiplied
            << QVideoFrame::Format_BGR32
            << QVideoFrame::Format_BGR24
            << QVideoFrame::Format_BGR565
            << QVideoFrame::Format_BGR555
            << QVideoFrame::Format_BGRA5658_Premultiplied
            << QVideoFrame::Format_AYUV444
            << QVideoFrame::Format_AYUV444_Premultiplied
            << QVideoFrame::Format_YUV444
            << QVideoFrame::Format_YUV420P
            << QVideoFrame::Format_YV12
            << QVideoFrame::Format_UYVY
            << QVideoFrame::Format_YUYV
            << QVideoFrame::Format_NV12
            << QVideoFrame::Format_NV21
            << QVideoFrame::Format_IMC1
            << QVideoFrame::Format_IMC2
            << QVideoFrame::Format_IMC3
            << QVideoFrame::Format_IMC4
            << QVideoFrame::Format_Y8
            << QVideoFrame::Format_Y16
            << QVideoFrame::Format_Jpeg
            << QVideoFrame::Format_CameraRaw
            << QVideoFrame::Format_AdobeDng;
}

bool QtCameraCapture::present(const QVideoFrame &frame)
{
    if (frame.isValid()) {
        QVideoFrame cloneFrame(frame);
        cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
        const QImage image(cloneFrame.bits(),
                           cloneFrame.width(),
                           cloneFrame.height(),
                           QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
        emit frameAvailable(image);
        cloneFrame.unmap();
        return true;
    }
    return false;
}

自定义QtCameraCapture类重写了QAbstractVideoSurface的supportedPixelFormats和present函数,只要获取到每一帧,present函数便会执行一次。

使用方法:

大致的使用思路为:

m_camera = new QCamera(m_cameraDeviceInfo);
m_cameraCapture = new QtCameraCapture;
m_camera->setViewfinder(m_cameraCapture);
connect(m_cameraCapture, SIGNAL(frameAvailable(QImage)), this, SLOT(grabImage(QImage)));
m_camera->start();

尤其要注意m_camera->setViewfinder(m_cameraCapture);
必须要进行这一步是设置才能生效,由此当QCamera调用start()函数后,便会获取到图像帧数据。

使用例程:

环境:win10 + Qt5.7.1 + mingw530 + USB摄像头(UVC)

如上文所提到的,利用Qt提供的QCamera等类,如果仅仅为了实现摄像头画面的显示,那么几句代码就可以实现了。但是如果希望有一个Camera模块,同时有提供数据帧的接口,则需要去获取每一帧数据的方法,这样的获取不是利用定时器去实现,而应该是摄像头有数据时,则获取到每一帧。本人做了一个简单的例子,大概思路如下:
利用QCamera等实现的摄像头每一帧数据的捕捉(只依赖Qt)_第1张图片

代码链接:

https://download.csdn.net/download/zefinng/10639260
ps:代码中除了录制功能外,其他功能均已实现。

效果图:

利用QCamera等实现的摄像头每一帧数据的捕捉(只依赖Qt)_第2张图片

出现的问题:

当QtCameraCapture中的present获取到的每一帧QImage后,在Widget得到后如果直接保存到本地文件,能够保存正常,但是水平方向发生了翻转;但如果直接用于显示,会出现程序异常中断的问题。因此在用信号送出QImage数据时,调用了QImage的mirrored函数进行了水平翻转,在传递给Widget用于显示,发现可以正常显示。这个问题还不清楚原因,欢迎交流讨论。

你可能感兴趣的:(Qt)