关闭

opencv图像的形态学操作:腐蚀与膨胀

532人阅读 评论(0) 收藏 举报
分类:

使用OpenCV提供的两种最基本的形态学操作,腐蚀与膨胀( Erosion 与 Dilation):

getStructuringElement

Returns a structuring element of the specified size and shape for morphological operations.

C++: Mat getStructuringElement(int shape, Size ksize, Point anchor=Point(-1,-1))
第一个参数:结构单元的形状。第二个参数:结构参数的尺寸。第三个参数:锚点位置(默认结构单元的中心)
Python: cv2.getStructuringElement(shape, ksize[, anchor]) → retval
C: IplConvKernel* cvCreateStructuringElementEx(int cols, int rows, int anchor_x, int anchor_y, int shape, int*values=NULL )
Parameters:
  • shape –

    Element shape that could be one of the following:

    • MORPH_RECT - a rectangular structuring element:

      E_{ij}=1

    • MORPH_ELLIPSE - an elliptic structuring element, that is, a filled ellipse inscribed into the rectangleRect(0, 0, esize.width, 0.esize.height)
    • MORPH_CROSS - a cross-shaped structuring element:

      E_{ij} =  \fork{1}{if i=\texttt{anchor.y} or j=\texttt{anchor.x}}{0}{otherwise}

    • CV_SHAPE_CUSTOM - custom structuring element (OpenCV 1.x API)
  • ksize – Size of the structuring element.
  • cols – Width of the structuring element
  • rows – Height of the structuring element
  • anchor – Anchor position within the element. The default value (-1, -1) means that the anchor is at the center. Note that only the shape of a cross-shaped element depends on the anchor position. In other cases the anchor just regulates how much the result of the morphological operation is shifted.
  • anchor_x – x-coordinate of the anchor
  • anchor_y – y-coordinate of the anchor
  • values – integer array of cols``*``rows elements that specifies the custom shape of the structuring element, when shape=CV_SHAPE_CUSTOM.

The function constructs and returns the structuring element that can be further passed to erode()dilate() ormorphologyEx() . But you can also construct an arbitrary binary mask yourself and use it as the structuring element.

Note

 

When using OpenCV 1.x C API, the created structuring element IplConvKernel* element must be released in the end using cvReleaseStructuringElement(&element).

           

dilate

Dilates an image by using a specific structuring element.

C++: void dilate(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, intborderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() )
Python: cv2.dilate(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) → dst
C: void cvDilate(const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 )
Parameters:
  • src – input image; the number of channels can be arbitrary, but the depth should be one of CV_8U,CV_16UCV_16SCV_32F` or ``CV_64F.
  • dst – output image of the same size and type as src.
  • kernel – structuring element used for dilation; if elemenat=Mat() , a 3 x 3 rectangular structuring element is used. Kernel can be created using getStructuringElement()
  • anchor – position of the anchor within the element; default value (-1, -1) means that the anchor is at the element center.
  • iterations – number of times dilation is applied.
  • borderType – pixel extrapolation method (see borderInterpolate for details).
  • borderValue – border value in case of a constant border

The function dilates the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the maximum is taken:

\texttt{dst} (x,y) =  \max _{(x',y'):  \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')

The function supports the in-place mode. Dilation can be applied several ( iterations ) times. In case of multi-channel images, each channel is processed independently.

Note

  • An example using the morphological dilate operation can be found at opencv_source_code/samples/cpp/morphology2.cpp

erode

Erodes an image by using a specific structuring element.

C++: void erode(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, intborderType=BORDER_CONSTANT, constScalar& borderValue=morphologyDefaultBorderValue() )
Python: cv2.erode(src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]]) → dst
C: void cvErode(const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1)
Parameters:
  • src – input image; the number of channels can be arbitrary, but the depth should be one of CV_8U,CV_16UCV_16SCV_32F` or ``CV_64F.
  • 输入图像
  • dst – output image of the same size and type as src.
  • 输出图像
  • kernel – structuring element used for erosion; if element=Mat() , a 3 x 3 rectangular structuring element is used. Kernel can be created using getStructuringElement().
  • 用于腐蚀操作的结构单元。如果使用Mat()则表示使用的是矩形结构单元。也可以使用getStructuringElement()函数获取。
  • anchor – position of the anchor within the element; default value (-1, -1) means that the anchor is at the element center.
  • 单元的锚点位置,默认位置是Point(-1,-1),表示这个锚点是单元的中心位置。
  • iterations – number of times erosion is applied.
  • 腐蚀操作的次数
  • borderType – pixel extrapolation method (see borderInterpolate for details).

  • borderValue – border value in case of a constant border

