2012-12-03 37 views
2

我參加了着色器課程,對計算機視覺和圖像處理感興趣。我想知道如何將GLSL着色器知識與圖像處理混合在一起?如果我使用GLSL實現圖像處理算法,我會獲得什麼?使用GLSL着色器進行圖像處理?

回答

-1

第一個明顯的答案是您獲得並行性。現在,爲什麼使用GLSL而不是說CUDA更靈活? GLSL不要求你有NVIDIA顯卡,所以它是一個更便攜的解決方案(儘管你仍然可以選擇OpenCL)。

您可以通過並行獲得什麼?大多數情況下,您可以獨立處理像素。例如,增加圖像的對比度通常需要您遍歷所有像素並應用像素值的仿射變換。如果每個像素都由一個單獨的線程處理,那麼您不必再執行此循環:您只需渲染一個四元組,然後應用在當前柵格化點處讀取紋理的像素着色器,然後輸出到渲染目標(或屏幕)變換的像素值。

缺點是您的數據需要駐留在GPU上:您需要將所有圖像傳輸到GPU,這可能需要一些時間,並且可以使並行化所帶來的加速無用。因此,GPU實現通常是在要進行的操作是計算密集型的時候,或者整個管道可以保留在GPU上時完成的(例如,如果目標是隻在屏幕上顯示修改後的圖像,則可以節省需求在CPU上傳回圖像)。

+1

的OpenCL爲什麼沒有提到,如果你談論的是CUDA? –

+0

根據「遊戲引擎寶石2」中描述的基準測試,GLSL在某些情況下勝過OpenCL和CUDA :) –

+0

Nicol:在我看來,它被提到我的答案的第三行,不是嗎? (我沒有編輯我的答案) – WhitAngl

-1

OpenGL 4.3(在SIGGRAPH 2012上發佈)支持Compute着色器。如果您嚴格執行圖形工作,並且已經使用OpenGL,則可能比OpenCL/OpenGL interop(或CUDA/OpenGL interop)更易於使用。

以下是Khronos關於何時使用4.3計算着色器與OpenCL的比較:Link to PDF; see slide 5

-1

案例研究:對CPU VS GPU片段着色器實時箱模糊

我實現了一個簡單的盒子模糊https://en.wikipedia.org/wiki/Box_blur算法的CPU和GPU的片段着色器,看看這是更快:

enter image description here

我的相機刷新率將FPS上限設置爲30,所以我測量了盒子的寬度,仍然保持30 FPS。

在一個聯想T430(2012),NVIDIA NVS5400的,Ubuntu 16.04與圖像尺寸960×540,最大寬度爲:

  • GPU:23
  • CPU:5

由於計算是二次的,加速是:

(23/5)^2 = 21.16 

在GPU上比CPU更快!

並非所有算法在GPU上都更快。例如,對單個圖像採用交換RGB的操作在CPU上達到30FPS,因此將GPU編程的複雜性添加到CPU上是沒有用的。

像其他CPU和GPU加速問題一樣,如果每個字節都有足夠的工作傳輸到GPU,並且基準測試是您能做的最好的事情,一般來說,二次算法或更糟的是GPU的一個很好的選擇。

代碼的主要部分(剛剛從GitHub克隆):

#include "common.h" 
#include "../v4l2/common_v4l2.h" 

static const GLuint WIDTH = 640; 
static const GLuint HEIGHT = 480; 
static const GLfloat vertices[] = { 
/* xy   uv */ 
    -1.0, 1.0, 0.0, 1.0, 
    0.0, 1.0, 0.0, 0.0, 
    0.0, -1.0, 1.0, 0.0, 
    -1.0, -1.0, 1.0, 1.0, 
}; 
static const GLuint indices[] = { 
    0, 1, 2, 
    0, 2, 3, 
}; 

static const GLchar *vertex_shader_source = 
    "#version 330 core\n" 
    "in vec2 coord2d;\n" 
    "in vec2 vertexUv;\n" 
    "out vec2 fragmentUv;\n" 
    "void main() {\n" 
    " gl_Position = vec4(coord2d, 0, 1);\n" 
    " fragmentUv = vertexUv;\n" 
    "}\n"; 
static const GLchar *fragment_shader_source = 
    "#version 330 core\n" 
    "in vec2 fragmentUv;\n" 
    "out vec3 color;\n" 
    "uniform sampler2D myTextureSampler;\n" 
    "void main() {\n" 
    " color = texture(myTextureSampler, fragmentUv.yx).rgb;\n" 
    "}\n"; 

static const GLchar *vertex_shader_source2 = 
    "#version 330 core\n" 
    "in vec2 coord2d;\n" 
    "in vec2 vertexUv;\n" 
    "out vec2 fragmentUv;\n" 
    "void main() {\n" 
    " gl_Position = vec4(coord2d + vec2(1.0, 0.0), 0, 1);\n" 
    " fragmentUv = vertexUv;\n" 
    "}\n"; 
static const GLchar *fragment_shader_source2 = 
    "#version 330 core\n" 
    "in vec2 fragmentUv;\n" 
    "out vec3 color;\n" 
    "uniform sampler2D myTextureSampler;\n" 
    "// pixel Delta. How large a pixel is in 0.0 to 1.0 that textures use.\n" 
    "uniform vec2 pixD;\n" 
    "void main() {\n" 

    /*"// Identity\n"*/ 
    /*" color = texture(myTextureSampler, fragmentUv.yx).rgb;\n"*/ 

    /*"// Inverter\n"*/ 
    /*" color = 1.0 - texture(myTextureSampler, fragmentUv.yx).rgb;\n"*/ 

    /*"// Swapper\n"*/ 
    /*" color = texture(myTextureSampler, fragmentUv.yx).gbr;\n"*/ 

    /*"// Double vision ortho.\n"*/ 
    /*" color = ("*/ 
    /*"  texture(myTextureSampler, fragmentUv.yx).rgb +\n"*/ 
    /*"  texture(myTextureSampler, fragmentUv.xy).rgb\n"*/ 
    /*" )/2.0;\n"*/ 

    /*"// Multi-me.\n"*/ 
    /*" color = texture(myTextureSampler, 4.0 * fragmentUv.yx).rgb;\n"*/ 

    /*"// Horizontal linear blur.\n"*/ 
    /*" int blur_width = 21;\n"*/ 
    /*" int blur_width_half = blur_width/2;\n"*/ 
    /*" color = vec3(0.0, 0.0, 0.0);\n"*/ 
    /*" for (int i = -blur_width_half; i <= blur_width_half; ++i) {\n"*/ 
    /*"  color += texture(myTextureSampler, vec2(fragmentUv.y + i * pixD.x, fragmentUv.x)).rgb;\n"*/ 
    /*" }\n"*/ 
    /*" color /= blur_width;\n"*/ 

    /*"// Square linear blur.\n"*/ 
    " int blur_width = 23;\n" 
    " int blur_width_half = blur_width/2;\n" 
    " color = vec3(0.0, 0.0, 0.0);\n" 
    " for (int i = -blur_width_half; i <= blur_width_half; ++i) {\n" 
    "  for (int j = -blur_width_half; j <= blur_width_half; ++j) {\n" 
    "   color += texture(\n" 
    "    myTextureSampler, fragmentUv.yx + ivec2(i, j) * pixD\n" 
    "   ).rgb;\n" 
    "  }\n" 
    " }\n" 
    " color /= (blur_width * blur_width);\n" 

    "}\n"; 

int main(int argc, char **argv) { 
    CommonV4l2 common_v4l2; 
    GLFWwindow *window; 
    GLint 
     coord2d_location, 
     myTextureSampler_location, 
     vertexUv_location, 
     coord2d_location2, 
     pixD_location2, 
     myTextureSampler_location2, 
     vertexUv_location2 
    ; 
    GLuint 
     ebo, 
     program, 
     program2, 
     texture, 
     vbo, 
     vao, 
     vao2 
    ; 
    unsigned int 
     cpu, 
     width, 
     height 
    ; 
    uint8_t *image; 
    float *image2 = NULL; 
    /*uint8_t *image2 = NULL;*/ 

    if (argc > 1) { 
     width = strtol(argv[1], NULL, 10); 
    } else { 
     width = WIDTH; 
    } 
    if (argc > 2) { 
     height = strtol(argv[2], NULL, 10); 
    } else { 
     height = HEIGHT; 
    } 
    if (argc > 3) { 
     cpu = (argv[3][0] == '1'); 
    } else { 
     cpu = 0; 
    } 

    /* Window system. */ 
    glfwInit(); 
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 
    window = glfwCreateWindow(2 * width, height, __FILE__, NULL, NULL); 
    glfwMakeContextCurrent(window); 
    glewInit(); 
    CommonV4l2_init(&common_v4l2, COMMON_V4L2_DEVICE, width, height); 

    /* Shader setup. */ 
    program = common_get_shader_program(vertex_shader_source, fragment_shader_source); 
    coord2d_location = glGetAttribLocation(program, "coord2d"); 
    vertexUv_location = glGetAttribLocation(program, "vertexUv"); 
    myTextureSampler_location = glGetUniformLocation(program, "myTextureSampler"); 

    /* Shader setup 2. */ 
    const GLchar *fs; 
    if (cpu) { 
     fs = fragment_shader_source; 
    } else { 
     fs = fragment_shader_source2; 
    } 
    program2 = common_get_shader_program(vertex_shader_source2, fs); 
    coord2d_location2 = glGetAttribLocation(program2, "coord2d"); 
    vertexUv_location2 = glGetAttribLocation(program2, "vertexUv"); 
    myTextureSampler_location2 = glGetUniformLocation(program2, "myTextureSampler"); 
    pixD_location2 = glGetUniformLocation(program2, "pixD"); 

    /* Create vbo. */ 
    glGenBuffers(1, &vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 
    glBindBuffer(GL_ARRAY_BUFFER, 0); 

    /* Create ebo. */ 
    glGenBuffers(1, &ebo); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); 
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 

    /* vao. */ 
    glGenVertexArrays(1, &vao); 
    glBindVertexArray(vao); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    glVertexAttribPointer(coord2d_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(vertices[0]), (GLvoid*)0); 
    glEnableVertexAttribArray(coord2d_location); 
    glVertexAttribPointer(vertexUv_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(vertices[0]))); 
    glEnableVertexAttribArray(vertexUv_location); 
    glBindVertexArray(0); 

    /* vao2. */ 
    glGenVertexArrays(1, &vao2); 
    glBindVertexArray(vao2); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    glVertexAttribPointer(coord2d_location2, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(vertices[0]), (GLvoid*)0); 
    glEnableVertexAttribArray(coord2d_location2); 
    glVertexAttribPointer(vertexUv_location2, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(vertices[0]))); 
    glEnableVertexAttribArray(vertexUv_location2); 
    glBindVertexArray(0); 

    /* Texture buffer. */ 
    glGenTextures(1, &texture); 
    glBindTexture(GL_TEXTURE_2D, texture); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 

    /* Constant state. */ 
    glViewport(0, 0, 2 * width, height); 
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 
    glActiveTexture(GL_TEXTURE0); 

    /* Main loop. */ 
    common_fps_init(); 
    do { 
     /* Blocks until an image is available, thus capping FPS to that. 
     * 30FPS is common in cheap webcams. */ 
     CommonV4l2_updateImage(&common_v4l2); 
     image = CommonV4l2_getImage(&common_v4l2); 
     glClear(GL_COLOR_BUFFER_BIT); 

     /* Original. */ 
     glTexImage2D(
      GL_TEXTURE_2D, 0, GL_RGB, width, height, 
      0, GL_RGB, GL_UNSIGNED_BYTE, image 
     ); 
     glUseProgram(program); 
     glUniform1i(myTextureSampler_location, 0); 
     glBindVertexArray(vao); 
     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 
     glBindVertexArray(0); 

     /* Optional CPU modification to compare with GPU shader speed. */ 
     if (cpu) { 
      image2 = realloc(image2, 3 * width * height * sizeof(image2[0])); 
      for (unsigned int i = 0; i < height; ++i) { 
       for (unsigned int j = 0; j < width; ++j) { 
        size_t index = 3 * (i * width + j); 

        /* Inverter. */ 
        /*image2[index + 0] = 1.0 - (image[index + 0]/255.0);*/ 
        /*image2[index + 1] = 1.0 - (image[index + 1]/255.0);*/ 
        /*image2[index + 2] = 1.0 - (image[index + 2]/255.0);*/ 

        /* Swapper. */ 
        /*image2[index + 0] = image[index + 1]/255.0;*/ 
        /*image2[index + 1] = image[index + 2]/255.0;*/ 
        /*image2[index + 2] = image[index + 0]/255.0;*/ 

        /* Square linear blur. */ 
        int blur_width = 5; 
        int blur_width_half = blur_width/2; 
        int blur_width2 = (blur_width * blur_width); 
        image2[index + 0] = 0.0; 
        image2[index + 1] = 0.0; 
        image2[index + 2] = 0.0; 
        for (int k = -blur_width_half; k <= blur_width_half; ++k) { 
         for (int l = -blur_width_half; l <= blur_width_half; ++l) { 
          int i2 = i + k; 
          int j2 = j + l; 
          // Out of bounds is black. TODO: do module to match shader exactly. 
          if (i2 > 0 && i2 < (int)height && j2 > 0 && j2 < (int)width) { 
           unsigned int srcIndex = index + 3 * (k * width + l); 
           image2[index + 0] += image[srcIndex + 0]; 
           image2[index + 1] += image[srcIndex + 1]; 
           image2[index + 2] += image[srcIndex + 2]; 
          } 
         } 
        } 
        image2[index + 0] /= (blur_width2 * 255.0); 
        image2[index + 1] /= (blur_width2 * 255.0); 
        image2[index + 2] /= (blur_width2 * 255.0); 
       } 
      } 
      glTexImage2D(
       GL_TEXTURE_2D, 0, GL_RGB, width, height, 
       0, GL_RGB, GL_FLOAT, image2 
      ); 
     } 

     /* Modified. */ 
     glUseProgram(program2); 
     glUniform1i(myTextureSampler_location2, 0); 
     glUniform2f(pixD_location2, 1.0/width, 1.0/height); 
     glBindVertexArray(vao2); 
     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 
     glBindVertexArray(0); 

     glfwSwapBuffers(window); 
     glfwPollEvents(); 
     common_fps_print(); 
    } while (!glfwWindowShouldClose(window)); 

    /* Cleanup. */ 
    if (cpu) { 
     free(image2); 
    } 
    CommonV4l2_deinit(&common_v4l2); 
    glDeleteBuffers(1, &vbo); 
    glDeleteVertexArrays(1, &vao); 
    glDeleteTextures(1, &texture); 
    glDeleteProgram(program); 
    glfwTerminate(); 
    return EXIT_SUCCESS; 
}