一、人脸检测与美颜:从MLKit到算法原理

说实话,人脸检测和美颜这块,我踩过的坑真不少。记得第一次做美颜相机项目时,我天真地以为直接调个滤镜就完事了。结果产品经理一句话把我问住了:「为什么我脸上痘痘还在?」嗯,从那以后我才真正开始研究人脸检测和美颜的底层逻辑。

今天咱们就聊聊MLKit人脸检测、磨皮、美白、大眼瘦脸这些核心算法。我会结合自己实际项目中的经验,把原理讲透,把坑说清楚。

1. MLKit人脸检测:Google给的利器

MLKit是Google提供的移动端机器学习框架。我习惯用它做人脸检测,因为它在Android上的集成非常丝滑,而且支持实时检测。

说白了,MLKit人脸检测的核心就三步:

  • 图像输入:从Camera或Bitmap获取帧数据
  • 特征提取:通过神经网络定位人脸关键点
  • 结果输出:返回人脸矩形框、 landmarks、欧拉角等

关键点说明:MLKit可以检测到468个人脸关键点,但实际美颜常用的只有几十个——比如眼睛轮廓、鼻子、嘴巴、眉毛等。我一般只取前68个点,够用且性能更好。

来看一段我项目里用过的代码:

// 初始化人脸检测器
val options = FaceDetectorOptions.Builder()
    .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
    .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
    .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_NONE)
    .build()

val detector = FaceDetection.getClient(options)

// 检测人脸
val task = detector.process(image)
task.addOnSuccessListener { faces ->
    for (face in faces) {
        val bounds = face.boundingBox
        val landmarks = face.landmarks
        // 拿到关键点坐标,开始美颜处理
    }
}

我的经验:性能模式选FAST,别用ACCURATE。我在小米8上测试过,FAST模式每帧处理时间约15ms,ACCURATE要40ms+。实时预览场景下,15ms已经够呛了。

2. 磨皮算法:不是简单模糊

很多人以为磨皮就是高斯模糊。其实不是。高斯模糊会把边缘也糊掉,看起来像塑料人。

我常用的磨皮算法是双边滤波。它能在平滑皮肤的同时保留边缘信息。原理很简单:

  • 空间域权重:距离越近的像素权重越大
  • 值域权重:像素值越接近的权重越大

这样,皮肤区域被平滑,但眼睛、眉毛等边缘被保留。

还有一种更高效的方法——表面模糊。它只对颜色相近的区域做模糊,边缘完全不碰。我在低端机上常用这个,性能比双边滤波好很多。

注意:磨皮强度不能拉满。我曾经给一个用户把脸磨成了「剥壳鸡蛋」,结果人家投诉说「我鼻子呢?」。一般强度控制在0.3-0.6之间比较自然。

3. 美白算法:调色板上的学问

美白不是简单地把亮度拉高。那样会过曝,而且肤色会发灰。

我习惯用HSL色彩空间来做美白。具体来说:

  1. 将RGB图像转换到HSL空间
  2. 只调整L(亮度)通道,且只对皮肤区域调整
  3. H(色相)和S(饱和度)保持不变
  4. 转回RGB

为什么要用HSL?因为RGB三个通道耦合太紧,调亮度会连带改变颜色。HSL把亮度和颜色分开了,操作起来更干净。

还有一个技巧——曲线调整。我经常用S形曲线:暗部压一点,亮部提一点,中间调微调。这样皮肤看起来通透,又不失层次感。

避坑指南:我曾经在美白时忘了做皮肤区域检测,结果把用户的白衬衫也「美白」了,变成了灰衬衫。所以一定要先做人脸分割,只对皮肤区域做美白。

4. 大眼瘦脸:图像变形的艺术

大眼和瘦脸,本质上是图像变形。我常用的算法是移动最小二乘法(MLS)。

原理是这样的:

  • 定义控制点(比如眼睛轮廓上的点)
  • 定义目标位置(眼睛放大后的点位置)
  • 计算每个像素到控制点的映射关系
  • 根据映射关系重新采样像素

说白了,就是把图像「推」或「拉」到新的位置。

来看一个简化版的瘦脸逻辑:

// 瘦脸:将脸颊两侧的点向内推
fun thinFace(bitmap: Bitmap, landmarks: List<PointF>): Bitmap {
    // 1. 找到左右脸颊的控制点
    val leftCheek = landmarks[0]  // 左脸颊点
    val rightCheek = landmarks[16] // 右脸颊点
    
    // 2. 定义目标位置(向内收缩10%)
    val targetLeft = PointF(
        leftCheek.x + (centerX - leftCheek.x) * 0.1f,
        leftCheek.y
    )
    
    // 3. 对每个像素做MLS变形
    // ... 具体实现略
}

我的建议:大眼瘦脸的参数一定要可调。不同人脸型不一样,固定参数会翻车。我一般给用户提供0-100的滑块,默认值设在30左右。

5. 整体流程:从检测到美颜

我把整个流程画了张图,方便你理解:

人脸检测与美颜处理流程 Camera帧输入 MLKit人脸检测 → 获取关键点 磨皮(双边滤波) 美白(HSL调整) 大眼瘦脸(MLS变形) 图像合成 → 输出美颜结果 显示/保存

流程其实不复杂。关键是要注意性能优化。我一般会在子线程做检测和变形,主线程只负责渲染。另外,帧率控制在25fps左右就够了,再高用户也看不出来。

6. 性能优化:别让手机发烫

美颜处理很吃性能。我踩过最大的坑就是——在低端机上跑全流程,手机直接变成暖手宝。

我的优化策略:

  • 降低分辨率:检测用480p,美颜用720p,输出用1080p
  • 复用Bitmap:别每帧都new,用对象池
  • GPU加速:能用RenderScript或OpenGL就别用CPU
  • 跳帧处理:每3帧做一次检测,中间2帧复用上次的关键点

核心思路:人脸检测是瓶颈,美颜是次瓶颈。把检测频率降下来,整体性能能提升50%以上。

7. 总结一下

人脸检测和美颜,说白了就是「找到脸 → 分析脸 → 美化脸」这三步。MLKit帮我们搞定了第一步,后面两步需要自己写算法。

我个人觉得,做美颜最重要的是自然。别追求极致效果,用户要的是「看起来像自己,但更好看」。这个度,需要你慢慢调参数去把握。

嗯,今天就聊到这儿。如果你在实现过程中遇到问题,欢迎交流。


公众号:蓝海资料掘金营,微信deep3321