zoukankan      html  css  js  c++  java
  • 基于OpenCV的面部交换

    需要装python库

    OpenCV

    dlib

    docopt(根据打开方式选择是否装)

    # -*- coding: UTF-8
    #本电脑试运行 命令 python F:python_projectswapswapface.py E:picturefacexx.jpg E:picturefaceangelababy.jpg
    #声明编码格式 方便中文注释~
    #实现命令行解析器 打开格式python 代码文件路径 图片1路径 图片2路径
    """
    swapface.py
    function:put one face's facial features onto another
    
    based on OpenCV,dlib
    this moudle used docopt
    
    Usage: faceswap [options] <image1> [<image2>]
    
    Options:
          -v --version      查看版本号
          -h --help         显示这个帮助文档
          -s --see          查看图片
          -c --combine      换脸
    """
    #命令行参数解析说明  [options]指定特定选项 完成特定任务
    import cv2   #导入调用opencv库
    import dlib  #导入dlib库
    import numpy as np   #以np别名导入docopt
    from docopt import docopt #导入docopt包中的docopt到文件
    __version__ = 'swapface 1.0'   #版本号
    
    #以下是一些要用的常量及路径初始化部分
    PREDICTOR_PATH = "F:/python_project/shape_predictor_68_face_landmarks.dat"   #训练模式的路径 注意斜杠别反了
    
    #图像放缩因子
    SCALE_FACTOR = 1
    FEATHER_AMOUNT = 11
    
    FACE_POINTS = list(range(17, 68))   #
    MOUTH_POINTS = list(range(48, 61))   #
    RIGHT_BROW_POINTS = list(range(17, 22))   #右眉
    LEFT_BROW_POINTS = list(range(22, 27))   #左眉
    RIGHT_EYE_POINTS = list(range(36, 42))   #右眼
    LEFT_EYE_POINTS = list(range(42, 48))   #左眼
    NOSE_POINTS = list(range(27, 35))   #
    JAW_POINTS = list(range(0, 17))   #下巴
    #元组 选取左右眉,左右眼,鼻子,嘴巴  位置的特征点索引
    ALIGN_POINTS = (LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS +
                                   RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS)
    #元组转列表 选取第二张脸对应面部特征用于覆盖到第一张脸
    OVERLAY_POINTS = [
        LEFT_EYE_POINTS + RIGHT_EYE_POINTS + LEFT_BROW_POINTS + RIGHT_BROW_POINTS,
        NOSE_POINTS + MOUTH_POINTS,
    ]
    #定义用于颜色矫正的模糊量,作为瞳孔距离的系数
    COLOUR_CORRECT_BLUR_FRAC = 0.6
    
    #实例化脸部检测器
    detector = dlib.get_frontal_face_detector()
    
    #根据路径加载训练模型 并 实例化特征提取器
    predictor = dlib.shape_predictor(PREDICTOR_PATH)
    
    #定义多脸和无脸类处理异常 以及 未打开异常
    class TooManyFaces(Exception):
        pass
    
    class NoFaces(Exception):
        pass
    
    class OpenFailed(Exception):
        pass
    
    #显示图片
    def show_image(board,path,time):
        # 读取这个图片
        image = cv2.imread(path,cv2.IMREAD_COLOR)
    
        # 初始化一个名为Combine的窗口
        cv2.namedWindow(board, flags=0)
    
        # 显示图片
        cv2.imshow(board, image)
    
        # 等待按键释放窗口
        if(time == 0):
            cv2.waitKey(time)
    
    #获取特征点函数
    def get_landmarks(im):
        #特征检测器
        rects = detector(im, 1)
    
        #如果检测到多张脸 抛多脸异常
        if len(rects) > 1:
            raise TooManyFaces
    
        #如果没有检测到脸 抛无脸异常
        if len(rects) == 0:
            raise NoFaces
    
        #返回一个n*2维的矩阵,该矩阵由检测到的脸部特征点坐标组成 特征提取器predictor
        return np.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()])
    
    #绘制凸多边形
    def draw_convex_hull(im, points, color):
        points = points.astype(np.int32)
        #检查一个曲线是否有凸性缺陷并纠正
        points = cv2.convexHull(points)
    
        #凸多边形填充成想要的颜色
        cv2.fillConvexPoly(im, points, color=color)
    
    #获取面部的掩码
    def get_face_mask(im, landmarks):
        im = np.zeros(im.shape[:2], dtype=np.float64)
    
        for group in OVERLAY_POINTS:
            draw_convex_hull(im,
                             landmarks[group],
                             color=1)
    
        im = np.array([im, im, im]).transpose((1,2,0))
    
        #应用高斯模糊
        im = (cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0) > 0) * 1.0
        im = cv2.GaussianBlur(im, (FEATHER_AMOUNT, FEATHER_AMOUNT), 0)
    
        return im
    
    #使用普氏分析(Procrustes analysis)调整脸部 包括倾斜位置不同等等都可以调整
    def transformation_from_points(points1,points2):
        #输入矩阵转换为64位float类型
        points1 = points1.astype(np.float64)
        points2 = points2.astype(np.float64)
    
        #对矩阵格列取均值,求矩心
        c1 = np.mean(points1,axis=0)
        c2 = np.mean(points2,axis=0)
    
        #每一个点集减去它的矩心。这两个矩心c1和c2可以用来找到完整的解决方案。
        points1 -= c1
        points2 -= c2
    
        #标准差
        s1 = np.std(points1)
        s2 = np.std(points2)
    
        #除去标准差 这消除了问题的组件缩放偏差。
        points1 /= s1
        points2 /= s2
    
        U, S, Vt = np.linalg.svd(points1.T * points2)
    
        R = (U * Vt).T
    
        #公式
        return np.vstack([np.hstack(((s2 / s1) * R,
                                     c2.T - (s2 / s1) * R * c1.T)),
                          np.matrix([0., 0., 1.])])
    
    #读取图片文件并获取特征点
    def read_im_and_landmarks(fname):
        #以RGB(红绿蓝)模式读取图片 就是读入彩色图片
        im = cv2.imread(fname,cv2.IMREAD_COLOR)
    
        if im is None:
            raise OpenFailed
    
        #对图片进行适当的放缩
        im = cv2.resize(im, (im.shape[1] * SCALE_FACTOR,
                             im.shape[0] * SCALE_FACTOR))
    
        #获取特征点
        s = get_landmarks(im)
    
        #返回图片和特征点组成的元组
        return im, s
    
    
    #变换图像
    def warp_im(im,M,dshape):
        output_im = np.zeros(dshape,dtype=im.dtype)
    
        #仿射函数,能对图像进行几何变换 三个参数 输入图像 变换矩阵np.float32类型 变换之后图像
        cv2.warpAffine(im,
                       M[:2],
                       (dshape[1],dshape[0]),
                       dst=output_im,
                       borderMode=cv2.BORDER_TRANSPARENT,
                       flags=cv2.WARP_INVERSE_MAP)
        return output_im
    
    #修正颜色 使得两张图片拼接时显得更加自然
    def correct_colours(im1,im2,landmarks1): #传入图1和图2以及图1的特征点
        blur_amount = COLOUR_CORRECT_BLUR_FRAC * np.linalg.norm(
            np.mean(landmarks1[LEFT_EYE_POINTS],axis=0) -
            np.mean(landmarks1[RIGHT_EYE_POINTS],axis=0))
    
        blur_amount = int(blur_amount)
    
        if blur_amount % 2 == 0:
            blur_amount += 1
    
        #高斯模糊
        im1_blur = cv2.GaussianBlur(im1,(blur_amount,blur_amount),0)
        im2_blur = cv2.GaussianBlur(im2,(blur_amount,blur_amount),0)
    
        #避免下面出现0除
        im2_blur += (128 * (im2_blur <= 1.0)).astype(im2_blur.dtype)
    
        return (im2.astype(np.float64) * im1_blur.astype(np.float64) / im2_blur.astype(np.float64))
    #主过程函数
    def main():
        arguments = docopt(__doc__, version=__version__)   #调用函数返回arguments字典型变量,记录了选项是否被选用了,参数的值是什么等信息,当程序从命令行运行时,我们就是根据arguments变量的记录来得知用户输入的选项和参数信息。
    
        if(arguments['--see'] != False):
            if(arguments['<image1>'] is not None):
                show_image('image1',arguments['<image1>'],1)
            if(arguments['<image2>'] is not None):
                show_image('image2',arguments['<image2>'],1)
        if(arguments['--combine'] == False):
            exit(0)
        # 1.获取图像与特征点
        im1,landmarks1 = read_im_and_landmarks(arguments['<image1>'])
        im2,landmarks2 = read_im_and_landmarks(arguments['<image2>'])
    
        # 2.选取两组图像特征矩阵中所需要的面部部位 计算转换信息 返回变换矩阵
        M = transformation_from_points(landmarks1[ALIGN_POINTS],landmarks2[ALIGN_POINTS])
    
        # 3.获取im2的面部掩码
        mask = get_face_mask(im2,landmarks2)
    
        # 4.将im2的掩码进行变化,使得与im1相符
        warped_mask = warp_im(mask,M,im1.shape)
    
        # 5.将二者的掩码进行连通
        combined_mask = np.max([get_face_mask(im1,landmarks1),warped_mask],axis=0)
    
        # 6.将第二幅图调整到与第一幅图相符
        warped_im2 = warp_im(im2,M,im1.shape)
    
        # 7.将im2的皮肤颜色进行修正,使其和im1的颜色尽量协调
        warped_corrected_im2 = correct_colours(im1,warped_im2,landmarks1)
    
        # 组合图像,获得结果
        output_im = im1 * (1.0 - combined_mask) + warped_corrected_im2 * combined_mask
    
        SavePath = 'F:/python_project/output.jpg'
    
        #保存图像 默认项目文件夹
        cv2.imwrite(SavePath,output_im)
    
        show_image('Combine',SavePath,0)
    
    if __name__=='__main__':
        main()

    命令行里运行 根据帮助文档 python 程序路径 -c 图片1路径 图片2路径

  • 相关阅读:
    RabbitMq使用说明
    php使用rabbitmq需安装amqp拓展协议
    新建springboot web项目pom报错
    HttpRunner Manager 接口自动化平台搭建
    数据库存储过程进行批量插入数据
    Windows系统下Robot Framework的安装
    利用Charles模拟客户端弱网环境进行弱网测试
    JMeter进行简单的接口压测
    JMeter的安装和使用
    grep, sed 和 awk 学习总结
  • 原文地址:https://www.cnblogs.com/8023spz/p/9551204.html
Copyright © 2011-2022 走看看