“换脸”案例源码详解 (Python)

faceswap_gui.py用于换脸,可与facemovie_gui.py身体互换源码(上一篇文章)对照观看

  1. 打开faceswap_gui换脸案例
  2. 点读取函数
  3. 数学变换方法
  4. 换脸函数
  5. 图像预处理函数
  6. 检测模式函数
  7. 绘制网格函数
  8. 获取关键点及标志的函数(2~8步与“身体互换”相同,未验证是否存在细微差别)
  9. 构建程序的图形化类
  10. 初始化处理函数(首先被调用)
  11. 程序入口

打开facemovie_gui.py换脸案例

在VScode中进入代码编辑状态。

导入相关库

'''faceswap_gui.py用于examples中的换脸''''''
导入基础包作用详解
'''
#导入包介绍开始
#cv2模块是OpenCV 2.0的简写,在计算机视觉项目的开发中,
#OpenCV作为较大众的开源库,拥有了丰富的常用图像处理函数库,
#采用C/C++语言编写,可以运行在Linux/Windows/Mac等操作系统上,能够快速的实现一些图像处理和识别的任务。
import cv2#math模块提供了许多对浮点数的数学运算函数。
import math#sys模块包括了一组非常实用的服务,内含很多函数方法和变量,
#用来处理Python运行时配置以及资源,从而可以与前当程序之外的系统环境交互。
import sys#NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,
#支持大量的维度数组与矩阵运算,
#此外也针对数组运算提供大量的数学函数库。
#在机器学习算法中大部分都是调用Numpy库来完成基础数值计算的。
import numpy as np
#导入包介绍结束#BlazeFace是一个非常轻量级的人脸检测器,
#其在许多嵌入式设备中都可以达到超实时的效率
#在一些性能较好的手机gpu中甚至可达亚毫秒级
from blazeface import *#cvs包是Aid内置的代替cv2的包,基本上cv2支持的函数cvs一样支持,cvs包在X模式下和非X模式下一样执行
#cvs更多详细介绍查看官网文档OpenCVhttps://www.aidlearning.net/showdoc/web/#/5?page_id=45
from cvs import *# tflite_gpu,GPU加速代码由AID提供,TensorFlow Lite 支持多种硬件加速器。GPU 是设计用来完成高吞吐量的大规模并行工作的。
# 因此,它们非常适合用在包含大量运算符的神经网络上,一些输入张量可以容易的被划分为更小的工作负载且可以同时执行,通常这会导致更低的延迟。
# 在最佳情况下,用 GPU 在实时应用程序上做推理运算已经可以运行的足够快,而这在以前是不可能的
import tflite_gpu
##############################################################################
back_img_path=('models/rs.jpeg','models/wy.jpeg','models/zyx.jpeg','models/monkey.jpg','models/star2.jpg','models/star1.jpg','models/star3.jpg','models/star4.jpg')#读图
faceimg=cv2.imread(back_img_path[0])
mod=-1
bfirstframe=True

点读取函数

'''
此函数读取点,输入文件路径,输出读取到的点集
'''
def readPoints(path) :#初始化一个空的点数组points = [];#读取点集with open(path) as file :for line in file :x, y = line.split()points.append((int(x), int(y)))#返回点return points

数学变换方法

'''
此函数应用仿射变换
'''
#ApplyAffineTransform使用srcTri、dstTri、src计算sto
#并输出一副图像的大小。
#应用一个仿射变换,这个变换由srcTri(源三角形)和dstTri(目标三角形)计算出
#输出一幅图像
def applyAffineTransform(src, srcTri, dstTri, size) :#仿射变换,又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。#仿射变换需要一个M矩阵,但是由于仿射变换比较复杂,一般直接找很难找到这个矩阵,#opencv提供了根据变换前后三个点的对应关系来自动求解M的函数,这个函数就是:cv2.GetAffineTransform(src, dst)#由给定的一对三角形找出仿射变换,warpMat为仿射变换矩阵warpMat = cv2.getAffineTransform( np.float32(srcTri), np.float32(dstTri) )#将得到的仿射变换应用至源图像dst = cv2.warpAffine( src, warpMat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )return dst'''
检查某特定点是否在一个具体矩形内
'''
#判断某点是否在一个矩形内
#一个矩形由对角的两个点(四个坐标数据)组成
def rectContains(rect, point) :#原理:point数组中的两个值分别代表该点的x坐标、y坐标#rect数组中的四个值分别理解为左上角点的x坐标、左上角点的y坐标、矩形宽、矩形高#如果某点坐标在矩形左边界以左#或在矩形上边界以上#则不存在于矩形内if point[0] < rect[0] : return Falseelif point[1] < rect[1] : return False#如果某点坐标在矩形右边界以右#或在矩形下边界以下#则不存在于矩形内elif point[0] > rect[0] + rect[2] : #rect[0] + rect[2] 即指矩形右边界的x坐标(以下y坐标同理)return Falseelif point[1] > rect[1] + rect[3] :return False#以上可能性排除即可判定点在矩形内return True'''
计算徳洛內三角形
'''
#计算徳洛內三角形
def calculateDelaunayTriangles(rect, points):#创建subdiv(细分)subdiv = cv2.Subdiv2D(rect);#将各点插入subdivfor p in points:subdiv.insert(p) #初始化三角形列表、徳洛內三角形、点集triangleList = subdiv.getTriangleList();delaunayTri = []pt = []    #遍历三角形列表中的三角形#每个三角形是一个长度为6的数组for t in triangleList:      #在点集中加入三角形中每个点的x、y坐标#append是内置函数,向列表末尾压入元素  pt.append((t[0], t[1]))pt.append((t[2], t[3]))pt.append((t[4], t[5]))pt1 = (t[0], t[1])pt2 = (t[2], t[3])pt3 = (t[4], t[5])        if rectContains(rect, pt1) and rectContains(rect, pt2) and rectContains(rect, pt3):ind = []#根据坐标,从68个人脸检测器得到脸上的面部点for j in range(0, 3):for k in range(0, len(points)):       #这里参数取1.0,若从三角形得到的点与输入points中各点在x、y坐标上距离均小于1.0,则向ind中加入下标k              if(abs(pt[j][0] - points[k][0]) < 1.0 and abs(pt[j][1] - points[k][1]) < 1.0):ind.append(k)    #三点构成一个三角形。#这里的三角形列表对应于FaceMorph中的tri.txtif len(ind) == 3:                                                delaunayTri.append((ind[0], ind[1], ind[2]))pt = []        return delaunayTri'''
仿射及透明混合
'''
#从img1和img2仿射并进行透明混合,得到img
def warpTriangle(img1, img2, t1, t2) :#找到每个三角形(t1、t2)的外接矩形(r1、r2)r1 = cv2.boundingRect(np.float32([t1]))r2 = cv2.boundingRect(np.float32([t2]))#以每个矩形的左上角为基点的偏移点#初始化为空列表t1Rect = [] t2Rect = []t2RectInt = []for i in range(0, 3):t1Rect.append(((t1[i][0] - r1[0]),(t1[i][1] - r1[1])))t2Rect.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))t2RectInt.append(((t2[i][0] - r2[0]),(t2[i][1] - r2[1])))# 填充三角形,得到遮罩(mask)mask = np.zeros((r2[3], r2[2], 3), dtype = np.float32)cv2.fillConvexPoly(mask, np.int32(t2RectInt), (1.0, 1.0, 1.0), 16, 0);#将变换后的图像应用于小矩形贴片img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]#img2Rect = np.zeros((r2[3], r2[2]), dtype = img1Rect.dtype)size = (r2[2], r2[3])img2Rect = applyAffineTransform(img1Rect, t1Rect, t2Rect, size)img2Rect = img2Rect * mask#将矩形贴片中的三角区域复制到输出图像img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]] = img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]] * ( (1.0, 1.0, 1.0) - mask )img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]] = img2[r2[1]:r2[1]+r2[3], r2[0]:r2[0]+r2[2]] + img2Rect

换脸函数

'''
换脸
'''def faceswap(points1,points2,img1,img2):# # Read images# filename1 ='sabina.jpg'# filename2 ='bid.jpg' # img1 = cv2.imread(filename1);# img2 = cv2.imread(filename2);img1Warped = np.copy(img2);    # Read array of corresponding points# points1 = readPoints('sabina.txt')# points2 = readPoints('bid.txt')    # 凸包(Convex Hull)是一个计算几何(图形学)中的概念。# #在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包#在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。#用不严谨的话来讲,给定二维平面上的点集,# 凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。# 寻找凸包hull1 = []hull2 = []#cv2.convexHull是opencv提供的寻找凸包函数hullIndex = cv2.convexHull(np.array(points2), returnPoints = False)for i in range(0, len(hullIndex)):hull1.append(points1[int(hullIndex[i])])hull2.append(points2[int(hullIndex[i])])# 由凸包边界上的点得到徳洛內三角形sizeImg2 = img2.shape    rect = (0, 0, sizeImg2[1], sizeImg2[0])dt = calculateDelaunayTriangles(rect, hull2)if len(dt) == 0:quit()# 对徳洛內三角形进行仿射变换for i in range(0, len(dt)):t1 = []t2 = []#为img1、img2获取对应于三角形列表的点集for j in range(0, 3):t1.append(hull1[dt[i][j]])t2.append(hull2[dt[i][j]])warpTriangle(img1, img1Warped, t1, t2)#计算遮罩hull8U = []for i in range(0, len(hull2)):hull8U.append((hull2[i][0], hull2[i][1]))mask = np.zeros(img2.shape, dtype = img2.dtype)  cv2.fillConvexPoly(mask, np.int32(hull8U), (255, 255, 255))r = cv2.boundingRect(np.float32([hull2]))    center = ((r[0]+int(r[2]/2), r[1]+int(r[3]/2)))#无缝克隆output = cv2.seamlessClone(np.uint8(img1Warped), img2, mask, center, cv2.NORMAL_CLONE)return output# cv2.imshow("Face Swapped", output)# cv2.waitKey(0)# cv2.destroyAllWindows()

图像预处理

#############################################################################tflite=tflite_gpu.tflite()'''
为tflite32模型进行图像预处理
'''
def preprocess_image_for_tflite32(image, model_image_size=192):# cv2.cvtColor(p1,p2) 是颜色空间转换函数,p1是需要转换的图片,p2是转换成何种格式。# cv2.COLOR_BGR2RGB 将BGR格式转换成RGB格式  # cv2.COLOR_BGR2GRAY 将BGR格式转换成灰度图片image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)image = cv2.resize(image, (model_image_size, model_image_size))image = np.expand_dims(image, axis=0)image = (2.0 / 255.0) * image - 1.0image = image.astype('float32')return image'''
预处理图像填充
'''
def preprocess_img_pad(img,image_size=128):# 将图像适配进128*128的正方形中# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)#np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。#np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。shape = np.r_[img.shape]pad_all = (shape.max() - shape[:2]).astype('uint32')pad = pad_all // 2# print ('pad_all',pad_all)img_pad_ori = np.pad(img,((pad[0],pad_all[0]-pad[0]), (pad[1],pad_all[1]-pad[1]), (0,0)),mode='constant')img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)img_pad = np.pad(img,((pad[0],pad_all[0]-pad[0]), (pad[1],pad_all[1]-pad[1]), (0,0)),mode='constant')img_small = cv2.resize(img_pad, (image_size, image_size))img_small = np.expand_dims(img_small, axis=0)# img_small = np.ascontiguousarray(img_small)img_small = (2.0 / 255.0) * img_small - 1.0img_small = img_small.astype('float32')# img_norm = self._im_normalize(img_small)return img_pad_ori, img_small, pad    

检测模式函数

'''
检测模式
'''
def plot_detections(img, detections, with_keypoints=True):output_img = imgprint(img.shape)x_min=0x_max=0y_min=0y_max=0print("Found %d faces" % len(detections))for i in range(len(detections)):ymin = detections[i][ 0] * img.shape[0]xmin = detections[i][ 1] * img.shape[1] ymax = detections[i][ 2] * img.shape[0]xmax = detections[i][ 3] * img.shape[1] w=int(xmax-xmin)h=int(ymax-ymin)h=max(w,h)h=h*1.5x=(xmin+xmax)/2.y=(ymin+ymax)/2.xmin=x-h/2.xmax=x+h/2.# ymin=y-h/2.# ymax=y+h/2.ymin=y-h/2.-0.08*hymax=y+h/2.-0.08*hx_min=int(xmin)y_min=int(ymin)x_max=int(xmax)y_max=int(ymax)            p1 = (int(xmin),int(ymin))p2 = (int(xmax),int(ymax))# print(p1,p2)cv2.rectangle(output_img, p1, p2, (0,255,255),2,1)# cv2.putText(output_img, "Face found! ", (p1[0]+10, p2[1]-10),cv2.FONT_ITALIC, 1, (0, 255, 129), 2)# if with_keypoints:#     for k in range(6):#         kp_x = int(detections[i, 4 + k*2    ] * img.shape[1])#         kp_y = int(detections[i, 4 + k*2 + 1] * img.shape[0])#         cv2.circle(output_img,(kp_x,kp_y),4,(0,255,255),4)return x_min,y_min,x_max,y_max

绘制网格函数

'''
绘制网格
'''
def draw_mesh(image, mesh, mark_size=2, line_width=1):"""Draw the mesh on an image"""# The mesh are normalized which means we need to convert it back to fit# the image size.image_size = image.shape[0]mesh = mesh * image_sizefor point in mesh:cv2.circle(image, (point[0], point[1]),mark_size, (0, 255, 128), -1)# Draw the contours.# Eyesleft_eye_contour = np.array([mesh[33][0:2],mesh[7][0:2],mesh[163][0:2],mesh[144][0:2],mesh[145][0:2],mesh[153][0:2],mesh[154][0:2],mesh[155][0:2],mesh[133][0:2],mesh[173][0:2],mesh[157][0:2],mesh[158][0:2],mesh[159][0:2],mesh[160][0:2],mesh[161][0:2],mesh[246][0:2], ]).astype(np.int32)right_eye_contour = np.array([mesh[263][0:2],mesh[249][0:2],mesh[390][0:2],mesh[373][0:2],mesh[374][0:2],mesh[380][0:2],mesh[381][0:2],mesh[382][0:2],mesh[362][0:2],mesh[398][0:2],mesh[384][0:2],mesh[385][0:2],mesh[386][0:2],mesh[387][0:2],mesh[388][0:2],mesh[466][0:2]]).astype(np.int32)# Lipscv2.polylines(image, [left_eye_contour, right_eye_contour], False,(255, 255, 255), line_width, cv2.LINE_AA)

获取关键点及标志的函数

'''
获取关键点
'''
def getkeypoint(image, mesh,landmark_point):image_size = image.shape[0]mesh = mesh * image_size# landmark_point = []for point in mesh:landmark_point.append((point[0], point[1]))return image# cv2.circle(image, (point[0], point[1]), 2, (255, 255, 0), -1)  '''
获取标志
'''
def draw_landmarks(image, mesh,landmark_point):image_size = image.shape[0]mesh = mesh * image_size# landmark_point = []for point in mesh:landmark_point.append((point[0], point[1]))cv2.circle(image, (point[0], point[1]), 2, (255, 255, 0), -1)if len(landmark_point) > 0:# 参考:https://github.com/tensorflow/tfjs-models/blob/master/facemesh/mesh_map.jpg# 左眉毛(55:内側、46:外側)cv2.line(image, landmark_point[55], landmark_point[65], (0, 0, 255), 2,-3)cv2.line(image, landmark_point[65], landmark_point[52], (0, 0, 255), 2,-3)cv2.line(image, landmark_point[52], landmark_point[53], (0, 0, 255), 2,-3)cv2.line(image, landmark_point[53], landmark_point[46],(0, 0, 255), 2,-3)# 右眉毛(285:内側、276:外側)cv2.line(image, landmark_point[285], landmark_point[295], (0, 0, 255),2)cv2.line(image, landmark_point[295], landmark_point[282], (0, 0, 255),2)cv2.line(image, landmark_point[282], landmark_point[283], (0, 0, 255),2)cv2.line(image, landmark_point[283], landmark_point[276], (0, 0, 255),2)# 左目 (133:目頭、246:目尻)cv2.line(image, landmark_point[133], landmark_point[173], (0, 0, 255),2)cv2.line(image, landmark_point[173], landmark_point[157], (0, 0, 255),2)cv2.line(image, landmark_point[157], landmark_point[158], (0, 0, 255),2)cv2.line(image, landmark_point[158], landmark_point[159], (0, 0, 255),2)cv2.line(image, landmark_point[159], landmark_point[160], (0, 0, 255),2)cv2.line(image, landmark_point[160], landmark_point[161], (0, 0, 255),2)cv2.line(image, landmark_point[161], landmark_point[246], (0, 0, 255),2)cv2.line(image, landmark_point[246], landmark_point[163], (0, 0, 255),2)cv2.line(image, landmark_point[163], landmark_point[144], (0, 0, 255),2)cv2.line(image, landmark_point[144], landmark_point[145], (0, 0, 255),2)cv2.line(image, landmark_point[145], landmark_point[153], (0, 0, 255),2)cv2.line(image, landmark_point[153], landmark_point[154], (0, 0, 255),2)cv2.line(image, landmark_point[154], landmark_point[155], (0, 0, 255),2)cv2.line(image, landmark_point[155], landmark_point[133], (0, 0, 255),2)# 右目 (362:目頭、466:目尻)cv2.line(image, landmark_point[362], landmark_point[398], (0, 0, 255),2)cv2.line(image, landmark_point[398], landmark_point[384], (0, 0, 255),2)cv2.line(image, landmark_point[384], landmark_point[385], (0, 0, 255),2)cv2.line(image, landmark_point[385], landmark_point[386], (0, 0, 255),2)cv2.line(image, landmark_point[386], landmark_point[387], (0, 0, 255),2)cv2.line(image, landmark_point[387], landmark_point[388], (0, 0, 255),2)cv2.line(image, landmark_point[388], landmark_point[466], (0, 0, 255),2)cv2.line(image, landmark_point[466], landmark_point[390], (0, 0, 255),2)cv2.line(image, landmark_point[390], landmark_point[373], (0, 0, 255),2)cv2.line(image, landmark_point[373], landmark_point[374], (0, 0, 255),2)cv2.line(image, landmark_point[374], landmark_point[380], (0, 0, 255),2)cv2.line(image, landmark_point[380], landmark_point[381], (0, 0, 255),2)cv2.line(image, landmark_point[381], landmark_point[382], (0, 0, 255),2)cv2.line(image, landmark_point[382], landmark_point[362], (0, 0, 255),2)# 口 (308:右端、78:左端)cv2.line(image, landmark_point[308], landmark_point[415], (0, 0, 255),2)cv2.line(image, landmark_point[415], landmark_point[310], (0, 0, 255),2)cv2.line(image, landmark_point[310], landmark_point[311], (0, 0, 255),2)cv2.line(image, landmark_point[311], landmark_point[312], (0, 0, 255),2)cv2.line(image, landmark_point[312], landmark_point[13], (0, 0, 255), 2)cv2.line(image, landmark_point[13], landmark_point[82], (0, 0, 255), 2)cv2.line(image, landmark_point[82], landmark_point[81], (0, 0, 255), 2)cv2.line(image, landmark_point[81], landmark_point[80], (0, 0, 255), 2)cv2.line(image, landmark_point[80], landmark_point[191], (0, 0, 255), 2)cv2.line(image, landmark_point[191], landmark_point[78], (0, 0, 255), 2)cv2.line(image, landmark_point[78], landmark_point[95], (0, 0, 255), 2)cv2.line(image, landmark_point[95], landmark_point[88], (0, 0, 255), 2)cv2.line(image, landmark_point[88], landmark_point[178], (0, 0, 255), 2)cv2.line(image, landmark_point[178], landmark_point[87], (0, 0, 255), 2)cv2.line(image, landmark_point[87], landmark_point[14], (0, 0, 255), 2)cv2.line(image, landmark_point[14], landmark_point[317], (0, 0, 255), 2)cv2.line(image, landmark_point[317], landmark_point[402], (0, 0, 255),2)cv2.line(image, landmark_point[402], landmark_point[318], (0, 0, 255),2)cv2.line(image, landmark_point[318], landmark_point[324], (0, 0, 255),2)cv2.line(image, landmark_point[324], landmark_point[308], (0, 0, 255),2)return image        

构建程序的图形化类

'''
MyApp类用于构建程序的图形化
'''
class MyApp(App):#初始化函数,self表示创建实例本身, *args传递可变参数,__init__()的第一个参数永远是selfdef __init__(self, *args):#这是对继承自父类的属性进行初始化。而且是用父类的初始化方法来初始化继承的属性。#也就是说,子类继承了父类的所有属性和方法,父类属性自然会用父类方法来进行初始化。#super() 函数是用于调用父类(超类)的一个方法。#super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,#但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。#MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表super(MyApp, self).__init__(*args)def idle(self):#在每次更新循环中idle函数都会被调用self.aidcam.update()#构建图形界面的主函数def main(self):#创建一个VBox容器,使用垂直方式(还有HBox水平盒子,以及widget)#定义变量main_container,它是整个图形界面的主框架,用于包含其他APP中的控件,相当于容器#gui.VBox这段代码是在手机上画出一个宽度为360px,高度为680px的程序主框架范围。#style是它的样式,margin:0 auto;相当于margin:0 auto 0 auto;即上下是0,左右是自动。#这时main_container中的元素会水平居中main_container = VBox(width=360, height=680, style={'margin':'0px auto'})#添加摄像头控件#OpencvVideoWidget函数是在手机 画一个用于显示调用手机摄像头拍摄图像的框,宽度340px,高度400px,#将它赋值给self.aidcam,aidcam表示摄像头控件。self.aidcam = OpencvVideoWidget(self, width=340, height=400)self.aidcam.style['margin'] = '10px'#给aidcam控件一个标记self.aidcam.identifier="myimage_receiver"#将设置好的摄像头显示框添加到APP主框架中main_container.append(self.aidcam)self.lbl = Label('点击图片选择你喜欢的明星脸:')#将设置好的标签显示框添加到APP主框架中main_container.append(self.lbl)#定义变量bottom_container,底部容器。框架范围:宽度为360px,高度为130px。#样式上下为0,左右自动。#这时bottom_container中的元素会水平居中bottom_container = HBox(width=360, height=130, style={'margin':'0px auto'})#若被点击,则加载相应的img至底部容器中self.img1 = Image('/res:'+os.getcwd()+'/'+back_img_path[0], height=80, margin='10px')self.img1.onclick.do(self.on_img1_clicked)bottom_container.append(self.img1)self.img2 = Image('/res:'+os.getcwd()+'/'+back_img_path[1], height=80, margin='10px')self.img2.onclick.do(self.on_img2_clicked)bottom_container.append(self.img2)self.img3 = Image('/res:'+os.getcwd()+'/'+back_img_path[2], height=80, margin='10px')self.img3.onclick.do(self.on_img3_clicked)bottom_container.append(self.img3)self.img4 = Image('/res:'+os.getcwd()+'/'+back_img_path[3], height=80, margin='10px')self.img4.onclick.do(self.on_img4_clicked)bottom_container.append(self.img4)bt_container = HBox(width=360, height=130, style={'margin':'0px auto'})self.img11 = Image('/res:'+os.getcwd()+'/'+back_img_path[4], height=80, margin='10px')self.img11.onclick.do(self.on_img11_clicked)bt_container.append(self.img11)self.img22 = Image('/res:'+os.getcwd()+'/'+back_img_path[5], height=80, margin='10px')self.img22.onclick.do(self.on_img22_clicked)bt_container.append(self.img22)self.img33 = Image('/res:'+os.getcwd()+'/'+back_img_path[6], height=80, margin='10px')self.img33.onclick.do(self.on_img33_clicked)bt_container.append(self.img33)self.img44 = Image('/res:'+os.getcwd()+'/'+back_img_path[7], height=80, margin='10px')self.img44.onclick.do(self.on_img44_clicked)bt_container.append(self.img44)        # self.bt1 = Button('抠图模式', width=100, height=30, margin='10px')# self.bt1.onclick.do(self.on_button_pressed1)# self.bt2 = Button('渲染模式', width=100, height=30, margin='10px')# self.bt2.onclick.do(self.on_button_pressed2)        # self.bt3 = Button('着色模式', width=100, height=30, margin='10px')# self.bt3.onclick.do(self.on_button_pressed3) main_container.append(bottom_container)main_container.append(bt_container)# main_container.append(self.bt1)# main_container.append(self.bt2)# main_container.append(self.bt3)return main_container#各图的点击响应函数def on_img1_clicked(self, widget):global faceimgbgnd=cv2.imread(back_img_path[0])faceimg=bgnd# global bfirstframe# bfirstframe=Trueglobal modmod=0def on_img2_clicked(self, widget):global faceimgbgnd=cv2.imread(back_img_path[1])faceimg=bgnd# global bfirstframe# bfirstframe=Trueglobal modmod=1def on_img3_clicked(self, widget):global faceimgbgnd=cv2.imread(back_img_path[2])       faceimg=bgnd# global bfirstframe# bfirstframe=Trueglobal modmod=2def on_img4_clicked(self, widget):global faceimgbgnd=cv2.imread(back_img_path[3])       faceimg=bgnd# global bfirstframe# bfirstframe=Trueglobal modmod=3        def on_img11_clicked(self, widget):global faceimgbgnd=cv2.imread(back_img_path[4])faceimg=bgnd# global bfirstframe# bfirstframe=Trueglobal modmod=4def on_img22_clicked(self, widget):global faceimgbgnd=cv2.imread(back_img_path[5])faceimg=bgnd# global bfirstframe# bfirstframe=Trueglobal modmod=5def on_img33_clicked(self, widget):global faceimgbgnd=cv2.imread(back_img_path[6])       faceimg=bgnd# global bfirstframe# bfirstframe=Trueglobal modmod=6def on_img44_clicked(self, widget):global faceimgbgnd=cv2.imread(back_img_path[7])       faceimg=bgnd# global bfirstframe# bfirstframe=Trueglobal modmod=7#按钮的按下响应函数,模式选择def on_button_pressed1(self, widget):global modmod=0def on_button_pressed2(self, widget):global modmod=1   def on_button_pressed3(self, widget):global modmod=2

获取关键点及标志的函数

 

初始化处理函数(首先被调用)

'''
用于初始化处理,换身体应用启动时首先被调用
'''
def process():cvs.setCustomUI()#指定输入图像的像素大小为128*128input_shape=[128,128]#输入数组的形状为一张图*128*128pix*3通道*4inShape =[1 * 128 * 128 *3*4,]#outShape= [1 * 896*16*4,1*896*1*4]model_path="models/face_detection_front.tflite"print('gpu:',tflite.NNModel(model_path,inShape,outShape,4,0))model_path="models/face_landmark.tflite"tflite.set_g_index(1)inShape1 =[1 * 192 * 192 *3*4,]outShape1= [1 * 1404*4,1*4]print('cpu:',tflite.NNModel(model_path,inShape1,outShape1,4,0))anchors = np.load('models/anchors.npy').astype(np.float32)camid=1cap=cvs.VideoCapture(camid)bFace=Falsex_min,y_min,x_max,y_max=(0,0,0,0)fface=0.0global bfirstframebfirstframe=Truefacepath="Biden.jpeg"# facepath="rs.jpeg"# faceimg=bgnd_matglobal faceimgfaceimg=cv2.resize(faceimg,(256,256))# roi_orifirst=faceimgpadfaceimg=faceimgfpoints=[]spoints=[]# mcap=cv2.VideoCapture('test.mp4')global modmod=-1while True:#循环读取摄像头捕捉到的帧frame= cvs.read()# _,mframe=mcap.read()#如果没读取到帧,就进入下一次读取循环,直到读取到帧if frame is None:continue#flip()的作用是使图像进行翻转,cv2.flip(filename, flipcode) #filename:需要操作的图像,flipcode:翻转方式,1水平翻转,0垂直翻转,-1水平垂直翻转#如果是前置摄像头,需要翻转图片,想象照镜子的原理if camid==1:frame=cv2.flip(frame,1)if mod>-1 or bfirstframe:x_min,y_min,x_max,y_max=(0,0,0,0)faceimg=cv2.resize(faceimg,(256,256))frame=faceimgbFace=Falseroi_orifirst=faceimgpadfaceimg=faceimgbfirstframe=Truefpoints=[]spoints=[]#记录起始时间start_time = time.time()    # img = preprocess_image_for_tflite32(frame,128)#进行图像填充预处理img_pad, img, pad = preprocess_img_pad(frame,128)# interpreter.set_tensor(input_details[0]['index'], img[np.newaxis,:,:,:])#bFace默认即为Falseif bFace==False:tflite.set_g_index(0)# 由于fp16的值区间比fp32的值区间小很多,所以在计算过程中很容易出现上溢出(Overflow,>65504 )和下溢出(Underflow,<6x10^-8  )的错误,# 溢出之后就会出现“Nan”的问题# 所以我们选择fp32# 分配内存并传入数据tflite.setTensor_Fp32(img,input_shape[1],input_shape[1])# 启动tensorflow模型,使之开始运行tflite.invoke()#参数0指定的是“生”框的模型raw_boxes = tflite.getTensor_Fp32(0)#参数指定分类器的模型classificators = tflite.getTensor_Fp32(1)#将三个参数加入到balzeface中,进行人脸检测,得到检测结果detections = blazeface(raw_boxes, classificators, anchors)[0]#若有结果,设置bFace为Trueif len(detections)>0 :bFace=True#如果检测有人脸if bFace:#遍历检测结果集for i in range(len(detections)):ymin = detections[i][ 0] * img_pad.shape[0]xmin = detections[i][ 1] * img_pad.shape[1] ymax = detections[i][ 2] * img_pad.shape[0]xmax = detections[i][ 3] * img_pad.shape[1] w=int(xmax-xmin)h=int(ymax-ymin)h=max(w,h)h=h*1.5x=(xmin+xmax)/2.y=(ymin+ymax)/2.xmin=x-h/2.xmax=x+h/2.ymin=y-h/2.ymax=y+h/2.ymin=y-h/2.-0.08*hymax=y+h/2.-0.08*hx_min=int(xmin)y_min=int(ymin)x_max=int(xmax)y_max=int(ymax)  x_min=max(0,x_min)y_min=max(0,y_min)x_max=min(img_pad.shape[1],x_max)y_max=min(img_pad.shape[0],y_max)roi_ori=img_pad[y_min:y_max, x_min:x_max]# cvs.imshow(roi)# roi_ori=roi_ori[:,:,::-1]roi =preprocess_image_for_tflite32(roi_ori,192)tflite.set_g_index(1)tflite.setTensor_Fp32(roi,192,192)# start_time = time.time()#启动模型tflite.invoke()#选择模型以计算网格mesh = tflite.getTensor_Fp32(0)ffacetmp = tflite.getTensor_Fp32(1)[0]print('fface:',abs(fface-ffacetmp))if abs(fface - ffacetmp) > 0.5:bFace=Falsefface=ffacetmpspoints=[]   # print('mesh:',mesh.shape)mesh = mesh.reshape(468, 3) / 192#如有第一帧if bfirstframe :getkeypoint(roi_ori,mesh,fpoints)roi_orifirst=roi_ori.copy()bfirstframe=Falsemod=-1# padfaceimg=img_padelse:getkeypoint(roi_ori,mesh,spoints)roi_ori=faceswap(fpoints,spoints,roi_orifirst,roi_ori)img_pad[y_min:y_max, x_min:x_max]=roi_orishape=frame.shapex,y=img_pad.shape[0]/2,img_pad.shape[1]/2# frame=roi_oriframe=img_pad[int(y-shape[0]/2):int(y+shape[0]/2), int(x-shape[1]/2):int(x+shape[1]/2)]#计算用时t = (time.time() - start_time)# print('elapsed_ms invoke:',t*1000)#定义标签内容lbs = 'Fps: '+ str(int(100/t)/100.)+" ~~ Time:"+str(t*1000) +"ms"#显示标签cvs.setLbs(lbs) #显示图像cvs.imshow(frame)#让程序休眠#time.sleep(秒数),其中“秒数”以秒为单位,可以是小数,0.1秒则代表休眠100毫秒。sleep(1)

程序入口

'''
入口
'''
if __name__ == '__main__':#initcv用于初始化initcv(process)startcv(MyApp)

AidLux“换脸”案例源码详解 (Python)相关推荐

  1. AidLux“人像抠图”案例源码详解 (Python)

    "人像抠图"案例源码详解 (Python) seg_gui_meet.py用于人像抠图 导入基础包作用详解 构建程序图形化类 初始化处理函数(人体抠图应用启动时首先被调用) 程序入 ...

  2. AidLux “人脸测试”案例源码详解

    "人脸检测"案例源码详解 testface.py用于进行人脸检测 构建APP框架和添加主要控件 人脸关键点识别的方法 打开人脸测试案例 1.在VScode中进入代码编辑状态. 2. ...

  3. AidLux“实时头发分割”案例源码详解

    "实时头发分割"案例源码详解 1. 构建APP框架和添加主要控件 2. 头发分割的方法 打开实时头发分割案例 1.在VScode中进入代码编辑状态. 2.代码存在路径在/examp ...

  4. Android开发之GPS测试完整案例源码详解之实时检测GPS的状态

    获取Android系统Location位置服务实例: /*** Get location manager system service** @return LocationManager*/publi ...

  5. 【卷积神经网络CNN 实战案例 GoogleNet 实现手写数字识别 源码详解 深度学习 Pytorch笔记 B站刘二大人 (9.5/10)】

    卷积神经网络CNN 实战案例 GoogleNet 实现手写数字识别 源码详解 深度学习 Pytorch笔记 B站刘二大人 (9.5/10) 在上一章已经完成了卷积神经网络的结构分析,并通过各个模块理解 ...

  6. 【 数据集加载 DatasetDataLoader 模块实现与源码详解 深度学习 Pytorch笔记 B站刘二大人 (7/10)】

    数据集加载 Dataset&DataLoader 模块实现与源码详解 深度学习 Pytorch笔记 B站刘二大人 (7/10) 模块介绍 在本节中没有关于数学原理的相关介绍,使用的数据集和类型 ...

  7. 【 卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10)】

    卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10) 本章主要进行卷积神经网络的相关数学原理和pytorch的对应模块进行推导分析 代码也是通过demo实 ...

  8. 【 线性回归 Linear-Regression torch模块实现与源码详解 深度学习 Pytorch笔记 B站刘二大人(4/10)】

    torch模块实现与源码详解 深度学习 Pytorch笔记 B站刘二大人 深度学习 Pytorch笔记 B站刘二大人(4/10) 介绍 至此开始,深度学习模型构建的预备知识已经完全准备完毕. 从本章开 ...

  9. Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解

    Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解 目录 Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解 一.OpenGL ES渲染管线 1.基本处 ...

最新文章

  1. css删除线_前端删除文字贯穿线的方法有哪些
  2. 我的博士之路(壮根美颜-康亚龙):五年读博路,苦熬曙光明
  3. MySQL查询的进阶操作--分页查询
  4. 教你学会Sql中 ROW_NUMBER的用法
  5. C#动态加载dll,dll目录指定
  6. REST framework 用户认证源码
  7. iOS获取ipa素材、提取ipa资源图片文件
  8. 邮件签名——html模板
  9. 快速:通过画图了解Racket
  10. 乐视贾跃亭任酷派集团执行董事 成酷派第二股东
  11. WPS文字设置奇偶页眉、下划线的方法步骤
  12. Android使用VAD检测是否说话
  13. (课程笔记)| 林轩田机器学习基石入门(一)
  14. python数据分析 | seaborn绘图学习
  15. 阿里云ECS服务器CentOS6.5vnc连接时报错Failed to connect to socket /tmp/dbus-xxxxxxx: Connection refused
  16. 【操作系统 - 1】先来先服务FCFS和短作业优先SJF进程调度算法
  17. python真值是什么意思_Python 为什么能支持任意的真值判断?
  18. 以地图视角回顾70年互联网发展
  19. (过程超详细)适合新手的ATK-ESP8266+STM32F103系列单片机通过MQTT协议直连阿里云的教程
  20. 动态网站设计与开发学习第三周体会

热门文章

  1. 飞桨框架v2.3 API最新升级!对科学计算、概率分布和稀疏Tensor等提供更全面支持!...
  2. 工具篇——MoneyFormatUtil(用于将人民币小写金额转换成大写金额)
  3. Linux解压缩解压tar.gz文件
  4. 如何训练出专属的 OpenAI Five ?
  5. 使用人性化的Linux防火墙CFW阻止DDOS攻击
  6. AlexNet论文翻译(中英文对照版)-ImageNet Classification with Deep Convolutional Neural Networks
  7. 课程向:深度学习与人类语言处理 ——李宏毅,2020 (P9)
  8. ESP Matter 环境搭建
  9. 在国企做软件测试工程师是一种什么样的体验:每天过的像打仗一样
  10. iOS13苹果登录的后台验证token(JAVA)