飞道的博客

积分图的一种CUDA并行运算

296人阅读  评论(0)

在前面的文章中,我们有讲积分图的基本原理、算法层面优化加速,以及SSE指令优化加速:

https://blog.csdn.net/shandianfengfan/article/details/109571376

https://blog.csdn.net/shandianfengfan/article/details/109571418

最近在项目中需要使用CUDA来优化非局部均值滤波算法,涉及到积分图的并行计算。因此本文我们来讲一下积分图的并行计算思路,并使用C++和CUDA来实现。

1. 前缀和的概念

我们举个简单的例子来说明前缀和,假设有一组数据:

其前缀和的计算过程如下:

以上计算过程概括成下面的公式:

所以,计算数组中每一个元素的前缀和,就是计算该元素与其前面的所有元素之和。

2. 分行、列并行计算

假设有一张m行n列的图像,我们知道,先分别计算其所有行的前缀和,得到行前缀和图像,再对行前缀和图像分别计算所有列的前缀和,即得到原图像的积分图。如下图所示:

以上计算过程为两个步骤:1. 计算行前缀和;2. 计算列前缀和。所以并行计算思路为:

1. 开启m个线程,每个线程分别对应原图像的一行,负责计算该行的前缀和,所有线程并行计算。该步骤计算结束之后,得到了m*n的行前缀和图像。

2. 开启n个线程,每个线程分别对应行前缀和图像的一列,负责计算该列的前缀和,所有线程并行计算。该步骤计算结束之后,则得到m*n的积分图像。

3. 分块并行计算

这是本文要实现的计算方法,确切地说,该方法也属于分行、列并行计算的方法,只不过它在分行列计算地基础上,进一步分块计算,看起来步骤变多了,实际上计算速度加快不少。

同样假设有一张m行n列的图像,现在要使用分块的思路并行计算其积分图,主要有6个步骤:

步骤一:将每一行数据分成每4个数据的数据块,开启m*(n/4)个线程,每个线程计算一个小块中4个数据的前缀和,所有线程并行计算。

步骤二:开启m个线程,每个线程对应一行数据,负责计算该行中所有小块中最后一个数据的前缀和。

步骤三:开启m*(n/4)个线程,每个线程对应一个小块。对于每一行,从第2个小块开始,将每小块中的数(除最后一个数外)加上它的前一小块的最后一个数,即可得到所有行的前缀和。

步骤四:将每一列数据分成每4个数据的数据块,开启(m/4)*n个线程,每个线程计算一个小块中4个数据的前缀和,所有线程并行计算。

步骤五:开启n个线程,每个线程对应一列数据,负责计算该列中所有小块中最后一个数据的前缀和。

步骤六:开启(m/4)*n个线程,每个线程对应一个小块。对于每一列,从第2个小块开始,将每小块中的数(除最后一个数外)加上它的前一小块的最后一个数,即可得到所有列的前缀和,也即原图的积分图像。

注意,这种分块的方法要求图像的行与列都是4的整数倍,如果不是则需要边缘填充使其为4的整数倍。

下面分步骤上代码。

步骤一核函数代码


   
  1. __ global__ void cuda_integal0_rc(float *src, float *integral, int row, int col)
  2. {
  3. int idx = threadIdx.x + blockDim.x * blockIdx.x; //col/4
  4. int idy = threadIdx.y + blockDim.y * blockIdx.y; //row
  5. if (idx < col / 4 && idy < row)
  6. {
  7. int offset = idy*col + idx * 4; //每个小块的首地址
  8. integral[offset] = src[offset];
  9. integral[offset + 1] = src[offset + 1] + integral[offset];
  10. integral[offset + 2] = src[offset + 2] + integral[offset + 1];
  11. integral[offset + 3] = src[offset + 3] + integral[offset + 2];
  12. }
  13. }

步骤二核函数代码


   
  1. __ global__ void cuda_integal1_rc(float *integral, int row, int col)
  2. {
  3. int idx = threadIdx.x + blockDim.x * blockIdx.x; //row
  4. if (idx < row)
  5. {
  6. int offset = idx*col;
  7. for ( int i = 7; i < col; i += 4)
  8. {
  9. integral[offset + i] += integral[offset + i - 4];
  10. }
  11. }
  12. }

步骤三核函数代码


   
  1. __ global__ void cuda_integal2_rc(float *integral, int row, int col)
  2. {
  3. int idx = threadIdx.x + blockDim.x * blockIdx.x; //col/4
  4. int idy = threadIdx.y + blockDim.y * blockIdx.y; //row
  5. if (idx > 0 && idx < col / 4 && idy < row)
  6. {
  7. int offset = idy*col + idx * 4; //每个小块的首地址
  8. float pre_sum = integral[idy*col + (idx* 4 - 1)]; //前一个小块的最后一个数
  9. integral[offset] += pre_sum;
  10. integral[offset + 1] += pre_sum;
  11. integral[offset + 2] += pre_sum;
  12. }
  13. }

步骤四核函数代码


   
  1. __ global__ void cuda_integal3_rc(float *integral, int row, int col)
  2. {
  3. int idx = threadIdx.x + blockDim.x * blockIdx.x; //col
  4. int idy = threadIdx.y + blockDim.y * blockIdx.y; //row/4
  5. if (idx < col && idy < row / 4)
  6. {
  7. int offset = idy* 4*col + idx; //每个小块的首地址
  8. int offset1 = offset + col;
  9. int offset2 = offset1 + col;
  10. int offset3 = offset2 + col;
  11. integral[offset1] += integral[offset];
  12. integral[offset2] += integral[offset1];
  13. integral[offset3] += integral[offset2];
  14. }
  15. }

步骤五核函数代码


   
  1. __ global__ void cuda_integal4_rc(float *integral, int row, int col)
  2. {
  3. int idx = threadIdx.x + blockDim.x * blockIdx.x; //col
  4. if (idx < col)
  5. {
  6. int col_4 = 4 * col;
  7. for ( int i = 7; i < row; i += 4)
  8.     {
  9. int offset = i*col + idx;
  10. integral[offset] += integral[offset - col_4];
  11. }
  12. }
  13. }

步骤六核函数代码


   
  1. __ global__ void cuda_integal5_rc(float *integral, int row, int col)
  2. {
  3. int idx = threadIdx.x + blockDim.x * blockIdx.x; //col
  4. int idy = threadIdx.y + blockDim.y * blockIdx.y; //row/4
  5. if (idx < col && idy > 0 && idy < row / 4)
  6. {
  7. int offset = idy* 4*col + idx; //每个小块的首地址
  8. float pre_sum = integral[(idy* 4 - 1)*col + idx]; //前一个小块的最后一个数
  9. integral[offset] += pre_sum;
  10. integral[offset + col] += pre_sum;
  11. integral[offset + col + col] += pre_sum;
  12. }
  13. }

主体函数代码


   
  1. void integal_cuda_rc(Mat src, Mat &dst)
  2. {
  3. Mat tmp;
  4. src.convertTo(tmp, CV_32F);
  5. int row_r = tmp.rows % 4;
  6. int col_r = tmp.cols % 4;
  7.    if (row_r || col_r)   //如果行与列不是4的整数倍,则边缘填充
  8. {
  9. copyMakeBorder(tmp, tmp, 0, row_r, 0, col_r, BORDER_REFLECT); //扩充边缘
  10. }
  11. const int row = tmp.rows;
  12. const int col = tmp.cols;
  13. float *integral;
  14. cudaMalloc(( void**)&integral, row*col* sizeof( float));
  15. dim3 k0block(16, 16); //row*(col/4)
  16. dim3 k0grid((col/4 + k0block.x - 1) / k0block.x, (row + k0block.y - 1) / k0block.y);
  17. dim3 k1block(16, 1); //row
  18. dim3 k1grid((row + k1block.x - 1) / k1block.x, 1);
  19. dim3 k2block = k0block; //row*(col/4)
  20. dim3 k2grid = k0grid;
  21. dim3 k3block(16, 16); //(row/4)*col
  22. dim3 k3grid((col + k3block.x - 1) / k3block.x, (row / 4 + k3block.y - 1) / k3block.y);
  23. dim3 k4block(16, 1); //col
  24. dim3 k4grid((col + k4block.x - 1) / k4block.x, 1);
  25. dim3 k5block = k3block;
  26. dim3 k5grid = k3grid;
  27. float *src_cuda;
  28. cudaMalloc(( void**)&src_cuda, row*col * sizeof( float));
  29. cudaMemcpy(src_cuda, ( float *)tmp.data, row*col * sizeof( float), cudaMemcpyHostToDevice);
  30. cuda_integal0_rc << <k0grid, k0block >> >(src_cuda, integral, row, col); //步骤一
  31. cuda_integal1_rc << <k1grid, k1block >> >(integral, row, col); //步骤二
  32. cuda_integal2_rc << <k2grid, k2block >> >(integral, row, col); //步骤三
  33. cuda_integal3_rc << <k3grid, k3block >> >(integral, row, col); //步骤四
  34. cuda_integal4_rc << <k4grid, k4block >> >(integral, row, col); //步骤五
  35. cuda_integal5_rc << <k5grid, k5block >> >(integral, row, col); //步骤六
  36. cudaMemcpy(( float *)tmp.data, integral, row*col * sizeof( float), cudaMemcpyDeviceToHost);
  37. cudaFree(integral);
  38. tmp(Rect( 0, 0, src.cols, src.rows)).copyTo(dst);
  39. }

好啦,本文就写到这里,下一篇文章更精彩哦,敬请期待!

微信公众号如下,欢迎扫码关注,欢迎私信技术交流:


转载:https://blog.csdn.net/shandianfengfan/article/details/113449531
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场