腐蚀和膨胀是图像的形态学处理中最基本的操作,之后遇见的开操作和闭操作都是腐蚀和膨胀操作的结合运算。腐蚀和膨胀的应用非常广泛,而且效果还很好:
腐蚀可以分割(isolate)独立的图像元素,膨胀用于连接(join)相邻的元素,这也是腐蚀和膨胀后图像最直观的展现
去噪:通过低尺寸结构元素的腐蚀操作很容易去掉分散的椒盐噪声点
图像轮廓提取:腐蚀操作
图像分割
等等...(在文后给出一则简单实用膨胀操作提取车牌数字区域的例子)
结构元素是形态学操作中最重要的概念,
如上图,B为结构元素。
腐蚀操作描述为:扫描图像的每一个像素,用结构元素与其覆盖的二值图像做“与”操作:如果都为1,结果图像的该像素为1,否则为0。
膨胀操作描述为:扫描图像的每一个像素,用结构元素与其覆盖的二值图像做“与”操作:如果都为0,结果图像的该像素为0,否则为1。
以上都是关于二值图像的形态学操作,对于灰度图像:
腐蚀操作
其中,g(x,y)为腐蚀后的灰度图像,f(x,y)为原灰度图像,B为结构元素。腐蚀运算是由结构元素确定的邻域块中选取图像值与结构元素值的差的最小值。
膨胀操作
其中,g(x,y)为腐蚀后的灰度图像,f(x,y)为原灰度图像,B为结构元素。 膨胀运算是由结构元素确定的邻域块中选取图像值与结构元素值的和的最大值。
在灰度图的形态学操作中,一般选择“平摊”的结构元素,即结构元素B的值为0,则上面对灰度图的形态学操作可简化如下:
好了,这就是基本的形态学操作——腐蚀和膨胀,下面是使用OpenCV对图像进行腐蚀和膨胀的程序,还是秉承我们一贯的原则:搁下理论,先直观地感觉图像处理算法的效果,实际项目需要时再深入挖掘!
程序分析 /* * FileName : eroding_and_dilating.cpp * Author : xiahouzuoxin @163.com * Version : v1.0 * Date : Fri 19 Sep 2014 07:42:12 PM CST * Brief : * * Copyright (C) MICL,USTB */ #include "cv.h" #include "highgui.h" #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; #define TYPE_MORPH_RECT (0) #define TYPE_MORPH_CROSS (1) #define TYPE_MORPH_ELLIPSE (2) #define MAX_ELE_TYPE (2) #define MAX_ELE_SIZE (20) Mat src, erode_dst, dilate_dst; const char *erode_wn = "eroding demo"; const char *dilate_wn = "dilating demo"; int erode_ele_type; int dilate_ele_type; int erode_ele_size; int dilate_ele_size; static void Erosion(int, void *); static void Dilation(int, void *); /* * @brief * @inputs * @outputs * @retval */ int main(int argc, char *argv[]) { if (argc < 2) { cout<<"Usage: ./eroding_and_dilating [file name]"<<endl; return -1; } src = imread(argv[1]); if (!src.data) { cout<<"Read image failure."<<endl; return -1; } // Windows namedWindow(erode_wn, WINDOW_AUTOSIZE); namedWindow(dilate_wn, WINDOW_AUTOSIZE); // Track Bar for Erosion createTrackbar("Element Type\n0:Rect\n1:Cross\n2:Ellipse", erode_wn, &erode_ele_type, MAX_ELE_TYPE, Erosion); // callback @Erosion createTrackbar("Element Size: 2n+1", erode_wn, &erode_ele_size, MAX_ELE_SIZE, Erosion); // Track Bar for Dilation createTrackbar("Element Type\n0:Rect\n1:Cross\n2:Ellipse", dilate_wn, &dilate_ele_type, MAX_ELE_TYPE, Dilation); // callback @Erosion createTrackbar("Element Size: 2n+1", dilate_wn, &dilate_ele_size, MAX_ELE_SIZE, Dilation); // Default start Erosion(0, 0); Dilation(0, 0); waitKey(0); return 0; } /* * @brief 腐蚀操作的回调函数 * @inputs * @outputs * @retval */ static void Erosion(int, void *) { int erode_type; switch (erode_ele_type) { case TYPE_MORPH_RECT: erode_type = MORPH_RECT; break; case TYPE_MORPH_CROSS: erode_type = MORPH_CROSS; break; case TYPE_MORPH_ELLIPSE: erode_type = MORPH_ELLIPSE; break; default: erode_type = MORPH_RECT; break; } Mat ele = getStructuringElement(erode_type, Size(2*erode_ele_size+1, 2*erode_ele_size+1), Point(erode_ele_size, erode_ele_size)); erode(src, erode_dst, ele); imshow(erode_wn, erode_dst); } /* * @brief 膨胀操作的回调函数 * @inputs * @outputs * @retval */ static void Dilation(int, void *) { int dilate_type; switch (dilate_ele_type) { case TYPE_MORPH_RECT: dilate_type = MORPH_RECT; break; case TYPE_MORPH_CROSS: dilate_type = MORPH_CROSS; break; case TYPE_MORPH_ELLIPSE: dilate_type = MORPH_ELLIPSE; break; default: dilate_type = MORPH_RECT; break; } Mat ele = getStructuringElement(dilate_type, Size(2*dilate_ele_size+1, 2*dilate_ele_size+1), Point(dilate_ele_size, dilate_ele_size)); dilate(src, dilate_dst, ele); imshow(dilate_wn, dilate_dst); }