opencv基础:形态学变换

363 阅读2分钟

1. 目标

在本文中,将学习如何:

  • 使用 OpenCV 函数cv::morphologyEx应用形态变换,例如:

    • 开运算 Opening
    • 闭运算 Closing
    • 形态学梯度 Morphological Gradient
    • 顶帽变换 Top Hat
    • 底帽变换 Black Hat

2. 理论

  • 笔记

    下面的解释来自Bradski 和 Kaehler的《Learning OpenCV 》一书。

在之前的文章中,介绍了两个基本的形态学操作:

  • 腐蚀
  • 膨胀

基于这两个,可以对图像进行更复杂的转换。在这里,将简要讨论 OpenCV 提供的 5 种操作:

2.1 开运算 Opening

  • 先对图像进行腐蚀然后进行膨胀。

    dst=open(src,element)=erode(dilate(src,element)) dst=open(src,element)=erode(dilate(src,element))
  • 用于移除小物体(假设物体在黑暗的前景中是明亮的)

  • 例如,看看下面的例子。左边的图像是原始图像,右边的图像是应用开放变换后的结果。我们可以观察到小点已经消失了。

    Morphology_2_Tutorial_Theory_Opening.png

2.2 闭运算 Closing

  • 先对图像的膨胀然后腐蚀。
dst=open(src,element)=dilate(erode(src,element))dst=open(src,element)=dilate(erode(src,element))
  • 用于去除孔洞(暗区)。

    Morphology_2_Tutorial_Theory_Closing.png

2.3 形态学梯度 Morphological Gradient

  • 图像膨胀和腐蚀的差。
dst=morphgrad(src,element)=dilate(src,element)erode(src,element)dst=morph_{grad}(src,element)=dilate(src,element)-erode(src,element)
  • 用于查找对象的轮廓,如下所示:

    Morphology_2_Tutorial_Theory_Gradient.png

2.4 顶帽变换 Top Hat

  • 它是输入图像与图像开运算的差。
dst=tophat(src,element)=srcopen(src,element) dst=tophat(src,element)=src-open(src,element)

Morphology_2_Tutorial_Theory_TopHat.png

2.5 底帽变换 Black Hat

  • 它是图像闭运算和输入图像之间的差
dst=blackhat(src,element)=close(src,element)src dst=blackhat(src,element)=close(src,element)-src

Morphology_2_Tutorial_Theory_BlackHat.png

3. 代码

本教程的代码如下所示。也可以在这里下载


#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
Mat src, dst;
int morph_elem = 0;
int morph_size = 0;
int morph_operator = 0;
int const max_operator = 4;
int const max_elem = 2;
int const max_kernel_size = 21;
const char* window_name = "Morphology Transformations Demo";
void Morphology_Operations( int, void* );
int main( int argc, char** argv )
{
  CommandLineParser parser( argc, argv, "{@input | baboon.jpg | input image}" );
  src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
  if (src.empty())
  {
    std::cout << "Could not open or find the image!\n" << std::endl;
    std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl;
    return EXIT_FAILURE;
  }
  namedWindow( window_name, WINDOW_AUTOSIZE ); // Create window
  createTrackbar("Operator:\n 0: Opening - 1: Closing  \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );
  createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
                  &morph_elem, max_elem,
                  Morphology_Operations );
  createTrackbar( "Kernel size:\n 2n +1", window_name,
                  &morph_size, max_kernel_size,
                  Morphology_Operations );
  Morphology_Operations( 0, 0 );
  waitKey(0);
  return 0;
}
void Morphology_Operations( int, void* )
{
  // Since MORPH_X : 2,3,4,5 and 6
  int operation = morph_operator + 2;
  Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
  morphologyEx( src, dst, operation, element );
  imshow( window_name, dst );
}

4. 代码解释

  1. 看一下 C++ 程序的一般结构:

    • 加载图像

    • 创建一个窗口来显示形态学操作的结果

    • 创建三个Trackbars供用户输入参数:

    • 第一个 trackbar Operator返回要使用的形态操作类型(morph_operator)。


 createTrackbar("Operator:\n 0: Opening - 1: Closing  \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );
  • 第二个 trackbar元素返回morph_elem,它表明我们的内核是哪种结构:
createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
                  &morph_elem, max_elem,
                  Morphology_Operations );
  • 最后的 trackbar Kernel Size返回要使用的内核的大小(morph_size
 createTrackbar( "Kernel size:\n 2n +1", window_name,
                  &morph_size, max_kernel_size,
                  Morphology_Operations );
  • 每次移动任何滑块时,都会调用用户函数Morphology_Operations来执行新的形态学操作,并且它将根据当前trackbar值更新输出图像。
void Morphology_Operations( int, void* )
{
  // Since MORPH_X : 2,3,4,5 and 6
  int operation = morph_operator + 2;
  Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
  morphologyEx( src, dst, operation, element );
  imshow( window_name, dst );
}

可以观察到执行形态变换的关键函数是cv::morphologyEx。在此示例中,使用四个参数(将其余参数保留为默认值):

  • src:源(输入)图像

  • dst : 输出图像

  • operation:要执行的形态变换类型。请注意,我们有 5 种选择:

    • 开运算:MORPH_OPEN:2
    • 闭运算:MORPH_CLOSE:3
    • 形态学梯度:MORPH_GRADIENT:4
    • 顶帽:MORPH_TOPHAT:5
    • 底帽:MORPH_BLACKHAT:6

    如您所见,值的范围是 <2-6>,这就是我们将 (+2) 加到 Trackbar 输入的值的原因:

    int operation = morph_operator + 2;
    

5. 结果

  • 编译上面的代码后,运行,将图像路径作为参数。使用图像的结果:狒狒.png

    Morphology_2_Tutorial_Original_Image.jpg

  • 这是显示窗口的两个快照。第一张图显示了使用带有跨内核的算子Opening后的输出。 第二张图片(右侧,显示了使用带有椭圆内核的Blackhat算子的结果。

    Morphology_2_Tutorial_Result.jpg

原文地址