您的位置:首页 > 运维架构

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

2012-05-17 09:06 423 查看
首先如何理解对灰度图像进行形态学操作?

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

在上述理解的基础上,如果我们对图像的腐蚀和膨胀的结果做差,就能提取图像的边界:因为边界区域,二者完全不同。(实际上,我们也可以用腐蚀或者膨胀的结果与源图像做差得出类似结果,但提取的边界会比较细)。可以看出,结构元越大,边界越粗。在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;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息