Pegue una imagen en otra imagen en dos coordenadas dadas con opacidad alterada usando PIL u OpenCV en Python
Tengo dos imágenes con puntos determinados, un punto para cada imagen, que deben alinearse para que la imagen resultante sea una suma de ambas imágenes, mientras que la imagen 2 se pega en la imagen 1 con un 40% de opacidad. He tomado esta pregunta en consideración, pero nuestro caso no coincide exactamente ya que la imagen coordinada la proporciona el usuario y las imágenes pueden tener una amplia gama de tamaños.
Imagen 1:

Imagen 2:

Resultado final (salida deseada):

Para esto, probé la img.paste()
función de PIL y reemplacé valores en una gran variedad de imágenes en cv2, y ambos dieron resultados que están lejos de los deseados.
Respuestas
Hice dos imágenes de entrada con ImageMagick así:
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


Luego ejecutó el siguiente código:
#!/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')
El resultado es este:

Las líneas que guardan imágenes con nombres comenzando "DEBUG-xxx.png"
son solo para una fácil depuración y se pueden eliminar. Puedo verlos todos fácilmente para ver qué está pasando con el código y puedo eliminarlos fácilmente quitando "DEBUG*png"
.
Sin más detalles, intentaré responder la pregunta lo mejor que pueda y nombraré todas las suposiciones adicionales que hice (y cómo manejarlas si no puede hacerlas).
Como no se proporcionaron imágenes, creé una imagen azul y verde con un punto negro como coordenada de fusión, usando el siguiente código:
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)
Esto da como resultado las siguientes imágenes:

Ahora supondré que las imágenes aún no contienen una capa alfa (ya que las creé sin ellas). Por lo tanto, cargaré la imagen y les agregaré una capa alfa:
import numpy as np
from PIL import Image
blue = Image.open('blue.png')
blue.putalpha(255)
green = Image.open('green.png')
green.putalpha(255)
Mi siguiente suposición es que conoce las coordenadas de fusión de antemano:
# Assuming x, y coordinates.
point_blue = (300, 100)
point_green = (50, 50)
Luego, puede crear una imagen vacía, que puede contener ambas imágenes fácilmente:
new_image = np.zeros((1000, 1000, 4), dtype=np.uint8)
Esta es una suposición muy extendida si no conoce el tamaño de la imagen de antemano y, en caso de que no lo sepa, tendrá que calcular el tamaño de combinación de las dos imágenes.
Luego, puede colocar el punto de imágenes en el centro de las imágenes recién creadas (en mi caso (500, 500). Para esto, usa los puntos de fusión como compensaciones. Y puede realizar una combinación alfa (en cualquier caso np.uint8(img_1*alpha + img_2*(1-alpha))
:) para fusionar el imágenes con diferente opacidad.
Que está en código:
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')
El resultado final será una imagen más grande, de las imágenes combinadas, nuevamente puede calcular el cuadro delimitador correcto, para que no sobresalgan estas enormes áreas de "nada". El resultado final se verá así:
