2019年11月13日 星期三

【3D感測器】如何擷取Intel RealSense™串流影像到OpenCV

上次已透過「【3D感測器】Intel RealSense™ SDK無痛安裝指引[1] 一文幫大家說明如何安裝,這次就幫大家說明如何從RealSense SDK擷取彩色、深度及紅外線串流影像並導入OpenCV中,方便後續開發自己的人機介面及相關計算功能。

Fig.1 Intel RealSense串流影像導入OpenCV Mat示意圖 (OmniXRI整理繪製) (點擊圖片放大)

我想大部份的人想開發一個3D的人機介面可以自由平移、縮放及旋轉視角是有點(大誤!是非常)困難的,所以RealSense SDK提供使用者輕易切換2D(平面)及3D(立體、深度)顯示及操作的介面。這項功能是基於通用3D繪圖引擎OpenGL達成的,雖然很方便操作,但於習慣自己用Visual Studio, QT及其它GUI工具開發人機介面程式的人反而有點不太方便,尤其是重度OpenCV使用者無法透過常用的VideoCapture函式直接取得RealSense各攝影機的串流影像,更是造成一些小困擾。



目前官方SDK範例程式"C:\Program Files (x86)\Intel RealSense SDK 2.0\samples\im-show\rs-imshow.cpp"可以讀取彩像影像和著虛擬色後的深度影像並傳至OpenCV Mat中再透過imshow函式進行顯示,但無法讀取紅外線影像。經過一番查找後,終於找到如何設定,首先建構一組接收管道(pipeline),再設定欲接收的串流影像類型及細部配置參數(config),接著就可以啟動(start)串流影像接收管道,最後把接收到的影格(frame)轉換成OpenCV Mat格式,就可透過imshow顯示或作其它處理使用。

這裡要注意的是,由於RealSense D435可以提供不同格式的影像(8bit, 16bit),而OpenCV只能顯示8bit,所以如遇到要顯示或運算16bit影像時要記得利用像convertScaleAbs之類的函式將16bit轉回8bit,才能正常處理。另外如果想轉存這些串流影像,則使用OpenCV VideoCapture及 VideoWriter函式處理即可。完整的範例如下所示,相關說明已於程式上註解,就不多贅述。

#include  // 引入RealSense標頭檔
#include    // 引入OpenCV標頭檔

using namespace std; // 使用std標準函式命名空間
using namespace cv;  // 使用OpenCV函式命名空間

// 主程式入口
int main(int argc, char * argv[]) try
{
  // 建構一個RealSense抽象設備的管道以容納擷取到的影像
  rs2::pipeline pipe;

  // 創建自定義參數以配置管道
  rs2::config cfg;

  // 設定影像尺寸(寬w,高h)
  const int w = 640;
  const int h = 480;

  // 設定欲顯示的影像流(可依需求啟動不一定要全設)
  cfg.enable_stream(RS2_STREAM_COLOR, w, h, RS2_FORMAT_BGR8, 30); // BGR888格式彩色影像 30fps
  cfg.enable_stream(RS2_STREAM_DEPTH, w, h, RS2_FORMAT_Z16, 30); // 16 bit格式灰階深度影像 30fps
  cfg.enable_stream(RS2_STREAM_INFRARED, 1, w, h, RS2_FORMAT_Y8, 30); // 8 bit格式左紅外線影像 30fps
  cfg.enable_stream(RS2_STREAM_INFRARED, 2, w, h, RS2_FORMAT_Y8, 30); // 8 bit格式右紅外線影像 30fps

  // 根據設定值啟動指定串流影像
  pipe.start(cfg);

  // 宣告深度(灰階)影像著虛擬色彩對應表
  rs2::colorizer color_map;

  while (waitKey(1) < 0) // 若有按鍵則結束顯示串流影像
  {
    // 等待下一組影像 
    rs2::frameset frames = pipe.wait_for_frames();

    // 取得每一張影像(可依需求不一定要全取)
    rs2::frame color_frame = frames.get_color_frame(); // 彩色影像
    rs2::frame depth_frame = frames.get_depth_frame().apply_filter(color_map); // 著虛擬色彩之深度影像
    rs2::frame irL_frame = frames.get_infrared_frame(1); // 左紅外線灰階影像
    rs2::frame irR_frame = frames.get_infrared_frame(2); // 右紅外線灰階影像
  
    // 建立OpenCV Mat格式之影像(可依需求不一定要全建立)
    Mat color_image(Size(w, h), CV_8UC3, (void*)color_frame.get_data(), Mat::AUTO_STEP); // 彩色影像
    Mat depth_image(Size(w, h), CV_8UC3, (void*)depth_frame.get_data(), Mat::AUTO_STEP); // 著虛擬色彩之深度影像
    Mat irL_image(Size(w, h), CV_8UC1, (void*)irL_frame.get_data(), Mat::AUTO_STEP); // 左紅外線灰階影像
    Mat irR_image(Size(w, h), CV_8UC1, (void*)irR_frame.get_data(), Mat::AUTO_STEP); // 右紅外線灰階影像  

    // 以OpenCV函式顯示擷取到影像(可依需求不一定要全顯示)
    imshow("Color Image", color_image); // 彩色影像
    imshow("Depth Image", depth_image); // 著虛擬色彩之深度影像
    imshow("Left IR Image", irL_image); // 左紅外線灰階影像
    imshow("Right IR Image", irR_image); // 右紅外線灰階影像
  }

  return EXIT_SUCCESS;
}
catch (const rs2::error & e) // 擷取錯誤呼叫碼及顯示
{
 std::cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n    " << e.what() << std::endl;
 return EXIT_FAILURE;
}
catch (const std::exception& e) // 擷取意外錯誤及顯示
{
 std::cerr << e.what() << std::endl;
 return EXIT_FAILURE;
}

小結

Intel RealSense SDK提供了很方便的2D/3D操作介面,但如果想透過OpenCV存取其產生的彩色、深度及紅外線串流影像進而產生客製化介面或更複雜應用,相信本文一定可以幫到大家。

參考文獻:

[1] 【3D感測器】Intel RealSense™ SDK無痛安裝指引
https://omnixri.blogspot.com/2019/10/3dintel-realsense-sdk.html

2 則留言:

  1. 請問realsense d系列可以在昏暗房間使用? 主要取深度影像

    回覆刪除
    回覆
    1. 可以的,深度影像是靠紅外線取像,就算在完全沒有光線的環境都能正確產出深度影像。但注意被偵測物件不能是透明或絨毛類或會吸光材料,就是拿雷射筆打在表面看不出是光點而是一團霧狀的材質。

      刪除

【頂置】簡報、源碼、系列文快速連結區

常有人反應用手機瀏覽本部落格時常要捲很多頁才能找到系列發文、開源專案、課程及活動簡報,為了方便大家快速查詢,特整理連結如下,敬請參考! Edge AI Taiwan 邊緣智能交流區 全像顯示與互動交流區 台科大(NTUST) 人工智慧與邊緣運算實務 開南大學...