21、视频特效与滤镜:GLSL滤镜实现、美颜算法、人脸检测集成

各位同学,今天我们来聊聊视频特效这块硬骨头。说实话,我刚入行那会儿,觉得滤镜就是调个颜色,美颜就是磨个皮,人脸检测更是遥不可及。直到我在一个直播项目里被产品经理追着要「实时大眼瘦脸」效果,才真正开始啃这些技术。

这一章,我会把GLSL滤镜、美颜算法、人脸检测这三块串起来讲。你想想看,它们本质上都是在做同一件事——对视频帧进行像素级的处理。只不过处理的方式和依据不同罢了。

核心思路:视频特效 = 帧数据获取 + GPU/CPU处理 + 渲染输出。滤镜走GPU,美颜走混合计算,人脸检测走AI模型。三者可以流水线式串联。

21.1 GLSL滤镜:用GPU给视频化妆

GLSL全称是OpenGL Shading Language。说白了,就是写一段小程序,让GPU去逐像素处理图像。为什么用GPU?因为视频是30帧每秒,一帧可能有200万个像素,CPU算不过来。

我在项目中遇到过一个问题:用CPU做颜色反转,720P视频直接掉到15帧。换成GLSL片元着色器,满帧运行。这就是GPU并行计算的优势。

21.1.1 滤镜的基本流程

一个典型的GLSL滤镜流程是这样的:

  1. 把视频帧上传到GPU纹理
  2. 绘制一个全屏矩形
  3. 在片元着色器里对每个像素做计算
  4. 输出到屏幕或下一个纹理

嗯,这里要注意:纹理坐标和屏幕坐标的映射关系。我刚开始写的时候,经常把图像搞反,就是因为忘了处理Y轴翻转。

21.1.2 几个经典滤镜的GLSL实现

先看一个最简单的灰度滤镜。它把RGB三个通道按亮度权重混合:

// 灰度滤镜 - 片元着色器
precision mediump float;
uniform sampler2D uTexture;
varying vec2 vTexCoord;

void main() {
    vec4 color = texture2D(uTexture, vTexCoord);
    float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
    gl_FragColor = vec4(vec3(gray), color.a);
}

这段代码里,dot函数做了一次点乘,把RGB三个值加权求和。权重0.299、0.587、0.114是标准亮度公式,人眼对绿色最敏感,所以绿色权重最高。

再看一个冷暖色调滤镜。这个我在一个短视频App里用过,用户很喜欢:

// 暖色调滤镜
precision mediump float;
uniform sampler2D uTexture;
uniform float uWarmth; // 0.0 ~ 1.0
varying vec2 vTexCoord;

void main() {
    vec4 color = texture2D(uTexture, vTexCoord);
    // 暖色:增强红色,减弱蓝色
    color.r += uWarmth * 0.15;
    color.b -= uWarmth * 0.12;
    gl_FragColor = color;
}

你可能会问:为什么只调红蓝通道?因为暖色调的本质就是增加红橙成分,减少蓝紫成分。绿色通道基本不动,否则肤色会发绿。

我的经验:写滤镜时,先把效果在PC上用工具调好,再翻译成GLSL。我习惯用Photoshop的曲线和色相工具做原型,然后反推数学公式。这样效率高很多。

21.2 美颜算法:不只是磨皮

美颜这个词,听起来很玄乎。其实拆开来看,核心就三件事:磨皮、美白、脸型调整。其中磨皮是最基础的,也是计算量最大的。

21.2.1 磨皮:双边滤波与高斯滤波

最简单的磨皮是高斯模糊。但高斯模糊有个问题——它会模糊整张图,包括边缘。结果就是眼睛、眉毛这些细节也糊了。

所以实际项目中,我们用的是双边滤波。它在高斯模糊的基础上,加了一个像素值差异的权重。颜色差异大的地方(比如皮肤和眼睛交界处),权重降低,边缘就保留下来了。

我曾经踩过一个坑:双边滤波在GPU上实现时,如果采样半径太大,性能会急剧下降。因为每个像素要采样周围几十个点。后来我改用快速双边滤波,先降采样再上采样,性能提升了3倍。

这里给一个简化版的双边滤波核心逻辑:

// 双边滤波 - 伪代码
for each pixel (x, y) {
    float sumWeight = 0.0;
    vec4 sumColor = vec4(0.0);
    for (int dx = -R; dx <= R; dx++) {
        for (int dy = -R; dy <= R; dy++) {
            vec4 neighbor = texture(uTexture, coord + vec2(dx, dy));
            float dist = length(vec2(dx, dy));
            float colorDiff = length(neighbor.rgb - center.rgb);
            float weight = exp(-dist*dist/(2*sigma_s*sigma_s)) 
                         * exp(-colorDiff*colorDiff/(2*sigma_r*sigma_r));
            sumColor += neighbor * weight;
            sumWeight += weight;
        }
    }
    output = sumColor / sumWeight;
}

两个sigma参数:sigma_s控制空间模糊程度,sigma_r控制边缘保留程度。我一般设sigma_s=10sigma_r=0.1,效果比较自然。

