【Unity英特尔深度摄像头开发】 - 盘古CG网-新媒体交互圈论坛-摄像头识别-盘古CG网

【Unity英特尔深度摄像头开发】

起先

我之前写过®关于英特尔实感™ T265 的文章,但现在我已经有了 D435,这是一款深度摄像头,我想先概述一下摄像头和 Unity 示例。

tips.hecomi.com

演示

图像流

f:id:hecomi:20190602141621p:plain

f:id:hecomi:20190602200922g:plain

多边形和合并

f:id:hecomi:20190604003616g:plain

特征

D400 系列的类型

有三种类型的 D400 系列可供购买:D415D435 和 D435i。 两者都配备了 28nm Intel® RealSense™ D4 视觉处理器 (D4 VPU),可以在设备端执行深度计算。 D415 和 D435 分别是 D4 VPU 和 D410 / D430 深度模块的组合,封装为相机(似乎也只有一个模块)。 D435i 是 D435,增加了 BMI055,这是一款与 T265 一样小的 6 轴陀螺仪。

外观

宽度不到 10 厘米,相当紧凑。

f:id:hecomi:20190604233724j:plain

与各种深度相机相比,它看起来像这样(右前)。

f:id:hecomi:20190604234759j:plain

规范

  D415 系列 D435 系列
