ScanPipeline.swift
55 lignes · 2068 octets
import UIKit /// Image provenance. VisionKit already returns perspective-corrected pages; /// picker images still need the `VisionScanEngine` pass. enum ScanSource { case documentCamera case photoLibrary } /// Orchestrates the Android `ScanCarteViewModel.processCapture` chain: /// (scan for picker images) -> orientation normalize -> OCR -> heuristic structuring. struct ScanPipeline { /// iOS counterpart of Android `ContactDraftUi`. struct Output { let ocrText: String let draft: ContactCard /// Corrected image for the draft preview (Android `ContactDraftUi.preview`). let preview: UIImage } let scanEngine: ScanEngine let ocrEngine: OcrEngine init(scanEngine: ScanEngine = VisionScanEngine(), ocrEngine: OcrEngine = VisionOcrEngine()) { self.scanEngine = scanEngine self.ocrEngine = ocrEngine } func process(_ image: UIImage, source: ScanSource) async throws -> Output { // Orientation first: UIImages carry orientation metadata that must be baked // into pixels before rectangle detection / warping (Android normalizes at // capture-decode time, before ScanCarteViewModel.processCapture runs). let oriented = ImageOrientation.orient(image).bitmap let corrected: UIImage switch source { case .documentCamera: // VisionKit already cropped + perspective-corrected the page. corrected = oriented case .photoLibrary: corrected = try await scanEngine.scan(oriented).bitmap } // VisionOcrEngine runs OcrPostProcessor on the raw text internally, // matching the TesseractOcrEngine contract. let ocr = try await ocrEngine.recognize(corrected) // Android calls ContactDraftMerge.merge(null, ocr); with a nil LLM card // that reduces to the deterministic heuristics over the OCR result. let draft = ContactHeuristicParser.parse(ocr) return Output(ocrText: ocr.rawText, draft: draft, preview: corrected) } }
GitRust