ARKit示例 - 第2部分:平面检测+可视化

ARKit - 检测到平面并映射到楼层

在我们的第一个hello world ARKit应用程序中,我们设置了我们的项目并渲染了一个虚拟3D立方体,可以在现实世界中渲染并在您移动时进行跟踪。

在本文中,我们将介绍从现实世界中提取3D几何体并对其进行可视化。检测几何对于增强现实应用程序非常重要,因为如果您希望能够感觉到您正在与现实世界交互,则需要知道用户点击了桌面,或者正在查看地板或其他表面获得逼真的3D互动。一旦我们在本文中完成了平面检测,在以后的文章中我们将使用它们将虚拟对象放置在现实世界中。

ARKit可以检测水平平面(我怀疑未来ARKit将检测更复杂的3D几何,但我们可能不得不等待深度感应相机,iPhone8可能......)。一旦我们检测到一个平面,我们就会将其可视化以显示平面的比例和方向。要查看所有内容,请观看以下视频:

youtube

注意:假设您跟随​​此处的代码:https://github.com/markdaws/arkit-by-example/tree/part2

计算机视觉概念

在我们深入研究代码之前,从高层次了解ARKit幕后发生的事情是很有用的,因为这项技术并不完美,并且在某些情况下它会崩溃并影响应用程序的性能。

增强现实的目的是能够在特定点将虚拟内容插入到现实世界中,并在您在现实世界中移动时拥有虚拟内容轨道。使用ARKit,其基本过程包括从iOS设备相机读取视频帧,为每个帧处理图像并提取特征点。功能可以很多,但您想尝试检测图像中可以跨多个帧跟踪的有趣功能。一个特征可能是一个物体的角落或纹理的一块织物的边缘等。有很多方法可以生成这些特征,你可以在网上阅读更多内容(例如搜索SIFT)但是为了我们的目的它已经足够了要知道每个图像提取的特征很多,可以唯一识别。

获得图像的功能后,您可以跟踪多个帧的功能,当用户在世界各地移动时,您可以获取这些相应的点并估计3D姿势信息,例如当前摄像机位置和位置。特征。当用户移动更多并且我们获得越来越多的功能时,这些3D姿势估计会得到改善。

对于平面检测,一旦您在3D中有许多特征点,您就可以尝试将平面拟合到这些点,并在比例,方向和位置方面找到最佳匹配。ARKit不断分析3D特征点,并在代码中报告它找回给我们的所有平面。

下面是我手机上看到沙发扶手的截图。正如您所看到的那样,面料具有良好的质感,可以跟踪的大量有趣和独特的功能,每个交叉都是ARKit发现的独特功能。

ARKit特征点提取 - 沙发面料

下一张照片是我拿走冰箱门的照片,注意点数不多:

ARKit特征提取 - 冰箱门反射的不良匹配

这很重要,因为ARKit要检测功能,您必须查看具有许多有趣功能的内容。导致特征提取不良的事情是:

  1. 光线  不足 - 光线不足或光线太强,镜面高光闪烁。尽量避免光线不足的环境。
  2. 缺乏纹理  - 如果你将你的相机指向白墙,那么提取真的没什么特别的,ARKit将无法找到或跟踪你。尽量避免看到纯色,有光泽的表面等区域。
  3. 快速移动  - 这对于ARKit来说是主观的,通常如果您只使用图像来检测和估计3D姿势,如果您移动相机太快,最终会出现模糊图像,导致跟踪失败。然而,ARKit使用称为视觉 - 惯性测距的东西以及图像信息ARKit使用设备运动传感器来估计用户转向的位置。这使得ARKit在跟踪方面非常强大。

在另一篇文章中,我们将测试不同的环境,以了解跟踪的执行情况。

添加调试可视化

在我们开始之前,向应用程序添加一些调试信息是有用的,即渲染从ARKit报告的世界原点,然后渲染ARKit检测到的特征点,这将有助于让您知道您所在的区域是跟踪好与否。为此,我们可以打开ARSCNView实例上的调试选项:

self.sceneView.debugOptions = 
  ARSCNDebugOptionShowWorldOrigin | 
  ARSCNDebugOptionShowFeaturePoints;

检测平面几何

在ARKit中,您可以通过在会话配置对象上设置planeDetection属性来指定要检测水平平面。此值可以设置为ARPlaneDetectionHorizo​​ntal或ARPlaneDetectionNone。

设置该属性后,您将开始获取回调以委派ARSCNViewDelegate协议的方法。那里有很多方法,我们首先使用的是:

/**
Called when a new node has been mapped to the given anchor.
@param renderer The renderer that will render the scene.
@param node The node that maps to the anchor.
@param anchor The added anchor.
*/
- (void)renderer:(id <SCNSceneRenderer>)renderer
      didAddNode:(SCNNode *)node
       forAnchor:(ARAnchor *)anchor {
}

每次ARKit检测到它认为是新平面时,都会调用此方法。我们得到两条信息,节点和锚点。SCNNode实例是ARKit创建的SceneKit节点,它有一些属性设置,如方向和位置,然后我们得到一个锚实例,这告诉我们使用有关已找到的特定锚的更多信息,例如大小和中心飞机

锚实例实际上是一个ARPlaneAnchor类型,从中我们可以得到飞机的范围和中心信息。

渲染飞机

通过上述信息,我们现在可以在虚拟世界中绘制SceneKit 3D平面。为此,我们创建一个继承自SCNNode 的Plane类。在构造函数方法中,我们创建平面并相应地调整它的大小:

// Create the 3D plane geometry with the dimensions reported
// by ARKit in the ARPlaneAnchor instance
self.planeGeometry = [SCNPlane planeWithWidth:anchor.extent.x height:anchor.extent.z];
SCNNode *planeNode = [SCNNode nodeWithGeometry:self.planeGeometry];
// Move the plane to the position reported by ARKit
planeNode.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z);
// Planes in SceneKit are vertical by default so we need to rotate
// 90 degrees to match planes in ARKit
planeNode.transform = SCNMatrix4MakeRotation(-M_PI / 2.0, 1.0, 0.0, 0.0);
// We add the new node to ourself since we inherited from SCNNode
[self addChildNode:planeNode];

现在我们有了我们的Plane类,回到ARSCNViewDelegate回调方法,我们可以在ARKit报告新的Anchor时创建我们的新平面:

- (void)renderer:(id <SCNSceneRenderer>)renderer 
      didAddNode:(SCNNode *)node 
       forAnchor:(ARAnchor *)anchor {
  if (![anchor isKindOfClass:[ARPlaneAnchor class]]) {
    return;
  }
  Plane *plane = [[Plane alloc] initWithAnchor: (ARPlaneAnchor *)anchor];
  [node addChildNode:plane];
}

注意:在实际代码中,我还在SCNPlane几何体上设置了网格材质,以使可视化看起来更好,为简洁起见,我在此省略了该代码。

更新飞机

如果您运行上面的代码,当您四处走动时,您将看到虚拟世界中渲染的新平面,但是当您四处移动时,平面无法正常生长。ARKit一直在分析场景,因为它发现一个平面比它认为更新平面范围值更大/更小。所以我们还需要更新Plane SceneKit已经渲染。

我们从另一个ARSCNViewDelegate方法获取此更新信息:

- (void)renderer:(id <SCNSceneRenderer>)renderer 
   didUpdateNode:(SCNNode *)node 
       forAnchor:(ARAnchor *)anchor {
  // See if this is a plane we are currently rendering
  Plane *plane = [self.planes objectForKey:anchor.identifier];
  if (plane == nil) {
    return;
  }
  [plane update:(ARPlaneAnchor *)anchor];
}

在我们的Plane类的更新方法中,我们然后更新平面的宽度和高度:

- (void)update:(ARPlaneAnchor *)anchor {
  self.planeGeometry.width = anchor.extent.x;
  self.planeGeometry.height = anchor.extent.z;
  // When the plane is first created it's center is 0,0,0 and 
  // the nodes transform contains the translation parameters. 
  // As the plane is updated the planes translation remains the 
  // same but it's center is updated so we need to update the 3D
  // geometry position
  self.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z);
}

现在我们已经进行了平面渲染和更新,我们可以看一下应用程序。我在SCNPlane几何体中添加了Tron样式的网格纹理,我在这里省略了它,但你可以查看源代码。

提取结果

下面是我走过房子的一部分,从上面的视频中检测到的飞机的一些屏幕截图:

这是我厨房里的一个小岛,ARKit很好地找到了范围并正确定位了飞机以匹配凸起的表面

image.png

这是一个看着地板的镜头,你可以看到当你移动ARKit时不断出现新的飞机,这很有趣,因为如果你正在开发一个应用程序,用户首先必须在一个空间中移动才能放置内容,当几何形状足够好使用时,为用户提供良好的视觉线索是很重要的。

image.png

下面是与上面相同的场景,但几秒钟后,ARKit将所有上述平面合并到一个平面上。请注意,在ARSCNViewDelegate回调中,您必须处理ARKit在合并平面时删除了ARPlaneAnchor实例的情况。

