chooseTarget
本函数是循环candidateArmors容器 推理numROIs容器内所有数字将推理结果,置信度,ID传入get_max() 更新装甲板的置信度和ID将符合要求的装甲板放入finalArmors容器
这个函数是非常重要的一个函数,可以直接判断装甲板的正确与否(通过数字识别),同时对装甲板的击打策略进行调整
if(candidateArmors.empty())
{
//cout<<"no target!!"<<endl;
return;
}
空容器检测,不可或缺的异常检测机制
单个装甲板的检测机制,分数直接打满,DNN推理是采用onnx模型,这里简单介绍一些数字识别的部分
数字识别的实现方法较多,我们采用机器学习的方法,数字识别主要是一种图像分类的问题,使用SVM、聚类算法、神经网络均可以实现
数字识别的训练我们通常会使用Python来进行,Pytorch框架是目前非常流行的一套深度学习框架,搭配Numpy我们可以轻松实现非常多的东西
我们使用 Pytorch 搭建出来的模型通常是 pt 格式,无法在C++下使用,但是有一种通用的模型结构 onnx
因为深度学习的框架和模型格式过多和杂,所以推出了一种标准的格式,也就是 onnx ,其允许在不同的深度框架之间使用
通过 onnx 模型,我们就可以轻松实现模型的部署,从而实现数字识别
如果你对机器学习、深度学习部分有一定的兴趣,可以观看最后的数字识别篇章
将会告诉你部分机器学习、深度学习的知识,同时给出深度学习的部分学习方法
不感兴趣可以直接跳过,不要求一定掌握数字识别部分
else if(candidateArmors.size() == 1)
{
// cout<<"get 1 target!!"<<endl;
// DNN推理
Mat out_blobs = Dnn_Detect.net_forward(numROIs);
float *outs = (float*)out_blobs.data;
if (get_max(outs, candidateArmors[0].confidence, candidateArmors[0].id))
{
#ifdef SHOW_NUMROI
cv::Mat numDst;
resize(numROIs[0],numDst,Size(200,300));
string name = to_string(candidateArmors[0].id) + ":" + to_string(candidateArmors[0].confidence*100) + "%";
// namedWindow("name",WINDOW_NORMAL);
imshow("name",numDst);
// printf("%d",armor.id);
// std::cout<<"number: "<<armor.id<<" type: "<<armor.type<<std::endl;
// string file_name = "../data/"+std::to_string(0)+"_"+std::to_string(cnt_count)+".jpg";
// cout<<file_name<<endl;
// imwrite(file_name,numDst);
// cnt_count++;
#endif
// 一个装甲板分数直接打满
candidateArmors[0].grade = 100;
finalArmors.emplace_back(candidateArmors[0]);
}
}
这里通过数字识别的方法得到装甲板的ID
bool ArmorDetector::get_max(const float *data, float &confidence, int &id)
{
// 默认初始化
confidence = data[0];
id = 0;
// 寻找最大置信度的ID
for (int i=0;i<categories;i++)
{
if (data[i] > confidence)
{
confidence = data[i];
id = i;
}
}
// 如果id为0或者id为2(工程) | 置信度小于阈值
if(id == 0 || id == 2 || confidence < thresh_confidence)
return false;
else
return true;
}
get_max函数是对模型的推导结果进行判断,得到最大置信度的ID,同时在这里会将工程机器人和错误ID进行删除
这意味着我们将不会对工程机器人进行锁定
int ArmorDetector::armorGrade(const Armor& checkArmor)
{
/////////id优先级打分项目////////////////////////
int id_grade;
int check_id = checkArmor.id;
id_grade = check_id == 1 ? 100 : 80;
////////end///////////////////////////////////
/////////最大装甲板板打分项目/////////////////////
// 最大装甲板,用面积,找一个标准值(固定距离(比如3/4米),装甲板大小(Armor.area)大约是多少,分大小装甲板)
// 比标准大就是100,小就是做比例,,,,可能小的得出来的值会很小
int height_grade;
double hRotation = checkArmor.size.height / height_standard;
if(candidateArmors.size()==1) hRotation=1;
height_grade = hRotation * 60;
//////////end/////////////////////////////////
////////靠近图像中心打分项目//////////////////////
// 靠近中心,与中心做距离,设定标准值,看图传和摄像头看到的画面的差异
int near_grade;
double pts_distance = POINT_DIST(checkArmor.center, Point2f(_src.cols * 0.5, _src.rows * 0.5));
near_grade = pts_distance/near_standard < 1 ? 100 : (near_standard/pts_distance) * 100;
////////end//////////////////////////////////
// 下面的系数得详细调节;
int final_grade = id_grade * id_grade_ratio +
height_grade * height_grade_ratio +
near_grade * near_grade_ratio;
return final_grade;
}
armorGrade 函数是装甲板的打分策略,我们通过ID、装甲板面积大小、与图像的中心距离来进行打分
选出我们认为当前最好的一个装甲板,为后续装甲板的锁定做准备
到此,整个识别的流程基本结束,其中一些不展开讲解的细节,例如数字识别(非必须掌握)、Number_DNN
将会在后面展开讲解