2、几何变换基础:旋转、平移、缩放、剪切变换的原理与实现

做数据增强,几何变换是最基础、最常用的一类操作。说白了,就是让图片在空间上「动一动」——转个角度、挪个位置、拉近拉远、甚至歪一歪。你想想看,真实世界里的人脸哪能每次都端端正正地出现在镜头正中央?

我刚开始做人脸识别那会儿,总觉得模型训练好了就能应付各种角度。结果一上线,用户侧脸、低头、歪头的情况全来了,模型直接崩了。嗯,从那以后,我再也不敢小看几何变换了。

2.1 几何变换的核心思想

几何变换的本质,就是建立原始图像像素和目标图像像素之间的映射关系。我们用一个变换矩阵来描述这种映射。对于二维图像,这个矩阵是 2×3 的,但为了统一处理,我们通常用齐次坐标,把它扩展成 3×3 矩阵。

核心公式:

[x']   [a  b  tx] [x]
[y'] = [c  d  ty] [y]
[1 ]   [0  0  1 ] [1]

其中 (x, y) 是原始坐标,(x', y') 是变换后的坐标。矩阵中的参数决定了变换的类型和程度。

我个人习惯把几何变换分成两类:

  • 刚体变换:只包含旋转和平移,保持物体形状和大小不变。人脸在平面内转动就属于这类。
  • 仿射变换:包含旋转、平移、缩放、剪切,保持平行线平行,但角度和长度会变。人脸从正面到侧面的近似效果可以用剪切来模拟。

2.2 旋转变换

旋转是最常见的增强手段。人脸在照片里很少是正着的,稍微偏个 10°、20° 都很正常。

旋转矩阵:

[cosθ  -sinθ  tx]
[sinθ   cosθ  ty]
[0      0     1 ]

θ 是旋转角度,顺时针为正。tx 和 ty 是平移量,用来保证旋转后图像不跑出画布。

我的经验:旋转角度一般控制在 ±15° 以内。超过 30°,人脸特征就开始严重变形,模型反而学不到有效信息。我在一个安防项目里试过 ±45° 旋转,结果模型把倒立的人脸当成了新类别,哭笑不得。

实现要点:

  • 旋转中心通常选图像中心,而不是左上角
  • 旋转后会产生空白区域,需要填充策略(补黑、补边缘像素、镜像填充)
  • 角度步长建议 5° 或 10°,太密了数据冗余,太疏了覆盖不全

2.3 平移变换

平移就是让人脸在图像里「溜达」一下。人脸检测框不可能每次都完美居中,平移能模拟这种偏移。

平移矩阵:

[1  0  tx]
[0  1  ty]
[0  0  1 ]

tx 和 ty 分别是水平和垂直方向的偏移量,单位是像素。

参数 推荐范围 说明
tx ±10% 图像宽度 水平偏移,过大导致人脸出框
ty ±10% 图像高度 垂直偏移,过大导致人脸出框

注意:平移后一定要检查人脸关键点是否还在图像内。我曾经因为平移过大,导致模型学到了「半张脸」的特征,推理时遇到半脸就误判。

2.4 缩放变换

缩放模拟的是人脸距离摄像头的远近变化。同一个人的脸,在 1 米和 2 米处拍出来,大小差很多。

缩放矩阵:

[sx  0   0]
[0   sy  0]
[0   0   1]

sx 和 sy 分别是水平和垂直方向的缩放因子。sx = sy 是等比例缩放,不等比例会把人脸拉胖或拉瘦。

我的建议:

  • 等比例缩放范围:0.8 ~ 1.2
  • 不等比例缩放:sx 和 sy 差异不要超过 0.1,否则人脸变形太明显
  • 缩放后图像尺寸会变,需要裁剪或填充到统一大小

避坑指南:我曾经在一个人脸支付项目里,把缩放因子设到了 0.5 ~ 2.0,想着覆盖更多场景。结果模型把「大头照」和「远景小人」当成了不同的人。后来我把范围缩到 0.85 ~ 1.15,效果反而更好。数据增强不是越猛越好,适度才是关键。

2.5 剪切变换

剪切变换(Shear)很多人不常用,但我觉得它特别有用。它能模拟人脸从正面到侧面的过渡效果,或者摄像头倾斜拍摄时的透视变形。

剪切矩阵:

[1    shx  0]
[shy  1    0]
[0    0    1]

shx 是水平剪切因子,shy 是垂直剪切因子。shx > 0 时,图像向右倾斜;shy > 0 时,图像向下倾斜。

剪切类型 参数范围 模拟场景
水平剪切 ±0.2 左右转头
垂直剪切 ±0.15 上下点头

小技巧:剪切变换可以和旋转组合使用,效果更接近真实的人脸姿态变化。我通常先做剪切,再做旋转,这样计算量更小,效果也更自然。

2.6 四种变换的对比与组合

这四种变换不是孤立的。在实际项目中,我通常把它们组合起来用。下面这张图展示了它们的关系:

几何变换知识体系 几何变换 旋转 平移 缩放 剪切 四种变换都是仿射变换的特例,可以组合使用,用 3×3 矩阵统一表示 常用组合策略 旋转 + 平移:模拟头部转动 缩放 + 平移:模拟远近变化 剪切 + 旋转:模拟姿态变化 参数范围建议 旋转:±15° 缩放:0.85 ~ 1.15 剪切:±0.2

2.7 代码实现示例

下面我用 OpenCV 演示四种变换的实现。这是我在项目中常用的代码模板:

import cv2
import numpy as np

def geometric_augmentation(image, angle=10, tx=10, ty=10, scale=1.1, shear=0.1):
    h, w = image.shape[:2]
    center = (w // 2, h // 2)
    
    # 1. 旋转矩阵
    rot_mat = cv2.getRotationMatrix2D(center, angle, 1.0)
    
    # 2. 平移矩阵
    trans_mat = np.float32([[1, 0, tx], [0, 1, ty]])
    
    # 3. 缩放矩阵
    scale_mat = np.float32([[scale, 0, 0], [0, scale, 0]])
    
    # 4. 剪切矩阵
    shear_mat = np.float32([[1, shear, 0], [shear, 1, 0]])
    
    # 组合变换:先剪切,再旋转,再缩放,最后平移
    combined = shear_mat @ rot_mat[:2, :]  # 注意矩阵乘法顺序
    combined = scale_mat @ combined
    combined = trans_mat @ combined
    
    result = cv2.warpAffine(image, combined, (w, h))
    return result

重要提醒:矩阵乘法的顺序会影响最终结果。我习惯从右往左读:先应用的变换放在最右边。上面代码的顺序是:剪切 → 旋转 → 缩放 → 平移。顺序不同,效果完全不同。

2.8 实战建议总结

做了这么多年人脸识别,我总结了几条几何变换的实战经验:

  • 不要贪多:每种变换的参数范围要合理,过度增强反而降低模型性能
  • 组合优于单一:多种变换组合使用,比单一变换效果更好
  • 保持人脸完整性:变换后要确保人脸关键点仍在图像内
  • 随机化参数:每次训练时随机生成参数,而不是固定几种变换
  • 验证集不做增强:几何变换只用于训练集,验证集和测试集保持原始数据

我的习惯:每次新增一种几何变换策略,我都会先在验证集上跑一轮,看看准确率变化。如果准确率下降超过 1%,说明这个策略有问题,需要调整参数或者直接去掉。数据增强不是越多越好,有效才是硬道理。