1. 程式人生 > >ARCore之路-計算機視覺之邊緣檢測二

ARCore之路-計算機視覺之邊緣檢測二

  在前面我們最後檢測出的邊緣影象使用了黑白顏色,實際上,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吧。