Wklej obraz do innego obrazu przy dwóch podanych współrzędnych ze zmienionym kryciem za pomocą PIL lub OpenCV w Pythonie
Mam dwa obrazy z określonymi punktami, po jednym punkcie na każdym obrazie, które należy wyrównać, aby obraz wynikowy był sumą obu obrazów, podczas gdy obraz 2 jest wklejany na obrazie 1 z kryciem 40%. Brałem to pytanie pod uwagę, ale w naszym przypadku nie odpowiada dokładnie jak obraz koordynować jest dostarczane przez użytkownika i obrazów może mieć szeroki zakres rozmiarów.
Obraz 1:

Obraz2:

Wynik końcowy (pożądana wydajność):

W tym celu wypróbowałem img.paste()
funkcję PIL i zamianę wartości w numpy tablicy obrazów w cv2, oba dające wyniki, które są dalekie od pożądanych.
Odpowiedzi
Zrobiłem dwa obrazy wejściowe w ImageMagick w ten sposób:
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


Następnie uruchomiono następujący kod:
#!/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')
Wynik jest taki:

Wiersze, które zapisują obrazy z nazwami zaczynającymi się, "DEBUG-xxx.png"
służą tylko do łatwego debugowania i można je usunąć. Mogę łatwo wyświetlić je wszystkie, aby zobaczyć, co się dzieje z kodem, i mogę je łatwo usunąć, usuwając "DEBUG*png"
.
Bez dalszych szczegółów postaram się odpowiedzieć na to pytanie najlepiej, jak potrafię i wymienię wszystkie dodatkowe założenia, które poczyniłem (i jak sobie z nimi poradzić, jeśli nie możesz ich zrobić).
Ponieważ nie było dostarczonych obrazów, utworzyłem niebiesko-zielony obraz z czarną kropką jako współrzędną scalającą, używając następującego kodu:
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)
W rezultacie powstają następujące obrazy:

Teraz założę, że obrazy nie zawierają jeszcze warstwy alfa (tak jak je stworzyłem bez). Dlatego załaduję obraz i dodam do nich warstwę 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)
Moje założenie jest takie, że znasz wcześniej współrzędne scalania:
# Assuming x, y coordinates.
point_blue = (300, 100)
point_green = (50, 50)
Następnie możesz utworzyć pusty obraz, który z łatwością pomieści oba obrazy:
new_image = np.zeros((1000, 1000, 4), dtype=np.uint8)
Jest to bardzo rozciągliwe założenie, jeśli wcześniej nie znasz rozmiaru obrazu, a jeśli tego nie wiesz, będziesz musiał obliczyć łączny rozmiar dwóch obrazów.
Następnie możesz umieścić kropkę obrazów w środku nowo utworzonych obrazów (w moim przypadku (500, 500). W tym celu używasz punktów łączenia jako przesunięć. I możesz wykonać mieszanie alfa (w każdym przypadku np.uint8(img_1*alpha + img_2*(1-alpha))
:), aby scalić obrazy z różnym kryciem.
Który jest w kodzie:
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')
Ostatecznym rezultatem będzie większy obraz połączonych obrazów, ponownie możesz obliczyć poprawną ramkę ograniczającą, aby nie wystawały ogromne obszary „niczego”. Ostateczny wynik będzie wyglądał następująco:
