(十二)深入了解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,因此整体结构与二维码扫描的配置几乎一致。我们无需重复搭建会话、添加输入等流程,只需在输出中指定不同的元数据类型即可完成切换。

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

输入

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

import UIKit
import AVFoundationclass 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: - AVCaptureMetadataOutputObjectsDelegatefunc 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).devicesreturn devices.first}}

输出配置

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

    /// 设置会话输出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 中定义的代理方法,将识别到的元数据传递到视图控制来处理。

    //MARK: - AVCaptureMetadataOutputObjectsDelegatefunc metadataOutput(_ output: AVCaptureMetadataOutput,didOutput metadataObjects: [AVMetadataObject],from connection: AVCaptureConnection) {delegate?.captureFace(metadataObjects)}

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

坐标转换与画面映射

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

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

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

            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 或 CAShapeLayerdrawFaceBoundingBox(in: faceFrame)}}

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

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

创建与更新人脸框

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

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

  1. 遍历所有识别到的人脸;
  2. 对每个 faceID 判断是否已有对应图层,没有则创建;
  3. 更新图层的 frame 与角度;
  4. 对丢失的人脸进行清除。
func didDetectFaces(_ faces: [AVMetadataFaceObject]) {let transformedFaces = faces.compactMap { face inreturn previewLayer.transformedMetadataObject(for: face) as? AVMetadataFaceObject}var lostFaceIDs = Set(faceLayers.keys)for face in transformedFaces {let faceID = face.faceIDlostFaceIDs.remove(faceID)let layer: CALayerif let existingLayer = faceLayers[faceID] {layer = existingLayer} else {layer = makeFaceLayer()overlayLayer.addSublayer(layer)faceLayers[faceID] = layer}layer.transform = CATransform3DIdentitylayer.frame = face.boundsif 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 来绘制矩形边框,并通过角度信息使其旋转对齐面部朝向。

func makeFaceLayer() -> CALayer {let layer = CALayer()layer.borderColor = UIColor.red.cgColorlayer.borderWidth = 2.0layer.cornerRadius = 4.0return layer
}

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

func transformForRollAngle(_ degrees: CGFloat) -> CATransform3D {let radians = degrees * .pi / 180.0return CATransform3DMakeRotation(radians, 0.0, 0.0, 1.0)
}func transformForYawAngle(_ degrees: CGFloat) -> CATransform3D {let radians = degrees * .pi / 180.0let yawTransform = CATransform3DMakeRotation(radians, 0.0, -1.0, 0.0)return CATransform3DConcat(yawTransform, orientationTransform())
}func orientationTransform() -> CATransform3D {let orientation = UIDevice.current.orientationlet angle: CGFloatswitch orientation {case .landscapeLeft:angle = .pi / 2case .landscapeRight:angle = -.pi / 2case .portraitUpsideDown:angle = .pidefault:angle = 0}return CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)
}

支持多人识别与人脸移除

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

结语

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/79466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kafka 与 RabbitMQ、RocketMQ 有何不同?

一、不同的诞生背景,塑造了不同的“性格” 名称 背景与目标 产品定位 Kafka 为了解决 LinkedIn 的日志收集瓶颈,强调吞吐与持久化 更像一个“可持久化的分布式日志系统” RabbitMQ 出自金融通信协议 AMQP 的实现,强调协议标准与广泛适…

配置 Web 服务器练习

一、要求 1.通过https://ip 可以访问到网站首页 2.通过 https://ip/private/ 实现用户访问控制,仅允许已经添加的 tom,jerry 能够访问到 private 子路径的界面 3.通过 https://ip/vrit/ 实现能够访问到将系统 /nginx/virt 目录下的网页文件&#xff0…

MySQL索引详解(下)(SQL性能分析,索引使用)

索引是MySQL性能优化的核心,但如何精准分析查询瓶颈、合理设计索引,是开发者必须掌握的技能。本文结合实战案例,系统讲解SQL性能分析工具链与索引使用技巧,帮助读者构建高性能数据库系统。 一、SQL性能分析:从宏观到微…

招行数字金融挑战赛数据赛道赛题一

赛题描述:根据提供的用户行为数据,选手需要分析用户行为特征与广告内容的匹配关系,准确预测用户对测试集广告的点击情况,通过AUC计算得分。 得分0.6120,排名60。 尝试了很多模型都没有能够提升效果,好奇大…

ORB-SLAM3和VINS-MONO的对比

直接给总结,整体上orbslam3(仅考虑带imu)在初始化阶段是松耦合,localmap和全局地图优化是紧耦合。而vins mono则是全程紧耦合。然后两者最大的区别就在于vins mono其实没有对地图点进行优化,为了轻量化,它一…

安装typescript时,npm install -g typescript报错

删除C:\Users\用户\下的.npmrc文件,如果你的没有,看是不是因为将隐藏的项目勾选上了,然后去掉勾选。 重新输入

[GESP202503 四级] 二阶矩阵c++

题目描述 小 A 有一个 n 行 m 列的矩阵 A。 小 A 认为一个 22 的矩阵 D 是好的,当且仅当 。其中 表示矩阵 D 的第 i 行第 j 列的元素。 小 A 想知道 A 中有多少个好的子矩阵。 输入 第一行,两个正整数 n,m。 接下来 n 行,每行 m 个整数…

基于flask+pandas+csv的报表实现

基于大模型根据提示词去写SQL执行SQL返回结果输出报表技术上可行的,但为啥还要基于pandas去实现呢? 原因有以下几点: 1、大模型无法满足实时性输出报表的需求; 2、使用大模型比较适合数据量比较大的场景,大模型主要…

Java学习笔记(对象)

一、对象本质 状态(State):通过成员变量(Field)描述 行为(Behavior):通过成员方法(Method)实现 class Person {String name;int age;void eat() {System.o…

Qt学习Day0:Qt简介

0. 关于Qt Qt是C的实践课,之前在C中学习的语法可以有具体的应用场景。Qt的代码量很大,不要死记硬背,学会查询文档的能力更加重要。 建议提升一下相关单词的储备量: 1. Qt是什么? Qt是一个基于C语言的图形用户界面&a…

React知识框架

一、核心概念 1. 组件化开发 核心思想:将 UI 拆分为独立、可复用的组件(函数组件/类组件)。组件特性:props(接收参数)、state(组件状态)、生命周期(类组件特有&#xf…

Django之账号登录及权限管理

账号登录及权限管理 目录 1.登录功能 2.退出登录 3.权限管理 4.代码展示合集 这篇文章, 会讲到如何实现账号登录。账号就是我们上一篇文章写的账号管理功能, 就使用那里面已经创建好的账号。这一次登录, 我们分为三种角色, 分别是员工, 领导, 管理员。不同的角色, 登录进去…

[学习]RTKLib详解:convkml.c、convrnx.c与geoid.c

本文是 RTKLlib详解 系列文章的一篇,目前该系列文章还在持续总结写作中,以发表的如下,有兴趣的可以翻阅。 [学习] RTKlib详解:功能、工具与源码结构解析 [学习]RTKLib详解:pntpos.c与postpos.c [学习]RTKLib详解&…

java 破解aspose.words 18.6 使用

资源包&#xff1a;https://download.csdn.net/download/qq_36598111/90787167 jar包是破解过的&#xff0c;直接可以使用。 引入jar&#xff0c;要引入本地的&#xff0c;不要直接引入仓库的 <dependency><groupId>com.aspose</groupId><artifactId>…

vue使用rules实现表单校验——校验用户名和密码

编写校验规则 常规校验 const rules {username: [{ required: true, message: 请输入用户名, trigger: blur },{ min: 5, max: 16, message: 长度在 5 到 16 个字符, trigger: blur }],password: [{ required: true, message: 请输入密码, trigger: blur },{ min: 5, max: 1…

宝塔服务安装使用的保姆级教程

宝塔介绍&#xff1a; 宝塔面板&#xff08;BT Panel&#xff09; 是一款 国产的服务器运维管理面板&#xff0c;主要用于简化 Linux/Windows 服务器的网站、数据库、FTP、防火墙等管理操作。它通过图形化界面&#xff08;Web端&#xff09;和命令行工具&#xff08;bt 命令&a…

数字化转型-4A架构之数据架构

4A架构系列文章 数字化转型-4A架构&#xff08;业务架构、应用架构、数据架构、技术架构&#xff09; 数字化转型-4A架构之业务架构 数字化转型-4A架构之应用架构 数字化转型-4A架构之数据架构 数字化转型-4A架构之技术架构 数据架构 Data Architecture&#xff08;DA&…

每日脚本 5.11 - 进制转换和ascii字符

前置知识 python中各个进制的开头 二进制 &#xff1a; 0b 八进制 &#xff1a; 0o 十六进制 : 0x 进制转换函数 &#xff1a; bin() 转为2进制 oct() 转换为八进制的函数 hex() 转换为16进制的函数 ascii码和字符之间的转换 &#xff1a; chr(97) 码转为字符 …

Vulnhub Lazysysadmin靶机攻击实战(一)

导语   靶机下载地址 https://download.vulnhub.com/lazysysadmin/Lazysysadmin.zip   靶机信息地址 https://www.vulnhub.com/entry/lazysysadmin-1,205/ 文章目录 信息收集扫描路径提权其他思路探索其他方式找密码总结下载安装好靶机之后启动虚拟机如下所示。 信息收集 我…

【DB2】DB2启动失败报错SQL1042C

在本地某次启动db2时报错SQL1042C&#xff0c;具体报错如下 [db2inst1standby ~]$ db2start 05/07/2025 16:32:53 0 0 SQL1042C An unexpected system error occurred. SQL1032N No start database manager command was issued. SQLSTATE57019在网上百度到说是需要…