【拜小白opencv】43-形态学滤波——综合示例【腐蚀、膨胀、开运算、闭运算、顶帽、黑帽 形态学梯度、内部梯度、外部梯度、X方向梯度、Y方向梯度】

常言道“温故而知新”,写此文章就是对自己目前学习内容的小小的总结与记录。

本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。

博主机器配置为:VS2013+opencv2.4.13+Win-64bit。

若本文能给读者带来一点点启示与帮助,我就很开心了。

====================分割线====================


本次,将前几篇关于形态学滤波的一些操作,综合了一下,编写了一个程序,可以操作这几种方法。 

一共实现了11种操作:腐蚀、膨胀、开运算、闭运算、顶帽、黑帽、形态学梯度(又为基本梯度)、内部梯度、外部梯度、X方向梯度、Y方向梯度

放一张思维导图,看不清的话,右键进行保存。


扫描二维码关注公众号,回复: 2644054 查看本文章



首先,还是先介绍每个操作是什么。 为方便,请直接点击链接,进行相关的查看。

腐蚀】—— 【拜小白opencv】36-形态学滤波1——腐蚀

膨胀】—— 【拜小白opencv】37-形态学滤波2——膨胀

开运算】—— 【拜小白opencv】38-形态学滤波3——开运算

闭运算】—— 【拜小白opencv】39-形态学滤波4——闭运算

顶帽】—— 【拜小白opencv】41-形态学滤波6——顶帽运算(OR礼帽运算、高帽运算)

黑帽】—— 【拜小白opencv】42-形态学滤波7——黑帽运算

形态学滤波】 以及内部梯度、外部梯度、X方向梯度、Y方向梯度都在这篇文章中——【拜小白opencv】40-形态学滤波5——形态学梯度(基本梯度、内部梯度、外部梯度、方向梯度)

=====================分割线===================

1-代码演示

/*
功能:综合示例——形态学滤波
一共包含11中操作:腐蚀、膨胀、开运算、闭运算、顶帽、黑帽
形态学梯度(又为基本梯度)、内部梯度、外部梯度、X方向梯度、Y方向梯度
*/

#include <opencv2/core/core.hpp>                  
#include <opencv2/highgui/highgui.hpp>                  
#include <opencv2/imgproc/imgproc.hpp>                 
#include <iostream> 
using namespace cv;
using namespace std;

#define WINDOWNAME "【形态学滤波-效果图】"

//-------------------【全局变量声明部分】------------------------
Mat g_srcImage; //源图像
Mat g_dstImage; //得到的效果图
int g_nElementShape = MORPH_RECT;	//元素结构的形状
int g_nStructElementSize = 3;	//结构元素(内核矩阵)的尺寸
int g_nMaxNum = 21; //内核最大值
int g_nTypeChoice = 0; //形态学操作类型选择

//-------------------【全局函数声明部分】------------------------
void on_TrackbarNumChange(int, void*); //类型选择-回调函数
void on_ElementSizeChange(int, void*); //内核大小变换-回调函数
void ShowHelpText();	//帮助文字显示
void Process();	//对应的形态学操作
void ErodeProcess();	//腐蚀操作
void DilateProcess();	//膨胀操作
void OpenProcess();	//开运算操作
void CloseProcess();	//闭运算操作
void TopHatProcess();	//顶帽操作
void BlackHatProcess();	//黑帽操作
void GradienteProcess();	//形态学梯度操作-即基本梯度
void InternalGradientProcess();	//内部梯度操作
void ExternalGradientProcess();	//外部操作
void xDirectGradientProcess();	//X方向梯度操作
void yDirectGradientProcess();	//Y方向梯度操作

//--------------------------------【主函数】------------------------------
int main()
{
	//载入图像
	g_srcImage = imread("D:\\OutPutResult\\ImageTest\\ju.jpg");
	if (!g_srcImage.data)
	{
		cout << "读取图片错误,请重新输入正确路径!\n";
		system("pause");
		return -1;
	}
	namedWindow("【原始图】", WINDOW_AUTOSIZE);
	namedWindow(WINDOWNAME, WINDOW_AUTOSIZE);
	imshow("【原始图】", g_srcImage);//显示原始图
	ShowHelpText();
	//创建轨迹条
	createTrackbar("类型选择", WINDOWNAME, &g_nTypeChoice, 10, on_TrackbarNumChange);
	createTrackbar("内核", WINDOWNAME, &g_nStructElementSize, g_nMaxNum, on_ElementSizeChange);
	//轮询获取按键信息
	while (1)
	{
		//执行回调函数
		on_TrackbarNumChange(g_nTypeChoice, 0);
		on_ElementSizeChange(g_nStructElementSize, 0);
		//获取按键
		int c;
		c = waitKey(0);
		//按下键盘Q键或者ESC,程序退出
		if ((char)c == 'q' || (char)c == 27 || (char)c == 'Q')
			break;
		//按下键盘按键1,使用矩形(Rectangle)结构元素MORPH_RECT   
		if ((char)c == 49)//键盘按键1的ASII码为49
			g_nElementShape = MORPH_RECT;
		//按下键盘按键2,使用十字形(Cross)结构元素MORPH_CROSS  
		else if ((char)c == 50)//键盘按键2的ASII码为50
			g_nElementShape = MORPH_CROSS;
		//按下键盘按键3,使用椭圆(Elliptic)结构元素MORPH_ELLIPSE
		else if ((char)c == 51)//键盘按键3的ASII码为51
			g_nElementShape = MORPH_ELLIPSE;
	}
	return 0;
}

//------------【on_TrackbarNumChange()函数】-------------
void on_TrackbarNumChange(int, void*)
{
	//类型之间效果已经切换,回调函数体内需调用一次对应的操作函数,使改变后的效果立即生效并显示出来
	Process();
}

//------------【on_ElementSizeChange()函数】--------------
void on_ElementSizeChange(int, void*)
{
	//内核尺寸已改变,回调函数体内需调用一次Process函数,使改变后的效果立即生效并显示出来
	Process();
}

//------------【进行对应的形态学操作】--------------
void Process()
{
	switch (g_nTypeChoice)
	{
	case 0:ErodeProcess(); break;
	case 1:DilateProcess(); break;
	case 2:OpenProcess(); break;
	case 3:CloseProcess(); break;
	case 4:TopHatProcess(); break;
	case 5:BlackHatProcess(); break;
	case 6:GradienteProcess(); break;
	case 7:InternalGradientProcess(); break;
	case 8:ExternalGradientProcess(); break;
	case 9:xDirectGradientProcess(); break;
	case 10:yDirectGradientProcess(); break;
	}
}

//-----------【描述:进行腐蚀操作】-----------
void ErodeProcess()
{
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));	//获取自定义核
	erode(g_srcImage, g_dstImage, element);	//进行腐蚀或膨胀操作
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行膨胀操作】-----------
void DilateProcess()
{
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));//获取自定义核	
	dilate(g_srcImage, g_dstImage, element);//进行膨胀操作
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行开运算操作】-----------
void OpenProcess()
{
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));	//获取自定义核
	morphologyEx(g_srcImage, g_dstImage, MORPH_OPEN, element);	//进行开运算操作:先腐蚀后膨胀
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行闭运算操作】-----------
void CloseProcess()
{
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));//获取自定义核	
	morphologyEx(g_srcImage, g_dstImage, MORPH_CLOSE, element);//进行闭运算操作:先膨胀再腐蚀
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行顶帽操作】-----------
void TopHatProcess()
{
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));	//获取自定义核
	morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT, element);	//进行顶帽操作:原图像与开运算结果图之差
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行黑帽操作】-----------
void BlackHatProcess()
{
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));	//获取自定义核
	morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);	//进行黑帽操作:闭运算结果图与原图像之差
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行形态学梯度操作】-----------
void GradienteProcess()
{
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));	//获取自定义核
	morphologyEx(g_srcImage, g_dstImage, MORPH_GRADIENT, element);	//进行形态学梯度操作:膨胀图像与腐蚀图像的之差
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行内部梯度操作】-----------
void InternalGradientProcess()
{
	Mat erode_ouput;
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));	//获取自定义核
	morphologyEx(g_srcImage, erode_ouput, MORPH_ERODE, element);//进行腐蚀操作
	subtract(g_srcImage, erode_ouput, g_dstImage, Mat());//计算内部梯度:原图像减去腐蚀之后的图像
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行外部梯度操作】-----------
void ExternalGradientProcess()
{
	Mat dilate_output;
	Mat element = getStructuringElement(g_nElementShape, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));	//获取自定义核
	morphologyEx(g_srcImage, dilate_output, MORPH_DILATE, element);//进行膨胀操作
	subtract(dilate_output, g_srcImage, g_dstImage, Mat());//计算外部梯度:膨胀后的图像减去原图像
	imshow(WINDOWNAME, g_dstImage);	//显示效果图
}

