形態變換
在opencv之膨脹與腐蝕中介紹了Dilation/Erosion的原理.建議先讀這一篇,搞懂原理. 這樣就可以很輕松地理解為什么本文的這些形態變換可以取得相應的效果.
基於此,我們可以組合出更多的形態變換以達到不同的目的.
有以下幾種:
- Opening
- Closing
- Morphological Gradient
- Top Hat
- Black Hat
Opening
先腐蝕再膨脹,可以把較小的目標去除.比如:
Closing
可以把物體內的小黑洞消除.比如:
Morphological Gradient
可以提取出物體的輪廓.
比如下圖,腐蝕和膨脹對物體內部的像素影響不大,(內部的局部最大值和最小值差不多),所以做完插值以后,邊緣的像素值差比較大,內部像素差值變為0,從而提取出物體輪廓.
Top Hat
Black Hat
from __future__ import print_function
import cv2 as cv
import numpy as np
import argparse
morph_size = 0
max_operator = 4
max_elem = 2
max_kernel_size = 21
title_trackbar_operator_type = 'Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat'
title_trackbar_element_type = 'Element:\n 0: Rect - 1: Cross - 2: Ellipse'
title_trackbar_kernel_size = 'Kernel size:\n 2n + 1'
title_window = 'Morphology Transformations Demo'
morph_op_dic = {0: cv.MORPH_OPEN, 1: cv.MORPH_CLOSE, 2: cv.MORPH_GRADIENT, 3: cv.MORPH_TOPHAT, 4: cv.MORPH_BLACKHAT}
def morphology_operations(val):
morph_operator = cv.getTrackbarPos(title_trackbar_operator_type, title_window)
morph_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_window)
morph_elem = 0
val_type = cv.getTrackbarPos(title_trackbar_element_type, title_window)
if val_type == 0:
morph_elem = cv.MORPH_RECT
elif val_type == 1:
morph_elem = cv.MORPH_CROSS
elif val_type == 2:
morph_elem = cv.MORPH_ELLIPSE
element = cv.getStructuringElement(morph_elem, (2*morph_size + 1, 2*morph_size+1), (morph_size, morph_size))
operation = morph_op_dic[morph_operator]
dst = cv.morphologyEx(src, operation, element)
cv.imshow(title_window, dst)
src = cv.imread("/home/sc/disk/keepgoing/opencv_test/j.png")
if src is None:
print('Could not open or find the image: ', args.input)
exit(0)
cv.namedWindow(title_window)
cv.createTrackbar(title_trackbar_operator_type, title_window , 0, max_operator, morphology_operations)
cv.createTrackbar(title_trackbar_element_type, title_window , 0, max_elem, morphology_operations)
cv.createTrackbar(title_trackbar_kernel_size, title_window , 0, max_kernel_size, morphology_operations)
morphology_operations(0)
cv.waitKey()
可以用上述代碼感受一下對不同圖片,采用不同操作,不同參數,得到的結果是怎樣的.
利用形態變換提取圖像中的水平線
看一個具體的例子
我們想從下圖中提取出水平線出來.
前面講過,膨脹和腐蝕都是通過卷積核去定義一個要從什么樣的區域去取局部極大值或局部極小值. 那為了完成水平線的提取,我們可以定義自己的特定形狀的卷積核去完成這個功能.
# 形態變換實現水平線和音符提取
import cv2 as cv
import numpy as np
def test():
src = cv.imread("/home/sc/disk/keepgoing/opencv_test/music.png",cv.IMREAD_COLOR)
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
gray = cv.bitwise_not(gray)
cv.imshow("gray",gray)
horizontal = np.copy(gray)
vertical = np.copy(gray)
##設計特定形狀卷積核
cols = horizontal.shape[1]
horizontal_size = cols // 30
horizontalStructure = cv.getStructuringElement(cv.MORPH_RECT, (horizontal_size, 1))
print(horizontalStructure)
horizontal1 = cv.erode(horizontal, horizontalStructure)
cv.imshow("h1",horizontal1)
horizontal2 = cv.dilate(horizontal1, horizontalStructure)
cv.imshow("h2",horizontal2)
##設計特定形狀卷積核
rows = vertical.shape[0]
verticalsize = rows // 30
verticalStructure = cv.getStructuringElement(cv.MORPH_RECT, (1, verticalsize))
vertical = cv.erode(vertical, verticalStructure)
vertical = cv.dilate(vertical, verticalStructure)
cv.imshow("v",vertical)
test()
if 27 == cv.waitKey(0):
cv.destroyAllWindows()
首先我們完成將圖像的預處理.
這里用了cv.bitwise_not(gray)將我們關注的部分變為亮的.不關注的變為暗的. 因為膨脹和腐蝕都是針對亮的區域而言的.指亮的區域的擴張或收縮.
接下來就是如何定義我們的卷積核呢?
以音符的提取為例,我們希望把水平方向的白色橫線去除,即我們更關注垂直方向的像素點. 水平方向的白色橫線的上下位置基本是黑色的背景. 所以我們需要的卷積核是一個如下:
這樣對白色橫線上的像素點,通過取該像素點上下方若干個點的最小值,把該像素點灰度值變為0.從而達到去除白色橫線的目的.
最終得到如下: