形态学操作 - 开操作、闭操作、顶帽、黑帽

  • 开操作:open

    • 先腐蚀后膨胀: d s t = o p e n ( s r c , e l e m e n t ) = d i l a t e ( e r o d e ( s r c , e l e m e n t ) ) dst = open(src,element) = dilate(erode(src, element)) dst=open(src,element)=dilate(erode(src,element))
    • 可以去掉小的对象,假设对象是前景色,背景是黑色:
      【OpenCV图像处理】1.11 形态学操作 - 开操作、闭操作、顶帽、黑帽_黑帽
  • 闭操作

    • 先膨胀后腐蚀(bin2): d s t = c l o s e ( s r c , e l e m e n t ) = e r o d e ( d i l a t e ( s r c , e l e m e n t ) ) dst = close(src, element) = erode(dilate(src, element)) dst=close(src,element)=erode(dilate(src,element))
    • 可以填充小的洞(file hole),假设对象是前景色,背景是黑色:下面的黑点消失了
      【OpenCV图像处理】1.11 形态学操作 - 开操作、闭操作、顶帽、黑帽_图像处理_02
  • 形态学梯度 - Morphological Gradient

    • 膨胀减去腐蚀: d s t = m o r p h g r a d ( s r c , e l e m e n t ) = d i l a t e ( s r c , e l e m e n t ) − e r o d e ( s r c , e l e m e n t ) dst = morph_{grad}(src, element) = dilate(src, element) - erode(src, element) dst=morphgrad​(src,element)=dilate(src,element)−erode(src,element)
    • 又称为基本梯度:其他还包括 内部梯度、方向梯度
      【OpenCV图像处理】1.11 形态学操作 - 开操作、闭操作、顶帽、黑帽_scala_03
  • 顶帽 - top hat

    • 顶帽 是原图像与开操作之间的差值图像
  • 黑帽

    • 黑帽是闭操作图像与源图像的差值图像
  • 相关API:morphologyEx(src, dest, CV_MOP_BLACKHAT, kernel);
    【OpenCV图像处理】1.11 形态学操作 - 开操作、闭操作、顶帽、黑帽_#include_04

#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>

using namespace std;
using namespace cv;

int main() {
    std::string path = "../fei.JPG";
    cv::Mat img = cv::imread(path, 5);

    string str_input = "input image";
    string str_output = "output image";
    
    if(img.empty())
    {
        std::cout << "open file failed" << std::endl;
        return -1;
    }
    
	//开闭、顶黑帽操作
    Mat kernel = getStructuringElement(MORPH_RECT, Size(11, 11), Point(-1, -1));
    Mat dest1, dest2, dest3,dest4;
    //CV_MOP_OPEN定义在 #include <opencv2/imgproc/types_c.h> 头文件中,需要包含进来
    morphologyEx(img, dest1, CV_MOP_OPEN, kernel);
    imshow(str_input, img);
    imshow("open image", dest1);

    morphologyEx(img, dest2, CV_MOP_CLOSE, kernel);
    imshow(str_input, img);
    imshow("close image", dest2);

    morphologyEx(img, dest3, CV_MOP_BLACKHAT, kernel);
    imshow(str_input, img);
    imshow("black hat image", dest3);

    morphologyEx(img, dest4, CV_MOP_TOPHAT, kernel);
    imshow(str_input, img);
    imshow("top hat image", dest4);

    Mat dest5, dest6, dest7;
    morphologyEx(img, dest5, CV_MOP_ERODE, kernel);
    imshow(str_input, img);
    imshow("erode image", dest5);

    morphologyEx(img, dest6, CV_MOP_DILATE, kernel);
    imshow(str_input, img);
    imshow("dilate image", dest6);

    morphologyEx(img, dest7, CV_MOP_GRADIENT, kernel);
    imshow(str_input, img);
    imshow("gradient image", dest7);
    
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
}

显示结果:

【OpenCV图像处理】1.11 形态学操作 - 开操作、闭操作、顶帽、黑帽_黑帽_05

注: 其中,CV_MOP_OPEN定义在 <opencv2/imgproc/types_c.h> 头文件中,在clion IDE中需要包含该头文件才能编译通过,可以看到该头文件中还包括以下几个枚举值,

enum {
  CV_MOP_ERODE =0,
  CV_MOP_DILATE =1,
  CV_MOP_OPEN =2,
  CV_MOP_CLOSE =3,
  CV_MOP_GRADIENT =4,
  CV_MOP_TOPHAT =5,
  CV_MOP_BLACKHAT =6
}

其中使用了CV_MOP_ERODECV_MOP_DILATE,发现和前面调用erodedilate函数的结果一致,morphologyEx调用和erode函数调用是否有区别(从图像看出来效果是一样的),有待进一步探索、学习。
【OpenCV图像处理】1.11 形态学操作 - 开操作、闭操作、顶帽、黑帽_图像处理_06

  • 更新
    • 查看了下源码,可以看到膨胀 就是直接调用erode函数,所以两个是一样的效果
    • 从性能方面看,直接调用在这里插入代码片erode的代价更小一些。
void 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();

#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" );
    }
}