常言道“温故而知新”,写此文章就是对自己目前学习内容的小小的总结与记录。
本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。
博主机器配置为:VS2013+opencv2.4.13+Win-64bit。
若本文能给读者带来一点点启示与帮助,我就很开心了。
====================分割线====================
1-形态学梯度
通常所说形态学梯度(Morphological Gradient)是膨胀图像与腐蚀图像的之差得到的图像,也是基本梯度。数学表达式如下:
梯度用于刻画目标边界或边缘位于图像灰度级剧烈变化的区域,形态学梯度根据膨胀或者腐蚀与原图作差组合来实现增强结构元素领域中像素的强度,突出高亮区域的外围。计算图像的形态学梯度是形态学重要操作,常常将膨胀和腐蚀基础操作组合起来一起使用实现一些复杂的图像形态学梯度。可以计算的梯度常见如下四种:
- 基本梯度
- 内部梯度
- 外部梯度
- 方向梯度
特点
- 形态学梯度操作的输出图像像素值是在对应结构元素而非局部过渡区域所定义的领域中灰度级强度变化的最大值。
- 对二值图像进行形态学操作可以将团块(blob)的边缘突出出来,可以用形态学梯度来保留物体的边缘轮廓。
=================分割线==============
2-morphologyEx()函数
作用:该函数可以进行形态学滤波的操作,里面包含了开运算、闭运算、形态学梯度、顶帽、黑帽、腐蚀、膨胀等。
- void morphologyEx( InputArray src, OutputArray dst,
- int op, InputArray kernel,
- Point anchor=Point(-1,-1), int iterations=1,
- int borderType=BORDER_CONSTANT,
- const Scalar& borderValue=morphologyDefaultBorderValue() );
参数解释:
- 参数1:输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
- 参数2:OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
- 参数3:int类型的op,表示形态学运算的类型,可以是如下之一的标识符:
————MORPH_CLOSE – 闭运算(Closing operation)
————MORPH_GRADIENT -形态学梯度(Morphological gradient)
————MORPH_TOPHAT - “顶帽”(“Top hat”)
————MORPH_BLACKHAT - “黑帽”(“Black hat“)
————MORPH_ERODE - “腐蚀”
————MORPH_DILATE - “膨胀”
————另有CV版本的标识符也可选择,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT等,这应该是OpenCV1.0系列版本遗留下来的标识符,和上面的“MORPH_OPEN”一样的效果。
- 参数4:InputArray类型的kernel,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement()配合这个参数的使用。getStructuringElement()函数会返回指定形状和尺寸的结构元素(内核矩阵)。关于getStructuringElement()函数,请见文章里有相关讲解:【拜小白opencv】36-形态学滤波1——腐蚀
- 参数5:Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
- 参数6:int类型的iterations,迭代使用函数的次数,默认值为1。
- 参数7:int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
- 参数8:const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
============分割线==================
3-代码演示
本文将展示2个程序代码。都可独立运行。
第一个程序,就是常规的使用morphologyEx()函数,实现形态学梯度操作。
第二个程序,实现上面提到的4种常见的梯度操作:基本梯度、内部梯度、外部梯度、方向梯度。
程序1代码如下:
==============间隔线============
- /*
- 功能:形态学梯度运算
- */
-
- #include
- #include
- #include
- #include
- using namespace std;
- using namespace cv;
-
- int main()
- {
- Mat srcImage, dstImage; //源图像,输出图像
- //---------【1】读取源图像并检查图像是否读取成功---------
- srcImage = imread("D:\OutPutResult\ImageTest\ju.jpg");
- if (!srcImage.data)
- {
- cout << "读取图片错误,请重新输入正确路径!
";
- system("pause");
- return -1;
- }
- imshow("【源图像】", srcImage);
- //---------【2】获取自定义核---------
- Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
- //---------【3】进行形态学梯度操作---------
- morphologyEx(srcImage, dstImage, MORPH_GRADIENT, element);
- //---------【4】显示效果图---------
- imshow("【效果图--形态学梯度】", dstImage);
- waitKey(0);
- return 0;
- }
==============间隔线============
程序2代码如下:
- /*
- 功能:实现4中形态学梯度:基本梯度、内部梯度、外部梯度、方向梯度
- */
-
- #include
- #include
- #include
- #include
- using namespace std;
- using namespace cv;
-
- int main()
- {
- Mat srcImage, grayImage; //源图像,输出图像,灰度图像
- //---------【1】读取源图像并检查图像是否读取成功---------
- srcImage = imread("D:\OutPutResult\ImageTest\ju.jpg");
- if (!srcImage.data)
- {
- cout << "读取图片错误,请重新输入正确路径!
";
- system("pause");
- return -1;
- }
- imshow("【源图像】", srcImage);
- //---------【2】获取自定义核及对源图像进行腐蚀与膨胀---------
- Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
- Mat erode_ouput, dilate_output;
- erode(srcImage, erode_ouput, element); //腐蚀
- dilate(srcImage, dilate_output, element); //膨胀
- //---------【3】计算基本梯度:膨胀后的图像减去腐蚀后的图像----------
- Mat basicGradient;
- subtract(dilate_output, erode_ouput, basicGradient, Mat());
- imshow("【基本梯度】", basicGradient);
- //---------【4】计算内部梯度:原图像减去腐蚀之后的图像----------
- Mat internalGradientImg;
- subtract(srcImage, erode_ouput, internalGradientImg, Mat());
- imshow("【内部梯度】", internalGradientImg);
- //---------【5】计算外部梯度:膨胀后的图像减去原图像----------
- Mat externalGradientImg;
- subtract(dilate_output, srcImage, externalGradientImg, Mat());
- imshow("【外部梯度】", externalGradientImg);
- //---------【6】方向梯度:使用X方向与Y方向的直线作为结构元素---------
- Mat hse = getStructuringElement(MORPH_RECT, Size(srcImage.cols / 16, 1));
- Mat vse = getStructuringElement(MORPH_RECT, Size(1, srcImage.rows / 16));
- Mat erode_direct, dilate_direct;
- Mat binImg, xDirectImg, yDirectImg;
- // 转为灰度图
- cvtColor(srcImage, grayImage, CV_BGR2GRAY);
- // 将灰度图二值化
- threshold(grayImage, binImg, 0, 255, CV_THRESH_OTSU);
- // X 方向梯度:膨胀与腐蚀之后得到图像求差值
- erode(binImg, erode_direct, hse);
- dilate(binImg, dilate_direct, hse);
- subtract(dilate_direct, erode_direct, xDirectImg, Mat());
- imshow("【X 方向梯度】", xDirectImg);
- // Y 方向梯度:膨胀与腐蚀之后得到图像求差值
- erode(binImg, erode_direct, vse);
- dilate(binImg, dilate_direct, vse);
- subtract(dilate_direct, erode_direct, yDirectImg, Mat());
- imshow("【Y 方向梯度】", yDirectImg);
-
- waitKey(0);
- return 0;
- }
====================分割线====================
4-显示结果
程序1的结果如下:

=========间隔线======
程序2的结果如下:



===================分割线===========
5-程序解释
程序1与程序2的内核都为5*5。
可以发现,基本梯度就是程序1当中的形态学梯度,是一样的。
程序2中,仔细发现内部梯度与外部梯度是有所区别的。
为了显示效果,在进行方向梯度操作时,先对源图像进行二值化了。其实也可用RGB图像进行操作,同学们可以自己试试。(PS.知道4种梯度是怎么用腐蚀,膨胀组合的就好)
参考文章:
图像处理之形态学梯度计算
=====================END===================
评论记录:
回复评论: