Photoruction工事中!

Photoructionの開発ブログです!

エレガントなIoUの計算方法

はじめに

こんにちは、AIエンジニアの志賀です。

最近はとっても暑い毎日が続いていますね。つい1ヶ月前と比べるとこんな暑さは信じられないですね。冬には「極寒より猛暑の方がマシ」と考えるのですが、やはりいざ夏になると「猛暑より極寒の方がマシ」と考えてしまいます。

最近、行動経済学に関連した本を読んだりするのですが、これもアンカリング効果とやらの所業なのでしょうか?

さて、今回はIoUの様々な計算方法の中でも、numpyのclip関数を使用したIoUの計算方法を紹介していきたいと思います。個人的には、最もスマートでエレガントだなあ。と思った計算方法だったので、この計算方法を紹介する次第です。

おさらい

IoUとは

IoUとは、Intersection over Unionの略で、2つの領域の重なり具合を示す指標となっております。

具体的な計算方法は、仮にAとBの領域があった場合に、AかつB / AまたはBとなります。

より詳細を知りたい方はこちらの記事を参照すると分かりやすいです。

 

参考図:

intersection = オレンジ部分

union = オレンジ部分 + 青部分



numpyのclip関数を使ったIoUの計算方法

numpyのclip関数とは

numpyのclip関数は、入力されたnumpy配列の各要素を指定された任意の値の範囲に収める加工を行う関数です。clip関数の第一引数にnumpy配列、第二, 第三引数にそれぞれ最小値、最大値を入力します。

以下、使用例になります。

x = np.arange(10)
print(x)
# [0 1 2 3 4 5 6 7 8 9]

print(np.clip(x, 3, 6))
# [3 3 3 3 4 5 6 6 6 6]

numpyのclip関数を使ってどのようにIoUを計算するか

clip関数は、IoUの計算の中でも、分子であるintersectionの計算に使用します。

具体的には、x, y座標のそれぞれにおいて、片方のbox1の最小値最大値を使って、もう片方のbox2に対してnp.clipを適用します。

このようにする事で、box1とbox2が重なる部分、つまりintersection領域のみが残るという具合です。

例えば、box1 = [0, 0, 10, 10], box2 = [5, 5, 13, 13]だった場合、

box1の最小値、最大値を使用したclip関数をbox2に適用してあげると次のようになります。

box1 = np.array([0, 0, 10, 10])
box2 = np.array([5, 5, 13, 13])
x_min, y_min, x_max, y_max = box1
# x軸方向にclip関数を適用
box2[0::2] = np.clip(box2[0::2], x_min, x_max)
print(box2)
# [ 5  5 10 13]

# y軸方向にclip関数を適用
box2[1::2] = np.clip(box2[0::2], y_min, y_max)
# intersectionの領域を獲得
print(box2)
# [ 5  5 10 10] (= intersectionの領域)

あとは intersectionの領域の面積(=intersection)の計算及びunionを計算してあげれば、IoUが求まります。

unionは、box1の面積 + box2の面積 - intersectionによって求まります。

実際のコード

以下実際のコードです。

より利便性を追求する為に、1つの領域(box)と他の複数の領域(other_boxes)のIoUsをまとめて計算します。

def calc_ious(box, other_boxes):
		eps=1e-5
    other_boxes = np.array(other_boxes).reshape(-1, 4)
		#intersectionsの計算
    other_boxes_cpy = np.copy(other_boxes)
    np.clip(other_boxes_cpy[:, 0::2], box[0], box[2], out=other_boxes_cpy[:, 0::2])
    np.clip(other_boxes_cpy[:, 1::2], box[1], box[3], out=other_boxes_cpy[:, 1::2])
    inters = (other_boxes_cpy[:, 2] - other_boxes_cpy[:, 0]) * (other_boxes_cpy[:, 3] - other_boxes_cpy[:, 1])
		#unionsの計算
		box_area = (box[2] - box[0]) * (box[3] - box[1])
    other_boxes_areas = (other_boxes[:, 2] - other_boxes[:, 0]) * (other_boxes[:, 3] - other_boxes[:, 1])
    unions = other_boxes_areas + box_area - inters
		#iousの計算
    ious = inters / (unions + eps)
    ious = ious.tolist()
    return ious

box = [5, 5, 12, 12]
other_boxes = [[5, 5, 13, 13], [10, 10, 30, 30]]
ious = calc_ious(box, other_boxes)
print(ious)
# [0.7656248803711124, 0.008988763842949127]

感想

個人的には、この計算方法を知った時はスパイファミリーのヘンダーソン先生並みに「エレガント!」と感動したのですが、如何だったでしょうか。中にはそんなの最初から知ってるよという方もいるかもしれませんが、温かい目で見て頂けると嬉しいです。笑

 

株式会社フォトラクションでは一緒に働く仲間を募集しています