实现目标:通过opencv读取视频文件,并通过OpenGL渲染播放视频
OpenGL版本:3.3(貌似3.3以上的版本都可以,没测)
OpenGL库:glfw,glad,glm
说明:glfw库是glut的升级版本,依赖于OpenGL,用于显示窗口,捕捉窗口事件的API,下载地址:
https://www.glfw.org/download.html
glad库是glew库的升级版,OpenGL的核心函数库,下载地址:
github:https://github.com/Dav1dde/glad
在线服务:https://glad.dav1d.de/
配置方法:下载解压后,将src中的glad.c文件复制到项目源文件下,并添加包含到项目中,在项目属性中C/C++-->常规-->附加包含目录:添加glad的include路径
glm:图像旋转平移缩放库,定义了vecn,matn(n=1,2,3,4)数据结构,下载地址:
https://glm.g-truc.net/0.9.8/index.html
配置方法:在项目属性中C/C++-->常规-->附加包含目录:添加glm库所在路径
opencv:3.2
开发环境:VS2015+win7+64+release
首先初始化OpenGL的窗口环境:
// glfw初始化
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//次版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//OpenGL核心
#ifdef __APPLE__//iOS mac平台需要?不懂
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif
//创建窗口
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return NULL;
}
//glfw上下文为当前主上下文,不懂
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return NULL;
}
//顶点数组
float vertices[] = {
//0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
//0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
//-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
//-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left
// positions // texture coords
1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top right
1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom left
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
//索引数组
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VAO,VBO,EBO;
glGenVertexArrays(1, &VAO);//创建顶点数组对象,一个顶点数组对象可以连接很多顶点和索引
glGenBuffers(1, &VBO);//创建顶点缓存对象
glGenBuffers(1, &EBO);//创建索引缓存对象
glBindVertexArray(VAO);//绑定VAO
glBindBuffer(GL_ARRAY_BUFFER, VBO);//对VBO赋值
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//初始化EBO
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//对顶点缓存对象位置属性赋值说明(对应的GLSL中的顶点着色器文件中的layout(location==0))
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//对顶点缓存对象纹理属性赋值(对应的GLSL中的顶点着色器文件中的layout(location==1))
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 *sizeof(float)));
glEnableVertexAttribArray(1);
//解绑VBO,VAO
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
其中有一个回调函数framebuffer_size_callback,这个回调函数是一个视口函数,用于响应用户改变窗口大小等信息,并重新渲染,函数如下:
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
接下来就是通过opencv读取视频
//for opencv
#include
//for openGL
#include
#include
#include
//for std
#include
#include
#include
//for .h
#include"shader.h"
using namespace cv;
using namespace std;
int main()
{
string filename = "F:/data/video/1.avi";
cv::VideoCapture cap(filename);
if (!cap.isOpened())
{
cout << "Can not open video" << endl;
getchar();
return -1;
}
cv::Mat _frame,frame;
int i = 0;
while(true){
cap>>_frame;
if (_frame.empty())
{
break;
}
++i;
frame = _frame.clone();
cv::namedWindow("opencv", 0);
cv::imshow("opencv", frame);
cv::waitKey(5);
}
}
上面这个程序实现了通过opencv读取视频并显示的过程,现在需要通过OpenGL显示出来,首先需要定义一个全局的窗口大小
int width=800,height=400;
然后通过OpenGL加载纹理,首先在while循环外面加上纹理对象的创建(创建一次就行,不然会很耗内存):
unsigned int texture;
glGenTextures(1, &texture);
开始在frame的下面加载纹理:
glPixelStorei(GL_PACK_ALIGNMENT, 1);//像素对齐
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);//
glBindTexture(GL_TEXTURE_2D, texture);//绑定纹理对象
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//设置X轴环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);//设置Y轴环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//设置纹理X轴缩放插值方式?
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//设置纹理Y轴缩放插值方式?
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, frame.cols, frame.rows,0, GL_BGR, GL_UNSIGNED_BYTE, frame.data);//若是图像大小很大(width>5000,height>5000),倒数第三个参数需要改成GL_RGB(不然就会是黑屏,OpenGL不转化太大的纹理)
//std::cout << "glTexImage2D doubleFisheyeImage error:" << glGetError() << std::endl;
glGenerateMipmap(GL_TEXTURE_2D);//生成多级纹理
glBindTexture(GL_TEXTURE_2D, 0);//解绑纹理
然后开始渲染窗口:
Shader ourShader("test.vs", "test.fs");//fisheye2pano
ourShader.use();
ourShader.setInt("texture1", 0);
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// bind textures on corresponding texture units
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
ourShader.use();
// render container
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);//交换颜色缓冲
glfwPollEvents();//检查有没有出发什么事件(比如键盘输入、鼠标移动等)。
}
其中Shader所在的.h文件如下:
#ifndef SHADER_H
#define SHADER_H
#include
#include
#include
#include
#include
#include
class Shader
{
public:
unsigned int ID;//着色器程序对象id
// constructor generates the shader on the fly
// ------------------------------------------------------------------------
Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
{
// 1. retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::string geometryCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
std::ifstream gShaderFile;
// ensure ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
// if geometry shader path is present, also load a geometry shader
if (geometryPath != nullptr)
{
gShaderFile.open(geometryPath);
std::stringstream gShaderStream;
gShaderStream << gShaderFile.rdbuf();
gShaderFile.close();
geometryCode = gShaderStream.str();
}
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char * fShaderCode = fragmentCode.c_str();
// 2. compile shaders
unsigned int vertex, fragment;
// vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// if geometry shader is given, compile geometry shader
unsigned int geometry;
if (geometryPath != nullptr)
{
const char * gShaderCode = geometryCode.c_str();
geometry = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
checkCompileErrors(geometry, "GEOMETRY");
}
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
if (geometryPath != nullptr)
glAttachShader(ID, geometry);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessery
glDeleteShader(vertex);
glDeleteShader(fragment);
if (geometryPath != nullptr)
glDeleteShader(geometry);
}
// activate the shader
// ------------------------------------------------------------------------
void use()
{
glUseProgram(ID);
}
// utility uniform functions
// ------------------------------------------------------------------------
void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
// ------------------------------------------------------------------------
void setInt(const std::string &name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setFloat(const std::string &name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setVec2(const std::string &name, const glm::vec2 &value) const
{
glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec2(const std::string &name, float x, float y) const
{
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
}
// ------------------------------------------------------------------------
void setVec3(const std::string &name, const glm::vec3 &value) const
{
glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec3(const std::string &name, float x, float y, float z) const
{
glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
}
// ------------------------------------------------------------------------
void setVec4(const std::string &name, const glm::vec4 &value) const
{
glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec4(const std::string &name, float x, float y, float z, float w)
{
glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);
}
// ------------------------------------------------------------------------
void setMat2(const std::string &name, const glm::mat2 &mat) const
{
glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
// ------------------------------------------------------------------------
void setMat3(const std::string &name, const glm::mat3 &mat) const
{
glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
// ------------------------------------------------------------------------
void setMat4(const std::string &name, const glm::mat4 &mat) const
{
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
private:
// utility function for checking shader compilation/linking errors.
// ------------------------------------------------------------------------
void checkCompileErrors(GLuint shader, std::string type)
{
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif
最后在while循环后面删除和释放OpenGL的对象:
glDeleteTextures(1, texture);
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glfwTerminate();
大功告成!!