在iOS平台上构建人像光效功能,核心并非单纯叠加亮度滤镜,而是结合摄像头采集的深度信息对画面进行重照明。Core Image框架从iOS 11开始逐步完善了与深度数据协作的能力,开发者可以通过AVFoundation获取AVDepthData,再将其桥接至Core Image的滤镜管线。漫反射光、轮廓光与舞台光三种效果,本质上是对深度图的不同区域施加差异化的光照模型,从而改变人物与背景的视觉层级关系。

深度图获取与Core Image输入构建
要实现人像光效,第一步必须获得可靠的深度图。在支持双摄或深感摄像头的设备上,AVCaptureDepthDataOutput可以输出与彩色帧对齐的离散深度数据。该数据以16位或32位浮点形式描述每个像素点到镜头的距离,值越小代表越靠近相机。我们需要将深度图转换为Core Image可识别的CIImage,同时注意彩色帧与深度图的分辨率差异,通常使用CIFilter的缩放或重采样保持空间对齐。
对齐后的深度图不能直接用于显示,而应当作为遮罩或权重图参与光照计算。例如漫反射光需要以人物区域为基准提升整体亮度,背景区域保持原状;轮廓光则依赖深度梯度提取边缘;舞台光更要求深度阈值将人物以外区域压至全黑。如果深度图噪声过大,建议先用CIBilateralFilter或自定义平滑核处理,否则光效边缘会出现明显抖动。
下面代码展示了如何从采样缓冲中生成CIImage深度图并做基础缩放,注意深度数据转CIImage时类型匹配:
import AVFoundation
import CoreImage
func depthCIImage(from depthData: AVDepthData) -> CIImage? {
let depthPixelBuffer = depthData.depthDataMap
let ciContext = CIContext()
// 将深度像素缓冲包装为CIImage
let depthImage = CIImage(cvPixelBuffer: depthPixelBuffer)
// 若彩色图尺寸不同,可在此缩放深度图
let scaled = depthImage.transformed(by: CGAffineTransform(scaleX: 0.5, y: 0.5))
return scaled
}
漫反射与轮廓光的光照模型实现
漫反射光效追求自然柔和的正面补光,其原理近似于在人物前方放置大型柔光箱。技术实现上,我们读取深度图得到人物前景掩码,对该掩码区域叠加一层低对比度、偏高曝光的亮度增益,同时利用高斯模糊后的深度掩码让增益在边缘渐变,避免硬切边。Core Image中可用CIBlendWithMask将提亮后的副本与原图按掩码融合,掩码本身由深度阈值生成。
轮廓光则关注人物与背景交界处的亮边。计算方式是对深度图做梯度运算,提取深度变化剧烈的边缘带,再沿该带添加冷色调或白光高亮。由于iOS设备算力有限,梯度提取可用CITemperatureAndTint配合自定义Kernel,或借助CIColorMatrix将边缘权重映射为发光强度。轮廓光强度应受控,否则在发丝等细节处会产生光晕伪影。
以下示例用Core Image自定义内核计算简易边缘权重并混合光效,展示轮廓光基本思路:
import CoreImage
let kernelSource = """
kernel vec4 contourLight(sampler src, sampler depth) {
vec2 dc = samplerCoord(depth);
float d = sample(depth, dc).r;
// 简单相邻采样求梯度
float dRight = sample(depth, dc + vec2(1.0, 0.0)).r;
float dUp = sample(depth, dc + vec2(0.0, 1.0)).r;
float edge = abs(d - dRight) + abs(d - dUp);
edge = clamp(edge * 20.0, 0.0, 1.0);
vec4 color = sample(src, samplerCoord(src));
// 沿边缘加白光
return color + vec4(edge * 0.6, edge * 0.6, edge * 0.6, 0.0);
}
"""
if let kernel = CIColorKernel(source: kernelSource) {
let output = kernel.apply(extent: src.extent, arguments: [src, depthImg])
}
舞台光效果与滤镜链性能优化
舞台光是三种效果中对比最强烈的形态:背景完全隐入黑暗,仅人物被聚光照明。其实现依赖深度阈值将非人物区域映射为纯黑,再对人物区施加径向衰减的亮斑。常用做法是先用深度图生成二元遮罩,遮罩外区域通过CIColorMatrix拉低明度至零;人物区则结合漫反射提亮,形成聚光灯错觉。由于舞台光舍弃背景,深度图误差会直接表现为人物被啃边,因此阈值需随场景光照动态调整。
在将漫反射、轮廓光、舞台光组合为可调滤镜链时,性能是关键瓶颈。每一级CIFilter都会占用GPU或CPU资源,频繁重算深度梯度会引发掉帧。推荐把深度衍生图(如掩码、边缘权重)缓存为CIImage并复用,只在参数变更时重渲染。同时用CIContext的缓存策略减少纹理上传。下表列出三种光效的运算侧重与优化点:
| 光效类型 | 主要运算 | 优化建议 |
|---|---|---|
| 漫反射 | 区域提亮与遮罩混合 | 预生成模糊掩码,降低采样率 |
| 轮廓光 | 深度梯度与边缘发光 | 限制梯度半径,避免全图卷积 |
| 舞台光 | 阈值分割与聚光衰减 | 动态阈值,缓存二元遮罩 |
代码层面,可用CIFilter链将多个效果顺序连接,但务必在后台串行队列处理渲染请求,防止主线程阻塞。下面示范用CIFilter生成舞台光遮罩并合成:
import CoreImage
func stageLight(src: CIImage, depth: CIImage) -> CIImage? {
// 深度转灰度遮罩:近处为白,远处为黑
let mask = CIFilter.colorMatrix()
mask.inputImage = depth
mask.rVector = CIVector(x: 1, y: 0, z: 0, w: 0)
mask.gVector = CIVector(x: 1, y: 0, z: 0, w: 0)
mask.bVector = CIVector(x: 1, y: 0, z: 0, w: 0)
// 用阈值滤镜强化人物区
let threshold = CIFilter.linearToSRGBToneCurve()
threshold.inputImage = mask.outputImage
// 混合原图与黑底
let blend = CIFilter.blendWithMask()
blend.inputImage = src
blend.backgroundImage = CIImage.black
blend.maskImage = threshold.outputImage
return blend.outputImage
}
综合来看,Core Image结合深度图实现人像光效是一条可行且高效的原生路径。开发者只要理清深度数据流向、光照模型差异以及滤镜链资源占用,就能在iOS端稳定输出漫反射、轮廓光与舞台光,而不必依赖第三方引擎。实际项目中建议针对设备代际做效果分级,老旧机型关闭梯度类轮廓光以保帧率。
Core_ImageiOS人像光效深度图修改时间:2026-08-13 06:33:33