import Vision import CoreML import CoreGraphics // MARK: - FOIL ENGINE actor FoilEngine { private var frameCounter = 0 private var lowConfidenceStreak = 0 static var model: VNCoreMLModel? = { guard AppConfig.enableFoilDetection else { return nil } return ModelManager.shared.getModel(name: "IYmtgFoilClassifier") }() private lazy var request: VNCoreMLRequest? = { guard let model = FoilEngine.model else { return nil } let req = VNCoreMLRequest(model: model) req.imageCropAndScaleOption = .scaleFill return req }() func addFrame(_ image: CGImage, orientation: CGImagePropertyOrientation = .up) -> String? { frameCounter += 1 // SAFETY: Prevent integer overflow in long-running sessions if frameCounter > 1000 { frameCounter = 0 } if frameCounter % 5 != 0 { return nil } guard let request = self.request else { return AppConfig.Defaults.defaultFoil } let handler = VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:]) do { try handler.perform([request]) guard let results = request.results as? [VNClassificationObservation], let top = results.first else { return AppConfig.Defaults.defaultFoil } if top.confidence > 0.85 { lowConfidenceStreak = 0 return top.identifier } else { lowConfidenceStreak += 1 if lowConfidenceStreak >= 3 { return AppConfig.Defaults.defaultFoil } else { return nil } } } catch { return AppConfig.Defaults.defaultFoil } } }