2019年5月6日 星期一

.使用 Python + Open CV 進行圖像處理(二)

OpenCV Python Tutorial For Beginners 3 - How to Read, Write, Show Images in OpenCV



源:磐创AI



圖像預處理,對於整個圖像處理任務來講,特別重要。如果我們沒有進行恰當的預處理,無論我們有多麼好的數據,也很難得到理想的結果。

本篇是視覺入門系列教程的第二篇。整個視覺入門系列內容如下:

理解顏色模型與在圖像上繪製圖形(圖像處理基本操作)。

基本的圖像處理與濾波技術。

從特徵檢測到人臉檢測。

圖像分割與分水嶺(Watershed)算法(TBU)
在邊緣和輪廓檢測中,噪聲對檢測的精度有很大的影響。因此,去除噪聲和控制像素值的大小,可以幫助模型聚焦於整體特徵,獲得更高的精度。對應的圖像處理技術包括:模糊化(Blurring)、閾值化(thresholding)和形態轉換(morphological transformation)。

本篇我們將詳細介紹,這幾個常見的圖像預處理技術。(本文假設讀者已經熟悉卷積的概念。)

模糊化(Blurring)
模糊化的目標是實現降噪。我們必須格外注意的是:如果我們把邊緣檢測算法,應用到高解析度的圖像上,我們就會得到很多我們不感興趣的檢測結果;


相反,如果我們把圖像模糊太多,我們就會丟失數據。因此,我們需要找到一個適當的模糊量,從而不失去理想的邊緣。

有多種技術用於實現模糊效果,在這裡我們討論OpenCV中常用的四種技術:平均模糊(Averaging blurring)、高斯模糊(Gaussian blurring)、中值模糊(median blurring)和雙邊濾波(bilateral filtering)

這四種技術應用一個共同的基本原理,即使用濾波器(內核)對圖像進行卷積運算。不同的是,在四種模糊方法中,使用的濾波器的值是不同的。

平均模糊(Average blurring)是取給定內核(kernel)區域下所有像素值的平均值替換中心的值。例如,假設給定一個大小為5X5的內核(kernel),我們計算卷積結果的平均值,並將結果放在給定區域的中心。示例如下:


如果我們增加內核的大小,像素值將更加歸一化。因此圖像也會變得越來越模糊。讓我們用下面的代碼對比處理結果。(為了便於比較,將把原始圖像加到結果中,進行對比顯示。)

