1.19【OpenCV圖像處理】Canny邊緣檢測

Canny()、邊緣、非極大值抑制、高低閾值、copyTo複製

1.19.1 Canny演算法原理

(1)Canny(坎尼)邊緣檢測演算法簡介是 John F. Canny 於 1986年開發出來的一個多級邊緣border檢測演算法,也被很多人認為是邊緣檢測的最優演算法, 是一個很好的邊緣檢測器,很常用也很實用的圖像處理方法,最優邊緣檢測的三個主要評價標準是:

低錯誤率: 標識出儘可能多的實際邊緣,同時儘可能的減少雜訊產生的誤報。

高定位性: 標識出的邊緣要與圖像中的實際邊緣儘可能接近。

最小響應: 圖像中的邊緣只能標識一次。

(《數字圖像處理》P465)

(2)Canny演算法實現步驟 – 這幾步Canny()已封裝好了

1)高斯模糊 - GaussianBlur() 高斯平滑濾波器卷積降噪

GaussianBlur(gray_src, gray_src, Size(3, 3), BORDER_DEFAULT); //模糊

2)灰度轉換 - cvtColor()

cvtColor(src, gray_src, CV_BGR2GRAY); //轉灰度

3)計算梯度幅值和角度圖像 – Sobel()/Scharr() X和Y方向的梯度

Canny(gray_src, edge_output, t1_value, t1_value * 2, 3, false); //canny演算法,高閾值,低閾值

4)非最大信號抑制 - 排除抑制非邊緣像素,僅僅保留了一些細線條(候選邊緣)

5)高低閾值處理和連接邊緣- 高於高閾值的保留,低於低閾值的捨棄,在高低閾值之間的只與高於高閾值的像素邊緣連接成線,輸出二值圖像 。

(3)計算梯度幅值和方向

此處按照Sobel濾波器的步驟:運用一對卷積陣列(分別作用於X和Y方向)卷積,並使用下列公式計算梯度幅值(也可以是絕對值相加的)和方向,將梯度方向近似為四個可能的角度之一(一般 0, 45, 90, 135):

梯度方向(邊緣法線方向):arctan(y/x) 意思是梯度在向哪個方向上變化率最大,根據方向角度大小就可以判斷梯度向哪個方向的變化趨勢。(梯度的方向arctan(y/x)角度取值為 -90度~90度,再加上90度,範圍是0~180度)可由邊緣法線方向來確定邊緣方向(相互垂直),如果邊緣法線方向的範圍是從-22.5度到22.5度,或者是從-157.5度到157.5度,則稱該邊緣為水平邊緣。

(4)非極大值抑制

非極大值抑制(Non-maximum suppression, NMS):邊緣信號很強只能有一個,排除抑制非邊緣像素,需要僅僅保留一些細線條(候選邊緣)。非極大值抑制(NMS)主要是為了更精確的定位某種特徵,比如用梯度變化表徵邊緣時,梯度變化較大的區域通常比較寬,所以利用x和y方向的梯度確定一個法向arctan(y/x),然後在法向上判斷當前梯度測量是否是一個峰值(或局部極大值),如果是就保留,不是極大值就抑制(如設置為0)。這樣的話就能將邊緣定位在1-2像素寬(相鄰像素有時候求極大值的方向恰好互不干擾)。

方法:在X和Y合起來的梯度上,尋找當前值最接近的邊緣方向,比較沿此邊緣方向上的左右兩邊的值,若當前值是最大的話保留當前值,若當前值不是最大的話就捨棄填 0 。

四個基本邊緣方向有水平邊緣(黃)、- 45度邊緣(綠)、垂直邊緣(藍)、+ 45度邊緣(紅),4種類型的邊緣方向的邊緣法線的角度範圍如下圖:

舉例:比較P5與沿邊緣方向兩邊鄰近的值P4和P6。

(5)高低閾值連接,輸出二值圖像

滯後閾值: 最後一步,Canny 使用了滯後閾值,滯後閾值需要兩個閾值(高閾值和低閾值):

1. 如果某一像素位置的幅值超過高閾值, 該像素被保留為邊緣像素。

2. 如果某一像素位置的幅值小於低閾值, 該像素被排除。

3. 如果某一像素位置的幅值在兩個閾值之間,該像素僅僅在連接到一個高於高閾值的像素時被保留。(閾值連接,與高於高閾值的像素連成線)

Canny推薦的高低閾值比在 2:1 到3:1之間。高閾值越低邊緣信息越多。

1.19.2 API使用

Canny(

InputArray src, // 必須8-bit的輸入圖像,不支持彩色圖像(輸入圖像可以是RGB每個通道佔8位就行,自動轉換了)

OutputArray edges,// 輸出邊緣圖像8位單通道, 一般都是二值圖像,背景是黑色

double threshold1,// 低閾值,常取高閾值的1/2或者1/3(要積累經驗值)

double threshold2,// 高閾值

int aptertureSize,// Soble運算元的大小,通常3x3,取值3

bool L2gradient // 計算圖像梯度gradient大小:選擇 true表示用L2(根號)來歸一化,否則默認false用L1(絕對值)來歸一化

/*Canny演算法邊緣檢測*/nvoid Canny_Demo(int, void*) {nMat edge_output;n GaussianBlur(gray_src, gray_src, Size(3, 3), BORDER_DEFAULT); //模糊nCanny(gray_src, edge_output, t1_value, t1_value * 2, 3, false); //canny演算法,高閾值,低閾值n imshow(output_title, edge_output); //邊緣二值化圖像(黑背景),Canny邊緣檢測的輸出是鑲嵌在黑色背景中的邊緣像素,因此其結果 dst 圖像除了被檢測的邊緣像素,其餘部分都為黑色。nn imshow("~output_title", ~edge_output); //取反輸出(白背景)nn dst.create(src.size(), src.type()); //創建空黑圖像dstn src.copyTo(dst, edge_output); //將原圖與邊緣二值化圖像的非零元素都複製到dst(彩色)。將src圖像拷貝到dst,但是僅僅拷貝掩碼不為0的像素。n imshow("copyTo image", dst);n}n


完整程序:

/*1.19 canny邊緣檢測*/n#include <opencv2/opencv.hpp>n#include <iostream> n#include <math.h>nusing namespace cv; //使用cv命名空間nnMat gray_src, src, dst;nint t1_value = 50; //低閾值默認nint max_value = 255;nconst char* output_title = "Canny Result";nvoid Canny_Demo(int, void*);nnint main(int argc, char** argv) { //argc 表示命令行輸入參數的個數(以空白符分隔),argv中存儲了所有的命令行參數n src = imread("E:/OpenCV/testimage/test7.jpg");nif (src.empty()) {n printf("could not load image...n");nreturn -1;n }nchar input_title[] = "input image";n namedWindow(input_title, CV_WINDOW_AUTOSIZE);n imshow(input_title, src);n namedWindow(output_title, CV_WINDOW_AUTOSIZE);nn cvtColor(src, gray_src, CV_BGR2GRAY); //轉灰度n createTrackbar("Threshold Value", output_title, &t1_value, max_value, Canny_Demo); //跟蹤條調整低閾值和高閾值n Canny_Demo(0, 0);nn waitKey(0);nreturn 0;n}n/*Canny演算法邊緣檢測*/nvoid Canny_Demo(int, void*) {nMat edge_output;n GaussianBlur(gray_src, gray_src, Size(3, 3), BORDER_DEFAULT); //模糊,可以不用,但用了效果更好nCanny(gray_src, edge_output, t1_value, t1_value * 2, 3, false); //canny演算法,低閾值,高閾值nimshow(output_title, edge_output); //邊緣二值化圖像(黑背景),Canny邊緣檢測的輸出是鑲嵌在黑色背景中的邊緣像素,因此其結果 dst 圖像除了被檢測的邊緣像素,其餘部分都為黑色。nn imshow("~output_title", ~edge_output); //取反輸出(白背景)nn dst.create(src.size(), src.type()); //創建空黑圖像dstn src.copyTo(dst, edge_output); //將原圖與邊緣二值化圖像的非零元素都複製到dst(彩色)。將src圖像拷貝到dst,但是僅僅拷貝掩碼不為0的像素。n imshow("copyTo image", dst);n}n

運行結果:


推薦閱讀:

圖像分割中的自動求閾值法
我的CUDA學習之旅——啟程
信號處理與數字媒體
基於FPGA的腐蝕膨脹演算法實現
從20秒塗鴉看文化屬性

TAG:OpenCV | 图像处理 | 计算机视觉 |