0

I'm trying to put an image on top of another using OpenCV / Pillow, while keeping the foreground image transparent. If you look at this image, you can see that everything is going smoothly, except I'm not sure how to keep the image transparent.

I've tried implementing this StackOverflow solution by @fireant into my program, but nothing seems to work out.

How can I execute index.py as normal, but keep the foreground png transparent?

Images

dot_transparent.png

image.jpg

index.py

import os
import numpy
import cv2
from PIL import Image
from os.path import join, dirname, realpath
import json

def upload_files():
    #https://github.com/Itseez/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml
    face_cascade = cv2.CascadeClassifier('/Users/matt/Python/LazerEyes/haarcascade_eye.xml')

    #https://github.com/Itseez/opencv/blob/master/data/haarcascades/haarcascade_eye.xml
    eye_cascade = cv2.CascadeClassifier('/Users/matt/Python/LazerEyes/haarcascade_eye.xml')

    img = cv2.imread('new.png')
    img_to_place = cv2.imread('dot_transparent.png')

    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    gray_to_place = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
    img_h, img_w = gray.shape
    img_to_place_h, img_to_place_w = gray_to_place.shape

    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    for (x,y,w,h) in faces:
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]
        eyes = eye_cascade.detectMultiScale(roi_gray)
        for (ex,ey,ew,eh) in eyes:
            resized_img = cv2.resize(img_to_place, (eh, ew), interpolation = cv2.INTER_AREA)
            resized_img_h, resized_img_w, _ = resized_img.shape

            roi_color[ey:ey+resized_img_h, ex:ex+resized_img_w, :] = resized_img
HansHirse
  • 18,010
  • 10
  • 38
  • 67
mattwelter
  • 39
  • 5

2 Answers2

1

Since you have specific alpha values for each pixel, I'd follow another approach, and implement a pixel-wise alpha blending, disregarding any alpha channel in the final image.

I borrowed some lines from Quang Hoang's answer:

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

# Read images
img = cv2.imread('image.jpg')
dot = cv2.imread('dot_transparent.png', cv2.IMREAD_UNCHANGED)

# Manual manipulating one eye
ex, ey, ew, eh = 1430, 1490, 400, 400
dot = cv2.resize(dot, (eh, ew))

# Prepare pixel-wise alpha blending
dot_alpha = dot[..., 3] / 255.0
dot_alpha = np.repeat(dot_alpha[..., np.newaxis], 3, axis=2)
dot = dot[..., :3]

# Pixel-wise alpha blending
img[ey:ey+eh, ex:ex+ew, :] = img[ey:ey+eh, ex:ex+ew, :] * (1 - dot_alpha) + dot * dot_alpha

# Output
plt.figure(figsize=(9, 9))
plt.imshow(img[..., [2, 1, 0]])
plt.tight_layout()
plt.show()

That'd be the output:

Output

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.9.1
Matplotlib:    3.3.4
NumPy:         1.20.1
OpenCV:        4.5.1
----------------------------------------
HansHirse
  • 18,010
  • 10
  • 38
  • 67
  • Thank you! That looks great but I tried implementing your solution in my code but received `ValueError: operands could not be broadcast together with shapes (22,22,3) (1000,1000,3)` How can I implement your solution in `index.py`? – mattwelter Feb 23 '21 at 21:50
  • Obviously, you haven't resized the "dot" image, this has shape `(1000, 1000, 3)` to fit the found ROI, which seems to have shape `(22, 22, 3)`. Without seeing your updated code, it's impossible to help. Please [edit](https://stackoverflow.com/posts/66339930/edit) your question, and put your updated code below your existing question. – HansHirse Feb 24 '21 at 07:49
0

Read the dot_transparent.png with cv2.IMREAD_UNCHANGE flag, then you have the alpha channel (otherwise, you only have 3 channel)

img = cv2.imread('faces.jpg')
to_replace = cv2.imread('dot_transparent.png', cv2.IMREAD_UNCHANGED)
# sample eye dectection
ex,ey,ew,eh = 1600, 1600,200,200

resized_replace = cv2.resize(to_replace, (eh,ew))

img[ey:ey+eh, ex:ex+ew] = np.where(resized_replace[...,-1:]<50, 
                                   img[ey:ey+eh, ex:ex+ew],
                                   resized_replace[...,:-1]
                            )

# show with imshow
plt.imshow(img[...,::-1])

Output:

enter image description here

Quang Hoang
  • 146,074
  • 10
  • 56
  • 74