【问题标题】:Why are there negative coordinate in the normalised object detection results? (CoreML,Vision,Swift, Ios)为什么归一化对象检测结果中存在负坐标? (CoreML、Vision、Swift、Ios)
【发布时间】:2019-09-21 01:18:31
【问题描述】:

我编译了这个例子。

https://developer.apple.com/documentation/vision/recognizing_objects_in_live_capture

我在 iPhone 7 Plus 上无法正常工作。绘制的矩形未覆盖检测到的项目。

我创建了一个自己的应用来进行调查。检测到的对象作为标准化边界返回。但是,边界在 Y 方向上可以为负。添加 0.2 的修正会使它们重新对齐。

检测似乎是从纵向框架的中心裁剪一个正方形来进行检测。我创建了一个正方形叠加层,当对象移出正方形到顶部或底部时,检测停止。正方形的顶部和底部在归一化坐标中分别为 0 和 1.0。

测试应用将数据从captureOutput 传递到VNImageRequestHandler。设置请求的代码也在下面。知道为什么观察结果有时在 Y 方向上是负的吗?为什么我需要添加偏移量才能将它们带回单位正方形并与图像对齐?

我已在我的测试应用中将相机设置为 4K。尚未尝试任何其他设置。

    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
            return
        }

        //let exifOrientation = exifOrientationFromDeviceOrientation()
        let exifOrientation = CGImagePropertyOrientation.up
        let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: exifOrientation, options: [:])
        do {
            try imageRequestHandler.perform(self.requests)
        } catch {
            print(error)
        }
    }
@discardableResult
func setupVision() -> NSError? {
    // Setup Vision parts
    let error: NSError! = nil

    guard let modelURL = Bundle.main.url(forResource: "ResistorModel", withExtension: "mlmodelc") else {
        return NSError(domain: "VisionObjectRecognitionViewController", code: -1, userInfo: [NSLocalizedDescriptionKey: "Model file is missing"])
    }
    do {
        let visionModel = try VNCoreMLModel(for: MLModel(contentsOf: modelURL))
        let objectRecognition = VNCoreMLRequest(model: visionModel, completionHandler: { (request, error) in
            DispatchQueue.main.async(execute: {
                // perform all the UI updates on the main queue
                if let results = request.results {
                    self.drawVisionRequestResults(results)
                }
            })
        })
        self.requests = [objectRecognition]
    } catch let error as NSError {
        print("Model loading went wrong: \(error)")
    }

    return error
}


    func drawVisionRequestResults(_ results: [Any]) {
        var pipCreated = false
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        detectionOverlay.sublayers = nil // remove all the old recognized objects
        for observation in results where observation is VNRecognizedObjectObservation {
            guard let objectObservation = observation as? VNRecognizedObjectObservation else {
                continue
            }
            // Select only the label with the highest confidence.
            let topLabelObservation = objectObservation.labels[0]
            if topLabelObservation.identifier == "resistor" {
                if (objectObservation.boundingBox.minX < 0.5) && (objectObservation.boundingBox.maxX > 0.5) && (objectObservation.boundingBox.minY < 0.3) && (objectObservation.boundingBox.maxY > 0.3) {
                    //print(objectObservation.boundingBox.minX)
                    //print(objectObservation.boundingBox.minY)

                    let bb = CGRect(x: objectObservation.boundingBox.minX, y:0.8 -  objectObservation.boundingBox.maxY, width: objectObservation.boundingBox.width, height: objectObservation.boundingBox.height)
                    //let bb = CGRect(x: 0.5,y: 0.5,width: 0.5,height: 0.5)
                        //let objectBounds = VNImageRectForNormalizedRect(bb, 500, 500)
                    let objectBounds = VNImageRectForNormalizedRect(bb, Int(detectionOverlay.bounds.width), Int(detectionOverlay.bounds.width))

//                    print(objectBounds)
//                    print(objectBounds.minX)
//                    print(objectBounds.minY)
//                    print(objectBounds.width)
//                    print(objectBounds.height)

                    print(objectObservation.boundingBox)
//                    print(objectBounds.minX)
//                    print(objectBounds.minY)
//                    print(objectBounds.width)
//                    print(objectBounds.height)

                    let textLayer = self.createTextSubLayerInBounds(objectBounds,
                                                                    identifier: topLabelObservation.identifier,
                                                                    confidence: topLabelObservation.confidence)

                    let shapeLayer = self.createRoundedRectLayerWithBounds(objectBounds)

                    shapeLayer.addSublayer(textLayer)
                    detectionOverlay.addSublayer(shapeLayer)

                    if !pipCreated {
                        pipCreated = true
                        let pip = Pip(imageBuffer: self.imageBuffer!)
                        if self.pip {
                            pipView.image = pip?.uiImage
                        } else {
                            pipView.image = nil
                        }
                    }
                }
            }
        }
        CATransaction.commit()
        doingStuff = false
    }

【问题讨论】:

  • 您是否使用 turicreate 创建了自己的模型?你能显示drawVisionRequests 代码吗?您是否使用 python 中的 coremltools 使用相同的图像测试了您的代码,以查看它是 Vision 还是返回负 y 坐标的模型?您也可以尝试仅使用 CoreML 而不是 Vision 包装器来测试最后一个想法。
  • 我使用的模型是用 turiCreate 创建的。一个还带有原始样品。我必须承认我没有测试过原始样品的故障是否是完全相同的问题。但是,开箱即用,它对我不起作用。作为应用程序开发的一部分,我将添加代码来拍摄图像。我会让你知道我是怎么过的。我还可以将模型加载到 MacOS 应用程序“RectLabel”中,它似乎排列正确。
  • 这可能是因为模型在原始输入图像的部分裁剪上工作,因此结果与该裁剪相关。您需要自己计算以将其转换回屏幕坐标(因为这完全取决于您的应用程序如何做事)。至于预测边界框上的负 x/y 坐标,这可能会发生。这只是意味着模型认为检测到的对象的中心不在屏幕上。
  • @MatthijsHollemans 我正在添加一个任意常数值来纠正负面影响。我尝试了不同高度的物体。偏移接缝保持不变。不是因为角落/中心坐标之间的混淆。我添加 0.2 没有明显的原因。被检测到的实际对象来自中心方形裁剪。我知道这是因为未检测到该区域之外的对象。横跨作物的物体仅与作物内部的钻头一样高。所以它没有检测到中心作物区域之外。我得到可以为负的偏移归一化值。 0.2 是从哪里来的?
  • 0.2 可能是因为您在屏幕上显示结果的方式使用了与模型正在处理的图像不同的坐标系和/或纵横比。另请注意,来自相机的图像具有自己的坐标系和纵横比。您需要在这三样东西(相机图像、Core ML 图像、显示图像)之间正确转换。

标签: ios swift coreml capture-output


【解决方案1】:

我不确定它为什么会这样。但是,我希望它使用整个图像来进行对象检测,并将结果绑定框归一化为原始肖像输入。另请注意,模型是以这种方式训练的。

有一个线程 https://github.com/apple/turicreate/issues/1016 涵盖了这个确切的问题。该示例不起作用,并且在您更改模型时也不起作用。

解决方案,在帖子的末尾,说要使用...

objectRecognition.imageCropAndScaleOption = .scaleFill

这使得检测使用了整个图像,并生成了标准化为整个图像的边界框。没有更多的任意偏移。可能训练几何和检测几何必须相同才能正确计算边界框。但是我不知道为什么。

【讨论】:

    猜你喜欢
    • 2022-09-23
    • 1970-01-01
    • 2018-08-13
    • 2020-12-20
    • 2020-03-10
    • 2019-03-15
    • 2020-01-18
    • 1970-01-01
    • 2018-05-01
    相关资源
    最近更新 更多