1. 程式人生 > >【OpenCV入門教程之十一】 形態學影象處理(二):開運算、閉運算、形態學梯度、頂帽、黑帽合輯

【OpenCV入門教程之十一】 形態學影象處理(二):開運算、閉運算、形態學梯度、頂帽、黑帽合輯

上篇文章中,我們重點了解了腐蝕和膨脹這兩種最基本的形態學操作,而運用這兩個基本操作,我們可以實現更高階的形態學變換。

所以,本文的主角是OpenCV中的morphologyEx函式,它利用基本的膨脹和腐蝕技術,來執行更加高階的形態學變換,如開閉運算、形態學梯度、“頂帽”、“黑帽”等等。

 先上幾張示例程式的截圖吧:


有沒有很熟悉這張圖?沒錯,這就是最近熱映的電影Captain America~

下面這張圖的效果就有些凶殘了:

OK,截圖先看到這裡。在正文之前先來嘮嘮和主題相關的事情。

第一件事,OpenCV最新版本更新到了2.4.9。

在寫這篇博文的兩天之前(4月25日上午),OpenCV官網頁面顯示最新版本還是2.4.8,但是通過淺墨細心地發現,文件頁面的標題已經悄悄而低調地改成了2.4.9.所以我們當時應該可以去斷定,OpenCV2.4.9應該馬上就要和我們見面了。

 

果然,OpenCV2.4.9就在兩天後(4月27日),正式在OpenCV官方網站上上線了。現在轉到OpenCV的官方主頁,赫然發現最新版本已然顯示為2.4.9:

大家可以自己前去看看以及下載最新版本的OpenCV。如果不出意外的話呢,下次文章我們就將緊跟時代,用上最新版本的OpenCV2.4.9進行講解和程式的書寫,所以,大家在看這篇文章之後呢,可以去下載當前最新的2.4.9版本並裝上配置好。

第二件事,是淺墨想跟大家做一個關於OpenCV系列文章的書寫內容和風格的思想彙報。

是這樣的,淺墨發現最近幾期寫出來的文章有些偏離自己開始開這個專欄的最初的願望——原理和概念部分佔的比重有些大,有些弱化OpenCV實際的使用。

寫這些博文的初心是教大家如何使用OpenCV來寫程式碼,原理部分我想很多朋友應該多少都懂,就算某些同學對某些概念有些模糊,大家也完全可以帶著關鍵詞句去google或者百度。

淺墨的想法是,以後的專欄文章原理部分儘量從簡,“深入”的原始碼剖析部分也是從簡,重點突出“淺出”部分,讓大家快速上手OpenCV函式的使用,這樣淺墨的工作量也會小很多,更新也會更勤。

PS:淺墨其實每次在寫影象處理原理部分的時候都特糾結,因為淺墨其實感興趣的和大家一樣,也是如何寫程式碼,而不是那些多多少少讓人提不起興趣來的影象處理公式和概念。這往往就照成了博文更新的拖延症。

所以呢,在淺墨以後寫的OpenCV文章中,原理和深入部分我們就點到為止,文章的拳頭內容是“淺出”部分,重點教大家如何快速上手OpenCV API。我想這也是大家一直期待和想要看到的淺墨出品的文章的樣子吧。:)

OK,大概就是這些。我們開始今天的正題。

一、理論與概念講解——從現象到本質

首先呢,要知道形態學的高階形態,往往都是建立在腐蝕和膨脹這兩個基本操作之上的。而關於腐蝕和膨脹,概念和細節以及相關程式碼可以看淺墨之前寫的這篇文章:

對膨脹和腐蝕心中有數了,接下來的高階形態學操作,應該就不難理解。

另外,為了下面對比和演示以及理解的方便,淺墨自己製作了一張毛筆字圖,這裡先上原圖:

 OK,我們開始講解。

1.1 開運算(Opening Operation)

開運算(Opening Operation),其實就是先腐蝕後膨脹的過程。其數學表示式如下:

開運算可以用來消除小物體、在纖細點處分離物體、平滑較大物體的邊界的同時並不明顯改變其面積。效果圖是這樣的:

實際效果圖:

1.2 閉運算(Closing Operation)

先膨脹後腐蝕的過程稱為閉運算(Closing Operation),其數學表示式如下:

 

閉運算能夠排除小型黑洞(黑色區域)。效果圖如下所示:

 

