計算機視覺基礎-圖像處理(邊緣檢測)cpp+python

6.1 簡介

6.1.1 什麼是邊緣?
邊緣是圖像強度函數快速變化的地方

6.1.2 如何檢測邊緣?
爲了檢測邊緣,我們需要檢測圖像中的不連續性,可以使用導數來檢測不連續性。
在這裏插入圖片描述如上圖所示,上圖的第一幅圖表示一張數字圖片,我們對水平紅線處進行求導,便可得到上圖二中的關係,可以看到在邊緣處有着較大的跳變。但是,導數也會受到噪聲的影響,因此建議在求導數之前先對圖像進行平滑處理(上圖三)。

但是,導數也會受到噪聲的影響,因此建議在求導數之前先對圖像進行平滑處理。 然後我們可以使用遮罩使用卷積來檢測邊緣。 同樣,我不打算討論數學部分,這裏我們僅關注實現細節。

6.2 學習目標

  • 掌握Sobel邊緣檢測的原理
  • 掌握Canny邊緣檢測的原理

6.3 內容介紹

  • Sobel,Canny算子介紹
  • OpenCV 代碼實踐

6.4 算法理論介紹與推薦

6.4.1 Sobel算子
Sobel 算子是一個主要用作邊緣檢測的離散微分算子 (discrete differentiation operator)。 它Sobel算子結合了高斯平滑和微分求導,用來計算圖像灰度函數的近似梯度。在圖像的任何一點使用此算子,將會產生對應的梯度矢量或是其法矢量。

Sobel卷積因子爲:
在這裏插入圖片描述該算子包含兩組3x3的矩陣,分別爲橫向及縱向,將之與圖像作平面卷積,即可分別得出橫向及縱向的亮度差分近似值。如果以A代表原始圖像,Gx及Gy分別代表經橫向及縱向邊緣檢測的圖像灰度值,其公式如下:
在這裏插入圖片描述具體計算如下:

Gx = (-1)*f(x-1, y-1) + 0*f(x,y-1) + 1*f(x+1,y-1)

  +(-2)*f(x-1,y) + 0*f(x,y)+2*f(x+1,y)

  +(-1)*f(x-1,y+1) + 0*f(x,y+1) + 1*f(x+1,y+1)

= [f(x+1,y-1)+2*f(x+1,y)+f(x+1,y+1)]-[f(x-1,y-1)+2*f(x-1,y)+f(x-1,y+1)]



Gy =1* f(x-1, y-1) + 2*f(x,y-1)+ 1*f(x+1,y-1)

  +0*f(x-1,y) 0*f(x,y) + 0*f(x+1,y)

  +(-1)*f(x-1,y+1) + (-2)*f(x,y+1) + (-1)*f(x+1, y+1)

= [f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1)]-[f(x-1, y+1) + 2*f(x,y+1)+f(x+1,y+1)]

其中f(a,b), 表示圖像(a,b)點的灰度值;

圖像的每一個像素的橫向及縱向灰度值通過以下公式結合,來計算該點灰度的大小:
在這裏插入圖片描述
通常,爲了提高效率 使用不開平方的近似值:
在這裏插入圖片描述
如果梯度G大於某一閥值 則認爲該點(x,y)爲邊緣點。然後可用以下公式計算梯度方向
在這裏插入圖片描述

Sobel算子根據像素點上下、左右鄰點灰度加權差,在邊緣處達到極值這一現象檢測邊緣。對噪聲具有平滑作用,提供較爲精確的邊緣方向信息,邊緣定位精度不夠高。當對精度要求不是很高時,是一種較爲常用的邊緣檢測方法。

6.4.2普利維特算子(Prewitt operate):

除sobel邊緣檢測外 還有Prewitt算子, 它的卷積因子如下:
在這裏插入圖片描述其他計算 和sobel差不多;

Prewitt算子利用像素點上下、左右鄰點灰度差,在邊緣處達到極值檢測邊緣。對噪聲具有平滑作用,定位精度不夠高。

6.4.3羅伯茨交叉邊緣檢測(Roberts Cross operator)

卷積因子如下:
在這裏插入圖片描述灰度公式爲:
在這裏插入圖片描述
近似公式爲:
在這裏插入圖片描述
具體計算如下:

G(x,y)=abs(f(x,y)-f(x+1,y+1))+abs(f(x,y+1)-f(x+1,y))

灰度方向 計算公式爲:
在這裏插入圖片描述
Roberts算子採用對角線方向相鄰兩像素之差近似梯度幅值檢測邊緣。檢測水平和垂直邊緣的效果好於斜向邊緣,定位精度高,對噪聲敏感

6.4.4 Canny邊緣檢測
Canny邊緣檢測於1986年由JOHN CANNY首次在論文《A Computational Approach to Edge Detection》中提出,就此拉開了Canny邊緣檢測算法的序幕。

Canny邊緣檢測是從不同視覺對象中提取有用的結構信息並大大減少要處理的數據量的一種技術,目前已廣泛應用於各種計算機視覺系統。Canny發現,在不同視覺系統上對邊緣檢測的要求較爲類似,因此,可以實現一種具有廣泛應用意義的邊緣檢測技術。邊緣檢測的一般標準包括:

  • 以低的錯誤率檢測邊緣,也即意味着需要儘可能準確的捕獲圖像中儘可能多的邊緣。
  • 檢測到的邊緣應精確定位在真實邊緣的中心。
  • 圖像中給定的邊緣應只被標記一次,並且在可能的情況下,圖像的噪聲不應產生假的邊緣。

爲了滿足這些要求,Canny使用了變分法。Canny檢測器中的最優函數使用四個指數項的和來描述,它可以由高斯函數的一階導數來近似。

在目前常用的邊緣檢測方法中,Canny邊緣檢測算法是具有嚴格定義的,可以提供良好可靠檢測的方法之一。由於它具有滿足邊緣檢測的三個標準和實現過程簡單的優勢,成爲邊緣檢測最流行的算法之一。

完成一個Canny邊緣檢測算法可以分爲以下四步:
在這裏插入圖片描述1.高斯濾波
邊緣檢測結果容易受到圖像噪聲的影響,圖片中一些噪聲會大大影像邊緣檢測。因此爲了使圖像平滑,可以用高斯濾波器內核與圖像進行卷積。 此步驟將使圖像稍微平滑,以減少邊緣檢測器上明顯噪聲的影響。

大小爲(2k+1)×(2k+1)的高斯濾波器核的方程式爲:
在這裏插入圖片描述在這裏插入圖片描述

注意,選擇高斯核的大小會影響檢測器的性能。 尺寸越大,檢測器對噪聲的靈敏度越低。 此外,隨着高斯濾波器核大小的增加,用於檢測邊緣的定位誤差將略有增加。一般5x5是一個比較不錯的trade off。

2.計算梯度強度和方向
進行高斯濾波後,圖像中的邊緣可以指向各個方向,接下來使用四個算子來檢測圖像中的水平、垂直和對角邊緣。邊緣檢測的算子(如Roberts,Prewitt,Sobel等)返回水平Gx和垂直Gy方向的一階導數值,由此便可以確定像素點的梯度G和方向θ

在這裏插入圖片描述其中G爲梯度強度, θ表示梯度方向,arctan爲反正切函數。通過上式我們可以得到一個梯度矩陣G和方向矩陣 θ。

【例3】角度的計算

見下圖,假設 θ有四條線,分別是0,45,90,135度線(0度和180重合,是一條線)。需要通過(2)式求出的θ進行近似,分類到這四條線或四個角度中。

比如計算出的θ=91度,則應將其歸類到90–270度方向
在這裏插入圖片描述3.非極大值抑制(NMS)
在每一點上,鄰域中心與沿着其對應的梯度方向的兩個像素相比,若中心像素爲最大值,則保留,否則中心置0,這樣可以抑制非極大值,保留局部梯度最大的點,以得到細化的邊緣。

對圖像進行梯度計算後,僅僅基於梯度值提取的邊緣仍然很模糊。對邊緣有且應當只有一個準確的響應。而非極大值抑制則可以幫助將局部最大值之外的所有梯度值抑制爲0。非極大值抑制是一種邊緣稀疏技術,非極大值抑制的作用在於“瘦”邊。直觀上地看,對第二步得到的圖片,邊緣由粗變細了。

4. 用雙閾值算法檢測和連接邊緣
一張圖解釋雙閾值算法檢測:
在這裏插入圖片描述雙閾值法非常簡單,我們假設兩類邊緣:經過非極大值抑制之後的邊緣點中,梯度值超過TH的稱爲強邊緣,梯度值小於TH大於TL的稱爲弱邊緣,梯度小於TL的不是邊緣。

可以肯定的是,強邊緣必然是邊緣點,因此必須將T1設置的足夠高,以要求像素點的梯度值足夠大(變化足夠劇烈),而弱邊緣可能是邊緣,也可能是噪聲,如何判斷呢?當弱邊緣的周圍8鄰域有強邊緣點存在時,就將該弱邊緣點變成強邊緣點,以此來實現對強邊緣的補充。實際中人們發現T1:T2=2:1的比例效果比較好,其中T1可以人爲指定,也可以設計算法來自適應的指定,比如定義梯度直方圖的前30%的分界線爲T1。檢查8鄰域的方法叫邊緣滯後跟蹤,連接邊緣的辦法還有區域生長法等等。

6.5 基於OpenCV的實現

6.5.1 Sobel算子
opencv使用sobel 算子的方法是cv2.Sobel

# -*- coding: utf-8 -*-
"""
cv2.Sobel(src, #參數是需要處理的圖像;
					ddepth, #圖像的深度,-1表示採用的是與原圖像相同的深度。目標圖像的深度必須大於等於原圖像的深度
					dx, #dx和dy表示的是求導的階數,0表示這個方向上沒有求導,一般爲012。
					dy[, 
					dst[, #輸出圖片
          ksize[,#Sobel算子的大小,必須爲1357。
          scale[, #縮放導數的比例常數,默認情況下沒有伸縮係數;
          delta[, #可選的增量,將會加到最終的dst中,同樣,默認情況下沒有額外的值加到dst中;
          borderType #判斷圖像邊界的模式。這個參數默認值爲cv2.BORDER_DEFAULT。
          ]]]]])  
"""
import cv2
import numpy as np
from matplotlib import pyplot as plt

#讀圖
img = cv2.imread('IMG/test.jpg',0)

sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)

#畫圖
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])

plt.subplot(1,3,2),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])

plt.subplot(1,3,3),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])

plt.show()

在這裏插入圖片描述
6.5.2 Canny算法

# -*- coding: utf-8 -*-

"""
cv2.Canny(image,            # 輸入原圖(必須爲單通道圖)
          threshold1,
          threshold2,       # 較大的閾值2用於檢測圖像中明顯的邊緣
          [, edges[,
          apertureSize[,    # apertureSize:Sobel算子的大小
          L2gradient ]]])   # 參數(布爾值)true: 使用更精確的L2範數進行計算(即兩個方向的倒數的平方和再開放),
                              false:使用L1範數(直接將兩個方向導數的絕對值相加)。
"""

import cv2
import numpy as np
import matplotlib.pyplot as plt

original_img = cv2.imread("IMG/test.jpg", 0)

# canny邊緣檢測
img1 = cv2.GaussianBlur(original_img,(3,3),0)
canny = cv2.Canny(img1, 50, 150)

# 畫圖
plt.subplot(1,2,1),plt.imshow(original_img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])

plt.subplot(1,2,2),plt.imshow(canny,cmap = 'gray')
plt.title('Canny'), plt.xticks([]), plt.yticks([])
plt.show()

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章