QOpenGLWidget视频画面上绘制矩形框
一、QPainter绘制
在QOpenGLWidget中可以绘制,并且和OpenGL的内容叠在一起。paintGL里面绘制完视频后,解锁资源,再用QPainter绘制矩形框。这种方式灵活性最好。
void VideoGLWidget::paintGL() {glClear(GL_COLOR_BUFFER_BIT);m_program.bind();//绘制视频数据// 解绑VAOglBindVertexArray(0);m_program.release();// ----------------- 绘制矩形框 -----------------QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setPen(QPen(QColor(Qt::red), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));QRectF drawRect = QRectF(0, 0, width() * 0.5, height() * 0.5);painter.drawRect(drawRect);painter.end();
}
二、OpenGL绘制
通过不同的QOpenGLShaderProgram,可以指定不同的着色器程序来实现矩形的绘制。
1)边框颜色参数要通过Uniform传递给OpenGL的顶点着色器。
2)动态矩形顶点缓冲更新,坐标归一化到OpenGL坐标系,顶点数据更新VBO
void VideoGLWidget::updateRectBuffer() {if (m_rects.isEmpty()) return;// 将 QRectF 转换为归一化坐标(-1 到 1)QVector<float> vertices;for (const QRectF &rect : m_rects) {float x1 = (rect.x() / m_videoSize.width()) * 2 - 1;float y1 = 1 - (rect.y() / m_videoSize.height()) * 2;float x2 = ((rect.x() + rect.width()) / m_videoSize.width()) * 2 - 1;float y2 = 1 - ((rect.y() + rect.height()) / m_videoSize.height()) * 2;// 每个矩形由 4 条线段组成(每条线段 2 个点)vertices << x1 << y1 << x2 << y1; // 上边vertices << x2 << y1 << x2 << y2; // 右边vertices << x2 << y2 << x1 << y2; // 下边vertices << x1 << y2 << x1 << y1; // 左边}// 更新 VBOglBindBuffer(GL_ARRAY_BUFFER, m_rectVBO);glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.constData(), GL_DYNAMIC_DRAW);
}
头文件 VideoGLWidget.h
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>class VideoGLWidget : public QOpenGLWidget, protected QOpenGLFunctions {
public:explicit VideoGLWidget(QWidget *parent = nullptr);~VideoGLWidget();// 更新视频帧(假设帧格式为 RGB32)void updateVideoFrame(const QImage &frame);// 更新动态矩形框列表(坐标相对于视频帧尺寸)void updateRects(const QList<QRectF> &rects);protected:void initializeGL() override;void paintGL() override;void resizeGL(int w, int h) override;private:// OpenGL 资源QOpenGLShaderProgram *m_videoShader; // 视频渲染着色器QOpenGLShaderProgram *m_rectShader; // 矩形框渲染着色器QOpenGLTexture *m_videoTexture; // 视频纹理GLuint m_rectVBO; // 矩形顶点缓冲对象QSize m_videoSize; // 视频帧尺寸QList<QRectF> m_rects; // 当前矩形框列表// 顶点数据相关void initRectBuffer();void updateRectBuffer();
};
实现文件 VideoGLWidget.cpp
OpenGL初始化
VideoGLWidget::VideoGLWidget(QWidget *parent) : QOpenGLWidget(parent), m_videoTexture(nullptr), m_rectVBO(0) {// 启用自动更新setAutoFillBackground(false);
}VideoGLWidget::~VideoGLWidget() {makeCurrent();delete m_videoTexture;delete m_videoShader;delete m_rectShader;glDeleteBuffers(1, &m_rectVBO);doneCurrent();
}void VideoGLWidget::initializeGL() {initializeOpenGLFunctions();glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// 初始化视频渲染着色器m_videoShader = new QOpenGLShaderProgram(this);m_videoShader->addShaderFromSourceCode(QOpenGLShader::Vertex,"attribute vec4 vertexIn;""attribute vec2 texCoordIn;""varying vec2 texCoord;""void main() {"" gl_Position = vertexIn;"" texCoord = texCoordIn;""}");m_videoShader->addShaderFromSourceCode(QOpenGLShader::Fragment,"varying vec2 texCoord;""uniform sampler2D videoTexture;""void main() {"" gl_FragColor = texture2D(videoTexture, texCoord);""}");m_videoShader->link();// 初始化矩形框渲染着色器m_rectShader = new QOpenGLShaderProgram(this);m_rectShader->addShaderFromSourceCode(QOpenGLShader::Vertex,"attribute vec2 position;""void main() {"" gl_Position = vec4(position, 0.0, 1.0);""}");m_rectShader->addShaderFromSourceCode(QOpenGLShader::Fragment,"uniform vec4 color;""void main() {"" gl_FragColor = color;""}");m_rectShader->link();// 初始化矩形顶点缓冲glGenBuffers(1, &m_rectVBO);
}
视频帧更新与纹理上传
void VideoGLWidget::updateVideoFrame(const QImage &frame) {makeCurrent();// 首次创建或尺寸变化时重新创建纹理if (!m_videoTexture || m_videoTexture->size() != frame.size()) {delete m_videoTexture;m_videoTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);m_videoTexture->setFormat(QOpenGLTexture::RGB8_UNorm);m_videoTexture->setSize(frame.width(), frame.height());m_videoTexture->allocateStorage();m_videoSize = frame.size();}// 上传帧数据到纹理(假设帧为 RGB32 格式)m_videoTexture->bind();glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame.width(), frame.height(),GL_BGRA, GL_UNSIGNED_BYTE, frame.bits());update();
}void VideoGLWidget::updateRects(const QList<QRectF> &rects) {m_rects = rects;updateRectBuffer(); // 更新顶点数据update();
}
动态矩形顶点缓冲更新
void VideoGLWidget::updateRectBuffer() {if (m_rects.isEmpty()) return;// 将 QRectF 转换为归一化坐标(-1 到 1)QVector<float> vertices;for (const QRectF &rect : m_rects) {float x1 = (rect.x() / m_videoSize.width()) * 2 - 1;float y1 = 1 - (rect.y() / m_videoSize.height()) * 2;float x2 = ((rect.x() + rect.width()) / m_videoSize.width()) * 2 - 1;float y2 = 1 - ((rect.y() + rect.height()) / m_videoSize.height()) * 2;// 每个矩形由 4 条线段组成(每条线段 2 个点)vertices << x1 << y1 << x2 << y1; // 上边vertices << x2 << y1 << x2 << y2; // 右边vertices << x2 << y2 << x1 << y2; // 下边vertices << x1 << y2 << x1 << y1; // 左边}// 更新 VBOglBindBuffer(GL_ARRAY_BUFFER, m_rectVBO);glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.constData(), GL_DYNAMIC_DRAW);
}
渲染主循环
void VideoGLWidget::paintGL() {glClear(GL_COLOR_BUFFER_BIT);// 渲染视频帧if (m_videoTexture) {m_videoShader->bind();m_videoTexture->bind();// 顶点坐标和纹理坐标(全屏四边形)static const GLfloat vertexData[] = {-1.0f, -1.0f, 0.0f, 0.0f,1.0f, -1.0f, 1.0f, 0.0f,-1.0f, 1.0f, 0.0f, 1.0f,1.0f, 1.0f, 1.0f, 1.0f};// 设置顶点属性glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), vertexData);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), vertexData + 2);glEnableVertexAttribArray(0);glEnableVertexAttribArray(1);// 绘制全屏四边形glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);m_videoShader->release();}// 渲染动态矩形框if (!m_rects.isEmpty()) {m_rectShader->bind();glBindBuffer(GL_ARRAY_BUFFER, m_rectVBO);// 设置颜色(红色,50%透明度)m_rectShader->setUniformValue("color", QVector4D(1.0f, 0.0f, 0.0f, 0.5f));// 设置顶点属性glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);glEnableVertexAttribArray(0);// 绘制线段(每个矩形 4 条边,每条边 2 个顶点)glLineWidth(2.0f);glDrawArrays(GL_LINES, 0, m_rects.size() * 8); // 4边 * 2点 = 8点/矩形m_rectShader->release();}
}
使用示例
// 在主窗口或控制器中
void MainWindow::onNewVideoFrame(const QImage &frame) {m_videoWidget->updateVideoFrame(frame);
}void MainWindow::onDetectionResult(const QList<QRectF> &rects) {m_videoWidget->updateRects(rects);
}
异步纹理上传,避免在主线程阻塞:
// 在单独线程处理视频解码
void DecoderThread::run() {while (running) {QImage frame = decodeFrame();QMetaObject::invokeMethod(m_videoWidget, "updateVideoFrame", Qt::QueuedConnection, Q_ARG(QImage, frame));}
}
三、实例代码
1、视频画面暂时使用图片纹理代替,矩形框支持OPianter和OpenGL方式。效果:

2、 工程代码
QOpenGLWidget绘制框代码下载
相关文章:
QOpenGLWidget视频画面上绘制矩形框
一、QPainter绘制 在QOpenGLWidget中可以绘制,并且和OpenGL的内容叠在一起。paintGL里面绘制完视频后,解锁资源,再用QPainter绘制矩形框。这种方式灵活性最好。 void VideoGLWidget::paintGL() {glClear(GL_COLOR_BUFFER_BIT);m_program.bi…...
Linux系统加固笔记
检查口令为空的账户 判断依据:存在则不符合 特殊的shell a./bin/false:将用户的shell设置为/bin/false,用户会无法登录,并且不会有任何提示信息b./sbib/nologin:nologin会礼貌的向用户发送一条消息,并且拒绝用户登录…...
【Go万字洗髓经】Golang中sync.Mutex的单机锁:实现原理与底层源码
本章目录 1. sync.Mutex锁的基本用法2. sync.Mutex的核心原理自旋到阻塞的升级过程自旋CAS 饥饿模式 3. sync.Mutex底层源码Mutex结构定义全局常量Mutex.Lock()方法第一次CAS加锁能够成功的前提是?竞态检测 Mutex.lockSlow()lockSlow的局部变量自旋空转state新值构造…...
npm前端模块化编程
1. 代码编写 使用npm和Webpack进行前端模块化编程的完整案例。这个案例将创建一个简单的网页,该网页显示一个标题和一个按钮,点击按钮会在控制台中打印一条消息。 步骤 1: 初始化项目 创建项目目录并初始化npm: mkdir my-modular-fronten…...
Django REST framework 源码剖析-认证器详解(Authentication)
Django REST framework 源码剖析-认证器详解(Authentication) 身份验证始终在视图的最开始运行,在权限和限制检查发生之前,以及在允许任何其他代码继续之前。request.user属性通常设置为contrib.auth包的user类的实例。request.auth属性用于任何其他身份…...
TCP/IP三次握手的过程,为什么要3次?
一:过程 第一次(SYN): 客户端发送一个带有SYN标志的TCP报文段给服务器,设置SYN1,并携带初始序列号Seqx(随机值),进入SYN_SENT状态。等待服务器相应。 第二次(…...
Centos6安装nerdctl容器运行时
Centos6安装nerdctl容器运行时 前言Centos6安装docker---失败--不可拉取镜像docker配置国内镜像加速 Centos6安装nerdctl-full容器管理工具为Centos6配置containerd服务开机自启动设置nerdctl自动补全 前言 本文写于2025年3月22日,因一些特殊业务需要用到Centos6Docker,但Cent…...
登录验证码的接口实习,uuid,code.
UID是唯一标识的字符串,下面是百度百科关于UUID的定义: UUID是由一组32位数的16进制数字所构成,是故UUID理论上的总数为16322128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。 UUID的标准…...
用fofa语法搜索漏洞
FOFA是一款非常强大的搜索引擎 关于对于fofa的描述是:FOFA(网络空间资产检索系统)是世界上数据覆盖更完整的IT设备搜索引擎,拥有全球联网IT设备更全的DNA信息。 探索全球互联网的资产信息,进行资产及漏洞影响范围分析…...
设计一个基于机器学习的光伏发电功率预测模型,以Python和Scikit - learn库为例
下面为你设计一个基于机器学习的光伏发电功率预测模型,以Python和Scikit - learn库为例。此模型借助历史气象数据和光伏发电功率数据来预测未来的光伏发电功率。 模型设计思路 数据收集:收集历史气象数据(像温度、光照强度、湿度等…...
20242817李臻《Linux⾼级编程实践》第6周
20242817李臻《Linux⾼级编程实践》第6周 一、AI对学习内容的总结 Linux进程间通信(IPC) 1. 进程间通信基本概念 作用: 数据传输:进程间传递数据(字节到兆字节级别)。共享数据:多个进程操作同一数据&…...
Vue 3中的Provide与Inject
在Vue 3中,provide和inject机制为组件间的通信提供了一种新的方式。不同于传统的父子组件通过props传递数据的方式,provide和inject允许祖先组件向其所有子孙组件提供数据,而无需通过中间层手动传递。这使得跨层级的组件通信变得更加直接和简…...
深入解析SQL2API平台:数据交互革新者
在数字化转型持续深入的当下,企业对数据的高效利用与管理的需求愈发迫切。SQL2API平台应运而生,成为助力企业突破数据交互困境的有力工具,特别是它由麦聪软件基于DaaS(数据即服务)产品创新衍生而来,备受业界…...
蓝桥杯算法题分享(二)
蓝桥杯算法题分享 本文将继续分享三道经典的蓝桥杯算法题,包括题目描述、解题思路和 Java 代码实现,帮助大家更好地理解算法的应用。对算法感兴趣的朋友可以点开我的主页查看我上周分享的另三道题。 第一题:次数差 题目描述 x 星球有 26 只…...
实战-MySQL5.7升级8.0遇到的四个问题
近期几个项目的MySQL由5.7升级到8.0,升级过程中遇到四个问题,记录下来分享一下: 第一个问题详见之前的文章: MySQL 5.7升级8.0报异常:处理新增关键字 第二个问题详见之前的文章: MySQL 5.7升级8.0报异常…...
为什么递归用栈?动态分配用堆?
文章目录 1. 区别2. 栈空间特点优点缺点 3. 堆空间特点优点缺点 4. 栈和堆的对比5. 总结 1. 区别 栈空间和堆空间是程序内存中的两块不同区域,分别用于不同的用途。 栈空间: 栈空间是由操作系统自动管理的内存区域,用于存储局部变量、函数…...
Java 中装饰者模式与策略模式在埋点系统中的应用
前言 在软件开发中,装饰者模式和策略模式是两种常用的设计模式,它们在特定的业务场景下能够发挥巨大的作用。本文将通过一个实际的埋点系统案例,探讨如何在 Java 中运用装饰者模式和策略模式,以及如何结合工厂方法模式来优化代码…...
计算机视觉总结
以下是针对上述问题的详细解答,并结合代码示例进行说明: 1. 改进YOLOv5人脸检测模块,复杂光照场景准确率从98.2%提升至99.5% 优化具体过程: 光照补偿:在数据预处理阶段,采用自适应光照补偿算法,对图像进行实时增强,以减少光照变化对人脸检测的影响。数据增强:在训练…...
无人设备遥控器之调度自动化技术篇
一、技术原理 信息采集与处理: 通过传感器、仪表等设备采集无人设备的各种数据,如位置、速度、状态等。 将采集到的数据传输到调度自动化系统中进行处理和分析,以获取设备的实时状态。 系统建模与优化: 调度自动化系统会根据…...
【AI】Orin Nano+ubuntu22.04上移植YoloV11,并使用DeepStream测试成功
【AI】郭老二博文之:AI学习目录汇总 1、准备工作 使用 sdk-manager 烧写 OrinNano, JetPack版本为6.0 DP,对应操作系统为:Ubuntu22.04 参见博客:【NVIDIA】Jetson Orin Nano系列:烧写Ubuntu22.04 2、安装 PyTorch 2.1 下载依赖 1)安装onnx pip install onnx -i h…...
K8S学习之基础四十五:k8s中部署elasticsearch
k8s中部署elasticsearch 安装并启动nfs服务yum install nfs-utils -y systemctl start nfs systemctl enable nfs.service mkdir /data/v1 -p echo /data/v1 *(rw,no_root_squash) >> /etc/exports exports -arv systemctl restart nfs创建运行nfs-provisioner需要的sa账…...
如何在 Windows 上安装并使用 Postman?
Postman 是一个功能强大的API测试工具,它可以帮助程序员更轻松地测试和调试 API。在本文中,我们将讨论如何在 Windows 上安装和使用 Postman。 Windows 如何安装和使用 Postman 教程?...
Langchain 提示词(Prompt)
基本用法 1. 基本概念 提示词模板 是一个字符串模板,其中包含一些占位符(通常是 {variable} 形式的),这些占位符可以在运行时被实际值替换。LangChain 提供了多种类型的提示词模板,以适应不同的使用场景。 2. 主要类…...
什么是PHP伪协议
PHP伪协议是一种特殊的URL格式,允许开发者以不同于传统文件路径访问和操作资源。以下是一些常见的PHP伪协议及其详细介绍: 常见的PHP伪协议 1. **file://** - **用途**:访问本地文件系统。 - **示例**:file:///path/to/file.txt。…...
python脚本处理excel文件
1.对比perl和python 分别尝试用perl和python处理excel文件,发现perl的比较复杂,比如说read excel就有很多方式 Spreadsheet::Read use Spreadsheet::ParseExcel 不同的method,对应的取sheet的cell方式也不一样。更复杂的是处理含有中文内…...
【腾讯云架构师技术沙龙2025.03.22】
大模型技术演进与行业影响分析 日期:2025年3月22日 主讲人:李建忠 《DeepSeek实战驱动行业智变—AI应用寒武纪》 整理:飞书语音转化DeepSeek分析汇总 一、技术演进:从快思考到慢思考 1. 早期争议与能力局限(2022-202…...
【SOC 芯片设计 DFT 学习专栏 -- IDDQ 测试 与 Burn-In 测试】
文章目录 IDDQ 测试与 Burn-In 测试IDDQ 测试工作原理测试过程优点局限性示例 2. Burn-In 测试工作原理测试过程优点局限性示例 总结对比 IDDQ 测试和 Burn-in 测试: IDDQ 测试与 Burn-In 测试 本文将详细介绍 DFT 中 IDDQ测试 和 burn-in测试模式 IDDQ 测试 IDD…...
Axure RP 9.0教程: 基于动态面板的元件跟随来实现【音量滑块】
文章目录 引言I 音量滑块的实现步骤添加底层边框添加覆盖层基于覆盖层创建动态面板添加滑块按钮设置滑块拖动效果引言 音量滑块在播放器类APP应用场景相对较广,例如调节视频的亮度、声音等等。 I 音量滑块的实现步骤 添加底层边框 在画布中添加一个矩形框:500 x 32,圆…...
JS—call,apply,bind:1分钟掌握三者的区别
个人博客:haichenyi.com。感谢关注 一. 目录 一–目录二–call三–apply四–bind五–三者对比 二. call 作用: 立即调用函数,显式指定this值,并逐个传递参数。 语法: func.call(thisArg, arg1, arg2, …) 特点&…...
Linux TTY设备汇总
目录 1. tty(终端设备统称) 2. ptm(伪终端主设备)与pts(伪终端从设备) 3. ttys(串行端口终端) 4. ttyACM(USB CDC ACM设备) 5. ttyGS(USB Gadget Serial设备) 主要联系 典型应用场景 TTY_CORE: drivers/tty/tty_io.c:tty_register_driver…...
