作者:派森酱 来源:Python技术 随着圣诞节的到来,节日气氛也越来越浓厚。大街上随处可见挂满饰品的圣诞树,好多小伙伴的头上也多了一顶红色牛角的圣诞帽。 往年在这个时候,好多P图软件会推出给头像加一顶圣诞帽的功能,甚至有一年大伙微信官方就可以在自己的微信头像上加一顶圣诞帽。 作为一个学习Python的技术人,自己可以写程序实现这个功能,做成一个软件当然是一件很酷的事情了。 今天就给大家分享一下如何用Python制作一款自动给头像添加圣诞帽的软件。 如果不想看实现,可以直接跳到文末获取软件。思路 我们的头像添加圣诞帽软件制作的大致思路如下:要实现一个软件,我们需要制作一个GUI界面,供用户操作。要实现头像戴圣诞帽功能,我们需要用户上传一张头像,我们还需要准备一顶圣诞帽子图片。要把圣诞帽戴在头像上,我们需要识别头像中的人脸和头部特征,然后将帽子放在头顶合成一张图片。 基于以上思路,我们制作这款软件的关键词有:GUI界面圣诞帽图片头像图片人脸识别打包软件实现 看了以上思路,相信大家脑海中已经对这个软件制作的过程有了一个大致的框架了。我们的实现主要分为:图像制作、GUI界面、打包三大块内容。准备工作 首先列举一下本次软件制作过程中需要用的的一些包模块:cv2 pipinstallopencvpythonos python系统模块dlib pipinstalldlibnumpy pipinstallpandasPySimpleGUI pipinstallPySimpleGui 温馨提示:这其中安装dlib会遇到很多坑以及很多困难,一般需要一边安装一边上网搜索报错,从而找到解决办法。保证安装过一次之后不想尝试第二次。图像制作准备圣诞帽 我们需要准备一个圣诞帽的图片,格式最好为png,因为png图片我们可以直接用Alpha通道作为掩膜使用。如果是jpg图片,需要先转换成png格式图片。注意这里的转换不是只改个后缀名,那样是行不通的。 我们用到的圣诞帽如下图: 为了能够与RGB通道的头像图片进行运算,我们需要把圣诞帽图像分离成RGB通道图像和alpha通道图像:r,g,b,acv2。split(hatimg)rgbhatcv2。merge((r,g,b))cv2。imwrite(hatalpha。jpg,a) 分离之后,得到的alpha通道图像如下所示: 人脸识别 我从我的百宝箱中选择了一张傻萌傻萌的图片作为程序的测试图片。 大家注意,因为我们要做人脸识别,然后自动添加帽子,所以选择的图片一定要是真人的正面照片,不然识别不了人脸,也就不知道在哪添加圣诞帽。 下面我们用dlib的正脸检测器进行人脸检测,用dlib提供的模型提取人脸的五个关键点。代码如下:dlib人脸关键点检测器predictorpathshapepredictor5facelandmarks。datpredictordlib。shapepredictor(predictorpath)dlib正脸检测器detectordlib。getfrontalfacedetector()正脸检测detsdetector(img,1)如果检测到人脸iflen(dets)0:fordindets:x,y,w,hd。left(),d。top(),d。right()d。left(),d。bottom()d。top()x,y,w,hfaceRectcv2。rectangle(img,(x,y),(xw,yh),(255,0,0),2,8,0)关键点检测,5个关键点shapepredictor(img,d)forpointinshape。parts():cv2。circle(img,(point。x,point。y),3,color(0,255,0))cv2。imshow(image,img)cv2。waitKey() 我们把图片打印出来的效果是这样的: 看到这个图片是不是有点熟悉,网上好多人脸识别的图片都是这样的。调整帽子大小 我们选取两个眼角的点,求中心作为放置帽子的x方向的参考坐标,y方向的坐标用人脸框上线的y坐标表示。 然后我们根据人脸检测得到的人脸的大小调整帽子的大小,使得帽子大小合适。 看到这里,你应该明白,我们头像的图片中人的脸越正面那么我们制作出来的效果越好。选取左右眼眼角的点point1shape。part(0)point2shape。part(2)求两点中心eyescenter((point1。xpoint2。x)2,(point1。ypoint2。y)2)根据人脸大小调整帽子大小factor1。5resizedhathint(round(rgbhat。shape〔0〕wrgbhat。shape〔1〕factor))resizedhatwint(round(rgbhat。shape〔1〕wrgbhat。shape〔1〕factor))ifresizedhathy:resizedhathy1根据人脸大小调整帽子大小resizedhatcv2。resize(rgbhat,(resizedhatw,resizedhath))帽子区域处理 我们先将帽子的alpha通道作为mask掩膜:maskcv2。resize(a,(resizedhatw,resizedhath))maskinvcv2。bitwisenot(mask) 接着,从人像图中去除需要添加帽子的区域:帽子相对与人脸框上线的偏移量dh0dw0原图ROIbgroiimg〔ydhresizedhath:ydh,xdw:xdwresizedhatw〕bgroiimg〔ydhresizedhath:ydh,(eyescenter〔0〕resizedhatw3):(eyescenter〔0〕resizedhatw32)〕原图ROI中提取放帽子的区域bgroibgroi。astype(float)maskinvcv2。merge((maskinv,maskinv,maskinv))alphamaskinv。astype(float)255相乘之前保证两者大小一致(可能会由于四舍五入原因不一致)alphacv2。resize(alpha,(bgroi。shape〔1〕,bgroi。shape〔0〕))print(alphasize:,alpha。shape)print(bgroisize:,bgroi。shape)bgcv2。multiply(alpha,bgroi)bgbg。astype(uint8) 提取后的效果图如下: 接下来,我们提取圣诞帽的区域:hatcv2。bitwiseand(resizedhat,resizedhat,maskmask) 提取后的效果图如下: 盖帽 图像处理的最后一步是盖帽了,就是把提取的圣诞帽区域和图片中提取的区域相加,然后再放到原图中去。这里需要注意的就是,相加之前resize一下保证两者大小一致相加之前保证两者大小一致(可能会由于四舍五入原因不一致)hatcv2。resize(hat,(bgroi。shape〔1〕,bgroi。shape〔0〕))两个ROI区域相加addhatcv2。add(bg,hat)cv2。imshow(addhat,addhat)把添加好帽子的区域放回原图img〔ydhresizedhath:ydh,(eyescenter〔0〕resizedhatw3):(eyescenter〔0〕resizedhatw32)〕addhat 最后,我们得到的效果图如下: GUI界面 我们先来看效果图: 然后再来看这部分的实现代码:importPySimpleGUIassgimportos。pathimportcv2filelistcolumn〔〔sg。Submit(生成,keyGo,size(15,1)),sg。Cancel(退出,keyCancel,size(15,1))〕,〔sg。Text(图片位置(选择文件夹)),sg。In(size(25,1),enableeventsTrue,keyFOLDER),sg。FolderBrowse(浏览),〕,〔sg。Listbox(values〔〕,enableeventsTrue,size(40,20),keyFILELIST)〕〕imageviewercolumn〔〔sg。Text(从左边图片列表中选择一张图片:)〕,〔sg。Image(keyIMAGE)〕〕layout〔〔sg。Column(filelistcolumn),sg。VSeperator(),sg。Column(imageviewercolumn),〕〕windowsg。Window(人像添加圣诞帽软件,layout)filenamewhileTrue:event,valueswindow。read()ifeventCanceloreventsg。WINCLOSED:breakifeventFOLDER:foldervalues〔FOLDER〕try:filelistos。listdir(folder)except:filelist〔〕fnames〔fforfinfilelistifos。path。isfile(os。path。join(folder,f))andf。lower()。endswith((。jpg,。png))〕window〔FILELIST〕。update(fnames)elifeventFILELIST:try:filenameos。path。join(values〔FOLDER〕,values〔FILELIST〕〔0〕)iffilename。endswith(。jpg):imcv2。imread(filename)cv2。imwrite(filename。replace(jpg,png),im)window〔IMAGE〕。update(filenamefilename。replace(jpg,png))exceptExceptionase:print(e)elifeventGo:try:outputaddhat(filename)展示效果cv2。imshow(output,output)cv2。waitKey(0)cv2。imwrite(output。png,output)print(output)window〔IMAGE〕。update(filenameoutput。png)except:print(OMG!添加失败了!)cv2。destroyAllWindows() 这里我选用的是PySimpleGUI框架来做的,比较简单。界面分为左右两部分,左边是两个按钮(确定和取消)加一个文件夹选择器,再加一个图片文件列表;右边是一个图片展示框。 左边选择文件夹后,会在下方列出文件夹里包含。png和。jpg的图片列表。点击图片列表中的图片,会在右边显示你所选择的图片。这个选中的图片也就是我们后面需要添加圣诞帽的图片。 这里需要注意的是,PySimpleGUI的图片展示默认只支持png格式的,所以我在展示的时候做了判断,如果是jpg格式的图片,我就用cv2将其转换成png格式,然后再进行展示。 到这里,我们的关键步骤就完成了。接下来就是将我们两部分代码进行整合。其实也很简单,只需要在GUI界面上用户点击生成按钮时,后台接收到图片的路径,传递给我们的图片处理函数,在处理完后将图片保存在文件夹下,并更新GUI界面右边的展示的图片即可。 最终的运行效果: 打包软件 打包软件我们还是用熟悉的pyinstaller模块,将代码打包成可执行的exe格式。 首先下载我们所需的模块包: pipinstallpyinstaller 接着在命令行敲下打包命令: pyinstallerchristmashat。py 这个christmashat。py就是我们所写的程序了。 打包比较耗时,耐心等着就行。打包完成后,在我们代码的目录下会生成三个文件夹:pycachebuilddist 我们只需要关注dist就行。dist文件夹下面是christmashat子文件夹,再进去就可以找到我们的christmashat。exe文件了。由于我们的程序运行有两个依赖文件,分别是我们的圣诞帽图片和我们的人脸识别训练集,所以我们需要将这两个文件放入这个EXE文件所在的文件夹下。 现在双击christmashat。exe文件就可以正常运行了。总结 本文从一个实际需求出发,向大家讲解了一个头像添加圣诞帽软件的诞生过程。相对于之前的一些小应用来说,涉及的知识点较多,可能还是有点复杂的。其中有一些知识点限于篇幅原因没有详细讲解,大家可以自己私下补充。