pythonで2つの矩形(長方形)のIoUを計算する

pythonでIoU (Intersection over Union)の計算方法を実装する方法を紹介します。IoUはSSDやYOLOといった物体検出AIを理解する上で重要な概念で、物体検出AIで出力される複数の矩形の重なり具合を表す定量的な指標です。

この記事でできること

2つの矩形(長方形)の重なり具合を表すIoUを計算するpythonの実装例を紹介します。

次の画像は、2つの矩形が重なってる様子を表した図です。IoUとはIntersection over Unionの略ですが、IntersectionとUnionが表す領域を次の図で表現しています。

IoU: 2つの矩形と、矩形の重なり(IntersectionとUnion)
IoU: 2つの矩形と、矩形の重なり(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については次の記事で紹介しています。

コメント

  1. yasu より:

    初めまして.
    記事を参考にさせていただきました.
    誤記と思われる場所を発見しましたので,ご確認よろしくお願いします.
    iou関数全部のコードの説明でのコメントアウト(1)と,iou関数の上から4行のコードの説明のコメントアウト(2)に相違があります.
    (1)# a, bは矩形を表すリストで、a=[xmin, xmax, ymin, ymax]
    (2) # a, bは矩形を表すリストで、a=[xmin, ymin, xmax, ymax]
    2が正しいと思います.
    よろしくお願いいたします.

    • aipy2020 より:

      yasuさん、ご指摘頂きまして誠にありがとうございます。
      (2)が正で間違いありません。記事を訂正いたしました。

タイトルとURLをコピーしました