我的编程空间,编程开发者的网络收藏夹
学习永远不晚

QT获取Android、Linux、Windows系统上的摄像头数据帧并处理显示

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

QT获取Android、Linux、Windows系统上的摄像头数据帧并处理显示

一、操作系统介绍

Linux系统:  ubuntu18.04 64位

Android系统:  Android 8.1/9.0

windows系统:  win10

QT版本:  5.12

二、需求介绍

使用QT本身代码在linux平台、Android平台、windows平台实时获取摄像头每一帧数据,进行处理再进行显示。

比如:  捕获数据之后传递给opencv实现图像识别、传递给ffmpeg实现MP4保存录制、或者实现rtsp实时推流。

如果仅仅是显示,不处理就很简单,这里介绍的方式是截取摄像头的原始数据。比如: YUYV、NV21格式。

三、QT获取摄像头数据需要用到的一些头文件

使用QT读取摄像头数据需要用到以下相关头文件:


#include 
#include 
#include 
#include 
#include 

在pro工程文件还需要加上:


QT += multimedia

四、QT获取摄像头的几种方式介绍

第一种方式:  

子类化QAbstractVideoSurface,重写present函数这种方法获取视频帧。

只要摄像头捕获到数据,就会调用Present函数,在Present函数函数就可以得到摄像头捕获的每一帧数据,非常适合做图像处理等操作。

经过测试这种方法在Android平台上无法使用,在windows和ubuntu平台都可以正常使用。


#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    QCamera * camera;
    QTimer *timer;
    QCameraViewfinder * view_finder; //取景器
    QList cameras;      //存放系统支持的摄像头列表
    QCameraImageCapture* camera_image_capture;
    QImageEncoderSettings iamge_setting;
private slots:
    void on_pushButton_open_camera_clicked();
    void on_pushButton_Camear_up_clicked();
    void Camear_handleFrame(QImage image);
private:
    Ui::MainWindow *ui;
};
class CameraFrameGrabber :public QAbstractVideoSurface
{
    Q_OBJECT
public:
    CameraFrameGrabber(QObject *parent = nullptr);
    QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const override;
    bool present(const QVideoFrame &frame) override;
signals:
    void frameAvailable(QImage frame);
public slots:
};
#endif // MAINWINDOW_H
2.2.2 mainwindow.cpp代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    cameras = QCameraInfo::availableCameras();
    if(cameras.count())
    {
       for(int i=0;icomboBox->addItem(tr("%1").arg(i));
       }
    }
    else
    {
       QMessageBox::warning(this,tr("提示"),"本机没有可用的摄像头!\n"
                                                "软件作者:DS小龙哥\n"
                                                "BUG反馈:1126626497@qq.com");
    }
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::Camear_handleFrame(QImage image)
{
    QImage image1=image.copy();
    ui->label_ImageDisplay->setPixmap(QPixmap::fromImage(image1));
}
void MainWindow::on_pushButton_open_camera_clicked()
{
    int i=ui->comboBox->currentText().toInt();
    
    camera = new QCamera(cameras.at(i));
    CameraFrameGrabber *_cameraFrameGrabber = new CameraFrameGrabber(this);
    camera->setViewfinder(_cameraFrameGrabber);
    connect(_cameraFrameGrabber, SIGNAL(frameAvailable(QImage)), this, SLOT(Camear_handleFrame(QImage)));
    camera->start();
}
void MainWindow::on_pushButton_Camear_up_clicked()
{
}
CameraFrameGrabber::CameraFrameGrabber(QObject *parent) :
    QAbstractVideoSurface(parent)
{
}
QList CameraFrameGrabber::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
    if (handleType == QAbstractVideoBuffer::NoHandle)
    {
        qDebug()<<"无效格式1."<<QList()<< QVideoFrame::Format_RGB32<< QVideoFrame::Format_ARGB32<< QVideoFrame::Format_ARGB32_Premultiplied<< QVideoFrame::Format_RGB565<< QVideoFrame::Format_RGB555;
        return QList()<< QVideoFrame::Format_RGB32<< QVideoFrame::Format_ARGB32<< QVideoFrame::Format_ARGB32_Premultiplied<< QVideoFrame::Format_RGB565<< QVideoFrame::Format_RGB555;
           
       }
       else
       {
          qDebug()<<"无效格式2."<<QList();
          return  QList();
       }
}
bool CameraFrameGrabber::present(const QVideoFrame &frame)
{
    if (frame.isValid()) {
        QVideoFrame cloneFrame(frame);
        cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
        //qDebug()<<"width="<<cloneFrame.width();
        //qDebug()<<"height="<<cloneFrame.height();
       // qDebug()<<"pixelFormat="<<cloneFrame.pixelFormat();
        const QImage image(cloneFrame.bits(),
                           cloneFrame.width(),
                           cloneFrame.height(),
                           QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
        emit frameAvailable(image);
        cloneFrame.unmap();
        return true;
    }
    return false;
}

第二种方式:

使用QVideoProbe类捕获视频帧

QvideoProbe适合所有平台使用。下面代码在Android、ubuntu、windows系统上测试成功显示。

在window和ubuntu系统代码设置摄像头输出的数据格式是YUYV,在我的Android平板上只能输出NV21格式,下面代码里编写了NV21、YUYUV转RGB24的代码。

  mainwindow.cpp代码


#include "mainwindow.h"
#include "ui_mainwindow.h"
//#define ANDROID_DEVICE
#ifdef ANDROID_DEVICE
//设置保存文件的路径
#define SAVE_FILE_PATH "/sdcard/DCIM/Camera/"
#else
//设置保存文件的路径
#define SAVE_FILE_PATH "./"
#endif

void MainWindow::SetStyle(const QString &qssFile) {
    QFile file(qssFile);
    if (file.open(QFile::ReadOnly)) {
        QString qss = QLatin1String(file.readAll());
        qApp->setStyleSheet(qss);
        QString PaletteColor = qss.mid(20,7);
        qApp->setPalette(QPalette(QColor(PaletteColor)));
        file.close();
    }
    else
    {
        qApp->setStyleSheet("");
    }
}
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->SetStyle(":/images/blue.css");     //设置样式表
    this->setWindowIcon(QIcon(":/log.ico")); //设置图标
    this->setWindowTitle("照相机");
    //获取本机可用摄像头
    cameras = QCameraInfo::availableCameras();
       if(cameras.count())
       {
           for(int i=0;icomboBox->addItem(tr("%1").arg(i));
           }
       }
       else
       {
           QMessageBox::warning(this,tr("提示"),"本机没有可用的摄像头!\n"
                                                    "软件作者:DS小龙哥\n"
                                                    "BUG反馈:1126626497@qq.com");
       }
       ui->pushButton_stop->setEnabled(false); //设置停止按钮不可用
       ui->pushButton_Camear_up->setEnabled(false);
       //创建工作目录
   #ifdef ANDROID_DEVICE
       QDir dir;
       if(!dir.exists("/sdcard/DCIM/Camera/"))
       {
           if(dir.mkpath("/sdcard/DCIM/Camera/"))
           {
               Log_Display("/sdcard/DCIM/Camera/目录创建成功.\n");
           }
           else
           {
               Log_Display("/sdcard/DCIM/Camera/目录创建失败.\n");
           }
       }
   #endif
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::Log_Display(QString text)
{
    ui->plainTextEdit->insertPlainText(text);
}
void MainWindow::on_pushButton_open_camera_clicked()
{
    int i=ui->comboBox->currentText().toInt();
    
    camera = new QCamera(cameras.at(i));
    m_pProbe = new QVideoProbe(this);
    if(m_pProbe != nullptr)
    {
        m_pProbe->setSource(camera); // Returns true, hopefully.
        connect(m_pProbe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(slotOnProbeFrame(QVideoFrame)), Qt::DirectConnection);
    }
    
    //camera->setCaptureMode(QCamera::CaptureStillImage);  //如果在Linux系统下运行就这样设置
     camera->setCaptureMode(QCamera::CaptureVideo);//如果在android系统下运行就这样设置
    
    camera->start();
    
    QCameraViewfinderSettings settings;
    settings.setPixelFormat(QVideoFrame::Format_YUYV); //设置像素格式  Android上只支持NV21格式
    settings.setResolution(QSize(640, 480)); //设置摄像头的分辨率
    camera->setViewfinderSettings(settings);
    //获取摄像头支持的分辨率、帧率等参数
    //获取摄像头支持的分辨率、帧率等参数
    
void MainWindow::slotOnProbeFrame(const QVideoFrame &frame)
{
   QVideoFrame cloneFrame(frame);
   cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
   //qDebug()<<"height:"<<cloneFrame.height();
   //qDebug()<<"width:"<<cloneFrame.width();
   //qDebug()<<"bytesPerLine:"<<cloneFrame.bytesPerLine();
   //qDebug()<<"mappedBytes:"<<cloneFrame.mappedBytes();
   qDebug()<<"pixelFormat:"<label_ImageDisplay->setPixmap(my_pixmap);
   }
   else if(cloneFrame.pixelFormat()==QVideoFrame::Format_YUYV)
   {
       unsigned char rgb_buffer[640*480*3];
       yuyv_to_rgb(cloneFrame.bits(),rgb_buffer,cloneFrame.width(),cloneFrame.height());
       const QImage image(rgb_buffer,
                          cloneFrame.width(),
                          cloneFrame.height(),
                          QImage::Format_RGB888);
       QPixmap my_pixmap;
       my_pixmap.convertFromImage(image);
       ui->label_ImageDisplay->setPixmap(my_pixmap);
   }
   else
   {
       Log_Display(tr("当前格式编码为%1,暂时不支持转换.\n").arg(cloneFrame.pixelFormat()));
   }
    cloneFrame.unmap();
}
void MainWindow::on_pushButton_Camear_up_clicked()
{
   const QPixmap pix=ui->label_ImageDisplay->pixmap()->copy();
   QDateTime dateTime(QDateTime::currentDateTime());
   //时间效果: 2020-03-05 16:25::04 周四
   QString qStr="";
   qStr+=SAVE_FILE_PATH;  //Android 手机的照相机文件夹
   qStr+=dateTime.toString("yyyy-MM-dd-hh-mm-ss");
   qStr+=".jpg";
   pix.save(qStr);
   Log_Display(tr("照片保存路径:%1\n").arg(qStr));
}
void MainWindow::YUV420P_to_RGB24(unsigned char *data, unsigned char *rgb, int width, int height)
{
    int index = 0;
    unsigned char *ybase = data;
    unsigned char *ubase = &data[width * height];
    unsigned char *vbase = &data[width * height * 5 / 4];
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            //YYYYYYYYUUVV
            unsigned char Y = ybase[x + y * width];
            unsigned char U = ubase[y / 2 * width / 2 + (x / 2)];
            unsigned char V = vbase[y / 2 * width / 2 + (x / 2)];
            rgb[index++] = Y + 1.402 * (V - 128); //R
            rgb[index++] = Y - 0.34413 * (U - 128) - 0.71414 * (V - 128); //G
            rgb[index++] = Y + 1.772 * (U - 128); //B
        }
    }
}

