使用QOpenGLWidget(调用GPU)渲染QImage加载的图片

本文介绍了OpenGL编程接口及其在Qt中的应用,特别是通过QOpenGLWidget展示3D图形,包括如何重写initializeGL(),resizeGL(),和paintGL()方法,以及如何使用纹理和着色器进行渲染。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、OpenGL介绍

OpenGL是一个用来加速渲染显示2D、3D 矢量图形的编程接口。这个接口底层依赖于硬件GPU,底层硬件接口的驱动都是由GPU厂家提供。

openGl也支持跨平台,windows、Linux、MAC 平台都可以使用。

QT封装有QOpenGLWidget可以更加方便的调用GPU 来渲染图片。

下面例子代码就介绍QOpenGLWidget类的使用说明,并编写一个例子代码,调用GPU加速渲染一张QImage加载的图片。

二、QOpenGLWidget类介绍

 

下面是来至官方帮助的文档的介绍,我这里做了简单的调整,并翻译了一下:

image.png

QOpenGLWidget类是一个用于呈现OpenGL图形的小部件。

QOpenGLWidget提供了显示集成到Qt应用程序中的OpenGL图形的功能。它的使用非常简单:使你的类继承自它,并像任何其他QWidget一样使用子类,但你可以选择使用QPaint和标准OpenGL渲染命令。

QOpenGLWidget提供了三个方便的虚拟函数,你可以在子类中重新实现这些函数以执行典型的OpenGL任务:

paintGL()-渲染OpenGL场景。每当小部件需要更新时调用。

resizeGL()-设置OpenGL视口、投影等。每当小部件调整大小时(以及首次显示时,因为所有新创建的小部件都会自动获得调整大小事件),都会调用该小部件。

initializeGL()-设置OpenGL资源和状态。在第一次调用resizeGL()或paintGL()之前调用一次。

如果需要从paintGL()以外的位置触发重新绘制(典型示例是使用计时器设置场景动画),则应调用小部件的update()函数来安排更新。

调用paintGL()、resizeGL()或initializeGL()时,小部件的OpenGL呈现上下文将变为当前。如果需要从其他位置(例如,在小部件的构造函数或自己的绘制函数中)调用标准OpenGL API函数,则必须首先调用makeCurrent()。

所有渲染都发生在OpenGL帧缓冲区对象中。makeCurrent()确保它在上下文中绑定。在paintGL()中的渲染代码中创建和绑定其他帧缓冲区对象时,请记住这一点。永远不要重新绑定ID为0的帧缓冲区。相反,调用defaultFramebufferObject()获取应该绑定的ID。

QOpenGLWidget允许在平台支持时使用不同的OpenGL版本和配置文件。只需通过setFormat()设置请求的格式。但是请记住,在同一窗口中有多个QOpenGLWidget实例需要它们都使用相同的格式,或者至少使用不会使上下文不可共享的格式。要解决此问题,请首选使用QSurfaceFormat::setDefaultFormat()而不是setFormat()。

注意:在某些平台(例如macOS)上,当请求OpenGL核心概要文件上下文时,在构造QApplication实例之前调用QSurfaceFormat::setDefaultFormat()是必需的。这是为了确保上下文之间的资源共享保持功能,因为所有内部上下文都是使用正确的版本和概要文件创建的。

绘画技巧

image.png

如上所述,子类QOpenGLWidget以以下方式呈现纯3D内容:

  1. 重新实现initializeGL()和resizeGL()函数,以设置OpenGL状态并提供透视转换。

  2. 重新实现paintGL()以绘制3D场景,仅调用OpenGL函数。

  3. 还可以使用QPaint在QOpenGLWidget子类上绘制2D图形: 在paintGL()中,不要发出OpenGL命令,而是构造一个QPainter对象以在小部件上使用。

  4. 使用QPaint的成员函数绘制基本体。仍然可以发出直接的OpenGL命令。但是,你必须确保调用画家的BeginativePainting()和endNativePainting()来包含这些内容。