實際效果圖:

1.3 形態學梯度(MorphologicalGradient)

形態學梯度(Morphological Gradient)為膨脹圖與腐蝕圖之差,數學表示式如下:

對二值影象進行這一操作可以將團塊(blob)的邊緣突出出來。我們可以用形態學梯度來保留物體的邊緣輪廓,如下所示:

 

實際素材效果圖:

1.4頂帽(Top Hat)

頂帽運算(Top Hat)又常常被譯為”禮帽“運算。為原影象與上文剛剛介紹的“開運算“的結果圖之差,數學表示式如下:

因為開運算帶來的結果是放大了裂縫或者區域性低亮度的區域,因此,從原圖中減去開運算後的圖,得到的效果圖突出了比原圖輪廓周圍的區域更明亮的區域,且這一操作和選擇的核的大小相關。

頂帽運算往往用來分離比鄰近點亮一些的斑塊。當一幅影象具有大幅的背景的時候,而微小物品比較有規律的情況下,可以使用頂帽運算進行背景提取。

如下所示:

素材效果圖:

1.5 黑帽(Black Hat)

黑帽(Black Hat)運算為”閉運算“的結果圖與原影象之差。數學表示式為:

黑帽運算後的效果圖突出了比原圖輪廓周圍的區域更暗的區域,且這一操作和選擇的核的大小相關。

所以,黑帽運算用來分離比鄰近點暗一些的斑塊。非常完美的輪廓效果圖:

 

實際素材效果圖:

二、深入——OpenCV原始碼分析溯源

本文的主角是OpenCV中的morphologyEx函式,它利用基本的膨脹和腐蝕技術,來執行更加高階的形態學變換,如開閉運算,形態學梯度,“頂帽”、“黑帽”等等。這一節我們來一起看一下morphologyEx函式的原始碼。

//-----------------------------------【erode()函式中文註釋版原始碼】----------------------------  
//   說明:以下程式碼為來自於計算機開源視覺庫OpenCV的官方原始碼  
//   OpenCV原始碼版本:2.4.8  
//   原始碼路徑:…\opencv\sources\modules\imgproc\src\morph.cpp  
//   原始檔中如下程式碼的起始行數:1369行  
//   中文註釋by淺墨  
//--------------------------------------------------------------------------------------------------------   
void cv::morphologyEx( InputArray _src,OutputArray _dst, int op,
                       InputArray kernel, Pointanchor, int iterations,
                       int borderType, constScalar& borderValue )
{
//拷貝Mat資料到臨時變數
   Mat src = _src.getMat(), temp;
   _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函式,為非常簡明乾淨的程式碼。

三、淺出——API函式快速上手

3.1 morphologyEx函式詳解

上面我們已經講到,morphologyEx函式利用基本的膨脹和腐蝕技術,來執行更加高階形態學變換,如開閉運算,形態學梯度,“頂帽”、“黑帽”等等。這一節我們來了解它的引數意義和使用方法。

C++: void morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArraykernel,
Pointanchor=Point(-1,-1),
intiterations=1,
intborderType=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再重新來實現一遍他們。

按著順序來列出吧,就直接列詳細註釋好的程式碼和執行結果了。

3.2 開運算示例程式

OpenCV中呼叫morphologyEx函式進行開運算操作的示例程式如下:

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//            描述:包含程式所依賴的標頭檔案
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【名稱空間宣告部分】---------------------------------------
//            描述:包含程式所使用的名稱空間
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函式】--------------------------------------------
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //載入原始圖  
       Mat image = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖
       //建立視窗  
       namedWindow("【原始圖】開運算"); 
       namedWindow("【效果圖】開運算"); 
       //顯示原始圖 
       imshow("【原始圖】開運算", image); 
       //定義核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //進行形態學操作
       morphologyEx(image,image, MORPH_OPEN, element);
       //顯示效果圖 
       imshow("【效果圖】開運算", image); 
 
       waitKey(0); 
 
       return 0; 
}

執行效果圖:


3.3 閉運算示例程式

OpenCV中呼叫morphologyEx函式進行閉運算操作的示例程式如下:

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//            描述:包含程式所依賴的標頭檔案
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【名稱空間宣告部分】---------------------------------------
//            描述:包含程式所使用的名稱空間
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函式】--------------------------------------------
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //載入原始圖  
       Mat image = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖
       //建立視窗  
       namedWindow("【原始圖】閉運算"); 
       namedWindow("【效果圖】閉運算"); 
       //顯示原始圖 
       imshow("【原始圖】閉運算", image); 
       //定義核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //進行形態學操作
       morphologyEx(image,image, MORPH_CLOSE, element);
       //顯示效果圖 
       imshow("【效果圖】閉運算", image); 
 
       waitKey(0); 
 
       return 0; 
}

執行效果圖:


3.4 形態學梯度示例程式

OpenCV中呼叫morphologyEx函式進行形態學梯度操作的示例程式如下:

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//            描述:包含程式所依賴的標頭檔案
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【名稱空間宣告部分】---------------------------------------
//            描述:包含程式所使用的名稱空間
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函式】--------------------------------------------
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //載入原始圖  
       Mat image = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖
       //建立視窗  
       namedWindow("【原始圖】形態學梯度"); 
       namedWindow("【效果圖】形態學梯度"); 
       //顯示原始圖 
       imshow("【原始圖】形態學梯度", image); 
       //定義核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //進行形態學操作
       morphologyEx(image,image, MORPH_GRADIENT, element);
       //顯示效果圖 
       imshow("【效果圖】形態學梯度", image); 
 
       waitKey(0); 
 
       return 0; 
}

執行效果圖:


3.5 頂帽運算(Top Hat)示例程式


OpenCV中呼叫morphologyEx函式進行頂帽運算操作的示例程式如下:

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//            描述:包含程式所依賴的標頭檔案
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【名稱空間宣告部分】---------------------------------------
//            描述:包含程式所使用的名稱空間
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函式】--------------------------------------------
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //載入原始圖  
       Mat image = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖
       //建立視窗  
       namedWindow("【原始圖】頂帽運算"); 
       namedWindow("【效果圖】頂帽運算"); 
       //顯示原始圖 
       imshow("【原始圖】頂帽運算", image); 
       //定義核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //進行形態學操作
       morphologyEx(image,image, MORPH_TOPHAT, element);
       //顯示效果圖 
       imshow("【效果圖】頂帽運算", image); 
 
       waitKey(0); 
 
       return 0; 
}

執行效果圖:


3.6 黑帽運算(BlackHat)示例程式

OpenCV中呼叫morphologyEx函式進行黑帽運算操作的示例程式如下:

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//            描述:包含程式所依賴的標頭檔案
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【名稱空間宣告部分】---------------------------------------
//            描述:包含程式所使用的名稱空間
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函式】--------------------------------------------
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //載入原始圖  
       Mat image = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖
       //建立視窗  
       namedWindow("【原始圖】黑帽運算"); 
       namedWindow("【效果圖】黑帽運算"); 
       //顯示原始圖 
       imshow("【原始圖】黑帽運算", image); 
       //定義核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //進行形態學操作
       morphologyEx(image,image, MORPH_BLACKHAT, element);
       //顯示效果圖 
       imshow("【效果圖】黑帽運算", image); 
 
       waitKey(0); 
 
       return 0; 
}

執行效果圖:


3.7 腐蝕(morphologyEx呼叫版)示例程式

OpenCV中呼叫morphologyEx函式進行腐蝕操作的示例程式如下:

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//            描述:包含程式所依賴的標頭檔案
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【名稱空間宣告部分】---------------------------------------
//            描述:包含程式所使用的名稱空間
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函式】--------------------------------------------
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //載入原始圖  
       Mat image = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖
       //建立視窗  
       namedWindow("【原始圖】腐蝕"); 
       namedWindow("【效果圖】腐蝕"); 
       //顯示原始圖 
       imshow("【原始圖】腐蝕", image); 
       //定義核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //進行形態學操作
       morphologyEx(image,image, MORPH_ERODE, element);
       //顯示效果圖 
       imshow("【效果圖】腐蝕", image); 
 
       waitKey(0); 
 
       return 0; 
}

執行效果圖:


3.8 膨脹(morphologyEx呼叫版)示例程式

OpenCV中呼叫morphologyEx函式進行膨脹操作的示例程式如下:

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//            描述:包含程式所依賴的標頭檔案
//----------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【名稱空間宣告部分】---------------------------------------
//            描述:包含程式所使用的名稱空間
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函式】--------------------------------------------
//            描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main( )
{
       //載入原始圖  
       Mat image = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖
       //建立視窗  
       namedWindow("【原始圖】膨脹"); 
       namedWindow("【效果圖】膨脹"); 
       //顯示原始圖 
       imshow("【原始圖】膨脹", image); 
       //定義核
       Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
       //進行形態學操作
       morphologyEx(image,image, MORPH_DILATE, element);
       //顯示效果圖 
       imshow("【效果圖】膨脹", image); 
 
       waitKey(0); 
 
       return 0; 
}

執行效果圖:


四、綜合示例——在實戰中熟稔

依然是每篇文章都會配給大家的一個詳細註釋的博文配套示例程式,把這篇文章中介紹的知識點以程式碼為載體,展現給大家。

這個示例程式中,一共有四個顯示影象的視窗。

原始圖一個,開/閉運算為一個,腐蝕/膨脹為一個,頂帽/黑帽運算為一個。

分別使用滾動條,來控制得到的形態學效果。且迭代值為10的時候,為中間。

另外,還可以通過鍵盤按鍵1,2,3以及空格,來調節成不同的元素結構(矩形、橢圓、十字形)。說明頁面如下:


廢話不多說,上程式碼吧:

//-----------------------------------【程式說明】----------------------------------------------
//		程式名稱::《【OpenCV入門教程之十一】形態學影象處理(一):膨脹與腐蝕  》 博文配套原始碼 
//		開發所用IDE版本:Visual Studio 2010
//   	開發所用OpenCV版本:	2.4.8
//		2014年4月25日 Create by 淺墨
//----------------------------------------------------------------------------------------------

//-----------------------------------【標頭檔案包含部分】---------------------------------------
//		描述:包含程式所依賴的標頭檔案
//---------------------------------------------------------------------------------------------- 
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

//-----------------------------------【名稱空間宣告部分】--------------------------------------
//		描述:包含程式所使用的名稱空間
//----------------------------------------------------------------------------------------------- 
using namespace std;
using namespace cv;


//-----------------------------------【全域性變數宣告部分】--------------------------------------
//		描述:全域性變數宣告
//-----------------------------------------------------------------------------------------------
Mat g_srcImage, g_dstImage;//原始圖和效果圖
int g_nElementShape = MORPH_RECT;//元素結構的形狀

//變數接收的TrackBar位置引數
int g_nMaxIterationNum = 10;
int g_nOpenCloseNum = 0;
int g_nErodeDilateNum = 0;
int g_nTopBlackHatNum = 0;



//-----------------------------------【全域性函式宣告部分】--------------------------------------
//		描述:全域性函式宣告
//-----------------------------------------------------------------------------------------------
static void on_OpenClose(int, void*);//回撥函式
static void on_ErodeDilate(int, void*);//回撥函式
static void on_TopBlackHat(int, void*);//回撥函式
static void ShowHelpText();//幫助文字顯示


//-----------------------------------【main( )函式】--------------------------------------------
//		描述:控制檯應用程式的入口函式,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main( )
{
	//改變console字型顏色
	system("color 2F");  

	ShowHelpText();

	//載入原圖
	g_srcImage = imread("1.jpg");//工程目錄下需要有一張名為1.jpg的素材圖
	if( !g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~! \n"); return false; }

	//顯示原始圖
	namedWindow("【原始圖】");
	imshow("【原始圖】", g_srcImage);

	//建立三個視窗
	namedWindow("【開運算/閉運算】",1);
	namedWindow("【腐蝕/膨脹】",1);
	namedWindow("【頂帽/黑帽】",1);

	//引數賦值
	g_nOpenCloseNum=9;
	g_nErodeDilateNum=9;
	g_nTopBlackHatNum=2;

	//分別為三個視窗建立滾動條
	createTrackbar("迭代值", "【開運算/閉運算】",&g_nOpenCloseNum,g_nMaxIterationNum*2+1,on_OpenClose);
	createTrackbar("迭代值", "【腐蝕/膨脹】",&g_nErodeDilateNum,g_nMaxIterationNum*2+1,on_ErodeDilate);
	createTrackbar("迭代值", "【頂帽/黑帽】",&g_nTopBlackHatNum,g_nMaxIterationNum*2+1,on_TopBlackHat);

	//輪詢獲取按鍵資訊
	while(1)
	{
		int c;

		//執行回撥函式
		on_OpenClose(g_nOpenCloseNum, 0);
		on_ErodeDilate(g_nErodeDilateNum, 0);
		on_TopBlackHat(g_nTopBlackHatNum,0);

		//獲取按鍵
		c = waitKey(0);

		//按下鍵盤按鍵Q或者ESC,程式退出
		if( (char)c == 'q'||(char)c == 27 )
			break;
		//按下鍵盤按鍵1,使用橢圓(Elliptic)結構元素結構元素MORPH_ELLIPSE
		if( (char)c == 49 )//鍵盤按鍵1的ASII碼為49
			g_nElementShape = MORPH_ELLIPSE;
		//按下鍵盤按鍵2,使用矩形(Rectangle)結構元素MORPH_RECT
		else if( (char)c == 50 )//鍵盤按鍵2的ASII碼為50
			g_nElementShape = MORPH_RECT;
		//按下鍵盤按鍵3,使用十字形(Cross-shaped)結構元素MORPH_CROSS
		else if( (char)c == 51 )//鍵盤按鍵3的ASII碼為51
			g_nElementShape = MORPH_CROSS;
		//按下鍵盤按鍵space,在矩形、橢圓、十字形結構元素中迴圈
		else if( (char)c == ' ' )
			g_nElementShape = (g_nElementShape + 1) % 3;
	}

	return 0;
}


//-----------------------------------【on_OpenClose( )函式】----------------------------------
//		描述:【開運算/閉運算】視窗的回撥函式
//-----------------------------------------------------------------------------------------------
static void on_OpenClose(int, void*)
{
	//偏移量的定義
	int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量絕對值
	//自定義核
	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
	//進行操作
	if( offset < 0 )
		morphologyEx(g_srcImage, g_dstImage, CV_MOP_OPEN, element);
	else
		morphologyEx(g_srcImage, g_dstImage, CV_MOP_CLOSE, element);
	//顯示影象
	imshow("【開運算/閉運算】",g_dstImage);
}


//-----------------------------------【on_ErodeDilate( )函式】----------------------------------
//		描述:【腐蝕/膨脹】視窗的回撥函式
//-----------------------------------------------------------------------------------------------
static void on_ErodeDilate(int, void*)
{
	//偏移量的定義
	int offset = g_nErodeDilateNum - g_nMaxIterationNum;	//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量絕對值
	//自定義核
	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
	//進行操作
	if( offset < 0 )
		erode(g_srcImage, g_dstImage, element);
	else
		dilate(g_srcImage, g_dstImage, element);
	//顯示影象
	imshow("【腐蝕/膨脹】",g_dstImage);
}


//-----------------------------------【on_TopBlackHat( )函式】--------------------------------
//		描述:【頂帽運算/黑帽運算】視窗的回撥函式
//----------------------------------------------------------------------------------------------
static void on_TopBlackHat(int, void*)
{
	//偏移量的定義
	int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量絕對值
	//自定義核
	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
	//進行操作
	if( offset < 0 )
		morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT , element);
	else
		morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);
	//顯示影象
	imshow("【頂帽/黑帽】",g_dstImage);
}

//-----------------------------------【ShowHelpText( )函式】----------------------------------
//		描述:輸出一些幫助資訊
//----------------------------------------------------------------------------------------------
static void ShowHelpText()
{
//輸出一些幫助資訊
	printf("\n\n\n\t請調整滾動條觀察影象效果~\n\n");
	printf( "\n\n\t按鍵操作說明: \n\n"
		"\t\t鍵盤按鍵【ESC】或者【Q】- 退出程式\n"
		"\t\t鍵盤按鍵【1】- 使用橢圓(Elliptic)結構元素\n"
		"\t\t鍵盤按鍵【2】- 使用矩形(Rectangle )結構元素\n"
		"\t\t鍵盤按鍵【3】- 使用十字型(Cross-shaped)結構元素\n"
		"\t\t鍵盤按鍵【空格SPACE】- 在矩形、橢圓、十字形結構元素中迴圈\n"
		"\n\n\t\t\t\t\t\t\t\t by淺墨"
		);
}

放出一些效果圖:

首先是原圖:


非常帥氣的Captain America有木有!

腐蝕效果圖:


膨脹效果圖:


開運算效果圖:


閉運算效果圖:


頂帽運算效果圖:


黑帽運算效果圖:


好的,就放出這些效果圖吧,具體更多的執行效果大家就自己下載示例程式回去玩~

本篇文章的配套原始碼請點選這裡下載:

OK,今天的內容大概就是這些,我們下篇文章見:)


相關推薦

OpenCV入門教程 形態學影象處理運算運算形態學梯度

上篇文章中,我們重點了解了腐蝕和膨脹這兩種最基本的形態學操作,而運用這兩個基本操作,我們可以實現更高階的形態學變換。所以,本文的主角是OpenCV中的morphologyEx函式,它利用基本的膨脹和腐蝕技術,來執行更加高階的形態學變換,如開閉運算、形態學梯度、“頂帽”、“黑帽

OpenCV入門教程OpenCV重對映 SURF特徵點檢測

                本篇文章中,我們一起探討了OpenCV中重對映和SURF特徵點檢測相關的知識點,主要一起了解OpenCV中重對映相關的函式remap,SURF演算法在OpenCV中的體現與應用。此博文一共有三個配套的麻雀雖小但五臟俱全的示例程式,其經過淺墨詳細註釋過的程式碼都在文中貼出,且文章

OpenCV入門教程 形態學影象處理膨脹與腐蝕

本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。 寫作當前博文時配套使用的OpenCV版本: 2.4.8本篇文章中,我們一起探究了影象處理中,最基本的形態學運算——膨脹與腐蝕。淺墨在文章開頭友情提醒,用人物照片做腐蝕和膨脹的素材圖片得到的效果會比較驚悚,毀三觀的,不建議嘗試

OpenCV入門教程OpenCV邊緣檢測 Canny運算元 Sobel運算元 Laplace運算元 Scharr濾波

                本篇文章中,我們將一起學習OpenCV中邊緣檢測的各種運算元和濾波器——Canny運算元,Sobel運算元,Laplace運算元以及Scharr濾波器。文章中包含了五個淺墨為大家準備的詳細註釋的博文配套原始碼。在介紹四塊知識點的時候分別一個,以及最後的綜合示例中的一個。文章末尾

opencv影象處理濾波器

     濾波器在影象處理中的應用非常廣泛,OpenCV也有個直接使用濾波器掩碼(核)的函式filter2D,將影象與核進行卷積運算得到目標影象。卷積是在每一個影象塊與某個運算元(核)之間進行的運算,而核就是一個固定大小的數值陣列。     &n

OpenCV-Python 影象處理影象的讀取顯示與儲存

說明: 本系列主要是學習OpenCV-Python文件的個人筆記。 很少有理論的敘述,都是函式名、引數描述、作用、應用場景、程式碼、效果圖。簡單明瞭,即學即用。 目標 學會讀取、顯示、儲存單張影象 對應的函式分佈為:cv2.imread() ,

OPENCV影象處理模糊

模糊是基本的影象處理方法。 在介紹這兩種方法之前先來介紹兩種常見的噪聲: 椒鹽噪聲 椒鹽噪聲是由影象感測器,傳輸通道,解碼處理等產生的黑白相間的亮暗點噪聲。椒鹽噪聲分為兩種即胡椒噪聲和鹽噪聲,胡椒噪聲是黑色的,屬於低灰度噪聲,鹽噪聲是白色的,屬於高灰度噪

OpenCV入門教程線性鄰域濾波專場方框濾波均值濾波與高斯濾波

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

OpenCV入門教程 非線性濾波專場 中值濾波 雙邊濾波

                正如我們上一篇文章中講到的,線性濾波可以實現很多種不同的影象變換。然而非線性濾波,如中值濾波器和雙邊濾波器,有時可以達到更好的實現效果。鄰域運算元的其他一些例子還有對二值影象進行操作的形態學運算元,用於計算距離變換和尋找連通量的半全域性運算元。 先上一張截圖:一、理論與概念講解

OpenCV入門教程線性鄰域濾波專場 方框濾波 均值濾波與高斯濾波

本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。 寫作當前博文時配套使用的OpenCV版本: 2.4.8本篇文章中,我們一起仔細探討了OpenCV影象處理技術中比較熱門的影象濾波操作。影象濾波系列文章淺墨準備花兩次更新的時間來講,此為上篇,為大家剖析了“方框濾波“,”均值濾波“和”高斯濾波“三種常見線性鄰域