image.png

这很有意思,因为我站在这个地板上方,距离它大约12到15英尺,在光线条件不佳的情况下,ARKit仍设法在那个距离上提取飞机,令人印象深刻!

image.png

这是一架在楼梯旁边的小墙顶上提取的飞机。注意平面如何延伸超过实际曲面的边缘。

image.png

识别外卖

以下是我从平面检测中发现的一些观点:

  1. 不要指望一架飞机与一个表面完美对齐,正如你从视频中看到的那样,飞机被检测到但是方向可能已经关闭,所以如果你正在开发一个AR应用程序,它想让几何图形真正准确效果你可能会失望。
  2. 边缘不是很好,以及对齐平面的实际延伸有时太小或太大,不要试图制作一个需要完美对齐的应用程序
  3. 跟踪非常强大且快速。正如你所看到的那样,当我绕着飞机走动到真实的世界时,我正在快速地移动相机并且看起来仍然很棒
  4. 特征提取让我印象深刻,即使在低光照和距离大约12-15英尺的距离下,ARKit仍然会提取一些平面。

下一个

在下一篇文章中,我们将使用这些平面开始在现实世界中放置3D对象,并且还可以更多地了解应用程序的对齐。

原文:https://blog.markdaws.net/arkit-by-example-part-2-plane-detection-visualization-10f05876d53 作者:Mark Dawson

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CDA数据分析师

【零一】#操作教程贴#从0开始,教你如何做数据分析#中阶#第八篇

大家好,我是零一。第一次用手机写文章,哈。在车上的时间看了一本书,余下的时间,我想应该可以写一篇文章。图片等到了地儿了,再用电脑补上。 我的公众微信号是sta...

21850
来自专栏企鹅号快讯

现在做 Web 全景合适吗?

Web 全景在以前带宽有限的条件下常常用来作为街景和 360° 全景图片的查看。它可以给用户一种 self-immersive 的体验,通过简单的操作,自由的查...

44980
来自专栏PPV课数据科学社区

一行R代码来实现繁琐的可视化

本文作者: 唐源,目前就职于芝加哥一家创业公司,曾参与和创作过多个被广泛使用的 R 和 Python 开源项目,是 ggfortify,lfda,metric-...

46050
来自专栏AI科技评论

学界 | Facebook AI实验室开源相似性搜索库Faiss:性能高于理论峰值55%,提速8.5倍

在用户日常搜索过程中,一个经常出现的问题即大多数返回的网站结果拥有完全相同或者几乎一样的信息。而应用了相似性搜索的相似引擎即可为用户返回最恰当、最合适的结果,同...

549100
来自专栏大数据挖掘DT机器学习

利用 Python、SciKit 和文本分类来构建客户行为描述模型

了解如何根据已购买产品中描述的文本属性来构建客户行为描述模型。SciKit 是一个强大的基于 Python 的机器学习包,可用于模型构造和评估,您可以利用它...

34750
来自专栏PPV课数据科学社区

“数学之美”系列九:如何确定网页和查询的相关性

[我们已经谈过了如何自动下载网页、如何建立索引、如何衡量网页的质量(Page Rank)。我们今天谈谈如何确定一个网页和某个查询的相关性。了解了这四个方面,一个...

31450
来自专栏ATYUN订阅号

AI算法可以通过脑电图读数检测睡眠障碍模式

脑电图(EEG)使用放置在头皮上的电极测量大脑中的电活动。睡眠专家可利用它来诊断和评估神经系统疾病,这可能是一项繁琐的工作,需要在数小时记录的大脑活动中注释峰值...

11830
来自专栏灯塔大数据

干货|一步步用python制作游戏外挂

玩过电脑游戏的同学对于外挂肯定不陌生,但是你在用外挂的时候有没有想过如何做一个外挂呢?(当然用外挂不是那么道义哈,呵呵),那我们就来看一下如何用Python来制...

880120
来自专栏机器学习算法与Python学习

干货 | 自然语言处理(5)之英文文本挖掘预处理流程

前言 自然语言处理(4)之中文文本挖掘流程详解(小白入门必读) 干货 | 自然语言处理(3)之词频-逆文本词频(TF-IDF)详解 干货 | 自然语言处理(...

901120
来自专栏机器学习算法原理与实践

英文文本挖掘预处理流程总结

    在中文文本挖掘预处理流程总结中,我们总结了中文文本挖掘的预处理流程,这里我们再对英文文本挖掘的预处理流程做一个总结。

16220

扫码关注云+社区

领取腾讯云代金券