vision-framework

vision-framework

热门

在 iOS 应用中实现计算机视觉功能,包括文本识别(OCR)、人脸检测、条码扫描、图像分割、对象跟踪和文档扫描。涵盖现代 Swift 原生 Vision API(iOS 18+)和传统 VNRequest 模式、VisionKit 的 DataScannerViewController 用于实时摄像头扫描,以及 CoreMLRequest/VNCoreMLRequest 用于自定义模型推理。适用于添加 OCR、条码扫描、人脸检测或自定义 Core ML 模型推理与 Vision 结合的场景。

932Star
47Fork
更新于 2026/7/15
SKILL.md
readonly只读
name
vision-framework
description

在 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

Vision 有两个不同的 API 层。新代码优先使用现代 API:Swift 原生请求类型加上 try await request.perform(on:)。将 VN*VNImageRequestHandlerVNSequenceRequestHandler、完成处理器和传统 CGRect 辅助方法保留在显式的传统回退部分或文件中。

方面 现代(iOS 18+) 传统
模式 let result = try await request.perform(on: image) VNImageRequestHandler + 完成处理器
请求类型 Swift 类型——结构体和类(RecognizeTextRequestDetectFaceRectanglesRequest ObjC 类(VNRecognizeTextRequestVNDetectFaceRectanglesRequest
并发 原生 async/await 完成处理器或同步 perform
观测结果 类型化返回值 [Any] 强制转换 results
可用性 iOS 18+ / macOS 15+ iOS 11+

现代 API 使用 ImageProcessingRequest 协议。每种请求类型都有 perform(on:orientation:) 方法,接受 CGImageCIImageCVPixelBufferCMSampleBufferDataURL。大多数请求是结构体;有状态的请求如 GeneratePersonSegmentationRequestTrackObjectRequestTrackRectangleRequestDetectTrajectoriesRequest 是最终类。

请求模式(现代 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 配合 VNImageRequestHandlerVNSequenceRequestHandler。加载 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 没有 trackingLevelqualityLevel

传统: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 使用 CoreMLRequestVNCoreMLRequest 运行已准备好的模型;将转换、性能分析、打包和生命周期决策交给 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 跨视频帧保留
  • [ ] 错误处理涵盖请求失败和空结果

参考