1.3形态學圖像處理:膨脹與腐蝕
1.3.1理論與概念講解
<1>形态學概述
形态學(morphology)一詞通常表示生物學的一個分支,該分支主要研究動植物的形态和結構。而我們圖像進行中指的形态學,往往表示的是數學形态學。下面一起來了解數學形态學的概念。
數學形态學(Mathematical morphology) 是一門建立在格論和拓撲學基礎之上的圖像分析學科,是數學形态學圖像處理的基本理論。其基本的運算包括:二值腐蝕和膨脹、二值開閉運算、骨架抽取、極限腐蝕、擊中擊不中變換、形态學梯度、Top-hat變換、顆粒分析、流域變換、灰值腐蝕和膨脹、灰值開閉運算、灰值形态學梯度等。
簡單來講,形态學操作就是基于形狀的一系列圖像處理操作。opencv為進行圖像的形态學變換提供了快捷、友善的函數。最基本的形态學操作有二種,他們是:膨脹與腐蝕(Dilation與Erosion)。
膨脹與腐蝕能實作多種多樣的功能,主要如下:
消除噪聲;
分割(isolate)出獨立的圖像元素,在圖像中連接配接(join)相鄰的元素;
尋找圖像中的明顯的極大值區域或極小值區域;
求出圖像的梯度。
我們在這裡給出下文會用到的,用于對比膨脹與腐蝕運算的“i”字樣毛筆字原圖:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yN1IDMxYTOkVTNyQGOhV2YxYzX2AzM1QTMxMzLcJTMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
圖1
【注】圖檔來自OpenCV_Tutorials網站。
在進行腐蝕和膨脹的講解之前,首先需要注意,腐蝕和膨脹是對白色部分(高亮部分)而言的,不是黑色部分。膨脹就是圖像中的高亮部分進行膨脹,“領域擴張”,效果圖擁有比原圖更大的高亮區域。腐蝕就是原圖中的高亮部分被腐蝕,“領域被蠶食”,效果圖擁有比原圖更小的高亮區域。
<2>膨脹
其實,膨脹就是求局部最大值的操作。按數學方面來說,膨脹或者腐蝕操作就是将圖像(或圖像的一部分區域,我們稱之為A)與核(我們稱之為B)進行卷積。
核可以是任何的形狀和大小,它擁有一個單獨定義出來的參考點,我們稱其為錨點(anchorpoint)。多數情況下,核是一個小的中間帶有參考點和實心正方形或者圓盤,其實,我們可以把核視為模闆或者掩碼。
而膨脹就是求局部最大值的操作,核B與圖形卷積,即計算核B覆寫的區域的像素點的最大值,并把這個最大值指派給參考點指定的像素。這樣就會使圖像中的高亮區域逐漸增長。如下圖所示,這就是膨脹操作的初衷。
圖2
膨脹的數學表達式:
膨脹效果圖(毛筆字):
圖3 左圖像:原始圖像反向,右圖像: 原始圖像反向的膨脹圖像
<3>腐蝕
再來看一下腐蝕,大家應該知道,膨脹和腐蝕是一對好基友,是相反的一對操作,是以腐蝕就是求局部最小值的操作。
我們一般都會把腐蝕和膨脹對應起來了解和學習。下文就可以看到,兩者的函數原型也是基本上一樣的。
原理圖:
圖4
腐蝕的數學表達式:
腐蝕效果圖(毛筆字):
圖5左圖像:原始圖像反向,右圖像: 原始圖像反向造成的侵蝕
1.3.2 OpenCV源碼分析
/*【erode ( )函數源代碼】*************************************************************
* @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)
* @源碼路徑:…\opencv\sources\modules\imgproc\src\morph.cpp
* @起始行數:1743行
********************************************************************************/
void cv::erode( InputArray src, OutputArray dst, InputArray kernel,
Point anchor, int iterations,
int borderType, const Scalar& borderValue )
{
//調用morphOp函數,并設定辨別符為MORPH_ERODE
morphOp( MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType, borderValue );
}
/*【dilate( )函數源代碼】*************************************************************
* @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)
* @源碼路徑:…\opencv\sources\modules\imgproc\src\morph.cpp
* @起始行數:1751行
********************************************************************************/
void cv::dilate( InputArray src, OutputArray dst, InputArray kernel,
Point anchor, int iterations,
int borderType, const Scalar& borderValue )
{
//調用morphOp函數,并設定辨別符為MORPH_DILATE
morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue );
}
可以發現erode和dilate這兩個函數内部就是調用了一下morphOp,隻是他們調用morphOp時,第一個參數辨別符不同,一個為MORPH_ERODE(腐蝕),一個為MORPH_DILATE(膨脹)。
morphOp函數的源碼在… opencv\sources\modules\imgproc\src\morph.cpp中的第1677行,有興趣的朋友們可以研究研究,這裡就不費時費力花篇幅展開分析了。
【注】筆者分析的是3.0.0源碼,對于3.0版本以上的源碼大同小異,請讀者自行對比學習。
1.3.3 API函數講解
<1>形态學膨脹——dilate函數
erode函數,使用像素鄰域内的局部極大運算符來膨脹一張圖檔,從src輸入,由dst輸出。支援就地(in-place)操作。
C++: void dilate( InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue() );
【參數】
第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。圖像通道的數量可以是任意的,但圖像深度應為CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
第二個參數,OutputArray類型的dst,即目标圖像,需要和源圖檔有一樣的尺寸和類型。
第三個參數,InputArray類型的kernel,膨脹操作的核。若為NULL時,表示的是使用參考點位于中心3x3的核。
我們一般使用函數 getStructuringElement配合這個參數的使用。getStructuringElement函數會傳回指定形狀和尺寸的結構元素(核心矩陣)。
其中,getStructuringElement函數的第一個參數表示核心的形狀,我們可以選擇如下三種形狀之一:
矩形: MORPH_RECT
交叉形: MORPH_CROSS
橢圓形: MORPH_ELLIPSE
而getStructuringElement函數的第二和第三個參數分别是核心的尺寸以及錨點的位置。我們一般在調用erode以及dilate函數之前,先定義一個Mat類型的變量來獲得getStructuringElement函數的傳回值。對于錨點的位置,有預設值Point(-1,-1),表示錨點位于中心。且需要注意,十字形的element形狀唯一依賴于錨點的位置。而在其他情況下,錨點隻是影響了形态學運算結果的偏移。
getStructuringElement函數相關的調用示例代碼如下:
int g_nStructElementSize = 3; //結構元素(核心矩陣)的尺寸
//擷取自定義核
Mat element = getStructuringElement(MORPH_RECT,
Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
Point( g_nStructElementSize, g_nStructElementSize ));
調用這樣之後,我們便可以在接下來調用erode或dilate函數時,第三個參數填儲存了getStructuringElement傳回值的Mat類型變量。對應于我們上面的示例,就是填element變量。
第四個參數,Point類型的anchor,錨的位置,其有預設值(-1,-1),表示錨位于中心。
第五個參數,int類型的iterations,疊代使用erode()函數的次數,預設值為1。
第六個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。注意它有預設值BORDER_DEFAULT。
第七個參數,const Scalar&類型的borderValue,當邊界為常數時的邊界值,有預設值morphologyDefaultBorderValue(),一般我們不用去管他。需要用到它時,可以看官方文檔中的createMorphologyFilter()函數得到更詳細的解釋。
使用erode函數,一般我們隻需要填前面的三個參數,後面的四個參數都有預設值。而且往往結合getStructuringElement一起使用。
調用範例:
//載入原圖
Mat image = imread("1.jpg");
//擷取自定義核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat out;
//進行膨脹操作
dilate(image, out, element);
用上面核心代碼架起來的完整程式代碼。
參見附件【demo1】
運作截圖。
圖6膨脹
<2>形态學腐蝕——erode函數
erode函數,使用像素鄰域内的局部極小運算符來腐蝕一張圖檔,從src輸入,由dst輸出。支援就地(in-place)操作。
C++: void erode(InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue()
);
【參數】
第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。圖像通道的數量可以是任意的,但圖像深度應為CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
第二個參數,OutputArray類型的dst,即目标圖像,需要和源圖檔有一樣的尺寸和類型。
第三個參數,InputArray類型的kernel,腐蝕操作的核心。若為NULL時,表示的是使用參考點位于中心3x3的核。我們一般使用函數 getStructuringElement配合這個參數的使用。getStructuringElement函數會傳回指定形狀和尺寸的結構元素(核心矩陣)。(具體看上文中淺出部分dilate函數的第三個參數講解部分)
第四個參數,Point類型的anchor,錨的位置,其有預設值(-1,-1),表示錨位于機關(element)的中心,我們一般不用管它。
第五個參數,int類型的iterations,疊代使用erode()函數的次數,預設值為1。
第六個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。注意它有預設值BORDER_DEFAULT。
第七個參數,const Scalar&類型的borderValue,當邊界為常數時的邊界值,有預設值morphologyDefaultBorderValue(),一般我們不用去管他。需要用到它時,可以看官方文檔中的createMorphologyFilter()函數得到更詳細的解釋。
同樣的,使用erode函數,一般我們隻需要填前面的三個參數,後面的四個參數都有預設值。而且往往結合getStructuringElement一起使用。
調用範例:
//載入原圖
Mat image = imread("1.jpg");
//擷取自定義核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat out;
//進行腐蝕操作
erode(image,out, element);
用上面核心代碼架起來的完整程式代碼。
參見附件【demo2】
運作結果:
圖7腐蝕操作
1.3.4膨脹與腐蝕綜合執行個體
這個示例程式中的效果圖視窗有兩個滾動條,顧名思義,第一個滾動條“腐蝕/膨脹”用于在腐蝕/膨脹之間進行切換;第二個滾動條”核心尺寸”用于調節形态學操作時的核心尺寸,以得到效果不同的圖像。
參見附件【demo3】
放出一些效果圖吧。
圖8
圖9
參考連結:
英文:https://docs.opencv.org/master/db/df6/tutorial_erosion_dilatation.html
中文:
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html#morphology-1
1.4形态學圖像處理:開運算、閉運算、形态學梯度、頂帽、黑帽合輯
1.4.1理論與概念講解
首先呢,要知道形态學的進階形态,往往都是建立在腐蝕和膨脹這兩個基本操作之上的。而關于腐蝕和膨脹,概念和細節以及相關代碼可以看前文。對膨脹和腐蝕心中有數了,接下來的進階形态學操作,應該就不難了解。另外,為了下面對比和示範以及了解的友善,筆者自己制作了一張毛筆字圖,這裡先上原圖:
圖10
<1>開運算(Opening Operation)
開運算(Opening Operation),其實就是先腐蝕後膨脹的過程。其數學表達式如下:
dst=open(src,element)=dilate(erode(src,element))
開運算可以用來消除小物體、在纖細點處分離物體、平滑較大物體的邊界的同時并不明顯改變其面積。
圖11左圖像:原始圖像反向,右圖像: 原始圖像反向的開運算
<2>閉運算(Closing Operation)
先膨脹後腐蝕的過程稱為閉運算(Closing Operation),其數學表達式如下:
dst=close(src,element)=erode(dilate(src,element))
閉運算能夠排除小型黑洞(黑色區域)。效果圖如下所示:
圖12左圖像:原始圖像反向,右圖像: 原始圖像反向的閉運算
<3>形态學梯度(MorphologicalGradient)
形态學梯度(Morphological Gradient)為膨脹圖與腐蝕圖之差,數學表達式如下:
dst=morphgrad(src,element)=dilate(src,element)−erode(src,element)
對二值圖像進行這一操作可以将團塊(blob)的邊緣突出出來。我們可以用形态學梯度來保留物體的邊緣輪廓,如下所示:
圖13
<4>頂帽(Top Hat)
頂帽運算(Top Hat)又常常被譯為”禮帽“運算。為原圖像與上文剛剛介紹的“開運算“的結果圖之差,數學表達式如下:
dst=tophat(src,element)=src−open(src,element)
因為開運算帶來的結果是放大了裂縫或者局部低亮度的區域,是以,從原圖中減去開運算後的圖,得到的效果圖突出了比原圖輪廓周圍的區域更明亮的區域,且這一操作和選擇的核的大小相關。
頂帽運算往往用來分離比鄰近點亮一些的斑塊。當一幅圖像具有大幅的背景的時候,而微小物品比較有規律的情況下,可以使用頂帽運算進行背景提取。
圖14
<5> 黑帽(Black Hat)
黑帽(Black Hat)運算為”閉運算“的結果圖與原圖像之差。數學表達式為:
dst=blackhat(src,element)=close(src,element)−src
黑帽運算後的效果圖突出了比原圖輪廓周圍的區域更暗的區域,且這一操作和選擇的核的大小相關。是以,黑帽運算用來分離比鄰近點暗一些的斑塊。非常完美的輪廓效果圖:
圖15
1.4.2 OpenCV源碼分析
本文的主角是OpenCV中的morphologyEx函數,它利用基本的膨脹和腐蝕技術,來執行更加進階的形态學變換,如開閉運算,形态學梯度,“頂帽”、“黑帽”等等。這一節我們來一起看一下morphologyEx函數的源代碼。
/*【morphologyEx ( )函數源代碼】******************************************************
* @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)
* @源碼路徑:…\opencv\sources\modules\imgproc\src\morph.cpp
* @起始行數:1822行
********************************************************************************/
void cv::morphologyEx( InputArray _src, OutputArray _dst, int op,
InputArray _kernel, Point anchor, int iterations,
int borderType, const Scalar& borderValue )
{
Mat kernel = _kernel.getMat();
if (kernel.empty())
{
kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(1,1));
}
#ifdef HAVE_OPENCL
Size ksize = kernel.size();
anchor = normalizeAnchor(anchor, ksize);
CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 &&
borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(),
ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue))
#endif
Mat src = _src.getMat(), temp; //拷貝Mat資料到臨時變量
_dst.create(src.size(), src.type());
Mat dst = _dst.getMat();
//一個大switch,根據不同的辨別符取不同的操作
switch( op )
{
case MORPH_ERODE:
erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
break;
case MORPH_DILATE:
dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
break;
case MORPH_OPEN:
erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
break;
case CV_MOP_CLOSE:
dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
break;
case CV_MOP_GRADIENT:
erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
dst -= temp;
break;
case CV_MOP_TOPHAT:
if( src.data != dst.data )
temp = dst;
erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue );
dst = src - temp;
break;
case CV_MOP_BLACKHAT:
if( src.data != dst.data )
temp = dst;
dilate( src, temp, kernel, anchor, iterations, borderType, borderValue );
erode( temp, temp, kernel, anchor, iterations, borderType, borderValue );
dst = temp - src;
break;
default:
CV_Error( CV_StsBadArg, "unknown morphological operation" );
}
}
看上面的源碼可以發現,其實morphologyEx函數其實就是内部一個大switch而已。根據不同的辨別符取不同的操作。比如開運算MORPH_OPEN,按我們上文中講解的數學表達式,就是先腐蝕後膨脹,即依次調用erode和dilate函數,為非常簡明幹淨的代碼。
【注】筆者分析的是2.4.9源碼,對于3.0版本以上的源碼大同小異,請讀者自行對比學習。
1.4.3 API函數講解
<1> morphologyEx函數詳解
上面我們已經講到,morphologyEx函數利用基本的膨脹和腐蝕技術,來執行更加進階形态學變換,如開閉運算,形态學梯度,“頂帽”、“黑帽”等等。這一節我們來了解它的參數意義和使用方法。
C++: void morphologyEx( InputArray src,
OutputArray dst,
int op,
InputArray kernel,
Pointanchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
constScalar& borderValue=morphologyDefaultBorderValue() );
【參數】
第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。圖像位深應該為以下五種之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
第二個參數,OutputArray類型的dst,即目标圖像,函數的輸出參數,需要和源圖檔有一樣的尺寸和類型。
第三個參數,int類型的op,表示形态學運算的類型,可以是如下之一的辨別符:
MORPH_OPEN – 開運算(Opening operation)
MORPH_CLOSE – 閉運算(Closing operation)
MORPH_GRADIENT -形态學梯度(Morphological gradient)
MORPH_TOPHAT - “頂帽”(“Top hat”)
MORPH_BLACKHAT - “黑帽”(“Black hat“)
另有CV版本的辨別符也可選擇,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT,這應該是OpenCV1.0系列版本遺留下來的辨別符,和上面的“MORPH_OPEN”一樣的效果。
第四個參數,InputArray類型的kernel,形态學運算的核心。若為NULL時,表示的是使用參考點位于中心3x3的核。我們一般使用函數 getStructuringElement配合這個參數的使用。getStructuringElement函數會傳回指定形狀和尺寸的結構元素(核心矩陣)。關于getStructuringElement我們上篇文章中講過了,這裡為了大家參閱友善,再寫一遍:
其中,getStructuringElement函數的第一個參數表示核心的形狀,我們可以選擇如下三種形狀之一:
矩形: MORPH_RECT
交叉形: MORPH_CROSS
橢圓形: MORPH_ELLIPSE
而getStructuringElement函數的第二和第三個參數分别是核心的尺寸以及錨點的位置。
我們一般在調用erode以及dilate函數之前,先定義一個Mat類型的變量來獲得getStructuringElement函數的傳回值。對于錨點的位置,有預設值Point(-1,-1),表示錨點位于中心。且需要注意,十字形的element形狀唯一依賴于錨點的位置。而在其他情況下,錨點隻是影響了形态學運算結果的偏移。
getStructuringElement函數相關的調用示例代碼如下:
int g_nStructElementSize = 3; //結構元素(核心矩陣)的尺寸
//擷取自定義核
Mat element =getStructuringElement(MORPH_RECT,
Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
Point(g_nStructElementSize, g_nStructElementSize ));
調用這樣之後,我們便可以在接下來調用erode、dilate或morphologyEx函數時,kernel參數填儲存getStructuringElement傳回值的Mat類型變量。對應于我們上面的示例,就是填element變量。
第五個參數,Point類型的anchor,錨的位置,其有預設值(-1,-1),表示錨位于中心。
第六個參數,int類型的iterations,疊代使用函數的次數,預設值為1。
第七個參數,int類型的borderType,用于推斷圖像外部像素的某種邊界模式。注意它有預設值BORDER_ CONSTANT。
第八個參數,const Scalar&類型的borderValue,當邊界為常數時的邊界值,有預設值morphologyDefaultBorderValue(),一般我們不用去管他。需要用到它時,可以看官方文檔中的createMorphologyFilter()函數得到更詳細的解釋。
其中的這些操作都可以進行就地(in-place)操作。且對于多通道圖像,每一個通道都是單獨進行操作。 OK,講解完畢,下面就是使用的範例。
為了友善大家需要的時候随時取用。下面我們依次列舉出開運算,閉運算,形态學梯度,頂帽,黑帽,腐蝕,膨脹的效果實作簡化版完整代碼。其實說白了,這些代碼基本上内容一緻,其實就是改一下morphologyEx裡面的第三個辨別符參數而已。核都是選的MORPH_RECT,矩形元素結構。另外,通過看源碼我們發現,最基本的腐蝕和膨脹操作也可以用morphologyEx函數來實作,他們由morphologyEx函數源碼中switch的前兩個case來實作(雖然在case體内就是簡單地各自調用了一下erode和dilation函數,但還是有寫出來的必要)。是以在這裡,我們也用morphologyEx再重新來實作一遍他們。
按着順序來列出吧,就直接列詳細注釋好的代碼和運作結果了。
<2>開運算示例程式
OpenCV中調用morphologyEx函數進行開運算操作的示例程式如下:
參考附件【demo4】,運作效果圖。
圖16
<3>閉運算示例程式
OpenCV中調用morphologyEx函數進行閉運算操作的示例程式如下。
參見附件【demo5】,運作效果圖。
圖17
<4>形态學梯度示例程式
OpenCV中調用morphologyEx函數進行形态學梯度操作的示例程式如下。
參見附件【demo6】,運作效果圖。
圖18
<5>頂帽運算(Top Hat)示例程式
OpenCV中調用morphologyEx函數進行頂帽運算操作的示例程式如下。
參見附件【demo7】,運作效果圖。
圖19
<6>黑帽運算(BlackHat)示例程式
OpenCV中調用morphologyEx函數進行黑帽運算操作的示例程式如下:
參見附件【demo8】,運作效果圖。
圖20
<7>腐蝕(morphologyEx調用版)示例程式
OpenCV中調用morphologyEx函數進行腐蝕操作的示例程式如下。
參見附件【demo9】,運作效果圖。
圖21
<8>膨脹(morphologyEx調用版)示例程式
OpenCV中調用morphologyEx函數進行膨脹操作的示例程式如下。
參見附件【demo10】,運作效果圖。
圖22
1.4.4形态學濾波綜合執行個體
這個示例程式中,一共有四個顯示圖像的視窗。原始圖一個,開/閉運算為一個,腐蝕/膨脹為一個,頂帽/黑帽運算為一個。分别使用滾動條,來控制得到的形态學效果。且疊代值為10的時候,為中間。另外,還可以通過鍵盤按鍵1,2,3以及空格,來調節成不同的元素結構(矩形、橢圓、十字形)。
參看附件【demo11】
圖23腐蝕/膨脹效果圖
圖24開/閉運算效果圖
圖25頂帽/黑帽運算效果圖
有沒有感覺很酷呢,繼續跟着部落客繼續學習吧,你會發現不一樣的驚奇喲!