1. IOU
交並比(Intersection-over-Union,IoU),目標檢測中使用的一個概念,是產生的候選框(candidate bound)與原標記框(ground truth bound)的交疊率,即它們的交集與並集的比值。最理想情況是完全重疊,即比值為1。
計算公式:
C++代碼:
struct bbox
{
int m_left;
int m_top;
int m_width;
int m_height;
bbox() {}
bbox(int left, int top, int width, int height)
{
m_left = left;
m_top = top;
m_width = width;
m_height = height;
}
};
float IOU_compute(const bbox b1, const bbox b2)
{
w = max(min((b1.m_left + b1.m_width), (b2.m_left + b2.m_width)) - max(b1.m_left, b2.m_left), 0);
h = max(min((b1.m_top + b1.m_height), (b2.m_top + b2.m_height)) - max(b1.m_top, b2.m_top), 0);
return w*h / (b1.m_width*b1.m_height + b2.m_width*b2.m_height - w*h);
}
2. NMS
NMS(non maximum suppression),中文名非極大值抑制,在很多計算機視覺任務中都有廣泛應用,如:邊緣檢測、目標檢測等。
在物體檢測中NMS(Non-maximum suppression)非極大抑制應用十分廣泛,其目的是為了消除多余的框,找到最佳的物體檢測的位置。
在RCNN系列算法中,會從一張圖片中找出很多個候選框(可能包含物體的矩形邊框),然后為每個矩形框為做類別分類概率。
就像上面的圖片一樣,定位一個車輛,最后算法就找出了一堆的方框,我們需要判別哪些矩形框是沒用的。
非極大值抑制:先假設有6個候選框,根據分類器類別分類概率做排序,從小到大分別屬於車輛的概率分別為A、B、C、D、E、F。
- 從最大概率矩形框(即面積最大的框)F開始,分別判斷A~E與F的重疊度IOU是否大於某個設定的閾值;
- 假設B、D與F的重疊度超過閾值,那么就扔掉B、D(因為超過閾值,說明D與F或者B與F,已經有很大部分是重疊的,那我們保留面積最大的F即可,其余小面積的B,D就是多余的,用F完全可以表示一個物體了,所以保留F丟掉B,D);並標記第一個矩形框F,是我們保留下來的。
- 從剩下的矩形框A、C、E中,選擇概率最大的E,然后判斷E與A、C的重疊度,重疊度大於一定的閾值,那么就扔掉;並標記E是我們保留下來的第二個矩形框。
- 一直重復這個過程,找到所有曾經被保留下來的矩形框。
C++代碼:
//升序排列
bool cmpScore(Bbox lsh, Bbox rsh) {
if (lsh.score < rsh.score)
return true;
else
return false;
}
void nms(vector<Bbox> &boundingBox_, const float overlap_threshold, string modelname = "Union"){
if(boundingBox_.empty()){
return;
}
//對各個候選框根據score的大小進行升序排列
sort(boundingBox_.begin(), boundingBox_.end(), cmpScore);
float IOU = 0;
float maxX = 0;
float maxY = 0;
float minX = 0;
float minY = 0;
vector<int> vPick;
int nPick = 0;
multimap<float, int> vScores; //存放升序排列后的score和對應的序號
const int num_boxes = boundingBox_.size();
vPick.resize(num_boxes);
for (int i = 0; i < num_boxes; ++i){
vScores.insert(pair<float, int>(boundingBox_[i].score, i));
}
while(vScores.size() > 0){
int last = vScores.rbegin()->second; //反向迭代器,獲得vScores序列的最后那個序列號
vPick[nPick] = last;
nPick += 1;
for (multimap<float, int>::iterator it = vScores.begin(); it != vScores.end();){
int it_idx = it->second;
maxX = max(boundingBox_.at(it_idx).x1, boundingBox_.at(last).x1);
maxY = max(boundingBox_.at(it_idx).y1, boundingBox_.at(last).y1);
minX = min(boundingBox_.at(it_idx).x2, boundingBox_.at(last).x2);
minY = min(boundingBox_.at(it_idx).y2, boundingBox_.at(last).y2);
//轉換成了兩個邊界框相交區域的邊長
maxX = ((minX-maxX+1)>0)? (minX-maxX+1) : 0;
maxY = ((minY-maxY+1)>0)? (minY-maxY+1) : 0;
//求交並比IOU
IOU = (maxX * maxY)/(boundingBox_.at(it_idx).area + boundingBox_.at(last).area - IOU);
if(IOU > overlap_threshold){
it = vScores.erase(it); //刪除交並比大於閾值的候選框,erase返回刪除元素的下一個元素
}else{
it++;
}
}
}
vPick.resize(nPick);
vector<Bbox> tmp_;
tmp_.resize(nPick);
for(int i = 0; i < nPick; i++){
tmp_[i] = boundingBox_[vPick[i]];
}
boundingBox_ = tmp_;
}