学习OpenCV范例(十一)——图像的腐蚀与膨胀

本文详细介绍了图像处理中的形态学操作——腐蚀与膨胀的基本原理及其应用。通过代码示例展示了如何利用OpenCV库中的函数实现这两种操作,并通过调整内核大小和形状观察图像的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这次范例相对比较简单,是涉及到形态学操作的问题,原理也是比较简单,学习起来比较轻松,大家看完这次的范例分析就可以明白到底图像的腐蚀和膨胀是怎么回事了。

1、原理

简单来讲,形态学操作就是基于形状的一系列图像处理操作。通过将 结构元素 作用于输入图像来产生输出图像。
最基本的形态学操作有二:腐蚀与膨胀(Erosion 与 Dilation)。 他们的运用广泛:
消除噪声
分割(isolate)独立的图像元素,以及连接(join)相邻的元素。
寻找图像中的明显的极大值区域或极小值区域。
通过以下图像,我们简要来讨论一下膨胀与腐蚀操作,膨胀是取像素值高的点,腐蚀相反,是取像素值低的点。


                      图1、原图

①、膨胀

此操作将图像 A 与任意形状的内核 (B),通常为正方形或圆形,进行卷积。
内核 B 有一个可定义的 锚点, 通常定义为内核中心点。
进行膨胀操作时,将内核 B 划过图像,将内核 B 覆盖区域的最大相素值提取,并代替锚点位置的相素。显然,这一最大化操作将会导致图像中的亮区开始”扩展” (因此有了术语膨胀 dilation )。对上图采用膨胀操作我们得到:

                图2、膨胀图片

与上面原图比较,可以明显的看到图像字母变粗了,膨胀了。

②、腐蚀

腐蚀在形态学操作家族里是膨胀操作的孪生姐妹。它提取的是内核覆盖下的相素最小值。
进行腐蚀操作时,将内核 B 划过图像,将内核 B 覆盖区域的最小相素值提取,并代替锚点位置的相素。
以与膨胀相同的图像作为样本,我们使用腐蚀操作。如下图:


                图3、腐蚀图片

与上面原图比较,可以明显的看到图像字母变细了,腐蚀了。

现在,我们编写一些代码,在窗口中再加上trackbar控件对图像进行操作,这样可以更直观的看到图像的变化。

2、代码实现

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include "stdafx.h"  
  2.   
  3. #include "opencv2/imgproc/imgproc.hpp"  
  4. #include "opencv2/highgui/highgui.hpp"  
  5. #include <stdlib.h>  
  6. #include <stdio.h>  
  7.   
  8. using namespace cv;  
  9.   
  10. /// 全局变量  
  11. Mat src, erosion_dst, dilation_dst;  
  12.   
  13. int erosion_elem = 0;  
  14. int erosion_size = 0;  
  15. int dilation_elem = 0;  
  16. int dilation_size = 0;  
  17. int const max_elem = 2;  
  18. int const max_kernel_size = 21;  
  19.   
  20. /** Function Headers */  
  21. void Erosion( intvoid* );  
  22. void Dilation( intvoid* );  
  23.   
  24. /** @function main */  
  25. int main( int argc, char** argv )  
  26. {  
  27.     /// Load 图像  
  28.     src = imread( "LinuxLogo.jpg" );  
  29.   
  30.     if( !src.data )  
  31.     { return -1; }  
  32.   
  33.     /// 创建显示窗口  
  34.     namedWindow( "Erosion Demo", CV_WINDOW_AUTOSIZE );  
  35.     namedWindow( "Dilation Demo", CV_WINDOW_AUTOSIZE );  
  36.     cvMoveWindow( "Dilation Demo", src.cols, 0 );  
  37.   
  38.     /// 创建腐蚀 Trackbar  
  39.     createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse""Erosion Demo",  
  40.         &erosion_elem, max_elem,  
  41.         Erosion );  
  42.   
  43.     createTrackbar( "Kernel size:\n 2n +1""Erosion Demo",  
  44.         &erosion_size, max_kernel_size,  
  45.         Erosion );  
  46.   
  47.     /// 创建膨胀 Trackbar  
  48.     createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse""Dilation Demo",  
  49.         &dilation_elem, max_elem,  
  50.         Dilation );  
  51.   
  52.     createTrackbar( "Kernel size:\n 2n +1""Dilation Demo",  
  53.         &dilation_size, max_kernel_size,  
  54.         Dilation );  
  55.   
  56.     /// Default start  
  57.     Erosion( 0, 0 );  
  58.     Dilation( 0, 0 );  
  59.   
  60.     waitKey(0);  
  61.     return 0;  
  62. }  
  63.   
  64. /**  @function Erosion  */  
  65. void Erosion( intvoid* )  
  66. {  
  67.     int erosion_type;  
  68.     if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }  
  69.     else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }  
  70.     else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }  
  71.   
  72.     Mat element = getStructuringElement( erosion_type,  
  73.         Size( 2*erosion_size + 1, 2*erosion_size+1 ),  
  74.         Point( erosion_size, erosion_size ) );  
  75.   
  76.     /// 腐蚀操作  
  77.     erode( src, erosion_dst, element );  
  78.     imshow( "Erosion Demo", erosion_dst );  
  79. }  
  80.   
  81. /** @function Dilation */  
  82. void Dilation( intvoid* )  
  83. {  
  84.     int dilation_type;  
  85.     if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }  
  86.     else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }  
  87.     else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }  
  88.   
  89.     Mat element = getStructuringElement( dilation_type,  
  90.         Size( 2*dilation_size + 1, 2*dilation_size+1 ),  
  91.         Point( dilation_size, dilation_size ) );  
  92.     ///膨胀操作  
  93.     dilate( src, dilation_dst, element );  
  94.     imshow( "Dilation Demo", dilation_dst );  
  95. }  

3、运行结果


          图4、原图


         图5、膨胀图片


         图6、腐蚀图片

4、用到的类和函数

getStructuringElement:

功能:返回一个指定大小和形态的结构元素

结构:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Mat getStructuringElement(int shape, Size ksize, Point anchor=Point(-1,-1))  
shape :核的类型
        MORPH_RECT:矩形,核的定义为:
         E_{ij}=1
      MORPH_ELLIPSE:椭圆
      MORPH_CROSS:交叉型,核的定义为:

      E_{ij} =  \fork{1}{if i=\texttt{anchor.y} or j=\texttt{anchor.x}}{0}{otherwise}
      CV_SHAPE_CUSTOM :自定义类型
ksize :核的大小
anchor : 锚点 位置。不指定锚点位置,则默认锚点在内核中心位置。

dilate:

功能:图像膨胀

结构:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void dilate(InputArray src, OutputArray dst, InputArray element, Point anchor=Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() )  
src :源图像
dst :目标图像,和源图像有同样的size和type
element :结构元素,可通过 getStructuringElement,如果element=Mat(),那么默认核为3*3的矩形结构
anchor:  锚点  位置。不指定锚点位置,则默认锚点在内核中心位置。
iterations :迭代次数
borderType :边缘点插值类型
实现原理:

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

函数支持(in-place)模式。膨胀可以重复进行 (iterations) 次. 对彩色图像,每个彩色通道单独处理。

erode:

功能:腐蚀图像

结构:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void erode(InputArray src, OutputArray dst, InputArray element, Point anchor=Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() )  
src :源图像
dst :目标图像,和源图像有同样的size和type
element :结构元素,可通过 getStructuringElement,如果element=Mat(),那么默认核为3*3的矩形结构
anchor:  锚点  位置。不指定锚点位置,则默认锚点在内核中心位置。
iterations :迭代次数
borderType :边缘点插值类型
实现原理:

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

函数支持(in-place)模式。膨胀可以重复进行 (iterations) 次. 对彩色图像,每个彩色通道单独处理。

createTrackbar:

功能:创建trackbar并将它添加到指定的窗口

结构:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int createTrackbar(const string& trackbarname, const string& winname, int* value, int count, TrackbarCallback onChange=0, void* userdata=0)  
trackbarname :被创建的trackbar名字
winname :窗口名字,这个窗口将为被创建trackbar的父对象
value :整数指针,它的值将反映滑块的位置。这个变量指定创建时的滑块位置
count :滑块位置的最大值。最小值一直是0
onChange :每次滑块位置被改变的时候,被调用函数的指针。这个函数应该被声明为void Foo(int,void*);第一个参数是trackbar的位置,第二个参数是userdata,如果没有回调函数,这个值可以设为NULL。
userdata :回调函数返回的数据,在没有使用全局变量的时候,可以通过它来处理trackbar事件
补充:

getTrackbarPos:

功能:获取trackbar的位置,也即是value的位置

结构:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int getTrackbarPos(const string& trackbarname, const string& winname)  
trackbarname :trackbar的名字
winname :trackbar父窗口的名字

setTrackbarPos:

功能:设置trackbar位置,也即是value的位置

结构:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void setTrackbarPos(const string& trackbarname, const string& winname, int pos)  
trackbarname :trackbar的名字
winname :trackbar父窗口的名字

pos :新的位置


网址:http://blog.youkuaiyun.com/chenjiazhou12/article/details/21406301

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值