21

Suppose I have a image with some dimension (1920, 1080, 3) , I want to extract out R,G,B values into separate arrays R , G, B . I tried to do it like

for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            B = np.append(B, image[i, j][0])
            G = np.append(G, image[i, j][1])
            R = np.append(R, image[i, j][2])

But as expected this is very slow , How can I do this with numpy in built function?

Sigma
  • 742
  • 2
  • 9
  • 24
  • 1
    This is basic indexing: `B = image[:,:,0]; G = image[:,:,1]; R = image[:,:,2]`. See pretty much any numpy tutorial. – Warren Weckesser Jan 06 '17 at 07:19
  • 1
    or just roll your axis, then take simple slices ... rolled = np.rollaxis(rgb,-1) ... r = rolled[0], g = rolled[1], b = rolled[2] – NaN Jan 06 '17 at 07:22
  • I would suggest you to install this extension in your vs code - https://marketplace.visualstudio.com/items?itemName=gsGupta.opencv-snippets&ssr=false It directly provides you with snippets of such things. – Gauri Shankar Gupta Jul 04 '20 at 11:29

3 Answers3

30

If you want it to use in OpenCV way then you may use cv2.split(), keeping in mind channels of your image:

b, g, r    = cv2.split(image) # For BGR image
b, g, r, a = cv2.split(image) # for BGRA image

Or if you may like direct numpy format then you may use directly [which seems to be more efficient as per comments of @igaurav]

b, g, r    = image[:, :, 0], image[:, :, 1], image[:, :, 2] # For RGB image
b, g, r, a = image[:, :, 0], image[:, :, 1], image[:, :, 2], image[:, :, 3] # for BGRA image

You may use np.shape[2] to check the number of channels in the given image.

ZdaR
  • 22,343
  • 7
  • 66
  • 87
  • 1
    Note: `python -m timeit -s "import urllib.request as r; import cv2; from os.path import exists as ex; b='bench.jpg'; r.urlretrieve('https://i.imgur.com/7vGjNmZ.jpg',b) if not ex(b) else False; x=cv2.imread(b)" "b,g,r = cv2.split(x)"` gives `33.1 ms`, and `"b,g,r = x[:,:,0], x[:,:,1], x[:,:,2]"` gives `803 nsec (0.000803 ms)`. Weird! The latter with numpy indexing does 3 function calls to the numpy C extension, and the OpenCV method only does 1 C call and should be optimized, but it's not. Unless we're doing something wrong, it seems OpenCV is a lot slower at splitting. This is good to know! – Mitch McMabers Dec 07 '19 at 23:57
  • 2
    Nevermind, I've figured it out. The Numpy method uses Numpy slicing which makes a new (very inefficient) Numpy array "view" which points at the old RAM and says "read every 3rd byte, skip 1st and 2nd bytes" to only look at a single channel. This is "fake-splitting" the channels, but is EXTREMELY INEFFICIENT at image processing. EVERY time you pass fake r,g or b (created this way) to an OpenCV or other library function, you cause a 30 millisecond delay for each function call as it has to recreate the "cheating" Numpy data into a real image object. Meanwhile, the OpenCV split creates 3 real img. – Mitch McMabers Dec 08 '19 at 00:05
  • Here is an article about the costs of cheating this way via Numpy. It may LOOK fast to use indexing ("ooh 0.000803 ms to split via numpy instead of 33.1ms to split via OpenCV"), but it's all a lie -- no data is split if you abuse the Numpy trick; no real "split" images are created in RAM -- and the Numpy trick is very, very slow in reality, because the data has to be fixed EVERY time you give such "fake splits" to OpenCV functions and other libraries: https://answers.opencv.org/question/219040/fastest-way-to-convert-bgr-rgb-aka-do-not-use-numpy-magic-tricks/ (`cv2.split()` is REAL splitting). – Mitch McMabers Dec 08 '19 at 00:05
  • 1
    Good helpful comments from your side buddy @MitchMcMabers. – ZdaR Dec 08 '19 at 01:32
13

dsplit it.

import numpy as np

def channelSplit(image):
    return np.dsplit(image,image.shape[-1])

[B,G,R]=channelSplit(image)

This works for RGB or RGBA images.

Daniel F
  • 13,620
  • 2
  • 29
  • 55
3

This works for me:

def split_channels(im: np.ndarray):
    assert len(im.shape) == 3 and im.shape[-1] == 3
    return np.squeeze(np.split(im, im.shape[-1], -1), axis=-1)

Note that, the np.split itself is not enough, which will leave you a (M, N, 1) image. But if you want to have (M, N), then the squeeze works.

You can remove the assert if you have other cases.

ch271828n
  • 15,854
  • 5
  • 53
  • 88