opencv之膨脹與腐蝕


腐蝕和膨脹 Erosion/Dilation

erosion/dilation,用白話說,就是讓圖像亮的區域收縮和擴張.

原理

  • 我們定義一個卷積核矩陣.這個矩陣可以是任何形狀的,但通常而言,是矩形或者圓形的.同時要定義一個錨點位置.
  • 用這個卷積核矩陣挨個地划過原始圖像矩陣,同時更改錨點位置的像素值.
  • 錨點位置的像素值更改為卷積核矩陣覆蓋的有效像素值中的最大值/最小值(分別對應膨脹/腐蝕).
    什么叫"有效"像素值呢?就是卷積核中不為0的那些位置.用公式表達的話,即:

膨脹和腐蝕,說白了就是個求"卷積核所表示的局部"的最大值最小值的過程.

我們來看一個例子:

import cv2
import numpy as np
def test1():
    img = np.zeros((10,10,1),np.uint8)
    img[3:7,3:7,:] = 255
    img[4:6,4:6,:] = 200
    
    kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
    erosion_dst = cv2.erode(img, kernel1)
    print(erosion_dst)

首先我們創建一個10 x 10的圖像,像素如下:

[[  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0 255 255 255 255   0   0   0]
 [  0   0   0 255 200 200 255   0   0   0]
 [  0   0   0 255 200 200 255   0   0   0]
 [  0   0   0 255 255 255 255   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]]

我們創建一個卷積核:

kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))

getStructuringElement api

三個參數分別為卷積核的形狀/大小/錨點位置. 默認錨點在矩陣的中心位置.

形狀有三種

上面代碼中我們創建的3 x 3矩形卷積核如下

用這個卷積核對原始圖像做腐蝕后得到的矩陣如下

即矩陣有如下變化:

[[  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0 255 255 255 255   0   0   0]
 [  0   0   0 255 200 200 255   0   0   0]
 [  0   0   0 255 200 200 255   0   0   0]
 [  0   0   0 255 255 255 255   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]]
 
-->

[[  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0 200 200   0   0   0   0]
 [  0   0   0   0 200 200   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]]

我們考慮第三行第四列img[2,3,:]這個像素.當我們的卷積核矩陣的錨點位置與該像素重合時,我們取周邊所有像素的最小值.最小值為0.所以該位置的像素值變為0. 其余位置的像素值同理可求.

我們稍微改一下我們的代碼,然后再看一下不同卷積核作用下的不同結果,會理解的更清楚

import cv2
import numpy as np
def test1():
    img = np.zeros((10,10,1),np.uint8)
    img[3:7,3:7,:] = 255
    img[4:6,4:6,:] = 200
    
    kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
    print(kernel1)
    erosion_dst = cv2.erode(img, kernel1)
    print(erosion_dst)

def test2():
    img = np.zeros((10,10,1),np.uint8)
    img[3:7,3:7,:] = 255
    img[4:6,4:6,:] = 200
    img[2,4,:] = 100
    
    kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
    erosion_dst = cv2.erode(img, kernel1)
    print(erosion_dst)
    
    kernel2 = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
    erosion_dst2 = cv2.erode(img, kernel2)
    print(erosion_dst2)

test2()

我們把原始圖像矩陣改為

[[  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0 100   0   0   0   0   0]
 [  0   0   0 255 255 255 255   0   0   0]
 [  0   0   0 255 200 200 255   0   0   0]
 [  0   0   0 255 200 200 255   0   0   0]
 [  0   0   0 255 255 255 255   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0]]

用kernal1時,kernal1如下:

以第四行,第五列的像素為例,用卷積核的錨點與之對應,此時計算的是其周圍八個像素的最小值,最小值為0.
所以我們得到的矩陣為

當我們用kernal2時,kernal2如下:

對第四行,第五列的像素,用卷積核的錨點與之對應,此時計算的不再是周圍八個像素的最小值,而是其正上方,正下方,正左邊,正右邊的四個像素的最小值.該值為100.
所以我們得到的矩陣為

opencv示例

from __future__ import print_function
import cv2 as cv
import numpy as np
import argparse

erosion_size = 0
max_elem = 2
max_kernel_size = 21

title_trackbar_element_type = 'Element:\n 0: Rect \n 1: Cross \n 2: Ellipse'
title_trackbar_kernel_size = 'Kernel size:\n 2n +1'
title_erosion_window = 'Erosion Demo'
title_dilatation_window = 'Dilation Demo'

def erosion(val):
    erosion_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_erosion_window)
    erosion_type = 0
    val_type = cv.getTrackbarPos(title_trackbar_element_type, title_erosion_window)
    if val_type == 0:
        erosion_type = cv.MORPH_RECT
    elif val_type == 1:
        erosion_type = cv.MORPH_CROSS
    elif val_type == 2:
        erosion_type = cv.MORPH_ELLIPSE
    element = cv.getStructuringElement(erosion_type, (2*erosion_size + 1, 2*erosion_size+1), (erosion_size, erosion_size))
    erosion_dst = cv.erode(src, element)
    cv.imshow(title_erosion_window, erosion_dst)
    
def dilatation(val):
    dilatation_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_dilatation_window)
    dilatation_type = 0
    val_type = cv.getTrackbarPos(title_trackbar_element_type, title_dilatation_window)
    if val_type == 0:
        dilatation_type = cv.MORPH_RECT
    elif val_type == 1:
        dilatation_type = cv.MORPH_CROSS
    elif val_type == 2:
        dilatation_type = cv.MORPH_ELLIPSE
    element = cv.getStructuringElement(dilatation_type, (2*dilatation_size + 1, 2*dilatation_size+1), (dilatation_size, dilatation_size))
    dilatation_dst = cv.dilate(src, element)
    cv.imshow(title_dilatation_window, dilatation_dst)

src = cv.imread("/home/sc/disk/keepgoing/opencv_test/j.png")
cv.namedWindow(title_erosion_window)
cv.createTrackbar(title_trackbar_element_type, title_erosion_window , 0, max_elem, erosion)
cv.createTrackbar(title_trackbar_kernel_size, title_erosion_window , 0, max_kernel_size, erosion)

cv.namedWindow(title_dilatation_window)
cv.createTrackbar(title_trackbar_element_type, title_dilatation_window , 0, max_elem, dilatation)
cv.createTrackbar(title_trackbar_kernel_size, title_dilatation_window , 0, max_kernel_size, dilatation)

erosion(0)
dilatation(0)
cv.waitKey()

通過createTrackbar在窗口上創建兩個bar,方便我們看不同種類不同大小的卷積核的影響.

cv.createTrackbar(title_trackbar_element_type, title_erosion_window , 0, max_elem, erosion)
cv.createTrackbar(title_trackbar_kernel_size, title_erosion_window , 0, max_kernel_size, erosion)

原始圖片:

處理效果:

opencv實現

https://github.com/opencv/opencv/blob/master/modules/imgproc/src/morph.dispatch.cpp


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM