OpenCV overlay 2 image based on image mask - python

I need overlay 2 images based on third image mask
Example
1.-I have this background
2.-I have this object image and also i have de segmentation image
Object image
I'm try to merge Backgound and Object image based on third image (mask image)
(mask image)
The final result is Background image + Object image(only based on mask)
Any idea..
I tried
import cv2
added_image = cv2.addWeighted(back_img,0.4,aug_demoimage,0.1,0)
But not working as expected.. any sugestion? thanks!

Solved
def get_only_object(img, mask, back_img):
fg = cv2.bitwise_or(img, img, mask=mask)
#imshow(fg)
# invert mask
mask_inv = cv2.bitwise_not(mask)
#fg_back = cv2.bitwise_or(back_img, back_img, mask=mask)
fg_back_inv = cv2.bitwise_or(back_img, back_img, mask=mask_inv)
#imshow(fg_back_inv)
final = cv2.bitwise_or(fg, fg_back_inv)
#imshow(final)
return final

You need to convert the object image into an RGBA image where the alpha channel is the mask image you have created. Once you do this, you can paste it to the background image.
def convert_to_png(img, a):
#alpha and img must have the same dimenstons
fin_img = cv2.cvtColor(img, cv2.COLOR_RGB2RGBA)
b, g, r, alpha = cv2.split(fin_img)
alpha = a
# plt.imshow(alpha);plt.title('alpha image');plt.show()
# plt.imshow(img);plt.title('original image');plt.show()
# plt.imshow(alpha);plt.title('fin alpha image');plt.show()
fin_img[:,:, 0] = img[:,:,0]
fin_img[:,:, 1] = img[:,:,1]
fin_img[:,:, 2] = img[:,:,2]
fin_img[:,:, 3] = alpha[:,:]
# plt.imshow(fin_img);plt.title('fin image');plt.show()
return fin_img
This function will combine the two images into an RGBA image.
y1, y2 = new_loc[1], new_loc[1] + img.shape[0]
x1, x2 = new_loc[0], new_loc[0] + img.shape[1]
alpha_s = img[:, :, 3] / 255.0
alpha_l = 1.0 - alpha_s
for c in range(0, 3):
fin_img[y1:y2, x1:x2, c] = (alpha_s * img[:, :, c] +
alpha_l * img[y1:y2, x1:x2, c])
And this will copy the Object image to the background image

Related

Get transparent background by using mask, I get white background

def combined_display(image, matte):
# calculate display resolution
w, h = image.width, image.height
rw, rh = 800, int(h * 800 / (3 * w))
# obtain predicted foreground
image = np.asarray(image)
if len(image.shape) == 2:
image = image[:, :, None]
if image.shape[2] == 1:
image = np.repeat(image, 3, axis=2)
elif image.shape[2] == 4:
image = image[:, :, 0:3]
matte = np.repeat(np.asarray(matte)[:, :, None], 3, axis=2) / 255
foreground = image * matte + np.full(image.shape, 255) * (1 - matte)
foreground = Image.fromarray(np.uint8(foreground))
foreground.save("imag.png", format="png")
return foreground
I'm trying to get transparent background like removebg, I don't want white background. Please help me with removing background and get transparent background.
Suppose you have a 3-channel image and a 1-channel mask. Then you can stack them together into a 4-channel image where the last channel is responsible for transparency.
import numpy as np
from PIL import Image
image = Image.open('image.png')
mask = Image.open('mask.png').convert('L')
image_with_transparency = np.dstack((image, mask))
Image.fromarray(image_with_transparency).save('image_trs.png')
The result:

cant overlay mask without the color changing?

