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


目录
腐蚀与膨胀
更多的形态学操作
腐蚀与膨胀
更多形态学操作

腐蚀与膨胀
形态学操作就是基于形状的一系列图像处理操作。
最基本的形态学操作就是腐蚀( 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 = https://www.it610.com/article/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 #includeusing 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); }


运行结果如下图:
OpenCV|OpenCV学习之路(十二) 形态学操作
文章图片

更多的形态学操作
通过腐蚀与膨胀这两个基本操作的结合,我们可以得到更多的形态学操作。主要有以下几种:
开运算(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;

黑帽运算后的效果图突出了比原图轮廓周围区域更暗的区域,且这一操作和选择的核的大小相关。
所以,黑帽运算用来分离比邻近点暗一些的斑块,效果图有着非常完美的轮廓。










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

    推荐阅读