cuda卷积映射

时间:2016-06-29 10:27:10

标签: c++ image-processing cuda

我试图为每个线程块复制一个图片补丁和相对的共享内存。

我的数据被复制(我使用矩阵)到共享内存之后,我想要一个关系,它映射共享内存中的掩码中心,我考虑进行卷积和掩码的中心在图像缓冲区中。

我想要那样,因为如果我尝试对图像进行卷积,则共享内存中的掩码中心似乎与全局内存中存储的图像缓冲区中心不对应。

在下面的代码中我写了一个简单图像黑白侵蚀算法的例子,当我把卷积的结果输出到输出图像时似乎是中心不对应。

我使用 512x512 px图像

编写我的样本

我的设置是:

//block and grid size
dim3 block(16,16);
dim3 grid(512/(block.x),512/(block.y),1);

这是我的内核:

#define STREL_SIZE 5

#define TILE_W 16
#define TILE_H 16

#define R (STREL_SIZE/2)

//size of tile image + apron
#define BLOCK_W (TILE_W+(2*R))
#define BLOCK_H (TILE_H+(2*R))


 __global__ void erode_multiple_img_SM_v2(unsigned char * buffer_in,
                            unsigned char * buffer_out,
                            int w,int h ){

    // Data cache: threadIdx.x , threadIdx.y
    __shared__ unsigned char data[TILE_W +STREL_SIZE  ][TILE_H +STREL_SIZE ];


     int col = blockIdx.x * blockDim.x + threadIdx.x;
     int row = blockIdx.y * blockDim.y + threadIdx.y;

     // global mem address of this thread
     int gLoc =  row*w +col;


     int x, y;  // image based coordinate



     if((col<w)&&(row<h)) {
         data[threadIdx.x][threadIdx.y]=buffer_in[gLoc];

     if (threadIdx.y > (h-STREL_SIZE))
          data[threadIdx.x][threadIdx.y + STREL_SIZE]=buffer_in[gLoc + STREL_SIZE];

     if (threadIdx.x >(w-STREL_SIZE))
          data[threadIdx.x + STREL_SIZE][threadIdx.y]=buffer_in[gLoc+STREL_SIZE];

     if ((threadIdx.x >(w-STREL_SIZE)) && (threadIdx.y > (h-STREL_SIZE)))
          data[threadIdx.x+STREL_SIZE][threadIdx.y+STREL_SIZE] =     buffer_in[gLoc+2*STREL_SIZE];

     //wait for all threads to finish read
     __syncthreads();

      unsigned char min_value = 255;
      for(x=0;x<STREL_SIZE;x++){
          for(y=0;y<STREL_SIZE;y++){
              min_value = min( (data[threadIdx.x+x][threadIdx.y+y]) , min_value);
              }

          }
      buffer_out[gLoc]= min_value;
      }
}

我的输入图片:

input

我的内核输出是:

output

其中 w 是图片的宽度,等于 512 , 其中 h 是图像的高度,等于 512

我用以下内容调用内核:

 erode_multiple_img_SM<<<grid,block>>>(dimage_src,dimage_dst,512,512);

dimage_src 是输入图像,数组缓冲区不是矩阵, dimage_dst 是输出图像缓冲区。

每个缓冲区的大小为 nElem * nImg * sizeof(unsigned char),其中 nElem = 512 * 512 是缓冲区的大小, nImg < / strong>是我想要处理的图像数量等于 1 。 我哪里错了?

代码更新:

__global__ void erode_multiple_img_SM_v2(unsigned char * buffer_in,
                            unsigned char * buffer_out,
                            int w,int h ){

// Data cache: threadIdx.x , threadIdx.y
__shared__ unsigned char data[TILE_W + STREL_SIZE-1 ][TILE_H + STREL_SIZE-1 ];

// global mem address of this thread
int col = blockIdx.x * blockDim.x + threadIdx.x;
int row = blockIdx.y * blockDim.y + threadIdx.y;



int gLoc =  row*w +col;



// each threads loads four values from global memory into shared mem
int x, y;   // image based coordinate



if((col<w)&&(row<h)) {

    data[threadIdx.x][threadIdx.y] = buffer_in[gLoc];

     if (threadIdx.y > (TILE_H-STREL_SIZE+1))
          data[threadIdx.x][threadIdx.y + STREL_SIZE-1]=buffer_in[(row + STREL_SIZE-1)*w + col];

     if (threadIdx.x > (TILE_W-STREL_SIZE+1))
           data[threadIdx.x + STREL_SIZE-1][threadIdx.y] = buffer_in[row*w+col + STREL_SIZE-1];

     if ((threadIdx.x > (TILE_W-STREL_SIZE+1)) && (threadIdx.y > (TILE_H-STREL_SIZE+1)))
           data[threadIdx.x + STREL_SIZE-1][threadIdx.y + STREL_SIZE-1] = buffer_in[(row + STREL_SIZE-1)*w + col + STREL_SIZE-1];

    //wait for all threads to finish read
     __syncthreads();



      unsigned char min_value = 255;
      for(x=0;x<STREL_SIZE;x++){
          for(y=0;y<STREL_SIZE;y++){
              min_value = min( (data[threadIdx.x+x][threadIdx.y+y]) , min_value);
              }

          }
      buffer_out[gLoc]= min_value;
      }

    }

