項目 widget-area-1 尚未註冊或是沒有一個 view.php 檔案.
項目 widget-area-1 尚未註冊或是沒有一個 view.php 檔案.
項目 search-input 尚未註冊或是沒有一個 view.php 檔案.

【OpenCV】用 C++ 計算 iou 的方法 與網路算法常見錯誤(內附範例程式碼) sample code

前言

在機器學習的領域中,我們很常會需要用到 iou 的計算,
iou 全名為 intersection over union,
能替兩張圖形重疊的範圍提供一個參考分數,是一個相對具有參考意義的值。
本篇文章中也提供範例程式碼,
另外也發現網路上查到的作法有很多類似的常見錯誤,一並分享在此文當中。
因為我自己也很常用XD,不時就會回來拿這段 function 去實作。

iou 的概念與公式

iou 基本上 = 兩矩形的交集 / 兩矩形的聯集

所以:

  • 完全重合時:得到最大值 1
  • 完全不重合時:得到最小值 0
  • 部分重合:得到 0~1 範圍的值

以下為圖解

iou

  • 圖片引用自: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

「正確」實驗結果分析

看下圖,我們可以大概知道圖形的分布

iou-1

  • 首先是紅框 (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 結果。

想看 python 計算 iou 方法,可見我的另外一篇文

【OpenCV】用 python OpenCV 計算 iou 的方法 (內附範例程式碼) sample code</