.. _Morphology_2: 更多形态学变换 ********************************* 目标 ===== 本文档尝试解答如下问题: .. container:: enumeratevisibleitemswithsquare * 如何使用OpenCV函数 :morphology_ex:`morphologyEx <>` 进行形态学操作: + 开运算 (Opening) + 闭运算 (Closing) + 形态梯度 (Morphological Gradient) + 顶帽 (Top Hat) + 黑帽(Black Hat) 原理 ======= .. note:: 以下内容来自于Bradski和Kaehler的大作 **Learning OpenCV** 。 前一节我们讨论了两种最基本的形态学操作: .. container:: enumeratevisibleitemswithsquare * 腐蚀 (Erosion) * 膨胀 (Dilation) 运用这两个基本操作,我们可以实现更高级的形态学变换。这篇文档将会简要介绍OpenCV提供的5种高级形态学操作: 开运算 (Opening) -------------------- * 开运算是通过先对图像腐蚀再膨胀实现的。 .. math:: dst = open( src, element) = dilate( erode( src, element ) ) * 能够排除小团块物体(假设物体较背景明亮) * 请看下面,左图是原图像,右图是采用开运算转换之后的结果图。 观察发现字母拐弯处的白色空间消失。 .. image:: images/Morphology_2_Tutorial_Theory_Opening.png :alt: Opening :align: center 闭运算(Closing) -------------------- * 闭运算是通过先对图像膨胀再腐蚀实现的。 .. math:: dst = close( src, element ) = erode( dilate( src, element ) ) * 能够排除小型黑洞(黑色区域)。 .. image:: images/Morphology_2_Tutorial_Theory_Closing.png :alt: Closing example :align: center 形态梯度(Morphological Gradient) -------------------------------------------- * 膨胀图与腐蚀图之差 .. math:: dst = morph_{grad}( src, element ) = dilate( src, element ) - erode( src, element ) * 能够保留物体的边缘轮廓,如下所示: .. image:: images/Morphology_2_Tutorial_Theory_Gradient.png :alt: Gradient :align: center 顶帽(Top Hat) -------------------- * 原图像与开运算结果图之差 .. math:: dst = tophat( src, element ) = src - open( src, element ) .. image:: images/Morphology_2_Tutorial_Theory_TopHat.png :alt: Top Hat :align: center 黑帽(Black Hat) -------------------- * 闭运算结果图与原图像之差 .. math:: dst = blackhat( src, element ) = close( src, element ) - src .. image:: images/Morphology_2_Tutorial_Theory_BlackHat.png :alt: Black Hat :align: center 源码 ====== 下面是本教程的源码, 你也可以从 `这里 `_ 下载。 .. code-block:: cpp #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include #include using namespace cv; /// 全局变量 Mat src, dst; int morph_elem = 0; int morph_size = 0; int morph_operator = 0; int const max_operator = 4; int const max_elem = 2; int const max_kernel_size = 21; char* window_name = "Morphology Transformations Demo"; /** 回调函数申明 */ void Morphology_Operations( int, void* ); /** @函数 main */ int main( int argc, char** argv ) { /// 装载图像 src = imread( argv[1] ); if( !src.data ) { return -1; } /// 创建显示窗口 namedWindow( window_name, CV_WINDOW_AUTOSIZE ); /// 创建选择具体操作的 trackbar createTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations ); /// 创建选择内核形状的 trackbar createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name, &morph_elem, max_elem, Morphology_Operations ); /// 创建选择内核大小的 trackbar createTrackbar( "Kernel size:\n 2n +1", window_name, &morph_size, max_kernel_size, Morphology_Operations ); /// 启动使用默认值 Morphology_Operations( 0, 0 ); waitKey(0); return 0; } /** * @函数 Morphology_Operations */ void Morphology_Operations( int, void* ) { // 由于 MORPH_X的取值范围是: 2,3,4,5 和 6 int operation = morph_operator + 2; Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); /// 运行指定形态学操作 morphologyEx( src, dst, operation, element ); imshow( window_name, dst ); } 解释 ============= #. 看一下程序的总体流程: * 装载图像 * 创建显示形态学操作的窗口 * 创建3个trackbar获取用户参数: * 第一个trackbar **"Operator"** 返回用户选择的形态学操作类型 (**morph_operator**). .. code-block:: cpp createTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations ); * 第二个trackbar **"Element"** 返回 **morph_elem**, 指定内核形状: .. code-block:: cpp createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name, &morph_elem, max_elem, Morphology_Operations ); * 第三个trackbar **"Kernel Size"** 返回内核大小(**morph_size**) .. code-block:: cpp createTrackbar( "Kernel size:\n 2n +1", window_name, &morph_size, max_kernel_size, Morphology_Operations ); * 每当任一标尺被移动, 用户函数 **Morphology_Operations** 就会被调用,该函数获取trackbar的当前值运行指定操作并更新显示结果图像。 .. code-block:: cpp /** * @函数 Morphology_Operations */ void Morphology_Operations( int, void* ) { // 由于 MORPH_X的取值范围是: 2,3,4,5 和 6 int operation = morph_operator + 2; Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); /// 运行指定形态学操作 morphologyEx( src, dst, operation, element ); imshow( window_name, dst ); } 运行形态学操作的核心函数是 :morphology_ex:`morphologyEx <>` 。在本例中,我们使用了4个参数(其余使用默认值): * **src** : 原 (输入) 图像 * **dst**: 输出图像 * **operation**: 需要运行的形态学操作。 我们有5个选项: + *Opening*: MORPH_OPEN : 2 + *Closing*: MORPH_CLOSE: 3 + *Gradient*: MORPH_GRADIENT: 4 + *Top Hat*: MORPH_TOPHAT: 5 + *Black Hat*: MORPH_BLACKHAT: 6 你可以看到, 它们的取值范围是 <2-6>, 因此我们要将从tracker获取的值增加(+2): .. code-block:: cpp int operation = morph_operator + 2; * **element**: 内核,可以使用函数:get_structuring_element:`getStructuringElement <>` 自定义。 结果 ======== * 在编译上面的代码之后, 我们可以运行结果,将图片路径输入。这里使用图像: **baboon.png**: .. image:: images/Morphology_2_Tutorial_Original_Image.jpg :alt: Morphology 2: Original image :align: center * 这里是显示窗口的两个截图。第一幅图显示了使用交错内核和 **开运算** 之后的结果, 第二幅图显示了使用椭圆内核和 **黑帽** 之后的结果。 .. image:: images/Morphology_2_Tutorial_Cover.jpg :alt: Morphology 2: Result sample :align: center 翻译者 ================= niesu@ `OpenCV中文网站 `_