Убедитесь, что ARReferenceImage больше не отображается в режиме просмотра камеры
Я хотел бы проверить, не видна ли 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), я хотел бы удалить поля.
Ответы
Ответ 1
Мне удалось решить проблему! Использовал немного кода Maybe1 и его концепцию для решения проблемы, но по-другому. Следующая строка кода по-прежнему используется для повторного включения распознавания изображений.
// 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 не отслеживает изменения положения или ориентации каждого обнаруженного изображения.
Ответ 2
Я не думаю, что в настоящее время это возможно.
Из документа " Распознавание изображений в AR Experience":
Создайте свой опыт AR для использования обнаруженных изображений в качестве отправной точки для виртуального контента.
ARKit не отслеживает изменения положения или ориентации каждого обнаруженного изображения. Если вы попытаетесь разместить виртуальный контент, который остается прикрепленным к обнаруженному изображению, этот контент может не отображаться правильно. Вместо этого, использование обнаружено изображения в качестве системы отсчета для начала динамической сцены.
Новый ответ для iOS 12.0
ARKit 2.0 и iOS 12, наконец, добавляют эту функцию либо через ARImageTrackingConfiguration
либо через свойство ARWorldTrackingConfiguration.detectionImages
которое теперь также отслеживает положение изображений.
Документация Apple к ARImageTrackingConfiguration
перечисляет преимущества обоих методов:
С ARImageTrackingConfiguration ARKit устанавливает 3D-пространство не путем отслеживания движения устройства по отношению к миру, а исключительно путем обнаружения и отслеживания движения известных 2D-изображений с учетом камеры. ARWorldTrackingConfiguration также может обнаруживать изображения, но каждая конфигурация имеет свои сильные стороны:
-
Мировое отслеживание имеет более высокую производительность, чем отслеживание только изображений, поэтому ваш сеанс может надежно отслеживать больше изображений одновременно с ARImageTrackingConfiguration.
-
Отслеживание только изображений позволяет привязывать виртуальный контент к известным изображениям только тогда, когда эти изображения видны с камеры. Мировое отслеживание с обнаружением изображений позволяет использовать известные изображения для добавления виртуального контента в мир 3D и продолжает отслеживать положение этого контента в мировом пространстве даже после того, как изображение больше не отображается.
-
Мировое отслеживание работает лучше всего в стабильной, неподвижной среде. Вы можете использовать отслеживание только изображений для добавления виртуального контента к известным изображениям в других ситуациях - например, рекламу внутри движущегося автомобиля метро.
Ответ 3
Правильный способ проверить, отслеживается ли отслеживаемое вами изображение в данный момент ARKit, - использовать свойство isTracked в ARImageAnchor на узле didUpdate для функции привязки.
Для этого я использую следующую структуру:
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
}
}
}
}
Это решение работает, когда вы хотите отслеживать несколько изображений одновременно и знать, когда какое-либо из них потеряно.
Примечание. Чтобы это решение работало, для параметра maximumNumberOfTrackedImages
в конфигурации AR должно быть установлено ненулевое число.
Ответ 4
Я не совсем уверен, что понял, что вы просите (извините), но если у меня есть, возможно, это может помочь...
Кажется, что для insideOfFrustum
работать правильно, что их должна быть какая-то SCNGeometry
связанная с узлом для ее работы (одного SCNNode недостаточно).
Например, если мы сделаем что-то подобное в 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 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 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 не отслеживает изменения положения или ориентации каждого обнаруженного изображения.
Ответ 5
Из документа " Распознавание изображений в AR Experience":
ARKit добавляет привязку изображения к сеансу ровно один раз для каждого ссылочного изображения в массиве определения идентификаторов сеансов. Если ваш опыт AR добавляет виртуальный контент в сцену при обнаружении изображения, это действие будет выполняться по умолчанию только один раз. Чтобы позволить пользователю снова использовать этот контент без перезапуска приложения, вызовите сеансы 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)
чтобы определить, не обнаружено ли обнаруженное изображение/маркер.
Надеюсь, это было ясно...
Ответ 6
Этот код работает, только если вы держите устройство строго горизонтально или вертикально. Если вы держите 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")
}
}
}
Ответ 7
Для чего это стоило, я потратил часы, пытаясь понять, как постоянно проверять ссылки на изображения. Функция 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")
}
}
}