1. 程式人生 > 實用技巧 >目標檢測中的IOU和CIOU原理講解以及應用(附測試程式碼)

目標檢測中的IOU和CIOU原理講解以及應用(附測試程式碼)

上期講解了目標檢測中的三種資料增強的方法,這期我們講講目標檢測中用來評估物件檢測演算法的IOU和CIOU的原理應用以及程式碼實現。

交併比IOU(Intersection over union)

在目標檢測任務中,我們用框框來定位物件,如下圖定點陣圖片中這個汽車,假設實際框是圖中紅色的框框,你的演算法預測給出的是紫色的框框,怎麼判斷你的演算法預測的這個框框的效果好壞呢?

這就用到我們的交併比函式IOU了,計算公式如下:

將我們圖片汽車的實際紅色框記為A,演算法的預測框記為B,交併比就是數學中A和B的交集A∩B跟A和B的並集的A∪B的面積之比,非常容易理解。IOU實際上衡量了兩個邊界框重疊地相對大小,預測框和真實框重疊越大,說明你的演算法預測效果比較好,IOU是一個評價指標。

那麼回到剛剛的問題:如何利用IOU進行判斷演算法的預測效果好壞呢?也就是這個預測框是否可以當做正確的定位到我們的目標汽車呢?

在計算機檢測任務中,如果我們演算法的預測框和實際框之間的交併比IOU≧0.5,那麼你的演算法預測結果是可以接受的,就說你預測的這個框框是正確的,這個閾值0.5,你可以設定得更高,邊界框越精確,在YOLOv3中正是用到這個IOU來對我們的先驗框進行了一個篩選,以及計算測試集的模型效果mAP時也用到了IOU進行閾值判斷。

效果圖

首先借助Opencv和numpy框畫兩個框,然後計算IOU進行展示,如下圖。

IOU實現程式碼

以框的左上角(x1,y1)和右下角(x2,y2)座標形式來計算它們之間的IOU。

import cv2
import numpy as np

def CountIOU(RecA, RecB):
xA = max(RecA[0], RecB[0])
yA = max(RecA[1], RecB[1])
xB = min(RecA[2], RecB[2])
yB = min(RecA[3], RecB[3])

# 計算交集部分面積
interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)

# 計算預測值和真實值的面積
RecA_Area = (RecA[2] - RecA[0] + 1) * (RecA[3] - RecA[1] + 1)
RecB_Area = (RecB[2] - RecB[0] + 1) * (RecB[3] - RecB[1] + 1)

# 計算IOU
iou = interArea / float(RecA_Area + RecB_Area - interArea)
return iou

if __name__ == "__main__":
img = np.zeros((512, 512, 3), np.uint8)
img.fill(255)
RecA = [50, 50, 300, 300] # x1,y1,x2,y2
RecB = [60, 60, 320, 320]
cv2.rectangle(img, (RecA[0], RecA[1]), (RecA[2], RecA[3]), (0, 255, 0), 5)
cv2.rectangle(img, (RecB[0], RecB[1]), (RecB[2], RecB[3]), (255, 0, 0), 5)
IOU = CountIOU(RecA, RecB)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, "IOU = %.2f" % IOU, (130, 190), font, 0.8, (0, 0, 0), 2)
cv2.imshow("image", img)
cv2.waitKey()
cv2.destroyAllWindows()

CIOU(Complete-IOU)

IoU是比值的概念,對目標物體的尺寸scale是不敏感的。我們在計算框框BBox的迴歸損失函式進行優化有多種優化方式,如在CIOU之前有GIOU、DIOU,而CIOU解決了一般IoU無法直接優化兩個框框沒有重疊的部分。

IoU經過GIOU再到DIOU最終發展到CIOU,CIOU將目標與框框anchor之間的距離,重疊率、尺度以及懲罰項都考慮進去,使得目標框迴歸變得更加穩定,不會像IoU和GIoU一樣出現訓練過程中發散等問題,而懲罰因子把預測框長寬比擬合目標框的長寬比考慮進去,在最新發布的yolov4中anchor的迴歸就是用的CIOU方式。

CIOU計算公式

效果圖

首先借助Opencv和numpy框畫兩個框,然後按照公式計算CIOU進行展示,如下圖。

程式碼實現:

import torch
import numpy as np
import cv2
import math


def box_ciou(b1, b2):
"""
輸入為: ----------
b1: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh
b2: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh
返回為: -------
ciou: tensor, shape=(batch, feat_w, feat_h, anchor_num, 1)
"""
# 求出預測框左上角右下角
b1_xy = b1[..., :2]
b1_wh = b1[..., 2:4]
b1_wh_half = b1_wh/2.
b1_mins = b1_xy - b1_wh_half
b1_maxes = b1_xy + b1_wh_half
# 求出真實框左上角右下角
b2_xy = b2[..., :2]
b2_wh = b2[..., 2:4]
b2_wh_half = b2_wh/2.
b2_mins = b2_xy - b2_wh_half
b2_maxes = b2_xy + b2_wh_half

# 求真實框和預測框所有的iou
intersect_mins = torch.max(b1_mins, b2_mins)
intersect_maxes = torch.min(b1_maxes, b2_maxes)
intersect_wh = torch.max(intersect_maxes - intersect_mins, torch.zeros_like(intersect_maxes))
intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
b1_area = b1_wh[..., 0] * b1_wh[..., 1]
b2_area = b2_wh[..., 0] * b2_wh[..., 1]
union_area = b1_area + b2_area - intersect_area
iou = intersect_area / (union_area + 1e-6)

# 計算中心的差距
center_distance = torch.sum(torch.pow((b1_xy - b2_xy), 2), axis=-1)
# 找到包裹兩個框的最小框的左上角和右下角
enclose_mins = torch.min(b1_mins, b2_mins)
enclose_maxes = torch.max(b1_maxes, b2_maxes)
enclose_wh = torch.max(enclose_maxes - enclose_mins, torch.zeros_like(intersect_maxes))
# 計算對角線距離
enclose_diagonal = torch.sum(torch.pow(enclose_wh,2), axis=-1)
ciou = iou - 1.0 * (center_distance) / (enclose_diagonal + 1e-7)
v = (4 / (math.pi ** 2)) * torch.pow((torch.atan(b1_wh[..., 0]/b1_wh[..., 1]) - torch.atan(b2_wh[..., 0]/b2_wh[..., 1])), 2)
alpha = v / (1.0 - iou + v)
ciou = ciou - alpha * v
return ciou

if __name__ == "__main__":
img = np.zeros((512, 512, 3), np.uint8)
img.fill(255)
RecA = [1, 90, 90, 150, 150]
RecB = [1, 150, 150, 200, 200]
a = torch.tensor(RecA, dtype=torch.float) # tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh
b = torch.tensor(RecB, dtype=torch.float)
cv2.rectangle(img, (int(RecA[1]-RecA[3]/2), int(RecA[2]-RecA[4]/2)), (int(RecA[1]+RecA[3]/2), int(RecA[2]+RecA[4]/2)), (0, 255, 0), 5)
cv2.rectangle(img,(int(RecB[1]-RecB[3]/2), int(RecB[2]-RecB[4]/2)), (int(RecB[1]+RecB[3]/2), int(RecB[2]+RecB[4]/2)), (255, 0, 0), 5)

CIOU = box_ciou(a,b)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, "CIOU = %.2f" % CIOU, (130, 190), font, 0.8, (0, 0, 0), 2)
cv2.imshow("image", img)
cv2.waitKey()
cv2.destroyAllWindows()

相關程式碼,還請關注微信公眾號:碼農的後花園,回覆關鍵字:IOU,下載使用。

更多有關python、深度學習和計算機程式設計和電腦知識的精彩內容,可以關注微信公眾號:碼農的後花園