某一天写代码的时候突然遇到一个场景,需要批量对标注信息box进行操作(box包括[x1,y1,x2,y2])。
最简单的操作就是,for循环遍历将box一个一个存到list中最终转化为numpy的二维数组进行操作:
bboxes = [] for k in range(num_objs): ann = anns[k] bbox = self._coco_box_to_bbox(ann['bbox']) # 构造array的时候需要 [[]] 二维方式构造 sbbox = np.array([[bbox[0], bbox[1], bbox[2], bbox[3]]]) bboxes.append(sbbox) if bboxes != []: bboxes = np.concatenate(bboxes, 0)
需要注意的是我们在构造numpy数组的时候,需要提前把二维这个维度信息告诉np.array:
>>> import numpy as np >>> a = np.array([1,2,3,4]) >>> b = [] >>> b.append(a) >>> b.append(a) >>> b [array([1, 2, 3, 4]), array([1, 2, 3, 4])] >>> c = np.concatenate(b) >>> c array([1, 2, 3, 4, 1, 2, 3, 4]) >>> c.shape # 此时concat后的c还是一维的 (8,) # 这样 >>> a = np.array([[1,2,3,4]]) # 需要提前指明2维 >>> b = [] >>> b.append(a) >>> b.append(a) >>> b [array([[1, 2, 3, 4]]), array([[1, 2, 3, 4]])] >>> c = np.concatenate(b) >>> c array([[1, 2, 3, 4], [1, 2, 3, 4]]) >>> c.shape # 此时concat后的维度才是2维 (2, 4)
在np.concatenate
后bboxes的维度是(N,5),此时可以通过这种方式去批量处理x1,y1,x2,y2
,
offset_x = 1 offset_y = 2 bboxes[:, 0] = bboxes[:, 0] + offset_x bboxes[:, 1] = bboxes[:, 1] + offset_y bboxes[:, 2] = bboxes[:, 2] + offset_x bboxes[:, 3] = bboxes[:, 3] + offset_y
numpy的array可以这样操作,但是对于list来说是不行的:
>>> a=[[1,2,3],[4,5,6]] >>> a[0] #取第一行是可以的 [1, 2, 3] >>> a[:,0] #尝试用数组索引方式失败 TypeError: list indices must be integers or slices, not tuple
这是因为python中的list
和numpy中的array
是完全不一样的两个东西,list可以存放不同类型的数据,比如int、float和str,甚至布尔型;而一个numpy数组中存放的数据类型必须全部相同,例如int或float。
在list中的数据类型保存的是数据的存放的地址,即指针而非数据(底层是C语言,这样想想也很正常),例如a=[1,2,3,4]
需要4个指针和四个数据,增加了存储和消耗cpu,而a=np.array([1,2,3,4])
只需要存放四个数据,读取和计算更加方便。
所以列表List可以存放不同类型的数据,因此列表中每个元素的大小可以相同,也可以不同,所以也就不支持一次性读取一列。即使是对于标准的二维数字列表([[1,2,3,4]]
这种),所以纯数字的我们最好都使用numpy的数据类型去操作。