1

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. Expected output

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
Moritz
  • 352
  • 1
  • 3
  • 15
  • 2
    Your question would be easier to understand if you showed your starting images and the result you expect. Any particular reason for using OpenCV? – Mark Setchell Nov 23 '18 at 21:02
  • @MarkSetchell thank you, I edited my post. – Moritz Nov 23 '18 at 21:59
  • The minimum effort approach would be to record the channel count of the original, convert it to BGRA, run the algorithm you know that works, and then convert the result back to the original format. – Dan Mašek Nov 23 '18 at 22:06
  • 1
    Do you actually need to do any blending, or is the thing you overlay always rectangular and non-transparent? If so, then it's just a matter of making sure both images are of the same depth, and copy the overlay into a ROI of the background. – Dan Mašek Nov 23 '18 at 22:09
  • @DanMašek I want to use those images as a training set for a cnn, I guess blending is not required. The images I'm overlaying are always non-transparent, but can very in shape (rectangular, triangular and circular mainly). Thanks for your hint, I'll look into this on monday. Is it not possible to combine images of different depth? – Moritz Nov 24 '18 at 12:33

1 Answers1

2

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
Moritz
  • 352
  • 1
  • 3
  • 15