形態學操作其實就是改變物體的形狀,比如腐蝕就是"變瘦",膨脹就是"變胖",看下圖就明白了:
形態學操作一般作用於二值化圖(也可直接作用於原圖),來連接相鄰的元素或分離成獨立的元素。腐蝕和膨脹是針對圖片中的白色部分!
腐蝕
腐蝕的效果是把圖片"變瘦",其原理是在原圖的小區域內取局部最小值。因為是二值化圖,只有0和255,所以小區域內有一個是0該像素點就為0:
這樣原圖中邊緣地方就會變成0,達到了瘦身目的
OpenCV中用cv2.erode()
函數進行腐蝕,只需要指定核的大小就行:
img = cv2.imread('j.bmp', 0) kernel = np.ones((5, 5), np.uint8) erosion = cv2.erode(img, kernel) # 腐蝕
這個核也叫結構元素,因為形態學操作其實也是應用卷積來實現的。
結構元素可以是矩形/橢圓/十字形,可以用cv2.getStructuringElement()
來生成不同形狀的結構元素,比如:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 矩形結構 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # 橢圓結構 kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) # 十字形結構
膨脹
膨脹與腐蝕相反,取的是局部最大值,效果是把圖片"變胖":
dilation = cv2.dilate(img, kernel) # 膨脹
開/閉運算
先腐蝕(瘦)后膨脹(胖)叫開運算(因為先腐蝕會分開物體,這樣容易記住),其作用是:分離物體,消除小區域。這類形態學操作用cv2.morphologyEx()
函數實現:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 定義結構元素 img = cv2.imread('j_noise_out.bmp', 0) opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 開運算
def open_demo(image): print(image.shape) gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) # kernel = np.ones((5,5),np.uint16) kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,5)) dst = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel) cv.imshow("open_demo",dst)
閉運算則相反:先膨脹后腐蝕(先膨脹會使白色的部分擴張,以至於消除/"閉合"物體里面的小黑洞,所以叫閉運算)
img = cv2.imread('j_noise_in.bmp', 0) closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 閉運算
def close_demo(image): print(image.shape) gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) cv.imshow("binary", binary) # kernel = np.ones((5,5),np.uint16) kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,5)) dst = cv.morphologyEx(binary,cv.MORPH_CLOSE,kernel) cv.imshow("close_demo",dst)
經驗之談:很多人對開閉運算的作用不是很清楚,看上圖↑: 如果我們的目標物體外面有很多無關的小區域,就用開運算去除掉; 如果物體內部有很多小黑洞,就用閉運算填充掉。
開操作去掉外部小的干擾,用腐蝕也能做到,二者的區別在於開操作只去掉外部小的干擾而保留了其他部分不變
其他形態學操作
- 形態學梯度:膨脹圖減去腐蝕圖,
dilation - erosion
,這樣會得到物體的輪廓:
img = cv2.imread('school.bmp', 0) gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
- 頂帽(tophat):原圖減去開操作后的圖:
src - opening
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
- 黑帽(blackhat):閉運算圖像與原圖像差值:
closing - src
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
def tophat_demo(image): gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU) # kernel = np.ones((5,5),np.uint16) kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) dst = cv.morphologyEx(gray, cv.MORPH_GRADIENT, kernel) # dst = cv.morphologyEx(binary, cv.MORPH_BLACKHAT, kernel) """給dst圖像添加亮度""" # cimage = np.array(gray.shape,np.uint8) # cimage = 120 # dst = cv.add(dst,cimage) cv.imshow("MORPH_GRADIENT_demo", dst)
對Binary二值圖進行腐蝕 膨脹等形態學操作
def erode_demo(image): print(image.shape) gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) # kernel = np.ones((5,5),np.uint16) kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,5)) dst = cv.erode(binary,kernel=kernel) cv.imshow("erode_demo",dst) def dilate_demo(image): print(image.shape) gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) cv.imshow("binary", binary) # kernel = np.ones((5,5),np.uint16) kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,5)) dst = cv.dilate(binary,kernel=kernel) cv.imshow("dilate_demo",dst)
對BGR原圖直接進行腐蝕 膨脹等形態學操作
"""BGR圖像直接進行腐蝕 膨脹""" src0 = cv.imread('beauty1.jpg') cv.imshow('input_image',src0) kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,5)) dilate_dst = cv.dilate(src0,kernel=kernel) cv.imshow("dilate_dst",dilate_dst) erode_dst = cv.erode(src0,kernel=kernel) cv.imshow("erode_dst",erode_dst)
參考:
https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html#morphological-ops
https://www.jianshu.com/p/05ef50ac89ac