上期讲解了目标检测中的三种数据增强的方法,这期我们讲讲目标检测中用来评估对象检测算法的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、深度学习和计算机编程和电脑知识的精彩内容,可以关注微信公众号:码农的后花园

目标检测中的IOU和CIOU原理讲解以及应用(附测试代码)的更多相关文章

  1. Adaboost原理及目标检测中的应用

    Adaboost原理及目标检测中的应用 whowhoha@outlook.com Adaboost原理 Adaboost(AdaptiveBoosting)是一种迭代算法,通过对训练集不断训练弱分类器 ...

  2. 目标检测算法(1)目标检测中的问题描述和R-CNN算法

    目标检测(object detection)是计算机视觉中非常具有挑战性的一项工作,一方面它是其他很多后续视觉任务的基础,另一方面目标检测不仅需要预测区域,还要进行分类,因此问题更加复杂.最近的5年使 ...

  3. 目标检测 1 : 目标检测中的Anchor详解

    咸鱼了半年,年底了,把这半年做的关于目标的检测的内容总结下. 本文主要有两部分: 目标检测中的边框表示 Anchor相关的问题,R-CNN,SSD,YOLO 中的anchor 目标检测中的边框表示 目 ...

  4. 目标检测算法之R-CNN和SPPNet原理

    一.R-CNN的原理 R-CNN的全称是Region-CNN,它可以说是第一个将深度学习应用到目标检测上的算法.后面将要学习的Fast R-CNN.Faster R-CNN全部都是建立在R-CNN基础 ...

  5. 【计算机视觉】目标检测中的指标衡量Recall与Precision

    [计算机视觉]目标检测中的指标衡量Recall与Precision 标签(空格分隔): [图像处理] 说明:目标检测性能指标Recall与Precision的理解. Recall与Precision ...

  6. 目标检测中proposal的意义

    在目标检测中,从很早就有候选区域的说法,也是在2008年可能就有人使用这个方法,在2014年的卷积神经网络解决目标检测问题的文章中,这个候选框方法大放异彩,先前的目标检测方法主要集中在使用滑动窗口的方 ...

  7. 目标检测中的bounding box regression

    目标检测中的bounding box regression 理解:与传统算法的最大不同就是并不是去滑窗检测,而是生成了一些候选区域与GT做回归.

  8. 目标检测中的anchor-based 和anchor free

    目标检测中的anchor-based 和anchor free 1.  anchor-free 和 anchor-based 区别 深度学习目标检测通常都被建模成对一些候选区域进行分类和回归的问题.在 ...

  9. 目标检测中特征融合技术(YOLO v4)(下)

    目标检测中特征融合技术(YOLO v4)(下) ASFF:自适应特征融合方式 ASFF来自论文:<Learning Spatial Fusion for Single-Shot Object D ...

随机推荐

  1. 数电学习笔记之CMOS传输门工作原理

    CMOS 传输门从结构上看是由一个PMOS和一个NMOS管组成 先简单粗略讲讲PMOS管和NMOS管导通与截止吧 首先我们MOS管有三个极,源极(S:Source).漏极(D:Drain)和栅极(G: ...

  2. Hexo博客部署到远程仓库(Conding、Gitee、Github)

    一.本地环境搭建 1.安装Git Git可以有效.高速的处理各种项目版本管理.也就是用来管理你的hexo博客文章,上传到GitHub的工具. Git下载地址 安装好了之后使用git -version查 ...

  3. SPM:Single-stage Multi-person Pose Machines

    figure1图b figure1 -a   figure3-a 图一-a

  4. 算法学习笔记:最近公共祖先(LCA问题)

    当我们处理树上点与点关系的问题时(例如,最简单的,树上两点的距离),常常需要获知树上两点的最近公共祖先(Lowest Common Ancestor,LCA).如下图所示: 2号点是7号点和9号点的最 ...

  5. 基于注解的DI

    目录 一.使用注解的步骤 二.@Component 三.@Value 四.@Autowired 五.@Qualifier 六.@Resource 七.XML和注解对比 通过spring的注解完成对ja ...

  6. Linux-Centos 用crontab定时运行python脚本详细步骤

    服务器总是要定时运行某个程序,而我在解决这个问题的时候遇到很多困难, 特此记录下来. 1.编辑crontab配置 crontab -e 服务器一般会安装好crontab,若没有安装请按命令安装 yum ...

  7. Kubernetes基于haproxy实现ingress服务暴露

    HAproxy Ingress控制器 HAproxy Ingress简介 HAProxy Ingress watches in the k8s cluster and how it builds HA ...

  8. linux root用户下没有.ssh目录

    .ssh 是记录密码信息的文件夹,如果没有登录过root的话,就没有 .ssh 文件夹,因此登录 localhost ,并输入密码就会生成了 ssh localhost

  9. Python3技巧:动态变量名

    Firstly 各位应该做过服务器运维吧,像这样: 那么,在服务器运维的程序中,最好的访问服务器的方式是:运维库名.服务器名 由于服务器名是动态的,所以变量名也是动态的.今天我们就来讲讲Python3 ...

  10. 第3章 SparkSQL解析

    第3章 SparkSQL解析 3.1 新的起始点SparkSession 在老的版本中,SparkSQL提供两种SQL查询起始点,一个叫SQLContext,用于Spark自己提供的SQL查询,一个叫 ...