圖像腐蝕、膨脹、基本原理和程序實現

分類:IT技術 時間:2016-10-07

圖像的腐蝕與膨脹

一、原理:

⑴ 圖像形態學處理的概念
        數字圖像處理中的形態學處理是指將數字形態學作為工具從圖像中提取對於表達和描繪區域形狀有用處的圖像分量,比如邊界、骨架以及凸殼,還包括用於預處理或後處理的形態學過濾、細化和修剪等。圖像形態學處理中我們感興趣的主要是二值圖像。

⑵ 二值圖像的邏輯運算
        邏輯運算盡管本質上很簡單,但對於實現以形態學為基礎額圖像處理算法是一種有力的補充手段。在圖像處理中用到的主要邏輯運算是:與、或和非(求補),它們可以互相組合形成其他邏輯運算。

⑶ 膨脹和腐蝕

        膨脹和腐蝕這兩種操作是形態學處理的基礎,許多形態學算法都是以這兩種運算為基礎的。

定義結構元素B為:

1 1
1 0
圖像元素與結構元素相乘,從而求得右下角元素值
(i-1,j+1) (i,j+1)
(i-1,j) 所求此點(i,j)

① 膨脹
⑴ 用結構元素B,掃描圖像A的每一個像素
⑵ 用結構元素與其覆蓋的二值圖像做“或”操作
⑶ 如果有一個元素為0,結果圖像的該像素為0。否則為255

② 腐蝕
         對Z中的集合A和B,B對A進行腐蝕的整個過程如下: 
⑴ 用結構元素B,掃描圖像A的每一個像素
⑵ 用結構元素與其覆蓋的二值圖像做“與”操作
⑶ 如果都為0,結果圖像的該像素為0。否則為255

腐蝕處理的結果是使原來的二值圖像減小一圈。

二、我再加一個輪廓提取,非常簡單的方法:用的是9X9的模板;

(i-1,j+1) (i,j+1) (i+1,j+1)
(i-1,j) 所求此點(i,j) (i+1,j)
(i-1,j-1) (i,j-1) (i+1,j_1)
三、代碼

#include<opencv2/opencv.hpp>
#include<iostream>
using  namespace cv;
using namespace std;

Mat srcImage, grayImage, binarygray, erosion, dilation, outline;


static void g_erosion(int, void*);
static void g_dilation(int, void*);
static void g_outline(int, void*);
static void ShowHelpText();

