形態(tài)學(xué)圖像處理(一):膨脹與腐蝕

【OpenCV入門教程之十】 形態(tài)學(xué)圖像處理(一):膨脹與腐蝕

一、理論與概念講解——從現(xiàn)象到本質(zhì)

形態(tài)學(xué)(morphology)一詞通常表示生物學(xué)的一個分支,該分支主要研究動植物的形態(tài)和結(jié)構(gòu)。而我們圖像處理中指的形態(tài)學(xué),往往表示的是數(shù)學(xué)形態(tài)學(xué)。

數(shù)學(xué)形態(tài)學(xué)(Mathematical morphology) 是一門建立在格論和拓?fù)鋵W(xué)基礎(chǔ)之上的圖像分析學(xué)科,是數(shù)學(xué)形態(tài)學(xué)圖像處理的基本理論。

其基本的運(yùn)算包括:二值腐蝕和膨脹、二值開閉運(yùn)算、骨架抽取、極限腐蝕、擊中擊不中變換、形態(tài)學(xué)梯度、Top-hat變換、顆粒分析、流域變換、灰值腐蝕和膨脹、灰值開閉運(yùn)算、灰值形態(tài)學(xué)梯度等。

最基本的形態(tài)學(xué)操作有二種:膨脹與腐蝕(Dilation與Erosion)。膨脹與腐蝕能實(shí)現(xiàn)多種多樣的功能,主要如下:

  • 消除噪聲
  • 分割(isolate)出獨(dú)立的圖像元素,在圖像中連接(join)相鄰的元素。
  • 尋找圖像中的明顯的極大值區(qū)域或極小值區(qū)域
  • 求出圖像的梯度

在進(jìn)行腐蝕和膨脹的講解之前,首先需要注意,腐蝕和膨脹是對白色部分(高亮部分)而言的,不是黑色部分。膨脹就是圖像中的高亮部分進(jìn)行膨脹,“領(lǐng)域擴(kuò)張”,效果圖擁有比原圖更大的高亮區(qū)域。腐蝕就是原圖中的高亮部分被腐蝕,“領(lǐng)域被蠶食”,效果圖擁有比原圖更小的高亮區(qū)域。

1.1 結(jié)構(gòu)元和腐蝕

1.2 膨脹

按數(shù)學(xué)方面來說,膨脹或者腐蝕操作就是將圖像(或圖像的一部分區(qū)域,我們稱之為A)與核(我們稱之為B)進(jìn)行卷積。核可以是任何的形狀和大小,它擁有一個單獨(dú)定義出來的參考點(diǎn),我們稱其為錨點(diǎn)(anchorpoint)。多數(shù)情況下,核是一個小的中間帶有參考點(diǎn)和實(shí)心正方形或者圓盤,其實(shí),我們可以把核視為模板或者掩碼。

而膨脹就是求局部最大值的操作,核B與圖形卷積,即計算核B覆蓋的區(qū)域(體現(xiàn)局部)的像素點(diǎn)的最大值,并把這個最大值賦值給參考點(diǎn)指定的像素。這樣就會使圖像中的高亮區(qū)域逐漸增長。


注意:其實(shí)右圖要比左圖大了一圈

膨脹可以理解為B的中心(錨點(diǎn))沿著A的外邊界走了一圈。膨脹是對高亮部分而言,A區(qū)域之外的部分 < A的高亮像素,所里外面被里面取代。

效果圖,高亮部分膨脹


膨脹的數(shù)學(xué)表達(dá)式:

(x, y)周邊區(qū)域(x+x', y+y')內(nèi)的最大值代替(x, y)的值。

1.3 腐蝕

腐蝕與膨脹是相反的操作,腐蝕是求局部最小值。

可與膨脹對比理解。

注意:其實(shí)右圖要比左圖小了一圈

腐蝕可以理解為B的中心(錨點(diǎn))沿著A的內(nèi)邊界走了一圈。腐蝕也是對高亮部分而言,A區(qū)域之外的部分 < A的高亮像素,所里里面被外面取代。

A中能完全包含B的像素被留下來了。

效果圖,高亮部分被腐蝕


二、函數(shù)和實(shí)例

2.1 函數(shù)源碼

opencv\sources\modules\imgproc\src\morph.cpp
// 腐蝕
void cv::erode( InputArray src, OutputArray dst, InputArray kernel,
                Point anchor, int iterations,
                int borderType, const Scalar& borderValue )
{
    CV_INSTRUMENT_REGION()

    morphOp( MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType, borderValue );
}

// 膨脹
void cv::dilate( InputArray src, OutputArray dst, InputArray kernel,
                 Point anchor, int iterations,
                 int borderType, const Scalar& borderValue )
{
    CV_INSTRUMENT_REGION()

    morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue );
}

erode和dilate這兩個函數(shù)內(nèi)部就是調(diào)用了一下morphOp,只是調(diào)用morphOp時,第一個參數(shù)標(biāo)識符不同,一個為MORPH_ERODE(腐蝕),一個為MORPH_DILATE(膨脹)。

這些函數(shù)在imgproc.hpp中后面的參數(shù)是設(shè)置了默認(rèn)值。

void dilate( InputArray src, OutputArray dst, InputArray kernel,
             Point anchor = Point(-1,-1), int iterations = 1,
             int borderType = BORDER_CONSTANT,
             const Scalar& borderValue = morphologyDefaultBorderValue() );
  • 第三個參數(shù),InputArray類型的kernel,膨脹操作的核。若為NULL時,表示的是使用參考點(diǎn)位于中心3x3的核。我們一般使用函數(shù)getStructuringElement返回指定形狀和尺寸的 結(jié)構(gòu)元(SE)
Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
# 第一個參數(shù)表示內(nèi)核的形狀,我們可以選擇如下三種形狀之一
# 矩形: MORPH_RECT
# 交叉形: MORPH_CROSS
# 橢圓形: MORPH_ELLIPSE
#
# 第二和第三個參數(shù)分別是內(nèi)核的尺寸以及錨點(diǎn)的位置。
# 我們一般在調(diào)用erode以及dilate函數(shù)之前,先定義一個Mat類型的變量來獲得getStructuringElement函數(shù)的返回值。
# 對于錨點(diǎn)的位置,有默認(rèn)值Point(-1,-1),表示錨點(diǎn)位于中心。
# 且需要注意,交叉形的element形狀唯一依賴于錨點(diǎn)的位置。
# 而在其他情況下,錨點(diǎn)只是影響了形態(tài)學(xué)運(yùn)算結(jié)果的偏移。
  • 第四個參數(shù),Point類型的anchor,錨的位置,默認(rèn)值(-1,-1),表示錨位于中心。
  • 第五個參數(shù),int類型的iterations,迭代使用erode()函數(shù)的次數(shù),默認(rèn)值為1。
  • 第六個參數(shù),int類型的borderType,用于推斷圖像外部像素的某種邊界模式。注意它有默認(rèn)值BORDER_DEFAULT。
  • 第七個參數(shù),const Scalar&類型的borderValue,當(dāng)邊界為常數(shù)時的邊界值,有默認(rèn)值morphologyDefaultBorderValue(),一般我們不用去管他。需要用到它時,可以看官方文檔中的createMorphologyFilter()函數(shù)得到更詳細(xì)的解釋。

2.2 實(shí)例

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>

using namespace cv;
using namespace std;


int main() {

    Mat src = imread("../pics/pig.jpg");

    namedWindow("src");
    imshow("src", src);

    // 獲得結(jié)構(gòu)元
    Mat se = getStructuringElement(MORPH_RECT, Size(10, 10));

    Mat dst;
    dilate(src, dst, se); // 后4個參數(shù)使用默認(rèn)值

    namedWindow("膨脹");
    imshow("膨脹", dst);

    waitKey(0);
}
Size(10, 10)

將膨脹代碼給為腐蝕

erode(src, dst, se);

三、總結(jié)

雖然膨脹和腐蝕是相反的操作,但是如果用同樣的SE連續(xù)執(zhí)行2個操作,也不一定能恢復(fù)原圖。其實(shí)就是開閉操作。

先腐蝕再膨脹 其實(shí)就是開操作??
開操作一般會平滑物體的輪廓,斷開較窄的狹頸并消除細(xì)的突出物。

erode(src, dst, se);
namedWindow("先腐蝕");
imshow("先腐蝕", dst);

dilate(dst, dst, se);
namedWindow("再膨脹");
imshow("再膨脹", dst);

先膨脹再腐蝕 其實(shí)就是閉操作??
閉操作也會平滑輪廓的一部分,但與開操作相反,通常會彌合較窄的間斷和細(xì)長的溝壑,消除小的孔洞,填補(bǔ)輪廓線中的斷裂。

dilate(src, dst, se);
namedWindow("先膨脹");
imshow("先膨脹", dst);

erode(dst, dst, se);
namedWindow("再腐蝕");
imshow("再腐蝕", dst);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容