void MainWindow::NV21_TO_RGB24(unsigned char *yuyv, unsigned char *rgb, int width, int height)
{
        const int nv_start = width * height ;
        int  index = 0, rgb_index = 0;
        uint8_t y, u, v;
        int r, g, b, nv_index = 0,i, j;
        for(i = 0; i < height; i++){
            for(j = 0; j  255)   r = 255;
                if(g > 255)   g = 255;
                if(b > 255)   b = 255;
                if(r < 0)     r = 0;
                if(g < 0)     g = 0;
                if(b < 0)     b = 0;
                index = rgb_index % width + (height - i - 1) * width;
                //rgb[index * 3+0] = b;
                //rgb[index * 3+1] = g;
                //rgb[index * 3+2] = r;
                //颠倒图像
                //rgb[height * width * 3 - i * width * 3 - 3 * j - 1] = b;
                //rgb[height * width * 3 - i * width * 3 - 3 * j - 2] = g;
                //rgb[height * width * 3 - i * width * 3 - 3 * j - 3] = r;
                //正面图像
                rgb[i * width * 3 + 3 * j + 0] = b;
                rgb[i * width * 3 + 3 * j + 1] = g;
                rgb[i * width * 3 + 3 * j + 2] = r;
                rgb_index++;
            }
        }
}
void MainWindow::YUV420P_TO_RGB24(unsigned char *yuv420p, unsigned char *rgb24, int width, int height) {
    int index = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x stop();
    delete  camera;
    delete m_pProbe;
    ui->pushButton_open_camera->setEnabled(true);
    ui->pushButton_stop->setEnabled(false); //设置停止按钮不可用
    ui->pushButton_Camear_up->setEnabled(false);
}

void MainWindow::yuyv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
{
    int x;
    int z=0;
    unsigned char *ptr = rgb_buffer;
    unsigned char *yuyv= yuv_buffer;
    for (x = 0; x < iWidth*iHeight; x++)
    {
        int r, g, b;
        int y, u, v;
        if (!z)
        y = yuyv[0] << 8;
        else
        y = yuyv[2] <> 8;
        g = (y - (88 * u) - (183 * v)) >> 8;
        b = (y + (454 * u)) >> 8;
        *(ptr++) = (r > 255) ? 255 : ((r  255) ? 255 : ((g  255) ? 255 : ((b < 0) ? 0 : b);
        if(z++)
        {
            z = 0;
            yuyv += 4;
        }
    }
}

mainwindow.h代码


#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
     void SetStyle(const QString &qssFile);
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    QCamera * camera;
    QTimer *timer;
    QCameraViewfinder * view_finder; //取景器
    QList cameras;      //存放系统支持的摄像头列表
    QCameraImageCapture* camera_image_capture;
    QImageEncoderSettings iamge_setting;
    QVideoProbe *m_pProbe;
    void Log_Display(QString text);
    void YUV420P_to_RGB24(unsigned char *data, unsigned char *rgb, int width, int height);
    void NV21_TO_RGB24(unsigned char *data, unsigned char *rgb, int width, int height);
    void YUV420P_TO_RGB24(unsigned char *yuv420p, unsigned char *rgb24, int width, int height);
    void yuyv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight);
private slots:
    void slotOnProbeFrame(const QVideoFrame &frame);
    void on_pushButton_open_camera_clicked();
    void on_pushButton_Camear_up_clicked();
    void on_pushButton_stop_clicked();
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

UI界面:

 

下面公众号里有全套QT、C、C++基础学习教程:


作者:DS小龙哥


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

QT获取Android、Linux、Windows系统上的摄像头数据帧并处理显示

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

QT获取Android、Linux、Windows系统上的摄像头数据帧并处理显示

一、操作系统介绍 Linux系统:  ubuntu18.04 64位 Android系统:  Android 8.1/9.0 windows系统:  win10 QT版本:  5.12 二、需求介绍 使用QT本身代码在linux平台、And
2022-06-06

编程热搜

目录