EAST算法NMS的优化思路

前言

仍然是EAST算法的优化,因为出于安全考虑,将在毕业后公开,望大家谅解。

传统NMS

思路:首先,EAST算法使用的是Locality-Aware NMS,局部NMS优于传统NMS,我们原来的思路是将Locality-Aware NMS更换为Softer-NMS,让我们先分析一下传统NMS的缺点。先附一篇传统NMS的代码吧:

def nms(bounding_boxes, Nt):
   if len(bounding_boxes) == 0:
       return [], []
   bboxes = np.array(bounding_boxes)

   # 计算 n 个候选框的面积大小
   x1 = bboxes[:, 0]
   y1 = bboxes[:, 1]
   x2 = bboxes[:, 2]
   y2 = bboxes[:, 3]
   scores = bboxes[:, 4]
   areas = (x2 - x1 + 1) * (y2 - y1 + 1)

   # 对置信度进行排序, 获取排序后的下标序号, argsort 默认从小到大排序
   order = np.argsort(scores)

   picked_boxes = []  # 返回值
   while order.size > 0:
       # 将当前置信度最大的框加入返回值列表中
       index = order[-1]
       picked_boxes.append(bounding_boxes[index])

       # 获取当前置信度最大的候选框与其他任意候选框的相交面积
       x11 = np.maximum(x1[index], x1[order[:-1]])
       y11 = np.maximum(y1[index], y1[order[:-1]])
       x22 = np.minimum(x2[index], x2[order[:-1]])
       y22 = np.minimum(y2[index], y2[order[:-1]])
       w = np.maximum(0.0, x22 - x11 + 1)
       h = np.maximum(0.0, y22 - y11 + 1)
       intersection = w * h

       # 利用相交的面积和两个框自身的面积计算框的交并比, 将交并比大于阈值的框删除
       ious = intersection / (areas[index] + areas[order[:-1]] - intersection)
       left = np.where(ious < Nt)
       order = order[left]
   return picked_boxes

通过代码我们大致了解传统NMS的步骤:
1)根据置信度得分进行排序;
2)选择置信度最高的边界框添加到最终输出列表中,将其从原始边界框列表中删除;
3)计算所有边界框的面积;
4)计算置信度最高的边界框与其它候选框的IoU;
5)删除IoU大于阈值的边界框;(一般IOU取0.3~0.5)
6)重复上述过程,直至原始边界框列表为空。
传统NMS思路很简单,但是缺点也很明显,如果重复部分大于阈值直接删掉,这样如果有两个不同的文本框也重合了那么他也会被直接删掉,这就是我们所想优化的。
于是我们找到了另外一种优化方案:soft-NMS。

soft-NMS

先上代码:

def soft_nms(bboxes, Nt=0.3, sigma2=0.5, score_thresh=0.3, method=2):
   # 在 bboxes 之后添加对于的下标[0, 1, 2...], 最终 bboxes 的 shape 为 [n, 5], 前四个为坐标, 后一个为下标
   res_bboxes = deepcopy(bboxes)
   N = bboxes.shape[0]  # 总的 box 的数量
   indexes = np.array([np.arange(N)])  # 下标: 0, 1, 2, ..., n-1
   bboxes = np.concatenate((bboxes, indexes.T), axis=1)  # concatenate 之后, bboxes 的操作不会对外部变量产生影响
   # 计算每个 box 的面积
   x1 = bboxes[:, 0]
   y1 = bboxes[:, 1]
   x2 = bboxes[:, 2]
   y2 = bboxes[:, 3]
   scores = bboxes[:, 4]
   areas = (x2 - x1 + 1) * (y2 - y1 + 1)

   for i in range(N):
       # 找出 i 后面的最大 score 及其下标
       pos = i + 1
       if i != N - 1:
           maxscore = np.max(scores[pos:], axis=0)
           maxpos = np.argmax(scores[pos:], axis=0)
       else:
           maxscore = scores[-1]
           maxpos = 0
       # 如果当前 i 的得分小于后面的最大 score, 则与之交换, 确保 i 上的 score 最大
       if scores[i] < maxscore:
           bboxes[[i, maxpos + i + 1]] = bboxes[[maxpos + i + 1, i]]
           scores[[i, maxpos + i + 1]] = scores[[maxpos + i + 1, i]]
           areas[[i, maxpos + i + 1]] = areas[[maxpos + i + 1, i]]
       # IoU calculate
       xx1 = np.maximum(bboxes[i, 0], bboxes[pos:, 0])
       yy1 = np.maximum(bboxes[i, 1], bboxes[pos:, 1])
       xx2 = np.minimum(bboxes[i, 2], bboxes[pos:, 2])
       yy2 = np.minimum(bboxes[i, 3], bboxes[pos:, 3])
       w = np.maximum(0.0, xx2 - xx1 + 1)
       h = np.maximum(0.0, yy2 - yy1 + 1)
       intersection = w * h
       iou = intersection / (areas[i] + areas[pos:] - intersection)
       # Three methods: 1.linear 2.gaussian 3.original NMS
       if method == 1:  # linear
           weight = np.ones(iou.shape)
           weight[iou > Nt] = weight[iou > Nt] - iou[iou > Nt]
       elif method == 2:  # gaussian
           weight = np.exp(-(iou * iou) / sigma2)
       else:  # original NMS
           weight = np.ones(iou.shape)
           weight[iou > Nt] = 0
       scores[pos:] = weight * scores[pos:]
   # select the boxes and keep the corresponding indexes
   inds = bboxes[:, 5][scores > score_thresh]
   keep = inds.astype(int)
   return res_bboxes[keep]

步骤和普通NMS类似,不同在于当IOU大于阈值,算法不再直接删除,而是降低分数且IOU越大就降低的越大。然后放回去继续重复运算。这样就避免了刚才说的情况。下面我们说一下Locality-Aware NMS,和为何我们的原本优化思路行不通。

Locality-Aware NMS

同样,我们先附代码:

import numpy as np
from shapely.geometry import Polygon

def intersection(g, p):
   # 取g,p中的几何体信息组成多边形
   g = Polygon(g[:8].reshape((4, 2)))
   p = Polygon(p[:8].reshape((4, 2)))
   # 判断g,p是否为有效的多边形几何体
   if not g.is_valid or not p.is_valid:
       return 0
   # 取两个几何体的交集和并集
   inter = Polygon(g).intersection(Polygon(p)).area
   union = g.area + p.area - inter
   if union == 0:
       return 0
   else:
       return inter / union

def weighted_merge(g, p):
   # 取g,p两个几何体的加权(权重根据对应的检测得分计算得到)
   g[:8] = (g[8] * g[:8] + p[8] * p[:8]) / (g[8] + p[8])
   # 合并后的几何体的得分为两个几何体得分的总和
   g[8] = (g[8] + p[8])
   return g

def standard_nms(S, thres):
   # 标准NMS
   order = np.argsort(S[:, 8])[::-1]
   keep = []
   while order.size > 0:
       i = order[0]
       keep.append(i)
       ovr = np.array([intersection(S[i], S[t]) for t in order[1:]])
       inds = np.where(ovr <= thres)[0]
       order = order[inds + 1]
   return S[keep]


def nms_locality(polys, thres=0.3):
   '''
  locality aware nms of EAST
  :param polys: a N*9 numpy array. first 8 coordinates, then prob
  :return: boxes after nms
  '''
   S = []  # 合并后的几何体集合
   p = None  # 合并后的几何体
   for g in polys:
       if p is not None and intersection(g, p) > thres:  # 若两个几何体的相交面积大于指定的阈值,则进行合并
           p = weighted_merge(g, p)
       else:  # 反之,则保留当前的几何体
           if p is not None:
               S.append(p)
           p = g
   if p is not None:
       S.append(p)
   if len(S) == 0:
       return np.array([])
   return standard_nms(np.array(S), thres)

Locality-Aware NMS:1.先对所有的output box集合结合相应的阈值(大于阈值则进行合并,小于阈值则不和并),依次遍历进行加权合并,得到合并后的bbox集合;2.对合并后的bbox集合进行标准的NMS操作。这里可以看到,首先,当IOU大于阈值,文本框会被直接合并,然后再去NMS,如果IOU小于阈值,就将文本框保留下来。
那么,为什么说利用soft-NMS优化行不通呢。这种合并方式是将面积扩大,而不是删除,很大程度上避免了误删的情况。虽然接下来的操作依然会删除一些文本框,但是相对之下这种方式删除的属于少数情况,而且相比于降低分数,合并无异于更合理。所以说再去用soft-NMS优化意义不大。

Locality-Aware NMS的优化思路

书接上段,在Locality-Aware NMS,大家可能想到了一个问题。为什么合并后还会有NMS操作。不应该都合并了吗。大家思考以下这种情况:假设原来有两个文本框并不重合,但是在合并以后出现了重合部分,这是合理的。也就是说相离很远的两个文本框,在逐渐合并中出现了重合部分。但是想一个问题,如果这两个仍然还都是文本框呢?这就涉及到我们的优化思路。一、我们可以再次进行合并,但是如果是这样,逻辑上这样的问题会一直存在,这样的合并是永无止境的(当然也不是,其实大约两三次就会合并完重复几次也是可以的),而且太大的文本框其实包含了一些没有文字的区域。当然这也是一种优化思路。二、我们可以调节阈值,我们可以看到编程中,合并的阈值和NMS的阈值是相同的,这有点赶尽杀绝的味道,但其实不是,这类似重复了两次同样的操作,只不过第一次是合并,第二次是过滤,相当于1.5次合并。其实他和思路一有异曲同工的作用。只不过是重复次数比较少。然后我们看这两个阈值,我们可以把合并的阈值调小一些,这一定意义上就相当于让更多的文本框合并。效果类似于多次合并,然后我们把NMS的阈值调大一点,也就是把门槛放宽一点,可以一定程度上防止误删。这就是我们的优化思路。

最后

这就是我的优化思路,可能不太成熟,望大家见谅。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值