【问题标题】:cuda convolution mappingcuda 卷积映射
【发布时间】:2016-11-01 00:04:34
【问题描述】:

我正在尝试为每个线程块复制图像补丁和相关围裙到共享内存。

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

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

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

我使用 512x512px 图像编写示例

我的设置是:

//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;
      }
}

我的输入图像:

我的内核输出是:

其中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 是我想要处理的图像数量等于 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;
      }

    }

我现在的输出是:

更新 2(版本 2 -working-):

我已经实现了另一个版本的算法。为此,我遵循slide,我发现它非常有用且解释清楚,尤其是作者谈论卷积的部分幻灯片 27

我将 blockgrid 设置更改为:

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

内核调用却保持不变:

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

内核的参数在哪里:

  1. dimage_src: 大小为 height x width 的 unsigned char 缓冲区,其中包含输入图像。
  2. dimage_dst:**大小为 **height x width 的 unsigned char 缓冲区,其中包含我的内核生成的输出图像。
  3. 512:第三个参数是图片的宽度。
  4. 512:第四个参数是图片的高度。

记住我的图像样本是黑白,但是这个版本的侵蚀也可以使用灰度

这里是我的工作内核

#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;

        }

     }

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

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

【问题讨论】:

  • 我把代码放在 github 上好吗?
  • 我不能把所有的代码都放在帖子里,因为它太大了。但如果您认为这有助于阅读问题,我也会调用内核?
  • 代码属于问题,不在外部链接中。所以是clear about this(“最短代码...在问题本身)。如果代码对于问题来说太长了,那么你还没有创建正确的@ 987654329@.
  • 不够好。你如何启动你的内核? w? h?

标签: c++ image-processing cuda


【解决方案1】:

您只需要 [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 部分,只有线程块中的部分线程会工作,还需要额外的边界检查。

对于第二部分,你应该使用 thread(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

【讨论】:

  • 谢谢你的回答,现在我很困惑。我不明白您如何将平铺图像坐标映射到共享内存。你能给我解释一下吗?同样对于第一部分,我必须更改 buffer_in[gLocl] 的索引吗?那么我还需要将块尺寸更改为 20x20
  • @userfi 我认为我的解释很清楚。我不能做得更好,否则答案将太长。我建议您阅读我前几天给您的链接以了解更多详细信息,它们是完美的教学文档。
  • @Eric 我也尝试按照您的描述修改我的代码,但结果相似
  • @Eric 也许我明白了,网格大小必须是:grid(512/TILE_H,512/TILE_W) 正确吗?
  • 黑色细线不存在。第一部分是 TILE_H x TILE_W
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-06
  • 2017-12-11
  • 2021-12-02
  • 2015-05-07
  • 2012-10-21
  • 2012-04-26
  • 2012-07-04
相关资源
最近更新 更多