kinect在openni下也能玩抠出人物换背景
之前想了个很拉风的名字《用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。
转载于:https://www.cnblogs.com/seacode/archive/2012/05/15/2502007.html
kinect在openni下也能玩抠出人物换背景相关推荐
- AE使用Keylight抠出人物身体教程-AE 人物抠像中文视频教程
分享一个AE使用Keylight抠出人物身体教程-AE 人物抠像教程,讲解的不错,中文讲解,超级实用 https://v.youku.com/v_show/id_XNDY5MDYxMzQ2NA==.h ...
- 用橡皮擦工具抠出人物和替换背景(每天一个PS小项目)
用魔术橡皮擦工具抠出人物 原图如下所示: 选择魔术橡皮擦工具.容差越大,擦除的范围越大.由于该图色调相似,所以应该将容差值设小,这里设为5: 选择人物图层,点击图中白色区域: 大部分白色区域被擦除后, ...
- 人物抠图换背景怎么做?教你几种抠图方法轻松抠出人物
怎么把图片中的人像抠出来换背景呢?在现代社会,照片已经成为人们生活的常见元素,而照片的后期处理也越来越受到人们的关注.当人们拍摄一张照片时,常常会想要将人像从背景中抠出来,以便更好地突出主题.此时,将 ...
- python抠出图片人像_Python+Paddlehub相片人像抠图精简源码实例
Python+Paddlehub相片人像抠图实例 无需PS软件,手动制作自己的抠图工具,在只有一张图片,需要细致地抠出人物的情况下,能帮你减少抠图步骤;在有多张图片需要抠的情况下,能直接帮你输出这些人 ...
- kinect的openni总结
Kinect到手快一个月了,期间查阅了很多资料,见识了很多牛人,他们的工作如此漂亮,让我大开眼界.现将自己所掌握的资料汇总于此,以便随时查阅. 首先是csdn上小斤童鞋的系列文章: Kinect开发教 ...
- 街机linux有安卓好吗,Ubuntu下用xmame玩街机游戏
Ubuntu下用xmame玩街机游戏 xmame是Windows下mame的Linux版,gxmame是xmame的图形化前端.mame号称是模拟效果最好.支持roms最多的模拟器,不过这个我就不清楚 ...
- linux 商业游戏,Ubuntu下安装试玩原生Linux版商业游戏Braid
这款原生Linux版商业游戏Braid国外出售价大约在80美元左右.每年在Linux平台上发布的原生商业游戏是屈指可数,而且这些游戏的质量也是参差不齐. 不过这款原生Linux版商业游戏Braid还是 ...
- 神经网络务实:Linux下用GPU玩转TensorFlow
神经网络务实:Linux下用GPU玩转TensorFlow 前言: 零.准备软硬件环境 1.主机:IBMs20工作站 2.显卡:GTX750TI显卡 3.操作系统:Ubuntu16 Linux 4.安 ...
- 在Windows下也可以玩Ghost
在Windows下也可以玩Ghost eNet硅谷动力 <script language="JavaScript" src="/tech/js/ad/con ...
最新文章
- 别把你的目光停留在周围
- earning Multi-Domain Convolutional Neural Networks for Visual Tracking
- Primavera 6.0
- WebAssembly的Qt
- ajax响应不显示值,Ajax响应200正常,但显示未能加载响应数据
- SPSS P小于0.05,结果真的有效吗?【SPSS 070期】
- java 生成32位字符串
- 以过来人身份给新手博主站长的几点小建议
- 深度学习笔记其一:基础知识和PYTORCH
- 信息论实验一:信源熵的计算
- 原生JS路由实现页面跳转
- i2c驱动之调用ioctl函数进行读写at24c08
- 广州网站建设哪些公司好
- C++阶段03笔记03【文件操作(文本文件读写、二进制文件读写)】
- 南京大学交叉培养计算机与金融招生人数,教务处组织召开计算机与金融工程实验班师生见面会...
- 智能音箱的差评|为什么我要买一堆垃圾回来吃灰!
- MAX30102的优缺点
- P1747 好奇怪的游戏
- describe 25% ,75%是什么意思
- ubuntu去除PDF文档密码
热门文章
- 诡异的DateTime.TryParseExact方法
- 消息队列mysql redis那个好_Redis与RabbitMQ作为消息队列的比较
- VS2008中源文件与模块生成时的文件不同,仍要让调试器使用它吗
- linux的system () 函数详解
- 最健康的睡眠时间究竟是多少?
- python每隔30s检查一次_用Python写一个“离线语音提示器”来提醒我们别忘记了时间...
- VS2010发布、打包安装程序超全超详细
- vue-cli项目引用文件/组件/库 的注意事项(一)
- Servlet拦截器
- golang反编译_【Golang】脱胎换骨的defer(一)