外部幀資料來源的輸入幀資料要求
為使外部幀資料來源正常工作,最重要的工作同時也是最棘手的部分是確保資料正確性。本文介紹了外部幀資料來源的輸入幀資料要求。
開始之前
輸入幀資料類型
在 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