1. 程式人生 > >opencv之形態變換

opencv之形態變換

形態變換

在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.從而達到去除白色橫線的目的.
最終得到如下: