我想检查相机视图中是否不再显示ARReferenceImage。目前我可以检查图像的节点是否在摄像机的视图中,但是当ARReferenceImage被另一个图像覆盖或者图像被移除时,该节点仍然可以在摄像机的视图中看到。
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
guard let node = self.currentImageNode else { return }
if let pointOfView = sceneView.pointOfView {
let isVisible = sceneView.isNode(node, insideFrustumOf: pointOfView)
print("Is node visible: \(isVisible)")
}
}
所以我需要检查图像是否不再可见而不是图像的节点可见性。但我不知道这是否可行。第一个屏幕截图显示了找到下方图像时添加的三个框。当覆盖找到的图像时(见截图2),我想删除这些框。
答案 0 :(得分:10)
我设法解决了这个问题!使用了一点MaybeI的代码和他的概念来解决问题,但以不同的方式。以下代码行仍用于重新激活图像识别。
// Delete anchor from the session to reactivate the image recognition
sceneView.session.remove(anchor: anchor)
让我解释一下。首先,我们需要添加一些变量。
// The scnNodeBarn variable will be the node to be added when the barn image is found. Add another scnNode when you have another image.
var scnNodeBarn: SCNNode = SCNNode()
// This variable holds the currently added scnNode (in this case scnNodeBarn when the barn image is found)
var currentNode: SCNNode? = nil
// This variable holds the UUID of the found Image Anchor that is used to add a scnNode
var currentARImageAnchorIdentifier: UUID?
// This variable is used to call a function when there is no new anchor added for 0.6 seconds
var timer: Timer!
以下评论的完整代码。
/// - Tag: ARImageAnchor-Visualizing
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else { return }
let referenceImage = imageAnchor.referenceImage
// The following timer fires after 0.6 seconds, but everytime when there found an anchor the timer is stopped.
// So when there is no ARImageAnchor found the timer will be completed and the current scene node will be deleted and the variable will set to nil
DispatchQueue.main.async {
if(self.timer != nil){
self.timer.invalidate()
}
self.timer = Timer.scheduledTimer(timeInterval: 0.6 , target: self, selector: #selector(self.imageLost(_:)), userInfo: nil, repeats: false)
}
// Check if there is found a new image on the basis of the ARImageAnchorIdentifier, when found delete the current scene node and set the variable to nil
if(self.currentARImageAnchorIdentifier != imageAnchor.identifier &&
self.currentARImageAnchorIdentifier != nil
&& self.currentNode != nil){
//found new image
self.currentNode!.removeFromParentNode()
self.currentNode = nil
}
updateQueue.async {
//If currentNode is nil, there is currently no scene node
if(self.currentNode == nil){
switch referenceImage.name {
case "barn":
self.scnNodeBarn.transform = node.transform
self.sceneView.scene.rootNode.addChildNode(self.scnNodeBarn)
self.currentNode = self.scnNodeBarn
default: break
}
}
self.currentARImageAnchorIdentifier = imageAnchor.identifier
// Delete anchor from the session to reactivate the image recognition
self.sceneView.session.remove(anchor: anchor)
}
}
在计时器完成时删除节点,表明没有找到新的ARImageAnchor。
@objc
func imageLost(_ sender:Timer){
self.currentNode!.removeFromParentNode()
self.currentNode = nil
}
这样,当覆盖图像或找到新图像时,将删除当前添加的scnNode。
遗憾的是,由于以下原因,此解决方案无法解决图像的定位问题:
ARKit不会跟踪每个检测到的图像的位置或方向的变化。
答案 1 :(得分:7)
我认为目前不可能。
来自Recognizing Images in an AR Experience documentation:
设计您的AR体验,将检测到的图像用作虚拟内容的起点。
ARKit不会跟踪每个检测到的图像的位置或方向的变化。如果您尝试将虚拟内容保留在检测到的图像上,则该内容可能无法正确保留。而是使用检测到的图像作为启动动态场景的参照系。
ARKit 2.0和iOS 12最终通过ARImageTrackingConfiguration
或ARWorldTrackingConfiguration.detectionImages
属性添加此功能,该属性现在也跟踪图像的位置。
ARImageTrackingConfiguration
的Apple文档列出了两种方法的优点:
使用ARImageTrackingConfiguration,ARKit不是通过跟踪设备相对于世界的运动来建立3D空间,而是仅通过检测和跟踪相机视图中已知2D图像的运动来建立3D空间。 ARWorldTrackingConfiguration也可以检测图像,但每种配置都有自己的优势:
世界跟踪的性能成本高于仅图像跟踪,因此您的会话可以使用ARImageTrackingConfiguration一次性可靠地跟踪更多图像。
仅限图像跟踪可让您将虚拟内容锚定到已知图像,只有当这些图像在相机视图中时。通过图像检测进行世界跟踪,您可以使用已知图像将虚拟内容添加到3D世界,并在图像不再可见时继续跟踪该内容在世界空间中的位置。
世界追踪在稳定,不动的环境中效果最佳。在更多情况下,您可以使用仅图像跟踪将虚拟内容添加到已知图像 - 例如,移动地铁车厢内的广告。
答案 2 :(得分:2)
检查ARKit当前是否跟踪的图像的正确方法是将didUpdate节点上ARImageAnchor中的“ isTracked”属性用于锚点功能。
为此,我使用下一个结构:
struct TrackedImage {
var name : String
var node : SCNNode?
}
然后是带有所有图像名称的该结构的数组。
var trackedImages : [TrackedImage] = [ TrackedImage(name: "image_1", node: nil) ]
然后在didAdd节点中进行定位,将新内容设置为场景,并将该节点添加到trackedImages数组中的相应元素上
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Check if the added anchor is a recognized ARImageAnchor
if let imageAnchor = anchor as? ARImageAnchor{
// Get the reference ar image
let referenceImage = imageAnchor.referenceImage
// Create a plane to match the detected image.
let plane = SCNPlane(width: referenceImage.physicalSize.width, height: referenceImage.physicalSize.height)
plane.firstMaterial?.diffuse.contents = UIColor(red: 1, green: 1, blue: 1, alpha: 0.5)
// Create SCNNode from the plane
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = -.pi / 2
// Add the plane to the scene.
node.addChildNode(planeNode)
// Add the node to the tracked images
for (index, trackedImage) in trackedImages.enumerated(){
if(trackedImage.name == referenceImage.name){
trackedImage[index].node = planeNode
}
}
}
}
最后,在didUpdate节点中找到锚点功能,我们在数组中搜索锚点名称,并检查isTracked属性是否为false。
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
var trackedImages : [TrackedImage] = [ TrackedImage(name: "image_1", node: nil) ]
if let imageAnchor = anchor as? ARImageAnchor{
// Search the corresponding node for the ar image anchor
for (index, trackedImage) in trackedImages.enumerated(){
if(trackedImage.name == referenceImage.name){
// Check if track is lost on ar image
if(imageAnchor.isTracked){
// The image is being tracked
trackedImage.node?.isHidden = false // Show or add content
}else{
// The image is lost
trackedImage.node?.isHidden = true // Hide or delete content
}
break
}
}
}
}
当您想同时跟踪多张图像并知道何时丢失任何图像时,此解决方案有效。
注意:要使此解决方案起作用,必须将AR配置中的maximumNumberOfTrackedImages
设置为非零数字。
答案 3 :(得分:0)
我不完全确定我理解了你的要求(如此道歉),但如果我有,那么也许这可能会有所帮助...
似乎insideOfFrustum
正常工作,它们必须与节点相关联SCNGeometry
以使其工作(仅SCNNode就不够了)。
例如,如果我们在delegate
回调中执行类似的操作,并将添加的SCNNode
保存到数组中:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. If Out Target Image Has Been Detected Than Get The Corresponding Anchor
guard let currentImageAnchor = anchor as? ARImageAnchor else { return }
//2. Print The Anchor ID & It's Associated Node
print("""
Anchor With ID Has Been Detected \(currentImageAnchor.identifier)
Associated Node Details = \(node)
""")
//3. Store The Node
imageTargets.append(node)
}
然后使用insideOfFrustum
方法,99%的时间它会说节点在视野中,即使我们知道它不应该是。
但是,如果我们做这样的事情(我们创建一个透明的标记节点,例如具有某种几何体的节点):
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. If Out Target Image Has Been Detected Than Get The Corresponding Anchor
guard let currentImageAnchor = anchor as? ARImageAnchor else { return }
//2. Print The Anchor ID & It's Associated Node
print("""
Anchor With ID Has Been Detected \(currentImageAnchor.identifier)
Associated Node Details = \(node)
""")
//3. Create A Transpanrent Geometry
node.geometry = SCNSphere(radius: 0.1)
node.geometry?.firstMaterial?.diffuse.contents = UIColor.clear
//3. Store The Node
imageTargets.append(node)
}
然后调用以下方法,它确实检测ARReferenceImage
是否在inView:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
//1. Get The Current Point Of View
guard let pointOfView = augmentedRealityView.pointOfView else { return }
//2. Loop Through Our Image Target Markers
for addedNode in imageTargets{
if augmentedRealityView.isNode(addedNode, insideFrustumOf: pointOfView){
print("Node Is Visible")
}else{
print("Node Is Not Visible")
}
}
}
关于另一个关于SCNNode被另一个被遮挡的观点,Apple Docs
:
inViewOfFrostrum
表示
不执行遮挡测试。也就是说,它返回 如果测试节点位于指定的视锥体内,则为true 无论该节点的内容是否被其他节点遮挡 几何形状。
再次,如果我没有正确理解你,请道歉,但希望它在某种程度上可能会有所帮助......
更新:
现在我完全理解你的问题,我同意@orangenkopf的说法,这是不可能的。由于文档说明:
ARKit不会跟踪每个位置或方向的变化 检测到的图像。
答案 4 :(得分:0)
来自Recognizing Images in an AR Experience documentation:
ARKit只为每个会话添加一个图像锚点 会话配置的detectImages数组中的参考图像。 如果您的AR体验在图像时将虚拟内容添加到场景中 如果检测到该操作,默认情况下只会发生一次。允许 用户无需重新启动应用即可再次体验该内容, 调用session的remove(anchor :)方法删除相应的 ARImageAnchor。移除锚点后,ARKit将添加一个新锚点 下次检测到图像时锚定。
所以,也许你可以为你的案例找到一个解决方法:
我们假设我们的结构可以保存我们检测到的ARImageAnchor
和相关的虚拟内容:
struct ARImage {
var anchor: ARImageAnchor
var node: SCNNode
}
然后,当调用renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)
时,将检测到的图像保存到ARImage的临时列表中:
...
var tmpARImages: [ARImage] = []
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let imageAnchor = anchor as? ARImageAnchor else { return }
let referenceImage = imageAnchor.referenceImage
// If the ARImage does not exist
if !tmpARImages.contains(where: {$0.anchor.referenceImage.name == referenceImage.name}) {
let virtualContent = SCNNode(...)
node.addChildNode(virtualContent)
tmpARImages.append(ARImage(anchor: imageAnchor, node: virtualContent))
}
// Delete anchor from the session to reactivate the image recognition
sceneView.session.remove(anchor: anchor)
}
如果您了解,当您的相机的视图指向图像/标记之外时,委托功能将无休止地循环...(因为我们从会话中移除了锚点)。
这个想法是将图像识别循环,保存在tmp列表中的检测到的图像和sceneView.isNode(node, insideFrustumOf: pointOfView)
函数结合起来,以确定检测到的图像/标记是否不再被查看。
我希望很清楚......
答案 5 :(得分:0)
仅当您严格水平或垂直握住设备时,此代码才有效。如果您握住iPhone倾斜或开始倾斜,则此代码不起作用:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
//1. Get The Current Point Of View
guard let pointOfView = augmentedRealityView.pointOfView else { return }
//2. Loop Through Our Image Target Markers
for addedNode in imageTargets{
if augmentedRealityView.isNode(addedNode, insideFrustumOf: pointOfView){
print("Node Is Visible")
}else{
print("Node Is Not Visible")
}
}
}
答案 6 :(得分:0)
关于它的价值,我花了几个小时试图弄清楚如何不断检查图像参考。 didUpdate函数就是答案。然后,您只需要使用.isTracked属性测试正在跟踪的参考图像即可。此时,您可以将.isHidden属性设置为true或false。这是我的例子:
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
let trackedNode = node
if let imageAnchor = anchor as? ARImageAnchor{
if (imageAnchor.isTracked) {
trackedNode.isHidden = false
print("\(trackedNode.name)")
}else {
trackedNode.isHidden = true
//print("\(trackedImageName)")
print("No image in view")
}
}
}