msdd’s blog

deep learning勉強中。プログラム関連のこと書きます。

TorchvisionでIoUの計算

はじめに

物体検出の評価指標で使われたりしているIoUを求めてみた。

IoUとは

Intersection over Unionの略である。Jaccard indexとも言われている。

物体検出の評価指標などで使われている。 物体検出では、物体を矩形で囲むことで、物体の位置を予測する。 正解もまた、矩形で囲まれたものが与えられる。この予測した矩形と正解の矩形を 評価する手法の1つとして、IoUが使われる。

2つの矩形があり、重なった部分の面積を面積の和で割ったものがIoUである。

f:id:msdd:20200310171456p:plain

2つの矩形が、まったく重ならなかったらIoU=0となり、完全に重なるとIoU=1となる。

f:id:msdd:20200310151355p:plainf:id:msdd:20200310151319p:plain

IoUを求めてみる

IoUを求めてみる。

バージョン

  • PyTorch : 1.4.0
  • Torchvision : 0.5.0

使った関数

torchvision.ops.boxes.box_iou(boxes1,boxes2)

torchvisionにはtorchvision.ops.boxesbox_iou(boxes1, boxes2)という関数がある。ドキュメントには載っていないが使えるので使う。

引数が矩形のboxes1とboxes2でそれぞれサイズがTensor[N, 4]、Tensor[M, 4]である。 この4の所は矩形の座標で(x1,y1,x2,y2)である。 返り値はiouでサイズがTensor[N, M]のテンソルである。

Google Colabで試した

矩形を2つ用意する。 最も左上の座標を(0,0)として右方向、下方向が+となる方向。 それぞれ左上の座標と左下の座標で矩形を表している。

一方の矩形にもう一方の矩形が含まれている例

box1は(0,0),(3,3)の矩形(黄色)、box2は(0,0),(5,5)の矩形(青)。

f:id:msdd:20200310163853p:plain:w400

import torchvision.ops.boxes as bops

box1=torch.tensor([[0,0,3,3]],dtype=torch.float)
box2=torch.tensor([[0,0,5,5]],dtype=torch.float)
print("box1:",box1)
print("box2:",box2)

area1,area2=bops.box_area(box1),bops.box_area(box2)
print("area1:",area1)
print("area2:",area2)

iou=bops.box_iou(box1,box2)
print(iou)

実行してみる。

box1: tensor([[0., 0., 3., 3.]])
box2: tensor([[0., 0., 5., 5.]])
area1: tensor([9.])
area2: tensor([25.])
tensor([[0.3600]])

重なり部分の面積=(3-0)(3-0)=9、和の部分の面積=(5-0)(5-0)=25となる。

f:id:msdd:20200310164145p:plain:w250
重なり部分
f:id:msdd:20200310164140p:plain:w250
和の部分

その結果、  IoU=\frac{9}{25}=0.36 となる

2つの矩形が重ならない例

box1:(5,0),(8,3)で、box:(0,0),(5,5)の場合

f:id:msdd:20200310165051p:plain:w400

box1=torch.tensor([[5,0,8,3]],dtype=torch.float)
box2=torch.tensor([[0,0,5,5]],dtype=torch.float)
print("box1:",box1)
print("box2:",box2)

iou=bops.box_iou(box1,box2)
print(iou)

実行してみる。

box1: tensor([[5., 0., 8., 3.]])
box2: tensor([[0., 0., 5., 5.]])
tensor([[0.]])

重なっている部分がないので、IoU=0となる。

一部が重なる例

box1:(3,1),(6,4)で、box:(0,0),(5,5)の場合

f:id:msdd:20200310165157p:plain:w400

box1=torch.tensor([[3,1,6,4]],dtype=torch.float)
box2=torch.tensor([[0,0,5,5]],dtype=torch.float)
print("box1:",box1)
print("box2:",box2)

iou=bops.box_iou(box1,box2)
print(iou[0][0].item())
print(iou)

実行してみる。

box1: tensor([[3., 1., 6., 4.]])
box2: tensor([[0., 0., 5., 5.]])
0.2142857164144516
tensor([[0.2143]])

重なり部分の面積は6、和の部分の面積は28なので、 IoU=\frac{6}{28}=0.214285...=0.2143 となる。

f:id:msdd:20200310165216p:plain:w250
重なり部分
f:id:msdd:20200310165213p:plain:w250
和の部分

入力のboxesが複数ある例

boxes1:(3,1),(6,4)の黄色の矩形と(1,3),(4,5)の紫の矩形 で、boxes2:(0,3),(2,6)の赤の矩形と(0,0),(5,5)の青の矩形の場合

f:id:msdd:20200310165540p:plain:w400

boxes1=torch.tensor([[3,1,6,4],[1,3,4,5]],dtype=torch.float) #黄, 紫
boxes2=torch.tensor([[0,3,2,6],[0,0,5,5]],dtype=torch.float) #赤, 青

print("boxes1:",boxes1)
print("boxes2:",boxes2)

iou=bops.box_iou(boxes1,boxes2)
print(iou)

出力は、

boxes1: tensor([[3., 1., 6., 4.], [1., 3., 4., 5.]])
boxes2: tensor([[0., 3., 2., 6.], [0., 0., 5., 5.]])
tensor([[0.0000, 0.2143],[0.2000, 0.2400]])

返り値のIoUは入力サイズのboxes1が2、boxes2が2より、2x2のtensorが返ってくる。

得られたIoUのtensorは下の表のようになる。縦がboxes1で横がboxes2の並びとなる。

(0,3),(2,6) (0,0),(5,5)
(3,1),(6,4) 黄色 0.0 0.2143
(1,3),(4,5)  0.2000 0.2400

f:id:msdd:20200310165557p:plain

つまり、iou[index1][index2]でboxes1[index1]とboxes2[index2]の2つの矩形のIoUを得られる。

つまった点

box_iouの入力のbox1,box2をdtype=torch.intのtensorで計算すると、結果が0になってしまう。 そのため、dtypeはfloatにする必要がある。

まとめ

IoUについて調べ、torchvisionのbox_iou関数を使ってIoUを求めてみた。 複数の矩形のIoUを一括で求められて便利。 今後は、物体検出の評価指標は他にも色々あるので調べてみたい。

参考サイト