形态学边缘检测的原理是利用膨胀与腐蚀变化区域特征来完成边缘检测,膨胀操作是将目标物体向周围邻域进行扩展,而腐蚀操作则是将目标物体像邻域进行收缩
因此图像的边缘恰好反映在形态学腐蚀与膨胀中变化的区域,因此只需要将膨胀得到的结果与腐蚀得到的结果图进行做差运算,就可以得到物体的形态学边缘。
在OpenCV中具体的实现方法可以使用morphologyEX()函数中的形态学梯度运算直接得到,具体实现代码在上一篇中已经给出,在这里不再赘述。
形态学滤波角点检测比边缘检测的过程要稍微复杂一些,但是基本原理是相似的。根据角点的性质,对源图像先利用 十字 型的结构元素进行膨胀,这种情况下只会使得目标物体在边缘处扩展,而角点则并不会发生变化;然后利用菱形的结构元素对上一步得到的图像进行腐蚀操作,这种情况下会使得目标物体在边缘处无变化,而角点处则会发生进一步收缩;接着用X型结构元素对源图像进行膨胀操作,这种情况下会使得角点灰度原装,同样边缘将腐蚀得更多。将得到的膨胀图与腐蚀图进行相减操作后,就能够得到图像的角点。
在给出具体实现程序之前先说明一下在OpenCV中的各种结构元素:
(1)MORPH_RECT:矩形结构元素,Eij=1;
(2)MORPH_ELLIPSE:椭圆结构元素,这个椭圆为内且与矩形Rect(0,0,esize.width,esize.height);
(3)MORPH_CROSS:交叉结构元素,当坐标 i 为 x 或 y 中心轴点式时,Eij = 1,其他情况下Eij = 0;
具体程序如下所示:
//使用形态学方法进行角点检测
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat srcImage = imread("2345.jpg");
if (!srcImage.data)
{
cout << "读入图片错误!" << endl;
system("pause");
return -1;
}
//将原图像进行灰度化
Mat GraySrc;
cvtColor(srcImage, GraySrc, CV_BGR2GRAY);
//定义结构元素
Mat CrossMat(5, 5, CV_8U, Scalar(0)); //十字型结构元素
Mat DiamondMat(5, 5, CV_8U, Scalar(1)); //菱形结构元素
Mat squareMat(5, 5, CV_8U, Scalar(1)); //方形结构元素
Mat XMat(5, 5, CV_8U, Scalar(0));
//十字形形状的结构元素
for (int i = 0; i < 5; i++)
{
CrossMat.at<uchar>(2, i) = 1;
CrossMat.at<uchar>(i, 2) = 1;
}
//定义菱形形状
DiamondMat.at<uchar>(0, 0) = 0;
DiamondMat.at<uchar>(0, 1) = 0;
DiamondMat.at<uchar>(1, 0) = 0;
DiamondMat.at<uchar>(4, 4) = 0;
DiamondMat.at<uchar>(3, 4) = 0;
DiamondMat.at<uchar>(4, 3) = 0;
DiamondMat.at<uchar>(4, 0) = 0;
DiamondMat.at<uchar>(4, 1) = 0;
DiamondMat.at<uchar>(3, 0) = 0;
DiamondMat.at<uchar>(0, 4) = 0;
DiamondMat.at<uchar>(0, 3) = 0;
DiamondMat.at<uchar>(1, 4) = 0;
//定义X型形状
for (int i = 0; i < 5; i++)
{
XMat.at<uchar>(i, i) = 1;
XMat.at<uchar>(4 - i, i) = 1;
}
//第一步:十字形对原图进行膨胀
Mat dstImage1;
dilate(GraySrc, dstImage1, CrossMat);
//第二步:菱形对上步结果进行腐蚀
erode(dstImage1, dstImage1, DiamondMat);
Mat dstImage2;
//第三步:使用X型结构元素对原图进行腐蚀
dilate(GraySrc, dstImage2, XMat);
//第四步:正方形对上一步的结果进行腐蚀
erode(dstImage2, dstImage2, squareMat);
//第五步:计算差值
absdiff(dstImage2, dstImage1, dstImage1);
threshold(dstImage1, dstImage1, 40, 255, THRESH_BINARY);
//绘图
for (int i = 0; i < dstImage1.rows; i++)
{
//获取行指针
const uchar *data = dstImage1.ptr<uchar>(i);
for (int j = 0; j < dstImage1.cols; j++)
{
//如果是角点,则进行绘制圆圈
if (data[j])
{
circle(srcImage, Point(j, i), 8, Scalar(0, 255, 0));
}
}
}
imshow("结果图", dstImage1);
imshow("原图像", srcImage);
waitKey();
return 0;
}
执行程序后,得到的结果图如下所示: