HandVu.cpp
上传用户:lijia5631
上传日期:2008-11-10
资源大小:1214k
文件大小:39k
源码类别:

视频捕捉/采集

开发平台:

MultiPlatform

  1. /**
  2.   * HandVu - a library for computer vision-based hand gesture
  3.   * recognition.
  4.   * Copyright (C) 2004 Mathias Kolsch, matz@cs.ucsb.edu
  5.   *
  6.   * This program is free software; you can redistribute it and/or
  7.   * modify it under the terms of the GNU General Public License
  8.   * as published by the Free Software Foundation; either version 2
  9.   * of the License, or (at your option) any later version.
  10.   *
  11.   * This program is distributed in the hope that it will be useful,
  12.   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.   * GNU General Public License for more details.
  15.   *
  16.   * You should have received a copy of the GNU General Public License
  17.   * along with this program; if not, write to the Free Software
  18.   * Foundation, Inc., 59 Temple Place - Suite 330, 
  19.   * Boston, MA  02111-1307, USA.
  20.   *
  21.   * $Id: HandVu.cpp,v 1.44 2006/01/03 21:44:15 matz Exp $
  22. **/
  23. // HandVu.cpp: main library implementation file
  24. //
  25. #include "Common.h"
  26. #include "CubicleWrapper.h"
  27. #include "Skincolor.h"
  28. #include "LearnedColor.h"
  29. #include "OpticalFlow.h"
  30. #include "CamShift.h"
  31. #include "Undistortion.h"
  32. #include "VisionConductor.h"
  33. #include "GestureServer.h"
  34. #include "HandVu.hpp"
  35. #include <fstream>
  36. #if defined(WIN32) && defined(DEBUG)
  37. //#include <streams.h>
  38. #endif
  39. #ifdef USE_MFC
  40. #ifdef _DEBUG
  41. #define new DEBUG_NEW
  42. #undef THIS_FILE
  43. static char THIS_FILE[] = __FILE__;
  44. #endif // _DEBUG
  45. #endif // USE_MFC
  46. /////////////////////////////////////////////////////////////////////////////
  47. // HandVu
  48. HandVu::HandVu()
  49.   : m_active(false),
  50.     m_tracking(false),
  51.     m_recognized(false),
  52.     m_id1_on(false),
  53.     m_do_track(true),
  54.     m_video_width(0),
  55.     m_video_height(0),
  56.     m_alignment_crosses(1),
  57.     m_buf_indx_cycler(-1),
  58.     m_curr_buf_indx(-1),
  59.     m_prev_buf_indx(-1),
  60.     m_rgbImage(NULL),
  61.     m_depthImage(NULL),
  62.     m_rightGrayImage(NULL),
  63.     m_center_depth(0),
  64.     m_initialized(false),
  65.     m_quit_thread(false),
  66.     m_pAsyncThread(NULL),
  67.     m_img_width(-1),
  68.     m_img_height(-1),
  69.     m_undistort(false),
  70.     m_overlay_level(0),
  71.     m_sample_time(0),
  72.     m_num_succ_dropped_frames(0),
  73.     m_max_normal_latency(-1),
  74.     m_max_abnormal_latency(-1),
  75.     m_determine_normal_latency(false),
  76.     m_t_start_processing(0),
  77.     m_time_to_learn_color(0),
  78.     m_min_time_between_learning_color(0),
  79.     m_adjust_exposure(false),
  80.     m_adjust_exposure_at_time(0),
  81.     m_pCameraController(NULL),
  82.     m_pClock(NULL)
  83. {
  84.   m_pCubicle = new CubicleWrapper();
  85.   m_pSkincolor = new Skincolor();
  86.   m_pLearnedColor = new LearnedColor();
  87.   m_pOpticalFlow = new OpticalFlow();
  88.   m_pCamShift = new CamShift();
  89.   m_pUndistortion = new Undistortion();
  90.   m_pConductor = new VisionConductor();
  91.   m_grayImages[0] = NULL;
  92.   m_grayImages[1] = NULL;
  93. //  g_ostream = fopen("c:\tmp\HVout.txt", "aw+");
  94.   g_ostream = NULL;
  95.   g_verbose = 0;
  96. }
  97. HandVu::~HandVu()
  98. {
  99.   // quit Aync thread
  100.   if (m_pAsyncThread) {
  101.     m_quit_thread = true; // will be set to false by thread upon exit
  102.     m_pAsyncThread->Resume();
  103.   }
  104.   
  105.   // our datastructures
  106.   delete m_pCubicle;
  107.   delete m_pSkincolor;
  108.   delete m_pLearnedColor;
  109.   delete m_pOpticalFlow;
  110.   delete m_pCamShift;
  111.   delete m_pUndistortion;
  112.   delete m_pConductor;
  113.   // images
  114.   cvReleaseImage(&m_grayImages[0]);
  115.   cvReleaseImage(&m_grayImages[1]);
  116.   cvReleaseImage(&m_depthImage);
  117.   cvReleaseImage(&m_rightGrayImage);
  118.   // wait for Aync thread, then delete ring buffer
  119.   if (m_pAsyncThread) {
  120.      m_pAsyncThread->Join();
  121.   }  
  122.   for (int b=0; b<(int)m_ring_buffer.size(); b++) {
  123.     cvReleaseImage(&m_ring_buffer[b]);
  124.   }
  125. }
  126. /* pCamCon may be NULL
  127. */
  128. void HandVu::Initialize(int width, int height, RefClock* pClock,
  129. CameraController* pCamCon)
  130. {
  131.   VERBOSE3(5, "HandVu: initializing with size %dx%d, CameraController: %s",
  132.     width, height, pCamCon?"yes":"no");
  133.   cvReleaseImage(&m_grayImages[0]);
  134.   cvReleaseImage(&m_grayImages[1]);
  135.   cvReleaseImage(&m_depthImage);
  136.   cvReleaseImage(&m_rightGrayImage);
  137.   m_grayImages[0] = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
  138.   m_grayImages[1] = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
  139.   m_rightGrayImage = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
  140.   m_depthImage     = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
  141.   m_pCubicle->Initialize(width, height);
  142.   m_pSkincolor->Initialize(width, height);
  143.   m_pLearnedColor->Initialize(width, height);
  144.   m_pOpticalFlow->Initialize(width, height);
  145.   m_pUndistortion->Initialize(width, height);
  146.   m_pCamShift->Initialize(width, height);
  147.   m_img_width = width;
  148.   m_img_height = height;
  149.   if (!pClock) {
  150.     throw HVException("no clock set!");
  151.   }
  152.   m_pClock = pClock;
  153.   if (!pCamCon) {
  154.     VERBOSE0(3, "no camera controller will be available");
  155.   }
  156.   m_pCameraController = pCamCon;
  157.   m_scan_area = CRect(width/3, height/3, 2*width/3, 2*height/3);
  158.   m_min_time_between_learning_color = 3*1000*1000; // 3 sec
  159.   // normal latencies etc.
  160.   m_determine_normal_latency = true;
  161.   m_max_normal_latency = 3000; // 3ms
  162.   m_max_abnormal_latency = 15000; // 15ms
  163.   m_last_latencies.resize(1);
  164.   m_initialized = true;
  165. }
  166. bool HandVu::ConductorLoaded() const
  167. {
  168.   return m_pConductor->IsLoaded();
  169. }
  170. void HandVu::LoadConductor(const string& filename)
  171. {
  172.   VERBOSE1(5, "HandVu: loading conductor from file %s",
  173.     filename.c_str());
  174.   m_pConductor->Load(filename);
  175.   // load a calibration matrix, if available, and set active
  176.   if (m_pConductor->m_camera_calib!="") {
  177.     m_pUndistortion->Load(m_pConductor->m_camera_calib.c_str());
  178.     m_undistort = true;
  179.   }
  180.   if (m_pConductor->m_adjust_exposure 
  181.     && m_pCameraController
  182.     && m_pCameraController->CanAdjustExposure())
  183.   {
  184.     bool turned_off = 
  185.       m_pCameraController->SetCameraAutoExposure(false);
  186.     if (!turned_off) {
  187.       throw HVException("can not turn off camera auto exposure");
  188.     }
  189.     m_adjust_exposure = true;
  190.   }
  191.   VERBOSE0(5, "HandVu: done loading conductor");
  192. }
  193. // put filters in the right state, supply scanners and cascades
  194. //
  195. void HandVu::StartRecognition(int id/*=0*/)
  196. {
  197.   if(!m_initialized) {
  198.     throw HVException("HandVu not initialized, cannot start");
  199.   }
  200.   if(!m_pConductor->IsLoaded()) {
  201.     throw HVException("no conductor loaded");
  202.   }
  203.   VERBOSE1(5, "HandVu: starting recognition for obj_id %d", id);
  204.   if (id==1) {
  205.     m_id1_on = true;
  206.     m_id1_x = -1;
  207.     m_id1_y = -1;
  208.     if (m_active) return;
  209.   } else if (id!=0) {
  210.     throw HVException("unknown ID");
  211.   }
  212.   // other detection
  213.   m_dt_first_match_time = 0;
  214.   m_dt_first_match = CuScanMatch();
  215.   // activate detection scanners
  216.   for (int cc=0; cc<m_pConductor->m_dt_cascades_end; cc++) {
  217.     cuSetScannerActive((CuCascadeID)cc, true);
  218.     CRect area(m_pConductor->m_orig_areas[cc].toRect(m_img_width, m_img_height));
  219.     cuSetScanArea((CuCascadeID)cc, area.left, area.top, area.right, area.bottom);
  220.   }
  221.   // de-activate recognition scanners
  222.   for (int cc=m_pConductor->m_rc_cascades_start;
  223.        cc<m_pConductor->m_rc_cascades_end; cc++) {
  224.     cuSetScannerActive((CuCascadeID)cc, false);
  225.   }
  226.   // the scan area is mostly used during tracking, but also
  227.   // to do the exposure control
  228.   CQuadruple quad;
  229.   GetDetectionArea(quad);
  230.   m_scan_area = quad.toRect(m_img_width, m_img_height);
  231.   m_frame_times.clear();
  232.   m_processed_frame_times.clear();
  233.   m_prcs_times.clear();
  234.   m_buf_indx_cycler = 0;
  235.   m_curr_buf_indx = m_buf_indx_cycler;
  236.   m_prev_buf_indx = 1-m_buf_indx_cycler;
  237.   m_do_track = true;
  238.   m_active = true;
  239.   m_tracking = false;
  240. }
  241. void HandVu::StopRecognition(int id/*=0*/)
  242. {
  243.   VERBOSE1(5, "HandVu: stopping recognition for obj_id %d", id);
  244.   if (id==1) {
  245.     m_id1_on = false;
  246.     return;
  247.   } else if (id!=0) {
  248.     throw HVException("unknown ID");
  249.   }
  250.   m_active = false;
  251. }
  252. /* --------------------------------------------------------
  253. * THE ALMIGHTY ProcessFrame routine !!!!!!!!!!!!!!!!!!!!!
  254. * ----------------------------------------------------------
  255. */
  256. HandVu::HVAction
  257. HandVu::ProcessFrame(GrabbedImage& inOutImage, const IplImage* rightImage)
  258. {
  259.   m_rgbImage = inOutImage.GetImage();
  260.   m_sample_time = inOutImage.GetSampleTime();
  261.   // sanity checks
  262.   if(!m_initialized) {
  263.     throw HVException("HandVu not initialized, cannot process");
  264.   }
  265.   if (m_rgbImage->width!=m_img_width || m_rgbImage->height!=m_img_height) {
  266.     throw HVException("image dimensions do not match initialization");
  267.   }
  268.   VERBOSE2(5, "HandVu: processing frame (active: %s, tracking: %s)",
  269.     m_active?"yes":"no", m_tracking?"yes":"no");
  270.   if (m_rgbImage->origin==1) {
  271.     m_rgbImage->origin = 0;
  272.     cvMirror(m_rgbImage, NULL, 0);
  273.   }
  274.   // check latency of the incoming frame - the result is used
  275.   // after KLT tracking
  276.   HVAction action = CheckLatency();
  277.   // return if we're not supposed to do anything
  278.   if (!m_active
  279.       || m_scan_area.left>=m_scan_area.right 
  280.       || m_scan_area.top>=m_scan_area.bottom) {
  281.     if (action==HV_PROCESS_FRAME) {
  282.       // adjust exposure
  283.       CheckAndCorrectExposure();
  284.       
  285.       // undistort image, adjust location of centroid
  286.       if (m_undistort) {
  287.         m_pUndistortion->Undistort(m_rgbImage);
  288.     //    m_pUndistortion->Transform(centroid);
  289.       }
  290.     }
  291.     
  292.     KeepStatistics(action);
  293.     DrawOverlay();
  294.     SendEvent();
  295.     return action;
  296.   }
  297.   // set current buffer
  298.   m_buf_indx_cycler = 1-m_buf_indx_cycler;
  299.   m_curr_buf_indx = m_buf_indx_cycler;
  300.   m_prev_buf_indx = 1-m_buf_indx_cycler;
  301.   // create gray scale image
  302.   {
  303.     int scan_width = m_scan_area.right-m_scan_area.left;
  304.     int cvt_left = max(0, m_scan_area.left-scan_width/2);
  305.     int cvt_width = min(m_scan_area.right+scan_width/2, m_rgbImage->width)-cvt_left;
  306.     int scan_height = m_scan_area.bottom-m_scan_area.top;
  307.     int cvt_top = max(0, m_scan_area.top-scan_height/2);
  308.     int cvt_height = min(m_scan_area.bottom+scan_height/2, m_rgbImage->height)-cvt_top;
  309.     ASSERT(cvt_height>0 && cvt_width>0);
  310.     if (!(cvt_height>0 && cvt_width>0)) {
  311.       KeepStatistics(action);
  312.       DrawOverlay();
  313.       SendEvent();
  314.       return action;
  315.     }
  316.     cvSetImageROI(m_rgbImage, cvRect(cvt_left, cvt_top, cvt_width, cvt_height));
  317.     cvSetImageROI(m_grayImages[m_curr_buf_indx], cvRect(cvt_left, cvt_top, cvt_width, cvt_height));
  318.     cvCvtColor(m_rgbImage, m_grayImages[m_curr_buf_indx], CV_BGR2GRAY);
  319.     
  320.     cvResetImageROI(m_rgbImage);
  321.     cvResetImageROI(m_grayImages[m_curr_buf_indx]);
  322.   }
  323.   // do the all-important, fast KLT tracking
  324.   m_recognized = false;
  325.   if (m_tracking) {    
  326.     m_tracking = DoTracking();
  327.     if (!m_tracking) {
  328.       // lost tracking
  329.       StartRecognition();
  330.     }
  331.   }
  332.   // take the recommendation from CheckLatency to heart
  333.   if (action!=HV_PROCESS_FRAME) {
  334.     KeepStatistics(action);
  335.     DrawOverlay();
  336.     SendEvent();
  337.     return action;
  338.   }
  339.   if (m_tracking) {    
  340.     m_recognized = DoRecognition();
  341.     FindSecondHand();
  342.   
  343.   } else {
  344.     m_recognized = DoDetection();
  345.   }
  346.   if (m_recognized && m_do_track) {
  347.     InitializeTracking();
  348.     m_tracking = true;
  349.   }
  350.   // restrict the general color segmentation ROI; this is
  351.   // not really important for anything but high overlay_levels
  352.   int scan_left = max(0, m_scan_area.left);
  353.   int scan_right = min(m_scan_area.right, m_rgbImage->width);
  354.   int scan_top = max(0, m_scan_area.top);
  355.   int scan_bottom = min(m_scan_area.bottom, m_rgbImage->height);
  356.   CRect scan_area(scan_left, scan_top, scan_right, scan_bottom);
  357.   // use stereo correspondence to obtain distance of hand from camera
  358.   if (m_tracking && rightImage) {
  359.     int sz = (scan_right-scan_left)/5;
  360.     CvRect area = cvRect((int)m_center_pos.x-sz, (int)m_center_pos.y-sz, 2*sz, 2*sz);
  361.     CalculateDepth(rightImage, area);
  362.   }
  363.   
  364.   // adjust exposure
  365.   CheckAndCorrectExposure();
  366.   // drawing
  367.   m_pSkincolor->DrawOverlay(m_rgbImage, m_overlay_level, CRect(m_last_match));
  368.   if (m_tracking) {
  369.     m_pLearnedColor->DrawOverlay(m_rgbImage, m_overlay_level, scan_area);
  370.   }
  371.   m_pCubicle->DrawOverlay(m_rgbImage, m_overlay_level);
  372.   if (m_tracking) {
  373.     if (m_pConductor->m_tr_type & VisionConductor::VC_TT_CAMSHIFT) {
  374.       m_pCamShift->DrawOverlay(m_rgbImage, m_overlay_level);
  375.     } else {
  376.       m_pOpticalFlow->DrawOverlay(m_rgbImage, m_overlay_level);
  377.     }
  378.   }
  379.   // undistort image, adjust location of centroid
  380.   if (m_undistort) {
  381.     m_pUndistortion->Undistort(m_rgbImage);
  382. //    m_pUndistortion->Transform(centroid);
  383.   }
  384.   KeepStatistics(action);
  385.   DrawOverlay();
  386.   SendEvent();
  387.   return action;
  388. }
  389. /* --------------------------------------------------------
  390. * ----------------------------------------------------------
  391. */
  392. /* C/C++ interface for AsyncProcessor
  393.  */
  394. void* asyncProcessor(void* arg)
  395. {
  396.   if (arg==NULL) {
  397.     fprintf(stderr, "asyncProcessor: no argument!!n");
  398.     return NULL;
  399.   }
  400.   HandVu* hv = (HandVu*) arg;
  401.   hv->AsyncProcessor();
  402. }
  403. /* allocate internal ring buffer, start processing thread
  404.  */
  405. void HandVu::AsyncSetup(int num_buffers, DisplayCallback* pDisplayCB)
  406. {
  407.   // sanity checks
  408.   if(!m_initialized) {
  409.     throw HVException("HandVu not initialized, cannot setup async processing");
  410.   }
  411.   if (num_buffers<=0 || 50<num_buffers) {
  412.     throw HVException("num_buffers must be between 1..50");
  413.   }
  414.   if (pDisplayCB==NULL) {
  415.     throw HVException("DisplayCallback can not be NULL");
  416.   }
  417.   // allocate ring buffer
  418.   m_ring_buffer.resize(num_buffers);
  419.   m_ring_buffer_occupied.resize(num_buffers);
  420.   for (int b=0; b<num_buffers; b++) {
  421.     CvSize size = cvSize(m_img_width, m_img_height);
  422.     m_ring_buffer[b] = cvCreateImage(size, IPL_DEPTH_8U, 3);
  423.     m_ring_buffer_occupied[b] = false;
  424.   }
  425.   m_pDisplayCallback = pDisplayCB;
  426.   
  427.   // start processing thread, it will suspended itself if
  428.   // m_process_queue is empty
  429.   m_pAsyncThread = new Thread(asyncProcessor, this);
  430.   m_pAsyncThread->Start();
  431. }
  432. /* insert the frame in the processing queue
  433.  */
  434. void HandVu::AsyncProcessFrame(int id, RefTime& t)
  435. {
  436.   // insert in queue
  437.   GrabbedImage gi(m_ring_buffer[id], t, id);
  438.   m_process_queue.push_back(gi);
  439.   
  440.   // wake up processing thread
  441.   m_pAsyncThread->Resume();
  442. }
  443. /* give the capture application a handle to an unused image in
  444.    the ring buffer
  445. */
  446. void HandVu::AsyncGetImageBuffer(IplImage** pImg, int* pID)
  447. {
  448.   // this is the only place where we occupy buffers, so we
  449.   // don't have to use a mutex (of course, we might fail
  450.   // for no good reason)
  451.   for (int b=0; b<(int)m_ring_buffer.size(); b++) {
  452.     if (!m_ring_buffer_occupied[b]) {
  453.       *pImg = m_ring_buffer[b];
  454.       *pID  = b;
  455.       return;
  456.     }
  457.   }
  458.   throw HVException("exceeded ring buffer size");
  459. }
  460. void HandVu::AsyncProcessor()
  461. {
  462.   for (;;) {
  463.     // suspend this thread if processing queue is empty
  464.     m_pAsyncThread->Lock();
  465.     if (m_process_queue.empty()) {
  466.       m_pAsyncThread->Suspend();
  467.     }
  468.     m_pAsyncThread->Unlock();
  469.     
  470.     while (!m_quit_thread && !m_process_queue.empty()) {
  471.       // get front of queue - don't lock
  472.       GrabbedImage gi = m_process_queue.front();
  473.       m_process_queue.pop_front();
  474.       
  475.       ASSERT(m_ring_buffer_occupied[gi.GetBufferID()]);
  476.       HVAction action = ProcessFrame(gi);
  477.       m_pDisplayCallback->Display(gi.GetImage(), action);
  478.       
  479.       // free up the buffer - we don't have to lock
  480.       m_ring_buffer_occupied[gi.GetBufferID()] = false;
  481.     }
  482.     
  483.     if (m_quit_thread) {
  484.       m_pAsyncThread->Stop();
  485.     }
  486.   }
  487. }
  488. HandVu::HVAction HandVu::CheckLatency()
  489. {
  490.   ASSERT(m_pClock);
  491.   m_t_start_processing = m_pClock->GetCurrentTimeUsec();
  492.   RefTime incoming_latency = 
  493.     max((RefTime)0, m_t_start_processing-m_sample_time);
  494.   if (m_determine_normal_latency) {
  495.     const int drop_num_frames = 25;
  496.     const int skip_first_num_frames = 15;
  497.     m_last_latencies.push_back(incoming_latency);
  498.     int num_frames_dropped = (int) m_last_latencies.size();
  499.     if (num_frames_dropped<drop_num_frames) {
  500.       // drop frame
  501.       VERBOSE1(3, "HandVu: dropping frame during latency computation (latency %ldus)", 
  502.                incoming_latency);
  503.       return HV_DROP_FRAME;
  504.     }
  505.     
  506.     // calculate average and max latencies
  507.     RefTime sum_latencies = 0;
  508.     RefTime max_latency = LONG_MIN;
  509.     for (int frm=skip_first_num_frames; frm<num_frames_dropped; frm++) {
  510.       RefTime lat = m_last_latencies[frm];
  511.       sum_latencies += lat;
  512.       if (lat>max_latency) {
  513.         max_latency = lat;
  514.       }
  515.     }
  516.     // average of (average and max), plus 5ms
  517.     int counted = num_frames_dropped-skip_first_num_frames;
  518.     m_max_normal_latency = min((max_latency + sum_latencies/counted)/2l
  519.                                 + 2000, // micro-second units -> 2ms
  520.                                max_latency + 1000); // -> 1ms
  521.     m_max_abnormal_latency = max(m_max_normal_latency*2, 
  522.       (RefTime)15000);
  523.     VERBOSE5(3, "HandVu: avg latency in %d frames: %dus, max: %dus -> norm: %dus, abnorm: %dus", 
  524.              counted, (int)(sum_latencies/counted), (int)(max_latency), 
  525.              (int)(m_max_normal_latency), (int)(m_max_abnormal_latency));
  526.     // stop latency computation
  527.     m_determine_normal_latency = false;
  528.     m_last_latencies.resize(1);
  529.   }
  530.   ASSERT(m_last_latencies.size()>0);
  531.   m_last_latencies[0] = incoming_latency;
  532.   if (incoming_latency>m_max_normal_latency) {
  533.     if (incoming_latency>m_max_abnormal_latency) {
  534.       const int max_succ_dropped_frames = 75; 
  535.       // todo: should base this on seconds, not frames dropped.
  536.       // like no frame for 5 sec: recompute
  537.       m_num_succ_dropped_frames++;
  538.       VERBOSE1(3, "HandVu: dropping frame (latency %ldms)", incoming_latency/1000);
  539.       if (m_num_succ_dropped_frames>max_succ_dropped_frames) {
  540.         m_determine_normal_latency = true;
  541.         VERBOSE1(2, "HandVu: warning - %d contiguous frames dropped", 
  542.                 m_num_succ_dropped_frames);
  543.         m_num_succ_dropped_frames = 0;
  544.       }
  545.       return HV_DROP_FRAME;
  546.     }
  547.     VERBOSE1(3, "HandVu: skipping frame (latency %ldms)", incoming_latency/1000);
  548.     return HV_SKIP_FRAME;
  549.   }
  550.   m_num_succ_dropped_frames = 0;
  551.   static bool quickreturn = false;
  552.   //  quickreturn = !quickreturn;
  553.   if (quickreturn) return HV_SKIP_FRAME;
  554.   
  555.   return HV_PROCESS_FRAME;
  556. }
  557. void HandVu::KeepStatistics(HVAction action)
  558. {
  559.   // times of frames: all frames
  560.   RefTime t_curr = m_pClock->GetCurrentTimeUsec();
  561.   m_frame_times.push_back(t_curr);
  562.   if (action==HV_PROCESS_FRAME) {
  563.     // completely processed frames
  564.     m_processed_frame_times.push_back(t_curr);
  565.   }
  566.   // processing time
  567.   RefTime prcs_time = t_curr-m_t_start_processing; // micro-second units
  568.   if (action==HV_PROCESS_FRAME || action==HV_SKIP_FRAME) {
  569.     m_prcs_times.push_back(prcs_time);
  570.   } else {
  571.     m_prcs_times.push_back(-1);
  572.   }
  573.   // keep only times from within the last second
  574.   // in the three _times arrays
  575.   // all frames that we saw
  576.   RefTime ago = t_curr-m_frame_times.front();
  577.   while (ago>1000000) { // 1 second
  578.     m_frame_times.erase(m_frame_times.begin());
  579.     m_prcs_times.erase(m_prcs_times.begin());
  580.     ago = t_curr-m_frame_times.front();
  581.     // we're guaranteed to have one element in there with ago==0,
  582.     // so we won't pop the list empty
  583.   }
  584.   // completed frames
  585.   while (!m_processed_frame_times.empty()) {
  586.     ago = t_curr-m_processed_frame_times.front();
  587.     if (ago<=1000000) { // 1 second
  588.       break;
  589.     }
  590.     m_processed_frame_times.erase(m_processed_frame_times.begin());
  591.   }
  592. }
  593. void HandVu::DrawOverlay()
  594. {
  595.   if (m_overlay_level>=1) {
  596.     CvFont font;
  597.     cvInitFont( &font, CV_FONT_VECTOR0, 0.5f /* hscale */, 
  598.                 0.5f /* vscale */, 0.1f /*italic_scale */, 
  599.                 1 /* thickness */);
  600.     // frames per second and min/max processing times
  601.     int fps = (int) m_frame_times.size();
  602.     int processed_fps = (int) m_processed_frame_times.size();
  603.     RefTime min_prcs_time=-1, max_prcs_time=-1;
  604.     for (int f=0; f<(int)m_prcs_times.size(); f++) {
  605.       if (m_prcs_times[f]==-1) {
  606.         continue;
  607.       }
  608.       if (m_prcs_times[f]<min_prcs_time || min_prcs_time==-1) {
  609.         min_prcs_time = m_prcs_times[f];
  610.       } 
  611.       if (m_prcs_times[f]>max_prcs_time || max_prcs_time==-1) {
  612.         max_prcs_time = m_prcs_times[f];
  613.       }
  614.     }
  615.     if (min_prcs_time==-1) {
  616.       ASSERT(max_prcs_time==-1);
  617.       min_prcs_time = 0;
  618.       max_prcs_time = 0;
  619.     }
  620.     char str[256];
  621.     sprintf(str, "%d (%d) fps, %d-%dms", fps, processed_fps, 
  622.             (int)(min_prcs_time/1000), (int)(max_prcs_time/1000)); 
  623.     CvSize textsize;
  624.     int underline;
  625.     cvGetTextSize( str, &font, &textsize, &underline );
  626.     CvPoint pos = cvPoint(m_rgbImage->width-textsize.width-5, textsize.height+10);
  627.     cvPutText(m_rgbImage, str, pos, &font, CV_RGB(0, 255, 0));
  628.     VERBOSE4(3, "HandVu: %d (%d) fps, %d-%dms latency", 
  629.              fps, processed_fps, (int)(min_prcs_time/1000), (int)(max_prcs_time/1000));
  630.     if (m_overlay_level>=2 && m_last_latencies.size()>0) {
  631.       RefTime last = m_last_latencies[m_last_latencies.size()-1];
  632.       char str[256];
  633.       sprintf(str, "(in latency: %dms)", (unsigned int)(last/1000)); 
  634.       CvSize textsize;
  635.       int underline;
  636.       cvGetTextSize(str, &font, &textsize, &underline );
  637.       CvPoint pos = cvPoint(m_rgbImage->width/2-textsize.width, textsize.height+10);
  638.       cvPutText(m_rgbImage, str, pos, &font, CV_RGB(0, 255, 0));
  639.     }
  640.     if (m_tracking) {
  641.       CvPoint pos = cvPoint(cvRound(m_center_pos.x), cvRound(m_center_pos.y));
  642.       cvCircle(m_rgbImage, pos, 13, CV_RGB(0, 0, 0), CV_FILLED);
  643.       cvCircle(m_rgbImage, pos, 10, CV_RGB(255, 255, 255), CV_FILLED);
  644. //      cvCircle(m_rgbImage, cvPoint(cvRound(m_center_pos.x), cvRound(m_center_pos.y)), 
  645.   //             5, CV_RGB(255, 0, 0), CV_FILLED);
  646.     }
  647.     if (m_overlay_level>=3) {
  648.       char str[256];
  649.       char* ptr = str;
  650.       if (!m_active) {
  651.         sprintf(ptr, "inactive ");
  652.         ptr += 9;
  653.       }
  654.       if (m_scan_area.left>=m_scan_area.right
  655.           || m_scan_area.top>=m_scan_area.bottom) 
  656.       {
  657.         sprintf(ptr, "zero-scan ");
  658.         ptr += 10;
  659.       }
  660.       if (m_undistort) {
  661.         sprintf(ptr, "u ");
  662.         ptr += 2;
  663.       }
  664.       if (m_adjust_exposure) {
  665.         sprintf(ptr, "e ");
  666.         ptr += 2;
  667.       }
  668.       if (ptr!=str) {
  669.         CvSize textsize;
  670.         int underline;
  671.         cvGetTextSize(str, &font, &textsize, &underline);
  672.         CvPoint pos = cvPoint(10, textsize.height+10);
  673.         cvPutText(m_rgbImage, str, pos, &font, CV_RGB(0, 255, 0));
  674.       }
  675.     }
  676.   }
  677. }
  678. bool HandVu::DoDetection()
  679. {
  680.   // scan cubicles
  681.   // todo RefTime before = m_pClock->GetCurrentTimeUsec();
  682.   m_pCubicle->Process(m_grayImages[m_curr_buf_indx]);
  683.   // todo RefTime after = m_pClock->GetCurrentTimeUsec();
  684.   // todo FILE* fp = fopen("c:\hv_tmp\times.txt", "a+");
  685.   // todo RefTime took = after-before;
  686.   // todo fprintf(fp, "%un", took);
  687.   // todo fclose(fp);
  688.   if (m_pCubicle->GotMatches()) {
  689.     m_last_match = m_pCubicle->GetBestMatch();
  690.     // got a match, but how and where?
  691.     // if m_dt_min_match_duration>0, we need more than a single match 
  692.     // but instead a succession of matches within a certain radius
  693.     // from each other
  694.     if (m_dt_first_match_time==0) {
  695.       m_dt_first_match = m_last_match;
  696.       m_dt_first_match_time = m_pClock->GetCurrentTimeUsec();
  697.     }
  698.       
  699.     VERBOSE4(4, "HandVu detection: area %d, %d, %d, %d", 
  700.              m_last_match.left, m_last_match.top, 
  701.              m_last_match.right, m_last_match.bottom);
  702.     // was the match close enough to the first match to be considered 
  703.     // within the same area?
  704.     int curr_center_x = (m_last_match.left+m_last_match.right)/2;
  705.     int curr_center_y = (m_last_match.top+m_last_match.bottom)/2;
  706.     int first_center_x =
  707.       (m_dt_first_match.left+m_dt_first_match.right)/2;
  708.     int first_center_y =
  709.       (m_dt_first_match.top+m_dt_first_match.bottom)/2;
  710.     int dx = curr_center_x-first_center_x;
  711.     int dy = curr_center_y-first_center_y;
  712.     if (sqrt((double) (dx*dx+dy*dy))<m_pConductor->m_dt_radius*m_img_width) {
  713.       // was the match duration long enough?
  714.       RefTime curr_time = m_pClock->GetCurrentTimeUsec();
  715.       if ((curr_time-m_dt_first_match_time)/1000
  716.           >= m_pConductor->m_dt_min_match_duration) 
  717.       {
  718.         // color verification
  719.         bool mostly_skin = VerifyColor();
  720.         if (mostly_skin) {
  721.           // that's it!
  722.           // turn off detection
  723.           for (int cc=0; cc<m_pConductor->m_dt_cascades_end; cc++) {
  724.             cuSetScannerActive((CuCascadeID)cc, false);
  725.           }
  726.           m_dt_first_match_time = 0;
  727.           // set scan area for tracking and recognition
  728.           int halfwidth = (m_last_match.right-m_last_match.left)/2;
  729.           int halfheight = (m_last_match.bottom-m_last_match.top)/2;
  730.           SetScanAreaVerified(CRect(m_last_match.left-halfwidth, m_last_match.top-halfheight, 
  731.                                     m_last_match.right+halfwidth, m_last_match.bottom+halfheight));
  732.           return true;
  733.         }
  734.       }
  735.     } else {
  736.       m_dt_first_match_time = 0;
  737.     }
  738.   } else {
  739.     m_dt_first_match_time = 0;
  740.   }
  741.   return false;
  742. }
  743. bool HandVu::DoRecognition()
  744. {
  745.   // set scan areas and
  746.   // activate detection scanners
  747.   for (int cc=m_pConductor->m_rc_cascades_start;
  748.        cc<m_pConductor->m_rc_cascades_end; cc++) {
  749.     cuSetScanArea((CuCascadeID)cc, 
  750.       m_scan_area.left, m_scan_area.top, m_scan_area.right, m_scan_area.bottom);
  751.     double sct = m_pConductor->m_rc_scale_tolerance;
  752.     cuSetScanScales((CuCascadeID)cc, m_last_match.scale/sct,
  753.                                  m_last_match.scale*sct);
  754.     cuSetScannerActive((CuCascadeID)cc, true);
  755.   }
  756.   m_pCubicle->Process(m_grayImages[m_curr_buf_indx]);
  757.   if (m_pCubicle->GotMatches()) {
  758.     m_last_match = m_pCubicle->GetBestMatch();
  759.     VERBOSE4(4, "HandVu detection: area %d, %d, %d, %d", 
  760.              m_last_match.left, m_last_match.top, 
  761.              m_last_match.right, m_last_match.bottom);
  762.     // set scan area for future
  763.     int halfwidth = (m_last_match.right-m_last_match.left)/2;
  764.     int halfheight = (m_last_match.bottom-m_last_match.top)/2;
  765.     SetScanAreaVerified(CRect(m_last_match.left-halfwidth, m_last_match.top-halfheight, 
  766.                               m_last_match.right+halfwidth, m_last_match.bottom+halfheight));
  767.     return true;
  768.   }
  769.   return false;
  770. }
  771. bool HandVu::VerifyColor()
  772. {
  773.   ConstMaskIt mask = m_pConductor->GetMask(m_last_match.name);
  774.   CRect roi(m_last_match);
  775.   double coverage =
  776.     m_pSkincolor->GetCoverage(m_rgbImage, roi, mask, false);
  777.   VERBOSE0(3, "HandVu verifyed color");
  778.   
  779.   bool sufficient = (coverage>=m_pConductor->m_dt_min_color_coverage);
  780.   if (sufficient) {
  781.     VERBOSE0(3, "HandVu: color match!");
  782.   } else {
  783.     VERBOSE0(3, "HandVu: insufficient color match");
  784.   }
  785.   return sufficient;
  786. }
  787. void HandVu::InitializeTracking()
  788. {
  789.   ConstMaskIt mask = m_pConductor->GetMask(m_last_match.name);
  790.   // if we haven't done so for some time, and the image was taken after
  791.   // m_time_to_learn_color:
  792.   if (m_time_to_learn_color<=m_sample_time) {
  793.     // learn the RGB lookup table and 
  794.     // use it for subsequent segmentations
  795.     m_pLearnedColor->LearnFromGroundTruth(m_rgbImage, m_last_match, mask);
  796.     m_time_to_learn_color = m_sample_time + m_min_time_between_learning_color;
  797.   }
  798.   if (m_pConductor->m_tr_type==VisionConductor::VC_TT_CAMSHIFT_HSV) {
  799.     m_pCamShift->PrepareTracking(m_rgbImage, NULL, CRect(m_last_match));
  800.   } else if (m_pConductor->m_tr_type==VisionConductor::VC_TT_CAMSHIFT_LEARNED) {
  801.     m_pCamShift->PrepareTracking(m_rgbImage, m_pLearnedColor, CRect(m_last_match));
  802.   } else {
  803.     // color segmentation provides a probability distribution to the
  804.     // optical flow filter to place features
  805.     m_pOpticalFlow->PrepareTracking(m_rgbImage,
  806.                                     m_grayImages[m_curr_buf_indx],
  807.                                     m_curr_buf_indx,
  808.                                     m_pLearnedColor, 
  809.                                     m_last_match,
  810.                                     mask,
  811.                                     m_pConductor->m_tr_num_KLT_features,
  812.                                     m_pConductor->m_tr_winsize_width,
  813.                                     m_pConductor->m_tr_winsize_height,
  814.                                     m_pConductor->m_tr_min_feature_distance,
  815.                                     m_pConductor->m_tr_max_feature_error);
  816.   }
  817.   m_center_pos.x = (m_last_match.right+m_last_match.left)/2.0f;
  818.   m_center_pos.y = (m_last_match.bottom+m_last_match.top)/2.0f;
  819. }
  820. bool HandVu::DoTracking()
  821. {
  822.   // the size of the last Cubicle match determines how far we let
  823.   // KLT features spread (during the call to Track)
  824.   int last_width  = m_last_match.right-m_last_match.left;
  825.   int last_height = m_last_match.bottom-m_last_match.top;
  826.   if (m_pConductor->m_tr_type & VisionConductor::VC_TT_CAMSHIFT) {
  827.     m_pCamShift->Track(m_rgbImage);
  828.     m_pCamShift->GetArea(m_scan_area);
  829.     m_center_pos.x = (m_scan_area.right+m_scan_area.left)/2.0f;
  830.     m_center_pos.y = (m_scan_area.bottom+m_scan_area.top)/2.0f;
  831.   } else {
  832.     bool flock = m_pConductor->m_tr_type==VisionConductor::VC_TT_OPTICAL_FLOW_FLOCK;
  833.     bool color = m_pConductor->m_tr_type==VisionConductor::VC_TT_OPTICAL_FLOW_COLOR;
  834.     if (m_pConductor->m_tr_type==VisionConductor::VC_TT_OPTICAL_FLOW_COLORFLOCK) {
  835.       flock = color = true;
  836.     }
  837.     int num_tracked =
  838.       m_pOpticalFlow->Track(m_rgbImage,
  839.                             m_grayImages[m_prev_buf_indx],
  840.                             m_grayImages[m_curr_buf_indx],
  841.                             m_prev_buf_indx, m_curr_buf_indx,
  842.                             last_width, last_height,
  843.                             flock, color);
  844.     
  845.     if (num_tracked<m_pConductor->m_tr_min_KLT_features) {
  846.       // lost tracking for sure
  847.       //    Beep(300, 150);
  848.       // don't change scan_area
  849.       return false;
  850.     }
  851.     m_pOpticalFlow->GetMeanFeaturePos(m_center_pos);
  852.     // VERBOSE1(4, "OpticalFlow features: %dn", event.m_num_features_tracked);
  853.   }
  854.   int rnx = cvRound(m_center_pos.x), rny = cvRound(m_center_pos.y);
  855.   SetScanAreaVerified(CRect(rnx-last_width, rny-last_height, 
  856.     rnx+last_width, rny+last_height));
  857.   return true;
  858. }
  859. void HandVu::CheckAndCorrectExposure()
  860. {
  861.   if (!m_adjust_exposure
  862.       || m_scan_area.left>=m_scan_area.right 
  863.       || m_scan_area.top>=m_scan_area.bottom)
  864.   {
  865.     return;
  866.   }
  867.   ASSERT(m_pCameraController);
  868.   ASSERT(m_pCameraController->CanAdjustExposure());
  869.   // is it time to adjust camera?
  870.   RefTime curr_time = m_pClock->GetCurrentTimeUsec();
  871.   if (curr_time<m_adjust_exposure_at_time) {
  872.     return;
  873.   }
  874.   // find out if we should change the exposure at all
  875.   int bins = 5;
  876.   CvHistogram* pHist = cvCreateHist(1, &bins, CV_HIST_ARRAY);
  877.   CvRect rect = 
  878.     cvRect(m_scan_area.left, m_scan_area.top, 
  879.     m_scan_area.right-m_scan_area.left, m_scan_area.bottom-m_scan_area.top);
  880.   cvSetImageROI(m_grayImages[m_curr_buf_indx], rect);
  881.   cvCalcHist(&m_grayImages[m_curr_buf_indx], pHist, 0, NULL);
  882.   cvResetImageROI(m_grayImages[m_curr_buf_indx]);
  883.   double bright_pixels = cvQueryHistValue_1D(pHist, 4);
  884.   bright_pixels /= (double) (rect.width*rect.height);
  885.   cvReleaseHist(&pHist);
  886.   VERBOSE1(4, "HandVu: bright_pixels %f", bright_pixels);
  887.   double exposure_factor = 1.0;
  888.   const double max_bright = 0.3;
  889.   const double min_bright = 0.1;
  890.   if (bright_pixels>max_bright) {
  891.     if (bright_pixels!=0) {
  892.       exposure_factor = max(0.75, max_bright/bright_pixels);
  893.     } else {
  894.       exposure_factor = 0.9;
  895.     }
  896.     m_adjust_exposure_at_time = curr_time+500000l; //.5 sec
  897.   } else if (bright_pixels<min_bright) {
  898.     if (bright_pixels!=0) {
  899.       exposure_factor = min(1.25, min_bright/bright_pixels);
  900.     } else {
  901.       exposure_factor = 1.1;
  902.     }
  903.     m_adjust_exposure_at_time = curr_time+500000l; //.5 sec
  904.   } else {
  905.     // we're right on, don't check again for some time
  906.     m_adjust_exposure_at_time = curr_time+1000000l; //1 sec
  907.   }
  908.   if (exposure_factor!=1.0) {
  909.     if (m_exposure_level<=0.01) {
  910.       m_exposure_level = 0.01;
  911.     } else {
  912.       m_exposure_level *= exposure_factor;
  913.     }
  914.     m_exposure_level = max(0.0, min(m_exposure_level, 1.0));
  915.     bool changed = m_pCameraController->SetExposure(m_exposure_level);
  916.     while (!changed && 0<m_exposure_level && m_exposure_level<1.0) {
  917.       m_exposure_level *= exposure_factor;
  918.       changed = m_pCameraController->SetExposure(m_exposure_level);
  919.     } 
  920.     VERBOSE2(4, "HandVu: set exposure level to %f (%d)n", m_exposure_level, changed);
  921.   }
  922. }
  923. void HandVu::FindSecondHand()
  924. {
  925.   if (m_id1_on) {
  926.     CvPoint2D32f pos;
  927.     m_pOpticalFlow->GetMeanFeaturePos(pos);
  928.     double w = m_last_match.right - m_last_match.left;
  929.     double h = m_last_match.bottom - m_last_match.top;
  930.     CRect area(10, cvRound(pos.y+h/3), cvRound(pos.x-w/3), m_rgbImage->height-10);
  931.     // pos is output parameter:
  932.     m_pLearnedColor->GetMostRightUpBlob(m_rgbImage, area, pos);
  933.     m_id1_x = pos.x;
  934.     m_id1_y = pos.y;
  935.   }
  936. }
  937. void HandVu::CalculateDepth(const IplImage* rightImage, const CvRect& area)
  938. {
  939. cvSetImageROI((IplImage*)rightImage, area);
  940. cvSetImageROI(m_rightGrayImage, area);
  941.   cvCvtColor(rightImage, m_rightGrayImage, CV_BGR2GRAY);
  942.   cvResetImageROI((IplImage*)rightImage);
  943.   cvSetImageROI(m_grayImages[m_curr_buf_indx], area);
  944.   int maxDisparity = min(30, area.width-1);
  945.   cvSetImageROI(m_depthImage, area);
  946.   cvFindStereoCorrespondence(m_grayImages[m_curr_buf_indx],
  947.                              m_rightGrayImage, CV_DISPARITY_BIRCHFIELD,
  948.                              m_depthImage, 
  949.                              maxDisparity, 15, 3, 6, 8, 15 );
  950.   cvResetImageROI(m_grayImages[m_curr_buf_indx]);
  951.   cvResetImageROI(m_rightGrayImage);
  952.   cvConvertScale(m_depthImage, m_depthImage, 255.0/(double)maxDisparity);
  953.   cvSetImageROI(m_rgbImage, area);
  954.   cvCvtColor(m_depthImage, m_rgbImage, CV_GRAY2BGR);
  955.   cvResetImageROI(m_rgbImage);
  956.   CvScalar s = cvAvg(m_depthImage);
  957.   m_center_depth = s.val[0];
  958. }
  959. /* SetDetectionArea sets the detection scan area in the video. 
  960. * The coordinates need not be in a specific orientation, i.e. left and right
  961. * can be switched and top and bottom can be switched.
  962. */
  963. void HandVu::SetDetectionArea(int left, int top, int right, int bottom)
  964. {
  965.   // righten area, smaller numbers to left and up
  966.   CRect scan_area(min(left, right), min(top, bottom),
  967.                   max(left, right), max(top, bottom));
  968.   for (int scc=0; scc<m_pConductor->m_dt_cascades_end; scc++) {
  969.     cuSetScanArea((CuCascadeID)scc, 
  970.       scan_area.left, scan_area.top, scan_area.right, scan_area.bottom);
  971.     m_pConductor->m_orig_areas[scc].fromRect(scan_area, m_img_width, m_img_height);
  972.   }
  973. }
  974. void HandVu::GetDetectionArea(CQuadruple& area) const
  975. {
  976.   // righten area, smaller numbers to left and up
  977.   area.left = area.top = 1.0;
  978.   area.right = area.bottom = 0.0;
  979.   for (int scc=0; scc<m_pConductor->m_dt_cascades_end; scc++) {
  980.     const CQuadruple& curr = m_pConductor->m_orig_areas[scc];
  981.     area.left = min(area.left, curr.left);
  982.     area.right = max(area.right, curr.right);
  983.     area.top = min(area.top, curr.top);
  984.     area.bottom = max(area.bottom, curr.bottom);
  985.   }
  986.   // if no detection cascades, or wrong data, set all to zero
  987.   if (area.left>=area.right || area.top>=area.bottom) {
  988.     area.left = area.right = area.top = area.bottom = 0.0;
  989.   }
  990. }
  991. void HandVu::SetOverlayLevel(int level)
  992. {
  993.   if (m_overlay_level<0 || m_overlay_level>MAX_OVERLAY_LEVEL) {
  994.     throw HVException("invalid overlay level");
  995.   }
  996.   m_overlay_level = level;
  997. }
  998. int HandVu::GetOverlayLevel()
  999. {
  1000.   return m_overlay_level;
  1001. }
  1002. bool HandVu::CanCorrectDistortion() const
  1003. {
  1004.   return m_pUndistortion->CanUndistort();
  1005. }
  1006. bool HandVu::IsCorrectingDistortion() const 
  1007. {
  1008.   if (!m_pUndistortion->CanUndistort()) {
  1009.     return false;
  1010.   }
  1011.   return m_undistort;
  1012. }
  1013. void HandVu::CorrectDistortion(bool enable)
  1014. {
  1015.   if (enable && !m_pUndistortion->CanUndistort()) {
  1016.     throw HVException("can not undistort");
  1017.   }
  1018.   m_undistort = enable;
  1019. }
  1020. void HandVu::SetAdjustExposure(bool enable/*=true*/)
  1021. {
  1022.   if (enable == m_adjust_exposure) {
  1023.     return;
  1024.   }
  1025.   if (!m_pCameraController) {
  1026.     throw HVException("no camera controller set");
  1027.   }
  1028.   if (!enable) {
  1029.     m_pCameraController->SetCameraAutoExposure(true);
  1030.     m_adjust_exposure = false;
  1031.     return;
  1032.   }
  1033.   if (!m_pCameraController->CanAdjustExposure()) {
  1034.     throw HVException("camera controller incapable of setting exposure");
  1035.   }
  1036.   m_exposure_level = m_pCameraController->GetCurrentExposure();
  1037.   m_adjust_exposure = enable;
  1038. }
  1039. bool HandVu::CanAdjustExposure() const
  1040. {
  1041.   return (m_pCameraController && m_pCameraController->CanAdjustExposure());
  1042. }
  1043. bool HandVu::IsAdjustingExposure() const
  1044. {
  1045.   return m_adjust_exposure;
  1046. }
  1047. void HandVu::GetState(int id, HVState& state) const
  1048. {
  1049.   ASSERT(m_pClock);
  1050.   state.m_tstamp = m_sample_time;
  1051.   state.m_obj_id = id;
  1052.   if (!m_active) {
  1053.     state.m_tracked = false;
  1054.     state.m_recognized = false;
  1055.     return;
  1056.   }
  1057.   if (id==0) {
  1058.     state.m_tracked = m_tracking;
  1059.     state.m_recognized = m_recognized;
  1060.     if (m_tracking || m_recognized) {
  1061.       state.m_center_xpos = m_center_pos.x/(float)m_img_width;
  1062.       state.m_center_ypos = m_center_pos.y/(float)m_img_height;
  1063.       state.m_scale = m_center_depth?m_center_depth:m_last_match.scale;
  1064.     } else {
  1065.       state.m_center_xpos = -1;
  1066.       state.m_center_ypos = -1;
  1067.       state.m_scale = 0;
  1068.     }
  1069.     if (m_recognized) {
  1070.       state.m_posture = m_last_match.name;
  1071.     } else {
  1072.       state.m_posture = "";
  1073.     }
  1074.     
  1075.   } else if (id==1) {
  1076.     if (!m_id1_on) {
  1077.       throw HVException("object ID1 is not being searched for");
  1078.     }
  1079.     if (m_id1_x==-1 || m_id1_y==-1) {
  1080.       state.m_tracked = false;
  1081.       state.m_center_xpos = -1;
  1082.       state.m_center_ypos = -1;
  1083.       state.m_recognized = false;
  1084.       state.m_posture = "";
  1085.       
  1086.     } else {
  1087.       state.m_tracked = true;
  1088.       state.m_center_xpos = m_id1_x / m_img_width;
  1089.       state.m_center_ypos = m_id1_y / m_img_height;
  1090.       state.m_recognized = false;
  1091.       state.m_posture = "";
  1092.     }
  1093.     
  1094.   } else {
  1095.     throw HVException("nothing known about this ID");
  1096.   }
  1097. }
  1098. void HandVu::SetLogfile(const string& filename)
  1099. {
  1100.   FILE* fp = fopen(filename.c_str(), "r");
  1101.   while (fp) {
  1102.     fclose(fp);
  1103.     string str("HandVu will not destroy existing file, please delete:n");
  1104.     str.append(filename);
  1105.     throw HVException(str);
  1106. //    fp = fopen(filename.c_str(), "r");
  1107.   }
  1108.   m_logfile_name = filename;
  1109. /* todo:  log stuff!
  1110. frame number
  1111. exposure control levels
  1112. latency result
  1113. state, actions -> dropped, skipped, processed
  1114. scan area
  1115. num features tracked, lost
  1116. coverage, TestLearned
  1117. */
  1118. }
  1119. void HandVu::SetScanAreaVerified(const CRect& area)
  1120. {
  1121.   double maxwidth = m_img_width*m_pConductor->m_rc_max_scan_width;
  1122.   if (area.right-area.left > maxwidth) {
  1123.     double halfwidth = maxwidth/2.0;
  1124.     double center = (double)(area.right+area.left)/2.0;
  1125.     m_scan_area.left = (int)(center-halfwidth);
  1126.     m_scan_area.right = (int)(center+halfwidth);
  1127.   } else {
  1128.     m_scan_area.left = area.left;
  1129.     m_scan_area.right = area.right;
  1130.   }
  1131.   double maxheight = m_img_height*m_pConductor->m_rc_max_scan_height;
  1132.   if (area.bottom-area.top > maxheight) {
  1133.     double halfheight = maxheight/2.0;
  1134.     double center = (double)(area.top+area.bottom)/2.0;
  1135.     m_scan_area.top = (int)(center-halfheight);
  1136.     m_scan_area.bottom = (int)(center+halfheight);
  1137.   } else {
  1138.     m_scan_area.top = area.top;
  1139.     m_scan_area.bottom = area.bottom;
  1140.   }
  1141.   VERBOSE4(4, "Set scan area to %d, %d, %d, %d", 
  1142.            m_scan_area.left, m_scan_area.top, m_scan_area.right, m_scan_area.bottom);
  1143. }