17

I am trying to make a transparent image and draw on it, and after I will addWeighted over the base image.

How can I initialize fully transparent image with width and hight in openCV python?

EDIT: I want to make a effect like in Photoshop, having stack of the layers, all stacked layers are initially transparent and drawing is performed on fully transparent layer. On the end I will merge all layers to get final image

2
  • 1
    This is very broad. Just use a new image (e.g. white), draw on it and somehow mark where you are drawing (not needed if you don't draw white as you can check for all pixels != white). Then all non-drawed weights are zero, when you combine these two images. (I'm not an opencv user and i made some assumptions about how addWeighted works). Commented Jun 16, 2017 at 17:49
  • 1
    OpenCV supports alpha channel but doesnt support rendering them. Commented Jun 16, 2017 at 18:54

4 Answers 4

25

For creating a transparent image you need a 4 channel matrix, 3 of which would represent RGB colors and the 4th channel would represent Alpha channel, To create a transparent image, you can ignore the RGB values and directly set the alpha channel to be 0. In Python OpenCV uses numpy to manipulate matrices, so a transparent image can be created as

import numpy as np
import cv2

img_height, img_width = 300, 300
n_channels = 4
transparent_img = np.zeros((img_height, img_width, n_channels), dtype=np.uint8)

# Save the image for visualization
cv2.imwrite("./transparent_img.png", transparent_img)
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you, I have tried to draw something on the image you have proposed, but it doesnt work. Image is truly created but I cannot draw a simple rectangle on it. Operation is completed, but when image is saved, rectangle cannot be seen.
Actually it worked out when i added rectangle with 4 channels, with alpha 255 value... thank you
@MichaelPresečan could you edit the answer and add the rectangle so the example is broader.
@MichaelPresečan Please edit the answer with your addition of the rectangle for others as well
18

Using the alpha channel as a binary mask

import cv2
import numpy as np

#create 3 separate BGRA images as our "layers"
layer1 = np.zeros((500, 500, 4))
layer2 = np.zeros((500, 500, 4))
layer3 = np.zeros((500, 500, 4))

#draw a red circle on the first "layer",
#a green rectangle on the second "layer",
#a blue line on the third "layer"
red_color = (0, 0, 255, 255)
green_color = (0, 255, 0, 255)
blue_color = (255, 0, 0, 255)
cv2.circle(layer1, (255, 255), 100, red_color, 5)
cv2.rectangle(layer2, (175, 175), (335, 335), green_color, 5)
cv2.line(layer3, (170, 170), (340, 340), blue_color, 5)

res = layer1[:] #copy the first layer into the resulting image

#copy only the pixels we were drawing on from the 2nd and 3rd layers
#(if you don't do this, the black background will also be copied)
cnd = layer2[:, :, 3] > 0
res[cnd] = layer2[cnd]
cnd = layer3[:, :, 3] > 0
res[cnd] = layer3[cnd]

cv2.imwrite("out.png", res)

enter image description here

With proper (or so I hope) alpha blending

The equations are taken from this Wikipedia page (the "over" operator in the "Description" section). It does not mention how to avoid possible divisions by 0 resulting alpha values in the second equation, but my common sense tells me that would only be the case if both source alpha values are 0, and therefore the resulting BGR should be 0, and that is confirmed by the Japanese Wikipedia page.

import cv2
import numpy as np

def blend(img_foreground, img_background):
    '''
    Alpha blending the foreground image on top of the background.
    Both should be BGRA.
    The value of each resulting pixel becomes
    A = A_foreground + A_background * (1 - A_foreground)
    BGR = (BGR_foreground * A_foreground + BGR_background * A_background * (1 - A_foreground)) / A
    If A is 0, BGR is [0, 0, 0].
    '''

    # blending the normalized alpha components
    alpha_a_norm = img_foreground[:, :, 3].astype(np.float32) / 255.0
    alpha_b_norm = img_background[:, :, 3].astype(np.float32) / 255.0
    alpha_o_norm = alpha_a_norm + alpha_b_norm * (1 - alpha_a_norm)

    # reshape alpha arrays to multiply BGR components
    aan = alpha_a_norm[:, :, np.newaxis]
    abn = alpha_b_norm[:, :, np.newaxis]
    aon = alpha_o_norm.copy()[:, :, np.newaxis] # going to modify, changing 0s to 1s, so use a copy instead of a view

    # blending the BGR components
    color_a = img_foreground[:, :, :3]
    color_b = img_background[:, :, :3]

    # this divisor may be 0, but only when both source's alphas are 0,
    # and the resulting color is, therefore, also 0.
    # to avoid invalid divisions or further checks for 0,
    # let's replace all 0-s to 1-s, so the division has no effect
    aon[aon == 0] = 1
    color_o = (color_a * aan + color_b * abn * (1 - aan)) / aon

    # de-normalizing resulting alpha back to 0~255, adding to the resulting BGR
    alpha_result = alpha_o_norm * 255
    result = np.dstack((color_o, alpha_result))
    return result.astype(np.uint8)

