1说明: 1。1首先:刘德华是我的偶像,一直很喜欢他,向他致敬,无意冒犯,仅供学习。 1。2技术要点:pythonOpenCV(cv2)dlibnumpy,代码详细,简单通俗,小白秒懂。 1。3图片来源:今日头条免费正版图库。 偶像刘德华:ldh。jpeg 来自网络,假设是我:me。jpeg 换脸后的效果,小bug,可能与光线有关 2准备: 2。1参看文章来源:https:github。comMister5ivechangeFaceImg下载,提取shapepredictor68facelandmarks。dat上面github下载的代码多,弃用。https:blog。csdn。netuglyscarecrowarticledetails77449576代码来源,并对代码进行修改,注释和删减 2。2环境: python3。8OpenCV4。2。0dlib19。19微软编辑器vscode深度操作系统deepinlinux。 3代码分析: 3。1第1步:导入模块importcv2importdlibimportsysimportnumpyasnp 3。2第2步:参数设定SCALEFACTOR1FEATHERAMOUNT11代表各个区域的关键点标号FACEPOINTSlist(range(17,68))MOUTHPOINTSlist(range(48,61))RIGHTBROWPOINTSlist(range(17,22))LEFTBROWPOINTSlist(range(22,27))RIGHTEYEPOINTSlist(range(36,42))LEFTEYEPOINTSlist(range(42,48))NOSEPOINTSlist(range(27,35))JAWPOINTSlist(range(0,17))pupillarydistance。瞳孔距离COLOURCORRECTBLURFRAC0。6 3。3第3步:加载模型,注意路径。模型需要在dlib包或者上面提到的github中获取。PREDICTORPATHhomexgjDesktopchangefacemodelshapepredictor68facelandmarks。datdetectordlib。getfrontalfacedetector()predictordlib。shapepredictor(PREDICTORPATH) 3。4第4步:函数定义获取关键点坐标位置,只获取一张人脸input:代表一张图片的numpyarrayoutput:682的关键点坐标位置matrixdefgetlandmarks(im):rectsdetector(im,1)returnnp。matrix(〔〔p。x,p。y〕forpinpredictor(im,rects〔0〕)。parts()〕)defreadimandlandmarks(fname):imcv2。imread(fname,cv2。IMREADCOLOR)imcv2。resize(im,(im。shape〔1〕SCALEFACTOR,im。shape〔0〕SCALEFACTOR))sgetlandmarks(im)returnim,sdefdrawconvexhull(im,points,color):pointscv2。convexHull(points)检测凸包函数cv2。fillConvexPoly(im,points,colorcolor)绘制好多边形后并填充点的顺序不同绘制出来的凸包也不同defgetfacemask(im,landmarks):imnp。zeros(im。shape〔:2〕,dtypenp。float64)drawconvexhull(im,landmarks,color1)imnp。array(〔im,im,im〕)。transpose((1,2,0))得到一个类似于3通道的图片returnim用普氏分析(Procrustesanalysis)调整脸部deftransformationfrompoints(points1,points2):points1points1。astype(np。float64)points2points2。astype(np。float64)c1np。mean(points1,axis0)c2np。mean(points2,axis0)points1c1points2c2计算标准差s1np。std(points1)s2np。std(points2)points1s1points2s2通过奇异值分解求得旋转矩阵RU,S,Vtnp。linalg。svd(points1。Tpoints2)R(UVt)。T维度:22仿射变换矩阵33numpy。hstack用来在第1个维度上拼接tupnumpy。vstack在第0个维度上拼接tupreturnnp。vstack(〔np。hstack(((s2s1)R,c2。T(s2s1)Rc1。T)),np。matrix(〔0。,0。,1。〕)〕)defwarpim(im,M,dshape):outputimnp。zeros(dshape,dtypeim。dtype)cv2。warpAffine(im,M〔:2〕,(dshape〔1〕,dshape〔0〕),dstoutputim,borderModecv2。BORDERTRANSPARENT,flagscv2。WARPINVERSEMAP)returnoutputim颜色校正defcorrectcolours(im1,im2,landmarks1):bluramountCOLOURCORRECTBLURFRACnp。linalg。norm(np。mean(landmarks1〔LEFTEYEPOINTS〕,axis0)np。mean(landmarks1〔RIGHTEYEPOINTS〕,axis0))bluramountint(bluramount)ifbluramount20:bluramount1im1blurcv2。GaussianBlur(im1,(bluramount,bluramount),0)im2blurcv2。GaussianBlur(im2,(bluramount,bluramount),0)Avoidpidebyzeroerrors。im2blur(128(im2blur1。0))。astype(im2blur。dtype)return(im2。astype(np。float64)im1blur。astype(np。float64)im2blur。astype(np。float64)) 3。5第5步:加载图片和开始换脸im1,landmarks1readimandlandmarks(homexgjDesktopchangefaceme。jpeg)需要换脸:me的脸im2,landmarks2readimandlandmarks(homexgjDesktopchangefaceldh。jpeg)偶像的脸:ldh的脸换脸点Mtransformationfrompoints(landmarks1,landmarks2)getfacemask()的定义是为一张图像和一个标记矩阵生成一个掩膜maskgetfacemask(im2,landmarks2)warpedmaskwarpim(mask,M,im1。shape)33。用min函数取掩膜区域效果更好combinedmasknp。min(〔getfacemask(im1,landmarks1),warpedmask〕,axis0)将图像2的掩膜转换到图像1的坐标空间warpedim2warpim(im2,M,im1。shape)warpedcorrectedim2correctcolours(im1,warpedim2,landmarks1)outputimim1(1。0combinedmask)warpedcorrectedim2combinedmask保存换脸后的图片cv2。imwrite(homexgjDesktopchangefaceoutput。jpg,outputim)outputimoutputim。astype(np。uint8)展示图片cv2。imshow(outputface,outputim)cv2。waitKey() 4操作和效果: 华仔,爱你一万年。