faceAverage.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. #!/usr/bin/env python
  2. # Copyright (c) 2016 Satya Mallick <spmallick@learnopencv.com>
  3. # All rights reserved. No warranty, explicit or implicit, provided.
  4. import os
  5. import cv2
  6. import numpy as np
  7. import math
  8. import sys
  9. # Read points from text files in directory
  10. def readPoints(path) :
  11. # Create an array of array of points.
  12. pointsArray = []
  13. #List all files in the directory and read points from text files one by one
  14. for filePath in sorted(os.listdir(path)):
  15. if filePath.endswith(".txt"):
  16. #Create an array of points.
  17. points = []
  18. # Read points from filePath
  19. with open(os.path.join(path, filePath)) as file :
  20. for line in file :
  21. x, y = line.split()
  22. points.append((int(x), int(y)))
  23. # Store array of points
  24. pointsArray.append(points)
  25. return pointsArray
  26. # Read all jpg images in folder.
  27. def readImages(path) :
  28. #Create array of array of images.
  29. imagesArray = []
  30. #List all files in the directory and read points from text files one by one
  31. for filePath in sorted(os.listdir(path)):
  32. if filePath.endswith(".jpg"):
  33. # Read image found.
  34. img = cv2.imread(os.path.join(path,filePath))
  35. # Convert to floating point
  36. img = np.float32(img)/255.0
  37. # Add to array of images
  38. imagesArray.append(img)
  39. return imagesArray
  40. # Compute similarity transform given two sets of two points.
  41. # OpenCV requires 3 pairs of corresponding points.
  42. # We are faking the third one.
  43. def similarityTransform(inPoints, outPoints) :
  44. s60 = math.sin(60*math.pi/180)
  45. c60 = math.cos(60*math.pi/180)
  46. inPts = np.copy(inPoints).tolist()
  47. outPts = np.copy(outPoints).tolist()
  48. xin = c60*(inPts[0][0] - inPts[1][0]) - s60*(inPts[0][1] - inPts[1][1]) + inPts[1][0]
  49. yin = s60*(inPts[0][0] - inPts[1][0]) + c60*(inPts[0][1] - inPts[1][1]) + inPts[1][1]
  50. inPts.append([np.int(xin), np.int(yin)])
  51. xout = c60*(outPts[0][0] - outPts[1][0]) - s60*(outPts[0][1] - outPts[1][1]) + outPts[1][0]
  52. yout = s60*(outPts[0][0] - outPts[1][0]) + c60*(outPts[0][1] - outPts[1][1]) + outPts[1][1]
  53. outPts.append([np.int(xout), np.int(yout)])
  54. tform = cv2.estimateAffinePartial2D(np.array([inPts]), np.array([outPts]))
  55. return tform[0]
  56. # Check if a point is inside a rectangle
  57. def rectContains(rect, point) :
  58. if point[0] < rect[0] :
  59. return False
  60. elif point[1] < rect[1] :
  61. return False
  62. elif point[0] > rect[2] :
  63. return False
  64. elif point[1] > rect[3] :
  65. return False
  66. return True
  67. # Calculate delanauy triangle
  68. def calculateDelaunayTriangles(rect, points):
  69. # Create subdiv
  70. subdiv = cv2.Subdiv2D(rect)
  71. # Insert points into subdiv
  72. for p in points:
  73. subdiv.insert((p[0], p[1]))
  74. # List of triangles. Each triangle is a list of 3 points ( 6 numbers )
  75. triangleList = subdiv.getTriangleList()
  76. # Find the indices of triangles in the points array
  77. delaunayTri = []
  78. for t in triangleList:
  79. pt = []
  80. pt.append((t[0], t[1]))
  81. pt.append((t[2], t[3]))
  82. pt.append((t[4], t[5]))
  83. pt1 = (t[0], t[1])
  84. pt2 = (t[2], t[3])
  85. pt3 = (t[4], t[5])
  86. if rectContains(rect, pt1) and rectContains(rect, pt2) and rectContains(rect, pt3):
  87. ind = []
  88. for j in range(0, 3):
  89. for k in range(0, len(points)):
  90. if(abs(pt[j][0] - points[k][0]) < 1.0 and abs(pt[j][1] - points[k][1]) < 1.0):
  91. ind.append(k)
  92. if len(ind) == 3:
  93. delaunayTri.append((ind[0], ind[1], ind[2]))
  94. return delaunayTri
  95. def constrainPoint(p, w, h) :
  96. p = ( min( max( p[0], 0 ) , w - 1 ) , min( max( p[1], 0 ) , h - 1 ) )
  97. return p
  98. # Apply affine transform calculated using srcTri and dstTri to src and
  99. # output an image of size.
  100. def applyAffineTransform(src, srcTri, dstTri, size) :
  101. # Given a pair of triangles, find the affine transform.
  102. warpMat = cv2.getAffineTransform( np.float32(srcTri), np.float32(dstTri) )
  103. # Apply the Affine Transform just found to the src image
  104. dst = cv2.warpAffine( src, warpMat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )
  105. return dst
  106. # Warps and alpha blends triangular regions from img1 and img2 to img
  107. def warpTriangle(img1, img2, t1, t2) :
  108. # Find bounding rectangle for each triangle
  109. r1 = cv2.boundingRect(np.float32([t1]))
  110. r2 = cv2.boundingRect(np.float32([t2]))
  111. # Offset points by left top corner of the respective rectangles
  112. t1Rect = []
  113. t2Rect = []
  114. t2RectInt = []
  115. for i in range(0, 3):
  116. t1Rect.append(((t1[i][0] - r1[0]),(t1[i][1] - r1[1])))
  117. t2Rect.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))
  118. t2RectInt.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))
  119. # Get mask by filling triangle
  120. mask = np.zeros((r2[3], r2[2], 3), dtype = np.float32)
  121. cv2.fillConvexPoly(mask, np.int32(t2RectInt), (1.0, 1.0, 1.0), 16, 0)
  122. # Apply warpImage to small rectangular patches
  123. img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]
  124. size = (r2[2], r2[3])
  125. img2Rect = applyAffineTransform(img1Rect, t1Rect, t2Rect, size)
  126. img2Rect = img2Rect * mask
  127. # Copy triangular region of the rectangular patch to the output image
  128. 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 )
  129. 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
  130. if __name__ == '__main__' :
  131. path = 'presidents/'
  132. # Dimensions of output image
  133. w = 600
  134. h = 600
  135. # Read points for all images
  136. allPoints = readPoints(path)
  137. # Read all images
  138. images = readImages(path)
  139. # Eye corners
  140. eyecornerDst = [ (np.int(0.3 * w ), np.int(h / 3)), (np.int(0.7 * w ), np.int(h / 3)) ]
  141. imagesNorm = []
  142. pointsNorm = []
  143. # Add boundary points for delaunay triangulation
  144. boundaryPts = np.array([(0,0), (w/2,0), (w-1,0), (w-1,h/2), ( w-1, h-1 ), ( w/2, h-1 ), (0, h-1), (0,h/2) ])
  145. # Initialize location of average points to 0s
  146. pointsAvg = np.array([(0,0)]* ( len(allPoints[0]) + len(boundaryPts) ), np.float32())
  147. n = len(allPoints[0])
  148. numImages = len(images)
  149. # Warp images and trasnform landmarks to output coordinate system,
  150. # and find average of transformed landmarks.
  151. for i in range(0, numImages):
  152. points1 = allPoints[i]
  153. # Corners of the eye in input image
  154. eyecornerSrc = [ allPoints[i][36], allPoints[i][45] ]
  155. # Compute similarity transform
  156. tform = similarityTransform(eyecornerSrc, eyecornerDst)
  157. # Apply similarity transformation
  158. img = cv2.warpAffine(images[i], tform, (w,h))
  159. # Apply similarity transform on points
  160. points2 = np.reshape(np.array(points1), (68,1,2))
  161. points = cv2.transform(points2, tform)
  162. points = np.float32(np.reshape(points, (68, 2)))
  163. # Append boundary points. Will be used in Delaunay Triangulation
  164. points = np.append(points, boundaryPts, axis=0)
  165. # Calculate location of average landmark points.
  166. pointsAvg = pointsAvg + points / numImages
  167. pointsNorm.append(points)
  168. imagesNorm.append(img)
  169. # Delaunay triangulation
  170. rect = (0, 0, w, h)
  171. dt = calculateDelaunayTriangles(rect, np.array(pointsAvg))
  172. # Output image
  173. output = np.zeros((h,w,3), np.float32())
  174. # Warp input images to average image landmarks
  175. for i in range(0, len(imagesNorm)) :
  176. img = np.zeros((h,w,3), np.float32())
  177. # Transform triangles one by one
  178. for j in range(0, len(dt)) :
  179. tin = []
  180. tout = []
  181. for k in range(0, 3) :
  182. pIn = pointsNorm[i][dt[j][k]]
  183. pIn = constrainPoint(pIn, w, h)
  184. pOut = pointsAvg[dt[j][k]]
  185. pOut = constrainPoint(pOut, w, h)
  186. tin.append(pIn)
  187. tout.append(pOut)
  188. warpTriangle(imagesNorm[i], img, tin, tout)
  189. # Add image intensities for averaging
  190. output = output + img
  191. # Divide by numImages to get average
  192. output = output / numImages
  193. # Display result
  194. cv2.imshow('image', output)
  195. cv2.waitKey(0)