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文件中,添加关键字

Qt读取摄像头_第1张图片

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


 
 
   
   
   
   
  1. #ifndef ABSTRACTVIDEOSURFACE_H
  2. #define ABSTRACTVIDEOSURFACE_H
  3. #include
  4. #include
  5. class AbstractVideoSurface : public QAbstractVideoSurface
  6. {
  7. Q_OBJECT
  8. public:
  9. AbstractVideoSurface(QObject* parent = NULL);
  10. virtual bool present(const QVideoFrame &frame);
  11. virtual QList supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const;
  12. signals:
  13. void sndImage(QImage);
  14. };
  15. #endif // ABSTRACTVIDEOSURFACE_H

 
 
   
   
   
   
  1. #include "abstractvideosurface.h"
  2. AbstractVideoSurface:: AbstractVideoSurface(QObject* parent):
  3. QAbstractVideoSurface(parent)
  4. {
  5. }
  6. bool AbstractVideoSurface::present(const QVideoFrame &frame)
  7. {
  8. // 该函数会在摄像头拍摄到每一帧的图片后,自动调用
  9. // 图像处理函数,目标是把图片的格式从 QVideoFrame转换成 QPixmap(或者是能够轻易的转化成QPixmap的其他类)
  10. QVideoFrame fm = frame; //拷贝构造,内部地址浅拷贝,视频帧依旧存放于不可读取的内存上,需要进行映射
  11. fm. map(QAbstractVideoBuffer::ReadOnly); //映射视频帧内容,映射到可访问的地址上
  12. //现在 QVideroFrame,视频帧数据可正常访问了
  13. /*
  14. QVideoFrame转换成 QPixmap ,我们要寻找转换的方式,一般思路上有2中
  15. 在QVideoFrame里面,寻找: 返回值为 QPixmap,参数为空,函数名类似于 toPixmap 的方法
  16. 或者在QVideoFrame的静态方法里面,寻找返回值为 QPixmap,参数为QVidoFrame的方法
  17. 或者在 QPixmap的构造函数里面,寻找参数为QVideoFrame
  18. 或者 QPixmap的静态方法里面,寻找返回值为 QPixmap,参数为QVideoFrame的方法
  19. 经过这两轮的寻找,没找到可以直接将 QVideoFrame转换成 QPixmap的方法
  20. QT里面有一个万能的图像类,叫做 QImage
  21. 因为一看构造函数,发现,构建一个QImage只需要图像的首地址,图像的宽度,图像的高度,(图像每一行的字节数),这些数据,任意图像类型,都可以轻易获取
  22. 例如QVideoFrame就能够轻易的获取
  23. 图像首地址:bits
  24. 图像宽度:width
  25. 图像高度:height
  26. 图像每行字节数:bytesPerLine
  27. 图像格式:需求格式为 QImage::Format_RGB32
  28. QImage::QImage(const uchar *data, int width, int height, Format format, QImageCleanupFunction cleanupFunction = Q_NULLPTR, void *cleanupInfo = Q_NULLPTR)
  29. */
  30. //QImage image(fm.bits(),fm.width(),fm.height(),QImage::Format_RGB32); 以下主要是图片格式的转化不同的形式,结果是一样的
  31. //QImage image(fm.bits(),fm.width(),fm.height(),fm.imageFormatFromPixelFormat(QVideoFrame::Format_RGB32));
  32. QImage image(fm.bits(),fm.width(),fm.height(),fm.imageFormatFromPixelFormat(fm.pixelFormat()));
  33. // 注意,摄像头拍摄到的图片,是上下左右相反的,所以我们还需要镜像一下
  34. image = image. mirrored( 1); //表示横向镜像,纵向镜像是默认值
  35. emit sndImage(image);
  36. }
  37. QList AbstractVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
  38. {
  39. Q_UNUSED(type);
  40. // 这个函数在 AbstractVideoSurface构造函数的时候,自动调用,目的是确认AbstractVideoSurface能够支持的像素格式,摄像头拍摄到的图片,他的像素格式是 RGB_3
  41. // 所以,我们要把RGB_32这个像素格式,添加到AbstractVideoSurface支持列表里面了
  42. // 当前函数,返回的就是AbstractVideoSurface所支持的像素格式的列表
  43. return QList() << QVideoFrame::Format_RGB32;
  44. // 此处的 operoatr<< 相遇 QList::append方法
  45. }

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

Qt读取摄像头_第2张图片

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


 
 
   
   
   
   
  1. #include
  2. #include
  3. #include "abstractvideosurface.h"
  4. #include
  5. #include
  6. #include
  7. #include
  8. namespace Ui {
  9. class Widget;
  10. }
  11. class Widget : public QWidget
  12. {
  13. Q_OBJECT
  14. public:
  15. explicit Widget(QWidget *parent = 0);
  16. ~ Widget();
  17. private:
  18. Ui::Widget *ui;
  19. QCamera* camera;
  20. QUdpSocket* sender;
  21. QUdpSocket* reciver;
  22. public slots:
  23. void rcvImage(QImage);
  24. void onReadyRead();
  25. private slots:
  26. void on_pushButton_clicked();
  27. void on_pushButton_2_clicked();
  28. };
  29. #endif // WIDGET_H

 
 
   
   
   
   
  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. Widget:: Widget(QWidget *parent) :
  4. QWidget(parent),
  5. ui( new Ui::Widget)
  6. {
  7. ui-> setupUi( this);
  8. sender = new QUdpSocket( this);
  9. reciver = new QUdpSocket( this);
  10. reciver-> bind( 12345,QUdpSocket::ReuseAddressHint);
  11. QObject:: connect(reciver, SIGNAL( readyRead()), this, SLOT( onReadyRead()));
  12. /*
  13. ① 通过QCameraInfo类的defaultCamera获取摄像头信息,以QCameraInfo对象保存
  14. ② 将包含有摄像头信息的QCameraInfo对象,传入QCamera构造函数,构造一个QCamera对象,该对象就能管理刚才获取到的摄像头了
  15. ③ QCamera对象,管理的摄像头,拍摄到的图片,必须交给QAbstructVideoSurface对象去处理
  16. QAbstructVideoSurface这个类是一个纯虚类,必须继承过后重写里面的纯虚方法
  17. */
  18. QCameraInfo info = QCameraInfo:: defaultCamera();
  19. camera = new QCamera(info);
  20. AbstractVideoSurface* surface = new AbstractVideoSurface( this); //图像处理类
  21. camera-> setViewfinder(surface); //将camera拍摄到的图片交给AbstractVideoSurface类对象里面的present函数进行处理
  22. QObject:: connect(surface, SIGNAL( sndImage(QImage)), this, SLOT( rcvImage(QImage)));
  23. }
  24. Widget::~ Widget()
  25. {
  26. delete ui;
  27. }
  28. void Widget::rcvImage(QImage image)
  29. {
  30. QPixmap pic = QPixmap:: fromImage(image);
  31. pic = pic. scaled(ui->label-> size());
  32. ui->label-> setPixmap(pic);
  33. QImage img = pic. toImage();
  34. /*
  35. writeDatagram里面,数据最多是一个QByteArray,所以,我们现在要想办法,把QImage存入QByteArray(或者转换成QByteArray)
  36. */
  37. /*
  38. 整个转换逻辑如下:
  39. QImage 里面有一个方法叫做 save,可以将QImage保存到 QIODevice的对象里面去
  40. QIODevice有一个派生类叫做 QBuffer,也就是说QIamge::save可以做到将QImage保存到一个QBuffer里面去
  41. QBuffer的构造函数,支持传入一个QByteArray,作为QBuffer的缓存区
  42. 也就是意味着:写入,保存进入QBuffer的数据,实际上是保存到了 QByteArray里面来
  43. */
  44. QByteArray arr;
  45. QBuffer buf(&arr); //将arr作为buf缓存区
  46. img. save(&buf, "JPG"); //将image保存到buf里面去,实际上就是保存到buf的缓存区arr里面去
  47. sender-> writeDatagram(arr,QHostAddress::Broadcast, 12345);
  48. if(reciver-> isOpen()){
  49. reciver-> close();
  50. }
  51. }
  52. void Widget::onReadyRead()
  53. {
  54. // 读取图片的时候,一样的道理,要将QByteArray里面的数据,作为QImage读取
  55. /*
  56. 有一个类叫做 QImageReader,里面有一个方法叫做read
  57. QImageReader的构造函数,支持传入一个QIODevice,能传入QIODeviec,就等于说是传入了一个QByteArray
  58. */
  59. int size = reciver-> pendingDatagramSize(); //获取图片尺寸
  60. QByteArray arr;
  61. arr. resize(size); //将arr适应图片大小
  62. reciver-> readDatagram(arr. data(),size); //QByteArray的data()方法,获取首地址
  63. // 读取数据,保存到arr里面
  64. QBuffer buf(&arr);
  65. QImageReader reader(&buf);
  66. QImage image = reader. read();
  67. QPixmap pic = QPixmap:: fromImage(image);
  68. ui->label-> setPixmap(pic);
  69. }
  70. void Widget::on_pushButton_clicked()
  71. {
  72. camera-> start();
  73. }
  74. void Widget::on_pushButton_2_clicked()
  75. {
  76. ui->label-> clear();
  77. camera-> stop();
  78. }

三.效果实现

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

Qt读取摄像头_第3张图片

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

四、扩展功能

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

你可能感兴趣的:(qt,开发语言)