1. 程式人生 > >realsense2在開發中的一些方法(深度幀與其對應的顏色幀對齊示例)

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);
            }
        }
    }
```