图像处理(十三):图像形态学

记忆力差的孩子得勤写日记!!

膨胀和腐蚀

《学习opencv》上有如下定义:

膨胀是指一些图像(或图像中的一部分区域,称之为A)与核(称之为B)进行卷积。核可以是任意形状或大小,它拥有一个单独定义出来的参考点(anchor  point)。多数情况下,核是一个小的中间带有参考点的实心正方形或圆盘。....巴拉巴拉.......

其实简单一点,腐蚀就是我现在需要寻找一个像素点及其周围像素点中像素的最小值,然后用这个像素值替代当前像素的像素值。周围指的就是卷积核大小,即对应着搜索半径,卷积核中心对应着当前像素位置。然后重复上述过程,遍历图像中的每一个像素点就实现了图像腐蚀操作

膨胀的过程与之类似,只不过将寻找最小值变成寻找最大值。

膨胀操作可以填补凹洞,腐蚀能够消除细的凸起。

开运算和闭运算

开运算 = 先腐蚀,后膨胀: 通常用来统计二值图像的区域值。

闭运算 = 先膨胀,后腐蚀:通常用来去除噪声引起的区域。

开运算和闭运算操作几乎都是“保留区域”形式的,最显著的效果是,闭运算消除低于其邻近的孤立点,而开运算是消除高于其邻域的孤立点。

礼帽运算和顶帽运算

TopHat(src) = src - open(src)   : 突出src周围区域更明亮的区域

BlackHat(src) = close(src) - src:突出src周围区域黑暗的区域

简而言之:对于非二值图像的腐蚀操作即寻找邻域最小值,膨胀操作即寻找领域最大值。

代码如下:

 

#include <opencv2/opencv.hpp>

using namespace cv;

const int directions[8][2] = { { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, -1 },  { 0, 1 }, { 1, -1 }, { 1, 0 }, { 1, 1 } };

int main()
{
	Mat src = imread("1.png");
	//Mat gray;
	//cvtColor(src, gray, CV_BGR2GRAY);

	int i, j, k;
	//int temp = 256;

	//Mat erode_Mat = Mat::zeros(gray.rows, gray.cols, CV_8UC1);
	Mat erode_Mat = Mat::zeros(src.rows, src.cols, CV_8UC3);

	// 腐蚀操作,即求局部极小值
	for (i = 1; i < src.rows - 1; i++)
		for (j = 1; j < src.cols - 1; j++)
		{
			int temp0 = 256;
			for (k = 0; k < 8; k++)
			{
				if (temp0 > src.at<Vec3b>(i + directions[k][0],j + directions[k][1])[0])
				{
					temp0 = src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[0];
				}
			}
			erode_Mat.at<Vec3b>(i, j)[0] = temp0;

			int temp1 = 256;
			for (k = 0; k < 8; k++)
			{
				if (temp1 > src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[1])
				{
					temp1 = src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[1];
				}
			}
			erode_Mat.at<Vec3b>(i, j)[1] = temp1;

			int temp2 = 256;
			for (k = 0; k < 8; k++)
			{
				if (temp2 > src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[2])
				{
					temp2 = src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[2];
				}
			}
			erode_Mat.at<Vec3b>(i, j)[2] = temp2;
			
		}

	Mat dilate_Mat = Mat::zeros(src.rows, src.cols, CV_8UC3);

	// 膨胀操作,即求局部极大值
	for (i = 1; i < src.rows - 1; i++)
		for (j = 1; j < src.cols - 1; j++)
		{
			int temp0 = -1;
			for (k = 0; k < 8; k++)
			{
				if (temp0 < src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[0])
				{
					temp0 = src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[0];
				}
			}
			dilate_Mat.at<Vec3b>(i, j)[0] = temp0;

			int temp1 = -1;
			for (k = 0; k < 8; k++)
			{
				if (temp1 < src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[1])
				{
					temp1 = src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[1];
				}
			}
			dilate_Mat.at<Vec3b>(i, j)[1] = temp1;

			int temp2 = -1;
			for (k = 0; k < 8; k++)
			{
				if (temp2 < src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[2])
				{
					temp2 = src.at<Vec3b>(i + directions[k][0], j + directions[k][1])[2];
				}
			}
			dilate_Mat.at<Vec3b>(i, j)[2] = temp2;

		}

	imshow("src", src);
	imshow("erode", erode_Mat);
	imshow("dilate", dilate_Mat);

	waitKey();

	return 0;

}

效果如下:

 


 

对于二值图像的腐蚀操作即寻找与定义核完全一致的像素,膨胀操作即寻找与定义核部分一致的像素。

针对二值图像的腐蚀和膨胀操作,代码如下:

 

#include <opencv2/opencv.hpp>

using namespace cv;

//定义的腐蚀核
const int se[9] = {-1, 1, -1,
					1, 1, 1,
				   -1, 1, -1};

const int se1[9] = { 0, 1, 0,
					1, 1, 1,
					0, 1, 0 };
// 对应3*3位置
const int locations[9][2] = { { -1, -1 }, { -1, 0 }, { -1, 1 },
							  { 0, -1 }, { 0, 0 }, { 0, 1 },
							  { 1, -1 }, { 1, 0 }, { 1, 1 } };

int main()
{
	Mat src = imread("1.jpg");
	Mat gray;
	cvtColor(src, gray, CV_BGR2GRAY);
	Mat threshold_Mat;

	threshold(gray, threshold_Mat, 100, 255, THRESH_BINARY_INV);

	Mat erode_Mat = Mat::zeros(gray.rows, gray.cols, CV_8UC1);


	int i, j, k;

	// erode过程
	for (i = 1; i < threshold_Mat.rows - 1; i++)
		for (j = 1; j < threshold_Mat.cols - 1; j++)
		{
			bool Match_flag = true;
			for (k = 0; k < 9; k++)
			{
				if (-1 == se[k]) continue;

				// 如果是前景
				if (1 == se[k])
				{
					if (threshold_Mat.at<uchar>(i + locations[k][0], j + locations[k][1]) != 255)
					{
						Match_flag = false;
						break;
					}
				}
				// 如果是背景
				else if (0 == se[k])
				{
					if (threshold_Mat.at<uchar>(i, j) != 0)
					{
						Match_flag = false;
						break;
					}
				}
			}

			if (Match_flag == true)
			{
				erode_Mat.at<uchar>(i, j) = 255;
			}

		}

	Mat dilate_Mat = Mat::zeros(gray.rows, gray.cols, CV_8UC1);
	// dilate过程
	for (i = 1; i < threshold_Mat.rows - 1; i++)
		for (j = 1; j < threshold_Mat.cols - 1; j++)
		{
			for (k = 0; k < 9; k++)
			{
				if (-1 == se1[k]) continue;
				if (1 == se1[k])
				{
					if (255 == threshold_Mat.at<uchar>(i + locations[k][0], j + locations[k][1]))
					{
						dilate_Mat.at<uchar>(i, j) = 255;
						break;
					}
				}
			}				

		}



	imshow("thresold", threshold_Mat);
	imshow("erode", erode_Mat);
	imshow("dilate", dilate_Mat);
	waitKey();

	return 0;
}

效果如下:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值