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

“换脸”案例源码详解 (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中进入代码编辑状态。

AidLux“换脸”案例源码详解 (Python)_第1张图片

导入相关库

'''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 False
    elif point[1] < rect[1] : 
        return False

    #如果某点坐标在矩形右边界以右
    #或在矩形下边界以下
    #则不存在于矩形内
    elif point[0] > rect[0] + rect[2] : #rect[0] + rect[2] 即指矩形右边界的x坐标(以下y坐标同理)
        return False
    elif point[1] > rect[1] + rect[3] :
        return False

    #以上可能性排除即可判定点在矩形内
    return True


'''
计算徳洛內三角形
'''
#计算徳洛內三角形
def calculateDelaunayTriangles(rect, points):
    #创建subdiv(细分)
    subdiv = cv2.Subdiv2D(rect);
    
    #将各点插入subdiv
    for 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.txt
            if 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.0
    image = 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.0
    img_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 = img
        print(img.shape)
        x_min=0
        x_max=0
        y_min=0
        y_max=0
        print("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.5
            
            x=(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*h
            ymax=y+h/2.-0.08*h
            
 
            
            x_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_size
    for point in mesh:
        cv2.circle(image, (point[0], point[1]),
                   mark_size, (0, 255, 128), -1)

    # Draw the contours.
    # Eyes
    left_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)
    # Lips
    cv2.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__()的第一个参数永远是self
    def __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 faceimg
        bgnd=cv2.imread(back_img_path[0])
        faceimg=bgnd
        # global bfirstframe
        # bfirstframe=True
        global mod
        mod=0
        
    def on_img2_clicked(self, widget):
        global faceimg
        bgnd=cv2.imread(back_img_path[1])
        faceimg=bgnd
        # global bfirstframe
        # bfirstframe=True
        global mod
        mod=1
        
    def on_img3_clicked(self, widget):
        global faceimg
        bgnd=cv2.imread(back_img_path[2])       
        faceimg=bgnd
        # global bfirstframe
        # bfirstframe=True
        global mod
        mod=2
        
    def on_img4_clicked(self, widget):
        global faceimg
        bgnd=cv2.imread(back_img_path[3])       
        faceimg=bgnd
        # global bfirstframe
        # bfirstframe=True
        global mod
        mod=3        

    def on_img11_clicked(self, widget):
        global faceimg
        bgnd=cv2.imread(back_img_path[4])
        faceimg=bgnd
        # global bfirstframe
        # bfirstframe=True
        global mod
        mod=4
        
    def on_img22_clicked(self, widget):
        global faceimg
        bgnd=cv2.imread(back_img_path[5])
        faceimg=bgnd
        # global bfirstframe
        # bfirstframe=True
        global mod
        mod=5
        
    def on_img33_clicked(self, widget):
        global faceimg
        bgnd=cv2.imread(back_img_path[6])       
        faceimg=bgnd
        # global bfirstframe
        # bfirstframe=True
        global mod
        mod=6
        
    def on_img44_clicked(self, widget):
        global faceimg
        bgnd=cv2.imread(back_img_path[7])       
        faceimg=bgnd
        # global bfirstframe
        # bfirstframe=True
        global mod
        mod=7
    
    #按钮的按下响应函数,模式选择
    def on_button_pressed1(self, widget):
        global mod
        mod=0
        
    def on_button_pressed2(self, widget):
        global mod
        mod=1   
        
    def on_button_pressed3(self, widget):
        global mod
        mod=2
    

获取关键点及标志的函数

	
    

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

'''
用于初始化处理,换身体应用启动时首先被调用
'''
def process():
    
    cvs.setCustomUI()
    
    #指定输入图像的像素大小为128*128
    input_shape=[128,128]
    #输入数组的形状为一张图*128*128pix*3通道*4
    inShape =[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=1
    cap=cvs.VideoCapture(camid)
    bFace=False
    x_min,y_min,x_max,y_max=(0,0,0,0)
    fface=0.0
    global bfirstframe
    bfirstframe=True
    facepath="Biden.jpeg"
    # facepath="rs.jpeg"
    # faceimg=bgnd_mat
    global faceimg
    faceimg=cv2.resize(faceimg,(256,256))
    # 
    roi_orifirst=faceimg
    padfaceimg=faceimg
    fpoints=[]
    spoints=[]
    # mcap=cv2.VideoCapture('test.mp4')
    global mod
    mod=-1
    
    while 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=faceimg
            bFace=False
            roi_orifirst=faceimg
            padfaceimg=faceimg
            bfirstframe=True
            fpoints=[]
            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默认即为False
        if 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为True
            if 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.5
                
                x=(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*h
                ymax=y+h/2.-0.08*h
                x_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=False
                fface=ffacetmp
                    
                
                spoints=[]   
                # print('mesh:',mesh.shape)
                mesh = mesh.reshape(468, 3) / 192
                #如有第一帧
                if bfirstframe :
                    getkeypoint(roi_ori,mesh,fpoints)
                    roi_orifirst=roi_ori.copy()
                    bfirstframe=False
                    mod=-1
                    # padfaceimg=img_pad
                else:
                    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_ori
                
                shape=frame.shape
                x,y=img_pad.shape[0]/2,img_pad.shape[1]/2
                # frame=roi_ori
                frame=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)

    

你可能感兴趣的:(python,计算机视觉,目标检测,深度学习,神经网络)