【QT】 Qt 摄像头编程

QT 中使用摄像头采集

QT 中使用摄像头就比较简单了,已经封装好了几个类直接使用即可,下面根据获取摄像头信息,创建摄像头对象等一步一步来介绍每个类。

注意: 目前只能在 window 使用,无法交叉编译到 linux 系统的开发板上

交叉编译无法识别这两个类库。

首先 在项目文件中加入多媒体模块,多媒体模块中包含了处理摄像头的类

  • QT += multimedia multimediawidgets

在这里插入图片描述

1> 摄像头信息类 (QCameraInfo)

QCameraInfo类用于查询可用的摄像头设备信息。你可以使用它来获取有关系统上连接的所有摄像头的详细信息(例如,设备名称、分辨率等),然后选择一个摄像头进行使用。

函数说明
QCameraInfo()默认构造函数,不会返回具体信息
QCameraInfo(const QString &deviceName)通过设备名称创建摄像头信息对象
static QList<QCameraInfo> availableCameras()返回可用摄像头的列表
QString deviceName() const获取设备名称
QString description() const获取摄像头描述信息
QCameraInfo::Position position() const获取摄像头位置(前置/后置)
bool isNull() const判断 QCameraInfo是否为空

示例

QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
for (const QCameraInfo &cameraInfo : cameras) {
    qDebug() << "Device Name: " << cameraInfo.deviceName();
    qDebug() << "Description: " << cameraInfo.description();
}

2> 摄像头类 (QCamera)

QCamera 类是 Qt 提供的用于访问和控制摄像头的类,它封装了对摄像头硬件的控制。

函数说明
QCamera(QObject *parent = nullptr)使用默认摄像头创建 QCamera
QCamera(const QCameraInfo &cameraInfo, QObject *parent = nullptr)指定摄像头设备创建 QCamera
void start()启动摄像头
void stop()停止摄像头
void setViewfinder(QAbstractVideoSurface *surface)设置摄像头的视频输出目标
void setCaptureMode(QCamera::CaptureMode mode)设置捕获模式(视频或静态图像)
QCameraImageCapture *imageCapture() const获取 QCameraImageCapture
用于图像捕获
void setFocusMode(QCameraFocus::FocusMode mode)设置摄像头对焦模式
void setExposureMode(QCameraExposure::ExposureMode mode)设置曝光模式

在这里插入图片描述

示例

// 选择第一个可用摄像头并启动
QCameraInfo cameraInfo = QCameraInfo::availableCameras().first();
QCamera *camera = new QCamera(cameraInfo, this);
QCameraViewfinder *viewfinder = new QCameraViewfinder(this);
camera->setViewfinder(viewfinder);//绑定摄像头到显示视图上
camera->start();

3> 摄像头截屏类(QCameraImageCapture)

QCameraImageCapture类用于捕获静态图像,并将其保存或处理。它通常与 QCamera配合使用。

函数说明
QCameraImageCapture(QCamera *camera, QObject *parent = nullptr)创建用于捕获图像的对象
void capture(int id = -1)捕获图像
void setCaptureDestination(QCameraImageCapture::CaptureDestination destination)设置图像捕获的目标位置(内存、文件等)
signals: void imageCaptured(int id, const QImage &preview)捕获图像后触发的信号
QCameraImageCapture::capture(const QString &file = QString())传入要保存文件的路径
  • 注意摄像头捕获图像会触发imageCaptured信号,你可以在这个信号槽函数对图像进一步处理。
QCameraImageCapture *imageCapture = new QCameraImageCapture(camera, this);
connect(imageCapture, &QCameraImageCapture::imageCaptured,
        this, &MainWindow::onImageCaptured);

// 捕获图像
imageCapture->capture();

4> 显示视图类(QCameraViewfinder)

QCameraViewfinder 是用于显示摄像头画面的视图窗口。你将摄像头的输出设置为这个视图窗口,就可以显示摄像头的画面。

函数说明
QCameraViewfinder(QWidget *parent = nullptr)创建视图窗口用于显示摄像头画面
void show()显示视图窗口
void resize(int width, int height)调整视图窗口大小
void setRenderHints(QPainter::RenderHints hints)设置渲染选项(如抗锯齿)

示例:

QCameraViewfinder *viewfinder = new QCameraViewfinder(this);
viewfinder->setGeometry(0, 0, 640, 480);  // 设置视图窗口大小
viewfinder->show();

5> 综合示例

QT 中融入 C 语言摄像头代码

思路:

  • 模仿当初我们硬件控制led灯的代码
  • 把你写的C语言代码封装成C++类添加到你的QT工程中

具体实现方法:

方法一:用定时器实现

方法二:用多线程实现

注意开发板中使用多线程(死循环的),如果无法终止(windows上可以的),解决方法用标志位来控制线程退出

方法一实现:

新建一个 cpp 文件

#ifndef MYCAMERA_H
#define MYCAMERA_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <linux/videodev2.h>  //V4L2的头文件,写代码需要用到这个头文件定义的一些数据类型
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

#define W 640
#define H 480

//自定义结构体把每个缓冲块的首地址和大小存放起来
struct bufmsg
{
    void *start; //存放每个缓冲块的首地址
    int len;  //存放每个缓冲块的实际大小
};

class mycamera
{
public:
    mycamera();
    int camera_init(); //初始化摄像头
    int camera_captrue(); //摄像头出队入队显示画面
    int camera_uninit(); //关闭摄像头
private:
    int camerafd;
    int lcdfd;
    int *lcdmem;
    //定义结构体数组存放四个缓冲块的信息
    struct bufmsg array[4];
    enum v4l2_buf_type mytype;
    struct v4l2_buffer vbuf;
};

#endif // MYCAMERA_H
#include "mycamera.h"

//封装函数把一组YUV转换得到ARGB数据
int yuvtoargb(int y, int u, int v) {
    int r, g, b;
    int pix;

    // 使用转换公式
    r = y + 1.4075 * (v - 128);
    g = y - 0.3455 * (u - 128) - 0.7169 * (v - 128);
    b = y + 1.779 * (u - 128);

    // 修正溢出
    if (r > 255) r = 255; if (r < 0) r = 0;
    if (g > 255) g = 255; if (g < 0) g = 0;
    if (b > 255) b = 255; if (b < 0) b = 0;

    // 组装成 ARGB 格式(Alpha 通道设为 0x00)
    pix = (0x00 << 24) | (r << 16) | (g << 8) | b;
    return pix;
}


int allyuyvtoargb(char *yuyvdata,int *argbdata)
{
    int i,j;
    for(i=0,j=0; i<W*H; i+=2,j+=4)  //循环总次数 W*H/2
    {
        //一组YUYV可以计算得到两组ARGB
        //Y1跟UV配合
        argbdata[i]=yuvtoargb(yuyvdata[j],yuyvdata[j+1],yuyvdata[j+3]);
        //Y2跟UV配合
        argbdata[i+1]=yuvtoargb(yuyvdata[j+2],yuyvdata[j+1],yuyvdata[j+3]);
    }
    return 0;
}

//初始化摄像头
int mycamera::camera_init()
{
    int i;
    int ret;

    //打开液晶屏的驱动
    lcdfd=open("/dev/fb0",O_RDWR);
    if(lcdfd==-1)
    {
        perror("打开液晶屏失败了!\n");
        return -1;
    }

    //映射得到液晶屏的首地址
    lcdmem=(int *)mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
    if(lcdmem==NULL)
    {
        perror("映射液晶屏失败了!\n");
        return -1;
    }

    //打开摄像头的驱动
    camerafd=open("/dev/video7",O_RDWR);
    if(camerafd==-1)
    {
        perror("打开摄像头驱动失败了!\n");
        return -1;
    }

    //提前设置好摄像头的采集格式
    struct v4l2_format vfmt;
    bzero(&vfmt,sizeof(vfmt));
    vfmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vfmt.fmt.pix.width=W;  //摄像头采集画面的宽
    vfmt.fmt.pix.height=H;  //摄像头采集画面的高
    vfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;  //摄像头采集画面的格式
    /*
                                                     V4L2_PIX_FMT_YUYV  表示YUV格式的画面
                                                     V4L2_PIX_FMT_JPEG  表示jpg格式的画面
                                              */
    ret=ioctl(camerafd,VIDIOC_S_FMT,&vfmt);
    if(ret==-1)
    {
        perror("设置摄像头采集格式失败了!\n");
        return -1;
    }

    //给摄像头的驱动发送命令申请缓冲区
    struct v4l2_requestbuffers reqbuf;
    bzero(&reqbuf,sizeof(reqbuf));
    reqbuf.count=4; //申请4个缓冲块
    reqbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory=V4L2_MEMORY_MMAP;

    ret=ioctl(camerafd,VIDIOC_REQBUFS,&reqbuf);
    if(ret==-1)
    {
        perror("申请缓冲区失败了!\n");
        return -1;
    }

    //分配缓冲区给你
    for(i=0; i<4; i++)  //第三步你申请了4个缓冲,因此需要循环四次分配四个不同的缓冲区给你
    {
        bzero(&vbuf,sizeof(vbuf));
        vbuf.index=i; //缓冲块的索引号
        vbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vbuf.memory=V4L2_MEMORY_MMAP;
        ret=ioctl(camerafd,VIDIOC_QUERYBUF,&vbuf);
        if(ret==-1)
        {
            perror("分配缓冲区失败了!\n");
            return -1;
        }
        //顺便映射得到每个缓冲区的首地址--》方便后续我们取出画面操作(通过指针运算搞定)
        array[i].len=vbuf.length; //存放大小
        array[i].start=mmap(NULL,vbuf.length,PROT_READ|PROT_WRITE,MAP_SHARED,camerafd,vbuf.m.offset);
        if(array[i].start==NULL)
        {
            perror("映射缓冲块的首地址失败了!\n");
            return -1;
        }

        //顺便申请画面入队--》跟摄像头说我打算等一会把画面数据往四个内存块里面存放了(先打招呼)
        ret=ioctl(camerafd,VIDIOC_QBUF,&vbuf);
        if(ret==-1)
        {
            perror("申请入队失败了!\n");
            return -1;
        }
    }

    //开始启动摄像头采集画面--》画面马上就有了
    mytype=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret=ioctl(camerafd,VIDIOC_STREAMON,&mytype);
    if(ret==-1)
    {
        perror("启动摄像头开始采集画面失败了!\n");
        return -1;
    }
    return 0;
}

//出队,入队显示画面
int mycamera::camera_captrue()
{
    int i,j;
    int ret;
    //定义数组存放转换得到一帧ARGB数据
    int argbbuf[W*H];

    for(i=0; i<4; i++)
    {
        bzero(&vbuf,sizeof(vbuf));
        vbuf.index=i; //缓冲块的索引号
        vbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vbuf.memory=V4L2_MEMORY_MMAP;
        //出队
        ret=ioctl(camerafd,VIDIOC_DQBUF,&vbuf);
        if(ret==-1)
        {
            perror("申请出队失败了!\n");
            return -1;
        }

        //把出队的画面显示出来
        //YUYV转换成ARGB,液晶屏不可以直接显示YUYV数据
        allyuyvtoargb((char *)(array[i].start),argbbuf);
        for(j=0; j<H; j++)
            memcpy(lcdmem+80+j*800,&argbbuf[j*W],W*4);  //每一次拷贝一行像素点

        //入队
        ret=ioctl(camerafd,VIDIOC_QBUF,&vbuf);
        if(ret==-1)
        {
            perror("申请入队失败了!\n");
            return -1;
        }
    }
    return 0;
}
//关闭摄像头
int mycamera::camera_uninit()
{
    int i,ret;
    //收尾
    ret=ioctl(camerafd,VIDIOC_STREAMOFF,&mytype);
    if(ret==-1)
    {
        perror("关闭摄像头失败了!\n");
        return -1;
    }
    close(camerafd);
    close(lcdfd);
    munmap(lcdmem,800*480*4);
    for(i=0; i<4; i++)
        munmap(array[i].start,array[i].len);
    return 0;
}


mycamera::mycamera() {}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "mycamera.h"
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();
    void fun();

    void on_pushButton_2_clicked();

private:
    Ui::MainWindow *ui;
    QTimer *timer;
    mycamera *yourcamera;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //创建摄像头对象
    yourcamera = new mycamera();
    //初始化摄像头
    yourcamera->camera_init();
    //创建定时器对象
    timer = new QTimer(this);
    //设置定时器间隔时间
    timer->setInterval(10);
    //关联信号
    connect(timer,SIGNAL(timeout()),this,SLOT(fun()));
}

MainWindow::~MainWindow()
{
    delete ui;
}
//启动摄像头
void MainWindow::on_pushButton_clicked()
{
    timer->start();
}

//槽函数
void MainWindow::fun()
{
    yourcamera->camera_captrue();
}


void MainWindow::on_pushButton_2_clicked()
{
    timer->stop();
}

在这里插入图片描述

方法二实现:

mycamera.h 里继承 QThread 类

mycamea.cpp

定一个标志位控制线程退出

int flag=1; //用来控制线程结束的标志位

在这里插入图片描述

mainwindow.h去掉定时器相关的函数即可

#include "mainwindow.h"
#include "ui_mainwindow.h"
extern int flag; //声明外部全局变量
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //创建摄像头对象
    yourcamera = new mycamera();
    //初始化摄像头
    yourcamera->camera_init();
}

MainWindow::~MainWindow()
{
    //关闭摄像头
    yourcamera->camera_uninit();
    delete ui;
}
//启动摄像头
void MainWindow::on_pushButton_clicked()
{
    flag=1;
    //start方法开启一个线程并自动执行run
    yourcamera->start();
}




void MainWindow::on_pushButton_2_clicked()
{
    //终止线程--》开发板采用这种方法无法终止线程
    //yourcamera.terminate();

    //修改标志位flag,把flag改成0
    flag=0;
    //回收线程
    yourcamera->wait();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值