IOU和NMS图解(附Python和C++代码)

1 IOU计算: 交并比

1.1 先上代码

Python版

class Iou:
    def compute_iou(self, x1, y1, x2, y2, x3, y3, x4, y4):
    
        #两矩阵面积之和
        area = (x2 - x1) * (y2 - y1) + (x4 - x3) * (y4 - y3)
		# 两矩阵相交部分的左上角和右下角坐标确定
        middle_x1 = max(x1, x3)
        middle_x2 = min(x2, x4)
        middle_y1 = max(y1, y3)
        middle_y2 = min(y2, y4)
        middle_w = max(0.0, middle_x2 - middle_x1)
        middle_h = max(0.0, middle_y2 - middle_y1)
        
        #两矩阵之交
        middle_area = middle_w * middle_h
        
        # return 交 / 并
        return middle_area / (area - middle_area)


if __name__=="__main__":
    rec1 = [27, 661, 47, 679]
    rec2 = [27, 662, 47, 682]
    a = Iou().compute_iou(*rec1, *rec2)
    print(f"a: {a}")

C++ 版

#include<iostream>
#include<vector>
using namespace std;

double compute_iou(const vector<vector<int>>& a, const vector<vector<int>>& b){
    int middle_x1 = max(a[0][0], b[0][0]);
    int middle_y1 = max(a[0][1], b[0][1]);
    int middle_x2 = min(a[1][0], b[1][0]);
    int middle_y2 = min(a[1][1], b[1][1]);
    int middle_w = max(0, middle_x2 - middle_x1);
    int middle_h = max(0, middle_y2 - middle_y1);
    int middle_area = middle_w * middle_h;
    int area = (a[1][0] - a[0][0]) * (a[1][1] - a[0][1]) + (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
    return double(middle_area) / (area - middle_area);
}

int main(){
    vector<vector<int>> a{{100, 100}, {120, 120}};
    vector<vector<int>> b{{110, 110}, {130, 130}};

    cout << compute_iou(a, b) << endl;
    return 0;
}

1.2 推导过程

在这里插入图片描述

IOU公式如下:

I O U = 两矩阵交 / 两矩阵并 IOU = 两矩阵交 /两矩阵并 IOU=两矩阵交/两矩阵并

以左上角为远点。已知两个矩阵的左上角和右下角坐标,计算IOU。具体地,
第一个矩形rec1,左上:(x1, y1) ,右下:(x2, y2) ;
第二个矩形rec2,左上:(x3, y3) ,右下:(x4, y4) ;
很容易计算两个矩阵的和,公式如下:
a r e a = r e c 1 + r e c 2 = ( x 2 − x 1 ) ∗ ( y 2 − y 1 ) + ( x 4 − x 3 ) ∗ ( y 4 − y 3 ) area = rec1 + rec2 = (x2 - x1)*(y2 - y1) + (x4 - x3)*(y4 - y3) area=rec1+rec2=(x2x1)(y2y1)+(x4x3)(y4y3)
要计算两矩阵的交,则要确定相交的面积的左上和右下坐标,则有:
m i d d l e _ l e f t = m a x ( x 1 , x 3 ) middle\_left = max(x1, x3) middle_left=max(x1,x3)
m i d d l e _ r i g h t = m i n ( x 2 , x 4 ) middle\_right = min(x2, x4) middle_right=min(x2,x4)
m i d d l e _ t o p = m a x ( y 1 , y 3 ) middle\_top = max(y1, y3) middle_top=max(y1,y3)
m i d d l e _ b u t t o m = m i n ( y 2 , y 4 ) middle\_buttom = min(y2, y4) middle_buttom=min(y2,y4)
因此矩阵的交为:
m i d d l e _ a r e a = m a x ( 0 , m i d d l e _ r i g h t − m i d d l e _ l e f t ) ∗ m a x ( 0 , m i d d l e _ b u t t o m − m i d d l e _ t o p ) middle\_area = max(0, middle\_right - middle\_left) * max(0, middle\_buttom - middle\_top) middle_area=max(0,middle_rightmiddle_left)max(0,middle_buttommiddle_top)

矩阵的并为:
矩阵的并 = 矩阵的和 − 矩阵的交 矩阵的并 = 矩阵的和 - 矩阵的交 矩阵的并=矩阵的和矩阵的交
因此最终公式为
I O U = m i d d l e _ a r e a a r e a − m i d d l e _ a r e a IOU = \frac{middle\_area}{area - middle\_area} IOU=areamiddle_areamiddle_area

2 NMS:非极大值抑制(Non-Maximum Suppression, NMS)

2.1 先上代码

import numpy as np
import matplotlib.pyplot as plt

class Solution:
    def nms(self, boxes, threshold):

        # 计算所有候选框面积,利用numpy一次计算所有的候选框
        x1, y1, x2, y2, score = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3],  boxes[:, 4]

        #计算每个框的面积
        areas = (x2 - x1 + 1) * (y2 - y1 + 1)

        # 对每个框的置信度进行从小到大排序,order存储的是对应score中从小到大的索引值
        order = np.argsort(score)

        # keep是返回值,经过 NMS 后挑选出的框
        keep = []

        while order.size > 0:
            # 将当前置信度最大的框加入返回值,并用其抑制iou>给定阈值的框,小于阈值的框继续做NMS,直到所有框被挑选完
            index = order[-1]
            keep.append(index)

            # 计算置信度最大的框和其余框的iou,这里计算的数量为当前order.size - 1.
            middle_x1 = np.maximum(x1[index], x1[order[:-1]])
            middle_x2 = np.minimum(x2[index], x2[order[:-1]])
            middle_y1 = np.maximum(y1[index], y1[order[:-1]])
            middle_y2 = np.minimum(y2[index], y2[order[:-1]])

            # 特殊情况处理,对左上角和右下角框的坐标进行判断:右下角坐标 - 左上角坐标 > 0 返回对应值,否则返回 0
            middle_w = np.maximum(0.0, middle_x2 - middle_x1 + 1)
            middle_h = np.maximum(0.0, middle_y2 - middle_y1 + 1)
            middle_area = middle_h * middle_w

            # 计算当前置信度最大的框与其余框的iou,将iou大于阈值的框删除
            iou_ratio = middle_area / (areas[index] + areas[order[:-1]] - middle_area)

            # 挑选小于给定阈值的iou继续进行 NMS
            left = np.where(iou_ratio < threshold)

            # 将所有< threshold的索引取出来
            order = order[left]

        return keep
    def plot_bbox(self, dets, c='k'):
        x1 = dets[:, 0]
        y1 = dets[:, 1]
        x2 = dets[:, 2]
        y2 = dets[:, 3]

        plt.plot([x1, x2], [y1, y1], c)
        plt.plot([x1, x1], [y1, y2], c)
        plt.plot([x1, x2], [y2, y2], c)
        plt.plot([x2, x2], [y1, y2], c)
        plt.title(" nms")



if __name__=="__main__":
    boxes = np.array([[100, 100, 250, 250, 0.8],
                      [250, 250, 420, 420, 0.95],
                      [220, 220, 320, 330, 0.92],
                      [100, 100, 210, 210, 0.7],
                      [230, 240, 325, 330, 0.81],
                      [220, 230, 315, 340, 0.9]])
    threshold = 0.7

    s1 = Solution()
    a = s1.nms(boxes, threshold)
    for i in boxes[a]:
        print(f"boxes_nms: {i}")

    # 可视化 NMS 效果
    plt.figure(1)
    ax1 = plt.subplot(1, 2, 1)
    ax2 = plt.subplot(1, 2, 2)

    plt.sca(ax1)
    s1.plot_bbox(boxes, 'k')  # before nms

    keep = a
    plt.sca(ax2)
    s1.plot_bbox(boxes[keep], 'r')  # after nm
    plt.show()

2.2 推导过程

  1. 我们先将所有候选框的置信度排序,因为我们最终是要最大的
  2. 将置信度最大的加入到最终的返回值中
  3. 将其他的候选框和当前置信度最大的框计算iou
  4. 如果iou大于一个阈值,则可删除(说明和置信度大的那个是重叠的)
  5. 将剩下的框重复以上过程
来源

python计算iou以及nms
python实现IOU计算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值