之前想了個很拉風的名字《用kinect玩穿越》,但是現在功能還不是很完善,細節處理也不是很好,臉皮沒有足夠的厚,所以呢還是叫換背景吧。
???? 這里面包含兩個技術要點:
一、摳出活動人物
????? 在微軟的SDK里深度圖像的前3位即0-2位就包含有玩家信息,所以提取很方便,可以參考博客http://www.cnblogs.com/yangecnu/archive/2012/04/04/KinectSDK_Depth_Image_Processing_Part1.html
而在openni下深度圖的每一位也是兩個字節16位,但沒有包含這方面的信息,所以就要找其他的方法來實現,在網上找了很久也沒有看到有人做了這方面的例子,反過頭來還得找openni的用戶手冊,發現有Scene Analyzer和User Generator可能可以達到我的要求。
?? (1)Scene Analyzer:Get Label Map: Provides a map in which each pixel has a meaningful label (它有一個區分前景和背景的功能,但我沒有具體去測試)
???(2)User Generator :Get User Pixels: Provides the pixels that represent the user. The output is a map of the pixels of the entire scene, where the pixels that represent the body are labeled User ID.(它有一個通過用戶的ID,來獲得用戶掩碼的功能,我就是用這個函數來做的)
??? 它的具體函數原型為:GetUserPixels (XnUserID user, SceneMetaData &smd) const
傳入的參數為用戶的ID和場景圖像的引用。
?
二、變換背景
???? 變換背景的方法為先將要更換的背景圖片存入到一個圖像數組,然后通過一個索引值index來獲取不同的背景圖像,那么這個索引值怎么改變呢,這里我用到了一個簡單的手勢識別,揮右手index加1,揮左手index減1(這里的手勢識別是通過骨骼數據的判斷來做的具體參考我的上一篇博客),最后將人物圖像疊加到背景圖片上,就可以實現說是的換背景功能了。說了這么多,先上圖吧。
實驗結果圖:
?圖片很模糊是因為投影儀的效果不好。可以看出由于沒做邊緣融合,圖像邊緣有很多的鋸齒。但做得好的話,設定好人物出現的區域和大小,真的會有穿越的感覺,哈哈!!
代碼:


