在 iOS App 中實作電腦視覺功能,包括文字辨識(OCR)、人臉偵測、條碼掃描、影像分割、物體追蹤與文件掃描。涵蓋現代 Swift 原生 Vision API(iOS 18+)與傳統 VNRequest 模式、VisionKit 的 DataScannerViewController 即時相機掃描,以及 CoreMLRequest/VNCoreMLRequest 自訂模型推論。適用於加入 OCR、條碼掃描、人臉偵測或自訂 Core ML 模型搭配 Vision 推論的情境。
Vision Framework
在圖片與影片中偵測文字、人臉、條碼、物體與人體姿勢,使用裝置端電腦視覺技術。優先採用現代 iOS 18+ 的請求 API,僅在部署目標需要時才載入傳統參考資料。
完整的程式碼模式請參閱 references/vision-requests.md,DataScannerViewController 整合請參閱 references/visionkit-scanner.md。
目錄
- 兩種 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)
}
條碼偵測
偵測一維與二維條碼,包含 QR Code。
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
)
}
遮罩合成與 Core Image 濾鏡整合模式請參閱 references/vision-requests.md。
物體追蹤
現代: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+ 公開的 Vision 容器,用於 CoreMLRequest:載入 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) {
// 在呈現後開始掃描,需在主執行緒上執行。
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 資源,若在主執行緒上執行會阻塞 UI。
不要: 對即時相機串流使用 .accurate 辨識等級。
要: 對即時影片使用 .fast,對靜態影像或離線處理使用 .accurate。
原因: 精確辨識對 30fps 影片來說太慢;快速辨識以品質換取速度。
不要: 假設每個 Vision 觀測結果都有相同的屬性。
要: 在撰寫共用輔助函式前,檢查每個觀測結果類型的邊界框、信心度、酬載、遮罩或角度欄位。
原因: 現代 Vision 回傳強型別觀測結果,且結果形狀因請求而異。
不要: 為每個影片影格重新建立有狀態的追蹤請求。
要: 保留同一個現代的 TrackObjectRequest 實例,或對傳統追蹤請求使用 VNSequenceRequestHandler。
原因: 追蹤依賴跨影格的時間上下文。
不要: 在只需要 QR Code 時請求所有條碼符號系統。
要: 僅在請求中指定所需的符號系統。
原因: 較少的符號系統意味著更快的偵測與更少的誤判。
不要: 假設 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






