4. 人脸对齐与归一化

任务说明

本案例实现人脸对齐与归一化:给定一张人脸图像及其5个关键点(左眼、右眼、鼻尖、左嘴角、右嘴角),通过仿射变换将人脸旋转到水平标准姿态,并统一裁剪为112×112像素的标准尺寸。该操作是人脸识别流程中特征提取前的必要预处理步骤。

实现方案

采用基于双眼坐标的相似变换(旋转+缩放+平移)完成对齐。核心思路:计算双眼连线与水平方向的夹角作为旋转角,以两眼中心为基准点进行旋转,再按固定比例缩放使两眼间距映射到目标值,最后裁剪出112×112区域。代码使用OpenCV的estimateAffinePartial2D函数,通过源关键点与目标关键点的对应关系直接求解仿射矩阵,避免手动推导旋转矩阵。

代码实现


import cv2
import numpy as np

# 标准人脸模板关键点坐标(112×112)
TEMPLATE_POINTS = np.array([
    [38.2946, 51.6963],   # 左眼
    [73.5318, 51.5014],   # 右眼
    [56.0252, 71.7366],   # 鼻尖
    [41.5493, 92.3655],   # 左嘴角
    [70.7299, 92.2041]    # 右嘴角
], dtype=np.float32)

def align_face(image, landmarks):
    """
    人脸对齐与归一化
    Args:
        image: BGR图像 (numpy数组)
        landmarks: 5个关键点坐标 [[x,y],...] 顺序与TEMPLATE_POINTS一致
    Returns:
        aligned: 112×112对齐后图像,若关键点无效返回None
    """
    if image is None or landmarks is None:
        return None
    
    src_pts = np.array(landmarks, dtype=np.float32).reshape(5, 2)
    
    # 使用部分仿射变换(旋转+缩放+平移)求解变换矩阵
    transform_matrix, inliers = cv2.estimateAffinePartial2D(
        src_pts, TEMPLATE_POINTS, method=cv2.RANSAC, ransacReprojThreshold=5.0
    )
    
    if transform_matrix is None:
        return None
    
    # 应用仿射变换并统一尺寸
    aligned = cv2.warpAffine(
        image, transform_matrix, (112, 112),
        flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0)
    )
    return aligned

# 示例:加载图像并定义关键点(以实际检测结果替换)
if __name__ == "__main__":
    # 假设已通过检测器获得以下关键点(单位:像素)
    sample_landmarks = [
        [112.5, 98.3],   # 左眼
        [168.2, 95.7],   # 右眼
        [142.0, 138.6],  # 鼻尖
        [118.4, 176.2],  # 左嘴角
        [160.8, 174.9]   # 右嘴角
    ]
    
    # 读取图像(请替换为实际路径)
    img = cv2.imread("face.jpg")
    if img is None:
        print("图像读取失败,请检查路径")
    else:
        result = align_face(img, sample_landmarks)
        if result is not None:
            cv2.imwrite("aligned_face.jpg", result)
            print("对齐完成,输出尺寸:", result.shape)
        else:
            print("对齐失败:关键点无效或变换求解失败")

关键说明

TEMPLATE_POINTS采用人脸识别领域广泛使用的标准模板坐标(源自公开数据集统计),保证不同输入人脸对齐后具有一致的几何分布。代码中estimateAffinePartial2D仅估计4自由度(旋转、缩放、平移),不包含剪切变形,符合人脸刚性对齐需求。RANSAC参数用于剔除异常关键点,若关键点质量可靠可改用cv2.LMEDS或直接使用最小二乘求解。

落地与验证

依赖安装:pip install opencv-python numpy

运行方式:将代码保存为face_align.py,准备一张包含正脸人脸的图像命名为face.jpg,将sample_landmarks替换为实际检测到的关键点坐标,执行python face_align.py。

验证方法:观察输出的aligned_face.jpg是否满足:①图像尺寸为112×112;②双眼处于同一水平线(y坐标接近);③人脸居中且尺度统一。可通过在输出图像上叠加模板关键点位置进行定量检查——若对齐正确,两眼中心应落在约(56, 51)附近,鼻尖在(56, 72)附近。

人脸对齐仿射变换流程 原始人脸图像 输入关键点(倾斜人脸) 仿射变换矩阵 M estimateAffinePartial2D (src_pts → 模板点) 旋转 + 缩放 + 平移 对齐后标准人脸 112×112 标准姿态 双眼连线旋转至水平 → 缩放至模板间距 → 平移至标准位置 warpAffine 应用 M 矩阵,输出固定 112×112 尺寸