├── README.md ├── heben.jpg ├── huge.jpg ├── swapface.py ├── ted_face.py └── yangmi.jpg /README.md: -------------------------------------------------------------------------------- 1 | # face 2 | 注意,需要下载shape_predictor_68_face_landmarks.dat放于代码文件夹内才能执行,dat下载链接:https://github.com/AKSHAYUBHAT/TensorFace/blob/master/openface/models/dlib/shape_predictor_68_face_landmarks.dat 3 | 4 | Python换脸,将两张图片中人物脸部的眼睛和嘴巴通过矩形截取互换 5 | -------------------------------------------------------------------------------- /heben.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pengfexue2/face/cb9549fb447bbd084757bffecb6b7bdf8a0d9a43/heben.jpg -------------------------------------------------------------------------------- /huge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pengfexue2/face/cb9549fb447bbd084757bffecb6b7bdf8a0d9a43/huge.jpg -------------------------------------------------------------------------------- /swapface.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import dlib 3 | import numpy as np 4 | from PIL import Image 5 | 6 | class TooManyFaces(Exception): 7 | pass 8 | 9 | class NoFaces(Exception): 10 | pass 11 | 12 | def get_landmarks(im): 13 | rects = detector(im, 1) 14 | if len(rects) > 1: 15 | raise TooManyFaces 16 | if len(rects) == 0: 17 | raise NoFaces 18 | return np.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()]) 19 | 20 | def transformation_from_points(points1, points2): 21 | 22 | points1 = points1.astype(np.float64) 23 | points2 = points2.astype(np.float64) 24 | 25 | c1 = np.mean(points1, axis=0) 26 | c2 = np.mean(points2, axis=0) 27 | points1 -= c1 28 | points2 -= c2 29 | 30 | s1 = np.std(points1) 31 | s2 = np.std(points2) 32 | points1 /= s1 33 | points2 /= s2 34 | 35 | U, S, Vt = np.linalg.svd(points1.T * points2) 36 | 37 | R = (U * Vt).T 38 | 39 | return np.vstack([np.hstack(((s2 / s1) * R,c2.T - (s2 / s1) * R * c1.T)),np.matrix([0., 0., 1.])]) 40 | 41 | 42 | def get_eye(img_src,record): 43 | img = cv2.imread(f"{img_src}.jpg") 44 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 45 | 46 | detector = dlib.get_frontal_face_detector() 47 | predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") 48 | 49 | faces = detector(gray) 50 | for face in faces: 51 | landmarks = predictor(gray, face) 52 | face_matrix = np.matrix([[p.x, p.y] for p in landmarks.parts()]) 53 | record[img_src] = face_matrix 54 | 55 | def draw_convex_hull(im, points, color): 56 | points = cv2.convexHull(points) 57 | cv2.fillConvexPoly(im, points, color=color) 58 | 59 | def get_face_mask(im, landmarks): 60 | im = np.zeros(im.shape[:2], dtype=np.float64) 61 | 62 | for group in OVERLAY_POINTS: 63 | draw_convex_hull(im, 64 | landmarks[group], 65 | color=1) 66 | 67 | im = np.array([im, im, im]).transpose((1, 2, 0)) 68 | 69 | im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0 70 | im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) 71 | 72 | return im 73 | 74 | def warp_im(im, M, dshape): 75 | output_im = np.zeros(dshape, dtype=im.dtype) 76 | cv2.warpAffine(im, 77 | M[:2], 78 | (dshape[1], dshape[0]), 79 | dst=output_im, 80 | borderMode=cv2.BORDER_TRANSPARENT, 81 | flags=cv2.WARP_INVERSE_MAP) 82 | return output_im 83 | def correct_colours(im1, im2, landmarks1): 84 | blur_amount = COLOUR_CORRECT_BLUR_FRAC * np.linalg.norm( 85 | np.mean(landmarks1[LEFT_EYE_POINTS], axis=0) - 86 | np.mean(landmarks1[RIGHT_EYE_POINTS], axis=0)) 87 | blur_amount = int(blur_amount) 88 | if blur_amount % 2 == 0: 89 | blur_amount += 1 90 | im1_blur = cv2.GaussianBlur(im1, (blur_amount, blur_amount), 0) 91 | im2_blur = cv2.GaussianBlur(im2, (blur_amount, blur_amount), 0) 92 | 93 | # Avoid divide-by-zero errors. 94 | im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype) 95 | 96 | return (im2.astype(np.float64) * im1_blur.astype(np.float64) / 97 | im2_blur.astype(np.float64)) 98 | 99 | def exchange(p1,p2,record): 100 | im1 = Image.open(f"{p1}.jpg") 101 | im1_eye = Image.open(f"{p2}_eye_resized.jpg") 102 | im1.paste(im1_eye,(record[p1][0],record[p1][1])) 103 | im1.save(f"{p1}_result.jpg") 104 | 105 | im2 = Image.open(f"{p2}.jpg") 106 | im2_eye = Image.open(f"{p1}_eye_resized.jpg") 107 | im2.paste(im2_eye,(record[p2][0],record[p2][1])) 108 | im2.save(f"{p2}_result.jpg") 109 | 110 | def process(src1,src2,record): 111 | get_eye(src1, record) 112 | get_eye(src2, record) 113 | 114 | M = transformation_from_points(record[src1][ALIGN_POINTS], 115 | record[src2][ALIGN_POINTS]) 116 | im1 = cv2.imread(f"{src1}.jpg") 117 | im2 = cv2.imread(f"{src2}.jpg") 118 | mask = get_face_mask(im2, record[src2]) 119 | warped_mask = warp_im(mask, M, im1.shape) 120 | combined_mask = np.max([get_face_mask(im1, record[src1]), warped_mask], 121 | axis=0) 122 | warped_im2 = warp_im(im2, M, im1.shape) 123 | warped_corrected_im2 = correct_colours(im1, warped_im2, record[src1]) 124 | 125 | output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 * combined_mask 126 | 127 | cv2.imwrite(f"{src1}_result.jpg", output_im) 128 | 129 | PREDICTOR_PATH = "shape_predictor_68_face_landmarks.dat" 130 | SCALE_FACTOR = 1 131 | FEATHER_AMOUNT = 11 132 | 133 | FACE_POINTS = list(range(17, 68)) 134 | MOUTH_POINTS = list(range(48, 61)) 135 | RIGHT_BROW_POINTS = list(range(17, 22)) 136 | LEFT_BROW_POINTS = list(range(22, 27)) 137 | RIGHT_EYE_POINTS = list(range(36, 42)) 138 | LEFT_EYE_POINTS = list(range(42, 48)) 139 | NOSE_POINTS = list(range(27, 35)) 140 | JAW_POINTS = list(range(0, 17)) 141 | 142 | # Points used to line up the images. 143 | ALIGN_POINTS = (LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS + 144 | RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS) 145 | 146 | # Points from the second image to overlay on the first. The convex hull of each 147 | # element will be overlaid. 148 | OVERLAY_POINTS = [ 149 | LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS, 150 | NOSE_POINTS + MOUTH_POINTS, 151 | ] 152 | 153 | # Amount of blur to use during colour correction, as a fraction of the 154 | # pupillary distance. 155 | COLOUR_CORRECT_BLUR_FRAC = 0.6 156 | detector = dlib.get_frontal_face_detector() 157 | predictor = dlib.shape_predictor(PREDICTOR_PATH) 158 | 159 | record ={} 160 | 161 | #修改此处命名“图片1名称”“图片2名称”后运行即可 162 | src1,src2 = "huge","yangmi" 163 | process(src1,src2,record) 164 | process(src2,src1,record) -------------------------------------------------------------------------------- /ted_face.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import dlib 3 | import numpy as np 4 | from PIL import Image 5 | 6 | 7 | 8 | def get_eye(img_src,record, record1,record2): 9 | 10 | img = cv2.imread(f"{img_src}.jpg") 11 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 12 | 13 | detector = dlib.get_frontal_face_detector() 14 | predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") 15 | 16 | faces = detector(gray) 17 | 18 | for face in faces: 19 | landmarks = predictor(gray, face) 20 | 21 | #右眼睛区域左上角点(x,y),右下角点(x0,y0) 22 | x, y, x0, y0 = landmarks.part(42).x - 5, min(landmarks.part(43).y, landmarks.part(44).y) - 5, landmarks.part(45).x + 5, max(landmarks.part(46).y, landmarks.part(47).y) + 5 23 | #左眼睛区域左上角点(x1,y1),右下角点(x2,y2) 24 | x1,y1,x2,y2 = landmarks.part(36).x-5,min(landmarks.part(37).y,landmarks.part(38).y)-5,landmarks.part(39).x+5,max(landmarks.part(40).y,landmarks.part(41).y)+5 25 | 26 | # 嘴巴区域左上角点(x3,y3),右下角点(x4,y4) 27 | x3, y3, x4, y4 = landmarks.part(48).x-5, min(landmarks.part(50).y,landmarks.part(52).y)-5, landmarks.part(54).x+5,landmarks.part(57).y +5 28 | 29 | cut = img[y:y0,x:x0] 30 | cut1 = img[y1:y2,x1:x2] 31 | cut2 = img[y3:y4,x3:x4] 32 | 33 | record[img_src] = (x, y, x0, y0) 34 | record1[img_src] = (x1,y1,x2,y2) 35 | record2[img_src] = (x3,y3,x4,y4) 36 | 37 | return cut,cut1,cut2 38 | 39 | def changeface(p1,p2): 40 | record={} 41 | record1={} 42 | record2={} 43 | 44 | eye_right, eye_left, mouth1 = get_eye(p1,record,record1,record2) 45 | eye_right2, eye_left2, mouth2 = get_eye(p2,record,record1,record2) 46 | 47 | eye_right_resized = cv2.resize(eye_right,(record[p2][2]-record[p2][0],record[p2][3]-record[p2][1]),interpolation=cv2.INTER_AREA) 48 | eye_left_resized = cv2.resize(eye_left, (record1[p2][2] - record1[p2][0], record1[p2][3] - record1[p2][1]),interpolation=cv2.INTER_AREA) 49 | 50 | eye_right2_resized = cv2.resize(eye_right2,(record[p1][2]-record[p1][0],record[p1][3]-record[p1][1]),interpolation=cv2.INTER_AREA) 51 | eye_left2_resized = cv2.resize(eye_left2, (record1[p1][2] - record1[p1][0], record1[p1][3] - record1[p1][1]),interpolation=cv2.INTER_AREA) 52 | 53 | mouth1_resized = cv2.resize(mouth1,(record2[p2][2]-record2[p2][0],record2[p2][3]-record2[p2][1]),interpolation=cv2.INTER_AREA) 54 | mouth2_resized = cv2.resize(mouth2, (record2[p1][2] - record2[p1][0], record2[p1][3] - record2[p1][1]),interpolation=cv2.INTER_AREA) 55 | 56 | im = cv2.imread(f"{p1}.jpg") 57 | obj = eye_right2_resized 58 | # Create an all white mask 59 | mask = 255 * np.ones(obj.shape, obj.dtype) 60 | center = (int((record[p1][0] + record[p1][2]) / 2), int((record[p1][1] + record[p1][3]) / 2)) 61 | # Seamlessly clone src into dst and put the results in output 62 | normal_clone = cv2.seamlessClone(obj, im, mask, center, cv2.NORMAL_CLONE) 63 | 64 | im1 = normal_clone 65 | obj1 = eye_left2_resized 66 | # Create an all white mask 67 | mask1 = 255 * np.ones(obj1.shape, obj1.dtype) 68 | center1 = (int((record1[p1][0] + record1[p1][2]) / 2), int((record1[p1][1] + record1[p1][3]) / 2)) 69 | # Seamlessly clone src into dst and put the results in output 70 | normal_clone1 = cv2.seamlessClone(obj1, im1, mask1, center1, cv2.NORMAL_CLONE) 71 | 72 | im2 = normal_clone1 73 | obj2 = mouth2_resized 74 | mask2 = 255 * np.ones(obj2.shape, obj2.dtype) 75 | center2 = (int((record2[p1][0] + record2[p1][2]) / 2), int((record2[p1][1] + record2[p1][3]) / 2)) 76 | normal_clone2 = cv2.seamlessClone(obj2, im2, mask2, center2, cv2.NORMAL_CLONE) 77 | cv2.imwrite(f"{p1}_result.jpg", normal_clone2) 78 | 79 | 80 | # src1 = input("请输入第一张图片名称,如baby.jpg请输入baby,无需后缀:") 81 | # src2 = input("请输入第二张图片名称,如baby.jpg请输入baby,无需后缀:") 82 | 83 | src1,src2 = "huge","heben" 84 | 85 | #add src2's eyes and mouth to src1's face 86 | changeface(src1,src2) 87 | #add src2's eyes and mouth to src1's face 88 | changeface(src2,src1) 89 | -------------------------------------------------------------------------------- /yangmi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pengfexue2/face/cb9549fb447bbd084757bffecb6b7bdf8a0d9a43/yangmi.jpg --------------------------------------------------------------------------------