一.基本介紹
噪聲:指圖像中的一些干擾因素,也可以理解為有那么一些點的像素值與周圍的像素值格格不入。常見的噪聲類型包括高斯噪聲和椒鹽噪聲。
濾波器:也可以叫做卷積核
- 低通濾波器是模糊,高通濾波器是銳化
- 低通濾波器就是允許低頻信號通過,在圖像中邊緣和噪點都相當于高頻部分,所以低通濾波器用于去除噪點、平滑和模糊圖像。高通濾波器則反之,用來增強圖像邊緣,進行銳化處理。
注意:椒鹽噪聲可以理解為斑點,隨機出現在圖像中的黑點或白點;高斯噪聲可以理解為拍攝圖片時由于光照等原因造成的噪聲。
這是高斯噪聲
這是椒鹽噪聲,有很多黑白的或者孤立的小點
1.1 均值濾波
cv.blur(img, ksize)
參數:
- ksize:代表卷積核的大小
取的是卷積核區域內元素的均值。
????????對于邊界的像素點,則會進行邊界填充,以確保卷積核的中心能夠對準邊界的像素點進行濾波操作。在OpenCV中,默認的是使用BORDER_REFLECT_101的方式進行填充,下面的濾波方法中除了中值濾波使用的是BORDER_REPLICATE進行填充之外,其他默認也是使用這個方式進行填充
import cv2 as cvimg = cv.imread('../images/lvbo2.png')# 均值濾波,用3*3的卷積核
blur = cv.blur(img, (3, 3))cv.imshow('img', img)
cv.imshow('blur', blur)
cv.waitKey(0)
cv.destroyAllWindows()
1.2?方框濾波
cv.boxFilter(img, ddepth,ksize, normalize)
參數:
- ksize:代表卷積核的大小
- ddepth:輸出圖像的深度,-1代表使用原圖像的深度。
指在每個像素點所使用的位數(bit depth),也就是用來表示圖像中每一個像素點的顏色信息所需的二進制位數。圖像深度決定了圖像能夠表達的顏色數量或灰度級。
- normalize:當normalize為True的時候,方框濾波就是均值濾波,權重就等于1/9;normalize為False的時候,每個像素權重都是1,相當于求區域內的像素和,超出部分做取模運算。
import cv2 as cvimg = cv.imread('../images/lvbo2.png')# 方框濾波,用3*3的卷積核,類似均值濾波
box = cv.boxFilter(img, -1,(3, 3),normalize=True)# 真正的方框濾波
box1 = cv.boxFilter(img, -1,(3, 3), normalize=False)cv.imshow('img', img)
cv.imshow('box', box)
cv.imshow('box1', box1)
cv.waitKey(0)
cv.destroyAllWindows()
跟均值濾波很像
????????可以看到這里的方框濾波顯示圖片是很接近白色的,因為其將像素點周圍的像素權重都設為1,然后相加得到該像素的值,所以很多都超過了255而進行了取模操作。
1.2 高斯濾波
cv.GaussianBlur(img, ksize, sigmaX)
參數:
- sigmaX:
就是高斯函數里的值,σx值越大,模糊效果越明顯。高斯濾波相比均值濾波效率要慢,但可以有效消除高斯噪聲,能保留更多的圖像細節,所以經常被稱為最有用的濾波器。
通過使用高斯函數(正態分布)作為卷積核來對圖像進行模糊處理。
import cv2 as cvimg = cv.imread('../images/lvbo2.png')# 高斯,用3*3的卷積核
Gauss = cv.GaussianBlur(img, (3, 3), 1)cv.imshow('img', img)
cv.imshow('Gauss', Gauss)cv.waitKey(0)
cv.destroyAllWindows()
1.3 中值濾波
cv.medianBlur(img, ksize)
????????中值濾波沒有核值,而是在原圖中從左上角開始,將卷積核區域內的像素值進行排序,并選取中值作為卷積核的中點的像素值。就是用區域內的中值來代替本像素值,所以那種孤立的斑點,如0或255很容易消除掉,適用于去除椒鹽噪聲和斑點噪聲
import cv2 as cv# 導入椒鹽噪聲圖片
img = cv.imread('../images/lvbo3.png')# 中值濾波,注意這里的3
median = cv.medianBlur(img, 3)cv.imshow('img', img)
cv.imshow('median', median)cv.waitKey(0)
cv.destroyAllWindows()
????????其實你會注意到,中值濾波的ksize為啥是個整數呢,前面的都是(x,x)。因為中值濾波是非線性的,且沒有核值不依賴卷積核權重。
1.4 雙邊濾波
cv.bilateralFilter(img,ksize,d,sigmaColor,sigmaSpace)
參數:
- d:過濾時周圍每個像素領域的直徑,這里已經設置了核大小。d=9===>9x9
- sigmaColor:在color space(值域空間)中過濾sigma。參數越大,那些顏色足夠相近的的顏色的影響越大。較大的sigmaColor意味著更大的顏色差異將被允許參與到加權平均中.
- sigmaSpace:在coordinate space(坐標空間)中過濾sigma。這個參數是坐標空間中的標準差,決定了像素位置對濾波結果的影響程度。
雙邊濾波的基本思路是同時考慮將要被濾波的像素點的空域(空間)信息(周圍像素點的位置的權重)和值域信息(周圍像素點的像素值的權重)。因為在邊緣處,臨近的像素點差異會比較大,如果只是使用空域信息來進行濾波的話,得到的結果必然是邊緣被模糊了,這樣我們就丟掉了邊緣信息。這也是一種非線性濾波。
import cv2 as cvimg = cv.imread('../images/lvbo2.png')# 雙邊濾波
sb = cv.bilateralFilter(img, 9, 100, 100)cv.imshow('img', img)
cv.imshow('sb', sb)cv.waitKey(0)
cv.destroyAllWindows()
注意:
關于2個sigma參數:
簡單起見,可以令2個sigma的值相等;
如果他們很小(小于10),那么濾波器幾乎沒有什么效果;
如果他們很大(大于150),那么濾波器的效果會很強,使圖像顯得非常卡通化。
關于參數d:
過大的濾波器(d>5)執行效率低。
對于實時應用,建議取d=5;
對于需要過濾嚴重噪聲的離線應用,可取d=9;
二.圖像梯度處理
2.1 圖像梯度
????????把圖片想象成連續函數,因為邊緣部分的像素值是與旁邊像素有明顯區別,所以對圖片局部求極值,就可以得到整幅圖片的邊緣信息了。不過圖片是二維的離散函數,導數就變成了差分,這個差分就稱為圖像的梯度。
2.2?垂直邊緣提取
????????這個核是用來提取圖片中的垂直邊緣(右側邊緣,若提取左邊緣卷積核中正負號換一下就好)的,中間像素就是這個卷積核卷積的結果。
????????當前列左右兩側的元素進行差分,由于邊緣(當前列)的值明顯小于(或大于)周邊像素,所以邊緣(當前列)的差分結果會明顯不同,這樣就提取出了垂直邊緣。簡單理解,就是在‘1’位置右邊的像素值相對于‘-1’位置左邊的像素值的差距會顯示在中間的像素中,所以中間的列就是旁邊兩列的垂直差分。
來介紹一下二維卷積函數:
cv2.filter2D(src, ddepth, kernel)
- src: 輸入圖像,一般為numpy數組。
- ddepth: 輸出圖像的深度,可以是負值(表示與原圖相同)、正值或其他特定值(常用-1 表示輸出與輸入具有相同的深度)。
- kernel: 卷積核,一個二維數組(通常為奇數大小的方形矩陣),用于計算每個像素周圍鄰域的加權和。
import cv2 as cv
import numpy as np# 模擬一張圖像,灰度圖
img=np.array([[100,102,109,110,98,20,19,18,21,22],[109,101,98,108,102,20,21,19,20,21],[109,102,105,108,98,20,22,19,19,18],[109,98,102,108,102,20,23,19,20,22],[109,102,105,108,98,20,22,19,20,18],[100,102,108,110,98,20,19,18,21,22],[109,101,98,108,102,20,22,19,20,21],[109,102,108,108,98,20,22,19,19,18],],dtype=np.float32)
# 定義卷積核,
kernel=np.array([[-1,0,1],[-2,0,2],[-1,0,1]],dtype=np.float32)
# 二維卷積操作
img2=cv.filter2D(img,-1,kernel)
# 打印卷積后的圖
print(img2)
[[ ? 0. ? -4. ? 30. ?-14. -356. -320. ? -6. ? ?2. ? 12. ? ?0.]
?[ ? 0. ?-17. ? 28. ?-10. -354. -317. ? -5. ? -3. ? ?7. ? ?0.]
?[ ? 0. ?-26. ? 29. ?-10. -352. -312. ? -4. ?-10. ? ?3. ? ?0.]
?[ ? 0. ?-22. ? 32. ?-14. -352. -310. ? -4. ?-11. ? ?4. ? ?0.]
?[ ? 0. ? -7. ? 30. ?-24. -354. -310. ? -5. ? -5. ? ?5. ? ?0.]
?[ ? 0. ? ?1. ? 29. ?-23. -356. -314. ? -6. ? ?0. ? ?9. ? ?0.]
?[ ? 0. ?-15. ? 28. ?-12. -354. -315. ? -5. ? -5. ? ?7. ? ?0.]
?[ ? 0. ?-24. ? 26. ?-12. -352. -312. ? -4. ?-10. ? ?2. ? ?0.]]
????????這里的值就是用上面的卷積核計算的,其中邊緣由于沒有左側的值無法做左右兩邊的差分,所以是0,也就是無梯度變化。然后中間兩列有著超出255的最大數值,這就會作為邊緣被提取出來。
????????差分后值的符號正負代表梯度的方向,差分值實際應該取絕對值,畢竟我們只是想看數值大小來提取邊緣。值超過了255都算(+-)255,即使是負號。