ARCore之路-计算机视觉之边缘检测二

版权声明:David Wang原创ARCore文章,仅供学习研究之用,不得用于任何商业目的,未经授权不得转载! https://blog.csdn.net/yolon3000/article/details/84146331

  在前面我们最后检测出的边缘图像使用了黑白颜色,实际上,Sobel算子只适用于灰度图像。色彩图像相对于灰度图像更难处理,为了达到更好的效果,很多图像处理算法仅仅只适用于灰度图像。因此,在这种情况下,要求我们把图像转成灰度图像再进行处理。经Sobel算子处理的图像中的物体轮廓很适合于计算机进行下一步的处理。如在车牌识别中,我们先使用Sobel算子对采集的图像进行处理可以大致圈定车牌的位置,对进一步的OCR字符识别提供极大的便利。
  虽然说我们检测图像边缘很大一部分目的是为下一步计算机处理做准备,但有时我们也会使用到边缘检测后的图像作为特定的视觉效果。

一、Sobel边缘检测的变异

  在上节中,我们知道采用Sobel边缘检测后图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:
   G = G x 2 + G y 2 G=\sqrt{Gx^2+Gy^2}
  通常,为了提高效率 使用不开平方的近似值:
   G = G x + G y G=|Gx|+|Gy|
  实际上,我们还可以对x与y轴单独进行处理以达到特定的效果。如:
   G = G x G x G=Gx * Gx
   G = G y G y G=Gy * Gy

  为了达到特定的视觉效果,我们对EdgeDetector.Detect()方法进行如下修改,我们采取了隔行与隔列处理的方式,以达到不完全破坏原图像的目的。具体代码如下:

       private static void Sobel(byte[] outputImage, IntPtr inputImage, int width, int height, int rowStride)
        {
            // Adjust buffer size if necessary.
            int bufferSize = rowStride * height;
            if (bufferSize != s_ImageBufferSize || s_ImageBuffer.Length == 0)
            {
                s_ImageBufferSize = bufferSize;
                s_ImageBuffer = new byte[bufferSize];
            }
            // Move raw data into managed buffer.
            System.Runtime.InteropServices.Marshal.Copy(inputImage, s_ImageBuffer, 0, bufferSize);

            // Detect edges.
            int threshold = 128;
            
            for (int j = 1; j < height - 1; j+=2)
            {
                for (int i = 1; i < width - 1; i+=2)
                {
                    // Offset of the pixel at [i, j] of the input image.
                    int offset = (j * width) + i;

                    // Neighbour pixels around the pixel at [i, j].
                    int a00 = s_ImageBuffer[offset - rowStride - 1];
                    int a01 = s_ImageBuffer[offset - rowStride];
                    int a02 = s_ImageBuffer[offset - rowStride + 1];
                    int a10 = s_ImageBuffer[offset - 1];
                    int a12 = s_ImageBuffer[offset + 1];
                    int a20 = s_ImageBuffer[offset + rowStride - 1];
                    int a21 = s_ImageBuffer[offset + rowStride];
                    int a22 = s_ImageBuffer[offset + rowStride + 1];

                    int xSum = -a00 - (2 * a10) - a20 + a02 + (2 * a12) + a22;
                    int ySum = a00 + (2 * a01) + a02 - a20 - (2 * a21) - a22;
                    
                    if ((xSum * xSum) > threshold)
                    {
                        outputImage[offset] = 0x2F;
                    }
                    else if ((ySum * ySum) > threshold)
                    {
                        outputImage[offset] = 0xDF;
                    }
                    else
                    {  
                        outputImage[offset] = s_ImageBuffer[offset];
                    }
                }
            }
        }

  达到的效果如下:

DavidWang原创

二、性能提示

  在上面的演示代码中,我们使用了最直接的方式来实现,代码简洁易懂,但有一个问题是性能。

(一)、单线程

  我们的算法能达到我们要演示的目的,但是运算非常耗时,并且还是单线程。 对于一副图像,比如800x600像素,在前面处理时,我们是从第1个像素开始,一直计算到最后一个像素。其实,目前不论手机还是个人电脑,处理器都是多核。那么完全可以将整副图像分成若干块,比如cpu为4核处理器,那么可以分成4块,每块图像大小为200x150,这样程序可以创建4个线程,每个处理器执行一个线程,每个线程处理一个图像块。虽然这样操作后,运算速度不会显著提升4倍,因为线程创建、释放、上下文切换都要耗些时间。但运算速度还是可以得到明显提升。

(二)、纯CPU

  CPU 是个多面手,但不必把所有的事都交给CPU处理。实际上, GPU处理速度比CPU要快得多,并且GPU架构就是为并行处理任务而生。将图像处理和渲染交给GPU处理可以成百上千倍的提升性能。
  在很多图像处理中,在CPU平台上无法实现高清图像的实时处理,严重滞后于应用的需求,而采用GPU可以相对CPU提高数百倍的性能,快速实现高清图像的实时处理,给图像处理带来了新的方法。目前,GPU在石油勘测、流体动力学模拟、天文计算、分子动力学仿真、生物计算、图像处理、音频视频编解码、医疗成像、金融、数据库等领域得到广泛的应用,在很多应用中都可获得2-3个数量级的加速比,极大地提高性能。因此,还是将可并发的任务交给GPU吧。

猜你喜欢

转载自blog.csdn.net/yolon3000/article/details/84146331