openGL shader实现DCT变换

目标:实现图像的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;

测试结果如下:

openGL shader实现DCT变换_第1张图片

 opencv的dct结果:

openGL的dct结果:

 

两者之差为:

 openGL shader实现DCT变换_第2张图片

基本上都是等于0,存在1e-5的量级误差,可能是GPU运算和CPU运算保留精度不同造成,但是可以基本上认定实现了图像的dct变换过程的openGL版本

 

 

 

你可能感兴趣的:(OpenGL)