rm中,装甲板的识别在比赛中可谓是最基础的算法。而在各个开源框架中,该算法也可以说最为成熟。出于学习目的,之后将对比多个高校或网络代码(),尝试学习各个rm装甲板识别算法的优点和流程。


这次先是东南大学(SEU-SuperNova-CVRA)开源的视觉算法:

cv::Mat binBrightImg;
cvtColor(_roiImg, _grayImg, COLOR_BGR2GRAY, 1);
cv::threshold(_grayImg, binBrightImg, _param.brightness_threshold, 255, cv::THRESH_BINARY);

先将_roiImg转成灰度图,方便后续转成二值图。
之后再将上图转为二值图,放到binBrightImg中,这里的_param.brightness_threshold是阈值,应用的为cv::THRESH_BINARY的方法。个人认为这里的方法还可以用CV_THRESH_OTSU也就是大津算法,效率有待后续测试。

下面这段代码先不要看,等到下述中出现“请回到顶部”字样的时候观看

// 把一个3通道图像转换成3个单通道图像
split(_roiImg,channels);//分离色彩通道
//预处理删除己方装甲板颜色
if(_enemy_color==RED)_grayImg=channels.at(2)-channels.at(0);//Get red-blue image;else _grayImg=channels.at(0)-channels.at(2);//Get blue-red image;
————————————————
版权声明:本文为CSDN博主「Raring_Ringtail」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010750137/article/details/96428059
cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
dilate(binBrightImg, binBrightImg, element);

进行膨胀处理,将二值图中的灯条变粗。

vector<vector<Point>> lightContours;
cv::findContours(binBrightImg.clone(), lightContours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

寻找轮廓,将找到的轮廓放置在lightcountours中,而这一步据网上说是相对费时的一段时间,其很大程度决定于前文的预处理情况。

之后是一个大循环来检测轮廓中的灯条。具体操作作为注释

for(const auto& contour : lightContours)//对每个轮廓都进行处理{//得到轮廓的面积//float lightContourArea = contourArea(contour);//筛选掉噪声//if(contour.size() <= 5 ||lightContourArea < _param.light_min_area) continue;//椭圆拟合生成相应的旋转矩形(注:只是用矩形去拟合灯条,方便获取灯条的长宽比等)RotatedRect lightRec = fitEllipse(contour);adjustRec(lightRec, ANGLE_TO_UP);//筛选出需要的灯条//if(lightRec.size.width / lightRec.size.height > _param.light_max_ratio ||lightContourArea / lightRec.size.area() < _param.light_contour_min_solidity) continue;//扩展矩形的长宽//lightRec.size.width *= _param.light_color_detect_extend_ratio;lightRec.size.height *= _param.light_color_detect_extend_ratio;//获取矩形的外接矩形框//Rect lightRect = lightRec.boundingRect();const Rect srcBound(Point(0, 0), _roiImg.size());//上面定义的lightRect为灯条的外接矩形,与检测区域srcBound交集。但实际上我不明白这一步有什么用。如果是为了排除检测区域外的灯条,可是轮廓就是提取自一开始的roiImg的吧。////此处我一开始没反应过来是为了做什么,经过查找才明白:&=操作是将两个矩阵的交集再存放到lightRect中。相类似的操作还有|(并集),+point(a,b)(平移一个point的向量(a,b)位移),+size(a,b)(长和宽分别加上a和b)//lightRect &= srcBound;//以后的操作(一直到#ifdef)都是为了颜色处理,特别是关于识别灯条是蓝色还是红色////但由于操作十分繁琐,所以我参考了网上来自江达小记的做法,请回到顶部//Mat lightImg = _roiImg(lightRect);Mat lightMask = Mat::zeros(lightRect.size(), CV_8UC1);Point2f lightVertexArray[4];lightRec.points(lightVertexArray);std::vector<Point> lightVertex;for(int i = 0; i < 4; i++){lightVertex.emplace_back(Point(lightVertexArray[i].x - lightRect.tl().x,lightVertexArray[i].y - lightRect.tl().y));}fillConvexPoly(lightMask, lightVertex, 255);if(lightImg.size().area() <= 0 || lightMask.size().area() <= 0) continue;cv::dilate(lightMask, lightMask, element);const Scalar meanVal = mean(lightImg, lightMask);if(((_enemy_color == BLUE) && (meanVal[BLUE] - meanVal[RED] > 20.0)) || (_enemy_color == RED && meanVal[RED] - meanVal[BLUE] > 20.0)){lightInfos.push_back(LightDescriptor(lightRec));}//若使用了两通道值相减的方法,则这里可以直接将结果保存下来。注意放入的是lightrec而不是lightrect的外接矩形信息//lightInfos.push_back(LightDescriptor(lightRec));

到这里,已经将各可能的灯条放入到lightInfos中去了,可是我们的目的是找到装甲板,所以下面的代码将会在这方面展开:

        //检查是否检测到灯条//if(lightInfos.empty()){return _flag = ARMOR_NO;}}//循环结束{//用到了C++11的lambda(可简单看作函数对象),设置了ld1和ld2两个参数,依照灯条中心的x坐标从左到右(opencv的坐标轴为横x竖y)。center为point2f类型的。//sort(lightInfos.begin(), lightInfos.end(), [](const LightDescriptor& ld1, const LightDescriptor& ld2){return ld1.center.x < ld2.center.x;});//设一个长为lightInfos.size(),值都为-1的数组//vector<int> minRightIndices(lightInfos.size(), -1);//遍历每一种组合//for(size_t i = 0; i < lightInfos.size(); i++){for(size_t j = i + 1; (j < lightInfos.size()); j++){const LightDescriptor& leftLight  = lightInfos[i];const LightDescriptor& rightLight = lightInfos[j];//计算左灯和右灯的角度差//float angleDiff_ = abs(leftLight.angle - rightLight.angle);//计算左灯和右灯的长度差之比(越相近该值越小)//float LenDiff_ratio = abs(leftLight.length - rightLight.length) / max(leftLight.length, rightLight.length);//通过阈值筛选灯条//if(angleDiff_ > _param.light_max_angle_diff_ ||LenDiff_ratio > _param.light_max_height_diff_ratio_){continue;}/**   proper location: // y value of light bar close enough *            // ratio of length and width is proper*///计算左右灯条中心距离//float dis = cvex::distance(leftLight.center, rightLight.center);//计算左右灯条长度的均值//float meanLen = (leftLight.length + rightLight.length) / 2;//灯条y的差//float yDiff = abs(leftLight.center.y - rightLight.center.y);//y差值的比率//float yDiff_ratio = yDiff / meanLen;//同前//float xDiff = abs(leftLight.center.x - rightLight.center.x);float xDiff_ratio = xDiff / meanLen;//灯条的距离与长度的比值(也就是嫌疑装甲板长和宽的比值)//float ratio = dis / meanLen;//对上面各量筛选,如果y差太大(y最好越相近越好),或者x差的太小,又或者装甲板长宽比不合适就排除掉。//if(yDiff_ratio > _param.light_max_y_diff_ratio_ ||xDiff_ratio < _param.light_min_x_diff_ratio_ ||ratio > _param.armor_max_aspect_ratio_ ||ratio < _param.armor_min_aspect_ratio_){continue;}// calculate pairs' info //通过长宽比来确定是大的还是小的装甲板//int armorType = ratio > _param.armor_big_armor_ratio ? BIG_ARMOR : SMALL_ARMOR;// calculate the rotation scorefloat ratiOff = (armorType == BIG_ARMOR) ? max(_param.armor_big_armor_ratio - ratio, float(0)) : max(_param.armor_small_armor_ratio - ratio, float(0));float yOff = yDiff / meanLen;//应该是rotationScore越接近0越好,看后续用处//float rotationScore = -(ratiOff * ratiOff + yOff * yOff);//生成相应的装甲板//ArmorDescriptor armor(leftLight, rightLight, armorType, _grayImg, rotationScore, _param);//将获得的嫌疑装甲板放到armors中去//_armors.emplace_back(armor);break;}}//此处删除debug内容////没找到的话。。//if(_armors.empty()){return _flag = ARMOR_NO;}}//此处删除调试信息////delete the fake armors_armors.erase(remove_if(_armors.begin(), _armors.end(), [](ArmorDescriptor& i){//这里就是识别装甲板的算法了//return !(i.isArmorPattern());}), _armors.end());//江达小记版本//_armors.erase(remove_if(_armors.begin(), _armors.end(), [this](ArmorDescriptor& i){//lamdba函数判断是不是装甲板,将装甲板中心的图片提取后让识别函数去识别,识别可以用svm或者模板匹配等return 0==(i.isArmorPattern(_small_Armor_template,_big_Armor_template,lastEnemy));
} ), _armors.end());//没有一个是装甲板的情况//if(_armors.empty()){_targetArmor.clear();//看是目标丢失还是没识别出来//if(_flag == ARMOR_LOCAL){//cout << "Tracking lost" << endl;return _flag = ARMOR_LOST;}else{//cout << "No armor pattern detected." << endl;return _flag = ARMOR_NO;}}//calculate the final scorefor(auto & armor : _armors){armor.finalScore = armor.sizeScore + armor.distScore + armor.rotationScore;}//choose the one with highest score, store it on _targetArmorstd::sort(_armors.begin(), _armors.end(), [](const ArmorDescriptor & a, const ArmorDescriptor & b){return a.finalScore > b.finalScore;});_targetArmor = _armors[0];//update the flag status    _trackCnt++;

而识别装甲板算法(也就是原文中的i.isArmorPattern())等后续有机会再准备。

转载于:https://www.cnblogs.com/IaCorse/p/11567363.html

东南大学RM装甲板识别算法详解相关推荐

  1. RoboMaster视觉教程(4)装甲板识别算法

    RoboMaster视觉教程(4)装甲板识别算法 概览 下面是一些资料链接,篇篇经典! 装甲板识别 test_sentry.cpp 分析一下装甲板 识别函数 int ArmorDetector::de ...

  2. 视觉组考核——装甲板识别

    视觉组考核--装甲板识别 识别Robomaster的装甲板的简易程序 算法分析 装甲板识别主要分这几步 图像处理->提取灯柱同时对灯柱进行筛选->灯条匹配->装甲板的筛选 1.图像处 ...

  3. python模拟手写笔迹_Python实现基于KNN算法的笔迹识别功能详解

    本文实例讲述了Python实现基于KNN算法的笔迹识别功能.分享给大家供大家参考,具体如下: 需要用到: Numpy库 Pandas库 手写识别数据 点击此处本站下载. 数据说明: 数据共有785列, ...

  4. 人脸识别系列三 | MTCNN算法详解上篇

    前言 我们前面分享了PCA,Fisher Face,LBPH三种传统的人脸识别算法,Dlib人脸检测算法.今天我们开始分享一下MTCNN算法,这个算法可以将人脸检测和特征点检测结合起来,并且MTCNN ...

  5. Robomaster装甲板识别-基于python+opencv的思路分享

    Robomaster视觉-装甲板识别 环境:windows10.pycharm2017..python3.64.opencv3 ***先上个效果图吧,图中是加了一些其他算法的,不是单独的识别,还有一些 ...

  6. Matlab人脸检测算法详解

    这是一个Matlab人脸检测算法详解 前言 人脸检测结果 算法详解 源代码解析 所调用函数解析 bwlabel(BW,n) regionprops rectangle 总结 前言 目前主流的人脸检测与 ...

  7. 目标检测 RCNN算法详解

    原文:http://blog.csdn.net/shenxiaolu1984/article/details/51066975 [目标检测]RCNN算法详解 Girshick, Ross, et al ...

  8. YOLOv5算法详解

    目录 1.需求解读 2.YOLOv5算法简介 3.YOLOv5算法详解 3.1 YOLOv5网络架构 3.2 YOLOv5实现细节详解 3.2.1 YOLOv5基础组件 3.2.2 输入端细节详解 3 ...

  9. 多重背包O(N*V)算法详解(——使用单调队列)

    多重背包O(N*V)算法详解(--使用单调队列) 多重背包问题: 有N种物品和容量为V的背包,若第i种物品,容量为v[i],价值为w[i],共有n[i]件.怎样装才能使背包内的物品总价值最大? 网上关 ...

最新文章

  1. 损失函数的可视化:浅论模型的参数空间与正则
  2. c free 使用MSDN library定制
  3. PHP 设计模式之策略模式
  4. 恭喜!清华大学,再添院士!
  5. 求老板放过!我Java8还没用呢,Java14又来了...
  6. OpenGL进阶演示样例1——动态画线(虚线、实线、颜色、速度等)
  7. golang协程——通道channel阻塞
  8. datetime 索引_超全的数据库建表/SQL/索引规范,适合贴在工位上!
  9. Golang groupcache LRU 缓存简介与用法
  10. ENVI Classic 二次开发简介
  11. GB28181 密解播放器
  12. 微信小程序MINA框架介绍
  13. CUX:固定资产台账报表源代码(信息较全)
  14. 关于项目管理的一些个人见解
  15. viewModel生命周期
  16. matlab实现多元高斯分布概率密度计算
  17. 将项目部署到云服务器上
  18. 王者荣耀 -- 上单及装备
  19. Python使用RMF聚类分析客户价值
  20. 题目0171-相同数字的积木游戏1

热门文章

  1. Vue.js-资料-组件化思想 —上
  2. Android的CheckBox(多选框)
  3. 20145309 《网络对抗技术》信息搜集与漏洞扫描
  4. Linux下chkconfig命令介绍
  5. 团队开发冲刺1.2(2015.5.10)
  6. 为什么前端工程师很难找?
  7. 利用c#开发一个telnet unix服务器或者防火墙的小工具(转)
  8. MapReduce之collect过程分析
  9. FPGA RAM存储器设计
  10. (27)呼吸灯verilog与VHDL编码(学无止境)