Copy area of ​​a triangular image using PIL

I have two PIL images and two sets of corresponding 2D points that form 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 a triangular region from image1 and convert it to the corresponding triangular region of image2. Is there a way to do this with PIL without having to copy pixel by pixel and compute the transform yourself?

+7
source share
2 answers

I was able to do this with an affinity transformation (thanks to this question ). After the affine transformation, the destination triangle is drawn to the mask and then inserted into the target image. Here is 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 100x100 source image looks like this (the triangle is superimposed in white):

src_before

The 250x250 destination image looks like this (the triangular area is filled with white):

dst_before

Then, after conversion and paste, the target image is as follows:

dst_after

+3
source

EDITED

This strategy is still associated with some pixel manipulation, but may use the APIs somewhat.

  • Convert source image to RGBA.
  • Find the smallest rectangle of your triangle.
  • Manually set all the pixels in the rectangle, but NOT the part of the triangle, until completely transparent. (Perhaps you can do this without much trouble using sets for the x / y, map and partial values.)
  • Find the smallest rectangle enclosed in a triangle in the target image.
  • Copy the rectangle to the target image, reducing the size of the source rectangle to the target size.
0
source

All Articles