libeblearntools
/home/rex/ebltrunk/tools/libeblearntools/include/tracking_thread.hpp
00001 /***************************************************************************
00002  *   Copyright (C) 2010 by Pierre Sermanet *
00003  *   pierre.sermanet@gmail.com *
00004  *   All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Redistribution under a license not approved by the Open Source
00014  *       Initiative (http://www.opensource.org) must display the
00015  *       following acknowledgement in all advertising material:
00016  *        This product includes software developed at the Courant
00017  *        Institute of Mathematical Sciences (http://cims.nyu.edu).
00018  *     * The names of the authors may not be used to endorse or promote products
00019  *       derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00024  * DISCLAIMED. IN NO EVENT SHALL ThE AUTHORS BE LIABLE FOR ANY
00025  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00026  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00028  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  ***************************************************************************/
00032 
00033 #ifndef TRACKING_THREAD_HPP_
00034 #define TRACKING_THREAD_HPP_
00035 
00036 #include <map>
00037 #include <string>
00038 #include <iostream>
00039 #include <algorithm>
00040 #include <vector>
00041 #include <stdlib.h>
00042 #include <sstream>
00043 #include <iomanip>
00044 #include <time.h>
00045 #include "libeblearn.h"
00046 
00047 #ifdef __GUI__
00048 #include "libeblearngui.h"
00049 #endif
00050 
00051 using namespace std;
00052 
00053 namespace ebl {
00054 
00056   // tracking thread
00057 
00058   template <typename Tnet>
00059   tracking_thread<Tnet>::tracking_thread(configuration &conf_,
00060                                          const char *arg2_)
00061     : conf(conf_), arg2(arg2_), mutex_out(),
00062       out_updated(false), dt(conf, arg2_), tpl(10, 10, 1) {
00063     dt.start();
00064   }
00065 
00066   template <typename Tnet>
00067   tracking_thread<Tnet>::~tracking_thread() {
00068   }
00069 
00070   template <typename Tnet>
00071   void tracking_thread<Tnet>::copy_bboxes(vector<bbox*> &bb) {
00072     // lock data
00073     pthread_mutex_lock(&mutex_out);
00074     // clear bboxes
00075     bboxes.clear();
00076     // copy bboxes
00077     for (ibox = bb.begin(); ibox != bb.end(); ++ibox) {
00078       bboxes.push_back(new bbox(**ibox));
00079     }
00080     // unlock data
00081     pthread_mutex_unlock(&mutex_out);
00082   }
00083 
00084   template <typename Tnet>
00085   void tracking_thread<Tnet>::set_out_updated() {
00086     // lock data
00087     pthread_mutex_lock(&mutex_out);
00088     // set flag
00089     out_updated = true;
00090     // unlock data
00091     pthread_mutex_unlock(&mutex_out);
00092   }
00093 
00094   template <typename Tnet>
00095   bool tracking_thread<Tnet>::get_data(vector<bbox*> &bboxes2,
00096                                        idx<ubyte> &frame2, idx<ubyte> &tpl2) {
00097     // lock data
00098     pthread_mutex_lock(&mutex_out);
00099     // only read data if it has been updated
00100     if (!out_updated) {
00101       // unlock data
00102       pthread_mutex_unlock(&mutex_out);
00103       return false;
00104     }
00105     // clear bboxes
00106     for (ibox = bboxes2.begin(); ibox != bboxes2.end(); ++ibox) {
00107       if (*ibox)
00108         delete *ibox;
00109     }
00110     bboxes2.clear();
00111     // copy bboxes pointers (now responsible for deleting them).
00112     for (ibox = bboxes.begin(); ibox != bboxes.end(); ++ibox) {
00113       bboxes2.push_back(*ibox);
00114     }
00115     // check frame is correctly allocated, if not, allocate.
00116     if (frame2.order() != frame.order()) 
00117       frame2 = idx<ubyte>(frame.get_idxdim());
00118     else if (frame2.get_idxdim() != frame.get_idxdim())
00119       frame2.resize(frame.get_idxdim());
00120     // copy frame
00121     idx_copy(frame, frame2);    
00122     // check tpl is correctly allocated, if not, allocate.
00123     if (tpl2.order() != tpl.order()) 
00124       tpl2 = idx<ubyte>(tpl.get_idxdim());
00125     else if (tpl2.get_idxdim() != tpl.get_idxdim())
00126       tpl2.resize(tpl.get_idxdim());
00127     // copy tpl
00128     idx_copy(tpl, tpl2);    
00129     // reset updated flag
00130     out_updated = false; 
00131     // unlock data
00132     pthread_mutex_unlock(&mutex_out);
00133     // confirm that we copied data.
00134     return true;
00135   }
00136 
00137   template <typename Tnet>
00138   void tracking_thread<Tnet>::draw(bbox *b) {
00139 #ifdef __GUI__
00140     disable_window_updates();
00141     clear_window();
00142     draw_matrix(frame, "in");
00143     draw_matrix(tpl, "tpl", 0, frame.dim(1));
00144     if (b) {
00145       ubyte red = 0, green = 0, blue = 0;
00146       if (b->class_id == -42)
00147         green = 255;
00148       else
00149         blue = 255;
00150       draw_box(b->h0, b->w0, b->width, b->height, red, green, blue);
00151     }
00152     enable_window_updates();
00153 #endif
00154   }
00155 
00156   template <typename Tnet>
00157   void tracking_thread<Tnet>::execute() { 
00158     try {
00159       // configuration
00160       string    cam_type   = conf.get_string("camera");
00161       int       height     = conf.get_int("input_height");
00162       int       width      = conf.get_int("input_width");
00163       bool      save_video = conf.exists_bool("save_video");
00164       bool      display    = conf.exists_bool("tracking_display");
00165       // initialize camera (opencv, directory, shmem or video)
00166       camera<ubyte> *cam = NULL;
00167       if (!strcmp(cam_type.c_str(), "directory")) {
00168         if (arg2) cam = new camera_directory<ubyte>(arg2, height,width);
00169         else eblerror("expected 2nd argument");
00170       } else if (!strcmp(cam_type.c_str(), "opencv"))
00171         cam = new camera_opencv<ubyte>(-1, height, width);
00172 #ifdef __LINUX__
00173       else if (!strcmp(cam_type.c_str(), "v4l2"))
00174         cam = new camera_v4l2<ubyte>(conf.get_cstring("device"), height, width);
00175 #endif
00176       else if (!strcmp(cam_type.c_str(), "shmem"))
00177         cam = new camera_shmem<ubyte>("shared-mem", height, width);
00178       else if (!strcmp(cam_type.c_str(), "video")) {
00179         if (arg2)
00180           cam = new camera_video<ubyte>
00181             (arg2, height, width, conf.get_uint("input_video_sstep"),
00182              conf.get_uint("input_video_max_duration"));
00183         else eblerror("expected 2nd argument");
00184       } else eblerror("unknown camera type");
00185       if (save_video)
00186         cam->start_recording();
00187       // other variables
00188       bool updated = false;
00189       bbox *b = new bbox;
00190       vector<bbox*> bb;
00191 #ifdef __OPENCV__
00192       IplImage *iplframe = NULL;
00193       IplImage *ipltpl = NULL;
00194 #endif
00195       // main loop
00196       while(!cam->empty()) {
00197         try {
00198           frame = cam->grab();
00199           // send new frame to vision_thread and check if new data was output
00200           dt.set_data(frame);
00201           updated = dt.get_data(bb, detframe);
00202           // update target position if vision thread ready
00203           if (updated) {
00204             // find bbox with max confidence
00205             if (bb.size() > 0) {
00206               double maxconf = -5.0;
00207               uint maxi = 0;
00208               for (uint i = 0; i < bb.size(); ++i)
00209                 if (bb[i]->confidence > maxconf) {
00210                   maxconf = bb[i]->confidence;
00211                   maxi = i;
00212                 }
00213               //              cout << "found bbox" << endl;
00214               b = bb[maxi];
00215               if (b) {
00216                 // update template
00217                 idx<ubyte> tmptpl = detframe.narrow(0, b->height, b->h0);
00218                 tmptpl = tmptpl.narrow(1, b->width, b->w0);
00219                 tpl = idx<ubyte>(tmptpl.get_idxdim());
00220                 idx_copy(tmptpl, tpl);
00221               }
00222             }
00223           } else { // tracking (only when we didnt just receive an update)
00224 #ifdef __OPENCV__
00225             iplframe = idx_to_iplptr(frame);
00226             ipltpl = idx_to_iplptr(tpl);
00227             CvPoint minloc, maxloc;
00228             double minval, maxval;
00229             // vector<CvPoint> found;
00230             // vector<double> confidences;
00231             // FastMatchTemplate(*iplframe, *ipltpl, &found, &confidences, 1, false,
00232             //                  5, 1, 15);
00233             //      CvRect searchRoi;
00234             //      cvSetImageROI( searchImage, searchRoi );
00235             // vector<CvPoint>::iterator p = found.begin();
00236             // vector<double>::iterator c = confidences.begin();
00237             // for ( ; p != found.end() && c != confidences.end(); ++p, ++c) {
00238             //  gui << at(p->x, p->y) << *c;
00239             // }
00240             IplImage *tm = cvCreateImage(cvSize(frame.dim(1)  - tpl.dim(1)  + 1,
00241                                                 frame.dim(0) - tpl.dim(0) + 1 ),
00242                                          IPL_DEPTH_32F, 1 );
00243             cvMatchTemplate(iplframe, ipltpl, tm, CV_TM_SQDIFF_NORMED);
00244             cvMinMaxLoc(tm, &minval, &maxval, &minloc, &maxloc, 0);
00245             //      gui << at(minloc.y, minloc.x) << "M";
00246             b->class_id = -42; // tell that this bbox is result of tracking
00247             b->h0 = minloc.y;
00248             b->w0 = minloc.x;
00249             //      cout << "maxloc h: " << maxloc.y << " w: " << maxloc.x << " minloc h:" << minloc.y << " w: " << minloc.x << endl;
00250 #endif
00251           }
00252           // make a copy of found bounding boxes
00253           copy_bboxes(bb);
00254           // switch 'updated' flag on to warn we just added new data
00255           set_out_updated();
00256           // display
00257           if (display)
00258             draw(b);
00259         } catch (string &err) {
00260           cerr << err << endl;
00261         }
00262       }
00263       if (save_video)
00264         cam->stop_recording(conf.exists_bool("use_original_fps") ?
00265                             cam->fps() : conf.get_uint("save_video_fps"));
00266       if (cam) delete cam;
00267     } catch(string &err) { eblerror(err.c_str()); }
00268   }
00269 
00270 } // end namespace ebl
00271 
00272 #endif /* TRACKING_THREAD_HPP_ */