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