使用PIL复制三角形图像区域

8 投票
2 回答
3411 浏览
提问于 2025-04-16 22:54

我有两张PIL格式的图片,还有两组对应的二维点,这些点可以组成一个三角形。

比如说:

image1:
100x100 pixels
points = [(10,10), (20,20), (10,20)]

image2:
250x250 pixels
points = [(35,30), (75,19), (50,90)]

我想把第一张图片中的三角形区域复制过来,并且调整它的形状,让它适合第二张图片中的对应三角形区域。有没有什么方法可以用PIL来做到这一点,而不需要我一个像素一个像素地复制和自己计算变换呢?

2 个回答

0

编辑

这个方法还是需要对像素进行一些处理,但可以稍微利用一下API。

  1. 把源图像转换成RGBA格式。
  2. 找到一个最小的矩形,可以把你的三角形完全包住。
  3. 手动把这个矩形内,但不在三角形里的所有像素设置为完全透明。 (你可以用集合来处理x/y值,结合mappartial,这样做可能不会太麻烦。)
  4. 在目标图像中找到三角形的最小包围矩形。
  5. 通过将源矩形缩放到目标大小,把这个矩形复制到目标图像中。
6

我通过一种叫做仿射变换的方法实现了这个功能(感谢这个问题)。在进行仿射变换后,我把目标三角形画到一个遮罩上,然后再把它粘贴到目标图像上。以下是我得到的结果:

import Image
import ImageDraw
import numpy

def transformblit(src_tri, dst_tri, src_img, dst_img):
    ((x11,x12), (x21,x22), (x31,x32)) = src_tri
    ((y11,y12), (y21,y22), (y31,y32)) = dst_tri

    M = numpy.array([
                     [y11, y12, 1, 0, 0, 0],
                     [y21, y22, 1, 0, 0, 0],
                     [y31, y32, 1, 0, 0, 0],
                     [0, 0, 0, y11, y12, 1],
                     [0, 0, 0, y21, y22, 1],
                     [0, 0, 0, y31, y32, 1]
                ])

    y = numpy.array([x11, x21, x31, x12, x22, x32])

    A = numpy.linalg.solve(M, y)

    src_copy = src_img.copy()
    srcdraw = ImageDraw.Draw(src_copy)
    srcdraw.polygon(src_tri)
    src_copy.show()
    transformed = src_img.transform(dst_img.size, Image.AFFINE, A)

    mask = Image.new('1', dst_img.size)
    maskdraw = ImageDraw.Draw(mask)
    maskdraw.polygon(dst_tri, fill=255)

    dstdraw = ImageDraw.Draw(dst_img)
    dstdraw.polygon(dst_tri, fill=(255,255,255))
    dst_img.show()
    dst_img.paste(transformed, mask=mask)
    dst_img.show()


im100 = Image.open('test100.jpg')
im250 = Image.open('test250.jpg')

tri1 = [(10,10), (20,20), (10,20)]
tri2 = [(35,30), (75,19), (50,90)]

transformblit(tri1, tri2, im100, im250)

源图像是100x100的,长这样(上面有一个白色的三角形):

src_before

目标图像是250x250的,长这样(里面填充了白色的三角形区域):

dst_before

然后在经过变换和粘贴后,目标图像变成了这样:

dst_after

撰写回答