??2?//從采集的視頻中扣出人物圖像,并疊加在背景上
??3?
??4?*******************************************************************************************/
??5?#include?<stdlib.h>???
??6?#include?<iostream>???
??7?#include?<vector>???
??8???
??9?#include?<XnCppWrapper.h>???
?10?#include?<XnModuleCppInterface.h>????
?11?#include?"cv.h"???
?12?#include?"highgui.h"???
?13?
?14?
?15?#include?<windows.h>
?16?
?17?using?namespace?std;??
?18?using?namespace?cv;??
?19???
?20??
?21???
?22?//【1】??三個生成器
?23?xn::UserGenerator?userGenerator;??
?24?xn::DepthGenerator?depthGenerator;??
?25?xn::ImageGenerator?imageGenerator;??
?26???
?27?/*?
?28?????XN_SKEL_HEAD??????????=?1,????XN_SKEL_NECK????????????=?2,?
?29???XN_SKEL_TORSO?????????=?3,????XN_SKEL_WAIST???????????=?4,?
?30?????XN_SKEL_LEFT_COLLAR????????=?5,????XN_SKEL_LEFT_SHOULDER????????=?6,?
?31???XN_SKEL_LEFT_ELBOW????????=?7,??XN_SKEL_LEFT_WRIST??????????=?8,?
?32???XN_SKEL_LEFT_HAND??????????=?9,????XN_SKEL_LEFT_FINGERTIP????=10,?
?33?????XN_SKEL_RIGHT_COLLAR????=11,????XN_SKEL_RIGHT_SHOULDER????=12,?
?34???XN_SKEL_RIGHT_ELBOW????????=13,??XN_SKEL_RIGHT_WRIST??????????=14,?
?35???XN_SKEL_RIGHT_HAND??????=15,????XN_SKEL_RIGHT_FINGERTIP????=16,?
?36?????XN_SKEL_LEFT_HIP??????????=17,????XN_SKEL_LEFT_KNEE????????????=18,?
?37???XN_SKEL_LEFT_ANKLE????????=19,??XN_SKEL_LEFT_FOOT????????????=20,?
?38???XN_SKEL_RIGHT_HIP??????????=21,????XN_SKEL_RIGHT_KNEE??????????=22,?
?39?????XN_SKEL_RIGHT_ANKLE????????=23,????XN_SKEL_RIGHT_FOOT??????????=24?????
?40?*/??
?41?//a?line?will?be?drawn?between?start?point?and?corresponding?end?point???
?42?int?startSkelPoints[14]={1,2,6,6,12,17,6,7,12,13,17,18,21,22};??
?43?int?endSkelPoints[14]={2,3,12,21,17,21,7,9,13,15,18,20,22,24};??
?44?
?45?//識別函數
?46?void?recogGesture(XnPoint3D??skelPointsIn[24],?int?flag,unsigned?int&?imgIndex);
?47?
?48???
?49?//?callback?function?of?user?generator:?new?user??//回調函數1?new?user?
?50?void?XN_CALLBACK_TYPE?NewUser(?xn::UserGenerator&?generator,?XnUserID?user,void*?pCookie?)??
?51?{??
?52?????cout?<<?"New?user?identified:?"?<<?user?<<?endl;??
?53?????//userGenerator.GetSkeletonCap().LoadCalibrationDataFromFile(?user,?"UserCalibration.txt"?);???
?54?????generator.GetPoseDetectionCap().StartPoseDetection("Psi",?user);??
?55?}??
?56???
?57?//?callback?function?of?user?generator:?lost?user???//回調函數2?lost?user
?58?void?XN_CALLBACK_TYPE?LostUser(?xn::UserGenerator&?generator,?XnUserID?user,void*?pCookie?)??
?59?{??
?60?????cout?<<?"User?"?<<?user?<<?"?lost"?<<?endl;??
?61?}??
?62???
?63?//?callback?function?of?skeleton:?calibration?start???//回調函數3?calibration?start
?64?void?XN_CALLBACK_TYPE?CalibrationStart(?xn::SkeletonCapability&?skeleton,XnUserID?user,void*?pCookie?)??
?65?{??
?66?????cout?<<?"Calibration?start?for?user?"?<<??user?<<?endl;??
?67?}??
?68???
?69?//?callback?function?of?skeleton:?calibration?end????//回調函數4?calibraton
?70?void?XN_CALLBACK_TYPE?CalibrationEnd(?xn::SkeletonCapability&?skeleton,XnUserID?user,XnCalibrationStatus?calibrationError,void*?pCookie?)??
?71?{??
?72?????cout?<<?"Calibration?complete?for?user?"?<<??user?<<?",?";??
?73?????if(?calibrationError==XN_CALIBRATION_STATUS_OK?)??
?74?????{??
?75?????????cout?<<?"Success"?<<?endl;??
?76?????????skeleton.StartTracking(?user?);??
?77?????????//userGenerator.GetSkeletonCap().SaveCalibrationDataToFile(user,?"UserCalibration.txt"?);???
?78?????}??
?79?????else??
?80?????{??
?81?????????cout?<<?"Failure"?<<?endl;??
?82?????????//For?the?current?version?of?OpenNI,?only?Psi?pose?is?available??//重新調用姿勢檢測函數
?83?????????((xn::UserGenerator*)pCookie)->GetPoseDetectionCap().StartPoseDetection(?"Psi",?user?);??
?84?????}??
?85?}??
?86???
?87?//?callback?function?of?pose?detection:?pose?start???//回調函數5?姿勢檢測,現只支持Psi姿勢
?88?void?XN_CALLBACK_TYPE?PoseDetected(?xn::PoseDetectionCapability&?poseDetection,const?XnChar*?strPose,XnUserID?user,void*?pCookie)??
?89?{??
?90?????cout?<<?"Pose?"?<<?strPose?<<?"?detected?for?user?"?<<??user?<<?endl;??
?91?????((xn::UserGenerator*)pCookie)->GetSkeletonCap().RequestCalibration(?user,?FALSE?);??
?92?????poseDetection.StopPoseDetection(?user?);??
?93?}??
?94???
?95?void?clearImg(IplImage*?inputimg)??
?96?{??
?97?????CvFont?font;??
?98?????cvInitFont(?&font,?CV_FONT_VECTOR0,1,?1,?0,?3,?5);??
?99?????memset(inputimg->imageData,255,640*480*3);??
100?}??
101???
102???
103?int?main(?int?argc,?char**?argv?)??
104?{??
105?????char?key=0;??
106???
107????unsigned?int?imageBGIndex?=?0;?//背景圖片的序號
108?????//?initial?context???
109?????xn::Context?context;??
110?????context.Init();??
111?????xn::ImageMetaData?imageMD;//openni中的圖像數據??
112?????
113?????//場景人物掩碼數據
114?????xn::SceneMetaData?sceneMD;//人物掩碼數據
115?
116?????//背景圖片數組
117?????IplImage*?imgSceneBGs[10];
118?
119?????char?imageBGnames[50];?//背景圖片名字
120?
121?????for(int?i=0;i<10;i++)?
122?????{
123?????????sprintf(imageBGnames,"d:\\pic\\%d.jpg",i);
124?????????IplImage*?imgBackground?=?cvLoadImage(imageBGnames,1);
125?????????imgSceneBGs[i]?=?cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);//給這10個指針分配內存
126?
127?????????cvResize(imgBackground,imgSceneBGs[i],CV_INTER_LINEAR);//改變背景圖像大小,存入數組
128?????}
129???
130?????IplImage*?cameraImg=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);??
131?
132?????IplImage*?imgScene16u=cvCreateImage(cvSize(640,480),IPL_DEPTH_16U,1);//人物掩碼原始數據
133?
134?????IplImage*?imgSceneShow=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);//人物掩碼顯示數據
135?
136?????IplImage*?imgSceneRGB?=?cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);//人物RGB顯示數據
137?
138?????
139?
140?????//IplImage*?imgSceneBG?=?cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);//背景RGB顯示數據
141?????
142?
143?????IplImage*?imgMerge?=?cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);//融合圖像
144?
145?????cvNamedWindow("311B實驗室",0);??
146?????//cvNamedWindow("FIGURE",0);
147?
148?????//?map?output?mode???
149?????XnMapOutputMode?mapMode;??
150?????mapMode.nXRes?=?640;??
151?????mapMode.nYRes?=?480;??
152?????mapMode.nFPS?=?30;??
153???
154?????//?create?generator???
155?????depthGenerator.Create(?context?);??
156?????depthGenerator.SetMapOutputMode(?mapMode?);??
157?????imageGenerator.Create(?context?);??
158?????userGenerator.Create(?context?);??
159???
160???//【2】???
161?????//?Register?callback?functions?of?user?generator?//為userGenerator注冊回調函數?
162?????XnCallbackHandle?userCBHandle;??
163?????userGenerator.RegisterUserCallbacks(?NewUser,?LostUser,?NULL,?userCBHandle?);??
164???
165???//【3】???
166?????//?Register?callback?functions?of?skeleton?capability??//為skeletonCap骨架校正注冊回調函數?
167?????xn::SkeletonCapability?skeletonCap?=?userGenerator.GetSkeletonCap();??
168?????skeletonCap.SetSkeletonProfile(?XN_SKEL_PROFILE_ALL?);??
169?????XnCallbackHandle?calibCBHandle;??
170?????skeletonCap.RegisterToCalibrationStart(?CalibrationStart,&userGenerator,?calibCBHandle?);??
171?????skeletonCap.RegisterToCalibrationComplete(?CalibrationEnd,&userGenerator,?calibCBHandle?);??
172???
173???//【4】???
174?????//?Register?callback?functions?of?Pose?Detection?capability???
175?????XnCallbackHandle?poseCBHandle;??
176?????userGenerator.GetPoseDetectionCap().RegisterToPoseDetected(?PoseDetected,&userGenerator,?poseCBHandle?);??
177???
178?
179?????//【4-1】矯正視角
180??????depthGenerator.GetAlternativeViewPointCap().SetViewPoint(?imageGenerator?);
181?????//?start?generate?data???
182?????context.StartGeneratingAll();??
183?
184?????
185?
186?????while(?key!=27?)??
187?????{??
188?????????context.WaitAndUpdateAll();??
189???
190?????????imageGenerator.GetMetaData(imageMD);??
191?????????memcpy(cameraImg->imageData,imageMD.Data(),640*480*3);??
192?????????cvCvtColor(cameraImg,cameraImg,CV_RGB2BGR);??
193?????????//?get?users???
194?????????XnUInt16?userCounts?=?userGenerator.GetNumberOfUsers();??
195?????????if(?userCounts?>?0?)??
196?????????{??
197?????????????XnUserID*?userID?=?new?XnUserID[userCounts];??
198?????????????userGenerator.GetUsers(?userID,?userCounts?);??
199?
200?????????????
201?????????????for(?int?i?=?0;?i?<?userCounts;?++i?)?//循環每個用戶?
202?????????????{??
203?
204?
205?????????????????//獲取用戶掩碼的圖像
206?????????????????userGenerator.GetUserPixels(userID[i],sceneMD);
207?????????????????memcpy(imgScene16u->imageData,sceneMD.Data(),640*480*2);//復制內存
208?????????????????cvConvertScale(imgScene16u,imgSceneShow,255/4.0,0);
209?
210?
211?
212?????????????????//【5】???
213?????????????????//?if?is?tracking?skeleton???
214?????????????????if(?skeletonCap.IsTracking(?userID[i]?)?)??
215?????????????????{??
216?????????????????????XnPoint3D?skelPointsIn[24],skelPointsOut[24];??
217?????????????????????XnSkeletonJointTransformation?mJointTran;??
218?????????????????????for(int?iter=0;iter<24;iter++)??
219?????????????????????{??
220?????????????????????????//XnSkeletonJoint?from?1?to?24?????????????
221?????????????????????????????????????????????????skeletonCap.GetSkeletonJoint(?userID[i],XnSkeletonJoint(iter+1),?mJointTran?);??
222?????????????????????????skelPointsIn[iter]=mJointTran.position.position;??
223?
224?????????????????????????
225?????????????????????}??
226?
227?????????????????????//識別動作并發送按鍵消息
228??????????????????????recogGesture(skelPointsIn,?i%2,imageBGIndex);
229?
230?????????????????????//將數據轉換到投影坐標系
231?????????????????????depthGenerator.ConvertRealWorldToProjective(24,skelPointsIn,skelPointsOut);??
232?
233?????????????????????//【6】畫圖???
234?????????????????/*????for(int?d=0;d<1;d++)??
235?????????????????????{??
236?????????????????????????CvPoint?startpoint?=?cvPoint(skelPointsOut[startSkelPoints[d]-1].X,skelPointsOut[startSkelPoints[d]-1].Y);??
237?????????????????????????CvPoint?endpoint?=?cvPoint(skelPointsOut[endSkelPoints[d]-1].X,skelPointsOut[endSkelPoints[d]-1].Y);??
238???????????????
239?????????????????????????cvCircle(cameraImg,startpoint,5,CV_RGB(0,0,255),12);??
240????????????????????????//?cvCircle(cameraImg,endpoint,3,CV_RGB(0,0,255),12);??
241????????????????????????//?cvLine(cameraImg,startpoint,endpoint,CV_RGB(0,0,255),4);??
242?????????????????????}??*/
243?????????????????}??
244?????????????}??
245?????????????delete?[]?userID;??
246?????????}??
247?????????//memset(imgSceneRGB->imageData,0,640*480*3);?//顯示數據清0
248?????????
249?????????//cvCopy(cameraImg,imgSceneRGB,imgSceneShow);
250?
251?????????cvCopy(imgSceneBGs[imageBGIndex%10],imgMerge);?//選擇一個背景并復制背景圖像
252?
253?????????//cvAdd(imgMerge,?cameraImg,imgMerge,imgSceneShow);//加入摳出的人物圖像
254?????????
255?????????cvCopy(cameraImg,imgMerge,imgSceneShow);//加入摳出的人物圖像
256?
257?????????cvShowImage("311B實驗室",imgMerge);??
258?????????//cvShowImage("FIGURE",imgSceneShow);
259???
260?????????key=cvWaitKey(30);??
261?????????
262???
263?????}??
264?????//?stop?and?shutdown???
265?????cvDestroyWindow("Camera");??
266?????cvReleaseImage(&cameraImg);??
267?????context.StopGeneratingAll();??
268?????context.Shutdown();??
269???
270?????return?0;??
271?}??
272?
273?/*********************************************************************************************
274?//動作識別函數,
275?(根據骨架坐標點識別動作,并觸發相應的虛擬按鍵,后期這兩個功能需要用兩個函數實現)
276?//輸入:skelPointsIn[24]骨骼數據,用戶標識?flag,背景圖片序號
277?
278?**********************************************************************************************/
279?void?recogGesture(XnPoint3D??skelPointsIn[24],?int?flag,unsigned?int&?imgIndex)
280?{
281?????????????????????
282?????????//上一幀手的z坐標
283????????static??float?preLeftHandZ,?preRightHandZ;
284?????
285?????????//獲取頭和右手的坐標點
286?????????XnPoint3D?skelPointHead,skelPointRightHand,skelPointLeftHand;
287?????????//軀干中心
288?????????XnPoint3D?skelPointTorso;
289?????????//XnPoint3D?skelPointRightTip;
290?????????skelPointHead?=?skelPointsIn[XN_SKEL_HEAD-1];
291?????????skelPointRightHand?=?skelPointsIn[XN_SKEL_RIGHT_HAND-1];
292?????????skelPointLeftHand?=?skelPointsIn[XN_SKEL_LEFT_HAND-1];
293?
294?????????//軀干中心點數據
295?????????skelPointTorso?=?skelPointsIn[XN_SKEL_TORSO-1];
296??????????
297??????????
298?????????//判斷右手出拳動作
299?????????if(skelPointHead.Z-skelPointRightHand.Z>400.0&&//?大于頭部坐標一定位置
300?????????????skelPointHead.Z-preRightHandZ<=400.0&&
301?????????????skelPointRightHand.Y-skelPointTorso.Y>50)//高于軀干中心100
302?????????????{
303?????????????????/*//發送按鍵->
304?????????????????keybd_event(37,0,0,0);
305?????????????????Sleep(15);?//不知道有沒有用??
306?????????????????keybd_event(37,0,KEYEVENTF_KEYUP,0);*/
307?????????????????++imgIndex?;
308?????????????
309?????????????????????????
310?????????????}
311?
312?????????//判斷左手出拳動作
313?????????if(skelPointHead.Z?-skelPointLeftHand.Z?>400.0&&//大于頭部Z坐標480
314?????????????skelPointHead.Z?-preLeftHandZ<=400.0&&
315?????????????skelPointLeftHand.Y-skelPointTorso.Y>50)//高于軀干中心100
316?????????{
317?
318?
319?????????????/*????//發送按鍵<-
320?????????????keybd_event(39,0,0,0);
321?????????????Sleep(15);
322?????????????keybd_event(39,0,KEYEVENTF_KEYUP,0);*/
323?
324?????????????--imgIndex;
325?
326?????????????????????????
327?????????}
328?
329?????????//判斷前進動作,兩手低下向前
330?????/*if(skelPointHead.Z-skelPointRightHand.Z>150&&skelPointHead.Z-skelPointLeftHand.Z>150&&//雙手手Z<頭Z200
331?????????????skelPointTorso.Y-skelPointRightHand.Y>150&&skelPointTorso.Y-skelPointLeftHand.Y>150)?//雙手Y低于軀干Y200
332?????????????
333?????????{
334?????
335?????????????//發送按鍵F5
336?????????????keybd_event(116,0,0,0);
337?????????????Sleep(15);
338?????????????keybd_event(116,0,KEYEVENTF_KEYUP,0);
339?????????
340?????????}*/
341?
342?????????//判斷后退動作,兩手低下向后
343?
344?????/*????????if(skelPointRightHand.Z-skelPointHead.Z>150&&skelPointLeftHand.Z-skelPointHead.Z>150&&?//雙手手Z>頭Z200
345?????????????skelPointTorso.Y-skelPointRightHand.Y>150&&skelPointTorso.Y-skelPointLeftHand.Y>150)//雙手Y低于軀干Y200
346?????????{
347?????????????//發送按鍵A
348?????????????keybd_event('A',0,0,0);
349?????????????Sleep(5);
350?????????????keybd_event('A',0,KEYEVENTF_KEYUP,0);
351?????????
352?????????
353?????????*/
354?????????????
355?????????//保留手坐標的歷史數據
356?????????preLeftHandZ?=?skelPointLeftHand.Z;
357?????????preRightHandZ?=?skelPointRightHand.Z;
358?????
359?
360?}
代碼沒時間來整理優化,寫的比較粗糙,歡迎大家拍磚。其中骨骼數據提取參考了小斤的博客http://blog.csdn.net/chenxin_130/article/details/6950480。
?
???