目标:实现图像的DCT变换
过程:
1)通过opencv读取图像;
2) 构建openGL窗口及初始化着色器程序;
3) 通过DCT原理编写shader脚本
4) 通过FBO离屏渲染得到DCT图像;
5) 和opencv自带的DCT函数结果对比。
4)在窗口中显示图像。
上代码:
#include
#include
#include
#include
#include"shader.h"
#include"openGLinit.h"
using namespace std;
using namespace cv;
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
int main()
{
int w= 800, h= 600;
GLFWwindow*window = init(w, h);
std::string imName = "F:/chess.jpg";
cv::Mat im = cv::imread(imName);
//resize image and calculate dct image
cv::resize(im,im,cv::Size(w,h));
cv::Mat dctSrc;
im.convertTo(dctSrc, CV_32F);
std::vector vImgs;
cv::split(dctSrc,vImgs);
for (int i = 0; i < vImgs.size(); ++i)
{
cv::dct(vImgs[i], vImgs[i]);
}
cv::merge(vImgs,dctSrc);
//init VAO,VBO
unsigned int Texture;
glGenTextures(1, &Texture);
GenTextureByMat(Texture, im);
float vertices[] = {
1.0f, 1.0f, 1.0f, 1.0f, // top right
1.0f, -1.0f, 1.0f, 0.0f, // bottom right
-1.0f, 1.0f, 0.0f, 1.0f, // top left
1.0f, -1.0f, 1.0f, 0.0f, // bottom right
-1.0f, -1.0f,0.0f, 0.0f, // bottom left
-1.0f, 1.0f,0.0f, 1.0f // top left
};
std::vector params(2, 2);
unsigned int VAO1, VBO1;
initVAO(VAO1, VBO1, vertices, 6, params);
//init shader program
time_t t0 = clock();
Shader dctshader2("dct.vs", "dctRow.fs");
Shader dctshader3("dct.vs", "dct2.fs");
Shader screenShader("5.1.framebuffers_screen.vs", "5.1.framebuffers_screen.fs");
dctshader2.use();
dctshader2.setInt("src", 0);
dctshader2.setVec2("textureSize", w, h);
dctshader3.use();
dctshader3.setInt("src", 0);
dctshader3.setVec2("textureSize", w, h);
screenShader.use();
screenShader.setInt("image", 0);
//init FBO
GLuint textures[1];
GLuint FBO1;
initFBO32F(FBO1, textures, 1, w, h);
GLuint dcttextures[1];
GLuint FBO2;
initFBO32F(FBO2, dcttextures, 1, w, h);
//first step for dct
glViewport(0, 0, w, h);
glBindFramebuffer(GL_FRAMEBUFFER, FBO1);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Texture);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
dctshader2.use();
glBindVertexArray(VAO1);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//second step for dct
glBindFramebuffer(GL_FRAMEBUFFER, FBO2);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
dctshader3.use();
glBindVertexArray(VAO1);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
cv::Mat DCTYImage2 = cv::Mat(cv::Size(w, h),CV_32FC3);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0, 0, w, h, GL_RGB, GL_FLOAT, DCTYImage2.data);
DCTYImage2 *= 255.0f;
cv::Mat diff = DCTYImage2 - dctSrc;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
time_t t1 = clock();
std::cout << "total time:" << t1 - t0 << std::endl;
//show in the openGL windows
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(VAO1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Texture);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
glfwPollEvents();
}
//delete all the malloc
glDeleteVertexArrays(1, &VAO1);
glDeleteBuffers(1, &VBO1);
glDeleteTextures(1, &Texture);
glDeleteTextures(1, textures);
glDeleteTextures(1, dcttextures);
glDeleteFramebuffers(1, &FBO1);
glDeleteFramebuffers(1, &FBO2);
glfwTerminate();
return 0;
}
其中include的几个程序中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
openGLinit.h 代码:
#include
#include
#include
#include
#include
static int index = 0, nextIndex = 1;
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
//init windows
GLFWwindow* init(int width, int height)
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//次版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//使用核心模式
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif
GLFWwindow*window = glfwCreateWindow(width, height, "LearnOpenGL", NULL, NULL);//创建窗口,设置窗口大小
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return NULL;
}
glfwMakeContextCurrent(window);//将窗口的上下文设置为当前线程的主上下文
//视口回调函数,这样OpenGL才知道怎样根据窗口大小显示数据和坐标
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))//初始化GLAD
{
std::cout << "Failed to initialize GLAD" << std::endl;
return NULL;
}
return window;
}
//init VAo
void initVAO(unsigned int& VAO, unsigned int& VBO, float* vertextices, const int& num, std::vector& vertexParams)
{
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
int numPart = vertexParams.size();
int numAll = 0;
for (int i = 0; i < numPart; ++i)
numAll += vertexParams[i];
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*numAll*num, vertextices, GL_STATIC_DRAW);
int offset = 0;
for (int i = 0; i < numPart; ++i)
{
if (i != 0)
offset += vertexParams[i - 1];
glVertexAttribPointer(i, vertexParams[i], GL_FLOAT, GL_FALSE, numAll * sizeof(float), (void*)(offset * sizeof(float)));
glEnableVertexAttribArray(i);
}
}
//generate texture by opencv Mat
void GenTextureByMat(unsigned int& texture, const cv::Mat& img)
{
int cn = img.channels();
assert(!img.empty() && (cn == 1 || cn == 2 || cn == 3) && (img.type() == CV_8U || img.type() == CV_32F));
glBindTexture(GL_TEXTURE_2D, texture);
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//std::cout << "glGenTextures error:" << glGetError() << std::endl;
if (cn == 1)
{
if (img.type() == CV_8UC1)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, img.cols, img.rows, 0, GL_RED,
GL_UNSIGNED_BYTE, img.data);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, img.cols, img.rows, 0, GL_RED,
GL_FLOAT, img.data);
}
}
else if (cn == 2)
{
if (img.type() == CV_8UC2)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, img.cols, img.rows, 0, GL_RG,
GL_UNSIGNED_BYTE, img.data);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, img.cols, img.rows, 0, GL_RG,
GL_FLOAT, img.data);
}
}
else {
if (img.type() == CV_8UC3)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.cols, img.rows, 0, GL_RGB,
GL_UNSIGNED_BYTE, img.data);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, img.cols, img.rows, 0, GL_RGB,
GL_FLOAT, img.data);
}
}
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
//init FBO
void initFBO(unsigned int& FBO, unsigned int* textures,/*, GLuint& rbo,*/
const int& numColorTexture, const int& width, const int& height)
{
//frame buffer
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
//color buffer
glGenTextures(numColorTexture, textures);
for (int i = 0; i < numColorTexture; ++i)
{
glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i], 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
// render buffer object
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "setup texture framebuffer failed" << glCheckFramebufferStatus(GL_FRAMEBUFFER) << std::endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
//init float FBO
void initFBO32F(unsigned int& FBO, unsigned int* textures,/*, GLuint& rbo,*/
const int& numColorTexture, const int& width, const int& height)
{
//frame buffer
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
//color buffer
glGenTextures(numColorTexture, textures);
for (int i = 0; i < numColorTexture; ++i)
{
glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, height, 0, GL_RGB, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i], 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
// render buffer object
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "setup texture framebuffer failed" << glCheckFramebufferStatus(GL_FRAMEBUFFER) << std::endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
时间是注释了将openGL中的纹理下载到本地的那几行代码运行时间,注释掉的代码如下:
cv::Mat DCTYImage2 = cv::Mat(cv::Size(w, h), CV_32FC3);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0, 0, w, h, GL_RGB, GL_FLOAT, DCTYImage2.data);
DCTYImage2 *= 255.0f;
cv::Mat diff = DCTYImage2 - dctSrc;
测试结果如下:
opencv的dct结果:
openGL的dct结果:
两者之差为:
基本上都是等于0,存在1e-5的量级误差,可能是GPU运算和CPU运算保留精度不同造成,但是可以基本上认定实现了图像的dct变换过程的openGL版本