变脸大师:基于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()

效果展示

准备换脸的图

准备接收换脸的图

结果

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

相关推荐
说私域3 分钟前
构建有温度的用户关系:开源 AI 智能名片、链动 2+1 模式与 S2B2C 商城小程序的作用
人工智能·小程序
计算机学姐3 分钟前
基于python+django+vue的影视推荐系统
开发语言·vue.js·后端·python·mysql·django·intellij-idea
newxtc12 分钟前
【天怡AI-注册安全分析报告-无验证方式导致安全隐患】
人工智能·安全
扎克begod25 分钟前
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
java·开发语言·python
WF1998071926 分钟前
秋招自我介绍
人工智能
Hoper.J28 分钟前
李宏毅2024生成式人工智能导论 中文镜像版指导与作业
人工智能·aigc
yuchangchenTT30 分钟前
你敢相信吗,我用AI撸了一个在线计算器网站!
人工智能·实用工具·365快速计算器·在线计算器
云天徽上36 分钟前
【目标检测】labelimg图像标注软件的使用流程
人工智能·目标检测·计算机视觉
不睡懒觉的橙38 分钟前
【医疗大数据】医疗保健领域的大数据管理:采用挑战和影响
大数据·人工智能·信息可视化·人机交互·健康医疗
Book_熬夜!1 小时前
Python基础(九)——正则表达式
python·正则表达式·php