├── README.md ├── face_Switching_dlib_opencv.ipynb └── 平均脸 ├── 001.jpg ├── Average+Face.ipynb ├── README.md ├── faceAverage.py ├── face_landmark_detection.py ├── 总统 ├── barak-obama.jpg ├── barak-obama.jpg.txt ├── bill-clinton.jpg ├── bill-clinton.jpg.txt ├── george-h-bush.jpg ├── george-h-bush.jpg.txt ├── george-w-bush.jpg ├── george-w-bush.jpg.txt ├── jimmy-carter.jpg ├── jimmy-carter.jpg.txt ├── ronald-regan.jpg └── ronald-regan.jpg.txt └── 脸们 ├── barak-obama.jpg ├── barak-obama.jpg.txt ├── bill-clinton.jpg ├── bill-clinton.jpg.txt ├── george-h-bush.jpg ├── george-h-bush.jpg.txt ├── george-w-bush.jpg ├── george-w-bush.jpg.txt ├── jimmy-carter.jpg ├── jimmy-carter.jpg.txt ├── ronald-regan.jpg └── ronald-regan.jpg.txt /README.md: -------------------------------------------------------------------------------- 1 | # 项目一:Face_Swapping 2 | 3 | 简单换脸、人脸对齐、关键点定位与画图 4 | 5 | 这是一个利用dlib进行关键点定位 + opencv处理的人脸对齐、换脸、关键点识别的小demo。原文来自于[《Switching Eds: Face swapping with Python, dlib, and OpenCV》](https://matthewearl.github.io/2015/07/28/switching-eds-with-python/) 6 | 该博文的[github](https://github.com/matthewearl/faceswap/blob/master/faceswap.py)地址中有所有的code。这边我的博客地址: 7 | http://blog.csdn.net/sinat_26917383/article/details/78564416 8 | 9 | 有人将其进行[中文翻译](http://python.jobbole.com/82546/)也有将其进行一定改编有以下两个案例: 10 | 11 | - 1.[《川普撞脸希拉里(基于 OpenCV 的面部特征交换)-2》](http://blog.csdn.net/oxuzhenyi/article/details/54982632) 12 | - [变脸](http://messcode.github.io/2016/04/17/switch-faces-using-python/) 13 | 14 | ### 变脸贴图: 15 | 从这张: 16 | ![这里写图片描述](http://7xrpb1.com1.z0.glb.clouddn.com/marked_img.jpg) 17 | 变为这张: 18 | ![这里写图片描述](http://7xrpb1.com1.z0.glb.clouddn.com/switched_face.jpg) 19 | 20 | 因为原文里面内容丰富,我觉得可以提取出很多有用的小模块,于是乎: 21 | . 22 | 23 | 提取一:关键点定位与画图 24 | ============ 25 | 26 | ``` 27 | import cv2 28 | import dlib 29 | import numpy 30 | import sys 31 | import matplotlib.pyplot as plt 32 | SCALE_FACTOR = 1 # 图像的放缩比 33 | 34 | def read_im_and_landmarks(fname): 35 | im = cv2.imread(fname, cv2.IMREAD_COLOR) 36 | im = cv2.resize(im, (im.shape[1] * SCALE_FACTOR, 37 | im.shape[0] * SCALE_FACTOR)) 38 | s = get_landmarks(im) 39 | 40 | return im, s 41 | 42 | def annotate_landmarks(im, landmarks): 43 | ''' 44 | 人脸关键点,画图函数 45 | ''' 46 | im = im.copy() 47 | for idx, point in enumerate(landmarks): 48 | pos = (point[0, 0], point[0, 1]) 49 | cv2.putText(im, str(idx), pos, 50 | fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 51 | fontScale=0.4, 52 | color=(0, 0, 255)) 53 | cv2.circle(im, pos, 3, color=(0, 255, 255)) 54 | return im 55 | ``` 56 | 57 | 然后实践就是载入原图: 58 | 59 | ``` 60 | im1, landmarks1 = read_im_and_landmarks('02.jpg') # 底图 61 | im1 = annotate_landmarks(im1, landmarks1) 62 | 63 | %matplotlib inline 64 | plt.subplot(111) 65 | plt.imshow(im1) 66 | ``` 67 | . 68 | 69 | 提取二:人脸对齐 70 | ======== 71 | 72 | 需要一张模板图来作为靠拢的对象图。 73 | 74 | ``` 75 | # 人脸对齐函数 76 | def face_Align(Base_path,cover_path): 77 | im1, landmarks1 = read_im_and_landmarks(Base_path) # 底图 78 | im2, landmarks2 = read_im_and_landmarks(cover_path) # 贴上来的图 79 | 80 | if len(landmarks1) == 0 & len(landmarks2) == 0 : 81 | raise ImproperNumber("Faces detected is no face!") 82 | if len(landmarks1) > 1 & len(landmarks2) > 1 : 83 | raise ImproperNumber("Faces detected is more than 1!") 84 | 85 | M = transformation_from_points(landmarks1[ALIGN_POINTS], 86 | landmarks2[ALIGN_POINTS]) 87 | warped_im2 = warp_im(im2, M, im1.shape) 88 | return warped_im2 89 | ``` 90 | #### 这里的步骤是: 91 | 92 | - 提取模板图、对齐图的landmarks; 93 | - 通过transformation_from_points计算对齐图向模板图的转移矩阵M,变换矩阵是根据以下公式计算出来的; 94 | - warp_im,将 im2 的掩码进行变化,使之与 im1 相符 95 | 96 | 实践的话就是: 97 | 98 | ``` 99 | FEATHER_AMOUNT = 19 # 匹配的时候,特征数量,现在是以11个点为基准点 11 15 17 100 | 101 | Base_path = '01.jpg' 102 | cover_path = '02.jpg' 103 | warped_mask = face_Align(Base_path,cover_path) 104 | ``` 105 | . 106 | 107 | 提取三:换脸 108 | ====== 109 | 110 | 主要函数: 111 | ``` 112 | def Switch_face(Base_path,cover_path): 113 | im1, landmarks1 = read_im_and_landmarks(Base_path) # 底图 114 | im2, landmarks2 = read_im_and_landmarks(cover_path) # 贴上来的图 115 | 116 | if len(landmarks1) == 0 & len(landmarks2) == 0 : 117 | raise ImproperNumber("Faces detected is no face!") 118 | if len(landmarks1) > 1 & len(landmarks2) > 1 : 119 | raise ImproperNumber("Faces detected is more than 1!") 120 | 121 | M = transformation_from_points(landmarks1[ALIGN_POINTS], 122 | landmarks2[ALIGN_POINTS]) 123 | mask = get_face_mask(im2, landmarks2) 124 | warped_mask = warp_im(mask, M, im1.shape) 125 | combined_mask = numpy.max([get_face_mask(im1, landmarks1), warped_mask], 126 | axis=0) 127 | warped_im2 = warp_im(im2, M, im1.shape) 128 | warped_corrected_im2 = correct_colours(im1, warped_im2, landmarks1) 129 | 130 | output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 * combined_mask 131 | return output_im 132 | ``` 133 | #### 主要步骤: 134 | - 提取模板图、对齐图的landmarks; 135 | - M,通过transformation_from_points计算对齐图向模板图的转移矩阵M; 136 | 137 | 138 | ``` 139 | matrix([[ 0.62876962, 0.20978991, -101.32973923], 140 | [ -0.20978991, 0.62876962, 79.11235991], 141 | [ 0. , 0. , 1. ]]) 142 | ``` 143 | 144 | - mask,得到基于对齐图的掩膜,get_face_mask函数,获取 im2 的面部掩码,mask长成这样: 145 | ![这里写图片描述](https://matthewearl.github.io/assets/switching-eds/mask.png) 146 | - warped_mask ,warp_im函数,将 im2 的掩码进行变化,使之与 im1 相符,跟上面的mask张一样(一个鼻子) 147 | - combined_mask ,将二者的掩码进行连通(跟warped_mask 长一样) 148 | - warped_im2 ,warp_im函数,第二次,将第二幅图像调整到与第一幅图像相符(对齐图片,斜了点) 149 | - warped_corrected_im2 ,correct_colours函数,将 im2 的皮肤颜色进行修正,使其和 im1 的颜色尽量协调(类似下图) 150 | 151 | - output_im 组合图像,获得结果 152 | 153 | 实践: 154 | 155 | ``` 156 | FEATHER_AMOUNT = 23 157 | 158 | Base_path = '03.jpg' 159 | cover_path = '02.jpg' 160 | output_im = Switch_face(Base_path,cover_path) 161 | ``` 162 | 163 | 164 | # 项目二:平均脸 165 | 新更新了一个平均脸的程序内容: 166 | ![这里写图片描述](https://www.learnopencv.com/wp-content/uploads/2016/05/average_best_actress-300x300.jpg) 167 | 168 | py代码以及相关数据地址:https://www.learnopencv.com/wp-content/uploads/2016/05/FaceAverage.zip 169 | 最初博文地址:https://www.learnopencv.com/average-face-opencv-c-python-tutorial/ 170 | 中文翻译:http://blog.csdn.net/GraceDD/article/details/51382952 171 | 中文改编地址:[《手把手:用OpenCV亲手给小扎、Musk等科技大佬们做一张“平均脸”(附Python代码)》](https://mp.weixin.qq.com/s?__biz=MjM5MTQzNzU2NA==&mid=2651654758&idx=1&sn=b60e2da0b4e9cffed660f44bd624eb9e&chksm=bd4c2df58a3ba4e32f938df33cdc780bd7041087c6f3b82cf059c036a50c12e97067a8d12815&mpshare=1&scene=1&srcid=1123eFDjNTtDFdq4GS8M2e8d#rd) 172 | 173 | 在完成各个library的安装后。 174 | 175 | - 第一步:将要平均的照片放入faces文档,确保图片为jpg格式。 176 | - 第二步:在终端运行 python face_landmark_detection.py 177 | shape_predictor_68_face_landmarks.dat 178 | faces,并在程序运行结束后将所有faces文档中的文件复制到presidents文档中(如无法完成dlib安装,可略过该步骤,直接用文摘菌提供的素材) 179 | - 第三步:在终端运行 python faceAverage.py 这样就能看到制作成功的平均脸了! 180 | 181 | 具体实现步骤: 182 | 183 | - 1.读入图 + 读入关键点信息 readPoints readImages 184 | - 2.平均脸的眼角位置(这样其他脸,按照眼睛位置对齐) eyecornerDst 185 | - 3.新的8个初始边界点 boundaryPts (为了后续做脸谱网络用的) 186 | - 4.设置初始平均脸 pointsAvg (随便找个脸68个关键点 + 8个初始点) 187 | - 5.根据眼睛位置,进行人脸初步对齐 188 | - 6.计算初始平均脸的脸谱网络76点(calculateDelaunayTriangles) 189 | - 7.根据脸谱网络二次人脸对齐 190 | ![这里写图片描述](https://www.learnopencv.com/wp-content/uploads/2016/05/image-warping-based-on-delaunay-triangulation-768x256.jpg) 191 | 本案例中进行了两次对齐,眼睛对齐之后,通过Warp Triangles 再此对齐。 192 | -------------------------------------------------------------------------------- /face_Switching_dlib_opencv.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 原文\n", 8 | "源码来源:https://matthewearl.github.io/2015/07/28/switching-eds-with-python/\n", 9 | "\n", 10 | "shape_predictor_68_face_landmarks模型下载链接: \n", 11 | "https://sourceforge.net/projects/dclib/files/dlib/v18.10/shape_predictor_68_face_landmarks.dat.bz2/download" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "import cv2\n", 23 | "import dlib\n", 24 | "import numpy\n", 25 | "import sys\n", 26 | "import matplotlib.pyplot as plt\n", 27 | "\n", 28 | "PREDICTOR_PATH = \"/.../shape_predictor_68_face_landmarks.dat\" # 68个关键点landmarks的模型文件\n", 29 | "SCALE_FACTOR = 1 # 图像的放缩比\n", 30 | "FEATHER_AMOUNT = 15 # 羽化边界范围,越大,羽化能力越大,一定要奇数,不能偶数\n", 31 | "\n", 32 | "# 68个点\n", 33 | "FACE_POINTS = list(range(17, 68)) # 脸\n", 34 | "MOUTH_POINTS = list(range(48, 61)) # 嘴巴\n", 35 | "RIGHT_BROW_POINTS = list(range(17, 22)) # 右眉毛\n", 36 | "LEFT_BROW_POINTS = list(range(22, 27)) # 左眉毛\n", 37 | "RIGHT_EYE_POINTS = list(range(36, 42)) # 右眼睛\n", 38 | "LEFT_EYE_POINTS = list(range(42, 48)) # 左眼睛\n", 39 | "NOSE_POINTS = list(range(27, 35)) # 鼻子\n", 40 | "JAW_POINTS = list(range(0, 17)) # 下巴\n", 41 | "\n", 42 | "# 选取用于叠加在第一张脸上的第二张脸的面部特征\n", 43 | "# 特征点包括左右眼、眉毛、鼻子和嘴巴\n", 44 | "# 是否数量变多之后,会有什么干扰吗?\n", 45 | "ALIGN_POINTS = (LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS +\n", 46 | " RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS)\n", 47 | "\n", 48 | "# Points from the second image to overlay on the first. The convex hull of each\n", 49 | "# element will be overlaid.\n", 50 | "OVERLAY_POINTS = [\n", 51 | " LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS,\n", 52 | " NOSE_POINTS + MOUTH_POINTS,\n", 53 | "] \n", 54 | "# 眼睛 ,眉毛 2 * 22\n", 55 | "# 鼻子,嘴巴 分开来\n", 56 | "\n", 57 | "# 定义用于颜色校正的模糊量,作为瞳孔距离的系数\n", 58 | "COLOUR_CORRECT_BLUR_FRAC = 0.6\n", 59 | "\n", 60 | "# 实例化脸部检测器\n", 61 | "detector = dlib.get_frontal_face_detector()\n", 62 | "# 加载训练模型\n", 63 | "# 并实例化特征提取器\n", 64 | "predictor = dlib.shape_predictor(PREDICTOR_PATH)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": { 71 | "collapsed": true 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "# 定义了两个类处理意外\n", 76 | "class TooManyFaces(Exception): \n", 77 | " pass\n", 78 | "\n", 79 | "class NoFaces(Exception):\n", 80 | " pass\n", 81 | "\n", 82 | "\n", 83 | "def get_landmarks(im):\n", 84 | " '''\n", 85 | " 通过predictor 拿到68 landmarks\n", 86 | " '''\n", 87 | " rects = detector(im, 1)\n", 88 | " \n", 89 | " if len(rects) > 1:\n", 90 | " raise TooManyFaces\n", 91 | " if len(rects) == 0:\n", 92 | " raise NoFaces\n", 93 | "\n", 94 | " return numpy.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()]) # 68*2的矩阵\n", 95 | "\n", 96 | "def annotate_landmarks(im, landmarks):\n", 97 | " '''\n", 98 | " 人脸关键点,画图函数\n", 99 | " '''\n", 100 | " im = im.copy()\n", 101 | " for idx, point in enumerate(landmarks):\n", 102 | " pos = (point[0, 0], point[0, 1])\n", 103 | " cv2.putText(im, str(idx), pos,\n", 104 | " fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,\n", 105 | " fontScale=0.4,\n", 106 | " color=(0, 0, 255))\n", 107 | " cv2.circle(im, pos, 3, color=(0, 255, 255))\n", 108 | " return im\n", 109 | "\n", 110 | "def draw_convex_hull(im, points, color):\n", 111 | " '''\n", 112 | " # 绘制凸多边形 计算凸包\n", 113 | " '''\n", 114 | " points = cv2.convexHull(points)\n", 115 | " cv2.fillConvexPoly(im, points, color=color)\n", 116 | "\n", 117 | "def get_face_mask(im, landmarks):\n", 118 | " '''获取面部特征部分(眉毛、眼睛、鼻子以及嘴巴)的图像掩码。\n", 119 | " 图像掩码作用于原图之后,原图中对应掩码部分为白色的部分才能显示出来,黑色的部分则不予显示,因此通过图像掩码我们就能实现对图像“裁剪”。\n", 120 | " 效果参考:https://dn-anything-about-doc.qbox.me/document-uid242676labid2260timestamp1477921310170.png/wm\n", 121 | " get_face_mask()的定义是为一张图像和一个标记矩阵生成一个遮罩,它画出了两个白色的凸多边形:一个是眼睛周围的区域,\n", 122 | " 一个是鼻子和嘴部周围的区域。之后它由11个(FEATHER_AMOUNT)像素向遮罩的边缘外部羽化扩展,可以帮助隐藏任何不连续的区域。\n", 123 | " '''\n", 124 | " im = numpy.zeros(im.shape[:2], dtype=numpy.float64)\n", 125 | "\n", 126 | " for group in OVERLAY_POINTS:\n", 127 | " draw_convex_hull(im,\n", 128 | " landmarks[group],\n", 129 | " color=1)\n", 130 | "\n", 131 | " im = numpy.array([im, im, im]).transpose((1, 2, 0))\n", 132 | "\n", 133 | " im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0\n", 134 | " im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0)\n", 135 | "\n", 136 | " return im\n", 137 | " \n", 138 | "def transformation_from_points(points1, points2):\n", 139 | " \"\"\"\n", 140 | " Return an affine transformation [s * R | T] such that:\n", 141 | " sum ||s*R*p1,i + T - p2,i||^2\n", 142 | " is minimized.\n", 143 | " \"\"\"\n", 144 | " # Solve the procrustes problem by subtracting centroids, scaling by the\n", 145 | " # standard deviation, and then using the SVD to calculate the rotation. See\n", 146 | " # the following for more details:\n", 147 | " # https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem\n", 148 | "\n", 149 | " points1 = points1.astype(numpy.float64)\n", 150 | " points2 = points2.astype(numpy.float64)\n", 151 | "\n", 152 | " c1 = numpy.mean(points1, axis=0)\n", 153 | " c2 = numpy.mean(points2, axis=0)\n", 154 | " points1 -= c1\n", 155 | " points2 -= c2\n", 156 | "\n", 157 | " s1 = numpy.std(points1)\n", 158 | " s2 = numpy.std(points2)\n", 159 | " points1 /= s1\n", 160 | " points2 /= s2\n", 161 | "\n", 162 | " U, S, Vt = numpy.linalg.svd(points1.T * points2)\n", 163 | "\n", 164 | " # The R we seek is in fact the transpose of the one given by U * Vt. This\n", 165 | " # is because the above formulation assumes the matrix goes on the right\n", 166 | " # (with row vectors) where as our solution requires the matrix to be on the\n", 167 | " # left (with column vectors).\n", 168 | " R = (U * Vt).T\n", 169 | "\n", 170 | " return numpy.vstack([numpy.hstack(((s2 / s1) * R,\n", 171 | " c2.T - (s2 / s1) * R * c1.T)),\n", 172 | " numpy.matrix([0., 0., 1.])])\n", 173 | "\n", 174 | "def read_im_and_landmarks(fname):\n", 175 | " im = cv2.imread(fname, cv2.IMREAD_COLOR)\n", 176 | " im = cv2.resize(im, (im.shape[1] * SCALE_FACTOR,\n", 177 | " im.shape[0] * SCALE_FACTOR))\n", 178 | " s = get_landmarks(im)\n", 179 | "\n", 180 | " return im, s\n", 181 | "\n", 182 | "def warp_im(im, M, dshape):\n", 183 | " '''\n", 184 | " 由 get_face_mask 获得的图像掩码还不能直接使用,因为一般来讲用户提供的两张图像的分辨率大小很可能不一样,而且即便分辨率一样,\n", 185 | " 图像中的人脸由于拍摄角度和距离等原因也会呈现出不同的大小以及角度,所以如果不能只是简单地把第二个人的面部特征抠下来直接放在第一个人脸上,\n", 186 | " 我们还需要根据两者计算所得的面部特征区域进行匹配变换,使得二者的面部特征尽可能重合。\n", 187 | " \n", 188 | " 仿射函数,warpAffine,能对图像进行几何变换\n", 189 | " 三个主要参数,第一个输入图像,第二个变换矩阵 np.float32 类型,第三个变换之后图像的宽高\n", 190 | " \n", 191 | " 对齐主要函数\n", 192 | " '''\n", 193 | " output_im = numpy.zeros(dshape, dtype=im.dtype)\n", 194 | " cv2.warpAffine(im,\n", 195 | " M[:2],\n", 196 | " (dshape[1], dshape[0]),\n", 197 | " dst=output_im,\n", 198 | " borderMode=cv2.BORDER_TRANSPARENT,\n", 199 | " flags=cv2.WARP_INVERSE_MAP)\n", 200 | " return output_im\n", 201 | "\n", 202 | "def correct_colours(im1, im2, landmarks1):\n", 203 | " '''\n", 204 | " 修改皮肤颜色,使两张图片在拼接时候显得更加自然。\n", 205 | " '''\n", 206 | " blur_amount = COLOUR_CORRECT_BLUR_FRAC * numpy.linalg.norm(\n", 207 | " numpy.mean(landmarks1[LEFT_EYE_POINTS], axis=0) -\n", 208 | " numpy.mean(landmarks1[RIGHT_EYE_POINTS], axis=0))\n", 209 | " blur_amount = int(blur_amount)\n", 210 | " if blur_amount % 2 == 0:\n", 211 | " blur_amount += 1\n", 212 | " im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0)\n", 213 | " im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0)\n", 214 | "\n", 215 | " # Avoid divide-by-zero errors.\n", 216 | " im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)\n", 217 | "\n", 218 | " return (im2.astype(numpy.float64) * im1_blur.astype(numpy.float64) /\n", 219 | " im2_blur.astype(numpy.float64))" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "metadata": { 226 | "collapsed": true 227 | }, 228 | "outputs": [], 229 | "source": [ 230 | "# 换脸函数\n", 231 | "def Switch_face(Base_path,cover_path):\n", 232 | " im1, landmarks1 = read_im_and_landmarks(Base_path) # 底图\n", 233 | " im2, landmarks2 = read_im_and_landmarks(cover_path) # 贴上来的图\n", 234 | " \n", 235 | " if len(landmarks1) == 0 & len(landmarks2) == 0 :\n", 236 | " raise ImproperNumber(\"Faces detected is no face!\")\n", 237 | " if len(landmarks1) > 1 & len(landmarks2) > 1 :\n", 238 | " raise ImproperNumber(\"Faces detected is more than 1!\")\n", 239 | " \n", 240 | " M = transformation_from_points(landmarks1[ALIGN_POINTS],\n", 241 | " landmarks2[ALIGN_POINTS])\n", 242 | " mask = get_face_mask(im2, landmarks2)\n", 243 | " warped_mask = warp_im(mask, M, im1.shape)\n", 244 | " combined_mask = numpy.max([get_face_mask(im1, landmarks1), warped_mask],\n", 245 | " axis=0)\n", 246 | " warped_im2 = warp_im(im2, M, im1.shape)\n", 247 | " warped_corrected_im2 = correct_colours(im1, warped_im2, landmarks1)\n", 248 | "\n", 249 | " output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 * combined_mask\n", 250 | " return output_im\n", 251 | "\n", 252 | "# 人脸对齐函数\n", 253 | "def face_Align(Base_path,cover_path):\n", 254 | " im1, landmarks1 = read_im_and_landmarks(Base_path) # 底图\n", 255 | " im2, landmarks2 = read_im_and_landmarks(cover_path) # 贴上来的图\n", 256 | " \n", 257 | " if len(landmarks1) == 0 & len(landmarks2) == 0 :\n", 258 | " raise ImproperNumber(\"Faces detected is no face!\")\n", 259 | " if len(landmarks1) > 1 & len(landmarks2) > 1 :\n", 260 | " raise ImproperNumber(\"Faces detected is more than 1!\")\n", 261 | " \n", 262 | " M = transformation_from_points(landmarks1[ALIGN_POINTS],\n", 263 | " landmarks2[ALIGN_POINTS])\n", 264 | " warped_im2 = warp_im(im2, M, im1.shape)\n", 265 | " return warped_im2" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": null, 271 | "metadata": {}, 272 | "outputs": [], 273 | "source": [ 274 | "'''\n", 275 | "换脸\n", 276 | "Base_path:是底图\n", 277 | "cover_path:从该图抽取内容覆盖到底图上\n", 278 | "'''\n", 279 | "FEATHER_AMOUNT = 23\n", 280 | "\n", 281 | "Base_path = '03.jpg'\n", 282 | "cover_path = '02.jpg'\n", 283 | "output_im = Switch_face(Base_path,cover_path)\n", 284 | "\n", 285 | "cv2.imwrite('/../output.jpg', output_im) # 换脸数据保存" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": null, 291 | "metadata": {}, 292 | "outputs": [], 293 | "source": [ 294 | "'''\n", 295 | "人脸对齐-dlib\n", 296 | "Base_path:模板图\n", 297 | "cover_path:需对齐的图\n", 298 | "'''\n", 299 | "FEATHER_AMOUNT = 19 # 匹配的时候,特征数量,现在是以11个点为基准点 11 15 17 \n", 300 | "\n", 301 | "Base_path = '01.jpg'\n", 302 | "cover_path = '02.jpg'\n", 303 | "warped_mask = face_Align(Base_path,cover_path)\n", 304 | "\n", 305 | "%matplotlib inline\n", 306 | "plt.subplot(111)\n", 307 | "plt.imshow(warped_mask) # 数据展示" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": null, 313 | "metadata": { 314 | "collapsed": true 315 | }, 316 | "outputs": [], 317 | "source": [ 318 | "# 关键点定位 画图函数\n", 319 | " # 画圈 + 数字\n", 320 | "def annotate_landmarks(im, landmarks):\n", 321 | " im = im.copy()\n", 322 | " for idx, point in enumerate(landmarks):\n", 323 | " pos = (point[0, 0], point[0, 1])\n", 324 | " cv2.putText(im, str(idx), pos,\n", 325 | " fontFace=cv2.FONT_HERSHEY_SCRIPT_SIMPLEX,\n", 326 | " fontScale=0.4,\n", 327 | " color=(0, 0, 255))\n", 328 | " cv2.circle(im, pos, 3, color=(0, 255, 255))\n", 329 | " return im" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": null, 335 | "metadata": {}, 336 | "outputs": [], 337 | "source": [ 338 | "im1, landmarks1 = read_im_and_landmarks('02.jpg') # 底图\n", 339 | "im1 = annotate_landmarks(im1, landmarks1)\n", 340 | "\n", 341 | "%matplotlib inline\n", 342 | "plt.subplot(111)\n", 343 | "plt.imshow(im1)" 344 | ] 345 | } 346 | ], 347 | "metadata": { 348 | "kernelspec": { 349 | "display_name": "Python 3", 350 | "language": "python", 351 | "name": "python3" 352 | }, 353 | "language_info": { 354 | "codemirror_mode": { 355 | "name": "ipython", 356 | "version": 3 357 | }, 358 | "file_extension": ".py", 359 | "mimetype": "text/x-python", 360 | "name": "python", 361 | "nbconvert_exporter": "python", 362 | "pygments_lexer": "ipython3", 363 | "version": "3.5.2" 364 | } 365 | }, 366 | "nbformat": 4, 367 | "nbformat_minor": 2 368 | } 369 | -------------------------------------------------------------------------------- /平均脸/001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/001.jpg -------------------------------------------------------------------------------- /平均脸/Average+Face.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## 平均脸\n", 8 | "![这里写图片描述](http://www.learnopencv.com/wp-content/uploads/2016/05/average_entrepreneur.jpg)\n", 9 | "![这里写图片描述](https://www.learnopencv.com/wp-content/uploads/2016/05/average_best_actress-300x300.jpg)" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "\n", 17 | "py代码以及相关数据地址:https://www.learnopencv.com/wp-content/uploads/2016/05/FaceAverage.zip\n", 18 | "\n", 19 | "最初博文地址:https://www.learnopencv.com/average-face-opencv-c-python-tutorial/\n", 20 | "中文翻译:http://blog.csdn.net/GraceDD/article/details/51382952\n", 21 | "\n", 22 | "\n", 23 | "中文改编地址:[《手把手:用OpenCV亲手给小扎、Musk等科技大佬们做一张“平均脸”(附Python代码)》](https://mp.weixin.qq.com/s?__biz=MjM5MTQzNzU2NA==&mid=2651654758&idx=1&sn=b60e2da0b4e9cffed660f44bd624eb9e&chksm=bd4c2df58a3ba4e32f938df33cdc780bd7041087c6f3b82cf059c036a50c12e97067a8d12815&mpshare=1&scene=1&srcid=1123eFDjNTtDFdq4GS8M2e8d#rd)" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": { 29 | "collapsed": true 30 | }, 31 | "source": [ 32 | "## 平均脸\n", 33 | "在完成各个library的安装后。\n", 34 | "第一步:将要平均的照片放入faces文档,确保图片为jpg格式。\n", 35 | "第二步:在终端运行 python face_landmark_detection.py shape_predictor_68_face_landmarks.dat faces,并在程序运行结束后将所有faces文档中的文件复制到presidents文档中(如无法完成dlib安装,可略过该步骤,直接用文摘菌提供的素材)\n", 36 | "第三步:在终端运行 python faceAverage.py \n", 37 | "这样就能看到制作成功的平均脸了!\n" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": { 44 | "collapsed": true 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "import os\n", 49 | "import cv2\n", 50 | "import numpy as np\n", 51 | "import math\n", 52 | "import sys\n", 53 | "\n", 54 | "# Read points from text files in directory\n", 55 | "def readPoints(path) :\n", 56 | " # Create an array of array of points.\n", 57 | " pointsArray = [];\n", 58 | "\n", 59 | " #List all files in the directory and read points from text files one by one\n", 60 | " for filePath in os.listdir(path):\n", 61 | " \n", 62 | " if filePath.endswith(\".txt\"):\n", 63 | " \n", 64 | " #Create an array of points.\n", 65 | " points = []; \n", 66 | " \n", 67 | " # Read points from filePath\n", 68 | " with open(os.path.join(path, filePath)) as file :\n", 69 | " for line in file :\n", 70 | " x, y = line.split()\n", 71 | " points.append((int(x), int(y)))\n", 72 | " \n", 73 | " # Store array of points\n", 74 | " pointsArray.append(points)\n", 75 | " \n", 76 | " return pointsArray;\n", 77 | "\n", 78 | "# Read all jpg images in folder.\n", 79 | "def readImages(path) :\n", 80 | " \n", 81 | " #Create array of array of images.\n", 82 | " imagesArray = [];\n", 83 | " \n", 84 | " #List all files in the directory and read points from text files one by one\n", 85 | " for filePath in os.listdir(path):\n", 86 | " if filePath.endswith(\".jpg\"):\n", 87 | " # Read image found.\n", 88 | " img = cv2.imread(os.path.join(path,filePath));\n", 89 | "\n", 90 | " # Convert to floating point\n", 91 | " img = np.float32(img)/255.0;\n", 92 | "\n", 93 | " # Add to array of images\n", 94 | " imagesArray.append(img);\n", 95 | " \n", 96 | " return imagesArray;\n", 97 | " \n", 98 | "# Compute similarity transform given two sets of two points.\n", 99 | "# OpenCV requires 3 pairs of corresponding points.\n", 100 | "# We are faking the third one.\n", 101 | "\n", 102 | "def similarityTransform(inPoints, outPoints) :\n", 103 | " s60 = math.sin(60*math.pi/180);\n", 104 | " c60 = math.cos(60*math.pi/180); \n", 105 | " \n", 106 | " inPts = np.copy(inPoints).tolist();\n", 107 | " outPts = np.copy(outPoints).tolist();\n", 108 | " \n", 109 | " xin = c60*(inPts[0][0] - inPts[1][0]) - s60*(inPts[0][1] - inPts[1][1]) + inPts[1][0];\n", 110 | " yin = s60*(inPts[0][0] - inPts[1][0]) + c60*(inPts[0][1] - inPts[1][1]) + inPts[1][1];\n", 111 | " \n", 112 | " inPts.append([np.int(xin), np.int(yin)]);\n", 113 | " \n", 114 | " xout = c60*(outPts[0][0] - outPts[1][0]) - s60*(outPts[0][1] - outPts[1][1]) + outPts[1][0];\n", 115 | " yout = s60*(outPts[0][0] - outPts[1][0]) + c60*(outPts[0][1] - outPts[1][1]) + outPts[1][1];\n", 116 | " \n", 117 | " outPts.append([np.int(xout), np.int(yout)]);\n", 118 | " \n", 119 | " tform = cv2.estimateRigidTransform(np.array([inPts]), np.array([outPts]), False); # 多个二维点对之间的仿射变换矩阵(使用误差最小准则)\n", 120 | " \n", 121 | " return tform;\n", 122 | "\n", 123 | "\n", 124 | "# Check if a point is inside a rectangle\n", 125 | "def rectContains(rect, point) :\n", 126 | " if point[0] < rect[0] :\n", 127 | " return False\n", 128 | " elif point[1] < rect[1] :\n", 129 | " return False\n", 130 | " elif point[0] > rect[2] :\n", 131 | " return False\n", 132 | " elif point[1] > rect[3] :\n", 133 | " return False\n", 134 | " return True\n", 135 | "\n", 136 | "# Calculate delanauy triangle\n", 137 | "def calculateDelaunayTriangles(rect, points):\n", 138 | " # Create subdiv\n", 139 | " subdiv = cv2.Subdiv2D(rect);\n", 140 | " \n", 141 | " # Insert points into subdiv\n", 142 | " for p in points:\n", 143 | " subdiv.insert((p[0], p[1]));\n", 144 | "\n", 145 | " \n", 146 | " # List of triangles. Each triangle is a list of 3 points ( 6 numbers )\n", 147 | " triangleList = subdiv.getTriangleList();\n", 148 | "\n", 149 | " # Find the indices of triangles in the points array\n", 150 | "\n", 151 | " delaunayTri = []\n", 152 | " \n", 153 | " for t in triangleList:\n", 154 | " pt = []\n", 155 | " pt.append((t[0], t[1]))\n", 156 | " pt.append((t[2], t[3]))\n", 157 | " pt.append((t[4], t[5]))\n", 158 | " \n", 159 | " pt1 = (t[0], t[1])\n", 160 | " pt2 = (t[2], t[3])\n", 161 | " pt3 = (t[4], t[5]) \n", 162 | " \n", 163 | " if rectContains(rect, pt1) and rectContains(rect, pt2) and rectContains(rect, pt3):\n", 164 | " ind = []\n", 165 | " for j in range(0, 3):\n", 166 | " for k in range(0, len(points)): \n", 167 | " if(abs(pt[j][0] - points[k][0]) < 1.0 and abs(pt[j][1] - points[k][1]) < 1.0):\n", 168 | " ind.append(k) \n", 169 | " if len(ind) == 3: \n", 170 | " delaunayTri.append((ind[0], ind[1], ind[2]))\n", 171 | "\n", 172 | " return delaunayTri\n", 173 | "\n", 174 | "\n", 175 | "def constrainPoint(p, w, h) :\n", 176 | " p = ( min( max( p[0], 0 ) , w - 1 ) , min( max( p[1], 0 ) , h - 1 ) )\n", 177 | " return p;\n", 178 | "\n", 179 | "# Apply affine transform calculated using srcTri and dstTri to src and\n", 180 | "# output an image of size.\n", 181 | "def applyAffineTransform(src, srcTri, dstTri, size) :\n", 182 | " \n", 183 | " # Given a pair of triangles, find the affine transform.\n", 184 | " warpMat = cv2.getAffineTransform( np.float32(srcTri), np.float32(dstTri) )\n", 185 | " \n", 186 | " # Apply the Affine Transform just found to the src image\n", 187 | " dst = cv2.warpAffine( src, warpMat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )\n", 188 | "\n", 189 | " return dst\n", 190 | "\n", 191 | "\n", 192 | "# Warps and alpha blends triangular regions from img1 and img2 to img\n", 193 | "def warpTriangle(img1, img2, t1, t2) :\n", 194 | "\n", 195 | " # Find bounding rectangle for each triangle\n", 196 | " r1 = cv2.boundingRect(np.float32([t1]))\n", 197 | " r2 = cv2.boundingRect(np.float32([t2]))\n", 198 | "\n", 199 | " # Offset points by left top corner of the respective rectangles\n", 200 | " t1Rect = [] \n", 201 | " t2Rect = []\n", 202 | " t2RectInt = []\n", 203 | "\n", 204 | " for i in range(0, 3):\n", 205 | " t1Rect.append(((t1[i][0] - r1[0]),(t1[i][1] - r1[1])))\n", 206 | " t2Rect.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))\n", 207 | " t2RectInt.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))\n", 208 | "\n", 209 | "\n", 210 | " # Get mask by filling triangle\n", 211 | " mask = np.zeros((r2[3], r2[2], 3), dtype = np.float32)\n", 212 | " cv2.fillConvexPoly(mask, np.int32(t2RectInt), (1.0, 1.0, 1.0), 16, 0);\n", 213 | "\n", 214 | " # Apply warpImage to small rectangular patches\n", 215 | " img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]\n", 216 | " \n", 217 | " size = (r2[2], r2[3])\n", 218 | "\n", 219 | " img2Rect = applyAffineTransform(img1Rect, t1Rect, t2Rect, size)\n", 220 | " \n", 221 | " img2Rect = img2Rect * mask\n", 222 | "\n", 223 | " # Copy triangular region of the rectangular patch to the output image\n", 224 | " 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 )\n", 225 | " \n", 226 | " 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\n", 227 | "\n", 228 | "\n" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": null, 234 | "metadata": { 235 | "collapsed": true, 236 | "scrolled": true 237 | }, 238 | "outputs": [], 239 | "source": [ 240 | "'''\n", 241 | "1.读入图 + 读入关键点信息 readPoints readImages \n", 242 | "2.平均脸的眼角位置(这样其他脸,按照眼睛位置对齐) eyecornerDst\n", 243 | "3.新的8个初始边界点 boundaryPts (为了后续做脸谱网络用的)\n", 244 | "4.设置初始平均脸 pointsAvg (随便找个脸68个关键点 + 8个初始点)\n", 245 | "5.根据眼睛位置,进行人脸初步对齐\n", 246 | "6.计算初始平均脸的脸谱网络76点(calculateDelaunayTriangles)\n", 247 | "7.根据脸谱网络二次人脸对齐\n", 248 | "\n", 249 | "'''\n", 250 | "# 1.读入图 + 读入关键点信息\n", 251 | "path = 'presidents/'\n", 252 | "\n", 253 | "# Dimensions of output image\n", 254 | "w = 600;\n", 255 | "h = 600;\n", 256 | "\n", 257 | "# Read points for all images\n", 258 | "allPoints = readPoints(path);\n", 259 | "allPoints\n", 260 | "\n", 261 | "# Read all images\n", 262 | "images = readImages(path);\n", 263 | "images\n", 264 | "\n", 265 | "# 2.平均脸的眼角位置(这样其他脸,按照眼睛位置对齐)\n", 266 | "# 对齐的眼角预先设定好\n", 267 | "# 确保两只眼睛的点都在一个水平线上,面部中心大约在离顶端三分之一高度的位置。所以我将眼角位置设为(0.3*宽,高/3)和(0.7*宽,高/3)。\n", 268 | "eyecornerDst = [ (np.int(0.3 * w ), np.int(h / 3)), (np.int(0.7 * w ), np.int(h / 3)) ];\n", 269 | "\n", 270 | "imagesNorm = [];\n", 271 | "pointsNorm = [];\n", 272 | "\n", 273 | "# 3.新的8个初始边界点 boundaryPts\n", 274 | "# Add boundary points for delaunay triangulation 根据图像长宽、设定8个初始边界点\n", 275 | "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) ]); \n", 276 | "\n", 277 | "# 4. 初始平均脸\n", 278 | "# Initialize location of average points to 0s\n", 279 | "pointsAvg = np.array([(0,0)]* ( len(allPoints[0]) + len(boundaryPts) ), np.float32()); # 初始化平均脸\n", 280 | "\n", 281 | "n = len(allPoints[0]);\n", 282 | "\n", 283 | "numImages = len(images)\n", 284 | "\n", 285 | "\n", 286 | "# 5.根据眼睛位置,进行人脸初步对齐\n", 287 | "# Warp images and trasnform landmarks to output coordinate system,\n", 288 | "# and find average of transformed landmarks.\n", 289 | "\n", 290 | "for i in range(0, numImages):\n", 291 | "\n", 292 | " points1 = allPoints[i];\n", 293 | "\n", 294 | " # Corners of the eye in input image\n", 295 | " eyecornerSrc = [ allPoints[i][36], allPoints[i][45] ] ;\n", 296 | "\n", 297 | " # Compute similarity transform 眼角对齐转换矩阵\n", 298 | " tform = similarityTransform(eyecornerSrc, eyecornerDst);  # 2 * 3 转换矩阵\n", 299 | "\n", 300 | " # Apply similarity transformation 通过眼角对齐转换矩阵 进行人脸对齐\n", 301 | " img = cv2.warpAffine(images[i], tform, (w,h));\n", 302 | "\n", 303 | " # Apply similarity transform on points\n", 304 | " points2 = np.reshape(np.array(points1), (68,1,2)); # (68, 1, 2) \n", 305 | "\n", 306 | " points = cv2.transform(points2, tform); # (68, 1, 2) * (2,3) --> (68, 1, 2)\n", 307 | "\n", 308 | " points = np.float32(np.reshape(points, (68, 2))); # (68, 2)\n", 309 | "\n", 310 | " # Append boundary points. Will be used in Delaunay Triangulation\n", 311 | " points = np.append(points, boundaryPts, axis=0) # point(68个) + boundaryPts (8个边界点) (76, 2)\n", 312 | "\n", 313 | " # Calculate location of average landmark points.\n", 314 | " pointsAvg = pointsAvg + points / numImages; # (76, 2) 初始平均脸\n", 315 | "\n", 316 | " pointsNorm.append(points); # 6*72*2\n", 317 | " imagesNorm.append(img); \n", 318 | "\n", 319 | "\n", 320 | "# 6.计算初始平均脸的脸谱网络76点(calculateDelaunayTriangles)\n", 321 | "# Delaunay triangulation\n", 322 | "rect = (0, 0, w, h);\n", 323 | "dt = calculateDelaunayTriangles(rect, np.array(pointsAvg)); # delanauy三角计算 142*3\n", 324 | "\n", 325 | "# 7 根据脸谱网络二次人脸对齐\n", 326 | "# Output image\n", 327 | "output = np.zeros((h,w,3), np.float32());\n", 328 | "\n", 329 | "# Warp input images to average image landmarks\n", 330 | "for i in range(0, len(imagesNorm)) :\n", 331 | " img = np.zeros((h,w,3), np.float32());\n", 332 | " # Transform triangles one by one\n", 333 | " for j in range(0, len(dt)) :\n", 334 | " tin = []; \n", 335 | " tout = [];\n", 336 | "\n", 337 | " for k in range(0, 3) : \n", 338 | " pIn = pointsNorm[i][dt[j][k]]; #需要对齐的脸的脸谱网络计算\n", 339 | " pIn = constrainPoint(pIn, w, h); # 约束wh,如果有越界的,要归到图像内\n", 340 | "\n", 341 | " pOut = pointsAvg[dt[j][k]]; # 平均脸的脸谱网络计算\n", 342 | " pOut = constrainPoint(pOut, w, h);\n", 343 | "\n", 344 | " tin.append(pIn); \n", 345 | " tout.append(pOut);\n", 346 | "\n", 347 | " warpTriangle(imagesNorm[i], img, tin, tout); # 把需要对齐的脸谱网络,向平均脸脸谱网络 转换\n", 348 | "\n", 349 | "\n", 350 | " # Add image intensities for averaging\n", 351 | " output = output + img;\n", 352 | "\n", 353 | "# Divide by numImages to get average\n", 354 | "output = output / numImages;\n", 355 | "\n", 356 | "# Save result\n", 357 | "cv2.imwrite('myaverageface.png', (output * 255).astype('uint8'))\n" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": null, 363 | "metadata": { 364 | "collapsed": true 365 | }, 366 | "outputs": [], 367 | "source": [] 368 | } 369 | ], 370 | "metadata": { 371 | "kernelspec": { 372 | "display_name": "Python 3", 373 | "language": "python", 374 | "name": "python3" 375 | }, 376 | "language_info": { 377 | "codemirror_mode": { 378 | "name": "ipython", 379 | "version": 3 380 | }, 381 | "file_extension": ".py", 382 | "mimetype": "text/x-python", 383 | "name": "python", 384 | "nbconvert_exporter": "python", 385 | "pygments_lexer": "ipython3", 386 | "version": "3.5.2" 387 | } 388 | }, 389 | "nbformat": 4, 390 | "nbformat_minor": 2 391 | } 392 | -------------------------------------------------------------------------------- /平均脸/README.md: -------------------------------------------------------------------------------- 1 | # 平均脸 2 | 新更新了一个平均脸的程序内容: 3 | ![这里写图片描述](https://www.learnopencv.com/wp-content/uploads/2016/05/average_best_actress-300x300.jpg) 4 | 5 | py代码以及相关数据地址:https://www.learnopencv.com/wp-content/uploads/2016/05/FaceAverage.zip 6 | 最初博文地址:https://www.learnopencv.com/average-face-opencv-c-python-tutorial/ 7 | 中文翻译:http://blog.csdn.net/GraceDD/article/details/51382952 8 | 中文改编地址:[《手把手:用OpenCV亲手给小扎、Musk等科技大佬们做一张“平均脸”(附Python代码)》](https://mp.weixin.qq.com/s?__biz=MjM5MTQzNzU2NA==&mid=2651654758&idx=1&sn=b60e2da0b4e9cffed660f44bd624eb9e&chksm=bd4c2df58a3ba4e32f938df33cdc780bd7041087c6f3b82cf059c036a50c12e97067a8d12815&mpshare=1&scene=1&srcid=1123eFDjNTtDFdq4GS8M2e8d#rd) 9 | 10 | 在完成各个library的安装后。 11 | 12 | - 第一步:将要平均的照片放入faces文档,确保图片为jpg格式。 13 | - 第二步:在终端运行 python face_landmark_detection.py 14 | shape_predictor_68_face_landmarks.dat 15 | faces,并在程序运行结束后将所有faces文档中的文件复制到presidents文档中(如无法完成dlib安装,可略过该步骤,直接用文摘菌提供的素材) 16 | - 第三步:在终端运行 python faceAverage.py 这样就能看到制作成功的平均脸了! 17 | 18 | 具体实现步骤: 19 | 20 | - 1.读入图 + 读入关键点信息 readPoints readImages 21 | - 2.平均脸的眼角位置(这样其他脸,按照眼睛位置对齐) eyecornerDst 22 | - 3.新的8个初始边界点 boundaryPts (为了后续做脸谱网络用的) 23 | - 4.设置初始平均脸 pointsAvg (随便找个脸68个关键点 + 8个初始点) 24 | - 5.根据眼睛位置,进行人脸初步对齐 25 | - 6.计算初始平均脸的脸谱网络76点(calculateDelaunayTriangles) 26 | - 7.根据脸谱网络二次人脸对齐 27 | ![这里写图片描述](https://www.learnopencv.com/wp-content/uploads/2016/05/image-warping-based-on-delaunay-triangulation-768x256.jpg) 28 | 本案例中进行了两次对齐,眼睛对齐之后,通过Warp Triangles 再此对齐。 29 | -------------------------------------------------------------------------------- /平均脸/faceAverage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt 3 | # 4 | # This example program shows how to find frontal human faces in an image and 5 | # estimate their pose. The pose takes the form of 68 landmarks. These are 6 | # points on the face such as the corners of the mouth, along the eyebrows, on 7 | # the eyes, and so forth. 8 | # 9 | # The face detector we use is made using the classic Histogram of Oriented 10 | # Gradients (HOG) feature combined with a linear classifier, an image pyramid, 11 | # and sliding window detection scheme. The pose estimator was created by 12 | # using dlib's implementation of the paper: 13 | # One Millisecond Face Alignment with an Ensemble of Regression Trees by 14 | # Vahid Kazemi and Josephine Sullivan, CVPR 2014 15 | # and was trained on the iBUG 300-W face landmark dataset (see 16 | # https://ibug.doc.ic.ac.uk/resources/facial-point-annotations/): 17 | # C. Sagonas, E. Antonakos, G, Tzimiropoulos, S. Zafeiriou, M. Pantic. 18 | # 300 faces In-the-wild challenge: Database and results. 19 | # Image and Vision Computing (IMAVIS), Special Issue on Facial Landmark Localisation "In-The-Wild". 2016. 20 | # You can get the trained model file from: 21 | # http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2. 22 | # Note that the license for the iBUG 300-W dataset excludes commercial use. 23 | # So you should contact Imperial College London to find out if it's OK for 24 | # you to use this model file in a commercial product. 25 | # 26 | # 27 | # Also, note that you can train your own models using dlib's machine learning 28 | # tools. See train_shape_predictor.py to see an example. 29 | # 30 | # 31 | # COMPILING/INSTALLING THE DLIB PYTHON INTERFACE 32 | # You can install dlib using the command: 33 | # pip install dlib 34 | # 35 | # Alternatively, if you want to compile dlib yourself then go into the dlib 36 | # root folder and run: 37 | # python setup.py install 38 | # or 39 | # python setup.py install --yes USE_AVX_INSTRUCTIONS 40 | # if you have a CPU that supports AVX instructions, since this makes some 41 | # things run faster. 42 | # 43 | # Compiling dlib should work on any operating system so long as you have 44 | # CMake and boost-python installed. On Ubuntu, this can be done easily by 45 | # running the command: 46 | # sudo apt-get install libboost-python-dev cmake 47 | # 48 | # Also note that this example requires scikit-image which can be installed 49 | # via the command: 50 | # pip install scikit-image 51 | # Or downloaded from http://scikit-image.org/download.html. 52 | 53 | import sys 54 | import os 55 | import dlib 56 | import glob 57 | import numpy as np 58 | from skimage import io 59 | 60 | def shape_to_np(shape): 61 | # initialize the list of (x, y)-coordinates 62 | coords = np.zeros((68, 2), dtype = int) 63 | 64 | # loop over the 68 facial landmarks and convert them 65 | # to a 2-tuple of (x, y)-coordinates 66 | for i in range(0, 68): 67 | coords[i] = (int(shape.part(i).x), int(shape.part(i).y)) 68 | 69 | # return the list of (x, y)-coordinates 70 | return coords 71 | 72 | if len(sys.argv) != 3: 73 | print( 74 | "Give the path to the trained shape predictor model as the first " 75 | "argument and then the directory containing the facial images.\n" 76 | "For example, if you are in the python_examples folder then " 77 | "execute this program by running:\n" 78 | "python face_landmark_detection.py shape_predictor_68_face_landmarks.dat faces\n" 79 | "You can download a trained facial shape predictor from:\n" 80 | " http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2") 81 | exit() 82 | 83 | predictor_path = sys.argv[1] 84 | faces_folder_path = sys.argv[2] 85 | 86 | detector = dlib.get_frontal_face_detector() 87 | predictor = dlib.shape_predictor(predictor_path) 88 | 89 | for f in glob.glob(os.path.join(faces_folder_path, "*.jpg")): 90 | print("Processing file: {}".format(f)) 91 | img = io.imread(f) 92 | 93 | 94 | # Ask the detector to find the bounding boxes of each face. The 1 in the 95 | # second argument indicates that we should upsample the image 1 time. This 96 | # will make everything bigger and allow us to detect more faces. 97 | dets = detector(img, 1) 98 | 99 | #print("Number of faces detected: {}".format(len(dets))) 100 | for k, d in enumerate(dets): 101 | # # Get the landmarks/parts for the face in box d. 102 | shape = predictor(img, d) 103 | shape_np = shape_to_np(shape) 104 | np.savetxt(f + '.txt', shape_np, fmt = '%i') 105 | -------------------------------------------------------------------------------- /平均脸/face_landmark_detection.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2016 Satya Mallick 4 | # All rights reserved. No warranty, explicit or implicit, provided. 5 | 6 | 7 | import os 8 | import cv2 9 | import numpy as np 10 | import math 11 | import sys 12 | 13 | # Read points from text files in directory 14 | def readPoints(path) : 15 | # Create an array of array of points. 16 | pointsArray = []; 17 | 18 | #List all files in the directory and read points from text files one by one 19 | for filePath in os.listdir(path): 20 | 21 | if filePath.endswith(".txt"): 22 | 23 | #Create an array of points. 24 | points = []; 25 | 26 | # Read points from filePath 27 | with open(os.path.join(path, filePath)) as file : 28 | for line in file : 29 | x, y = line.split() 30 | points.append((int(x), int(y))) 31 | 32 | # Store array of points 33 | pointsArray.append(points) 34 | 35 | return pointsArray; 36 | 37 | # Read all jpg images in folder. 38 | def readImages(path) : 39 | 40 | #Create array of array of images. 41 | imagesArray = []; 42 | 43 | #List all files in the directory and read points from text files one by one 44 | for filePath in os.listdir(path): 45 | if filePath.endswith(".jpg"): 46 | # Read image found. 47 | img = cv2.imread(os.path.join(path,filePath)); 48 | 49 | # Convert to floating point 50 | img = np.float32(img)/255.0; 51 | 52 | # Add to array of images 53 | imagesArray.append(img); 54 | 55 | return imagesArray; 56 | 57 | # Compute similarity transform given two sets of two points. 58 | # OpenCV requires 3 pairs of corresponding points. 59 | # We are faking the third one. 60 | 61 | def similarityTransform(inPoints, outPoints) : 62 | s60 = math.sin(60*math.pi/180); 63 | c60 = math.cos(60*math.pi/180); 64 | 65 | inPts = np.copy(inPoints).tolist(); 66 | outPts = np.copy(outPoints).tolist(); 67 | 68 | xin = c60*(inPts[0][0] - inPts[1][0]) - s60*(inPts[0][1] - inPts[1][1]) + inPts[1][0]; 69 | yin = s60*(inPts[0][0] - inPts[1][0]) + c60*(inPts[0][1] - inPts[1][1]) + inPts[1][1]; 70 | 71 | inPts.append([np.int(xin), np.int(yin)]); 72 | 73 | xout = c60*(outPts[0][0] - outPts[1][0]) - s60*(outPts[0][1] - outPts[1][1]) + outPts[1][0]; 74 | yout = s60*(outPts[0][0] - outPts[1][0]) + c60*(outPts[0][1] - outPts[1][1]) + outPts[1][1]; 75 | 76 | outPts.append([np.int(xout), np.int(yout)]); 77 | 78 | tform = cv2.estimateRigidTransform(np.array([inPts]), np.array([outPts]), False); 79 | 80 | return tform; 81 | 82 | 83 | # Check if a point is inside a rectangle 84 | def rectContains(rect, point) : 85 | if point[0] < rect[0] : 86 | return False 87 | elif point[1] < rect[1] : 88 | return False 89 | elif point[0] > rect[2] : 90 | return False 91 | elif point[1] > rect[3] : 92 | return False 93 | return True 94 | 95 | # Calculate delanauy triangle 96 | def calculateDelaunayTriangles(rect, points): 97 | # Create subdiv 98 | subdiv = cv2.Subdiv2D(rect); 99 | 100 | # Insert points into subdiv 101 | for p in points: 102 | subdiv.insert((p[0], p[1])); 103 | 104 | 105 | # List of triangles. Each triangle is a list of 3 points ( 6 numbers ) 106 | triangleList = subdiv.getTriangleList(); 107 | 108 | # Find the indices of triangles in the points array 109 | 110 | delaunayTri = [] 111 | 112 | for t in triangleList: 113 | pt = [] 114 | pt.append((t[0], t[1])) 115 | pt.append((t[2], t[3])) 116 | pt.append((t[4], t[5])) 117 | 118 | pt1 = (t[0], t[1]) 119 | pt2 = (t[2], t[3]) 120 | pt3 = (t[4], t[5]) 121 | 122 | if rectContains(rect, pt1) and rectContains(rect, pt2) and rectContains(rect, pt3): 123 | ind = [] 124 | for j in xrange(0, 3): 125 | for k in xrange(0, len(points)): 126 | if(abs(pt[j][0] - points[k][0]) < 1.0 and abs(pt[j][1] - points[k][1]) < 1.0): 127 | ind.append(k) 128 | if len(ind) == 3: 129 | delaunayTri.append((ind[0], ind[1], ind[2])) 130 | 131 | 132 | 133 | return delaunayTri 134 | 135 | 136 | def constrainPoint(p, w, h) : 137 | p = ( min( max( p[0], 0 ) , w - 1 ) , min( max( p[1], 0 ) , h - 1 ) ) 138 | return p; 139 | 140 | # Apply affine transform calculated using srcTri and dstTri to src and 141 | # output an image of size. 142 | def applyAffineTransform(src, srcTri, dstTri, size) : 143 | 144 | # Given a pair of triangles, find the affine transform. 145 | warpMat = cv2.getAffineTransform( np.float32(srcTri), np.float32(dstTri) ) 146 | 147 | # Apply the Affine Transform just found to the src image 148 | dst = cv2.warpAffine( src, warpMat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 ) 149 | 150 | return dst 151 | 152 | 153 | # Warps and alpha blends triangular regions from img1 and img2 to img 154 | def warpTriangle(img1, img2, t1, t2) : 155 | 156 | # Find bounding rectangle for each triangle 157 | r1 = cv2.boundingRect(np.float32([t1])) 158 | r2 = cv2.boundingRect(np.float32([t2])) 159 | 160 | # Offset points by left top corner of the respective rectangles 161 | t1Rect = [] 162 | t2Rect = [] 163 | t2RectInt = [] 164 | 165 | for i in xrange(0, 3): 166 | t1Rect.append(((t1[i][0] - r1[0]),(t1[i][1] - r1[1]))) 167 | t2Rect.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1]))) 168 | t2RectInt.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1]))) 169 | 170 | 171 | # Get mask by filling triangle 172 | mask = np.zeros((r2[3], r2[2], 3), dtype = np.float32) 173 | cv2.fillConvexPoly(mask, np.int32(t2RectInt), (1.0, 1.0, 1.0), 16, 0); 174 | 175 | # Apply warpImage to small rectangular patches 176 | img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]] 177 | 178 | size = (r2[2], r2[3]) 179 | 180 | img2Rect = applyAffineTransform(img1Rect, t1Rect, t2Rect, size) 181 | 182 | img2Rect = img2Rect * mask 183 | 184 | # Copy triangular region of the rectangular patch to the output image 185 | 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 ) 186 | 187 | 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 188 | 189 | 190 | 191 | if __name__ == '__main__' : 192 | 193 | path = 'presidents/' 194 | 195 | # Dimensions of output image 196 | w = 600; 197 | h = 600; 198 | 199 | # Read points for all images 200 | allPoints = readPoints(path); 201 | 202 | # Read all images 203 | images = readImages(path); 204 | 205 | # Eye corners 206 | eyecornerDst = [ (np.int(0.3 * w ), np.int(h / 3)), (np.int(0.7 * w ), np.int(h / 3)) ]; 207 | 208 | imagesNorm = []; 209 | pointsNorm = []; 210 | 211 | # Add boundary points for delaunay triangulation 212 | 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) ]); 213 | 214 | # Initialize location of average points to 0s 215 | pointsAvg = np.array([(0,0)]* ( len(allPoints[0]) + len(boundaryPts) ), np.float32()); 216 | 217 | n = len(allPoints[0]); 218 | 219 | numImages = len(images) 220 | 221 | # Warp images and trasnform landmarks to output coordinate system, 222 | # and find average of transformed landmarks. 223 | 224 | for i in xrange(0, numImages): 225 | 226 | points1 = allPoints[i]; 227 | 228 | # Corners of the eye in input image 229 | eyecornerSrc = [ allPoints[i][36], allPoints[i][45] ] ; 230 | 231 | # Compute similarity transform 232 | tform = similarityTransform(eyecornerSrc, eyecornerDst); 233 | 234 | # Apply similarity transformation 235 | img = cv2.warpAffine(images[i], tform, (w,h)); 236 | 237 | # Apply similarity transform on points 238 | points2 = np.reshape(np.array(points1), (68,1,2)); 239 | 240 | points = cv2.transform(points2, tform); 241 | 242 | points = np.float32(np.reshape(points, (68, 2))); 243 | 244 | # Append boundary points. Will be used in Delaunay Triangulation 245 | points = np.append(points, boundaryPts, axis=0) 246 | 247 | # Calculate location of average landmark points. 248 | pointsAvg = pointsAvg + points / numImages; 249 | 250 | pointsNorm.append(points); 251 | imagesNorm.append(img); 252 | 253 | 254 | 255 | # Delaunay triangulation 256 | rect = (0, 0, w, h); 257 | dt = calculateDelaunayTriangles(rect, np.array(pointsAvg)); 258 | 259 | # Output image 260 | output = np.zeros((h,w,3), np.float32()); 261 | 262 | # Warp input images to average image landmarks 263 | for i in xrange(0, len(imagesNorm)) : 264 | img = np.zeros((h,w,3), np.float32()); 265 | # Transform triangles one by one 266 | for j in xrange(0, len(dt)) : 267 | tin = []; 268 | tout = []; 269 | 270 | for k in xrange(0, 3) : 271 | pIn = pointsNorm[i][dt[j][k]]; 272 | pIn = constrainPoint(pIn, w, h); 273 | 274 | pOut = pointsAvg[dt[j][k]]; 275 | pOut = constrainPoint(pOut, w, h); 276 | 277 | tin.append(pIn); 278 | tout.append(pOut); 279 | 280 | 281 | warpTriangle(imagesNorm[i], img, tin, tout); 282 | 283 | 284 | # Add image intensities for averaging 285 | output = output + img; 286 | 287 | 288 | # Divide by numImages to get average 289 | output = output / numImages; 290 | 291 | # Save result 292 | cv2.imwrite('myaverageface.png', (output * 255).astype('uint8')) 293 | -------------------------------------------------------------------------------- /平均脸/总统/barak-obama.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/总统/barak-obama.jpg -------------------------------------------------------------------------------- /平均脸/总统/barak-obama.jpg.txt: -------------------------------------------------------------------------------- 1 | 361 578 2 | 366 666 3 | 372 754 4 | 389 842 5 | 423 925 6 | 475 998 7 | 537 1061 8 | 607 1110 9 | 688 1122 10 | 774 1103 11 | 847 1045 12 | 912 977 13 | 966 901 14 | 995 820 15 | 1009 732 16 | 1011 645 17 | 1012 558 18 | 429 538 19 | 466 489 20 | 526 470 21 | 590 472 22 | 650 491 23 | 737 487 24 | 795 467 25 | 858 465 26 | 917 486 27 | 952 533 28 | 693 540 29 | 693 596 30 | 693 651 31 | 693 710 32 | 609 742 33 | 650 750 34 | 693 761 35 | 734 748 36 | 773 737 37 | 504 558 38 | 537 541 39 | 572 539 40 | 607 557 41 | 572 560 42 | 538 562 43 | 778 555 44 | 815 535 45 | 850 537 46 | 886 556 47 | 850 559 48 | 814 557 49 | 526 846 50 | 577 819 51 | 643 811 52 | 692 819 53 | 739 809 54 | 800 814 55 | 851 834 56 | 802 900 57 | 742 935 58 | 692 942 59 | 641 937 60 | 577 906 61 | 539 848 62 | 644 832 63 | 693 838 64 | 740 830 65 | 836 838 66 | 740 892 67 | 691 901 68 | 642 896 69 | -------------------------------------------------------------------------------- /平均脸/总统/bill-clinton.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/总统/bill-clinton.jpg -------------------------------------------------------------------------------- /平均脸/总统/bill-clinton.jpg.txt: -------------------------------------------------------------------------------- 1 | 330 660 2 | 340 724 3 | 350 785 4 | 365 847 5 | 391 905 6 | 431 957 7 | 478 1002 8 | 526 1044 9 | 583 1054 10 | 643 1040 11 | 706 996 12 | 772 950 13 | 824 895 14 | 858 829 15 | 868 758 16 | 867 684 17 | 869 605 18 | 350 608 19 | 370 576 20 | 408 562 21 | 451 561 22 | 493 572 23 | 582 564 24 | 627 545 25 | 674 539 26 | 722 547 27 | 761 571 28 | 539 627 29 | 540 674 30 | 540 721 31 | 541 769 32 | 502 797 33 | 526 804 34 | 552 809 35 | 579 798 36 | 605 789 37 | 407 642 38 | 429 626 39 | 460 623 40 | 486 640 41 | 460 648 42 | 430 650 43 | 620 630 44 | 644 610 45 | 674 607 46 | 703 622 47 | 678 632 48 | 648 633 49 | 479 868 50 | 505 853 51 | 534 846 52 | 560 851 53 | 591 842 54 | 630 842 55 | 669 853 56 | 635 893 57 | 598 912 58 | 566 917 59 | 537 915 60 | 507 902 61 | 492 866 62 | 536 858 63 | 562 860 64 | 593 855 65 | 656 853 66 | 595 886 67 | 563 891 68 | 535 888 69 | -------------------------------------------------------------------------------- /平均脸/总统/george-h-bush.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/总统/george-h-bush.jpg -------------------------------------------------------------------------------- /平均脸/总统/george-h-bush.jpg.txt: -------------------------------------------------------------------------------- 1 | 398 556 2 | 406 618 3 | 415 681 4 | 423 741 5 | 439 801 6 | 474 852 7 | 524 892 8 | 586 921 9 | 656 925 10 | 727 909 11 | 792 875 12 | 847 831 13 | 886 775 14 | 901 708 15 | 902 638 16 | 900 569 17 | 900 501 18 | 430 503 19 | 446 460 20 | 485 436 21 | 532 429 22 | 575 442 23 | 656 439 24 | 697 417 25 | 745 414 26 | 790 428 27 | 821 460 28 | 618 486 29 | 621 529 30 | 623 572 31 | 626 617 32 | 585 648 33 | 609 655 34 | 635 661 35 | 661 648 36 | 686 635 37 | 490 514 38 | 513 495 39 | 540 491 40 | 567 507 41 | 542 513 42 | 515 516 43 | 689 494 44 | 715 471 45 | 743 467 46 | 768 483 47 | 746 488 48 | 718 492 49 | 531 733 50 | 568 718 51 | 609 711 52 | 642 713 53 | 676 704 54 | 722 700 55 | 766 709 56 | 728 758 57 | 685 778 58 | 650 784 59 | 614 785 60 | 571 775 61 | 542 733 62 | 610 724 63 | 644 724 64 | 678 717 65 | 757 712 66 | 681 758 67 | 646 765 68 | 612 765 69 | -------------------------------------------------------------------------------- /平均脸/总统/george-w-bush.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/总统/george-w-bush.jpg -------------------------------------------------------------------------------- /平均脸/总统/george-w-bush.jpg.txt: -------------------------------------------------------------------------------- 1 | 391 566 2 | 399 635 3 | 411 703 4 | 424 770 5 | 451 834 6 | 493 887 7 | 543 935 8 | 604 974 9 | 674 983 10 | 745 969 11 | 804 923 12 | 853 870 13 | 890 809 14 | 907 738 15 | 910 664 16 | 909 595 17 | 906 526 18 | 429 534 19 | 451 489 20 | 496 468 21 | 548 466 22 | 597 481 23 | 677 473 24 | 724 449 25 | 776 441 26 | 826 456 27 | 857 495 28 | 638 517 29 | 639 563 30 | 641 608 31 | 642 656 32 | 592 680 33 | 619 691 34 | 650 700 35 | 681 687 36 | 709 673 37 | 494 542 38 | 517 524 39 | 547 521 40 | 575 537 41 | 547 543 42 | 519 545 43 | 705 526 44 | 730 503 45 | 759 501 46 | 786 515 47 | 762 522 48 | 733 525 49 | 554 775 50 | 589 760 51 | 623 754 52 | 656 757 53 | 688 750 54 | 732 749 55 | 778 755 56 | 739 793 57 | 699 809 58 | 664 813 59 | 630 812 60 | 593 800 61 | 571 774 62 | 625 766 63 | 658 767 64 | 690 762 65 | 764 758 66 | 694 784 67 | 661 788 68 | 628 786 69 | -------------------------------------------------------------------------------- /平均脸/总统/jimmy-carter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/总统/jimmy-carter.jpg -------------------------------------------------------------------------------- /平均脸/总统/jimmy-carter.jpg.txt: -------------------------------------------------------------------------------- 1 | 271 525 2 | 269 590 3 | 269 655 4 | 274 721 5 | 287 787 6 | 313 843 7 | 355 890 8 | 402 924 9 | 467 938 10 | 538 933 11 | 612 911 12 | 683 879 13 | 740 831 14 | 779 772 15 | 798 703 16 | 810 632 17 | 821 557 18 | 293 470 19 | 319 438 20 | 362 426 21 | 410 430 22 | 455 444 23 | 509 450 24 | 569 438 25 | 630 435 26 | 688 452 27 | 726 491 28 | 473 490 29 | 464 528 30 | 454 565 31 | 445 606 32 | 406 648 33 | 429 656 34 | 455 664 35 | 489 656 36 | 523 650 37 | 336 497 38 | 362 478 39 | 394 478 40 | 422 504 41 | 391 507 42 | 359 505 43 | 562 509 44 | 593 485 45 | 627 486 46 | 658 505 47 | 627 514 48 | 593 513 49 | 366 746 50 | 394 728 51 | 431 719 52 | 460 725 53 | 492 716 54 | 544 727 55 | 596 750 56 | 542 780 57 | 493 791 58 | 459 793 59 | 428 791 60 | 393 777 61 | 381 747 62 | 431 743 63 | 460 744 64 | 492 740 65 | 581 749 66 | 493 768 67 | 460 771 68 | 429 768 69 | -------------------------------------------------------------------------------- /平均脸/总统/ronald-regan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/总统/ronald-regan.jpg -------------------------------------------------------------------------------- /平均脸/总统/ronald-regan.jpg.txt: -------------------------------------------------------------------------------- 1 | 367 536 2 | 367 602 3 | 372 667 4 | 379 734 5 | 395 799 6 | 424 857 7 | 464 906 8 | 512 945 9 | 572 961 10 | 642 952 11 | 713 919 12 | 778 873 13 | 828 816 14 | 859 754 15 | 871 688 16 | 876 623 17 | 881 555 18 | 402 487 19 | 428 453 20 | 473 447 21 | 518 459 22 | 562 477 23 | 621 486 24 | 667 463 25 | 718 454 26 | 770 462 27 | 803 498 28 | 587 514 29 | 583 554 30 | 579 595 31 | 574 639 32 | 530 667 33 | 552 676 34 | 577 685 35 | 604 678 36 | 632 672 37 | 446 522 38 | 470 511 39 | 496 512 40 | 522 526 41 | 494 531 42 | 468 529 43 | 665 532 44 | 691 520 45 | 719 520 46 | 745 533 47 | 718 538 48 | 691 538 49 | 476 756 50 | 511 744 51 | 548 739 52 | 578 745 53 | 607 741 54 | 646 749 55 | 692 764 56 | 645 799 57 | 603 814 58 | 572 816 59 | 539 811 60 | 506 790 61 | 488 758 62 | 547 755 63 | 576 760 64 | 606 758 65 | 680 764 66 | 604 787 67 | 574 790 68 | 543 784 69 | -------------------------------------------------------------------------------- /平均脸/脸们/barak-obama.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/脸们/barak-obama.jpg -------------------------------------------------------------------------------- /平均脸/脸们/barak-obama.jpg.txt: -------------------------------------------------------------------------------- 1 | 358 574 2 | 365 662 3 | 377 749 4 | 396 834 5 | 424 917 6 | 472 995 7 | 532 1063 8 | 604 1113 9 | 687 1125 10 | 772 1105 11 | 844 1048 12 | 907 977 13 | 957 902 14 | 983 824 15 | 998 739 16 | 1006 655 17 | 1010 572 18 | 427 537 19 | 465 491 20 | 523 470 21 | 585 470 22 | 645 487 23 | 741 484 24 | 799 464 25 | 862 463 26 | 920 487 27 | 954 533 28 | 691 540 29 | 693 595 30 | 693 650 31 | 695 710 32 | 609 742 33 | 649 751 34 | 691 762 35 | 732 749 36 | 773 738 37 | 497 561 38 | 532 543 39 | 570 540 40 | 607 560 41 | 570 563 42 | 533 566 43 | 779 557 44 | 816 537 45 | 854 539 46 | 889 558 47 | 854 562 48 | 816 560 49 | 528 839 50 | 579 819 51 | 642 812 52 | 692 819 53 | 738 810 54 | 798 813 55 | 851 832 56 | 802 900 57 | 741 935 58 | 693 942 59 | 642 938 60 | 579 908 61 | 542 843 62 | 643 833 63 | 692 838 64 | 739 830 65 | 836 837 66 | 740 895 67 | 692 904 68 | 642 897 69 | -------------------------------------------------------------------------------- /平均脸/脸们/bill-clinton.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/脸们/bill-clinton.jpg -------------------------------------------------------------------------------- /平均脸/脸们/bill-clinton.jpg.txt: -------------------------------------------------------------------------------- 1 | 81 359 2 | 81 364 3 | 82 370 4 | 85 376 5 | 87 381 6 | 91 385 7 | 94 389 8 | 100 392 9 | 105 392 10 | 112 391 11 | 119 388 12 | 125 384 13 | 129 378 14 | 132 371 15 | 133 364 16 | 132 356 17 | 131 348 18 | 81 351 19 | 82 349 20 | 84 348 21 | 87 348 22 | 90 349 23 | 97 346 24 | 101 344 25 | 106 342 26 | 111 342 27 | 116 344 28 | 93 353 29 | 93 357 30 | 93 362 31 | 93 366 32 | 91 370 33 | 93 371 34 | 95 371 35 | 97 370 36 | 100 369 37 | 84 355 38 | 86 353 39 | 89 353 40 | 91 354 41 | 89 355 42 | 86 356 43 | 102 351 44 | 104 349 45 | 108 349 46 | 111 349 47 | 108 351 48 | 105 352 49 | 96 382 50 | 95 379 51 | 95 377 52 | 97 377 53 | 98 377 54 | 102 377 55 | 106 378 56 | 103 379 57 | 101 379 58 | 99 380 59 | 98 380 60 | 97 380 61 | 96 381 62 | 96 380 63 | 97 379 64 | 99 379 65 | 105 378 66 | 100 376 67 | 98 376 68 | 97 376 69 | -------------------------------------------------------------------------------- /平均脸/脸们/george-h-bush.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/脸们/george-h-bush.jpg -------------------------------------------------------------------------------- /平均脸/脸们/george-h-bush.jpg.txt: -------------------------------------------------------------------------------- 1 | 395 551 2 | 404 613 3 | 412 673 4 | 419 734 5 | 434 794 6 | 467 846 7 | 518 890 8 | 579 921 9 | 651 925 10 | 725 910 11 | 793 872 12 | 850 825 13 | 888 767 14 | 903 699 15 | 904 629 16 | 903 561 17 | 900 492 18 | 428 502 19 | 444 459 20 | 485 435 21 | 532 429 22 | 577 442 23 | 651 439 24 | 692 417 25 | 741 414 26 | 787 426 27 | 819 456 28 | 617 486 29 | 620 529 30 | 622 573 31 | 625 619 32 | 585 647 33 | 608 656 34 | 634 662 35 | 661 648 36 | 685 634 37 | 492 513 38 | 515 495 39 | 541 491 40 | 569 505 41 | 543 511 42 | 516 514 43 | 689 492 44 | 715 472 45 | 742 467 46 | 768 482 47 | 745 489 48 | 717 492 49 | 531 732 50 | 568 719 51 | 609 712 52 | 642 714 53 | 674 704 54 | 719 700 55 | 767 707 56 | 726 753 57 | 682 773 58 | 649 779 59 | 614 780 60 | 571 771 61 | 541 733 62 | 611 724 63 | 644 724 64 | 676 718 65 | 757 712 66 | 678 754 67 | 645 761 68 | 612 760 69 | -------------------------------------------------------------------------------- /平均脸/脸们/george-w-bush.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/脸们/george-w-bush.jpg -------------------------------------------------------------------------------- /平均脸/脸们/george-w-bush.jpg.txt: -------------------------------------------------------------------------------- 1 | 389 567 2 | 398 636 3 | 411 704 4 | 424 770 5 | 451 833 6 | 492 885 7 | 543 932 8 | 600 969 9 | 671 978 10 | 741 964 11 | 801 922 12 | 851 871 13 | 888 811 14 | 906 743 15 | 910 670 16 | 909 601 17 | 908 532 18 | 431 535 19 | 452 491 20 | 495 467 21 | 545 462 22 | 595 470 23 | 679 463 24 | 727 445 25 | 778 440 26 | 825 456 27 | 855 494 28 | 638 514 29 | 640 561 30 | 641 607 31 | 643 656 32 | 592 681 33 | 619 690 34 | 650 698 35 | 681 686 36 | 709 674 37 | 494 541 38 | 517 524 39 | 546 520 40 | 574 534 41 | 548 541 42 | 519 545 43 | 704 525 44 | 729 503 45 | 757 501 46 | 784 513 47 | 760 521 48 | 732 523 49 | 553 774 50 | 589 760 51 | 624 753 52 | 656 757 53 | 687 749 54 | 730 750 55 | 775 756 56 | 736 793 57 | 697 807 58 | 663 812 59 | 631 810 60 | 593 798 61 | 570 774 62 | 626 766 63 | 657 767 64 | 689 761 65 | 761 759 66 | 693 783 67 | 660 788 68 | 629 785 69 | -------------------------------------------------------------------------------- /平均脸/脸们/jimmy-carter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/脸们/jimmy-carter.jpg -------------------------------------------------------------------------------- /平均脸/脸们/jimmy-carter.jpg.txt: -------------------------------------------------------------------------------- 1 | 274 507 2 | 272 573 3 | 271 638 4 | 273 705 5 | 284 775 6 | 308 836 7 | 351 887 8 | 400 924 9 | 464 940 10 | 539 935 11 | 617 910 12 | 689 873 13 | 745 822 14 | 779 759 15 | 795 691 16 | 808 621 17 | 817 546 18 | 294 465 19 | 318 434 20 | 360 428 21 | 405 434 22 | 448 448 23 | 505 454 24 | 561 442 25 | 619 437 26 | 676 451 27 | 717 488 28 | 471 491 29 | 463 530 30 | 453 570 31 | 443 610 32 | 407 648 33 | 428 657 34 | 455 664 35 | 487 656 36 | 522 649 37 | 338 496 38 | 363 477 39 | 395 477 40 | 423 503 41 | 391 508 42 | 360 507 43 | 560 508 44 | 590 485 45 | 625 486 46 | 656 505 47 | 625 515 48 | 590 514 49 | 371 748 50 | 395 727 51 | 430 718 52 | 461 723 53 | 493 715 54 | 543 724 55 | 596 750 56 | 543 785 57 | 495 797 58 | 461 800 59 | 427 797 60 | 395 780 61 | 385 747 62 | 430 743 63 | 461 746 64 | 494 741 65 | 580 748 66 | 495 769 67 | 462 772 68 | 429 767 69 | -------------------------------------------------------------------------------- /平均脸/脸们/ronald-regan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattzheng/Face_Swapping/11628a2e35a0a449b956dff26a1da2859c9ced47/平均脸/脸们/ronald-regan.jpg -------------------------------------------------------------------------------- /平均脸/脸们/ronald-regan.jpg.txt: -------------------------------------------------------------------------------- 1 | 369 523 2 | 368 588 3 | 373 652 4 | 379 719 5 | 393 785 6 | 422 844 7 | 464 898 8 | 515 944 9 | 577 960 10 | 646 949 11 | 716 911 12 | 785 868 13 | 837 811 14 | 868 747 15 | 879 681 16 | 885 619 17 | 892 554 18 | 399 496 19 | 423 458 20 | 468 450 21 | 517 460 22 | 563 478 23 | 620 485 24 | 672 463 25 | 724 455 26 | 777 462 27 | 810 499 28 | 589 515 29 | 586 558 30 | 582 600 31 | 577 645 32 | 530 666 33 | 554 677 34 | 579 688 35 | 608 680 36 | 635 672 37 | 449 519 38 | 473 509 39 | 499 510 40 | 525 525 41 | 497 528 42 | 471 526 43 | 666 530 44 | 695 519 45 | 721 520 46 | 746 531 47 | 721 535 48 | 695 535 49 | 475 756 50 | 511 744 51 | 548 741 52 | 574 749 53 | 607 744 54 | 648 750 55 | 697 763 56 | 647 796 57 | 604 811 58 | 570 813 59 | 539 808 60 | 506 789 61 | 489 758 62 | 546 756 63 | 574 761 64 | 606 758 65 | 683 764 66 | 605 784 67 | 572 788 68 | 544 783 69 | --------------------------------------------------------------------------------