17
votes

I have a RGB image img which is of shape (2560L, 1920L, 3L) and another single channel image mask which is of shape (2560L, 1920L). Now, I want to make this mask of shape (2560L, 1920L, 3L) i.e. I want to copy this single channel data into all the three channels.

I'm doing it as follows.

np.array([[[j,j,j] for j in i] for i in mask])

Is there a faster way of doing this using numpy?

4

4 Answers

13
votes
np.repeat(mask.reshape(2560L, 1920L, 1L), 3, axis=2)
10
votes

If you absolutely want to have the mask being (2560, 1920, 3), you can simply expand it along an axis (there are several ways to do that, but this one is quite straightforward):

>>> mask = np.random.random_integers(0, 255, (15, 12))
>>> mask_3d = mask[:, :, None] * np.ones(3, dtype=int)[None, None, :]
>>> mask.shape
(15L, 12L)
>>> mask_3d.shape
(15L, 12L, 3L)

However, in general, you can use these broadcasts directly. For instance, if you want to multiply your image by your mask:

>>> img = np.random.random_integers(0, 255, (15, 12, 3))
>>> img.shape
(15L, 12L, 3L)
>>> converted = img * mask[:, :, None]
>>> converted.shape
(15L, 12L, 3L)

So you never have to create the (n, m, 3) mask: broadcasting is done on the fly by manipulating the strides of the mask array, rather than creating a bigger, redundant one. Most of the numpy operations support this kind of broadcasting: binary operations (as above), but also indexing:

>>> # Take the lower part of the image
>>> mask = np.tri(15, 12, dtype=bool)
>>> # Apply mask to first channel
>>> one_channel = img[:, :, 0][mask]
>>> one_channel.shape
(114L,)
>>> # Apply mask to all channels
>>> pixels = img[mask]
>>> pixels.shape
(114L, 3L)
>>> np.all(pixels[:, 0] == one_channel)
True
5
votes

The dimensions can be expanded along the last axis and then tiled as follows.

mask = np.random.randn(200, 150)
mask3d = np.tile(mask[:, :, None], [1, 1, 3])

[1, 1, 3] tiles the mask 3 times in the last dimension.

0
votes
np.repeat(mask[..., np.newaxis], 3, axis=-1)