OpenCV入門教程 影象的載入,顯示和輸出 一站式完全解析

毛星雲,網路ID「淺墨」,90後,熱愛遊戲開發、遊戲引擎、計算機圖形、實時渲染等技術,就職於騰訊互娛。 微軟最有價值專家 著作《Windows遊戲程式設計之從零開始》、《OpenCV3程式設計入門》 碩士就讀於南京航空航天大學航天學院(2013級碩士研究生),已於2016年三月畢業。本科

OpenCV入門教程 一覽眾山小OpenCV 2.4.8 or OpenCV 2.4.9元件結構全解析

毛星雲,網路ID「淺墨」,90後,熱愛遊戲開發、遊戲引擎、計算機圖形、實時渲染等技術,就職於騰訊互娛。 微軟最有價值專家 著作《Windows遊戲程式設計之從零開始》、《OpenCV3程式設計入門》 碩士就讀於南京航空航天大學航天學院(2013級碩士研究生),已於2016年三月畢業。本科

OpenCV入門教程 ROI區域影象疊加&初級影象混合 全剖析

本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。 寫作當前博文時配套使用的OpenCV版本: 2.4.8在這篇文章裡,我們一起學習了在OpenCV中如何定義感興趣區域ROI,如何使用addWeighted函式進行影象混合操作,以及將ROI和addWeighted函式結合起來

OpenCV入門教程 玩轉OpenCV原始碼生成OpenCV工程解決方案與OpenCV原始碼編譯

毛星雲,網路ID「淺墨」,90後,熱愛遊戲開發、遊戲引擎、計算機圖形、實時渲染等技術,就職於騰訊互娛。 微軟最有價值專家 著作《Windows遊戲程式設計之從零開始》、《OpenCV3程式設計入門》 碩士就讀於南京航空航天大學航天學院(2013級碩士研究生),已於2016年三月畢業。本科

REACT NATIVE 系列教程外掛的安裝使用與更新(示例:REACT-NATIVE-TAB-NAVIGATOR)

本篇主要來詳細介紹如何安裝、升級外掛及講解一個react-native-tab-navigator的示例。本文舉例使用的外掛:react-native-tab-navigator ,選項卡形式的導航1. 通過  https://www.npmjs.com 找到我們想使用的外掛, 搜尋:react-native

Packet Tracer 思科模擬器入門教程 路由器靜態路由配置

實驗目標 掌握靜態路由的配置方法和技巧; 掌握通過靜態路由方式實現網路的連通性; 熟悉廣域網線纜的連結方式; 實驗背景      學校有新舊兩個校區,每個校區是一個獨立的區域網,為了使新舊校區能夠正常相互通訊,共享資源。每個校

小說連載網絡紅顏美女網絡工程師第一天上班就被燙傷了腳……

網絡 職場 美女 入職 簡介:這是一段描寫網絡工程師生活的故事。故事中沒有英雄,沒有勵誌,也沒有所謂的雞湯文化和狼性文化。有的,或許是一種對技術的執著,對愛情的渴望或者是對名利的一種追求,但又能追求到什麽呢?聲明:本故事所出現的人名,公司名均為虛構,如有雷同恰屬巧合小說將在本站博客和本人微信公

Java EE入門教程系列第一章Java EE的概述——Java EE技術框架和開發工具

1.3Java EE的技術框架 從技術的角度劃分,完整的Java EE分成了4個部分:元件技術、服務技術、通訊技術和架構技術。 下面給出的是一個適合初學者的體系結構簡化圖,暫時接觸不到的部分統一用“支援技術”表示,我們暫時只專注於與應用級開發相關的技術即可。 1.元件技術 這是

AI實戰快速掌握TensorFlow計算圖會話

在前面的文章中,我們已經完成了AI基礎環境的搭建(見文章:Ubuntu + Anaconda + TensorFlow + GPU + PyCharm搭建AI基礎環境),以及初步瞭解了TensorFlow的特點和基本操作(見文章:快速掌握TensorFlow(一)),接下來將繼續學習掌握Tenso

專欄 - Win8Metro影象處理C#

Win8Metro影象處理(C#) 使用Win8 Metro C#程式設計,由簡到難,給大家系統介紹數字影象處理這門學科的各種基礎知識。