C++(cv::mat)单类别和多类别nms

该文介绍了单类别和多类别非极大值抑制(NMS)的实现方法,用于处理目标检测中的重叠框。首先,按置信度降序排列框,然后计算两框的交并比(IoU),若超过阈值则去除低置信度的框。在多类别NMS中,先按类别拆分框,再分别应用单类别NMS策略。
摘要由CSDN通过智能技术生成

1.单类别nms

auto nms(const cv::Mat& boxes, float iou_thres){
  // 按照置信度降序排列所有的 box
  int n = boxes.rows;
  cv::Mat sorted_indices;
  // 获取排序后的索引
  cv::sortIdx(boxes.col(4), sorted_indices, cv::SORT_EVERY_COLUMN + cv::SORT_DESCENDING);
  // 根据排序后的索引对原始数组进行重排
  cv::Mat sorted_boxes(boxes.size(), boxes.type());
  for (int i = 0; i < sorted_indices.rows; i++) {
    //std::cout<<sorted_indices.at<int>(i)<<std::endl;
    int idx = sorted_indices.at<int>(i);
    boxes.row(idx).copyTo(sorted_boxes.row(i));
}

  // 定义一个向量,用于存储保留下来的 box 的索引值
  std::vector<int> keep_indices;

  // 定义一个向量,用于存储每个 box 的面积
  std::vector<float> area(n);
  for (int i = 0; i < n; ++i) {
      float x1 = sorted_boxes.at<float>(i, 0);
      float y1 = sorted_boxes.at<float>(i, 1);
      float x2 = sorted_boxes.at<float>(i, 2);
      float y2 = sorted_boxes.at<float>(i, 3);
      area[i] = (x2 - x1 + 1) * (y2 - y1 + 1);

      bool keep = true;
      for (int j = 0; j < keep_indices.size(); ++j) {
          int k = keep_indices[j];
          float xx1 = std::max(x1, sorted_boxes.at<float>(k, 0));
          float yy1 = std::max(y1, sorted_boxes.at<float>(k, 1));
          float xx2 = std::min(x2, sorted_boxes.at<float>(k, 2));
          float yy2 = std::min(y2, sorted_boxes.at<float>(k, 3));
          float w = std::max(0.0f, xx2 - xx1 + 1);
          float h = std::max(0.0f, yy2 - yy1 + 1);
          float overlap = w * h / (area[i] + area[k] - w * h);//两框交并比计算
          if (overlap >= iou_thres) {
              keep = false;
              break;
          }
      }
      if (keep) {
          keep_indices.push_back(i);
      }
  }
  
  // 根据保留下来的 box 的索引值,从原始的 box 信息中提取出保留下来的 box,并返回结果
  //cv::Mat kept_boxes(keep_indices.size(), 6, CV_32F);
  std::vector<std::vector<float>> kept_boxes(keep_indices.size(), std::vector<float>(6, 0));
  for (int i = 0; i < keep_indices.size(); ++i) {
      int k = keep_indices[i];
      //根据你的后处理自行返回结果:std::vector或cv::Mat
      //注释的为cv::Mat
      // kept_boxes.at<float>(i, 0) = sorted_boxes.at<float>(k, 0);
      // kept_boxes.at<float>(i, 1) = sorted_boxes.at<float>(k, 1);
      // kept_boxes.at<float>(i, 2) = sorted_boxes.at<float>(k, 2);
      // kept_boxes.at<float>(i, 3) = sorted_boxes.at<float>(k, 3);
      // kept_boxes.at<float>(i, 4) = sorted_boxes.at<float>(k, 4);
      // kept_boxes.at<float>(i, 5) = sorted_boxes.at<float>(k, 5);//classes 类别维度
      kept_boxes[i][0] = sorted_boxes.at<float>(k, 0);
      kept_boxes[i][1] = sorted_boxes.at<float>(k, 1);
      kept_boxes[i][2] = sorted_boxes.at<float>(k, 2);
      
      kept_boxes[i][3] = sorted_boxes.at<float>(k, 3);
      kept_boxes[i][4] = sorted_boxes.at<float>(k, 4);
      kept_boxes[i][5]= sorted_boxes.at<float>(k, 5);//classes
      
      
  }
  return kept_boxes;
}

2.多类别的nms

先把得出来的结果输出框,按类别进行划分

    //按类别拆分
    std::vector<cv::Mat> class_box_map;
    for(int c = 0; c < num_class; c++){
      cv::Mat classic_filter_res;
      for (int row = 0; row< filter_new_res.rows; row++) {
        if(static_cast<int>(filter_new_res.at<float>(row, 5)) == c){
          cv::Mat row_mat = filter_new_res.rowRange(row, row + 1);
          classic_filter_res.push_back(row_mat);
        }
      }
      class_box_map.push_back(classic_filter_res.clone());
    }

然后依次将每一类送入上文标题1的nms

for(int j = 0; j < class_box_map.size(); j++){
      if(!class_box_map[j].empty()){
        cv:: Mat copy_mat(class_box_map[j].size(), class_box_map[j].type());
        class_box_map[j].copyTo(copy_mat);//可能有的代码没必要加,可自行删除
        //std::cout<<"copy_mat: "<<copy_mat.row(0)<<std::endl;

        std::vector<std::vector<float>> final_boxes = nms(copy_mat, iou_thres);//nms

下面是使用C++OpenCV库实现边缘模板匹配算法匹配多个目标的完整代码,注释中有详细的说明: ```c++ #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { // 加载原始图像和模板图像 Mat img = imread("image.jpg"); Mat temp = imread("template.jpg"); // 将模板图像转换为灰度图像 Mat temp_gray; cvtColor(temp, temp_gray, COLOR_BGR2GRAY); // 使用Canny边缘检测算法检测模板图像的边缘 Mat edges; Canny(temp_gray, edges, 50, 200); // 在原始图像中搜索与模板图像匹配的边缘 Mat result; matchTemplate(img, edges, result, TM_CCOEFF_NORMED); // 使用阈值来确定匹配结果的位置 double threshold = 0.8; Mat locations; findNonZero(result > threshold, locations); // 在原始图像中绘制矩形框来标记匹配的位置 for (int i = 0; i < locations.total(); i++) { Point loc = locations.at<Point>(i); rectangle(img, loc, Point(loc.x + temp.cols, loc.y + temp.rows), Scalar(0, 0, 255), 2); } // 显示结果 namedWindow("Result", WINDOW_NORMAL); imshow("Result", img); waitKey(0); destroyAllWindows(); return 0; } ``` 这段代码与之前的代码基本相同,只是在绘制矩形框时使用了`locations.at<Point>(i)`来获取匹配结果的位置。这样可以遍历所有的匹配结果,并在原始图像中绘制矩形框来标记它们。 需要注意的是,如果图像中有多个目标,可能会出现匹配结果重叠的情况。这时可以对匹配结果进行非极大值抑制(Non-Maximum Suppression,NMS),只保留得分最高的匹配结果。NMS的具体实现可以参考其他的开源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值