目标检测阅读总结(一)之RCNN以及NMS

本文深入解析R-CNN目标检测流程,包括候选区域生成、特征提取、分类与边界框回归等步骤,并探讨硬负例挖掘技术及非极大值抑制算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开始看目标检测方面论文,里面有很多经典,会在这儿记录下论文中的优点和代码中的问题,也会把别人blog比较好的观点总结。

阅读顺序差不多按照: https://github.com/amusi/awesome-object-detection

R-CNN

参考:https://blog.youkuaiyun.com/shenxiaolu1984/article/details/51066975

https://blog.youkuaiyun.com/briblue/article/details/82012575

 

pipeline:

1. 使用selective search生成1k-2k个候选框;

2. 对于候选区域,提取cnn特征(4096-dim),这里是裁剪出来然后做一个16的padding,然后resize到227*227。(为了让bounding box拥有上下文内容context。)

3. 对于正负样本的分类,对于4096-dim feature接一个svm进行分类。

4. 为了减少识别的错误,在用一个线性回归模型对框进行微调。

4.1. 一个scale变换,对于中心点以及长宽;

4.2 这里的L2正则化很关键;

4.3 组的pair对很重要,不然会导致a hopeless learning problem。这里使用的iou阈值为0.6。

 

3.1. 由于负样本很多,所以会对于负样本使用hard negative mining。

hard negative mining 

R-CNN引用了这篇文章 “Object Detection with Discriminatively Trained Part Based Models.”

由于训练的阶段负样本过多,超过1w个,对于模型来说使用全部的负样本是不可行的,所以需要构建一个既有负样本又有hard negative的样本集合。

Bootstrapping methods:先用负样本组成集合训练,然后收集那些被错误分类的例子,去训练新的模型,这样重复几次。

proposed data-mining methods:为了找到一个属于训练集的小子集,初始的时候用原始的作为一个cache,每一次迭代会移除一些比较简单的样本,增加一些新的比较难的样本。

具体流程如下

1. 先初始一个$C_{t}$ \subseteq $D$, 这里的 D是原来的训练集。

2. 训练,直到参数稳定分出两个集合,一个是错误分类的,一个是正确分类的。

3. 缩小C_{t}, 去除那些被正确分类的,得到集合C_{t^{'}}

4. 增大C_{t+1},把 C_{t^{'}}和一些属于 D的样本形成并集,得到新一轮的子集C_{t+1}

Non-Maximum-Suppression

非极大值抑制,在目标检测,回归了很多bounding boxes出来之后,要最终确定哪一个是最终使用的时候,需要用nms进行bounding boxes的剔除。

具体过程:对于图片每一个类别,先找到置信度(网络predict的概率)最大的值,先对于这一类别的其他框做iou的操作,然后有一个nms的iou超参,高于设定iou的就被剔除。

如此对于每一个类别进行重复操作,这样排除了很多置信度低的框。

下面贴一下自己写的nms代码,并简单分析一下。

#include <iostream>
#include <string>
#include <set>
#include <cmath>
#include <vector>
#include <tuple>
using namespace std;
float iou_cal(vector<float> box1, vector<float> box2){
    float right_x = min(box1[2],box2[2]);
    float right_y = min(box1[3],box2[3]);
    float left_x = max(box1[2],box2[2]);
    float left_y = max(box1[3],box2[3]);
    
    float overlap = fmax(0.,left_x-right_x) * fmax(0.,left_y-right_y);
    float area_sum = (box1[2]-box1[0])*(box1[3]-box1[1])+(box2[2]-box2[0])*(box2[3]-box2[1]);
    
    return overlap/(area_sum-overlap);
}
tuple<vector<float>,int> find_base(vector<vector<float> > boxes){
    vector<float> boxes_max{-1,-1,-1,-1};
    float max_pb = 0.;
    int index;
    for(int i = 0; i<boxes.size();i++){
        if(boxes[i][0]>max_pb){
            max_pb = boxes[i][0];
            boxes_max[0] = boxes[i][1];
            boxes_max[1] = boxes[i][2];
            boxes_max[2] = boxes[i][3];
            boxes_max[3] = boxes[i][4];
            index = i;
        }
    }
    return make_tuple(boxes_max,index);
    
}
void nms(vector<vector<float> > &boxes,float iou_value){ // single class
    tuple<vector<float>,int> box_value;
    vector<float> boxes_base;
    float iou = 0.0;
    int index;
    box_value = find_base(boxes);
    boxes_base = get<0>(box_value);
    index = get<1>(box_value);
    for(int i=0;i<boxes.size();i++){
        if(i!=index){
            iou = iou_cal(boxes_base,boxes[i]);
            if(iou>iou_value){
                boxes.erase(boxes.begin()+i);
            }
        }
    }
}
void print_boxes(vector<vector<float> > boxes){
    for(int i=0;i<boxes.size();i++){
        for(int j=0;j<boxes[0].size();j++){
            cout << boxes[i][j] << " " ;
        }
        cout << endl;
    }
}

int main(int argc, const char * argv[]) {
    vector<float> box1{0.6,100,98,300,400};
    vector<float> box2{0.7,85,60,250,500};
    vector<float> box3{0.8,70,49,200,350};
    vector<vector<float> > boxes;
    boxes.push_back(box1);
    boxes.push_back(box2);
    boxes.push_back(box3);
    print_boxes(boxes);
    nms(boxes,0.5);
    print_boxes(boxes);
    return 0;
}

上面代码考虑单一类别的情况,多类别加一个for循环即可,首先是要找到置信度最大的候选框,通过find_base函数。之后进行for循环,对于其他候选框进行判断,计算iou占比,如果小于设定的阈值(这里设定的为0.5)那么就剔除这个框。

tip:c++语法要注意nms的输入应为引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值