Swift下基于摄像头的CoreML双模型VNCoreMLRequest执行问题
解决CoreML同时运行两个模型的问题
嘿,我之前也踩过这个坑!VNCoreMLRequest本身是为单个模型设计的,没法直接把两个模型塞进同一个请求里——这就是你用数组添加模型时报错的原因。不过别担心,有几种靠谱的方法能实现你要的“同时用摄像头输入跑两个模型”的需求,我给你拆解一下:
方案1:创建两个独立VNCoreMLRequest,用VNSequenceRequestHandler并行处理
这是最直接的方案,思路是给每个模型单独创建一个VNCoreMLRequest,然后把这两个请求一起交给VNSequenceRequestHandler执行,它会自动并行处理两个模型的推理。
代码示例
// 提前初始化两个CoreML模型对应的VNCoreMLModel guard let objectDetectionModel = try? VNCoreMLModel(for: ObjectDetector().model), let imageClassificationModel = try? VNCoreMLModel(for: ImageClassifier().model) else { fatalError("加载模型失败,请检查模型文件") } // 为第一个模型创建请求(比如目标检测) let detectionRequest = VNCoreMLRequest(model: objectDetectionModel) { request, error in guard let results = request.results as? [VNRecognizedObjectObservation], error == nil else { print("目标检测出错:\(error?.localizedDescription ?? "未知错误")") return } // 处理目标检测结果,注意:回调在后台线程,更新UI要切回主线程 DispatchQueue.main.async { // 这里写更新UI或处理结果的逻辑 } } // 为第二个模型创建请求(比如图像分类) let classificationRequest = VNCoreMLRequest(model: imageClassificationModel) { request, error in guard let results = request.results as? [VNClassificationObservation], error == nil else { print("图像分类出错:\(error?.localizedDescription ?? "未知错误")") return } // 处理分类结果 DispatchQueue.main.async { // 这里写更新UI或处理结果的逻辑 } } // 在摄像头捕捉回调中执行两个请求 func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } let handler = VNSequenceRequestHandler() do { // 把两个请求放进数组,交给handler并行处理 try handler.perform([detectionRequest, classificationRequest], on: pixelBuffer) } catch { print("执行请求出错:\(error)") } }
这个方案的好处是不需要修改模型,每个模型的逻辑完全独立,调试起来也方便。
方案2:用CoreML Tools把两个模型打包成一个组合模型
如果你的两个模型需要共享输入或者要把结果统一处理,可以用CoreML Tools把它们合并成一个“组合模型”,这样就能用单个VNCoreMLRequest来跑了。
代码示例(Python端打包模型)
import coremltools as ct # 加载两个本地的CoreML模型 model1 = ct.models.MLModel("ObjectDetector.mlmodel") model2 = ct.models.MLModel("ImageClassifier.mlmodel") # 创建并行组合模型:同一个输入图像,同时跑两个模型,输出各自的结果 combined_model = ct.models.combine.Parallel( model1, model2, input_names=["image"], # 两个模型的输入都叫"image",要和原模型一致 output_names=["detection_results", "classification_results"] ) # 保存组合后的模型 combined_model.save("CombinedModel.mlmodel")
然后在iOS项目里导入这个CombinedModel.mlmodel,创建单个VNCoreMLRequest就能拿到两个模型的结果了:
guard let combinedModel = try? VNCoreMLModel(for: CombinedModel().model) else { fatalError("加载组合模型失败") } let combinedRequest = VNCoreMLRequest(model: combinedModel) { request, error in guard let results = request.results as? [VNMLFeatureValueObservation], error == nil else { print("组合模型推理出错:\(error?.localizedDescription ?? "未知错误")") return } // 分别提取两个模型的结果 if let detectionResult = results.first(where: { $0.featureName == "detection_results" })?.featureValue.multiArrayValue { // 处理目标检测结果 } if let classificationResult = results.first(where: { $0.featureName == "classification_results" })?.featureValue.multiArrayValue { // 处理分类结果 } }
这个方案适合需要把两个模型的结果紧密结合的场景,缺点是需要额外的Python步骤来打包模型。
注意事项
- 不管用哪种方案,都要确保模型的输入尺寸和摄像头输出的图像尺寸匹配,否则会出现推理错误。
- CoreML的请求回调是在后台线程执行的,如果要更新UI,一定要用
DispatchQueue.main.async切回主线程。 - 如果两个模型的推理耗时都比较长,并行处理可能会增加CPU/GPU负载,记得测试设备的性能表现。
内容的提问来源于stack exchange,提问作者jKraut
相关产品推荐
相关产品推荐

