OpenCV学习笔记(八):形态学morpholgy(2):开/闭运算,形态学梯度、顶帽/黑帽morphologyEx()

OpenCV学习笔记(八):形态学morpholgy(2):开、闭运算,形态学梯度、顶帽、黑帽:morphologyEx()

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

一、开运算(Opening Operation)

其实就是先腐蚀后膨胀的过程:
在这里插入图片描述
开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。

二、闭运算(Closing Operation)

先膨胀后腐蚀的过程:
在这里插入图片描述
闭运算能够排除小型黑洞(黑色区域),可以将团块的边缘突出出来。

三、形态学梯度(Morphological Gradient)

膨胀图与腐蚀图之差:
在这里插入图片描述
对二值图像进行这一操作可以将团块(blob)的边缘突出出来。我们可以用形态学梯度来保留物体的边缘轮廓。

四、顶帽(Top Hat)

顶帽运算(Top Hat)又常常被译为”礼帽“运算,源图与“开运算图“之差:
在这里插入图片描述
因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,所以顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取

五、黑帽(Black Hat)

为”闭运算“的结果图与原图像之差:
在这里插入图片描述
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,黑帽运算用来分离比邻近点暗一些的斑块。

六、morphologyEx函数详解:

C++: void morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArraykernel,
Pointanchor=Point(-1,-1),
intiterations=1,
intborderType=BORDER_CONSTANT,
constScalar& borderValue=morphologyDefaultBorderValue() );

第一个参数,InputArray类型的src,输入图像,即源图像,(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“)

第五个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。

getStructuringElement函数相关的调用示例代码如下

//结构元素(内核矩阵)的尺寸
int g_nStructElementSize = 3;
 
//获取自定义核
Mat element =getStructuringElement(MORPH_RECT,
       Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
       Point(g_nStructElementSize, g_nStructElementSize ));

第一个参数表示内核的形状,我们可以选择如下三种形状之一

矩形: MORPH_RECT
交叉形: MORPH_CROSS
椭圆形: MORPH_ELLIPSE

第二和第三个参数分别是内核的尺寸以及锚点的位置,对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心

六、示例代码:

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

Mat g_srcImage, g_dstImage;				//原始图和效果图
int g_nElementShape = MORPH_RECT;		//元素结构的形状

//变量接收的TrackBar位置参数
int g_nMaxIterationNum = 10;		// 最大方向值
int g_nOpenCloseNum = 0;			// 开闭/运算内核值
int g_nTopBlackHatNum = 0;			// 顶帽/黑帽内核值

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 1、载入原图
    g_srcImage = imread("F:/C++/2. OPENCV 3.1.0/TEST/综合.png");
    if( !g_srcImage.data ) { printf("Oh,no,读取srcImage错误~! \n"); return false; }

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

    // 3、创建窗口
    namedWindow("【开运算/闭运算】",1);
    namedWindow("【顶帽/黑帽】",1);

    // 4、参数赋值
    g_nOpenCloseNum=9;
    g_nTopBlackHatNum=10;

    // 5、分别为二个窗口创建滚动条
    createTrackbar("迭代值", "【开运算/闭运算】",&g_nOpenCloseNum,g_nMaxIterationNum*2+1,on_OpenClose);
    createTrackbar("迭代值", "【顶帽/黑帽】",&g_nTopBlackHatNum,g_nMaxIterationNum*2+1,on_TopBlackHat);

    // 6、轮询获取按键信息
    while(1)
    {
        //执行回调函数
        on_OpenClose(g_nOpenCloseNum, 0);
        on_TopBlackHat(g_nTopBlackHatNum,0);

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

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

    return a.exec();
}

1)开运算/闭运算 回调函数

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 )
        // 偏移量小于0 时,开运算
        morphologyEx(g_srcImage, g_dstImage, MORPH_OPEN, element);
    else if (offset == 0)
        g_srcImage.copyTo(g_dstImage);
    else
        // 偏移量大于0 时,闭运算
        morphologyEx(g_srcImage, g_dstImage, MORPH_CLOSE, element);

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

2)顶帽运算/黑帽运算 回调函数

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(-1, -1) );
    //进行操作
    if( offset < 0 )
        morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT , element);
    else if (offset == 0)
         g_srcImage.copyTo(g_dstImage);
    else
        morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);

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

结果:
MORPH_ELLIPSE
OpenCV学习笔记(八):形态学morpholgy(2):开/闭运算,形态学梯度、顶帽/黑帽morphologyEx()_第1张图片
OpenCV学习笔记(八):形态学morpholgy(2):开/闭运算,形态学梯度、顶帽/黑帽morphologyEx()_第2张图片
MORPH_RECT
OpenCV学习笔记(八):形态学morpholgy(2):开/闭运算,形态学梯度、顶帽/黑帽morphologyEx()_第3张图片
OpenCV学习笔记(八):形态学morpholgy(2):开/闭运算,形态学梯度、顶帽/黑帽morphologyEx()_第4张图片
MORPH_CROSS
OpenCV学习笔记(八):形态学morpholgy(2):开/闭运算,形态学梯度、顶帽/黑帽morphologyEx()_第5张图片
OpenCV学习笔记(八):形态学morpholgy(2):开/闭运算,形态学梯度、顶帽/黑帽morphologyEx()_第6张图片

你可能感兴趣的:(OpenCV)