学习记录 --用CUDA完成泛洪算法

算法思路

  • 传统的泛红算法主要有两种方法,递归或者队列扫描,这两种方法在总体思路大致都是从种子点开始,逐步扫描周围的符合要求的点(比如和种子点颜色距离小于一个规定的值),当出现符合要求的点后将此点染色,同时继续在此点周围扫描下一个符合要求的点。
  • 此cuda泛洪算法也借鉴了传统的cpu算法,从种子点开始“生长”,但与cpu算法不同的是,使用cuda时的“扫描”下一个符合要求的点时,是并行处理的。这就意味着,当生长的区域的接近圆形块状区域时,使用cuda的扫描将比cpu快很多倍,可以理解为cpu扫描一次只能判断一个点,cuda一次可以判断当前已染色区域轮廓外的一圈点。

具体实现

  • 首先确定cuda所使用的block与thread:因为存在扫描的循环何时停止问题,各线程之间需要一个共同的变量来判断while循环是否进行下去,而各线程的处理进度肯定各不相同,所以此时需要一个线程间的同步:__syncthread() 。所以,我选择在一个block里装满线程,正好也可以使用block内的共享内存来完成while循环的判断。
  • 借鉴cpu使用队列来存储需扫描的点的思路,我直接创建了一个与原始图片同样大小的数组state[](通过x和y计算offset,同样是一维数组),数组初始化全0,表示所有点未被访问过。在扫描的过程中,当某个点被染色后,就讲此点所对应的state数组中的值改为1,表示已访问过,且此后的扫描将不再访问此点。
  • 定义一个共享内存need_continue,作为主while循环的判定条件,定义一个共享内存more,表示此次循环有扫描到新的符合条件的点。在每一次的主while循环的开始,先将more置0,同步一下。此后若有线程扫描到新的点,便将more置1。此轮主循环的最后,若more是1,则主while循环继续,否则,就退出循环,结束计算。

具体代码

CUDA主函数

void floodFillwithGPU( uchar * mat_data, int rows, int cols, int startX, int startY,int randR,int randG,int randB) {
    uchar* gpu_mat_data;
    uchar* gpu_mat_state;
    cudaError_t cudaStatus;

    //申请空间
    cudaStatus = cudaMalloc((void**)&gpu_mat_data, rows * cols * 3);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto err;
    }
    cudaStatus = cudaMalloc((void**)&gpu_mat_state, rows * cols  );
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto err;
    }
    cudaStatus = cudaMemcpy(gpu_mat_data,mat_data, 
        rows * cols * 3,
        cudaMemcpyHostToDevice);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto err;
    }

    kernel << < 1, threadsPerBlock >> > (gpu_mat_data, gpu_mat_state, startX, startY, rows, cols, randR, randG, randB);
    
    cudaStatus = cudaMemcpy(mat_data, gpu_mat_data,
        rows * cols * 3,
        cudaMemcpyDeviceToHost);
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMemcpy failed!");
        goto err;
    }

err:
    cudaFree(gpu_mat_data);
    cudaFree(gpu_mat_state);
}

核函数

__global__ void kernel(unsigned char* mat_p, unsigned char* state_p, int startX, int startY , int rows, int cols, int randR, int randG, int randB) {
    __shared__ int R,G,B;
    __shared__ int need_continue;
    __shared__ int more;

    int size = rows * cols;
    int cnt = 0;
    int i;
    int offset = threadIdx.x;
    int root_offset = startX + startY * cols;

    while (offset < size) {
        //初始化,获得root颜色
        if(offset == root_offset) {
            R = mat_p[root_offset * 3 + 0];
            G = mat_p[root_offset * 3 + 1];
            B = mat_p[root_offset * 3 + 2];
            deal(offset , mat_p, state_p, R, G, B,randR,randG,randB);
            //more = true;
            state_p[offset] = 1;
        }
        else {
            state_p[offset] = 0;
        }
        offset += blockDim.x;
    }
    if (threadIdx.x == 0) {
        need_continue = 1;
        more = 0;
    }

    __syncthreads();

    while (need_continue) {
        offset = threadIdx.x;
        while (offset < size) {
            // cols 为每行元素个数
            if (state_p[offset] == 1) {
                if (!more) {
                    atomicAdd(&more, 1);
                }
                state_p[offset ] = 2;
                if (offset % cols == 0) {//left side
                    if (offset / cols == 0) { //top side
                        deal(offset + 1, mat_p, state_p, R, G, B, randR, randG, randB);
                        deal(offset + cols, mat_p, state_p, R, G, B, randR, randG, randB);
                    }
                    else if (offset / cols == rows -1 ) { //bottom
                        deal(offset + 1, mat_p, state_p, R, G, B, randR, randG, randB);
                        deal(offset - cols, mat_p, state_p, R, G, B, randR, randG, randB);
                    }
                    else {
                        deal(offset + 1, mat_p, state_p, R, G, B, randR, randG, randB);
                        deal(offset + cols, mat_p, state_p, R, G, B, randR, randG, randB);
                        deal(offset - cols, mat_p, state_p, R, G, B, randR, randG, randB);
                    }
                }
                else if (offset % cols == cols - 1) { //right side
                    if (offset / cols == 0) { //top side
                        deal(offset - 1, mat_p, state_p, R, G, B, randR, randG, randB);
                        deal(offset + cols, mat_p, state_p, R, G, B, randR, randG, randB);
                    }
                    else if (offset / cols == rows - 1) { //bottom
                        deal(offset - 1, mat_p, state_p, R, G, B, randR, randG, randB);
                        deal(offset - cols, mat_p, state_p, R, G, B, randR, randG, randB);
                    }
                    else {
                        deal(offset - 1, mat_p, state_p, R, G, B, randR, randG, randB);
                        deal(offset + cols, mat_p, state_p, R, G, B, randR, randG, randB);
                        deal(offset - cols, mat_p, state_p, R, G, B, randR, randG, randB);
                    }
                }
                else if (offset / cols == 0) { //just top
                    deal(offset - 1, mat_p, state_p, R, G, B, randR, randG, randB);
                    deal(offset + 1, mat_p, state_p, R, G, B, randR, randG, randB);
                    deal(offset + cols, mat_p, state_p, R, G, B, randR, randG, randB);
                }
                else if (offset / cols == rows - 1) { //just top
                    deal(offset - 1, mat_p, state_p, R, G, B, randR, randG, randB);
                    deal(offset + 1, mat_p, state_p, R, G, B, randR, randG, randB);
                    deal(offset - cols, mat_p, state_p, R, G, B, randR, randG, randB);
                }
                else {
                    deal(offset - cols, mat_p, state_p, R, G, B, randR, randG, randB);
                    deal(offset + cols, mat_p, state_p, R, G, B, randR, randG, randB);
                    deal(offset - 1, mat_p, state_p, R, G, B, randR, randG, randB);
                    deal(offset + 1, mat_p, state_p, R, G, B, randR, randG, randB);
                }
            }
            offset += blockDim.x;
        }
        __syncthreads();

        if (threadIdx.x == 0) {
            if (more) {
                need_continue = true;
                more = 0;
            }
            else {
                need_continue = false;
            }
        }
        __syncthreads();
    }
}

 上色函数

__device__ void deal( int new_offset, unsigned char* mat_p, unsigned char* state_p,int R,int G,int B, int randR, int randG, int randB) {
    if (state_p[new_offset] == 0) {
        int r = mat_p[new_offset * 3 + 0];
        int g = mat_p[new_offset * 3 + 1];
        int b = mat_p[new_offset * 3 + 2];
        if( fabsf(r-R)<10 && fabsf(g-G)<10 && fabsf(b-B)<10 ){
        //if(r==R && g==G && b==B){
            state_p[ new_offset ] = 1;
            mat_p[new_offset * 3 + 0] = randB;
            mat_p[new_offset * 3 + 1] = randG;
            mat_p[new_offset * 3 + 2] = randR;
        }
    }
}

最终效果

因为并行处理的存在,所以在处理块状区域时使用cuda比传统cpu算法快上不少。但对于细长条形的区域,此cuda法将比传统cpu算法慢上不少。当处理的区域为块状区域时(如下图)

种子点取紫色区域中心时,此cuda算法需15ms左右,传统cpu算法需110ms左右

种子点取蓝色区域红色‘+’位置时,此cuda算法需50ms左右,传统cpu算法需100ms左右

而当填充这种细长条形区域时,此cuda算法需1s左右,而传统cpu算法仅需110ms左右

而当填充上图橙红色区域时(上图橙红色区域为迷宫的‘路’,是完全连通的),对于这种既细长又有一定的宽度的图形,种子点选取为图片左上角圆形迷宫外蓝色‘+’处。此时此cuda算法需1150ms左右,传统cpu算法需1200ms左右。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值