【问题标题】:Convert VNRectangleObservation points to other coordinate system将 VNRectangleObservation 点转换为其他坐标系
【发布时间】:2018-06-04 08:25:32
【问题描述】:

我需要转换 VNRectangleObservation 收到的 CGPoints (bottomLeft, bottomRight, topLeft, topRight) 到另一个坐标系(例如屏幕上的视图坐标)。

我定义一个请求:

    // Rectangle Request
    let rectangleDetectionRequest = VNDetectRectanglesRequest(completionHandler: handleRectangles)
    rectangleDetectionRequest.minimumSize = 0.5
    rectangleDetectionRequest.maximumObservations = 1

我在委托调用中从摄像头获取 sampleBuffer,并执行检测请求:

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {

    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {return}
    var requestOptions:[VNImageOption:Any] = [:]
    if let cameraIntrinsicData = CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, nil) {
        requestOptions = [.cameraIntrinsics:cameraIntrinsicData]
    }
    let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: CGImagePropertyOrientation(rawValue:6)!, options: requestOptions)
    do {
        try imageRequestHandler.perform(self.requests)
    } catch {
        print(error)
    }

}

稍后在 completionHandler 中我收到结果:

func handleRectangles (request:VNRequest, error:Error?) {

     guard let results = request.results as? [VNRectangleObservation] else { return }

     let flipTransform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -self.previewView.frame.height)
     let scaleTransform = CGAffineTransform.identity.scaledBy(x: self.previewView.frame.width, y: self.previewView.frame.height)

     for rectangle in results {
        let rectangleBounds = rectangle.boundingBox.applying(scaleTransform).applying(flipTransform)
        // convertedTopLeft = conversion(rectangle.topLeft) 
        // convertedTopRight = conversion(rectangle.topRight) 
        // convertedBottomLeft = conversion(rectangle.bottomLeft) 
        // convertedBottomRight = conversion(rectangle.bottomRight) 
    }
}

这适用于 CGRect 的 boundingBox,但我需要将 CGPoints 转换为另一个视图的坐标系。 问题是我不知道怎么从sampleBuffer:CMSampleBuffer的坐标系到previewView坐标系的变换。

谢谢!

【问题讨论】:

    标签: ios swift computer-vision apple-vision


    【解决方案1】:

    这只是将变换应用于 CGPoint 本身的问题,其中 size 是我需要转置四个点的目标视图的 CGSize。

        let transform = CGAffineTransform.identity
            .scaledBy(x: 1, y: -1)
            .translatedBy(x: 0, y: -size.height)
            .scaledBy(x: size.width, y: size.height)
    
        let convertedTopLeft = rectangle.topLeft.applying(transform)
        let convertedTopRight = rectangle.topRight.applying(transform)
        let convertedBottomLeft = rectangle.bottomLeft.applying(transform)
        let convertedBottomRight = rectangle.bottomRight.applying(transform)
    

    【讨论】:

    • 这很完美,但是如何通过稍微修改它的角来再次删除这个变换呢?我需要再次将这些点传递给 CIPerspectiveCorrection。
    【解决方案2】:

    @mihaicris 回答有效,但仅限纵向模式。在横向上,我们需要做一些不同的事情。

    if UIApplication.shared.statusBarOrientation.isLandscape {
        transform = CGAffineTransform.identity
            .scaledBy(x: -1, y: 1)
            .translatedBy(x: -size.width, y: 0)
            .scaledBy(x: size.width, y: size.height)
    } else {
        transform = CGAffineTransform.identity
            .scaledBy(x: 1, y: -1)
            .translatedBy(x: 0, y: -size.height)
            .scaledBy(x: size.width, y: size.height)
    }
    
    let convertedTopLeft = rectangle.topLeft.applying(transform)
    let convertedTopRight = rectangle.topRight.applying(transform)
    let convertedBottomLeft = rectangle.bottomLeft.applying(transform)
    let convertedBottomRight = rectangle.bottomRight.applying(transform)
    

    【讨论】:

      【解决方案3】:

      我假设您使用图层作为相机,图层为AVCaptureVideoPreviewLayer。 (https://developer.apple.com/documentation/avfoundation/avcapturevideopreviewlayer)。 所以如果你想转换单点,使用这个函数:layerPointConvertedhttps://developer.apple.com/documentation/avfoundation/avcapturevideopreviewlayer/1623502-layerpointconverted)。请注意,由于VNRectangleObservation 坐标系,y 是倒置的。

      let convertedTopLeft: CGPoint = cameraLayer.layerPointConverted(fromCaptureDevicePoint: CGPoint(x: rectangle.topLeft.x, y: 1 - rectangle.topLeft.y))
      let convertedTopRight: CGPoint = cameraLayer.layerPointConverted(fromCaptureDevicePoint: CGPoint(x: rectangle.topRight.x, y: 1 - rectangle.topRight.y))
      let convertedBottomLeft: CGPoint = cameraLayer.layerPointConverted(fromCaptureDevicePoint: CGPoint(x: rectangle.bottomLeft.x, y: 1 - rectangle.bottomLeft.y))
      let convertedBottomRight: CGPoint = cameraLayer.layerPointConverted(fromCaptureDevicePoint: CGPoint(x: rectangle.bottomRight.x, y: 1 - rectangle.bottomRight.y))
      

      希望对你有帮助

      【讨论】:

      • 谢谢,我会在我的代码中检查你的答案,因为它可能是补偿预览层的方面填充重力点属性的更好解决方案。
      • 酷,如果有帮助请告诉我
      • 您好,我尝试了 layerPointConverted 方法,但它没有按预期工作。将输入点作为捕获设备坐标 (0,0 - 1,0 - 0,1 - 1,1) 的角点,转换后的点 x 坐标偏离目标视图框架。就像该方法没有考虑 previewView 的 aspectFill videogravity 属性(与屏幕尺寸完全一样,而不是更大)。我认为这个函数知道如何补偿偏移量..
      • @mihaicris 我遇到了同样的问题,x 坐标偏离了目标视图框架。但是,如果您查看 Apple 文档 (developer.apple.com/documentation/avfoundation/…),它会说“通过此方法执行的转换会考虑图层的帧大小和接收器的 videoGravity 属性。”我会调查这个问题,看看我是否能够弄清楚发生了什么
      猜你喜欢
      • 2011-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-23
      • 2012-09-06
      相关资源
      最近更新 更多