# Import the image and convert to RGB
img = cv2.imread('text.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Plot the image with different kernel sizes
kernels = [5, 11, 17]
fig, axs = plt.subplots(nrows = 1, ncols = 3, figsize = (20, 20))
for ind, s in enumerate(kernels):
img_blurred = cv2.blur(img, ksize = (s, s))
ax = axs[ind]
ax.imshow(img_blurred)
ax.axis('off')
plt.show()


中值模糊(Medium blurring)和平均模糊(Average blurring)是一樣的,只是它使用的是中值,而不是平均值。正由於這個特性,當我們需要處理圖像中,突然出現的噪音時(如「椒鹽噪音」),使用中值模糊(medium blurring)的效果要比平均模糊(average blurring)效果好。


高斯模糊(Gaussian blurring)是使用「值」具有高斯分佈的核函數。由於這些值是由高斯函數生成的,因此它的參數需要一個Sigma值。如上圖,內核的值在靠近中心的地方變高,在靠近角的地方變小。將該方法應用於具有正態分布的噪聲,如白噪聲,效果較好。

雙邊濾波(Bilateral Filtering)是高斯模糊的一個高級版本。模糊化不僅可以溶解噪聲,而且還會平滑邊緣。而雙邊濾波器,能在去除噪聲的同時,保持邊緣銳化。這是由於它不僅使用高斯分佈值,還同時考慮了距離和像素值的差異。因此,需要指定Sigma Space和Sigma Color這兩個參數。

# Blur the image
img_0 = cv2.blur(img, ksize = (7, 7))
img_1 = cv2.GaussianBlur(img, ksize = (7, 7), sigmaX = 0)
img_2 = cv2.medianBlur(img, 7)
img_3 = cv2.bilateralFilter(img, 7, sigmaSpace = 75, sigmaColor =75)
# Plot the images
images = [img_0, img_1, img_2, img_3]
fig, axs = plt.subplots(nrows = 1, ncols = 4, figsize = (20, 20))
for ind, p in enumerate(images):
ax = axs[ind]
ax.imshow(p)
ax.axis('off')
plt.show()


閾值化(Thres holding)
圖像的閾值化,就是利用圖像像素點分布規律,設定閾值進行像素點分割,進而得到圖像的二值圖像。我們需要設置閾值和最大值,然後據此相應地進行像素值轉換。

常用的閾值化包含有五種不同的類型:二進制閾值化、反二進制閾值化、閾值化到零、反閾值化到零,和閾值截斷。

img = cv2.imread('gradation.png')
# Thresholding
_, thresh_0 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
_, thresh_1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
_, thresh_2 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
_, thresh_3 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
_, thresh_4 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
# Plot the images
images = [img, thresh_0, thresh_1, thresh_2, thresh_3, thresh_4]
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (13, 13))
for ind, p in enumerate(images):
ax = axs[ind//3, ind%3]
ax.imshow(p)
plt.show()



如上圖所示,每種類型的閾值,都可以用數學公式表示,I(x, y)是像素點的強度(也稱為點(x, y)的像素值)。上圖中的圖像示例,可以更直接的理解,不同閾值化類型之間的區別。

只取一個閾值,並將其應用於圖像的所有部分,並不能滿足我們的全部需求。如果我們有一張在多個不同區域,亮度差異較多的圖片這種情況,將一個值應用於整個圖像,一般不利於我們的圖像處理任務。

其對應更好的方法,是對圖像的每個部分使用不同的閾值。對應這種情況,有另外一種閾值化技術,稱為自適應閾值化(Adaptive threshilding)。透過對圖像鄰域內閾值的計算,可以得到不同光照條件下的較好結果。

# Convert the image to grayscale
img = cv2.imread('text.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Adaptive Thresholding
_, thresh_binary = cv2.threshold(img, thresh = 127, maxval = 255, type = cv2.THRESH_BINARY)
adap_mean_2 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 7, 2)
adap_mean_2_inv = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY_INV, 7, 2)
adap_mean_8 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 7, 8)
adap_gaussian_8 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 7, 8)

我們需要將顏色模式,轉換為灰度來進行自適應閾值化。自適應閾值的參數有maxValue(在上面的示例中設置為255)、adaptive Method、thres hold Type、blocksize 和 C。這裡使用的自適應方法有兩種:adaptive thres hold meanc 和adaptive thres hold gaussianc。讓我們透過下方代碼,對比自適應閾值化的不同結果。

# Plot the images
images = [img, thresh_binary, adap_mean_2, adap_mean_2_inv,
adap_mean_8, adap_gaussian_8]
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 15))
for ind, p in enumerate(images):
ax = axs[ind%2, ind//2]
ax.imshow(p, cmap = 'gray')
ax.axis('off')
plt.show()


如上圖所示,左邊為原始圖像,與二進制閾值化結果圖。對比二進制閾值化結果圖,與右上方兩張結果圖(由adaptive thres hold meanc方法生成)可得,後者生成了更為詳細的結果。我們還可以看出,當C值更大時,圖像將變得更顯式。

C代表從均值或加權均值中,減去值的大小。透過觀察上圖右子圖上下兩幅圖像,我們還可以對比查看相同C值下adaptive thres hold meanc 和 adaptive thres hold _gaussianc兩種方法生成的不同效果圖。

沒有留言:

張貼留言