21.2.2 美白:简单的颜色映射

美白比磨皮简单多了。本质上就是提高亮度,同时保持色相不变。我常用的方法是:

  1. 把RGB转到HSV或HSL空间
  2. 提高V(明度)或L(亮度)分量
  3. 转回RGB

但要注意:不能全局提亮,否则高光区域会过曝。我一般会做一个亮度掩膜,只对中间调区域做提亮,高光和阴影保持不动。

注意:美颜算法一定要考虑性能。在移动端,720P视频每帧处理时间不能超过16ms(60帧)或33ms(30帧)。如果磨皮+美白+脸型调整一起上,很容易超时。我的建议是:分步测试,每步控制在5ms以内。

21.3 人脸检测集成:让特效知道「脸在哪」

没有人脸检测,美颜就是盲人摸象。你都不知道眼睛在哪,怎么去放大眼睛?不知道鼻子在哪,怎么去瘦鼻?

Android上集成人脸检测,主流方案有两个:

方案 优点 缺点 适用场景
Google ML Kit 集成简单,官方维护 需要Google Play Services 国内App慎用
MediaPipe 跨平台,离线可用 包体较大(约5MB) 直播、短视频App
自研/第三方SDK 定制化强,性能可控 开发成本高 大厂旗舰产品

我个人推荐MediaPipe。它在移动端的优化做得很好,能检测468个面部关键点,足够做精细的美颜特效了。

21.3.1 集成流程

以MediaPipe为例,集成人脸检测的步骤大致如下:

  1. 添加依赖:com.google.mediapipe:facemesh
  2. 初始化检测器:配置模型路径、运行模式(GPU/CPU)
  3. 每帧传入图像数据,获取检测结果
  4. 从结果中提取关键点坐标(如左眼中心、右眼中心、鼻尖等)
  5. 将坐标传递给美颜算法或滤镜

这里有个关键点:坐标映射。人脸检测返回的坐标通常是归一化的(0~1),而纹理坐标也是0~1。但要注意,摄像头预览可能有裁剪或旋转,需要做相应的变换。

我记得有一次,人脸检测框总是偏左。查了半天,发现是前置摄像头做了镜像,但检测器不知道。加上一个水平翻转就解决了。

21.3.2 人脸关键点驱动的美颜

有了人脸关键点,美颜就可以「指哪打哪」了。举个例子:

  • 大眼效果:根据左右眼关键点,计算眼睛区域,用局部缩放算法放大
  • 瘦脸效果:根据下颌线关键点,用图像扭曲算法向内收缩
  • 口红效果:根据嘴唇关键点,生成嘴唇掩膜,叠加颜色

这些效果的核心都是图像变形。常用的算法有:

  • 局部缩放(大眼):以眼睛中心为圆心,半径内像素向外或向内偏移
  • 移动最小二乘(瘦脸):根据控制点的位移,平滑地扭曲整张脸
  • 网格变形:把脸分成网格,每个顶点根据关键点移动,像素插值

我建议初学者先从局部缩放入手。它原理简单,效果直观,而且性能开销小。等熟悉了再上更复杂的算法。

21.4 整体架构:把三块串起来

最后,我们看看这三块怎么协同工作。下面这张图展示了完整的处理流水线:

视频特效处理流水线架构 摄像头/视频帧 人脸检测 MediaPipe/ML Kit 关键点提取 468个面部点 渲染输出 SurfaceView/GLSurfaceView GLSL滤镜处理 颜色变换、特效、风格化 美颜算法 磨皮、美白、脸型调整 特效叠加 贴纸、AR效果、动态滤镜 关键点驱动 处理顺序:帧输入 → 人脸检测 → 关键点提取 → 滤镜/美颜/特效 → 渲染输出 性能关键点:每帧总处理时间 < 33ms(30fps),建议分步测试各模块耗时 人脸检测约5-10ms | 滤镜约2-5ms | 美颜约5-15ms | 渲染约2-3ms

从架构图可以看出,人脸检测是「大脑」,它告诉后续模块「脸在哪、眼睛在哪、嘴巴在哪」。GLSL滤镜是「化妆师」,负责整体色调和风格。美颜算法是「修图师」,负责局部精细化处理。

在实际项目中,我建议按这个顺序做:

  1. 先跑通GLSL滤镜,确保GPU渲染链路正常
  2. 再接入人脸检测,拿到关键点数据
  3. 最后实现美颜算法,用关键点驱动

每一步都要做性能测试。我曾经在一个项目里,美颜算法写得太重,导致发热严重。后来把磨皮的采样半径从5降到3,效果几乎没变,但温度降了3度。

我的习惯:在开发阶段,我会在屏幕上显示FPS和每帧耗时。这样调优的时候,改一行代码就能看到效果。推荐用ChoreographerSurfaceFlinger的dumpsys来监控。

好了,这一章的内容就到这里。GLSL滤镜、美颜算法、人脸检测,这三块是视频特效的基石。掌握了它们,你就能做出抖音、美颜相机里的大部分效果。剩下的,就是创意和调参了。

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