《OpenCV》—— dlib(换脸操作)

news2025/4/21 18:25:46

文章目录

    • dlib换脸介绍
    • 仿射变换
      • 在 dlib 换脸中的应用
    • 换脸操作

dlib换脸介绍

  • dlib 换脸是基于 dlib 库实现的一种人脸替换技术,以下是关于它的详细介绍:
    • 原理
      • 人脸检测:dlib 库中包含先进的人脸检测器,如基于 HOG(方向梯度直方图)特征和线性分类器的方法,能够在图像或视频帧中快速定位人脸的位置,确定人脸的边界框。
      • 关键点检测:利用 dlib 预训练的形状预测模型,如shape_predictor_68_face_landmarks.dat,可以精准定位出人脸的 68 个关键点,这些点分布在眼睛、眉毛、鼻子、嘴巴、下巴等部位,精确描述了人脸的形状和表情。
      • 图像变换与融合:通过计算源人脸和目标人脸关键点之间的变换关系,如仿射变换、Delaunay 三角剖分等,将源人脸的形状和姿态调整到与目标人脸匹配。然后,采用图像融合技术,将调整后的源人脸与目标图像进行融合,使换脸效果更加自然。
    • 实现步骤
      • 安装相关库:主要安装 dlib 库,还可能需要 opencv、numpy 等库配合。在 Python 中可以使用pip install dlib等命令安装。
      • 人脸检测与关键点提取:使用 dlib 的人脸检测器和形状预测器,加载图像或视频帧,检测其中的人脸并提取关键点。
      • 计算变换关系:根据源人脸和目标人脸的关键点,计算出两者之间的变换矩阵,确定如何将源人脸变换到目标人脸的位置和姿态。
      • 人脸替换:将源人脸按照计算出的变换关系进行变形和调整,然后将其融合到目标图像的对应位置上。
      • 后处理:对换脸后的图像进行一些后处理操作,如调整颜色、亮度、对比度等,使换脸效果更加自然逼真。
    • 优缺点
      • 优点:具有较高的人脸检测和关键点定位精度,能够处理不同姿态、表情和光照条件下的人脸;提供了丰富的函数和工具,方便开发者进行二次开发和集成;在处理速度上相对较快,能够满足一些实时性要求不高的应用场景。
      • 缺点:对图像和视频的质量要求较高,如果输入的图像分辨率低、模糊或存在遮挡,可能会影响换脸效果;对于复杂的场景和特殊的光照条件,换脸效果可能不够自然;在实时性要求较高的场景中,如实时视频通话换脸,可能需要进一步优化才能满足性能要求。

仿射变换

仿射变换(Affine Transformation)是一种在二维或三维空间中广泛应用的线性变换,它能够保持图像的 “平直性” 和 “平行性”,在计算机视觉、图形学等领域有诸多应用,比如在 dlib 换脸中就用于调整源人脸的形状和姿态以匹配目标人脸。
在这里插入图片描述

在这里插入图片描述

在 dlib 换脸中的应用

在这里插入图片描述

图片:在这里插入图片描述

仿射变换代码:

import cv2
import numpy as np

# 读取图像,这里读取的图像文件名为 'c_luo.png'
# cv2.imread 函数用于从指定路径读取图像,返回一个表示图像的 NumPy 数组
img = cv2.imread('c_luo.png')

# 获取图像的高度和宽度
# img.shape 返回一个包含图像维度信息的元组,元组的前两个元素分别是图像的高度和宽度
height, width = img.shape[:2]

# 定义源图像的三个关键点
# 这里使用 np.float32 创建一个 3x2 的浮点型 NumPy 数组
# 三个点分别是图像的左上角 (0, 0)、左下角 (0, height - 1) 和右上角 (width - 1, 0)
mat_src = np.float32([[0, 0], [0, height - 1], [width - 1, 0]])

# 定义目标图像对应的三个关键点
# 同样是一个 3x2 的浮点型 NumPy 数组
# 这里的三个点是经过变换后源图像三个关键点对应的目标位置
mat_dst = np.float32([[0, 0], [100, height - 100], [width - 100, 100]])

# 计算仿射变换矩阵
# cv2.getAffineTransform 函数根据源图像和目标图像的三个对应关键点,计算仿射变换矩阵
# 该矩阵用于描述从源图像到目标图像的仿射变换关系
M = cv2.getAffineTransform(mat_src, mat_dst)

# 应用仿射变换
# cv2.warpAffine 函数将仿射变换应用到输入图像 img 上
# M 是前面计算得到的仿射变换矩阵
# (width, height) 是输出图像的大小
dst = cv2.warpAffine(img, M, (width, height))

# 将原始图像和经过仿射变换后的图像水平拼接在一起
# np.hstack 函数用于将多个数组水平堆叠,方便同时显示原始图像和变换后的图像
imgs = np.hstack([img, dst])

# 创建一个可调整大小的窗口
# cv2.namedWindow 函数用于创建一个指定名称的窗口,cv2.WINDOW_NORMAL 表示窗口可以调整大小
cv2.namedWindow('imgs', cv2.WINDOW_NORMAL)

# 在创建的窗口中显示拼接后的图像
cv2.imshow('imgs', imgs)

# 等待用户按下任意按键
# cv2.waitKey(0) 表示无限期等待用户按键,按键后程序继续执行
cv2.waitKey(0)

这段代码的主要功能是对一张图像进行仿射变换,并将原始图像和变换后的图像拼接在一起显示。通过指定源图像和目标图像的三个对应关键点,计算出仿射变换矩阵,然后将该变换应用到图像上。

结果:
在这里插入图片描述

换脸操作

对下方图片进行换脸操作:
在这里插入图片描述
在这里插入图片描述
实现代码:

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):
    """
    生成人脸掩码
    :param im: 输入图像
    :param keyPoints: 人脸关键点
    :return: 人脸掩码图像
    """
    # 创建一个与输入图像高度和宽度相同的零矩阵
    im = np.zeros(im.shape[:2], dtype=np.float64)
    for p in POINTS:
        # 计算关键点的凸包
        points = cv2.convexHull(keyPoints[p])
        # 填充凸包区域为 1
        cv2.fillConvexPoly(im, points, color=1)
    # 将单通道掩码扩展为三通道
    im = np.array([im, im, im]).transpose((1, 2, 0))
    # 对掩码进行高斯模糊处理
    im = cv2.GaussianBlur(im, (25, 25), 0)
    return im

def getM(points1, points2):
    """
    计算仿射变换矩阵
    :param points1: 源图像的关键点
    :param points2: 目标图像的关键点
    :return: 仿射变换矩阵
    """
    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

    # 进行奇异值分解
    U, S, Vt = np.linalg.svd(points1.T * points2)
    # 计算旋转矩阵
    R = (U * Vt).T
    return np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T))

def getKeyPoints(im):
    """
    检测图像中的人脸关键点
    :param im: 输入图像
    :return: 人脸关键点矩阵
    """
    # 使用 dlib 的人脸检测器检测人脸
    rects = detector(im, 1)
    # 使用 dlib 的形状预测器预测关键点
    shape = predictor(im, rects[0])
    # 将关键点转换为矩阵形式
    s = np.matrix([[p.x, p.y] for p in shape.parts()])
    return s

def normalColor(a, b):
    """
    颜色校正
    :param a: 源图像
    :param b: 目标图像
    :return: 颜色校正后的目标图像
    """
    # 定义高斯核大小
    ksize = (71, 71)
    # 对源图像和目标图像进行高斯模糊
    aGauss = cv2.GaussianBlur(a, ksize, 0)
    bGauss = cv2.GaussianBlur(b, ksize, 0)
    # 避免除以零
    bGauss = np.where(bGauss == 0, 1e-8, bGauss)
    # 计算权重
    weight = aGauss / bGauss
    return b * weight

# 读取源图像和目标图像
a = cv2.imread('c_luo.png')
b = cv2.imread('mu_ba_pei.png')

# 初始化 dlib 的人脸检测器
detector = dlib.get_frontal_face_detector()
# 初始化 dlib 的形状预测器,需要提前下载 shape_predictor_68_face_landmarks.dat 文件
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')

# 检测源图像和目标图像的人脸关键点
aKeyPoints = getKeyPoints(a)
bKeyPoints = getKeyPoints(b)

# 保存目标图像的原始副本
bOriginal = b.copy()

# 生成源图像的人脸掩码
aMask = getFaceMask(a, aKeyPoints)
# 显示源图像的人脸掩码
cv2.imshow('aMask', aMask)
cv2.waitKey()

# 生成目标图像的人脸掩码(使用源图像的关键点)
bMask = getFaceMask(b, aKeyPoints)
# 显示目标图像的人脸掩码
cv2.imshow('bMask', bMask)
cv2.waitKey()

