(十二)深入了解AVFoundation-采集:人脸识别与元数据处理

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| (一)深入了解AVFoundation:框架概述与核心模块解析-CSDN博客 |
| (二) 深入了解AVFoundation - 播放:AVFoundation 播放基础入门-CSDN博客 |
| (三)深入了解AVFoundation-播放:AVPlayer 进阶 播放状态 & 进度监听全解析_avplayer 播放状态-CSDN博客 |
| (四)深入理解AVFoundation-播放:高度自定义视频播放器 UI-CSDN博客 |
| (五)深入了解AVFoundation-播放:多音轨、字幕、倍速播放与横竖屏切换-CSDN博客 |
| (六)深入了解AVFoundation-播放:AirPlay、画中画后台播放_air.av-CSDN博客 |
| (七)深入了解AVFoundation-采集:采集系统架构与 AVCaptureSession 全面梳理_avcapturesession startrunning子线程调用-CSDN博客 |
| (八)深入了解AVFoundation-采集:拍照功能的实现_ios avcapturephotooutput-CSDN博客 |
| (九)深入了解AVFoundation-采集:拍照 摄像头切换、拍照参数和照片数据EXIF 信息-CSDN博客 |
| (十)深入了解AVFoundation-采集:录制视频功能的实现-CSDN博客 |
| (十一)深入了解AVFoundation-采集:二维码识别-CSDN博客 |
| (十二)深入了解AVFoundation-采集:人脸识别与元数据处理-CSDN博客 |

引言

在 AVFoundation 的采集体系中,元数据输出(AVCaptureMetadataOutput)不仅可用于识别二维码等图像信息,也支持对特定对象的实时检测与追踪,其中就包括人脸识别功能。与二维码扫描相比,人脸识别在逻辑处理和 UI 显示方面更具互动性和复杂性,也更贴近实际产品需求,如人脸识别登录、镜头跟踪拍摄、美颜滤镜等功能。

本篇将基于前文二维码采集的基础,深入讲解如何利用 AVFoundation 实现人脸的实时识别、坐标转换、识别框绘制与多人追踪。我们将粗略概括重复的输入与权限设置,聚焦在 元数据处理的核心逻辑UI 动态响应机制,帮助你构建一个功能清晰、体验流畅的人脸识别系统。

配置回顾:输入与输出设置

在本节中,我们简要回顾用于人脸识别的输入与输出配置流程。由于人脸识别同样基于 AVCaptureMetadataOutput,因此整体结构与二维码扫描的配置几乎一致。我们无需重复搭建会话、添加输入等流程,只需在输出中指定不同的元数据类型即可完成切换。

下面我们快速回顾相关代码配置,并重点指出区别所在。

输入

输入部分与二维码识别完全相同。我们使用设备的摄像头作为输入源,添加到会话中用于实时视频采集:

Swift 复制代码
import UIKit
import AVFoundation

class PHCaptureFaceController: NSObject,AVCaptureMetadataOutputObjectsDelegate {
    
    /// 会话
    let session = AVCaptureSession()
    /// 输出
    private let metadataOutput = AVCaptureMetadataOutput()
    /// 输入
    private var captureDeviceInput: AVCaptureDeviceInput?
    /// 队列
    private let sessionQueue = DispatchQueue(label: "com.example.captureSession")
    /// 代理
    weak var delegate: PHCaptureProtocol?
    
    /// 配置会话
    func setupConfigureSession() {
        session.beginConfiguration()
        // 1.设置会话预设
        setupSessionPreset()
        // 2.设置会话输入
        if !setupSessionInput(device: self.getDefaultCameraDevice()) {
            delegate?.captureError(NSError(domain: "PHCaptureQRController", code: 1001, userInfo: [NSLocalizedDescriptionKey: "Failed to add input"]))
            return
        }
        // 3.设置会话输出
        if !setupSessionOutput() {
            delegate?.captureError(NSError(domain: "PHCaptureQRController", code: 1002, userInfo: [NSLocalizedDescriptionKey: "Failed to add output"]))
            return
        }
        session.commitConfiguration()
    }
    
    /// 设置会话话预设
    private func setupSessionPreset() {
        session.sessionPreset = .photo
    }
    
    /// 设置会话输入
    private func setupSessionInput(device: AVCaptureDevice? = nil) -> Bool {
        // 1.获取摄像头
        guard let device = device else { return false }
        do {
            captureDeviceInput = try AVCaptureDeviceInput(device: device)
            if session.canAddInput(captureDeviceInput!) {
                session.addInput(captureDeviceInput!)
            } else {
                return false
            }
        } catch {
            delegate?.captureError(error)
            return false
        }
        return true
    }
    
    /// 设置会话输出
    private func setupSessionOutput() -> Bool {
       
    }
        
    /// 启动会话
    func startSession() {
        sessionQueue.async {
            if !self.session.isRunning {
                self.session.startRunning()
            }
        }
    }
    
    /// 停止会话
    func stopSession() {
        sessionQueue.async {
            if self.session.isRunning {
                self.session.stopRunning()
            }
        }
    }
    
    //MARK: - AVCaptureMetadataOutputObjectsDelegate
    func metadataOutput(_ output: AVCaptureMetadataOutput,
                        didOutput metadataObjects: [AVMetadataObject],
                        from connection: AVCaptureConnection) {
        delegate?.captureFace(metadataObjects)
    }
    
    /// 获取默认摄像头
    private func getDefaultCameraDevice() -> AVCaptureDevice? {
       return getCameraDevice(position: .back)
    }
    
    /// 获取指定摄像头
    private func getCameraDevice(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
        let devices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices
        return devices.first
    }

}

输出配置

输出依然使用 AVCaptureMetadataOutput,但这里我们将 metadataObjectTypes 设置为人脸识别类型 .face。

Swift 复制代码
    /// 设置会话输出
    private func setupSessionOutput() -> Bool {
        if session.canAddOutput(metadataOutput) {
            session.addOutput(metadataOutput)
            metadataOutput.metadataObjectTypes = [.face]
            // 设置 代理及输出队列
            metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            
        } else {
            return false
        }
        return true
    }

在设置完成后,只要摄像头画面中检测到人脸,系统就会将人脸对象作为 AVMetadataFaceObject 回调到代理方法中,供后续处理。

元数据处理核心逻辑

关于这部分的内容我们分成三个小结来介绍一下:

  • 实现代理方法与人脸识别回调。
  • 坐标准话与画面映射。
  • 绘制人脸框与多人识别支持。

实现代理方法与人脸识别回调

同样我们实现 AVCaptureMetadataOutputObjectsDelegate中定义的代理方法,将识别到的元数据传递到视图控制来处理。

Swift 复制代码
    //MARK: - AVCaptureMetadataOutputObjectsDelegate
    func metadataOutput(_ output: AVCaptureMetadataOutput,
                        didOutput metadataObjects: [AVMetadataObject],
                        from connection: AVCaptureConnection) {
        delegate?.captureFace(metadataObjects)
    }

在 captureFace() 方法中,我们可以读取到人脸信息包括人脸所在的区域以及人脸id:

坐标转换与画面映射

在二维码识别中,我们已经进行过了一次坐标转换,同样在获取到人脸元数据对象后,我们也需要进行坐标转换,将人脸在摄像头图像中的位置坐标,准确映射到实际的 UI 预览图层中,用于绘制人脸框或进行其他界面响应。

AVFoundation 中使用的是摄像头原始坐标系(以图像像素为基础,左上角为 (0,0),右下为 (1,1) 的比例坐标),而我们在界面上使用的却是 UIKit 的坐标系,因此需要进行一次坐标系转换

AVCaptureVideoPreviewLayer 提供了现成的方法来进行这一转换:

Swift 复制代码
            if let faceObject = face as? AVMetadataFaceObject {
                // 获取人脸的矩形区域
                let transformedMetadataObject = previewLayer.transformedMetadataObject(for: faceObject)
                if let transformedFace = previewLayer.transformedMetadataObject(for: faceObject) {
                    // transformedFace.bounds 就是可以直接用于绘图的 CGRect(相对于界面的坐标系)
                    let faceFrame = transformedFace.bounds
                    // 后续用于添加 UIView 或 CAShapeLayer
                    drawFaceBoundingBox(in: faceFrame)
                }
                
            }

绘制人脸框与多人识别支持

将人脸元数据转换为 UI 坐标后,我们可以将其用于实际的界面反馈,例如在人脸区域绘制可视化边框,提升识别体验。由于摄像头中可能出现多个面孔,绘制逻辑需要支持动态增减人脸框,并根据系统识别结果不断更新。

创建与更新人脸框

每一个被识别到的人脸对象都包含一个唯一的 faceID,我们可以用它作为字典的 Key,将其与对应的 CALayer一一对应。

以下是一个标准的绘制流程:

  1. 遍历所有识别到的人脸;
  2. 对每个 faceID 判断是否已有对应图层,没有则创建;
  3. 更新图层的 frame 与角度;
  4. 对丢失的人脸进行清除。
Swift 复制代码
func didDetectFaces(_ faces: [AVMetadataFaceObject]) {
    let transformedFaces = faces.compactMap { face in
        return previewLayer.transformedMetadataObject(for: face) as? AVMetadataFaceObject
    }

    var lostFaceIDs = Set(faceLayers.keys)

    for face in transformedFaces {
        let faceID = face.faceID
        lostFaceIDs.remove(faceID)

        let layer: CALayer
        if let existingLayer = faceLayers[faceID] {
            layer = existingLayer
        } else {
            layer = makeFaceLayer()
            overlayLayer.addSublayer(layer)
            faceLayers[faceID] = layer
        }

        layer.transform = CATransform3DIdentity
        layer.frame = face.bounds

        if face.hasRollAngle {
            let rollTransform = transformForRollAngle(face.rollAngle)
            layer.transform = CATransform3DConcat(layer.transform, rollTransform)
        }

        if face.hasYawAngle {
            let yawTransform = transformForYawAngle(face.yawAngle)
            layer.transform = CATransform3DConcat(layer.transform, yawTransform)
        }
    }

    for faceID in lostFaceIDs {
        faceLayers[faceID]?.removeFromSuperlayer()
        faceLayers.removeValue(forKey: faceID)
    }
}

图层样式与角度旋转

我们可以使用 CALayer 来绘制矩形边框,并通过角度信息使其旋转对齐面部朝向。

Swift 复制代码
func makeFaceLayer() -> CALayer {
    let layer = CALayer()
    layer.borderColor = UIColor.red.cgColor
    layer.borderWidth = 2.0
    layer.cornerRadius = 4.0
    return layer
}

人脸元数据中的 rollAngle 表示绕 Z 轴(平面内)旋转,而 yawAngle 表示绕 Y 轴(前后方向)旋转:

Swift 复制代码
func transformForRollAngle(_ degrees: CGFloat) -> CATransform3D {
    let radians = degrees * .pi / 180.0
    return CATransform3DMakeRotation(radians, 0.0, 0.0, 1.0)
}

func transformForYawAngle(_ degrees: CGFloat) -> CATransform3D {
    let radians = degrees * .pi / 180.0
    let yawTransform = CATransform3DMakeRotation(radians, 0.0, -1.0, 0.0)
    return CATransform3DConcat(yawTransform, orientationTransform())
}

func orientationTransform() -> CATransform3D {
    let orientation = UIDevice.current.orientation
    let angle: CGFloat

    switch orientation {
    case .landscapeLeft:
        angle = .pi / 2
    case .landscapeRight:
        angle = -.pi / 2
    case .portraitUpsideDown:
        angle = .pi
    default:
        angle = 0
    }

    return CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
}

支持多人识别与人脸移除

在每一帧中,我们根据当前返回的面孔数组来判断有哪些 faceID 不再出现,从而动态移除对应的图层。这样既保持了界面的同步性,也避免了残留 UI 的问题。

结语

本篇我们聚焦于 AVFoundation 中人脸识别的实现方式,从元数据输出类型的设置出发,详细讲解了识别流程、坐标转换,以及如何支持多人识别与人脸框绘制。借助系统提供的 AVMetadataFaceObject,我们可以较为轻松地将摄像头中的面孔在界面上实时呈现,为人脸相关的 UI 效果打下基础。

尽管 AVFoundation 的人脸识别功能较为基础,但对于实时展示、面部 UI 跟随等需求已经足够。更复杂的面部关键点检测、情绪识别等功能,则可以结合 Vision 框架进一步拓展,我们将在后续章节中继续探索。

相关推荐
胖虎13 天前
(十)深入了解AVFoundation-采集:录制视频功能的实现
视频采集·拍摄·视频录制·avfoundation·ios录制视频
胖虎115 天前
(八)深入了解AVFoundation-采集:拍照功能的实现
拍照·avfoundation·ios拍照·自定义拍照
假客套18 天前
2025 活体识别+人脸认证工具类【阿里云api,需要先申请试用】
阿里云·人脸识别·活体检测·java工具类
胖虎118 天前
(七)深入了解AVFoundation-采集:采集系统架构与 AVCaptureSession 全面梳理
视频采集·avfoundation·avcapture·capturesession
知来者逆1 个月前
探索在视频深度伪造中的细微的表情变化或对特定面部特征的小改动检测方法
图像处理·人工智能·目标检测·计算机视觉·人脸识别
胖虎11 个月前
(五)深入了解AVFoundation-播放:多音轨、字幕、倍速播放与横竖屏切换
avplayer·视频播放器·字幕·avfoundation·音轨
西红柿土豆丶1 个月前
人脸考勤管理一体化系统(人脸识别系统,签到打卡)
python·深度学习·opencv·人脸识别·人脸识别系统·考勤管理系统·签到打卡
计算机徐师兄1 个月前
Python Django基于人脸识别的票务管理系统(附源码,文档说明)
python·django·人脸识别·票务系统·票务管理系统·票务管理·人脸识别票务系统
小白教程1 个月前
python人脸检测、人脸识别、活体检测入门学习教程
开发语言·python·人脸识别·人脸检测·活体检测·活体认证·python人脸识别