在 Python 中使用 PIL 或 OpenCV 将一个图像粘贴到两个给定坐标处的另一个图像,改变不透明度
Paste an image to another image at two given co-ordinates with altered opacity using PIL or OpenCV in Python
我有两张带有给定点的图像,每张图像一个点,需要对齐,以便结果图像是两张图像的总和,而图像 2 以 40% 的不透明度粘贴在图像 1 上。我已经考虑到这个 但我们的案例并不完全匹配,因为图像坐标是由用户提供的,图像的尺寸范围很广。
图 1:
图 2:
最终结果(期望输出):
为此,我尝试了 PIL 的 img.paste()
函数并替换了 cv2 中 numpy 图像数组中的值,两者都给出了远非预期的结果。
在没有更多细节的情况下,我会尽力回答这个问题,并会列出我所做的所有额外假设(以及如果你做不到的话如何处理)。
由于没有提供图片,我创建了一个蓝色和绿色的图片,以黑点作为合并坐标,使用以下代码:
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)
这会产生以下图像:
现在我假设图像还不包含 alpha 层(因为我创建时没有)。因此,我将加载图像并为其添加一个 alpha 层:
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)。为此你使用合并点作为偏移量。你可以执行 alpha 混合(在任何情况下: 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')
最终结果将是一个更大的图像,合并后的图像,你可以再次计算出正确的边界框,所以你没有 'nothing' 这些巨大的区域突出。最终结果将如下所示:
我用 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"
.
轻松删除它们
我有两张带有给定点的图像,每张图像一个点,需要对齐,以便结果图像是两张图像的总和,而图像 2 以 40% 的不透明度粘贴在图像 1 上。我已经考虑到这个
图 1:
图 2:
最终结果(期望输出):
为此,我尝试了 PIL 的 img.paste()
函数并替换了 cv2 中 numpy 图像数组中的值,两者都给出了远非预期的结果。
在没有更多细节的情况下,我会尽力回答这个问题,并会列出我所做的所有额外假设(以及如果你做不到的话如何处理)。
由于没有提供图片,我创建了一个蓝色和绿色的图片,以黑点作为合并坐标,使用以下代码:
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)
这会产生以下图像:
现在我假设图像还不包含 alpha 层(因为我创建时没有)。因此,我将加载图像并为其添加一个 alpha 层:
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)。为此你使用合并点作为偏移量。你可以执行 alpha 混合(在任何情况下: 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')
最终结果将是一个更大的图像,合并后的图像,你可以再次计算出正确的边界框,所以你没有 'nothing' 这些巨大的区域突出。最终结果将如下所示:
我用 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"
.