40

What is the best way to convert RGB image to RGBA in python using opencv?

Let's say I have one array with shape

(185, 198, 3) - it is RGB

and the other is alpha mask with shape (185, 198)

How to merge them and save to file?

ZdaR
  • 22,343
  • 7
  • 66
  • 87
Oleg
  • 3,080
  • 3
  • 40
  • 51

6 Answers6

71

With opencv3, this should work:

Python

# First create the image with alpha channel
rgba = cv2.cvtColor(rgb_data, cv2.COLOR_RGB2RGBA)

# Then assign the mask to the last channel of the image
rgba[:, :, 3] = alpha_data

C++

# First create the image with alpha channel
cv::cvtColor(rgb_data, rgba , cv::COLOR_RGB2RGBA);

# Split the image for access to alpha channel
std::vector<cv::Mat>channels(4);
cv::split(rgba, channels);

# Assign the mask to the last channel of the image
channels[3] = alpha_data;

# Finally concat channels for rgba image
cv::merge(channels, 4, rgba);
kaanoner
  • 841
  • 6
  • 6
  • 3
    This works like charm. This answer must be the accepted Answer – Trect Sep 07 '19 at 11:40
  • @kaanoner python: Can someone explain how this works? I am getting unexpected results - 3 does nothing, 0-2 produce various colours, and I don't understand how it's supposed to work. – Ddefin Orsstt Dec 23 '19 at 16:42
  • cvtcolor creates an extra channel for alpha layer on top of your three channel rgb data. you then assign to this new channel (by selecting it with [:,:,3]) your alpha data which is one channel. in the end you have four channel rgba image filled with the combination of your rgb and alpha channels. – kaanoner Dec 30 '19 at 23:14
  • This is the easiest method – Lewis Morris Nov 12 '20 at 20:35
43

You may use cv2.merge() to add the alpha channel to the given RGB image, but first you need to split the RGB image to R, G and B channels, as per the documentation:

Python: cv2.merge(mv[, dst])

  • mv – input array or vector of matrices to be merged; all the matrices in mv must have the same size and the same depth.

And this can be done as:

b_channel, g_channel, r_channel = cv2.split(img)

alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 50 #creating a dummy alpha channel image.

img_BGRA = cv2.merge((b_channel, g_channel, r_channel, alpha_channel))
ZdaR
  • 22,343
  • 7
  • 66
  • 87
8

Since OpenCV images are just Numpy arrays, you can do this in one-line, nice and fast with Numpy. So here is the setup code:

import numpy as np

# We'll synthesise a random image and a separate alpha channel full of 128 - semitransparent
im    = np.random.randint(0,256,(480,640,3), dtype=np.uint8)
alpha = np.full((480,640), 128, dtype=np.uint8)

And here is the solution which is simply to stack the alpha channel onto the image in the "depth" axis, hence dstack():

result = np.dstack((im, alpha))
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
5

Here is an another simple example using Grabcut, it helps to get the right order of channels when saving the image on disk vs pyplot.

from matplotlib import pyplot as plt
import numpy as np
import cv2

img = cv2.imread('image.jpg')

mask = np.zeros(img.shape[:2], np.uint8)
bgdModel = np.zeros((1,65), np.float64)
fgdModel = np.zeros((1,65), np.float64)
rect = (50, 50, 450, 290)

# Grabcut 
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)

r_channel, g_channel, b_channel = cv2.split(img) 
a_channel = np.where((mask==2)|(mask==0), 0, 255).astype('uint8')  

img_RGBA = cv2.merge((r_channel, g_channel, b_channel, a_channel))
cv2.imwrite("test.png", img_RGBA)

# Now for plot correct colors : 
img_BGRA = cv2.merge((b_channel, g_channel, r_channel, a_channel))

plt.imshow(img_BGRA), plt.colorbar(),plt.show()
snoob dogg
  • 2,491
  • 3
  • 31
  • 54
4
import cv2
import numpy as np
import skimage.exposure



path_input_image="./input_image.png"


input_image = cv2.imread(path_input_image2, cv2.IMREAD_UNCHANGED)


input_image_alphachann = np.full((input_image.shape[0],input_image.shape[1]), 128, dtype=np.uint8)

output_image = np.dstack((input_image, input_image_alphachann))


print(input_image.shape)
print(output_image.shape)
#(400, 200, 3); 3 channell rgb
#(400, 200, 4); 4c channel rgba



print(input_image.dtype)
print(output_image.dtype)
# uint8




path_output_image=path_input_image+'.alpha.png'
cv2.imwrite(path_output_image, output_image)

quine9997
  • 685
  • 7
  • 13
4

I'll post here my answer in C++ since it may be helpful to others (there are already enough answers in python):

  std::vector<cv::Mat> matChannels;
  cv::split(mat, matChannels);
  
  // create alpha channel
  cv::Mat alpha(...);
  matChannels.push_back(alpha);

  cv::merge(matChannels, dst);
Adriel Jr
  • 2,451
  • 19
  • 25