前言:
数学形态学提供了一组有用的方法,能够用来调整分割区域的形状以获得比较理想的结果,它最初是从数学中的集合论发展而来并用于处理二值图的,虽然运算很简单,但是往往可以产生很好的效果,后来这些方法推广到普通的灰度级图像处理中。常用的形态学处理方法包括:腐蚀、膨胀、开运算、闭运算、顶帽运算、底帽运算,其中膨胀与腐蚀是图像处理中最常用的形态学操作手段,其他方法是两者相互组合而产生的。
一、膨胀
跟卷积操作类似,假设有图像A和结构元素B,结构元素B在A上面移动,其中B定义其中心为锚点,计算B覆盖下A的最大像素值用来替换锚点的像素,其中B作为结构体可以是任意形状。我们回忆一下中值平滑操作——取每一个位置的矩形领域内值的中值作为该位置的输出灰度值,图像的膨胀操作与中值平滑操作类似,它是取每一个位置的矩形领域内值的最大值作为该位置的输出灰度值。不同的是,这里的领域不再单纯是矩形结构的,也可以是椭圆形结构的、十字交叉形结构的等,其中红色是参考点,也称为锚点(anchor point),如下所示:
因此取每个位置领域内最大值,所以膨胀后输出图像的总体亮度的平均值比起原图会有所升高,图像中比较亮的区域的面积会变大,而较暗物体的尺寸会减小甚至消失。
1.1 结构元形状构造函数
getStructuringElement( int shape, Size ksize, Point anchor )
其参数解释如下:
shape:1)MORPH_RECT 表示产生矩形的结构元
2)MORPH_ELLIPSEM 表示产生椭圆形的结构元
3)MORPH_CROSS 表示产生十字交叉形的结构元
ksize:表示结构元的尺寸,即(宽,高),必须是奇数anchor:表示结构元的锚点,即参考点。默认值Point(-1, -1)代表中心像素为锚点
1.2 膨胀操作API
OpenCV中提供给了dilate()函数来完成膨胀操作,其函数声明如下:
其参数解释如下:
src:表示输入矩阵
element:表示结构元,即 函数getStructuringElement( )的返回值
anchor:结构元的锚点,即参考点
iterations:膨胀操作的次数,默认为一次
borderType:边界扩充类型
borderValue:边界扩充值
1.3 创建TrackBar
opencv提供了一种称为轨迹条或滑动条(Trackbar)的控件工具,能够直观的改变出现处理时的参数,实时看到更改这些参数时对于图像处理结果的影响。createTrackbar这个函数我们以后会经常用到,它创建一个可以调整数值的轨迹条,并将轨迹条附加到指定的窗口上,使用起来很方便。首先大家要记住,它往往会和一个回调函数配合起来使用。先看下他的函数声明:
int createTrackbar(conststring& trackbarname, conststring& winname, int* value, int count, TrackbarCallback onChange=0,void* userdata=0);
其中参数解释如下:
trackbarname,表示轨迹条的名字,用来代表我们创建的轨迹条。
winname,填窗口的名字,表示这个轨迹条会依附到哪个窗口上,即对应namedWindow()创建窗口时填的某一个窗口名。
value,一个指向整型的指针,表示滑块的位置。并且在创建时,滑块的初始位置就是该变量当前的值。
count,表示滑块可以达到的最大位置的值。滑块最小的位置的值始终为0。
onChange,首先注意他有默认值0。这是一个指向回调函数的指针,每次滑块位置改变时,这个函数都会进行回调。并且这个函数的原型必须为void XXXX(int,void*);其中第一个参数是轨迹条的位置,第二个参数是用户数据(看下面的参数)。如果回调是NULL指针,表示没有回调函数的调用,仅第三个参数value有变化。
userdata,他也有默认值0。这个参数是用户传给回调函数的数据,用来处理轨迹条事件。如果使用的第三个参数value实参是全局变量的话,完全可以不去管这个userdata参数。
这个createTrackbar函数,为我们创建一个具有特定名称和范围的轨迹条(Trackbar,或者说是滑块范围控制工具),指定一个和轨迹条位置同步的变量。而且要指定回调函数onChange(第五个参数),在轨迹条位置改变的时候来调用这个回调函数。并且我们知道,创建的轨迹条显示在指定的winname(第二个参数)所代表的窗口上。
1.4 代码演示
接下来编写演示代码:
#include
#include
using namespace cv;
Mat src, dst; // 全局变量
int element_size = 3; //全局变量
int max_size = 21; // 全局变量
void CallBack_func(int, void*);
int main( )
{
src = imread("test7.png");
if (src.empty())
{
printf("could not load the image...\n");
return -1;
}
namedWindow("原图:", CV_WINDOW_AUTOSIZE);
imshow("原图:", src);
namedWindow("膨胀操作后:", CV_WINDOW_AUTOSIZE);
createTrackbar("结构元尺寸 :", "膨胀操作后:", &element_size, max_size, CallBack_func);
CallBack_func(element_size, 0);
waitKey(0);
return 0;
}
void CallBack_func(int, void*)
{
int s = element_size * 2 + 1;
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1)); //创建结构元
dilate(src, dst, structureElement, Point(-1, -1), 1); //调用膨胀API
imshow("膨胀操作后:", dst);
}
运行程序,如下:
二、腐蚀
腐蚀操作与膨胀操作类似,只是它取结构元所指定的领域内值的最小值作为该位置的输出灰度值。因为取每个位置领域内最小值,所以腐蚀后输出图像的总体亮度的平均值比起原图会有所降低,图像中比较亮的区域的面积会变小甚至消失,而较暗物体的尺寸会扩大。
OpenCV中提供给了erode()函数来完成膨胀操作,其函数声明如下:
其中参数解释如下:
src:表示输入矩阵
dst:表示输出矩阵
element:表示结构元,即 函数getStructuringElement( )的返回值
anchor:结构元的锚点,即参考点
iterations:腐蚀操作的次数,默认为一次
borderType:边界扩充类型
borderValue:边界扩充值
接下来我们编写演示代码:
#include
#include
using namespace cv;
Mat src, dst; // 全局变量
int element_size = 3; //全局变量
int max_size = 21; // 全局变量
void CallBack_func(int, void*);
int main( )
{
src = imread("test7.png");
if (src.empty())
{
printf("could not load the image...\n");
return -1;
}
namedWindow("原图:", CV_WINDOW_AUTOSIZE);
imshow("原图:", src);
namedWindow("膨胀操作后:", CV_WINDOW_AUTOSIZE);
createTrackbar("结构元尺寸 :", "膨胀操作后:", &element_size, max_size, CallBack_func);
CallBack_func(element_size, 0);
waitKey(0);
return 0;
}
void CallBack_func(int, void*)
{
int s = element_size * 2 + 1;
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1)); //创建结构元
erode(src, dst, structureElement); //调用腐蚀API
imshow("膨胀操作后:", dst);
}
运行程序,如下: