本文主要內容來自于 OpenCV-Python 教程 的 OpenCV 中的圖像處理 部分,這部分的全部主要內容如下:
-
學習在不同色彩空間之間改變圖像。另外學習跟蹤視頻中的彩色對象。
-
學習對圖像應用不同的幾何變換,比如旋轉、平移等。
-
學習使用全局閾值、自適應閾值、Otsu 的二值化等將圖像轉換為二值圖像。
-
學習模糊圖像,使用自定義內核過濾圖像等。
-
了解形態學變換,如侵蝕、膨脹、開放、閉合等。
-
學習尋找圖像漸變、邊緣等。
-
學習通過 Canny 邊緣檢測尋找邊緣。
-
學習關于圖像金字塔的內容,以及如何使用它們進行圖像混合。
-
所有關于 OpenCV 中的輪廓的內容。
-
所有關于 OpenCV 中的直方圖的內容。
-
在 OpenCV 中遇到不同的圖像變換,如傅里葉變換、余弦變換等。
-
學習使用模板匹配在圖像中搜索對象。
-
學習在一幅圖像中探測線。
-
學習在一幅圖像中探測圓。
-
學習使用分水嶺分割算法分割圖像。
-
學習使用 GrabCut 算法提取前景
目標
學習:
- 使用各種低通濾波器模糊圖像
- 將定制過濾器應用于圖像(2D 卷積)
2D卷積(圖像過濾)
與一維信號一樣,圖像也可以使用各種低通濾波器 (LPF)、高通濾波器 (HPF) 等進行濾波。LPF 有助于消除噪聲、模糊圖像等。HPF 過濾器有助于在圖像中找到邊緣。
OpenCV 提供了一個函數 cv.filter2D()來將內核與圖像進行卷積。例如,我們將在圖像上嘗試平均濾波器。一個 5x5 平均濾波器內核如下所示:
操作是這樣的:保持這個內核高于一個像素,將所有低于這個內核的 25 個像素相加,取平均值,然后用新的平均值替換中心像素。對圖像中的所有像素繼續該操作。試試這段代碼并檢查結果:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
def image_filtering():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('opencv-logo.png'))
kernel = np.ones((5, 5), np.float32) / 25
dst = cv.filter2D(img, -1, kernel)
row, col, _ = img.shape
edge = np.full((row, 10, 3), (255, 255, 255), np.uint8);
images = [img, edge, dst]
dst = cv.hconcat(images)
cv.imshow("Image", dst)
cv.waitKey(-1)
cv.destroyAllWindows()
if __name__ == "__main__":
image_filtering()
結果如下:
圖像模糊(圖像平滑)
圖像模糊是通過將圖像與低通濾波器內核進行卷積來實現的。它對于去除噪聲很有用。它實際上從圖像中去除了高頻內容(比如噪聲,邊緣)。所以在這個操作中邊緣有點模糊(也有不模糊邊緣的模糊技術)。OpenCV 提供了四種主要的模糊技術類型。
1. 平均
這是通過將圖像與歸一化框濾波器進行卷積來完成的。它只是取內核區域下所有像素的平均值并替換中心元素。這通過函數 cv.blur() 或 cv.boxFilter() 完成。檢查關于內核的文檔來了解更多細節。我們應該指定內核的寬度和高度。一個 3x3 歸一化框濾波器將看起來像下面這樣:
注意
如果不想使用歸一化的框濾波器,則使用 cv.boxFilter()。給函數傳遞一個參數 normalize=False。
看一下下面的示例演示,它使用一個 5x5 大小的內核:
def averaging_filter():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('opencv-logo-white.png'))
blur = cv.blur(img, (5, 5))
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()
if __name__ == "__main__":
averaging_filter()
結果如下:
高斯模糊
在這個方法中,不是使用框濾波器,而是使用一個高斯內核。它通過函數 cv.GaussianBlur() 完成。我們應該指定內核的寬度和高度,它們應該是正奇數。我們還應該指定 X 和 Y 方向的標準差,sigmaX 和 sigmaY。如果只指定了 sigmaX,sigmaY 將取與 sigmaX 相同的值。如果兩者都為零,則它們根據內核大小計算得出。高斯模糊在從圖像中移除高斯噪聲非常有效。
如果你想,你可以使用函數 cv.getGaussianKernel() 創建一個高斯內核。
上面的代碼可以修改以用于高斯模糊:
blur = cv.GaussianBlur(img, (5, 5), 0)
如果使用函數 cv.getGaussianKernel(),則需要如下的代碼:
kernel = cv.getGaussianKernel(5, 0)
kernel_2D = kernel @ kernel.transpose()
blur = cv.filter2D(img, -1, kernel_2D)
結果如下:
3. 中值模糊
這里,函數 cv.medianBlur() 取內核區域下所有像素的中值,并將中心元素替換為該中值。這對圖像中的椒鹽噪聲非常有效。有趣的是,在上述濾波器中,中心元素是新計算的值,可能是圖像中的像素值或新值。但是在中值模糊中,中心元素總是被圖像中的某個像素值替換。它有效地降低了噪音。它的內核大小應該是一個正奇數。
在這個演示中,我為原始圖像添加了 50% 的噪點并應用了中值模糊。添加噪點,通過如下的 salt_and_pepper(image, n) 函數完成。檢查結果:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
import random
def salt_and_pepper(image, n):
print(image.shape)
for i in range(int(n / 2)):
row = random.randint(0, image.shape[0] - 1)
col = random.randint(0, image.shape[1] - 1)
write_black = random.randint(0, 2)
if write_black == 0:
image[row][col] = (255, 255, 255)
else:
image[row][col] = (0, 0, 0)
return image
def median_blurring():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('opencv-logo-white.png'))
img = salt_and_pepper(img, img.shape[0] * img.shape[1])
median = cv.medianBlur(img, 5)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(median), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()
if __name__ == "__main__":
median_blurring()
結果如下:
4. 雙邊濾波
cv.bilateralFilter() 函數在保持邊緣銳利的同時去除噪聲非常有效。但與其它濾波器相比,這個操作速度較慢。我們已經看到高斯濾波器采用像素周圍的鄰域,并找到其高斯加權平均值。這個高斯濾波器是一個單獨的空間函數,即在濾波時考慮附近的像素。它不考慮像素是否具有幾乎相同的強度。它不考慮像素是否是邊緣像素。所以它也模糊了邊緣,這是我們不想做的。
雙邊濾波在空間上也采用了高斯濾波器,但多了一個高斯濾波器,它是像素差的函數。空間的高斯函數確保只考慮附近的像素進行模糊處理,而強度差異的高斯函數確保只考慮那些與中心像素具有相似強度的像素進行模糊處理。所以它保留了邊緣,因為邊緣處的像素會有很大的強度變化。
下面的示例顯示了雙邊濾波器的使用(有關參數的詳細信息,請訪問文檔)。
def bilateral_blurring():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('bilateral.jpg'))
img = img[0:img.shape[0], 0:int(img.shape[1] / 2)]
blur = cv.bilateralFilter(img, 9, 75, 75)
images = [img, blur]
dest = cv.hconcat(images)
cv.imshow("Image", dest)
cv.waitKey(-1)
cv.destroyAllWindows()
if __name__ == "__main__":
bilateral_blurring()
結果如下:
看,表面上的紋理消失了,但邊緣依然存在。
其它資源
- 關于 雙邊濾波 的詳細信息
練習
參考文檔
使用帶有高斯核的cv2.GaussianBlur和cv2.filter2D的不同結果?
使用Python-OpenCV向圖片添加噪聲的實現(高斯噪聲、椒鹽噪聲)
Done.