opencv3学习笔记(六)-------形态学滤波

形态学(morphology)之数学形态学-----是一门建立在格伦和拓扑基础之上的图像分析学科,是数学形态学图像处理的基本理论。基本运算包括:二值腐蚀和膨胀、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换、灰值腐蚀和膨胀、灰值开闭运算、灰值形态学梯度等。

核心API函数------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()
 )

函数参数

第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。

第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。

第三个参数,int类型的op,表示形态学运算的类型,可以是如下之一的标识符:

第四个参数,InputArray类型的kernel,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。 

第五个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。

第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。

  • MORPH_OPEN -------- 开运算
  • MORPH_CLOSE --------闭运算
  • MORPH_GRADIENT --------形态学梯度(Morphological gradient)
  • MORPH_TOPHAT-------顶帽
  • MORPH_BLACKHAT------黑帽
  • MORPH_ERODE------腐蚀
  • MORPH_DILATE-------膨胀

第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。

第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。

1、膨胀与腐蚀

        膨胀-------原图中的高亮部分进行膨胀,类似于“邻域扩张”,效果图拥有比原图更大的高亮区域;

 膨胀(dilate)就是求局部最大值的操作。从数学角度来说,就是将图像与核(模板或掩码)进行卷积。

膨胀:用结构元素的中心点对准当前正在遍历的这个像素, 
然后取当前结构元素所覆盖下的原图对应区域内的所有像素的最大值,用这个最大值替换当前像素值,给图像中的对象边界添加像素,使二值图像扩大一圈。 其步骤即用结构元素,扫描图像的每一个像素 ,然后用结构元素与其覆盖的二值图像做“与”操作。如果都为0,结果图像的该像素为0,否则为1。 也就是在结构元素覆盖范围下,只要有一个像素符和结构元素像素相同,那么中心点对应点就为1,否则为0。

函数原型:

void dilate(
InputArray src,               //原图像
OutputArray dst,              //目标图像
InputArray kernel,            //膨胀操作的核由getStructuringElement()函数配合
Point anchor=Point(-1,-1),    //锚的位置,默认(-1,-1)
int iteration=1,              //迭代使用erode()函数的次数,默认(1)
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue()    //边界为常数时的边界值
);

        腐蚀-------原图中的高亮部分被腐蚀,类似于“邻域被吞噬”,效果图拥有比原图更小的高亮区域。

腐蚀(erode)就是求局部最小值操作。从数学角度来说,就是将图像与核(模板或掩码)进行卷积。

腐蚀:用结构元素的中心点对准当前正在遍历的这个像素, 
然后取当前结构元素所覆盖下的原图对应区域内的所有像素的最小值,用这个最小值替换当前像素值,删除对象边界的某些像素,使二值图像减小一圈。 步骤即用结构元素,扫描图像的每一个像素然后用结构元素与其覆盖的二值图像做“与”操作 。如果都为1,结果图像的该像素为1,否则为0 。也就是查找被处理图像中能不能找到和结构元素相同的矩阵。如果存在那么中心点所对应的点就为1,否则为0。

函数原型:

void erode(
InputArray src,               //原图像
OutputArray dst,              //目标图像
InputArray kernel,            //膨胀操作的核由getStructuringElement()函数配合
Point anchor=Point(-1,-1),    //锚的位置,默认(-1,-1)
int iteration=1,              //迭代使用erode()函数的次数,默认(1)
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue()    //边界为常数时的边界值
);

腐蚀和膨胀的第三个参数:InputArray类型的kernel,操作的核。一般使用函数getStructuringElement,此函数会返回指定形状和尺寸的结构元素(内核矩阵)。结构元素就相当于我们在滤波中所涉及到的模板,也就是说它是一个给定像素的矩阵,这个矩阵可以是任意形状的, 一般情况下都是正方形,圆形或者菱形。但是在结构元素中有一个中心点(也叫做anchor point)和模板中心一样,处理后的结果赋值给和这个中心点对齐的像素点。处理的过程也是基本相同。结构元素和卷积模板的区别在于,膨胀是以集合运算为基础的,卷积是以算数运算为基础的。 

getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1),  Point(Absolute_offset, Absolute_offset));

         参数一:表示内核的形状*  矩形:MORPH_RECT *  交叉形:MORPH_CROSS *  椭圆形:MORPH_ELLIPSE

         参数二、三:分别是内核的尺寸以及锚点的位置。

         我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。

膨胀与腐蚀(针对图像中的高亮部分)能实现的功能:

           ①消除噪音;(低尺寸结构元素的腐蚀操作很容易去掉分散的椒盐噪声点)

           ②分割出独立的图像元素(腐蚀),在图像中链接相邻的元素(膨胀);

           ③寻找图像中明显的极大值区域或极小值区域;

           ④求出图像的梯度。

           ⑤腐蚀:删除对象边界的某些像素 ;膨胀:给图像中的对象边界添加像素

开运算: 先腐蚀,再膨胀,可清除一些小东西(亮的),放大局部低亮度的区域 

dst=open(src,element)=dilate(erode(src,element));

闭运算: 先膨胀,再腐蚀,可清除小黑点 

dst=colse(src,element)=erode(dilate(src,element));

形态学梯度: 膨胀图与腐蚀图之差,提取物体边缘 

dst=morphgrad(src,element)=dilate(src,element)-erode(src,element);

顶帽: 原图像-开运算图,突出原图像中比周围亮的区域 

dst=tophat(src,element)=src-open(src,element);

黑帽: 闭运算图-原图像,突出原图像中比周围暗的区域

dst=blackhat(src,element)=close(src,element)-src;

