更改numpy数组的步幅(修改数据)
我有一个numpy数组,我想在改变它的strides
的同时,修改它的数据,这样新的数组就能描述相同的数字排列。有没有什么办法可以做到这一点呢?
背景:我用cv2.imdecode()
读取了一张图片,这样得到的图像是BGR格式的,最低位的步幅设置为3(也就是说,不同像素的颜色通道之间没有间隔)。我想用cairo
这个库来修改这张图片,而这个库需要使用步幅为4(也就是两个连续像素之间有一个字节的间隔)。那么,最好的解决办法是什么呢?(我希望能尽量优化,因为我需要做很多次这个操作)。
1 个回答
假设输入的数组是 (n,m,3)
,那么可以通过简单地拼接一个 (n,m,1)
的数组来扩展成 (n,m,4)
。
X = np.ones((n,m,3), dtype='byte')
F = np.zeros((n,m,1), dtype='byte')
X1 = np.concatenate([X,F], axis=2)
这些数组的步幅(也就是每次移动的距离)分别是 (3*m,3,1)
、(m,1,1)
和 (4*m,4,1)
。
同样的数据可以放在
In [72]: dt0 = np.dtype([('R','u1'), ('G','u1'), ('B','u1')])
In [73]: X=np.ones((n,m),dtype=dt0)
In [74]: X.strides
Out[74]: (150, 3)
In [75]: X.shape
Out[75]: (30, 50)
目标数组的类型是 dt = np.dtype([('R','u1'), ('G','u1'), ('B','u1'), ('A','u1')])
,具体可以参考这个链接:http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html
将RGB字段映射到RGBA
但是要用这些数据类型进行拼接,我们需要将数据转换成 (n,m,3)
的形状。看起来重新分配 data
属性就可以解决这个问题。
n, m = 2, 4
dt1 = np.dtype([('R','u1'), ('G','u1'), ('B','u1'), ('A','u1')])
dt0 = np.dtype([('R','u1'), ('G','u1'), ('B','u1')])
X = np.arange(n*m*3,dtype='u1').reshape(n,m,3)
print repr(X)
X0 = np.zeros((n,m), dtype=dt0)
X0.data = X.data
print repr(X0)
X0.strides # (12, 3)
X1 = np.zeros((n,m), dtype=dt1)
F = np.zeros((n,m,1), dtype='u1')
X01 = np.concatenate([X, F], axis=2)
X1.data = X01.data
print repr(X1)
X1.strides # (12, 4)
这样就会产生:
array([[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11)],
[(12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23)]],
dtype=[('R', 'u1'), ('G', 'u1'), ('B', 'u1')])
array([[(0, 1, 2, 0), (3, 4, 5, 0), (6, 7, 8, 0), (9, 10, 11, 0)],
[(12, 13, 14, 0), (15, 16, 17, 0), (18, 19, 20, 0), (21, 22, 23, 0)]],
dtype=[('R', 'u1'), ('G', 'u1'), ('B', 'u1'), ('A', 'u1')])
使用重叠的数据类型
这里有一种方法可以使用重叠的数据类型,而不是拼接:
dt0 = np.dtype([('R','u1'), ('G','u1'), ('B','u1')])
dt1 = np.dtype([('R','u1'), ('G','u1'), ('B','u1'), ('A','u1')])
dtb = np.dtype({'names':['rgb','rgba'],
'offsets':[0,0],
'formats':[dt0, dt1]})
X0 = np.zeros((n,m), dtype=dt0)
X0.data = X.data
X1 = np.zeros((n,m), dtype=dtb)
X1['rgb'][:] = X0
print repr(X1['rgba'])
或者如果不使用单独命名的字节字段,那就更简单了:
dt0 = np.dtype(('u1',(3,)))
dt1 = np.dtype(('u1',(4,)))
dtb = np.dtype({'names':['rgb','rgba'],
'offsets':[0,0],
'formats':[dt0, dt1]})
X = np.arange(n*m*3,dtype='u1').reshape(n,m,3)
X1 = np.zeros((n,m), dtype=dtb)
X1['rgb'][:] = X
X1['rgba']
的形状是 (n,m,4)
,步幅是 (m*4, 4, 1)
。
X1['rgb']
的形状是 (n,m,3)
,但步幅也是 (m*4, 4, 1)
。
使用 as_strided
这种形状的差异,但步幅相似,提示我们可以使用 as_strided
。首先创建一个空的目标数组,然后用 as_strided
从 X
中选择一部分元素来填充这个目标数组:
X1 = np.zeros((n,m,4),dtype='u1')
np.lib.stride_tricks.as_strided(X1, shape=X.shape, strides=X1.strides)[:] = X
print X1