变脸大师:基于OpenCV与Dlib的人脸换脸技术实现

目录

简介

重新简介

思路解析

[1. 加载人脸检测器和特征点预测模型](#1. 加载人脸检测器和特征点预测模型)

[2. 读取两张人脸图片](#2. 读取两张人脸图片)

[3. 获取人脸的特征点](#3. 获取人脸的特征点)

[4. 使用Delaunay三角剖分](#4. 使用Delaunay三角剖分)

[5. 仿射变换三角形](#5. 仿射变换三角形)

[6. 三角形变形并复制](#6. 三角形变形并复制)

[7. 脸部轮廓掩模](#7. 脸部轮廓掩模)

[8. 无缝克隆换脸](#8. 无缝克隆换脸)

[9. 缩放图像](#9. 缩放图像)

[10. 显示换脸结果](#10. 显示换脸结果)

整体代码

效果展示

准备换脸的图

准备接收换脸的图

结果


简介

能让你秒变吴彦祖(或者你的女神,随你挑)的神奇换脸程序!呀哈哈哈哈(博主本人已疯),熬夜秃头(夸张一下)换来的。

想象一下,早上醒来,对着镜子一照,哎呀妈呀,这不是我一直梦寐以求的那张脸吗?别急着去整容医院,先试试我的换脸神器吧!只需简单几步,你就能在朋友圈里上演一场"大变活人"的好戏,保证让你的朋友们惊掉下巴,直呼内行!

当然啦,开发这玩意儿可不是闹着玩的。我经历了无数次的代码调试、算法优化,还有那些让人头疼的bug大战。但每当看到换脸效果越来越自然,我就像看到了自己的孩子慢慢长大一样,心里那叫一个美滋滋啊!

好啦,废话不多说,接下来就让我带你走进这个换脸世界的奇妙之旅吧!记得系好安全带,因为接下来的内容可能会让你的脑洞大开,笑到肚子疼哦!

呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃呃

写得出来上面的话我真的是个正常人吗

重新简介

在现代的图像处理技术中,人脸识别和图像变形已经不再是遥不可及的技术。通过一些简单的算法和工具,我们可以实现很多有趣的效果,比如今天要介绍的"人脸置换"。在这篇文章中,我将向大家展示如何使用OpenCV和Dlib库实现一个有趣的换脸程序,代码不仅能识别人脸,还能通过仿射变换,将一张脸的特征平滑地融合到另一张脸上。that`s ez


思路解析

实现了两个图像之间的"换脸"功能,使用了Dlib库的人脸检测器、特征点预测器,以及OpenCV的图像处理函数。具体的实现思路如下:

1. 加载人脸检测器和特征点预测模型

复制代码
 
python 复制代码
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('./model/shape_predictor_68_face_landmarks.dat')

首先,代码加载了Dlib的正面人脸检测器detector,以及68个特征点预测模型predictor,后者会用来提取人脸的关键特征点。

2. 读取两张人脸图片

复制代码
 
python 复制代码
img1 = cv2.imread("./imgs/006.jpg")
img2 = cv2.imread("./imgs/008.jpg")

接着,使用OpenCV的imread函数读取两张图像。img1是要移植脸部特征的图片,img2是脸部特征的目标图片。

3. 获取人脸的特征点

复制代码
 
python 复制代码
def get_landmarks(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    faces = detector(gray)
    if len(faces) == 0:
        return None
    landmarks = predictor(gray, faces[0])
    return np.array([[p.x, p.y] for p in landmarks.parts()])

这个函数将图像转换为灰度图后,通过detector检测人脸区域。如果检测到人脸,则使用predictor提取人脸的68个特征点,返回一个包含这些特征点坐标的NumPy数组。每个特征点对应面部的特定位置,如眼睛、鼻子、嘴巴等。

4. 使用Delaunay三角剖分

复制代码
 
python 复制代码
def get_triangles(landmarks):
    rect = cv2.boundingRect(np.array([landmarks]))
    subdiv = cv2.Subdiv2D(rect)
    subdiv.insert(landmarks.tolist())
    triangles = subdiv.getTriangleList()
    triangles = np.array(triangles, dtype=np.int32)
    ...

该函数通过Delaunay三角剖分算法,将人脸特征点分割为多个三角形。Delaunay三角剖分可以确保生成的三角形不会产生过小的角度,这样更利于仿射变换。

5. 仿射变换三角形

复制代码
 
python 复制代码
def apply_affine_transform(src, src_tri, dst_tri, size):
    warp_mat = cv2.getAffineTransform(np.float32(src_tri), np.float32(dst_tri))
    dst = cv2.warpAffine(src, warp_mat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR,
                         borderMode=cv2.BORDER_REFLECT_101)
    return dst

这个函数实现了将一个三角形从源图像变换到目标图像。通过仿射变换,将src_tri中的三角形变换为dst_tri中的相应三角形,返回变换后的图像。

6. 三角形变形并复制

复制代码
 
python 复制代码
def warp_triangle(img1, img2, t1, t2):
    r1 = cv2.boundingRect(np.array([t1]))
    r2 = cv2.boundingRect(np.array([t2]))
    ...

warp_triangle函数使用仿射变换将每个三角形从img1中的位置变形到img2_new_face中的对应位置。这是将源人脸区域贴到目标人脸的核心步骤。

7. 脸部轮廓掩模

复制代码
 
python 复制代码
hull2 = cv2.convexHull(np.array(landmarks2))
mask = np.zeros_like(img2[:, :, 0])
cv2.fillConvexPoly(mask, np.int32(hull2), 255)

在仿射变换之后,通过convexHull构建目标人脸区域的凸包,即覆盖整个脸部的轮廓,并生成一个掩模mask用于后续的无缝克隆。

8. 无缝克隆换脸

复制代码
 
python 复制代码
seamless_img = cv2.seamlessClone(np.uint8(img2_new_face), img2, mask, center, cv2.NORMAL_CLONE)

seamlessClone函数将换脸后的区域与目标图像进行无缝融合。这个函数会避免图像边界产生不自然的效果,从而使换脸看起来更加逼真。

9. 缩放图像

复制代码
 
python 复制代码
scale_factor = 0.9
dim = (int(seamless_img.shape[1] * scale_factor), int(seamless_img.shape[0] * scale_factor))
resized_img = cv2.resize(seamless_img, dim, interpolation=cv2.INTER_AREA)

为了适应显示器大小(你们自己调调,分辨率高的图片就调小一点,分辨率低的就拉满吧),这里设置了一个缩放因子scale_factor(0.9),将最终生成的图像缩小到原来的90%。

10. 显示换脸结果

复制代码
 
python 复制代码
cv2.imshow("Face Swap", resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

最后,使用imshow显示处理后的换脸结果,并等待用户按下任意键后关闭窗口。


整体代码

python 复制代码
import cv2
import numpy as np
import dlib

# 加载人脸检测器和特征点预测模型
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('./model/shape_predictor_68_face_landmarks.dat')

# 读取两张人脸图片
img1 = cv2.imread("./imgs/006.jpg")
img2 = cv2.imread("./imgs/002.jpg")

# 获取人脸的特征点
def get_landmarks(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    faces = detector(gray)
    if len(faces) == 0:
        return None
    landmarks = predictor(gray, faces[0])
    return np.array([[p.x, p.y] for p in landmarks.parts()])

# 使用Delaunay三角剖分
def get_triangles(landmarks):
    rect = cv2.boundingRect(np.array([landmarks]))
    subdiv = cv2.Subdiv2D(rect)
    subdiv.insert(landmarks.tolist())
    triangles = subdiv.getTriangleList()
    triangles = np.array(triangles, dtype=np.int32)

    indexes = []
    for t in triangles:
        pts = []
        for i in range(3):
            for j, p in enumerate(landmarks):
                if (t[i * 2], t[i * 2 + 1]) == (p[0], p[1]):
                    pts.append(j)
        if len(pts) == 3:
            indexes.append(pts)
    return indexes

# 仿射变换三角形
def apply_affine_transform(src, src_tri, dst_tri, size):
    warp_mat = cv2.getAffineTransform(np.float32(src_tri), np.float32(dst_tri))
    dst = cv2.warpAffine(src, warp_mat, (size[0], size[1]), None, flags=cv2.INTER_LINEAR,
                         borderMode=cv2.BORDER_REFLECT_101)
    return dst

# 变形并复制
def warp_triangle(img1, img2, t1, t2):
    r1 = cv2.boundingRect(np.array([t1]))
    r2 = cv2.boundingRect(np.array([t2]))

    t1_rect = []
    t2_rect = []
    t2_rect_int = []

    for i in range(3):
        t1_rect.append(((t1[i][0] - r1[0]), (t1[i][1] - r1[1])))
        t2_rect.append(((t2[i][0] - r2[0]), (t2[i][1] - r2[1])))
        t2_rect_int.append(((t2[i][0] - r2[0]), (t2[i][1] - r2[1])))

    img1_rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]

    size = (r2[2], r2[3])
    img2_rect = apply_affine_transform(img1_rect, t1_rect, t2_rect, size)

    mask = np.zeros((r2[3], r2[2], 3), dtype=np.float32)
    cv2.fillConvexPoly(mask, np.int32(t2_rect_int), (1.0, 1.0, 1.0), 16, 0)

    img2_rect = img2_rect * 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]] + img2_rect

# 主程序
landmarks1 = get_landmarks(img1)
landmarks2 = get_landmarks(img2)

if landmarks1 is None or landmarks2 is None:
    print("未能检测到人脸")
    exit()

triangles = get_triangles(landmarks1)

# 复制人脸区域
img2_new_face = np.zeros_like(img2)

for tri in triangles:
    t1 = [landmarks1[tri[0]], landmarks1[tri[1]], landmarks1[tri[2]]]
    t2 = [landmarks2[tri[0]], landmarks2[tri[1]], landmarks2[tri[2]]]

    warp_triangle(img1, img2_new_face, t1, t2)

# 构建脸部轮廓掩模
hull2 = cv2.convexHull(np.array(landmarks2))
mask = np.zeros_like(img2[:, :, 0])
cv2.fillConvexPoly(mask, np.int32(hull2), 255)

# 将换脸后的区域融合
r = cv2.boundingRect(np.int32(hull2))  # 使用 np.int32 类型
center = ((r[0] + int(r[2] / 2), r[1] + int(r[3] / 2)))

seamless_img = cv2.seamlessClone(np.uint8(img2_new_face), img2, mask, center, cv2.NORMAL_CLONE)

# 缩放因子
scale_factor = 0.3

# 计算新尺寸
width = int(seamless_img.shape[1] * scale_factor)
height = int(seamless_img.shape[0] * scale_factor)
dim = (width, height)

# 缩放图像
resized_img = cv2.resize(seamless_img, dim, interpolation=cv2.INTER_AREA)

# 显示换脸效果
cv2.imshow("Face Swap", resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

效果展示

准备换脸的图

准备接收换脸的图

结果

我正如彭于晏一样的帅气(*^▽^*)

相关推荐
果冻人工智能19 分钟前
2025 年将颠覆商业的 8 大 AI 应用场景
人工智能·ai员工
代码不行的搬运工21 分钟前
神经网络12-Time-Series Transformer (TST)模型
人工智能·神经网络·transformer
进击的六角龙21 分钟前
深入浅出:使用Python调用API实现智能天气预报
开发语言·python
檀越剑指大厂21 分钟前
【Python系列】浅析 Python 中的字典更新与应用场景
开发语言·python
石小石Orz23 分钟前
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
javascript·人工智能·算法
湫ccc29 分钟前
Python简介以及解释器安装(保姆级教学)
开发语言·python
孤独且没人爱的纸鹤32 分钟前
【深度学习】:从人工神经网络的基础原理到循环神经网络的先进技术,跨越智能算法的关键发展阶段及其未来趋势,探索技术进步与应用挑战
人工智能·python·深度学习·机器学习·ai
阿_旭34 分钟前
TensorFlow构建CNN卷积神经网络模型的基本步骤:数据处理、模型构建、模型训练
人工智能·深度学习·cnn·tensorflow
羊小猪~~35 分钟前
tensorflow案例7--数据增强与测试集, 训练集, 验证集的构建
人工智能·python·深度学习·机器学习·cnn·tensorflow·neo4j
lzhlizihang37 分钟前
python如何使用spark操作hive
hive·python·spark