使用 Vision 检测人物、面部和姿态
Detect people, faces, and poses using Vision
2021年6月11日
一句话判断
Vision 框架在 iOS 15 把人体分析能力从「检测有没有人」升级到「分析人在干什么」——全身姿态检测和人体分割是最大的两个新能力。
这场 Session 讲了什么
Vision 框架是 Apple 在端侧视觉智能的核心框架。iOS 15 对人体分析能力做了全面升级,覆盖了从检测到理解的完整链路。
人体姿态检测(Body Pose Detection)是最大的新增功能。VNDetectHumanBodyPoseRequest 可以检测人体 19 个关键点(头、肩、肘、腕、髋、膝、踝等),支持多人同时检测。这个能力可以直接用于健身 app 的动作矫正、体感游戏的动作输入、或者运动分析。
人体分割(Human Segmentation)也有了改进。VNGeneratePersonSegmentationRequest 现在支持三种输出模式:前景掩码(二值图)、高质量抠图(带 Alpha 通道)和粗糙的深度图。这使得视频通话中的虚拟背景、AR 中的前景遮挡成为可能。
面部检测方面,VNDetectFaceCaptureQualityRequest 是新的 API,可以评估一张照片中面部的拍摄质量(对焦、曝光、表情),帮你自动筛选最佳照片。
值得深挖的点
全身姿态检测与手部姿态检测的协作
Body Pose 和 Hand Pose 可以组合使用。先检测全身姿态,定位手部区域,然后在手部区域上运行 Hand Pose Detection 获取手指级别的精度。这种级联方式比直接在全图上跑 Hand Pose 更高效——因为 Hand Pose 检测的计算量远大于 Body Pose。
Person Segmentation 在视频通话中的应用
视频通话中的虚拟背景需要实时分割前景人物。VNGeneratePersonSegmentationRequest 在 A14+ 芯片上可以跑到 30fps 以上,满足了实时性要求。但要注意,Vision 的 segmentation 是基于单帧的,不会做时域一致性处理。这意味着在帧与帧之间,边缘可能会闪烁。如果需要更平滑的效果,建议在输出掩码上应用简单的时域滤波。
代码片段
// 检测人体全身姿态
import Vision
class BodyPoseDetector {
let request = VNDetectHumanBodyPoseRequest()
func detect(image: CGImage) throws -> [VNHumanBodyPoseObservation] {
let handler = VNImageRequestHandler(cgImage: image, options: [:])
try handler.perform([request])
return request.results ?? []
}
func analyzePose(observation: VNHumanBodyPoseObservation) {
// 获取各部位的关键点
let bodyParts: [VNHumanBodyPoseObservation.JointName] = [
.nose, .leftShoulder, .rightShoulder,
.leftElbow, .rightElbow,
.leftWrist, .rightWrist,
.leftHip, .rightHip,
.leftKnee, .rightKnee
]
for joint in bodyParts {
if let point = try? observation.recognizedPoint(joint) {
print("\(joint.rawValue): (\(point.location.x), \(point.location.y)) 置信度: \(point.confidence)")
}
}
}
}
// 人体分割 - 生成前景掩码
class PersonSegmenter {
let request = VNGeneratePersonSegmentationRequest()
init() {
request.qualityLevel = .accurate // 高质量模式
request.outputPixelFormat = kCVPixelFormatType_OneComponent8 // 灰度掩码
}
func segment(image: CGImage) throws -> CGImage? {
let handler = VNImageRequestHandler(cgImage: image, options: [:])
try handler.perform([request])
guard let observation = request.results?.first else { return nil }
return try? observation.generateScaledMaskForImage(
forInstances: observation.allInstances,
from: handler
)
}
// 应用虚拟背景
func applyVirtualBackground(
original: CGImage,
mask: CGImage,
background: CGImage
) -> CGImage? {
// 使用 CIImage 的 blendWithMask 滤镜合成
let originalCI = CIImage(cgImage: original)
let maskCI = CIImage(cgImage: mask)
let backgroundCI = CIImage(cgImage: background)
.cropped(to: originalCI.extent)
let blended = backgroundCI.applyingFilter(
"CIBlendWithMask",
parameters: [
kCIInputMaskImageKey: maskCI,
kCIInputBackgroundImageKey: originalCI
]
)
let context = CIContext()
return context.createCGImage(blended, from: blended.extent)
}
}
// 评估面部拍摄质量
func evaluateFaceQuality(image: CGImage) {
let request = VNDetectFaceCaptureQualityRequest()
let handler = VNImageRequestHandler(cgImage: image, options: [:])
try? handler.perform([request])
if let result = request.results?.first {
print("面部拍摄质量: \(result.faceCaptureQuality)")
// 值在 0.0 - 1.0 之间,> 0.8 为高质量
}
}
最佳实践
Body Pose Detection 在人物距离相机太远(画面中人物高度小于画面的 1/5)时,关键点检测的精度会显著下降。如果你的 app 需要精确的姿态分析,建议先用 VNDetectHumanRectanglesRequest 检测人物边界框,过滤掉太远的人。
Person Segmentation 的 qualityLevel 有三档:.fast(低精度高速度)、.balanced(平衡)、.accurate(高精度低速度)。视频通话用 .balanced,照片编辑用 .accurate。.fast 模式的边缘质量太粗糙,几乎不可用于生产环境。
还有什么值得关注
VNTrackHumanBodyPoseRequest可以在视频流中追踪特定人物的姿态,避免每帧重新检测。- Vision 框架的请求现在支持
async/await封装,不再需要 completion handler。 - 人体姿态数据可以直接导出为 JSON,方便与服务端的 ML 模型配合使用。