OpenCV学习笔记(七)——高级形态学操作

前言:

    本文的主角是OpenCV的morphologyEX函数,它利用基本的膨胀和腐蚀技术,来实现更加高级的形态学变换,如开闭运算、形态学梯度、“顶帽”和“黑帽”等。

    首先,我们需要知道,形态学的高级形态,往往都是建立在腐蚀和膨胀这两个基本操作之上的。而关于腐蚀和膨胀的概念和演示代码请参考上一节。有了腐蚀和膨胀的基础,再来学习高级形态学操作就不难了。


一、morphologyEX函数

     OpenCV中提供了一个morphologyEX函数来实现各种高级的形态学处理,其函数声明如下:

OpenCV学习笔记(七)——高级形态学操作_第1张图片

其参数解释如下:

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
  • 第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
  • 第三个参数,int类型的op,表示形态学运算的类型,可以是如下之一的标识符:                                                

         MORPH_OPEN – 开运算(Opening operation)

         MORPH_CLOSE – 闭运算(Closing operation)

         MORPH_GRADIENT -形态学梯度(Morphological gradient)

         MORPH_TOPHAT - “顶帽”(“Top hat”)

         MORPH_BLACKHAT - “黑帽”(“Black hat")

      注意:另有CV版本的标识符也可选择,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT,这应该是OpenCV1.0系列版本遗留下来的标识符,和上面的“MORPH_OPEN”一样的效果。

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

接下来编写演示代码。


二、开操作

#include 
#include 
#include 

using namespace cv;
using namespace std;

Mat src, dst;       //定义输入图像和输出图像(两个全局变量)
Mat kernel;         // 定义结构元
int r=1;             //定义结构元半径
int r_max=20;		   //定义结构元最大半径
string window = "开操作";

void callBack_func(int, void*);

int main( )
{
	src = imread("test9.png");
	if (src.empty())
	{
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("原图:", CV_WINDOW_AUTOSIZE);
	imshow("原图:", src);
	namedWindow(window, CV_WINDOW_AUTOSIZE);
	createTrackbar("半径", window, &r,r_max, callBack_func);
	callBack_func(0,0);                    
	waitKey(0);
	return 0;
}

void callBack_func(int ,void *)
{
	Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1));  //创建结构元
	morphologyEx(src, dst, MORPH_OPEN, kernel, Point(-1, -1), 1);     // 形态学处理--开运算
	imshow(window,dst);               // 显示形态学处理后的效果
}

运行程序,我们先看一下原图:

OpenCV学习笔记(七)——高级形态学操作_第2张图片           

然后是作开运算后的效果:

OpenCV学习笔记(七)——高级形态学操作_第3张图片

我们拖动TrackBar,看看不同结构元半径对开运算的影响效果:

OpenCV学习笔记(七)——高级形态学操作_第4张图片

OpenCV学习笔记(七)——高级形态学操作_第5张图片

OpenCV学习笔记(七)——高级形态学操作_第6张图片

从图中我们可以很明显的看出,当结构元半径调整到4时,白色小方块消失了,而大矩形方块的面积似乎变化不大。因此,我们可以利用先腐蚀后膨胀的开运算过程,来消除小物体,在平滑较大物体的边界的同时不明显改变其面积。


三、闭运算

#include 
#include 
#include 

using namespace cv;
using namespace std;

Mat src, dst;       //定义输入图像和输出图像(两个全局变量)
Mat kernel;         // 定义结构元
int r=1;             //定义结构元半径
int r_max=20;		   //定义结构元最大半径
string window = "开操作";

void callBack_func(int, void*);

int main( )
{
	src = imread("test10.png");
	if (src.empty())
	{
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("原图:", CV_WINDOW_AUTOSIZE);
	imshow("原图:", src);
	namedWindow(window, CV_WINDOW_AUTOSIZE);
	createTrackbar("半径", window, &r,r_max, callBack_func);
	callBack_func(0,0);
	waitKey(0);
	return 0;
}

void callBack_func(int ,void *)
{
	Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1));  //创建结构元
	morphologyEx(src, dst, MORPH_CLOSE, kernel, Point(-1, -1), 1);     // 形态学处理--闭运算
	imshow(window,dst);               // 显示形态学处理后的效果
}

运行程序,我们先看下原图:

OpenCV学习笔记(七)——高级形态学操作_第7张图片

然后再看不同结构元半径下,闭操作对图像的影响:

OpenCV学习笔记(七)——高级形态学操作_第8张图片

OpenCV学习笔记(七)——高级形态学操作_第9张图片

OpenCV学习笔记(七)——高级形态学操作_第10张图片

OpenCV学习笔记(七)——高级形态学操作_第11张图片

很明显,当结构元半径到达4时,白色矩阵方块内部的小洞就彻底消失了,而白色小斑点依然没有变化。因此,闭运算能排除小型黑洞(黑色区域)。

四、形态学梯度

    形态学梯度就是指膨胀结果减去腐蚀结果。因为膨胀是取领域内的最大值,从而增大亮度高的区域的面积(在二值图中,就是增大白色区域的面积);而腐蚀是取邻域内的最小值,从而减小亮度高的区域的面积,所以,形态学梯度得到的就是图像中物体的边界。

