目的:
如果只是单纯地显示摄像头拍摄的画面,利用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 <QObject>
#include <QAbstractVideoSurface>
#include <QDebug>
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<QVideoFrame::PixelFormat> 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模块,同时有提供数据帧的接口,则需要去获取每一帧数据的方法,这样的获取不是利用定时器去实现,而应该是摄像头有数据时,则获取到每一帧。本人做了一个简单的例子,大概思路如下:
代码链接:
https://download.csdn.net/download/zefinng/10639260
ps:代码中除了录制功能外,其他功能均已实现。
效果图:
出现的问题:
当QtCameraCapture中的present获取到的每一帧QImage后,在Widget得到后如果直接保存到本地文件,能够保存正常,但是水平方向发生了翻转;但如果直接用于显示,会出现程序异常中断的问题。因此在用信号送出QImage数据时,调用了QImage的mirrored函数进行了水平翻转,在传递给Widget用于显示,发现可以正常显示。这个问题还不清楚原因,欢迎交流讨论。