恩…雖然由於 PrimeSense 被 Apple 收購的關係,OpenNI 官網已經關閉、而 PrimeSense 的 NiTE 也不再維護了,不過包括 Heresy 自己在內,還是有一些人有在用 OpenNI,所以這邊還是寫一下吧。
這邊主要的問題,是在最後的 OpenNI 2.2 和 NiTE 2.2 的環境下,在把 Kinect 的資料錄下來成 ONI 檔之後,錄下來的檔案會無法再給 NiTE 使用。(在建立 UserTracker 的時候,會出現「memory access violation error」的錯誤)
而 Heresy 雖然滿早就知道這個問題了,但是由於自己都沒碰到、也無法重現,所以也就放著沒管了。不過最近剛好有人向 Heresy 詢問這個問題,所以就認真地和他詢問了一些資訊,來試著釐清、解決了。
基本上,目前就 Heresy 所知,問題應該是這樣的:
NiTE 以前(可能是 2.0)沒有使用到 XN_STREAM_PROPERTY_PARAM_COEFF 這個參數,所以在 OpenNI 錄製 ONI 檔的時候沒有特別去處理它也可以用;但是後來改版後,由於需要這個額外的參數,所以如果沒有把他寫進 ONI 檔,就會造成 NiTE 讀不到該筆資料、而無法使用的問題。
而由於 OpenNI 2 支援 Kinect 的模組是透過 Kinect for Windows SDK 另外開發的,所以雖然 PS1080(ASUS Xtion)的支援有做好、沒有問題;但是相對的,Kinect 模組這邊就沒有加上這個參數了…
也就是說,這個問題應該僅限於新版 NiTE 和 Kinect 感應器。再加上 NiTE 在這部分的錯誤處理沒做好,所以下場就是在遇到這個問題的時候直接當掉了。
這個問題當初在官方論壇上也有不少相關討論,不過現在也都找不到了。而由於這個問題看來只會發生在 Kinect 感應器上,所以這也是 Heresy 始終無法重現這個問題的原因了…(Heresy 主要是用 Xtion)
確認問題後,接下來就是看要怎麼解決了…Heresy 本來以為這個問題已經在 GitHub 上的 OpenNI 2.3 開發版(連結)解決了;但是認真看了一下,才發現不但沒有解決,而且還有別的錄製 ONI 檔的問題、有可能導致錄出來的 ONI 檔完全不能用(issue 78)…
所以,Heresy 就決定自己跳下去改了。
Recorder 紀錄 Property 的方式
首先,Heresy 之前有寫過一篇《OpenNI 2 的 Driver 模組(概念)》,大致解釋了 OpenNI 2 的驅動程式模組的架構;不過當時應該是針對 2.1 版,後來更新到 2.2 版之後,有做了一些小改變,不過概念差異不大就是了(不過程式寫法得改)。
而後來,Heresy 自己也有根據這個概念、自己下去改過 改過 Kinect 的驅動程式模組,並且寫過 WebCam4OpenNI2 和 Virtual Device for OpenNI 2 這兩個驅動程式模組。
不過…透過這次的機會,Heresy 也才發現,Heresy 自己寫的驅動程式模組,由於沒有實做某些部分,在拿來錄製成 ONI 檔的時候,都沒辦法把 property 的資料記錄下來…
欠缺的部分,主要就是 oni::Driver::StreamBase::notifyAllProperties() 這個 video stream 類別的成員函式的實作。基本上,它要做的事,是讓該物件所有的 property 透過 raisePropertyChanged() 這個函式、發出變更通知,告訴其他模組這些 property 的值有變動。
而 OpenNI 的 Recorder 在透過 attach() 加入新的 VideoStream 的時候,就會去呼叫到這個函式,來讓 Recorder 知道要記錄那些 property。由於 Heresy 之前都沒有實作這個函式,所以 Recorder 沒辦法取得 property 的更新、自然就沒辦法把它們記錄下來了…
至於現在 Kinect 錄製的 ONI 無法使用的問題,則是因為他在 notifyAllProperties() 裡面,沒有透過 raisePropertyChanged() 這個函式去處理 XN_STREAM_PROPERTY_PARAM_COEFF 這個屬性的關係,所以導致錄製出來的 ONI 獨缺這個 property。
程式修改
既然知道原因,就好辦了。基本上,只要加上 notifyAllProperties(),並在裡面針對需要的 property、使用 raisePropertyChanged() 這個函式來發出變更通知就可以了!
針對 Virtual Device for OpenNI 2 的部分,Heresy 已經補上了 video stream 的 notifyAllProperties() 函式,並確認可以錄製下來了。有需要的話,可以到 GitHub 上下載更新後的檔案;其版本為 v02、連結是:https://github.com/VIML/VirtualDeviceForOpenNI2/releases。
至於 Kinect 的部分,Heresy 也基於 OpenNI 2.3 的版本,做了修改,有興趣看改動的地方,可以參考 Heresy 送到 OpenNI 的 pull request(連結);基本上就是在 DepthKinectStream.cpp 這個檔案的 DepthKinectStream::notifyAllProperties() 這個函式裡面、加三行程式而已:
size = sizeof(PARAM_COEFF_VAL);
getProperty(XN_STREAM_PROPERTY_PARAM_COEFF, nBuff, &size);
raisePropertyChanged(XN_STREAM_PROPERTY_PARAM_COEFF, nBuff, size);
而只是想使用的話,也可以到 https://github.com/KHeresy/OpenNI2/releases/tag/KinectOniFix 這邊下載修改好的編譯版本;只需要針對 32/64 位元下載後、把他改名為「kinect.dll」、並取代原有的檔案就可以了。
而由於這個版本是基於 OpenNI 2.3 來的,所以它的 Kinect 支援功能會比 2.2 版更完整!有需要的話,請搭配新的 KinectProperties.h(連結)做額外的參數控制。雖然實作不一樣,不過使用概念可以參考 Heresy 之前的《OpenNI 2 的 Kinect 驅動模組加強版》一文。
修正現有 ONI 檔案
如果是已經有錄好的 ONI 檔不能用該怎麼辦呢?Heresy 自己也試著搭配 Virtual Device for OpenNI 2 寫了一個名為「oniFixer」的小程式,可以針對 ONI 檔做處理、幫他加上這些必要的 property。
這個程式的專案在:
可以在 release 的地方、下載預先編譯好的執行檔(win32 版):
要使用的話,基本上就是用命令提示字元、執行:
oniFixer old.oni new.oni
之後,這個程式會開啟 old.oni 檔,確認內部的 property,並在開啟實際的裝置(Kinect 或 Xtion)、來補足必要的 property,並透過一個 Virtual Device,來重新做錄製。而最後產生的 new.oni,應該就可以正確地搭配 NiTE 2.2 使用了~
不過,目前的版本沒有針對 IR 做處理,而彩色影像因為沒有指定有損壓縮,所以檔案可能會變大不少。之後有機會再看要怎麼把這個設定參數化吧。
heresy,你好,我想问下:
鉴于OpenNI和NITE已经不再更新,特别是NiTE是一个闭源的软件,如果用其他类型的摄像头,输出景深图到OpenNI,不使用NiTE,然后再在OpenNI1.X下进行少量开发和修改,能不能直接实现体感功能(类似于Window KinectSDK那样的骨骼追踪)来满足游戏开发需求?
to 八荒
OpenNI 1 雖然在介面上有定義骨架追蹤的部分,但是實際上核心還是需要 NiTE 來做計算,問題基本上和 OpenNI 2 是相同的。
to heresy
哦,明白了。有没有开源项目可以实现这样的功能呢,或者是说,我看NiTE很久没更新了,微软的SDK一直在更新,微软的东西越来越好用,但想在Android上做开发又没办法用。
to 八荒
很遺憾,目前沒有看到足以取代 NiTE 的方案。