
vision-framework
热门在 iOS 应用中实现计算机视觉功能,包括文本识别(OCR)、人脸检测、条码扫描、图像分割、对象跟踪和文档扫描。涵盖现代 Swift 原生 Vision API(iOS 18+)和传统 VNRequest 模式、VisionKit 的 DataScannerViewController 用于实时摄像头扫描,以及 CoreMLRequest/VNCoreMLRequest 用于自定义模型推理。适用于添加 OCR、条码扫描、人脸检测或自定义 Core ML 模型推理与 Vision 结合的场景。
在 iOS 应用中实现计算机视觉功能,包括文本识别(OCR)、人脸检测、条码扫描、图像分割、对象跟踪和文档扫描。涵盖现代 Swift 原生 Vision API(iOS 18+)和传统 VNRequest 模式、VisionKit 的 DataScannerViewController 用于实时摄像头扫描,以及 CoreMLRequest/VNCoreMLRequest 用于自定义模型推理。适用于添加 OCR、条码扫描、人脸检测或自定义 Core ML 模型推理与 Vision 结合的场景。
Vision 框架
在图像和视频中检测文本、人脸、条码、对象和身体姿态,使用设备端计算机视觉。优先使用现代 iOS 18+ 请求 API,仅在部署目标需要时加载传统参考。
参见 references/vision-requests.md 获取完整代码模式,以及 references/visionkit-scanner.md 了解 DataScannerViewController 集成。
目录
- 两代 API
- 请求模式(现代 API)
- 文本识别(OCR)
- 人脸检测
- 条码检测
- 文档扫描(iOS 26+)
- 图像分割
- 对象跟踪
- 其他请求类型
- Core ML 集成
- VisionKit: DataScannerViewController
- 常见错误
- 审查清单
- 参考
两代 API
Vision 有两个不同的 API 层。新代码优先使用现代 API:Swift 原生请求类型加上 try await request.perform(on:)。将 VN*、VNImageRequestHandler、VNSequenceRequestHandler、完成处理器和传统 CGRect 辅助方法保留在显式的传统回退部分或文件中。
| 方面 | 现代(iOS 18+) | 传统 |
|---|---|---|
| 模式 | let result = try await request.perform(on: image) |
VNImageRequestHandler + 完成处理器 |
| 请求类型 | Swift 类型——结构体和类(RecognizeTextRequest、DetectFaceRectanglesRequest) |
ObjC 类(VNRecognizeTextRequest、VNDetectFaceRectanglesRequest) |
| 并发 | 原生 async/await | 完成处理器或同步 perform |
| 观测结果 | 类型化返回值 | 从 [Any] 强制转换 results |
| 可用性 | iOS 18+ / macOS 15+ | iOS 11+ |
现代 API 使用 ImageProcessingRequest 协议。每种请求类型都有 perform(on:orientation:) 方法,接受 CGImage、CIImage、CVPixelBuffer、CMSampleBuffer、Data 或 URL。大多数请求是结构体;有状态的请求如 GeneratePersonSegmentationRequest、TrackObjectRequest、TrackRectangleRequest 和 DetectTrajectoriesRequest 是最终类。
请求模式(现代 API)
所有现代 Vision 请求遵循相同模式:创建请求,调用 perform(on:),处理类型化结果。
import Vision
func recognizeText(in image: CGImage) async throws -> [String] {
var request = RecognizeTextRequest()
request.recognitionLevel = .accurate
request.recognitionLanguages = [Locale.Language(identifier: "en-US")]
let observations = try await request.perform(on: image)
return observations.compactMap { observation in
observation.topCandidates(1).first?.string
}
}
传统模式(iOS 18 之前)
对于 iOS 18 之前的目标,使用对应的 VNRequest 配合 VNImageRequestHandler 或 VNSequenceRequestHandler。加载 references/vision-requests.md 获取完整的传统请求和处理模式。
文本识别(OCR)
现代:RecognizeTextRequest(iOS 18+)
var request = RecognizeTextRequest()
request.recognitionLevel = .accurate // 实时使用 .fast
request.recognitionLanguages = [
Locale.Language(identifier: "en-US"),
Locale.Language(identifier: "fr-FR"),
]
request.usesLanguageCorrection = true
request.customWords = ["SwiftUI", "Xcode"] // 领域特定术语
let observations = try await request.perform(on: cgImage)
for observation in observations {
guard let candidate = observation.topCandidates(1).first else { continue }
let text = candidate.string
let confidence = candidate.confidence // 0.0 ... 1.0
let bounds = observation.boundingBox // NormalizedRect
}
传统:VNRecognizeTextRequest
传统请求使用字符串语言标识符和参考中的处理模式;两代都支持精确和快速识别级别。
人脸检测
检测人脸矩形、特征点(眼睛、鼻子、嘴巴)和拍摄质量。
// 现代 API
let faceRequest = DetectFaceRectanglesRequest()
let faces = try await faceRequest.perform(on: cgImage)
for face in faces {
let boundingBox = face.boundingBox // NormalizedRect
let roll = face.roll // Measurement<UnitAngle>
let yaw = face.yaw // Measurement<UnitAngle>
}
// 特征点(眼睛、鼻子、嘴巴轮廓)
var landmarkRequest = DetectFaceLandmarksRequest()
let landmarkFaces = try await landmarkRequest.perform(on: cgImage)
for face in landmarkFaces {
let landmarks = face.landmarks
let leftEye = landmarks?.leftEye.points
let nose = landmarks?.nose.points
}
坐标系
Vision 使用归一化坐标系,原点在左下角。显示前转换为 UIKit(原点在左上角):
import Vision
func imageRectForDisplay(_ rect: NormalizedRect, imageSize: CGSize) -> CGRect {
rect.toImageCoordinates(imageSize, origin: .upperLeft)
}
条码检测
检测一维和二维条码,包括二维码。
var request = DetectBarcodesRequest()
let symbologies: [BarcodeSymbology] = [.qr, .ean13, .code128, .pdf417]
request.symbologies = symbologies
let barcodes = try await request.perform(on: cgImage)
for barcode in barcodes {
let payload = barcode.payloadString // 解码内容
let symbology = barcode.symbology // .qr, .ean13 等
let bounds = barcode.boundingBox // NormalizedRect
}
先对局部值进行类型标注,然后分别赋值请求属性。
文档扫描(iOS 26+)
RecognizeDocumentsRequest 提供结构化文档读取,具有超越基本 OCR 的布局理解。返回 DocumentObservation 对象,包含嵌套的 Container 结构,用于段落、表格、列表和条码。目前,Vision 对每张图像返回一个文档观测。
var request = RecognizeDocumentsRequest()
let documents = try await request.perform(on: cgImage)
for observation in documents {
let container = observation.document
// 全文内容
let fullText = container.text
// 结构化访问段落
for paragraph in container.paragraphs {
let paragraphText = paragraph.text
}
// 表格和列表
for table in container.tables { /* 结构化表格数据 */ }
for list in container.lists { /* 结构化列表数据 */ }
// 文档中检测到的嵌入条码
for barcode in container.barcodes { /* 条码数据 */ }
// 如果检测到文档标题
if let title = container.title { print(title) }
}
对于更简单的文档相机扫描,使用 VisionKit 的 VNDocumentCameraViewController,它提供全屏相机 UI,支持自动拍摄、透视校正和多页扫描。
图像分割
现代:GeneratePersonSegmentationRequest(iOS 18+)
var request = GeneratePersonSegmentationRequest()
request.qualityLevel = .accurate // .balanced, .fast
let mask = try await request.perform(on: cgImage)
// mask 是一个 PixelBufferObservation,包含 pixelBuffer 属性
let maskBuffer = mask.pixelBuffer
// 使用 Core Image 应用蒙版:CIFilter.blendWithMask()
传统:VNGeneratePersonSegmentationRequest
对于较旧的目标,VNGeneratePersonSegmentationRequest 通过第一个像素缓冲观测暴露其蒙版;使用参考中的处理程序和蒙版合成配方。
质量级别:
.accurate—— 最佳质量,最慢(约1秒),全分辨率.balanced—— 良好质量,中等速度(约100毫秒),960x540.fast—— 最低质量,最快(约10毫秒),256x144,适合实时
实例分割(iOS 18+)
为每个人生成单独的蒙版,实现个体效果。
// 现代 API(iOS 18+)
let request = GeneratePersonInstanceMaskRequest()
let observation = try await request.perform(on: cgImage)
let indices = observation.allInstances
for index in indices {
let mask = try observation.generateMask(for: IndexSet(integer: index))
// mask 是一个 CVPixelBuffer,仅此人物可见
}
// 传统 API(iOS 17+)
let request = VNGeneratePersonInstanceMaskRequest()
let handler = VNImageRequestHandler(cgImage: cgImage)
try handler.perform([request])
guard let result = request.results?.first else { return }
let indices = result.allInstances
for index in indices {
let instanceMask = try result.generateMaskedImage(
ofInstances: IndexSet(integer: index),
from: handler,
croppedToInstancesExtent: false
)
}
参见 references/vision-requests.md 了解蒙版合成和 Core Image 滤镜集成模式。
对象跟踪
现代:TrackObjectRequest(iOS 18+)
TrackObjectRequest 是一个有状态请求,跨帧维护跟踪上下文。
// 使用检测到的对象边界框初始化
let initialObservation = DetectedObjectObservation(boundingBox: detectedBox)
let request = TrackObjectRequest(detectedObject: initialObservation)
for pixelBuffer in framePixelBuffers {
let results = try await request.perform(on: pixelBuffer)
if let tracked = results.first {
let updatedBounds = tracked.boundingBox // NormalizedRect
}
}
现代 TrackObjectRequest 没有 trackingLevel 或 qualityLevel。
传统:VNTrackObjectRequest
对于较旧的目标,使用 VNTrackObjectRequest 配合一个保留的 VNSequenceRequestHandler,并将每个结果反馈为下一个输入观测。参考中包含完整循环。
其他请求类型
Vision 提供其他请求,详见 references/vision-requests.md:
| 请求 | 用途 |
|---|---|
ClassifyImageRequest |
分类场景内容(户外、食物、动物等) |
GenerateAttentionBasedSaliencyImageRequest |
单个 SaliencyImageObservation,表示观众注意力集中的区域 |
GenerateObjectnessBasedSaliencyImageRequest |
单个 SaliencyImageObservation,表示类似对象的区域 |
GenerateForegroundInstanceMaskRequest |
前景对象分割(非特定人物) |
DetectRectanglesRequest |
检测矩形形状(文档、卡片、屏幕) |
DetectHorizonRequest |
检测水平角度,用于自动调平照片 |
DetectHumanBodyPoseRequest |
检测身体关节点(肩膀、肘部、膝盖) |
DetectHumanBodyPose3DRequest |
3D 人体姿态估计 |
DetectHumanHandPoseRequest |
检测手部关节点和手指位置 |
DetectAnimalBodyPoseRequest |
检测动物身体关节点位置 |
DetectFaceCaptureQualityRequest |
人脸拍摄质量评分(0–1),用于照片选择 |
TrackRectangleRequest |
跨视频帧跟踪矩形对象 |
TrackOpticalFlowRequest |
视频帧之间的光流 |
DetectTrajectoriesRequest |
检测视频中的对象轨迹 |
以上所有现代请求类型均适用于 iOS 18+ / macOS 15+。
Core ML 集成
通过 Vision 运行自定义 Core ML 模型,实现自动图像预处理。
Vision 使用 CoreMLRequest 或 VNCoreMLRequest 运行已准备好的模型;将转换、性能分析、打包和生命周期决策交给 coreml。
import CoreML
import Vision
// 现代 API(iOS 18+):CoreMLRequest 接受 CoreMLModelContainer。
let model = try MLModel(contentsOf: modelURL)
let container = try CoreMLModelContainer(model: model, featureProvider: nil)
let request = CoreMLRequest(model: container)
let results = try await request.perform(on: cgImage)
// 分类模型
if let classification = results.first as? ClassificationObservation {
let label = classification.identifier
let confidence = classification.confidence
}
CoreMLModelContainer 是 iOS 18+ 中 CoreMLRequest 的公共 Vision 容器:加载 MLModel,使用 CoreMLModelContainer(model:featureProvider:) 包装,然后将该容器传递给 CoreMLRequest(model:)。在审查通过 Vision 使用 Core ML 时,说明结果映射:分类器产生 ClassificationObservation,图像输出产生 PixelBufferObservation,通用预测器产生 CoreMLFeatureValueObservation。
// 传统 API
let vnModel = try VNCoreMLModel(for: model)
let request = VNCoreMLRequest(model: vnModel) { request, error in
guard let results = request.results as? [VNClassificationObservation] else { return }
let topResult = results.first
}
let handler = VNImageRequestHandler(cgImage: cgImage)
try handler.perform([request])
VisionKit: DataScannerViewController
DataScannerViewController 提供实时摄像头扫描文本和条码;参见 references/visionkit-scanner.md。VisionKit 使用 VNBarcodeSymbology;现代 DetectBarcodesRequest 使用 BarcodeSymbology。
快速开始
import AVFoundation
import Vision
import VisionKit
@MainActor
func presentScanner() async {
// 在请求相机访问之前添加 NSCameraUsageDescription。
guard await AVCaptureDevice.requestAccess(for: .video) else { return }
guard DataScannerViewController.isSupported,
DataScannerViewController.isAvailable else { return }
let scannerSymbologies: [VNBarcodeSymbology] = [.qr, .ean13]
let scanner = DataScannerViewController(
recognizedDataTypes: [
.text(languages: ["en"]),
.barcode(symbologies: scannerSymbologies)
],
qualityLevel: .balanced,
recognizesMultipleItems: true,
isHighFrameRateTrackingEnabled: true,
isHighlightingEnabled: true
)
scanner.delegate = self
present(scanner, animated: true) {
// 在呈现后开始扫描,在主 actor 上。
try? scanner.startScanning()
}
}
SwiftUI 集成
将 DataScannerViewController 包装在 UIViewControllerRepresentable 中,并在 updateUIViewController 中使用 Task { @MainActor in try? controller.startScanning() } 启动;参见 references/visionkit-scanner.md。
常见错误
不要: 在新的 iOS 18+ 项目中使用传统的 VNImageRequestHandler API。
应该: 使用现代 Swift 原生请求,配合 perform(on:) 和 async/await。
原因: 现代 API 提供类型安全、更好的 Swift 并发支持和更清晰的错误处理。
不要: 在绘制边界框之前忘记转换归一化坐标。
应该: 对于现代观测使用 NormalizedRect.toImageCoordinates(_:origin:),对于传统 CGRect 观测使用 VNImageRectForNormalizedRect(_:_:_:)。
原因: Vision 使用归一化坐标(0...1),原点在左下角;UIKit 使用点,原点在左上角。
不要: 在主线程上运行 Vision 请求。
应该: 在后台线程上执行请求,或使用分离任务中的 async/await。
原因: 图像分析是 CPU/GPU 密集型任务,如果在主 actor 上运行会阻塞 UI。
不要: 对实时摄像头流使用 .accurate 识别级别。
应该: 对实时视频使用 .fast,对静态图像或离线处理使用 .accurate。
原因: 精确识别对于 30fps 视频太慢;快速识别以质量换取速度。
不要: 认为每个 Vision 观测都具有相同的属性。
应该: 在编写共享辅助方法之前,检查每个观测类型的边界框、置信度、有效载荷、蒙版或角度字段。
原因: 现代 Vision 返回强类型观测,结果形状因请求而异。
不要: 为每个视频帧重新创建有状态的跟踪请求。
应该: 保留相同的现代 TrackObjectRequest 实例,或对传统跟踪请求使用 VNSequenceRequestHandler。
原因: 跟踪依赖于跨帧的时间上下文。
不要: 在只需要二维码时请求所有条码符号。
应该: 在请求中仅指定需要的符号。
原因: 更少的符号意味着更快的检测和更少的误报。
不要: 假设 DataScannerViewController 在所有设备上都可用。
应该: 在呈现之前检查 isSupported(硬件)和 isAvailable(用户权限)。
原因: 需要 A12+ 芯片;isAvailable 还检查相机访问授权。
审查清单
- [ ] 使用现代 Vision API(iOS 18+),除非目标部署更旧版本
- [ ] Vision 请求在主线外运行(async/await 或后台队列)
- [ ] 在 UI 显示前转换归一化坐标
- [ ] 应用置信度阈值过滤低质量观测
- [ ] 识别级别匹配用例(视频用
.fast,静态图像用.accurate) - [ ] 当输入语言已知时,为文本识别设置语言提示
- [ ] 条码符号仅限于需要的类型
- [ ] 在呈现前检查
DataScannerViewController的可用性 - [ ] 在 Info.plist 中为 VisionKit 添加相机使用描述(
NSCameraUsageDescription) - [ ] 在呈现前请求 VisionKit 相机访问,并在呈现后开始扫描
- [ ] 人物分割质量级别适合用例
- [ ] 有状态的跟踪请求或
VNSequenceRequestHandler跨视频帧保留 - [ ] 错误处理涵盖请求失败和空结果
参考
- Vision 请求模式:references/vision-requests.md
- VisionKit 扫描器集成:references/visionkit-scanner.md
- Apple 文档:Vision |
VisionKit |
RecognizeTextRequest |
DataScannerViewController |
CoreMLRequest |
CoreMLModelContainer





