深度学习(三十五)异构计算GLSL学习笔记(1)

异构计算GLSL学习笔记(1)

原文地址:http://blog.csdn.net/hjimce/article/details/51475644

作者:hjimce

最近开始学习深度学习的一些gpu编程,大体学了cuda后,感觉要在手机上跑深度学习,没有nvidia显卡,不能加速。所以只能老老实实的学习opengl的shader编程,进行gpu通用计算加速,总的感觉shader编程比cuda编程难,还好自己之前研究生的时候,经常用opengl的一些API函数,对opengl比较了解。对于每一种语言,最简单的学习程序就是:hello world,下面记录一下shader编程最简单的例子,利用gpu加速实现彩色图像转灰度图像。大体包含五个步骤:

1、初始化环境、创建shader程序等;

2、根据需求,编写片元着色器的纹理图像处理代码;

3、从cpu传入数据(利用uniform类型变量,传数据到shader中,如果是图片可以直接采用纹理传入);

4、设置渲染模型、纹理坐标,绘制矩形,进行渲染计算;

5、取回图片处理后的数据;

一、opengl与shader环境初始化、编译链接

(1)初始化环境

//1、初始化环境
	glutInit(&argc, argv);
	//glutInitWindowSize(512,512);  
	//glutInitWindowPosition(100,100); 
	glutCreateWindow("GLEW Test"); 
	glewExperimental = GL_TRUE;
	glewInit();
	if (!glewIsSupported("GL_VERSION_4_0")) 
	{
		std::cerr << "Failed to initialize GLEW with OpenGL 4.0!" << std::endl;
		return EXIT_FAILURE;
	}

(2)创建链接、编译shader程序

//2、读取、创建shader程序,编译连接等
	auto program_id = ShaderProgram("shader/gl_texture.vert", "shader/gl_texture.frag");
	glUseProgram(program_id);

ShaerProgram函数的代码如下:

GLuint ShaderProgram(const std::string &vertex_shader_file, const std::string &fragment_shader_file) {
  // 创建shader程序
  auto vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
  auto fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
  auto result = GL_FALSE;
  auto info_length = 0;

  // 读取shader源码
  std::ifstream vertex_shader_stream(vertex_shader_file);
  std::string vertex_shader_code((std::istreambuf_iterator<char>(vertex_shader_stream)), std::istreambuf_iterator<char>());

  std::ifstream fragment_shader_stream(fragment_shader_file);
  std::string fragment_shader_code((std::istreambuf_iterator<char>(fragment_shader_stream)), std::istreambuf_iterator<char>());

  // 编译顶点shader代码
  std::cout << "Compiling Vertex Shader ..." << std::endl;
  auto vertex_shader_code_ptr = vertex_shader_code.c_str();
  glShaderSource(vertex_shader_id, 1, &vertex_shader_code_ptr, NULL);
  glCompileShader(vertex_shader_id);

  // Check vertex shader log
  glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
  if (result == GL_FALSE) {
    glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_length);
    std::string vertex_shader_log((unsigned int)info_length, ' ');
    glGetShaderInfoLog(vertex_shader_id, info_length, NULL, &vertex_shader_log[0]);
    std::cout << vertex_shader_log << std::endl;
  }

  // 编译片元着色器代码
  std::cout << "Compiling Fragment Shader ..." << std::endl;
  auto fragment_shader_code_ptr = fragment_shader_code.c_str();
  glShaderSource(fragment_shader_id, 1, &fragment_shader_code_ptr, NULL);
  glCompileShader(fragment_shader_id);

  // Check fragment shader log
  glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
  if (result == GL_FALSE) {
    glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_length);
    std::string fragment_shader_log((unsigned long)info_length, ' ');
    glGetShaderInfoLog(fragment_shader_id, info_length, NULL, &fragment_shader_log[0]);
    std::cout << fragment_shader_log << std::endl;
  }

  // 创建链接程序
  std::cout << "Linking Shader Program ..." << std::endl;
  auto program_id = glCreateProgram();
  glAttachShader(program_id, vertex_shader_id);
  glAttachShader(program_id, fragment_shader_id);
  glBindFragDataLocation(program_id, 0, "FragmentColor");
  glLinkProgram(program_id);

  //打印编译信息,如果编译错误,就可以看见错误信息了
  glGetProgramiv(program_id, GL_LINK_STATUS, &result);
  if (result == GL_FALSE) {
    glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_length);
    std::string program_log((unsigned long)info_length, ' ');
    glGetProgramInfoLog(program_id, info_length, NULL, &program_log[0]);
    std::cout << program_log << std::endl;
  }
  glDeleteShader(vertex_shader_id);
  glDeleteShader(fragment_shader_id);

  return program_id;
}

