AR参考图像平面在iOS Swift中的位置是否正确? [英] AR refernce image plane was not position properly in iOS Swift?
问题描述
我正在使用ar参考图像处理名片资料信息.当参考被检测到时,它将显示公司首席执行官的详细信息,地址,照片和团队成员的信息等.最初检测到的图像将使用 runAction 胺向右移动.
I am working on business card profile information using ar reference image. When reference detects it will show company ceo details, address, photo and team members information, etc. Initially image detected it plane will move to right using runAction amintion.
我的问题是检测到飞机反射,飞机位置稳定并且在这里和那里移动.如何用参考图像拟合平面位置.
My question is detected ar refrence, plane position was stable and its moving here and there. How to fit plane position with ar reference image.
这是我的结果截图:[![在此处输入图片描述] [1]] [1]
Here is the screenshot my result:[![enter image description here][1]][1]
这是我使用的代码:
var weboverlayview: CALayer?
var loadWeb: UIWebView?
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
sceneView.autoenablesDefaultLighting = true
let ARScene = SCNScene()
sceneView.scene = ARScene
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.vertical, .horizontal]
configuration.detectionImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil)
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let anchorNode = SCNNode()
anchorNode.name = "anchor"
sceneView.scene.rootNode.addChildNode(anchorNode)
return anchorNode
}
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
var labelNode:SCNNode?
var companyLabelNode: SCNNode?
var addressLabelNode: SCNNode?
var webaddressLabelNode: SCNNode?
var maillabelNode: SCNNode?
var mobileLabelNode:SCNNode?
var teamLabelNode:SCNNode?
guard let imageAnchor = anchor as? ARImageAnchor else {return}
if let imageName = imageAnchor.referenceImage.name {
print(imageName)
if imageName == "card"{
let plane = SCNPlane(width: 20,height: 24)
plane.firstMaterial?.diffuse.contents = UIColor.black.withAlphaComponent(0.75)
plane.cornerRadius = 0.25
let planeNodee = SCNNode(geometry: plane)
planeNodee.eulerAngles.x = -.pi / 2
planeNodee.runAction(SCNAction.moveBy(x: -5, y: 0, z: 19, duration: 0.75))
labelNode = self.addLabel(text: "Gowdhaman Kandasamy \nFounder and CEO", anchor: imageAnchor)
labelNode?.runAction(SCNAction.moveBy(x: -1.3, y: 1, z: 16.8, duration: 0.75))
companyLabelNode = self.addLabel(text: "CZ Smart Mobility", anchor: imageAnchor)
companyLabelNode?.runAction(SCNAction.moveBy(x: 1.5, y: 1, z: 22, duration: 0.75))
addressLabelNode = self.addAddressLabel(text: "Official Address:\n\n1st floor, TBI Office,\nDr.col JEPPIAR Research Park,\nResearch and development center,\nSathyabama University,\nChennai-600119\nTamil nadu, India.", anchor: imageAnchor)
addressLabelNode?.runAction(SCNAction.moveBy(x: -4.8, y: 1, z: 16.8, duration: 0.75))
let userImagePlane = SCNPlane(width: 3.5, height: 3.5)
userImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "gow")
userImagePlane.cornerRadius = 0.25
let userPlaneNode = SCNNode(geometry: userImagePlane)
userPlaneNode.eulerAngles.x = -.pi/2
userPlaneNode.runAction(SCNAction.moveBy(x: -1, y: 1, z: 9.5, duration: 0.75))
let webImagePlane = SCNPlane(width: 1, height: 1)
webImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "web")
webImagePlane.cornerRadius = 0.25
let webPlanenode = SCNNode(geometry: webImagePlane)
webPlanenode.eulerAngles.x = -.pi/2
webPlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 12.5, duration: 0.75))
webaddressLabelNode = addAddressLabel(text: "www.czsm.co.in", anchor: imageAnchor)
webaddressLabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 18.2, duration: 0.75))
let mailImagePlane = SCNPlane(width: 1, height: 1)
mailImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "mail")
mailImagePlane.cornerRadius = 0.25
let mailPlanenode = SCNNode(geometry: mailImagePlane)
mailPlanenode.eulerAngles.x = -.pi/2
mailPlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 17.5, duration: 0.75))
maillabelNode = addAddressLabel(text: "gowdhaman@czsm.co.in", anchor: imageAnchor)
maillabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 23.2, duration: 0.75))
let mobileImagePlane = SCNPlane(width: 1, height: 1)
mobileImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "mobile")
mobileImagePlane.cornerRadius = 0.25
let mobilePlanenode = SCNNode(geometry: mobileImagePlane)
mobilePlanenode.eulerAngles.x = -.pi/2
mobilePlanenode.runAction(SCNAction.moveBy(x: -7, y: 1, z: 23.9, duration: 0.75))
mobileLabelNode = addAddressLabel(text: "+919941123110", anchor: imageAnchor)
mobileLabelNode?.runAction(SCNAction.moveBy(x: -8.7, y: 1, z: 29.7, duration: 0.75))
/************Team members*************/
teamLabelNode = self.addLabel(text: "Team Members", anchor: imageAnchor)
teamLabelNode?.runAction(SCNAction.moveBy(x: -9.8, y: 1, z: 22, duration: 0.75))
let sivaImagePlane = SCNPlane(width: 2.7, height: 2.7)
sivaImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "pic2")
sivaImagePlane.cornerRadius = 0.25
let sivaPlanenode = SCNNode(geometry: sivaImagePlane)
sivaPlanenode.eulerAngles.x = -.pi/2
sivaPlanenode.runAction(SCNAction.moveBy(x: -11, y: 1, z: 9.5, duration: 0.75))
let parameshImagePlane = SCNPlane(width: 2.7, height: 2.7)
parameshImagePlane.firstMaterial?.diffuse.contents = UIImage(named: "pic4")
parameshImagePlane.cornerRadius = 0.25
let parameshPlanenode = SCNNode(geometry: parameshImagePlane)
parameshPlanenode.eulerAngles.x = -.pi/2
parameshPlanenode.runAction(SCNAction.moveBy(x: -11, y: 1, z: 9.5, duration: 0.75))
node.addChildNode(planeNodee)
node.addChildNode(labelNode!)
node.addChildNode(companyLabelNode!)
node.addChildNode(userPlaneNode)
node.addChildNode(addressLabelNode!)
node.addChildNode(webPlanenode)
node.addChildNode(webaddressLabelNode!)
node.addChildNode(mailPlanenode)
node.addChildNode(maillabelNode!)
node.addChildNode(mobilePlanenode)
node.addChildNode(mobileLabelNode!)
node.addChildNode(teamLabelNode!)
node.addChildNode(sivaPlanenode)
node.addChildNode(parameshPlanenode)
// node.addChildNode(czwebPlaneNode)
self.sceneView.scene.rootNode.addChildNode(node)
}
}
}
func webButton() {
}
func addLabel(text: String, anchor: ARImageAnchor) -> SCNNode {
let plane = SCNPlane(width: 10,
height: 4)
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = (-.pi)/2
planeNode.eulerAngles.y = (-.pi)/2
// planeNode.eulerAngles.z = (-.pi)/2
let skScene = SKScene(size: CGSize(width: 400, height: 100))
skScene.backgroundColor = UIColor.clear
let substrings: [String] = text.components(separatedBy: "\n")
for aSubstring in substrings {
let lbl = SKLabelNode(text: aSubstring)
lbl.fontSize = 18
lbl.numberOfLines = 1
lbl.fontColor = UIColor.white
lbl.fontName = "Avenir-medium"
let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl.fontSize
print("yname::::\(y)")
lbl.position = CGPoint(x: 0, y: y)
lbl.horizontalAlignmentMode = .left
lbl.yScale *= -1
skScene.addChild(lbl)
}
let material = SCNMaterial()
material.isDoubleSided = false
material.diffuse.contents = skScene
plane.materials = [material]
return planeNode
}
func addCompanyLabel(text: String, anchor: ARImageAnchor) -> SCNNode {
let plane1 = SCNPlane(width: 10,
height: 4)
let planeNode1 = SCNNode(geometry: plane1)
planeNode1.eulerAngles.x = (-.pi)/2
planeNode1.eulerAngles.y = (-.pi)/2
// planeNode.eulerAngles.z = (-.pi)/2
let skScene1 = SKScene(size: CGSize(width: 400, height: 100))
skScene1.backgroundColor = UIColor.clear
let substrings: [String] = text.components(separatedBy: "\n")
for aSubstring in substrings {
let lbl1 = SKLabelNode(text: aSubstring)
lbl1.fontSize = 20
lbl1.numberOfLines = 1
lbl1.fontColor = UIColor.white
lbl1.fontName = "Avenir-medium"
let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl1.fontSize
print("ycompanname::::\(y)")
lbl1.position = CGPoint(x: 0, y: y)
lbl1.horizontalAlignmentMode = .left
lbl1.yScale *= -1
skScene1.addChild(lbl1)
}
let material = SCNMaterial()
material.isDoubleSided = false
material.diffuse.contents = skScene1
plane1.materials = [material]
return planeNode1
}
func addAddressLabel(text: String, anchor: ARImageAnchor) -> SCNNode {
let plane = SCNPlane(width: 10,
height: 4)
let planeNode = SCNNode(geometry: plane)
planeNode.eulerAngles.x = (-.pi)/2
planeNode.eulerAngles.y = (-.pi)/2
// planeNode.eulerAngles.z = (-.pi)/2
let skScene = SKScene(size: CGSize(width: 500, height: 200))
skScene.backgroundColor = UIColor.clear
let substrings: [String] = text.components(separatedBy: "\n")
for aSubstring in substrings {
let lbl = SKLabelNode(text: aSubstring)
lbl.fontSize = 20
// lbl.numberOfLines = 1
lbl.fontColor = UIColor.white
lbl.fontName = "Avenir-medium"
let y = CGFloat(substrings.index(of: aSubstring)! + 1) * lbl.fontSize
print("yaddress::::\(y)")
lbl.position = CGPoint(x: 0, y: y)
lbl.horizontalAlignmentMode = .left
lbl.yScale *= -1
skScene.addChild(lbl)
}
let material = SCNMaterial()
material.isDoubleSided = false
material.diffuse.contents = skScene
plane.materials = [material]
return planeNode
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
// guard let planeAnchor = anchor as? ARPlaneAnchor else {return}
// let planeGeometry = planeAnchor.geometry
// guard let device = MTLCreateSystemDefaultDevice() else {return}
// let plane = ARSCNPlaneGeometry(device: device)
// plane?.update(from: planeGeometry)
// node.geometry = plane
// node.geometry?.firstMaterial?.diffuse.contents = UIColor.black
// node.geometry?.firstMaterial?.transparency = 1
// node.geometry?.firstMaterial?.fillMode = SCNFillMode.lines
}
}
像这样的参考平面的例外结果将适合卡片 https://www.facebook .com/oscarfalmer/videos/10156651667309345/
Excepted result like this ar reference plane will fit to card https://www.facebook.com/oscarfalmer/videos/10156651667309345/
推荐答案
首先需要考虑的是使用ARWorldTrackingConfiguration
还是ARImageTrackingConfiguration
(IOS12
).
The first thing you need to consider is whether you want to use ARWorldTrackingConfiguration
or ARImageTrackingConfiguration
(IOS12
).
如果使用ARImageTrackingConfiguration
,则不能使用PlaneDetection,因为这是仅图像"跟踪配置:
If you use ARImageTrackingConfiguration
, you cant make use of PlaneDetection, as this is an Image Only tracking configuration:
,您可以将虚拟内容仅锚定到已知图像上 图像在相机视野范围内.带图像检测的世界跟踪 使您可以使用已知图像将虚拟内容添加到3D世界中,并且 继续跟踪该内容在世界空间中的位置 在图像不再可见后.
which lets you anchor virtual content to known images only when those images are in view of the camera. World tracking with image detection lets you use known images to add virtual content to the 3D world, and continues to track the position of that content in world space even after the image is no longer in view.
如果您希望您的内容在任何时候(在摄像机视角下)始终保持锚定在图像上,那将是最好的选择,
This would be your best bet, if you want your content to stay anchored to the image at all times (when in view of the camera) since:
它以六个自由度(6DOF)跟踪其运动: 具体来说,是三个旋转轴(横摇,俯仰和偏航),以及 三个平移轴(在x,y和z中移动).
it tracks their movement with six degrees of freedom (6DOF): specifically, the three rotation axes (roll, pitch, and yaw), and three translation axes (movement in x, y, and z).
另一方面,如果您想同时检测ARPlaneAnchors
和ARImageAnchors
,但又不担心与ARImageAnchor
关联的任何内容都不会持续跟踪,则应该使用ARWorldTrackingConfiguration
.
On the other hand if you want to detect ARPlaneAnchors
, as well as ARImageAnchors
, but aren't bothered that any content associated with your ARImageAnchor
won't track constantly then you should use ARWorldTrackingConfiguration
.
正如@Trinca还说的那样,您需要确保为图像提供的测量结果尽可能准确,因为ARKit
使用这些值来返回图像的physicalSize
和physicalWidth
,这将允许您进行虚拟内容以更准确地放置(例如,如果您指定的尺寸大于现实生活中图像的实际尺寸,则您将无法准确对齐虚拟内容).
As @Trinca also said you need to ensure that the measurements you provide for your image are as accurate as possible, as ARKit
uses these to return the physicalSize
and physicalWidth
of your image which will allow your virtual content to placed more accurately (e.g if you specify a larger size than the actual size of your image in real life, your not going to be able to align your virtual content accurately).
在创建名片或任何imageTarget时,我们必须确保在ARReferenceImage Settings
框中正确设置了尺寸:
When creating a business card or any imageTarget we must make sure that our dimensions are accurately set in the ARReferenceImage Settings
box:
然后我们可以检查是否像这样检测到了我们的imageTarget
:
We can then check to see if our imageTarget
is detected like so:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. Check We Have Detected An ARImageAnchor & Check It's The One We Want
guard let validImageAnchor = anchor as? ARImageAnchor,
let targetName = validImageAnchor.referenceImage.name, targetName == "TargetCard" else { return}
//2. Check To See The Detected Size Of Our Business Card (Should By 5cm*3cm)
let businessCardWidth = validImageAnchor.referenceImage.physicalSize.width
let businessCardHeight = validImageAnchor.referenceImage.physicalSize.height
print(
"""
We Have Detected Business Card With Name \(targetName)
\(targetName)'s Width Is \(businessCardWidth)
\(targetName)'s Height Is \(businessCardHeight)
""")
}
检查了我们检测到的大小是否正确之后,我们就可以放置与此相关的任何内容.
Having checked that our detected size is accurate we can then place whatever content we like in relation to this.
创建程序SCNScene
而不是通过编程的方式来完成所需的结果,这是一种更简单的方法.
Rather than doing everything programatically, an easier way to achieve the results you are looking for is to create an SCNScene
.
更新:
在您请求示例项目时,我为每个人创建了一个完全可用的示例,可以在这里下载: ARKit名片
As you have asked for an example project I have created a fully working example for everyone which can be download here: ARKit Business Card
在没有详细介绍每个Class
的情况下,我将为您提供基本的详细信息.
Without going through every Class
in detail I will provide you with the basic details.
我们将使用SCNScene
作为可重用模板,其中包含一系列SCNNode
用作按钮,并且在按下按钮时可以执行不同的操作.
We will use an SCNScene
as a reusable template, which contains a range of SCNNode
which are used as buttons and which can perform different actions when they are pressed.
基本模板如下:
BusinessCard节点使用BusinessCardData Struct初始化,如下所示:
The BusinessCard Node is initialised with A BusinessCardData Struct which looks like so:
typealias SocialLinkData = (link: String, type: SocialLink)
/// The Information For The Business Card Node & Contact Details
struct BusinessCardData{
var firstName: String
var surname: String
var position: String
var company: String
var address: BusinessAddress
var website: SocialLinkData
var phoneNumber: String
var email: String
var stackOverflowAccount: SocialLinkData
var githubAccount: SocialLinkData
}
/// The Associates Business Address
struct BusinessAddress{
var street: String
var city: String
var state: String
var postalCode: String
var coordinates: (latittude: Double, longtitude: Double)
}
/// The Type Of Social Link
///
/// - Website: Business Website
/// - StackOverFlow: StackOverFlow Account
/// - GitHub: Github Account
enum SocialLink: String{
case Website
case StackOverFlow
case GitHub
}
因此,所提供的所有数据都映射到模板中的每个SCNNode
,并有助于执行必要的功能.
Whereby all the data provided is mapped to each SCNNode
in the template, and helps to perform the necessary functions.
通过使用struct
我们可以简单地创建多个交互式名片,例如:
By using a struct
we can create multiple interactive business cards simply e.g:
//--------------------------
//MARK: - ARSessionDelegate
//--------------------------
extension ViewController: ARSCNViewDelegate{
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//1. Check We Have A Valid Image Anchor
guard let imageAnchor = anchor as? ARImageAnchor else { return }
//2. Get The Detected Reference Image
let referenceImage = imageAnchor.referenceImage
//3. Load Our Business Card
if let matchedBusinessCardName = referenceImage.name, matchedBusinessCardName == "BlackMirrorz"{
//4. Create Our Business Card
let businessCardData = BusinessCardData(firstName: "Josh",
surname: "Robbins",
position: "Software Engineer",
company: "BlackMirrorz",
address: BusinessAddress(street: "1 Infinite Loop",
city: "Cupertino",
state: "CA",
postalCode: "95015",
coordinates: (latittude: 37.3349, longtitude: -122.0090201)),
website: SocialLinkData(link: "https://www.blackmirrorz.tech", type: .Website),
phoneNumber: "+821076337633",
email: "josh.robbins@blackmirroz.tech",
stackOverflowAccount: SocialLinkData(link: "https://stackoverflow.com/users/8816868/josh-robbins", type: .StackOverFlow),
githubAccount: SocialLinkData(link: "https://github.com/BlackMirrorz", type: .GitHub))
//5. Assign It To The Business Card Node
let businessCard = BusinessCard(data: businessCardData, cardType: .noProfileImage)
businessCardPlaced = true
node.addChildNode(businessCard)
}
}
}
由于设计已经完成,所以我们不需要进行任何复杂的计算.一切都为我们做好了!
Since the design is already laid out, we dont need to do any complex calculations. Everything is done for us!
用户交互使用以下图标完成:
Interaction by the user is done using the following icons:
-
StackOverFlow Button
会弹出一个WKWebView
幻灯片,以显示用户StackOverFlow
帐户. -
GitHub Button
会显示WKWebView
的幻灯片,以显示用户GitHub
帐户. -
Internet Button
会显示WKWebView
幻灯片,以显示用户网站. -
Phone Button
允许用户拨打商务电话号码. -
SMS Button
出现一个MFMessageComposeViewController
,允许用户向业务发送短信. -
Email Button
出现一个MFMailComposeViewController
,允许用户通过电子邮件发送业务. -
Contact Button
创建一个CNMutableContact
并将业务另存为用户设备上的新联系人. -
Location Button
会显示MKMapView
幻灯片,以显示用户的公司位置.
- The
StackOverFlow Button
presents a slide outWKWebView
to display the usersStackOverFlow
Account. - The
GitHub Button
presents a slide outWKWebView
to display the usersGitHub
Account. - The
Internet Button
presents a slide outWKWebView
to display the users website. - The
Phone Button
allows the user to call the Business Telephone Number. - The
SMS Button
presents anMFMessageComposeViewController
allowing the user to send a text message to the business. - The
Email Button
presents anMFMailComposeViewController
allowing the user to email the business. - The
Contact Button
creates aCNMutableContact
and saves the business as a new contact on the users device. - The
Location Button
presents a slide outMKMapView
to display the users Businesses Location.
由于将WKWebView
呈现为SCNMaterial
,因此我不得不寻找其他方法来使内容完全交互.
Since rendering a WKWebView
as an SCNMaterial
, I had to look at other ways to allow the content to be fully interactive.
因此,我利用了Jonkykong
的神话般的仓库SideMenu
,可以在这里找到:
SideMenu
As such I made use of the fabulous repository SideMenu
by Jonkykong
which is available here:
SideMenu
这使用户仍然可以体验ARKit
的同时,还可以实现几乎分屏的效果:
This allows the user to still experience ARKit
whilst allowing an almost split screen like effect:
一如既往,希望它能帮助您和其他对学习ARKit感兴趣的人...
As always, hope it helps you and everyone else who is interested in learning ARKit...
这篇关于AR参考图像平面在iOS Swift中的位置是否正确?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!