Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hackassin
GitHub Repository: hackassin/learnopencv
Path: blob/master/FaceSwap/faceSwap.cpp
3118 views
1
#include <opencv2/opencv.hpp>
2
#include <iostream>
3
#include <fstream>
4
#include <string>
5
using namespace cv;
6
using namespace std;
7
8
//Read points from text file
9
vector<Point2f> readPoints(string pointsFileName){
10
vector<Point2f> points;
11
ifstream ifs (pointsFileName.c_str());
12
float x, y;
13
int count = 0;
14
while(ifs >> x >> y)
15
{
16
points.push_back(Point2f(x,y));
17
18
}
19
20
return points;
21
}
22
23
// Apply affine transform calculated using srcTri and dstTri to src
24
void applyAffineTransform(Mat &warpImage, Mat &src, vector<Point2f> &srcTri, vector<Point2f> &dstTri)
25
{
26
// Given a pair of triangles, find the affine transform.
27
Mat warpMat = getAffineTransform( srcTri, dstTri );
28
29
// Apply the Affine Transform just found to the src image
30
warpAffine( src, warpImage, warpMat, warpImage.size(), INTER_LINEAR, BORDER_REFLECT_101);
31
}
32
33
34
// Calculate Delaunay triangles for set of points
35
// Returns the vector of indices of 3 points for each triangle
36
static void calculateDelaunayTriangles(Rect rect, vector<Point2f> &points, vector< vector<int> > &delaunayTri){
37
38
// Create an instance of Subdiv2D
39
Subdiv2D subdiv(rect);
40
41
// Insert points into subdiv
42
for( vector<Point2f>::iterator it = points.begin(); it != points.end(); it++)
43
subdiv.insert(*it);
44
45
vector<Vec6f> triangleList;
46
subdiv.getTriangleList(triangleList);
47
vector<Point2f> pt(3);
48
vector<int> ind(3);
49
50
for( size_t i = 0; i < triangleList.size(); i++ )
51
{
52
Vec6f t = triangleList[i];
53
pt[0] = Point2f(t[0], t[1]);
54
pt[1] = Point2f(t[2], t[3]);
55
pt[2] = Point2f(t[4], t[5 ]);
56
57
if ( rect.contains(pt[0]) && rect.contains(pt[1]) && rect.contains(pt[2])){
58
for(int j = 0; j < 3; j++)
59
for(size_t k = 0; k < points.size(); k++)
60
if(abs(pt[j].x - points[k].x) < 1.0 && abs(pt[j].y - points[k].y) < 1)
61
ind[j] = k;
62
63
delaunayTri.push_back(ind);
64
}
65
}
66
67
}
68
69
70
// Warps and alpha blends triangular regions from img1 and img2 to img
71
void warpTriangle(Mat &img1, Mat &img2, vector<Point2f> &t1, vector<Point2f> &t2)
72
{
73
74
Rect r1 = boundingRect(t1);
75
Rect r2 = boundingRect(t2);
76
77
// Offset points by left top corner of the respective rectangles
78
vector<Point2f> t1Rect, t2Rect;
79
vector<Point> t2RectInt;
80
for(int i = 0; i < 3; i++)
81
{
82
83
t1Rect.push_back( Point2f( t1[i].x - r1.x, t1[i].y - r1.y) );
84
t2Rect.push_back( Point2f( t2[i].x - r2.x, t2[i].y - r2.y) );
85
t2RectInt.push_back( Point(t2[i].x - r2.x, t2[i].y - r2.y) ); // for fillConvexPoly
86
87
}
88
89
// Get mask by filling triangle
90
Mat mask = Mat::zeros(r2.height, r2.width, CV_32FC3);
91
fillConvexPoly(mask, t2RectInt, Scalar(1.0, 1.0, 1.0), 16, 0);
92
93
// Apply warpImage to small rectangular patches
94
Mat img1Rect;
95
img1(r1).copyTo(img1Rect);
96
97
Mat img2Rect = Mat::zeros(r2.height, r2.width, img1Rect.type());
98
99
applyAffineTransform(img2Rect, img1Rect, t1Rect, t2Rect);
100
101
multiply(img2Rect,mask, img2Rect);
102
multiply(img2(r2), Scalar(1.0,1.0,1.0) - mask, img2(r2));
103
img2(r2) = img2(r2) + img2Rect;
104
105
106
}
107
108
109
int main( int argc, char** argv)
110
{
111
//Read input images
112
string filename1 = "ted_cruz.jpg";
113
string filename2 = "donald_trump.jpg";
114
115
Mat img1 = imread(filename1);
116
Mat img2 = imread(filename2);
117
Mat img1Warped = img2.clone();
118
119
//Read points
120
vector<Point2f> points1, points2;
121
points1 = readPoints(filename1 + ".txt");
122
points2 = readPoints(filename2 + ".txt");
123
124
//convert Mat to float data type
125
img1.convertTo(img1, CV_32F);
126
img1Warped.convertTo(img1Warped, CV_32F);
127
128
129
// Find convex hull
130
vector<Point2f> hull1;
131
vector<Point2f> hull2;
132
vector<int> hullIndex;
133
134
convexHull(points2, hullIndex, false, false);
135
136
for(int i = 0; i < hullIndex.size(); i++)
137
{
138
hull1.push_back(points1[hullIndex[i]]);
139
hull2.push_back(points2[hullIndex[i]]);
140
}
141
142
143
// Find delaunay triangulation for points on the convex hull
144
vector< vector<int> > dt;
145
Rect rect(0, 0, img1Warped.cols, img1Warped.rows);
146
calculateDelaunayTriangles(rect, hull2, dt);
147
148
// Apply affine transformation to Delaunay triangles
149
for(size_t i = 0; i < dt.size(); i++)
150
{
151
vector<Point2f> t1, t2;
152
// Get points for img1, img2 corresponding to the triangles
153
for(size_t j = 0; j < 3; j++)
154
{
155
t1.push_back(hull1[dt[i][j]]);
156
t2.push_back(hull2[dt[i][j]]);
157
}
158
159
warpTriangle(img1, img1Warped, t1, t2);
160
161
}
162
163
// Calculate mask
164
vector<Point> hull8U;
165
for(int i = 0; i < hull2.size(); i++)
166
{
167
Point pt(hull2[i].x, hull2[i].y);
168
hull8U.push_back(pt);
169
}
170
171
Mat mask = Mat::zeros(img2.rows, img2.cols, img2.depth());
172
fillConvexPoly(mask,&hull8U[0], hull8U.size(), Scalar(255,255,255));
173
174
// Clone seamlessly.
175
Rect r = boundingRect(hull2);
176
Point center = (r.tl() + r.br()) / 2;
177
178
Mat output;
179
img1Warped.convertTo(img1Warped, CV_8UC3);
180
seamlessClone(img1Warped,img2, mask, center, output, NORMAL_CLONE);
181
182
imshow("Face Swapped", output);
183
waitKey(0);
184
destroyAllWindows();
185
186
187
return 1;
188
}
189
190