這次的主題是將上篇的去背功能套用到Color Frame上頭
特別獨立拿出來講是因為要再次面對空間轉換的事情
要是連同上一篇一起寫的話...
不要說讀的人了, 我自己都吃不消
廢話不多說!!來看Demo
讓大家失望了...這次沒有長頸鹿...
不過有一個當時在撰寫時靈機一動所做的"Party特效"
此效果配上長頸鹿一起服用效果更佳!!
========分隔線========
純粹看這個Demo可能會看不太懂
在此簡單列一下這個影片做了些甚麼
- 取出Color Frame的資訊並繪出(00:00~00:13)
- 切換到Depth Space,並透過空間轉換填上Color Frame的資訊(00:13~00:30)
- 透過Body Index Frame,在Depth Space上進行去背(00:31~00:40)
- 透過Body Index Frame,在Color Space上進行去背(00:41~00:52)
- PARTY!!!(00:53~)
※本篇關於Body Index做去背以及空間轉換部分可參考前篇:
這次的新朋友只有一位!Color Frame!!
=Color Frame=
與之前的Depth Frame類似
在初始化方面,一樣是get_ColorFrameSource() + OpenReader()
取得資料方面,同樣可以使用IFrameDescription來取得Color Frame的size
取得資料方面,同樣可以使用IFrameDescription來取得Color Frame的size
不同的地方來了!!Color Image Format
不像Depth Map每個Pixel就是一個unsigned short,彩色資訊就是有他麻煩色彩空間(Color Space)問題
SDK 2.0將他提供的色彩資訊透過ColorImageFormat這個enum表示,共以下5種:
- ColorImageFormat_Rgba
- ColorImageFormat_Yuv
- ColorImageFormat_Bgra
- ColorImageFormat_Bayer
- ColorImageFormat_Yuy2
一般軟體內常用的應該就是Rgba以及Bgra,比方說前者使用在OF上,後者則是OpenCV有在用
Yuv以及Yuv2則是影視串流常用的格式,筆者Kinect V2所得到的初始格式就是Yuv
最後關於Bayer,似乎是某種特殊Filter,沒有研究!
當然,Kinect SDK並沒有很殘忍的只給我們特定格式讓開發者自己找方式轉換
它提供了一個轉換的function來讓你可以依自己需求的格式去取出Color Frame
以下直接用這部份的程式碼說明:
1: pColorFrame_->get_RawColorImageFormat(&ImgFormat_);
2:
3: if(ImgFormat_ == ColorImageFormat_Rgba)
4: {
5: pColorFrame_->AccessRawUnderlyingBuffer(&iColorBufferSize_, &pColorBuffer_);
6: }
7: else
8: {
9: iColorBufferSize_ = COLOR_WIDTH * COLOR_HEIGHT * COLOR_CHANNEL;
10: pColorBuffer_ = new BYTE[iColorBufferSize_];
11: pColorFrame_->CopyConvertedFrameDataToArray(iColorBufferSize_, pColorBuffer_, ColorImageFormat_Rgba);
12: }
1:取得預設的Color Image Format
4:6:直接取得Color Frame Buffer Size與Color Frame Buffer
假設預設的Kinect V2就是Rgba的話,那跟其他frame一樣,使用AccessRawUnderlyingBuffer來取得即可
9:11:將資料轉換為需要得格式並取出
若Image format不是自己需要的(OF為RGBA),就可用CopyConvertedFrameDataToArray的方式來轉換
跟AccessRawUnderlyingBuffer直接指向Buffer位置的方式不同,他是將Buffer的資料Copy到提供的array中
因此得告知轉換大小(Buffer Size),並給予對應的空間(new Buffer Size)
這裡的COLOR_WIDTH = 1920 COLOR_HEIGHT = 1080 COLOR_CHANNEL = 4
一般來說透過IFrameDescription來取得是較為恰當的作法
這個用法要特別記得pColorBuffer_在最後得release
不管你是在每一次update才使用的區域變數,還是整個class的成員變數
==================================================
在正式進入今天的程式碼前
還有兩件事情要先想清楚!
首先!到底要做什麼?
這篇的目標是呈現出擁有色彩資訊(Color Frame)的去背結果
去背的部份,是透過Body Index來對深度圖做出去背的效果
因此,要得到我們想要的結果有兩種作法:
特別是Kinect V2兩者的解析度差這麼多的情況下,實在沒道理用第二種作法
不過既然Kinect SDK都提供這個功能了!說不一定哪天會用到
再來!到底要怎麼做?
讓我們復習一下,Kinect共有三個空間:Camera Space(3D)、Color Space(2D)、Depth Space(2D)
無論上面的哪一種作法,都得面對Color Frame(Color Space)與Body Index Frame(Depth Space)之間的對應
這樣兩張影像空間的對應處理,稱為Image Mapping
(※最常出現這個詞的應該是在處理Image Warping中,由於本篇重點不在於影像處理,因此就不多敘述)
一般在做法上,會使用reverse-mapping的方式做處理(又稱Inverse-mapping)
因此,針對上面的兩種作法,具體的實現方式是:
ok, 搞清楚這兩件事情後,就該來看程式碼了!
首先是作法1
(※以下指列出主要處理的部份,關於資訊取得的程式碼請參考前篇)
Color Frame to Depth Space
4:6:直接取得Color Frame Buffer Size與Color Frame Buffer
假設預設的Kinect V2就是Rgba的話,那跟其他frame一樣,使用AccessRawUnderlyingBuffer來取得即可
9:11:將資料轉換為需要得格式並取出
若Image format不是自己需要的(OF為RGBA),就可用CopyConvertedFrameDataToArray的方式來轉換
跟AccessRawUnderlyingBuffer直接指向Buffer位置的方式不同,他是將Buffer的資料Copy到提供的array中
因此得告知轉換大小(Buffer Size),並給予對應的空間(new Buffer Size)
這裡的COLOR_WIDTH = 1920 COLOR_HEIGHT = 1080 COLOR_CHANNEL = 4
一般來說透過IFrameDescription來取得是較為恰當的作法
這個用法要特別記得pColorBuffer_在最後得release
不管你是在每一次update才使用的區域變數,還是整個class的成員變數
==================================================
在正式進入今天的程式碼前
還有兩件事情要先想清楚!
首先!到底要做什麼?
這篇的目標是呈現出擁有色彩資訊(Color Frame)的去背結果
去背的部份,是透過Body Index來對深度圖做出去背的效果
因此,要得到我們想要的結果有兩種作法:
- 在Color Frame上,根據對應的Body Index判斷是否要繪出該Pixel
- 在Body Index上,填入Color Frame中對應pixel的顏色
特別是Kinect V2兩者的解析度差這麼多的情況下,實在沒道理用第二種作法
不過既然Kinect SDK都提供這個功能了!說不一定哪天會用到
再來!到底要怎麼做?
讓我們復習一下,Kinect共有三個空間:Camera Space(3D)、Color Space(2D)、Depth Space(2D)
無論上面的哪一種作法,都得面對Color Frame(Color Space)與Body Index Frame(Depth Space)之間的對應
這樣兩張影像空間的對應處理,稱為Image Mapping
(※最常出現這個詞的應該是在處理Image Warping中,由於本篇重點不在於影像處理,因此就不多敘述)
一般在做法上,會使用reverse-mapping的方式做處理(又稱Inverse-mapping)
因此,針對上面的兩種作法,具體的實現方式是:
- 將Color Frame的pixel轉到Depth Space中來取得Body Index資訊
- 將Body Index的pixel轉到Color Space中來取得Color Frame資訊
ok, 搞清楚這兩件事情後,就該來看程式碼了!
首先是作法1
(※以下指列出主要處理的部份,關於資訊取得的程式碼請參考前篇)
Color Frame to Depth Space
1: DepthSpacePoint* pDepthCoordinate_ = nullptr;
2: pDepthCoordinate_ = new DepthSpacePoint[COLOR_WIDTH * COLOR_HEIGHT];
3: _pCoordinateMapper->MapColorFrameToDepthSpace(iDepthBufferSize_, pDepthBuffer_, COLOR_WIDTH*COLOR_HEIGHT, pDepthCoordinate_);
4:
5: ofPixels TmpDisplay_;
6: TmpDisplay_.allocate(COLOR_WIDTH, COLOR_HEIGHT, ofImageType::OF_IMAGE_COLOR);
7: unsigned char * acDisplay_ = TmpDisplay_.getPixels();
8:
9: for(int idx_ = 0; idx_ < (COLOR_WIDTH * COLOR_HEIGHT); ++idx_)
10: {
11: if(!_bBackgroundSub)
12: {
13: acDisplay_[idx_ * 3] = pColorBuffer_[idx_ * 4];
14: acDisplay_[idx_ * 3 + 1] = pColorBuffer_[idx_ * 4 + 1];
15: acDisplay_[idx_ * 3 + 2] = pColorBuffer_[idx_ * 4 + 2];
16: }
17: else
18: {
19: int iDepthX_ = static_cast<int>(pDepthCoordinate_[idx_].X + .5);
20: int iDepthY_ = static_cast<int>(pDepthCoordinate_[idx_].Y + .5);
21:
22: acDisplay_[idx_ * 3] = 0;
23: acDisplay_[idx_ * 3 + 1] = 0;
24: acDisplay_[idx_ * 3 + 2] = 0;
25: if(iDepthX_ >= 0 && iDepthX_ < DEPTH_WIDTH && iDepthY_ >= 0 && iDepthY_ < DEPTH_HEIGHT)
26: {
27: int iDepthDisplayIdx_ = iDepthX_ + (iDepthY_ * DEPTH_WIDTH);
28:
29: if(pBodyIndexBuffer_[iDepthDisplayIdx_] != 0xff)
30: {
31: acDisplay_[idx_ * 3] = pColorBuffer_[idx_ * 4];
32: acDisplay_[idx_ * 3 + 1] = pColorBuffer_[idx_ * 4 + 1];
33: acDisplay_[idx_ * 3 + 2] = pColorBuffer_[idx_ * 4 + 2];
34: }
35: }
36: }
37: }
38:
39: _Display.setFromPixels(acDisplay_, COLOR_WIDTH, COLOR_HEIGHT, OF_IMAGE_COLOR);
1:3:取得Color Frame轉換到Depth Space的對應座標
使用的function為MapColorFrameToDepthSpace,輸入的參數為:
其中第三個參數其實就是表示第四個array point的大小
※這裡要特別注意,雖然我們只要Body Index與Color Frame的資訊
但Kinect SDK提供的轉換功能需要用到Depth Frame才能計算轉換座標
若有其他方法的歡迎提供!!
5:8:初始化要呈現的影像
根據需求而定,若要加上底圖可以改為OF_IMAGE_COLOR_ALPHA
19:20:四捨五入取得Depth Space中的座標
一般的作法會是參考周邊的pixel並考慮權重來決定數值,不過這裡為了方便就直接四捨五入
25:35:根據Body Index的值決定要不要填入Color Frame資訊
轉換後得到的Depth Space座標是有可能在實際範圍外的
因此要先確認在範圍內後,再去取得數值(不然就會壞給你看)
當判斷不是軀體範圍時,Body Index的數值為255,因此只要Body Index不為255的就填入顏色
※由於Color Frame是RGBA,因此index的部份是x4
=====================================================
使用的function為MapColorFrameToDepthSpace,輸入的參數為:
- Depth Frame Buffer Size (512 x 424)
- Depth Frame Buffer
- Color Image Size (1920 x 1080)
- Depth Space Point(output)
其中第三個參數其實就是表示第四個array point的大小
※這裡要特別注意,雖然我們只要Body Index與Color Frame的資訊
但Kinect SDK提供的轉換功能需要用到Depth Frame才能計算轉換座標
若有其他方法的歡迎提供!!
5:8:初始化要呈現的影像
根據需求而定,若要加上底圖可以改為OF_IMAGE_COLOR_ALPHA
19:20:四捨五入取得Depth Space中的座標
一般的作法會是參考周邊的pixel並考慮權重來決定數值,不過這裡為了方便就直接四捨五入
25:35:根據Body Index的值決定要不要填入Color Frame資訊
轉換後得到的Depth Space座標是有可能在實際範圍外的
因此要先確認在範圍內後,再去取得數值(不然就會壞給你看)
當判斷不是軀體範圍時,Body Index的數值為255,因此只要Body Index不為255的就填入顏色
※由於Color Frame是RGBA,因此index的部份是x4
=====================================================
接下來是作法2!整個架構跟作法1其實大同小異
Body Index to Color Space
1: ColorSpacePoint* pColorCoordinate_ = nullptr;
2: pColorCoordinate_ = new ColorSpacePoint[DEPTH_WIDTH * DEPTH_HEIGHT];
3: _pCoordinateMapper->MapDepthFrameToColorSpace(iDepthBufferSize_, pDepthBuffer_, iDepthBufferSize_, pColorCoordinate_);
4:
5: ofPixels TmpDisplay_;
6: TmpDisplay_.allocate(DEPTH_WIDTH, DEPTH_HEIGHT, ofImageType::OF_IMAGE_COLOR);
7: unsigned char * acDisplay_ = TmpDisplay_.getPixels();
8:
9: for(int idx_ = 0; idx_ < iDepthBufferSize_; ++idx_)
10: {
11: if(_bBackgroundSub && pBodyIndexBuffer_[idx_] == 0xff)
12: {
13: acDisplay_[idx_ * 3] = 0;
14: acDisplay_[idx_ * 3 + 1] = 0;
15: acDisplay_[idx_ * 3 + 2] = 0;
16: continue;
17: }
18:
19: int iColorX_ = static_cast<int>(pColorCoordinate_[idx_].X + .5);
20: int iColorY_ = static_cast<int>(pColorCoordinate_[idx_].Y + .5);
21:
22: if(iColorX_ >= 0 && iColorX_ < COLOR_WIDTH && iColorY_ >= 0 && iColorY_ < COLOR_HEIGHT)
23: {
24: int iColorDisplayIdx_ = iColorX_ * COLOR_CHANNEL + (iColorY_ * COLOR_WIDTH * COLOR_CHANNEL);
25:
26: acDisplay_[idx_ * 3] = pColorBuffer_[iColorDisplayIdx_];
27: acDisplay_[idx_ * 3 + 1] = pColorBuffer_[iColorDisplayIdx_ + 1];
28: acDisplay_[idx_ * 3 + 2] = pColorBuffer_[iColorDisplayIdx_ + 2];
29: }
30: else
31: {
32: acDisplay_[idx_ * 3] = 0;
33: acDisplay_[idx_ * 3 + 1] = 0;
34: acDisplay_[idx_ * 3 + 2] = 0;
35: }
36: }
37:
38: _Display.setFromPixels(acDisplay_, DEPTH_WIDTH, DEPTH_HEIGHT, OF_IMAGE_COLOR);
1:3:取得Depth Frame轉換到Color Space的對應座標
使用的function為MapDepthFrameToColorSpace,輸入的參數為:
- Depth Frame Buffer Size (512 x 424)
- Depth Frame Buffer
- Depth Image Size (512 x 424)
- Color Space Point(output)
因為我們是要取得Depth Space中所有pixel對應在Color Space的座標
因此Size就是Depth Frame的大小(512 x 424)
22:34:填入Color Frame資訊
寫法跟作法1有點不同,不過整理概念是一樣的,只是檢查的先後順序有差
一樣要是用四捨五入的方式大概取得Color Space中的座標
並檢查是否在Color Frame的範圍內
要特別小心iColorDisplayIdx_的計算方式喔!
=====================================================
至於來亂的"PARTY"功能...
其實就只是用ofSetColor配合Blend Mode來完成
這裡就簡單列出部份程式碼
1: ofEnableBlendMode(OF_BLENDMODE_ADD);
2: if(_bIsColorToDepth)
3: {
4: ofSetColor(255, 0, 0);
5: _Display.draw(ofRandom(-20, 20),ofRandom(-20, 20), DEPTH_WIDTH, DEPTH_HEIGHT);
6: ofSetColor(0, 255, 0);
7: _Display.draw(ofRandom(-20, 20),ofRandom(-20, 20), DEPTH_WIDTH, DEPTH_HEIGHT);
8: ofSetColor(0, 0, 255);
9: _Display.draw(ofRandom(-20, 20),ofRandom(-20, 20), DEPTH_WIDTH, DEPTH_HEIGHT);
10: }
========分隔線========
以上就是利用RGB Camera進行去背
到此除了直接使用IR Camera外,大部份的基本功能都已經使用過了
下一篇就要來整合這到此為篇的結果,並加上Face Detection的功能
來做出一個較為完整的Demo
不好意思,請教一下,您video裡面應該是....作法一?根據我查到的這篇:
回覆刪除http://stackoverflow.com/questions/29084773/kinect-v2-for-windows-depth-to-color-image-misalignment-and
似乎因為depth與color的角度差問題,當想要在背景填入原來的color資訊時,會產生重影。
這問題....有解嗎?我想要在原先的color當背景下,再填入您現在的去背影像的話.......
很抱歉因為我在使用Kinect上還沒有這種需求
刪除所以並沒有繼續研究下去
方便請你多描述一下你的用途嗎?
看看能不能有沒有機會幫你避過這個問題
不好意思,我有在信箱看到你的留言~但不知道為什麼Blog就是看不到(刪掉了?
刪除根據提到的原始需求,我是理解成你想要取得在color frame(1920x1080)下的剪影?
其實這樣就等於是我Demo影片中的第四部份
"透過Body Index Frame,在Color Space上進行去背(00:41~00:52)"
那這個部份文中有提到,是對BodyIndex與Color Frame使用reverse-mapping
也就是方法一的部份!就是對每個Color Frames上的pixel計算在Body Index上的位置然後根據Body Index的值決定這個Pixel是不是剪影的一部份
這樣取得的去背結果就是高解析度的去背效果喔
不知道這樣有沒有幫到你啊...
我也覺得奇怪,明明送出了,但在頁面就是看不到。
刪除可以請教您對C#熟嗎? 我們因為是要放在Unity裡面寫,
所以它的版本目前只支援C#
不好意思,我現有段程式關於二維坐標與三維坐標間的轉換一直出不來,您能否幫忙看看?謝謝!
回覆刪除您好,
回覆刪除想請教一個有關Kinect2裡HighDefinitionFace的問題,
實現HDFace的步驟為:彩色影像->骨架資訊->建立HDFace,
我照著上面步驟抓取資訊完成第一步(彩色影像)與第二步(骨架資訊)後,
在第三步CreateHighDefinitionFaceFrameSource這個函示都會發生問題,
一直持續回傳failed值。
不過對照網路上幾個使用Direct2D實現HDFace的程式,
發現程式的資料結構大致上是相同的,
所以很困惑為什麼會出錯,
除了要include kinect.face.h和增加Kinect20.Face.lib,
還有其他"屬性設定"或"環境設定"的要求我沒有注意到的嗎?
謝謝您了。