当前位置: 首页 > news >正文

【Qt从摄像头视频中获取数据】

有时候需要在视频上画图,所以需要能获取到每一帧视频数据。

以前从视频文件或视频流中得到帧,一般都是使用qt + ffmpeg或qt + vlc。

qt对显示处理视频大体有以下方法:

  1. QMediaPlayer + QVideoWidget

这种方法只适合简单的显示视频功能,不适合对视频进行处理(比如画图)

  1. QMediaPlayer + QGraphicsVideoItem + QGraphicsScene + QGraphicsView

这种方法功能强大,除了显示视频功能,还可以做复杂的图形处理(具体可以查看QGraphicsScene的使用)

  1. 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从摄像头视频中获取数据】

有时候需要在视频上画图&#xff0c;所以需要能获取到每一帧视频数据。 以前从视频文件或视频流中得到帧&#xff0c;一般都是使用qt ffmpeg或qt vlc。 qt对显示处理视频大体有以下方法&#xff1a; QMediaPlayer QVideoWidget 这种方法只适合简单的显示视频功能&#xff…...

视频截取中的UI小组件

引言 视频截取在社交类 APP 中十分常见。有了上传视频的功能&#xff0c;就不可避免地需要提供截取和编辑的选项。如果我们过度依赖第三方库&#xff0c;项目的代码可能会变得异常臃肿&#xff0c;因为这些库往往包含许多我们用不到的功能&#xff0c;而且它们的 UI 样式和功能…...

java设计模式--结构型模式

结构性模式&#xff1a;适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式 适配器模式 适配器模式&#xff08;Adapter Pattern&#xff09; 充当两个不兼容接口之间的桥梁&#xff0c;属于结构型设计模式。目的是将一个类的接口转换为另一个接口&am…...

消息中间件:Kafka消息丢失与堆积问题分析与解决方案

消息中间件&#xff1a;Kafka消息丢失与堆积问题分析与解决方案 Kafka作为分布式消息系统&#xff0c;广泛应用于实时数据流处理、大数据分析等领域。然而&#xff0c;在实际应用中&#xff0c;Kafka可能会面临消息丢失和消息堆积的问题&#xff0c;这些问题如果得不到有效处理…...

mac终端代理配置指南

终端代理配置指南 在 macOS 中&#xff0c;你可以通过几种不同的方法来配置终端代理。这里介绍两种常见的设置方式&#xff1a;使用 alias 和 shell 函数。 方法 1&#xff1a;使用 Alias 配置代理 打开终端配置文件 默认情况下&#xff0c;macOS 终端使用的是 zsh。如果你的系…...

mbedTLS生成客户端,服务端密钥及CA证书

1. mbedTLS源码&#xff1a;https://github.com/Mbed-TLS/mbedtls.git 2. 生成步骤&#xff1a; 2.1 编译上述源码 2.2 生成CA私钥和自签名证书&#xff1a; 进入编译的build目录&#xff0c;比如&#xff1a;/mbedtls-development/build# 2.2.1生成CA私钥 执行下面的命令&…...

如何有效应对突发技术故障:以网易云音乐为例

引言 在互联网行业&#xff0c;任何一个在线服务都可能遭遇突发的技术故障。这些故障不仅影响用户体验&#xff0c;还可能对公司的品牌形象造成损害。因此&#xff0c;如何快速响应并高效解决这些问题成为了每一个开发团队的重要课题。本文将以网易云音乐在2024年8月19日下午遭…...

上传文件到github仓库

REF: https://blog.csdn.net/litianxiang_kaola/article/details/74075151 已有repository&#xff0c;往仓库里更新内容 点击gitlab里的clone 在git bash中使用git clone&#xff0c;这个时候会将网上的仓库下载到本地&#xff0c;你可以把想要更新的内容直接拖到仓库里 …...

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 &#xff0c;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 协议分析

注&#xff1a; 1. 本文所分析的fastboot源码不是android下的源码&#xff0c;而是恩智浦芯片厂商在IMX6UL芯片的uboot源码中自己实现的源码&#xff0c;二者不同&#xff0c;请读者注意区分。一些图片是网上找到的&#xff0c;出处不好注明&#xff0c;请见谅。 2. 分析fastbo…...

redis hash类型的命令

1.hset 格式&#xff1a; hset key field value [field value ...]&#xff08;value是字符串&#xff09; 返回值&#xff1a;设置成功的键值对的个数 2.hget&#xff1a;获取键值对 格式:hget key field 3.hexists&#xff1a;判断hash中是否存在指定 格式&#xff1a;…...

【OpenCV】 中使用 Lucas-Kanade 光流进行对象跟踪和路径映射

文章目录 一、说明二、什么是Lucas-Kanade 方法三、Lucas-Kanade 原理四、代码实现4.1 第 1 步&#xff1a;用户在第一帧绘制一个矩形4.2 第 2 步&#xff1a;从图像中提取关键点4.3 第 3 步&#xff1a;跟踪每一帧的关键点 一、说明 本文针对基于光流法的目标追踪进行叙述&am…...

ES 支持乐观锁吗?如何实现的?

本篇主要介绍一下Elasticsearch的并发控制和乐观锁的实现原理&#xff0c;列举常见的电商场景&#xff0c;关系型数据库的并发控制、ES的并发控制实践。 并发场景 不论是关系型数据库的应用&#xff0c;还是使用Elasticsearch做搜索加速的场景&#xff0c;只要有数据更新&…...

前端宝典十一:前端工程化稳定性方案

一、工程化体系介绍 1、什么是前端工程化 前端工程化 前端 软件工程&#xff1b;前端工程化 将工程方法系统化地应用到前端开发中&#xff1b;前端工程化 系统、严谨、可量化的方法开发、运营和维护前端应用程序&#xff1b;前端工程化 基于业务诉求&#xff0c;梳理出最…...

yum 数据源的切换

本来准备安装一个ntp 服务器时间进行同步&#xff0c;但是使用yum install ntp -y 但是却失败了 原因是yum自带的镜像源不能用了&#xff0c;所以要想使用yum 多功能只能切换yum 对应的镜像源了 如果你的服务商是可以使用wget命令的&#xff1a; wget -O /etc/yum.repos.d/Ce…...

MySQL入门学习-命令行工具.mysqlbinlog

MySQL 命令行工具mysqlbinlog用于处理二进制日志文件。 一、关于mysqlbinlog工具的详细介绍&#xff1a; 1、命令行工具mysqlbinlog的特点和使用方法&#xff1a; - 特点&#xff1a; - 可以解析和查看二进制日志文件的内容。 - 支持多种输出格式&#xff0c;如文本、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(_("该区名已存在&#xff0c;无需再填加…...

使用预训练的 ONNX 格式的目标检测模型(基于 YOLOv8n-pose)姿态监测

具体步骤如下&#xff1a; 加载图像&#xff1a; 从指定路径读取一张图像&#xff08;这里假设图像名为bus.jpg&#xff09;。将图像从 BGR 颜色空间转换为 RGB 颜色空间。 图像预处理&#xff1a; 计算图像的高度、宽度&#xff0c;并确定其中的最大值作为新图像的边长。创建一…...

matlab实现模拟退火算法

模拟退火算法&#xff08;Simulated Annealing, SA&#xff09;是一种通用概率优化算法&#xff0c;用于在给定的大搜索空间内寻找问题的近似全局最优解。该算法灵感来源于物理学中固体物质的退火过程&#xff0c;其中温度逐渐降低&#xff0c;粒子逐渐趋于能量最低状态。 在M…...

【Prettier】代码格式化工具Prettier的使用和配置介绍

前言 前段时间&#xff0c;因为项目的prettier的配置和eslint格式检查有些冲突&#xff0c;在其prettier官网和百度了一些配置相关的资料&#xff0c;在此做一些总结&#xff0c;以备不时之需。 Prettier官网 Prettier Prettier 是一种前端代码格式化工具&#xff0c;支持ja…...

【计算机网络】网络基础

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…...

MFC在对话框中实现打印和打印预览

首先在这里感谢互联网的大哥们&#xff01;同时我讨厌动不动就是要vip才能查看&#xff01;所以我写的不需要vip就能看。只求点个赞。 直接上代码&#xff0c;新建6个文件CPrintFrame.cpp&#xff1b;CPrintFrame.h&#xff1b;CPrintPreviewView.cpp&#xff1b;CPrintPrevie…...

移动端页面出现闪屏

v-cloak 的作用和用法 用法&#xff1a; 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时&#xff0c;这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。官方API {{msg}} HTML 绑定 Vue实例&#xff0c;在页面加载时…...

elasticsearch的高亮查询三种模式查询及可能存在的问题

目录 高亮查询使用介绍 高亮参数 三种分析器 可能存在的查询问题 fvh查询时出现StringIndexOutOfBoundsException越界 检索高亮不正确 参考文档 高亮查询使用介绍 Elasticsearch 的高亮&#xff08;highlight&#xff09;可以从搜索结果中的一个或多个字段中获取突出显…...

【精品实战项目】深度学习预测、深度强化学习优化、附源码数据手把手教学

目录 前言 一、预测算法数据与代码介绍(torch和mxnet都有) 1.1 数据介绍 1.2 代码介绍 1.3 优化介绍 二、深度强化学习算法优化 2.1 DDPG 介绍 DPG--deterministic policy gradient DQN--deep Q-network DDPG--deep deterministic policy gradient 三、其他算法 总结…...

JavaScript 手写仿深拷贝

实现对象参数的深拷贝并返回拷贝之后的新对象&#xff0c;因为参数对象和参数对象的每个数据项的数据类型范围仅在数组、普通对象&#xff08;{}&#xff09;、基本数据类型中且无需考虑循环引用问题&#xff0c;所以不需要做过多的数据类型判断&#xff0c;核心步骤有&#xf…...

spring低版本设置cookie的samesite属性

场景&#xff1a;比较古老的项目了&#xff0c;ssh架子&#xff0c;Chrome 51 开始&#xff0c;浏览器的 Cookie 新增加了一个SameSite属性&#xff0c;可用于防止 CSRF 攻击和用户追踪。因此需要给其字段赋值。 网上找了很多资源&#xff0c;由于jar版本比较低&#xff0c;没有…...

GPT4o编写步进电机控制代码

我给出的要求如下&#xff1a; 基于STM32F407 HAL库&#xff0c;写一个步进电机控制程序&#xff0c;需要控制8个步进电机&#xff0c;我会给出描述步进电机的结构体变量&#xff0c;基于这些变量需要你做出以下功能&#xff0c;电机脉冲通过定时器中断翻转脉冲引脚的电平实现…...

关于Spring Boot的自动配置

目录 1.EnableAutoConfiguration注解 2.SpringBootConfiguration注解 3.Import注解 4.spring.factories 5.总结 &#xff08;1&#xff09;EnableAutoConfiguration &#xff08;2&#xff09;AutoConfigurationImportSelector &#xff08;3&#xff09; SpringFactoriesLoade…...