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
相关产品推荐
相关产品推荐