From the below image I have created below mask with the following code:
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_color, upper_color)
res = cv2.bitwise_and(img,img, mask= mask)
Image:
The mask:
I want to overlay res that looks like this:
I use this below code to overlay it.
bgra = cv2.addWeighted(bgra, 1, res, 1, 0)
in a loop that changes the color of the original image
for i in tqdm((range(rang)), ascii=True, desc = 'Loops'):
hmod += speed
alpha = img[:, :, 3]
bgr = img[:, :, 0:3]
hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
hnew = np.mod(h + hmod, 180).astype(np.uint8)
hsv_new = cv2.merge([hnew, s, v])
bgr_new = cv2.cvtColor(hsv_new, cv2.COLOR_HSV2BGR)
bgra = cv2.cvtColor(bgr_new, cv2.COLOR_BGR2BGRA)
bgra[:, :, 3] = alpha
if maskPresent[-1]:
bgra = cv2.addWeighted(bgra, 1, res, 1, 0)
if i == 0:
chromas = bgra
else:
try:
chromas = cv2.vconcat([bgra, chromas])
except MemoryError:
raise MemoryError('Out of memory(Try using an image with a lesser resolution or use a higher speed)')
but this also changes the color of the mask when the image is concatenated even though the res variable stays constant. Does anyone know a workaround?
chromares = cv2.bitwise_and(bgra, bgra, mask= mask)
bgra -= chromares
bgra += originalres #what was res```

Problems with transparent overlapping image in openCv

I have issue where I have transparent part of image, but I am not sure if it really is transparent because I can not see what is behind transparent part and it is look like there is nothing but under it should be another image, problem looks like this or like this. If I do not use masks for deleting black edges it looks like this
I want to make edges completely transparent or entirely delete them.
Thank you for your help. Have a nice day
this is how I load image
trainImg = cv2.imread(fnames[x], cv2.IMREAD_UNCHANGED)
trainImg_gray = cv2.cvtColor(trainImg, cv2.COLOR_RGB2GRAY)
queryImg = cv2.imread(fnames[y], cv2.IMREAD_UNCHANGED)
queryImg_gray = cv2.cvtColor(queryImg, cv2.COLOR_RGB2GRAY)
this is how I work with pictures
height = trainImg.shape[0] + queryImg.shape[0]
width = trainImg.shape[1] + queryImg.shape[1]
dim = (trainImg.shape[1], trainImg.shape[0])
print(fnames_cropped[x])
trainImg = cv2.imread(fnames_cropped[x])
(b, g, r) = cv2.split(trainImg)
trainImg = cv2.merge([r, g, b])
trainImg = cv2.resize(trainImg, dim)
trainImg = trim(trainImg)
dim = (queryImg.shape[1], queryImg.shape[0])
print(fnames_cropped[y])
queryImg = cv2.imread(fnames_cropped[y])
(b, g, r) = cv2.split(queryImg)
queryImg = cv2.merge([r, g, b])
queryImg = cv2.resize(queryImg, dim)
queryImg = trim(queryImg)
# Make mask of black pixels - mask is True where image is black
RGB = np.array(trainImg)
h, w = RGB.shape[:2]
# Add an alpha channel, fully opaque (255)
RGBA = np.dstack((RGB, np.zeros((h, w), dtype=np.uint8) + 255))
# Make mask of black pixels - mask is True where image is black
mBlack = (RGBA[:, :, 0:3] == [0, 0, 0]).all(2)
# Make all pixels matched by mask into transparent ones
RGBA[mBlack] = (0, 0, 0, 0)
trainImg = RGBA
# Make mask of black pixels - mask is True where image is black
RGB = np.array(queryImg)
h, w = RGB.shape[:2]
# Add an alpha channel, fully opaque (255)
RGBA = np.dstack((RGB, np.zeros((h, w), dtype=np.uint8) + 255))
# Make mask of black pixels - mask is True where image is black
mBlack = (RGBA[:, :, 0:3] == [0, 0, 0]).all(2)
# Make all pixels matched by mask into transparent ones
RGBA[mBlack] = (0, 0, 0, 0)
queryImg = RGBA
result = cv2.warpPerspective(trainImg, H, (width, height), flags=cv2.INTER_NEAREST, borderMode=cv2.BORDER_CONSTANT)
im = Image.fromarray(trim(result))
result[0:queryImg.shape[0], 0:queryImg.shape[1]] = queryImg
im = Image.fromarray(trim(result))
im.save(feature_extractor+feature_matching+fnames_cropped[x]+fnames_cropped[y])

how to fix png image onto another image with opencv with transparent background

I want to add glasses PNG image onto another image, but i get the PNG image with white background which hides the face.
I need to know what i did wrong please.
Original face image
Original glasses image
Expected result
import cv2
import numpy
img = cv2.imread("barack-obama.jpg")
lunette = cv2.imread("lunette.png", cv2.IMREAD_UNCHANGED)
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eyes = eye_cascade.detectMultiScale(img, scaleFactor = 1.1, minNeighbors = 5)
r = 500.0 / img.shape[1]
dim = (500, int(img.shape[0] * r))
resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
grey = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(grey, 1.3, 5)
for (x,y,w,h) in faces:
roi_grey = grey[y:y+h, x:x+w]
roi_color = resized[y:y + h, x:x + w]
eyes = eye_cascade.detectMultiScale(roi_grey)
for (ex,ey,ew,eh) in eyes:
cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
lunette = cv2.resize(lunette , (w,h))
w, h, c = lunette.shape
for i in range(0, w):
for j in range(0, h):
if lunette[i, j][0] != 0:
resized[y + i, x + j] = lunette[i, j][1]
#help please
cv2.imshow('img',resized)
cv2.waitKey(0)
Here is one way to do that in Python/OpenCV/Numpy:
Read the background image (Obama)
Read the overlay image (glasses) unchanged
Extract the base BGR channels from the overlay image
Extract the alpha channel from the overlay image as a mask
Insert the BGR channels of the overlay into the background image at the desired location
Insert the mask into a black image the size of the background image at the desired location
Use Numpy where to composite the new background and overlay images using the new mask image
Save the result
import cv2
import numpy as np
# read background image
img = cv2.imread("obama.jpg")
ht, wd = img.shape[:2]
# read overlay image
img2 = cv2.imread("sunglasses.png", cv2.IMREAD_UNCHANGED)
ht2, wd2 = img2.shape[:2]
# extract alpha channel as mask and base bgr images
bgr = img2[:,:,0:3]
mask = img2[:,:,3]
# insert bgr into img at desired location and insert mask into black image
x = 580
y = 390
bgr_new = img.copy()
bgr_new[y:y+ht2, x:x+wd2] = bgr
mask_new = np.zeros((ht,wd), dtype=np.uint8)
mask_new[y:y+ht2, x:x+wd2] = mask
mask_new = cv2.cvtColor(mask_new, cv2.COLOR_GRAY2BGR)
# overlay the base bgr image onto img using mask
result = np.where(mask_new==255, bgr_new, img)
# save results
cv2.imwrite('obama_glasses.jpg', result)
# display results
cv2.imshow('bgr', bgr)
cv2.imshow('mask', mask)
cv2.imshow('bgr_new', bgr_new)
cv2.imshow('mask_new', mask_new)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
You can use cvzone library for solve this problem.
install cvzone library
pip install cvzone
import cvzone in your project
import cvzone
imread your emoji with following format
lunette = cv2.imread("lunette.png", cv2.IMREAD_UNCHANGED)
now replace the your emoji like this:
YOUR BACKGROUND PICTURE = cvzone.overlayPNG(YOUR BACKGROUND PICTURE, lunette , [x, y])

Overlay images of different size and no of channels

I'm trying to overlay random images (natural scene images should be overlayed with sign images) using OpenCV and Python. They can vary in size, file extension and no. of channels (and many more, I guess). So I'm resizing the sign images according to the size of the natural scene image and put them onto the latter.
I have implemented fireant's code found here: overlay a smaller image on a larger image python OpenCv
But it only works for images with 4 channels.
Using cv2.addWeighted() always crops the larger image (scene image) to the size of the smaller image (sign image). Has anybody an idea how to do that? Help is highly appreciated.
EDIT: See the expected output below. At first the, escape route sign and the background are separate images.
And this is my code, it is working, but since a lot of my images seem to have only 3 channels, I would like to get it working for those also.
import cv2
import time
import math
import os
pathSigns = "/home/moritz/Schreibtisch/Signs"
pathScenes = "/home/moritz/Schreibtisch/Scenes"
i = 0
for fSigns in os.listdir(pathSigns):
fSigns = os.path.join(pathSigns, fSigns)
s_img = cv2.imread(fSigns, -1)
for fScenes in os.listdir(pathScenes):
try:
l_img = cv2.imread(os.path.join(pathScenes, fScenes))
l_height, l_width, l_channels = l_img.shape
TARGET_PIXEL_AREA = (l_height * l_width) * 0.05
ratio = float(s_img.shape[1]) / float(s_img.shape[0])
s_new_h = int(math.sqrt(TARGET_PIXEL_AREA / ratio) + 0.5)
s_new_w = int((s_new_h * ratio) + 0.5)
s_img = cv2.resize(s_img,(s_new_w, s_new_h))
x_offset=y_offset=50
# l_img[y_offset:y_offset+s_img.shape[0],
x_offset:x_offset+s_img.shape[1]] = s_img
y1, y2 = y_offset, y_offset + s_img.shape[0]
x1, x2 = x_offset, x_offset + s_img.shape[1]
height, width, channels = s_img.shape
if channels <= 3:
alpha_s = s_img[:, :, 2] / 255.0
alpha_l = 1.0 - alpha_s
else:
alpha_s = s_img[:, :, 3] / 255.0
alpha_l = 1.0 - alpha_s
for c in range(0, 3):
l_img[y1:y2, x1:x2, c] = (alpha_s * s_img[:, :, c] +
alpha_l * l_img[y1:y2, x1:x2, c])
fResult = "/home/moritz/Schreibtisch/results/data_" + str(i) +
".png"
i += 1
cv2.imwrite(fResult, l_img)
except IndexError:
pass
thanks to #DanMaĆĄek hint and How to crop or remove white background from an image, I have worked out a solution. The following code will first remove white background from the smaller image, then set all images to 4 channels and then overlay the larger image with a smaller image. Works for me.
import cv2
import time
import math
import os
import numpy as np
pathSigns = "/home/moritz/Schreibtisch/Signs"
pathScenes = "/home/moritz/Schreibtisch/Scenes"
i = 0
for fSigns in os.listdir(pathSigns):
fSigns = os.path.join(pathSigns, fSigns)
s_img = cv2.imread(fSigns, -1)
s_height, s_width, s_channels = s_img.shape
# crop image
gray = cv2.cvtColor(s_img, cv2.COLOR_BGR2GRAY)
th, threshed = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
morphed = cv2.morphologyEx(threshed, cv2.MORPH_CLOSE, kernel)
_, cnts, _ = cv2.findContours(morphed, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnt = sorted(cnts, key=cv2.contourArea)[-1]
x,y,w,h = cv2.boundingRect(cnt)
s_img = s_img[y:y+h, x:x+w]
# set channels to 4
if s_channels < 4:
s_img = cv2.cvtColor(s_img, cv2.COLOR_BGR2BGRA)
for fScenes in os.listdir(pathScenes):
try:
l_img = cv2.imread(os.path.join(pathScenes, fScenes))
l_height, l_width, l_channels = l_img.shape
if l_channels < 4:
l_img = cv2.cvtColor(l_img, cv2.COLOR_BGR2BGRA)
TARGET_PIXEL_AREA = (l_height * l_width) * 0.05
ratio = float(s_img.shape[1]) / float(s_img.shape[0])
s_new_h = int(math.sqrt(TARGET_PIXEL_AREA / ratio) + 0.5)
s_new_w = int((s_new_h * ratio) + 0.5)
s_img = cv2.resize(s_img,(s_new_w, s_new_h))
x_offset=y_offset=50
y1, y2 = y_offset, y_offset + s_img.shape[0]
x1, x2 = x_offset, x_offset + s_img.shape[1]
alpha_s = s_img[:, :, 3] / 255.0
alpha_l = 1.0 - alpha_s
for c in range(0, 3):
l_img[y1:y2, x1:x2, c] = (alpha_s * s_img[:, :, c] + alpha_l *
l_img[y1:y2, x1:x2, c])
fResult = "/home/moritz/Schreibtisch/results/data_" + str(i) + ".png"
i += 1
cv2.imwrite(fResult, l_img)
except IndexError:
pass

Categories