int main()
{
	system("color 3f");
	ShowHelpText();

	srcImage = imread("D://vvoo//cell.jpg");
	cvtColor(srcImage, grayImage, CV_RGB2GRAY);

	int threshold;
	cout << "input threshold: " << endl;
	cin >> threshold;

	//二值化
	binarygray = Mat::zeros(grayImage.rows, grayImage.cols, grayImage.type());
	{
		for (int i = 0; i <grayImage.rows; i++)
		{
			for (int j = 0; j < grayImage.cols; j++)
			{
				if (grayImage.data[i*grayImage.step + j] > threshold)
				{
					binarygray.data[i*binarygray.step + j] = 255;
				}
				else
				{
					binarygray.data[i*binarygray.step + j] = 0;
				}
			}
		}
	}
	//腐蝕
	g_erosion(0, 0);
	//膨脹
	g_dilation(0, 0);
	//輪廓提取
	g_outline(0, 0);

	imshow("原圖", srcImage);
	imshow("binarygray", binarygray);

	waitKey(0);
	return 0;
}
static void g_erosion(int, void*)
{
	erosion = Mat::zeros(binarygray.rows, binarygray.cols, binarygray.type());
	{
		for (int i = 1; i < binarygray.rows; i++)
		{
			for (int j = 1; j < binarygray.cols; j++)
			{
				if (binarygray.data[(i - 1)*binarygray.step + j] + binarygray.data[(i - 1)*binarygray.step + j + 1] + binarygray.data[i*binarygray.step + j + 1] == 0)
				{
					erosion.data[i*erosion.step + j] = 0;
				}
				else
				{
					erosion.data[i*erosion.step + j] = 255;
				}
			}

		}

	}
	imshow("erosion_1", erosion);
}
static void g_dilation(int, void*)
{
	dilation = Mat::zeros(binarygray.rows, binarygray.cols, binarygray.type());

	for (int i = 1; i < binarygray.rows; i++)
	{
		for (int j = 1; j < binarygray.cols; j++)
		{
			if (binarygray.data[(i - 1)*binarygray.step + j] == 0 || binarygray.data[(i - 1)*binarygray.step + j - 1] == 0 || binarygray.data[i*binarygray.step + j + 1] == 0)
			{
				dilation.data[i*dilation.step + j] = 0;
			}
			else
			{
				dilation.data[i*dilation.step + j] = 255;
			}
		}

	}

	imshow("dilation_1", dilation);
}
static void g_outline(int, void*)
{
	outline = Mat::zeros(binarygray.rows, binarygray.cols, binarygray.type());

	for (int i = 1; i < binarygray.rows; i++)
	{
		for (int j = 1; j < binarygray.cols; j++)
		{
			if (binarygray.data[i*binarygray.step + j + 1] + binarygray.data[(i - 1)*binarygray.step + j]
				+ binarygray.data[i*binarygray.step + j - 1] + binarygray.data[(i - 1)*binarygray.step + j - 1]
				+ binarygray.data[(i + 1)*binarygray.step + j - 1] + binarygray.data[(i + 1)*binarygray.step + j]
				+ binarygray.data[(i - 1)*binarygray.step + j + 1] + binarygray.data[(i + 1)*binarygray.step + j + 1] == 2040)
			{
				outline.data[i*erosion.step + j] = 255;
			}
			if (binarygray.data[i*binarygray.step + j + 1] + binarygray.data[(i - 1)*binarygray.step + j]
				+ binarygray.data[i*binarygray.step + j - 1] + binarygray.data[(i - 1)*binarygray.step + j - 1]
				+ binarygray.data[(i + 1)*binarygray.step + j - 1] + binarygray.data[(i + 1)*binarygray.step + j]
				+ binarygray.data[(i - 1)*binarygray.step + j + 1] + binarygray.data[(i + 1)*binarygray.step + j + 1] == 0)
			{
				outline.data[i*erosion.step + j] = 255;
			}
		}


	}
	imshow("outline", outline);
}
static void ShowHelpText()
{
	cout << "\n\n本程序涉及到:"<<"腐蝕(erosion)、膨脹(dilation)、輪廓提取(outline)。\n\n" << endl;
}
四、運行結果




五、調用Opencv的erode()函數和dilate()函數實現腐蝕和膨脹功能

1)erode函數,使用像素鄰域內的局部極小運算符來腐蝕一張圖片,從src輸入,由dst輸出。支持就地(in-place)操作。

看一下函數原型:

 void erode(
  InputArray src,
  OutputArray dst,
  InputArray kernel,
  Point anchor=Point(-1,-1),
  int iterations=1,
  int borderType=BORDER_CONSTANT,
  const Scalar& borderValue=http://blog.csdn.net/qq_29540745/article/details/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一起使用。

2)dilate函數原型

函數原型:

C++: void dilate(
  InputArray src,
  OutputArray dst,
  InputArray kernel,
  Point anchor=Point(-1,-1),
  int iterations=1,
  int borderType=BORDER_CONSTANT,
  const Scalar& borderValue=http://blog.csdn.net/qq_29540745/article/details/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函數的第二和第三個參數分別是內核的尺寸以及錨點的位置。

3)代碼實現

#include<opencv2/opencv.hpp>
#include<iostream>
using  namespace cv;
using namespace std;

#define WINDOWN_NAME_1 "原圖"
#define WINDOWN_NAME_2 "腐蝕圖"
#define WINDOWN_NAME_3 "膨脹圖"

int main()
{
	Mat srcImage = imread("D://vvoo//cell.jpg");

	//獲取自定義核
	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
	Mat out_erosion, out_dilate;

	//進行膨脹操作
	erode(srcImage, out_erosion, element);
	dilate(srcImage, out_dilate, element);

	imshow(WINDOWN_NAME_1, srcImage);
	imshow(WINDOWN_NAME_2, out_erosion);
	imshow(WINDOWN_NAME_3, out_dilate);

	waitKey(0);
	return 0;

}

4)運行結果



和自己寫的比較下比較一下,差別比較大,主要是因為結構元素大小的關系,我的是2*2,Opencv是15*15的。

我也是初學者,歡迎糾正!



六、參考資料

1.system("color 3f");//輸出窗口和字體顏色可變化,3代表窗口顏色(綠色),f代表窗口裏字體顏色(白色)

全部顏色為:


2.圖像腐蝕、膨脹、細化基本原理

3. 形態學圖像處理(一): 膨脹與腐蝕


Tags: 程序 元素

文章來源:


ads
ads

相關文章
ads

相關文章

ad