图像处理(使用计算着色器)

前面层卷积(使用片元着色器)还不够快,这里准备使用计算着色器。

先实现一个小目标:即使用计算着色器作图像处理。也就是一个卷积。

 

以《GPGPU基础(五):使用compute shader进行通用计算及示例》一文为模板。
 



有个疑问:该文为什么要计算2次,并使用了3个纹理呢?

终于在《OpenGL编程指南(第八版)-中文扫描版》的第12.4.2章《计算着色器-示例-图像处理》找到答案:第一次对图像水平处理,第二次垂直处理。

所以我们去掉哪个中间纹理,只要两个纹理(输入,输出),一次计算(卷积)就可以了。

先载入图像到下面结构中:

struct bmp_data
{
	int	width;    //宽
	int     height;   //高
	int     depth;	//通道 深度
	unsigned char * data; //rgbrgb...排列
};

设置纹理及传送格式:

    textureParameters.texTarget = GL_TEXTURE_2D;
	textureParameters.texInternalFormat =  GL_RGBA32F;
	textureParameters.texFormat = GL_RGB;//或GL_RGBA
	textureParameters.type =  GL_FLOAT;//GL_UNSIGNED_BYTE

不使用 GL_UNSIGNED_BYTE,是因为卷积核是float 的。(输入除255)

每个工作组只要一个计算单元:

layout (local_size_x = 1, local_size_y = 1) in;

需要 图像的宽(iWidth) * 高(iHeight) 个工作组:

	mNumGroupsX = iWidth;
	mNumGroupsY = iHeight;
    glDispatchCompute(mNumGroupsX, mNumGroupsY, 1);//调度计算

纹理图像附加到帧缓冲对象:

	//纹理图像附加到帧缓冲对象
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureParameters.texTarget, inputTexID, 0);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, textureParameters.texTarget, outputTexID, 0);

传送参数到着色器:

	glUniform1fv(glGetUniformLocation(glslProgram, "kernel"), 9, kernel);
	glUniform1i(glGetUniformLocation(glslProgram, "width"), iWidth);
	glUniform1i(glGetUniformLocation(glslProgram, "height"), iHeight);

总流程:

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

	bmp_data bmp;
	char name[] = "测试1.jpg";

	char jpgname[256];
	if (argc == 2)
		strcpy_s(jpgname, strlen(argv[1]) + 1, argv[1]);
	else
		strcpy_s(jpgname, strlen(name) + 1, name);


	//载入图片
	loadjpg(jpgname, bmp); 

	iWidth = bmp.width;
	iHeight = bmp.height;
	iSize = iWidth * iHeight;

    // 创建测试数据
    int  iNoData = 4 * iSize;        //数据总数
    //pfInput = new float[unNoData];
	bmp2rgba( bmp, pfInput);
    float *pfOutput = new float[iNoData];

    // 为GL创建变量
    textureParameters.texTarget = GL_TEXTURE_2D;
	textureParameters.texInternalFormat =  GL_RGBA32F;
	textureParameters.texFormat = GL_RGBA;//GL_RGB;
	textureParameters.type =  GL_FLOAT;
    CReader reader;

    // 初始化 glut  glew
    initGLUT(argc, argv);
    glewInit();
	    
    // 初始化FBO
    initFBO(iWidth, iHeight);


	//需要的计算工作组
	mNumGroupsX = iWidth;
	mNumGroupsY = iHeight;
	//mNumGroupsX = iWidth,	mNumGroupsY = iHeight 则 xSize=1,ySize=1
	//textureParameters.shader_source = ShaderSource(xSize, ySize);
	textureParameters.shader_source = //ShaderSource(1, 1);
	{
		"#version 430 core\n"
			"layout (local_size_x = 1, local_size_y = 1) in;\n"
			"uniform int width;\n"
			"uniform int height;\n"

			"// 传递卷积核\n"
			"uniform float kernel[9];\n"

			"layout(rgba32f, binding = 0) uniform image2D input_image;\n"
			"layout(rgba32f, binding = 1) uniform image2D output_image;\n"


			"void main(void)\n"
			"{\n"

			"	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n"
			"	if (pos.x >= width || pos.y >= height ) \n"
			"		return;\n"

			"	vec4 data ;\n"
			"	vec4 result = vec4(0.0, 0.0, 0.0, 0.0);\n"
			"	int k_index = 0;\n"
			"	int Radius = 1;//半核宽\n"
			"	for (int y = pos.y - Radius; y < pos.y + Radius + 1; y += 1) //对准核心\n"
			"	{\n"
			"		for (int x = pos.x - Radius; x < pos.x + Radius + 1; x += 1)\n"
			"		{\n"
			"			data = imageLoad(input_image, ivec2(x, y));\n"
			"			if (x >= 0.0 && y >= 0.0 && x < width && y < height)//相当于边界以 0 填充\n"
			"			{\n"
			"				result += data * kernel[k_index]; //积和\n"
			"			}\n"
			"			k_index++;\n"
			"		}\n"
			"	}\n"
			"	imageStore(output_image, pos.xy, result);\n"
			"}\n"
	};
	//printf(textureParameters.shader_source);
    initGLSL(GL_COMPUTE_SHADER);

    createTextures();

    performCompute(inputTexID, outputTexID);


    // 获取GPU结果
    transferFromTexture(pfOutput);


    //for (int i = 0; i < 15; i++) {//unNoData
    //    cout << "input:" << pfInput[i] << " output:" << pfOutput[i] << endl;
    //}
	rgba2bmp(pfOutput,bmp);
	savejpg(bmp, "44.jpg");

	//system("pause");
    // 清理
    glDetachShader(glslProgram, fragmentShader);
    glDeleteShader(fragmentShader);
    glDeleteProgram(glslProgram);
    glDeleteFramebuffersEXT(1, &fb);
    glDeleteTextures(1, &inputTexID);
    glDeleteTextures(1, &outputTexID);
    glutDestroyWindow(glutWindowHandle);

    // 出口
    delete pfInput;
    delete pfOutput;
    return EXIT_SUCCESS;
}

其它请去看《GPGPU基础(五):使用compute shader进行通用计算及示例》文。

当卷积核如下时的效果图:

	float kernel[9] = {
		// 'unsharp'   反锐化对比度增强滤波器
		-0.1667f,   -0.6667f,	-0.1667f,
		-0.6667f,    4.3333f,	-0.6667f,
		-0.1667f,   -0.6667f,	-0.1667f
	};

原图:

卷积后图:

结束。


版权声明:本文为juebai123原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。