类型错误:列表索引必须为整数或切片,而不是cupy.core.core.ndarray。

3
在目标检测算法中,使用非极大值抑制(NMS)来丢弃物体(例如车辆)的多余检测结果。
通常,水平边界框用于目标检测算法,并且已经存在水平NMS的GPU实现,但是我想要拥有旋转边界框的GPU实现。
CPU实现已经完成,但我正在努力使用CuPy包将CPU版本转换为GPU版本。下面是我编写的代码。在代码部分之后,您可以看到错误信息。
我的问题是什么原因导致了TypeError:list indices must be integers or slices, not cupy.core.core.ndarray?
    from shapely.geometry import Polygon as shpoly
    import time
    
    #### CPU implementation
    import numpy as np   
    
    def polygon_iou(poly1, poly2):
      """
      Intersection over union between two shapely polygons.
      """
      if not poly1.intersects(poly2): # this test is fast and can accelerate calculation
        iou = 0
      else:
        try:
          inter_area = poly1.intersection(poly2).area
          union_area = poly1.area + poly2.area - inter_area
          iou = float(inter_area) / float(union_area)
        except shapely.geos.TopologicalError:
          warnings.warn("'shapely.geos.TopologicalError occured, iou set to 0'", UserWarning)
          iou = 0
        except ZeroDivisionError:
          iou = 0
      return iou
    
    def polygon_from_array(poly_):
      """
      Create a shapely polygon object from gt or dt line.
      """
      polygon_points = np.array(poly_).reshape(4, 2)
      polygon = shpoly(polygon_points).convex_hull
      return polygon
    
    def nms(dets, thresh):
        scores = dets[:, 8]
        order = scores.argsort()[::-1]
        polys = []
        areas = []
        for i in range(len(dets)):
            tm_polygon = polygon_from_array(dets[i,:8])
            polys.append(tm_polygon)
        keep = []
        while order.size > 0:
            ovr = []
            i = order[0]
            keep.append(i)
            for j in range(order.size - 1):
                iou = polygon_iou(polys[i], polys[order[j + 1]])
                ovr.append(iou)
            ovr = np.array(ovr)
            inds = np.where(ovr <= thresh)[0]
            order = order[inds + 1]
        return keep
    
    
    #### GPU implementation
    import cupy as cp  
      
    def polygon_iou_gpu(poly1, poly2):
      """
      Intersection over union between two shapely polygons.
      """
      if not poly1.intersects(poly2): # this test is fast and can accelerate calculation
        iou = 0
      else:
        try:
          inter_area = poly1.intersection(poly2).area
          union_area = poly1.area + poly2.area - inter_area
          iou = float(inter_area) / float(union_area)
        except shapely.geos.TopologicalError:
          warnings.warn("'shapely.geos.TopologicalError occured, iou set to 0'", UserWarning)
          iou = 0
        except ZeroDivisionError:
          iou = 0
      return iou
    
    def polygon_from_array_gpu(poly_):
      """
      Create a shapely polygon object from gt or dt line.
      """
      polygon_points = cp.array(poly_).reshape(4, 2)
      polygon = shpoly(polygon_points).convex_hull
      return polygon
    
    def nms_gpu(dets, thresh):
        scores = dets[:, 8]
        order = scores.argsort()[::-1]
        polys = []
        areas = []
        for i in range(len(dets)):
            tm_polygon = polygon_from_array_gpu(dets[i,:8])
            polys.append(tm_polygon)
        keep = []
        while order.size > 0:
            ovr = []
            i = order[0]
            keep.append(i)
            for j in range(order.size - 1):   
                iou = polygon_iou_gpu(polys[i], polys[order[j + 1]])
                ovr.append(iou)
            ovr = np.array(ovr)
            inds = np.where(ovr <= thresh)[0]
            order = order[inds + 1]
        return keep
    
    
    if __name__ == '__main__':
        import random
        boxes = np.random.randint(0,100,(1000,8))
        scores = np.random.rand(1000, 1)
        dets = np.hstack((boxes, scores[:])).astype(np.float32)

    
        thresh = 0.1
        start = time.time()
        keep = nms(dets, thresh)
        print("CPU implementation took: {}".format(time.time() - start))
    
        cp.cuda.Device(1)
        dets_gpu = cp.array(dets)
        start = time.time()
        keep = nms_gpu(dets_gpu, thresh)
        print("GPU implementation took: {}".format(time.time() - start))

错误消息为:

CPU implementation took: 0.3672311305999756

Traceback (most recent call last):

File "nms_rotated.py", line 117, in

keep = nms_gpu(dets_gpu, thresh)

File "nms_rotated.py", line 97, in nms_gpu

iou = polygon_iou_gpu(polys[i], polys[order[j + 1]])

TypeError: list indices must be integers or slices, not cupy.core.core.ndarray

更新:2019年2月13日 我尝试了@Yuki Hashimoto的答案,将iou = polygon_iou_gpu(polys[i], polys[order[j + 1]])替换为iou = polygon_iou_gpu(polys[i.get()], polys[order[j + 1].get()])。没有出现任何错误,但GPU版本比CPU版本慢多倍。

使用100000个随机检测:

      CPU implementation took: 47.125494956970215
      GPU implementation took: 142.08464860916138
2个回答

2
简述:使用PFN官方的非极大值抑制
详细说明: 使用cp.where,它返回与某个条件匹配的list对象。
“corochann”的回答并不推荐,因为“polys”是一个列表,而列表不应该被“np.ndarray”切片(注入另一个依赖项也不推荐...)。
>>> polys[order.get()]  # get method returns np.ndarray
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: only integer scalar arrays can be converted to a scalar index
>>> polys[order[j + 1].get()]
### some result in some case, but this may fails depending on your env.###

1
谢谢。我检查了Chainer中NMS的实现,但它只适用于水平边界框。正如我在问题中提到的,针对水平边界框的NMS已经被多次实现,但是对于旋转边界框,没有CUDA加速的NMS。我试图删除代码对Shapely的依赖,但无法找到一个简单的方法来计算两个多边形的交集并计算它们的面积,以进行交并比计算。 - Majid Azimi
我尝试了你的答案。它有效,但在进行10000个随机检测时,GPU的速度比CPU慢得多。 - Majid Azimi
需要注意的是,在几乎所有情况下,CPU比GPU更快。只有在涉及图形或矩阵运算时,GPU更快(大量并行性是其中之一的例外)。 - Yuki Hashimoto
具有GPU实现的水平边界框的NMS比CPU实现快多倍。我将实现旋转边界框的相同加速。我认为,仍然应该比CPU更快,因为NMS操作通常没有太大变化。 - Majid Azimi
好的,谢谢。我认为问题基本上已经得到了回答,但是我没有实现我所希望的。不幸的是,我不熟悉CUDA和并行算法。我试图计算两个多边形的交集和它们的面积,但发现这很复杂,无法处理。 - Majid Azimi
显示剩余2条评论

0

[更新于2019/2/13]

请参考@yuki-hashimoto的答案,更为合适。


正如错误信息所述

TypeError: 列表索引必须是整数或切片,而不是cupy.core.core.ndarray

我猜测 order 是 cupy 数组? 如果是这种情况,那么 polys[order[j + 1]] 使用的索引 order[j+1] 是 cupy 数组,可能会导致问题。 尝试使用 cuda.to_cpu(array) 方法将它们转换为 numpy 数组怎么样?

from chainer import cuda
iou = polygon_iou_gpu(polys[i], polys[cuda.to_cpu(order[j + 1])])

将其转换为numpy数组实际上是朝着CPU实现的方向发展。目标是通过使用CuPy来加速。 - Majid Azimi
它只向GPU发送索引“order”,这只是很小的一部分。 如果你想获得GPU的好处,可以在GPU上计算大数组。我猜上面的示例代码使用了小数组,在这种情况下,我认为简单地在CPU上计算会更快。 - corochann
1
哦,现在我明白你的意思了。我已经修改了代码以考虑随机100个检测。但我仍然有同样的问题。我尝试了@yuki-hashimoto的答案。我也尝试了你的答案。它可以工作,但GPU的速度比CPU慢得多,当有10000个随机检测时。 - Majid Azimi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接