PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/webkit-plugin-mac/ocv_freenect.mm

http://github.com/doug/depthjs
Objective C++ | 474 lines | 303 code | 91 blank | 80 comment | 48 complexity | 08761c4ae18ad791bab6c997bd0b2439 MD5 | raw file
Possible License(s): AGPL-3.0
  1. /*
  2. * ocv_freenect.cpp
  3. * OpenCVTries1
  4. *
  5. * Created by Roy Shilkrot on 11/19/10.
  6. *
  7. */
  8. #define LIBUSB_DEBUG 5
  9. #include "ocv_freenect.hpp"
  10. #include <math.h>
  11. static pthread_t freenect_thread = 0;
  12. static int die = 0;
  13. static BOOL dead = YES;
  14. static pthread_mutex_t buf_mutex = PTHREAD_MUTEX_INITIALIZER;
  15. static Mat depthMat(cv::Size(640,480),CV_16UC1,Scalar(0));
  16. static Mat rgbMat(cv::Size(640,480),CV_8UC3,Scalar(0));
  17. static freenect_device *f_dev;
  18. static int freenect_angle = 15;
  19. static pthread_cond_t frame_cond = PTHREAD_COND_INITIALIZER;
  20. static int got_frames = 0;
  21. static freenect_context *f_ctx;
  22. void *freenect_threadfunc(void* arg);
  23. // void depth_cb(freenect_device *dev, void *depth, uint32_t timestamp);
  24. // void rgb_cb(freenect_device *dev, freenect_pixel *rgb, uint32_t timestamp);
  25. void send_event(const string& etype, const string& edata);
  26. void* freenect_threadfunc(void* arg) { //all this thread does is to fetch events from freenect
  27. cout << "freenect thread started"<<endl;
  28. while(!die && freenect_process_events(f_ctx) >= 0 ) {}
  29. cout << "freenect dead"<<endl;
  30. return NULL;
  31. }
  32. void depth_cb(freenect_device *dev, void *depth, uint32_t timestamp)
  33. {
  34. pthread_mutex_lock(&buf_mutex);
  35. //copy to ocv buf...
  36. //memcpy(depthMat.data, depth, FREENECT_DEPTH_SIZE);
  37. memcpy(depthMat.datastart, depth, FREENECT_DEPTH_11BIT_SIZE);
  38. got_frames++;
  39. pthread_cond_signal(&frame_cond);
  40. pthread_mutex_unlock(&buf_mutex);
  41. }
  42. void rgb_cb(freenect_device *dev, void *rgb, uint32_t timestamp)
  43. {
  44. pthread_mutex_lock(&buf_mutex);
  45. got_frames++;
  46. //copy to ocv_buf..
  47. //memcpy(rgbMat.data, rgb, FREENECT_RGB_SIZE);
  48. memcpy(rgbMat.datastart, rgb, FREENECT_VIDEO_RGB_SIZE);
  49. pthread_cond_signal(&frame_cond);
  50. pthread_mutex_unlock(&buf_mutex);
  51. }
  52. //functions defined in the other file...
  53. extern Scalar refineSegments(const Mat& img,
  54. Mat& mask,
  55. Mat& dst,
  56. vector<cv::Point>& contour,
  57. vector<cv::Point>& second_contour,
  58. cv::Point2i& previous);
  59. extern void makePointsFromMask(Mat& maskm,vector<Point2f>& points, bool _add = false);
  60. extern void drawPoint(Mat& out,vector<Point2f>& points,Scalar color, Mat* maskm = NULL);
  61. extern bool SendEventToBrowser(const string& eventJson); // depthjs.cc
  62. void send_event(const string& etype, const string& edata) {
  63. stringstream ss;
  64. ss << "{\"type\":\"" << etype << "\",\"data\":{" << edata << "}}";
  65. SendEventToBrowser(ss.str());
  66. }
  67. int initFreenect() {
  68. int res = 0;
  69. //setup Freenect...
  70. if (freenect_init(&f_ctx, NULL) < 0) {
  71. printf("freenect_init() failed\n");
  72. return 1;
  73. }
  74. freenect_set_log_level(f_ctx, FREENECT_LOG_ERROR);
  75. int nr_devices = freenect_num_devices (f_ctx);
  76. printf ("Number of devices found: %d\n", nr_devices);
  77. int user_device_number = 0;
  78. // if (argc > 1)
  79. // user_device_number = atoi(argv[1]);
  80. //
  81. // if (nr_devices < 1)
  82. // return 1;
  83. if (freenect_open_device(f_ctx, &f_dev, user_device_number) < 0) {
  84. printf("Could not open device\n");
  85. return 1;
  86. }
  87. freenect_set_tilt_degs(f_dev,freenect_angle);
  88. freenect_set_led(f_dev,LED_RED);
  89. freenect_set_depth_callback(f_dev, depth_cb);
  90. //freenect_set_rgb_callback(f_dev, rgb_cb);
  91. //freenect_set_rgb_format(f_dev, FREENECT_FORMAT_RGB);
  92. freenect_set_video_callback(f_dev, rgb_cb);
  93. freenect_set_video_format(f_dev, FREENECT_VIDEO_RGB);
  94. freenect_set_depth_format(f_dev, FREENECT_DEPTH_11BIT);
  95. freenect_start_depth(f_dev);
  96. freenect_start_video(f_dev);
  97. //start the freenect thread to poll for events
  98. res = pthread_create(&freenect_thread, NULL, freenect_threadfunc, NULL);
  99. if (res) {
  100. printf("pthread_create of freenect_thread failed\n");
  101. return 1;
  102. }
  103. return 0;
  104. }
  105. void send_image(const Mat& img) {
  106. // s_sendmore(socket, "image");
  107. //
  108. // Mat _img;
  109. // if(img.type() == CV_8UC1) {
  110. // Mat _tmp; resize(img, _tmp,Size(160,120)); //better to resize the gray data and not RGB
  111. // cvtColor(_tmp, _img, CV_GRAY2RGB);
  112. // } else {
  113. // resize(img, _img, Size(160,120));
  114. // }
  115. //
  116. // s_send(socket, (const char*)_img.data);
  117. }
  118. //Calculating the laplacian of a 2D curve. Thanks Y.Gingold!
  119. Mat laplacian_mtx(int N, bool closed_poly) {
  120. Mat A = Mat::zeros(N, N, CV_64FC1);
  121. Mat d = Mat::zeros(N, 1, CV_64FC1);
  122. //## endpoints
  123. //if(closed_poly) {
  124. A.at<double>(0,1) = 1;
  125. d.at<double>(0,0) = 1;
  126. A.at<double>(N-1,N-2) = 1;
  127. d.at<double>(N-1,0) = 1;
  128. //} else {
  129. // A.at<double>(0,1) = 1;
  130. // d.at<double>(0,0) = 1;
  131. //}
  132. //## interior points
  133. for(int i = 1; i <= N-2; i++) {
  134. A.at<double>(i, i-1) = 1;
  135. A.at<double>(i, i+1) = 1;
  136. d.at<double>(i,0) = 0.5;
  137. }
  138. Mat Dinv = Mat::diag( d );
  139. return Mat::eye(N,N,CV_64FC1) - Dinv * A;
  140. }
  141. void calc_laplacian(Mat& X, Mat& Xlap) {
  142. static Mat lapX = laplacian_mtx(X.rows,false);
  143. //a feeble attempt to save up in memory allocation.. in 99.9% of the cases this if fires
  144. if(lapX.rows != X.rows) lapX = laplacian_mtx(X.rows,false);
  145. Mat _X; //handle non-64UC2 matrices
  146. if (X.type() != CV_64FC2) {
  147. X.convertTo(_X, CV_64FC2);
  148. } else {
  149. _X = X;
  150. }
  151. vector<Mat> v; split(_X,v);
  152. v[0] = v[0].t() * lapX.t();
  153. v[1] = v[1].t() * lapX.t();
  154. cv::merge(v,Xlap);
  155. Xlap = Xlap.t();
  156. }
  157. typedef enum MODES {
  158. MODE_NONE,
  159. MODE_POINTER,
  160. MODE_PANNER,
  161. MODE_TAB_SWITHCER
  162. } modes;
  163. modes mode_state = MODE_NONE;
  164. void* ocvFreenectThread(void* arg) {
  165. Mat depthf;
  166. Mat frameMat(rgbMat);
  167. Mat blobMaskOutput(frameMat.size(),CV_8UC1),
  168. outC(frameMat.size(),CV_8UC3);
  169. Mat prevImg(frameMat.size(),CV_8UC1),
  170. nextImg(frameMat.size(),CV_8UC1),
  171. prevDepth(depthMat.size(),CV_8UC1);
  172. vector<Point2f> prevPts,nextPts;
  173. vector<uchar> statusv;
  174. vector<float> errv;
  175. cv::Rect cursor(frameMat.cols/2,frameMat.rows/2,10,10);
  176. bool update_bg_model = true;
  177. int fr = 1;
  178. int register_ctr = 0,register_secondbloc_ctr = 0;
  179. bool registered = false;
  180. Point2i appear(-1,-1); double appearTS = -1;
  181. Point2i midBlob(-1,-1);
  182. Point2i lastMove(-1,-1);
  183. int hcr_ctr = -1;
  184. vector<int> hc_stack(20); int hc_stack_ptr = 0;
  185. die = false;
  186. dead = NO;
  187. while (!die) {
  188. fr++;
  189. pthread_mutex_lock(&buf_mutex);
  190. //Linear interpolation
  191. {
  192. Mat _tmp = (depthMat - 400.0); //minimum observed value is ~440. so shift a bit
  193. _tmp.setTo(Scalar(2048), depthMat > ((!registered) ? 700.0 : 750.0)); //cut off at 600 to create a "box" where the user interacts
  194. _tmp.convertTo(depthf, CV_8UC1, 255.0/1648.0); //values are 0-2048 (11bit), account for -400 = 1648
  195. }
  196. {
  197. Mat _tmp;
  198. depthMat.convertTo(_tmp, CV_8UC1, 255.0/2048.0);
  199. cvtColor(_tmp, outC, CV_GRAY2BGR);
  200. }
  201. pthread_mutex_unlock(&buf_mutex);
  202. // { //saving the frames to files for debug
  203. // stringstream ss; ss << "depth_"<<fr<<".png";
  204. // imwrite(ss.str(), depthf);
  205. // }
  206. //Logarithm interpolation - try it!, It should be more "sensitive" for closer depths
  207. // {
  208. // Mat tmp,tmp1;
  209. // depthMat.convertTo(tmp, CV_32FC1);
  210. // log(tmp,tmp1);
  211. // tmp1.convertTo(depthf, CV_8UC1, 255.0/7.6246189861593985);
  212. // }
  213. Mat blobMaskInput = depthf < 255; //anything not white is "real" depth
  214. vector<cv::Point> ctr,ctr2;
  215. Scalar blb = refineSegments(Mat(),blobMaskInput,blobMaskOutput,ctr,ctr2,midBlob); //find contours in the foreground, choose biggest
  216. /////// blb :
  217. //blb[0] = x, blb[1] = y, blb[2] = 1st blob size, blb[3] = 2nd blob size.
  218. // uint mode_counters[3] = {0};
  219. if(blb[0]>=0 && blb[2] > 500) { //1st blob detected, and is big enough
  220. //cvtColor(depthf, outC, CV_GRAY2BGR);
  221. //closest point to the camera
  222. cv::Point minLoc; double minval,maxval;
  223. minMaxLoc(depthf, &minval, &maxval, &minLoc, NULL, blobMaskInput);
  224. circle(outC, minLoc, 5, Scalar(0,255,0), 3);
  225. Scalar mn,stdv;
  226. meanStdDev(depthf,mn,stdv,blobMaskInput);
  227. //cout << "min: " << minval << ", max: " << maxval << ", mean: " << mn[0] << endl;
  228. blobMaskInput = depthf < (mn[0] + stdv[0]*.5);
  229. blb = refineSegments(Mat(),blobMaskInput,blobMaskOutput,ctr,ctr2,midBlob);
  230. if(blb[0] >= 0 && blb[2] > 300) {
  231. //draw contour
  232. Scalar color(0,0,255);
  233. for (int idx=0; idx<ctr.size()-1; idx++)
  234. line(outC, ctr[idx], ctr[idx+1], color, 1);
  235. line(outC, ctr[ctr.size()-1], ctr[0], color, 1);
  236. if(ctr2.size() > 0) {
  237. Scalar color2(255,0,255);
  238. for (int idx=0; idx<ctr2.size()-1; idx++)
  239. line(outC, ctr2[idx], ctr2[idx+1], color2, 2);
  240. line(outC, ctr2[ctr2.size()-1], ctr2[0], color2, 2);
  241. }
  242. //draw "major axis"
  243. // Vec4f _line;
  244. Mat curve(ctr);
  245. // fitLine(curve, _line, CV_DIST_L2, 0, 0.01, 0.01);
  246. // line(outC, cv::Point(blb[0]-_line[0]*70,blb[1]-_line[1]*70),
  247. // cv::Point(blb[0]+_line[0]*70,blb[1]+_line[1]*70),
  248. // Scalar(255,255,0), 1);
  249. //blob center
  250. circle(outC, cv::Point(blb[0],blb[1]), 50, Scalar(255,0,0), 3);
  251. // cout << "min depth " << minval << endl;
  252. register_ctr = MIN((register_ctr + 1),60);
  253. if(blb[3] > 5000)
  254. register_secondbloc_ctr = MIN((register_secondbloc_ctr + 1),60);
  255. if (register_ctr > 30 && !registered) {
  256. registered = true;
  257. appear.x = -1;
  258. update_bg_model = false;
  259. lastMove.x = blb[0]; lastMove.y = blb[1];
  260. cout << "blob size " << blb[2] << endl;
  261. if(register_secondbloc_ctr < 30) {
  262. if(blb[2] > 10000) {
  263. cout << "register panner" << endl;
  264. send_event("Register", "\"mode\":\"openhand\"");
  265. } else {
  266. cout << "register pointer" << endl;
  267. send_event("Register", "\"mode\":\"theforce\"");
  268. }
  269. } else {
  270. cout << "register tab swithcer" << endl;
  271. send_event("Register", "\"mode\":\"twohands\"");
  272. }
  273. }
  274. if(registered) {
  275. stringstream ss;
  276. ss << "\"x\":" << (int)floor(blb[0]*100.0/640.0)
  277. << ",\"y\":" << (int)floor(blb[1]*100.0/480.0)
  278. << ",\"z\":" << (int)(mn[0] * 2.0);
  279. //cout << "move: " << ss.str() << endl;
  280. send_event("Move", ss.str());
  281. //---------------------- fist detection ---------------------
  282. //calc laplacian of curve
  283. vector<cv::Point> approxCurve; //approximate curve
  284. approxPolyDP(curve, approxCurve, 10.0, true);
  285. Mat approxCurveM(approxCurve);
  286. Mat curve_lap;
  287. calc_laplacian(approxCurveM, curve_lap); //calc laplacian
  288. hcr_ctr = 0;
  289. for (int i=0; i<approxCurve.size(); i++) {
  290. double n = norm(((Point2d*)(curve_lap.data))[i]);
  291. if (n > 10.0) {
  292. //high curvature point
  293. circle(outC, approxCurve[i], 3, Scalar(50,155,255), 2);
  294. hcr_ctr++;
  295. }
  296. }
  297. hc_stack.at(hc_stack_ptr) = hcr_ctr;
  298. hc_stack_ptr = (hc_stack_ptr + 1) % hc_stack.size();
  299. Scalar _avg = mean(Mat(hc_stack));
  300. if (abs(_avg[0] - (double)hcr_ctr) > 5.0) { //a big change in curvature = hand fisted/opened?
  301. cout << "Hand click!" << endl;
  302. send_event("HandClick", "");
  303. }
  304. if (mode_state == MODE_NONE) {
  305. }
  306. { //some debug on screen..
  307. stringstream ss; ss << "high curve pts " << hcr_ctr << ", avg " << _avg[0];
  308. putText(outC, ss.str(), cv::Point(50,50), CV_FONT_HERSHEY_PLAIN, 2.0,Scalar(0,0,255), 2);
  309. }
  310. } else {
  311. //not registered, look for gestures
  312. if(appear.x<0) {
  313. //first appearence of blob
  314. appear = midBlob;
  315. // update_bg_model = false;
  316. appearTS = getTickCount();
  317. cout << "appear ("<<appearTS<<") " << appear.x << "," << appear.y << endl;
  318. } else {
  319. //blob was seen before, how much time passed
  320. double timediff = ((double)getTickCount()-appearTS)/getTickFrequency();
  321. if (timediff > .2 && timediff < 1.0) {
  322. //enough time passed from appearence
  323. line(outC, appear, cv::Point(blb[0],blb[1]), Scalar(0,0,255), 3);
  324. if (appear.x - blb[0] > 100) {
  325. cout << "right"<<endl; appear.x = -1;
  326. send_event("SwipeRight", "");
  327. update_bg_model = true;
  328. register_ctr = 0;
  329. } else if (appear.x - blb[0] < -100) {
  330. cout << "left" <<endl; appear.x = -1;
  331. send_event("SwipeLeft", "");
  332. update_bg_model = true;
  333. register_ctr = 0;
  334. } else if (appear.y - blb[1] > 100) {
  335. cout << "up" << endl; appear.x = -1;
  336. send_event("SwipeUp", "");
  337. update_bg_model = true;
  338. register_ctr = 0;
  339. } else if (appear.y - blb[1] < -100) {
  340. cout << "down" << endl; appear.x = -1;
  341. send_event("SwipeDown", "");
  342. update_bg_model = true;
  343. register_ctr = 0;
  344. }
  345. }
  346. if(timediff >= 1.0) {
  347. cout << "a ghost..."<<endl;
  348. update_bg_model = true;
  349. //a second passed from appearence - reset 1st appear
  350. appear.x = -1;
  351. appearTS = -1;
  352. midBlob.x = midBlob.y = -1;
  353. }
  354. }
  355. }
  356. send_image(outC);
  357. }
  358. } else {
  359. send_image(depthf);
  360. register_ctr = MAX((register_ctr - 1),0);
  361. register_secondbloc_ctr = MAX((register_secondbloc_ctr - 1),0);
  362. }
  363. if (register_ctr <= 15 && registered) {
  364. midBlob.x = midBlob.y = -1;
  365. registered = false;
  366. mode_state = MODE_NONE;
  367. update_bg_model = true;
  368. cout << "unregister" << endl;
  369. send_event("Unregister", "");
  370. }
  371. }
  372. printf("-- done!\n");
  373. pthread_join(freenect_thread, NULL);
  374. dead = YES;
  375. return NULL;
  376. }
  377. void killOcvFreenect() {
  378. if (die) return;
  379. die = true;
  380. }
  381. BOOL isDead() {
  382. return dead;
  383. }