BG66: 圖像形態學操作知多少

近幾年開始接觸圖像處理或者計算機視覺領域的朋友可能對形態學操作比較陌生,畢竟現在最火的是使用深度學習的方法來解決圖像的基本問題。不過,這並不是說傳統的圖像處理方法沒有用武之地。首先,熟知基礎的圖像處理方法是進行原始創新的基石;其次,傳統方法的可解釋性往往更強,這在一些工業生產中往往是極為重要的。現在很多生產流水線上工件的視覺檢驗仍然是使用基本的圖像處理方法,雖然並不包含多麼高深的策略,但是簡單實用,魯棒性高。

1. 二值化

圖像的形態學操作包括:腐蝕、膨脹、開運算、閉運算以及梯度運算等等,這些操作都是基於二值圖像的。對於彩色圖或者灰度圖,需要先轉換成只有黑白兩色的二值圖像。OpenCV提供了threashold()函數專門完成這種工作。下面的例子就是將一張RGB的彩色圖轉換成二值圖的示例。

<code>import cv2
import matplotlib
%matplotlib notebook
import matplotlib.pyplot as plt

img = cv2.imread('../data/bg66/opencv-icon.png')
plt.figure(figsize=(10.8, 5.4))
# 原圖
plt.subplot(1,2,1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('RGB')
# 二值化處理
plt.subplot(1,2,2)
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
_, img = cv2.threshold(img, 200, 255, 1)
plt.imshow(img, cmap ='gray')
plt.title('Binary')
plt.savefig('../output/bg66/binary.png', dpi=300, bbox_inches='tight')/<code>

Output:

BG66: 圖像形態學操作知多少

2. 腐蝕

腐蝕實際上就是模版與二值圖像進行“與”操作,即模版在圖像上逐像素滑動,只有模版全都覆蓋到白色像素的時候結果圖像中該像素位置為白色,否則為黑色。更加直觀的理解就是“削皮”。“腐蝕”會將二值圖像內的白色目標“削”去表面層,每迭代一次,大約“削”去模版大小的一半厚度的“皮”。這裡就是用OpenCV的圖標來演示一下OpenCV模塊中提供的腐蝕操作。

<code>import numpy as np

plt.figure(figsize=(10.8, 5.4))
# 顯示輸入圖
plt.subplot(1,2,1)
plt.imshow(img, cmap='gray')
plt.title('Origin')
# 顯示腐蝕圖
plt.subplot(1,2,2)
kernel = np.ones((5,5), dtype=np.uint8)
erode_img = cv2.erode(img, kernel, iterations=10)
plt.imshow(erode_img, cmap='gray')
plt.title('erode image')
plt.savefig('../output/bg66/erode.png', dpi=300, bbox_inches='tight')/<code>

Output:

BG66: 圖像形態學操作知多少

從腐蝕操作的結果圖像看來,白色目標物體“變瘦”了一圈,“腐蝕”這個名字還是非常形象的。當然在實際運用的時候需要選擇迭代次數,即要腐蝕到足夠,又不能將目標全都腐蝕掉。

3. 膨脹

膨脹實際上就是模版與二值圖像進行“或”操作,即模版在圖像上逐像素滑動,只需模版覆蓋到至少一個白色像素的時候結果圖像中該像素位置為白色,否則為黑色。直觀的理解就是“穿衣”,每次迭代都給白色目標穿上一層“衣服”,衣服的厚度大約為模版大小的一半。OpenCV提供了dilate()函數來完成這種操作,具體演示如下所示:

<code>plt.figure(figsize=(10.8, 5.4))
# 顯示原圖
plt.subplot(1,2,1)
plt.imshow(img, cmap='gray')
plt.title('Origin')
# 顯示膨脹圖
plt.subplot(1,2,2)
dilate_img = cv2.dilate(img, kernel, iterations=10)
plt.imshow(dilate_img, cmap='gray')
plt.title('dilate image')
plt.savefig('../output/bg66/dilate.png', dpi=300, bbox_inches='tight')/<code>

Output:

BG66: 圖像形態學操作知多少

從以上的結果圖像來看,“膨脹”這個名字也很形象,就像是吹氣球一樣填充了起來。

4. 開運算

先進行腐蝕操作後進行膨脹操作,合併起來就是“開運算”。乍一聽還覺得這是吃飽撐的,但是仔細一想發現別有洞天。其原因就在於,腐蝕和膨脹這兩個操作並不是完全的互逆運算,因為腐蝕和膨脹會造成一些結構的消失,這就是另一方運算無法恢復的。OpenCV提供了cv2.morphologyEx()函數,傳送cv2.MORPH_OPEN參數就可以執行開運算。以下例程就是演示“開運算”的一個用途——消除圖像中的“噪聲點”,一般這些噪聲點都是比較分立,且比較細小。

<code>plt.figure(figsize=(10.8, 5.4))
# 在圖像上加入隨機噪聲
plt.subplot(1,2,1)
x_rand = np.random.randint(img.shape[1], size=(1000))
y_rand = np.random.randint(img.shape[0], size=(1000))
n_img = img.copy()
n_img[y_rand, x_rand] = 255
plt.imshow(n_img, cmap='gray')
plt.title('white-noised image')
# 使用開運算
plt.subplot(1,2,2)
open_img = cv2.morphologyEx(n_img, cv2.MORPH_OPEN, kernel)
plt.imshow(open_img, cmap='gray')
plt.title('open operation')
plt.savefig('../output/bg66/open.png', dpi=300, bbox_inches='tight')/<code>

Output:

BG66: 圖像形態學操作知多少

從結果圖像看出,開運算將“噪聲點”清除得很乾淨。當然這是生成的噪聲點,基本上大小為一個像素,所以清理得比較乾淨。在實際情況中,可能需要調整模版的大小,從而有效應對。

5. 閉運算

既然有先腐蝕後膨脹的開運算,那麼就應該也有先膨脹後腐蝕的“閉運算”。OpenCV提供了cv2.morphologyEx()函數,傳送cv2.MORPH_CLOSE參數就可以執行閉運算。在實際應用中,閉運算常常被用於填補白色目標上存在的一些空洞。以下例子就展示了閉運算的這一用途。

<code>plt.figure(figsize=(10.8, 5.4))
# 在圖像上加入隨機噪聲
plt.subplot(1,2,1)
x_rand = np.random.randint(img.shape[1], size=(1000))
y_rand = np.random.randint(img.shape[0], size=(1000))
n_img = img.copy()
n_img[y_rand, x_rand] = 0
plt.imshow(n_img, cmap='gray')
plt.title('black-noised image')
# 使用開運算
plt.subplot(1,2,2)
close_img = cv2.morphologyEx(n_img, cv2.MORPH_CLOSE, kernel)
plt.imshow(close_img, cmap='gray')
plt.title('close operation')
plt.savefig('../output/bg66/close.png', dpi=300, bbox_inches='tight')/<code>

Output:

BG66: 圖像形態學操作知多少

從結果圖看來,白色目標上一些細小的黑色空洞都已經被填補上了。還是需要注意一下,這裡的空洞是人為生成的大小為一個像素的空洞,因此填補起來比較容易一些,如果空洞較大,那麼填補就會出現問題,這是應當適當調整模版的大小。

6. 形態學梯度

還有一個比較常用的算子——形態學梯度。為什麼不直接稱為梯度?因為這裡所說的“形態學梯度”與數學上的梯度存在相似性,但並不完全是“梯度”。“形態學梯度”的生成方式是原二值圖像減去腐蝕之後的二值圖像,這在表觀上就是白色物體的輪廓。OpenCV提供了cv2.morphologyEx()函數,傳送cv2.MORPH_GRADIENT參數就可以執行形態學梯度運算,示例如下:

<code>plt.figure(figsize=(10.8, 5.4))
plt.subplot(1,2,1)
plt.imshow(img, cmap='gray')
plt.title('Origin')
# 計算梯度圖,也就是輪廓圖
plt.subplot(1,2,2)
grad_img = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
plt.imshow(grad_img, cmap='gray')
plt.title('gradient image')
plt.savefig('../output/bg66/grad.png', dpi=300, bbox_inches='tight')/<code>

Output:

BG66: 圖像形態學操作知多少

結果圖像表明形態學梯度能夠提取白色目標的輪廓。通常這個操作用於獲取邊緣輪廓,為之後的目標定位和形狀建模提供便利。

除了以上5個常用的形態學操作之外,還有其他一些操作,都是基本的形態學操作之間或者與原圖之間操作的組合。掌握了基本的圖像形態學操作,處理一些工業場景下的問題,比如工件形狀、工件裂紋或者工件特定部位的位置等等任務就得心應手了。


分享到:


相關文章: