我有以下与for循环一起使用的操作。有人可以建议一种使用numpy将操作向量化的方法吗?
# rgb is a 3 channel image
# points are computed using vector mult op (same size as rgb image)
# dtypes - rgb is uint8 and points is float
buffer = []
for v in range(rgb.shape[1]):
for u in range(rgb.shape[0]):
X,Y,Z = points[u,v,:]
r,g,b = rgb[u,v,:]
buffer.append(struct.pack('ffffBBBBIII', X,Y,Z,0.0,
b,g,r,
255,0,0,0))
我想压缩以上操作并获取缓冲区。任何指针都会有所帮助
答案 0 :(得分:1)
如果结构类型(C类型)和numpy数值类型之间有对应关系,这应该非常简单。 struct的文档为here,而numpy的文档为here。相关的转换为:
'f'
-> np.single
(Python没有等效类型)'B'
-> np.ubyte
'I'
-> np.uintc
您可以通过创建custom dtype
来将输出创建为值的数组,就像struct
可以做到的那样:
dt = np.dtype([(c, np.single) for c in 'XYZW'] +
[(c, np.ubyte) for c in 'RGBA'] +
[('', np.intc, 3)])
为每个通道(例如[('X', np.single), ('Y', np.single), ...
)创建单独字段而不是为所有通道(例如[('XYZW', np.single, 4), ...
)创建单个字段的原因是您希望能够以统一的步幅访问数组。您将不会分配给其的空白部分可以是每个元素中的单个块:('zeros', np.intc, 3)
。
您可以具有其他dtype,以提供所需的结果。例如,您可以命名您的字段,或将其拆分为各个通道。在视图中,我建议您在写完输出数组后这样做,以简化处理。
现在您有了dtype,用它创建一个数组:
output = np.zeros(rgb.shape[:2], dtype=dt)
现在,您可以将dt.fields
属性与output.setfield
结合使用来存储字段:
for name, plane in zip('XYZ', np.moveaxis(points, -1, 0)):
tp, offset, *_ = dt.fields[name]
output.setfield(plane, tp, offset)
for name, plane in zip('RGB', np.moveaxis(rgb, -1, 0)):
tp, offset, *_ = dt.fields[name]
output.setfield(plane, tp, offset)
tp, offset, *_ = dt.fields['A']
output.setfield(255, tp, offset)
您可以使用itertoools.chain
缩短到一个循环:
from itertools import chain
for name, plane in zip('XYZRGBA', chain(np.moveaxis(points, -1, 0),
np.moveaxis(rgb, -1, 0),
[255])):
tp, offset, *_ = dt.fields[name]
output.setfield(plane, tp, offset)
请注意,循环在这里并不是很昂贵。它仅经历七个迭代。结果的每个元素都是结构调用所创建形式的正确缓冲区。您可以通过遍历output
数组来丢弃形状信息。
结果是一个具有自定义dtype的数组,其结构格式规范与'ffffBBBBIII'
等效。每个元素canis都是一个标量,可以通过字段名称进行索引:
>>> output[0, 0]['W']
0.0
您可以根据需要在数组中创建备用视图,例如,将值分组为类别或类似内容:
>>> dt2 = np.dtype([('p', np.single, 4), ('i', np.ubyte, 4), ('z', np.intc, 3)]
>>> output2 = output.view(dtype=dt2)
>>> output2[0, 0]['p']
array([0.501182 , 0.7935149, 0.9981835, 0. ], dtype=float32) # Random example data
此视图不复制数据,只是以不同的方式解释现有缓冲区。在内部,该表示形式仍然是您尝试使用struct
实现的压缩版本。
答案 1 :(得分:0)
我可以这样想:
def pack(points, rgb):
X, Y, Z = points.transpose(2, 0, 1)
r, g, b = rgb.transpose(2, 0, 1)
rec = np.rec.fromarrays([X,Y,Z,r,g,b])
def fn(point):
X, Y, Z, r, g, b = point
return struct.pack('ffffBBBBIII', X, Y, Z, 0.0, b, g, r, 255, 0, 0, 0)
return np.vectorize(fn)(rec)
答案 2 :(得分:-1)
您可以将structured arrray与dp一起使用
- app
|
|- Module 1
|
|- Module 2
|
|- Module 3
然后,您可以调用数组的方法ndarray.tobytes()来获取缓冲区。