Python에서 PIL 또는 OpenCV를 사용하여 불투명도가 변경된 두 개의 주어진 좌표에서 이미지를 다른 이미지에 붙여 넣습니다.

Dec 10 2020

결과 이미지가 두 이미지의 합계가되도록 정렬해야하는 지정된 포인트가있는 두 개의 이미지가 있습니다. 이미지 2는 불투명도가 40 % 인 이미지 1에 붙여 넣습니다. 이 질문 을 고려했지만 이미지 좌표는 사용자가 제공하고 이미지의 크기가 다양 할 수 있으므로 사례가 정확히 일치하지 않습니다.

이미지 1 :

Image2 :

최종 결과 (원하는 출력) :

이를 위해 나는 img.paste()PIL의 기능을 시도 하고 cv2의 numpy 배열의 값을 대체하여 원하는 결과와는 거리가 먼 결과를 제공했습니다.

답변

1 MarkSetchell Dec 11 2020 at 19:07

ImageMagick으로 다음 과 같이 두 개의 입력 이미지를 만들었습니다 .

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)

이것은 이미지 크기를 미리 알지 못하는 경우 훨씬 확장 된 가정이며, 이것을 모르는 경우 두 이미지의 결합 크기를 계산해야합니다.

그런 다음 새로 생성 된 이미지의 중앙에 이미지 점을 배치 할 수 있습니다 (제 경우에는 (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')

최종 결과는 결합 된 이미지의 더 큰 이미지가 될 것입니다. 다시 올바른 경계 상자를 계산할 수 있으므로 이러한 '아무것도'가 튀어 나오지 않는 거대한 영역이 없습니다. 최종 결과는 다음과 같습니다.