A solution upfront.

Background image

width/height/ratio: 300 / 375 / 0.800

Foreground image

width/height/ratio: 400 / 464 / 0.862

Overlay

from PIL import Image

imbg = Image.open("bg.png")
imfg = Image.open("fg.png")
imbg_width, imbg_height = imbg.size
imfg_resized = imfg.resize((imbg_width, imbg_height), Image.LANCZOS)
imbg.paste(imfg_resized, None, imfg_resized)
imbg.save("overlay.png")

Discussion

The most important information you have given in your question were:

  • the aspect ratios of your foreground and background images are not equal, but similar
  • the top left and bottom right corners of both images need to be aligned in the end.

The conclusion from these points is: the aspect ratio of one of the images has to change. This can be achieved with the resize() method (not with thumbnail(), as explained below). To summarize, the goal simply is:

Resize the image with larger dimensions (foreground image) to the exact dimensions of the smaller background image. That is, do not necessarily maintain the aspect ratio of the foreground image.

That is what the code above is doing.

Two comments on your approach:

First of all, I recommend using the newest release of Pillow (Pillow is the continuation project of PIL, it is API-compatible). In the 2.7 release they have largely improved the image re-scaling quality. The documentation can be found at http://pillow.readthedocs.org/en/latest/reference.

Then, you obviously need to take control of how the aspect ratio of both images evolves throughout your program. thumbnail(), for instance, does not alter the aspect ratio of the image, even if your size tuple does not have the same aspect ratio as the original image. Quote from the thumbnail() docs:

This method modifies the image to contain a thumbnail version of itself, no larger than the given size. This method calculates an appropriate thumbnail size to preserve the aspect of the image

So, I am not sure where you were going exactly with your (643,597) tuple and if you are possibly relying on the thumbnail to have this exact size afterwards.

Answer from Dr. Jan-Philip Gehrcke on Stack Overflow
๐ŸŒ
Quora
quora.com โ€บ How-can-I-overlay-2-images-of-different-sizes-using-Python
How to overlay 2 images of different sizes using Python - Quora
Answer (1 of 2): overlay of 2 images of different sizes I posted details on github: pydemo/overlay Background image Foreground image Get images width/height [code]b_h, b_w, b_ch = background.shape o_h, o_w, o_ch = overlay.shape [/code]Scale background image [code] W = 800 imgScale =...
Top answer
1 of 1
9

A solution upfront.

Background image

width/height/ratio: 300 / 375 / 0.800

Foreground image

width/height/ratio: 400 / 464 / 0.862

Overlay

from PIL import Image

imbg = Image.open("bg.png")
imfg = Image.open("fg.png")
imbg_width, imbg_height = imbg.size
imfg_resized = imfg.resize((imbg_width, imbg_height), Image.LANCZOS)
imbg.paste(imfg_resized, None, imfg_resized)
imbg.save("overlay.png")

Discussion

The most important information you have given in your question were:

  • the aspect ratios of your foreground and background images are not equal, but similar
  • the top left and bottom right corners of both images need to be aligned in the end.

The conclusion from these points is: the aspect ratio of one of the images has to change. This can be achieved with the resize() method (not with thumbnail(), as explained below). To summarize, the goal simply is:

Resize the image with larger dimensions (foreground image) to the exact dimensions of the smaller background image. That is, do not necessarily maintain the aspect ratio of the foreground image.

That is what the code above is doing.

Two comments on your approach:

First of all, I recommend using the newest release of Pillow (Pillow is the continuation project of PIL, it is API-compatible). In the 2.7 release they have largely improved the image re-scaling quality. The documentation can be found at http://pillow.readthedocs.org/en/latest/reference.

Then, you obviously need to take control of how the aspect ratio of both images evolves throughout your program. thumbnail(), for instance, does not alter the aspect ratio of the image, even if your size tuple does not have the same aspect ratio as the original image. Quote from the thumbnail() docs:

This method modifies the image to contain a thumbnail version of itself, no larger than the given size. This method calculates an appropriate thumbnail size to preserve the aspect of the image

So, I am not sure where you were going exactly with your (643,597) tuple and if you are possibly relying on the thumbnail to have this exact size afterwards.

Discussions

python - How can I overlay two images of people of different sizes and aspect ratios if I have body part coordinates? - Stack Overflow
I have two images of people(one person each picture), and I want to overlap them as best I can so that they are roughly the same size in the frame and their hips or shoulders overlap. For example say More on stackoverflow.com
๐ŸŒ stackoverflow.com
How do you overlay 2 different images/maps of different size with different features and adjusting what to show?

In addition to hotels/restaurants, what do the maps show? Do they show the same features? My first attempt would be to rescale one map and then overlay the maps on each other using 50% opacity or something, but that may look really bad. If you can extract just the locations, you might be able to overlay then on OpenStreetMaps or some other common map so the combination looks consistent

More on reddit.com
๐ŸŒ r/learnpython
8
1
April 9, 2023
How to overlay two images of different dimensions containing the same object
I am trying to overlay images of the same tissue slice. One image shows the prefixed slice stained with a certain dye and the other is postfixed (which washes the dye out) and costained with cellular markers. I want to overlay these two images to see which cell types contain the dye. More on forum.image.sc
๐ŸŒ forum.image.sc
2
0
October 4, 2021
python - Overlay images of different size and no of channels - Stack Overflow
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 ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
November 23, 2018
๐ŸŒ
Moonbooks
moonbooks.org โ€บ Articles โ€บ How-to-overlay--superimpose-two-images-using-python-and-pillow-
How to overlay / superimpose two images using python and pillow ?
August 24, 2022 - If the two images have different size, a solution is to use resize(), example: from PIL import Image import numpy as np img = Image.open("data_mask_1354_2030.png") print(img.size) background = Image.open("background_730_1097.png") print(background.size) # resize the image size = (1354,2030) ...
๐ŸŒ
Stack Overflow
stackoverflow.com โ€บ questions โ€บ 67535613 โ€บ how-can-i-overlay-two-images-of-people-of-different-sizes-and-aspect-ratios-if-i
python - How can I overlay two images of people of different sizes and aspect ratios if I have body part coordinates? - Stack Overflow
Now if I overlay A' and B' the shoulders and hips should be in roughly the same spots. I have to do this for tens of thousands of images so a photo editor wont work. I thought about using a big blank image but ran into issues doing it with numpy, I also have to scale the people so they're roughly the same size ... So you want the two images overlayed on top of each other in the final output?
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ overlay-an-image-on-another-image-in-python
Overlay an image on another image in Python - GeeksforGeeks
January 3, 2021 - Then we called the paste method to overlay/paste the passed image on img1. The first argument is img2 which is the image object of the image containing transparent text. This image would be used for overlay. The second argument is a size 2 tuple denoting the coordinates of img1, where the img2 should be pasted.
Find elsewhere
๐ŸŒ
GitHub
github.com โ€บ pydemo โ€บ overlay
GitHub - pydemo/overlay: Overlay 2 images using python and OpenCV ยท GitHub
#OVERLAY OPACITY = 0.7 added_image = cv2.addWeighted(new_background,0.6,square,0.4,0) cv2.imshow('adjusted', added_image) cv2.waitKey() cv2.imwrite(out, added_image)
Author: pydemo
๐ŸŒ
Image.sc
forum.image.sc โ€บ image analysis
How to overlay two images of different dimensions containing the same object - Image Analysis - Image.sc Forum
October 4, 2021 - I am trying to overlay images of the same tissue slice. One image shows the prefixed slice stained with a certain dye and the other is postfixed (which washes the dye out) and costained with cellular markers. I want to oโ€ฆ
Top answer
1 of 1
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
๐ŸŒ
YouTube
youtube.com โ€บ watch
OpenCV 12: Blending two Images of Different Size | Python | OpenCV - YouTube
OpenCV 12: Blending two Images of Different Size | Python | OpenCVGitHub JupyterNotebook: https://github.com/siddiquiamir/OpenCVGitHub Data: https://github.c...
Published: August 16, 2023
๐ŸŒ
Python Examples
pythonexamples.org โ€บ pillow-image-overlay
Image Overlaying in Python with Pillow
You may change the position, or change the size of the overlay image to get the desired results. In this Python Pillow Tutorial, we learned how to overlay an image on a base image using PIL.Image.paste() function, with the help of examples.
Top answer
1 of 9
181

