OpenCV中的improc组件形态学滤波——开运算,闭运算,形态学梯度,顶帽,黑帽(17)

形态滤波:开运算,闭运算,形态学梯度,顶帽,黑帽

OpenCV中的morphologyEx函数,通过运用这两个形态学基本操作,实现了更高极的形态学变换,如开闭运算,形态学梯度,顶帽,黑帽等。

1.1 开运算

开运算(Opening Operation),就是先腐蚀后膨胀的过程
其数学表达公式为:dst = open(src, element) = dilate(erode(src, element))
作用:可以用来消除小物体在纤细点处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积。

1.2 闭运算

闭运算(Closing Operation),就是先膨胀后腐蚀的过程
其表达式为dst = close(src, element) = erode(dilate(src, element));
作用:闭运算能够排除小型黑洞(黑色区域)。

1.3 形态学梯度

形态学梯度(Morphological Gradient)是膨胀图与腐蚀图之差
表达式:dst = morph - grad(src, element) = dilate(src, element) - erode(src, element)
作用:对二值图进行这一操作可以将团块(blob)的边缘突出来,可用用来保留物体的边缘轮廓。

1.4 顶帽

顶帽运算(Top Hat) 又称为“礼帽”运算,是 原图像与开运算的结果图之差
表达式:dst = tophat(src, element) = src - open(src, element)
开运算带来的结果是放大了裂缝或者局部低亮度的区域。因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这个操作与选择的核的大小相关。
作用:顶帽运算通常用来分离比邻近点亮一些的斑块。在一幅图像中具有大幅背景,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

1.5 黑帽

黑帽(Black Hat)运算是闭运算的结果图与原图的差
表达式: dst = blackhat(src, element) = close(src , element) - src
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这操作和核的大小相关。
作用:黑猫运算用来分离比邻近点更暗的一些斑点,效果图有非常完美的轮廓

1.6 形态学滤波OpenCV源码分析

morphologyEx函数利用膨胀和腐蚀技术来执行更加高级的形态学变换,如开闭运算,形态学梯度,顶帽,黑帽等,所以我们来看看morphologyEx函数的源码;

void morphologyEx( InputArray _src, OutputArray _dst, int op,
                       InputArray _kernel, Point anchor, int iterations,
                       int borderType, const Scalar& borderValue )
{
    CV_INSTRUMENT_REGION();

    CV_Assert(!_src.empty());

    Mat kernel = _kernel.getMat();
    if (kernel.empty())
    {
        kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(1,1));
    }
#ifdef HAVE_OPENCL
    Size ksize = kernel.size();
    anchor = normalizeAnchor(anchor, ksize);

    CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
        anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 &&
        borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(),
        ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue))
#endif

    Mat src = _src.getMat(), temp;
    _dst.create(src.size(), src.type());
    Mat dst = _dst.getMat();

#if !IPP_DISABLE_MORPH_ADV
    //CV_IPP_RUN_FAST(ipp_morphologyEx(op, src, dst, kernel, anchor, iterations, borderType, borderValue));
#endif

    switch( op )
    {
    case MORPH_ERODE:
        erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case MORPH_DILATE:
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case MORPH_OPEN:
        erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
        dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case MORPH_CLOSE:
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case MORPH_GRADIENT:
        erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        dst -= temp;
        break;
    case MORPH_TOPHAT:
        if( src.data != dst.data )
            temp = dst;
        erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
        dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue );
        dst = src - temp;
        break;
    case MORPH_BLACKHAT:
        if( src.data != dst.data )
            temp = dst;
        dilate( src, temp, kernel, anchor, iterations, borderType, borderValue );
        erode( temp, temp, kernel, anchor, iterations, borderType, borderValue );
        dst = temp - src;
        break;
    case MORPH_HITMISS:
        CV_Assert(src.type() == CV_8UC1);
        if(countNonZero(kernel) <=0)
        {
            src.copyTo(dst);
            break;
        }
        {
            Mat k1, k2, e1, e2;
            k1 = (kernel == 1);
            k2 = (kernel == -1);

            if (countNonZero(k1) <= 0)
                e1 = Mat(src.size(), src.type(), Scalar(255));
            else
                erode(src, e1, k1, anchor, iterations, borderType, borderValue);

            if (countNonZero(k2) <= 0)
                e2 = Mat(src.size(), src.type(), Scalar(255));
            else
            {
                Mat src_complement;
                bitwise_not(src, src_complement);
                erode(src_complement, e2, k2, anchor, iterations, borderType, borderValue);
            }
            dst = e1 & e2;
        }
        break;
    default:
        CV_Error( CV_StsBadArg, "unknown morphological operation" );
    }
}