# create 3 separate BGRA images as our "layers"
layer1 = np.zeros((500, 500, 4))
layer2 = np.zeros((500, 500, 4))
layer3 = np.zeros((500, 500, 4))

# create 3 colors of different opacity
red_color   = (0, 0, 255, 255) # red, fully opaque
green_color = (0, 255, 0, 127) # green, half-opaque
blue_color  = (255, 0, 0,  64) # blue, quarter-opaque

# draw 3 shapes with our colors
cv2.circle(layer1, (255, 255), 100, red_color, 5)
cv2.rectangle(layer2, (175, 175), (335, 335), green_color, 5)
cv2.line(layer3, (170, 170), (340, 340), blue_color, 5)

# create a fully transparent image to blend on top of
res = np.full(shape=(500, 500, 4), fill_value=0, dtype=np.uint8)

# alternatively, to draw on top of an existing image,
# that image has to have an alpha channel, too,
# or we have to add it:
#res = cv2.imread("an_500x500_RGB_image.jpg")
#alpha = np.full((500, 500), 255, dtype=np.uint8)
#res = np.dstack((res, alpha))

# blend the "layers" one by one
res = blend(layer1, res)
res = blend(layer2, res)
res = blend(layer3, res)

cv2.imwrite("out.png", res)

enter image description hereenter image description here

2 Comments

5

To convert an image's white parts to transparent:

import cv2
import numpy as np

img = cv2.imread("image.png", cv2.IMREAD_UNCHANGED)
img[np.where(np.all(img[..., :3] == 255, -1))] = 0
cv2.imwrite("transparent.png", img)

1 Comment

Really? Does this work for you? Is a white background turn black your idea of a transparent image? I might undestand your will to help but not why do you waste people's time ...
-1

I draw on paper, take photo of drawing on phone, send it to computer (jpeg - just standard photo then send by email or messenger) and then do image parsing.

I wanted to make all my drawings transparent with black color of elements.
it works much better than expected, but filesize increased from 140kb to 3.3mb.
i think size may be reduced but i dont have need for this right now.

it also takes around 20s to parse 1536x2048x24 bpp image.

import cv2
import time 

# variable declarations
img_name = "piestyl.jpeg"
output_name = "piestyl_transparent.png"


# for execution time measurement
start = time.time()

img = cv2.imread(img_name, cv2.IMREAD_GRAYSCALE)

imgBGRA = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
height, width, x = imgBGRA.shape

end = time.time()
print("Time taken to load image: ", end - start)

start = time.time()
for h in range(height):
    for w in range(width):
        pData = imgBGRA[h][w]
        R = pData[0]
        G = pData[1]
        B = pData[2]
        Y = 0.2126 * R + 0.7152 * G + 0.0722 * B

        # if > 128 then it is whiteish, so we change it to transparent
        if (Y > 128):
          pData[3]=0
          imgBGRA[h][w]=pData

        # you can do part for blackish too, if needed (y < 128)
        else:
          pass

end = time.time()
print("Time taken to parse: ", end - start)
cv2.imwrite(output_name, imgBGRA)

4 Comments

This is really poor on so many levels. You should avoid iterating over images with for loops, it is slow and inefficient. You should think about how many channels you really have/need. Try to favour OpenCV or Numpy for thresholding or any image processing.
Also, since you've done the work to write and test the code and process an image, you should add the input and resulting output images.
this answer does not properly involve the alpha channel in any blending calculation. it treats the alpha channel as binary. there isn't even any blending or compositing going on. this merely calculates a gray value and then sets the alpha channel to 0.
the entire question ought to be closed as a dupe of stackoverflow.com/questions/40895785/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.