【QT】摄像头调用

一、基础知识

关于QT的摄像头调用: QCamera类,这个类专门用来管理摄像头,获取摄像头拍摄的图片,但是注意,这个类不做图片处理 ,QCamera类获取到的图片,必须交给 QAbstructVideoSurface类去处理 。

QAbstructVideoSurface这个类里面,处理图片的方法叫做 present,如果我们想自定义处理图片的话,只需要重写present即可 。注意QAbstructVideoSurface是一个纯虚类,他还一定要继承后重写 图片处理的方式:例如格式转换,例如镜像转换等等,总之处理我们能用为止

什么叫能用为止?我们承载图片的控件叫做 QLabel,QLabel能够放图片的格式为 QPixMap ,而摄像头获取到的图片的格式为 QVideoFrame ,所以,我们要做的具体的图像处理过程就是将图像的格式才能够QVideoFrame转换成QPixMap 。最后,QCamera仅仅是一个摄像头的管理类,管理摄像头信息

我们先要获取我们电脑的上的摄像头的信息,去给QCamera进行管理 ,使用 QCameraInfo 类型获取,方法 defaultCamera,这是一个静态方法,获取QCameraInfo对象,然后通过QCamera构造函数,传入这个QCameraInfo对象,去构建一个QCamera对象

总结下来,整个步骤如下

① 通过QCameraInfo类的defaultCamera获取摄像头信息,以QCameraInfo对象保存

② 将包含有摄像头信息的QCameraInfo对象,传入QCamera构造函数,构造一个QCamera对象,该对象就能管理刚才获取到的摄像头了

③ QCamera对象,管理的摄像头,拍摄到的图片,必须交给QAbstructVideoSurface对象去处理

QAbstructVideoSurface这个类是一个纯虚类,必须继承过后重写里面的纯虚方法

注意:QCamera 以及 QCameraInfo 这几个类,必须在.pro文件里面追加 QT += multimedia

二、代码实现

1.在新建的QT中.pro文件中,添加关键字

2.重写QAbstructVideoSurface这个类,在QT中新建一个C++的文件,abstractvideosurface.h和abstractvideosurface.cpp,相关的函数可在QT中寻找函数原型

#ifndef ABSTRACTVIDEOSURFACE_H
#define ABSTRACTVIDEOSURFACE_H

#include 
#include 

class AbstractVideoSurface : public QAbstractVideoSurface
{
    Q_OBJECT
public:
    AbstractVideoSurface(QObject* parent = NULL);
    virtual bool present(const QVideoFrame &frame);
    virtual QList supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const;

signals:
    void sndImage(QImage);


};

#endif // ABSTRACTVIDEOSURFACE_H
#include "abstractvideosurface.h"

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

}

bool AbstractVideoSurface::present(const QVideoFrame &frame)
{
// 该函数会在摄像头拍摄到每一帧的图片后,自动调用

//  图像处理函数,目标是把图片的格式从 QVideoFrame转换成 QPixmap(或者是能够轻易的转化成QPixmap的其他类)
    QVideoFrame fm = frame;//拷贝构造,内部地址浅拷贝,视频帧依旧存放于不可读取的内存上,需要进行映射
    fm.map(QAbstractVideoBuffer::ReadOnly);//映射视频帧内容,映射到可访问的地址上

    //现在 QVideroFrame,视频帧数据可正常访问了
/*
    QVideoFrame转换成 QPixmap ,我们要寻找转换的方式,一般思路上有2中
    在QVideoFrame里面,寻找: 返回值为 QPixmap,参数为空,函数名类似于 toPixmap 的方法
        或者在QVideoFrame的静态方法里面,寻找返回值为 QPixmap,参数为QVidoFrame的方法

    或者在 QPixmap的构造函数里面,寻找参数为QVideoFrame
        或者 QPixmap的静态方法里面,寻找返回值为 QPixmap,参数为QVideoFrame的方法

    经过这两轮的寻找,没找到可以直接将 QVideoFrame转换成 QPixmap的方法
    QT里面有一个万能的图像类,叫做 QImage
        因为一看构造函数,发现,构建一个QImage只需要图像的首地址,图像的宽度,图像的高度,(图像每一行的字节数),这些数据,任意图像类型,都可以轻易获取
        例如QVideoFrame就能够轻易的获取
            图像首地址:bits
            图像宽度:width
            图像高度:height
            图像每行字节数:bytesPerLine
            图像格式:需求格式为 QImage::Format_RGB32
    QImage::QImage(const uchar *data, int width, int height, Format format, QImageCleanupFunction cleanupFunction = Q_NULLPTR, void *cleanupInfo = Q_NULLPTR)
*/
    //QImage image(fm.bits(),fm.width(),fm.height(),QImage::Format_RGB32); 以下主要是图片格式的转化不同的形式,结果是一样的
    //QImage image(fm.bits(),fm.width(),fm.height(),fm.imageFormatFromPixelFormat(QVideoFrame::Format_RGB32));
    QImage image(fm.bits(),fm.width(),fm.height(),fm.imageFormatFromPixelFormat(fm.pixelFormat()));

    // 注意,摄像头拍摄到的图片,是上下左右相反的,所以我们还需要镜像一下
    image = image.mirrored(1);//表示横向镜像,纵向镜像是默认值
    emit sndImage(image);
}

