今天主要是研究另一个比较综合的例子。上一个例子只是简单地检测两个平行直线的线段长度和平行线之间的距离,主要应用了直线细化、霍夫变换等主要手段,那么这一次,我们来对三角形进行处理,对于初学者来说,这样一个三角形图片是经典的:
那么问题来了:
1. 求该三角形的周长
2. 求该三角形的面积
实际上这两个问题就是一个问题:三角形的位置信息?如果我们能得到三角形的三个顶点坐标,那么实际上我们就可以掌握这个三角形所有的信息。于是,我们将这个问题演化成这样一个问题:
求三角形三个顶点的坐标
我们不妨来整理一下思路:
1.该图像噪声比较明显,而且属于椒盐噪声,比较适合用中值滤波得到交清晰的图像;
2.进行边缘检测,对于相对来说比较复杂的图像,我们考虑用经典实用的canny算法进行边缘检测;
3.如果边缘存在厚度,我们要进行边缘细化,这张图边缘黑白分界明显,目测没有必要细化;
4.霍夫变换得到三条直线信息。想要得到线段范围信息,只能采用PPHT方法。
如果顺利的话,四步就能够得到我们想要的结果。那我们看看这样做的效果如何把:
Step1:滤波(在此之前转为灰度图像,此处略)
cvSmooth(gray, middle, CV_MEDIAN, 5, 5); --------->
Step2:边缘检测
cvCanny(middle, canny, 80, 160);
可以看出,不需要边缘细化
Step3:霍夫变换
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines;
lines = cvHoughLines2(canny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 100, 100, 2000);
int n = 0;
for (int i = 0; i < lines->total; ++i)
{
CvPoint* point = (CvPoint*)cvGetSeqElem(lines, i);
cvCircle(result, point[0], 3, CV_RGB(0, 255, 0));
cvCircle(result, point[1], 3, CV_RGB(0, 255, 0));
cvLine(result, point[0], point[1], CV_RGB(0, 255, 0));
n++;
我们可以看到,整体的检测效果还是不错的,但是三角形的顶点检测不一,也就是说三个边长并没有完全检测出来,有漏检测的问题。这个实际上是调参数很难弥补的问题,三条边长检测总会存在误差,所以我们要想办法来解决这个问题。
我们发现,虽然顶点没有检测得很好,但是直线方向检测的非常准确,如果我们能作出这三条直线,求出三条直线的交点,我们就可以完美地得到三角形的顶点,如图:
程序如下:
//这是找两条直线的交点的函数
CvPoint FindPoint(double k1, double b1, int judge1, double k2, double b2, int judge2)
{
CvPoint point;
if (judge1 == 0 && judge2 == 1)
{//第一条直线斜率不存在
point.x = k1;
point.y = k2 * point.x + b2;
}
else if (judge1 == 1 && judge2 == 0)
{
point.x = k2;
point.y = k1 * point.x + b1;
}
else if (judge1 == 0 && judge2 == 0)
{
point.x = 0;
point.y = 0;
}
else
{
point.x = (int)((b2 - b1) / (k1 - k2));
point.y = (int)(k1 * point.x + b1);
}
return point;
}
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines;
lines = cvHoughLines2(canny, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 100, 100, 2000);
int n = 0;
double k[3];//保存斜率值
double b[3];//保存截距值
double judge[3];//保存是否斜率不存在的判断值
for (int i = 0; i < lines->total; ++i)
{
CvPoint* point = (CvPoint*)cvGetSeqElem(lines, i);
if (point[0].x == point[1].x)//斜率不存在
{
k[i] = point[0].x;
b[i] = point[0].x;//保存x的值,便于函数计算
judge[i] = 0;//斜率不存在
}
else
{
judge[i] = 1;//斜率存在
//计算斜率
double k0 = (double)(point[0].y - point[1].y) / (double)(point[0].x - point[1].x);
//计算截距
double b0 = (double)(point[0].x * point[1].y - point[1].x * point[0].y) / (double)(point[0].x - point[1].x);
k[i] = k0;
b[i] = b0;
}
n++;
}
std::cout << n << endl;
//找三角形的三个顶点位置
CvPoint last1, last2, last3;
last1 = FindPoint(k[0], b[0], judge[0], k[1], b[1], judge[1]);
last2 = FindPoint(k[0], b[0], judge[0], k[2], b[2], judge[2]);
last3 = FindPoint(k[2], b[2], judge[2], k[1], b[1], judge[1]);
cvCircle(result, last1, 3, CV_RGB(255, 0, 0));
cvCircle(result, last2, 3, CV_RGB(255, 0, 0));
cvCircle(result, last3, 3, CV_RGB(255, 0, 0));
cvLine(result, last1, last2, CV_RGB(0, 255, 0));
cvLine(result, last1, last3, CV_RGB(0, 255, 0));
cvLine(result, last3, last2, CV_RGB(0, 255, 0));
是不是很不错呢。
得到了三个顶点信息后,那么周长、面积什么都是小意思了。
<!--EndFragment-->