需要裝python庫
OpenCV
dlib
docopt(根據打開方式選擇是否裝)
# -*- coding: UTF-8 #本電腦試運行 命令 python F:\python_project\swap\swapface.py E:\picture\face\xx.jpg E:\picture\face\angelababy.jpg #聲明編碼格式 方便中文注釋~ #實現命令行解析器 打開格式python 代碼文件路徑 圖片1路徑 圖片2路徑 """ swapface.py function:put one face's facial features onto another based on OpenCV,dlib this moudle used docopt Usage: faceswap [options] <image1> [<image2>] Options: -v --version 查看版本號 -h --help 顯示這個幫助文檔 -s --see 查看圖片 -c --combine 換臉 """ #命令行參數解析說明 [options]指定特定選項 完成特定任務 import cv2 #導入調用opencv庫 import dlib #導入dlib庫 import numpy as np #以np別名導入docopt from docopt import docopt #導入docopt包中的docopt到文件 __version__ = 'swapface 1.0' #版本號 #以下是一些要用的常量及路徑初始化部分 PREDICTOR_PATH = "F:/python_project/shape_predictor_68_face_landmarks.dat" #訓練模式的路徑 注意斜杠別反了 #圖像放縮因子 SCALE_FACTOR = 1 FEATHER_AMOUNT = 11 FACE_POINTS = list(range(17, 68)) #臉 MOUTH_POINTS = list(range(48, 61)) #嘴 RIGHT_BROW_POINTS = list(range(17, 22)) #右眉 LEFT_BROW_POINTS = list(range(22, 27)) #左眉 RIGHT_EYE_POINTS = list(range(36, 42)) #右眼 LEFT_EYE_POINTS = list(range(42, 48)) #左眼 NOSE_POINTS = list(range(27, 35)) #鼻 JAW_POINTS = list(range(0, 17)) #下巴 #元組 選取左右眉,左右眼,鼻子,嘴巴 位置的特征點索引 ALIGN_POINTS = (LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS + RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS) #元組轉列表 選取第二張臉對應面部特征用於覆蓋到第一張臉 OVERLAY_POINTS = [ LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS, NOSE_POINTS + MOUTH_POINTS, ] #定義用於顏色矯正的模糊量,作為瞳孔距離的系數 COLOUR_CORRECT_BLUR_FRAC = 0.6 #實例化臉部檢測器 detector = dlib.get_frontal_face_detector() #根據路徑加載訓練模型 並 實例化特征提取器 predictor = dlib.shape_predictor(PREDICTOR_PATH) #定義多臉和無臉類處理異常 以及 未打開異常 class TooManyFaces(Exception): pass class NoFaces(Exception): pass class OpenFailed(Exception): pass #顯示圖片 def show_image(board,path,time): # 讀取這個圖片 image = cv2.imread(path,cv2.IMREAD_COLOR) # 初始化一個名為Combine的窗口 cv2.namedWindow(board, flags=0) # 顯示圖片 cv2.imshow(board, image) # 等待按鍵釋放窗口 if(time == 0): cv2.waitKey(time) #獲取特征點函數 def get_landmarks(im): #特征檢測器 rects = detector(im, 1) #如果檢測到多張臉 拋多臉異常 if len(rects) > 1: raise TooManyFaces #如果沒有檢測到臉 拋無臉異常 if len(rects) == 0: raise NoFaces #返回一個n*2維的矩陣,該矩陣由檢測到的臉部特征點坐標組成 特征提取器predictor return np.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()]) #繪制凸多邊形 def draw_convex_hull(im, points, color): points = points.astype(np.int32) #檢查一個曲線是否有凸性缺陷並糾正 points = cv2.convexHull(points) #凸多邊形填充成想要的顏色 cv2.fillConvexPoly(im, points, color=color) #獲取面部的掩碼 def get_face_mask(im, landmarks): im = np.zeros(im.shape[:2], dtype=np.float64) for group in OVERLAY_POINTS: draw_convex_hull(im, landmarks[group], color=1) im = np.array([im, im, im]).transpose((1,2,0)) #應用高斯模糊 im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0 im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) return im #使用普氏分析(Procrustes analysis)調整臉部 包括傾斜位置不同等等都可以調整 def transformation_from_points(points1,points2): #輸入矩陣轉換為64位float類型 points1 = points1.astype(np.float64) points2 = points2.astype(np.float64) #對矩陣格列取均值,求矩心 c1 = np.mean(points1,axis=0) c2 = np.mean(points2,axis=0) #每一個點集減去它的矩心。這兩個矩心c1和c2可以用來找到完整的解決方案。 points1 -= c1 points2 -= c2 #標准差 s1 = np.std(points1) s2 = np.std(points2) #除去標准差 這消除了問題的組件縮放偏差。 points1 /= s1 points2 /= s2 U, S, Vt = np.linalg.svd(points1.T * points2) R = (U * Vt).T #公式 return np.vstack([np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T)), np.matrix([0., 0., 1.])]) #讀取圖片文件並獲取特征點 def read_im_and_landmarks(fname): #以RGB(紅綠藍)模式讀取圖片 就是讀入彩色圖片 im = cv2.imread(fname,cv2.IMREAD_COLOR) if im is None: raise OpenFailed #對圖片進行適當的放縮 im = cv2.resize(im, (im.shape[1] * SCALE_FACTOR, im.shape[0] * SCALE_FACTOR)) #獲取特征點 s = get_landmarks(im) #返回圖片和特征點組成的元組 return im, s #變換圖像 def warp_im(im,M,dshape): output_im = np.zeros(dshape,dtype=im.dtype) #仿射函數,能對圖像進行幾何變換 三個參數 輸入圖像 變換矩陣np.float32類型 變換之后圖像 cv2.warpAffine(im, M[:2], (dshape[1],dshape[0]), dst=output_im, borderMode=cv2.BORDER_TRANSPARENT, flags=cv2.WARP_INVERSE_MAP) return output_im #修正顏色 使得兩張圖片拼接時顯得更加自然 def correct_colours(im1,im2,landmarks1): #傳入圖1和圖2以及圖1的特征點 blur_amount = COLOUR_CORRECT_BLUR_FRAC * np.linalg.norm( np.mean(landmarks1[LEFT_EYE_POINTS],axis=0) - np.mean(landmarks1[RIGHT_EYE_POINTS],axis=0)) blur_amount = int(blur_amount) if blur_amount % 2 == 0: blur_amount += 1 #高斯模糊 im1_blur = cv2.GaussianBlur(im1,(blur_amount,blur_amount),0) im2_blur = cv2.GaussianBlur(im2,(blur_amount,blur_amount),0) #避免下面出現0除 im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype) return (im2.astype(np.float64) * im1_blur.astype(np.float64) / im2_blur.astype(np.float64)) #主過程函數 def main(): arguments = docopt(__doc__, version=__version__) #調用函數返回arguments字典型變量,記錄了選項是否被選用了,參數的值是什么等信息,當程序從命令行運行時,我們就是根據arguments變量的記錄來得知用戶輸入的選項和參數信息。 if(arguments['--see'] != False): if(arguments['<image1>'] is not None): show_image('image1',arguments['<image1>'],1) if(arguments['<image2>'] is not None): show_image('image2',arguments['<image2>'],1) if(arguments['--combine'] == False): exit(0) # 1.獲取圖像與特征點 im1,landmarks1 = read_im_and_landmarks(arguments['<image1>']) im2,landmarks2 = read_im_and_landmarks(arguments['<image2>']) # 2.選取兩組圖像特征矩陣中所需要的面部部位 計算轉換信息 返回變換矩陣 M = transformation_from_points(landmarks1[ALIGN_POINTS],landmarks2[ALIGN_POINTS]) # 3.獲取im2的面部掩碼 mask = get_face_mask(im2,landmarks2) # 4.將im2的掩碼進行變化,使得與im1相符 warped_mask = warp_im(mask,M,im1.shape) # 5.將二者的掩碼進行連通 combined_mask = np.max([get_face_mask(im1,landmarks1),warped_mask],axis=0) # 6.將第二幅圖調整到與第一幅圖相符 warped_im2 = warp_im(im2,M,im1.shape) # 7.將im2的皮膚顏色進行修正,使其和im1的顏色盡量協調 warped_corrected_im2 = correct_colours(im1,warped_im2,landmarks1) # 組合圖像,獲得結果 output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 * combined_mask SavePath = 'F:/python_project/output.jpg' #保存圖像 默認項目文件夾 cv2.imwrite(SavePath,output_im) show_image('Combine',SavePath,0) if __name__=='__main__': main()
命令行里運行 根據幫助文檔 python 程序路徑 -c 圖片1路徑 圖片2路徑
