【DFL学习日记#4】从landmarks到aligned,坐标与仿射变换矩阵,完全解读
上图是官方在项目里提供的示意图。下面我根据索引号对应又画了一版:
当使用 tuple(start, stop) 形式定义范围并通过 slice(*tuple) 创建切片时,遵循的是左闭右开区间原则。
landmarks_68_pt = { "mouth": (48,68),
"right_eyebrow": (17, 22),
"left_eyebrow": (22, 27),
"right_eye": (36, 42),
"left_eye": (42, 48),
"nose": (27, 36), # missed one point
"jaw": (0, 17) }
slice(*landmarks_68_pt["right_eyebrow"]) 实际上等同于 slice(17, 22) ,它会从 int_lmrks 数组中选择索引为17、18、19、20、21的元素,总共5个点,而不包括索引为22的点。
# 人脸对齐变换过程详解(附具体数值示例)
DeepFaceLab中的人脸对齐是一个将不同姿态、角度的人脸标准化为统一格式的关键过程。下面我将通过具体的数值例子来详细演示这个过程。
## 一、人脸对齐的基本原理
人脸对齐主要通过以下步骤实现:
1. 1.
使用面部地标点(landmarks)估计变换关系
2. 2.
计算仿射变换矩阵
3. 3.
应用变换提取对齐后的人脸图像
## 二、具体数值演示
### 步骤1:假设我们有一组检测到的面部地标点
假设在一张800x600的原始图像中,检测到了一个人脸,其关键点坐标如下(仅显示部分关键坐标):
```
# 实际检测到的面部关键点(部分)
image_landmarks = np.array([
# 眼睛、鼻子、嘴巴等关键点(示例值)
, # 左眼左上角
, # 左眼右上角
, # 左眼中心
, # 右眼左上角
, # 右眼右上角
, # 右眼中心
, # 鼻尖
, # 左嘴角
, # 右嘴角
# ... 更多关键点
], dtype=np.float32)
```
### 步骤2:使用Umeyama算法计算相似变换
首先,系统使用 umeyma 函数将检测到的地标点与标准模板地标点进行匹配:
```
# 从LandmarksProcessor.py中,系统使用
这些特定区域的关键点
selected_landmarks = np.concatenate
(,
image_landmarks])
我这里看了很久才看懂。48号点和54号点是两个嘴角的坐标
# landmarks_2D_new是预定义的标准模板
# 计算从图像关键点到标准模板的相似变换矩阵
similarity_mat = umeyama
(selected_landmarks,
landmarks_2D_new, True)
# 取前两行用于2D变换
transform_mat = similarity_mat
``` Umeyama算法计算过程:
1. 1.
计算源点和目标点的均值:
```
src_mean = # 假设的源
点均值
dst_mean = # 目标模板
的均值
```
2. 2.
去中心化处理后计算协方差矩阵
3. 3.
通过SVD分解得到旋转和缩放分量
4. 4.
最终得到的相似变换矩阵可能类似于:
```
# 示例变换矩阵(2x3)
transform_mat = [
,# 第
一行:a, b, tx
[-1.3, 119.8, -22000.5]# 第
二行:-b, a, ty
]
```
### 步骤3:计算仿射变换的三个关键点
接下来,系统使用变换矩阵计算对齐所需的三个关键点:
```
# 计算变换后的标准点在原图中的坐标
g_p = transform_points(np.float32
([(0,0),(1,0),(1,1),(0,1),(0.5,0.
5)]), transform_mat, True)
g_c = g_p# 中心点
# 计算对角线向量
tb_diag_vec = (g_p-g_p).
astype(np.float32)
tb_diag_vec /= np.linalg.norm
(tb_diag_vec)
bt_diag_vec = (g_p-g_p).
astype(np.float32)
bt_diag_vec /= np.linalg.norm
(bt_diag_vec)
# 计算缩放因子(假设使用FULL类型,
padding=0.2109375)
mod = (1.0/1.0) * (np.linalg.norm
(g_p-g_p)*(0.2109375*np.sqrt
(2.0) + 0.5))
# 计算仿射变换的三个源点
l_t = np.array([
g_c - tb_diag_vec*mod,
g_c + bt_diag_vec*mod,
g_c + tb_diag_vec*mod
])
```
假设计算结果为:
```
l_t = np.array([
,# 左上点
,# 右上点
# 右下点
])
```
### 步骤4:计算最终的仿射变换矩阵
```
# 定义目标图像中的三个对应点(假设输出大小
为512x512)
pts2 = np.float32([(0,0), (512,0),
(512,512)])
# 计算仿射变换矩阵
final_mat = cv2.getAffineTransform
(l_t, pts2)
```
最终得到的仿射变换矩阵可能如下:
```
final_mat = [
, # 第一
行:a, b, tx
[-0.2, 3.18, -370.6] # 第二
行:-b, a, ty
]
```
### 步骤5:应用仿射变换提取对齐的人脸
```
# 应用仿射变换
aligned_face = cv2.warpAffine
(original_image, final_mat, (512,
512), cv2.INTER_LANCZOS4)
# 同时变换关键点到新坐标系
aligned_landmarks =
LandmarksProcessor.transform_points
(image_landmarks, final_mat)
```
## 三、变换过程的关键点解释
1. 1.
相似变换(Umeyama) :
- 保持形状不变的变换,包含旋转、缩放和平移
- 用于建立检测到的人脸与标准模板之间的对应关系
2. 2.
仿射变换 :
- 允许旋转、缩放、剪切和平移
- 通过三个对应点对定义
- 在DeepFaceLab中,用于将人脸区域映射到标准大小的输出图像
3. 3.
面部类型的影响 :
- 不同的 face_type 参数(FULL、WHOLE_FACE、HEAD等)会影响:
- padding 值:决定提取区域的大小
- 中心点位置:如WHOLE_FACE会向下调整7%以包含更多额头
4. 4.
对齐的效果 :
- 无论原始人脸在图像中的位置、角度和大小如何
- 经过变换后,所有人脸都将:
- 大小统一(如512x512)
- 面部特征点位置标准化
- 方向一致(通常是正面朝向)
## 四、实际效果演示
原始图像中的人脸:
- 可能位于图像的任何位置
- 可能有旋转、倾斜
- 大小不一
对齐后的人脸:
- 总是位于输出图像的中心
- 方向标准化(眼睛水平,嘴巴水平)
- 大小统一(由image_size参数决定)
- 面部特征相对位置一致
这种对齐过程对于后续的深度学习模型训练至关重要,因为它确保了输入数据的一致性,大大提高了模型的学习效率和效果。
### 1. Umeyama函数返回的3×3矩阵结构
Umeyama函数返回的3×3齐次变换矩阵结构如下:
```
[ a b tx ]
[ c d ty ]
[ 0 0 1 ]
```
其中:
- 是2×2的旋转和缩放矩阵
- 是平移向量
- 最后一行 是齐次坐标的标准形式
### 2. 为什么要取前两行
从 transform_points 函数的实现可以看出:
```
def transform_points(points, mat,
invert=False):
if invert:
mat = cv2.
invertAffineTransform(mat)
points = np.expand_dims
(points, axis=1)
points = cv2.transform(points,
mat, points.shape)
points = np.squeeze(points)
return points
```
这个函数使用了OpenCV的两个关键函数:
- cv2.invertAffineTransform() :用于计算仿射变换的逆矩阵
- cv2.transform() :用于应用仿射变换到点集
重要的是:这两个OpenCV函数都期望输入的是2×3的仿射变换矩阵 ,而不是3×3的齐次矩阵。
### 3. 实际效果示例
当我们执行 mat = umeyama(...) 时,我们实际上是提取了:
```
[ a b tx ]
[ c d ty ]
```
这正好是OpenCV所需的2×3仿射变换矩阵格式。这样处理后,我们就可以:
1. 1.
使用 cv2.invertAffineTransform 计算逆变换
2. 2.
使用 cv2.transform 将变换应用到图像中的点
### 4. 为什么Umeyama返回3×3矩阵
Umeyama函数返回3×3矩阵是为了遵循齐次坐标变换的数学规范,这样可以统一处理旋转、缩放、平移等变换。但在实际的OpenCV图像处理中,为了效率和API兼容性,通常使用简化的2×3矩阵表示仿射变换。
### 总结
取前两行 的操作本质上是 将数学上标准的3×3齐次变换矩阵转换为OpenCV API所需的2×3仿射变换矩阵 ,使得后续的点变换操作能够正常工作。 umeyama 面部关键点变换代码详细解析
mat = umeyama( np.concatenate ( [ image_landmarks , image_landmarks ] ) , landmarks_2D_new, True)
这行代码的主要功能是计算从原始面部关键点到标准对齐空间的相似变换矩阵,这是深度伪造技术中实现面部对齐的关键步骤。
代码详解
1. 面部关键点选择
np.concatenate ( [ image_landmarks , image_landmarks ] )
concatenate合并后:
- 它是一个形状为(33,2)的NumPy数组
- 包含了33个人脸特征点的坐标(32个来自,1个来自)
- 每个坐标点是一个(x,y)对,表示特征点在图像中的位置
2. 标准参考点
landmarks_2D_new 是一个预定义的标准面部关键点数组,包含了:
和上面对应的33个坐标点
3. Umeyama 变换算法
umeyama() 函数实现了 Shinji Umeyama 在 1991 年发表的论文《Least-squares estimation of transformation parameters between two point patterns》中的算法,用于计算两个点集之间的最佳相似变换矩阵。该函数的参数:
第一个参数:源点集(输入图像中的实际面部关键点)
第二个参数:目标点集(标准模板点)
第三个参数 True:表示同时估计缩放因子
函数返回一个 (3×3) 的齐次变换矩阵,但代码中使用 只取前两行,这实际上是一个仿射变换矩阵,用于 2D 坐标变换。
4. 技术意义
这行代码实现了:
面部对齐:将不同姿势、大小的面部统一变换到标准空间
相似变换:保持面部特征之间的几何关系,同时考虑平移、旋转和缩放
关键点选择策略:只使用面部的关键特征点(眼睛、眉毛、鼻子和嘴角),这些点受表情变化影响较小,有利于稳定的对齐
在整个面部处理流程中的作用
在 get_transform_mat 函数中,这行代码计算的变换矩阵后续被用于:
1.
获取面部在全局空间中的位置和大小
2.
计算面部边界和对角线向量
3.
根据不同的面部类型(WHOLE_FACE、HEAD等)进行进一步调整
4.
最终生成一个适合提取和处理面部区域的变换矩阵
这个变换矩阵是深度伪造技术中实现面部替换、调整和合成的基础,确保了不同来源的面部可以在相同的空间中进行比较和处理。 ### 为什么会出现这种现象:对齐后鼻子都在画面中心,双眼几乎在一条水平线上
1. 1.
特征点权重分布 :在 np.concatenate(, image_landmarks]) 中,眼睛、鼻子和部分嘴巴的特征点占了绝大多数,这些点会对变换结果产生主要影响。
2. 2.
标准模板的作用 : landmarks_2D_new 作为目标标准模板,其中心点(鼻子区域)位于坐标约(0.49, 0.3-0.5)附近,而眼睛区域的y坐标相近,这自然导致对齐后:
- 鼻子会被映射到图像中心附近
- 眼睛连线会被尽可能校正为水平
3. 3.
最小二乘优化的结果 :Umeyama算法寻找的是整体误差最小的变换,对于侧脸,眼睛和鼻子是最可靠的特征点,所以会优先将它们对齐到标准位置。
### 这种对齐方式的优势
1. 1.
统一训练输入 :无论人脸朝向如何,关键特征(眼睛、鼻子)都位于相对固定的位置,这有利于神经网络学习。
2. 2.
保持局部特征关系 :即使整体是侧脸,局部特征(如眼睛形状、鼻子轮廓)的相对关系仍然得到保留。
3. 3.
平衡视角差异 :在训练数据中,不同角度的人脸经过这种对齐后,视角差异被部分标准化,提高了模型的泛化能力。
这种对齐方法虽然不能将侧脸转为正脸,但它提供了一种有效的折中方案,既保留了人脸的基本特征,又使得不同角度的人脸在统一的坐标系中有一定的可比性,这对深度学习模型的训练非常有帮助。 {:3_41:}{:3_41:}{:3_41:}{:3_41:}
页:
[1]