1. 程式人生 > >0032-使用OpenCV對影象作邊緣檢測(Canny、Sobel、Laplace)

0032-使用OpenCV對影象作邊緣檢測(Canny、Sobel、Laplace)

邊緣檢測是影象處理和計算機視覺中的基本問題,邊緣檢測的目的是標識數字影象中亮度變化明顯的點。影象屬性中的顯著變化通常反映了屬性的重要事件和變化。 這些包括(i)深度上的不連續、(ii)表面方向不連續、(iii)物質屬性變化和(iv)場景照明變化。 邊緣檢測是影象處理和計算機視覺中,尤其是特徵提取中的一個研究領域。影象邊緣檢測大幅度地減少了資料量,並且剔除了可以認為不相關的資訊,保留了影象重要的結構屬性。

OpenCV提供了相關函式來實現各種邊緣檢測運算元,具體的各種邊緣檢測的運算元的演算法原理請大家自行百度,下面分別介紹並給出示例程式碼。

一、Canny邊緣檢測運算元
OpenCV提供了Canny函式

來實現Canny邊緣檢測運算元,函式原型如下
C++: void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false )
引數意義如下
image:輸入影象,其型別要求只能是8位的
edges:邊緣輸出,其型別要求是單通道的8點陣圖像,和輸入影象有相同的大小。
threshold1:第一個滯後性閾值。
threshold2:第二個滯後性閾值。
apertureSize:使用Sobel運算元的孔徑大小,有預設值為3。
L2gradient
:是否使用 L_2 norm  =\sqrt{(dI/dx)^2 + (dI/dy)^2來計算影象的梯度幅值,預設值為false,表示不使用L_2 norm來計算梯度幅值,此時使用L_1 norm來計算梯度幅值。
補充說明:兩個滯後性閾值中較小的那個用來確定邊緣連線,較大的那個用來初始化強邊緣的檢測,詳細原理可參見連結:http://en.wikipedia.org/wiki/Canny_edge_detector
使用Canny函式進行邊緣檢測的程式碼如下
程式碼中用到的影象下載連結:https://pan.baidu.com/s/1hs8ImVU 密碼:irnd

影象處理開發資料、影象處理開發需求、影象處理接私活掙零花錢,可以搜尋公眾號"qxsf321",並關注!

//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

#include <iostream>

using namespace cv;
using namespace std;

int main()
{
        //載入原始圖    
        Mat src = imread("20.jpg");  
        Mat src_1 = imread("20.jpg", 0);
        Mat gray = imread("20.jpg", 0);

        imshow("原圖", src);

        //----------------------------------------------------------------------------------  
        //  一、最簡單的canny用法,拿到原圖後直接用。  
        //----------------------------------------------------------------------------------  
        Canny(src_1, src_1, 100, 150, 3);
        imshow("簡單用法的Canny邊緣檢測結果", src_1);

        //----------------------------------------------------------------------------------  
        //  二、高階的canny用法,轉成灰度圖,降噪,用canny,最後將得到的邊緣作為掩碼,拷貝原圖到效果圖上,得到彩色的邊緣圖  
        //----------------------------------------------------------------------------------  
        Mat dst, edge;

        // 【1】建立與src同類型和大小的矩陣(dst)  
        dst.create(gray.size(), gray.type());

        // 【2】先用使用 3x3核心來降噪  
        blur(gray, edge, Size(3, 3));

        // 【3】執行Canny運算元  
        Canny(edge, edge, 100, 150, 3);

        imshow("高階用法的Canny邊緣檢測結果", edge);

        //【4】將g_dstImage內的所有元素設定為0   
        dst = Scalar::all(0);

        //【5】使用Canny運算元輸出的邊緣圖g_cannyDetectedEdges作為掩碼,來將原圖g_srcImage拷到目標圖g_dstImage中  
        src.copyTo(dst, edge);

        //【6】顯示效果圖   
        imshow("高階用法的合成圖", dst);

        waitKey(0);

        return 0;
}

程式碼執行截果如下圖所示


二、Sobel運算元
OpecnCV提供了Sobel函式來計算影象的一階,二階,三階導數,使用的運算元是擴充套件了的Sobel運算元。
函式原型如下
C++: void Sobel(InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
引數意義如下
src:源影象
dst:目標影象,和源圖的大小和通道數一樣。
ddepth:目標影象的深度,支援的深度如下:
    src.depth() = CV_8U, ddepth = -1/CV_16S/CV_32F/CV_64F
    src.depth() = CV_16U/CV_16S, ddepth = -1/CV_32F/CV_64F
    src.depth() = CV_32F, ddepth = -1/CV_32F/CV_64F
    src.depth() = CV_64F, ddepth = -1/CV_64F
    當 ddepth = -1時,目標影象和源影象的深度一樣,當深度是8位時,程式會對數作截斷處理。
dx:x方向的求導階數
dy:y方向的求導階數
ksize:擴充套件Sobel運算元的大小,可以的取值為1,3,5,7,值得注意的是,當ksize=CV_SCHARR (-1)時,使用3×3的Scharr 濾波器進行計算,這樣精度更高。Scharr在x方向和y方向上的核運算元矩陣為:

Scharr函式的原型如下
C++: void Scharr(InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
這裡就不對Scharr的引數多做介紹了,也不單獨給Scharr的使用示例,因為它就是Sobel運算元的一個特例,和Sobel運算元的使用上,除了沒有ksize這個引數外,其它都一樣。
scale:可選的導數計算比例因子,預設不使用比例因子。
borderType:邊界處理方法,具體的詳見帖子https://blog.csdn.net/lehuoziyuan/article/details/84101788
值得注意的是:Sobel運算元在計算中進行了高斯平滑和差分運算,這樣或多或少對噪聲有消除作用。可以設定(xorder = 1, yorder = 0, ksize = 3)或(xorder = 0, yorder = 1, ksize = 3)來計算在x或y方向上的一階導數。
一階導數的核運算元如下

二階導數的核運算元如下



使用Sobel函式計算影象邊緣的程式碼如下

影象處理開發資料、影象處理開發需求、影象處理接私活掙零花錢,可以搜尋公眾號"qxsf321",並關注!
程式碼中用到的影象下載連結:https://pan.baidu.com/s/1gfAggZh 密碼:dnol

//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

#include <iostream>

using namespace cv;
using namespace std;

//-----------------------------------【標頭檔案包含部分】---------------------------------------  
//            描述:包含程式所依賴的標頭檔案  
//----------------------------------------------------------------------------------------------  
#include <opencv2/opencv.hpp>  
#include<opencv2/highgui/highgui.hpp>  
#include<opencv2/imgproc/imgproc.hpp>  

//-----------------------------------【名稱空間宣告部分】---------------------------------------  
//            描述:包含程式所使用的名稱空間  
//-----------------------------------------------------------------------------------------------  
using namespace cv;  
//-----------------------------------【main( )函式】--------------------------------------------  
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始  
//-----------------------------------------------------------------------------------------------  
int main( )  
{  
    //【0】建立 grad_x 和 grad_y 矩陣  
    Mat grad_x, grad_y;  
    Mat abs_grad_x, abs_grad_y,dst;  

    //【1】載入原始圖    
    Mat src = imread("21.jpg");  

    //【2】顯示原始圖   
    imshow("【原始圖】sobel邊緣檢測", src);   

    //【3】求 X方向梯度  
    Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );  
    convertScaleAbs( grad_x, abs_grad_x );  
    imshow("【效果圖】 X方向Sobel", abs_grad_x);   

    //【4】求Y方向梯度  
    Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );  
    convertScaleAbs( grad_y, abs_grad_y );  
    imshow("【效果圖】Y方向Sobel", abs_grad_y);   

    //【5】合併梯度(近似)  
    addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );  
    imshow("【效果圖】整體方向Sobel", dst);   

    waitKey(0);   
    return 0;   
}

