realsense2在开发中的一些方法(深度帧与其对应的颜色帧对齐示例)

#rs-align Sample

##概述

此示例演示了`rs2 :: align`对象的用法,该对象允许用户在深度和其他一些流(投影方式)之间进行对齐,反之亦然。点击

对齐实用程序基于所提供的深度数据执行每像素几何变换,并且不适合于对齐本质上为2D的图像,例如颜色,IR或鱼眼。此外,转换需要进行未失真(整流)的图像,因此不适用于IR校准流。


在此示例中,我们将深度帧与其对应的颜色帧对齐。
我们生成一个大小为彩色流的新帧,但内容是在颜色传感器坐标系中计算的深度数据。换句话说,使用颜色传感器的原点和尺寸来重建“捕获”的深度图像。
然后,我们使用原始颜色和重新投影的深度帧(在此阶段对齐)来确定每个颜色像素的深度值。

使用此信息,我们“移除”比某些用户定义的距离更远(远离相机)的颜色框的背景。

该示例显示用于控制从原始彩色图像显示的最大距离的GUI。

##预期输出

应用程序应打开一个窗口并显示来自摄像头的视频流。

<p align =“center”> <img src =“https://raw.githubusercontent.com/wiki/IntelRealSense/librealsense/res/align-expected.gif”alt =“screenshot gif”/> </ p>

该窗口应具有以下元素:
- 在窗口的左侧是一个垂直滑块,用于控制深度剪切距离。
- 带有灰色背景的彩色图像
- 相应的(彩色)深度图像。

##代码概述

与任何SDK应用程序一样,我们包括英特尔实感跨平台API:

```CPP
#include <librealsense2 / rs.hpp>
```

在这个例子中,我们还将使用`example.hpp`的辅助库:

```CPP
#include“../ example.hpp”
```

`examples.hpp`让我们可以轻松打开一个新窗口并准备渲染纹理。

我们还包含2个头文件,它们将帮助我们在窗口应用程序中呈现GUI控件:

```CPP
#include <imgui.h>
#include“imgui_impl_glfw.h”
```

