某一天写代码的时候突然遇到一个场景,需要批量对标注信息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的数据类型去操作。