外部幀數據源的輸入幀數據要求
爲使外部幀數據源正常工作,最重要的工作同時也是最棘手的部分是確保數據正確性。本文介紹了外部幀數據源的輸入幀數據要求。
開始之前
輸入幀數據類型
在 Unity 中,外部幀數據源通常需要在兩個不同時間接收不同的數據,根據外部數據輸入時間和數據特徵,我們將這兩組數據稱爲:
- 相機幀數據(camera frame data)
- 渲染幀數據(rendering frame data)
不同類型的外部幀數據源對這兩組數據的需求不同:
- 圖像和設備運動數據輸入擴展:同時需要相機幀數據及渲染幀數據
- 圖像輸入擴展:只需要相機幀數據
相機幀數據
數據需求:
- 時間戳(timestamp)
- 原始物理相機圖像數據(raw camera image data)
- 內參(intrinsics,包括圖像大小、焦距、主點。如果有畸變還需要畸變模型和畸變參數)
- 外參(extrinsics,Tcw 或 Twc,標定的矩陣,表達物理相機相對設備/頭的 pose 原點的物理偏移)
- 跟蹤狀態(tracking status)
- 設備位姿(device pose)
數據時間:
- 物理相機曝光中點
數據使用:
- API 調用時間:可根據外部代碼的設計改變。一個大多數設備使用的常規方法是在 3D 引擎的渲染更新中查詢,然後根據設備數據的時間戳來判斷是否進一步進行數據處理
- API 調用線程:3D 引擎的 game thread 或任何其它線程(如果使用到的所有外部 API 都是線程安全的)
Unity 中 API 調用示例如下:
void TryInputCameraFrameData()
{
double timestamp;
if (timestamp == curTimestamp) { return; }
curTimestamp = timestamp;
PixelFormat format;
Vector2Int size;
Vector2Int pixelSize;
int bufferSize;
var bufferO = TryAcquireBuffer(bufferSize);
if (bufferO.OnNone) { return; }
var buffer = bufferO.Value;
IntPtr imageData;
buffer.tryCopyFrom(imageData, 0, 0, bufferSize);
var historicalHeadPose = new Pose();
MotionTrackingStatus trackingStatus = (MotionTrackingStatus)(-1);
using (buffer)
using (var image = Image.create(buffer, format, size.x, size.y, pixelSize.x, pixelSize.y))
{
HandleCameraFrameData(deviceCamera, timestamp, image, cameraParameters, historicalHeadPose, trackingStatus);
}
}
渲染幀數據
數據需求:
- 時間戳(timestamp)
- 跟蹤狀態(tracking status)
- 設備位姿(device pose)
數據時間:
- 上屏時刻。TimeWarp 不計算在內。相同時刻的 device pose 數據會由外部(比如設備 SDK)用來設置虛擬攝像機的 transform 以渲染當前幀。
附註
TimeWarp(有時也稱爲 Reprojection 或 ATW/PTW)是 VR/AR 頭顯中常用的一種降低延遲的技術。它會在渲染完成後,根據最新的頭部位姿對圖像進行再次扭曲變換,以補償渲染期間產生的頭部運動。EasyAR 需要的是渲染開始時用於設置虛擬攝像機的位姿對應的時刻,而不是 TimeWarp 後實際上屏的時刻。
數據使用:
- API 調用時間:3D 引擎的每個渲染幀
- API 調用線程:3D 引擎的 game thread
Unity 中 API 調用示例如下:
private void InputRenderFrameMotionData()
{
double timestamp = 0e-9;
var headPose = new Pose();
MotionTrackingStatus trackingStatus = (MotionTrackingStatus)(-1);
HandleRenderFrameData(timestamp, headPose, trackingStatus);
}
數據要求細節
物理相機圖像數據:
- 圖像座標系:在傳感器水平時獲取的數據也應是水平的。數據應該以左上角爲原點,行優先存儲。圖像不應翻轉或顛倒。
- 圖像 FPS:正常 30 或 60 fps 的數據都可以。如果高 fps 有特殊影響,爲達到合理的算法效果,最小可接受幀率爲 2。建議使用高於 2 的 fps,通常情況下使用原始數據幀率即可。
- 圖像尺寸:爲獲取更好的計算結果,最大邊應爲 960 或更大。正常不鼓勵在數據鏈路中進行耗時的圖像縮放,建議直接使用原始數據,除非完整大小的數據拷貝時間已經長得無法接受。圖像分辨率不能小於 640*480。
- 像素格式:優先跟蹤效果並綜合考慮性能,通常格式優先順序爲 YUV > RGB > RGBA > Gray (YUV中的Y分量)。在使用 YUV 數據時,需要完整的數據定義,包括數據封裝和填充細節。相較單通道圖像而言,使用彩色圖像 Mega 的效果會更好,但其它功能影響不大。
- 數據訪問:數據指針或等價實現。最好在數據鏈路中消除所有可能的非必須拷貝。HandleRenderFrameData 中 EasyAR 複製一份數據,之後異步使用,該同步調用完成後就不再使用圖像數據。注意數據所有權。
時間戳:
- 所有時間戳都應時鐘同步,最好是硬件同步。數據單位是秒,但精度要求達到納秒或儘可能高。
跟蹤狀態:
- 跟蹤狀態由設備定義,需要包含跟蹤丟失(VIO不可用)的狀態。如有更多等級則更好。
設備位姿:
- 所有 pose(包括 3D 引擎中虛擬攝像機的 transform)都應使用同一個原點。
- 所有 pose 以及外參應該使用相同的座標軸系統。
- 在 Unity 中,pose 數據的座標軸系統類型應爲 Unity 座標軸系統或 EasyAR 座標軸系統。如果輸入擴展由 EasyAR 實現且使用了其它座標軸系統定義方式,應提供清晰的座標軸系統定義或給出轉換到 Unity 座標軸系統或 EasyAR 座標軸系統的方法。
- 在 Unity 中,如果使用 Unity XR 框架,只需要兼容 XROrigin.TrackingOriginMode.Device 模式即可。
內參:
- 所有數值都應與圖像數據匹配。如有需要應在輸入 EasyAR 之前對內參進行縮放。
- 如果輸入擴展由 EasyAR 實現,應說明內參是否會在每一幀變化(區別是對應 API 應該調用一次還是每幀調用)。
外參:
- 在頭顯上必須提供真實數據。
- 它是一個標定矩陣,表達物理相機相對設備/頭的 pose 原點的物理偏移。如果設備的 pose 和物理相機 pose 相等,它應該是單位陣。
- Apple Vision Pro 對應接口爲: CameraFrame.Sample.Parameters.extrinsics,需要注意其數據定義與接口所需數據有區別,EasyAR 內部是進行轉換之後再使用的。
- 在 Unity 中,外參的座標軸系統類型應爲 Unity 座標軸系統或 EasyAR 座標軸系統。如果輸入擴展由 EasyAR 實現且使用了其它座標軸系統定義方式,應提供清晰的座標軸系統定義或給出轉換到 Unity 座標軸系統或 EasyAR 標軸系統的方法。
- 在頭顯設備中,通常存在多個不同定義的座標系,這個不同可能包括座標軸原點、朝向、左右手錶達等。外參應在同一座標系下計算,該接口數據需要同一座標系下的座標變換,而非兩個不同定義的座標系的變換矩陣。
性能:
- 數據應以最優效率提供。在大多數實現中,API 調用會發生在渲染過程,所以建議即使在底層需要進行耗時操作的情況下,也不要阻塞 API 調用,或者以合理的方式來使用這些 API。
- 如果輸入擴展由 EasyAR 實現,需要對所有耗時 API 調用進行說明。
多相機:
- 至少一個相機的數據是需要的。這個相機可以是 RGB 相機、VST 相機、定位相機等中的任意一個。在頭顯上如果只輸入一個相機的數據,通常推薦使用在中央或在眼睛附近的 RGB 相機或 VST 相機。
- 使用多相機可提升 EasyAR 算法效果。所有可用相機某一時刻的相機幀數據應在在同一個時間點同時輸入。
多相機目前尚未完全支持,可以聯繫 EasyAR 獲取更多細節。
後續步驟
- 創建 圖像和設備運動數據輸入擴展
- 創建 圖像輸入擴展
- 創建 頭顯擴展包
相關主題
- EasyAR 座標系
- 圖像輸入擴展示例 Workflow_FrameSource_ExternalImageStream