Qt获取摄像头画面的每一帧数据
继承自QAbstractVideoSurface,实现它的一些虚函数,可以在实现的虚函数 present 中获取到摄像头画面的一帧数据,拿到数据之后就可以转换处理了,这里拿到数据之后,绘制在主窗口上了。
主要代码:
首先在 .pro 中加入 multimedia 模块:
QT += core gui multimedia multimediawidgets
MainWindow.cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QtWidgets>
#include <QVideosurfaceFormat>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
camera_ = new QCamera;
surface_ = new MyVideoSurface(this);
camera_->setViewfinder(surface_);
camera_->start();//这里启动
}
MainWindow::~MainWindow()
{
delete ui;
}
QSize MainWindow::sizeHint() const
{
return surface_->surfaceFormat().sizeHint();
}
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
if (surface_->isActive()) {
const QRect videoRect = surface_->videoRect();
if (!videoRect.contains(event->rect())) {
QRegion region = event->region();
region = region.subtracted(videoRect);
QBrush brush = palette().background();
for (const QRect &rect : region){
painter.fillRect(rect, brush);
}
}
surface_->paint(&painter);//在主窗口绘制
} else {
painter.fillRect(event->rect(), palette().background());
}
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
QMainWindow::resizeEvent(event);
surface_->updateVideoRect();
}
MyVideoSurface.h
#ifndef MYVIDEOSURFACE_H
#define MYVIDEOSURFACE_H
#include <QAbstractVideoSurface>
#include <QImage>
#include <QRect>
#include <QVideoFrame>
class MyVideoSurface : public QAbstractVideoSurface
{
Q_OBJECT
public:
MyVideoSurface(QWidget *widget, QObject *parent = 0);
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const override;
bool isFormatSupported(const QVideoSurfaceFormat &format) const override;
bool start(const QVideoSurfaceFormat &format) override;
bool present(const QVideoFrame &frame) override;
void stop() override;
QRect videoRect() const;
void updateVideoRect();
void paint(QPainter *painter);
private:
QWidget * widget_;
QImage::Format imageFormat_;
QRect targetRect_;
QSize imageSize_;
QVideoFrame currentFrame_;
};
#endif
MyVideoSurface.cpp
#include "MyVideoSurface.h"
#include <QtWidgets>
#include <qabstractvideosurface.h>
#include <qvideosurfaceformat.h>
#include <QVideoSurfaceFormat>
MyVideoSurface::MyVideoSurface(QWidget *widget, QObject *parent)
: QAbstractVideoSurface(parent)
, widget_(widget)
, imageFormat_(QImage::Format_Invalid)
{
}
QList<QVideoFrame::PixelFormat> MyVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
if (handleType == QAbstractVideoBuffer::NoHandle) {
return QList<QVideoFrame::PixelFormat>()<< QVideoFrame::Format_RGB32<< QVideoFrame::Format_ARGB32<< QVideoFrame::Format_ARGB32_Premultiplied<< QVideoFrame::Format_RGB565<< QVideoFrame::Format_RGB555;
} else {
return QList<QVideoFrame::PixelFormat>();
}
}
bool MyVideoSurface::isFormatSupported(const QVideoSurfaceFormat & format) const
{
return QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat()) != QImage::Format_Invalid && !format.frameSize().isEmpty() && format.handleType() == QAbstractVideoBuffer::NoHandle;
}
bool MyVideoSurface::start(const QVideoSurfaceFormat &format)
{
const QImage::Format imageFormat_ = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
const QSize size = format.frameSize();
if (imageFormat_ != QImage::Format_Invalid && !size.isEmpty()) {
this->imageFormat_ = imageFormat_;
widget_->resize(size);
QAbstractVideoSurface::start(format);
widget_->updateGeometry();
updateVideoRect();
return true;
}
return false;
}
void MyVideoSurface::stop()
{
currentFrame_ = QVideoFrame();
targetRect_ = QRect();
QAbstractVideoSurface::stop();
widget_->update();
}
bool MyVideoSurface::present(const QVideoFrame &frame) //每一帧摄像头的数据,都会经过这里
{
if (surfaceFormat().pixelFormat() != frame.pixelFormat() || surfaceFormat().frameSize() != frame.size()) {
setError(IncorrectFormatError);
stop();
return false;
}
currentFrame_ = frame;
widget_->repaint(targetRect_);
return true;
}
QRect MyVideoSurface::videoRect() const
{
return targetRect_;
}
void MyVideoSurface::updateVideoRect()
{
QSize size = surfaceFormat().sizeHint();
size.scale(widget_->size().boundedTo(size), Qt::KeepAspectRatio);
targetRect_ = QRect(QPoint(0, 0), size);
targetRect_.moveCenter(widget_->rect().center());
}
void MyVideoSurface::paint(QPainter *painter)//这里绘制每一帧数据
{
if (currentFrame_.map(QAbstractVideoBuffer::ReadOnly)) {
//img就是转换的数据了
QImage img = QImage(currentFrame_.bits(),currentFrame_.width(),currentFrame_.height(),currentFrame_.bytesPerLine(),imageFormat_).mirrored(true,false).scaled(widget_->size());
painter->drawImage(targetRect_, img, QRect(QPoint(0,0),img.size()));
currentFrame_.unmap();
}
}
效果: