3

I have a numpy array and I want to change its strides while at the same time modifying its data so that the new array describes the same logical alignment of numbers. Is there any way to do that?

BACKGROUND: I read an image file with cv2.imdecode(), which produces a BGR image with a lowest-order stride set to 3 (so no gaps between channels of different pixels). I want to modify this image with the cairo package, which wants to work with stride 4 (that is, a gap of a byte between two consecutive pixels). Which is the best way to do that? (I would also like to be as optimized as possible, as I have to do this many times).

3
  • So is the input array (n,m,3) shape? And the output (n,m,4)? If so, how about concatenating a np.zeros((n,m,1)) array? Commented Sep 7, 2014 at 5:07
  • Oh yes, that's it. It was trivial, actually, but it didn't come to my mind. Sorry for the noise. If you want to write it as an answer, I can accept it. Commented Sep 7, 2014 at 8:12
  • I've added an as_strided solution Commented Sep 8, 2014 at 16:56

1 Answer 1

6

Assuming the input array is (n,m,3), then it can be expanded to (n,m,4) by simply concatenating a (n,m,1) array.

X = np.ones((n,m,3), dtype='byte')
F = np.zeros((n,m,1), dtype='byte')
X1 = np.concatenate([X,F], axis=2)

Strides for these are (3*m,3,1), (m,1,1) and (4*m,4,1).

The same data could put in

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)

With the target having dt = np.dtype([('R','u1'), ('G','u1'), ('B','u1'), ('A','u1')]) http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html

Mapping RGB fields to RGBA

But to do concatenation with these dtypes, we'd do some sort of casting to the (n,m,3) shape. Looks like reassigning the data attribute will do the trick.

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)

producing:

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')])

Using overlapping dtypes

Here's a way of doing it with overlapping dtypes rather than concatenation:

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'])

Or without the individual named byte fields it's even simpler:

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'] is (n,m,4), with strides (m*4, 4, 1).

X1['rgb'] is (n,m,3), but with the same strides (m*4, 4, 1).

Using as_strided

That difference in shape, but similarity in strides, suggest's using as_strided. Create the empty target array, and use as_strided to select a subset of elements to receive values from 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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.