【问题标题】:OpenCL Kernel for CannyCanny 的 OpenCL 内核
【发布时间】:2017-11-05 16:32:21
【问题描述】:

我正在尝试使用 OpenCL 内核 以非常简单的方式实现 Canny Edge Detection

我正在使用原始的SobelFilter 内核来执行非最大抑制和阈值处理等步骤。

但我对达到像素并对其进行数学计算感到迷茫:

__kernel void sobel_filter(__global uchar4* inputImage, __global uchar4* outputImage)

你能给我一些想法或展示简单的例子来实现这一点吗?将不胜感激。 问候。

【问题讨论】:

  • 运行时遇到什么错误?
  • 你是如何将数据传递给 GPU 的?作为图像缓冲区还是简单缓冲区?
  • hypot 表示sqrt(x² + y²),除以 2 意味着它只占用一半,例如使用 char(因此最大为 255 的一半)作为缓冲区而不是 uchar?采用 float4 的 sqrt 也意味着分别对每个元素(其中 4 个)进行 sqrt,就像这里的红色、绿色、蓝色一样。 convert_ 是高效转换(可能)

标签: c++ image-processing opencl canny-operator sobel


【解决方案1】:

Sobel 过滤器在内核执行中本质上可分离为 X 和 Y 维度。因此,可以在同一个内核循环中仅扫描 X 维度或仅 Y 维度或两者都进行扫描,以实现边缘特征检测。

在这里使用用户azer89的解决方案:Image Processing - Implementing Sobel Filter

我准备了这个内核:

__kernel void postProcess(__global uchar * input, __global uchar * output)
{
    int resultImgSize=1024;
    int pixelX=get_global_id(0)%resultImgSize; // 1-D id list to 2D workitems(each process a single pixel)
    int pixelY=get_global_id(0)/resultImgSize;
    int imgW=resultImgSize;
    int imgH=resultImgSize;


    float kernelx[3][3] = {{-1, 0, 1}, 
                           {-2, 0, 2}, 
                           {-1, 0, 1}};
    float kernely[3][3] = {{-1, -2, -1}, 
                           {0,  0,  0}, 
                           {1,  2,  1}};

    // also colors are separable
    int magXr=0,magYr=0; // red
    int magXg=0,magYg=0;
    int magXb=0,magYb=0;

    // Sobel filter
    // this conditional leaves 10-pixel-wide edges out of processing
    if( (pixelX<imgW-10) && (pixelY<imgH-10) && (pixelX>10) && (pixelY>10) )
    { 
        for(int a = 0; a < 3; a++)
        {
            for(int b = 0; b < 3; b++)
            {            
                int xn = pixelX + a - 1;
                int yn = pixelY + b - 1;

                int index = xn + yn * resultImgSize;
                magXr += input[index*4] * kernelx[a][b];
                magXg += input[index*4+1] * kernelx[a][b];
                magXb += input[index*4+2] * kernelx[a][b];
                magYr += input[index*4] * kernely[a][b];
                magYg += input[index*4+1] * kernely[a][b];
                magYb += input[index*4+2] * kernely[a][b];
            }
         }
    }

    // magnitude of x+y vector
    output[(pixelX+pixelY*resultImgSize)*4]  =sqrt((float)(magXr*magXr + magYr*magYr)) ;
    output[(pixelX+pixelY*resultImgSize)*4+1]=sqrt((float)(magXg*magXg + magYg*magYg)) ;
    output[(pixelX+pixelY*resultImgSize)*4+2]=sqrt((float)(magXb*magXb + magYb*magYb)) ;
    output[(pixelX+pixelY*resultImgSize)*4+3]=255;

}

这里的索引与 4 相乘,因为它们被解释为 uchar 数组作为内核参数。 uchar 是 OpenCL 中的一个字节(至少对于我的系统而言)。

这是一个视频:


Sobel Filter Example

如果它也适合你,你应该接受azer89 的解决方案。但这不是很优化,对于低端 GPU 可能需要 1-2 毫秒,而对于 1024x1024 图像只有 CPU 则更长时间。图像数据使用字节数组(C# 语言)发送到 OpenCL 缓冲区(不是图像缓冲区),内核启动选项为:

  • 全局范围 = 1024*1024(每个像素处理 1 个线程)
  • 本地范围 = 256(这不重要)
  • 缓冲区拷贝大小1024*1024*4(rgba格式字节)

这里的 kernelx 和 kernely 2D 数组也是 float,因此将它们设为 char 可以使其更快。如果结果看起来比预期的要丰富多彩,您也可以检查结果(钳位,除法,...)。主机端表示/解释对于处理颜色下溢和溢出也很重要。

【讨论】:

  • 感谢侯赛因先生的解答和指导。它就像一个 魅力!
  • 把更多的精力放在 C++ 方面。那么,如果您使用 sycl 和类似语法的最新 opencl 糖,那么您将不会有任何困难。
【解决方案2】:

ARM 计算库有精巧的实现 Canny CL kernel

【讨论】:

  • 感谢您的指导!
猜你喜欢
  • 2011-12-06
  • 2019-12-10
  • 2016-01-17
  • 2018-02-08
  • 2013-04-24
  • 2013-05-01
  • 2019-05-26
  • 2012-02-05
相关资源
最近更新 更多