这是一张经过处理后的红灯的图像,我们需要找到其中的红灯,可以看到是两个圆,用霍夫圆之后发现其中调参非常麻烦,于是写了一个根据轮廓来分析圆的算法。
算法思想:findContours()找到图像的轮廓,使用minEnclosingCircle()找到轮廓的最小包围矩形,计算轮廓上的每个点到圆心的距离和半径的差值,进行累加(简单来说就是求轮廓中每个点到圆心距离和半径的方差),当累加和小于某个值时,我们认为它是一个圆。
核心函数:
//对轮廓进行分析
float calculateCircularity(vector contours)
{
Point2f center;
float radius = 0;
minEnclosingCircle((Mat)contours, center, radius);
float fsum = 0;
float fcompare = 0;
for (int i = 0; i < contours.size(); i++)
{
Point2f ptmp = contours[i];
//计算距离
float fdistance = sqrt(((float)ptmp.x - center.x)*((float)ptmp.x - center.x) + ((float)ptmp.y - center.y)*((float)ptmp.y - center.y));
//累加距离到圆心的差值
float fdiff = abs(fdistance - radius);
fsum = fsum + fdiff;
}
fcompare = fsum / (float)contours.size();
return fcompare;
}
首先找到轮廓,并且对绘制出轮廓,蓝色为轮廓,其中包含了我们需要的红灯和不需要的数字
vector>contours;
vectorhierarchy;
findContours(binary,contours,hierarchy, RETR_LIST,CHAIN_APPROX_SIMPLE);
drawContours(src, contours, -1, Scalar(255, 0, 0));
我们对提取的每个轮廓进行圆的特性分析,并且输出得到的差值累加和
可以看到有两个累加和比较小的,不确定是否是两个红灯,暂时设置阈值为0.6,将轮廓分析后累加和小于阈值的用绿色进行绘制
成功找到两个红灯
下面贴上整个工程代码
#include
#include
using namespace std;
using namespace cv;
//对轮廓进行分析
float calculateCircularity(vector contours)
{
Point2f center;
float radius = 0;
minEnclosingCircle((Mat)contours, center, radius);
float fsum = 0;
float fcompare = 0;
for (int i = 0; i < contours.size(); i++)
{
Point2f ptmp = contours[i];
//计算距离
float fdistance = sqrt(((float)ptmp.x - center.x)*((float)ptmp.x - center.x) + ((float)ptmp.y - center.y)*((float)ptmp.y - center.y));
//累加距离到圆心的差值
float fdiff = abs(fdistance - radius);
fsum = fsum + fdiff;
}
fcompare = fsum / (float)contours.size();
return fcompare;
}
int main()
{
Mat src = imread("cc.png");//读入有圆的图片
imwrite("src.jpg", src);
//红绿灯在图像上方,可以选择只分析上半部分图像
Mat src_copy = src(Rect(0, 0, src.cols, src.rows / 2));
Mat gray;
Mat binary;
vectormatSplit;
cvtColor(src_copy, gray, COLOR_BGR2GRAY);
//二值化
threshold(gray, binary, 100, 255, THRESH_OTSU);
//闭操作
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
morphologyEx(binary, binary, MORPH_CLOSE, kernel);
//找轮廓
vector>contours;
vectorhierarchy;
findContours(binary,contours,hierarchy, RETR_LIST,CHAIN_APPROX_SIMPLE);
vector>DrawContour;
for (int i = 0; i < contours.size(); i++)
{
//对每个轮廓进行分析
float fCircle = calculateCircularity(contours[i]);
cout << fCircle << endl;
//符合圆的特性,则加入vector
if(fCircle<0.6)
DrawContour.push_back(contours[i]);
}
drawContours(src, DrawContour, -1, Scalar(0, 255, 0), 3);
imwrite("circle.jpg", src);
waitKey(0);
return 0;
}