1. Morphing two images
Python code using OpenCV:
import numpy as np
import cv2
import sys
# Read points from text file
def readPoints(path) :
# Create an array of points.
points = [];
# Read points
with open(path) as file :
for line in file :
x, y = line.split()
points.append((int(x), int(y)))
return points
# Apply affine transform calculated using srcTri and dstTri to src and
# output an image of size.
def applyAffineTransform(src, srcTri, dstTri, size) :
# Given a pair of triangles, find the affine transform.
warpMat = cv2.getAffineTransform( np.float32(srcTri), np.float32(dstTri) )
# Apply the Affine Transform just found to the src image
dst = cv2.warpAffine( src, warpMat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )
return dst
# Warps and alpha blends triangular regions from img1 and img2 to img
def morphTriangle(img1, img2, img, t1, t2, t, alpha) :
# Find bounding rectangle for each triangle
r1 = cv2.boundingRect(np.float32([t1]))
r2 = cv2.boundingRect(np.float32([t2]))
r = cv2.boundingRect(np.float32([t]))
# Offset points by left top corner of the respective rectangles
t1Rect = []
t2Rect = []
tRect = []
for i in range(0, 3):
tRect.append(((t[i][0] - r[0]),(t[i][1] - r[1])))
t1Rect.append(((t1[i][0] - r1[0]),(t1[i][1] - r1[1])))
t2Rect.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))
# Get mask by filling triangle
mask = np.zeros((r[3], r[2], 3), dtype = np.float32)
cv2.fillConvexPoly(mask, np.int32(tRect), (1.0, 1.0, 1.0), 16, 0);
# Apply warpImage to small rectangular patches
img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]
img2Rect = img2[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]]
size = (r[2], r[3])
warpImage1 = applyAffineTransform(img1Rect, t1Rect, tRect, size)
warpImage2 = applyAffineTransform(img2Rect, t2Rect, tRect, size)
# Alpha blend rectangular patches
imgRect = (1.0 - alpha) * warpImage1 + alpha * warpImage2
# Copy triangular region of the rectangular patch to the output image
img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] = img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] * ( 1 - mask ) + imgRect * mask
if __name__ == '__main__' :
filename1 = 'hillary_clinton.jpg'
filename2 = 'donald_trump.jpg'
alpha = 0.5
# Read images
img1 = cv2.imread(filename1);
img2 = cv2.imread(filename2);
# Convert Mat to float data type
img1 = np.float32(img1)
img2 = np.float32(img2)
# Read array of corresponding points
points1 = readPoints(filename1 + '.txt')
points2 = readPoints(filename2 + '.txt')
points = [];
# Compute weighted average point coordinates
for i in range(0, len(points1)):
x = ( 1 - alpha ) * points1[i][0] + alpha * points2[i][0]
y = ( 1 - alpha ) * points1[i][1] + alpha * points2[i][1]
points.append((x,y))
# Allocate space for final output
imgMorph = np.zeros(img1.shape, dtype = img1.dtype)
# Read triangles from tri.txt
with open("tri.txt") as file :
for line in file :
x,y,z = line.split()
x = int(x)
y = int(y)
z = int(z)
t1 = [points1[x], points1[y], points1[z]]
t2 = [points2[x], points2[y], points2[z]]
t = [ points[x], points[y], points[z] ]
# Morph one triangle at a time.
morphTriangle(img1, img2, imgMorph, t1, t2, t, alpha)
# Display Result
cv2.imshow("Morphed Face", np.uint8(imgMorph))
cv2.waitKey(0)
![](https://static.wixstatic.com/media/a70a1f_4ec097ae147344f4b24e66c5dc5ffdbe~mv2.jpg/v1/fill/w_600,h_800,al_c,q_85,enc_avif,quality_auto/a70a1f_4ec097ae147344f4b24e66c5dc5ffdbe~mv2.jpg)
![](https://static.wixstatic.com/media/a70a1f_05f72aa695224509b0ec0431b99ff019~mv2.jpg/v1/fill/w_600,h_800,al_c,q_85,enc_avif,quality_auto/a70a1f_05f72aa695224509b0ec0431b99ff019~mv2.jpg)
![](https://static.wixstatic.com/media/a70a1f_f193abfeeef84d58986dda9c0f12888d~mv2.png/v1/fill/w_603,h_768,al_c,q_90,enc_avif,quality_auto/a70a1f_f193abfeeef84d58986dda9c0f12888d~mv2.png)
2. Warping
Python code:
import numpy as np
from PIL import Image
def quad_as_rect(quad):
if quad[0] != quad[2]: return False
if quad[1] != quad[7]: return False
if quad[4] != quad[6]: return False
if quad[3] != quad[5]: return False
return True
def quad_to_rect(quad):
assert(len(quad) == 8)
assert(quad_as_rect(quad))
return (quad[0], quad[1], quad[4], quad[3])
def rect_to_quad(rect):
assert(len(rect) == 4)
return (rect[0], rect[1], rect[0], rect[3], rect[2], rect[3], rect[2], rect[1])
def shape_to_rect(shape):
assert(len(shape) == 2)
return (0, 0, shape[0], shape[1])
def griddify(rect, w_div, h_div):
w = rect[2] - rect[0]
h = rect[3] - rect[1]
x_step = w / float(w_div)
y_step = h / float(h_div)
y = rect[1]
grid_vertex_matrix = []
for _ in range(h_div + 1):
grid_vertex_matrix.append([])
x = rect[0]
for _ in range(w_div + 1):
grid_vertex_matrix[-1].append([int(x), int(y)])
x += x_step
y += y_step
grid = np.array(grid_vertex_matrix)
return grid
def distort_grid(org_grid, max_shift):
new_grid = np.copy(org_grid)
x_min = np.min(new_grid[:, :, 0])
y_min = np.min(new_grid[:, :, 1])
x_max = np.max(new_grid[:, :, 0])
y_max = np.max(new_grid[:, :, 1])
new_grid += np.random.randint(- max_shift, max_shift + 1, new_grid.shape)
new_grid[:, :, 0] = np.maximum(x_min, new_grid[:, :, 0])
new_grid[:, :, 1] = np.maximum(y_min, new_grid[:, :, 1])
new_grid[:, :, 0] = np.minimum(x_max, new_grid[:, :, 0])
new_grid[:, :, 1] = np.minimum(y_max, new_grid[:, :, 1])
return new_grid
def grid_to_mesh(src_grid, dst_grid):
assert(src_grid.shape == dst_grid.shape)
mesh = []
for i in range(src_grid.shape[0] - 1):
for j in range(src_grid.shape[1] - 1):
src_quad = [src_grid[i , j , 0], src_grid[i , j , 1],
src_grid[i + 1, j , 0], src_grid[i + 1, j , 1],
src_grid[i + 1, j + 1, 0], src_grid[i + 1, j + 1, 1],
src_grid[i , j + 1, 0], src_grid[i , j + 1, 1]]
dst_quad = [dst_grid[i , j , 0], dst_grid[i , j , 1],
dst_grid[i + 1, j , 0], dst_grid[i + 1, j , 1],
dst_grid[i + 1, j + 1, 0], dst_grid[i + 1, j + 1, 1],
dst_grid[i , j + 1, 0], dst_grid[i , j + 1, 1]]
dst_rect = quad_to_rect(dst_quad)
mesh.append([dst_rect, src_quad])
return mesh
im = Image.open('hillary_clinton.jpg')
dst_grid = griddify(shape_to_rect(im.size), 4, 4)
src_grid = distort_grid(dst_grid, 50)
mesh = grid_to_mesh(src_grid, dst_grid)
im = im.transform(im.size, Image.MESH, mesh)
im.show()
![](https://static.wixstatic.com/media/a70a1f_9276c7dfd04940098444d3a1f7432d8c~mv2.png/v1/fill/w_502,h_606,al_c,q_85,enc_avif,quality_auto/a70a1f_9276c7dfd04940098444d3a1f7432d8c~mv2.png)
Comments