首页 > 其他 > 详细

《Mali OpenCL SDK v1.1.0》教程样例之六“索贝尔滤波器”

时间:2014-03-14 15:49:22      阅读:391      评论:0      收藏:0      [点我收藏+]

  Sobel图像滤波器是一种简单的卷积滤波器,主要用于边缘检测算法。


样例结果

bubuko.com,布布扣

输入图像

bubuko.com,布布扣

输出图像


算法


  一种做图像边缘检测的技术是,找出图像的梯度。大梯度值的区域对应图像色彩或密度的剧变区域。典型地,这些区域是边缘。


  如果你对于幅图像卷积两个Sobel算子,你会得到两个输出:

 > X方向的梯度(dX)

 > Y方向的梯度(dY)


  通常梯度被合并,给出一个总的梯度图。


  Sobel算子是:

dX:

 -1  0  1
 -2  0  2
 -1  0  1

dY:

  1  2  1
  0  0  0
 -1 -2 -1

  卷积非常适合做并行化处理。每个输出像素仅依赖恒定的输入像素,而不是其它任何像素的输出。因此,每个像素可以独立和同时地计算。



实现


填充


  值得注意的是,我们在此并不考虑填充。在两个维度,输出图像相比小两个像素。因为,每个输出需要围绕它的像素,计算边沿像素的输出是不可能的。在此样例中,我们简单地把边缘输出像素用其初始值。


  有时需要输出信号的大小与输入信号的大小一样,这种种情况下,"填充"必须应用到输入中,考虑到滤波应用的实际本性上是减少大小的。填充策略会有不同,但是对于图像,通常的选择是在所有边上重复边缘值(例如,最外层的像素集),或者在一些边上(在某些情况下)。


实现细节


  简单起见,我们在一个8比特通道上实现Sobel滤波器。为了在RGB图像上做Sobel滤波,你可以独立地在每个通道上运行Sobel滤波,然后组合结果。


  在这个例子中,我们取了一张RGB图像,转换成8位辉度图,然后发送到GPU。


  每个Sobel计算给出一个中心掩码像素的输出。中心像素的输出值是围绕像素的3x3网格上的像素值乘以Sobel掩码的和。这可以被分成三个阶段,通过为网格上的每一行独立地做乘法和累加(这一方法被使用在这个样例中)。


代码


  注意:我们考虑矢量化是在本教程中使用的主要优化技术。为了显示使用适量的重要性,一个不包含矢量的Sobel滤波器的OpenCL实现被包含在sobel_no_vectors sample(sobel_no_vectors.cpp和sobel_no_vectors.cl)。在Mali-T600系列GPU上,矢量化的版本快很多。


  除非另作说明,否则所有的代码片段来自sobel.cl中的OpenCL内核。


1. 选择内核大小


  我们在内核中使用向量类型,因此我们实际上每个内核输出16个结果。看下面更多的向量化细节。内核使用3x3的Sobel滤波器到一个输入图像中的18x3的窗,在两个输出图像中来生成16x1结果,代表梯度的dx和dy。

    /*
     * Each kernel calculates 16 output pixels in the same row (hence the ‘* 16‘).
     * column is in the range [0, width] in steps of 16.
     * row is in the range [0, height].
     */
    const int column = get_global_id(0) * 16;
    const int row = get_global_id(1) * 1;
    /* Offset calculates the position in the linear data for the row and the column. */
    const int offset = row * width + column;
  当我们在sobel.cpp中排队内核时,相应地减少工作大小:

    /*
     * Each instance of the kernel operates on a 16 * 1 portion of the image.
     * Therefore, the global work size must be width / 16 by height / 1 work items.
     */
    size_t globalWorksize[2] = {width / 16, height / 1};

2. 加载输入数据


  Mali-T600系列GPU具有128位矢量寄存器,可以在矢量类型上做运算。因此,我们使用OpenCL矢量,来使得更加有效地使用硬件,从而有更高的性能。


  这里我们从一行数据中做矢量加载:

    /*
     * First row of input.
     * In a scalar Sobel calculation you would load 1 value for leftLoad, middleLoad and rightLoad.
     * In the vector case we load 16 values for each.
     * leftLoad, middleLoad and rightLoad load 16-bytes of data from the first row.
     * The data they load overlaps. e.g. for the first column and row, leftLoad is 0->15, middleLoad is 1->16 and rightLoad is 2->17.
     * So we‘re actually loading 18-bytes of data from the first row.
     */
    uchar16 leftLoad = vload16(0, inputImage + (offset + 0));
    uchar16 middleLoad = vload16(0, inputImage + (offset + 1));
    uchar16 rightLoad = vload16(0, inputImage + (offset + 2));

3. 转换数据

  在Mali-T600系列GPU上,扩展和收缩数据类型是一个很容易地操作。在此,我们将8比特每像素数据转换成16比特每像素。

    /*
     * Convert the data from unsigned chars to shorts (8-bit unsigned to 16-bit signed).
     * The calculations can overflow 8-bits so we require larger intermediate storage.
     * Additionally, the values can become negative so we need a signed type.
     */
    short16 leftData = convert_short16(leftLoad);
    short16 middleData = convert_short16(middleLoad);
    short16 rightData = convert_short16(rightLoad);

4. 做计算


  然后,我们在计算16个像素的结果。在Mali-T600系列GPU上,每个向量计算可以作为一个单一操作完成。

    /*
     * Calculate the results for the first row.
     * Looking at the Sobel masks above for the first line of input,
     * the dX calculation is the sum of 1 * leftData, 0 * middleData, and -1 * rightData.
     * The dY calculation is the sum of 1 * leftData, 2 * middleData, and 1 * rightData.
     * This is what is being calculated below, except we have removed the
     * unnecessary calculations (multiplications by 1 or 0) and we are calculating 16 values at once.
     * This pattern repeats for the other 2 rows of data.
     */
    short16 dx = rightData - leftData;
    short16 dy = rightData + leftData + middleData * (short)2;

5. 排列结果


  最后,我们收缩和存储数据。我们使用一个向量存储一次写16个结果:

    /*
     * Store the results.
     * The range of outputs from our Sobel calculations is [-1020, 1020].
     * In order to output this as an 8-bit signed char we must divide it by 8 (or shift right 3 times).
     * This gives the range [-128, 128]. Depending on what type of output you require,
     * (signed/unsigned, seperate/combined gradients) it is possible to do more of the calculations on the GPU using OpenCL.
     * In this sample we‘re assuming that the application requires signed uncombined gradient outputs.
     */
    vstore16(convert_char16(dx >> 3), 0, outputImageDX + offset + width + 1);
    vstore16(convert_char16(dy >> 3), 0, outputImageDY + offset + width + 1);
  因为数据作为两个分开的梯度图返回,我们在CPU上将它们先组合,然后将它们作为一个位图写入。



运行样例


  参见样例1。


《Mali OpenCL SDK v1.1.0》教程样例之六“索贝尔滤波器”,布布扣,bubuko.com

《Mali OpenCL SDK v1.1.0》教程样例之六“索贝尔滤波器”

原文:http://blog.csdn.net/cloud_desktop/article/details/21236095

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!