samer@0
|
1 #include "testApp.h"
|
samer@0
|
2
|
samer@0
|
3 #define IMGWIDTH 640
|
samer@0
|
4 #define IMGHEIGHT 480
|
samer@0
|
5
|
samer@0
|
6 static int Colors[6][3] = {
|
samer@0
|
7 255, 0, 0,
|
samer@0
|
8 0, 200, 0,
|
samer@0
|
9 0, 0, 255,
|
samer@0
|
10 200, 175, 0,
|
samer@0
|
11 0, 200, 255,
|
samer@0
|
12 255, 0, 255
|
samer@0
|
13 };
|
samer@0
|
14
|
samer@0
|
15
|
samer@0
|
16 /* Not using these for the time being
|
samer@0
|
17 // Callback: A user was found
|
samer@0
|
18 void XN_CALLBACK_TYPE NewUserDetected(UserGenerator& rGenerator,XnUserID nID ,void* pCookie) {
|
samer@0
|
19 printf("New User %d\n", nID);
|
samer@0
|
20 send_osc_i(((testApp*)pCookie)->sender),"/birth",nID);
|
samer@0
|
21 }
|
samer@0
|
22
|
samer@0
|
23 // Callback: An existing user was lost
|
samer@0
|
24 void XN_CALLBACK_TYPE UserLostDetected(UserGenerator& rGenerator ,XnUserID nID ,void* pCookie) {
|
samer@0
|
25 printf("Lost user %d\n", nID);
|
samer@0
|
26 send_osc_i(((testApp*)pCookie)->sender),"/death",nID);
|
samer@0
|
27 }
|
samer@0
|
28 */
|
samer@0
|
29
|
samer@0
|
30 static void inline send_osc_i(ofxOscSender& sender, const char *path, int arg) {
|
samer@0
|
31 ofxOscMessage msg;
|
samer@0
|
32 msg.setAddress(path);
|
samer@0
|
33 msg.addIntArg(arg);
|
samer@0
|
34 sender.sendMessage(msg);
|
samer@0
|
35 }
|
samer@0
|
36
|
samer@0
|
37 static inline void check_rc(XnStatus rc, const char *tag){
|
samer@0
|
38 if (rc != XN_STATUS_OK) {
|
samer@0
|
39 printf("FAILURE: %s - %s\n",tag,xnGetStatusString(rc));
|
samer@0
|
40 OF_EXIT_APP(0);
|
samer@0
|
41 }
|
samer@0
|
42 }
|
samer@0
|
43
|
samer@0
|
44 static inline int check_nonfatal(XnStatus rc, const char *tag){
|
samer@0
|
45 if (rc != XN_STATUS_OK) {
|
samer@0
|
46 printf("WARNING: %s - %s\n",tag,xnGetStatusString(rc));
|
samer@0
|
47 return 1;
|
samer@0
|
48 } else return 0;
|
samer@0
|
49 }
|
samer@0
|
50
|
samer@0
|
51 //--------------------------------------------------------------
|
samer@0
|
52
|
samer@0
|
53 class int3d {
|
samer@0
|
54 public:
|
samer@0
|
55 int X, Y, Z;
|
samer@0
|
56 inline void set(int x, int y, int z) { X=x; Y=y; Z=z; }
|
samer@0
|
57 inline void add(int x, int y, int z) { X+=x; Y+=y; Z+=z; }
|
samer@0
|
58 inline void div(float d, XnVector3D *p) { p->X=X/d; p->Y=Y/d; p->Z=Z/d; }
|
samer@0
|
59 inline void cpy(XnVector3D *p) { p->X=X; p->Y=Y; p->Z=Z; }
|
samer@0
|
60 };
|
samer@0
|
61
|
samer@0
|
62 static void project_into_plane(XnPlane3D& plane, XnVector3D& r, XnVector3D *q) {
|
samer@0
|
63 float l = (r.X-plane.ptPoint.X)*plane.vNormal.X
|
samer@0
|
64 + (r.Y-plane.ptPoint.Y)*plane.vNormal.Y
|
samer@0
|
65 + (r.Z-plane.ptPoint.Z)*plane.vNormal.Z;
|
samer@0
|
66
|
samer@0
|
67 q->X = r.X - l*plane.vNormal.X;
|
samer@0
|
68 q->Y = r.Y - l*plane.vNormal.Y;
|
samer@0
|
69 q->Z = r.Z - l*plane.vNormal.Z;
|
samer@0
|
70 }
|
samer@0
|
71
|
samer@0
|
72 class UserStats {
|
samer@0
|
73 public:
|
samer@0
|
74 struct int3d left, right, top, bottom, sum;
|
samer@0
|
75 int numPixels;
|
samer@0
|
76
|
samer@0
|
77 UserStats() {
|
samer@0
|
78 left.X=IMGWIDTH;
|
samer@0
|
79 top.Y=IMGHEIGHT;
|
samer@0
|
80 right.X=bottom.Y=0;
|
samer@0
|
81 sum.X=sum.Y=sum.Z=0;
|
samer@0
|
82 numPixels=0;
|
samer@0
|
83 }
|
samer@0
|
84
|
samer@0
|
85 inline int area() { return numPixels; }
|
samer@0
|
86 inline void centroid(XnVector3D *p) { sum.div(numPixels,p); }
|
samer@0
|
87 inline void accum(int x, int y, int z) {
|
samer@0
|
88 if (x<left.X) left.set(x,y,z);
|
samer@0
|
89 if (x>right.X) right.set(x,y,z);
|
samer@0
|
90 if (y<top.Y) top.set(x,y,z);
|
samer@0
|
91 if (y>bottom.Y) bottom.set(x,y,z);
|
samer@0
|
92 sum.add(x,y,z);
|
samer@0
|
93 numPixels++;
|
samer@0
|
94 }
|
samer@0
|
95
|
samer@0
|
96 };
|
samer@0
|
97
|
samer@0
|
98
|
samer@0
|
99 //--------------------------------------------------------------
|
samer@0
|
100
|
samer@0
|
101 testApp::testApp(const char *host, int port, int FPS) {
|
samer@0
|
102 ofSetupOpenGL(&window, IMGWIDTH,IMGHEIGHT, OF_WINDOW);
|
samer@8
|
103 ofSetWindowTitle("Melody Triangle Tracker");
|
samer@0
|
104 ofSetFrameRate(FPS);
|
samer@0
|
105
|
samer@0
|
106 // initialise OSC sender
|
samer@0
|
107 printf("OSC target: %s:%d.\n",host,port);
|
samer@0
|
108 printf("Will run at %d frames per second.\n",FPS);
|
samer@0
|
109 sender.setup(host, port);
|
samer@0
|
110 draw_image=2;
|
samer@0
|
111 }
|
samer@0
|
112
|
samer@0
|
113 static XnMapOutputMode fps_map_mode(int fps) {
|
samer@0
|
114 XnMapOutputMode mapMode;
|
samer@0
|
115
|
samer@0
|
116 mapMode.nXRes = IMGWIDTH;
|
samer@0
|
117 mapMode.nYRes = IMGHEIGHT;
|
samer@0
|
118 mapMode.nFPS = fps;
|
samer@0
|
119 return mapMode;
|
samer@0
|
120 }
|
samer@0
|
121
|
samer@0
|
122 void testApp::setup() {
|
samer@0
|
123
|
samer@0
|
124 font.loadFont("frabk.ttf", 12);
|
samer@0
|
125 check_rc(context.Init(),"initialise context");
|
samer@0
|
126
|
samer@0
|
127 {
|
samer@0
|
128 XnLicense license;
|
samer@0
|
129 strcpy(license.strVendor,"PrimeSense");
|
samer@0
|
130 strcpy(license.strKey,"0KOIk2JeIBYClPWVnMoRKn5cdY4=");
|
samer@0
|
131 check_rc(context.AddLicense(license),"add license");
|
samer@0
|
132 }
|
samer@0
|
133
|
samer@0
|
134 check_rc(depthGenerator.Create(context),"create depth generator");
|
samer@0
|
135 check_rc(depthGenerator.SetMapOutputMode(fps_map_mode(30)),"config depth generator");
|
samer@0
|
136 check_rc(sceneAnalyzer.Create(context),"create scene analyzer");
|
samer@0
|
137 check_rc(sceneAnalyzer.SetMapOutputMode(fps_map_mode(30)),"config scene analyzer");
|
samer@0
|
138 check_rc(userGenerator.Create(context),"create user generator");
|
samer@0
|
139
|
samer@0
|
140 //XnCallbackHandle user_cb_handle;
|
samer@0
|
141 //userGenerator.RegisterUserCallbacks(NewUserDetected,UserLostDetected,this,user_cb_handle);
|
samer@0
|
142
|
samer@0
|
143 max_depth = depthGenerator.GetDeviceMaxDepth();
|
samer@0
|
144 printf("Depth generator max depth=%d.\n",max_depth);
|
samer@0
|
145
|
samer@0
|
146 imagePixels = new unsigned char[IMGWIDTH*IMGHEIGHT];
|
samer@0
|
147 userImage.allocate(IMGWIDTH, IMGHEIGHT, OF_IMAGE_GRAYSCALE);
|
samer@0
|
148 for(int i=0;i<MAX_USERS;i++) users[i].present=false;
|
samer@0
|
149 ofBackground(255, 255, 255);
|
samer@0
|
150
|
samer@0
|
151 printf("Ready to start generating.\n");
|
samer@0
|
152 check_rc( context.StartGeneratingAll(), "start generating");
|
samer@0
|
153 }
|
samer@0
|
154
|
samer@0
|
155
|
samer@0
|
156 //--------------------------------------------------------------
|
samer@0
|
157 void testApp::update() {
|
samer@0
|
158 if (check_nonfatal(context.WaitOneUpdateAll(depthGenerator),"update")) return;
|
samer@0
|
159
|
samer@0
|
160 timestamp = depthGenerator.GetTimestamp();
|
samer@0
|
161 got_floor = (sceneAnalyzer.GetFloor(floor_pie)==XN_STATUS_OK);
|
samer@0
|
162 if (got_floor) { // project camera onto floor
|
samer@0
|
163 XnVector3D origin = {0,0,0};
|
samer@0
|
164 project_into_plane(floor_pie, origin, &floor_offset);
|
samer@0
|
165 }
|
samer@0
|
166
|
samer@0
|
167 const XnDepthPixel *depthMap = depthGenerator.GetDepthMap();
|
samer@0
|
168
|
samer@0
|
169 // maybe transfer depth map to image
|
samer@0
|
170 if (draw_image==2)
|
samer@0
|
171 for (int i=0; i<IMGWIDTH*IMGHEIGHT; i++)
|
samer@0
|
172 imagePixels[i] = (255*depthMap[i])/max_depth;
|
samer@0
|
173
|
samer@0
|
174 // get pixels for all users, for each compute centroid and bounding rectangle
|
samer@0
|
175 {
|
samer@0
|
176 UserStats stats[MAX_USERS];
|
samer@0
|
177
|
samer@0
|
178 userGenerator.GetUserPixels(0, sceneMetaData); // 0 for all users
|
samer@0
|
179 unsigned short *userRawPixels = (unsigned short*)sceneMetaData.Data();
|
samer@0
|
180
|
samer@0
|
181 if (draw_image==1) {
|
samer@0
|
182 for (int i=0, y=0; y<IMGHEIGHT; y++) {
|
samer@0
|
183 for (int x=0; x<IMGWIDTH; x++, i++) {
|
samer@0
|
184 int id=userRawPixels[i];
|
samer@8
|
185 if (id>0 && id<=MAX_USERS) {
|
samer@0
|
186 stats[id-1].accum(x,y,depthMap[i]);
|
samer@0
|
187 imagePixels[i]=(128/MAX_USERS)*id;
|
samer@0
|
188 } else imagePixels[i] = 255;
|
samer@0
|
189 }
|
samer@0
|
190 }
|
samer@0
|
191 } else {
|
samer@0
|
192 for (int i=0, y=0; y<IMGHEIGHT; y++) {
|
samer@0
|
193 for (int x=0; x<IMGWIDTH; x++, i++) {
|
samer@0
|
194 int id=userRawPixels[i];
|
samer@8
|
195 if (id>0 && id<=MAX_USERS) {
|
samer@8
|
196 stats[id-1].accum(x,y,depthMap[i]);
|
samer@8
|
197 }
|
samer@0
|
198 }
|
samer@0
|
199 }
|
samer@0
|
200 }
|
samer@0
|
201
|
samer@0
|
202 for (int id0=0; id0<MAX_USERS; id0++) {
|
samer@0
|
203 if (stats[id0].area()>0) {
|
samer@0
|
204 if (!users[id0].present) {
|
samer@0
|
205 send_osc_i(sender,"/birth",id0+1);
|
samer@8
|
206 send_osc_i(sender,"/randinit",id0+1);
|
hekeus@5
|
207 send_osc_i(sender,"/start",id0+1);
|
samer@0
|
208 users[id0].present=true;
|
samer@0
|
209 }
|
samer@0
|
210 update_user(id0+1,stats[id0],&users[id0]);
|
samer@0
|
211 } else {
|
samer@0
|
212 if (users[id0].present) {
|
samer@0
|
213 send_osc_i(sender,"/death",id0+1);
|
samer@0
|
214 users[id0].present=false;
|
samer@0
|
215 }
|
samer@0
|
216 }
|
samer@0
|
217 }
|
samer@0
|
218
|
samer@0
|
219 }
|
samer@0
|
220 if (draw_image) {
|
samer@0
|
221 userImage.setFromPixels(imagePixels, IMGWIDTH, IMGHEIGHT, OF_IMAGE_GRAYSCALE);
|
samer@0
|
222 }
|
samer@0
|
223 }
|
samer@0
|
224
|
samer@0
|
225 void testApp::update_user(int id, UserStats& stats, UserData *user) {
|
samer@0
|
226 XnVector3D proj[3], real[3];
|
samer@0
|
227 int *coors=user->plot_coors;
|
samer@0
|
228
|
samer@0
|
229 stats.centroid(&proj[0]); // centroid in device coordinate
|
samer@0
|
230 stats.left.cpy(&proj[1]); // left edge in device coordinates
|
samer@0
|
231 stats.right.cpy(&proj[2]); // right edge in device coordinates
|
samer@0
|
232
|
samer@0
|
233 // save values required for drawing later
|
samer@0
|
234 coors[0]=floor(proj[0].X);
|
samer@0
|
235 coors[1]=floor(proj[0].Y);
|
samer@0
|
236 coors[2]=floor(proj[0].Z);
|
samer@0
|
237 coors[3]=stats.left.X;
|
samer@0
|
238 coors[4]=stats.right.X;
|
samer@0
|
239 coors[5]=stats.top.Y;
|
samer@0
|
240 coors[6]=stats.bottom.Y;
|
samer@0
|
241
|
samer@0
|
242 if (got_floor) {
|
samer@0
|
243 ofxOscMessage m;
|
samer@0
|
244 XnVector3D on_floor;
|
samer@0
|
245
|
samer@0
|
246 // convert to camera-centric world coordinates
|
samer@0
|
247 depthGenerator.ConvertProjectiveToRealWorld(3,proj,real);
|
samer@0
|
248
|
samer@0
|
249 // projection of centroid onto floor...
|
samer@0
|
250 project_into_plane(floor_pie,real[0],&on_floor);
|
samer@0
|
251
|
samer@0
|
252 // ...relative to projection of camera on floor
|
samer@0
|
253 on_floor.X -= floor_offset.X;
|
samer@0
|
254 on_floor.Y -= floor_offset.Y;
|
samer@0
|
255 on_floor.Z -= floor_offset.Z;
|
samer@0
|
256
|
samer@0
|
257 m.setAddress("/track");
|
samer@0
|
258 m.addIntArg(id);
|
samer@0
|
259
|
samer@0
|
260 m.addFloatArg(on_floor.X);
|
samer@0
|
261 // m.addFloatArg(on_floor.Y); // not interesting
|
samer@0
|
262 m.addFloatArg(on_floor.Z);
|
samer@0
|
263
|
samer@0
|
264 { // distances to edges of bounding box
|
samer@0
|
265 // corrected for perspective
|
samer@0
|
266 float z=coors[2]; // depth
|
samer@0
|
267 m.addIntArg(z*(coors[0]-coors[3]));
|
samer@0
|
268 m.addIntArg(z*(coors[4]-coors[0]));
|
samer@0
|
269 m.addIntArg(z*(coors[1]-coors[5]));
|
samer@0
|
270 m.addIntArg(z*(coors[6]-coors[1]));
|
samer@0
|
271 // area, corrected for perspective
|
samer@0
|
272 m.addIntArg(z*sqrt(stats.area()));
|
samer@0
|
273 }
|
samer@0
|
274
|
samer@0
|
275 sender.sendMessage(m);
|
samer@0
|
276 }
|
samer@0
|
277 }
|
samer@0
|
278
|
samer@0
|
279 //--------------------------------------------------------------
|
samer@0
|
280 static inline void draw_rect(float x1, float x2, float y1, float y2){
|
samer@0
|
281 ofLine(x1,y1,x1,y2);
|
samer@0
|
282 ofLine(x1,y1,x2,y1);
|
samer@0
|
283 ofLine(x1,y2,x2,y2);
|
samer@0
|
284 ofLine(x2,y1,x2,y2);
|
samer@0
|
285 }
|
samer@0
|
286
|
samer@0
|
287
|
samer@0
|
288 void testApp::draw(){
|
samer@0
|
289 char buf[64];
|
samer@0
|
290 ofSetColor(255,255,255);
|
samer@8
|
291 ofScale(ofGetWidth()/(float)IMGWIDTH,ofGetHeight()/(float)IMGHEIGHT);
|
samer@0
|
292
|
samer@0
|
293 if (draw_image) userImage.draw(0, 0, IMGWIDTH, IMGHEIGHT);
|
samer@0
|
294 // ofSetColor(0);
|
samer@0
|
295 // ofLine(IMGWIDTH,0,IMGWIDTH,IMGHEIGHT);
|
samer@0
|
296
|
samer@0
|
297 if (draw_time) {
|
samer@0
|
298 ofSetColor(180,0,0);
|
samer@0
|
299 sprintf(buf,"%llu",timestamp);
|
samer@0
|
300 font.drawString(buf,0,IMGHEIGHT-8);
|
samer@0
|
301 }
|
samer@0
|
302
|
samer@0
|
303 for(int k=0; k<MAX_USERS; k++) {
|
samer@0
|
304 if (users[k].present) {
|
samer@0
|
305 int *coors=users[k].plot_coors; // pointer to coors for this user
|
samer@0
|
306 int *color=Colors[k%6];
|
samer@0
|
307
|
samer@0
|
308 ofSetColor(color[0],color[1],color[2]);
|
samer@0
|
309 ofRect(coors[0],coors[1],10,10);
|
samer@0
|
310 font.drawString(ofToString(k+1), coors[0]-10, coors[1]-10);
|
samer@0
|
311 draw_rect(coors[3],coors[4],coors[5],coors[6]);
|
samer@0
|
312 // ofRect(coors[0]+IMGWIDTH, (IMGHEIGHT*coors[2])/max_depth, 10, 10);
|
samer@0
|
313 }
|
samer@0
|
314 }
|
samer@8
|
315 if (!got_floor) {
|
samer@0
|
316 ofSetColor(255,0,0);
|
samer@0
|
317 ofRect(16,16,16,16);
|
samer@0
|
318 }
|
samer@0
|
319 }
|
samer@0
|
320
|
samer@0
|
321 void testApp::keyPressed(int key){
|
samer@0
|
322 switch (key) {
|
samer@0
|
323 case 'd': draw_image = (draw_image+1)%3; break;
|
samer@0
|
324 case 'f':
|
samer@0
|
325 printf("floor plane: (%f,%f,%f), <%f,%f,%f>\n",
|
samer@0
|
326 floor_pie.ptPoint.X, floor_pie.ptPoint.Y, floor_pie.ptPoint.Z,
|
samer@0
|
327 floor_pie.vNormal.X, floor_pie.vNormal.Y, floor_pie.vNormal.Z);
|
samer@0
|
328 break;
|
samer@0
|
329 case 't': draw_time = !draw_time; break;
|
samer@8
|
330 case 'F': ofToggleFullscreen(); break;
|
samer@0
|
331 }
|
samer@0
|
332 }
|
samer@0
|
333
|
samer@0
|
334 void testApp::keyReleased(int key) {}
|
samer@0
|
335 void testApp::mouseMoved(int x, int y ) {}
|
samer@0
|
336 void testApp::mouseDragged(int x, int y, int button) {}
|
samer@0
|
337 void testApp::mousePressed(int x, int y, int button) {}
|
samer@0
|
338 void testApp::mouseReleased(int x, int y, int button) {}
|
samer@0
|
339 void testApp::windowResized(int w, int h) {}
|
samer@0
|
340
|