Python OpenCV 形態學 (六)
形態學的操作包括:腐蝕、膨脹、細化、開運算、閉運算
數字影象處理中的形態學處理是指將數字形態學作為工具從影象中提取對於表達和描繪區域形狀有用處的影象分量,
比如:邊界、骨架、以及凸殼,還包括用於預處理或後處理的形態學過濾、細化和修剪等。影象形態學處理中主要是
二值影象。
一 基本概念:
1. 二值影象:
二值影象是每個畫素只有兩個可能值的數字影象。常用黑白、B&W、單色影象表示二值影象,但是也可以用來表示
每個畫素只有一個取樣值的任何影象,例如灰度影象等。
二值影象經常出現在數字影象處理中作為影象掩碼或者在影象分割、二值化和dithering的結果中出現。二值影象經常
使用點陣圖格式儲存。所以可以解釋為二維整數格 Z2 ,
1. 二值影象的邏輯運算
在影象處理中主要用到的邏輯運算有: 與、或、非(求補)。
2. 膨脹
膨脹和腐蝕是形態學處理的基礎。
是以得到B的相對與它自身原點的映像並且由Z對映像進行移位為基礎的。A被B膨脹是所有位移Z的集合,這樣,和A至少
有一個元素是重疊的。
過程:1. 用結構元素B,掃描影象A的每一個畫素
2. 用結構元素與其覆蓋的二值影象做 “與” 操作
3. 如果都為0,結果影象的該畫素為0;否則為1
3. 腐蝕
對Z中的集合A和B,B對A進行腐蝕的整個過程如下:
1. 用結構元素B,掃描影象A的每一個畫素
2. 用結構元素與其覆蓋的二值影象做 “與”操作
3. 如果都為1,結果影象的該畫素為1. 否則為0
腐蝕處理的結果是使原來的二值影象減小一圈
4. 匹配
5. 開閉運算
開運算:是先腐蝕,後膨脹處理。
閉運算:是先膨脹,後腐蝕處理。
6. 細化
影象細化一般作為一種影象預處理技術出現,目的是提取源影象的骨架,即將原影象中線條寬度大於1個畫素的線條細化成
只有一個畫素寬,形成“骨架”,形成骨架後能比較容易的分析影象,如提取影象的特徵。
細化基本思想是“層層剝奪”,即從線條邊緣開始一層一層向裡剝奪,直到線條剩下一個畫素的為止:影象細化大大地壓縮了
原始影象的資料量,並保持其形狀的基本拓撲結構不變,
細化演算法應滿足以下條件:
1.) 將條形區域變成一條薄線;
2.)薄線應位於原條形區域的中心:
3.) 薄線應保持原影象的拓撲特性.
細化分成序列細化和並行細化,序列細化即是一邊檢測滿足細化條件的點,一邊刪除細化點;並行細化即是檢測細化點的時候
不進行點的刪除只進行標記,而在檢測完整幅影象後一次性去除要細化的點。
常用的影象細化演算法有 hilditch 演算法, pavlidis 演算法和 rosenfeld 演算法等。
注: 進行細化演算法前要先對影象進行二值化,即影象中只包含 (黑)和 (白)兩種顏色。
二: 定義結構元素
形態學處理的核心就是定義結構元素,在 Opencv python 中,使用自帶的 getStructuringElement 函式,也可以使用
Numpy 的 ndarray 來定義一個結構元素。
1. 使用getStructuringElement 定義一個結構元素。
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5)) # MORPH_CROSS 十字形結構
這裡定義了一個 5 * 5 的十字形結構元素
2. 使用NumPy 的 ndarray 來定義結構元素
element = np.uint8(np.zeros((5,5)))
getStructuringElement 與 Numpy 定義的元素結構是完全一樣的
[[0 0 1 0 0]
[0 0 1 0 0]
[1 1 1 1 1]
[0 0 1 0 0]
[0 0 1 0 0]]
三: 腐蝕 膨脹
效果示例程式碼:
#!/usr/bin/env python
# encoding: utf-8
import cv2
import numpy as np
img = cv2.imread('2.jpg',0)
#OpenCV定義的結構元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))
#顯示原始二值影象
cv2.imshow("original",img)
#腐蝕影象
eroded = cv2.erode(img,kernel)
#顯示腐蝕後的影象
cv2.imshow("Eroded Image",eroded);
#膨脹影象
dilated = cv2.dilate(img,kernel)
#顯示膨脹後的影象
cv2.imshow("Dilated Image",dilated);
#原影象
cv2.imshow("Origin", img)
#NumPy定義的結構元素
NpKernel = np.uint8(np.ones((3,3)))
Nperoded = cv2.erode(img,NpKernel)
#顯示腐蝕後的影象
cv2.imshow("Eroded by NumPy kernel",Nperoded);
cv2.waitKey(0)
cv2.destroyAllWindows()
效果圖:
注意:
腐蝕和膨脹,表面看上去好像是一對互逆的操作,實際上,這兩種操作不具有互逆的關係。
四: 開運算 閉運算
開運算和閉運算正是依據腐蝕和膨脹的不可逆性演變而來。
開運算和閉運算是將腐蝕和膨脹照一定的次序進行處理,兩者也是不可逆的,即先開後閉運算並不能得到原先的影象。
使用示例程式碼:
#!/usr/bin/env python
# encoding: utf-8
import cv2
import numpy as np
img = cv2.imread('2.jpg',0)
#定義結構元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5, 5))
#閉運算
closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
#顯示腐蝕後的影象
cv2.imshow("Close",closed);
#開運算
opened = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
#顯示腐蝕後的影象
cv2.imshow("Open", opened);
cv2.waitKey(0)
cv2.destroyAllWindows()
效果圖:
閉運算用來連線被誤分為許多小塊的物件,而開運算用於移除由影象噪音形成的斑點。因此,某些情況下可以連續運用這兩種運算。
如對一副二值圖連續使用閉運算和開運算,將獲得影象中的主要物件。同樣,如果想消除影象中的噪聲(影象中的小點),也可以對
影象先用開運算後用閉運算,不過這樣也會消除一些破碎的物件。
五:用形態學運算檢測邊和角點
這裡只作為介紹使用,實際使用時使用 Canny 或 Harris 等演算法。
1.)檢測邊緣
膨脹時,影象中的物體會向周圍“擴張”;腐蝕時,影象中的物體會“收縮”。由於變化區域只發生在邊緣,所以這時將兩幅影象相減,
得到的就是影象中物體的邊緣。
示例:
#!/usr/bin/env python
# encoding: utf-8
import cv2
import numpy as np
image = cv2.imread("2.jpg",0);
#構造一個3×3的結構元素
element = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))
dilate = cv2.dilate(image, element)
erode = cv2.erode(image, element)
#將兩幅影象相減獲得邊,第一個引數是膨脹後的影象,第二個引數是腐蝕後的影象
result = cv2.absdiff(dilate,erode);
#上面得到的結果是灰度圖,將其二值化以便更清楚的觀察結果
retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY);
#反色,即對二值圖每個畫素取反
result = cv2.bitwise_not(result);
#顯示影象
cv2.imshow("result",result);
cv2.waitKey(0)
cv2.destroyAllWindows()
影象效果:
2.)檢測拐角
第一步:與邊緣檢測不同,拐角的檢測過程有些複雜,但原理相同,不同的是先用十字形的結構元素膨脹畫素,
這種情況下只會在邊緣處“擴張”,角點不發生變化。接著用菱形的結構元素腐蝕原影象,導致只有在拐角
處才會“收縮”,而直線邊緣都發生變化。
第二步:用X形膨脹原影象,角點膨脹的比邊要多。這樣第二次用方塊腐蝕時,角點恢復原狀,而邊要腐蝕
的更多,所以當兩幅影象相減時,只保留了拐角處。
示例:
#!/usr/bin/env python
# encoding: utf-8
import cv2
import numpy as np
image = cv2.imread("mini.jpg", 0)
origin = cv2.imread("mini.jpg")
#構造5×5的結構元素,分別為十字形、菱形、方形和X型
cross = cv2.getStructuringElement(cv2.MORPH_CROSS,(5, 5))
#菱形結構元素的定義稍麻煩一些
diamond = cv2.getStructuringElement(cv2.MORPH_RECT,(5, 5))
diamond[0, 0] = 0
diamond[0, 1] = 0
diamond[1, 0] = 0
diamond[4, 4] = 0
diamond[4, 3] = 0
diamond[3, 4] = 0
diamond[4, 0] = 0
diamond[4, 1] = 0
diamond[3, 0] = 0
diamond[0, 3] = 0
diamond[0, 4] = 0
diamond[1, 4] = 0
square = cv2.getStructuringElement(cv2.MORPH_RECT,(5, 5))
x = cv2.getStructuringElement(cv2.MORPH_CROSS,(5, 5))
#使用cross膨脹影象
result1 = cv2.dilate(image,cross)
#使用菱形腐蝕影象
result1 = cv2.erode(result1, diamond)
#使用X膨脹原影象
result2 = cv2.dilate(image, x)
#使用方形腐蝕影象
result2 = cv2.erode(result2,square)
#result = result1.copy()
#將兩幅閉運算的影象相減獲得角
result = cv2.absdiff(result2, result1)
#使用閾值獲得二值圖
retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY)
#在原圖上用半徑為5的圓圈將點標出。
for j in range(result.size):
y = j / result.shape[0]
x = j % result.shape[0]
if result[x, y] == 255:
cv2.circle(image, (y, x), 5, (255,0,0))
cv2.imshow("Result", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果圖:
提示:
sunny2038 在部落格中提出OpenCV 中函式引數中使用的座標系和 NumPy 的 ndarray 的座標系是不同的,
本文參考和轉載:
http://blog.csdn.net/sunny2038/article/details/9137759
http://blog.csdn.net/rocky_shared_image/article/details/7821823
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/opening_closing_hats/opening_closing_hats.html