findLights
本函数寻找轮廓,遍历每个轮廓,传入isLight()判断是否为灯条,是则判断颜色,将符合的灯条放入candidateLights容器
接下来将会对代码进行拆分讲解
vector<vector<cv::Point>> contours;
vector<cv::Vec4i> hierarchy;
cv::findContours(_binary, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
#ifdef DRAW_LIGHTS_CONTOURS
cv::Mat showSrc = _src.clone();
for(int i=0;i< contours.size();i++)
cv::drawContours(showSrc,contours,i,Scalar(255,0,0),2,LINE_8);
// namedWindow("showSrc",WINDOW_NORMAL);
imshow("showSrc",showSrc);
#endif
这里使用OpenCV的findContours函数寻找每一个轮廓
前面我们通过 setImage 函数对图像已经进行了预处理,这里已经是一个二值化图像
DRAW_LIGHTS_CONTOURS 宏查看匹配到的所有轮廓
注意:在调试过程中查看图片时不要出现同名的窗口
if (contours.size() < 2)
{
// printf("no 2 contours\n");
return;
}
如果轮廓的数量小于两个,那自然不必说,不可能存在装甲板
接下来一段代码便是本函数的核心了,如下:
for (auto & contour : contours)
{
RotatedRect r_rect = minAreaRect(contour);
Light light = Light(r_rect);
if (isLight(light, contour))
{
cv::Rect rect = r_rect.boundingRect();
if (0 <= rect.x && 0 <= rect.width && rect.x + rect.width <= _src.cols &&
0 <= rect.y && 0 <= rect.height && rect.y + rect.height <= _src.rows)
{
cv::Mat roi = _src(rect);
cv::Mat mask = _binary(rect);
Scalar sum = cv::mean(roi,mask);
light.lightColor = sum[2] > sum[0] ? RED : BLUE;
// 颜色不符合电控发的就不放入 1:RED 2:BLUE
if(light.lightColor == 2)
{
candidateLights.emplace_back(light);
#ifdef DRAW_LIGHTS_RRT
cv::Mat showSrc = _src.clone();
Point2f vertice_lights[4];
light.points(vertice_lights);
for (int i = 0; i < 4; i++) {
line(showSrc, vertice_lights[i], vertice_lights[(i + 1) % 4], CV_RGB(255, 0, 0),2,LINE_8);
}
//circle(showSrc,light.center,5,Scalar(0,0,0),-1);
// namedWindow("showSrc",WINDOW_NORMAL);
imshow("showLignt", showSrc);
#endif //DRAW_LIGHTS_RRT
}
}
}
}
接下来将一个一个部分进行介绍
for (auto & contour : contours)
for ( : )这个特性是C++11引入,这种用法这种其他语言例如 php,Java 等都有
这个写法可以实现对容器的遍历,auto是一个自动推导类型,可以自动判断返回的类型,如果不懂,当一个固定写法即可
RotatedRect r_rect = minAreaRect(contour);
Light light = Light(r_rect);
这里使用OpenCV的minAreaRect函数寻找每一个轮廓的最小外接矩形,同时创建一个Light的结构体来保存轮廓数据
Light这个结构体已经移动到 include/robot_struct.h 在 Armor_Detection.h 也保留了注释痕迹方便查看
接下去则是 isLight(light, contour) 函数,这个函数是对灯条的判断,如下:
bool ArmorDetector::isLight(Light& light, vector<Point> &cnt)
{
double height = light.height;
double width = light.width;
if(height <= 0 || width <= 0)
return false;
// 高一定要大于宽
bool standing_ok = height > width;
// 高宽比条件
double hw_ratio = height / width;
bool hw_ratio_ok = light_min_hw_ratio < hw_ratio && hw_ratio < light_max_hw_ratio;
// 外接矩形面积和像素点面积之比条件
double area_ratio = contourArea(cnt) / (height * width);
bool area_ratio_ok = light_min_area_ratio < area_ratio && area_ratio < light_max_area_ratio;
// 灯条角度条件
bool angle_ok = fabs(90.0 - light.angle) < light_max_angle;
// cout<<"angle: "<<light.angle<<endl;
// 限制面积条件
bool area_ok = contourArea(cnt) < light_max_area;
// 灯条判断的条件总集
bool is_light = hw_ratio_ok && area_ratio_ok && angle_ok && standing_ok && area_ok;
if(!is_light)
{
// cout<<hw_ratio<<" "<<contourArea(cnt) / light_max_area<<" "<<light.angle<<endl;
}
return is_light;
}
这个函数判断条件写的很清楚,这里不做解释
下面则使用 boundingRect() 函数获取轮廓矩形边界信息
cv::Rect rect = r_rect.boundingRect();
if (0 <= rect.x && 0 <= rect.width && rect.x + rect.width <= _src.cols &&
0 <= rect.y && 0 <= rect.height && rect.y + rect.height <= _src.rows)
{
}
if判断是一个异常错误验证,防止出现奇怪的数据被处理
cv::Mat roi = _src(rect);
cv::Mat mask = _binary(rect);
Scalar sum = cv::mean(roi,mask);
light.lightColor = sum[2] > sum[0] ? RED : BLUE;
// 颜色不符合电控发的就不放入 1:RED 2:BLUE
if(light.lightColor == 2)
{
candidateLights.emplace_back(light);
#ifdef DRAW_LIGHTS_RRT
cv::Mat showSrc = _src.clone();
Point2f vertice_lights[4];
light.points(vertice_lights);
for (int i = 0; i < 4; i++) {
line(showSrc, vertice_lights[i], vertice_lights[(i + 1) % 4], CV_RGB(255, 0, 0),2,LINE_8);
}
//circle(showSrc,light.center,5,Scalar(0,0,0),-1);
// namedWindow("showSrc",WINDOW_NORMAL);
imshow("showLignt", showSrc);
#endif //DRAW_LIGHTS_RRT
}
这里是对灯条的颜色进行判断,使用了roi和mask
ROI:感兴趣区域(简单的说就是截图部分)
Mask:掩摸区域(掩摸的部分就较为复杂,掩摸简单的理解就是用来标记要计算的区域)
这里的ROI和Mask都是在原图上取灯条的部分,再使用 mean 平均值函数来计算每一个通道平均值,返回一个长度为3的数组(简单理解)
sum[2] > sum[0] ? RED : BLUE;
c++的三目运算符判断颜色,这里已经计算了每一个通道的平均值,通过判断R通道和B通道哪个大来判断颜色
相信你应该知道RGB模型,也知道RGB模型在OpenCV里面的顺序是BGR
candidateLights.emplace_back(light);
然后将所有符合要求的灯条放入容器中