我的输出现在是:

new output

更新2(版本2 - 工作 - ):

我已经实现了算法的另一个版本。为此,我遵循我发现非常有用并且解释得很好的slide,特别是作者谈论卷积的部分幻灯片27

我将阻止网格设置更改为:

dim3 block(20,20);
dim3 grid(512/(block.x)+ block.x,512/(block.y)+block.y);

内核调用相反ramain相同:

erode_multiple_img_SM<<<grid,block>>>(dimage_src,dimage_dst,512,512);

内核的参数是:

  1. dimage_src:包含输入图像的大小为高度x宽度的无符号字符的缓冲区。
  2. dimage_dst:**包含大小**高度x宽度的无符号字符的缓冲区,包含我的内核生成的输出图像。
  3. 512:第三个参数是图像的宽度。
  4. 512:第四个参数是图像的高度。
  5. 请记住我的图片样本黑白,但此版本的侵蚀也适用于灰度

    这里是工作内核

    #define STREL_W 5
    #define STREL_H 5
    
    #define STREL_SIZE 5
    
    
    #define TILE_W 16
    #define TILE_H 16
    
    #define R (STREL_SIZE/2)
    
    
    #define BLOCK_W (TILE_W+(2*R))
    #define BLOCK_H (TILE_H+(2*R))
    
    __global__ void erode_multiple_img_working(unsigned char * buffer_in,
                                unsigned char * buffer_out,
                                int w,int h ){
    
    
        __shared__ unsigned char fast_acc_mat[BLOCK_w][BLOCK_H];
    
        int ty = threadIdx.y;
        int tx = threadIdx.x;
    
    
        int row_o = blockIdx.y * TILE_W + ty;
        int col_o = blockIdx.x * TILE_H + tx;
    
    
        int row_i = row_o - R;
        int col_i = col_o - R;
    
        //in of img size
        if((row_i >= 0) && (row_i < h) && (col_i >= 0) && (col_i < w) ){
    
            fast_acc_mat[ty][tx] = buffer_in[ row_i * w + col_i];
    
        }
        else{
    
            fast_acc_mat[ty][tx] = 0;
    
        }
    
    
        __syncthreads();
    
    
    
    
    
        if( ty < TILE_H && tx < TILE_W ){
    
            unsigned char min_val=255;
            for(int i = 0; i < STREL_SIZE; i++) {
                for(int j = 0; j < STREL_SIZE; j++) {
    
                    min_val = min( fast_acc_mat[i+ty][j+tx] , min_val );
    
                }
            }
            if(row_o < h && col_o < w)
                    buffer_out[row_o * w + col_o] = min_val;
    
            }
    
         }
    

    这是我的侵蚀图片(输出):

    image eroded

    我实现了一个方案,显示 Eric 描述的算法部分如何在共享内存中加载 TILE 的像素:

    enter image description here

1 个答案:

答案 0 :(得分:2)

你只需要[20] [20]共享内存,而不是[21] [21]。它应该改为

__shared__ unsigned char data[TILE_W + STREL_SIZE-1][TILE_H + STREL_SIZE-1];

另一个问题是数据加载。正确的方法是从输入读取(16 + 4)x(16 + 4)像素,共享内存,协同使用(16 x 16)个线程。这可以分为 4部分:

1)第一部分:主题(0:15,0:15)加载像素(0:15,0:15)

2)第二部分:主题(0:15,12:15)加载像素(0:15,16:19)

3)第三部分:主题(12:15,0:15)加载像素(16:19,0:15)

4)第四部分:主题(12:15,12:15)加载像素(16:19,16:19)

但是在您的代码中,您正在弄乱索引。 对于第2~4部分,只有线程块中的一些线程可以工作,并且还需要额外的边界检查。

对于第二部分,你应该使用线程(0:15,12:15)来读取像素(0:15,16:19)

 if (threadIdx.y > (TILE_H-STREL_SIZE))
      data[threadIdx.x][threadIdx.y + STREL_SIZE-1] = row + STREL_SIZE-1<h ? buffer_in[(row + STREL_SIZE-1)*w + col] : 0;

第3和第4部分需要进行类似的修改

 if (threadIdx.x > (TILE_W-STREL_SIZE))
      data[threadIdx.x + STREL_SIZE-1][threadIdx.y] = col + STREL_SIZE-1<w ? buffer_in[row*w+col + STREL_SIZE-1] : 0;

 if ((threadIdx.x > (TILE_W-STREL_SIZE)) && (threadIdx.y > (TILE_H-STREL_SIZE)))
      data[threadIdx.x + STREL_SIZE-1][threadIdx.y + STREL_SIZE-1] = (row + STREL_SIZE-1<h && col + STREL_SIZE-1<w) ? buffer_in[(row + STREL_SIZE-1)*w + col + STREL_SIZE-1] : 0;

然后你应该能够得到正确的结果图像,虽然会有2x2像素移位,因为你在(0 ... 4,0 ... 4)上进行卷积而不是(-2。 .2,-2 ...... 2)。

有关详情,请阅读

http://igm.univ-mlv.fr/~biri/Enseignement/MII2/Donnees/convolutionSeparable.pdf

https://www.evl.uic.edu/sjames/cs525/final.html