QList AbstractVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
{
    Q_UNUSED(type);
//  这个函数在  AbstractVideoSurface构造函数的时候,自动调用,目的是确认AbstractVideoSurface能够支持的像素格式,摄像头拍摄到的图片,他的像素格式是 RGB_3

// 所以,我们要把RGB_32这个像素格式,添加到AbstractVideoSurface支持列表里面了
// 当前函数,返回的就是AbstractVideoSurface所支持的像素格式的列表
    return QList() << QVideoFrame::Format_RGB32;
    // 此处的 operoatr<< 相遇 QList::append方法
}

3.构建简单的QT窗口ui文件

 4.在widget.h文件和widget.cpp文件中,将功能完善,其中涉及到数据广播里的知识,不了解的可以参考一下前一篇【QT】数据广播

#include 
#include 
#include "abstractvideosurface.h"
#include 
#include 
#include 
#include 


namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;
    QCamera* camera;
    QUdpSocket* sender;
    QUdpSocket* reciver;

public slots:
    void rcvImage(QImage);
    void onReadyRead();

private slots:
    void on_pushButton_clicked();
    void on_pushButton_2_clicked();
};

#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    sender = new QUdpSocket(this);
    reciver = new QUdpSocket(this);
    reciver->bind(12345,QUdpSocket::ReuseAddressHint);
    QObject::connect(reciver,SIGNAL(readyRead()),this,SLOT(onReadyRead()));

/*
    ① 通过QCameraInfo类的defaultCamera获取摄像头信息,以QCameraInfo对象保存
    ② 将包含有摄像头信息的QCameraInfo对象,传入QCamera构造函数,构造一个QCamera对象,该对象就能管理刚才获取到的摄像头了
    ③ QCamera对象,管理的摄像头,拍摄到的图片,必须交给QAbstructVideoSurface对象去处理
        QAbstructVideoSurface这个类是一个纯虚类,必须继承过后重写里面的纯虚方法
*/
    QCameraInfo info = QCameraInfo::defaultCamera();
    camera = new QCamera(info);
    AbstractVideoSurface* surface = new AbstractVideoSurface(this);//图像处理类
    camera->setViewfinder(surface);//将camera拍摄到的图片交给AbstractVideoSurface类对象里面的present函数进行处理

    QObject::connect(surface,SIGNAL(sndImage(QImage)),this,SLOT(rcvImage(QImage)));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::rcvImage(QImage image)
{
    QPixmap pic = QPixmap::fromImage(image);
    pic = pic.scaled(ui->label->size());
    ui->label->setPixmap(pic);

    QImage img = pic.toImage();

/*
    writeDatagram里面,数据最多是一个QByteArray,所以,我们现在要想办法,把QImage存入QByteArray(或者转换成QByteArray)
*/
/*
    整个转换逻辑如下:
    QImage 里面有一个方法叫做 save,可以将QImage保存到 QIODevice的对象里面去
    QIODevice有一个派生类叫做 QBuffer,也就是说QIamge::save可以做到将QImage保存到一个QBuffer里面去
    QBuffer的构造函数,支持传入一个QByteArray,作为QBuffer的缓存区
        也就是意味着:写入,保存进入QBuffer的数据,实际上是保存到了 QByteArray里面来
*/
    QByteArray arr;
    QBuffer buf(&arr);//将arr作为buf缓存区
    img.save(&buf,"JPG");//将image保存到buf里面去,实际上就是保存到buf的缓存区arr里面去
    sender->writeDatagram(arr,QHostAddress::Broadcast,12345);
    if(reciver->isOpen()){
        reciver->close();
    }
}

void Widget::onReadyRead()
{
//  读取图片的时候,一样的道理,要将QByteArray里面的数据,作为QImage读取
/*
    有一个类叫做 QImageReader,里面有一个方法叫做read
    QImageReader的构造函数,支持传入一个QIODevice,能传入QIODeviec,就等于说是传入了一个QByteArray
*/
    int size = reciver->pendingDatagramSize();//获取图片尺寸
    QByteArray arr;
    arr.resize(size);//将arr适应图片大小

    reciver->readDatagram(arr.data(),size);//QByteArray的data()方法,获取首地址
    // 读取数据,保存到arr里面

    QBuffer buf(&arr);
    QImageReader reader(&buf);
    QImage image = reader.read();

    QPixmap pic = QPixmap::fromImage(image);
    ui->label->setPixmap(pic);

}

void Widget::on_pushButton_clicked()
{
    camera->start();
}

void Widget::on_pushButton_2_clicked()
{
    ui->label->clear();
    camera->stop();
}

三.效果实现

1.将QT文件编译后,点击运行,再点击开启按钮,摄像头调用,点击关闭按钮,摄像头关闭

 2.摄像头开启,效果实现!

四、扩展功能

如果想要界面美观化,可添加资源文件,修改UI文件,如果想扩展功能,可在cpp文件中添加相应功能代码

你可能感兴趣的:(QT界面开发,qt)