OpenCV学习之路(十二) 形态学操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dashujua/article/details/82316754

目录

腐蚀与膨胀

更多的形态学操作


腐蚀与膨胀

更多形态学操作

腐蚀与膨胀

形态学操作就是基于形状的一系列图像处理操作。

最基本的形态学操作就是腐蚀( erode )和膨胀( dilate )。其主要功能如下:

消除噪声;

分割出独立的图像元素,在图像中连接相邻的元素;

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

求出图像的梯度。

1. 膨胀函数: dilate() 。函数原型如下:

void dilate(InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1, -1), int iterations = 1, int borderType = BORDER_DEFAULT, const Scalar& borderValue = morphologyDefaultBorderValue());

(1)第一个参数,InputArray 类型的 src,源图像。图像通道的数量可以是任意的,但图像深度应为 CV_8U、CV_16U、CV_16S、CV_32F、CV_64F 之一。

(2)第二个参数,OutputArray 类型的 dst,输出图像。图像的尺寸和类型应和源图像保持一致。

(3)第三个参数,InputArray 类型的 kernel,膨胀操作的核。当为 NULL 时,表示使用参考点位于中心的 3*3 的核。一般使用 getStructingElement(int shape, Size ksize, Point anchor = Point(-1, -1)) 函数来设定内核矩阵的形状和尺寸。shape 参数表示内核矩阵的形状(矩形:MORPH_RECT;交叉形:MORPH_CROSS;椭圆形:MORPH_ELLIPSE)。使用如下所示:

int kernelSize = 3;

Mat element = 
    getStructingElement(MORPH_RECT, Size(2 * kernelSize + 1, 2 * kernelSize + 1);

(4)第四个参数,Point 类型的 anchor。默认值为Point(-1, -1),位于内核的中心点。

(5)第五个参数,int 类型的 iterations,迭代使用 dilate() 函数的次数,默认为 1。

(6)第六个参数,int 类型的 borderType,用于推断图像外部像素的某种边界模式。默认值为 MORPH_DEFAULT。

(7)第七个参数,const Scalar& 类型的 borderValue,当边界为常数时的边界值,有默认值 morphologyDefaultBorderValue() ,一般不管。

 

2. 腐蚀函数:erode()。原型如下:

void erode(InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1, -1), int iterations = 1, int borderType = BORDER_DEFAULT, const Scalar& morphologyDefaultBorderValue());

(1)第一个参数:源图像。参考 dilate() 函数。

(2)第二个参数,输出图像。参考 dilate() 函数。

(3)第三个参数,腐蚀内核。参考 dilate() 函数。

(4)第四个参数,内核矩阵的锚点。参考 dilate() 函数。

(5)第五个参数,erode() 函数执行的迭代次数。参考 dilate() 函数。

(6)第六个参数,图像外部像素的边界模式。参考 dilaye() 函数。

(7)第七个参数,边界为常数时的边界值。参考 dilate() 函数。

 

腐蚀与膨胀简单示例:

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace cv;
using namespace std;

void on_Erosion(int, void*);
void on_Dilation(int, void*);

int g_erosionType = 0;
int g_dilationType = 0;
int g_erosionSize = 1;
int g_dilationSize = 1;
int g_maxErosionType = 2;
int g_maxDilationType = 2;
int g_maxErosionSize = 31;
int g_maxDilationSize = 31;

const char* erosionTypeStr = "内核类型";
const char* erosionSizeStr = "内核大小";
const char* dilationTypeStr = "内核类型";
const char* dilationSizeStr = "内核大小";

Mat srcErosionImage, dstErosionImage, srcDilationImage, dstDilationImage;

