Highlight
Vision 是 Apple 的计算机视觉框架,提供人脸检测、文字识别、人体姿态估计、条码扫描等 31 种图像分析请求。今年 Apple 用 Swift 原生 API 重新设计了框架,支持 async/await、throws 和 Swift 6 严格并发检查(01:09)。
核心内容
Vision 框架一直是 Apple 平台上做计算机视觉的首选工具。它能检测人脸和面部特征点(眼睛、鼻子、嘴巴),支持 18 种语言的文字识别(包括韩语、瑞典语、中文),还能追踪人体姿态和手势轨迹(00:24)。
但旧 API 有个明显问题:基于 Objective-C 的 completion handler 设计,与 Swift 的现代异步模式格格不入。代码里充斥着嵌套回调和 NSError 处理,而且不支持 Swift 6 的并发安全检查(11:13)。
今年 Apple 彻底重写了 Vision 的 Swift API。核心变化只有三条规则:去掉 VN 前缀、去掉 completion handler、用 async/await 替代回调。结果就是代码量从 10 行减少到 6 行,而且类型安全、并发安全(12:11)。
除了 API 现代化,Apple 还解决了两个长期痛点:归一化坐标系转换(新 API toImageCoordinates()),以及同时执行多个请求时的流式结果处理(performAll 返回 AsyncStream)(05:37, 08:22)。
最后还有两个新功能:图像美学评分(CalculateImageAestheticsScoresRequest,评估照片质量和 memorable 程度),以及 holistic body pose(在 DetectHumanBodyPoseRequest 上设置 detectsHands = true,同时检测身体和手部姿态)(14:51, 15:21)。
详细内容
Vision 的核心概念很简单:向图像提问(request),得到答案(observation)。框架提供 31 种不同的请求类型,涵盖图像分类、文字识别、条码检测、人体姿态估计等(02:59)。
基础用法:扫描条码
新 API 的极简风格体现在三行代码:
// ([04:06](https://developer.apple.com/videos/play/wwdc2024/10163/?time=246))
let request = DetectBarcodesRequest()
let barcodeObservations = try await request.perform(on: image)
// barcodeObservations 包含检测到的每个条码
- 第一行:创建请求,不再需要 VN 前缀
- 第二行:用
try await执行请求,结果直接返回,无需 completion handler - 第三行:处理观察结果,每个 observation 代表一个检测到的条码
如果需要指定条码类型以提高性能(比如只扫描 EAN-13):
// ([06:38](https://developer.apple.com/videos/play/wwdc2024/10163/?time=398))
request.symbologies = [.ean13]
坐标转换
Vision 的坐标系是归一化的(0 到 1),原点在左下角。而 SwiftUI 的原点在左上角。新 API 提供了 toImageCoordinates() 方法来转换:
// ([05:42](https://developer.apple.com/videos/play/wwdc2024/10163/?time=342))
let boundingBox = observation.boundingBox
let convertedBox = boundingBox.toImageCoordinates(
imageSize: image.size,
origin: .upperLeft
)
boundingBox:Vision 返回的归一化坐标框imageSize:原始图像的尺寸origin:指定输出坐标的原点位置,upperLeft适配 SwiftUI 等框架
批量请求:一起执行
如果需要同时执行多个请求(比如既扫条码又识别文字),推荐用 ImageRequestHandler:
// ([07:36](https://developer.apple.com/videos/play/wwdc2024/10163/?time=456))
let handler = ImageRequestHandler(image: image)
let (barcodeResults, textResults) = try await handler.perform(
DetectBarcodesRequest(),
RecognizeTextRequest()
)
ImageRequestHandler:作为图像的容器,可以批量执行请求perform:使用 parameter pack 语法,可传入任意数量的请求- 返回值:每个请求对应一个结果,用 tuple 解包获取
批量请求:流式处理
上述方法需要等待所有请求完成。如果希望每个请求完成就立即处理,用 performAll:
// ([08:44](https://developer.apple.com/videos/play/wwdc2024/10163/?time=524))
let handler = ImageRequestHandler(image: image)
for await result in try await handler.performAll(
DetectBarcodesRequest(),
RecognizeTextRequest()
) {
switch result {
case .barcode(let observations):
// 条码检测完成,立即处理
handleBarcodes(observations)
case .text(let observations):
// 文字识别完成,立即处理
handleText(observations)
}
}
performAll:返回 AsyncStream,每个请求完成就推送结果for await: Swift Concurrency 的标准流处理语法switch:根据结果类型分发处理逻辑
并发优化:处理多张图像
当需要处理大量图像时,用 TaskGroup 并行执行,但要注意限制并发数以控制内存:
// ([10:18](https://developer.apple.com/videos/play/wwdc2024/10163/?time=618))
await withTaskGroup(of: Void.self) { group in
var imageIterator = images.makeIterator()
var activeTasks = 0
let maxConcurrency = 5
while activeTasks < maxConcurrency, let nextImage = imageIterator.next() {
group.addTask {
await generateThumbnail(from: nextImage)
activeTasks -= 1
}
activeTasks += 1
}
}
withTaskGroup:Swift Concurrency 的并发任务组maxConcurrency:限制同时执行的任务数,Vision 建议限制在 5 个左右以减少内存占用addTask:添加并发任务,每个任务处理一张图像
迁移旧代码
三步迁移法:
// 旧 API
// ([12:32](https://developer.apple.com/videos/play/wwdc2024/10163/?time=752))
let handler = VNImageRequestHandler(image: image)
let request = VNDetectBarcodesRequest()
request completionHandler = { request, error in
guard let observations = request.results else { return }
// 处理结果
}
try handler.perform([request])
// 新 API
let request = DetectBarcodesRequest()
let observations = try await request.perform(on: image)
// 处理结果
- 步骤 1:去掉 VN 前缀(
VNDetectBarcodesRequest→DetectBarcodesRequest) - 步骤 2:去掉 completion handler,用
async/await替代 - 步骤 3:去掉
VNImageRequestHandler,直接用request.perform(on:)
图像美学评分
新 API 可以评估图像质量:
// ([14:54](https://developer.apple.com/videos/play/wwdc2024/10163/?time=894))
let request = CalculateImageAestheticsScoresRequest()
let observation = try await request.perform(on: image)
print(observation.overallScore) // -1 到 1,越高越好
print(observation.isUtility) // true 表示工具类图像(截图、收据等)
overallScore:综合质量评分,范围 -1 到 1isUtility:是否为工具类图像(技术上没问题但不值得分享)
Holistic Body Pose
同时检测身体和手部姿态:
// ([15:38](https://developer.apple.com/videos/play/wwdc2024/10163/?time=938))
let request = DetectHumanBodyPoseRequest()
request.detectsHands = true
let observation = try await request.perform(on: image)
print(observation.leftHandObservation) // 左手姿态
print(observation.rightHandObservation) // 右手姿态
detectsHands:设置为 true 后,返回的 observation 会包含手部数据leftHandObservation/rightHandObservation:新增属性,分别对应左右手的姿态信息
核心启发
-
1. 迁移到新 API:新项目直接用新 API,旧项目渐进迁移。先从最常用的请求开始(
DetectBarcodesRequest、RecognizeTextRequest),三步法去前缀、去 handler、加 async/await。好处是代码更少、类型安全、支持 Swift 6 严格并发检查。 -
2. 用
toImageCoordinates()替代手动计算:Vision 的归一化坐标系(左下原点)常与 SwiftUI(左上原点)或其他框架不一致。用新 API 的坐标转换方法消除手动计算的错误。指定origin: .upperLeft即可适配大部分 UI 框架。 -
3. 用
performAll处理多请求场景:如果需要同时执行多个 Vision 请求(比如既扫条码又识别文字),用performAll拿 AsyncStream。每个请求完成就立即处理,不必等待所有请求。这对用户体验更友好,比如扫到条码就立即显示结果,不等文字识别完成。 -
4. 限制并发数控制内存:处理大量图像时,用 TaskGroup 并行执行,但限制并发数(建议 5 个左右)。Vision 请求可能占用较多内存,限制并发可防止内存峰值过高导致系统压力。
-
5. 利用图像美学评分优化相册:用
CalculateImageAestheticsScoresRequest评估用户照片库,自动过滤低质量照片(模糊、曝光问题),或识别工具类图像(截图、收据)。可以做”精选集”功能,只展示 memorable 的照片。
关联 Session
- Bring your machine learning and AI models to Apple silicon — 了解如何优化机器学习模型以利用 Apple silicon 的算力
- Deploy machine learning and AI models on-device with Core ML — 学习在设备上部署 ML 模型时优化速度和内存性能的新方法
- Build a great Lock Screen camera capture experience — 探索如何通过 Lock Screen Camera Capture API 在锁屏提供相机捕获功能
- What’s new in Create ML — 探索 Create ML 更新,包括 visionOS 对象追踪模型模板
评论
GitHub Issues · utterances