首页 > 其他分享 >使用OpenCV实现换脸

使用OpenCV实现换脸

时间:2024-03-29 21:04:04浏览次数:17  
标签:实现 cv2 OpenCV POINTS im 图像 np 人脸 换脸

使用OpenCV实现换脸

换脸介绍

换脸技术,顾名思义,是一种在不改变原始人物的基本特征,如发型、脸颊轮廓等前提下,巧妙地将该人物的五官特征替换为另一人的五官特征的技术。

算法原理与流程

易容术算法的关键步骤在于精准定位图像中的人脸位置,随后利用仿射变换技术,巧妙地将一个人的五官特征移植到另一人的脸部,实现换脸效果。
具体步骤如下:

  1. 关键点定位:利用dlib库精准识别图像a和图像b中人脸的68个关键点,这些关键点为后续操作提供了重要的面部结构信息。
  2. 人脸区域获取与校正:根据关键点信息,我们绘制人脸凸包轮廓,生成对应的人脸掩模amask和bmask。鉴于图像尺寸、脸型、位置及方位的多样性,我们需对图像b中的人脸进行校正,确保其与图像a中的人脸在大小、形状上保持一致。
  3. 变换矩阵构造:基于SVD技术,利用已获取的人脸关键点,我们构造出两幅图像间的变换矩阵,该矩阵为后续的人脸对齐和融合提供了关键参数。
  4. 掩模校正与融合:利用变换矩阵,我们对图像b的掩模bmask进行校正,使其与图像a的掩模amask尽可能匹配。接着,我们将这两个校正后的掩模进行融合,得到最终的融合掩模mask。
  5. 人脸图像校正:再次利用变换矩阵,对图像b中的人脸进行校正,使其与图像a中的人脸在大小、方向上保持一致,得到校正后的图像bWarp。
  6. 颜色校正:为了使图像bWarp与图像a在颜色上更加协调,我们进行颜色校正。首先计算图像a和图像b的高斯变换,然后基于这些变换计算颜色比值ratio,最后通过调整图像b的颜色来实现与图像a的接近。
  7. 换脸操作:最终,在新的人脸图像中,我们根据掩模mask来定义融合区域。在mask指定的区域内,我们使用校正并调整颜色后的图像bcolor;而在mask以外的区域,则保持图像a的原始内容。通过这一步骤,我们成功实现了换脸效果。

效果

输入a:
在这里插入图片描述
输入b:
在这里插入图片描述
输出:
在这里插入图片描述

程序

import cv2
import dlib
import numpy as np

# ==================点集处理==================
# 关键点分配,五官的起止索引
JAW_POINTS = list(range(0, 17))
RIGHT_BROW_POINTS = list(range(17, 22))
LEFT_BROW_POINTS = list(range(22, 27))
NOSE_POINTS = list(range(27, 35))
RIGHT_EYE_POINTS = list(range(36, 42))
LEFT_EYE_POINTS = list(range(42, 48))
MOUTH_POINTS = list(range(48, 61))
FACE_POINTS = list(range(17, 68))
# 关键点集
POINTS = [LEFT_BROW_POINTS + RIGHT_EYE_POINTS +
          LEFT_EYE_POINTS + RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS]
# 处理为元组,后续使用方便
POINTStuple = tuple(POINTS)


# =================自定义函数:获取脸部(脸部掩码)=================
def getFaceMask(im, keyPoints):
    im = np.zeros(im.shape[:2], dtype=np.float64)
    for p in POINTS:
        points = cv2.convexHull(keyPoints[p])  # 获取凸包
        cv2.fillConvexPoly(im, points, color=1)  # 填充凸包
    # 单通道im构成3通道im(3,行,列),改变形状(行、列、3)适应OpenCV
    # 原有形状:(3、高、宽),改变形状为(高、宽、3)
    im = np.array([im, im, im]).transpose((1, 2, 0))
    ksize = (15, 15)
    im = cv2.GaussianBlur(im, ksize, 0)
    return im


