Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying triangular image region with PIL

I have two PIL images and two sets of corresponding 2D points that make a triangle.

For example:

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

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

I want to copy the triangular region from image1 and transform it to fit into the corresponding triangular region of image2. Is there any way to do this with PIL without having to copy pixel by pixel and calculate the transformation myself?

like image 349
jterrace Avatar asked Aug 04 '11 18:08

jterrace


1 Answers

I was able to do this with an affine transformation (thanks to this question). After the affine transformation, the destination triangle is drawn to a mask and then pasted on to the destination image. Here's what I came up with:

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)

The source 100x100 image looks like this (triangle overlaid in white):

src_before

The destination 250x250 image looks like this (triangular region filled in with white):

dst_before

And then after the transformation and pasting, the destination image looks like this:

dst_after

like image 107
jterrace Avatar answered Nov 11 '22 01:11

jterrace