- RealSenseTM互動開發實戰
- 王曰海 湯振宇 吳新天
- 3594字
- 2019-01-03 00:39:35
2.3 原始數據流獲取和處理
這一節將介紹獲取和處理原始視頻流和音頻流的常見任務。
1.獲取單個彩色或深度流
SenseManager接口實現I/O設備配置和獲取數據流,可以采用過程調用或事件回調方法來使用該接口。
(1)采用過程調用獲取彩色圖像樣本
例2-3說明了如何采用過程調用獲取彩色圖像樣本。
1)使用EnableStream函數選擇一個彩色流,再利用Init函數初始化流水線。
2)在循環中使用AcquireFrame函數來等待彩色圖像采樣到達,然后通過QuerySample進行獲取。
3)使用ReleaseFrame函數釋放這一幀,并等待下一個樣本。
4)利用Close函數進行清理過程。
可以使用CaptureManager接口中的過濾函數(如FilterByDeviceInfo)來自定義數據流選擇過程,例如選擇一個指定的攝像頭;也可使用QueryCaptureManager函數(或者captureManager屬性)來獲取CaptureManager接口實例。
例2-3 采用過程調用獲取彩色圖像樣本
// 創建PXCMSenseManager實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 選擇彩色數據流 sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_COLOR,640,480); // 初始化流樣本 sm.Init(); for (;;) { // 該函數阻塞直到彩色樣本到達 if (sm.AcquireFrame(true).isError()) break; // 獲取樣本 PXCMCapture.Sample sample=sm.QuerySample(); // 對圖像sample.color進行操作 ...... // 獲取下一個樣本 sm.ReleaseFrame(); } // 清理過程 sm.close();
(2)利用事件回調獲取深度樣本
例2-4展示了如何使用SenseManager接口的事件回調函數來獲取60 fps的深度樣本,其中主要過程是創建事件處理程序和調用StreamFrame函數。
例2-4 利用SenseManager的事件回調函數獲取深度樣本
class MyHandler implements PXCMSenseManager.Handler { //注冊事件處理程序 public pxcmStatus OnNewSample(int mid, PXCMCapture.Sample sample) { // 對sample.color進行操作 ...... // 返回NO ERROR繼續;如有錯誤則退出循環 return pxcmStatus.PXCM_STATUS_NO_ERROR; } ...... }; // 創建SenseManager實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 在320×240×60fps啟動深度流 sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_DEPTH,320,240,60); // 初始化事件處理器 MyHandler handler=new MyHandler(); sm.Init(handler); // 深度流樣本 sm.StreamFrames(true); // 清理過程 sm.close();
2.采集非對齊的彩色和深度樣本
例2-5展示了如何采集非對齊的彩色和深度樣本。例子中,當采樣數據準備好時,SenseManager單獨處理某個數據流的每個樣本。每個數據流可能以不同的幀率呈現樣本。例2-5做了如下工作:
1)使用EnableStream函數選擇彩色和深度流,然后使用Init函數初始化處理流水線。
2)在循環中,使用AcquireFrame函數ifall=false來等待樣本到達并讀取樣本。
3)使用QuerySample函數獲取樣本,并檢驗彩色和深度樣本相應的狀態和過程。
4)使用ReleaseFrame函數釋放當前幀,并讀取下一個樣本。
5)使用Close函數清理過程。
例2-5 利用SenseManager捕捉非對齊的彩色和深度數據
// 創建SenseManager實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 選擇彩色和深度流 sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_COLOR,640,480,30); sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_DEPTH,320,240,30); // 初始化流樣本 sm.Init(); for (;;) { // 該函數阻塞直到有樣本到達 if (sm.AcquireFrame(false).isError()) break; // 獲取樣本 PXCMCapture.Sample sample=sm.QuerySample(); if (sample!=null) { if (sample.color!=null) { // 對彩色樣本進行操作 ...... } if (sample.depth!=null) { // 對深度樣本進行操作 ...... } } // 獲取下一樣本 sm.ReleaseFrame(); } // 清理過程 sm.close();
同樣可以使用StreamFrame函數和SenseManager事件回調實現獲取非對齊彩色和深度樣本,如例2-6所示。
例2-6 利用SenseManager事件回調獲取彩色和深度樣本
class MyHandler implements PXCMSenseManager.Handler { public pxcmStatus OnNewImage(int mid, PXCMCapture.Sample sample) { if (sample.color!=null) { // 對彩色樣本進行操作 ...... } if (sample.depth!=null) { // 對深度樣本進行操作 ...... } //返回NO ERROR繼續;有錯誤則退出循環 return pxcmStatus.PXCM_STATUS_NO_ERROR; } }; // 創建SenseManager實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 選擇彩色和深度流 sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_COLOR,640,480,30); sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_DEPTH,320,240,30); // 初始化處理器 MyHandler handler=new MyHandler(); sm.Init(handler); // 生成樣本流 sm.StreamFrames(true); // 清理 sm.close();
3.采集對齊的彩色和深度流
例2-7展示了如何采集對齊的彩色和深度樣本。該例子在同步了兩種數據幀的到達時間之后立刻處理數據。
例2-7的具體工作如下:
1)使用EnableStream函數選擇彩色和深度流,之后利用Init函數初始化處理流水線。
2)在循環中,AcquireFrame函數采用ifall=true選項來等待所有流的樣本都準備好。
3)用QuerySample函數獲取彩色和深度樣本。
4)用ReleaseFrame函數釋放幀,并等待讀取下一個樣本。
5)用Close函數進行清理。
例2-7 采集對齊的彩色和深度樣本
// 創建一個 SenseManager 實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 選擇彩色和深度數據流 sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_COLOR,640,480,30); sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_DEPTH,320,240,30); // 初始化數據流 sm.Init(); for (;;) { // 這個函數保持阻塞直到樣本準備好 if (sm.AcquireFrame(true).isError()) break; // 獲取樣本 PXCMCapture.Sample sample=sm.QuerySample(); // 在樣本上進行處理: sample.color & sample.depth ...... // 準備下一幀處理 sm.ReleaseFrame(); } // 清理 sm.close();
可以使用StreamFrames函數和SenseManager事件回調來采集彩色和深度樣本,如例2-8所示。
例2-8 利用SenseManager回調函數獲取對齊的彩色和深度樣本
class MyHandler implements PXCMSenseManager.Handler { public pxcmStatus OnNewSample(int mid, PXCMCapture.Sample sample) { // 在 sample.color 和 sample.depth樣本上處理 ...... //返回NO_ERROR繼續;如果出錯則報錯并放棄 return pxcmStatus.PXCM_STATUS_NO_ERROR; } }; // 創建一個 SenseManager 實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 選擇彩色和深度數據流 PXCMVideoModule.DataDesc ddesc=new PXCMVideoModule.DataDesc(); ddesc.deviceInfo.streams=EnumSet.of(PXCMCapture.StreamType.STREAM_TYPE_COLOR, PXCMCapture.StreamType.STREAM_TYPE_DEPTH); sm.EnableStreams(ddesc); // 初始化處理器 handler MyHandler handler=new MyHandler(); sm.Init(handler); // 流式處理 sm.StreamFrames(true); // 清理 sm.close();
4.啟動強力同步化
SDK支持基于硬件的彩色和深度流同步,可以產生成對的擁有相近時間戳的彩色和深度樣本。這種同步環節在很多應用場合非常有用,例如3D背景分割。
可以利用STREAM_OPTION_STRONG_STREAM_SYNC選項來啟動強力同步化。選項必須設置對所有需要同步的數據流生效,否則該選項將被忽略。毋庸置疑,這些流需要擁有相同的幀率。例如需要同步彩色和深度流,就可以利用EnableStreams函數對彩色和深度流同時指定這個選項,如例2-9所示。
注 以攝像頭F200為例,如果有應用程序請求啟動強力同步化,系統將會有明顯的停頓并重啟攝像頭流。因此,只有當確實需要時才使用強力同步化。
例2-9 通過強力同步獲取對齊的彩色和深度樣本
// 創建一個 SenseManager 實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 選擇彩色和深度流 PXCMVideoModule.DataDesc ddesc=new PXCMVideoModule.DataDesc(); ddesc.deviceInfo.streams=EnumSet.of(PXCMCapture.StreamType.STREAM_TYPE_COLOR, PXCMCapture.StreamType.STREAM_TYPE_DEPTH); ddesc.streams.color.options=EnumSet.Of(PXCMCapture.Device.StreamOption.STREAM_OPTION_STRONG_STREAM_SYNC); ddesc.streams.depth.options=EnumSet.Of(PXCMCapture.Device.StreamOption.STREAM_OPTION_STRONG_STREAM_SYNC); sm.EnableStreams(ddesc); // 初始化 sm.Init(); for (;;) { // 函數保持阻塞直到采樣數據準備好 if (sm.AcquireFrame(true).isError()) break; // 獲取樣本 PXCMCapture.Sample sample=sm.QuerySample(); // 在 sample.color 和 sample.depth樣本上處理 ...... // 獲取下一幀數據 sm.ReleaseFrame(); } // 清理 sm.close();
5.采集未矯正數據流
SDK支持R200攝像頭采集未矯正的數據流。矯正是修正鏡頭失真以及對齊的過程。未矯正的數據流是從攝像頭傳感器得來的原始圖像。
SDK利用STREAM_OPTION_UNRECTIFIED選項識別未矯正的數據流配置(參見StreamProle結構)。如果沒有指定這個命令選項,數據流默認配置為采用矯正。可以指定STREAM_OPTION_UNRECTIFIED選項獲取一個未矯正數據流,如例2-10所示。
注 不是所有的R200算法模塊都支持未矯正數據流,只有當需要時才能使用這種方式。
可以通過QueryStreamProjectionParametersEx函數指定STREAM_OPTION_UNRECTIFIED選項來獲取未矯正攝像頭標定參數。
例2-10 獲取未校正彩色樣本
void CaptureAlignedColorDepthSamples() { // 創建一個 SenseManager 實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 選擇彩色和深度流 PXCMVideoModule.DataDesc ddesc=new PXCMVideoModule.DataDesc(); ddesc.deviceInfo.streams=EnumSet.of(PXCMCapture.StreamType.STREAM_TYPE_COLOR); ddesc.streams.color.options=EnumSet.Of(PXCMCapture.Device.StreamOption.STREAM_OPTION_UNRECTIFIED); sm.EnableStreams(ddesc); //初始化 sm.Init(); for (;;) { if (sm.AcquireFrame(true).isError()) break; // 獲取樣本 PXCMCapture.Sample sample=sm.QuerySample(); // 在 sample.color上進行處理 ...... // 進行下一幀準備 sm.ReleaseFrame(); } // 清理 sm.close(); }
6.記錄和回放
可以記錄任何流序列到文件,并在之后播放這些流文件。
啟動文件記錄和回放需要的工作如下:
1)在CaptureManager實例中使用SetFileName函數。
2)在記錄模式,提供一個文件名并設定為記錄模式。
3)在回放模式,提供一個文件名并設定為非記錄模式。
SDK對文件名沒有限制,但在記錄模式中文件必須是可寫的。
注 可以利用QueryCaptureManager函數獲取一個CaptureManager實例。
例2-11展示了如何記錄和回放已采集的彩色樣本。
注 在記錄過程中,樣本按應用程序所處理后的狀態記錄到硬盤。如果應用程序采集未對齊的彩色和深度樣本,那么在硬盤上的樣本就是未對齊的。如果應用程序使樣本對齊,那么在硬盤上的樣本就是已對齊的。
例2-11 利用SenseManager進行彩色數據流的記錄和回放
void RecordORPlayback(string file, boolean record) { // 創建一個 SenseManager 實例 PXCMSenseManager sm=PXCMSenseManager.CreateInstance(); // 設置文件記錄和回放 sm.QueryCaptureManager().SetFileName(file,record); // 選擇彩色數據流 sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_COLOR,640,480,0); // 初始化并記錄300幀 sm.Init(); for (int i=0;i<300;i++) { if (sm.AcquireFrame(true).isError()) break; // 獲取樣本 PXCMCapture.Sample sample=sm.QuerySample(); // 在sample.color上進行處理 ...... // 進行下一幀數據處理準備 sm.ReleaseFrame(); } // 清理 sm.close(); }
7.回放模式
可以按以下配置SDK的文件回放操作:

如果想要精確定位回放中的任意幀,可以選擇pause=true和realtime=false。例2-12展示了如何基于幀索引精確獲取幀數據。
例2-12 精確幀定位
// 創建SenseManager實例 PXCMSenseManager sm = PXCMSenseManager.CreateInstance(); // 設置回放文件名稱 sm.QueryCaptureManager().SetFileName(filename, false); // 啟動流和初始化 sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_COLOR, 0, 0); sm.Init(); // 設置realtime=true和pause=false sm.QueryCaptureManager().SetRealtime(false); sm.QueryCaptureManager().SetPause(true); // 流循環 for (int i = 0; i < nframes; i+=3) { // 設置在每3幀數據上進行操作 sm.captureManager.SetFrameByIndex(i); sm.FlushFrame(); // 等待幀到達 pxcmStatus sts = sm.AcquireFrame(true); if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR) break; // 獲取樣本并進行操作,圖像在sample.color中 PXCMCapture.Sample sample = sm.QuerySample(); ...... // 等待處理下一幀 sm.ReleaseFrame(); } // 清除 sm.close();
8.文件壓縮
在磁盤上記錄原始彩色和深度圖像數據會給系統增加巨大的負擔。舉個例子,假設彩色分辨率配置為RGB32×1920×1080×30fps,深度分辨率配置為640×480×30fps,SDK需要大約272MB/s的磁盤I/O帶寬來寫入數據。這個要求使得大多數旋轉磁盤和某些緩慢的SSD無法進行文件記錄。
為了解決這個問題,SDK開發了一個試驗性的功能,即在數據寫入磁盤之前對其進行壓縮。這個壓縮功能采用H.264(I-frame only,constant QP)進行彩色圖像編碼和Lempel–Ziv–Oberhumer(LZO)進行深度圖像編碼。壓縮率在彩色圖像數據上大約為10:1,在深度圖像數據上為2:1。而相比之前例子的高寫入帶寬要求,磁盤I/O帶寬可降低為32MB/s。
文件壓縮功能要求系統具有如下配置:Intel Iris Graphics和最新的Intel Iris Graphics驅動。
需要設置以下注冊項來控制文件記錄功能:
Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\SOFTWARE\Intel\RSSDK\FileRecording] "DisableH264Compression"=dword:0 "H264_QPI"=dword:8 "DisableLZOCompression"=dword:0
SDK默認H.264壓縮在彩色圖像流中應用,LZO在其他數據流中應用。H.264 QPI(I-frame quantization parameter)取值范圍為0(最小壓縮)到50(最大壓縮)。一個利用H.264壓縮的記錄文件只能在安裝Intel Iris Graphics的系統中播放。
SDK文件的記錄和回放功能有以下限制:
1)文件記錄可能會影響應用程序運行時間,而組織數據并寫入磁盤也是較重的工作負載,因此只有在必要的時候才啟用文件記錄。
2)計時程序在實時回放模式下計算數據流的準確呈現時間,故對運行時間敏感。因此如果系統在重負載時,文件回放程序可能會跳幀播放。為了保證數據幀的準確回放,應關閉實時模式。
9.訪問圖像和音頻數據
為了和其他運行庫實現共享訪問或互操作,SDK提供了圖像接口提取圖像存儲。
可以利用AcquireAccess函數來鎖定訪問圖像存儲,并在ImageData結構中獲取圖像存儲細節。圖像存儲的數據平面由data.planes[0-3]指出,其基址(pitch)存儲在data.pitches[0-3]。當圖像存儲訪問完成時,需要使用ReleaseAccess函數來解除訪問鎖定。例2-13展示了如何讀取圖像緩存。
例2-13 讀取圖像緩沖
// 圖像是一個PXCMImage實例 PXCMImage.ImageData data; image.AcquireAccess(PXCMImage.Access.ACCESS_READ,data); ...... //圖像平面由data.planes[0-3]指出,pitch存儲在data.pitches[0-3] image.ReleaseAccess(data);
10.創建圖像實例
可以利用Session接口的CreateImage函數來創建一個圖像實例。應用程序需要保證用來創建圖像實例(PXCMImage)的圖像緩存具有更長的生命周期。例2-14展示了如何在位圖中創建PXCMImage實例。
例2-14 從位圖創建圖像實例
// 讀取位圖存入內存 Bitmap bitmap = (Bitmap)Image.FromFile(file); // 圖像信息 PXCMImage.ImageInfo iinfo = new PXCMImage.ImageInfo(); iinfo.width = bitmap.Width; iinfo.height = bitmap.Height; iinfo.format = PXCMImage.PixelFormat.PIXEL_FORMAT_RGB32; /* 創建圖像 */ PXCMImage image=session.CreateImage(iinfo); /* 復制數據 */ PXCMImage.ImageData idata; image.AcquireAccess(PXCMImage.Access.ACCESS_WRITE, out idata); BitmapData bdata = new BitmapData(); bdata.Scan0 = idata.planes[0]; bdata.Stride = idata.pitches[0]; bdata.PixelFormat = PixelFormat.Format32bppRgb; bdata.Width = bitmap.Width; bdata.Height = bitmap.Height; BitmapData bdata2 = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly | ImageLockMode.UserInputBuffer, PixelFormat.Format32bppRgb, bdata); image.ReleaseAccess(idata); bitmap.UnlockBits(bdata2); ...... // 對圖像進行其他操作 image.Dispose();