这些头文件是我们用于呈现GUI元素的[ImGui](https://github.com/ocornut/imgui)库的一部分。

接下来,我们声明三个函数来帮助代码看起来更清晰:
```CPP
void render_slider(rect location,float&clipping_dist);
void remove_background(rs2 :: video_frame&color,const rs2 :: depth_frame&depth_frame,float depth_scale,float clipping_dist);
float get_depth_scale(rs2 :: device dev);
rs2_stream find_stream_to_align(const std :: vector <rs2 :: stream_profile>&streams);
bool profile_changed(const std :: vector <rs2 :: stream_profile>&current,const std :: vector <rs2 :: stream_profile>&prev);
```

`render_slider(..)`是所有GUI代码的所在,我们不会在本概述中介绍此函数。

`remove_background(..)`获取深度和彩色图像(假设彼此对齐),深度比例单位和用户希望显示的最大距离,并更新颜色框以使其背景(任何深度距离大于允许的最大值的像素被移除。

`get_depth_scale(..)`尝试从管道设备中找到深度传感器并检索其深度比例单位。

`find_stream_to_align(..)`遍历给定的流并验证它是否具有深度剖面并尝试找到另一个深度应该对齐的剖面。

`profile_changed()`检查当前的流配置文件是否包含前一个流配置文件。


前往'main`:

我们首先定义一些变量,用于显示窗口并将图像和GUI渲染到屏幕:

```CPP
//创建并初始化GUI相关对象
窗口应用程序(1280,720,“CPP - 对齐示例”); //简单的窗口处理
ImGui_ImplGlfw_Init(app,false); // ImGui图书馆初始化
rs2 :: colorizer c; //帮助着色深度图像
纹理渲染器; //帮助渲染图像
```

接下来,我们定义一个`rs2 :: pipeline`,它是使用RealSense深度相机的顶级API。
`rs2 :: pipeline`自动从所有连接的摄像机中选择一个摄像头,所以我们可以简单地调用`pipeline :: start()`并配置摄像头并进行流式传输:

```CPP
//创建管道以轻松配置和启动摄像头
rs2 ::管道管道;
//调用管道的start()而不使用任何其他参数将启动第一个设备
//使用默认流。
// start函数返回管道用于启动设备的管道配置文件
rs2 :: pipeline_profile profile = pipe.start();
```

在程序的这一点上,配置摄像机并且可以从管道获得流。

在实际使用帧之前,我们尝试获取深度相机的深度比例单位。深度比例单位用于将深度像素数据(16位无符号)转换为公制单位。

```CPP
//每个深度相机可能有不同的深度像素单位,所以我们在这里得到它
//使用管道的配置文件,我们可以检索管道使用的设备
float depth_scale = get_depth_scale(profile.get_device());
```

这些单位表示为对应于深度值1的深度米。例如,如果我们有一个值为2且深度比例单位为0.5的深度像素,那么该像素距离是2 x 0.5 = 1米。相机。

然后,我们创建一个`align`对象:

```CPP
// Pipeline可以选择没有颜色流的设备
//如果没有颜色流,请选择将深度与另一个流对齐
rs2_stream align_to = find_stream_to_align(profile.get_streams());

//创建一个rs2 :: align对象。
// rs2 :: align允许我们执行深度帧与其他帧的对齐
//“align_to”是我们计划对齐深度帧的流类型。
rs2 :: align align(align_to);
```

`rs2 :: align`是一个实用程序类,它执行2帧的图像对齐(注册)。
基本上,来自第一图像的每个像素将被变换,使得它与第二图像中的对应像素匹配。
`rs2 :: align`对象在两个输入图像之间进行转换,从源图像到使用`align_to`参数指定的某个目标图像。

现在是应用程序的有趣部分。我们启动主循环,仅在窗口关闭时才会中断:


```CPP
while(app)//应用程序还活着吗?
{
```

在循环中,我们要做的第一件事是阻塞程序,直到`align`对象返回`rs2 :: frameset`。 `rs2 :: frameset`是一个对象,它包含一组帧并提供一个用于轻松访问它们的接口。

```CPP
  //使用对齐对象,我们阻止应用程序,直到框架集可用
  rs2 :: frameset frameset = pipe.wait_for_frames();
```

从`wait_for_frames`返回的`frameset`应该包含一组对齐的帧。如果获取帧时出错,则可能抛出异常,但如果管道设法使用新设备重新配置自身,它将执行此操作并从新设备返回帧。
在接下来的行中,我们检查管道是否切换了它的设备,如果是,则更新对齐对象和样本所需的其余对象。

```CPP
// rs2 :: pipeline :: wait_for_frames()可以替换设备错误或断开连接时使用的设备。
//由于rs2 :: align将深度与其他某些流对齐,我们需要确保未更改流
//在调用wait_for_frames()之后;
if(profile_changed(pipe.get_active_profile()。get_streams(),profile.get_streams()))
{
    //如果配置文件已更改,请更新对齐对象,并获取新设备的深度比例
    profile = pipe.get_active_profile();
    align_to = find_stream_to_align(profile.get_streams());
    align = rs2 :: align(align_to);
    depth_scale = get_depth_scale(profile.get_device());
}
```

此时`align`对象是有效的,并且能够将深度帧与其他帧对齐。

```CPP
    //获取经过处理的对齐框
    auto processed = align.process(frameset);

    //尝试同时获取颜色和对齐的深度帧
    rs2 :: video_frame other_frame = processed.first_or_default(align_to);
    rs2 :: depth_frame aligned_depth_frame = processed.get_depth_frame();

    //如果其中一个不可用,请继续迭代
    if(!aligned_depth_frame ||!other_frame)
    {
        继续;
    }
```
请注意,颜色框的类型为`rs2 :: video_frame`,深度框的类型为`rs2 :: depth_frame`(源自`rs2 :: video_frame`并添加了特殊的深度相关功能)。

在获得两个对齐的颜色和深度帧之后,我们调用`remove_background(..)`函数从彩色图像中去除背景。
这是一个简单的函数,它执行一个天真的背景分割算法。
```CPP
    //将两个帧传递给remove_background,以便“剥离”背景
    //注意:在此示例中,我们更改颜色框的缓冲区,而不是复制它并更改副本
    //在实际应用中不建议使用此行为,因为颜色框可以在其他地方使用
    remove_background(color_frame,aligned_depth_frame,depth_scale,depth_clipping_distance);

```

循环的其余部分包含负责渲染和GUI控件的代码。我们不会详细说明。让我们来看看`remove_background(..)`:


```CPP
void remove_background(rs2 :: video_frame&other_frame,const rs2 :: depth_frame&depth_frame,float depth_scale,float clipping_dist)
{
```

在函数的开头,我们采用指向两个帧的原始缓冲区的指针,这样我们就可以改变彩色图像(而不是创建一个新的缓冲区)。

``CPP
    const uint16_t * p_depth_frame = reinterpret_cast <const uint16_t *>(depth_frame.get_data());
    uint8_t * p_other_frame = reinterpret_cast <uint8_t *>(const_cast <void *>(other_frame.get_data()));
```

接下来,我们遍历帧的每个像素。

```CPP
    //使用OpenMP尝试并行化循环
    #pragma omp parallel for schedule(动态)
    for(int y = 0; y <height; y ++)
    {
        auto depth_pixel_index = y * width;
        for(int x = 0; x <width; x ++,++ depth_pixel_index)
        {
```
计算该像素的深度距离:
```CPP
            //获取当前像素的深度值
            auto pixels_distance = depth_scale * p_depth_frame [depth_pixel_index];

```

如果该距离无效(`pixels_distance <= 0.f`)或远离用户请求的最大距离(`pixels_distance> clipping_dist`),那么我们应该从生成的彩色图像中剥离该像素。
```CPP
            //检查深度值是无效(<= 0)还是大于threashold
            if(pixels_distance <= 0.f || pixels_distance> clipping_dist)
            {
```
通过“剥离”我们的意思是我们只是用灰色绘制该像素。

```CPP
                //计算其他帧缓冲区中当前像素的偏移量
                auto offset = depth_pixel_index * other_bpp;

                //将像素设置为“背景”颜色(0x999999)
                std :: memset(&p_other_frame [offset],0x99,other_bpp);
            }
        }
    }
```

猜你喜欢

转载自blog.csdn.net/qq_42393859/article/details/85062587