Fügen Sie ein Bild mit PIL oder OpenCV in Python an zwei vorgegebenen Koordinaten mit geänderter Deckkraft in ein anderes Bild ein

Dec 10 2020

Ich habe zwei Bilder mit vorgegebenen Punkten, einen Punkt pro Bild, die ausgerichtet werden müssen, damit das Ergebnisbild eine Summe beider Bilder ist, während Bild 2 mit 40% Deckkraft auf Bild 1 eingefügt wird. Ich habe diese Frage berücksichtigt, aber unser Fall stimmt nicht genau überein, da die Bildkoordinate vom Benutzer bereitgestellt wird und Bilder einen großen Größenbereich haben können.

Bild 1:

Bild 2:

Endergebnis (gewünschte Ausgabe):

Zu diesem img.paste()Zweck habe ich versucht, die Funktion von PIL zu verwenden und Werte in einer Reihe von Bildern in cv2 zu ersetzen. Beide liefern Ergebnisse, die alles andere als erwünscht sind.

Antworten

1 MarkSetchell Dec 11 2020 at 19:07

Ich habe mit ImageMagick zwei Eingabebilder wie folgt erstellt :

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

Dann lief der folgende Code:

#!/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')

Das Ergebnis ist folgendes:

Die Zeilen, in denen Bilder mit beginnenden Namen gespeichert "DEBUG-xxx.png"werden, dienen nur zum einfachen Debuggen und können entfernt werden. Ich kann sie alle leicht anzeigen, um zu sehen, was mit dem Code los ist, und ich kann sie alle einfach durch Entfernen löschen "DEBUG*png".

Thymen Dec 11 2020 at 05:04

Ohne weitere Details werde ich versuchen, die Frage so gut wie möglich zu beantworten und alle zusätzlichen Annahmen zu benennen, die ich gemacht habe (und wie ich damit umgehen soll, wenn Sie sie nicht machen können).

Da keine Bilder bereitgestellt wurden, habe ich mit dem folgenden Code ein blaues und grünes Bild mit einem schwarzen Punkt als Zusammenführungskoordinate erstellt:

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)

Dies führt zu folgenden Bildern:

Jetzt gehe ich davon aus, dass die Bilder noch keine Alpha-Ebene enthalten (wie ich sie ohne erstellt habe). Deshalb werde ich das Bild laden und ihnen eine Alpha-Ebene hinzufügen:

import numpy as np

from PIL import Image

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

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

Ich gehe davon aus, dass Sie die Zusammenführungskoordinaten im Voraus kennen:

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

Dann können Sie ein leeres Bild erstellen, das beide Bilder problemlos aufnehmen kann:

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

Dies ist eine weitreichende Annahme, wenn Sie die Bildgröße vorher nicht kennen und falls Sie dies nicht wissen, müssen Sie die Kombinationsgröße der beiden Bilder berechnen.

Dann können Sie den Bildpunkt in der Mitte der neu erstellten Bilder platzieren (in meinem Fall (500, 500). Dazu verwenden Sie die Zusammenführungspunkte als Offsets. Und Sie können eine Alpha-Überblendung (in jedem Fall :) durchführen np.uint8(img_1*alpha + img_2*(1-alpha)), um die zusammenzuführen Bilder mit unterschiedlicher Deckkraft.

Welches ist im 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')

Das Endergebnis wird ein größeres Bild der kombinierten Bilder sein. Auch hier können Sie den richtigen Begrenzungsrahmen berechnen, sodass diese riesigen Bereiche mit „nichts“ nicht hervorstehen. Das Endergebnis sieht folgendermaßen aus: