目標:
在這個教程中你將學會:
應用兩個最常用的形態學操作:膨脹和腐蝕。爲了這個目的,你將使用下面的OpenCV函數:
erode()
Dilate()
形態學操作:
簡要的說:就是一系列基於形狀的處理圖像的操作。形態學操作對輸入圖像應用一個結構元素並且產生輸出圖像。
最基本的形態學操作是:膨脹和腐蝕。他們有很廣泛的應用,例如:
取出噪音;
分離單個的元素並且把分開的元素在一幅圖像中聯通;
找出一幅圖像的碰撞或者孔洞。
我們將使用下面這幅圖像來簡要地解釋一下膨脹和腐蝕:
腐蝕:
這個操作是以B爲覈對圖像A進行卷積處理,這個核可以有任意的形狀和尺寸,通常是一個方形或圓形。
核B有一個錨點,通常是核的中心;
當核B對圖像進行掃描的時候,我們計算被B所掩蓋的像素的最大值,並且在錨點用最大值取代圖像的像素值。正如你所推斷,這種最大化操作導致圖像中亮的區域增多(因此也叫作腐蝕)。以上述圖像爲例,對其應用腐蝕操作我們可以得到:
背景(亮)腐蝕字母的黑色區域。
膨脹:
這個操作時腐蝕的姊妹操作。這個操作所做的就是去計算被核所掩蓋的像素的最小值。
當核B在掃描圖像的時候,我們計算被核所掩蓋的圖像像素的最小值,並且在錨點用這個最小值去替換該像素值。
和腐蝕的例子相似,我們可以將膨脹操作應用到上述的圖像中。你可以在下面的結果中看到圖像的亮區域(背景),變得更窄了,然而字母的黑色區域變得更大了。
代碼:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "highgui.h"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/// Global variables
Mat src, erosion_dst, dilation_dst;
int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;
/** Function Headers */
void Erosion(int, void*);
void Dilation(int, void*);
/** @function main */
int main(int argc, char** argv)
{
/// Load an image
src = imread("F:/Photo/OpenCV_Photo/lena.jpg");
if (!src.data)
{
return -1;
}
/// Create windows
namedWindow("Erosion Demo", CV_WINDOW_AUTOSIZE);
namedWindow("Dilation Demo", CV_WINDOW_AUTOSIZE);
cvMoveWindow("Dilation Demo", src.cols, 0);
/// Create Erosion Trackbar
createTrackbar("Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
&erosion_elem, max_elem,
Erosion);
createTrackbar("Kernel size:\n 2n +1", "Erosion Demo",
&erosion_size, max_kernel_size,
Erosion);
/// Create Dilation Trackbar
createTrackbar("Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
&dilation_elem, max_elem,
Dilation);
createTrackbar("Kernel size:\n 2n +1", "Dilation Demo",
&dilation_size, max_kernel_size,
Dilation);
/// Default start
Erosion(0, 0);
Dilation(0, 0);
waitKey(0);
return 0;
}
/** @function Erosion */
void Erosion(int, void*)
{
int erosion_type;
if (erosion_elem == 0){ erosion_type = MORPH_RECT; }
else if (erosion_elem == 1){ erosion_type = MORPH_CROSS; }
else if (erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
Mat element = getStructuringElement(erosion_type,
Size(2 * erosion_size + 1, 2 * erosion_size + 1),
Point(erosion_size, erosion_size));
/// Apply the erosion operation
erode(src, erosion_dst, element);
imshow("Erosion Demo", erosion_dst);
}
/** @function Dilation */
void Dilation(int, void*)
{
int dilation_type;
if (dilation_elem == 0){ dilation_type = MORPH_RECT; }
else if (dilation_elem == 1){ dilation_type = MORPH_CROSS; }
else if (dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
Mat element = getStructuringElement(dilation_type,
Size(2 * dilation_size + 1, 2 * dilation_size + 1),
Point(dilation_size, dilation_size));
/// Apply the dilation operation
dilate(src, dilation_dst, element);
imshow("Dilation Demo", dilation_dst);
}
解釋:
1、 大多數的代碼以應該都知道了,下面讓我們來檢查這個程序的大概結構:
首先載入一幅圖像
創建兩個窗口;
爲每一個操作創建一個滑動條;
每次你移動滑動條,用戶函數就會響應,然後更新窗口中顯示的圖像;
下面讓我們來分析這兩個函數:
2、 膨脹:
erode( src, erosion_dst, element );
從上面我們可以看到,膨脹操作erode函數結構三個參數。其中element是我們要進行膨脹操作的核。如果我們沒有指定,那麼默認的核是一個3X3的矩陣,我們也可以自己指定核。爲了我們自己來定義核,我們使用了下面的函數:OpenCV提供了一個函數getStructuringElement,可以獲取常用的結構元素的形狀:矩形(包括線形)、橢圓(包括圓形)及十字形:MORPH_RECT, MORPH_ELLIPSE, MORPH_CROSS
Matelement = getStructuringElement( erosion_type,
Size(2*erosion_size + 1, 2*erosion_size+1 ),
Point( erosion_size,erosion_size ) );
然後我們只需要指定我們核的大小以及錨點。如果沒有指定,那麼它就假設在覈的中心。
有了上述這些,我們已經可以去對我們的圖像做膨脹操作了。
注意:還有另外一個參數允許你一次性做多次膨脹操作,在這個簡單的例子中,就不介紹了,請查看相關資料獲得更多詳細的介紹。
3、 腐蝕:
腐蝕操作從上面的代碼中可以看出,它的實現過程和膨脹的過程非常相似。這裏就不多做介紹了。