前言
在機器學習的領域中,我們很常會需要用到 iou 的計算,
iou 全名為 intersection over union,
能替兩張圖形重疊的範圍提供一個參考分數,是一個相對具有參考意義的值。
本篇文章中也提供範例程式碼,
另外也發現網路上查到的作法有很多類似的常見錯誤,一並分享在此文當中。
因為我自己也很常用XD,不時就會回來拿這段 function 去實作。
iou 的概念與公式
iou 基本上 = 兩矩形的交集 / 兩矩形的聯集
所以:
- 完全重合時:得到最大值 1
- 完全不重合時:得到最小值 0
- 部分重合:得到 0~1 範圍的值
以下為圖解
- 圖片引用自:https://blog.csdn.net/IAMoldpan/article/details/78799857
用 C++ 實作計算 iou 的 function – 網路常見「錯誤」示範
iou – 常見「錯誤」範例程式碼
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main()
{
Rect bbox_1(10, 20, 30, 40);
Rect bbox_2(20, 30, 40, 50);
Rect bbox_and;
bbox_and = bbox_1 & bbox_2;
Rect bbox_or;
bbox_or = bbox_1 | bbox_2;
cout<< "bbox_and.area() = " << bbox_and.area() << endl;
cout<< "bbox_or.area() = " << bbox_or.area() << endl;
cout<< "iou = " << (bbox_and.area()*1.0 / bbox_or.area()) << endl;
return 0;
}
編譯與結果
> g++ iou.cpp -o iou.out -std=c++11 `pkg-config --cflags opencv`; ./iou.out
bbox_and.area() = 600
bbox_or.area() = 3000
iou = 0.2
「正確」實驗結果分析
看下圖,我們可以大概知道圖形的分布
- 首先是紅框 (bbox_1),面積為 30*40=1200
- 再來是綠框 (bbox_2),面積為 40*50=2000
- 紅綠框交疊部分 ,面積為 (40-20)*(60-30)=600
- 紅綠框聯集,面積為 1200+2000-600 = 2600 (兩個相加後,扣掉重複部分)
得到結果 600/2600 = 0.23076923076 (就是我們上面最後印出來的結果囉!)
所以上面的程式問題出在哪?
網路上常見做 iou 算法的錯誤是出在計算「兩個矩形的聯集」的部份,
如上面的程式碼,使用了「bbox_1 | bbox_2」,
這就是關鍵問題所在,「Rectangle OR」計算出的結果是「最大範圍的 Rectangle」,
用下圖來說,我們其實算出的是「藍色的部份」
「Rectangle OR」算出的「藍色的部份 Rectangle」,
並非我們要的「聯集」,請見最上面的定義。
這就是網路上常見的「錯誤計算 iou 程式碼」。
用 C++ 實作計算 iou 的 function – 「正確」示範
iou – 「正確」範例程式碼
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main()
{
Rect bbox_1(10, 20, 30, 40);
Rect bbox_2(20, 30, 40, 50);
Rect bbox_and;
bbox_and = bbox_1 & bbox_2;
float bbox_or = bbox_1.area() + bbox_2.area() - bbox_and.area();
cout<< "bbox_and.area() = " << bbox_and.area() << endl;
cout<< "bbox_or.area() = " << bbox_or << endl;
cout<< "iou = " << (bbox_and.area()*1.0 / bbox_or) << endl;
return 0;
}
可以注意我們只有改寫「聯集」的地方,將他改寫成正確的算法。
這樣計算出來才是正確的。
編譯與結果
> g++ iou.cpp -o iou.out -std=c++11 `pkg-config --cflags opencv`; ./iou.out
bbox_and.area() = 600
bbox_or.area() = 2600
iou = 0.230769
我們得到了正確的 iou 結果。