Python Opencv: How to blend images

Introduction

In this tutorial we will learn how to use Python and OpenCV to blend two images.

This tutorial was tested on Windows 8.1, with version 4.1.2 of OpenCV. The Python version used was 3.7.2.

Blending the images

We will start, as usual, by importing the cv2 module, so we have access to all the functions we need to blend images.

import cv2

Then we are going to read from the file system the two images we want to blend. I’m going to experiment with a photo of a monument and my cat, but you can play around with different images to check which interesting effects you can obtain.

For a detailed explanation on how to read images from the file system and displaying them, please check this previous post. As covered in the post, we will obtain the images as numpy ndarrays.

img1 = cv2.imread('C:/Users/N/Desktop/monument.png')
img2 = cv2.imread('C:/Users/N/Desktop/olivia.jpg')

In order for us to be able to blend both images, they need to have the same dimensions. So, we will convert both images we have just read to be 400×400. Naturally, depending on the images you are using, you may convert only one of them or, if they are already the same size, you can skip this step. For an introduction on how to resize images with OpenCV and Python, please follow this link.

img1 = cv2.resize(img1, (400, 400))
img2 = cv2.resize(img2, (400, 400))

Finally, to blend both images, we will call the addWeighted function from the cv2 module. This function allows us to blend the images by applying the following function to them [1]:

BlendedImage =α⋅Img1+(1-α)⋅Img2+γ

If we change (1-α) by β, to align the mathematical representation with the parameters of the function, we get:

BlendedImage =α⋅Img1+β⋅Img2+γ

In short, alpha and beta are the weights that image 1 and image 2 will contribute to the final result, respectively. Taking in consideration the previous expression, the addWeighted function receives the following parameters:

  • First image;
  • Alpha: the weight of the first image. Should be a float between 0.0 and 1.0. We will set it to 0.3;
  • Second image;
  • Beta: the weight of the second image. Should also be a float between 0.0 and 1.0. We will set it to 0.7;
  • Gamma, which we won’t be using. So, we set it to 0.0.

As output, the addWeighted function returns the blended image.

result = cv2.addWeighted(img1, 0.3, img2, 0.7, 0.0);

After this, we will display the resulting image in a window. We will also display the original images in two other windows, for comparison. After that, we will wait until the user clicks a key to destroy all windows and end the program.

cv2.imshow('blend', result)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)

cv2.waitKey(0)
cv2.destroyAllWindows()

The complete Python script can be seen below.

import cv2

img1 = cv2.imread('C:/Users/N/Desktop/monument.png')
img2 = cv2.imread('C:/Users/N/Desktop/olivia.jpg')

img1 = cv2.resize(img1, (400, 400))
img2 = cv2.resize(img2, (400, 400))

result = cv2.addWeighted(img1, 0.3, img2, 0.7, 0.0);

cv2.imshow('blend', result)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)

cv2.waitKey(0)
cv2.destroyAllWindows()

To test the previous code, simply run it in a tool of your choice. I’m using PyCharm, a Python IDE. Don’t forget to change the paths of the images to point to the ones in your machine.

You should get an output similar to figure 1. As can be seen, we obtained the blended image alongside the two original images used.

Output of the OpenCV program to blend images, showing the result and the original images.
Figure 1 – Output of the program, showing the blended image and the original images.

A more complex example

Now that we have covered the basics, we will check a slightly more complex example where we will add a slider to the OpenCV window, to be able to vary the weight of each image to the final result. For a detailed explanation on how to add a slider to a window, please check here.

Like before, we start by importing the cv2 module, followed by reading both images and resizing them to be 400×400.

We will then display the second image in a window called “blend“. It will be the one we will use every time we update the weights to display the resulting image. Note that we need to have the window created beforehand to add the slider.

The reason why we are showing the second image when we initialize the window is because we will start with a alpha of 0.0 (which means beta will be equal to 1.0). That corresponds to showing the second image only. Then, later when the slider callback executes from changing its value, we will perform the blend operation.

import cv2

img1 = cv2.imread('C:/Users/N/Desktop/monument.png')
img2 = cv2.imread('C:/Users/N/Desktop/olivia.jpg')

img1 = cv2.resize(img1, (400, 400))
img2 = cv2.resize(img2, (400, 400))

cv2.imshow('blend', img2)

Then we will add the slider to the window with a call to the createTrackbar function. It will be used to set the value of alpha. We will provide the following arguments:

  • Name of the slider widget. We will call it “slider“;
  • Name of the window where the slider will be added. Recall that we have called our window “blend“;
  • Initial value of the slider in the scale. We will start it at the value zero;
  • Maximum value of the slider scale. Note that the slider doesn’t support receiving floating point values and if we use a maximum of scale of 1, the scale will only have two values. So, we will pass the value 100 to be able to fine tune the values, which we will later normalize in the callback function to a value between 0 and 1;
  • Callback function that will be executed when the position of the slider changes. We will analyze the implementation below, but we will call on_change to this function.
cv2.createTrackbar('slider', 'blend', 0, 100, on_change)

To end this part of the code, we will wait for the user to click any button to destroy the window and end the script.

cv2.waitKey(0)
cv2.destroyAllWindows()

The implementation of the callback function will be very simple. Recall from the previous that the callback function receives the current value of the slider.

def on_change(val):
    #Implementation of the callback

The first thing we will do is printing the value to the console, for debugging purposes.

print(val)

Then, we will normalize the received value (between 0 and 100) to an alpha between 0 and 1. We do this simply by dividing the received value by 100.

alpha = val/100

Recall from the previous section that beta can be calculated as 1-alpha, which we will also do.

beta = (1.0 - alpha)

Now that we have both alpha and beta, we will blend the images we already read, using these weights. Once we obtain the result, we will show it in the window we have called “blend“.

result = cv2.addWeighted(img1, alpha, img2, beta, 0.0);
cv2.imshow('blend', result)

The full callback function is shown in the snippet below.

def on_change(val):
    print(val)
    alpha = val/100
    beta = (1.0 - alpha)
    result = cv2.addWeighted(img1, alpha, img2, beta, 0.0);
    cv2.imshow('blend', result)

The complete code is shown in the snippet below.

import cv2


def on_change(val):
    print(val)
    alpha = val/100
    beta = (1.0 - alpha)
    result = cv2.addWeighted(img1, alpha, img2, beta, 0.0);
    cv2.imshow('blend', result)


img1 = cv2.imread('C:/Users/N/Desktop/monument.png')
img2 = cv2.imread('C:/Users/N/Desktop/olivia.jpg')

img1 = cv2.resize(img1, (400, 400))
img2 = cv2.resize(img2, (400, 400))

cv2.imshow('blend', img2)

cv2.createTrackbar('slider', 'blend', 0, 100, on_change)

cv2.waitKey(0)
cv2.destroyAllWindows()

Once again, simply run the previous Python script to test it. You should get a result like the one illustrated in figure 2. As can be seen, we can use the slider to control the weights of each of the original images to the final result.

Image blending weights controlled by slider.
Figure 2 – Image blending weights controlled by slider.

References

[1] https://docs.opencv.org/4.1.2/d2/de8/group__core__array.html#gafafb2513349db3bcff51f54ee5592a19

Leave a Reply

Discover more from techtutorialsx

Subscribe now to keep reading and get access to the full archive.

Continue reading