提示:
python
轉載請詳細註明原做者及出處,謝謝!
算法
本文介紹使用OpenCV-Python進行形態學處理函數
本文不介紹形態學處理的基本概念,因此讀者須要預先對其有必定的瞭解。測試
形態學處理的核心就是定義結構元素,在OpenCV-Python中,可使用其自帶的getStructuringElement函數,也能夠直接使用NumPy的ndarray來定義一個結構元素。首先來看用getStructuringElement函數定義一個結構元素:ui
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
這就定義了一個5×5的十字形結構元素,以下:spa
也能夠用NumPy來定義結構元素,以下:code
NpKernel = np.uint8(np.zeros((5,5))) for i in range(5): NpKernel[1, i] = 1 NpKernel[i, 1] = 1
這二者方式定義的結構元素徹底同樣:對象
[[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]]
這裏能夠看出,用OpenCV-Python內置的常量定義橢圓(MORPH_ELLIPSE)和十字形結構(MORPH_CROSS)元素要簡單一些,若是定義矩形(MORPH_RECT)和自定義結構元素,則二者差很少。圖片
本篇文章將用參考資料1中的相關章節的圖片作測試:utf-8
下面先以腐蝕圖像爲例子介紹如何使用結構元素:
#coding=utf-8 import cv2 import numpy as np img = cv2.imread('D:/binary.bmp',0) #OpenCV定義的結構元素 kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3)) #腐蝕圖像 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()
如上所示,腐蝕和膨脹的處理很簡單,只需設置好結構元素,而後分別調用cv2.erode(...)和cv2.dilate(...)函數便可,其中第一個參數是須要處理的圖像,第二個是結構元素。返回處理好的圖像。
結果以下:
瞭解形態學基本處理的同窗都知道,開運算和閉運算就是將腐蝕和膨脹按照必定的次序進行處理。但這二者並非可逆的,即先開後閉並不能獲得原先的圖像。代碼示例以下:
#coding=utf-8 import cv2 import numpy as np img = cv2.imread('D:/binary.bmp',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中相關章節的圖片:
代碼以下:
#coding=utf-8 import cv2 import numpy image = cv2.imread("D:/building.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()
處理結果以下:
與邊緣檢測不一樣,拐角的檢測的過程稍稍有些複雜。但原理相同,所不一樣的是先用十字形的結構元素膨脹像素,這種狀況下只會在邊緣處「擴張」,角點不發生變化。接着用菱形的結構元素腐蝕原圖像,致使只有在拐角處纔會「收縮」,而直線邊緣都未發生變化。
第二步是用X形膨脹原圖像,角點膨脹的比邊要多。這樣第二次用方塊腐蝕時,角點恢復原狀,而邊要腐蝕的更多。因此當兩幅圖像相減時,只保留了拐角處。示意圖以下(示意圖來自參考資料1):
代碼以下:
#coding=utf-8 import cv2 image = cv2.imread("D:/building.jpg", 0) origin = cv2.imread("D:/building.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()
注意,因爲封裝的緣故,OpenCV中函數參數中使用的座標系和NumPy的ndarray的座標系是不一樣的,在50行能夠看出來。抽空我向OpenCV郵件列表提一下,看個人理解是否是正確的。
你們能夠驗證一下,好比在代碼中插入這兩行代碼,就能知道結果了:
cv2.circle(image, (5, 10), 5, (255,0,0)) image[5, 10] = 0
經過上面的代碼就能檢測到圖像中的拐角並標出來,效果圖以下:
固然,這只是個形態學處理示例,檢測結果並很差。
未完待續...
在未來的某一篇文章中將作個總結,介紹下OpenCV中經常使用的函數,如threshold、bitwise_xxx,以及繪製函數等。
一、《Opencv2 Computer Vision Application Programming Cookbook》
二、《OpenCV References Manule》
若是以爲本文寫的還能夠的話,請輕點「頂」,方便讀者、以及您的支持是我寫下去的最大的兩個動力。