接下来我们来编写演示代码:

#include 
#include 
#include 

using namespace cv;
using namespace std;

Mat src, dst;       //定义输入图像和输出图像(两个全局变量)
Mat kernel;         // 定义结构元
int r=1;             //定义结构元半径
int r_max=20;		   //定义结构元最大半径
string window = "开操作";

void callBack_func(int, void*);

int main( )
{
	src = imread("test9.png");
	if (src.empty())
	{
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("原图:", CV_WINDOW_AUTOSIZE);
	imshow("原图:", src);
	namedWindow(window, CV_WINDOW_AUTOSIZE);
	createTrackbar("半径", window, &r,r_max, callBack_func);
	callBack_func(0,0);
	waitKey(0);
	return 0;
}

void callBack_func(int ,void *)
{
	Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1));  //创建结构元
	morphologyEx(src, dst, MORPH_GRADIENT, kernel, Point(-1, -1), 1);      // 形态学处理--形态学梯度
	imshow(window,dst);               // 显示形态学处理后的效果
}

运行程序,我们先看下原图:

OpenCV学习笔记(七)——高级形态学操作_第12张图片

然后再看下不同结构元半径下的形态学梯度处理后的效果:

OpenCV学习笔记(七)——高级形态学操作_第13张图片

OpenCV学习笔记(七)——高级形态学操作_第14张图片

OpenCV学习笔记(七)——高级形态学操作_第15张图片

显然,在图像经过阈值处理后,我们可以用形态学梯度来提取物体边缘部分。


五、顶帽

       顶帽变换的定义是原图像减去开运算结果,开运算可以消除暗背景下的较亮区域,那么如果用原图减去开运算结果就可以得到原图中灰度较亮的区域,所以又称白顶帽变换。

      我们编写演示代码如下:

#include 

using namespace cv;
using namespace std;

Mat src, dst,dst2;       //定义输入图像和输出图像(三个全局变量)
Mat kernel;         // 定义结构元
int r=1;             //定义结构元半径
int r_max=20;		   //定义结构元最大半径
string window = "开运算";

void callBack_func(int, void*);

int main( )
{
	src = imread("test9.png");
	if (src.empty())
	{
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("原图:", CV_WINDOW_AUTOSIZE);
	imshow("原图:", src);
	namedWindow(window, CV_WINDOW_AUTOSIZE);
	createTrackbar("半径", window, &r,r_max, callBack_func);
	callBack_func(0,0);
	waitKey(0);
	return 0;
}

void callBack_func(int ,void *)
{
	Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1));  //创建结构元
	morphologyEx(src, dst, MORPH_OPEN, kernel, Point(-1, -1), 1);      // 形态学处理--开运算
	imshow(window,dst);               // 显示形态学处理后的效果
	morphologyEx(src, dst2, MORPH_TOPHAT, kernel, Point(-1, -1), 1);      // 形态学处理--开帽变换
	imshow("顶帽", dst2);               // 显示开帽变换后的效果
}

运行程序,当结构元半径为1时:

OpenCV学习笔记(七)——高级形态学操作_第16张图片

当结构元半径为3时:

OpenCV学习笔记(七)——高级形态学操作_第17张图片

当结构元半径为4时:

OpenCV学习笔记(七)——高级形态学操作_第18张图片


六、底帽变换

       底帽变换的定义是图像减去闭运算结果,闭运算可以删除亮度较高背景下的较暗区域,那么用原图减去闭运算结果就可以得到原图像中灰度较暗的区域,所以又称黑底帽变换。

我们编写演示代码:

#include 
#include 
#include 

using namespace cv;
using namespace std;

Mat src, dst,dst2;       //定义输入图像和输出图像(三个全局变量)
Mat kernel;         // 定义结构元
int r=1;             //定义结构元半径
int r_max=20;		   //定义结构元最大半径
string window = "闭运算";

void callBack_func(int, void*);

int main( )
{
	src = imread("test10.png");
	if (src.empty())
	{
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("原图:", CV_WINDOW_AUTOSIZE);
	imshow("原图:", src);
	namedWindow(window, CV_WINDOW_AUTOSIZE);
	createTrackbar("半径", window, &r,r_max, callBack_func);
	callBack_func(0,0);
	waitKey(0);
	return 0;
}

void callBack_func(int ,void *)
{
	Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1));     //创建结构元
	morphologyEx(src, dst, MORPH_CLOSE, kernel, Point(-1, -1), 1);           // 形态学处理--闭运算
	imshow(window,dst);                  // 显示形态学处理后的效果
	morphologyEx(src, dst2, MORPH_BLACKHAT, kernel, Point(-1, -1), 1);       // 形态学处理--底帽变换
	imshow("底帽", dst2);               // 显示底帽变换后的效果
}

运行程序,我们可以看到:

OpenCV学习笔记(七)——高级形态学操作_第19张图片

改变结构元的半径:

OpenCV学习笔记(七)——高级形态学操作_第20张图片

显然那个小洞被找出来了。


七、总结

        形态学处理往往是在阈值处理以后才进行的,因为当图像被转化为二值图以后,这个时候只有黑白两者颜色,然后再进行形态学处理是效果最好的。


你可能感兴趣的:(OpenCV)