就是利用形态学处理中的腐蚀和膨胀操作进行的角点检测、边缘检测。
第一步:十字型核-------->【对原图:膨胀操作】
效果:原图在水平和垂直方向会扩展,而45度.135度方向没有得到扩展
目的:目的是使得在下一步的腐蚀操作中,保证腐蚀后的边缘与原图一致,而只有角点被腐蚀掉
第二步:菱形核-------->【对第一步的结果:腐蚀操作】
效果:使得第一步的结果在水平和垂直方向被腐蚀,而在45度.135度等方向也有一定腐蚀效果,使用菱形而不用十字进行腐蚀是为了斜方向得到腐蚀
第一步+第二部的最终结果:原图的边缘不发生变化,仅有角点被腐蚀
上图来源:https://blog.csdn.net/z827997640/article/details/80536547
个人认为,角点检测到这里就应该结束了,最后一步直接用原图与第二步的结果相减,就可以得出角点了
这里给出测试代码:
#include
#include
#include
#include
using namespace std;
using namespace cv;
void main()
{
Mat srcImg = imread("F:\\opencv_re_learn\\contour1.jpg");
if (!srcImg.data){
cout << "failed to read" << endl;
system("pause");
return;
}
Mat srcGray;
cvtColor(srcImg, srcGray, CV_RGB2GRAY);
Mat thresh;
thresh = srcGray.clone();
threshold(srcGray, thresh, 176, 255, CV_THRESH_BINARY_INV);
imshow("thresh", thresh);
//定义核
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 X(5, 5, CV_8U, Scalar(0));
//十字形核
for (int i = 0; i < 5; i++){
CrossMat.at(2, i) = 1;
CrossMat.at(i, 2) = 1;
}
//菱形形状核
diamondMat.at(0, 0) = 0;
diamondMat.at(0, 1) = 0;
diamondMat.at(1, 0) = 0;
diamondMat.at(4, 4) = 0;
diamondMat.at(3, 4) = 0;
diamondMat.at(4, 3) = 0;
diamondMat.at(4, 0) = 0;
diamondMat.at(4, 1) = 0;
diamondMat.at(3, 0) = 0;
diamondMat.at(0, 4) = 0;
diamondMat.at(0, 3) = 0;
diamondMat.at(1, 4) = 0;
//X形状核
for (int i = 0; i < 5; i++){
X.at(i, i) = 1;
X.at(4 - i, i) = 1;
}
//第一步:十字型核,【膨胀操作】
//原图在水平和垂直方向会扩展,而45度.135度方向没有得到扩展
//目的是使得在下一步的腐蚀操作中,保证腐蚀后的边缘与原图一致,
//而只有角点被腐蚀掉
Mat result;
dilate(thresh, result, CrossMat);
imshow("1:Cross", result);
//第二步:菱形核,【腐蚀操作】
//效果:使得第一步的结果在水平和垂直方向被腐蚀,而在45度.135度等方向也,
//有一定腐蚀效果,使用菱形而不用十字进行腐蚀是为了斜方向得到腐蚀
//第一第二步操作后的结果是:原图的边缘不发生变化,仅有角点被腐蚀
erode(result, result, diamondMat);
imshow("2:diamond", result);
//计算差值
absdiff(thresh, result, result);
threshold(result, result, 30, 255, THRESH_BINARY);
//绘制
for (int i = 0; i < result.rows; i++){
//获取行指针
const uchar* data = result.ptr(i);
for (int j = 0; j < result.cols; j++){
//如果是角点,则绘制圆圈
if (data[j]){
circle(srcImg, Point(j, i), 8,
Scalar(0, 0, 255));
}
}
}
imshow("src", srcImg);
imshow("result", result);
waitKey(0);
}
测试结果:
实际上,算法给出还有第三第四步
第三步: X型核-------->【对原图:膨胀操作】
效果:使得原图在水平和垂直,45度,135度等倾斜方向都有扩展
第四步:正方形核---------->【对第三步结果:腐蚀操作】
效果:又腐蚀掉一次
第三第四部操作后的结果是:原图角点不发生变化,而水平和垂直方向的边缘似乎被腐蚀掉
最后一步:将第二步与第四步的结果相减,获得角点
以上四步的全部代码如下:
#include
#include
#include
#include
using namespace std;
using namespace cv;
void main()
{
Mat srcImg = imread("F:\\opencv_re_learn\\contour1.jpg");
if (!srcImg.data){
cout << "failed to read" << endl;
system("pause");
return;
}
Mat srcGray;
cvtColor(srcImg, srcGray, CV_RGB2GRAY);
Mat thresh;
thresh = srcGray.clone();
threshold(srcGray, thresh, 176, 255, CV_THRESH_BINARY_INV);
imshow("thresh", thresh);
//定义核
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 X(5, 5, CV_8U, Scalar(0));
//十字形核
for (int i = 0; i < 5; i++){
CrossMat.at(2, i) = 1;
CrossMat.at(i, 2) = 1;
}
//菱形形状核
diamondMat.at(0, 0) = 0;
diamondMat.at(0, 1) = 0;
diamondMat.at(1, 0) = 0;
diamondMat.at(4, 4) = 0;
diamondMat.at(3, 4) = 0;
diamondMat.at(4, 3) = 0;
diamondMat.at(4, 0) = 0;
diamondMat.at(4, 1) = 0;
diamondMat.at(3, 0) = 0;
diamondMat.at(0, 4) = 0;
diamondMat.at(0, 3) = 0;
diamondMat.at(1, 4) = 0;
//X形状核
for (int i = 0; i < 5; i++){
X.at(i, i) = 1;
X.at(4 - i, i) = 1;
}
//第一步:十字型核,【膨胀操作】
//原图在水平和垂直方向会扩展,而45度.135度方向没有得到扩展
//目的是使得在下一步的腐蚀操作中,保证腐蚀后的边缘与原图一致,
//而只有角点被腐蚀掉
Mat result;
dilate(thresh, result, CrossMat);
imshow("1:Cross", result);
//第二步:菱形核,【腐蚀操作】
//效果:使得第一步的结果在水平和垂直方向被腐蚀,而在45度.135度等方向也,
//有一定腐蚀效果,使用菱形而不用十字进行腐蚀是为了斜方向得到腐蚀
//第一第二步操作后的结果是:原图的边缘不发生变化,仅有角点被腐蚀
erode(result, result, diamondMat);
imshow("2:diamond", result);
//第三步:X型核对原图膨胀
//效果:使得原图在水平和垂直,45度,135度等倾斜方向都有扩展
Mat result2;
dilate(thresh, result2, X);
imshow("X", result2);
//第四步:正方形核,【腐蚀操作】
//效果:又腐蚀掉一次,第三第四部操作后的结果是,原图角点不发生变化,
//而水平和垂直方向的边缘似乎被腐蚀掉
erode(result2, result2, squareMat);
imshow("squ", result2);
//计算差值
absdiff(result2, result, result);
threshold(result, result, 30, 255, THRESH_BINARY);
//绘制
for (int i = 0; i < result.rows; i++){
//获取行指针
const uchar* data = result.ptr(i);
for (int j = 0; j < result.cols; j++){
//如果是角点,则绘制圆圈
if (data[j]){
circle(srcImg, Point(j, i), 8,
Scalar(0, 0, 255));
}
}
}
imshow("src", srcImg);
imshow("result", result);
waitKey(0);
}