【Qt从摄像头视频中获取数据】
有时候需要在视频上画图,所以需要能获取到每一帧视频数据。
以前从视频文件或视频流中得到帧,一般都是使用qt + ffmpeg或qt + vlc。
qt对显示处理视频大体有以下方法:
- QMediaPlayer + QVideoWidget
这种方法只适合简单的显示视频功能,不适合对视频进行处理(比如画图)
- QMediaPlayer + QGraphicsVideoItem + QGraphicsScene + QGraphicsView
这种方法功能强大,除了显示视频功能,还可以做复杂的图形处理(具体可以查看QGraphicsScene的使用)
- QMediaPlayer + QAbstractVideoSurface
这种方法比较简单,是我下面要介给的。可以获取到每一帧视频数据,基本可以实现与qt + ffmpeg或qt + vlc相同的效果。
自定义VideoSurface:
class VideoSurface : public QAbstractVideoSurface
{Q_OBJECTpublic:VideoSurface(QObject *parent = Q_NULLPTR);~VideoSurface();QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;bool present(const QVideoFrame &frame);signals:void frameAvailable(QVideoFrame &frame);};
实现如下:
VideoSurface::VideoSurface(QObject *parent): QAbstractVideoSurface(parent)
{
}VideoSurface::~VideoSurface()
{
}QList<QVideoFrame::PixelFormat> VideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{QList<QVideoFrame::PixelFormat> listPixelFormats;listPixelFormats << QVideoFrame::Format_ARGB32<< QVideoFrame::Format_ARGB32_Premultiplied<< QVideoFrame::Format_RGB32<< QVideoFrame::Format_RGB24<< QVideoFrame::Format_RGB565<< QVideoFrame::Format_RGB555<< QVideoFrame::Format_ARGB8565_Premultiplied<< QVideoFrame::Format_BGRA32<< QVideoFrame::Format_BGRA32_Premultiplied<< QVideoFrame::Format_BGR32<< QVideoFrame::Format_BGR24<< QVideoFrame::Format_BGR565<< QVideoFrame::Format_BGR555<< QVideoFrame::Format_BGRA5658_Premultiplied<< QVideoFrame::Format_AYUV444<< QVideoFrame::Format_AYUV444_Premultiplied<< QVideoFrame::Format_YUV444<< QVideoFrame::Format_YUV420P<< QVideoFrame::Format_YV12<< QVideoFrame::Format_UYVY<< QVideoFrame::Format_YUYV<< QVideoFrame::Format_NV12<< QVideoFrame::Format_NV21<< QVideoFrame::Format_IMC1<< QVideoFrame::Format_IMC2<< QVideoFrame::Format_IMC3<< QVideoFrame::Format_IMC4<< QVideoFrame::Format_Y8<< QVideoFrame::Format_Y16<< QVideoFrame::Format_Jpeg<< QVideoFrame::Format_CameraRaw<< QVideoFrame::Format_AdobeDng;//qDebug() << listPixelFormats;// Return the formats you will supportreturn listPixelFormats;
}bool VideoSurface::present(const QVideoFrame &frame)
{// Handle the frame and do your processingif (frame.isValid()){QVideoFrame cloneFrame(frame);emit frameAvailable(cloneFrame);return true;}return false;
}
看了上面的代码就知道,只需要外部连接frameAvailable信号就可以获取到每一帧数据。
具体使用:
QMediaPlayer *mediaPlayer = new QMediaPlayer;
VideoSurface *videoSurface = new VideoSurface;
mediaPlayer->setVideoOutput(videoSurface);
mediaPlayer->setMedia(QUrl("rtsp://admin:admin@192.168.1.112"));
mediaPlayer->play();
把frameAvailable信号与显示窗口的槽连接,比如:
connect(videoSurface, SIGNAL(frameAvailable(QVideoFrame &)), this, SLOT(ProcessFrame(QVideoFrame &)));
void QtVideoTest::ProcessFrame(QVideoFrame &frame)
{qDebug() << "=============ProcessFrame===============";qDebug() << "width : " << frame.width() << " height : " << frame.height();qDebug() << "start time : " << frame.startTime()/1000 << "ms";qDebug() << "end time : " << frame.endTime()/1000 << "ms";qDebug() << "pixelFormat :" << frame.pixelFormat();frame.map(QAbstractVideoBuffer::ReadOnly);QImage recvImage(frame.bits(), frame.width(), frame.height(), QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat()));qDebug() << "frame data size :" << frame.mappedBytes();frame.unmap();
}
如上,QVideoFrame转QImage,拿QImage进行画图操作就简单了。
Qt6.2.3获取摄像头录制YUV文件,逐帧获取录制,使用QVideoSink
Qt6.2.3获取摄像头录制YUV文件,逐帧获取录制,使用QVideoSink
必须要使用到QVideoSink,相应的依赖模块有:QMediaCaptureSession、QMediaDevices、QCamera、QVideoFrame
具体的使用方法是:
1、创建一个QMediaCaptureSession:
QMediaCaptureSession m_captureSession;
2、设置摄像头:
m_captureSession.setCamera(new QCamera(QMediaDevices::defaultVideoInput()));
3、设置YUV逐帧输出:
m_captureSession.setVideoSink(new QVideoSink m_videoSink);
4、设置YUV格式:
void QVideoSink::setVideoFrame(const QVideoFrame &frame)
5、主动获取一帧YUV数据:
QVideoFrame QVideoSink::videoFrame() const
6、被动接收每一帧:
connect(m_videoSink, &QVideoSink::videoFrameChanged, YourClass, yourParseFunction);
7、获取帧数据:
videoFrame.map(QVideoFrame::ReadOnly);
m_file.write((const char *)videoFrame.bits(0和1), videoFrame.mappedBytes(0和1));
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCameraFormat>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);m_camera = new QCamera(QMediaDevices::defaultVideoInput());m_captureSession.setCamera(m_camera);m_captureSession.setVideoSink(&m_videoSink);connect(&m_videoSink, &QVideoSink::videoFrameChanged, this, &MainWindow::on_frame_changed);m_camera->start();m_file.setFileName("test.nv12");m_file.open(QIODevice::WriteOnly | QIODevice::Truncate);
}MainWindow::~MainWindow()
{m_camera->stop();m_file.close();delete ui;
}void MainWindow::on_frame_changed(const QVideoFrame &frame)
{QVideoFrame videoFrame = frame;/*** 我的电脑上返回的格式是Format_NV12:18,每个像素1.5字节,一帧1382400字节,* 如果你需要用别的YUV或者RGB格式,则需要自己设置QVideoSink或者QCamera格式*/QVideoFrameFormat::PixelFormat pixelFormat = videoFrame.pixelFormat();int width = videoFrame.width();int height = videoFrame.height();int planeCount = videoFrame.planeCount();uchar *pdata = nullptr;int len = 0;videoFrame.map(QVideoFrame::ReadOnly);for (int i = 0; i < planeCount; i++) {pdata = videoFrame.bits(i);len = videoFrame.mappedBytes(i);m_file.write((const char *)pdata, len);}frame_num++;qDebug("%d, %d, %d, %d, %d, %p, %d", frame_num,pixelFormat, width, height, planeCount, pdata, len);videoFrame.unmap();
}
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QCamera>
#include <QMediaCaptureSession>
#include <QMediaDevices>
#include <QVideoSink>
#include <QVideoFrame>
#include <QFile>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;QMediaCaptureSession m_captureSession;QVideoSink m_videoSink;QCamera *m_camera;QFile m_file;int frame_num = 0;private slots:void on_frame_changed(const QVideoFrame &frame); // 一帧视频到来的信号
};
#endif // MAINWINDOW_H
main.cpp:
/*** \brief Qt6.2.3要逐帧获取YUV需要使用QVideoSink和QMediaCaptureSession* \details 编译运行后,窗口弹出即开始录制YUV文件,关闭窗口后文件保存* \note 保存的文件在本工程文件夹上级目录中的debug*目录下的test.nv12* 我的电脑摄像头默认输出是YUV NV12格式1280 * 720* 播放方式是安装ffmpeg软件后,在test.nv12文件目录下使用命令:* ffplay -f rawvideo -pix_fmt nv12 -video_size 1280x720 test.nv12*/#include "mainwindow.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}
005_yuv_input_save_file.pro:
QT += core gui
QT += multimediagreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++17# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \mainwindow.cppHEADERS += \mainwindow.hFORMS += \mainwindow.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
mainwindow.ui:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>MainWindow</class><widget class="QMainWindow" name="MainWindow"><property name="geometry"><rect><x>0</x><y>0</y><width>800</width><height>600</height></rect></property><property name="windowTitle"><string>MainWindow</string></property><widget class="QWidget" name="centralwidget"><layout class="QVBoxLayout" name="verticalLayout"><item><widget class="QPushButton" name="pushButton"><property name="text"><string>窗口不要开太久!开几秒就够了,否则存储的YUV文件会把你的硬盘塞满!</string></property></widget></item></layout></widget><widget class="QMenuBar" name="menubar"><property name="geometry"><rect><x>0</x><y>0</y><width>800</width><height>22</height></rect></property></widget><widget class="QStatusBar" name="statusbar"/></widget><resources/><connections/>
</ui>
报错:`QVideoFrame::unmap() was called more times then QVideoFrame::map()
原因:格式对不上QVideoFrame转QImage格式对照表`
QVideoFrame Class
参考:QT——QCamera摄像头的切换、分辨率切换、截图显示
cv::QVideoFrame转QImage失败原因及解决方案
相关文章:
【Qt从摄像头视频中获取数据】
有时候需要在视频上画图,所以需要能获取到每一帧视频数据。 以前从视频文件或视频流中得到帧,一般都是使用qt ffmpeg或qt vlc。 qt对显示处理视频大体有以下方法: QMediaPlayer QVideoWidget 这种方法只适合简单的显示视频功能ÿ…...
视频截取中的UI小组件
引言 视频截取在社交类 APP 中十分常见。有了上传视频的功能,就不可避免地需要提供截取和编辑的选项。如果我们过度依赖第三方库,项目的代码可能会变得异常臃肿,因为这些库往往包含许多我们用不到的功能,而且它们的 UI 样式和功能…...
java设计模式--结构型模式
结构性模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式 适配器模式 适配器模式(Adapter Pattern) 充当两个不兼容接口之间的桥梁,属于结构型设计模式。目的是将一个类的接口转换为另一个接口&am…...
消息中间件:Kafka消息丢失与堆积问题分析与解决方案
消息中间件:Kafka消息丢失与堆积问题分析与解决方案 Kafka作为分布式消息系统,广泛应用于实时数据流处理、大数据分析等领域。然而,在实际应用中,Kafka可能会面临消息丢失和消息堆积的问题,这些问题如果得不到有效处理…...
mac终端代理配置指南
终端代理配置指南 在 macOS 中,你可以通过几种不同的方法来配置终端代理。这里介绍两种常见的设置方式:使用 alias 和 shell 函数。 方法 1:使用 Alias 配置代理 打开终端配置文件 默认情况下,macOS 终端使用的是 zsh。如果你的系…...
mbedTLS生成客户端,服务端密钥及CA证书
1. mbedTLS源码:https://github.com/Mbed-TLS/mbedtls.git 2. 生成步骤: 2.1 编译上述源码 2.2 生成CA私钥和自签名证书: 进入编译的build目录,比如:/mbedtls-development/build# 2.2.1生成CA私钥 执行下面的命令&…...
如何有效应对突发技术故障:以网易云音乐为例
引言 在互联网行业,任何一个在线服务都可能遭遇突发的技术故障。这些故障不仅影响用户体验,还可能对公司的品牌形象造成损害。因此,如何快速响应并高效解决这些问题成为了每一个开发团队的重要课题。本文将以网易云音乐在2024年8月19日下午遭…...
上传文件到github仓库
REF: https://blog.csdn.net/litianxiang_kaola/article/details/74075151 已有repository,往仓库里更新内容 点击gitlab里的clone 在git bash中使用git clone,这个时候会将网上的仓库下载到本地,你可以把想要更新的内容直接拖到仓库里 …...
clip-path实现图片边角的裁剪
img {clip-path: polygon(0 7px,7px 0,calc(100% - 20px) 0,100% 20px,100% 100%,16px 100%,0 calc(100% - 16px));}每一个逗号隔开的就是路径坐标 左上角的两个点 0 7px ,7px 0 右上角 calc(100% - 20px) 0,100% 20px 相当于通过这些点练成的线的圈起来的部分就是剩…...
【C++ Primer Plus习题】2.7
问题: 解答: #include <iostream> using namespace std;void print(int hour, int minute) {cout << "Time:" << hour << ":" << minute << endl; }int main() {int hour0;int minute 0;cout << "请输入…...
uboot中 fastboot udp 协议分析
注: 1. 本文所分析的fastboot源码不是android下的源码,而是恩智浦芯片厂商在IMX6UL芯片的uboot源码中自己实现的源码,二者不同,请读者注意区分。一些图片是网上找到的,出处不好注明,请见谅。 2. 分析fastbo…...
redis hash类型的命令
1.hset 格式: hset key field value [field value ...](value是字符串) 返回值:设置成功的键值对的个数 2.hget:获取键值对 格式:hget key field 3.hexists:判断hash中是否存在指定 格式:…...
【OpenCV】 中使用 Lucas-Kanade 光流进行对象跟踪和路径映射
文章目录 一、说明二、什么是Lucas-Kanade 方法三、Lucas-Kanade 原理四、代码实现4.1 第 1 步:用户在第一帧绘制一个矩形4.2 第 2 步:从图像中提取关键点4.3 第 3 步:跟踪每一帧的关键点 一、说明 本文针对基于光流法的目标追踪进行叙述&am…...
ES 支持乐观锁吗?如何实现的?
本篇主要介绍一下Elasticsearch的并发控制和乐观锁的实现原理,列举常见的电商场景,关系型数据库的并发控制、ES的并发控制实践。 并发场景 不论是关系型数据库的应用,还是使用Elasticsearch做搜索加速的场景,只要有数据更新&…...
前端宝典十一:前端工程化稳定性方案
一、工程化体系介绍 1、什么是前端工程化 前端工程化 前端 软件工程;前端工程化 将工程方法系统化地应用到前端开发中;前端工程化 系统、严谨、可量化的方法开发、运营和维护前端应用程序;前端工程化 基于业务诉求,梳理出最…...
yum 数据源的切换
本来准备安装一个ntp 服务器时间进行同步,但是使用yum install ntp -y 但是却失败了 原因是yum自带的镜像源不能用了,所以要想使用yum 多功能只能切换yum 对应的镜像源了 如果你的服务商是可以使用wget命令的: wget -O /etc/yum.repos.d/Ce…...
MySQL入门学习-命令行工具.mysqlbinlog
MySQL 命令行工具mysqlbinlog用于处理二进制日志文件。 一、关于mysqlbinlog工具的详细介绍: 1、命令行工具mysqlbinlog的特点和使用方法: - 特点: - 可以解析和查看二进制日志文件的内容。 - 支持多种输出格式,如文本、SQ…...
WARNING XXX is not overriding the create method in batch
WARNING XXX is not overriding the create method in batch api.modeldef create(self, vals):quvals[name]youqu self.env[crm.qu].sudo().search([(name, , qu),(shi_id,,vals[shi_id])])if len(youqu)>0:raise UserError(_("该区名已存在,无需再填加…...
使用预训练的 ONNX 格式的目标检测模型(基于 YOLOv8n-pose)姿态监测
具体步骤如下: 加载图像: 从指定路径读取一张图像(这里假设图像名为bus.jpg)。将图像从 BGR 颜色空间转换为 RGB 颜色空间。 图像预处理: 计算图像的高度、宽度,并确定其中的最大值作为新图像的边长。创建一…...
matlab实现模拟退火算法
模拟退火算法(Simulated Annealing, SA)是一种通用概率优化算法,用于在给定的大搜索空间内寻找问题的近似全局最优解。该算法灵感来源于物理学中固体物质的退火过程,其中温度逐渐降低,粒子逐渐趋于能量最低状态。 在M…...
AI 术语通俗词典:矩阵乘法
矩阵乘法是线性代数、数据分析、机器学习和人工智能中非常核心的一个术语。它用来描述两组二维数值结构之间的一种特定运算规则。这个运算结果仍然是一个矩阵,但它并不是简单地把对应位置的元素相乘,而是通过“行与列”的组合来生成新的数值。如果说矩阵…...
OpenClaw飞书机器人配置:Phi-3-mini-128k-instruct对话式任务触发
OpenClaw飞书机器人配置:Phi-3-mini-128k-instruct对话式任务触发 1. 为什么选择飞书OpenClawPhi-3的组合? 去年团队规模扩张到15人时,我突然发现每天要花2小时处理各种琐碎请求:"下周会议材料准备好了吗?"…...
爬虫对抗实战 - ZLibrary 反爬机制分析与突破
一、背景介绍1. 爬虫与反爬的永恒博弈网络爬虫的核心原理是通过程序模拟 HTTP/HTTPS 请求,获取网页数据并解析提取,广泛应用于数据采集、搜索引擎索引、数据分析等场景。网站部署反爬措施的核心必要性:保护服务器资源,避免恶意爬虫…...
“16QAM调制与解调系统的SystemView仿真及分析”
通信原理 systemview 16QAM调制与解调系统的仿真 16QAM调制解调系统与解调系统的仿真 用SystemView建立一个16QAM调制解调器电路,分析理解系统的各个模块功能,观察波形图 判断是不是实现了16QAM调制解调系统功能 基本要求: (1)在SystemView软 件中构建短波16QAM仿真…...
OBS屏幕录制全攻略:从零开始轻松上手
1. OBS屏幕录制入门指南 第一次接触OBS的朋友可能会被它复杂的界面吓到,但其实它的核心功能非常简单。我刚开始用OBS时也走了不少弯路,现在就把这些经验分享给大家。OBS Studio(Open Broadcaster Software)是一款开源免费的屏幕录…...
2026届最火的六大降AI率平台实测分析
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 要让AIGC(人工智能生成内容)检测率降低,关键之处便在于把…...
毕业党速看:这款 AI 论文神器太疯狂,输入标题直接生成万字长文
赶 due 党、论文特困生直接狂喜!谁懂啊家人们,以前写论文从选题到憋出万字初稿,至少得熬半个月,现在输入一个论文标题,短短 20 分钟就能自动生成结构完整、逻辑通顺、带真实参考文献的万字长文,从摘要、引言…...
新手福音:在快马平台通过生成式提示零基础学懂lstm情感分析
今天想和大家分享一个特别适合深度学习新手的实践项目——用LSTM做文本情感分析。作为一个刚入门NLP的小白,我最初看到"长短期记忆网络"这个词就头大,直到在InsCode(快马)平台上通过生成式提示直接获得了可运行的代码项目,才真正理…...
终极宝可梦随机化指南:Universal Pokemon Randomizer ZX 完全使用教程
终极宝可梦随机化指南:Universal Pokemon Randomizer ZX 完全使用教程 【免费下载链接】universal-pokemon-randomizer-zx Public repository of source code for the Universal Pokemon Randomizer ZX 项目地址: https://gitcode.com/gh_mirrors/un/universal-po…...
失业期PHP程序员玻璃心,伪勤奋,固守旧认知的庖丁解牛
“失业期 PHP 程序员的玻璃心、伪勤奋、固守旧认知”,这三者并非独立的性格缺陷,而是一套互为因果的“心理防御闭环”。 它们共同构成了一个**“认知牢笼”**:因为害怕面对真实的残酷(玻璃心),所以用低价值…...