A simple way to achieve what you want:

import cv2
s_img = cv2.imread("smaller_image.png")
l_img = cv2.imread("larger_image.jpg")
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

Update

I suppose you want to take care of the alpha channel too. Here is a quick and dirty way of doing so:

s_img = cv2.imread("smaller_image.png", -1)

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])

2 of 9
28

Using @fireant's idea, I wrote up a function to handle overlays. This works well for any position argument (including negative positions).

def overlay_image_alpha(img, img_overlay, x, y, alpha_mask):
    """Overlay `img_overlay` onto `img` at (x, y) and blend using `alpha_mask`.

    `alpha_mask` must have same HxW as `img_overlay` and values in range [0, 1].
    """
    # Image ranges
    y1, y2 = max(0, y), min(img.shape[0], y + img_overlay.shape[0])
    x1, x2 = max(0, x), min(img.shape[1], x + img_overlay.shape[1])

    # Overlay ranges
    y1o, y2o = max(0, -y), min(img_overlay.shape[0], img.shape[0] - y)
    x1o, x2o = max(0, -x), min(img_overlay.shape[1], img.shape[1] - x)

    # Exit if nothing to do
    if y1 >= y2 or x1 >= x2 or y1o >= y2o or x1o >= x2o:
        return

    # Blend overlay within the determined ranges
    img_crop = img[y1:y2, x1:x2]
    img_overlay_crop = img_overlay[y1o:y2o, x1o:x2o]
    alpha = alpha_mask[y1o:y2o, x1o:x2o, np.newaxis]
    alpha_inv = 1.0 - alpha

    img_crop[:] = alpha * img_overlay_crop + alpha_inv * img_crop

Example usage:

import numpy as np
from PIL import Image

# Prepare inputs
x, y = 50, 0
img = np.array(Image.open("img_large.jpg"))
img_overlay_rgba = np.array(Image.open("img_small.png"))

# Perform blending
alpha_mask = img_overlay_rgba[:, :, 3] / 255.0
img_result = img[:, :, :3].copy()
img_overlay = img_overlay_rgba[:, :, :3]
overlay_image_alpha(img_result, img_overlay, x, y, alpha_mask)

# Save result
Image.fromarray(img_result).save("img_result.jpg")

Result:

If you encounter errors or unusual outputs, please ensure:

  • img should not contain an alpha channel. (e.g. If it is RGBA, convert to RGB first.)
  • img_overlay has the same number of channels as img.
๐ŸŒ
OpenCV Q&A Forum
answers.opencv.org โ€บ question โ€บ 95032 โ€บ how-can-i-fit-and-then-overlay-2-images-which-have-different-resolution
How can I fit and then overlay 2 images which have different resolution ? - OpenCV Q&A Forum
May 27, 2016 - One represents has a mesh, which is supposed to overlay the Layer1 . I didn't find how can I do this using the opencv. I know that is possible change image resolution, however, I don't know how to fit both images ... #!/usr/bin/python import cv2 from matplotlib import pyplot as plt import numpy as np img1 = cv2.imread('transparency.jpg') img2 = cv2.imread('La1.png') row1,cols1, ch1 = img1.shape row2,cols2, ch2 = img2.shape res = cv2.resize(img2, None , fx = (1.* row1 /row2 ), fy =(1.* cols1 /cols2 ), interpolation = cv2.INTER_CUBIC)
๐ŸŒ
Plotly
community.plotly.com โ€บ ๐Ÿ“Š plotly python
How to overlay two images with opacity - ๐Ÿ“Š Plotly Python - Plotly Community Forum
June 7, 2023 - I have a simple problem, I want to overlay two rgb images (np.arrays with three channels and same size) over each other, with opacity setting for the top so I can see both. fig_visual_check = go.Figure() fig_visual_check = fig_visual_check.add_trace(go.Image(z=images_merged_visual[0], opacity=0.5)) fig_visual_check = fig_visual_check.add_trace(go.Image(z=images_merged_visual[1], opacity=1)) fig_visual_check However, the result is just a black image.
Top answer
1 of 1
9

