腐蚀和膨胀是对二维图片的进行操作的形态学运算,简单来讲形态学操作就是基于形状的一系列图像处理操作,通过将结构元素作用于输入图像来产生输出图像。腐蚀(Erosion)和膨胀(Dilation)是最基本的形态学操作,他们运用广泛主要有:
. 消除噪声
. 分割(ioslate)独立的图像元素以及连接(join)相邻的元素
. 寻找图像中的明显的极大值区域或极小值区域
. 求出图像的梯度
给出图片如下:

图片背景为白色,字母为黑色。下面将根据此图形对膨胀腐蚀的原理进行说明。
1.膨胀
膨胀(dilate)就是求局部最大值的操作,从数学的角度来讲膨胀或者腐蚀就是将一幅二维图像或图像的一部分(称之为A)与一个模板也就是核(称为B)进行卷集运算的过程。
核可以是任何形状和大小,与之前介绍的模板一样,核有一个可定义的锚点。
. 膨胀操作是将图像A与任意形状的内核B(通常为正方形或者圆形)进行卷集
. 内核B中可定义的锚点通称定义为内核中心点
. 进行膨胀操作时,将内核B在图像A上进行滑动操作,将内核B覆盖的区域的最大像素值提取并提到内核B锚点位置的像素。
. 上述操作将会导致图像中的亮区开始扩展,因为白色的像素值要远远大于黑色的像素值。经膨胀运算后其图形如下

对于膨胀运算其数学表达式如下:

opencv提供了dilate函数来实现膨胀操作,其原型如下

C++: void dilate(InputArray src, OutputArray dst, 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: 输出图像,与原图像有相同的尺寸和类型。
. InpurArray kernel: 用于膨胀操作的kernel,当参数=Mat()即NULL时,kernel是一个锚点位于中心的3x3模板。可以通过getStructuringElement函数来制定kernel的形状和尺寸,其函数原型如下:

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): 锚点位置

一般在调用erode以及dilate函数之前需要定义一个Mat类型的变量来获得getStructuringElement()函数的返回值,对于锚点如果没有特殊要求使用默认值即可

. Point anchor = Point(-1, -1): 锚点位置
. int iterations=1: 迭代使用膨胀的次数
. int borderType = BORDER_CONSTANT: 用于推断图像外部像素的某种边界模式,其有默认值BORDER_CONSTANT,可以通过cv::BorderTypes查询其他的方法。
. const Scalar & borderValue = morphologyDefaultBorderValue() : 边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般不用进行设置,如果有特殊需要,可以查看 createMorphologyFilter()函数得到详细信息。

2.腐蚀
. 腐蚀在形态学操作家族里是膨胀操作的孪生姐妹,它是提取内核覆盖下的像素最小值
. 进行腐蚀操作时,将内核窗口在图像A上进行滑动,将内核B覆盖的区域最小像素值提取并代替锚点位置的像素值
. 腐蚀操作将会导致图像中像素值较低的元素进行扩展,造成黑色部分加粗,如下图所示:

腐蚀操作的数学表达式如下:

opencv提供erode来实现腐蚀操作,其原型如下:

void cv::erode  (   InputArray      src,
        OutputArray     dst,
        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: 输出图像,与输入图像尺寸类型一致。
. Input Array kernel: 用于腐蚀操作的kernel,当参数=Mat()即NULL时,kernel是一个锚点位于中心的3x3模板。可以通过getStructuringElement函数来制定kernel的形状和尺寸,具体用法请参考对膨胀的表述
. Point anchor = Point(-1, -1): 锚点位置
. int iterations = 1: 迭代腐蚀操作次数,有默认值1
. int borderType = BORDER_CONSTANT: 用于推断图像外部像素的某种边界模式,其有默认值BORDER_CONSTANT,可以通过cv::BorderTypes查询其他的方法。
. const Scalar & borderValue = morphologyDefaultBorderValue(): 边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般不用进行设置,如果有特殊需要,可以查看 createMorphologyFilter()函数得到详细信息。

示例程序:

#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_dilateImage;
Mat g_erodeImage;

//定义两个轨迹条属性
const int typeMaxValue = 2;         //膨胀腐蚀采用图形的类型
const int kernelSizeMaxVale = 21;   //采用kernel的尺寸最大值

int dilateTypeValue = 0;            //默认膨胀操作模板图像类型
int dilateSizeValue = 5;            //默认模板图像尺寸
int erodeTypeValue = 0;
int erodeSizeValue = 5;

//定义两个回调函数
void dilateFun(int, void*);         //膨胀回调函数
void erodeFun(int, void*);          //腐蚀回调函数

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

    //判断图像是否打开成功
    if(g_srcImage.empty())
    {
        cout << "图像加载失败!" << endl;
        return -1;
    }
    else
        cout << "图像加载成功!" << endl << endl;
    namedWindow("原图像", WINDOW_AUTOSIZE);
    imshow("原图像",g_srcImage);

    //定义膨胀操作窗口属性及轨迹条性质
    namedWindow("图像膨胀", WINDOW_AUTOSIZE);
    namedWindow("图像腐蚀", WINDOW_AUTOSIZE);

    char typeName[20];
    sprintf(typeName, "模板类型 %d", typeMaxValue);

    char sizeName[20];
    sprintf(sizeName, "模板尺寸 %d", kernelSizeMaxVale);

    //创建膨胀轨迹条
    createTrackbar(typeName, "图像膨胀", &dilateTypeValue, typeMaxValue, dilateFun);
    createTrackbar(sizeName, "图像膨胀", &dilateSizeValue, kernelSizeMaxVale, dilateFun);
    dilateFun(dilateTypeValue, 0);
    dilateFun(dilateSizeValue, 0);

    //创建腐蚀轨迹条
    createTrackbar(typeName, "图像腐蚀", &erodeTypeValue, typeMaxValue, erodeFun);
    createTrackbar(sizeName, "图像腐蚀", &erodeSizeValue, kernelSizeMaxVale, erodeFun);
    erodeFun(erodeTypeValue, 0);
    erodeFun(erodeSizeValue, 0);

    waitKey(0);

    return 0;
}

//膨胀回调函数
void dilateFun(int, void*)
{
    //确定模板图像类型
    int dilate_type;
    if(dilateTypeValue == 0)
        dilate_type = MORPH_RECT;
    else if (dilateTypeValue == 1)
        dilate_type = MORPH_CROSS;
    else
        dilate_type = MORPH_ELLIPSE;

    Mat element = getStructuringElement(dilate_type, Size(2*dilateSizeValue + 1, 2*dilateSizeValue + 1));
    dilate(g_srcImage, g_dilateImage, element);     //膨胀操作
    imshow("图像膨胀", g_dilateImage);

}

//腐蚀回调函数
void erodeFun(int, void*)
{
    //确定模板图像类型
    int erode_type;
    if(erodeTypeValue == 0)
        erode_type = MORPH_RECT;
    else if (erodeTypeValue == 1)
        erode_type = MORPH_CROSS;
    else
        erode_type = MORPH_ELLIPSE;

    Mat element = getStructuringElement(erode_type, Size(2*erodeSizeValue + 1, 2*erodeSizeValue + 1));
    erode(g_srcImage, g_erodeImage, element);     //腐蚀操作
    imshow("图像腐蚀", g_erodeImage);
}

运行结果如下:

更多推荐

opencv学习(二十四)之腐蚀与膨胀