如何从图像坐标传递到 ARKit 世界坐标?

How to pass from coordinates of an image to ARKit World Coordinates?

我遇到了一个不知道如何解决的问题。

简介:项目和目标说明

我有一张白色的 A4 sheet 纸,我在上面用笔画了一个 电路:

我用 ARKit RectangleDetection 检测了这张图片,我保存了这张将用作纹理的图片。

然后我将 SCNPlane 锚定在这张图片上,并将保存的图片设置为这架飞机的 texture


我需要什么?

目标 是将 SCNPlane 坐标转换为纹理坐标(用作纹理的 2D 图像)。我不明白如何在图像上获得一个点并在 SCNPlane 上获得相同的点。怎么做?



我已经更新了这个 post 我也会 post 解决方案:)

此答案旨在对我认为可以解决您的问题的方法进行高级描述。不幸的是,我无法用代码给出详细的描述,但我希望这能对你有所帮助。

第一种方法

使用纸上已知的图像,将此图像设置为参考图像,并使用 ARKit 跟踪此图像。

基本上,您将使用此图像作为 space 中的已知点来绘制元素。有关更多上下文,请参见下图。

跟踪已知图像,您将有一个代表此图像的 SCNNode,如果我没记错的话,它的局部坐标系原点位于其中心(如图所示)。可以在这个节点上添加children,它们会在已知图像的坐标系中,即x轴,y轴,z轴和A4纸一样。

在描述的情况下,x_a > 0,并且 y_a < 0,而 x_b > 0,且y_b > 0,如果正x轴向右,正y轴上升(你需要确认这一点,因为我不确定)。

第二种方法

这与您想要的更相似,但更难。您可以使用您绘制的标志作为参考。您可以通过在定义平面的 3 个已知点上执行命中测试(参见 docs),从真实纸张坐标系映射到 ARKit 坐标系。

您不需要用户触摸屏幕来执行命中测试。您所需要的只是归一化图像坐标 space 上的坐标。此步骤将在您处理图像后进行。我想应该是这样的:

图像处理

  1. 你执行你需要的过滤器来提取你想要的信息;
  2. 作为附加步骤,过滤器应识别 3 个已知点,可用于创建平面;
  3. 您添加相对于此平面的元素。

这种方法有什么问题吗?我认为在 ARKit 上没有简单的方法可以做到这一点,所以你需要实现大部分逻辑。

最后的话

使用第一种方法更容易,所以我会使用第一种方法作为初始原型。也许稍后您可以更改为更高级的方法。

反正我觉得最复杂的一步就是图像处理了。如果您可以从论文中提取所需的信息,您可能可以使用任何一种方法来绘制元素。

我通过创建坐标转换解决了我的问题。

下图给出了大概的思路:

所以,使用这个公式我可以通过两种类型的坐标:纹理坐标SCN平面坐标

使用以下函数我可以进行坐标转换(从 SCNPlane -> 到纹理)(从纹理 -> 到 SCNPlane)。

func changeIntervalSheetSCNPlane(typeOfConversion: Bool, convStruct: ConversionSheetSCNPlane) -> SCNVector3 {

// Formula
//
//  NOTE:
//  - x € [xA, xB]  | y € [yA, yB]
//  - x' € [x'A, x'B]  | y' € [y'A, y'B]        x' = "x primo"
//
// x-xA/xB-xA = x'-x'A/x'B-x'A  => x' = (x'B-x'A/xB-xA)*(x-xA)+x'A
// y-yA/yB-yA = y'-y'A/y'B-y'A  => y' = (y'B-y'A/yB-yA)*(y-yA)+y'A
//
// x,y = Sheet  | x',y' = SCNPlane

var x: Float, y: Float, xP: Float, yP: Float,
    xPB: Float?, xPA: Float?, xB: Float?, xA: Float?,
    yPB: Float?, yPA: Float?, yB: Float?, yA: Float?

if (typeOfConversion) {
    x = Float(convStruct.sheetXYPoint!.x)
    y = Float(convStruct.sheetXYPoint!.y)

    // Calculate x' value
    xPB = Float(convStruct.planeTopRightPoint.x)
    xPA = Float(convStruct.planeTopLeftPoint.x)

    xB = Float(convStruct.sheetTopRightPoint.x)
    xA = Float(convStruct.sheetTopLeftPoint.x)

    // Calculate y' value
    yPB = Float(convStruct.planeTopRightPoint.y)
    yPA = Float(convStruct.planeBottomLeftPoint.y)

    yB = Float(convStruct.sheetTopRightPoint.y)
    yA = Float(convStruct.sheetBottomLeftPoint.y)
} else {
    x = Float(convStruct.planeXYPoint!.x)
    y = Float(convStruct.planeXYPoint!.y)

    // Calculate x' value
    xPB = Float(convStruct.sheetTopRightPoint.x)
    xPA = Float(convStruct.sheetTopLeftPoint.x)

    xB = Float(convStruct.planeTopRightPoint.x)
    xA = Float(convStruct.planeTopLeftPoint.x)

    // Calculate y' value
    yPB = Float(convStruct.sheetTopRightPoint.y)
    yPA = Float(convStruct.sheetBottomLeftPoint.y)

    yB = Float(convStruct.planeTopRightPoint.y)
    yA = Float(convStruct.planeBottomLeftPoint.y)
}

let xPB_xPA = xPB!-xPA!                             // (x'B-x'A)
let xB_xA = xB!-xA!                                 // (xB-xA)
let x_xA = x-xA!                                    // (x-xA)
xP = (xPB_xPA/xB_xA)*(x_xA)+xPA!                    // x'

let yPB_yPA = yPB!-yPA!                             // (y'B-y'A)
let yB_yA = yB!-yA!                                 // (yB-yA)
let y_yA = y-yA!                                    // (y-yA)
yP = (yPB_yPA/yB_yA)*(y_yA)+yPA!                     // y'

let position = SCNVector3(xP, yP, 0)

return position
}

下面的结构定义了Texture和SCNPlane的边以及公式的点P(x,y)和P'(x',y')

// Structure that contains the edges of Texture and SCNPlane
struct ConversionSheetSCNPlane {

// ========== TEXTURE ==========
// Point (X,Y) of the texture
public var sheetXYPoint: CGPoint?

// The four texture edge coordinates
public var sheetTopLeftPoint: CGPoint
public var sheetTopRightPoint: CGPoint
public var sheetBottomLeftPoint: CGPoint
public var sheetBottomRightPoint: CGPoint

// ========== SCNPLANE ==========
// Point (X,Y) of the plane
public var planeXYPoint: CGPoint?

// The four plane edge coordinates
public var planeTopLeftPoint: CGPoint
public var planeTopRightPoint: CGPoint
public var planeBottomLeftPoint: CGPoint
public var planeBottomRightPoint: CGPoint
}