Homographies compose, so if you have the homographies between img1 and img2 and between img2 and img3 then the composition of those two homographies gives the homography between img1 and img3.

Your sizes are off of course because you're trying to match img3 to the stitched image containing img1 and img2. But you don't need to do that. Don't stitch them until you have all the homographies between each successive pair of images. Then you can proceed in one of two ways; work from the back or work from the front. I'll use for e.g. h31 to refer to the homography which warps img3 into coordinates of img1.

From the front (pseudocode):

warp img2 into coordinates of img1 with h21
warp img3 into coordinates of img1 with h31 = h32 @ h21
warp img4 into coordinates of img1 with h41 = h43 @ h31
...
stitch/blend images together

Here @ is the matrix multiplication operator, which will achieve our homography composition (note that it is safest to divide by the final entry in the homography to ensure that they're all scaled the same).

From the back (pseudocode):

...
warp prev stitched img into coordinates of img3 with h43
stitch warped stitched img with img3
warp prev stitched img into coordinates of img2 with h32
stitch warped stitched img with img2
warp prev stitched img into coordinates of img1 with h21
stitch warped stitched img with img1

The idea is either you start from the front, and warp everything into the first images coordinate frame, or start from the back, warp to the previous image and stitch, and then warp that stitched image into the previous image, and repeat. I think the first method is probably easier. In either case you have to worry about the propagation of errors in your homography estimation as they will build up over multiple composed homographies.

This is the naรฏve approach to blend multiple images together with just the homographies. The more sophisticated method is to use bundle adjustment, which takes into account feature points across all images. Then for good blending the steps are gain compensation to remove camera gain adjustments and vignetting, and then multi-band blending to prevent blurring. See the seminal paper from Brown and Lowe here and a brilliant example and free demo software here.

๐ŸŒ
GitConnected
levelup.gitconnected.com โ€บ how-to-approach-image-overlay-problems-ad2d4a8e22bc
How to approach image overlay problems | by Shaurya Agarwal | Level Up Coding
December 14, 2021 - In order to do that, you must reshape the overlaying image to the size whose pixels values you want to replace.
๐ŸŒ
Stack Overflow
stackoverflow.com โ€บ questions โ€บ 45598317 โ€บ overlay-multiple-images-in-python
Overlay multiple images in python - Stack Overflow
August 10, 2017 - Currently I am searching the way to do it, ... One approach is to store all your images as a list, and then iterate through each overlapping pair of images and callcv2.addWeighted() on each element in your list, passing in the last aggregate ...
๐ŸŒ
Matplotlib
matplotlib.org โ€บ stable โ€บ gallery โ€บ images_contours_and_fields โ€บ layer_images.html
Layer images with alpha blending โ€” Matplotlib 3.11.2 documentation
import matplotlib.pyplot as plt import numpy as np def func3(x, y): return (1 - x / 2 + x**5 + y**3) * np.exp(-(x**2 + y**2)) # make these smaller to increase the resolution dx, dy = 0.05, 0.05 x = np.arange(-3.0, 3.0, dx) y = np.arange(-3.0, 3.0, dy) X, Y = np.meshgrid(x, y) # when layering multiple images, the images need to have the same # extent. This does not mean they need to have the same shape, but # they both need to render to the same coordinate system determined by # xmin, xmax, ymin, ymax. Note if you use different interpolations # for the images their apparent extent could be diff