//-----------【描述:进行X方向梯度操作】-----------
void xDirectGradientProcess()
{
	if (g_nStructElementSize == 0)
	{
		imshow(WINDOWNAME, g_srcImage);	//显示原图
	}
	else
	{
		Mat hse = getStructuringElement(g_nElementShape, Size(g_srcImage.cols / g_nStructElementSize, 1));
		Mat erode_direct, dilate_direct;
		erode(g_srcImage, erode_direct, hse);
		dilate(g_srcImage, dilate_direct, hse);
		subtract(dilate_direct, erode_direct, g_dstImage, Mat()); // X 方向梯度:膨胀与腐蚀之后得到图像求差值  
		imshow(WINDOWNAME, g_dstImage);	//显示效果图
	}
}

//-----------【描述:进行Y方向梯度操作】-----------
void yDirectGradientProcess()
{
	if (g_nStructElementSize == 0)
	{
		imshow(WINDOWNAME, g_srcImage);	//显示原图
	}
	else
	{
		Mat vse = getStructuringElement(g_nElementShape, Size(1, g_srcImage.rows / g_nStructElementSize));
		Mat erode_direct, dilate_direct;
		erode(g_srcImage, erode_direct, vse);
		dilate(g_srcImage, dilate_direct, vse);
		subtract(dilate_direct, erode_direct, g_dstImage, Mat()); // Y 方向梯度:膨胀与腐蚀之后得到图像求差值  
		imshow(WINDOWNAME, g_dstImage);	//显示效果图
	}
}

//------------------【程序一些提示操作信息】-----------------
void ShowHelpText()
{
	cout << "-----------------------------------------------------------" << endl;
	cout << "\t请调整滚动条观察效果\n" << endl;
	cout << "\t按键操作说明:" << endl;
	cout << "\t\t键盘按键【Esc】或者【Q】-退出程序" << endl;
	cout << "\t\t键盘按键【1】--使用矩形<Rectangle>结构内核" << endl;
	cout << "\t\t键盘按键【2】--使用十字形<Cross-shaped>结构内核" << endl;
	cout << "\t\t键盘按键【3】--使用椭圆<Elliptic>结构内核" << endl;
	cout << "-----------------------------------------------------------" << endl;
	cout << "\t类型选择说明:" << endl;
	cout << "\t\t0——腐蚀" << endl;
	cout << "\t\t1——膨胀" << endl;
	cout << "\t\t2——开运算" << endl;
	cout << "\t\t3——闭运算" << endl;
	cout << "\t\t4——顶帽操作" << endl;
	cout << "\t\t5——黑帽操作" << endl;
	cout << "\t\t6——形态学梯度(基本梯度)" << endl;
	cout << "\t\t7——内部梯度" << endl;
	cout << "\t\t8——外部梯度" << endl;
	cout << "\t\t9——X方向梯度" << endl;
	cout << "\t\t10——Y方向梯度" << endl;
}

=====================分割线====================

2-显示结果

【原图像】


【0-腐蚀结果-见下图】


【1-膨胀结果-见下图】


【2-开运算结果-见下图】


【3-闭运算结果-见下图】


【4-顶帽结果-见下图】


【5-黑帽结果-见下图】


【6-形态学梯度结果-见下图】


【7-内部梯度结果-见下图】


【8-外部结果-见下图】


【9-X方向梯度结果-见下图】


【10-Y方向梯度结果-见下图】


======================分割线====================

3-程序解释

1)窗口【形态学滤波-效果图】中写的内核,并不是真正内核的大小,而应该是Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1)。显示是仅是g_nStructElementSize 的值,要注意区分。

2)可以在窗口【形态学滤波-效果图】上,通过按数字【1】、【2】、【3】来改变内核的形状,有三种,矩形、十字形、椭圆形。

=====================END=======================

猜你喜欢

转载自blog.csdn.net/sinat_36264666/article/details/78690909