vision-framework

vision-framework

熱門

在 iOS App 中實作電腦視覺功能,包括文字辨識(OCR)、人臉偵測、條碼掃描、影像分割、物體追蹤與文件掃描。涵蓋現代 Swift 原生 Vision API(iOS 18+)與傳統 VNRequest 模式、VisionKit 的 DataScannerViewController 即時相機掃描,以及 CoreMLRequest/VNCoreMLRequest 自訂模型推論。適用於加入 OCR、條碼掃描、人臉偵測或自訂 Core ML 模型搭配 Vision 推論的情境。

932星標
47分支
更新於 2026/7/15
SKILL.md
readonlyread-only
name
vision-framework
description

在 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 世代

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)
}

條碼偵測

偵測一維與二維條碼,包含 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 沒有 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+ 公開的 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
  • [ ] 錯誤處理涵蓋請求失敗與空結果

參考資料