函数原型`void 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());

  • 参数一:src输入图像,图像位深应该为:CV_8U, CV_16U, CV_16S, CV_32F , CV_64F其一;
  • 参数二:输出图像,和原图像同尺寸同类型;
  • 参数三:op,表示形态学运算的类型;
标志符含义
MORPH_OPEN开运算
MORPH_CLOSE闭运算
MORPH_GRADIENT形态学梯度
MORPH_TOPHAT顶帽
MORPH_BLACKHAT黑帽
MORPH_ERODE腐蚀
MORPH_DILATE膨胀
  • 参数四:kernel,形态学运算的内核。若为NULL, 表示参考点位于中心3x3的核。一般配合getStructurngElement这个函数使用。该函数会返回指定形状和尺寸的结果元素(内核矩阵)。
    之后在调用erode,dilate和morphologyEx函数时,由kernel参数填保存getStructuringElement返回值的Mat类型变量。

  • 参数五:anchor,锚的位置,默认值(-1, -1)表示锚位于中心;

  • 参数六:迭代使用函数的次数;

  • 参数七:用于推断图像外部 像素的某种边界模式;

  • 参数八:borderValue,当 边界为常数时,默认值morphologyDefaultBorderValu(),一般不用管;

1.7 综合实例:形态学滤波

共有四个显示窗口,原始图像一个,开/闭运算一个,腐蚀/膨胀一个,顶帽/黑帽一个。他们分别使用滑动条来控制得到的形态学效果,且迭代为10的时候为中间点。另外,可以通过键盘1 ,2, 3以及空格来调节成不同的元素结果(矩形,椭圆,十字形)。
实例代码:

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace std;
using namespace cv;

Mat g_srcImage, g_dstImage;
int g_nElementShape = MORPH_RECT;  //元素结构形状

int g_nOpenCloseNume = 0;
int g_nMaxIteratiorNum = 10;
int g_nEnrodeDilateNum = 0;
int g_nTopBlackHatNum = 0;


void on_OpenClose(int , void*);
void on_ErodeDilate(int , void*);
void on_TopBlackHat(int, void*);

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

    namedWindow("原图");
    namedWindow("腐蚀/膨胀");
    namedWindow("开/闭运算");
    namedWindow("顶帽/黑帽");

    imshow("原图", g_srcImage);

    g_nOpenCloseNume = 9;
    g_nEnrodeDilateNum = 9;
    g_nTopBlackHatNum = 2;
    createTrackbar("迭代值", "开/闭运算", &g_nOpenCloseNume, g_nMaxIteratiorNum * 2 + 1, on_OpenClose);
    createTrackbar("迭代值", "腐蚀/膨胀", &g_nEnrodeDilateNum, g_nMaxIteratiorNum * 2 + 1, on_ErodeDilate);
    createTrackbar("迭代值", "顶帽/黑帽", &g_nTopBlackHatNum, g_nMaxIteratiorNum * 2 + 1, on_TopBlackHat);

    while(1) {
        int c;
        on_OpenClose(g_nOpenCloseNume, 0);
        on_ErodeDilate(g_nEnrodeDilateNum, 0);
        on_TopBlackHat(g_nTopBlackHatNum, 0);

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

        //按下q或esc退出
        if((char) c ==  'q' || (char) c == 27) break;
        //按下1, 使用椭圆结构
        if((char) c == 49) 
            g_nElementShape = MORPH_ELLIPSE;
        else if((char) c == 50)
            g_nElementShape = MORPH_RECT;
        else if ((char) c == 51)
            g_nElementShape = MORPH_CROSS;

        //按下空格,在矩形,椭圆,十字形结构元素中循环
        else if((char) c == ' ' )
            g_nElementShape = (g_nElementShape + 1) % 3;
    }
    return 0;

}

void on_OpenClose(int , void*)
{
    //偏移量的的定义
    int offset = g_nOpenCloseNume - g_nMaxIteratiorNum;  // 偏移量
    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) {
        morphologyEx(g_srcImage, g_dstImage, MORPH_OPEN, element);
    } else {
        morphologyEx(g_srcImage, g_dstImage, MORPH_CLOSE, element);
    }
    imshow("开/闭运算", g_dstImage);

}

void on_ErodeDilate(int , void*)
{

    int offset = g_nEnrodeDilateNum - g_nMaxIteratiorNum ;
    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) {
        erode(g_srcImage, g_dstImage, element);
    } else {
        dilate(g_srcImage, g_dstImage, element);
    }
    imshow("腐蚀/膨胀", g_dstImage);

}

void on_TopBlackHat(int, void*)
{

    int offset = g_nTopBlackHatNum - g_nMaxIteratiorNum ;
    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) {
        morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT, element);
    } else {
        morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);
    }
    imshow("顶帽/黑帽", g_dstImage);


}

运行结果
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值