如何从图像坐标传递到 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 上的坐标。此步骤将在您处理图像后进行。我想应该是这样的:
图像处理
- 你执行你需要的过滤器来提取你想要的信息;
- 作为附加步骤,过滤器应识别 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
}
我遇到了一个不知道如何解决的问题。
简介:项目和目标说明
我有一张白色的 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 上的坐标。此步骤将在您处理图像后进行。我想应该是这样的:
图像处理
- 你执行你需要的过滤器来提取你想要的信息;
- 作为附加步骤,过滤器应识别 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
}