目錄:
- 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(),其返回值爲(),的單位爲像素,的單位是弧度。這個函數的第一個參數是一個二值化圖像,所以在進行霍夫變換之前要首先進行二值化,或者進行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.霍夫圓變換
圓形的數學表達式爲,其中( )爲圓心的座標,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 中文教程----段力輝