使用SwiftUI展示VNRecognizedObjectObservation边界框时偏移错误
Vision框架物体检测边界框在SwiftUI中位置偏移问题
我使用Vision框架进行物体检测,获取到[VNRecognizedObjectObservation]后,会对归一化矩形做转换再展示:
let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -CGFloat(height)) VNImageRectForNormalizedRect(normalizedRect, width, height) // 因使用SwiftUI展示,应用该变换 .applying(transform)
宽度和高度取自SwiftUI的GeometryReader:
Image(...) .resizable() .scaledToFit() .overlay { GeometryReader { geometry in // 后续通过ZStack和ForEach遍历[VNRecognizedObjectObservation],id取\.uuid,代码如下: let calculatedRect = calculateRect(boundingBox, geometry) Rectangle() .frame(width: calculatedRect.width, height: calculatedRect.height) .offset(x: calculatedRect.origin.x, y: calculatedRect.origin.y) } }
但目前存在问题:即使是正方形图片,多数边界框位置都不正确(仅少数准确)。该问题与模型无关——使用同一个MLModel处理同一张图片,在Xcode模型预览中边界框定位非常精准。
应用内展示效果:
Xcode模型预览效果:
更新:最小可复现代码
在macOS SwiftUI项目的ContentView.swift中使用以下代码,并将YOLOv3Tiny.mlmodel添加至项目包,即可复现相同问题:
import SwiftUI import Vision import CoreML class Detection: ObservableObject { let imgURL = URL(string: "https://i.imgur.com/EqsxxTc.jpg")! // Xcode预览生成的对应图片:https://i.imgur.com/6IPNQ8b.png @Published var objects: [VNRecognizedObjectObservation] = [] func getModel() -> VNCoreMLModel? { if let modelURL = Bundle.main.url(forResource: "YOLOv3Tiny", withExtension: "mlmodelc") { if let mlModel = try? MLModel(contentsOf: modelURL, configuration: MLModelConfiguration()) { return try? VNCoreMLModel(for: mlModel) } } return nil } func detect() async { guard let model = getModel(), let tiff = NSImage(contentsOf: imgURL)?.tiffRepresentation else { fatalError("Either YOLOv3Tiny.mlmodel is not in project bundle, or image failed to load.") // YOLOv3Tiny模型获取地址:https://ml-assets.apple.com/coreml/models/Image/ObjectDetection/YOLOv3Tiny/YOLOv3Tiny.mlmodel } let request = VNCoreMLRequest(model: model) { (request, error) in DispatchQueue.main.async { self.objects = (request.results as? [VNRecognizedObjectObservation]) ?? [] } } try? VNImageRequestHandler(data: tiff).perform([request]) } func deNormalize(_ rect: CGRect, _ geometry: GeometryProxy) -> CGRect { let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -CGFloat(geometry.size.height)) return VNImageRectForNormalizedRect(rect, Int(geometry.size.width), Int(geometry.size.height)).applying(transform) } } struct ContentView: View { @StateObject var detection = Detection() var body: some View { AsyncImage(url: detection.imgURL) { img in img.resizable().scaledToFit().overlay { GeometryReader { geometry in ZStack { ForEach(detection.objects, id: \.uuid) { object in let rect = detection.deNormalize(object.boundingBox, geometry) Rectangle() .stroke(lineWidth: 2) .foregroundColor(.red) .frame(width: rect.width, height: rect.height) .offset(x: rect.origin.x, y: rect.origin.y) } } } } } placeholder: { ProgressView() } .onAppear { Task { await self.detection.detect() } } } }
编辑补充: 经进一步测试确认,Vision返回的位置数据完全正确,deNormalize()函数计算出的矩形位置和尺寸也无问题,因此问题根源必然在SwiftUI的展示逻辑中。
内容的提问来源于stack exchange,提问作者Swiftly
相关产品推荐
相关产品推荐