# =========自定义函数:根据二人的脸部关键点集,构建映射矩阵M===========
def getM(points1, points2):
    # 调整数据类型
    points1 = points1.astype(np.float64)
    points2 = points2.astype(np.float64)
    # 归一化:(数值-均值)/标准差
    # 计算均值
    c1 = np.mean(points1, axis=0)
    c2 = np.mean(points2, axis=0)
    # 减去均值
    points1 -= c1
    points2 -= c2
    # 计算标准差
    s1 = np.std(points1)
    s2 = np.std(points2)
    # 除标准差
    points1 /= s1
    points2 /= s2
    # 奇异值分解,Singular Value Decomposition
    U, S, Vt = np.linalg.svd(points1.T * points2)
    # 通过U和Vt找到R
    R = (U * Vt).T
    # 返回得到的M
    return np.vstack([np.hstack(((s2 / s1) * R,
                                 c2.T - (s2 / s1) * R * c1.T)),
                      np.matrix([0., 0., 1.])])


# =================自定义函数:获取图像关键点集=================
def getKeyPoints(im):
    rects = detector(im, 1)
    s = np.matrix([[p.x, p.y] for p in predictor(im, rects[0]).parts()])
    return s


# =================自定义函数:统一颜色=================
def normalColor(a, b):
    ksize = (111, 111)  # 非常大的核,去噪等运算时为11就比较大了
    # 分别针对原始图像、目标图像进行高斯滤波
    aGauss = cv2.GaussianBlur(a, ksize, 0)
    bGauss = cv2.GaussianBlur(b, ksize, 0)
    # 计算目标图像调整颜色的权重值
    weight = aGauss / bGauss
    return b * weight


# =========模式初始化=============
PREDICTOR = "shape_predictor_68_face_landmarks.dat"
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(PREDICTOR)
# =============初始化:读取原始人脸a和b==============
a = cv2.imread(r"person/aa.jpg")
b = cv2.imread(r"person/bb.jpg")
bOriginal = b.copy()  # 用来显示原始图像b时使用
# =========step1:获取关键点集===============
aKeyPoints = getKeyPoints(a)
bKeyPoints = getKeyPoints(b)
# =============step2:获取换脸的两个人的脸部模板===================
aMask = getFaceMask(a, aKeyPoints)
bMask = getFaceMask(b, bKeyPoints)
# ============step3:根据二者的关键点集构建仿射映射的M矩阵==================
M = getM(aKeyPoints[POINTStuple], bKeyPoints[POINTStuple])
# ============step4:将b的脸部(bmask)根据M仿射变换到a上==============
dsize = a.shape[:2][::-1]
# 目标输出与图像a大小一致
# 需要注意,shape是(行、列),warpAffine参数dsize是(列、行)
# 使用a.shape[:2][::-1],获取a的(列、行)
bMaskWarp = cv2.warpAffine(bMask,
                           M[:2],
                           dsize,
                           borderMode=cv2.BORDER_TRANSPARENT,
                           flags=cv2.WARP_INVERSE_MAP)
# ============step5:获取脸部最大值(两个脸模板叠加)=================
mask = np.max([aMask, bMaskWarp], axis=0)
# =============step6:使用仿射矩阵M,将b映射到a===================
bWrap = cv2.warpAffine(b,
                       M[:2],
                       dsize,
                       borderMode=cv2.BORDER_TRANSPARENT,
                       flags=cv2.WARP_INVERSE_MAP)
# ==========step7:让颜色更自然一些=================
bcolor = normalColor(a, bWrap)
out = a * (1.0 - mask) + bcolor * mask
# =========输出原始人脸、换脸结果===============
cv2.imshow("a", a)
cv2.imshow("b", bOriginal)
cv2.imshow("out", out / 255)
cv2.waitKey()
cv2.destroyAllWindows()

标签:实现,cv2,OpenCV,POINTS,im,图像,np,人脸,换脸
From: https://blog.csdn.net/summerriver1/article/details/137122375

相关文章

  • Python 基于 xlsxwriter 实现百万数据导出 excel
    增量导出+自动切换sheet⚠️excel中的每个sheet最多只能保存1048576行数据#获取项目的根路径rootPathcurPath=os.path.abspath(os.path.dirname(__file__))rootPath=curPath[:curPath.find(你的项目名称+"/")+len(你的项目名称+"/")]#临时......
  • matlab实现神经网络
    一、原理人工神经网络是具有适应性的简单神经元组成的广泛并互连的网络,它的组织能够模拟生物神经系统对真实世界物体作出的交互式反应。人工神经网络具有自学习、自组织、较好的容错性和优良的非线性逼近能力将神经网络的学习能力引入到模糊系统中,将模糊系统的模糊化处理、模......
  • 用C语言实现汉诺塔游戏
    汉诺塔游戏。游戏目标是将A柱子上的盘子移动到C柱子上,且每次小的盘子要放在大的盘子上面。如只有一个盘子则直接移至C柱子。以如图所示为例子。3个盘子要移动至C柱子,具体步骤为:A到C,A到B,C到B,A到C,B到A,B到C,A到C。总共7步。也就是先把A柱子两个盘子(n-1)通过C柱子移......
  • 网络编程:百度api实现地理编码与逆地理编码
    1.使用geopy库实现百度地理位置编码功能:2.使用requests库实现百度地理位置编码功能:3.使用geocoder库实现百度地理位置编码功能:4.使用http.client库实现百度地理位置编码功能:5.使用socket库实现百度地理位置编码功能:6.使用学习的四个库实现百度地理位置逆编码功能:......
  • SpringBoot项目接入Nacos的实现步骤
    前言项目中没有使用nacos官方提供的方式使用SpringBoot的集成方式来进行集成,而是使用了AlibabaSpringCloud的依赖包进行集成。原因是因为官网提供的SpringBoot集成方式中,同时使用配置中心和服务发现功能,会使得服务发现功能配置的部分属性冲突不生效。最直接的就是配置中心和服......
  • 操作系统实验6之信号量的实现与应用
    操作系统中常用信号量相关系统调用函数用法1、sem_open:用于创建或打开一个命名的信号量。点击查看代码#include<semaphore.h>sem_t*sem_open(constchar*name,intoflag,mode_tmode,unsignedintvalue);name:信号量的名称,必须以斜杠开头,例如/my_semaphore。oflag:......
  • postgresql自定义函数实现功能有两个数组arr1,arr2,返回第一个数组中不在第二个数组的
    CREATEORREPLACEFUNCTIONarray_difference(arr1text[],arr2text[])RETURNStext[]AS$$DECLAREresult_arrtext[];BEGIN--初始化结果数组为一个空数组result_arr:='{}';--遍历第一个数组中的每个元素FORiIN1..array_leng......
  • postgresql自定义函数实现三个数组存在相同数据,且在第四个数组中不存在的数据
    --使用postgresql语言写一个函数,实现以下功能:--1有管理权限用户数组、列表权限用户数组、查看权限用户数组、无权限用户数组四个用户数组--2当无权限用户数组存在用户数据时,如果管理权限用户数组,列表权限用户数组,查看权限用户数组中存在相同的用户数据,并且和无权限用户数......
  • C++精品小案例:C++中的多态性及其实现、模板元编程及其在C++中的应用
    1.C++中的多态性及其实现多态性是面向对象编程的三大特性之一,它允许使用父类类型的指针或引用来指向子类对象,并通过这个父类类型的指针或引用来调用实际子类的成员函数。这样,就可以在运行时确定应该调用哪个具体的函数实现,从而实现一个接口多种形态。多态性主要通过虚函数来......
  • 【经典游戏】Java实现飞机大战小游戏
    【引言】90、00后的童年是游戏机,当时的飞机大战,一度风靡全球。 经典项目java实现飞机大战射击游戏资源-CSDN文库一、游戏设计实现飞机大战游戏是一个很有趣的项目,可以帮助你学习和练习Java编程的各种方面,包括面向对象设计、图形界面编程等。下面是一个详细的分析,涵盖了......