int main()
{
	srcErosionImage = imread("cat.jpg");
	srcDilationImage = imread("cat.jpg");
	imshow("原图", srcDilationImage);
	if (srcErosionImage.empty())
	{
		cout << "源图片读取错误" << endl;
	}
	if (srcDilationImage.empty())
	{
		cout << "源图片读取错误" << endl;
	}

	namedWindow("腐蚀效果图", WINDOW_AUTOSIZE);
	namedWindow("膨胀效果图", WINDOW_AUTOSIZE);

	createTrackbar(erosionTypeStr, "腐蚀效果图", &g_erosionType, g_maxErosionType, on_Erosion);
	createTrackbar(erosionSizeStr, "腐蚀效果图", &g_erosionSize, g_maxErosionSize, on_Erosion);

	createTrackbar(dilationTypeStr, "膨胀效果图", &g_dilationType, g_maxDilationType, on_Dilation);
	createTrackbar(dilationSizeStr, "膨胀效果图", &g_dilationSize, g_maxDilationSize, on_Dilation);

	on_Erosion(0, 0);
	on_Dilation(0, 0);

	waitKey(0);
	return 0;
}

void on_Erosion(int, void*)
{
	/*
	内核类型

		0: 矩形
		1: 交叉形
		2: 椭圆形
	*/
	if (g_erosionType == 0)
	{
		g_erosionType = MORPH_RECT;
	}
	else if (g_erosionType == 1)
	{
		g_erosionType = MORPH_CROSS;
	}
	else if (g_erosionType == 2)
	{
		g_erosionType = MORPH_ELLIPSE;
	}

	Mat element = getStructuringElement(g_erosionType, Size(2 * g_erosionSize + 1, 2 * g_erosionSize + 1));

	erode(srcErosionImage, dstErosionImage, element);

	imshow("腐蚀效果图", dstErosionImage);
}

void on_Dilation(int, void*)
{
	/*
	内核类型

	0: 矩形
	1: 交叉形
	2: 椭圆形
	*/
	if (g_dilationType == 0)
	{
		g_dilationType = MORPH_RECT;
	}
	else if (g_dilationType == 1)
	{
		g_dilationType = MORPH_CROSS;
	}
	else if (g_dilationType == 2)
	{
		g_dilationType = MORPH_ELLIPSE;
	}

	Mat element = getStructuringElement(g_dilationType, Size(2 * g_dilationSize + 1, 2 * g_dilationSize + 1));

	dilate(srcDilationImage, dstDilationImage, element);

	imshow("膨胀效果图", dstDilationImage);
}

运行结果如下图:

更多的形态学操作

通过腐蚀与膨胀这两个基本操作的结合,我们可以得到更多的形态学操作。主要有以下几种:

开运算(Opening )

闭运算(Closing)

形态学梯度(Morphological Gradient)

顶帽(Top Hat)

黑帽(Black Hat)

1. 开运算,其实就是先腐蚀后膨胀的过程。其数学表达式如下:

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

开运算可以用来消除小物体,在纤细点处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积。

 

2.闭运算,其实就是先膨胀后腐蚀的过程。其数学表达式如下:

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

闭运算能够排除小型黑洞(黑色区域)。

3.形态学梯度,其实就是膨胀图与腐蚀图之差。其数学表达式如下:

dst = morph-grad(src, element) = dilate(src, element)− erode(src, element);

对二值图像进行这一操作可以将团块(blob)的边缘突出出来。可以使用形态学梯度来保留物体的边缘轮廓。

4.顶帽,其实就是原图像与开运算效果图之差。数学表达式如下:

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

 开运带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图像中减去开运算的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作与选择的核的大小相关。

顶帽运算往往用来分离比邻近点亮一些的斑块。在一幅图像具有大幅的背景,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

5.黑帽,其实就是闭运算的效果图与原图像之差,其数学表达式如下:

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

黑帽运算后的效果图突出了比原图轮廓周围区域更暗的区域,且这一操作和选择的核的大小相关。

所以,黑帽运算用来分离比邻近点暗一些的斑块,效果图有着非常完美的轮廓。

猜你喜欢

转载自blog.csdn.net/dashujua/article/details/82316754
今日推荐