Swift 4中基于摄像头的实时人脸追踪问题咨询
关于Swift人脸追踪的两个问题解答
嘿,我来帮你把这些问题理清楚~
一、旧代码无效的原因:代理方法的变更
没错,你猜的完全正确——Swift 4之后AVCaptureOutput的代理方法确实有了关键性修改,这就是你旧代码运行无效果的核心原因!
早期的方法签名是这样的:
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!)
而Swift 4及以后,苹果调整了参数的可选性和命名,正确的代理方法签名应该是:
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { // 这里处理摄像头输出的帧数据 }
关键变化点:
- 移除了所有参数后的
!(隐式可选类型),改为非可选类型 - 参数名从
captureOutput简化为output,didOutputSampleBuffer简化为didOutput
如果你的代码还在使用旧的方法签名,系统会认为这是一个自定义方法,不会触发AVCapture的代理回调,自然就看不到任何效果了。
二、用CIFaceFeature监测人脸关键点的实现步骤
要实现人脸关键点监测,你可以在上面的新代理方法里,结合CIDetector和CIFaceFeature来处理每帧画面,具体步骤如下:
1. 提前创建人脸检测器
建议在类初始化时创建,避免重复初始化消耗性能:
private lazy var faceDetector: CIDetector = { // 配置检测器选项:高精度+开启追踪 let options = [ CIDetectorAccuracy: CIDetectorAccuracyHigh, CIDetectorTracking: true, CIDetectorLandmarks: true // 必须开启这个才能检测关键点 ] as [String: Any] return CIDetector(ofType: CIDetectorTypeFace, context: nil, options: options)! }()
2. 在代理方法中处理帧数据并检测人脸
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { // 1. 将SampleBuffer转换为CIImage guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } let ciImage = CIImage(cvImageBuffer: imageBuffer) // 2. 检测人脸特征 guard let faceFeatures = faceDetector.features(in: ciImage) as? [CIFaceFeature] else { return } // 3. 遍历人脸,提取关键点 for face in faceFeatures { // 基础关键点:左眼、右眼、鼻子、嘴巴 if face.hasLeftEyePosition { let leftEyePoint = convertCItoUIKitPoint(face.leftEyePosition, imageSize: ciImage.extent.size) print("左眼坐标:\(leftEyePoint)") } if face.hasRightEyePosition { let rightEyePoint = convertCItoUIKitPoint(face.rightEyePosition, imageSize: ciImage.extent.size) print("右眼坐标:\(rightEyePoint)") } // 更精细的关键点:比如眼睛轮廓、嘴巴轮廓(需要开启CIDetectorLandmarks) if let leftEyeLandmarks = face.leftEyeLandmarks { for point in leftEyeLandmarks.pointsInImage(imageSize: ciImage.extent.size) { let convertedPoint = convertCItoUIKitPoint(point, imageSize: ciImage.extent.size) print("左眼轮廓点:\(convertedPoint)") } } } } // 辅助方法:转换CoreImage坐标系到UIKit坐标系(CoreImage原点在左下角,UIKit在左上角) private func convertCItoUIKitPoint(_ point: CGPoint, imageSize: CGSize) -> CGPoint { return CGPoint(x: point.x, y: imageSize.height - point.y) }
注意事项
- 必须在检测器配置中设置
CIDetectorLandmarks: true,否则无法获取精细的轮廓关键点 - 坐标转换很重要,否则你拿到的点会和屏幕显示的位置颠倒
- 如果需要实时绘制关键点,建议把绘制逻辑放到主线程执行(因为代理方法在子线程)
内容的提问来源于stack exchange,提问作者coder
相关产品推荐
相关产品推荐

