PythonでPILまたはOpenCVを使用して、不透明度を変更した2つの指定された座標で画像を別の画像に貼り付けます

Dec 10 2020

結果の画像が両方の画像の合計になるように位置合わせする必要がある、指定されたポイントを持つ2つの画像があります。画像2は、不透明度40%で画像1に貼り付けられます。私はこの質問を考慮に入れましたが、画像の座標はユーザーによって提供され、画像はさまざまなサイズを持つ可能性があるため、私たちのケースは完全には一致しません。

画像1:

画像2:

最終結果(望ましい出力):

このために、私はimg.paste()PILの機能を試し、cv2の画像のnumpy配列の値を置き換えましたが、どちらも望ましい結果にはほど遠いものでした。

回答

1 MarkSetchell Dec 11 2020 at 19:07

ImageMagickで次のように2つの入力画像を作成しました。

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

次に、次のコードを実行しました。

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

結果は次のとおりです。

名前が始まる画像を保存する行は、"DEBUG-xxx.png"デバッグを容易にするためのものであり、削除できます。それらすべてを簡単に表示してコードで何が起こっているかを確認でき、を削除することですべてを簡単に削除できます"DEBUG*png"

Thymen Dec 11 2020 at 05:04

これ以上の詳細なしで、私はできる限り質問に答えようとし、私が行ったすべての追加の仮定(およびそれらを行うことができない場合のそれらの処理方法)に名前を付けます。

提供された画像がなかったため、次のコードを使用して、マージ座標として黒のドットを使用して青と緑の画像を作成しました。

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)

これにより、次の画像が表示されます。

ここで、画像にまだアルファレイヤーが含まれていないと仮定します(画像を作成しなかったため)。したがって、画像をロードして、アルファレイヤーを追加します。

import numpy as np

from PIL import Image

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

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

私の次の仮定は、マージ座標を事前に知っていることです。

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

次に、両方の画像を簡単に保持できる空の画像を作成できます。

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

事前に画像サイズがわからない場合、これは非常に大きな仮定です。これがわからない場合は、2つの画像の合計サイズを計算する必要があります。

次に、新しく作成した画像の中央に画像ドットを配置できます(私の場合は(500、500)。このために、マージポイントをオフセットとして使用します。また、アルファブレンディング(いずれの場合も:)を実行np.uint8(img_1*alpha + img_2*(1-alpha))して、異なる不透明度を使用した画像。

これはコードにあります:

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

最終的な結果は、結合された画像のより大きな画像になります。ここでも、正しい境界ボックスを計算できるため、「何もない」というこれらの巨大な領域が突き出ることはありません。最終結果は次のようになります。