The function erodes the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the minimum is taken:

\texttt{dst} (x,y) =  \min _{(x',y'):  \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')

The function supports the in-place mode. Erosion can be applied several ( iterations ) times. In case of multi-channel images, each channel is processed independently.

Note

  • An example using the morphological erode operation can be found at opencv_source_code/samples/cpp/morphology2.cpp


     

createTrackbar

Creates a trackbar and attaches it to the specified window.

C++: int createTrackbar(const String& trackbarname, const String& winname, int* value, int count, TrackbarCallbackonChange=0, void* userdata=0)
1第一个参数:工具条的名字。第二个参数:存放工具条的窗口名称。第三个参数:工具条中要改变的参数。第四个参数:要改变参数的最大值。第五个参数:是这个值要作用的函数。

Python: cv2.createTrackbar(trackbarName, windowName, value, count, onChange) → None
C: int cvCreateTrackbar(const char* trackbar_name, const char* window_name, int* value, int count, CvTrackbarCallback on_change=NULL )
Parameters:
  • trackbarname – Name of the created trackbar.
  • winname – Name of the window that will be used as a parent of the created trackbar.
  • value – Optional pointer to an integer variable whose value reflects the position of the slider. Upon creation, the slider position is defined by this variable.
  • count – Maximal position of the slider. The minimal position is always 0.
  • onChange – Pointer to the function to be called every time the slider changes position. This function should be prototyped as void Foo(int,void*); , where the first parameter is the trackbar position and the second parameter is the user data (see the next parameter). If the callback is the NULL pointer, no callbacks are called, but only value is updated.
  • userdata – User data that is passed as is to the callback. It can be used to handle trackbar events without using global variables.

The function createTrackbar creates a trackbar (a slider or range control) with the specified name and range, assigns a variable value to be a position synchronized with the trackbar and specifies the callback function onChange to be called on the trackbar position change. The created trackbar is displayed in the specified window winname.

Note

 

[Qt Backend Only] winname can be empty (or NULL) if the trackbar should be attached to the control panel.

Clicking the label of each trackbar enables editing the trackbar values manually.

Note

  • An example of using the trackbar functionality can be found at opencv_source_code/samples/cpp/connected_components.cpp

形态学操作

  • 简单来讲,形态学操作就是基于形状的一系列图像处理操作。通过将结构元素作用于输入图像来产生输出图像。

  • 最基本的形态学操作有二:腐蚀与膨胀(Erosion 与 Dilation)。 他们的运用广泛:

    • 消除噪声
    • 分割(isolate)独立的图像元素,以及连接(join)相邻的元素。
    • 寻找图像中的明显的极大值区域或极小值区域。
  • 通过以下图像,我们简要来讨论一下膨胀与腐蚀操作(译者注:注意这张图像中的字母为黑色,背景为白色,而不是一般意义的背景为黑色,前景为白色):

    Original image

膨胀

  • 此操作将图像 A 与任意形状的内核 (B),通常为正方形或圆形,进行卷积。

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

  • 进行膨胀操作时,将内核 B 划过图像,将内核 B 覆盖区域的最大相素值提取,并代替锚点位置的相素。显然,这一最大化操作将会导致图像中的亮区开始”扩展” (因此有了术语膨胀 dilation )。对上图采用膨胀操作我们得到:

    Dilation result - Theory example

背景(白色)膨胀,而黑色字母缩小了。

腐蚀

  • 腐蚀在形态学操作家族里是膨胀操作的孪生姐妹。它提取的是内核覆盖下的相素最小值。

  • 进行腐蚀操作时,将内核 B 划过图像,将内核 B 覆盖区域的最小相素值提取,并代替锚点位置的相素。

  • 以与膨胀相同的图像作为样本,我们使用腐蚀操作。从下面的结果图我们看到亮区(背景)变细,而黑色区域(字母)则变大了。

    Erosion result - Theory example

源码

下面是本教程的源码, 你也可以从 here 下载。

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"


using namespace cv;

Mat src, erosion_dst, dilation_dst;

int erosion_elem = 1;
int erosion_size = 1;
int dilation_elem = 1;
int dilation_size = 1;
int const max_elem = 3;
int const max_kernel_size = 21;

/** Function Headers */
void Erosion( int, void* );
void Dilation( int, void* );

/** @function main */
int main( )
{
  /// Load 图像
  src = imread( "tupian.jpg" );

  if( !src.data )
  { return -1; }

  /// 创建显示窗口
  namedWindow( "Erosion Demo", CV_WINDOW_AUTOSIZE );
  namedWindow( "Dilation Demo", CV_WINDOW_AUTOSIZE );
  cvMoveWindow( "Dilation Demo", src.cols, 0 );

  /// 创建腐蚀 Trackbar
  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 );

  /// 创建膨胀 Trackbar
  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 );

  /// Default start
  Erosion( 1, 0 );
  Dilation( 1, 0 );

  waitKey(0);
  return 0;
}

/**  @function Erosion  */
void Erosion( int, void* )
{
  int erosion_type;
  if( erosion_elem == 1 ){ erosion_type = MORPH_RECT; }
  else if( erosion_elem == 2 ){ erosion_type = MORPH_CROSS; }
  else if( erosion_elem == 3 ) { 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 );
}

/** @function Dilation */
void Dilation( int, void* )
{
  int dilation_type;
  if( dilation_elem == 1){ dilation_type = MORPH_RECT; }
  else if( dilation_elem == 2 ){ dilation_type = MORPH_CROSS; }
  else if( dilation_elem == 3) { 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 );
}

解释

  1. 大部分代码应该不需要解释了 (如果有任何疑问,请回头参考前面的教程)。 让我们来回顾一下本程序的总体流程:

    • 装载图像 (可以是 RGB图像或者灰度图 )
    • 创建两个显示窗口 (一个用于膨胀输出,一个用于腐蚀输出)
    • 为每个操作创建两个 Trackbars:
      • 第一个 trackbar “Element” 返回 erosion_elem 或者 dilation_elem
      • 第二个 trackbar “Kernel size” 返回 erosion_size 或者 dilation_size 。
    • 每次移动标尺, 用户函数 Erosion 或者 Dilation 就会被调用,函数将根据当前的trackbar位置更新输出图像。

    让我们分析一下这两个函数:

  2. Erosion:

    /**  @function Erosion  */
    void Erosion( int, void* )
    {
      int erosion_type;
      if( erosion_elem == 1){ erosion_type = MORPH_RECT; }
      else if( erosion_elem == 2 ){ erosion_type = MORPH_CROSS; }
      else if( erosion_elem == 3 ) { 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 );
    }
    
    • 进行 腐蚀 操作的函数是 erode 。 它接受了三个参数:

      • src: 原图像

      • erosion_dst: 输出图像

      • element: 腐蚀操作的内核。 如果不指定,默认为一个简单的 3x3 矩阵。否则,我们就要明确指定它的形状,可以使用函数 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

      然后,我们还需要指定内核大小,以及 锚点 位置。不指定锚点位置,则默认锚点在内核中心位置。

    • 就这些了,我们现在可以对图像进行腐蚀操作了。

    Note

     

    OpenCV的 erode 函数还有另外的参数,其中一个参数允许你一下对图像进行多次腐蚀操作。在这个简单的文档中没有用到它,但是你可以参考OpenCV的使用手册。

  3. Dilation:

下面是膨胀的代码,你可以看到,它和 Erosion 函数是多么相似。 这里我们同样可以指定内核的形状,锚点和大小。

/** @function Dilation */
void Dilation( int, void* )
{
  int dilation_type;
  if( dilation_elem == 1 ){ dilation_type = MORPH_RECT; }
  else if( dilation_elem == 2  ){ dilation_type = MORPH_CROSS; }
  else if( dilation_elem == 3 ) { 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 );
}

结果

  • 编译并使用图像路径作为参数运行程序,比如我们使用以下图像:

    Original image

    下面是操作的结果。 更改Trackbars的位置就会产生不一样的输出图像,自己试试吧。 最后,你还可以通过增加第三个Trackbar来控制膨胀或腐蚀的次数。

    Dilation and Erosion application

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:4882次
    • 积分:137
    • 等级:
    • 排名:千里之外
    • 原创:7篇
    • 转载:3篇
    • 译文:2篇
    • 评论:4条
    文章存档
    最新评论