1

I am trying to overlay a transparent PNG over a JPG image, I somehow cannot get it to work, what I tried:

import cv2
import numpy as np

a = cv2.imread("a.jpeg")
b = cv2.imread("b.png", cv2.IMREAD_UNCHANGED)

# add alpha channel to jpeg
(h, w) = a.shape[:2]
a = np.dstack([a, np.ones((h, w), dtype="uint8") * 0])

c = cv2.add(b,a)
cv2.imwrite("out.jpeg", c)

The following code will combine the two images, but the PNG transparency is not correct, it's somehow more opaque than it should be. ( I read this could be a issue with opencv ? not reading alpha correctly from png )

What I am trying to do is simply stack two image over another, a background JPG and put over a PNG that has some transparent zones, both images are the same size.

Thanks!

Mihail
  • 81
  • 1
  • 8
  • Could you provide the images you're using? – rayryeng May 29 '19 at 09:05
  • You are just adding... which may give you wrong results.... (a pixel could result in white if it is a pixel (255, 0, 255) and (0,255,0) and I guess you want to preserve the colors) First, what do you want to do? have a semi transparent image over a background or a solid image over the background (where the transparency is 0)? Also, post an example image, and if possible what you want to achieve – api55 May 29 '19 at 09:26
  • `np.ones(...) * 0` – why do you not use `np.zeros`? And maybe this is the cause of the problem, where you mix up an alpha channel containing ones vs. zeros (transparent vs. opaque)? – mkrieger1 May 29 '19 at 09:35
  • @api55: I want to have a solid PNG overlay-ed over a JPG background, 0 transparency. I manage to achieve this result by using the ImageMagick convert, like: convert -composite background.jpg overlay.png result.jpg – Mihail May 29 '19 at 10:09
  • 1
    Possible duplicate of [How to join png with alpha / transparency in a frame in realtime](https://stackoverflow.com/questions/36921496/how-to-join-png-with-alpha-transparency-in-a-frame-in-realtime) – Dan Mašek May 29 '19 at 13:02

1 Answers1

1

I found a example on github I modified a bit, works as expected:

import numpy as np
import cv2

img = cv2.imread('1_calib.jpeg')
overlay_t = cv2.imread('ol.png',-1) # -1 loads with transparency

def overlay_transparent(bg_img, img_to_overlay_t):
    # Extract the alpha mask of the RGBA image, convert to RGB 
    b,g,r,a = cv2.split(img_to_overlay_t)
    overlay_color = cv2.merge((b,g,r))

    mask = cv2.medianBlur(a,5)

    # Black-out the area behind the logo in our original ROI
    img1_bg = cv2.bitwise_and(bg_img.copy(),bg_img.copy(),mask = cv2.bitwise_not(mask))

    # Mask out the logo from the logo image.
    img2_fg = cv2.bitwise_and(overlay_color,overlay_color,mask = mask)

    # Update the original image with our new ROI
    bg_img = cv2.add(img1_bg, img2_fg)

    return bg_img

cv2.imshow('image',overlay_transparent(img, overlay_t))
cv2.waitKey(0)
Mihail
  • 81
  • 1
  • 8
  • I was about to write the answer jeje, BTW the medianBlur is not needed and to obtain mask you can do `mask = img_to_overlay_t[:,:,3]` and overlay_color can be obtained with `overlay_color = img_to_overlay_t[:,:, :-1]` – api55 May 29 '19 at 11:18
  • Indeed, thanks! I did a couple more tests and if the overlay has small areas of transparency, then the output is bad, has a lot of artifacts in those areas. – Mihail May 29 '19 at 11:29
  • if the image is not total solid (for example 50% transparent) you need to multiply the value with the percentage and the old value with the inverted percentage instead of blacking it out. This will look more natural in this areas with artifacts. Remember that OpenCV uses 255 as 100% and 0 as 0% so if you want from 0 to 1 you need to divde that channel by 255. – api55 May 29 '19 at 11:56