赞
踩
1、开运算
开运算(Opening Operation),其实就是先腐蚀后膨胀的过程。其数学表达式如下:
- //-----------------------------------【头文件包含部分】---------------------------------------
- // 描述:包含程序所依赖的头文件
- //----------------------------------------------------------------------------------------------
- #include <opencv2/opencv.hpp>
- #include<opencv2/highgui/highgui.hpp>
- #include<opencv2/imgproc/imgproc.hpp>
-
- //-----------------------------------【命名空间声明部分】---------------------------------------
- // 描述:包含程序所使用的命名空间
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- //-----------------------------------【main( )函数】--------------------------------------------
- // 描述:控制台应用程序的入口函数,我们的程序从这里开始
- //-----------------------------------------------------------------------------------------------
- int main()
- {
- //载入原始图
- Mat image = imread("../jz.jpg"); //工程目录下应该有一张名为1.jpg的素材图
- //创建窗口
- namedWindow("【原始图】开运算");
- namedWindow("【效果图】开运算");
- //显示原始图
- imshow("【原始图】开运算", image);
- //定义核
- Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
- //进行形态学操作
- morphologyEx(image, image, MORPH_OPEN, element);
- //显示效果图
- imshow("【效果图】开运算", image);
-
- waitKey(0);
-
- return 0;
- }
2、闭运算
先膨胀后腐蚀的过程称为闭运算(Closing Operation),其数学表达式如下:
闭运算能够排除小型黑洞(黑色区域)。
- //-----------------------------------【头文件包含部分】---------------------------------------
- // 描述:包含程序所依赖的头文件
- //----------------------------------------------------------------------------------------------
- #include <opencv2/opencv.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include<opencv2/imgproc/imgproc.hpp>
-
- //-----------------------------------【命名空间声明部分】---------------------------------------
- // 描述:包含程序所使用的命名空间
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- //-----------------------------------【main( )函数】--------------------------------------------
- // 描述:控制台应用程序的入口函数,我们的程序从这里开始
- //-----------------------------------------------------------------------------------------------
- int main()
- {
- //载入原始图
- Mat image = imread("../jz.jpg"); //工程目录下应该有一张名为1.jpg的素材图
- //创建窗口
- namedWindow("【原始图】闭运算");
- namedWindow("【效果图】闭运算");
- //显示原始图
- imshow("【原始图】闭运算", image);
- //定义核
- Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
- //进行形态学操作
- morphologyEx(image, image, MORPH_CLOSE, element);
- //显示效果图
- imshow("【效果图】闭运算", image);
-
- waitKey(0);
-
- return 0;
- }
形态学梯度(Morphological Gradient)为膨胀图与腐蚀图之差,数学表达式如下:
对二值图像进行这一操作可以将团块(blob)的边缘突出出来。
- //-----------------------------------【头文件包含部分】---------------------------------------
- // 描述:包含程序所依赖的头文件
- //----------------------------------------------------------------------------------------------
- #include <opencv2/opencv.hpp>
- #include<opencv2/highgui/highgui.hpp>
- #include<opencv2/imgproc/imgproc.hpp>
-
- //-----------------------------------【命名空间声明部分】---------------------------------------
- // 描述:包含程序所使用的命名空间
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- //-----------------------------------【main( )函数】--------------------------------------------
- // 描述:控制台应用程序的入口函数,我们的程序从这里开始
- //-----------------------------------------------------------------------------------------------
- int main()
- {
- //载入原始图
- Mat image = imread("../jz.jpg"); //工程目录下应该有一张名为1.jpg的素材图
- //创建窗口
- namedWindow("【原始图】形态学梯度");
- namedWindow("【效果图】形态学梯度");
- //显示原始图
- imshow("【原始图】形态学梯度", image);
- //定义核
- Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
- //进行形态学操作
- morphologyEx(image, image, MORPH_GRADIENT, element);
- //显示效果图
- imshow("【效果图】形态学梯度", image);
-
- waitKey(0);
-
- return 0;
- }
顶帽运算(Top Hat)又常常被译为”礼帽“运算。为原图像与上文刚刚介绍的“开运算“的结果图之差,数学表达式如下:
因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。
顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
- //-----------------------------------【头文件包含部分】---------------------------------------
- // 描述:包含程序所依赖的头文件
- //----------------------------------------------------------------------------------------------
- #include <opencv2/opencv.hpp>
- #include<opencv2/highgui/highgui.hpp>
- #include<opencv2/imgproc/imgproc.hpp>
-
- //-----------------------------------【命名空间声明部分】---------------------------------------
- // 描述:包含程序所使用的命名空间
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- //-----------------------------------【main( )函数】--------------------------------------------
- // 描述:控制台应用程序的入口函数,我们的程序从这里开始
- //-----------------------------------------------------------------------------------------------
- int main()
- {
- //载入原始图
- Mat image = imread("../jz.jpg"); //工程目录下应该有一张名为1.jpg的素材图
- //创建窗口
- namedWindow("【原始图】顶帽运算");
- namedWindow("【效果图】顶帽运算");
- //显示原始图
- imshow("【原始图】顶帽运算", image);
- //定义核
- Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
- //进行形态学操作
- morphologyEx(image, image, MORPH_TOPHAT, element);
- //显示效果图
- imshow("【效果图】顶帽运算", image);
-
- waitKey(0);
-
- return 0;
- }
黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。数学表达式为:
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。
所以,黑帽运算用来分离比邻近点暗一些的斑块。
- //-----------------------------------【头文件包含部分】---------------------------------------
- // 描述:包含程序所依赖的头文件
- //----------------------------------------------------------------------------------------------
- #include <opencv2/opencv.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include<opencv2/imgproc/imgproc.hpp>
-
- //-----------------------------------【命名空间声明部分】---------------------------------------
- // 描述:包含程序所使用的命名空间
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- //-----------------------------------【main( )函数】--------------------------------------------
- // 描述:控制台应用程序的入口函数,我们的程序从这里开始
- //-----------------------------------------------------------------------------------------------
- int main()
- {
- //载入原始图
- Mat image = imread("../jz.jpg"); //工程目录下应该有一张名为1.jpg的素材图
- //创建窗口
- namedWindow("【原始图】黑帽运算");
- namedWindow("【效果图】黑帽运算");
- //显示原始图
- imshow("【原始图】黑帽运算", image);
- //定义核
- Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
- //进行形态学操作
- morphologyEx(image, image, MORPH_BLACKHAT, element);
- //显示效果图
- imshow("【效果图】黑帽运算", image);
-
- waitKey(0);
-
- return 0;
- }
为什么我们在后面几个案例中使用了morphologyEx函数呢?事实上,本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行更加高级的形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。这一节我们来一起看一下morphologyEx函数的源代码。
- //-----------------------------------【erode()函数中文注释版源代码】----------------------------
- void cv::morphologyEx( InputArray _src,OutputArray _dst, int op,InputArray kernel, Pointanchor, int iterations,
- int borderType, constScalar& borderValue )
- {
- //拷贝Mat数据到临时变量
- Mat src = _src.getMat(), temp;
- _dst.create(src.size(), src.type());
- Mat dst = _dst.getMat();
-
- //一个大switch,根据不同的标识符取不同的操作
- switch( op )
- {
- case MORPH_ERODE:
- erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
- break;
- case MORPH_DILATE:
- dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
- break;
- case MORPH_OPEN:
- erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
- dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
- break;
- case CV_MOP_CLOSE:
- dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
- erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
- break;
- case CV_MOP_GRADIENT:
- erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
- dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
- dst -= temp;
- break;
- case CV_MOP_TOPHAT:
- if( src.data != dst.data )
- temp = dst;
- erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
- dilate( temp, temp, kernel, anchor,iterations, borderType, borderValue );
- dst = src - temp;
- break;
- case CV_MOP_BLACKHAT:
- if( src.data != dst.data )
- temp = dst;
- dilate( src, temp, kernel, anchor, iterations, borderType, borderValue);
- erode( temp, temp, kernel, anchor, iterations, borderType, borderValue);
- dst = temp - src;
- break;
- default:
- CV_Error( CV_StsBadArg, "unknown morphological operation" );
- }
- }
看上面的源码可以发现,其实morphologyEx函数其实就是内部一个大switch而已。根据不同的标识符取不同的操作。比如开运算MORPH_OPEN,按我们上文中讲解的数学表达式,就是先腐蚀后膨胀,即依次调用erode和dilate函数,为非常简明干净的代码。
- C++: void morphologyEx(InputArray src,OutputArray dst,int op,InputArraykernel,Pointanchor=Point(-1,-1),
- intiterations=1,intborderType=BORDER_CONSTANT,constScalar& borderValue=morphologyDefaultBorderValue() );
另有CV版本的标识符也可选择,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT,这应该是OpenCV1.0系列版本遗留下来的标识符,和上面的“MORPH_OPEN”一样的效果。
- 第四个参数,InputArray类型的kernel,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。关于getStructuringElement我们上篇文章中讲过了,这里为了大家参阅方便,再写一遍:
其中,getStructuringElement函数的第一个参数表示内核的形状,我们可以选择如下三种形状之一:
- 矩形: MORPH_RECT
- 交叉形: MORPH_CROSS
- 椭圆形: MORPH_ELLIPSE
而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。
我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。
getStructuringElement函数相关的调用示例代码如下:
- int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
-
- //获取自定义核
- Mat element =getStructuringElement(MORPH_RECT,
- Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
- Point(g_nStructElementSize, g_nStructElementSize ));
调用这样之后,我们便可以在接下来调用erode、dilate或morphologyEx函数时,kernel参数填保存getStructuringElement返回值的Mat类型变量。对应于我们上面的示例,就是填element变量。
- 第五个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
- 第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
- 第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
- 第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
其中的这些操作都可以进行就地(in-place)操作。且对于多通道图像,每一个通道都是单独进行操作。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。