当仅使用QPaint执行绘制时,也可以像对普通小部件一样执行绘制:通过重新实现paintEvent()。 重新实现paintEvent()函数。构造一个针对小部件的QPaint对象。将小部件传递给构造函数或QPaint::begin()函数。 使用QPaint的成员函数绘制基本体。绘制完成后,QPaint实例将被销毁。或者,显式调用QPaint::end()。

三、例子代码

3.1 头文件: 重载QOpenGLWidget

ifndef MYGLWIDGET_H
#define MYGLWIDGET_H

#include <QObject>
#include <QOpenGLWidget>
#include <GL/gl.h>
#include <GL/glu.h>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QGLWidget>
#include <QImage>

class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit MyGLWidget(QWidget *parent = 0);

signals:

public slots:
    void initializeGL() Q_DECL_OVERRIDE;
    void resizeGL(int w, int h) Q_DECL_OVERRIDE;
    void paintGL() Q_DECL_OVERRIDE;
    void setImage(const QImage &image);
    void initTextures();
    void initShaders();
private:
    QVector<QVector3D> vertices;
    QVector<QVector2D> texCoords;
    QOpenGLShaderProgram program;
    QOpenGLTexture *texture;
    QMatrix4x4 projection;

};

#endif // MYGLWIDGET_H

3.2 源文件:QOpenGLWidget

include "myglwidget.h"
#include <QDebug>
#include <QGraphicsOpacityEffect>
MyGLWidget::MyGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{

}

void MyGLWidget::initTextures()
{
    // 加载 Avengers.jpg 图片
    texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
    texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
    texture->setMagnificationFilter(QOpenGLTexture::Linear);
    //重复使用纹理坐标
    //纹理坐标(1.1, 1.2)与(0.1, 0.2)相同
    texture->setWrapMode(QOpenGLTexture::Repeat);
    //设置纹理大小
    texture->setSize(this->width(), this->height());
    //分配储存空间
    texture->allocateStorage();
}

void MyGLWidget::initShaders()
{
    //纹理坐标
    texCoords.append(QVector2D(0, 1)); //左上
    texCoords.append(QVector2D(1, 1)); //右上
    texCoords.append(QVector2D(0, 0)); //左下
    texCoords.append(QVector2D(1, 0)); //右下
    //顶点坐标
    vertices.append(QVector3D(-1, -1, 1));//左下
    vertices.append(QVector3D(1, -1, 1)); //右下
    vertices.append(QVector3D(-1, 1, 1)); //左上
    vertices.append(QVector3D(1, 1, 1));  //右上
    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    const char *vsrc =
            "attribute vec4 vertex;\n"
            "attribute vec2 texCoord;\n"
            "varying vec2 texc;\n"
            "void main(void)\n"
            "{\n"
            "    gl_Position = vertex;\n"
            "    texc = texCoord;\n"
            "}\n";
    vshader->compileSourceCode(vsrc);//编译顶点着色器代码

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char *fsrc =
            "uniform sampler2D texture;\n"
            "varying vec2 texc;\n"
            "void main(void)\n"
            "{\n"
            "    gl_FragColor = texture2D(texture,texc);\n"
            "}\n";
    fshader->compileSourceCode(fsrc); //编译纹理着色器代码

    program.addShader(vshader);//添加顶点着色器
    program.addShader(fshader);//添加纹理碎片着色器
    program.bindAttributeLocation("vertex", 0);//绑定顶点属性位置
    program.bindAttributeLocation("texCoord", 1);//绑定纹理属性位置
    // 链接着色器管道
    if (!program.link())
        close();
    // 绑定着色器管道
    if (!program.bind())
        close();
}

void MyGLWidget::initializeGL()
{
    initializeOpenGLFunctions(); //初始化OPenGL功能函数
    glClearColor(0, 0, 0, 0);    //设置背景为黑色
    glEnable(GL_TEXTURE_2D);     //设置纹理2D功能可用
    initTextures();              //初始化纹理设置
    initShaders();               //初始化shaders
}

