二值图像处理开运算与闭运算

举报
Gere 发表于 2022/08/07 19:23:53 2022/08/07
【摘要】 上一篇介绍了形态学的基本操作膨胀和腐蚀,我们这一篇将利用膨胀和腐蚀操作实现对图像更高级的形态学操作,而这些都是建立在膨胀和腐蚀操作基础之上。首先形态学的主要用途是获取物体拓扑和结果信息,它通过物体和结构元素的某些运算,得到物体更本质的形态,在图像处理中的主要应用有:(1). 利用形态学的基本运算对图像进行观察和处理,从而达到改善图像质量的目的(2). 描述和定义图像的各种几何参数和特征如面积...

上一篇介绍了形态学的基本操作膨胀和腐蚀,我们这一篇将利用膨胀和腐蚀操作实现对图像更高级的形态学操作,而这些都是建立在膨胀和腐蚀操作基础之上。
首先形态学的主要用途是获取物体拓扑和结果信息,它通过物体和结构元素的某些运算,得到物体更本质的形态,在图像处理中的主要应用有:
(1). 利用形态学的基本运算对图像进行观察和处理,从而达到改善图像质量的目的
(2). 描述和定义图像的各种几何参数和特征如面积、周长、连通、颗粒度、骨架和方向性
我们通过腐蚀和膨胀两种基本的形态学操作实现开运算、闭运算、形态梯度、顶帽、黑帽五种形态学操作。
1.开运算(Opening)
开运算是通过先对图像腐蚀再膨胀实现,其原理表达式如下:

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

能够排除小团块物体(假设物体较背景明亮),开运算的结果删除了不能包含结构元素的对象区域,平滑了对象的轮廓,断开了狭窄的连接,去掉了细小的突出部分,如下图所示:左图是原图像,右图是采用开运算转换之后的结果图,可以发现字母拐弯处的白色空间消失。
这里写图片描述

2.闭运算(Closing)
闭运算在数学上是先膨胀再腐蚀的结果,其原理表达式如下:

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

能够排除小型黑洞(黑色区域),能够平滑对象的轮廓,但是与开运算不同的是闭运算一般会将狭窄的缺口连接起来形成细长的弯口,并填充比结构元素小的洞。
如下图所示:
这里写图片描述

3.形态梯度(Morphological Gradient)
形态梯度是膨胀图与腐蚀图之差,其操作原理表达式如下

dst=morph(src,element)=dilate(src,element)-erode(src,element)

形态梯度操作能够保留物体的边缘轮廓,如下图所示:
这里写图片描述

4.顶帽(Top Hat)
顶帽操作是原图像与开运算结果图之差,其原理表达式如下:

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

开运算的结果是放大了裂缝或局部降低亮度的区域,因此从原图中减去开运算后的图得到的效果图能够突出比原图轮廓周围的区域更明亮的区域,且这一操作与选择的核的大小有关。
顶帽操作往往用来分离比邻近点亮一些的板块,在一幅图像具有大幅背景而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。如下图所示:
这里写图片描述

5.黑帽(Black Hat)
黑帽运算是闭运算结果图与原图像之差,其原理表达式如下:

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

黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,这一操作也与选择的核尺寸有关。所以黑帽运算用来分离比邻近点暗一些的斑块,效果图有着非常完美的轮廓。如下图所示:
这里写图片描述

opencv中提供了形态学操作函数morphologyEx()来实现开运算、闭运算、形态学梯度、顶帽、黑帽等五种相对高级的操作,也可以实现膨胀核腐蚀两种基本的形态学操作。其原型如下:

void cv::morphologyEx   (InputArray src,
        OutputArray dst,
        int op,
        InputArray kernel,
        Point anchor = Point(-1,-1),
        int iterations = 1,
        int borderType = BORDER_CONSTANT,
        const Scalar & borderValue = morphologyDefaultBorderValue() 
    )   

参数解释:
. InputArray src: 输入图像,可以是Mat类型,对于图像通道数无要求,但图像深度必须是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F
. OutPutArray dst: 目标图像,与原图像尺寸核类型相同
. int op: 形态学运算的类型,可以通过MorphTypes查看,如下所示:
标识符 | 运算类型
MORPH_OPEN: 开运算
MORPH_CLOSE :闭运算
MORPH_GRADIENT: 形态学梯度
MORPH_TOPHAT:顶帽运算
MORPH_BLACKHAT: 黑帽运算
MORPH_ERODE :腐蚀运算
MORPH_DILATE :膨胀运算
MORPH_HITMISS: 击中击不中运算(只支持CV_8UC1类型的二值图像)

. InputArray kernel: 形态学运算的内核,如果是Mat()则表示的是参考点位于内核中心3x3的核,前面也提到一般使用前需要定义一个Mat变量结合getStructuringElement()函数使用,getStructuringElement会返回指定形状和尺寸的结构元素,这里再重申一下getStructuringElement的参数,其函数原型如下:

Mat cv::getStructuringElement   (   int     shape,
        Size    ksize,
        Point   anchor = Point(-1,-1) 
    ) 

参数解释:
. int shape: kernel的形状,由cv::MorphShapes指定,如下:
这里写图片描述
分别是矩形(MORPH_RECT)、交叉形(MORPH_CROSS)、椭圆形(MORPH_ELLIPSE)
. Size ksize: kernel的尺寸
. Point anchor = Point(-1, -1): 锚点位置

. Point anchor=Point(-1, -1): 锚点位置
. int iterations=1: 迭代使用函数的次数,默认值为1
. int borderType=BORDER_CONSTANT: 用于推断图像外部像素的某种边界模式,有默认值BORDER_CONSTANT
. const Scalar & borderValue=morphologyDefaultBorderValue(): 当边界为常数时的边界值,可以通过createMorphologyFilter() 查看更多细节。

这些形态学操作都是可执行就地操作(in-place),对于多通道图像,每个图像通道进行单独操作。

示例代码如下:

/*
 *本程序将会产生5副目标图片与原图像进行对比
 *每幅图像采用的模板Type和Size均可通过轨迹条进行调节
 *如有兴趣也可尝试使用轨迹条改变形态学的运算类型可大幅减少代码量
*/

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

//定义全局变量
Mat g_srcImage;
Mat g_dstImageOpen, g_dstImageClose, g_dstImageGradient;
Mat g_dstImageTopHat, g_dstImageBlackHat;

//定义两个轨迹条参数
const int kernelTypeMaxValue = 2;
const int kernelSizeMaxValue = 20;
int kernelTypeValue = 1;
int kernelSizeValue = 10;

//定义模板类型声明函数
int kernelTypeFun(int kernelTypeValue);

//定义两个回调函数
void openOperation(int, void*);
void closeOperation(int, void*);
void gradientOperation(int, void*);
void topHatOperation(int, void*);
void blackHatOperation(int, void*);

int main()
{
    g_srcImage = imread("baboon.jpg");

    //判断图像是否加载成功
    if(!g_srcImage.data)
    {
        cout << "图像加载失败!" << endl;
        return -1;
    }
    else
        cout << "图像加载成功!" << endl << endl;

    namedWindow("原图像", WINDOW_AUTOSIZE);
    imshow("原图像", g_srcImage);

    //定义轨迹条参数
    char kernelTypeName[20];
    char kernelSizeName[20];

    sprintf(kernelTypeName, "模板类型 %d\n 0-Rect 1-Cross 2-Ellipse", kernelTypeMaxValue);
    sprintf(kernelSizeName, "模板尺寸 %d", kernelSizeMaxValue);

    namedWindow("开运算", WINDOW_AUTOSIZE);
    namedWindow("闭运算", WINDOW_AUTOSIZE);
    namedWindow("形态学梯度", WINDOW_AUTOSIZE);
    namedWindow("顶帽", WINDOW_AUTOSIZE);
    namedWindow("黑帽", WINDOW_AUTOSIZE);

    //创建两个轨迹条
    createTrackbar(kernelTypeName, "开运算", &kernelTypeValue, kernelTypeMaxValue, openOperation);
    createTrackbar(kernelSizeName, "开运算", &kernelSizeValue, kernelSizeMaxValue, openOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);

    createTrackbar(kernelTypeName, "闭运算", &kernelTypeValue, kernelTypeMaxValue, closeOperation);
    createTrackbar(kernelSizeName, "闭运算", &kernelSizeValue, kernelSizeMaxValue, closeOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);

    createTrackbar(kernelTypeName, "形态学梯度", &kernelTypeValue, kernelTypeMaxValue, gradientOperation);
    createTrackbar(kernelSizeName, "形态学梯度", &kernelSizeValue, kernelSizeMaxValue, gradientOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);

    createTrackbar(kernelTypeName, "顶帽", &kernelTypeValue, kernelTypeMaxValue, topHatOperation);
    createTrackbar(kernelSizeName, "顶帽", &kernelSizeValue, kernelSizeMaxValue, topHatOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);

    createTrackbar(kernelTypeName, "黑帽", &kernelTypeValue, kernelTypeMaxValue, blackHatOperation);
    createTrackbar(kernelSizeName, "黑帽", &kernelSizeValue, kernelSizeMaxValue, blackHatOperation);
    openOperation(kernelTypeValue, 0);
    openOperation(kernelSizeValue, 0);



    waitKey(0);

    return 0;
}

//形态学操作模板类型判断
int kernelTypeFun(int kernelTypeValue)
{
    int kernel_type;
    if(kernelTypeValue == 0)
        kernel_type = MORPH_RECT;
    else if(kernelTypeValue == 1)
        kernel_type = MORPH_CROSS;
    else
        kernel_type = MORPH_ELLIPSE;

    return kernel_type;
}

//开运算
void openOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageOpen, MORPH_OPEN, element);
    imshow("开运算", g_dstImageOpen);
}

//闭运算
void closeOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageClose, MORPH_CLOSE, element);
    imshow("闭运算", g_dstImageClose);
}

//形态学梯度
void gradientOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageGradient, MORPH_GRADIENT, element);
    imshow("形态学梯度", g_dstImageGradient);
}

//顶帽
void topHatOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageTopHat, MORPH_TOPHAT, element);
    imshow("顶帽", g_dstImageTopHat);
}

//黑帽
void blackHatOperation(int, void*)
{
    int kernel_type;
    kernel_type = kernelTypeFun(kernelTypeValue);

    Mat element = getStructuringElement(kernel_type, Size(kernelSizeValue * 2 + 1,
                    kernelSizeValue * 2 + 1));
    morphologyEx(g_srcImage, g_dstImageBlackHat, MORPH_BLACKHAT, element);
    imshow("黑帽", g_dstImageBlackHat);
}

程序运行结果如下:
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。