Collez une image sur une autre image à deux coordonnées données avec une opacité modifiée en utilisant PIL ou OpenCV en Python

Dec 10 2020

J'ai deux images avec des points donnés, un point par image, qui doivent être alignées pour que l'image résultante soit une somme des deux images, tandis que l'image 2 est collée sur l'image 1 avec une opacité de 40%. J'ai pris cette question en considération mais notre cas ne correspond pas exactement car la coordonnée d'image est fournie par l'utilisateur et les images peuvent avoir une large gamme de tailles.

Image 1:

Image2:

Résultat final (sortie souhaitée):

Pour cela, j'ai essayé la img.paste()fonction de PIL et le remplacement des valeurs dans le tableau numpy d'images en cv2, les deux donnant des résultats loin d'être souhaités.

Réponses

1 MarkSetchell Dec 11 2020 at 19:07

J'ai fait deux images d'entrée avec ImageMagick comme ceci:

magick -size 300x400 xc:"rgb(1,204,255)" -fill red -draw "point 280,250" 1.png
magick -size 250x80  xc:"rgb(150,203,0)" -fill red -draw "point 12,25"   2.png

Ensuite, exécutez le code suivant:

#!/usr/bin/env python3
"""
Paste one image on top of another such that given points in each are coincident.
"""

from PIL import Image

# Open images and ensure RGB
im1 = Image.open('1.png').convert('RGB')
im2 = Image.open('2.png').convert('RGB')

# x,y coordinates of point in each image
p1x, p1y = 280, 250
p2x, p2y = 12, 25

# Work out how many pixels of space we need left, right, above, below common point in new image
pL = max(p1x, p2x)
pR = max(im1.width-p1x,  im2.width-p2x)
pT = max(p1y, p2y)
pB = max(im1.height-p1y, im2.height-p2y)

# Create background in solid white
bg = Image.new('RGB', (pL+pR, pT+pB),'white')
bg.save('DEBUG-bg.png')

# Paste im1 onto background
bg.paste(im1, (pL-p1x, pT-p1y))
bg.save('DEBUG-bg+im1.png')

# Make 40% opacity mask for im2
alpha = Image.new('L', (im2.width,im2.height), int(40*255/100))
alpha.save('DEBUG-alpha.png')

# Paste im2 over background with alpha
bg.paste(im2, (pL-p2x, pT-p2y), alpha)
bg.save('result.png')

Le résultat est le suivant:

Les lignes qui enregistrent les images avec des noms commençant "DEBUG-xxx.png"sont juste pour un débogage facile et peuvent être supprimées. Je peux facilement les voir tous pour voir ce qui se passe avec le code et je peux facilement les supprimer tous en les supprimant "DEBUG*png".

Thymen Dec 11 2020 at 05:04

Sans plus de détails, je vais essayer de répondre à la question du mieux que je peux et nommer toutes les hypothèses supplémentaires que j'ai faites (et comment les gérer si vous ne pouvez pas les faire).

Puisqu'il n'y avait aucune image fournie, j'ai créé une image bleue et verte avec un point noir comme coordonnée de fusion, en utilisant le code suivant:

import numpy as np

from PIL import Image, ImageDraw


def create_image_with_point(name, color, x, y, width=3):
    image = np.full((400, 400, 3), color, dtype=np.uint8)
    image[y - width:y + width, x - width:x + width] = (0, 0, 0)
    image = Image.fromarray(image, mode='RGB')
    ImageDraw.Draw(image).text((x - 15, y - 20), 'Point', (0, 0, 0))
    image.save(name)
    return image


blue = create_image_with_point('blue.png', color=(50, 50, 255), x=300, y=100)
green = create_image_with_point('green.png', color=(50, 255, 50), x=50, y=50)

Cela donne les images suivantes:

Maintenant, je vais faire l'hypothèse que les images ne contiennent pas encore de couche alpha (comme je les ai créées sans). Par conséquent, je vais charger l'image et leur ajouter une couche alpha:

import numpy as np

from PIL import Image

blue = Image.open('blue.png')
blue.putalpha(255)

green = Image.open('green.png')
green.putalpha(255)

Mon hypothèse suivante est que vous connaissez à l'avance les coordonnées de fusion:

# Assuming x, y coordinates.
point_blue = (300, 100)
point_green = (50, 50)

Ensuite, vous pouvez créer une image vide, qui peut contenir facilement les deux images:

new_image = np.zeros((1000, 1000, 4), dtype=np.uint8)

Ceci est une hypothèse très étendue si vous ne connaissez pas la taille de l'image à l'avance, et au cas où vous ne le sauriez pas, vous devrez calculer la taille de combinaison des deux images.

Ensuite, vous pouvez placer le point des images au centre des images nouvellement créées (dans mon cas (500, 500). Pour cela, vous utilisez les points de fusion comme décalages. Et vous pouvez effectuer un mélange alpha (dans tous les cas np.uint8(img_1*alpha + img_2*(1-alpha)):) pour fusionner le images utilisant une opacité différente.

Qui est dans le code:

def place_image(image: Image, point_xy: tuple[int, int], dest: np.ndarray, alpha: float = 1.) -> np.ndarray:

    # Place the merging dot on (500, 500).
    offset_x, offset_y = 500 - point_xy[0], 500 - point_xy[1]

    # Calculate the location of the image and perform alpha blending.
    destination = dest[offset_y:offset_y + image.height, offset_x:offset_x + image.width]
    destination = np.uint8(destination * (1 - alpha) + np.array(image) * alpha)

    # Copy the 'merged' imaged to the destination location.
    dest[offset_y:offset_y + image.height, offset_x:offset_x + image.width] = destination
    return dest

# Add the background image blue with alpha 1
new_image = place_image(blue, point_blue, dest=new_image, alpha=1)

# Add the second image with 40% opacity
new_image = place_image(green, point_green, dest=new_image, alpha=0.4)

# Store the resulting image.
image = Image.fromarray(new_image)
image.save('result.png')

Le résultat final sera une image plus grande, des images combinées, encore une fois, vous pouvez calculer la bonne boîte englobante, de sorte que vous n'ayez pas ces énormes zones de «rien» qui ressortent. Le résultat final ressemblera à ceci: