织梦CMS - 轻松建站从此开始!

罗索

【换脸系列2】浪漫七夕♥和你的TA交♂换身体吧

jackyhwei 发布于 2020-03-05 19:10 点击:次 
项目地址: QuantumLiu/FaceSwapper 百度网盘 **** 还有三天就是七夕情人节了,你是不是正在规划怎么和TA度过一个浪漫的七夕节呢? 情书? 玫瑰、红酒、烛光晚餐? 买买买? 相信你的七夕已经安
TAG: 换脸  

项目地址:QuantumLiu/FaceSwapper

百度网盘

****

还有三天就是七夕情人节了,你是不是正在规划怎么和TA度过一个浪漫的七夕节呢?

情书?

玫瑰、红酒、烛光晚餐?

买买买?

相信你的七夕已经安排得很浪漫了~

这里还有一款因缺思厅的Python程序,可以为你的浪漫七夕增添一点幽默!

这个程序实现了一张照片内两个人脸的互换,照片可以来自你和TA的合影,也可以是和你的好朋友、老板、仇人,甚至任何你想恶搞的人~

(我说我一个单身狗,怎么想的做这个程序,强行喂自己狗粮,摔!)

 

 

 

 (本文使用图片,除特朗普和希拉里以外都来自pixabay别样网

使用

程序提供了GUI版,也提供了命令行版和接口。

最简单的,下载程序以后(coupleswapper.exe以及ico文件,txt文件和data文件夹),

双击程序,打开主界面。

或者通过源码:

  1. python coupleswapper.py 

 

点击加载图片,选择要打开的照片。

 

 

 

 

 程序会弹出Origin窗口预览。

 

 接下来点击转换,即可完成转换,会弹出result窗口预览。

 

 如果你想看一看对比反差并保存对比图,请点击保存对比,会弹出预览图片并让你选择保存位置。

如果只想保存转换后的结果,请点击保存结果。

祝各位玩的开心~

汪!

预告:编写本程序让单身狗作者受到了来自自己的1T点暴击,本系列下一作品,是单身狗七夕YY神器【换脸系列3】和女神/男神在一起的应该是我!

编程实现

在本系列上一篇【换脸系列1】军装照刷爆朋友圈?教你用Python+深度学习自制换脸软件!(改进)中,我们实现了将一张照片上的脸换到另一张照片的头上,并且用面向对象编程的方法定义了一个Faceswapper类。

 

这一次,我们是要在一张照片上,将两个脸互换。

显而易见,读取和写入图片、人脸定位和特征提取,这些功能我们都在Faceswapper里实现了,新的任务需要的操作是一样的,我们希望直接复用已有的操作。

使用类的继承是最常用的实现复用的方法。

我们新定义一个类Coupleswapper,他继承自Faceswapper类。

  1. class Coupleswapper(Faceswapper): 
  2.     ''' 
  3.     双人照人脸交换器类,继承自Faceswapper类 
  4.     实例化时载入多个照片资源 
  5.     ''' 

这样,新的Coupleswapper类继承了父类的所有属性和方法。

接下来,我们根据任务的不同,改写一些类。

在父类中的get_landmarks方法中,我们要求脸数只能为1,否则抛出异常,最后返回一个landmarks的martrix。

现在,我们做的是给一张图片的两个脸换脸,脸数应当>=2,而且最后一次返回一个有两组landmark的list。所以我们需要改写get_landmarks方法。

  1. def get_landmarks(self,im,fname,n=2): 
  2.     ''' 
  3.     人脸定位和特征提取,定位到两张及以上脸或者没有人脸将抛出异常 
  4.     im: 
  5.         照片的numpy数组 
  6.     fname: 
  7.         照片名字的字符串 
  8.     返回值: 
  9.         人脸特征(x,y)坐标的矩阵 
  10.     ''' 
  11.     rects = self.detector(im, 1) 
  12.  
  13.     if len(rects) >=5: 
  14.         raise TooManyFaces('Too many faces in '+fname) 
  15.     if len(rects) <2: 
  16.         raise NoFace('No enough face in' +fname) 
  17.     return [np.matrix([[p.x, p.y] for p
  18.  in self.predictor(im, rect).parts()]) for rect in rects] 

当然,执行换脸完整过程的swap函数也要改写。

换每个脸的过程和原程序差不多,但是我们需要一个copy作为输出画布,还要在两个图片间进行互为脸/头来源进行循环。

  1. def swap(self,im_name): 
  2.     ''' 
  3.     主函数 人脸交换 
  4.     im_name 
  5.         合影图片的键名字符串 
  6.     ''' 
  7.     im,landmarks=self.heads[im_name] 
  8.     out_im=im.copy()#画布 
  9.     for i in [1,-1]: 
  10.         landmarks_head,landmarkslandmarks_face=landmarks[:2][::i]#实现倒序 
  11.         M = self.transformation_from_points(landmarks_head[self.ALIGN_POINTS], 
  12.                                        landmarks_face[self.ALIGN_POINTS]) 
  13.  
  14.         face_mask = self.get_face_mask(im, landmarks_face) 
  15.         warped_mask = self.warp_im(face_mask, M, im.shape) 
  16.         combined_mask = np.max([self.get_face_mask(im, landmarks_head), warped_mask], 
  17.                                   axis=0
  18.         warped_face = self.warp_im(im, M, im.shape) 
  19.         warped_corrected_im = self.correct_colours(im, warped_face, landmarks_head) 
  20.         out_imout_im=out_im * (1.0 - combined_mask) + warped_corrected_im * combined_mask 
  21.     return out_im 

GUI界面

面向大众的程序还要有直观、易操作的GUI界面。

我们使用pyqt+pyinstaller来实习GUI和exe发布。

  1. import os,traceback 
  2. from PyQt5.QtWidgets import QFileDialog 
  3. from PyQt5 import QtCore,QtGui, QtWidgets 
  4. import cv2 
  5. import numpy as np 
  6. from coupleswapper import Coupleswapper,TooManyFaces,NoFace 
  7.  
  8. class Ui_Form(QtWidgets.QMainWindow): 
  9.     def __init__(self): 
  10.         super(Ui_Form,self).__init__() 
  11.         self.swapper=[] 
  12.         self.im_path='' 
  13.         self.cur_im_path='' 
  14.         self.img_swapped=None 
  15.         self.img_ori=None 
  16.         self.compare=None 
  17.  
  18.     def setupUi(self, Form): 
  19.         Form.setObjectName("Form") 
  20.         Form.resize(270, 360) 
  21.         Form.setAccessibleName("") 
  22.         self.verticalLayout = QtWidgets.QVBoxLayout(Form) 
  23.         self.verticalLayout.setObjectName("verticalLayout") 
  24.         Form.setWindowIcon(QtGui.QIcon('./male_female.ico')) 
  25.         self.help_label=QtWidgets.QLabel(Form) 
  26.         self.verticalLayout.addWidget(self.help_label) 
  27.         with open('./readme.txt','r',encoding='utf8') as f: 
  28.             self.readme=f.read() 
  29.         #显示运行log 
  30.         self.statu_text=QtWidgets.QTextBrowser(Form) 
  31.         selfself.vb=self.statu_text.verticalScrollBar() 
  32.         self.verticalLayout.addWidget(self.statu_text)#支持滚轮 
  33.         #加载按钮 
  34.         self.bt_load = QtWidgets.QPushButton(Form) 
  35.         self.bt_load.setDefault(True) 
  36.         self.bt_load.setObjectName("bt_load") 
  37.         self.bt_load.clicked.connect(self.load_image) 
  38.         self.verticalLayout.addWidget(self.bt_load) 
  39.         #转换按钮 
  40.         self.bt_swap = QtWidgets.QPushButton(Form) 
  41.         self.bt_swap.setDefault(True) 
  42.         self.bt_swap.setObjectName("bt_swap") 
  43.         self.bt_swap.clicked.connect(self.swap) 
  44.         self.verticalLayout.addWidget(self.bt_swap) 
  45.         #保存结果按钮 
  46.         self.bt_save = QtWidgets.QPushButton(Form) 
  47.         self.bt_save.setDefault(True) 
  48.         self.bt_save.setObjectName("bt_save") 
  49.         self.bt_save.clicked.connect(self.save_result) 
  50.         self.verticalLayout.addWidget(self.bt_save) 
  51.         #保存对比图按钮 
  52.         self.bt_save_comp = QtWidgets.QPushButton(Form) 
  53.         self.bt_save_comp.setDefault(True) 
  54.         self.bt_save_comp.clicked.connect(self.save_compare) 
  55.         self.bt_save_comp.setObjectName("bt_save_comp") 
  56.         self.verticalLayout.addWidget(self.bt_save_comp) 
  57.  
  58.  
  59.         self.retranslateUi(Form) 
  60.         QtCore.QMetaObject.connectSlotsByName(Form) 
  61.  
  62.     def retranslateUi(self, Form): 
  63.         _translate = QtCore.QCoreApplication.translate 
  64.         Form.setWindowTitle(_translate("Form", "交换♂身体")) 
  65.         self.bt_load.setText(_translate("Form", "加载图片")) 
  66.         self.bt_swap.setText(_translate("Form", "转换")) 
  67.         self.bt_save.setText(_translate("Form", "保存结果")) 
  68.         self.bt_save_comp.setText(_translate("Form", "保存对比")) 
  69.         self.help_label.setText(_translate("Form",self.readme)) 
  70.         self.statu_text.setText(_translate('Form','欢迎使用,请选择文件')) 
  71.  
  72.  
  73.     def load_image(self): 
  74.         ''' 
  75.         加载原图 
  76.         ''' 
  77.         try: 
  78.             im_path,_=QFileDialog.getOpenFileName(self,'打开图片文件'
  79. ,'./','Image Files(*.png *.jpg *.bmp)') 
  80.             if not os.path.exists(im_path): 
  81.                 return 
  82.             self.im_path=im_path 
  83.             self.statu_text.append('打开图片文件:'+self.im_path) 
  84.             if not self.swapper: 
  85.                 self.swapper=Coupleswapper([self.im_path]) 
  86.             elif not self.im_path== self.cur_im_path: 
  87.                 self.swapper.load_heads([self.im_path]) 
  88.             selfself.img_ori=self.swapper.heads[os.path.split(self.im_path)[-1]][0] 
  89.             cv2.imshow('Origin',self.img_ori) 
  90.         except (TooManyFaces,NoFace): 
  91.             self.statu_text.append(traceback.format_exc()+' 人脸定位失败,请重新选择!保证照片中有两张可识别的人脸。') 
  92.             return 
  93.  
  94.     def swap(self): 
  95.         ''' 
  96.         执行换脸 
  97.         ''' 
  98.         if not (self.swapper and os.path.exists(self.im_path)): 
  99.             return 
  100.         self.statu_text.append('转换成功!') 
  101.         selfself.img_swapped=self.swapper.swap(os.path.split(self.im_path)[-1]) 
  102.         self.img_swapped[self.img_swapped>254.9]=254.9 
  103.         selfself.img_swapped=self.img_swapped.astype('uint8') 
  104.         cv2.imshow('Result',self.img_swapped) 
  105.  
  106.     def save_result(self): 
  107.         ''' 
  108.         保存结果 
  109.         ''' 
  110.         output_path,_=QFileDialog.getSaveFileName(self,'选择保存位置'
  111. ,'./','Image Files(*.png *.jpg *.bmp)') 
  112.         if not output_path: 
  113.             self.statu_text.append('无效路径,请重新选择') 
  114.             return 
  115.         self.swapper.save(output_path,self.img_swapped) 
  116.         self.statu_text.append('成功保存到:'+output_path) 
  117.  
  118.     def save_compare(self): 
  119.         ''' 
  120.         保存对比图 
  121.         ''' 
  122.         self.compare=np.concatenate([self.img_ori,self.img_swapped],1) 
  123.         cv2.imshow('Compare',self.compare) 
  124.         output_path,_=QFileDialog.getSaveFileName(self,'选择保存位置'
  125. ,'./','Image Files(*.png *.jpg *.bmp)') 
  126.         if not output_path: 
  127.             self.statu_text.append('无效路径,请重新选择') 
  128.             return 
  129.         self.swapper.save(output_path,self.compare) 
  130.         self.statu_text.append('成功保存对比图到:'+output_path) 
  131. if __name__ == "__main__": 
  132.     import sys 
  133.     app = QtWidgets.QApplication(sys.argv) 
  134.     Form = QtWidgets.QWidget() 
  135.     ui = Ui_Form() 
  136.     ui.setupUi(Form) 
  137.     try: 
  138.         Form.show() 
  139.     except: 
  140.         traceback.print_exc() 
  141.     finally: 
  142.         cv2.destroyAllWindows() 
  143.     sys.exit(app.exec_()) 

 

(天清)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/202003/17625.html]
本文出处:zhihu 作者:天清 原文
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
相关文章
发表评论
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容