现在的位置: 首页 > 综合 > 正文

我的OpenCV学习笔记(17):利用形态学学滤波检测边沿和角点

2013年08月12日 ⁄ 综合 ⁄ 共 2960字 ⁄ 字号 评论关闭

首先如何理解对灰度图像进行形态学操作?

一种比较形象的方法是将灰度图像看做是“等高线”:亮的区域代表山峰,而暗的区域代表山谷,图像的边沿就对应于峭壁。如果腐蚀一幅图像,会导致山谷被扩展,而峭壁减少了。相反的,如果膨胀一幅图像,峭壁则会增加。但是这两种情况下,中间的部分(大片的谷底和高原)基本保持不变。

在上述理解的基础上,如果我们对图像的腐蚀和膨胀的结果做差,就能提取图像的边界:因为边界区域,二者完全不同。(实际上,我们也可以用腐蚀或者膨胀的结果与源图像做差得出类似结果,但提取的边界会比较细)。可以看出,结构元越大,边界越粗。在OpenCV中,将形态学操作函数morphologyEx 的第4个参数设为MORPH_GRADIENT,就能完成上述工作。

利用形态学操作获取角点稍微有一些复杂,他的基本方法是对一幅图像先腐蚀,在膨胀。但是这两次操作使用的结构元却不同。这些结构元的选取使得直线保持不变,但是由于他们各自作用的效果,角点处的边沿被影响了。我们结合一幅图来说明:

 

第一个方块是源图像,当被十字结构元膨胀后,除了那些十字形不能对到的角点,其他部分被扩展了。作用结果在显示在中间那个图。膨胀后的图形被菱形结构元腐蚀。腐蚀将边沿变回了原来的位置,但是把角点放得更远了,因为他们没有被膨胀。这样就获得最右边的图像,可以看到,它失去了角点。相同的过程作用于X型和方形结构元。他们两个是前两个结构元的旋转版本,会捕捉45度角点的角点。最终,两幅图像的差分将会提取角点特征。

最后我们看一下代码:

首先是定义一个类来支持我们要做的操作:

#if ! defined MORPHOF
#define MORPHOF

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;

class MorphoFeatures
{
private:
	//产生二值图像的门限
	int threshold;
	//结构元
	Mat cross;
	Mat diamond;
	Mat square;
	Mat x;
	//对图像二值化
	void applyThreshold(Mat &result)
	{
		if(threshold>0)
			cv::threshold(result,result,threshold,255,THRESH_BINARY_INV);
	}


public:
	//构造函数
	MorphoFeatures():threshold(-1),
					cross(5,5,CV_8U,Scalar(0)),
					diamond(5,5,CV_8U,Scalar(255)),
					square(5,5,CV_8U,Scalar(255)),
					x(5,5,CV_8U,Scalar(0))
	{
		//创建十字型结构元
		for(int i = 0; i < 5; i++)
		{
			cross.at<uchar>(2,i) = 255;
			cross.at<uchar>(i,2) = 255;
		}

		//创建菱形结构元:手动画,只需要把不是菱形的部分变白即可
		diamond.at<uchar>(0,0)= 0;
		diamond.at<uchar>(0,1)= 0;
		diamond.at<uchar>(1,0)= 0;
		diamond.at<uchar>(4,4)= 0;
		diamond.at<uchar>(3,4)= 0;
		diamond.at<uchar>(4,3)= 0;
		diamond.at<uchar>(4,0)= 0;
		diamond.at<uchar>(4,1)= 0;
		diamond.at<uchar>(3,0)= 0;
		diamond.at<uchar>(0,4)= 0;
		diamond.at<uchar>(0,3)= 0;
		diamond.at<uchar>(1,4)= 0;

		//创建X形
		for(int i = 0; i < 5; i++)
		{
			//主对角线
			x.at<uchar>(i,i) = 255;
			//副对角线
			x.at<uchar>(4-i,i) = 255;
		}

	}
	
	//设置门限函数
	void setThreshold(int t)
	{
		threshold = t;
	}

	//获取当前门限
	int getThreshold() const
	{
		return threshold;
	}

	//检测直线函数
	Mat getEdges(const Mat &image)
	{
		Mat result;
		//获取图像的梯度
		morphologyEx(image,result,cv::MORPH_GRADIENT,Mat());
		//结果二值化
		applyThreshold(result);
		return result;
	}

	//检测角点函数
	Mat getCorners(const Mat &image)
	{
		Mat result;
		dilate(image,result,cross);
		erode(result,result,diamond);
		Mat result2;
		dilate(image,result2,x);
		erode(result2,result2,square);
		absdiff(result2,result,result);
		applyThreshold(result);
		return result;
	}

	//在角点处画圆
	void drawOnImage(const Mat &binary,Mat &image)
	{
		Mat_<uchar>::const_iterator it = binary.begin<uchar>();
		Mat_<uchar>::const_iterator itend = binary.end<uchar>();
		for(int i = 0;it != itend;++it,++i)
		{
			if(!*it)
				circle(image,Point(i%image.step,i/image.step),5,Scalar(255,0,0));
		}
	}

};



#endif

然后看看main函数:

#include <opencv2/highgui/highgui.hpp>
#include "morphoFeatures.h"

int main()
{
		
	Mat image = imread("D:/picture/images/building.jpg",0);
	if(!image.data)
		return -1;
	imshow("源图像",image);
	
	MorphoFeatures morpho;
	morpho.setThreshold(40);

	//获取边沿
	Mat edges;
	edges = morpho.getEdges(image);
	imshow("边沿",edges);

	//获取角点
	morpho.setThreshold(-1);
	Mat corners;
	corners = morpho.getCorners(image);
	morphologyEx(corners,corners,MORPH_TOPHAT,Mat());
	threshold(corners,corners,40,255,THRESH_BINARY_INV);
	//imshow("角点",corners);
	
	//展示图片上的角点
	morpho.drawOnImage(corners,image);
	imshow("图片上的角点",image);
	

	waitKey(0);
	return 0;
}

 

抱歉!评论已关闭.