IOU
IOU就是两个图像的交并比,这是一个图像检测中很重要的概念,想要构建YOLO系列的目标检测对应函数,就必须要先写一个计算交并比的程序,什么是交并比,就是两个图像交集的面积比上并集的面积,如果用下面的图来举例子的话。
I O U = S 3 S 1 + S 2 − S 3 IOU = \frac{S_3}{S_1+S_2-S_3} IOU=S1+S2−S3S3
由于目标检测的过程中我们大部分用的都是矩形框,根据算法我们都是知道两个矩形的左上角点的坐标和矩形的高和宽
根据计算IOU的通用办法我们只需要再算出两个矩形的右下角坐标,用这四个坐标就可以确定并算出交集部分的左上角坐标和右下角坐标,这样就可以得到交集区域的宽和高,进一步就能算出交集区域的面积,算出了交集区域的面积,就可以算出交并比。
这里会有一个很明显的问题就是随着求交并比的两个大的矩形的位置的不同,交集矩形的坐标的表示方式也会有不同,接下来我们遍历一下所有的情况,最后找出一个规律。下面这个图画的累死我了,公式还各种变形,就这样吧哪天再改。
这里其实有一个规律,交集区域的左上角坐标都是由两个相交矩形的左上角坐标决定的,同理右下角的坐标都是由相交矩形的右下角坐标决定的,这里可以停下来仔细看一下上面的图思考一下,验证一下下面的规律,这样有助于代码的理解。
- 左上角点的横坐标 x _ b o x x\_box x_box是 x x x与 x 1 x_1 x1的最小值
- 左上角点的纵坐标 y _ b o x y\_box y_box是 y y y与 y 1 y_1 y1的最小值
- 右下角点的横坐标 x _ w _ b o x x\_w\_box x_w_box是 x _ w x\_w x_w与 x 1 _ w 1 x_1\_w_1 x1_w1的最大值
- 右下角点的纵坐标 y _ h _ b o x y\_h\_box y_h_box是 y _ h y\_h y_h与 y 1 _ h 1 y_1\_h_1 y1_h1的最大值
在知道了左上角标和右下角标之后就可以算出交集的宽和高,然后就可以算出交集的面积,则就能求出总体的IOU
b o x _ w = x _ w _ b o x − x _ b o x box\_w=x\_w\_box-x\_box box_w=x_w_box−x_box
b o x _ h = y _ h _ b o x − y _ b o x box\_h=y\_h\_box-y\_box box_h=y_h_box−y_box
S 3 = b o x _ w × b o x _ h S_3=box\_w×box\_h S3=box_w×box_h
上面都是相交的情况那吐过两个矩形没有相交呢,那 b o x _ w box\_w box_w和 b o x _ h box\_h box_h算出来一定会有至少一个变量小于0,我们用一种情况举例,其他情况也可以自己尝试的在纸上画一画,验证一下。
也就是说只要我们判断到计算出来的宽和高有小于0的那就证明两个矩形框并没有交集也就是IOU为0,明确了计算规则和,不相交的判断条件。接下来写代码,如果代码看起来感觉还是稍微有一点吃力,那就再自己看一下上面的内容,自己画画吸收的会更好。
numpy单独计算版本
import numpy as np
# coordinate = [x,y,w,h]
def IOU(coordinate,coordinate1):
x = coordinate[0]
y = coordinate[1]
w = coordinate[2]
h = coordinate[3]
x1 = coordinate1[0]
y1 = coordinate1[1]
w1 = coordinate1[2]
h1 = coordinate1[3]
x_box = np.max([x,x1])
y_box = np.max([y,y1])
x_w_box = np.min([x+w, x1+w1])
y_h_box = np.min([y+h, y1+h1])
box_w = x_w_box - x_box
box_h = y_h_box - y_box
if box_w<0 or box_h<0:
return 0
box_s = box_w*box_h
iou = box_s/(w*h + w1*h1 -box_s)
return iou
if __name__ == "__main__":
x = np.array([1,1,1,1])
y = np.array([0.5,0.5,1,1])
y1 = np.array([4,4,1,1])
print(IOU(x,y))
#0.14285714285714285
print(IOU(x,y1))
#0
numpy批量计算版本
单独计算版本有个问题,就是在实际的目标检测中由于有minibatch维度,我们需要同时计算多组坐标的IOU但是单独计算版本只能计算一个坐标的IOU,如果想计算多组坐标的IOU就需要循环依次运算,我们知道for运算在python中是非常浪费时间的,所以在训练中每次都需要大量调用for循环无疑是不明智的所以我们需要对输入进来的多组坐标进行同时运算并输出一组IOU值。
这里将if语句换成了np.where语句。作用是判断数组中的是否有元素满足某一条件,满足怎么样,不满足怎么样,根据我们的任务目标当我们检测到目标区域的长和宽的数组中有小于等于0的数那我们就将该负数换成0这样最后计算出来的对应的IOU就是0.这块可以简单思考一下,如果实在不理解去搜一下np.where的语法。
import numpy as np
# coordinate = [x,y,w,h]
def IOU(coordinate,coordinate1):
x = coordinate[:,0]
y = coordinate[:,1]
w = coordinate[:,2]
h = coordinate[:,3]
x1 = coordinate1[:,0]
y1 = coordinate1[:,1]
w1 = coordinate1[:,2]
h1 = coordinate1[:,3]
x_box = np.max([x,x1],axis=0)
y_box = np.max([y,y1],axis=0)
q = x+w
q1 = x1+w1
x_w_box = np.min([x+w, x1+w1])
y_h_box = np.min([y+h, y1+h1])
box_w = x_w_box - x_box
box_h = y_h_box - y_box
box_w = np.where(box_w>0,box_w,0)
box_h = np.where(box_h>0,box_h,0)
box_s = box_w*box_h
iou = box_s/(w*h + w1*h1 -box_s)
return iou
if __name__ == "__main__":
x = np.array([[1,1,1,1],[1,1,1,1]])
y = np.array([[0.5,0.5,1,1],[4,4,1,1]])
print(IOU(x,y))
#[0.14285714 0. ]
pytorch批量计算版本
numpy批量计算版本还有一个问题,那就是我们在做训练的时候是需要用到框架的,为了让数据在GPU上运算我们得把数据都转换成张量,所以实际上是需要一个使用框架的函数来运算IOU的代码。很简单只需要把np换成torch就好了
import torch
# coordinate = [x,y,w,h]
def IOU(coordinate:torch.Tensor,coordinate1:torch.Tensor):
x = coordinate[:, 0]
y = coordinate[:, 1]
w = coordinate[:, 2]
h = coordinate[:, 3]
x1 = coordinate1[:, 0]
y1 = coordinate1[:, 1]
w1 = coordinate1[:, 2]
h1 = coordinate1[:, 3]
x_box = torch.max(x,x1)
y_box = torch.max(y,y1)
x_w_box = torch.min(x+w, x1+w1)
y_h_box = torch.min(y+h, y1+h1)
box_w = x_w_box - x_box
box_h = y_h_box - y_box
box_w = torch.where(box_w>0,box_w,0)
box_h = torch.where(box_h>0,box_h,0)
box_s = box_w*box_h
iou = box_s/(w*h + w1*h1 -box_s)
return iou
if __name__ == "__main__":
x = torch.Tensor([[1,1,1,1],[1,1,1,1],[1,1,1,1]])
y = torch.Tensor([[0.5,0.5,1,1],[4,4,1,1],[1,1,1,1]])
print(IOU(x,y))
#tensor([0.1429, 0.0000, 1.0000])
结束
从这篇开始开始尝试进军目标检测领域,加油代码人!