OpenCV基础应用 3.形态学图像处理

       数学形态学起初是对二值图像提出的,而后扩展到灰度图像。形态学关注的是形状:将图像和形状看做是点集,根据形状利用数学形态学处理图像。通过这种方式,形态学算子定义的是局部变换,把那些要表达的像素值看做集合。这种改变像素值的方式是通过定义击中或不击中变换进行形式化的。

       集合X表达的目标可以通过集合B所表达的结构元素来检测。不同的结构元素可以用来改变对集合X的处理。击中或不击中变换可以定义为点算子:

       上式中,x表示集合X中的元素,也就是图像中的像素。带c上标表示集合的补集。结构元素B有两部分表达,它们引用与集合X或其补集。

       图1-1显示的是一帧二值图像和一个结构元素。图像像素被分成属于集合X和属于其补集的像素。结构元素B分解为两个补集,每个自己用来分析集合X及其补集。图中,用黑色表示B1的元素,白色表示B2的元素,从而表明它们分别应用于X和其补集。

       处理过程:将结构元素B移动到图像中的每一个像素,并逐像素与模板B进行对比,如果图像值与结构元素的值相等,那么该图像像素属于所得集合X⊗B。

一. 腐蚀与膨胀

       形态学算子的最简单形式是当B1或B2中任何一个为空。当B1为空时定义的是腐蚀(减少),而当B2为空时,定义的时膨胀(膨胀)。即腐蚀定义为:

       膨胀处理:

1.1 腐蚀

       在腐蚀算子中,击中或不击中变换规定如果结构元素B1中的每个点平移到x后属于集合X,则像素x属于腐蚀集合。由于B1中所有的点必须在集合X内,所以该算子可以去除X内目标边界上的像素。这样一来,它可以使集合腐蚀或缩小。

       下图显示的腐蚀算子的处理,图1-2 (a)定义结构元素B1的3*3模板。其中心元素是该集合的原点。图1-2 (b)显示一帧包含黑色像素区域的图像,这些黑色像素定义集合X。图1-2 (c)显示腐蚀结果,该结果仅由黑色像素组成,灰色部分时被腐蚀算子去除的像素。例如,当结构元素移动到图1-2 (c)的位置时,中心点像素被去除,因为这是X内只有5个像素与该结构元素对应。

       腐蚀算子最常见的应用之一是对阈值处理后的图像去除噪声。

       腐蚀操作在OpenCV中定义如下:

void erode( InputArray src,  
    OutputArray dst,  
    InputArray kernel,  
    Point anchor=Point(-1,-1),  
    int iterations=1,  
    int borderType=BORDER_CONSTANT,  
    const Scalar& borderValue=morphologyDefaultBorderValue() );

       参数意义:

  • 参数src: 输入图像
  • 参数dst: 输出图像
  • 参数kernel: 腐蚀操作的模板。若为NULL,表示使用参考点位于中心3*3的核。一般使用函数getStructuringElement配合这个参数使用。
  • 参数anchor: 锚点,默认(-1,-1),表示锚点位于中心。
  • 参数iterations: 迭代使用erode的次数,默认为1。
  • 参数borderType: 处理边界模式。
  • 参数borderValue:

       应用示例:

#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
using namespace std;
using namespace cv;
#define IMAGE_PATH	"../ImageSet/焊盘二值图.bmp"
int main()
{
	Mat img = cv::imread(IMAGE_PATH);
	if(img.empty())
	{
		cout<<"Read image failed"<<endl;
		return 0;
	}
	cv::imshow("Raw Image", img);
	Mat element = getStructuringElement(MORPH_RECT, Size(5,5));
	Mat dstImage;
	erode(img, dstImage, element);
	imshow("Erode", dstImage);
	waitKey(0);
	return 0;
}

       使用5*5的大小的模板进行腐蚀,对比如图1-3所示。

1.2 膨胀

       如果补集被腐蚀,则集合X被膨胀。如图1-4所示为膨胀处理。图1-4 (a)定义了结构元素集合B2。其中的元素用白色表示,它们属于X的补集。图1-4 (b)显示一帧图像,图1-4 (c)显示图像的膨胀结果。黑色和灰色像素属于X的膨胀。灰色像素为新加入集合的像素。膨胀过程中,把结构元素放在补集的每个像素上,如果没有完全包含这些结构元素,那么将对应像素从补集中去除,因而该像素称为X的一部分。例如结构元素移动到图1-4 (c)中网格位置时,将中心像素去除,由于这是模板像素中有一个在X内。

       膨胀操作在OpenCV中封装为dilate函数,定义如下:

void dilate(InputArray src,  
    OutputArray dst,  
    InputArray kernel,  
    Point anchor=Point(-1,-1),  
    int iterations=1,  
    int borderType=BORDER_CONSTANT,  
    const Scalar& borderValue=morphologyDefaultBorderValue() 
)
  • 参数src: 输入图像
  • 参数dst: 输出图像
  • 参数kernel: 腐蚀操作的模板。若为NULL,表示使用参考点位于中心3*3的核。一般使用函数getStructuringElement配合这个参数使用。
  • 参数anchor: 锚点,默认(-1,-1),表示锚点位于中心。
  • 参数iterations: 迭代使用erode的次数,默认为1。
  • 参数borderType: 处理边界模式。
  • 参数borderValue:

       应用实例:

#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
using namespace std;
using namespace cv;

int main()
{
	Mat img = cv::imread("../ImageSet/dilate_0.bmp");
	if(img.empty())
	{
        cout<<"Read image failed"<<endl;
        return 0;
	}
	cv::imshow("Raw Image", img);
	Mat element = getStructuringElement(MORPH_RECT, Size(5,5));
	Mat dstImage;
	dilate(img, dstImage, element);
	cv::imshow("dilate", dstImage);
	waitKey(0);
	return 0;
}

       膨胀前后对比如图1-4所示:

二. 形态学基本操作

       开运算:先腐蚀再膨胀,用来消除小物体

       闭运算:先膨胀再腐蚀,用于排除小型黑洞

       形态学梯度:就是膨胀图与俯视图之差,用于保留物体的边缘轮廓。

       顶帽:原图像与开运算图之差,用于分离比邻近点亮一些的斑块。

       黑帽:闭运算与原图像之差,用于分离比邻近点暗一些的斑块。

       OpenCV的函数morphologyEx()封装了这些操作的,第三个参数指定了相应的操作。

含义

MORPH_OPEN

开运算

MORPH_CLOSE

闭运算

MORPH_GRADIENT

形态学梯度

MORPH_TOPHAT

顶帽

MORPH_BLACKHAT

黑帽

MORPH_ERODE

腐蚀

MORPH_DILATE

膨胀

2.1 开运算

       开运算表示如下:

       一般来说,开运算使图像的轮廓变得光滑,断开狭窄的连接和消除细毛刺。

       应用实例:

void Image_Open()
{
	Mat img = cv::imread("../ImageSet/dilate_0.bmp");
	if(img.empty())
	{
         cout<<"Read image failed"<<endl;
         return ;
	}
	cv::imshow("Raw Image", img);

	Mat element = getStructuringElement(MORPH_RECT, Size(5,5));
	Mat dstImage;
	morphologyEx(img, dstImage, MORPH_OPEN, element);
	cv::imshow("Open", dstImage);
}

       效果对比图如图2-1所示:

2.2 闭运算

       闭运算表示如下:

       同样使图像的轮廓变得光滑,但与开操作相反,它能消除狭窄的间断和长细的鸿沟,消除小的孔洞,并填补轮廓线中的裂痕。

       闭运算的处理效果如图2-2所示,中心的黑洞消除了。

三. 边界提取与跟踪

       轮廓是对物体形状的有力描述,对图像分析和识别十分有用。

3.1 边界提取

       在二值图像中提取边界,最简单的方法是将物体内部的点删除,具体来讲,可以逐个像素扫描,如果发现一个黑点的8个邻域都是黑点,则该点为内部点,在目标图像中将它删除。实际上这相当一个3×3的结构元素对原图像进行腐蚀,使得只有那些8个邻域都有黑点的内部点被保留,再利用原图像减去腐蚀后的图像,敲好删除了这些内部点,留下边界像素,这个过程可以参见图3-1。

       OpenCV代码示例:

#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"

using namespace std;
using namespace cv;

int main()
{
	Mat img = imread("../ImageSet/IMG_Erode_1.bmp");
	if(img.empty())
	{
        cout<<"Read image failed"<<endl;
        return 0;
	}
	imshow("Raw", img);
	Mat element = getStructuringElement(MORPH_RECT, Size(3,3));
	Mat dstImage;
	erode(img, dstImage, element);
	imwrite("Erode.bmp", dstImage);

	Mat dst;
	addWeighted(img, 1, dstImage, -1, 0, dst);
	imwrite("result.bmp", dst);
	waitKey(0);
}

       处理效果:

3.2 边界跟踪

       为了依次记录边界上的各个像素,边界跟踪首先按照某种顺序扫描规则找到目标物体边界上的一个像素,然后以此点为起始点,根据某种顺序依次找出物体边界上的其余像素,直到又回到起始点,完成整条边界的跟踪。

       例如,我们按照从左到右,从上到下的顺序扫描图像,这样我们可以先找到最左上方的边界点P0,如果以逆时针顺序查找边界点,则从P0点的左下方,下方,右方的顺序查找是否是黑点,直到回到起始点。如图3-3所示。

       OpenCV将轮廓提取封装在了findContours函数中,定义如下:

void findContours( InputArray image, OutputArrayOfArrays contours,
          int mode, int method, Point offset = Point());

       参数说明:

  • image: 输入图像,必须是单通道二值图。
  • contours: 输出的边界,类型为vector>。
  • mode: 检测方法。

mode

说明

RETR_EXTERNAL

只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略

RETR_LIST

检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系

RETR_CCOMP

检测所有的轮廓,但所有轮廓只建立两个等级关系

RETR_TREE

检测所有轮廓,所有轮廓建立一个等级树结构。

  • method: 定义轮廓的近似方法:CHAIN_APPROX_NONE:保存边界上所有连续的轮廓点,CHAIN_APPROX_SIMPLE:只保留轮廓的拐点信息。
  • Point: 偏移,所有轮廓信息相对于原始图像的偏移。

       示例代码:

#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"

using namespace std;
using namespace cv;
int main()
{
	Mat img = imread("../ImageSet/IMG_Erode_1.bmp");
	if(img.empty())
	{
        cout<<"Read image failed"<<endl;
        return;
	}

	Mat gray_img;
	cvtColor(img, gray_img, COLOR_BGR2GRAY);
	threshold(gray_img, gray_img, 100,255,THRESH_BINARY);
	imshow("Raw", gray_img);
	vector<vector<Point> >contours;
	findContours(gray_img, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
	Mat result;
	result.create(Size(img.cols,img.rows), CV_8UC1);
	result.setTo(Scalar(0));
	drawContours(result, contours, -1, Scalar(255), 2);
	imwrite("result.bmp", result);
	waitKey(0);
}

       效果如图3-4,可以发现findContours没有找到图像内部的孔洞边界,这是因为参数RETR_EXTERNAL指定只检测外围边界。

四 最小包围盒

       OpenCV提供了多种求解轮廓的最小包围形状。

  • 返回指定点集最外部矩形边界:boundingRect()
  • 寻找给定的点集可旋转的最小包围矩形:minAreaRect()
  • 寻找最小包围圆形:minEnclosingCircle()
  • 用椭圆拟合二维点集:fitEllipse()
  • 逼近多边形曲线:approxPolyDP()

       计算轮廓的几何特征:

  • 重心、主轴、面积等特征: moments()
  • 轮廓面积: contourArea()
  • 轮廓长度: arcLength()

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV是一个非常强大的计算机视觉库,提供了丰富的图像处理和计算机视觉功能。其中包括形态学图像处理,也就是对图像进行形态学操作,比如膨胀、腐蚀、开运算、闭运算等。 下面是一个示例代码,展示了如何使用OpenCV进行形态学图像处理: ```cpp #include <opencv2/opencv.hpp> using namespace cv; int main() { // 读取图像 Mat img = imread("test.jpg"); // 将图像转换为灰度图像 cvtColor(img, img, COLOR_BGR2GRAY); // 对图像进行膨胀操作 Mat dilated; Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); dilate(img, dilated, element); // 对图像进行腐蚀操作 Mat eroded; erode(img, eroded, element); // 对图像进行开运算操作 Mat opened; morphologyEx(img, opened, MORPH_OPEN, element); // 对图像进行闭运算操作 Mat closed; morphologyEx(img, closed, MORPH_CLOSE, element); // 显示结果图像 namedWindow("Original Image", WINDOW_NORMAL); namedWindow("Dilated Image", WINDOW_NORMAL); namedWindow("Eroded Image", WINDOW_NORMAL); namedWindow("Opened Image", WINDOW_NORMAL); namedWindow("Closed Image", WINDOW_NORMAL); imshow("Original Image", img); imshow("Dilated Image", dilated); imshow("Eroded Image", eroded); imshow("Opened Image", opened); imshow("Closed Image", closed); // 等待用户按下任意键 waitKey(0); return 0; } ``` 这段代码可以对一张名为“test.jpg”的图像进行膨胀、腐蚀、开运算和闭运算操作,并将结果显示出来。需要注意的是,在进行形态学操作之前,需要将图像转换为灰度图像。另外,需要使用`getStructuringElement()`函数创建一个结构元素,用于形态学操作。 在实际应用中,可以根据具体需求选择不同的形态学操作,以便达到更好的图像处理效果

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值