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

    科技2022-07-16  135

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

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

     

    以《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 };

    原图:

    卷积后图:

    结束。

    Processed: 0.010, SQL: 8