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