美文网首页CV
形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑

形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑

作者: 谢小帅 | 来源:发表于2017-07-25 20:13 被阅读88次

    【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑

    一、概念

    1.1 开操作(Opening Operation)

    开操作(Opening Operation)先腐蚀后膨胀,其数学表达式如下:

    开操作

    1.2 闭操作(Closing Operation)

    闭操作(Closing Operation)先膨胀后腐蚀,其数学表达式如下:

    闭操作

    1.3 形态学梯度(Morphological Gradient)

    形态学梯度(Morphological Gradient)为膨胀图与腐蚀图之差,数学表达式如下:


    对二值图像进行这一操作可以将团块(blob)的边缘突出出来。我们可以用形态学梯度来 保留物体的边缘轮廓

    形态学梯度 形态学梯度 = 膨胀 - 腐蚀

    1.4 顶帽(Top Hat)

    顶帽运算(Top Hat)为原图像与“开运算“的结果图之差,数学表达式如下:

    开运算放大了局部低亮度区域,因此,从原图中减去开运算后的图,得到的效果图 突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。
    顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

    1.5 黑帽(Black Hat)

    黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。数学表达式为:

    顶帽 = src - 开操作 黑帽 = 闭操作 - src

    顶帽和黑帽都是做差求得的,最后显示的都是放大的区域。

    • 顶帽:放大的暗区域,即原图中的亮区域。
    • 黑帽:放大的亮区域,即原图中的暗区域。

    二、OpenCV 源码

    morph.cpp 2038
    
    void cv::morphologyEx( InputArray _src, OutputArray _dst, int op,
                           InputArray _kernel, Point anchor, int iterations,
                           int borderType, const Scalar& borderValue )
    {
        CV_INSTRUMENT_REGION()
    
        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();
    
        Mat k1, k2, e1, e2;     //only for hit and miss op
    
        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 CV_MOP_CLOSE: // 闭操作 = 先膨胀再腐蚀
            dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
            erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
            break;
        case CV_MOP_GRADIENT: // 形态学梯度 = 膨胀 - 腐蚀
            erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
            dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
            dst -= temp;
            break;
        case CV_MOP_TOPHAT: // 顶帽 = src - 开操作
            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 CV_MOP_BLACKHAT: // 黑帽 = 闭操作 - src
            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);
            k1 = (kernel == 1);
            k2 = (kernel == -1);
            if (countNonZero(k1) <= 0)
                e1 = src;
            else
                erode(src, e1, k1, anchor, iterations, borderType, borderValue);
            if (countNonZero(k2) <= 0)
                e2 = src;
            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" );
        }
    }
    

    三、实例

    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    // 窗口名称
    const string names[] = {"腐蚀", "膨胀", "开操作", "闭操作", "形态学梯度", "顶帽", "黑帽"};
    
    // 7种运算
    const int op[] = {MORPH_ERODE, MORPH_DILATE, MORPH_OPEN, MORPH_CLOSE, MORPH_GRADIENT, MORPH_TOPHAT, MORPH_BLACKHAT};
    const int opNum = 7;
    
    int main() {
    
        Mat src = imread("../pics/mo.png", 0);
    
        namedWindow("src");
        imshow("src", src);
    
        // 获得结构元
        Mat se = getStructuringElement(MORPH_RECT, Size(15, 15));
    
        // 执行7种运算
        Mat dst;
        for (int i = 0; i < opNum; ++i) {
            morphologyEx(src, dst, op[i], se);
            namedWindow(names[i]);
            imshow(names[i], dst);
            dst = Mat::zeros(dst.size(), CV_8UC3); // 清空dst
        }
    
        waitKey(0);
    }
    

    原图


    mo.png
    Size(15, 15)
    
    Size(5, 5)
    

    由于膨胀腐蚀操作的都是高亮部分,所以将原图颜色反转,可以通过观察字的变化。

    PS中图像反相


    反相之后

    原图


    mob.png
    Size(5, 5)
    

    执行 7 个操作。

    其他实验结果


    顶帽和黑帽的图看着还行,开操作的眼睛也还行,其他的都看不成了😂


    相关文章

      网友评论

        本文标题:形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑

        本文链接:https://www.haomeiwen.com/subject/vgdbkxtx.html