feat(iOS): 更新MNN后端模型配置优化性能

将MNN主模型从Qwen3.5-4B(~2.64GiB)降级为Qwen3.5-2B(~1.1GiB),因为4B版本
实测运行过慢,影响用户体验。iPhone17+/SME2设备使用2B模型,保留MLX
兜底方案用于模拟器和备用场景,确保AI推理性能和存储效率的平衡。
```
This commit is contained in:
link2026
2026-06-09 22:20:07 +08:00
parent ca5a3fa38b
commit b79ae54b7b
40 changed files with 1327 additions and 452 deletions

View File

@@ -2,7 +2,7 @@ import SwiftUI
import SwiftData
import UIKit
/// ·
/// ·
/// ()/ () OCR+LLM Indicator
///
/// :
@@ -15,8 +15,6 @@ struct QuickRegionCaptureFlow: View {
@Environment(\.modelContext) private var ctx
let onClose: () -> Void
@AppStorage(QuickRegionRecognitionEngine.storageKey)
private var recognitionEngineRaw = QuickRegionRecognitionEngine.defaultValue.rawValue
@State private var phase: Phase = .idle
enum Phase {
@@ -59,7 +57,7 @@ struct QuickRegionCaptureFlow: View {
onCancel: { onClose() },
onRetake: { phase = .idle }
)
.navigationTitle(String(appLoc: "核对异常项"))
.navigationTitle(String(appLoc: "核对指标"))
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
@@ -97,29 +95,18 @@ struct QuickRegionCaptureFlow: View {
}
}
// MARK: - ( OCR LLM)
// MARK: - ( Vision OCR Qwen3 )
/// /,( RegionAdjustView )
/// :
/// - Apple Vision:Vision OCR Qwen3-1.7B
/// - Qwen3-VL: Qwen3-VL
/// :Vision OCR Qwen3
/// (VL :,OCR)
private func recognizeRegion(_ image: UIImage) async -> (items: [QuickRegionItem], warning: String?) {
let engine = QuickRegionRecognitionEngine(storedValue: recognitionEngineRaw)
switch engine {
case .appleVision:
return await recognizeWithAppleVision(image)
case .qwenVL:
return await recognizeWithQwenVL(image)
}
}
private func recognizeWithAppleVision(_ image: UIImage) async -> (items: [QuickRegionItem], warning: String?) {
do {
let text = try await OCRService.recognizeText(in: image)
if Task.isCancelled { return ([], nil) } // :
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
#if DEBUG
print("🔤 [OCR · region] recognized text:\n\(trimmed)\n--- end OCR ---")
NSLog("KKDBG-OCR region text:\n%@\n--- end OCR ---", trimmed)
#endif
if trimmed.isEmpty {
return ([], String(appLoc: "没识别到文字,挪一下框再试"))
@@ -139,30 +126,6 @@ struct QuickRegionCaptureFlow: View {
}
}
private func recognizeWithQwenVL(_ image: UIImage) async -> (items: [QuickRegionItem], warning: String?) {
let prepared = RegionImageCropper.prepareForQwenVL(image)
guard let data = prepared.jpegData(compressionQuality: 0.95) else {
return ([], String(appLoc: "图片编码失败,手动补充"))
}
#if DEBUG
print("🖼️ [Qwen3-VL region] prepared image=\(Int(prepared.size.width))x\(Int(prepared.size.height)), bytes=\(data.count)")
#endif
do {
let parsed = try await CaptureService.shared.recognizeRegion(imageData: data)
if Task.isCancelled { return ([], nil) }
let items = Self.buildItems(from: parsed)
return (items, items.isEmpty ? String(appLoc: "没读出指标,挪一下框再试") : nil)
} catch CaptureError.modelNotReady {
return ([], String(appLoc: "模型未就绪,请在模型管理下载或切回 Apple Vision"))
} catch let CaptureError.parseFailed(msg) {
return ([], String(appLoc: "解析失败:\(msg)"))
} catch let CaptureError.inferenceFailed(msg) {
return ([], Task.isCancelled ? nil : String(appLoc: "识别失败:\(msg)"))
} catch {
return ([], Task.isCancelled ? nil : String(appLoc: "未知错误:\(error.localizedDescription)"))
}
}
/// LLM ,(high/low)
private static func buildItems(from parsed: [ParsedReport.ParsedIndicator]) -> [QuickRegionItem] {
let mapped = parsed.map {