前面层卷积(使用片元着色器)还不够快,这里准备使用计算着色器。
先实现一个小目标:即使用计算着色器作图像处理。也就是一个卷积。
以《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版权协议,转载请附上原文出处链接和本声明。