1. 程式人生 > >OpenCV4Android學習之影象基本特徵檢測

OpenCV4Android學習之影象基本特徵檢測

影象中的資訊包括邊緣、直線、橢圓、色塊或輪廓、角點等形式,這些資訊在計算機視覺和影象處理語境中通常被稱為特徵。下面就來了解一些結合OpenCV在Android平臺上的常規的特徵檢測演算法,這裡使用AndroidStudio開發平臺,當然Eclipse也一樣。如果對在這兩個IDE上部署OpenCV不熟悉的話,可以參考我之前寫的這篇文章:Eclipse與AndroidStudio關於OpenCV4Android庫的部署

1.邊緣和焦點檢測
邊緣檢測和角點檢測時兩種最基本也是非常有用的特徵檢測演算法,這些演算法包括高斯差分、Canny邊緣檢測器、Sobel運算元和Harris角點。比如我們想在一幅圖中找到不同目標的邊界或者角點,用於分析目標在影象中的旋轉或移動等情況,這時候就需要知道邊緣和角點的資訊了。

  • 高斯差分
    這裡我們先了解下什麼是邊緣和高斯模糊方法,因為下面要用到邊緣這個性質和對影象進行高斯模糊來計算邊緣點,即邊緣點。

    • 簡單的說,邊緣就是影象中畫素亮度變化明顯的點。
    • 高斯(Gaussian)模糊是最常用的模糊方法,是將指定畫素變換為其與周邊畫素加權平均後的值,權重就是高斯分佈函式計算出來的值。OpenCV提供了GaussianBlur()的內建函式,我們可以在應用中使用它執行高斯模糊。這裡是一篇關於高斯模糊演算法的介紹:高斯模糊演算法

    形如:

    Mat src;
    Imgproc.GaussianBlur(src,src,new Size(6,6),0);

    讓我們回到高斯差分技術,其演算法分成三步:

    • 將影象轉換為弧度影象
    • 用兩個不同的模糊半徑對灰色影象執行高斯模糊
    • 對上面兩幅影象相減,得到一副只包含邊緣點的結果影象

    在應用中,建立一個用於給定影象計算邊緣的函式:

    /**
     * 高斯差分
     */
    private void DifferenceOfGaussian() {
        Mat grayMat = new Mat();
        Mat blur1 = new Mat();
        Mat blur2 = new Mat();
        //將影象轉換為灰度
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
        //以兩個不同的模糊半徑對影象做模糊處理,前兩個引數分別是輸入和輸出影象,第三個引數指定應用濾波器時所用核的尺寸,最後一個引數指定高斯函式中的標準差數值
Imgproc.GaussianBlur(grayMat, blur1, new Size(15, 15), 5); Imgproc.GaussianBlur(grayMat, blur2, new Size(21, 21), 5); //將兩幅模糊後的影象相減 Mat DoG = new Mat(); Core.absdiff(blur1, blur2, DoG); //反轉二值閾值化 Core.multiply(DoG, new Scalar(100), DoG); Imgproc.threshold(DoG, DoG, 50, 255, Imgproc.THRESH_BINARY_INV); //將Mat轉換為點陣圖 Utils.matToBitmap(DoG, currentBitmap); loadImageToImageView(); } /** * 設定影象 */ private void loadImageToImageView() { ImageView imgView = (ImageView) findViewById(R.id.image_view); imgView.setImageBitmap(currentBitmap); }

處理結果圖:

高斯差分

  • Canny邊緣檢測器
    Canny邊緣檢測器演算法使用了多向灰度梯度和滯後閾值化等複雜技巧,它是邊緣檢測的最優方法。
    演算法分為四個步驟:
    • 平滑圖:使用合適的模糊半徑執行高斯模糊來減少影象內的噪聲
    • 計算影象梯度:計算影象梯度,並將其分為垂直、水平和斜對角
    • 非最大值:由影象梯度計算得到梯度方向,檢查某一畫素在梯度正方向和負方向的區域性最大值,然後抑制該畫素
    • 用滯後閾值化選擇邊緣:檢查某一條邊緣是否明顯到足以作為最終輸出,最後取出所有不夠明顯的邊緣

下面是OpenCV對該演算法在Android上的實現程式碼:

    /**
     * Canny邊緣檢測器
     */
    private void Canny() {
        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        //將影象轉換為灰度
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_RGB2GRAY);
        Imgproc.Canny(grayMat, cannyEdges, 10, 100);
        //將Mat轉換回點陣圖
        Utils.matToBitmap(cannyEdges, currentBitmap);
        loadImageToImageView();
    }
    /**
     * 設定影象
     */
    private void loadImageToImageView() {
        ImageView imgView = (ImageView) findViewById(R.id.image_view);
        imgView.setImageBitmap(currentBitmap);
    }

