原理
主要是在调用模板匹配函数后,生成了结果图,对结果图进行筛选,对于大于阈值并且不重叠的进行保留,最终实现多目标的匹配。对于判断不重叠的方式主要使用nms的方式。
nms:对于每个矩形框,从小到大进行排序,然后进行遍历从第二个开始判断与矩形框的list的每一个是否相交,相交则舍弃,否则保留,一下为代码实现,全部复制后进行编译即可运行,记得自己替换模板与图片路径。
代码
所需的头文件
#include <opencv2/opencv.hpp>
#include <algorithm>
结构体的定义,方便保存数据
/**
* @author stf
* @brief nms data struct
* @date 2022-12-27
* @param val match template min val
* @param loc match template min loc
* @param roi match template result rect roi
* @param getRect Function:use loc and temp size to calibrate roi
**/
struct nms_data_struct
{
float val;
cv::Point loc;
cv::Rect roi;
void getRect(cv::Size temp_size)
{
roi=cv::Rect(loc.x,loc.y,temp_size.width,temp_size.height);
}
};
函数编写,其中,comp函数是比较函数,使用algorithm方法;nms_detect函数是判断两个矩形是否相交,相交则直接舍弃,否则保留;nms_temp_min是主要实现函数,最后以图片形式保存结果
/**
* @author stf
* @date 2022-12-27
* @brief sort compare function
**/
bool comp(nms_data_struct data1,nms_data_struct data2)
{
return data1.val<data2.val;
}
/**
* @author stf
* @brief nms detect if rect is overlap
* @date 2022-12-27
* @param data Contains the structure of the matrix to be judged
* @param data_list rect list for judged
* @return true:the rect is not overlap,false:the rect is overlap
**/
int nms_detect(nms_data_struct data,std::vector<nms_data_struct> data_list)
{
for(int i=0;i<data_list.size();i++)
{
cv::Rect rect=data.roi&data_list[i].roi;
if(rect.width||rect.height)
return false;
}
return true;
}
/**
* @author stf
* @brief nms to calibrate min match template
* @date 2022-12-27
* @param input optional,to draw on this img
* @param result matchTemplate result img
* @param temp_size template image size
* @param save_output whether use save output
* @param thr min threshold
* @return 0 It's fixed at the moment,You can add a judgment to return other
**/
int nms_temp_min(cv::Mat &input,cv::Mat result,cv::Size temp_size,bool save_output=true,float thr=0.03)
{
// get minVal vector and minLoc vector
std::vector<nms_data_struct> nms_data;
for(int i=0;i<result.cols;i++)
{
for(int j=0;j<result.rows;j++)
{
if(result.at<float>(j,i)<=thr)
{
nms_data_struct data;
data.val=result.at<float>(j,i);
data.loc=cv::Point(i,j);
data.getRect(temp_size);
nms_data.push_back(data);
}
}
}
//Sort from smallest to largest
std::sort(nms_data.begin(),nms_data.end(),comp);
//nms
std::vector<nms_data_struct> nms_output_list;
for(int i=0;i<nms_data.size();i++)
{
if(i==0)
{
nms_data_struct data;
data.val=nms_data[i].val;
data.loc=nms_data[i].loc;
data.roi=nms_data[i].roi;
nms_output_list.push_back(data);
continue;
}
int ret=nms_detect(nms_data[i],nms_output_list);
if(ret)
{
nms_data_struct data;
data.val=nms_data[i].val;
data.loc=nms_data[i].loc;
data.getRect(temp_size);
nms_output_list.push_back(data);
}
}
//save output in current path
if(save_output)
{
for(int i=0;i<nms_output_list.size();i++)
{
cv::rectangle(input,nms_output_list[i].roi,cv::Scalar(0,255,0));
cv::Size text_size=cv::getTextSize(std::to_string(nms_output_list[i].val),cv::FONT_HERSHEY_COMPLEX,1.0f,1,(int *)int());
cv::rectangle(input,cv::Rect(nms_output_list[i].loc.x,nms_output_list[i].loc.y-text_size.height,text_size.width,\
text_size.height),cv::Scalar(0,255,0),-1);
cv::putText(input,std::to_string(nms_output_list[i].val),nms_output_list[i].loc,cv::FONT_HERSHEY_COMPLEX,1.0f,\
cv::Scalar(0,0,0));
}
cv::imwrite("../nms_output.jpg",input);
}
return 0;
}
主函数编写,调用以上函数
int main()
{
cv::Mat img=cv::imread("../b1.jpg");
cv::Mat temp=cv::imread("../temp_bd.jpg");
cv::Mat result;
cv::matchTemplate(img,temp,result,1);
nms_temp_min(img,result,temp.size(),true,0.06);
return 0;
}