数字图像处理合集——形态学处理

实现:
1.熟悉二值形态学膨胀/腐蚀/开/闭运算,理解二值形态学算法的几何意义和数学原理。
2.掌握二值形态学骨架提取算法原理及其实现。
3.掌握形态学方法检测角点算法原理及实现。

1.编写二值形态学膨胀/腐蚀/开/闭运算算法程序,并调试实现。
二值形态学膨胀/腐蚀/开/闭运算均可通过opencv的接口函数实现,膨胀的接口函数为:dilate(image, out, element);腐蚀的接口函数为:erode(image, out, element);开运算的接口函数为:morphologyEx(thresh, open_result, MORPH_OPEN, element);闭运算的接口函数为:morphologyEx(thresh, close_result, MORPH_CLOSE, element);同时,以上操作均可通过morphologyEx函数完成,对其中的参数进行转换即可。

MORPH_ERODE为腐蚀,MORPH_DILATE为膨胀,MORPH_OPEN为开运算,MORPH_CLOSE为闭运算。

实现结果:
二值形态学膨胀/腐蚀/开/闭运算实现效果图如图1-图4所示。
图像膨胀:
数字图像处理合集——形态学处理_第1张图片
图像腐蚀:
数字图像处理合集——形态学处理_第2张图片
图像开运算:
数字图像处理合集——形态学处理_第3张图片

图像闭运算
数字图像处理合集——形态学处理_第4张图片

2.编写利用二值形态学对二值图像进行边缘细化(骨架提取)的算法程序。
使用K3M算法进行骨架提取操作,本算法属于迭代腐蚀边界的一类算法,该类算法的思想是,假定从二值图像中物体的边界处同时开始燃烧,物体就会被逐步细化,但在燃烧过程中要保证满足-定条件的点被保留或者被“烧掉”,以确定燃烧结束后,剩下的最后一像素宽的图像为图像的骨架。这些条件的确定没有统一的标准,各个算法采取了不同的方案。一般来讲,为了满足计算的速度要求和算法的准确,迭代中算法会对图像边界上某点的3*3邻域内进行检查,判断是否满足要求。本算法每次循环都会进行六次检测,分别是:

Phase0:标记出图像的边界,
Phase1:如果该点的邻域中有3个点(非零,以下皆如此)相邻,删除该点
Phase2:如果该点的邻域中有3或4个点相邻,删除该点。
Phase3:如果该点的邻域中有3,4, 5个点相邻,删除该点。
Phase4:如果该点的邻域中有3, 4, 5, 6个点相邻,删除该点。
Phase5:如果该点的邻域中有3, 4, 5, 6, 7个点相邻,删除该点。
Phase6:剩余的边界点取消标记,如果Phase 5中没有点被修改,停止迭代,否则返回Phase 0。

主要实现代码如下所示:

set<int> GetAi(int a[], int length)//获取A0~A5
{
	set<int> vec;
	int neighbour[] = { 1,2,4,8,16,32,64,128,1,2,4,8,16,32,64 };
	for (int i = 0; i<length; i++)
		for (int j = 0; j<8; j++)
		{
			int sum = 0;
			for (int k = j; k <= j + a[i]; k++)
				sum += neighbour[k];
			vec.insert(sum);		//在vec首部插入sum
			std::cout << sum << " ";
		}
	std::cout << std::endl;
	return vec;
}
//迭代腐蚀
bool erodephase(list<cv::Point> &border, cv::Mat&Input, int neighbour[][3], const set<int>& A)
{
	auto pt = border.begin();
	bool result = false;
	while (pt != border.end())
	{
		int weight = 0;
		for (int j = -1; j <= 1; ++j)
			for (int k = -1; k <= 1; k++)
				weight += neighbour[j + 1][k + 1] * Input.at<uchar>(pt->y + j, pt->x + k);
		if (std::find(A.begin(), A.end(), weight) != A.end())
		{
			Input.at<uchar>(pt->y, pt->x) = 0;
			pt = border.erase(pt);
			result = true;
		}
		else
			++pt;
	}
	return result;
}
//找边界 
void findborder(list<cv::Point2i>& border, const cv::Mat&Input)
{
	int cnt = 0;
	int rows = Input.rows;
	int cols = Input.cols;
	cv::Mat bordermat = Input.clone();
	for (int row = 1; row<rows - 1; ++row)
		for (int col = 1; col<cols - 1; ++col)
		{
			int weight = 0;
			for (int j = -1; j <= 1; ++j)
				for (int k = -1; k <= 1; k++)
				{
					if (Input.at<uchar>(row + j, col + k) == 1)
						++cnt;
				}
			if (cnt == 9)
				bordermat.at<uchar>(row, col) = 0;
			cnt = 0;
		}

	for (int row = 1; row<rows - 1; ++row)
		for (int col = 1; col < cols - 1; ++col)
		{
			if (bordermat.at<uchar>(row, col) == 1)
				border.push_back(cv::Point2i(col, row));
		}

}
//最后一步,得到骨架
void finalerode(cv::Mat&Input, int neighbour[][3], const set<int>& A)
{
	int rows = Input.rows;
	int cols = Input.cols;
	for (int m = 1; m<rows - 1; ++m)
		for (int n = 1; n<cols - 1; ++n)
		{
			int weight = 0;
			for (int j = -1; j <= 1; ++j)
				for (int k = -1; k <= 1; k++)
				{
					weight += neighbour[j + 1][k + 1] * Input.at<uchar>(m + j, n + k);
				}

			if (std::find(A.begin(), A.end(), weight) != A.end())
				Input.at<uchar>(m, n) = 0;
		}
}
void thin(Mat &Input) //Input是二值图像
{
	int a0[] = { 1,2,3,4,5,6 };
	int a1[] = { 2 };
	int a2[] = { 2,3 };
	int a3[] = { 2,3,4 };
	int a4[] = { 2,3,4,5 };
	int a5[] = { 2,3,4,5,6 };
	set<int> A0 = GetAi(a0, 6);
	set<int> A1 = GetAi(a1, 1);
	set<int> A2 = GetAi(a2, 2);
	set<int> A3 = GetAi(a3, 3);
	set<int> A4 = GetAi(a4, 4);
	set<int> A5 = GetAi(a5, 5);
	list<cv::Point2i> border;
	bool continue_ = true;
	int neighbour[3][3] = 
	{
		{ 128,1,2 },
	    { 64,0,4  },
	    { 32,16,8 }
	};
	while (continue_)		
	{
		continue_ = false;
		findborder(border, Input);
		erodephase(border, Input, neighbour, A1);
		erodephase(border, Input, neighbour, A2);
		erodephase(border, Input, neighbour, A3);
		erodephase(border, Input, neighbour, A4);
		continue_ = erodephase(border, Input, neighbour, A5);		border.clear();
	}
	finalerode(Input, neighbour, A0);}

实现效果:
二值形态学对二值图像进行边缘细化(骨架提取)实现效果图如图5所示。
图像骨架提取:
数字图像处理合集——形态学处理_第5张图片

3.编写形态学检测角点算法程序,并调试实现。
定义十字形核,菱形形状核,X形状核,分别进行两次膨胀、腐蚀操作,在不同的方向上进行膨胀腐蚀操作,并画出角点。主要代码如下所示:

int point(Mat & srcImg)
{
	Mat srcGray;
	imshow("【原图】原始图像", srcImg);
	cvtColor(srcImg, srcGray, CV_RGB2GRAY);
	Mat thresh;
	thresh = srcGray.clone();
	threshold(srcGray, thresh, 176, 255, CV_THRESH_BINARY_INV);
	imshow("【效果图】二值图像", thresh);
	//定义核
	Mat CrossMat(5, 5, CV_8U, Scalar(0));
	Mat diamondMat(5, 5, CV_8U, Scalar(1));
	Mat squareMat(5, 5, CV_8U, Scalar(1));
	Mat X(5, 5, CV_8U, Scalar(0));
	//十字形核
	for (int i = 0; i < 5; i++) {
		CrossMat.at<uchar>(2, i) = 1;
		CrossMat.at<uchar>(i, 2) = 1;
	}
	//菱形形状核
	diamondMat.at<uchar>(0, 0) = 0;
	diamondMat.at<uchar>(0, 1) = 0;
	diamondMat.at<uchar>(1, 0) = 0;
	diamondMat.at<uchar>(4, 4) = 0;
	diamondMat.at<uchar>(3, 4) = 0;
	diamondMat.at<uchar>(4, 3) = 0;
	diamondMat.at<uchar>(4, 0) = 0;
	diamondMat.at<uchar>(4, 1) = 0;
	diamondMat.at<uchar>(3, 0) = 0;
	diamondMat.at<uchar>(0, 4) = 0;
	diamondMat.at<uchar>(0, 3) = 0;
	diamondMat.at<uchar>(1, 4) = 0;
	//X形状核
	for (int i = 0; i < 5; i++) {
		X.at<uchar>(i, i) = 1;
		X.at<uchar>(4 - i, i) = 1;
	}
	Mat result;
	dilate(thresh, result, CrossMat);
	imshow("【效果图】膨胀操作", result);
	erode(result, result, diamondMat);
	imshow("【效果图】腐蚀操作_腐蚀角点", result);
	Mat result2;
	dilate(thresh, result2, X);
	imshow("【效果图】膨胀操作_膨胀水平、垂直及45°、135°方向", result2);
	erode(result2, result2, squareMat);
	imshow("【效果图】腐蚀操作_腐蚀水平和垂直方向", result2);
	//计算差值
	absdiff(result2, result, result);
	threshold(result, result, 30, 255, THRESH_BINARY);
	//绘制
	for (int i = 0; i < result.rows; i++) {
		//获取行指针
		const uchar* data = result.ptr<uchar>(i);
		for (int j = 0; j < result.cols; j++) {
			//如果是角点,则绘制圆圈
			if (data[j]) {
				circle(srcImg, Point(j, i), 8,
					Scalar(0, 0, 255));
			}
		}
	}
	imshow("【效果图】圈出角点", srcImg);
	imshow("【效果图】提取角点", result);
	waitKey(0);
	return 0;
}

实现效果:
形态学检测角点实现效果图如图6所示。
图像角点提取:
数字图像处理合集——形态学处理_第6张图片

合集入口

测试资源下载入口

你可能感兴趣的:(初学者C++,数字图像处理,c++,opencv,图像处理)