二、编写shader程序

上面在创建shader程序的时候,需要shader源码,对于图像处理来说,顶点着色器我们一般不需要用到,主要是利用片元着色器进行图像处理的相关计算。

(1)顶点着色器源码文件gl_texture.vert的代码如下:

#version 400
//顶点着色器输入
in vec2 Position;
in vec2 TexCoord;

//顶点着色器的输出,这个变量会进入片元着色器
out vec2 fragTexCoord;

void main() {
  // Copy the input to the fragment shader
  fragTexCoord =TexCoord.xy;

  // Calculate the final position on screen
  // Note the visible portion of the screen is in <-1,1> range for x and y coordinates
  gl_Position = vec4(Position.x,Position.y, 0.0, 1.0);
}
(2)片元着色器gl_texture.frag
#version 400
// 需要注意opengl纹理输入
uniform sampler2D Texture;

// 来自顶点着色器
in vec2 fragTexCoord;
//输出 
out vec4 FragmentColor;

void main() {
  // Lookup the color in Texture on coordinates given by fragTexCoord
  vec3 pColor = texture(Texture, fragTexCoord.xy).rgb;
	//转换成灰度图像
  float gray=pColor.r*0.2126+ 0.7152* pColor.g + 0.0722*pColor.b;
  FragmentColor =vec4(gray, gray,gray, 1.0);

}

三、opencv读取图片、输入纹理、启用纹理等

//3、设置纹理相关参数、或者输入shader计算所需要的数据
	auto texture_id = LoadImage("2.jpg", height_width, height_width);//读入一张图片,转成纹理格式,把并把图片数据拷贝到opengl纹理单元。
	auto texture_attrib = glGetUniformLocation(program_id, "Texture");//找到shader程序中,变量名为Texture,类型为uniform的变量索引
	glUniform1i(texture_attrib,0);
	glActiveTexture(GL_TEXTURE0 + 0);//启用第一个纹理,并绑定纹理数据
	glBindTexture(GL_TEXTURE_2D, texture_id);

LoadImage函数代码如下:

GLuint LoadImage(const std::string &image_file, unsigned int width, unsigned int height)
{
  // Create new texture object
  GLuint texture_id;
  glGenTextures(1, &texture_id);
  glBindTexture(GL_TEXTURE_2D, texture_id);
 
  // Set mipmaps
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

  cv::Mat texture_cv=cv::imread(image_file);
  if (texture_cv.empty())
  {
	  return -1;
  }
  else 
  {
	  glTexImage2D(GL_TEXTURE_2D, 0, 3, texture_cv.cols, texture_cv.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, texture_cv.data);
	  return texture_id;
  } 

}

四、绘制渲染、计算

通过绘制一个矩形,该矩形的纹理刚好就是我们的图片,这样就可以实现gpu图片处理了:

//4、设置渲染相关参数(矩形4个顶点、及其对应的纹理坐标)
	InitializeGeometry(program_id);
//5、绘制、渲染
	glClearColor(0.f,0.f,0.f,1.f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

InitializeGeometry函数:

void InitializeGeometry(GLuint program_id) 
{
  // Generate a vertex array object
  GLuint vao;
  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);

  // 顶点缓存
  GLfloat tempv[12] = {1.0f,1.0f,-1.0f,1.0f,1.0f,-1.0f,-1.0f, -1.0f};  //二维顶点坐标,分别为矩形的四个顶点坐标
  std::vector<GLfloat> vertex_buffer(tempv , tempv+12);  
  // Generate a vertex buffer object
  GLuint vbo;
  glGenBuffers(1, &vbo);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glBufferData(GL_ARRAY_BUFFER, vertex_buffer.size() * sizeof(GLfloat), vertex_buffer.data(), GL_STATIC_DRAW);

  // Setup vertex array lookup
  auto position_attrib = glGetAttribLocation(program_id, "Position");
  glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
  glEnableVertexAttribArray(position_attrib);

  // Generate another vertex buffer object for texture coordinates
  GLfloat temptex[8] = {1.0f,0.0f,0.0f,0.0f,1.0f,1.0f,0.0f, 1.0f}; 
  std::vector<GLfloat> texcoord_buffer(temptex,temptex+8);

  GLuint tbo;
  glGenBuffers(1, &tbo);
  glBindBuffer(GL_ARRAY_BUFFER, tbo);
  glBufferData(GL_ARRAY_BUFFER, texcoord_buffer.size() * sizeof(GLfloat), texcoord_buffer.data(), GL_STATIC_DRAW);

  auto texcoord_attrib = glGetAttribLocation(program_id, "TexCoord");
  glVertexAttribPointer(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
  glEnableVertexAttribArray(texcoord_attrib);

  glBindVertexArray(vao);
}

这个需要设置好顶点坐标和纹理坐标。

五、取出数据到opencv中

渲染完毕后,我们就要把图像处理的结果保存回opencv的cv::Mat上,然后看看处理结果

void show(int height=height_width,int width=height_width)
{
	cv::Mat img=cv::Mat::zeros(height, width, CV_8UC3);

	//use fast 4-byte alignment (default anyway) if possible
	//glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4);

	//set length of one complete row in destination data (doesn't need to equal img.cols)
//glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize());
	glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);//opencv存储为BGR顺序
	cv::flip(img, img, 0);//需要翻转
	cv::imshow("result",img);
	cv::waitKey(0);

}

最后贴一下主程序流程的完整代码:
#include "stdafx.h"
#include <stdlib.h>
#include "include/glew.h"
#include "include/GLUT.H"
#include <opencv2/opencv.hpp>
#include "shader.h"
#define height_width 512


void show(int height=height_width,int width=height_width)
{
	cv::Mat img=cv::Mat::zeros(height, width, CV_8UC3);

	//use fast 4-byte alignment (default anyway) if possible
	//glPixelStorei(GL_PACK_ALIGNMENT, (img.step & 3) ? 1 : 4);

	//set length of one complete row in destination data (doesn't need to equal img.cols)
//glPixelStorei(GL_PACK_ROW_LENGTH, img.step/img.elemSize());
	glReadPixels(0, 0, img.cols, img.rows, GL_BGR, GL_UNSIGNED_BYTE, img.data);//opencv存储为BGR顺序
	cv::flip(img, img, 0);//需要翻转
	cv::imshow("result",img);
	cv::waitKey(0);

}


int main( int argc, char** argv )
{


//1、初始化环境
	glutInit(&argc, argv);
	//glutInitWindowSize(512,512);  
	//glutInitWindowPosition(100,100); 
	glutCreateWindow("GLEW Test"); 
	glewExperimental = GL_TRUE;
	glewInit();
	if (!glewIsSupported("GL_VERSION_4_0")) 
	{
		std::cerr << "Failed to initialize GLEW with OpenGL 4.0!" << std::endl;
		return EXIT_FAILURE;
	}
//2、读取、创建shader程序,编译连接等
	auto program_id = ShaderProgram("shader/gl_texture.vert", "shader/gl_texture.frag");
	glUseProgram(program_id);

//3、设置纹理相关参数、或者输入shader计算所需要的数据
	auto texture_id = LoadImage("2.jpg", height_width, height_width);//读入一张图片,转成纹理格式,把并把图片数据拷贝到opengl纹理单元。
	auto texture_attrib = glGetUniformLocation(program_id, "Texture");//找到shader程序中,变量名为Texture,类型为uniform的变量索引
	glUniform1i(texture_attrib,0);
	glActiveTexture(GL_TEXTURE0 + 0);//启用第一个纹理,并绑定纹理数据
	glBindTexture(GL_TEXTURE_2D, texture_id);


//4、设置渲染相关参数(矩形4个顶点、及其对应的纹理坐标)
	InitializeGeometry(program_id);
//5、绘制、渲染
	glClearColor(0.f,0.f,0.f,1.f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//6、取回数据到opencv,并显示结果

	show();//




	return 0;
}

结果:

       

原图                                                                                               处理结果

****************************转载请保留原文地址、作者等信息********************************

你可能感兴趣的:(深度学习(三十五)异构计算GLSL学习笔记(1))