圖像的膨脹與腐蝕——OpenCV與C++的具體實現


1. 膨脹與腐蝕的原理

膨脹與腐蝕是數學形態學在圖像處理中最基礎的操作。在筆者之前的文章《圖像的卷積(濾波)運算(一)——圖像梯度》《圖像的卷積(濾波)運算(二)——高斯濾波》具體介紹了圖像卷積\濾波的具體的概念與操作,圖像的膨脹與腐蝕其實也是一種類似的卷積操作。其卷積操作非常簡單,對於圖像的每個像素,取其一定的鄰域,計算最大值/最小值作為新圖像對應像素位置的像素值。其中,取最大值就是膨脹,取最小值就是腐蝕。

2. 膨脹的具體實現

1) OpenCV實現

在OpenCV中實現了圖像膨脹的函數dilate(),可以直接調用:

Mat img = imread(imagename, IMREAD_GRAYSCALE);
if (img.empty())
{
	fprintf(stderr, "Can not load image %s\n", imagename);
	return -1;
}

//OpenCV方法
Mat dilated_cv;
dilate(img, dilated_cv, Mat());

dilate()函數第一個參數表示輸入影像,第二個參數表示輸出影像,第三個表示一個默認的核,在3X3的范圍內尋找最大值。

2) C/C++實現

在一般的圖像處理時,圖像讀寫是由專門的組件進行讀取的。這這里仍然使用OpenCV進行讀取,以免增加復雜性。而在CV::Mat類中,提供了at()函數訪問某一行某一列的像素值,可以通過at()函數去訪問每一個像素的領域。

與之前OpenCV實現的一樣,對於每一個像素,遍歷以其像素位置為中心的3X3鄰域,取最大值作為新圖像對應位置的像素值。
其具體實現如下:

//從文件中讀取成灰度圖像
const char* imagename = "D:\\Data\\imgDemo\\lena.jpg";
Mat img = imread(imagename, IMREAD_GRAYSCALE);
if (img.empty())
{
	fprintf(stderr, "Can not load image %s\n", imagename);
	return -1;
}

//自定義方法
Mat dilated_my;
dilated_my.create(img.rows, img.cols, CV_8UC1);
for (int i = 0; i < img.rows; ++i)
{
	for (int j = 0; j < img.cols; ++j)
	{	
		//uchar minV = 255;
		uchar maxV = 0;
	
		//遍歷周圍最大像素值
		for (int yi = i-1; yi <= i+1; yi++)
		{
			for (int xi = j-1; xi <= j+1; xi++)
			{					
				if (xi<0||xi>= img.cols|| yi<0 || yi >= img.rows)
				{
					continue;
				}					
				//minV = (std::min<uchar>)(minV, img.at<uchar>(yi, xi));
				maxV = (std::max<uchar>)(maxV, img.at<uchar>(yi, xi));			
			}
		}
		dilated_my.at<uchar>(i, j) = maxV;
	}
}	

3) 驗證與結果

為了驗證自己的算法是否正確,可以通過把兩者膨脹的結果通過compare()函數進行比較。compare()函數會逐個比較兩者的像素值,如果相同就會返回255(白色),如果不相同就會返回0(黑色)。整個過程的具體實現如下:

#include <iostream>
#include <algorithm>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
	//從文件中讀取成灰度圖像
	const char* imagename = "D:\\Data\\imgDemo\\lena.jpg";
	Mat img = imread(imagename, IMREAD_GRAYSCALE);
	if (img.empty())
	{
		fprintf(stderr, "Can not load image %s\n", imagename);
		return -1;
	}
	
	//OpenCV方法
	Mat dilated_cv;
	dilate(img, dilated_cv, Mat());

	//自定義方法
	Mat dilated_my;
	dilated_my.create(img.rows, img.cols, CV_8UC1);
	for (int i = 0; i < img.rows; ++i)
	{
		for (int j = 0; j < img.cols; ++j)
		{	
			//uchar minV = 255;
			uchar maxV = 0;
		
			//遍歷周圍最大像素值
			for (int yi = i-1; yi <= i+1; yi++)
			{
				for (int xi = j-1; xi <= j+1; xi++)
				{					
					if (xi<0||xi>= img.cols|| yi<0 || yi >= img.rows)
					{
						continue;
					}					
					//minV = (std::min<uchar>)(minV, img.at<uchar>(yi, xi));
					maxV = (std::max<uchar>)(maxV, img.at<uchar>(yi, xi));			
				}
			}
			dilated_my.at<uchar>(i, j) = maxV;
		}
	}	

	//比較兩者的結果
	Mat c;
	compare(dilated_cv, dilated_my, c, CMP_EQ);

	//顯示
	imshow("原始", img);
	imshow("膨脹_cv", dilated_cv);
	imshow("膨脹_my", dilated_my);
	imshow("比較結果", c);
		
	waitKey();
	
	return 0;
}

其運行結果如下所示。可以發現最后的比較結果是一張白色的圖像,說明自己實現的算法是正確的。

3. 腐蝕的具體實現

同樣的辦法可以實現圖像腐蝕的過程,只要將求局部最大值改成局部最小值就可以了。具體實現過程如下:

#include <iostream>
#include <algorithm>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
	//從文件中讀取成灰度圖像
	const char* imagename = "D:\\Data\\imgDemo\\lena.jpg";
	Mat img = imread(imagename, IMREAD_GRAYSCALE);
	if (img.empty())
	{
		fprintf(stderr, "Can not load image %s\n", imagename);
		return -1;
	}
	
	//OpenCV方法
	Mat eroded_cv;
	erode(img, eroded_cv, Mat());

	//自定義方法
	Mat eroded_my;
	eroded_my.create(img.cols, img.rows, CV_8UC1);
	for (int i = 0; i < img.rows; ++i)
	{
		for (int j = 0; j < img.cols; ++j)
		{	
			uchar minV = 255;
			//uchar maxV = 0;
		
			//遍歷周圍最大像素值
			for (int yi = i-1; yi <= i+1; yi++)
			{
				for (int xi = j-1; xi <= j+1; xi++)
				{					
					if (xi<0||xi>= img.cols|| yi<0 || yi >= img.rows)
					{
						continue;
					}					
					minV = (std::min<uchar>)(minV, img.at<uchar>(yi, xi));
					//maxV = (std::max<uchar>)(maxV, img.at<uchar>(yi, xi));			
				}
			}
			eroded_my.at<uchar>(i, j) = minV;
		}
	}	

	//比較兩者的結果
	Mat c;
	compare(eroded_cv, eroded_my, c, CMP_EQ);

	//顯示
	imshow("原始", img);
	imshow("膨脹_cv", eroded_cv);
	imshow("膨脹_my", eroded_my);
	imshow("比較結果", c);
		
	waitKey();
	
	return 0;
}

其運行結果如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM