OpenCV Canny邊緣檢測器

2021-02-19 17:03 更新

目標

在本教程中,您將學習如何:

  • 使用OpenCV函數(shù)cv :: Canny來實現(xiàn)Canny Edge Detector。

理論

該Canny邊緣檢測是由約翰·F·坎尼在1986年也知道很多的開發(fā)最佳的檢測,坎尼算法旨在滿足三個主要標準:

  • 低錯誤率:意味著只有現(xiàn)有邊緣的良好檢測。
  • 良好的定位:檢測到的邊緣像素與實際邊緣像素之間的距離必須最小化。
  • 最小響應:每個邊緣只有一個檢測器響應。

步驟

  • 濾除任何噪音。高斯濾波器用于此目的??赡苁褂玫牡母咚箖?nèi)核的示例如下所示:size=5

Canny邊緣檢測器

  • 找到圖像的強度梯度。為此,我們遵循類似于Sobel的程序:

應用一對卷積面罩 (在 x 和 y directions:

Canny邊緣檢測器

查找梯度強度和方向:

Canny邊緣檢測器

方向四舍五入為四個可能的角度之一(即0,45,90或135)

  • 應用非最大抑制。這將刪除不被認為是邊緣的一部分的像素。因此,只有細線(候選邊)將保留。
  • 滯后:最后一步。Canny確實使用兩個閾值(上限和下限):
  1. 如果像素梯度高于上限閾值,則像素被接受為邊緣
  2. 如果像素梯度值低于較低閾值,則會被拒絕。
  3. 如果像素梯度在兩個閾值之間,那么只有當它連接到高于上限閾值的像素時才被接受。

Canny推薦上限:2:1和3:1之間的較低比例。

  • 有關更多詳細信息,您可以隨時咨詢您最喜愛的Computer Vision書籍。

Code

  • 這個程序是做什么的?
  1. 要求用戶輸入數(shù)值以設置我們的Canny Edge Detector的下限(通過Trackbar)。
  2. 應用Canny Detector并生成一個蒙版(亮線表示黑色背景上的邊緣)。
  3. 應用在原始圖像上獲得的蒙版,并將其顯示在窗口中。
  • 教程代碼如下所示。您也可以從這里下載

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
Mat src, src_gray;
Mat dst, detected_edges;
int edgeThresh = 1;
int lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
const char* window_name = "Edge Map";
static void CannyThreshold(int, void*)
{
    blur( src_gray, detected_edges, Size(3,3) );
    Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
    dst = Scalar::all(0);
    src.copyTo( dst, detected_edges);
    imshow( window_name, dst );
}
int main( int, char** argv )
{
  src = imread( argv[1], IMREAD_COLOR ); // Load an image
  if( src.empty() )
    { return -1; }
  dst.create( src.size(), src.type() );
  cvtColor( src, src_gray, COLOR_BGR2GRAY );
  namedWindow( window_name, WINDOW_AUTOSIZE );
  createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
  CannyThreshold(0, 0);
  waitKey(0);
  return 0;
}

說明

  • 創(chuàng)建一些必需的變量:
Mat src, src_gray;
Mat dst, detected_edges;
int edgeThresh = 1;
int lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
const char* window_name = "Edge Map";

請注意以下事項:

  1. 我們建立一個較低的上限閾值3:1(與可變比率)的比率。
  2. 我們設置內(nèi)核大小為(Sobel操作由Canny函數(shù)內(nèi)部執(zhí)行)。3
  3. 我們?yōu)榈南孪揲撝翟O置最大值。100
  • 加載源圖像:
  src = imread( argv[1], IMREAD_COLOR ); // Load an image
  if( src.empty() )
    { return -1; }
  • 創(chuàng)建一個相同類型和大小的src(為dst)的矩陣:
  dst.create(src.size(),src.type());
  cvtColor(src,src_gray,COLOR_BGR2GRAY);
  • 創(chuàng)建一個窗口來顯示結果:
  namedWindow(window_name,WINDOW_AUTOSIZE);
  • 創(chuàng)建一個跟蹤欄,供用戶輸入Canny檢測器的下限:
  createTrackbar(“Min Threshold:”,window_name,&lowThreshold,max_lowThreshold,CannyThreshold);

觀察以下內(nèi)容:

  1. Trackbar要控制的變量是lowThreshold,最大值為max_lowThreshold(我們先前設置為100)
  2. 每次Trackbar注冊一個動作時,將調用回調函數(shù)CannyThreshold。
  • 讓我們檢查CannyThreshold函數(shù),一步一步:
  1. 首先,我們用內(nèi)核大小為3的過濾器模糊圖像:    
  1.     blur(src_gray,detected_edges,Size(3,3));
    
  2. 然后,我們應用OpenCV函數(shù)cv :: Canny
        Canny(detected_edges,detected_edges,lowThreshold,lowThreshold * ratio,kernel_size);
    

其中的解釋:

    • detected_edges:源圖像,灰度
    • detected_edges:檢測器的輸出(可以與輸入相同)
    • lowThreshold:用戶移動軌跡欄輸入的值
    • highThreshold:在程序中設置為下限閾值的三倍(在Canny的建議之后)
    • kernel_size:我們將它定義為3(要在內(nèi)部使用的Sobel內(nèi)核的大?。?/li>
  • 我們用零填充dst圖像(意味著圖像是完全黑色的)。
    dst = Scalar :: all(0);
  • 最后,我們將使用函數(shù)cv :: Mat :: copyTo僅映射被識別為邊緣的圖像的區(qū)域(在黑色背景上)。cv :: Mat :: copy將src映像復制到dst上。但是,它只會復制它們具有非零值的位置中的像素。由于Canny檢測器的輸出是黑色背景上的邊緣輪廓,因此所得到的dst在所有區(qū)域都將為黑色,但檢測到的邊緣為黑色。
  •     src.copyTo(dst,detected_edges);
    
  • 我們顯示結果:
    imshow(window_name,dst);

結果

  • 在編譯上面的代碼之后,我們可以運行它作為參數(shù)作為圖像的路徑。例如,使用以下圖像作為輸入:

Canny邊緣檢測器

  • 移動滑塊,嘗試不同的閾值,我們得到以下結果:

Canny邊緣檢測器

  • 注意圖像如何與邊緣區(qū)域上的黑色背景疊加。
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號