【圖像處理】邊緣檢測

目錄:

  • 1、邊緣檢測常用的算子canny算子、sobel算子和laplacian算子,嘗試各自效果
  • 2、霍夫變換,直線檢測和圓檢測

圖像梯度

當用均值濾波器降低圖像噪聲時,會帶來圖像模糊的副作用。但是當我們要求清晰的圖像時,就不能使用之前的圖像變換來求像素值了。圖像模糊是因爲圖像中物體的輪廓不明顯,輪廓邊緣灰度變化不強烈,層次感不強造成的,那麼反過來考慮,輪廓邊緣灰度變化明顯些,層次感強些是不是圖像就更清晰些呢。此時,我們可以使用圖像的函數來定義圖像,可以使用函數的導數(梯度)來表示的變化程度,這樣既可以避免直接修改圖像的像素值而引進新的模糊,還能更直觀的顯示出圖像的變化。比如,在圖像中物體的邊緣的變化量比較大,物體中的變化量較小,因此可以用圖像梯度來進行邊緣檢測以及一系列的圖像處理。

一、邊緣檢測

邊緣檢測常用的算子有:高通濾波器(sobel算子,Scharr 算子,laplacian算子)和canny算子。

Sobel,Scharr 其實就是求一階或二階導數。Scharr 是對 Sobel(使用小的卷積核求解求解梯度角度時)的優化。Laplacian 是求二階導數。

1. sobel算子和Scharr 算子

Sobel 算子是高斯平滑與微分操作的結合體,所以它的抗噪聲能力很好。你可以設定求導的方向(xorder 或 yorder)。還可以設定使用的卷積核的大小(ksize)。如果 ksize=-1,會使用 3x3 的 Scharr 濾波器,它的的效果要比 3x3 的 Sobel 濾波器好(而且速度相同,所以在使用 3x3 濾波器時應該儘量使用 Scharr 濾波器)。3x3 的 Scharr 濾波器卷積核如下:

在這裏插入圖片描述

2. Laplacian算子

拉普拉斯算子可以使用二階導數的形式定義,可假設其離散實現類似於二階 Sobel 導數,事實上,OpenCV 在計算拉普拉斯算子時直接調用 Sobel 算子。計算公式如下:

在這裏插入圖片描述

拉普拉斯濾波器的卷積核爲:

在這裏插入圖片描述

代碼示例:

import numpy as np 
import cv2

img = cv2.imread("6.jpg",0)
# 使用同爲5*5的卷積核,cv2.CV_64F 爲圖像的深度,梯度爲負數時使用cv_8U時會把邊界丟失
laplacian = cv2.Laplacian(img,cv2.CV_64F)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)

cv2.imshow("img",img)
cv2.imshow("laplacian",laplacian)
cv2.imshow("sobelx",sobelx)
cv2.imshow("sobely",sobely)

在這裏插入圖片描述

3. Canny邊緣檢測

canny邊緣檢測時一種非常流行的邊緣檢測算法,是由很多步驟組合而成的。

3.1 噪聲去除

因爲邊緣檢測容易受到噪聲的影響,所以需要使用高斯濾波或其他方法將噪聲去除。

3.2計算圖像梯度

對於平滑後的圖像使用Sobel算子計算水平核豎直方向的圖像梯度(方法如上),根據得到的兩幅梯度圖找到邊界的梯度核方向。

3.3非極大值抑制

在獲得梯度的方向和大小之後,應該對整幅圖像做一個掃描,去除那些非邊界上的點。對每一個像素進行檢查,看這個點的梯度是不是周圍具有相同梯度方向的點中最大的。

3.4滯後閾值

現在要確定那些邊界纔是真正的邊界。這時我們需要設置兩個閾值:minVal 和 maxVal。當圖像的灰度梯度高於 maxVal 時被認爲是真的邊界,那些低於 minVal 的邊界會被拋棄。如果介於兩者之間的話,就要看這個點是否與某個被確定爲真正的邊界點相連,如果是就認爲它也是邊界點,如果不是就拋棄。

代碼示例:

import cv2

img = cv2.imread("img.jpg",0)
# opencv中只需要一個方法就可以完成以上步驟,100,200爲設置的閾值
edges = cv2.Canny(img,100,200)
cv2.imshow("img",img)
cv2.imshow("edges",edges)

在這裏插入圖片描述

由於圖像無噪聲,所以效果較好。

再來一張有噪聲的:

在這裏插入圖片描述

由於噪聲點較大,效果中將噪聲點誤判爲邊界。

二、霍夫變換

霍夫變換在檢測各種形狀的的技術中非常流行,如果你要檢測的形狀可以用數學表達式寫出,你就可以是使用霍夫變換檢測它。及時要檢測的形狀存在一點破壞或者扭曲也可以使用。直線和圓都是可以用數學表達式表示出來的,所以圖像中的直線和圓是可以用霍夫變換得到的。

1.霍夫直線變換

在opencv中,霍夫直線變換的函數爲cv2.HoughLines(),其返回值爲(ρ,θ\rho,\theta),ρ\rho的單位爲像素,θ\theta的單位是弧度。這個函數的第一個參數是一個二值化圖像,所以在進行霍夫變換之前要首先進行二值化,或者進行Canny 邊緣檢測。第二和第三個值分別代表 ρ 和 θ 的精確度。第四個參數是閾值,只有累加其中的值高於閾值時才被認爲是一條直線,也可以把它看成能檢測到的直線的最短長度(以像素點爲單位)。

代碼示例:

# -*- coding:utf-8 -*-
import numpy as np 
import cv2

img = cv2.imread("img.jpg")

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,10,100,apertureSize = 3)
lines = cv2.HoughLines(edges,1,np.pi/180,50)
for line in lines:
	for rho,theta in line:
	    a = np.cos(theta)
	    b = np.sin(theta)
	    x0 = a*rho
	    y0 = b*rho
	    x1 = int(x0 + 1000*(-b))
	    y1 = int(y0 + 1000*(a))
	    x2 = int(x0 - 1000*(-b))
	    y2 = int(y0 - 1000*(a))
	    cv2.line(img,(x1,y1),(x2,y2),(0,0,255),1)	
cv2.imshow("img",img)

原圖:在這裏插入圖片描述變換後:在這裏插入圖片描述

2.霍夫圓變換

圓形的數學表達式爲(xx0)2+(yy0)2=r2(x - x_0)^2 + (y - y_0)^2 = r^2,其中(x0,y0x_0,y_0 )爲圓心的座標,r 爲圓的直徑。從這個等式中我們可以看出:一個圓環需要 3個參數來確定。所以進行圓環霍夫變換的累加器必須是 3 維的,這樣的話效率就會很低。所以 OpenCV 用來一個比較巧妙的辦法,霍夫梯度法,它可以使用邊界的梯度信息。我們要使用的函數爲 cv2.HoughCircles()。

代碼示例:(引用自opencv教程)

import numpy as np 
import cv2

img = cv2.imread("imgs.jpg",0)
img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
	#繪製圓
	cv2.circle(cimg,(i[0],i[1]),i[2],(255,0,0),2)
	#繪製圓心
	cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)
cv2. imshow('detected circles',cimg)
cv2. imwrite('circle.jpg',cimg)

原圖:在這裏插入圖片描述圓變換:在這裏插入圖片描述

這裏略偷懶一下,使用教程中的圖片及代碼來進行描述。在霍夫直線變換中閾值設置存在問題,導致效果不明顯。

知其然知其所以然,這裏只是提供了簡單的opencv接口和原理,想要了解更多原理,需要更加深入的研究。

參考:《數字圖像處理》第三版
OpenCV-Python 中文教程----段力輝

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