目标:
在这个教程中你将学会:
应用两个最常用的形态学操作:膨胀和腐蚀。为了这个目的,你将使用下面的OpenCV函数:
erode()
Dilate()
形态学操作:
简要的说:就是一系列基于形状的处理图像的操作。形态学操作对输入图像应用一个结构元素并且产生输出图像。
最基本的形态学操作是:膨胀和腐蚀。他们有很广泛的应用,例如:
取出噪音;
分离单个的元素并且把分开的元素在一幅图像中联通;
找出一幅图像的碰撞或者孔洞。
我们将使用下面这幅图像来简要地解释一下膨胀和腐蚀:
腐蚀:
这个操作是以B为核对图像A进行卷积处理,这个核可以有任意的形状和尺寸,通常是一个方形或圆形。
核B有一个锚点,通常是核的中心;
当核B对图像进行扫描的时候,我们计算被B所掩盖的像素的最大值,并且在锚点用最大值取代图像的像素值。正如你所推断,这种最大化操作导致图像中亮的区域增多(因此也叫作腐蚀)。以上述图像为例,对其应用腐蚀操作我们可以得到:
背景(亮)腐蚀字母的黑色区域。
膨胀:
这个操作时腐蚀的姊妹操作。这个操作所做的就是去计算被核所掩盖的像素的最小值。
当核B在扫描图像的时候,我们计算被核所掩盖的图像像素的最小值,并且在锚点用这个最小值去替换该像素值。
和腐蚀的例子相似,我们可以将膨胀操作应用到上述的图像中。你可以在下面的结果中看到图像的亮区域(背景),变得更窄了,然而字母的黑色区域变得更大了。
代码:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "highgui.h"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/// Global variables
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
src = imread("F:/Photo/OpenCV_Photo/lena.jpg");
if (!src.data)
{
return -1;
}
/// Create windows
namedWindow("Erosion Demo", CV_WINDOW_AUTOSIZE);
namedWindow("Dilation Demo", CV_WINDOW_AUTOSIZE);
cvMoveWindow("Dilation Demo", src.cols, 0);
/// Create Erosion 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);
/// Create Dilation 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(0, 0);
Dilation(0, 0);
waitKey(0);
return 0;
}
/** @function Erosion */
void Erosion(int, void*)
{
int erosion_type;
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));
/// Apply the erosion operation
erode(src, erosion_dst, element);
imshow("Erosion Demo", erosion_dst);
}
/** @function Dilation */
void Dilation(int, void*)
{
int dilation_type;
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);
}
解释:
1、 大多数的代码以应该都知道了,下面让我们来检查这个程序的大概结构:
首先载入一幅图像
创建两个窗口;
为每一个操作创建一个滑动条;
每次你移动滑动条,用户函数就会响应,然后更新窗口中显示的图像;
下面让我们来分析这两个函数:
2、 膨胀:
erode( src, erosion_dst, element );
从上面我们可以看到,膨胀操作erode函数结构三个参数。其中element是我们要进行膨胀操作的核。如果我们没有指定,那么默认的核是一个3X3的矩阵,我们也可以自己指定核。为了我们自己来定义核,我们使用了下面的函数:OpenCV提供了一个函数getStructuringElement,可以获取常用的结构元素的形状:矩形(包括线形)、椭圆(包括圆形)及十字形:MORPH_RECT, MORPH_ELLIPSE, MORPH_CROSS
Matelement = getStructuringElement( erosion_type,
Size(2*erosion_size + 1, 2*erosion_size+1 ),
Point( erosion_size,erosion_size ) );
然后我们只需要指定我们核的大小以及锚点。如果没有指定,那么它就假设在核的中心。
有了上述这些,我们已经可以去对我们的图像做膨胀操作了。
注意:还有另外一个参数允许你一次性做多次膨胀操作,在这个简单的例子中,就不介绍了,请查看相关资料获得更多详细的介绍。
3、 腐蚀:
腐蚀操作从上面的代码中可以看出,它的实现过程和膨胀的过程非常相似。这里就不多做介绍了。