Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hackassin
GitHub Repository: hackassin/learnopencv
Path: blob/master/FaceSwap/faceSwap.py
3118 views
1
#! /usr/bin/env python
2
3
import sys
4
import numpy as np
5
import cv2
6
7
# Read points from text file
8
def readPoints(path) :
9
# Create an array of points.
10
points = [];
11
12
# Read points
13
with open(path) as file :
14
for line in file :
15
x, y = line.split()
16
points.append((int(x), int(y)))
17
18
19
return points
20
21
# Apply affine transform calculated using srcTri and dstTri to src and
22
# output an image of size.
23
def applyAffineTransform(src, srcTri, dstTri, size) :
24
25
# Given a pair of triangles, find the affine transform.
26
warpMat = cv2.getAffineTransform( np.float32(srcTri), np.float32(dstTri) )
27
28
# Apply the Affine Transform just found to the src image
29
dst = cv2.warpAffine( src, warpMat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )
30
31
return dst
32
33
34
# Check if a point is inside a rectangle
35
def rectContains(rect, point) :
36
if point[0] < rect[0] :
37
return False
38
elif point[1] < rect[1] :
39
return False
40
elif point[0] > rect[0] + rect[2] :
41
return False
42
elif point[1] > rect[1] + rect[3] :
43
return False
44
return True
45
46
47
#calculate delanauy triangle
48
def calculateDelaunayTriangles(rect, points):
49
#create subdiv
50
subdiv = cv2.Subdiv2D(rect);
51
52
# Insert points into subdiv
53
for p in points:
54
subdiv.insert(p)
55
56
triangleList = subdiv.getTriangleList();
57
58
delaunayTri = []
59
60
pt = []
61
62
for t in triangleList:
63
pt.append((t[0], t[1]))
64
pt.append((t[2], t[3]))
65
pt.append((t[4], t[5]))
66
67
pt1 = (t[0], t[1])
68
pt2 = (t[2], t[3])
69
pt3 = (t[4], t[5])
70
71
if rectContains(rect, pt1) and rectContains(rect, pt2) and rectContains(rect, pt3):
72
ind = []
73
#Get face-points (from 68 face detector) by coordinates
74
for j in xrange(0, 3):
75
for k in xrange(0, len(points)):
76
if(abs(pt[j][0] - points[k][0]) < 1.0 and abs(pt[j][1] - points[k][1]) < 1.0):
77
ind.append(k)
78
# Three points form a triangle. Triangle array corresponds to the file tri.txt in FaceMorph
79
if len(ind) == 3:
80
delaunayTri.append((ind[0], ind[1], ind[2]))
81
82
pt = []
83
84
85
return delaunayTri
86
87
88
# Warps and alpha blends triangular regions from img1 and img2 to img
89
def warpTriangle(img1, img2, t1, t2) :
90
91
# Find bounding rectangle for each triangle
92
r1 = cv2.boundingRect(np.float32([t1]))
93
r2 = cv2.boundingRect(np.float32([t2]))
94
95
# Offset points by left top corner of the respective rectangles
96
t1Rect = []
97
t2Rect = []
98
t2RectInt = []
99
100
for i in xrange(0, 3):
101
t1Rect.append(((t1[i][0] - r1[0]),(t1[i][1] - r1[1])))
102
t2Rect.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))
103
t2RectInt.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))
104
105
106
# Get mask by filling triangle
107
mask = np.zeros((r2[3], r2[2], 3), dtype = np.float32)
108
cv2.fillConvexPoly(mask, np.int32(t2RectInt), (1.0, 1.0, 1.0), 16, 0);
109
110
# Apply warpImage to small rectangular patches
111
img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]
112
#img2Rect = np.zeros((r2[3], r2[2]), dtype = img1Rect.dtype)
113
114
size = (r2[2], r2[3])
115
116
img2Rect = applyAffineTransform(img1Rect, t1Rect, t2Rect, size)
117
118
img2Rect = img2Rect * mask
119
120
# Copy triangular region of the rectangular patch to the output image
121
img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]] = img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]] * ( (1.0, 1.0, 1.0) - mask )
122
123
img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]] = img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]] + img2Rect
124
125
126
if __name__ == '__main__' :
127
128
# Make sure OpenCV is version 3.0 or above
129
(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
130
131
if int(major_ver) < 3 :
132
print >>sys.stderr, 'ERROR: Script needs OpenCV 3.0 or higher'
133
sys.exit(1)
134
135
# Read images
136
filename1 = 'ted_cruz.jpg'
137
filename2 = 'donald_trump.jpg'
138
139
img1 = cv2.imread(filename1);
140
img2 = cv2.imread(filename2);
141
img1Warped = np.copy(img2);
142
143
# Read array of corresponding points
144
points1 = readPoints(filename1 + '.txt')
145
points2 = readPoints(filename2 + '.txt')
146
147
# Find convex hull
148
hull1 = []
149
hull2 = []
150
151
hullIndex = cv2.convexHull(np.array(points2), returnPoints = False)
152
153
for i in xrange(0, len(hullIndex)):
154
hull1.append(points1[int(hullIndex[i])])
155
hull2.append(points2[int(hullIndex[i])])
156
157
158
# Find delanauy traingulation for convex hull points
159
sizeImg2 = img2.shape
160
rect = (0, 0, sizeImg2[1], sizeImg2[0])
161
162
dt = calculateDelaunayTriangles(rect, hull2)
163
164
if len(dt) == 0:
165
quit()
166
167
# Apply affine transformation to Delaunay triangles
168
for i in xrange(0, len(dt)):
169
t1 = []
170
t2 = []
171
172
#get points for img1, img2 corresponding to the triangles
173
for j in xrange(0, 3):
174
t1.append(hull1[dt[i][j]])
175
t2.append(hull2[dt[i][j]])
176
177
warpTriangle(img1, img1Warped, t1, t2)
178
179
180
# Calculate Mask
181
hull8U = []
182
for i in xrange(0, len(hull2)):
183
hull8U.append((hull2[i][0], hull2[i][1]))
184
185
mask = np.zeros(img2.shape, dtype = img2.dtype)
186
187
cv2.fillConvexPoly(mask, np.int32(hull8U), (255, 255, 255))
188
189
r = cv2.boundingRect(np.float32([hull2]))
190
191
center = ((r[0]+int(r[2]/2), r[1]+int(r[3]/2)))
192
193
194
# Clone seamlessly.
195
output = cv2.seamlessClone(np.uint8(img1Warped), img2, mask, center, cv2.NORMAL_CLONE)
196
197
cv2.imshow("Face Swapped", output)
198
cv2.waitKey(0)
199
200
cv2.destroyAllWindows()
201
202
203