pythonでIoU (Intersection over Union)の計算方法を実装する方法を紹介します。IoUはSSDやYOLOといった物体検出AIを理解する上で重要な概念で、物体検出AIで出力される複数の矩形の重なり具合を表す定量的な指標です。
この記事でできること
2つの矩形(長方形)の重なり具合を表すIoUを計算するpythonの実装例を紹介します。
次の画像は、2つの矩形が重なってる様子を表した図です。IoUとはIntersection over Unionの略ですが、IntersectionとUnionが表す領域を次の図で表現しています。

Intersectionは濃い青色で表した部分を表し、Unionは2つの矩形を合わせた太い線で囲まれた全体の部分を表します。
IoUの定義は、Intersectionの面積とUnionの面積の比率Intersection/Unionです。
$$ IoU = \frac{Intersectionの面積}{Unionの面積} $$
このIoUを計算するpythonの実装例を紹介します。
Intersectionの面積は2つの矩形の重なりが無ければ0となります。つまり、IoUの値は2つの矩形に重なりが無ければ0となり、2つの矩形が全く同じならば1となります。
上の図のように部分的に重なっている場合は、IoUの値は0~1となり、IoUが大きいほど2つの矩形の重なり具合が大きいことを表します。
IoU(Intersection over Union)を計算する関数の実装例
まず、IoUを計算する関数の実装例を次に記載します。このサンプルコードは、2つの矩形a, bを引数とするIoU(a, b)という関数となっています。
def iou(a, b):
# a, bは矩形を表すリストで、a=[xmin, ymin, xmax, ymax]
ax_mn, ay_mn, ax_mx, ay_mx = a[0], a[1], a[2], a[3]
bx_mn, by_mn, bx_mx, by_mx = b[0], b[1], b[2], b[3]
a_area = (ax_mx - ax_mn + 1) * (ay_mx - ay_mn + 1)
b_area = (bx_mx - bx_mn + 1) * (by_mx - by_mn + 1)
abx_mn = max(ax_mn, bx_mn)
aby_mn = max(ay_mn, by_mn)
abx_mx = min(ax_mx, bx_mx)
aby_mx = min(ay_mx, by_mx)
w = max(0, abx_mx - abx_mn + 1)
h = max(0, aby_mx - aby_mn + 1)
intersect = w*h
iou = intersect / (a_area + b_area - intersect)
return iou
コードの内容を順番に説明します。
def iou(a, b):
# a, bは矩形を表すリストで、a=[xmin, ymin, xmax, ymax]
ax_mn, ay_mn, ax_mx, ay_mx = a[0], a[1], a[2], a[3]
bx_mn, by_mn, bx_mx, by_mx = b[0], b[1], b[2], b[3]
まず、矩形をどのようにプログラムで表現するかですが、ここでは矩形を要素4つのリスト[xmin, ymin, xmax, ymax]で表します。xmin等の各要素は、次に説明する矩形の頂点座標に基づく値です。
xmin, ymin, xmax, ymaxはleft, top, right, bottomとも呼ばれ、矩形の左上の点の座標と矩形の右下の画像の座標を表す値で、画像処理の分野ではよく扱われる表現です。
視覚的には下図を見て頂ければわかると思います。

次に、入力された矩形aとbの面積をそれぞれ計算します。
a_area = (ax_mx - ax_mn + 1) * (ay_mx - ay_mn + 1)
b_area = (bx_mx - bx_mn + 1) * (by_mx - by_mn + 1)
ここでは、矩形の2つの辺の長さを掛け算し、面積を計算しています。
辺の長さに+1しているのは、後のIoUの計算で0除算を避けるためのものです。辺の長さとしては正確には+1は不要かもしれませんが、矩形の座標のつけ方のポリシーとして短辺の長さが0となるものすごく細長い矩形をそもそも許すか(辺の長さ0を便宜上1とする)ということに関わってきますので、+1は便宜上だったり考え方の問題なのかと思います。
次にIntersectionの矩形の左上の座標と、右下の座標を計算します。(abx_mn, aby_mn)はIntersectionの矩形の左上の座標、(abx_mx, aby_mx)はIntersectionの矩形の右上の座標です。
abx_mn = max(ax_mn, bx_mn)
aby_mn = max(ay_mn, by_mn)
abx_mx = min(ax_mx, bx_mx)
aby_mx = min(ay_mx, by_mx)
次の処理では、Intersectionの面積を計算しています。
w = max(0, abx_mx - abx_mn + 1)
h = max(0, aby_mx - aby_mn + 1)
intersect = w*h
w, hはIntersectionのwidth(幅)とheight(高さ)を表します。max()関数を使っている理由ですが、2つの矩形の重なりが無い場合に(w=abx_mx – abx_mn + 1)が負になりますので(hも同様)、それを便宜上0としてIntersectionの面積である変数”Intersect”を0とするためです。
次に、いよいよIoUを計算します。
iou = intersect / (a_area + b_area - intersect)
return iou
IoUの定義Intersection/Unionの分母であるUnionの計算は、矩形aの面積a_area、矩形bの面積b_areaを足したあと、重複する部分の面積Intersectを引いています。分子のIntersectionの面積は上述で説明した変数Intersectそのものですね。これでIoUの計算ができました。
関数iou(a, b)の使用例
上で紹介した関数iou(a, b)の使用例を説明します。
a = [100,100,200,200]
b = [100,150,200,250]
iou_ab = iou(a, b)
print("iou =", iou_ab)
このサンプルスクリプトでは、矩形a=[100,100,200,200]と矩形b = [100,150,200,250]のiouを計算して、print文でIoUの値を標準出力しています。
このスクリプトを実行すると次のような出力が得られます。
iou = 0.33774834437086093
おわりに
今回は2つの矩形の重なり具合を表すIoUの計算方法を紹介しました。
IoUの計算そのものに大きな意味がありませんが、IoUは物体検出AIやコンピュータビジョンで重要となる概念です。例えば、複数の矩形をマージする処理であるNMS (Non-Maximum Suppression)で、IoUが使われています。NMSについては次の記事で紹介しています。
コメント
初めまして.
記事を参考にさせていただきました.
誤記と思われる場所を発見しましたので,ご確認よろしくお願いします.
iou関数全部のコードの説明でのコメントアウト(1)と,iou関数の上から4行のコードの説明のコメントアウト(2)に相違があります.
(1)# a, bは矩形を表すリストで、a=[xmin, xmax, ymin, ymax]
(2) # a, bは矩形を表すリストで、a=[xmin, ymin, xmax, ymax]
2が正しいと思います.
よろしくお願いいたします.
yasuさん、ご指摘頂きまして誠にありがとうございます。
(2)が正で間違いありません。記事を訂正いたしました。