起因是steam叠加界面在不启用OpenGL的OpenCV窗口上无法启用,进而无法在全屏时弹出成就、f12截图;尝试过程不易,总结多方经验才最终成功,故记录之,望对看到的人有所帮助。
一、重新编译出支持OpenGL的OpenCV
参考文章:
[转]OpenCV2.4.12 开启OpenGL启用三维可视化支持(不需VIP)
OpenCV2.4.12 开启OpenGL启用三维可视化支持(优快云原文,需VIP)
对文章描述的补充与修正:
- 可以把BUILD_opencv_world勾上,这样就不用管理一大堆lib了,只用opencv_world开头的lib和dll就行了
- 文章中提到的python报错其实可以通过取消python相关选项解决(前提是你不需要用到python)
- 类似的,Java相关的其实也不必要,取消勾选
- test相关的应该也不必要,取消勾选能省一些make时间(图中勾选那个估计也不必要,但本人成功实现功能的版本配置就是如下,保险起见原样截图)
- 文章有提到“OpenGL仅支持32位操作系统”:
opencv官方文档也似乎的确这么说:- OpenCV:OpenGL 互操作性 - OpenCV 计算机视觉库
- 但实际上采用64位配置是没问题的,仅支持32位应该是OpenGL库只能用OpenGL32.lib
- 对于这一步
其实在#include <GL/gl.h>之前加#include<Windows.h>就行,因为GL.h可能因为权限不够不能更改
二、用WINDOW_OPENGL属性的OpenCV窗口显示Mat
1. 通过实现一个小效果,确认OpenGL功能启用(可选)
参考文章(可以先看看该文章中的代码对应效果能否实现,再进入用OpenGL支持的窗口显示Mat的步骤):
opengl学习笔记(二):使用OpenCV来创建OpenGL窗口 - feifanren - 博客园
2. 头文件和OpenGL相关库
#include<opencv2/opencv.hpp>
#include<opencv2/core/opengl.hpp>
#include<Windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "glu32.lib")
注意,因为这里同时包括了 #include<opencv2/opencv.hpp>和#include<Windows.h>,可能会遇到“byte不明确”类似的问题,参见:解决启用C++17后byte重定义的问题(byte ambiguous ) - 松山居士 - 博客园
本人的解决办法是删除所有.h中的using namespace std; 改为使用类似using std::cout;这样的语句(实际上更具体地说,为了方便,把常用的std命名空间里的using声明都放到一个头文件里,这样直接include这个头文件就省事了)
3.回调函数
后面在setOpenGlDrawCallback时会将其设置为OpenGL窗口的回调函数。
static void on_image_update_clbk(void* param) {
cv::ogl::Texture2D* backgroundTex = (cv::ogl::Texture2D*)param;
glEnable(GL_TEXTURE_2D);
backgroundTex->bind();
cv::ogl::render(*backgroundTex);
glDisable(GL_TEXTURE_2D);
}
4.绘制过程的总函数
本人是将其设为一个类里的成员函数的,下面的代码对应修改成了外部函数,若有需要可自行更改传参模式
注意,若之前已存在同名非WINDOW_OPENGL窗口,则WINDOW_OPENGL在此处并不会成功启用
另外,setOpenGlContext应在与OpenGL相关的操作前调用,如Texture2D的转换甚至是定义(不过Texture2D指针就无关了)
cv::ogl::Texture2D* tex=nullptr;
void showWindow(Mat& finalShow)
{
string winName="Your Window Name";// 可设为你自己想要的窗口名
namedWindow(winName, WINDOW_OPENGL);// 注意,若存在同名窗口,则WINDOW_OPENGL在此处并不会成功启用
// 若需要全屏可以启用
//if (FULL_SCREEN) {
// setWindowProperty(winName, WND_PROP_FULLSCREEN, WINDOW_FULLSCREEN);
//}
// 将openGL上下文设置为对应窗口
setOpenGlContext(winName);
// 将Mat转换为Texture2D
//cv::ogl::Texture2D tex(finalShow,1); // 也可以改为使用这行而把下面的if else注释掉,如此还需要把setOpenGlDrawCallback时的tex改为传入&tex。理论上性能会降低,但实际似乎并无大区别
if (tex == nullptr) {
tex = new ogl::Texture2D(finalShow,1);
}
else {
tex->copyFrom(finalShow);// 易错点!此处不能使用convertToTexture2D
}
// 防止低分辨率图像自动放大时的抗锯齿导致的模糊
GLuint id=tex->texId();
glBindTexture(GL_TEXTURE_2D, id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// 设置回调函数
setOpenGlDrawCallback(winName, on_image_update_clbk, tex);
// 设置鼠标回调,onMouse是自己写的,若不需要鼠标操控则不需要启用这行
//setMouseCallback(winName, onMouse, this);
// 不加下面这行会一直卡在第一帧
updateWindow(winName);
// 会降低一些帧数,但若没有其他的消息处理调用的话会导致无法显示
waitKey(1);
}
5.(附)waitKey的替代方案
在主循环调用即可,性能比waitKey好多了,而且也解决了负载较重时鼠标回调延迟的问题。
// 用于强制处理消息队列
void processMessages()
{
MSG msg; // 定义一个 MSG 结构体,用于存储消息
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg); // 将虚拟键消息转换为字符消息
DispatchMessage(&msg); // 将消息发送到窗口过程函数进行处理
}
}