1. 程式人生 > >Opencv影象識別從零到精通(21)-----canny運算元邊緣檢測

Opencv影象識別從零到精通(21)-----canny運算元邊緣檢測

          最後來看看canny運算元,這個是被成為最好的運算元,因為過程多,有準測,後面會列出來,也是邊緣檢測的最後一個,所以這裡作為結尾,來看看各個邊緣檢測的效果。

邊緣檢測結果比較

  • Roberts運算元檢測方法對具有陡峭的低噪聲的影象處理效果較好,但是利用roberts運算元提取邊緣的結果是邊緣比較粗,因此邊緣的定位不是很準確。
  • Sobel運算元檢測方法對灰度漸變和噪聲較多的影象處理效果較好,sobel運算元對邊緣定位不是很準確,影象的邊緣不止一個畫素。
  • Prewitt運算元檢測方法對灰度漸變和噪聲較多的影象處理效果較好。但邊緣較寬,而且間斷點多。
  • Laplacian運算元法對噪聲比較敏感,所以很少用該運算元檢測邊緣,而是用來判斷邊緣畫素視為與影象的明區還是暗區。
  • Canny方法不容易受噪聲干擾,能夠檢測到真正的弱邊緣。優點在於,使用兩種不同的閾值分別檢測強邊緣和弱邊緣,並且當弱邊緣和強邊緣相連時,才將弱邊緣包含在輸出影象中

canny對邊緣檢測質量進行分析時,有3個原則:

  • 1、信噪比準則
  • 2、定位精度準則
  • 3、單邊緣響應準則

       canny邊緣檢測的基本思想是:首先對影象選擇一定的Gauss濾波器進行平滑濾波,然後採用非極值抑制技術進行處理得到最後的邊緣影象。

具體步驟:

1、用高斯濾波器平滑影象

       對影象進行高斯濾波,聽起來很玄乎,其實就是根據待濾波的畫素點及其鄰域點的灰度值按照一定的引數規則進行加權平均。這樣可以有效濾去理想影象中疊加的高頻噪聲。


2、用一階偏導的有限差分來計算梯度的幅值和方向

       影象灰度值得梯度可使用一階有限差分來進行近似,這樣就可以得影象在xy方向上偏導數的兩個矩陣。常用的梯度運算元就是Roberts.sobel,prewitt.,canny

3、對梯度幅值進行非極大值抑制

      影象梯度幅值矩陣中的元素值越大,說明影象中該點的梯度值越大,但這不不能說明該點就是邊緣(這僅僅是屬於影象增強的過程)。在Canny演算法中,非極大值抑制是進行邊緣檢測的重要步驟,通俗意義上是指尋找畫素點區域性最大值,將非極大值點所對應的灰度值置為0,這樣可以剔除掉一大部分非邊緣的點

4、用雙閾值演算法檢測和連線邊緣

   Canny

演算法中減少假邊緣數量的方法是採用雙閾值法。選擇兩個閾值(關於閾值的選取方法在擴充套件中進行討論),根據高閾值得到一個邊緣影象,這樣一個影象含有很少的假邊緣,但是由於閾值較高,產生的影象邊緣可能不閉合,未解決這樣一個問題採用了另外一個低閾值。

     通俗的來說:就是在進行邊緣檢測時,還是要用到濾波減小噪聲,先通過在水平和垂直方向的一階偏導,求得梯度的幅值和方向,這樣每個點都可能有4中方向情況(0,45,90,135度),在區域性範圍內,保留在同一方向上,梯度最大的點,非最大就置零,最後使用2個閾值T1和T2(T1<T2),T2用來找到每條線段,T1用來在這些線段的兩個方向上延伸尋找邊緣的斷裂處,並連線這些邊緣。

<span style="font-size:18px;">C++: void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false )  </span>
  • 第一個引數,InputArray型別的image,輸入影象,即源影象,填Mat類的物件即可,且需為單通道8點陣圖像。
  • 第二個引數,OutputArray型別的edges,輸出的邊緣圖,需要和源圖片有一樣的尺寸和型別。
  • 第三個引數,double型別的threshold1,第一個滯後性閾值。
  • 第四個引數,double型別的threshold2,第二個滯後性閾值。
  • 第五個引數,int型別的apertureSize,表示應用Sobel運算元的孔徑大小,其有預設值3。
  • 第六個引數,bool型別的L2gradient,一個計算影象梯度幅值的標識,有預設值false。
<span style="font-size:18px;">#include "opencv2/imgproc/imgproc.hpp"  
#include "opencv2/highgui/highgui.hpp"  
#include <stdlib.h>  
#include <stdio.h>  
#include <iostream>  
using namespace cv;  
using namespace std;  
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*)  
{  
  

    Canny( src_gray, 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("D:\\lena.jpg",CV_LOAD_IMAGE_COLOR);  
  if( !src.data )  
    { return -1; }  
  dst.create( src.size(), src.type() );  
  cvtColor( src, src_gray, CV_BGR2GRAY );  
  namedWindow( window_name, CV_WINDOW_AUTOSIZE );  
  createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );  
  CannyThreshold(0, 0);  
  waitKey(0);  
  return 0;  
} </span>

matlab

<span style="font-size:18px;">I=imread('d:\lena.jpg');
I1=rgb2gray(I);
img1=edge(I1,'canny',[0.03,0.08],3);
subplot(121),imshow(I);
subplot(122),imshow(img1)</span>



影象識別演算法交流 QQ群:145076161,歡迎影象識別與影象演算法,共同學習與交流