将ARFrame捕获的图像转换为UIImage方向问题

9

我想检测球,并且让AR模型与其进行交互。我使用了opencv来检测球并发送球的中心点,我可以在hitTest中使用该坐标获取sceneView中的坐标。我一直在使用以下函数将CVPixelBuffer转换为UIImage

static func convertToUIImage(buffer: CVPixelBuffer) -> UIImage?{
    let ciImage = CIImage(cvPixelBuffer: buffer)
    let temporaryContext = CIContext(options: nil)
    if let temporaryImage = temporaryContext.createCGImage(ciImage, from: CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(buffer), height: CVPixelBufferGetHeight(buffer)))
    {
        let capturedImage = UIImage(cgImage: temporaryImage)
        return capturedImage
    }
    return nil
}

这是我得到的旋转图像:

在此输入图片描述

后来我发现可以使用以下方法更改方向:

let capturedImage = UIImage(cgImage: temporaryImage, scale: 1.0, orientation: .right)

虽然在设备竖屏时可以正确显示方向,但将设备旋转到横屏后,图像会被旋转。

现在我考虑使用 viewWillTransition 来处理它。但在此之前,我想知道:

  1. 是否有其他方法可以转换具有正确方向的图像?
  2. 为什么会发生这种情况?
1个回答

16

1. 是否有其他方法可以转换具有正确方向的图像?

您可以尝试使用ARSCNView(继承自SCNView)的snapshot(),它会:

将视图的内容绘制并将其作为新图像对象返回

因此,如果您有一个对象如下:

@IBOutlet var arkitSceneView:ARSCNView!

你只需要这样做:

let imageFromArkitScene:UIImage? = arkitSceneView.snapshot()

2. 为什么会发生这种情况?

这是因为 CVPixelBuffer 来自于 ARFrame,而 ARFrame 是从设备相机中连续捕获的:

由正在运行的 AR 会话从设备相机中捕获 (连续)。

由于摄像头方向不随设备旋转而改变(它们是分开的),为了能够将你的帧方向调整为当前视图,你应该使用 displayTransform(for:viewportSize:) 提取的仿射变换来重新调整从相机捕获的图像:

返回一个仿射变换,用于在屏幕上渲染相机图像的适当坐标空间与归一化图像坐标之间进行转换。

你可以在这里找到很好的文档和使用示例。

let orient = UIApplication.shared.statusBarOrientation
let viewportSize = yourSceneView.bounds.size
let transform = frame.displayTransform(for: orient, viewportSize: viewportSize).inverted()
var finalImage = CIImage(cvPixelBuffer: pixelBuffer).transformed(by: transform)

感谢您的回答。快照从来不是一个选项,因为我必须在每一帧中定位球。'displayTransform(for:viewportSize:)'可能是我需要的。 - Alok Subedi
1
欢迎,希望你能找到一个好的折中方案。此外,如果你必须在每帧处理,最好使用像素缓冲区+显示转换,这似乎是一个更高效的解决方案。 - mugx
1
是的,这就是我想要的。谢谢。 - Alok Subedi
我发现当方向被纠正后,图像仍然会被拉伸。例如:https://dev59.com/Tbfna4cB1Zd3GeqPlgw8 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接