# 计算仿射变换矩阵
M = getM(aKeyPoints[POINTStuple], bKeyPoints[POINTStuple])

# 获取源图像的尺寸(宽度和高度)
dsize = a.shape[:2][::-1]

# 对目标图像的掩码进行仿射变换
bMaskWarp = cv2.warpAffine(bMask, M, dsize,
                           borderMode=cv2.BORDER_TRANSPARENT,
                           flags=cv2.WARP_INVERSE_MAP)
# 显示变换后的目标图像掩码
cv2.imshow('bMaskWarp', bMaskWarp)
cv2.waitKey()

# 合并源图像掩码和变换后的目标图像掩码
mask = np.max([aMask, bMaskWarp], axis=0)
# 显示合并后的掩码
cv2.imshow('mask', mask)
cv2.waitKey()

# 对目标图像进行仿射变换
bWrap = cv2.warpAffine(b, M, dsize,
                       borderMode=cv2.BORDER_TRANSPARENT,
                       flags=cv2.WARP_INVERSE_MAP)
# 显示变换后的目标图像
cv2.imshow('bWrap', bWrap)
cv2.waitKey()

# 对变换后的目标图像进行颜色校正
bcolor = normalColor(a, bWrap)
# 显示颜色校正后的目标图像
cv2.imshow('bcolor', bcolor)
cv2.waitKey()

# 融合源图像和颜色校正后的目标图像
out = a * (1.0 - mask) + bcolor * mask
# 显示源图像
cv2.imshow('a', a)
# 显示目标图像的原始副本
cv2.imshow('b', bOriginal)
# 显示换脸后的结果图像
cv2.imshow('out', out / 255)
cv2.waitKey()
# 关闭所有 OpenCV 窗口
cv2.destroyAllWindows()

结果:

在这里插入图片描述
可通过结果看出换脸成功。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2313954.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

修改Flutter项目使用的JAVA版本

使用Android studio开发Flutter过程中,会默认使用Android studio自带的JDK。因为新版Android studio中的JDK版本过高,导致项目编译时总是无法完成,报【 unsupported class file major version 65】错误,如下: 解决这个…

虚拟dom的diff中的双端比较算法

‌双端比较算法是Vue中用于高效比较新旧VNode子节点的一种策略‌。该算法的核心思想是,通过从新旧VNode子节点的两端开始比较,逐步向中间靠拢,以找到最小的差异并据此更新DOM。以下是双端比较算法的大致流程: ‌初始化指针‌&…

VMware安装Windows server 2016

1、新建虚拟机,选择自定义模式 2、选择兼容性 4、命名虚拟机 5、固件类型 EFI 虚拟磁盘类型,不同电脑推荐的类型不同,用默认的就行 删除声卡和打印机 检查网络配置 选择本地的Windows server 2016的系统镜像,系统镜像可以去Window…

HippoRAG 2 原理精读

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 整体流程离线索引阶段在线检索和问答阶段 总结 整体流程 从上图可以看出,整个流程分为两个阶段 1、离线索引阶段 2、在线检索和问答阶段 离线索引阶段…

三:FFMPEG拉流读取模块的讲解

FFMPEG拉流读取模块在远程监控项目最核心的作用是读取UVC摄像头传输的H264码流,并对其码流进行帧的提取,提取完成之后则把数据传输到VDEC解码模块进行解码。而在我们这个项目中,UVC推流的功能由FFMPEG的命令完成。 FFMPEG拉流读取模块的API…

《苍穹外卖》SpringBoot后端开发项目核心知识点与常见问题整理(DAY1 to DAY3)

目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功: 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具:Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与Ap…

QT系列教程(20) Qt 项目视图便捷类

视频连接 https://www.bilibili.com/video/BV1XY41127t3/?vd_source8be9e83424c2ed2c9b2a3ed1d01385e9 Qt项目视图便捷类 Qt项目视图提供了一些便捷类,包括QListWidget, QTableWidget, QTreeWidget等。我们分别介绍这几个便捷类。 我们先创建一个Qt …

动态扩缩容引发的JVM堆内存震荡:从原理到实践的GC调优指南

目录 一、典型案例:系统发布后的GC雪崩事件 (一)故障现象 1. 刚刚启动时 GC 次数较多 2. 堆内存锯齿状波动 3. GC日志特征:Allocation Failure (二)问题定位 二、原理深度解析:JVM内存弹…

AI智能眼镜主控芯片:技术演进与产业生态的深度解析

一、AI智能眼镜的技术挑战与主控芯片核心诉求 AI智能眼镜作为XR(扩展现实)技术的代表产品,其核心矛盾在于性能、功耗与体积的三角平衡。主控芯片作为设备的“大脑”,需在有限空间内实现复杂计算、多模态交互与全天候续航&#xf…

微服务拆分-远程调用

我们在查询购物车列表的时候,它有一个需求,就是不仅仅要查出购物车当中的这些商品信息,同时还要去查到购物车当中这些商品的最新的价格和状态信息,跟购物车当中的快照进行一个对比,从而去提醒用户。 现在我们已经做了服…

[网络爬虫] 动态网页抓取 — Selenium 介绍 环境配置

🌟想系统化学习爬虫技术?看看这个:[数据抓取] Python 网络爬虫 - 学习手册-CSDN博客 0x01:Selenium 工具介绍 Selenium 是一个开源的便携式自动化测试工具。它最初是为网站自动化测试而开发的,类似于我们玩游戏用的按…

【RAGFlow】windows本地pycharm运行

原因 由于官方只提供了docker部署,基于开源代码需要实现自己内部得逻辑,所以需要本地pycharm能访问,且docker运行依赖得其余组件,均需要使用开发服务器得配置。 修改过程 安装python 项目依赖于Python 版本:>3.1…

树莓派5首次开机保姆级教程(无显示器通过VNC连接树莓派桌面)

第一次开机详细步骤 步骤一:树莓派系统烧录1 搜索打开烧录软件“Raspberry Pi Imager”2 选择合适的设备、系统、SD卡3 烧录配置选项 步骤二:SSH远程树莓派1 树莓派插电2 网络连接(有线或无线)3 确定树莓派IP地址 步骤三&#xff…

html-表格标签

一、表格标签 1. 表格的主要作用 表格主要用于显示、展示数据,因为它可以让数据显示的非常的规整,可读性非常好。特别是后台展示数据 的时候,能够熟练运用表格就显得很重要。一个清爽简约的表格能够把繁杂的数据表现得很有条理。 总…

大模型安全新范式:DeepSeek一体机内容安全卫士发布

2月以来,DeepSeek一体机几乎成为了政企市场AI消费的最强热点。 通过一体机的方式能够缩短大模型部署周期,深度结合业务场景,降低中小企业对于大模型的使用门槛。据不完全统计,已约有超过60家企业基于DeepSeek推出一体机产品。 但…

数据分析绘制随时间顺序变化图加入线性趋势线——numpy库的polyfit计算一次多项式拟合

import pandas as pd import numpy as np import matplotlib.pyplot as plt# 导入数据 data pd.read_csv(rC:\Users\11712\notebooktrain1.csv)# 假设数据包含 date_time 和 speed 列 data[date_time] pd.to_datetime(data[date_time]) # 确保时间列是 datetime 类型 data.s…

密闭空间可燃气体监测终端:守护城市命脉,智驭燃气安全!

近年来,陕西省高度重视燃气安全,出台了一系列政策文件,旨在全面加强城镇燃气安全监管,防范化解重大安全风险。2023年,陕西省安委会印发《全省城镇燃气安全专项整治工作方案》,明确要求聚焦燃气经营、输送配…

阿里千问大模型(Qwen2.5-VL-7B-Instruct)部署

参考链接 知乎帖子 B站视频 huggingface 镜像网站(不太全,比如 Qwen/Qwen2.5-VL-7B-Instruct就没有) huggingface 5种下载方式汇总 通过huggingface-cli下载模型 不一样的部分是预训练权重的下载和demo 首先安装huggingface_hub pip insta…

【Go学习实战】03-3-文章评论及写文章

【Go学习实战】03-3-文章评论及写文章 文章评论注册valine获取凭证加载评论页面 写文章修改cdn位置完善功能查看页面 发布文章POST发布文章发布文章测试 查询文章详情查询详情测试 修改文章修改文章测试 写文章图片上传前端后端逻辑测试 文章评论 这里我们的博客因为是个轻量级…

从零开始用AI开发游戏(一)

1. 核心玩法设计 核心目标:玩家需在随机生成的3D迷宫中寻找出口,躲避陷阱、收集道具、解开谜题。核心机制: 随机生成迷宫:每次游戏生成不同结构的迷宫(递归分割算法或深度优先搜索)。第一人称视角&#xf…