You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenCV中OpenGLDrawCallback回调函数无法触发的问题求助

解决OpenCV OpenGLDrawCallback未触发的问题

我之前也碰到过一模一样的情况——确认了OpenCV编译时带了OpenGL和QT支持,窗口能正常显示流,但OpenGL回调就是没反应。核心问题出在你循环里的cv::imshow调用,它会完全绕过OpenGL的渲染流程,直接用传统方式把帧画到窗口上,导致回调根本得不到执行的机会。

下面是具体的修复步骤和调整后的代码:

关键问题分析

  • cv::imshow的工作机制是将Mat数据直接传递给窗口后端(比如Qt)进行绘制,这会覆盖你设置的OpenGL回调逻辑,两者无法同时工作。
  • 要触发OpenGLDrawCallback,你需要手动控制窗口更新,并且完全由回调函数负责渲染帧内容。

修复后的代码示例

1. 调整OpenGL回调函数

首先修改回调函数,让它能接收并渲染帧数据(这里添加了基础的纹理渲染逻辑,确保回调触发后能看到画面):

#include <opencv2/opencv.hpp>
#include <GL/gl.h>
#include <iostream>

void on_opengl(void* userdata) {
    std::cout << "OpenGL callback triggered!" << std::endl;
    
    cv::Mat* frame = static_cast<cv::Mat*>(userdata);
    if (!frame || frame->empty()) return;

    // 初始化纹理(仅第一次执行)
    static GLuint texture_id = 0;
    if (texture_id == 0) {
        glGenTextures(1, &texture_id);
        glBindTexture(GL_TEXTURE_2D, texture_id);
        // 设置纹理参数
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    }

    // 将OpenCV帧数据上传到OpenGL纹理
    glBindTexture(GL_TEXTURE_2D, texture_id);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, frame->cols, frame->rows, 0, GL_BGR, GL_UNSIGNED_BYTE, frame->data);

    // 绘制全屏四边形(覆盖整个窗口)
    glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex2f(-1.0f, -1.0f);
        glTexCoord2f(1, 0); glVertex2f(1.0f, -1.0f);
        glTexCoord2f(1, 1); glVertex2f(1.0f, 1.0f);
        glTexCoord2f(0, 1); glVertex2f(-1.0f, 1.0f);
    glEnd();
}

2. 修改主循环逻辑

移除cv::imshow,改用cv::updateWindow触发回调,并通过userData传递当前帧:

int main() {
    cv::VideoCapture stream("stream_ip");
    if (!stream.isOpened()) {
        printf("Couldn't open stream! %s\n", strerror(errno));
        return -1;
    }

    // 创建带OpenGL支持的窗口
    cv::namedWindow("rtsp_stream", cv::WINDOW_OPENGL);
    cv::setWindowProperty("rtsp_stream", cv::WND_PROP_FULLSCREEN, cv::WINDOW_FULLSCREEN);
    cv::setOpenGlContext("rtsp_stream");

    cv::Mat frame;
    char k;
    bool continueStream = true;

    while (continueStream) {
        if (stream.read(frame)) {
            // 将当前帧传递给回调函数
            cv::setOpenGlDrawCallback("rtsp_stream", on_opengl, &frame);
            // 手动触发窗口更新,这会调用OpenGL回调
            cv::updateWindow("rtsp_stream");

            k = cv::waitKey(1);
            switch(k) {
                case 0x1b: // ESC键退出
                    printf("Closing stream.\n");
                    continueStream = false;
                    break;
            }
        } else {
            printf("Failed to read frame from stream!\n");
            break;
        }
    }

    // 清理资源
    cv::destroyWindow("rtsp_stream");
    stream.release();
    return 0;
}

额外注意事项

  • 确保你的Qt版本和OpenCV编译时依赖的Qt版本一致,避免上下文兼容性问题。
  • 如果回调还是没触发,可以尝试在窗口创建后先调用一次cv::updateWindow,测试回调是否能正常执行。
  • OpenCV的OpenGL窗口使用归一化坐标系(范围-1到1),所以绘制全屏四边形时用这个坐标范围即可覆盖整个窗口。

内容的提问来源于stack exchange,提问作者user3738243

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:53:05