RetinaFace开源模型实战:基于关键点的瘦脸/大眼/美白等基础美颜算法接入教程

你有没有想过,那些手机App里一键就能完成的瘦脸、大眼、美白效果,背后其实并不神秘?它们大多依赖一个共同的基础——精准的人脸关键点定位。而RetinaFace,就是目前开源社区中人脸检测与关键点识别最稳、最准、最容易上手的模型之一。

这篇文章不讲论文、不堆参数,只带你从零开始,把RetinaFace跑起来,再用它输出的5个关键点(双眼中心、鼻尖、左右嘴角),真正动手实现几个实用的美颜小功能:让眼睛变大一点、让脸型更立体、让肤色更均匀。整个过程不需要训练模型,不用配环境,镜像已预装好所有依赖,10分钟内你就能看到自己照片被“智能调整”后的效果。

1. 为什么是RetinaFace?它到底能给你什么

很多人一听到“人脸关键点”,第一反应是“我只需要检测出人脸框就够了”。但真实场景中,光有框远远不够。比如你想做瘦脸,框只能告诉你“这张脸在哪”,却没法告诉你“颧骨在哪、下颌线在哪、眼睛该往哪拉”。这时候,关键点就是桥梁——它把一张模糊的脸,变成了可计算、可变形、可编辑的坐标集合。

RetinaFace之所以被广泛选用,不是因为它最炫酷,而是因为它足够“靠谱”:

  • 它能稳定输出5个高精度关键点:左眼中心、右眼中心、鼻尖、左嘴角、右嘴角。这5个点虽然不多,但恰好覆盖了面部最核心的形变区域;
  • 对小脸、侧脸、部分遮挡(比如戴口罩、头发遮挡)的鲁棒性极强,不像一些轻量模型在合影里经常漏检;
  • 推理速度快,ResNet50版本在单张RTX 4090上平均耗时不到80ms,完全满足本地实时处理需求;
  • 关键点坐标是浮点数,精度到像素级小数,为后续几何变换(比如仿射缩放、三角形网格变形)提供了扎实基础。

换句话说,RetinaFace不是终点,而是你搭建个性化美颜能力的第一块稳固地砖。它不负责“变美”,但它确保你每次操作,都精准落在该落的地方。

2. 镜像环境准备:3步启动,无需安装任何东西

本教程基于CSDN星图提供的 RetinaFace人脸检测与关键点绘制镜像,已为你预装全部依赖并优化推理流程。你不需要配置CUDA、不用pip install一堆包、也不用下载模型权重——所有这些,镜像启动即就绪。

2.1 环境配置一览

这个镜像不是简单打包,而是针对实际工程使用做了针对性优化:

组件 版本 说明
Python 3.11 兼容新特性,同时保持生态稳定
PyTorch 2.5.0+cu124 适配CUDA 12.4,GPU加速开箱即用
CUDA / cuDNN 12.4 / 9.x 与主流显卡(RTX 30/40系)完美匹配
ModelScope 默认 自动加载官方模型,免手动下载
代码位置 /root/RetinaFace 所有脚本、示例、工具集中在此目录

注意:所有路径和命令均以镜像内默认环境为准,无需额外修改。如果你用的是Docker或云平台一键部署,启动后直接SSH进入即可。

2.2 快速验证:先看一眼关键点长什么样

别急着写代码,我们先确认环境真的跑通了。

打开终端,执行以下三步:

cd /root/RetinaFace
conda activate torch25
python inference_retinaface.py

几秒钟后,你会在当前目录下看到一个新文件夹 face_results,里面有一张名为 retinaface_result.jpg 的图片。打开它——你将看到一张带绿色检测框和5个醒目的红色圆点的图像。这5个红点,就是RetinaFace为你找到的左眼、右眼、鼻尖、左嘴角、右嘴角。

成功标志:红点清晰、位置合理、无明显偏移(比如点在眉毛上或嘴外)。如果出现错位,大概率是输入图过暗或角度过大,稍后我们会讲如何提升稳定性。

3. 提取关键点坐标:从“画点”到“拿数据”

上面的脚本只是可视化,真正驱动美颜的是坐标数值。我们需要把那5个红点变成Python里可运算的 (x, y) 列表。

3.1 修改推理脚本,导出关键点数据

原脚本 inference_retinaface.py 只负责绘图,我们要让它也“吐出”坐标。打开该文件,找到绘图逻辑附近(通常在 for b in boxes: 循环内),添加如下代码:

# 在绘制每个关键点前,添加这一行(约第120行左右)
landmarks = dets[i, 5:15].reshape(5, 2)  # Reshape to (5, 2)
print("Detected landmarks (x, y):")
for idx, (x, y) in enumerate(landmarks):
    print(f"  Point {idx+1}: ({x:.1f}, {y:.1f})")

保存后再次运行:

python inference_retinaface.py --input ./my_test.jpg

你会看到类似这样的输出:

Detected landmarks (x, y):
  Point 1: (124.3, 187.6)  # 左眼中心
  Point 2: (178.9, 186.2)  # 右眼中心
  Point 3: (152.1, 224.8)  # 鼻尖
  Point 4: (132.5, 256.4)  # 左嘴角
  Point 5: (171.7, 255.9)  # 右嘴角

小贴士:这5个点的顺序是固定的(ModelScope官方模型约定),无需记忆,直接按索引使用即可:landmarks[0] 是左眼,landmarks[1] 是右眼,以此类推。

3.2 封装成函数:一键获取关键点

为了后续复用,我们把它封装成一个干净的函数。新建文件 face_utils.py

# face_utils.py
import cv2
import numpy as np
from models.retinaface import RetinaFace

def get_face_landmarks(image_path, threshold=0.5):
    """
    输入图片路径,返回检测到的首个人脸的5个关键点坐标
    返回: list of tuples [(x1,y1), (x2,y2), ..., (x5,y5)] or None
    """
    detector = RetinaFace(model_path='/root/RetinaFace/models/Resnet50_Final.pth')
    img = cv2.imread(image_path)
    if img is None:
        print(f"Error: cannot load image {image_path}")
        return None
    
    # 检测
    dets = detector.detect_faces(img, threshold=threshold)
    if len(dets) == 0:
        print("No face detected.")
        return None
    
    # 取置信度最高的一张脸
    best_det = max(dets, key=lambda x: x['score'])
    landmarks = best_det['landmarks']  # ModelScope接口已结构化,直接取
    return [
        (int(landmarks['left_eye'][0]), int(landmarks['left_eye'][1])),
        (int(landmarks['right_eye'][0]), int(landmarks['right_eye'][1])),
        (int(landmarks['nose'][0]), int(landmarks['nose'][1])),
        (int(landmarks['left_mouth'][0]), int(landmarks['left_mouth'][1])),
        (int(landmarks['right_mouth'][0]), int(landmarks['right_mouth'][1]))
    ]

# 测试
if __name__ == "__main__":
    pts = get_face_landmarks("./my_test.jpg")
    print("Key points:", pts)

现在,只要调用 get_face_landmarks("xxx.jpg"),你就能拿到一个包含5个 (x, y) 元组的列表,美颜算法的“原材料”就齐了。

4. 动手实现三大基础美颜:瘦脸、大眼、美白

有了关键点,接下来就是发挥创意的时候。我们不追求工业级效果,而是用最直观、最易理解的方式,让你亲眼看到坐标变化如何改变一张脸。

4.1 瘦脸:用三角形网格做局部缩放

瘦脸的本质,是向内收缩脸颊区域。我们不用复杂算法,而是用一个经典技巧:以双眼和鼻尖构成一个三角形,再以嘴角为锚点,沿该三角形中线方向压缩。

# 在 face_utils.py 中追加
def apply_face_shrink(image, landmarks, shrink_ratio=0.15):
    """
    基于关键点的简易瘦脸
    shrink_ratio: 压缩强度 (0.0 ~ 0.3 合理)
    """
    img = image.copy()
    h, w = img.shape[:2]
    
    # 计算脸部中心线(鼻尖到两眼中心连线的中点)
    left_eye, right_eye, nose, left_mouth, right_mouth = landmarks
    eye_center_x = (left_eye[0] + right_eye[0]) // 2
    eye_center_y = (left_eye[1] + right_eye[1]) // 2
    
    # 脸部宽度参考:两嘴角距离
    mouth_width = right_mouth[0] - left_mouth[0]
    
    # 构造变形网格:只对脸颊区域做水平压缩
    # 这里简化为:以鼻尖为基准,向左右各取 mouth_width * 0.4 区域进行缩放
    delta_x = int(mouth_width * shrink_ratio)
    
    # 创建映射表(仅示意,生产环境建议用cv2.remap)
    map_x = np.zeros((h, w), dtype=np.float32)
    map_y = np.zeros((h, w), dtype=np.float32)
    for y in range(h):
        for x in range(w):
            # 如果在左脸颊区域(鼻尖x左侧一定范围)
            if x < nose[0] and abs(x - nose[0]) < mouth_width * 0.4:
                map_x[y, x] = x + (nose[0] - x) * shrink_ratio
            # 如果在右脸颊区域(鼻尖x右侧一定范围)
            elif x > nose[0] and abs(x - nose[0]) < mouth_width * 0.4:
                map_x[y, x] = x - (x - nose[0]) * shrink_ratio
            else:
                map_x[y, x] = x
            map_y[y, x] = y
    
    return cv2.remap(img, map_x, map_y, cv2.INTER_LINEAR)

# 使用示例
img = cv2.imread("./my_test.jpg")
pts = get_face_landmarks("./my_test.jpg")
if pts:
    shrunk_img = apply_face_shrink(img, pts, shrink_ratio=0.18)
    cv2.imwrite("./shrink_result.jpg", shrunk_img)

效果:脸部轮廓更紧致,下颌线更清晰,但不会失真——因为变形严格限制在关键点定义的区域内。

4.2 大眼:以眼中心为圆心,局部放大

大眼不是简单地把整张图放大,而是以眼球中心为原点,对周围小区域做平滑放大。我们用OpenCV的 cv2.resize + cv2.getRotationMatrix2D 组合实现:

def apply_enlarge_eyes(image, landmarks, scale_factor=1.25, radius=30):
    """
    局部放大双眼区域
    scale_factor: 放大倍数 (1.1 ~ 1.4)
    radius: 影响半径(像素)
    """
    img = image.copy()
    left_eye, right_eye, *_ = landmarks
    
    for eye_center in [left_eye, right_eye]:
        x, y = eye_center
        # 截取眼部ROI
        roi_x1 = max(0, x - radius)
        roi_y1 = max(0, y - radius)
        roi_x2 = min(img.shape[1], x + radius)
        roi_y2 = min(img.shape[0], y + radius)
        roi = img[roi_y1:roi_y2, roi_x1:roi_x2].copy()
        
        # 放大ROI
        h, w = roi.shape[:2]
        new_h, new_w = int(h * scale_factor), int(w * scale_factor)
        enlarged = cv2.resize(roi, (new_w, new_h), interpolation=cv2.INTER_CUBIC)
        
        # 计算居中粘贴位置
        paste_x = x - new_w // 2
        paste_y = y - new_h // 2
        paste_x1 = max(0, paste_x)
        paste_y1 = max(0, paste_y)
        paste_x2 = min(img.shape[1], paste_x + new_w)
        paste_y2 = min(img.shape[0], paste_y + new_h)
        
        # 粘贴回原图(带alpha混合更自然,此处简化)
        h_paste = paste_y2 - paste_y1
        w_paste = paste_x2 - paste_x1
        if h_paste > 0 and w_paste > 0:
            enlarged_cropped = enlarged[:h_paste, :w_paste]
            img[paste_y1:paste_y2, paste_x1:paste_x2] = enlarged_cropped
    
    return img

# 使用
enlarged_img = apply_enlarge_eyes(img, pts, scale_factor=1.3)
cv2.imwrite("./enlarge_eyes.jpg", enlarged_img)

效果:眼睛看起来更大、更有神,但边缘过渡自然,没有“贴图感”。

4.3 美白:基于关键点划定肤色区域,定向提亮

美白的关键是只提亮皮肤,不提亮头发、衣服、背景。我们利用关键点围成的“面部区域”作为掩膜(mask),再对HSV空间的V通道做温和提升:

def apply_skin_whiten(image, landmarks, strength=0.12):
    """
    基于面部关键点的区域美白
    strength: 提亮强度 (0.05 ~ 0.2)
    """
    img = image.copy()
    h, w = img.shape[:2]
    
    # 构建面部多边形掩膜(五点连成面)
    pts = np.array(landmarks, dtype=np.int32)
    # 补充下巴点(估算:两嘴角中点向下延伸)
    chin_x = (landmarks[3][0] + landmarks[4][0]) // 2
    chin_y = landmarks[3][1] + (landmarks[3][1] - landmarks[2][1]) * 2
    pts = np.append(pts, [[chin_x, chin_y]], axis=0)
    
    mask = np.zeros((h, w), dtype=np.uint8)
    cv2.fillConvexPoly(mask, pts, 255)
    
    # 转HSV,只调V通道
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(hsv)
    
    # 对掩膜内区域提亮V值
    v_masked = cv2.bitwise_and(v, v, mask=mask)
    v_masked = np.clip(v_masked.astype(np.float32) * (1 + strength), 0, 255).astype(np.uint8)
    
    # 合并回HSV并转BGR
    hsv_whitened = cv2.merge([h, s, v_masked])
    return cv2.cvtColor(hsv_whitened, cv2.COLOR_HSV2BGR)

# 使用
whitened_img = apply_skin_whiten(img, pts, strength=0.15)
cv2.imwrite("./whitened.jpg", whitened_img)

效果:肤色更均匀、透亮,但嘴唇、瞳孔、发色等非皮肤区域完全不受影响。

5. 整合与进阶:从单功能到流水线

上面三个函数是独立的,但在实际使用中,你往往希望“一键美颜”:输入一张图,自动完成检测→瘦脸→大眼→美白→保存。

5.1 构建美颜流水线

新建 beautify_pipeline.py

import cv2
from face_utils import get_face_landmarks, apply_face_shrink, apply_enlarge_eyes, apply_skin_whiten

def full_beautify(input_path, output_path, 
                  shrink_ratio=0.15, 
                  eye_scale=1.28, 
                  whiten_strength=0.14):
    img = cv2.imread(input_path)
    if img is None:
        print("Failed to load image")
        return
    
    pts = get_face_landmarks(input_path)
    if not pts:
        print("No face detected, skipping beautify")
        cv2.imwrite(output_path, img)
        return
    
    # 顺序执行(注意:顺序影响最终效果,建议先瘦脸,再大眼,最后美白)
    result = apply_face_shrink(img, pts, shrink_ratio)
    result = apply_enlarge_eyes(result, pts, eye_scale)
    result = apply_skin_whiten(result, pts, whiten_strength)
    
    cv2.imwrite(output_path, result)
    print(f"Beautified image saved to {output_path}")

if __name__ == "__main__":
    full_beautify("./my_test.jpg", "./beautified_result.jpg")

运行它,一张融合了三项基础美颜效果的图片就生成了。

5.2 实用建议与避坑指南

  • 关键点质量决定上限:如果RetinaFace本身关键点偏移超过5像素,后续所有美颜都会失真。建议对低质量输入(如暗光、侧脸)先做直方图均衡化或简单锐化;
  • 参数要微调,不要硬套shrink_ratio=0.15 是通用值,但对圆脸可能需0.18,对窄脸可能0.12,建议准备一组测试图反复调试;
  • 避免过度处理:大眼超过1.4倍、美白超过0.2,容易产生塑料感。真实美颜App通常默认值都偏保守;
  • 性能提示:上述算法都是CPU实现,单图处理约300~500ms。如需实时(>25fps),建议将核心变形逻辑改用CUDA或TensorRT加速。

6. 总结:你已经掌握了美颜的底层钥匙

回顾一下,我们完成了什么:

  • 用一行命令启动了预装RetinaFace的镜像,跳过了所有环境配置;
  • 修改脚本,把“画红点”变成了“输出坐标”,拿到了可编程的关键点数据;
  • 动手实现了瘦脸、大眼、美白三个最常用的基础美颜功能,每一步都基于关键点坐标,逻辑清晰、代码简短;
  • 将它们组装成一条可复用的流水线,输入一张图,输出一张美化后的图。

这远不止是一篇“教程”。它是一把钥匙——打开了从“调用API”到“理解原理”再到“自主定制”的大门。你不再需要依赖某个App的固定滤镜,而是可以根据自己的审美,调整参数、增删功能、甚至替换为更先进的关键点模型(比如106点、230点)。

美颜的本质,从来不是掩盖,而是增强;不是千人一面,而是让每个人的特点,被更精准地表达出来。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