【QT】摄像头调用 (有源码 **)

目录

【QT】摄像头调用 (有源码 **)

---------------------------------------------------------

注:【QT】摄像头调用,原作者贴出的代码,有些小错误。修改可参考“QT: error: invalid use of incomplete type ‘class Ui::Widget‘ , ui(new Ui::Widget) 错误处理办法 -1”  https://blog.csdn.net/ken2232/article/details/135581921

---------------------------------------------------------

关联参考:

qt视频播放界面类设计(有源码**)
https://blog.csdn.net/ken2232/article/details/135601449

Qt视频播放器[QMediaPlayer+QVideowidget] (有源码**)
https://blog.csdn.net/ken2232/article/details/135598822

摄像头标定:原理、方法、步骤 (**)
https://blog.csdn.net/ken2232/article/details/135644249

---------------------------------------------------------

标定问题:

摄像头的制造者,才需要考虑标定问题?

对于摄像头的使用者来说,不需要考虑标定问题。除非使用者也是制作者;比如:市面上没有现成的产品,而需要使用者自己搞定?又或者由于成本问题等等。

====================================

【QT】摄像头调用 (有源码 **)

  https://blog.csdn.net/m0_61573070/article/details/125780286

一、基础知识

关于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中寻找函数原型

abstractvideosurface.h

    #ifndef ABSTRACTVIDEOSURFACE_H
    #define ABSTRACTVIDEOSURFACE_H
     
    #include <QAbstractVideoSurface>
    #include <QImage>
     
    class AbstractVideoSurface : public QAbstractVideoSurface
    {
        Q_OBJECT
    public:
        AbstractVideoSurface(QObject* parent = NULL);
        virtual bool present(const QVideoFrame &frame);
        virtual QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const;
     
    signals:
        void sndImage(QImage);
         
    };
     
    #endif // ABSTRACTVIDEOSURFACE_H

  

abstractvideosurface.cpp

    #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<QVideoFrame::PixelFormat> AbstractVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
    {
        Q_UNUSED(type);
    //  这个函数在  AbstractVideoSurface构造函数的时候,自动调用,目的是确认AbstractVideoSurface能够支持的像素格式,摄像头拍摄到的图片,他的像素格式是 RGB_3
     
    // 所以,我们要把RGB_32这个像素格式,添加到AbstractVideoSurface支持列表里面了
    // 当前函数,返回的就是AbstractVideoSurface所支持的像素格式的列表
        return QList<QVideoFrame::PixelFormat>() << QVideoFrame::Format_RGB32;
        // 此处的 operoatr<< 相遇 QList::append方法
    }

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

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

widget.h

    #include <QCamera>
    #include <QCameraInfo>
    #include "abstractvideosurface.h"
    #include <QImage>
    #include <QUdpSocket>
    #include <QBuffer>
    #include <QImageReader>    
     
    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

  

widget.cpp

    #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文件中添加相应功能代码

————————————————
版权声明:本文为CSDN博主「多学多练」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_61573070/article/details/125780286

Demo: https://download.csdn.net/download/ken2232/88757272

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值