综合示例:

//---------------【综合示例:形态学滤波】----------------
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

//-----------------------【全局变量声明部分】----------------------
//    描述:全局变量声明
//-----------------------------------------------------------------
Mat g_srcImage, g_dstImage;//原始图和效果图

//元素结构的形状(矩形MORPH_RECT)(交叉形MORPH_CROSS)(椭圆形MORPH_ELLIPSE)
int g_nElementShape = MORPH_RECT;

//变量接收的TracknBar位置参数
int g_nMaxIterationNum = 10;
int g_nOpenCloseNum = 0;  //开运算和闭运算
int g_nErodeDilateNum = 0;//腐蚀和膨胀
int g_nTopBlackHatNum = 0;//顶帽和黑帽

//-----------------------【全局函数声明部分】----------------------
//    描述:全局函数声明
//-----------------------------------------------------------------
static void on_OpenClose(int, void*);//回调函数
static void on_ErodeDilate(int, void*);
static void on_TopBlackHat(int, void*);
static void ShowHelpText();//帮助文字显示

//-----------------------【main()函数】---------------------------
//    描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------
int main() {
	//载入图像
	g_srcImage = imread("lenna.jpg");
	if (!g_srcImage.data) { printf("读取图片出错~!\n"); return false; }

	//显示图像
	namedWindow("【原始图像】");
	imshow("【原始图像】",g_srcImage);

	//创建三个窗口
	namedWindow("【开运算/闭运算】",1);
	namedWindow("【腐蚀/膨胀】",1);
	namedWindow("【顶帽运算/黑帽运算】",1);

	//参数赋值
	g_nOpenCloseNum = 9;
	g_nErodeDilateNum = 9;
	g_nTopBlackHatNum =9;

	//分别为三个窗口创建滚动条
	createTrackbar("迭代值", "【开运算/闭运算】", &g_nOpenCloseNum, g_nOpenCloseNum * 2 + 1,on_OpenClose);
	createTrackbar("迭代值", "【腐蚀/膨胀】", &g_nErodeDilateNum, g_nErodeDilateNum* 2 + 1, on_ErodeDilate);
	createTrackbar("迭代值", "【顶帽运算/黑帽运算】", &g_nTopBlackHatNum, g_nTopBlackHatNum * 2 + 1, on_TopBlackHat);

	//轮询获取按键信息
	while (1) {
		int c;

		//执行回调函数
		on_OpenClose(g_nOpenCloseNum, 0);
		on_ErodeDilate(g_nErodeDilateNum, 0);
		on_TopBlackHat(g_nTopBlackHatNum, 0);

		//获取按键
		c = waitKey(0);

		//按下键盘按键Q或者ESC,程序退出
		if ((char)c == 'q' || (char)c == 27)
			break;
		//按下键盘按键1,使用椭圆结构元素MORPH_ELLIPSE
		if ((char)c == 49)//按键1的ASII码为49
			g_nElementShape = MORPH_ELLIPSE;
		//按下键盘按键2,使用矩形结构元素MORPH_RECT
		else if ((char)c == 50)
			g_nElementShape = MORPH_RECT;
		//按下键盘按键3,使用十字形结构元素MORPH_CROSS
		else if ((char)c == 51)
			g_nElementShape = MORPH_CROSS;
		//按下键盘按键space,在矩形、椭圆、十字形结构元素中循环
		else if ((char)c == ' ')
			g_nElementShape = (g_nElementShape + 1) % 3;
	}
	return 0;
}

//-----------------------【on_OpenClose()函数】---------------------------
//    描述:【开运算/闭运算】窗口的回调函数
//------------------------------------------------------------------------
static void on_OpenClose(int,void*) {
	//偏移量的定义
	int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值

	//自定义核
	Mat element = getStructuringElement(g_nElementShape,Size(Absolute_offset*2+1,Absolute_offset*2+1),
		Point(Absolute_offset,Absolute_offset));

	//进行操作
	if (offset < 0)
		morphologyEx(g_srcImage, g_dstImage, MORPH_OPEN, element);
	else
		morphologyEx(g_srcImage,g_dstImage,MORPH_CLOSE,element);

	//显示图像
	imshow("【开运算/闭运算】",g_dstImage);
}

//-----------------------【on_ErodeDilate()函数】---------------------------
//    描述:【腐蚀/膨胀】窗口的回调函数
//------------------------------------------------------------------------
static void on_ErodeDilate(int, void*) {
	//偏移量的定义
	int offset = g_nErodeDilateNum - g_nMaxIterationNum;//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值

	//自定义核
	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1),
		Point(Absolute_offset, Absolute_offset));

	//进行操作
	if (offset < 0)
		erode(g_srcImage, g_dstImage, element);
	else
		dilate(g_srcImage, g_dstImage, element);

	//显示图像
	imshow("【腐蚀/膨胀】", g_dstImage);
}


//-----------------------【on_TopBlackHat()函数】---------------------------
//    描述:【顶帽运算/黑帽运算】窗口的回调函数
//------------------------------------------------------------------------
static void on_TopBlackHat(int, void*) {
	//偏移量的定义
	int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量
	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值

	//自定义核
	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1),
		Point(Absolute_offset, Absolute_offset));

	//进行操作
	if (offset < 0)
		morphologyEx(g_srcImage, g_dstImage,CV_MOP_TOPHAT, element);
	else
		morphologyEx(g_srcImage, g_dstImage, CV_MOP_BLACKHAT, element);

	//显示图像
	imshow("【顶帽运算/黑帽运算】", g_dstImage);
}

运行结果:

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值