faceAverage.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /*
  2. * Copyright (c) 2016 Satya Mallick <spmallick@learnopencv.com>
  3. * All rights reserved. No warranty, explicit or implicit, provided.
  4. */
  5. #include <opencv2/opencv.hpp>
  6. #include <iostream>
  7. #include <fstream>
  8. #include <dirent.h>
  9. #include <stdlib.h>
  10. #include <algorithm>
  11. #include <vector>
  12. using namespace cv;
  13. using namespace std;
  14. // Compute similarity transform given two pairs of corresponding points.
  15. // OpenCV requires 3 pairs of corresponding points.
  16. // We are faking the third one.
  17. void similarityTransform(std::vector<cv::Point2f>& inPoints, std::vector<cv::Point2f>& outPoints, cv::Mat &tform)
  18. {
  19. double s60 = sin(60 * M_PI / 180.0);
  20. double c60 = cos(60 * M_PI / 180.0);
  21. vector <Point2f> inPts = inPoints;
  22. vector <Point2f> outPts = outPoints;
  23. inPts.push_back(cv::Point2f(0,0));
  24. outPts.push_back(cv::Point2f(0,0));
  25. inPts[2].x = c60 * (inPts[0].x - inPts[1].x) - s60 * (inPts[0].y - inPts[1].y) + inPts[1].x;
  26. inPts[2].y = s60 * (inPts[0].x - inPts[1].x) + c60 * (inPts[0].y - inPts[1].y) + inPts[1].y;
  27. outPts[2].x = c60 * (outPts[0].x - outPts[1].x) - s60 * (outPts[0].y - outPts[1].y) + outPts[1].x;
  28. outPts[2].y = s60 * (outPts[0].x - outPts[1].x) + c60 * (outPts[0].y - outPts[1].y) + outPts[1].y;
  29. tform = cv::estimateRigidTransform(inPts, outPts, false);
  30. }
  31. // Read points from list of text file
  32. void readPoints(vector<string> pointsFileNames, vector<vector<Point2f> > &pointsVec){
  33. for(size_t i = 0; i < pointsFileNames.size(); i++)
  34. {
  35. vector<Point2f> points;
  36. ifstream ifs(pointsFileNames[i]);
  37. float x, y;
  38. while(ifs >> x >> y)
  39. points.push_back(Point2f((float)x, (float)y));
  40. pointsVec.push_back(points);
  41. }
  42. }
  43. // Read names from the directory
  44. void readFileNames(string dirName, vector<string> &imageFnames, vector<string> &ptsFnames)
  45. {
  46. DIR *dir;
  47. struct dirent *ent;
  48. int count = 0;
  49. //image extensions
  50. string imgExt = "jpg";
  51. string txtExt = "txt";
  52. vector<string> files;
  53. if ((dir = opendir (dirName.c_str())) != NULL)
  54. {
  55. /* print all the files and directories within directory */
  56. while ((ent = readdir (dir)) != NULL)
  57. {
  58. if(strcmp(ent->d_name,".") == 0 || strcmp(ent->d_name,"..") == 0 )
  59. {
  60. //count++;
  61. continue;
  62. }
  63. string temp_name = ent->d_name;
  64. files.push_back(temp_name);
  65. }
  66. std::sort(files.begin(),files.end());
  67. for(int it=0;it<files.size();it++)
  68. {
  69. string path = dirName;
  70. string fname=files[it];
  71. if (fname.find(imgExt, (fname.length() - imgExt.length())) != std::string::npos)
  72. {
  73. path.append(fname);
  74. imageFnames.push_back(path);
  75. }
  76. else if (fname.find(txtExt, (fname.length() - txtExt.length())) != std::string::npos)
  77. {
  78. path.append(fname);
  79. ptsFnames.push_back(path);
  80. }
  81. }
  82. closedir (dir);
  83. }
  84. }
  85. // Calculate Delaunay triangles for set of points
  86. // Returns the vector of indices of 3 points for each triangle
  87. static void calculateDelaunayTriangles(Rect rect, vector<Point2f> &points, vector< vector<int> > &delaunayTri){
  88. // Create an instance of Subdiv2D
  89. Subdiv2D subdiv(rect);
  90. // Insert points into subdiv
  91. for( vector<Point2f>::iterator it = points.begin(); it != points.end(); it++)
  92. subdiv.insert(*it);
  93. vector<Vec6f> triangleList;
  94. subdiv.getTriangleList(triangleList);
  95. vector<Point2f> pt(3);
  96. vector<int> ind(3);
  97. for( size_t i = 0; i < triangleList.size(); i++ )
  98. {
  99. Vec6f t = triangleList[i];
  100. pt[0] = Point2f(t[0], t[1]);
  101. pt[1] = Point2f(t[2], t[3]);
  102. pt[2] = Point2f(t[4], t[5 ]);
  103. if ( rect.contains(pt[0]) && rect.contains(pt[1]) && rect.contains(pt[2])){
  104. for(int j = 0; j < 3; j++)
  105. for(size_t k = 0; k < points.size(); k++)
  106. if(abs(pt[j].x - points[k].x) < 1.0 && abs(pt[j].y - points[k].y) < 1)
  107. ind[j] = k;
  108. delaunayTri.push_back(ind);
  109. }
  110. }
  111. }
  112. // Apply affine transform calculated using srcTri and dstTri to src
  113. void applyAffineTransform(Mat &warpImage, Mat &src, vector<Point2f> &srcTri, vector<Point2f> &dstTri)
  114. {
  115. // Given a pair of triangles, find the affine transform.
  116. Mat warpMat = getAffineTransform( srcTri, dstTri );
  117. // Apply the Affine Transform just found to the src image
  118. warpAffine( src, warpImage, warpMat, warpImage.size(), INTER_LINEAR, BORDER_REFLECT_101);
  119. }
  120. // Warps and alpha blends triangular regions from img1 and img2 to img
  121. void warpTriangle(Mat &img1, Mat &img2, vector<Point2f> t1, vector<Point2f> t2)
  122. {
  123. // Find bounding rectangle for each triangle
  124. Rect r1 = boundingRect(t1);
  125. Rect r2 = boundingRect(t2);
  126. // Offset points by left top corner of the respective rectangles
  127. vector<Point2f> t1Rect, t2Rect;
  128. vector<Point> t2RectInt;
  129. for(int i = 0; i < 3; i++)
  130. {
  131. //tRect.push_back( Point2f( t[i].x - r.x, t[i].y - r.y) );
  132. t2RectInt.push_back( Point((int)(t2[i].x - r2.x), (int)(t2[i].y - r2.y)) ); // for fillConvexPoly
  133. t1Rect.push_back( Point2f( t1[i].x - r1.x, t1[i].y - r1.y) );
  134. t2Rect.push_back( Point2f( t2[i].x - r2.x, t2[i].y - r2.y) );
  135. }
  136. // Get mask by filling triangle
  137. Mat mask = Mat::zeros(r2.height, r2.width, CV_32FC3);
  138. fillConvexPoly(mask, t2RectInt, Scalar(1.0, 1.0, 1.0), 16, 0);
  139. // Apply warpImage to small rectangular patches
  140. Mat img1Rect, img2Rect;
  141. img1(r1).copyTo(img1Rect);
  142. Mat warpImage = Mat::zeros(r2.height, r2.width, img1Rect.type());
  143. applyAffineTransform(warpImage, img1Rect, t1Rect, t2Rect);
  144. // Copy triangular region of the rectangular patch to the output image
  145. multiply(warpImage,mask, warpImage);
  146. multiply(img2(r2), Scalar(1.0,1.0,1.0) - mask, img2(r2));
  147. img2(r2) = img2(r2) + warpImage;
  148. }
  149. // Constrains points to be inside boundary
  150. void constrainPoint(Point2f &p, Size sz)
  151. {
  152. p.x = min(max( (double)p.x, 0.0), (double)(sz.width - 1));
  153. p.y = min(max( (double)p.y, 0.0), (double)(sz.height - 1));
  154. }
  155. int main( int argc, char** argv)
  156. {
  157. // Directory containing images.
  158. string dirName = "presidents";
  159. // Add slash to directory name if missing
  160. if (!dirName.empty() && dirName.back() != '/')
  161. dirName += '/';
  162. // Dimensions of output image
  163. int w = 600;
  164. int h = 600;
  165. // Read images in the directory
  166. vector<string> imageNames, ptsNames;
  167. readFileNames(dirName, imageNames, ptsNames);
  168. //cout << imageNames.size() << ptsNames.size();
  169. // Exit program if no images or pts are found or if the number of image files does not match with the number of point files
  170. if(imageNames.empty() || ptsNames.empty() || imageNames.size() != ptsNames.size()){
  171. exit(EXIT_FAILURE);
  172. }
  173. // Read points
  174. vector<vector<Point2f> > allPoints;
  175. readPoints(ptsNames, allPoints);
  176. int n = allPoints[0].size();
  177. //cout << n<< endl;
  178. // Read images
  179. vector<Mat> images;
  180. for(size_t i = 0; i < imageNames.size(); i++)
  181. {
  182. Mat img = imread(imageNames[i]);
  183. img.convertTo(img, CV_32FC3, 1/255.0);
  184. if(!img.data)
  185. {
  186. cout << "image " << imageNames[i] << " not read properly" << endl;
  187. }
  188. else
  189. {
  190. images.push_back(img);
  191. }
  192. }
  193. if(images.empty())
  194. {
  195. cout << "No images found " << endl;
  196. exit(EXIT_FAILURE);
  197. }
  198. int numImages = images.size();
  199. // Eye corners
  200. vector<Point2f> eyecornerDst, eyecornerSrc;
  201. eyecornerDst.push_back(Point2f( 0.3*w, h/3));
  202. eyecornerDst.push_back(Point2f( 0.7*w, h/3));
  203. eyecornerSrc.push_back(Point2f(0,0));
  204. eyecornerSrc.push_back(Point2f(0,0));
  205. // Space for normalized images and points.
  206. vector <Mat> imagesNorm;
  207. vector < vector <Point2f> > pointsNorm;
  208. // Space for average landmark points
  209. vector <Point2f> pointsAvg(allPoints[0].size());
  210. // 8 Boundary points for Delaunay Triangulation
  211. vector <Point2f> boundaryPts;
  212. boundaryPts.push_back(Point2f(0,0));
  213. boundaryPts.push_back(Point2f(w/2, 0));
  214. boundaryPts.push_back(Point2f(w-1,0));
  215. boundaryPts.push_back(Point2f(w-1, h/2));
  216. boundaryPts.push_back(Point2f(w-1, h-1));
  217. boundaryPts.push_back(Point2f(w/2, h-1));
  218. boundaryPts.push_back(Point2f(0, h-1));
  219. boundaryPts.push_back(Point2f(0, h/2));
  220. // Warp images and trasnform landmarks to output coordinate system,
  221. // and find average of transformed landmarks.
  222. for(size_t i = 0; i < images.size(); i++)
  223. {
  224. vector <Point2f> points = allPoints[i];
  225. // The corners of the eyes are the landmarks number 36 and 45
  226. eyecornerSrc[0] = allPoints[i][36];
  227. eyecornerSrc[1] = allPoints[i][45];
  228. // Calculate similarity transform
  229. Mat tform;
  230. similarityTransform(eyecornerSrc, eyecornerDst, tform);
  231. // Apply similarity transform to input image and landmarks
  232. Mat img = Mat::zeros(h, w, CV_32FC3);
  233. warpAffine(images[i], img, tform, img.size());
  234. transform( points, points, tform);
  235. // Calculate average landmark locations
  236. for ( size_t j = 0; j < points.size(); j++)
  237. {
  238. pointsAvg[j] += points[j] * ( 1.0 / numImages);
  239. }
  240. // Append boundary points. Will be used in Delaunay Triangulation
  241. for ( size_t j = 0; j < boundaryPts.size(); j++)
  242. {
  243. points.push_back(boundaryPts[j]);
  244. }
  245. pointsNorm.push_back(points);
  246. imagesNorm.push_back(img);
  247. }
  248. // Append boundary points to average points.
  249. for ( size_t j = 0; j < boundaryPts.size(); j++)
  250. {
  251. pointsAvg.push_back(boundaryPts[j]);
  252. }
  253. // Calculate Delaunay triangles
  254. Rect rect(0, 0, w, h);
  255. vector< vector<int> > dt;
  256. calculateDelaunayTriangles(rect, pointsAvg, dt);
  257. // Space for output image
  258. Mat output = Mat::zeros(h, w, CV_32FC3);
  259. Size size(w,h);
  260. // Warp input images to average image landmarks
  261. for(size_t i = 0; i < numImages; i++)
  262. {
  263. Mat img = Mat::zeros(h, w, CV_32FC3);
  264. // Transform triangles one by one
  265. for(size_t j = 0; j < dt.size(); j++)
  266. {
  267. // Input and output points corresponding to jth triangle
  268. vector<Point2f> tin, tout;
  269. for(int k = 0; k < 3; k++)
  270. {
  271. Point2f pIn = pointsNorm[i][dt[j][k]];
  272. constrainPoint(pIn, size);
  273. Point2f pOut = pointsAvg[dt[j][k]];
  274. constrainPoint(pOut,size);
  275. tin.push_back(pIn);
  276. tout.push_back(pOut);
  277. }
  278. warpTriangle(imagesNorm[i], img, tin, tout);
  279. }
  280. // Add image intensities for averaging
  281. output = output + img;
  282. }
  283. // Divide by numImages to get average
  284. output = output / (double)numImages;
  285. // Display result
  286. imshow("image", output);
  287. waitKey(0);
  288. return 0;
  289. }