處理結果圖:

Canny邊緣檢測器

  • Sobel運算元
    Sobel運算元和Canny邊緣檢測一樣,計算畫素灰度梯度。
    演算法分為四個步驟:
    • 將影象轉換為灰度影象
    • 計算水平方向灰度梯度絕對值
    • 計算垂直方向灰度梯度絕對值
    • 計算最終梯度

下面是OpenCV對該演算法在Android上的實現程式碼:

    /**
     * Sobel運算元
     */
    private void Sobel() {
        Mat grayMat = new Mat();
        //用來儲存結果的Mat
        Mat sobel = new Mat(); 
        //分別用於儲存梯度和絕對梯度的Mat
        Mat grad_x = new Mat();
        Mat abs_grad_x = new Mat();
        Mat grad_y = new Mat();
        Mat abs_grad_y = new Mat();
        //將影象轉換為灰度
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
        //計算水平方向梯度
        Imgproc.Sobel(grayMat, grad_x, CvType.CV_16S, 1, 0, 3, 1, 0);
        //計算垂直方向梯度
        Imgproc.Sobel(grayMat, grad_y, CvType.CV_16S, 0, 1, 3, 1, 0);
        //計算兩個方向上梯度絕對值
        Core.convertScaleAbs(grad_x, abs_grad_x);
        Core.convertScaleAbs(grad_y, abs_grad_y);
        //計算結果梯度
        Core.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 1, sobel);
        //將Mat轉換為點陣圖
        Utils.matToBitmap(sobel, currentBitmap);
        loadImageToImageView();
    }
     /**
     * 設定影象
     */
    private void loadImageToImageView() {
        ImageView imgView = (ImageView) findViewById(R.id.image_view);
        imgView.setImageBitmap(currentBitmap);
    }

處理結果圖:

Sobel運算元

  • Harris角點檢測
    Harris焦點檢測器是一種在角點檢測中最常用的技術,在影象上使用滑動視窗計算亮度的變化。
    下面是OpenCV對該演算法在Android上的實現程式碼:
    /**
     * Harris角點
     */
    private void HarrisCorner() {
        Mat grayMat = new Mat();
        Mat corners = new Mat();
        //將影象轉換成灰度
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
        Mat tempDst = new Mat();
        //找出角點
        Imgproc.cornerHarris(grayMat, tempDst, 2, 3, 0.04);
        //在新的影象上繪製角點
        Mat tempDstNorm = new Mat();
        Core.normalize(tempDst, tempDstNorm, 0, 255, Core.NORM_MINMAX);
        Core.convertScaleAbs(tempDstNorm, corners);
        //將Mat轉換為點陣圖
        Random r = new Random();
        for (int i = 0; i < tempDstNorm.cols(); i++) {
            for (int j = 0; j < tempDstNorm.rows(); j++) {
                double[] value = tempDstNorm.get(j, i);
                if (value[0] > 150)
                    Core.circle(corners, new Point(i, j), 5, new Scalar(r.nextInt(255)), 2);
            }
        }
        //將Mat轉換為點陣圖
        Utils.matToBitmap(corners, currentBitmap);
        loadImageToImageView();
    }
    /**
     * 設定影象
     */
    private void loadImageToImageView() {
        ImageView imgView = (ImageView) findViewById(R.id.image_view);
        imgView.setImageBitmap(currentBitmap);
    }

處理結果圖:

Harris角點檢測

2.霍夫變換
對於影象分析來說,除了邊緣和角點外,還需要檢測如直線、圓、橢圓等其他相關形狀。例如我們想在一張桌子的影象中檢測簽字筆等,對於這種情況,一般採用霍夫變換這種技術,它被廣泛採用的利用數學等式的引數形式在影象中檢測形狀的技術。通常我們考慮二維形狀的霍夫變換,比如直線和圓,它相對於球體等複雜形狀而言更為簡單。

  • 霍夫直線
    我們對於霍夫變化一般不會直接對影象執行演算法,因為影象中任何一條明顯的直線都一定是邊緣,反之則不然。OpenCV提供了兩種實現霍夫直線的方式:標準霍夫直線和概率霍夫直線。它們的主要區別在於概率霍夫直線選取經過隨機取樣的邊緣點子集,而不是所有的邊緣點。因為這種方式處理的點更少,所以這使得演算法的執行速度更快,而不會降低演算法的效果。
    下面是OpenCV對該演算法在Android上的實現程式碼:

    /**
     * 霍夫直線
     */
    private void HoughLines() {
        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        Mat lines = new Mat();
        //將影象轉換成灰度
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
        //這裡使用Canny邊緣檢測技術計算影象中的邊緣,使用其他的也可以
        Imgproc.Canny(grayMat, cannyEdges, 10, 100);
        Imgproc.HoughLinesP(cannyEdges, lines, 1, Math.PI / 180, 50, 20, 20);
        Mat houghLines = new Mat();
        houghLines.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC1);
        //在影象上繪製直線
        for (int i = 0; i < lines.cols(); i++) {
            double[] points = lines.get(0, i);
            double x1, y1, x2, y2;
            x1 = points[0];
            y1 = points[1];
            x2 = points[2];
            y2 = points[3];
            Point pt1 = new Point(x1, y1);
            Point pt2 = new Point(x2, y2);
            //在一副影象上繪製直線
            Core.line(houghLines, pt1, pt2, new Scalar(255, 0, 0), 1);
        }
        //將Mat轉換為點陣圖
        Utils.matToBitmap(houghLines, currentBitmap);
        loadImageToImageView();
    }
    /**
     * 設定影象
     */
    private void loadImageToImageView() {
        ImageView imgView = (ImageView) findViewById(R.id.image_view);
        imgView.setImageBitmap(currentBitmap);
    }

    處理結果圖:

    霍夫直線檢測

  • 霍夫圓

    與霍夫直線類似,步驟也一樣。
    下面是OpenCV對該演算法在Android上的實現程式碼:

    /**
     *霍夫圓
     */
    private void HoughCircles() {
        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        Mat circles = new Mat();
        //將影象轉換成灰度
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
       //這裡使用Canny邊緣檢測技術計算影象中的邊緣,使用其他的也可以
        Imgproc.Canny(grayMat, cannyEdges, 10, 100);
        Imgproc.HoughCircles(cannyEdges, circles, Imgproc.CV_HOUGH_GRADIENT, 1, cannyEdges.rows() / 15);
        Mat houghCircles = new Mat();
        houghCircles.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC1);
        //在影象上繪製圓形
        for (int i = 0; i < circles.cols(); i++) {
            double[] parameters = circles.get(0, i);
            double x, y;
            int r;
            x = parameters[0];
            y = parameters[1];
            r = (int) parameters[2];
            Point center = new Point(x, y);
            //在一副影象上繪製圓形
            Core.circle(houghCircles, center, r, new Scalar(255, 0, 0), 1);
        }
        //將Mat轉換為點陣圖
        Utils.matToBitmap(houghCircles, currentBitmap);
        loadImageToImageView();
    }
     /**
     * 設定影象
     */
    private void loadImageToImageView() {
        ImageView imgView = (ImageView) findViewById(R.id.image_view);
        imgView.setImageBitmap(currentBitmap);
    }

處理結果圖:

霍夫圓檢測

3.輪廓
有時候我們只想關注於感興趣的目標,這就需要將影象分解成更小的片元,比如我們在一角、五角和一元硬幣中分析五角的情況。我們有兩種實現途徑,一種是使用霍夫圓,另一種就是利用輪廓檢測將影象分割為更小的部分,每個部分代表一個特定的硬幣。輪廓通常以影象中的邊緣來計算,邊緣與輪廓的區別在於輪廓是閉合的,邊緣可以是任意的。
下面是OpenCV對該演算法在Android上的實現程式碼:

    /**
     * 輪廓檢測
     */
    private void Contours() {
        Mat grayMat = new Mat();
        Mat cannyEdges = new Mat();
        Mat hierarchy = new Mat();
        List<MatOfPoint> contourList = new ArrayList<MatOfPoint>(); //A list to store all the contours
        //儲存輪廓列表
        Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
         //這裡使用Canny邊緣檢測技術計算影象中的邊緣,使用其他的也可以
        Imgproc.Canny(originalMat, cannyEdges, 10, 100);
        //找出輪廓
        Imgproc.findContours(cannyEdges, contourList, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
        //在新的影象上繪製輪廓
        Mat contours = new Mat();
        contours.create(cannyEdges.rows(), cannyEdges.cols(), CvType.CV_8UC3);
        Random r = new Random();
        for (int i = 0; i < contourList.size(); i++) {
            Imgproc.drawContours(contours, contourList, i, new Scalar(r.nextInt(255), r.nextInt(255), r.nextInt(255)), -1);
        }
        //將Mat轉換為點陣圖
        Utils.matToBitmap(contours, currentBitmap);
        loadImageToImageView();
    }
    /**
     * 設定影象
     */
    private void loadImageToImageView() {
        ImageView imgView = (ImageView) findViewById(R.id.image_view);
        imgView.setImageBitmap(currentBitmap);
    }

處理結果圖:

輪廓檢測

以上就是影象的基本特徵在Android裝置上實現的不同演算法,也是我們在Android相關應用開發中的基礎。