一、基础知识
关于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文件中添加相应功能代码