執行結果如下圖所示:



三、Laplace運算元
OpecnCV提供了Laplacian函式來計算影象的拉普拉斯運算
函式原型如下
C++: void Laplacian(InputArray src, OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
引數意義如下:
src:源影象
dst:目標影象,和源影象的大小和通道數一樣。
ddepth:目標影象深度。
ksize:核算子的大小,用計算二階導數時使用。
delta:可選的delta值,結果進行儲存前的附加值、
borderType:邊界處理方法,具體的詳見帖子https://blog.csdn.net/lehuoziyuan/article/details/84101788
這個函式實際上就是把使用Sobel在x方向和y方向運算元計算出的二階層數進行合成,從而得到自己的計算結果。
使用Laplacian函式計算影象邊緣的程式碼如下

影象處理開發資料、影象處理開發需求、影象處理接私活掙零花錢,可以搜尋公眾號"qxsf321",並關注!
程式碼中用到的影象下載連結:https://pan.baidu.com/s/1slJptsL 密碼:tiki

//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

#include <iostream>

using namespace cv;
using namespace std;

//-----------------------------------【標頭檔案包含部分】---------------------------------------  
//            描述:包含程式所依賴的標頭檔案  
//----------------------------------------------------------------------------------------------  
#include <opencv2/opencv.hpp>  
#include<opencv2/highgui/highgui.hpp>  
#include<opencv2/imgproc/imgproc.hpp>  

//-----------------------------------【名稱空間宣告部分】---------------------------------------  
//            描述:包含程式所使用的名稱空間  
//-----------------------------------------------------------------------------------------------  
using namespace cv;


//-----------------------------------【main( )函式】--------------------------------------------  
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始  
//-----------------------------------------------------------------------------------------------  
int main()
{
        //【0】變數的定義  
        Mat src, src_gray, dst, abs_dst;

        //【1】載入原始圖    
        src = imread("22.jpg");  

        //【2】顯示原始圖   
        imshow("【原始圖】影象Laplace變換", src);

        //【3】使用高斯濾波消除噪聲  
        GaussianBlur(src, src, Size(3, 3), 0, 0, BORDER_DEFAULT);

        //【4】轉換為灰度圖  
        cvtColor(src, src_gray, CV_RGB2GRAY);

        //【5】使用Laplace函式  
        Laplacian(src_gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT);

        //【6】計算絕對值,並將結果轉換成8位  
        convertScaleAbs(dst, abs_dst);

        //【7】顯示效果圖  
        imshow("【效果圖】影象Laplace變換", abs_dst);

        waitKey(0);

        return 0;
}

執行結果如下圖所示: