【opencv 450 Image Processing】Eroding and Dilating腐蚀和膨胀

Goal

在本教程中,您将学习如何:

应用两个非常常见的形态算子:侵蚀和膨胀。 为此,您将使用以下 OpenCV 函数:

  • cv::erode  腐蚀
  • cv::dilate  膨胀

笔记

下面的解释属于 Bradski 和 Kaehler 的《Learning OpenCV》一书。

Morphological Operations

形态学运算

  1. 简而言之:一组基于形状处理图像的操作。 形态学操作将结构元素应用于输入图像并生成输出图像。
  2. 最基本的形态学运算是:腐蚀和膨胀。 它们具有广泛的用途,即:

去除噪音

隔离单个元素并连接图像中的不同元素。

查找图像中的强度凹凸或孔洞

  • Removing noise
  • Isolation of individual elements and joining disparate elements in an image.
  • Finding of intensity bumps or holes in an image
  1. 我们将以下图为例简要说明膨胀和腐蚀:

【opencv 450 Image Processing】Eroding and Dilating腐蚀和膨胀_第1张图片

 

Dilation 膨胀

此操作包括将图像 A 与一些内核 (B) 进行卷积,该内核可以具有任何形状或大小,通常是正方形或圆形。

内核 B 有一个定义的锚点,通常是内核的中心。

当内核 B 在图像上扫描时,我们计算与 B 重叠的最大像素值,并将锚点位置的图像像素替换为该最大值。 正如您可以推断的那样,这种最大化操作会导致图像中的明亮区域“增长”(因此称为膨胀)。

膨胀运算为:

以上图为例。 应用膨胀我们可以得到:

【opencv 450 Image Processing】Eroding and Dilating腐蚀和膨胀_第2张图片

 

字母的明亮区域在背景的黑色区域周围扩大。

Erosion 腐蚀

这个操作是dilation膨胀的姐妹。 它计算给定内核区域的局部最小值。

内核 B 在图像上扫描时,我们计算与 B 重叠的最小像素值,并用该最小值替换锚点下的图像像素

腐蚀操作为:

类似于膨胀的例子,我们可以将腐蚀算子应用于原始图像(如上所示)。 您可以在下面的结果中看到图像的明亮区域变得更薄,而暗区域变得更大。

【opencv 450 Image Processing】Eroding and Dilating腐蚀和膨胀_第3张图片

 

Code

他的教程代码如下所示。 你也可以在这里下载 here https://github.com/opencv/opencv/tree/4.x/samples/cpp/tutorial_code/ImgProc/Morphology_1.cpp

/**

 * @file Morphology_1.cpp

 * @brief Erosion腐蚀 and Dilation膨胀 sample code

 * @author OpenCV team

 */



#include "opencv2/imgproc.hpp"

#include "opencv2/highgui.hpp"

#include 



using namespace cv;

using namespace std;



/// 全局变量

Mat src, erosion_dst, dilation_dst;



int erosion_elem = 0;

int erosion_size = 0;//腐蚀尺寸

int dilation_elem = 0;

int dilation_size = 0;//膨胀尺寸

int const max_elem = 2;

int const max_kernel_size = 21;



/** Function Headers */

void Erosion( int, void* );

void Dilation( int, void* );



/**

 * @function main

 */

int main( int argc, char** argv )

{

  /// 加载图像 Load an image

  CommandLineParser parser( argc, argv, "{@input | LinuxLogo.jpg | input image}" );

  src = imread( samples::findFile( parser.get( "@input" ) ), IMREAD_COLOR );//读取彩色图像

  if( src.empty() )

  {

    cout << "无法打开或找不到图像!\n" << endl;

    cout << "Usage: " << argv[0] << " " << endl;

    return -1;

  }



  /// 创建窗口

  namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );

  namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );

  moveWindow( "Dilation Demo", src.cols, 0 );//向右移动膨胀demo窗口



  /// 创建腐蚀元素滑动条,回调函数Erosion    Create Erosion Trackbar

  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",

          &erosion_elem, max_elem,

          Erosion );// 关联值 erosion_elem

  //腐蚀内核尺寸滑动条,回调函数Erosion

  createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",

          &erosion_size, max_kernel_size,

          Erosion );// 关联值 erosion_size



  /// 创建膨胀元素滑动条 回调函数Dilation    Create Dilation Trackbar

  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",

          &dilation_elem, max_elem,

          Dilation );// 关联值dilation_elem

//膨胀内核尺寸滑动条,回调函数Dilation

  createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",

          &dilation_size, max_kernel_size,

          Dilation );// 关联值dilation_size



  /// 默认开始Default start

  Erosion( 0, 0 );

  Dilation( 0, 0 );



  waitKey(0);

  return 0;

}



//![erosion]

/**

 * @function 腐蚀Erosion

 */

void Erosion( int, void* )

{

  int erosion_type = 0;//腐蚀类型 默认0

  if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }

  else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }

  else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }



  //![kernel]

  Mat element = getStructuringElement( erosion_type,

                       Size( 2*erosion_size + 1, 2*erosion_size+1 ),

                       Point( erosion_size, erosion_size ) );//腐蚀内核矩阵

  //![kernel]



  /// 应用腐蚀操作 Apply the erosion operation

  erode( src, erosion_dst, element );

  imshow( "Erosion Demo", erosion_dst );

}

//![erosion]



//![dilation]

/**

 * @function 膨胀Dilation

 */

void Dilation( int, void* )

{

  int dilation_type = 0;

  if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }

  else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }

  else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }



  Mat element = getStructuringElement( dilation_type,

                       Size( 2*dilation_size + 1, 2*dilation_size+1 ),

                       Point( dilation_size, dilation_size ) );//膨胀内核矩阵



  ///应用膨胀操作  Apply the dilation operation

  dilate( src, dilation_dst, element );

  imshow( "Dilation Demo", dilation_dst );

}

//![dilation]

Explanation

此处显示的大部分材料都是微不足道的(如果您有任何疑问,请参阅前面部分的教程)。 让我们检查一下 C++ 程序的一般结构:

int main( int argc, char** argv )

{

  CommandLineParser parser( argc, argv, "{@input | LinuxLogo.jpg | input image}" );

  src = imread( samples::findFile( parser.get( "@input" ) ), IMREAD_COLOR );

  if( src.empty() )

  {

    cout << "Could not open or find the image!\n" << endl;

    cout << "Usage: " << argv[0] << " " << endl;

    return -1;

  }

  namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );

  namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );

  moveWindow( "Dilation Demo", src.cols, 0 );

  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",

          &erosion_elem, max_elem,

          Erosion );

  createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",

          &erosion_size, max_kernel_size,

          Erosion );

  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",

          &dilation_elem, max_elem,

          Dilation );

  createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",

          &dilation_size, max_kernel_size,

          Dilation );

  Erosion( 0, 0 );

  Dilation( 0, 0 );

  waitKey(0);

  return 0;

}
  1. 加载图像(可以是 BGR 或灰度)
  2. 创建两个窗口(一个用于扩张输出,另一个用于侵蚀)
  3. 为每个操作创建一组两个 Trackbar:

第一个滑动条“元素”返回erosion_elem 或dilation_elem

第二个滑动条“内核大小”返回对应操作的侵蚀内核尺寸大小或膨胀大小。

  1. 调用一次腐蚀和膨胀来显示初始图像。

每次我们移动任何滑块时,都会调用用户的函数 Erosion 或 Dilation,它会根据当前的轨迹栏值更新输出图像。

我们来分析这两个函数:

The erosion function 腐蚀函数

void Erosion( int, void* )

{

  int erosion_type = 0;

  if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }

  else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }

  else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }

  Mat element = getStructuringElement( erosion_type,

                       Size( 2*erosion_size + 1, 2*erosion_size+1 ),

                       Point( erosion_size, erosion_size ) );//获取内核

  erode( src, erosion_dst, element );

  imshow( "Erosion Demo", erosion_dst );

}

执行腐蚀操作的函数是 cv::erode 。 正如我们所见,它接收三个参数:

src:源图像

erosion_dst:输出图像

element:这是我们将用来执行操作的内核。 如果我们不指定,则默认为简单的 3x3 矩阵。 否则,我们可以指定它的形状。 为此,我们需要使用函数 cv::getStructuringElement :

​
Mat element = getStructuringElement( erosion_type,

Size( 2*erosion_size + 1, 2*erosion_size+1 ),

Point( erosion_size, erosion_size ) );

我们可以为内核选择三种形状中的任何一种:

矩形框:MORPH_RECT

交叉:MORPH_CROSS

椭圆MORPH_ELLIPSE

然后,我们只需要指定内核的大小和锚点。 如果未指定,则假定它位于中心。

就这些。 我们准备好对我们的图像进行侵蚀。

The dilation function  膨胀函数

代码如下。 如您所见,它与侵蚀的代码片段完全相似。 在这里,我们还可以选择定义我们的内核、它的锚点和要使用的运算符的大小。

void Dilation( int, void* )

{

  int dilation_type = 0;

  if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }

  else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }

  else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }

  Mat element = getStructuringElement( dilation_type,

                       Size( 2*dilation_size + 1, 2*dilation_size+1 ),

                       Point( dilation_size, dilation_size ) );

  dilate( src, dilation_dst, element );

  imshow( "Dilation Demo", dilation_dst );

}

笔记

此外,还有更多参数允许您一次执行多个腐蚀/膨胀(迭代),还可以设置边框类型和值。 但是,我们没有在这个简单的教程中使用这些。 您可以查看参考以获取更多详细信息。

Results

编译上面的代码并使用图像作为参数执行它(或者如果使用 python,则运行脚本)。 如果您不提供图像作为参数,则将使用默认示例图像 (LinuxLogo.jpg)。

例如,使用此图像:

【opencv 450 Image Processing】Eroding and Dilating腐蚀和膨胀_第4张图片

 

我们得到下面的结果。 改变 Trackbars 中的索引自然会给出不同的输出图像。 试试看! 您甚至可以尝试添加第三个 Trackbar 来控制迭代次数。

【opencv 450 Image Processing】Eroding and Dilating腐蚀和膨胀_第5张图片

 

(取决于编程语言,输出可能略有不同或只有 1 个窗口)

你可能感兴趣的:(opencv,opencv,c++,计算机视觉)