void MyGLWidget::resizeGL(int w, int h)
{
    // 计算窗口横纵比
    qreal aspect = qreal(w) / qreal(h ? h : 1);
    // 设置近平面值 3.0, 远平面值 7.0, 视场45度
    const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;
    // 重设投影
    projection.setToIdentity();
    // 设置透视投影
    projection.perspective(fov, static_cast<float>(aspect), zNear, zFar);
}

void MyGLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕缓存和深度缓冲
    QMatrix4x4 matrix;
    matrix.translate(0.0, 0.0, -5.0);                   //矩阵变换
    program.enableAttributeArray(0);
    program.enableAttributeArray(1);
    program.setAttributeArray(0, vertices.constData());
    program.setAttributeArray(1, texCoords.constData());
    program.setUniformValue("texture", 0); //将当前上下文中位置的统一变量设置为value
    texture->bind();  //绑定纹理
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//绘制纹理
    texture->release(); //释放绑定的纹理
    texture->destroy(); //消耗底层的纹理对象
    texture->create();
}

void MyGLWidget::setImage(const QImage &image)
{
    texture->setData(image); //设置纹理图像
    //设置纹理细节
    texture->setLevelofDetailBias(-1);//值越小,图像越清晰
    update();
}

3.3 主函数: 调用测试

UI界面拖一个Qwidget控件,提升为MyGLwidget类(继承QopenGLWidget重写的类)

image.png

image.png

include "widget.h"
#include "ui_widget.h"
#include "myglwidget.h"
#include <QGraphicsOpacityEffect>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //this->setWindowFlags(Qt::FramelessWindowHint);
    //this->setWindowOpacity(0.5);
    connect(&timer, &QTimer::timeout, this, &Widget::slotUpdate);
    timer.start(40);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::slotUpdate()
{
    ui->widget->setImage(QImage(":/image/1161.24.png"));
}

image.png

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

作者:DS小龙哥
链接:https://juejin.cn/post/7023184503166369822
 

 

<think>好的,我需要帮助用户解决如何在Qt中集成Faster R-CNN模型并通过摄像头实现实时物体识别的问题。首先,我需要理解用户的需求。他们希望将已经训练好的Faster R-CNN模型与自定义的Qt界面结合,调用摄像头进行实时检测。这涉及到几个关键步骤:模型转换、Qt界面开发、摄像头调用、模型集成以及性能优化。 首先,模型转换。Faster R-CNN通常是用PyTorch或TensorFlow等框架训练的,但Qt主要用C++开发,所以可能需要将模型转换为ONNX格式,或者使用LibTorch(PyTorch的C++版本)来加载模型。我需要考虑不同框架的兼容性,以及如何确保转换后的模型保持相同的精度和性能。 接下来是Qt界面开发。用户需要调用摄像头,Qt中常用的方法是使用QCamera类和相关模块。同时,界面需要显示视频流和检测结果,这可能需要用到QGraphicsView或OpenGL来高效渲染图像。此外,还需要处理摄像头帧的捕获和传递给模型进行推理的流程。 然后是模型集成部分。在C++环境中加载深度学习模型可能会遇到依赖库的问题,比如需要配置OpenCV、LibTorch等。需要指导用户正确配置这些库,并编写代码将摄像头捕获的帧预处理成模型需要的输入格式,执行推理,然后解析输出结果,绘制边界框和标签。 性能优化是关键,尤其是实时性要求。Faster R-CNN本身计算量较大,可能无法达到很高的帧率。需要考虑模型简化、使用GPU加速、多线程处理(比如将推理放在单独的线程中,避免阻塞主界面线程)以及降低输入图像分辨率等方法。此外,可能还需要提及异步处理或帧跳过策略来平衡延迟和性能。 还要注意引用用户提供的资料,比如引用[1]中提到Faster R-CNN在水果检测中的应用,可以举例说明如何将模型应用到具体场景中。同时,引用[2]提到的性能优化方法,如多目标检测和模型优化技巧,需要结合到解决方案中。 最后,需要验证整个流程的可行性,确保各步骤衔接顺畅,比如摄像头数据是否能正确捕获,模型推理结果是否准确显示在界面上,以及整体性能是否满足实时需求。可能还需要提示用户进行测试和调试,处理可能出现的异常情况,比如摄像头无法打开、模型加载失败等。 总结一下,步骤大致分为:模型转换和准备、Qt界面搭建、摄像头集成、模型推理集成、性能优化。需要详细说明每个步骤的具体操作,并给出代码示例和配置建议,同时引用相关文献支持建议的有效性。</think>将Faster R-CNN模型与Qt界面集成实现实时检测,可分为以下步骤: ### 一、模型转换与封装 1. **模型导出** 使用PyTorch的`torch.jit.trace`将训练好的Faster R-CNN模型转换为TorchScript格式: ```python traced_model = torch.jit.trace(model, example_input) traced_model.save("faster_rcnn.pt") ``` 或转换为ONNX格式以实现跨平台兼容[^1]。 2. **C++推理接口** 在Qt项目中配置LibTorch(PyTorch C++ API),创建推理类: ```cpp #include <torch/script.h> class Detector { public: void init(const std::string& model_path) { module_ = torch::jit::load(model_path); } std::vector<DetectionResult> predict(cv::Mat& frame); }; ``` ### 二、Qt界面开发 1. **摄像头模块** 使用`QCamera`和`QVideoProbe`捕获实时画面: ```cpp QCamera* camera = new QCamera(QCameraInfo::defaultCamera()); QVideoProbe* probe = new QVideoProbe; connect(probe, &QVideoProbe::videoFrameProbed, [=](const QVideoFrame& frame){ processFrame(frame); }); ``` 2. **图像显示组件** 使用`QGraphicsView`+`QGraphicsPixmapItem`实现带覆盖层的显示: ```cpp QGraphicsScene* scene = new QGraphicsScene; QGraphicsPixmapItem* pixmapItem = scene->addPixmap(QPixmap()); ``` ### 三、系统集成流程 1. **帧处理流水线** ```mermaid graph LR A[摄像头捕获] --> B[转换为OpenCV Mat] B --> C[图像预处理] C --> D[模型推理] D --> E[解析边界框] E --> F[绘制检测结果] F --> G[显示到Qt界面] ``` 2. **关键代码实现** ```cpp void MainWindow::processFrame(const QVideoFrame& frame){ QImage image = frame.image(); cv::Mat cvFrame(image.height(), image.width(), CV_8UC3, image.bits(), image.bytesPerLine()); // 预处理 cv::resize(cvFrame, cvFrame, cv::Size(800,600)); tensor = torch::from_blob(cvFrame.data, {1,600,800,3}, torch::kByte); tensor = tensor.permute({0,3,1,2}).to(torch::kFloat); // 推理 auto outputs = detector_->predict(tensor); // 绘制结果 for(const auto& res : outputs) { cv::rectangle(cvFrame, res.bbox, cv::Scalar(0,255,0), 2); } // 更新界面 QPixmap pixmap = QPixmap::fromImage(QImage( cvFrame.data, cvFrame.cols, cvFrame.rows, QImage::Format_RGB888)); pixmapItem->setPixmap(pixmap); } ``` ### 四、性能优化策略 1. **异步推理** 使用`QThreadPool`创建专用推理线程: ```cpp class InferenceTask : public QRunnable { void run() override { auto results = detector_->predict(frame_); emit resultReady(results); } }; ``` 2. **GPU加速** 启用CUDA加速(需NVIDIA显卡支持): ```cpp tensor = tensor.to(torch::kCUDA); module_.to(torch::kCUDA); ``` 3. **分辨率优化** 将输入分辨率从Faster R-CNN默认的$800\times 600$降低到$640\times 480$,可提升约30%的帧率[^2]。 ### 五、部署注意事项 1. **依赖项打包** 需包含: - LibTorch动态库 - OpenCV 4.x DLLs - CUDA运行时库(若使用GPU) 2. **跨平台兼容** 在Windows下使用MSVC编译器,Linux下建议配置LD_LIBRARY_PATH环境变量。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值