常言道“温故而知新”,写此文章就是对自己目前学习内容的小小的总结与记录。
本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。
博主机器配置为:VS2013+opencv2.4.13+Win-64bit。
若本文能给读者带来一点点启示与帮助,我就很开心了。
====================分割线====================
本次,将前几篇关于形态学滤波的一些操作,综合了一下,编写了一个程序,可以操作这几种方法。
一共实现了11种操作:腐蚀、膨胀、开运算、闭运算、顶帽、黑帽、形态学梯度(又为基本梯度)、内部梯度、外部梯度、X方向梯度、Y方向梯度。
放一张思维导图,看不清的话,右键进行保存。
首先,还是先介绍每个操作是什么。 为方便,请直接点击链接,进行相关的查看。
【腐蚀】—— 【拜小白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=======================