我的OpenCV学习笔记(16):利用工具条调用基本的形态学操作
我的OpenCV学习笔记(16):利用工具条调用基本的形态学操作
http://blog.csdn.net/thefutureisour/article/details/7565726?locationNum=1&fps=1

版权声明:本文为博主原创文章,未经博主允许不得转载。
这次主要介绍两方面的内容,一部分是形态学操作,另一部分是工具条。
先说形态学操作。这里只介绍4种简单的:腐蚀、膨胀、开、闭。最基本的形态学操作是腐蚀和膨胀。其他的操作可以通过腐蚀和膨胀推导出来。
用集合论的观点介绍他们非常麻烦。这里换一种思路:我们先做一定的假设:对于一幅图像:前景(我们感兴趣的部分)是白色的;背景(不感兴趣的部分)是黑色的。然后就可以望文生义一下了:腐蚀操作会使得前景变小,而膨胀会使得前景变大。这主要是当结构元(用来对图像处理的基本模版)作用于图像的边沿时,两种操作的定义引起的。腐蚀操作时只有当整个结构元都在图像边沿内时,锚点(结构元与图像中每个像素对齐的点,通常取作结构元的几何中心)对准的像素才会被保留,判为前景;否则这个点判为背景。膨胀操作则是只要结构元与图像有交集时,锚点对准的像素就会被保留,判为前景。腐蚀可以用来消除一些小的误检测的前景;而膨胀则可以填充一些小洞。
注意到,用3*3的模板腐蚀3次与用7*7的模板腐蚀一次效果是相同的。膨胀的结果可以类推。
有了这两个运算的基本定义,我们就可以定义开运算和闭运算了:开运算就是腐蚀以后再膨胀,而闭运算是膨胀以后再腐蚀。闭运算使得小洞被填上,临近的目标连接到了一起(任何结构元容纳不下的小洞或者缝隙都会被填充)。而开运算则会除去一些小的斑点。
对一幅图像多次使用开运算而闭运算是没有意义的。这与腐蚀和膨胀不同。
在OpenCV中,使用erode和dilate实现腐蚀与膨胀。要实现开运算或者闭运算,当然可以通过腐蚀完以后膨胀之类的方法完成,但是OpenCV通过了一个函数morphologyEx,通过改变函数中的参数来实现不同的形态学运算。下面的代码对这4种运算进行了简单的说明:
- #include <opencv2/core/core.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- using namespace cv;
- int main( void )
- {
- Mat image = imread("D:/picture/images/binary.bmp");
- if(!image.data)
- return -1;
- imshow("源图像",image);
- //腐蚀操作
- Mat eroded;
- //默认情况下,结构元为3*3
- erode(image,eroded,Mat());
- erode(eroded,eroded,Mat());
- erode(eroded,eroded,Mat());
- imshow("腐蚀结果",eroded);
- //使用自定义的结构元
- Mat element(7,7,CV_8U,Scalar(1));
- erode(image,eroded,element);
- imshow("7*7结构元腐蚀结果",eroded);
- //使用默认的结构元重复操作也能得到相同的结果
- erode(image,eroded,Mat(),Point(-1,-1),3);
- imshow("使用默认结构元重复3次",eroded);
- //膨胀操作
- Mat dilated;
- dilate(image,dilated,Mat());
- imshow("膨胀结果",dilated);
- //使用闭运算
- Mat element5(5,5,CV_8U,Scalar(1));
- Mat closed;
- morphologyEx(image,closed,cv::MORPH_CLOSE,element5);
- imshow("5*5结构元闭运算",closed);
- //通过先膨胀再腐蚀的方法得到闭运算
- Mat result;
- dilate(image,result,element5);
- erode(result,result,element5);
- imshow("先膨胀,再腐蚀等于闭运算",result);
- //使用开运算
- Mat opened;
- morphologyEx(image,opened,cv::MORPH_OPEN,element5);
- imshow("5*5结构元开运算",opened);
- //先闭运算再开运算
- morphologyEx(image,result,cv::MORPH_CLOSE,element5);
- morphologyEx(result,result,cv::MORPH_OPEN,element5);
- imshow("先闭运算,在开运算",result);
- waitKey(0);
- return 0;
- }
下面我们着重看如何使用工具条。
在OpenCV中,使用createTrackbar函数来创建工具条。第一个参数是工具条的名字,第二个参数是工具条要放到哪个窗口上(说来惭愧,我以前一直觉得namedWindow这句话很多余,直接在imshow的时候填上窗口名就行了,干嘛非要namedWindow呢?现在才发现了它的作用),第三个参数是工具条改变的是哪个值(顺带设定了初始位置也是这个值),第四个参数是改变的值的最大值。第五个参数是这个值是作用于哪个函数的。第六个值是用户传给回调函数的数据。不过因为我这里使用的是全局变量,所以没有使用最后一个参数。
下面看看程序:
- #include <opencv2/core/core.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- #include <iostream>
- using namespace cv;
- using namespace std;
- //定义全局变量:
- Mat image;
- Mat eroded;
- Mat dilated;
- Mat opened;
- Mat closed;
- //测试的形态学运算:腐蚀、膨胀、开、闭
- //为每个形态学运算创建一个控件
- //控件需要的参数:
- //每种形态学操作需要单独确定的参数:
- //结构元的种类:
- int erosion_element = 0;
- int dilation_element = 0;
- int open_element = 0;
- int close_element = 0;
- //结构元的大小
- int erosion_size = 0;
- int dilation_size = 0;
- int open_size = 0;
- int close_size = 0;
- //四种形态学操作共用的参数
- //结构元种类的最大值:一个3种:正方形、十字、椭圆
- int const max_elements = 2;
- //结构元的最大值(四种共用一个)
- int const max_kernel_size = 5;
- //工具条的回调函数
- void Erosion(int,void*);
- void Dilation(int,void*);
- void Open(int, void*);
- void Close(int,void*);
- int main()
- {
- cout<<"结构元类型:0-矩形,1-十字,2-椭圆"<<endl;
- cout<<"结构元大小:2*n+1"<<endl;
- image = imread("D:/picture/images/binary.bmp");
- if(!image.data)
- return -1;
- imshow("源图像",image);
- eroded.create(image.rows,image.cols,image.type());
- dilated.create(image.rows,image.cols,image.type());
- namedWindow("腐蚀");
- namedWindow("膨胀");
- namedWindow("开运算");
- namedWindow("闭运算");
- //创建工具条
- //腐蚀操作:
- createTrackbar("核函数类型","腐蚀",&erosion_element,max_elements,Erosion);
- createTrackbar("核函数大小","腐蚀",&erosion_size,max_kernel_size,Erosion);
- //调用腐蚀函数
- Erosion(0,0);
- //膨胀操作
- createTrackbar("结构元类型","膨胀",&dilation_element,max_elements,Dilation);
- createTrackbar("结构元大小","膨胀",&dilation_size,max_kernel_size,Dilation);
- Dilation(0,0);
- //开操作
- createTrackbar("结构元类型","开运算",&open_element,max_elements,Open);
- createTrackbar("核函数大小","开运算",&open_size,max_kernel_size,Open);
- Open(0,0);
- //闭操作
- createTrackbar("结构元类型","闭运算",&close_element,max_elements,Close);
- createTrackbar("核函数大小","闭运算",&close_size,max_kernel_size,Close);
- Close(0,0);
- waitKey(0);
- return 0;
- }
- //回调函数:
- //腐蚀函数:不需要输入参数
- void Erosion(int,void*)
- {
- int erosion_type;
- if(erosion_element == 0)
- {
- erosion_type = MORPH_RECT;
- }
- else if(erosion_element == 1)
- {
- erosion_type = MORPH_CROSS;
- }
- else if(erosion_element == 2)
- {
- erosion_type = MORPH_ELLIPSE;
- }
- //获取腐蚀元素:参数为:类型、大小、锚点
- Mat element = getStructuringElement(erosion_type,Size(2*erosion_size+1,2*erosion_size+1),Point(-1,-1));
- //进行腐蚀运算:参数为:原图像、结果、腐蚀元素
- erode(image,eroded,element);
- //显示腐蚀结果
- imshow("腐蚀",eroded);
- }
- //膨胀回调函数
- void Dilation(int,void*)
- {
- int dilation_type;
- if(dilation_element == 0)
- {
- dilation_type = MORPH_RECT;
- }
- else if(dilation_element == 1)
- {
- dilation_type = MORPH_CROSS;
- }
- else if(dilation_element == 2)
- {
- dilation_type = MORPH_ELLIPSE;
- }
- //获取腐蚀元素:参数为:类型、大小、锚点
- Mat element = getStructuringElement(dilation_type,Size(2*dilation_size+1,2*dilation_size+1),Point(-1,-1));
- //进行腐蚀运算:参数为:原图像、结果、腐蚀元素
- dilate(image,dilated,element);
- //显示腐蚀结果
- imshow("膨胀",dilated);
- }
- void Open(int, void*)
- {
- int open_type;
- if(open_element == 0)
- {
- open_type = MORPH_RECT;
- }
- else if(open_element == 1)
- {
- open_type = MORPH_CROSS;
- }
- else if(open_element == 2)
- {
- open_type = MORPH_ELLIPSE;
- }
- //获取腐蚀元素:参数为:类型、大小、锚点
- Mat element = getStructuringElement(open_type,Size(2*open_size+1,2*open_size+1),Point(-1,-1));
- //进行开运算
- morphologyEx(image,opened,cv::MORPH_OPEN,element);
- imshow("开运算",opened);
- }
- //闭操作
- void Close(int,void*)
- {
- int close_type;
- if(close_element == 0)
- {
- close_type = MORPH_RECT;
- }
- else if(close_element == 1)
- {
- close_type = MORPH_CROSS;
- }
- else if(close_element == 2)
- {
- close_type = MORPH_ELLIPSE;
- }
- //获取腐蚀元素:参数为:类型、大小、锚点
- Mat element = getStructuringElement(close_type,Size(2*close_size+1,2*close_size+1),Point(-1,-1));
- //进行开运算
- morphologyEx(image,closed,cv::MORPH_CLOSE,element);
- imshow("闭运算",closed);
- }
美中不足的是,为了改变形态学操作的结构元种类和大小,我定义了许多的全局变量。
如果想知道形态学操作的更多内容,还是看冈萨雷斯的那本经典的《数字图像处理》吧!