使用环境 室内/室外
深度法 主动红外立体
快门方式 卷帘快门 全局快门
传感器 (像素大小) 1.4 微米 x 1.4 微米 3 微米 x 3 微米
处理器 D4 VPU
模块 D410 系列 D430 系列
大小 99 毫米 x 20 毫米 x 23 毫米 90 毫米 x 25 毫米 x 25 毫米
连接器 USB Type-C
深度 Camella
分辨率 高达 1280 x 720
视场角 63.4° x 40.4° (+/- 3°) 85.2° x 58° (+/- 3°)
帧率 高达 90 fps
最小距离 0.16 米 0.11 米
最大距离 约 10 m(取决于环境)
RGB 摄像头
分辨率 1920 x 1080 像素
帧率 30 帧率 (fps
视场角 69.4° x 42.5° (+/- 3°) 69.4° x 42.5° (+/- 3°)

software.intel.com

D415 和 D435 都使用红外投影仪来照亮图案,并使用两个红外摄像头捕捉它以计算深度(主动红外立体)。 除了快门方式、最小距离和深度视角外,形状基本相同。 其他更详细的规格可以在 PDF 中找到。

英特尔®实感™ D400 系列 (DS5) 产品家族数据表

关于 SDK

software.intel.com

与 F200 问世时的英特尔®实感™ SDK 不同,英特尔®实感™ SDK 2.0 基本上只提供数据流,似乎不包含手部或面部攻击功能(问题 当我调查它时,据说有未来的计划……

tips.hecomi.com

如果您想使用识别系统的功能,您可以将获取的流传输到另一个识别器(例如 OpenCV)看来有必要流向这样的。

购买

您可以从官方网站购买,但也可以从 Switch Science 购买。

官方

开关科学

www.switch-science.com

环境

本文已在以下环境中进行了验证:

设置

硬件

首先,参考下面的官方 Get Started 设置设备

www.intelrealsense.com

开发工具包

从以下位置下载 RealSense SDK

www.intelrealsense.com

f:id:hecomi:20190526191042p:plain

f:id:hecomi:20190526191059p:plain

如果您已经将其安装在其他相机设置(例如 T265)上,请查看新版本。

作确认

如果您打开已安装的 SDK 中包含的 Intel RealSense Viewer,则可以检查摄像头的运行情况。

f:id:hecomi:20190526191204p:plain

同时,您可以检查深度和 RGB 相机。 您还可以看到,如上面的规格所示,视角是不同的。

f:id:hecomi:20190531234746g:平原

固件更新

SDK 发行说明列出了推荐的固件版本。

github.com

例如,在撰写本文时,最新的 SDK 2.21.0 需要高于 5.11.6.200 的固件

在 Viewer 中,单击 Info 查看设备详细信息查看设备的固件版本。 我的有一个 “!”,它似乎是比所需的最低版本更旧的固件。 您可以通过单击“!”或访问以下页面来安装固件更新所需的最新文件。 安装 bin 文件和更新程序.exe并从命令行更新到最新固件

downloadcenter.intel.com

downloadcenter.intel.com

打开下载的 .exe 并转到 1。 选择 Update Camera Devices(更新摄像头设备),选择要使用的实感(如果只有一个实感,则选择 1),然后输入下载.bin的路径并按 Enter。

f:id:hecomi:20190526233201p:plain

f:id:hecomi:20190526233316p:plain

f:id:hecomi:20190526233447p:plain

如果您从 RealSense 查看器中再次检查它,您可以看到它已被更新,因为之前显示的 “!” 已经消失,并且它是一个新版本(您也可以从命令行更新程序中检查它)。

f:id:hecomi:20190526233749p:plain

统一

我将从 GitHub 版本安装最新版本。

github.com

将其导入到新创建的项目中,打开 Assets > RealSenseSDK2.0 > Scenes > StartHere 场景,并验证您是否看到类似于以下内容的页面。

f:id:hecomi:20190527004501p:plain

现在,您可以开始使用 Unity。

关于用于实感 SDK 2.0 的 Unity 包装器

在深入研究这些示例之前,我们先快速了解一下 Unity Wrapper for RealSense SDK 2.0 的设计。 如果你读过前面的 T265 文章,你会想要刷新:组件是起点。 如果您在此处注册要从设备获取的信息,则可以将图像反映在 指定的 中。RsDeviceRsStreamTextureRendererTexture

RsDevice 设备

f:id:hecomi:20190317131654p:plain

Process Mode 允许您指定哪个线程处理实感提供的数据。 默认情况下,它是 ,在这种情况下,我们创建一个新的并处理飞入其中的事件(或者更准确地说,轮询和检索)。 时,它将在 Unity 的主线程上进行处理。 除非你想轻松使用 Unity 的 API,否则我认为这很好。FrameMultithreadedThreadFrameUntiy ThreadMultithreaded

Requested Serial Number 允许您在连接多个 RealSense 设备时通过指定其序列号来指定特定的 RealSense 设备。 您可以在 RealSense Viewer 或下面描述的组件中轻松找到序列号。RsDeviceInspector

Profiles 设置要检索的数据的定义。 是您要检索的流数,而 D435 支持 ,因此最多可以为 3。 每个元素都是 ,它指定流的类型、传入数据的格式(例如纹理)、帧速率和分辨率。 如果这里有不匹配,你会对 config 错误感到生气。SizeDepthIRColorRsVideoStreamRequestExternalException: rs2_pipeline_start_with_config

f:id:hecomi:20190602153901p:plain

Stream Index 对于只有一个(RGB 流或深度)的摄像机,指定 0 ~ N(对于两个或更多摄像机)(红外摄像机)。

顺便说一句,由于每个设备可以采用的流是固定的,因此我认为我希望有一个机制来多包装一点并为每个设备准备一个组件,或者一个机制来为组件提供配置文件。RsDeviceScriptableObjectRsDevice

RsStreamTextureRenderer (RSStream纹理渲染器)

f:id:hecomi:20190601233009p:plain

在大多数示例中,具有组件的游戏对象是具有 .RsStreamTextureRendererRsDevice

f:id:hecomi:20190602142045p:plain

RsDeviceInspector

RsDeviceInspector 如果将组件附加到与 相同的游戏对象,则可以检查和调整该设备的设置。 您也可以在此处找到序列号。RsDevice

f:id:hecomi:20190602163752p:plain

让我们看一下可用的每个示例,让您了解 D435 可以做什么。

UI 图像示例(深度和颜色)

执行时,将启动场景。 这是一个示例,分别显示上述两个中的深度图像和 RGB 相机图像。TexturesDepthAndColorCanvasRawImage

f:id:hecomi:20190602153024p:plain

f:id:hecomi:20190602141621p:plain

正如我在上一节中所写的,使用 、 和 指定要从设备中获取的流 (、 、 ) 深度的着色是在着色器中完成的。 您可以使用滑块指定要着色的深度范围。 此外,如果在下拉菜单中指定其他类型(如 ),则颜色方案将更改。RsDeviceDepthIRColorJetInferno

f:id:hecomi:20190602143420p:plain

这是一张如下图所示的图像,大小为 256 * 8 px,颜色图类型为 v(值为 0 ~ 7),值将滑块指定的范围标准化(图像在此处放大,以便于查看)。

f:id:hecomi:20190602152847p:plain

如果您查看 Assets/RealSenseSDK2.0/Shaders/Depth.shader 的相关部分,它如下所示(为清楚起见,一些代码已被修改):

half4 frag (v2f_img pix) : SV_Target
{
    // デプスの範囲を ushort から meter に変換
    float z = tex2D(_MainTex, pix.uv).r * 0xffff * _DepthScale;

    // スライダで指定された値に正規化
    z = (z - _MinRange) / (_MaxRange - _MinRange);
    if (z <= 0) return 0;

    // カラーマップの選択(0 ~ 7)
    float2 v = 1 - (_Colormap + 0.5) * _Colormaps_TexelSize.y;

    // カラーマップから色をサンプリング
    return tex2D(_Colormaps, float2(z, v));
}

UI 图像样本(深度 & IR)

在上一个示例中,显示了彩色图像,但这是显示 IR 图像的示例。

f:id:hecomi:20190602154732p:plain

由于它是一种主动方法,因此您可以看到照射模式。 获取 IR 图像的方法是使用 设置 IR 流,然后使用 将其流向指定的纹理。RsDeviceRsStreamTextureRenderer

f:id:hecomi:20190602165950p:plain

f:id:hecomi:20190602155729p:plain

检索所有纹理

虽然它不在示例中,但让我们看看如何将所有纹理引入此流程中。 设置如下:RsDevice

f:id:hecomi:20190602170921p:plain

由于有两个 IR 相机,因此通过分别指定 Indices 1 和 2 来分隔左侧和右侧。 如果还有 4 个纹理,并将它们设置为流入 中的每个字段,则会获得 4 个纹理,如下所示:RsStreamTextureRendererCanvasRawImagetexture

f:id:hecomi:20190602171122p:plain

点云处理块 (深度)

接下来是点。 你可以四处移动它。

F:id:hecomi:20190602182218g:平原

此外,您还可以在 UI 上指定参数,例如填充深度孔洞。

f:id:hecomi:20190602182627p:plain

有点复杂,所以我会一步一步地解释它。

如何绘制

使用组件更新网格,如下所示:RsPointCloudRenderer

  1. Drawing 在运行时生成一个网格(32 位索引格式),其顶点等于深度纹理中的像素
  2. 将要提供的数据 () 排队(在单独的线程中)Frame
  3. LateUpdate() 在计时 (主线程) 从队列中检索数据
  4. Intel.RealSense.Points.CopyVertices() 复制到 中生成的网格的顶点
  5. 从 2. 重复

使用特殊材质 () 绘制此网格。 在此材质的着色器 () 中,几何着色器描述了将每个顶点转换为方形多边形的过程。PointCloudMatCustom/PointCloudGem

顺便说一句,你看不到命名空间中函数的实现,因为它们是 Unity 项目中预构建的 DLL。 不过,代码可以在 GitHub 上找到,所以如果你想了解里面的过程,可以直接查看 GitHub 来查看。 它主要通过类与本机 DLL (librealsense.dll) 交互。Intel.RealSense.Points.CopyVertices()Intel.RealSenseNativeMethods

github.com

RsProcessingBlock (RS处理块)

如何绘制 2. 中描述的“提供的数据”不是直接取自 ,而是通过名为 的组件获取的。 此组件可以指定继承自 的参数对象。RsDeviceRsProcessingPipeRsProcessingProifleScriptableObject

f:id:hecomi:20190602192309p:plain

f:id:hecomi:20190602192320p:plain

RsProcessingProfile 如上所示,您可以注册从 multiple 继承的类的实例。 这些块导致本机实现者能够将帧数据作为后处理处理。 具体来说,它是一个过滤过程,它变薄 (),限制特定区域 () 之间的值,并填充孔 ()。RsProcessingBlockRsDecimationFilterRsThreasholdFilterRsHoleFillingFilter

github.com

这将采用示例的形式,您可以在摆弄 UI 时检查该示例。

点云采样(颜色)

这会将 RGB 相机中的颜色添加到上一个样本中。

f:id:hecomi:20190602200922g:plain

基本上,网格是由与以前相同的机制生成的,并且纹理是使用 传递给材质的。 然而,正如我们在UI图像样本(深度和颜色)样本中看到的,RGB摄像头的视角和深度是不同的,所以我们必须对此做些什么。RsPointCloudRendererRsStreamTextureRenderer

为了匹配视角,会生成一个名为 的纹理,该纹理将获取 RGB 相机纹理数据的坐标。 点网格的每个顶点都通过此纹理转换其自己的 UV,并使用生成的 UV 对 RGB 图像进行采样。RsPointCloudRendereruvmap

f:id:hecomi:20190603001637p:plain

fixed4 frag (g2f i) : SV_Target
{
    float2 uv = tex2D(_UVMap, i.uv);
    if(any(uv <= 0 || uv >= 1)) discard;
    uv += 0.5 * _MainTex_TexelSize.xy;
    return tex2D(_MainTex, uv) * _Color;
}

这可以通过 从数据中检索。 那么这张地图是从哪里来的呢?_UVMapFrameIntel.RealSense.Points.TextureData

RsPointCloud 云

其中一个块称为 。RsPointCloud

f:id:hecomi:20190603225045p:plain

如果您想使用点,请在您的个人资料中注册此块。 这有点深,但如果你看一下实现,似乎这个块提供了地图。

首先,在 C# 方面,该块生成并执行 . 实现如下:Intel.RealSense.PointCloudPointCloud.Process()PointCloud

我们传递给 ,这是基类。 这是一个函数,它返回一个指针,该指针指向在本机端 (C++) 创建的类的实例。 对于其他块,有一些类(例如 和 e.g.)执行每个类,并且它们都继承了两者之间的一些继承,但它们继承自 .ProcessingBlockNativeMethods.rs2_create_pointcloud()pointcloudhole_filling_filterdecimation_filterrs2::processing_block

返回到 C#。 此类进一步将此给定指针传递给基以保存它。ProcessingBlockBase.Object

此类能够不时调用 C++,并通过 . 通过这种方式,通过各种路由进行调用。 在其中,执行 ,其中计算 map。Base.ObjectDispose()deleteHandleHandlepointcloud::process_frame()pointcloud::process_depth_frame()pointcloud::get_texture_map()

在其中,将深度纹理的 3D 坐标映射到 RGB 坐标上的 2D 平面坐标。 看起来这个缓冲区被转换成了可以在 Unity 中使用的纹理,然后传递给着色器来为着色器中的每个点着色。rs2_project_point_to_pixel()Texture2D.LoadRawTextureData()

对齐样本(深度和颜色)

这是将彩色图像与深度图像或深度图像与彩色图像进行匹配的示例。

f:id:hecomi:20190603232021g:plain

这可以通过添加 .ProcessingBlockRsAlign

f:id:hecomi:20190603232601p:plain

此外,使用 不是 ,而是 而不是 ,并检索处理后的结果。RsStreamTextureRendererSourceRsDeviceRsProcessingPipe

f:id:hecomi:20190603232753p:plain

该机制与我们之前看到的相同,在 C++ 端调用本机函数。RsPointCloud

深度分割(深度和颜色)

2D 图像中 z 距离处的分割样本。

f:id:hecomi:20190603234326g:plain

与以前一样,颜色和深度图像是分层的。 前景中的 RGB 纹理面是使用应用了着色器的材质绘制的,这是在着色器中使用滑块给定的值来提取具有 Alpha 的特定 Z 内的颜色。RsAlignCustom/BgSegsmoothstep

f:id:hecomi:20190604002721p:plain

AR 背景(深度和颜色)

最后一个是与一个相当有趣的多边形对象合并的示例。

f:id:hecomi:20190604003616g:plain

该机制是使用 将深度 camela 图像写入深度缓冲区。 深度纹理与彩色图片对齐,颜色纹理使用相同的方法绘制。CommandBufferRsAlignCommandBuffer

CommandBuffer 如果您不了解它,请阅读以下文章。

tips.hecomi.com

另外,我之前对 Deferred 做了类似的机制,所以我会发布它。

tips.hecomi.com

让我们看一下代码。

public class RsARBackgroundRenderer : MonoBehaviour
{
    ...
    IEnumerator Start()
    {
        ...
        // 背景を描画する UnityEngine.XR.ARBackgroundRenderer の生成
        // 指定したマテリアル・テクスチャが背景に表示されるようになる
        bg = new ARBackgroundRenderer()
        {
            backgroundMaterial = material,
            mode = ARRenderMode.MaterialAsBackground,
            backgroundTexture = material.mainTexture
        };

        // デプスカメラとポリゴンオブジェクトのマージ用にデプスバッファを生成するようにする
        cam.depthTextureMode |= DepthTextureMode.Depth;

        // デプスバッファにデプスカメラから得たテクスチャを書き込むカメラのコマンドバッファを作成する
        // また、コピー用のシャドウマップをクリア(白で塗りつぶす)しておく
        var updateCamDepthTexture = new CommandBuffer() { name = "UpdateDepthTexture" };
        updateCamDepthTexture.Blit(BuiltinRenderTextureType.None, BuiltinRenderTextureType.CurrentActive, material);
        updateCamDepthTexture.SetGlobalTexture("_ShadowMapTexture", Texture2D.whiteTexture);
        cam.AddCommandBuffer(CameraEvent.AfterDepthTexture, updateCamDepthTexture);

        // ディレクショナルライトを取ってくる(最低 1 つあることを前提にしている)
        var light = FindObjectOfType<Light>();

        // シャドウマップをコピーする
        var copyScreenSpaceShadow = new CommandBuffer { name = "CopyScreenSpaceShadow" };
        int shadowCopyId = Shader.PropertyToID("_ShadowMapTexture");
        copyScreenSpaceShadow.GetTemporaryRT(shadowCopyId, -1, -1, 0);
        copyScreenSpaceShadow.CopyTexture(BuiltinRenderTextureType.CurrentActive, shadowCopyId);
        copyScreenSpaceShadow.SetGlobalTexture(shadowCopyId, shadowCopyId);
        light.AddCommandBuffer(LightEvent.AfterScreenspaceMask, copyScreenSpaceShadow);
    }
    ...
    void Update()
    {
        ...
        // スクリーンサイズが変化してるか調べる
        var s = new Vector2Int(Screen.width, Screen.height);
        if (screenSize == s) return;
        screenSize = s;

        // 変化していたらプロジェクション行列を作って書き換え
        var projectionMatrix = new Matrix4x4
        {
            m00 = intrinsics.fx,
            m11 = -intrinsics.fy,
            m03 = intrinsics.ppx / intrinsics.width,
            m13 = intrinsics.ppy / intrinsics.height,
            m22 = (cam.nearClipPlane + cam.farClipPlane) * 0.5f,
            m23 = cam.nearClipPlane * cam.farClipPlane,
        };
        float r = (float)intrinsics.width / Screen.width;
        projectionMatrix = Matrix4x4.Ortho(
            0, 
            Screen.width * r, 
            Screen.height * r, 
            0, 
            cam.nearClipPlane, 
            cam.farClipPlane) * projectionMatrix;
        projectionMatrix.m32 = -1;

        // なのでインスペクタ上の FOV などのカメラパラメタは効かなくなる
        cam.projectionMatrix = projectionMatrix;
    }
    ...
}

UnityEngine.XR.ARBackgroundRenderer 您可以轻松地将彩色图片转换为背景或将其关闭。 使用 this 显示背景。

docs.unity3d.com

此外,不仅在相机中注册命令缓冲区以写入深度相机值,还准备了光线的命令缓冲区,以便阴影下降并检索阴影贴图,并注册要注册的命令。 摄像机的命令缓冲区绘制的着色器如下所示:_ShadowMapTextureSetGlobalTexture()

fixed4 frag (v2f i, out float depth : DEPTH) : SV_Target
{
    ...
    float d = tex2D(_DepthTex, i.uv);
    d = d * 0xffff * 0.001;
    depth = LinearEyeDepth(d);
    ...
    float4 col = tex2D(_MainTex, i.uv);
    ...
    col *= tex2D(_ShadowMapTexture, i.uv1);

    return col;
}

如果您使用 frame debugger 查看流程,则更容易理解。

f:id:hecomi:20190604231304g:plain

结论

英特尔 我研究了实感 D435(D400 系列)。 与上一代相比,有一个缺点,即由于缺乏识别系统,仅使用 RealSense SDK 创建交互变得困难,但创建结合深度和颜色图像的表达式变得非常容易。 特别是在你想使用深度创建表达式的情况下,我认为能够用一根电缆和非常小的设备在硬件方面进行各种处理的优势非常大。 它还支持多种不同类型的 RealSense,因此将其与 T265(参考)结合使用可能会很有趣。 我想在下一个应用程序部分尝试各种事情。

请登录后发表评论

    没有回复内容