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

视频捕捉/采集

开发平台:

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: OpticalFlow.cpp,v 1.20 2005/08/16 00:09:55 matz Exp $
  22. **/
  23. #include "Common.h"
  24. #include "Skincolor.h"
  25. #include "OpticalFlow.h"
  26. #include "Exceptions.h"
  27. #include <time.h>
  28. #ifdef HAVE_FLOAT_H
  29. #include <float.h>
  30. #endif
  31. #if defined(WIN32) && defined(DEBUG)
  32. //#include <streams.h>
  33. #endif
  34. //
  35. // Constructor
  36. //
  37. OpticalFlow::OpticalFlow()
  38.   : m_num_features_tracked(0),
  39.     m_recent_max_rdv(0),
  40.     m_recent_max_rdv_decay(0),
  41.     m_condens_is_tracking(false),
  42.     m_pConDens(NULL),
  43.     m_num_features_lost(0),
  44.     m_winsize_width(-1),
  45.     m_winsize_height(-1),
  46.     m_min_distance(-1),
  47.     m_pProbDistrProvider(NULL),
  48.     m_num_pyramid_levels(3),
  49.     m_target_num_features(-1),
  50.     m_saved_prev_indx(-1),
  51.     m_saved_curr_indx(-1),
  52.     m_max_feature_error(-1),
  53.     m_prev_buf_meaningful(false),
  54.     m_prepared(false)
  55. {
  56.   m_pyramids[0] = NULL;
  57.   m_pyramids[1] = NULL;
  58.   m_tmpEVImage[0] = NULL;
  59.   m_tmpEVImage[1] = NULL;
  60.   m_mean_feature_pos.x = -1;
  61.   m_mean_feature_pos.y = -1;
  62.   srand((unsigned) time(NULL));
  63. } // (Constructor)
  64. OpticalFlow::~OpticalFlow()
  65. {
  66.   cvReleaseImage(&m_pyramids[0]);
  67.   cvReleaseImage(&m_pyramids[1]);
  68.   cvReleaseImage(&m_tmpEVImage[0]);
  69.   cvReleaseImage(&m_tmpEVImage[1]);
  70.   
  71.   if (m_pConDens) {
  72.     cvReleaseConDensation(&m_pConDens);
  73.     m_pConDens = NULL;
  74.   }
  75. }
  76. void OpticalFlow::Initialize(int width, int height)
  77. {
  78.   CvSize imgsize = cvSize(width, height);
  79.   cvReleaseImage(&m_pyramids[0]);
  80.   cvReleaseImage(&m_pyramids[1]);
  81.   m_pyramids[0] = cvCreateImage(imgsize, IPL_DEPTH_8U, 1);
  82.   m_pyramids[1] = cvCreateImage(imgsize, IPL_DEPTH_8U, 1);
  83.   cvReleaseImage(&m_tmpEVImage[0]);
  84.   cvReleaseImage(&m_tmpEVImage[1]);
  85.   m_tmpEVImage[0] = cvCreateImage(imgsize, IPL_DEPTH_32F, 1);
  86.   m_tmpEVImage[1] = cvCreateImage(imgsize, IPL_DEPTH_32F, 1);
  87. }
  88. #pragma warning (disable:4786)
  89. void OpticalFlow::PrepareTracking(IplImage* rgbImage,
  90.                                   IplImage* currGrayImage, int curr_indx,
  91.                                   ProbDistrProvider* pProbProv,
  92.                                   const CuScanMatch& match,
  93.                                   ConstMaskIt mask,
  94.                                   int target_num_features,
  95.                                   int winsize_width,
  96.                                   int winsize_height,
  97.                                   double min_distance,
  98.                                   double max_feature_error)
  99. {
  100.   m_pProbDistrProvider = pProbProv;
  101.   m_target_num_features = target_num_features;
  102.   m_num_features_tracked = 0;
  103.   m_prev_buf_meaningful = false;
  104.   m_winsize_width = winsize_width;
  105.   m_winsize_height = winsize_height;
  106.   m_min_distance = min_distance;
  107.   m_max_feature_error = max_feature_error;
  108.   
  109.   // first find a big set of features that sits on corners
  110.   int num_corners = m_target_num_features*3;
  111.   CPointVector corners;
  112.   corners.resize(num_corners);
  113.   CRect bbox(match);
  114.   FindGoodFeatures(currGrayImage, bbox, corners);
  115.   // then play with the color probability distribution to pick
  116.   // the ones that are on skin color, or if those aren't enough,
  117.   // pick some additional ones on skin colored pixels
  118.   m_features[0].resize(m_target_num_features);
  119.   m_features[1].resize(m_target_num_features);
  120.   m_feature_status.resize(m_target_num_features);
  121.   m_errors.resize(m_target_num_features);
  122.   PickSkinColoredFeatures(rgbImage, corners, m_features[curr_indx], match, mask);
  123.   // fine-tune feature locations
  124.   cvFindCornerSubPix(currGrayImage,
  125.                      (CvPoint2D32f*) &m_features[curr_indx][0],
  126.                      m_target_num_features, 
  127.                      cvSize(5,5), cvSize(-1,-1),
  128.                      cvTermCriteria( CV_TERMCRIT_ITER, 10, 0.1f ));
  129.   // set status right for these features
  130.   for (int i=0; i<m_target_num_features; i++) {
  131.     m_feature_status[i] = 1;
  132.   }
  133.   GetAverage(m_features[curr_indx], m_mean_feature_pos);
  134.   m_condens_is_tracking = false;
  135.   m_condens_init_rect = CRect(match);
  136.   m_prepared = true;
  137. }
  138. #pragma warning (default:4786)
  139. int OpticalFlow::Track(IplImage* rgbImage,
  140.                        IplImage* prevImage, IplImage* currImage,
  141.                        int prev_indx, int curr_indx, 
  142.                        int last_width, int last_height,
  143.                        bool flock, bool use_prob_distr)
  144. {
  145.   ASSERT(m_prepared);
  146.   m_prev_buf_meaningful = true;
  147.   LKPyramids(prevImage, currImage, prev_indx, curr_indx);
  148. #if 0
  149.   if (todo) {
  150.     /* track a number of KLT features with an n-stage pyramid
  151.     * and globally optimize their positions with Condensation
  152.     */
  153.     const int condens_num_samples = 128;
  154.     if (!m_condens_is_tracking) {
  155.       InitCondensation(condens_num_samples);
  156.       ASSERT(m_pConDens->SamplesNum==condens_num_samples);
  157.       m_condens_is_tracking = true;
  158.     }
  159.     UpdateCondensation(rgbImage, prev_indx, curr_indx);
  160.     ASSERT(m_pConDens->SamplesNum==condens_num_samples);
  161.   }
  162. #endif
  163.   if (flock) {
  164.     ConcentrateFeatures(rgbImage, m_features[curr_indx], m_feature_status, 
  165.                         last_width, last_height, use_prob_distr);
  166.   } else {
  167.     AddNewFeatures(rgbImage, m_features[curr_indx], m_feature_status, 
  168.                    last_width, last_height, use_prob_distr);
  169.   }
  170.   GetAverage(m_features[curr_indx], m_mean_feature_pos);
  171.   m_saved_prev_indx = prev_indx;
  172.   m_saved_curr_indx = curr_indx;
  173.   return m_num_features_tracked;
  174. } // Track
  175. void OpticalFlow::DrawOverlay(IplImage* iplImage, int overlay_level)
  176. {
  177.   DrawFeaturesStatistics(iplImage, overlay_level);
  178. }
  179. void OpticalFlow::GetMeanFeaturePos(CvPoint2D32f& mean)
  180. {
  181.   mean = m_mean_feature_pos;
  182. }
  183. /* track a number of KLT features with an n-stage pyramid
  184. */
  185. void OpticalFlow::LKPyramids(IplImage* prevImage, IplImage* currImage,
  186.                              int prev_indx, int curr_indx)
  187. {
  188.   CvTermCriteria criteria =
  189.     cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 20, 0.03);
  190.   int flags = 0;
  191.   if (m_num_features_tracked>0) {
  192.     flags |= CV_LKFLOW_PYR_A_READY;
  193.   }
  194.   /* note: m_num_pyramid_levels can only be changed before the
  195.    * playback is started.  To release this restriction, the m_pyramids
  196.    * must be re-initialized, that is pyr0_ready set appropriately.
  197.   */
  198.   // in last frame, and possibly during Get/SetFeatures, how many of
  199.   // them were lost?
  200.   ASSERT((int)m_feature_status.size()>=m_target_num_features);
  201.   if (m_target_num_features>0) {
  202.     cvCalcOpticalFlowPyrLK(prevImage, // frame A
  203.                            currImage,   // frame B
  204.                            m_pyramids[prev_indx], // buffer for pyramid for A
  205.                            m_pyramids[curr_indx], // buffer for pyramid for B
  206.                            // feature points to track in A
  207.                            (CvPoint2D32f*) &m_features[prev_indx][0], 
  208.                            // calculated positions in B
  209.                            (CvPoint2D32f*) &m_features[curr_indx][0],
  210.                            // number of feature points to track
  211.                            m_target_num_features,
  212.                            // search window size per pyramid level
  213.                            cvSize(m_winsize_width, m_winsize_height),
  214.                            // max number of pyramid levels
  215.                            m_num_pyramid_levels,
  216.                            // array pos will be set to 1 if corresponding
  217.                            // feature point was found
  218.                            &m_feature_status[0],
  219.                            // may be NULL, diff btw old
  220.                            // and new area around features
  221.                            &m_errors[0],
  222.                            criteria, // iteration termination criteria
  223.                            flags  // todo: put estimate, see Documentation
  224.       );
  225.     int count = m_num_features_tracked = m_target_num_features;
  226.     for (int cnt1=0, k=0; cnt1<count; cnt1++) {
  227.       if (m_feature_status[cnt1] && m_errors[cnt1]<m_max_feature_error) {
  228.         m_features[prev_indx][k] = m_features[prev_indx][cnt1];
  229.         m_features[curr_indx][k] = m_features[curr_indx][cnt1];
  230.         k++;
  231.       } else {
  232.         m_feature_status[cnt1] = 0;
  233.         m_num_features_tracked --;
  234.       }
  235.     }
  236.   }
  237.   m_num_features_lost = m_target_num_features-m_num_features_tracked;
  238. }
  239. void OpticalFlow::DrawFeaturesStatistics(IplImage* pImage, int overlay_level)
  240. {
  241. #if 0
  242.   // for drawing statistics bars
  243.   double height = (double)pImage->height/2.0;
  244.   int xpos = 0;
  245.   const int width = 20;
  246.   const int space = 5;
  247.   if (overlay_level>=3) {
  248.     // num_features
  249.     double goal = m_target_num_features;
  250.     double rf = (goal-(double)m_num_features_lost)/goal;
  251.     cvRectangle (pImage, cvPoint(xpos, pImage->height),
  252.       cvPoint(xpos+width, pImage->height-(int)height),
  253.       CV_RGB(255,0,0), 1);
  254.     cvRectangle (pImage, cvPoint(xpos, pImage->height),
  255.       cvPoint(xpos+width, pImage->height-(int)(height*rf)),
  256.       CV_RGB(255,0,0), CV_FILLED);
  257.     xpos += width+space;
  258.   }
  259. #endif
  260.   const CvScalar black = CV_RGB(0, 0, 0);
  261.   const CvScalar blue  = CV_RGB(255, 0, 0);
  262. //  const CvScalar green = CV_RGB(0, 255, 0);
  263.   const CvScalar red   = CV_RGB(0, 0, 255);
  264.   const CvScalar yellow= CV_RGB(0, 255, 255);
  265.   const CvScalar white = CV_RGB(255, 255, 255);
  266.   if (m_condens_is_tracking && overlay_level>=3) {
  267.     // a circle at each sample location, its size indicating its confidence
  268.     int num_samples = m_pConDens->SamplesNum;
  269.     double min_conf = DBL_MAX;
  270.     double max_conf = DBL_MIN;
  271.     for (int scnt=0; scnt<num_samples; scnt++) {
  272.       min_conf = min(min_conf, m_sample_confidences[scnt]);
  273.       max_conf = max(max_conf, m_sample_confidences[scnt]);
  274.     }
  275.     double size_scale = 10.0/(max_conf-min_conf);
  276.     for (int scnt=0; scnt<num_samples; scnt++) {
  277.       int x = cvRound(m_pConDens->flSamples[scnt][0]);
  278.       int y = cvRound(m_pConDens->flSamples[scnt][2]);
  279.       int size = cvRound(1.0+(m_sample_confidences[scnt]-min_conf)*size_scale);
  280.       cvCircle(pImage, cvPoint(x, y), size, yellow, 1); 
  281. //      VERBOSE3(3, "%d: %f -> %d", scnt, m_sample_confidences[scnt], size);
  282.     }
  283.   }
  284.   if (overlay_level>=3) {
  285.     for (int cnt2 = 0; cnt2 < m_num_features_tracked; cnt2 ++) {
  286.       // predicted location - blue
  287.       if ((int)m_tmp_predicted.size()>cnt2) {
  288.         int xp = cvRound(m_tmp_predicted[cnt2].x);
  289.         int yp = cvRound(m_tmp_predicted[cnt2].y);
  290.         cvCircle(pImage, cvPoint(xp, yp), 3, blue, CV_FILLED); 
  291.       }
  292. #if 0
  293.       // old location - white
  294.       if (false && m_prev_buf_meaningful) {
  295.         int xo = cvRound(m_features[m_saved_prev_indx][cnt2].x);
  296.         int yo = cvRound(m_features[m_saved_prev_indx][cnt2].y);
  297.         cvCircle(pImage, cvPoint(xo, yo), 2, white, CV_FILLED); 
  298.       }
  299. #endif
  300.       // observation - red
  301.       if ((int)m_features_observation.size()>cnt2) {
  302.         int xp = cvRound(m_features_observation[cnt2].x);
  303.         int yp = cvRound(m_features_observation[cnt2].y);
  304.         cvCircle(pImage, cvPoint(xp, yp), 2, red, 1); 
  305.       }
  306.     }
  307.   }
  308.   if (overlay_level>=2) {
  309.     for (int cnt2 = 0; cnt2 < m_num_features_tracked; cnt2 ++) {
  310.       // new location - green
  311.       int xn = cvRound(m_features[m_saved_curr_indx][cnt2].x);
  312.       int yn = cvRound(m_features[m_saved_curr_indx][cnt2].y);
  313.       cvCircle(pImage, cvPoint(xn, yn), 5, black, CV_FILLED);
  314.       cvCircle(pImage, cvPoint(xn, yn), 3, white, CV_FILLED);
  315.     }
  316.   }
  317. #if 0
  318.   if (m_prev_buf_meaningful && overlay_level>=3) {
  319.     // also draw rose during following loop
  320.     int rose_x = 100;
  321.     int rose_y = pImage->height-100;
  322.     int len_factor = 2;
  323.     double dx_sum = 0;
  324.     double dy_sum = 0;
  325.     double vel_sqr_sum = 0;
  326.     for (int ft=0; ft<m_num_features_tracked; ft++) {
  327.       // current
  328.       double x1 = m_features[m_saved_curr_indx][ft].x;
  329.       double y1 = m_features[m_saved_curr_indx][ft].y;
  330.       // previous frame
  331.       double x2 = m_features[m_saved_prev_indx][ft].x;
  332.       double y2 = m_features[m_saved_prev_indx][ft].y;
  333.       // velocity
  334.       double dx = x1-x2;
  335.       double dy = y1-y2;
  336.       dx_sum += dx;
  337.       dy_sum += dy;
  338.       double vel = sqrt(dx*dx+dy*dy);
  339.       vel_sqr_sum += vel*vel;
  340.       cvLine(pImage, 
  341.              cvPoint(rose_x, rose_y),
  342.              cvPoint(rose_x+(int)dx*len_factor, rose_y+(int)dy*len_factor),
  343.              CV_RGB(255,255,255), 2);
  344.     }
  345.     double dx_mean = dx_sum/(double)m_num_features_tracked;
  346.     double dy_mean = dy_sum/(double)m_num_features_tracked;
  347.     double vel_mean = sqrt(dx_mean*dx_mean+dy_mean*dy_mean);
  348.     double dir_mean;
  349.     if (vel_mean>0) {
  350.       dir_mean = atan(dy_mean/dx_mean);
  351.       if (dx_mean<0) dir_mean += M_PI;
  352.       if (dir_mean<0) dir_mean += 2.0*M_PI;
  353.       if (dir_mean>=2.0*M_PI) dir_mean -= 2.0*M_PI;
  354.     } else {
  355.       dir_mean = 0;
  356.     }
  357.     ASSERT(0<=dir_mean && dir_mean<2.0*M_PI);
  358.     
  359.     double vel_stddev = 0;
  360.     double dir_stddev = 0;
  361.     for (int ft=0; ft<m_num_features_tracked; ft++) {
  362.       // current
  363.       double x1 = m_features[m_saved_curr_indx][ft].x;
  364.       double y1 = m_features[m_saved_curr_indx][ft].y;
  365.       // previous frame
  366.       double x2 = m_features[m_saved_prev_indx][ft].x;
  367.       double y2 = m_features[m_saved_prev_indx][ft].y;
  368.       // velocity
  369.       double dx = x1-x2;
  370.       double dy = y1-y2;
  371.       dx_sum += dx;
  372.       dy_sum += dy;
  373.       double vel = sqrt(dx*dx+dy*dy);
  374.       vel_stddev += (vel-vel_mean)*(vel-vel_mean);
  375.       double dir = atan(dy/dx);
  376.       if (dx<0) dir += M_PI;
  377.       if (dir<0) dir += 2.0*M_PI;
  378.       if (dir>=2.0*M_PI) dir -= 2.0*M_PI;
  379.       double dirdiff = min(fabs(dir-dir_mean), fabs(dir-2.0*M_PI-dir_mean));
  380.       dirdiff = min(dirdiff, fabs(dir+2.0*M_PI-dir_mean));
  381.       dir_stddev += dirdiff*dirdiff;
  382.     }
  383.     vel_stddev /= (double)m_num_features_tracked;
  384.     vel_stddev = sqrt(vel_stddev);
  385.     dir_stddev /= (double)m_num_features_tracked;
  386.     dir_stddev = sqrt(dir_stddev);
  387.     
  388.     // mean velocity and direction
  389.     cvLine(pImage, 
  390.            cvPoint(rose_x, rose_y), 
  391.            cvPoint(rose_x+(int)dx_mean*len_factor,
  392.                    rose_y+(int)dy_mean*len_factor),
  393.            CV_RGB(255,0,0), 8);
  394.     // +/- stddev velocity
  395.     double vel_stddev_x = cos(dir_mean)*vel_stddev;
  396.     double vel_stddev_y = sin(dir_mean)*vel_stddev;
  397.     cvLine(pImage, 
  398.            cvPoint(rose_x+(int)(dx_mean-vel_stddev_x)*len_factor,
  399.                    rose_y+(int)(dy_mean-vel_stddev_y)*len_factor),
  400.            cvPoint(rose_x+(int)(dx_mean+vel_stddev_x)*len_factor,
  401.                    rose_y+(int)(dy_mean+vel_stddev_y)*len_factor),
  402.            CV_RGB(255,255,0), 2);
  403.     cvCircle( pImage, cvPoint(rose_x, rose_y), 30, CV_RGB(255,0,0), 1 );
  404.     
  405.     // bars for direction stddev
  406.     /*
  407.       double rd = dir_stddev/2*M_PI;
  408.       cvRectangle (pImage, cvPoint(xpos, pImage->height),
  409.       cvPoint(xpos+width, pImage->height-(int)height), CV_RGB(0,255,255), 1);
  410.       cvRectangle (pImage, cvPoint(xpos, pImage->height),
  411.       cvPoint(xpos+width, pImage->height-(int)(height*rd)),
  412.       CV_RGB(0,255,255), CV_FILLED);
  413.       xpos += width+space;
  414.     */
  415.     // bars for direction stddev multiplied with velocity
  416.     if (m_recent_max_rdv_decay>0) {
  417.       // draw peak bar
  418.       m_recent_max_rdv_decay--;
  419.       if (m_recent_max_rdv_decay==0) {
  420.         m_recent_max_rdv = 0;
  421.       } else {
  422.         cvRectangle (pImage, cvPoint(xpos, pImage->height), 
  423.                      cvPoint(xpos+width,
  424.                              pImage->height-(int)(height*m_recent_max_rdv)),
  425.                      CV_RGB(0,0,255), CV_FILLED);
  426.       }
  427.     }
  428.     double rdv = vel_mean*dir_stddev/(2*M_PI*5);
  429.     if (rdv>m_recent_max_rdv) {
  430.       m_recent_max_rdv = rdv;
  431.       m_recent_max_rdv_decay = 10;
  432.     }
  433.     cvRectangle (pImage, cvPoint(xpos, pImage->height),
  434.                  cvPoint(xpos+width, pImage->height-(int)height),
  435.                  CV_RGB(0,255,255), 1);
  436.     cvRectangle (pImage, cvPoint(xpos, pImage->height),
  437.                  cvPoint(xpos+width, pImage->height-(int)(height*rdv)),
  438.                  CV_RGB(0,255,255), CV_FILLED);
  439.     xpos += width+space;
  440.   }
  441. #endif
  442. }
  443. /** find good features to track
  444.  */
  445. void OpticalFlow::FindGoodFeatures(IplImage* grayImage, const CRect& bbox,
  446.                                    CPointVector& corners) {
  447.   double quality = 0.1; 
  448.   //only those corners are selected, which minimal eigen value is
  449.   //non-less than maximum of minimal eigen values on the image,
  450.   //multiplied by quality_level. For example, quality_level = 0.1
  451.   //means that selected corners must be at least 1/10 as good as
  452.   //the best corner.
  453.   int count = (int) corners.size();
  454.   int width = min(grayImage->width, bbox.right-bbox.left);
  455.   int height = min(grayImage->height, bbox.bottom-bbox.top);
  456.   CvRect cvBox = cvRect(bbox.left, bbox.top, width, height);
  457.   cvSetImageROI(grayImage, cvBox);
  458.   cvSetImageROI(m_tmpEVImage[0], cvBox);
  459.   cvSetImageROI(m_tmpEVImage[1], cvBox);
  460.   cvGoodFeaturesToTrack(grayImage, m_tmpEVImage[0], m_tmpEVImage[1],
  461.     (CvPoint2D32f*) &corners[0], &count, quality, m_min_distance);
  462.   ASSERT(count); // we want at least SOME features!
  463.   cvResetImageROI(grayImage);
  464.   cvResetImageROI(m_tmpEVImage[0]);
  465.   cvResetImageROI(m_tmpEVImage[1]);
  466.   corners.resize(count);
  467.   // their locations are relative to the bbox origin - translate them
  468.   for (int cnt=0; cnt<count; cnt++) {
  469.     corners[cnt].x += bbox.left;
  470.     corners[cnt].y += bbox.top;
  471.   }
  472. }
  473. #define MIN_PROB_FOREGROUND 0.5
  474. /* pick features on foreground color
  475.  */
  476. #pragma warning (disable:4786)
  477. void OpticalFlow::PickSkinColoredFeatures(IplImage* rgbImage,
  478.                                           const CPointVector& corners,
  479.                                           CPointVector& features,
  480.                                           const CuScanMatch& match,
  481.                                           ConstMaskIt mask)
  482. {
  483.   ASSERT(m_pProbDistrProvider);
  484.   
  485.   int num_features = (int) features.size();
  486.   int poolsize = (int) corners.size();
  487.   CDoubleVector corner_probs(poolsize);
  488.   int width = match.right-match.left;
  489.   int height = match.bottom-match.top;
  490.   double scale_x = (double)(width-1)/((*mask).second.GetWidth()-1);
  491.   double scale_y = (double)(height-1)/((*mask).second.GetHeight()-1);
  492.   // create a vector with probabilities, combined of observed color and mask
  493.   for (int pp=0; pp<poolsize; pp++) {
  494.     ColorBGR* color;
  495.     GetPixel(rgbImage, (int) corners[pp].x, (int) corners[pp].y, &color);
  496.     double color_prob = m_pProbDistrProvider->LookupProb(*color);
  497.     int m_x = cvRound((corners[pp].x-match.left)/scale_x);
  498.     int m_y = cvRound((corners[pp].y-match.top)/scale_y);
  499.     double loc_prob = (*mask).second.GetProb(m_x, m_y);
  500.     corner_probs[pp] = color_prob*loc_prob;
  501.   }
  502.   // for each feature, find the most skin-colored corner and pick it
  503.   for (int fcnt=0; fcnt<num_features; fcnt++) {
  504.     double max_prob = -DBL_MAX;
  505.     int max_corner_indx = -1;
  506.     for (int pp=0; pp<poolsize; pp++) {
  507.       if (corner_probs[pp]>max_prob) {
  508.         max_prob = corner_probs[pp];
  509.         max_corner_indx = pp;
  510.       }
  511.     }
  512.     ASSERT(max_prob>=.0);
  513.     if (max_prob>=MIN_PROB_FOREGROUND) {
  514.       // found a (the best!) corner with skin color
  515.       features[fcnt].x = (float)corners[max_corner_indx].x;
  516.       features[fcnt].y = (float)corners[max_corner_indx].y;
  517.       corner_probs[max_corner_indx] = .0;
  518.     } else {
  519.       // pick our own random location, somewhere with skin color
  520.       // and far away enough from the other features if possible
  521.       int x, y;
  522.       double prob;
  523.       int cnt = 0;
  524.       bool too_close;
  525.       // try to find a location that was segmented as foreground color
  526.       do {
  527.         x = match.left+(rand()%width);
  528.         y = match.top+(rand()%height);
  529.         x = min(x, rgbImage->width-1);
  530.         y = min(y, rgbImage->height-1);
  531.         x = max(x, 0);
  532.         y = max(y, 0);
  533.         ColorBGR* color;
  534.         GetPixel(rgbImage, x, y, &color);
  535.         double color_prob = m_pProbDistrProvider->LookupProb(*color);
  536.         int m_x = cvRound((double)(x-match.left)/scale_x);
  537.         int m_y = cvRound((double)(y-match.top)/scale_y);
  538.         double loc_prob = (*mask).second.GetProb(m_x, m_y);
  539.         prob = color_prob*loc_prob;
  540.         too_close = TooCloseToOthers(features, fcnt, fcnt);
  541.         cnt++;
  542.       } while ((prob<MIN_PROB_FOREGROUND || too_close) && cnt<20);
  543.       features[fcnt].x = (float)x;
  544.       features[fcnt].y = (float)y;
  545.     
  546.     }
  547.   }
  548. }
  549. #pragma warning (default:4786)
  550. /*
  551. * find feature that has the minimum
  552. * cumulative distance from all other features;
  553. * will discard the discard_num_furthest distances in the computation
  554. */
  555. int OpticalFlow::FindCentroid(const CDoubleMatrix& distances,
  556.                               int discard_num_furthest)
  557. {
  558.   double min_cum_dist = DBL_MAX;
  559.   int min_cum_dist_indx = -1;
  560.   int num_elems = (int)distances.size();
  561.   ASSERT(num_elems>discard_num_furthest);
  562.   vector<double> furthest; // will be sorted highest to smallest
  563.   furthest.resize(discard_num_furthest);
  564.   for (int icnt1=0; icnt1<num_elems; icnt1++) {
  565.     double cum_dist=0;
  566.     for (int f=0; f<discard_num_furthest; f++) furthest[f]=0;
  567.     for (int icnt2=0; icnt2<num_elems; icnt2++) {
  568.       double dist;
  569.       if (icnt1<icnt2) {
  570.         dist = distances[icnt1][icnt2];
  571.       } else if (icnt2<icnt1) {
  572.         dist = distances[icnt2][icnt1];
  573.       } else {
  574.         ASSERT(icnt1==icnt2);
  575.         continue;
  576.       }
  577.       // check if it's a far-away feature, update "furthest" vector if so.
  578.       // only add the distance if it's not too far away
  579.       for (int f=0; f<discard_num_furthest; f++) {
  580.         if (dist>furthest[f]) {
  581.           // add smallest "furthest" dist to the sum before we kick it out
  582.           // of the vector
  583.           cum_dist += furthest[discard_num_furthest-1];
  584.           for (int s=f; s<discard_num_furthest-1; s++) {
  585.             furthest[s+1] = furthest[s];
  586.           }
  587.           furthest[f] = dist;
  588.           break;
  589.         }
  590.       }
  591.       cum_dist += dist;
  592.     }
  593.     if (cum_dist<min_cum_dist) {
  594.       min_cum_dist = cum_dist;
  595.       min_cum_dist_indx = icnt1;
  596.     }
  597.   }
  598.   return min_cum_dist_indx;
  599. }
  600. /*
  601. * find average of feature locations
  602. */
  603. void OpticalFlow::GetAverage(const CPointVector& features, CvPoint2D32f& avg)
  604. {
  605.   double sum_x = 0;
  606.   double sum_y = 0;
  607.   int num_elems = (int) features.size();
  608.   for (int icnt=0; icnt<num_elems; icnt++) {
  609.     sum_x += features[icnt].x;
  610.     sum_y += features[icnt].y;
  611.   }
  612.   avg.x = (float)(sum_x/(double)num_elems);
  613.   avg.y = (float)(sum_y/(double)num_elems);
  614. }
  615. /* fill in half of the matrix with the distance measures;
  616. * only the upper half is filled in, i.e. if row<col there's a value
  617. */
  618. void OpticalFlow::DistanceMatrix(const CPointVector& features,
  619.                                  CDoubleMatrix& distances)
  620. {
  621.   int num_features = (int)features.size();
  622.   distances.resize(num_features);
  623.   for (int row=0; row<num_features; row++) {
  624.     distances[row].resize(num_features);
  625.     for (int col=row+1; col<num_features; col++) {
  626.       double dx = features[col].x-features[row].x;
  627.       double dy = features[col].y-features[row].y;
  628.       distances[row][col] = sqrt(dx*dx+dy*dy);
  629.     }
  630.   }
  631. }
  632. bool OpticalFlow::TooCloseToOthers(const CPointVector& features, int indx, int len)
  633. {
  634.   for (int fcnt=0; fcnt<len; fcnt++) {
  635.     if (indx==fcnt) continue;
  636.     double dx = features[fcnt].x-features[indx].x;
  637.     double dy = features[fcnt].y-features[indx].y;
  638.     double dist = sqrt(dx*dx+dy*dy);
  639.     if (dist<m_min_distance) {
  640.       return true;
  641.     }
  642.   }
  643.   return false;
  644. }
  645. /* randomly re-place one of two points that are too close to each other,
  646. * move others that are too far from the centroid towards the centroid
  647. */
  648. void OpticalFlow::ConcentrateFeatures(IplImage* rgbImage,
  649.                                       CPointVector& features,
  650.                                       CStatusVector& status,
  651.                                       int last_width, int last_height,
  652.                                       bool use_prob_distr)
  653. {
  654.   ASSERT(m_pProbDistrProvider);
  655.   int round_or_square = 2; // the area in which we will concentrate the features, 1 is round, 2 is rectangular
  656.   CDoubleMatrix distances;
  657.   DistanceMatrix(features, distances);
  658.   // discard the highest 15percent of distances when computing centroid
  659.   int discard_num_distances = (int)(0.15*(double)distances.size());
  660.   int centroid_index = FindCentroid(distances, discard_num_distances);
  661.   CvPoint2D32f centroid = features[centroid_index];
  662.   double halfwidth = last_width/2.0;
  663.   double halfheight = last_height/2.0;
  664.   double left = centroid.x-halfwidth;
  665.   double right = centroid.x+halfwidth;
  666.   double top = centroid.y-halfheight;
  667.   double bottom = centroid.y+halfheight;
  668.   int num_features = (int)features.size();
  669.   for (int fcnt=0; fcnt<num_features; fcnt++) {
  670.     m_feature_status[fcnt] = 1;
  671.     for (int scnd=fcnt+1; scnd<num_features; scnd++) {
  672.       double dist;
  673.       if (fcnt<scnd) {
  674.         dist = distances[fcnt][scnd];
  675.       } else { // scnd<fcnt
  676.         dist = distances[scnd][fcnt];
  677.       }
  678.       int cnt=0;
  679.       bool too_close = dist<m_min_distance;
  680.       while (too_close && cnt<20) {
  681.         // try to find a location that was segmented as foreground color
  682.         // and one that's far away enough from all other features
  683.         int x, y;
  684.         double prob = 1.0;
  685.         int prob_cnt = 0;
  686.         do {
  687.           x = (int) (centroid.x-last_width/2.0+(rand()%last_width));
  688.           y = (int) (centroid.y-last_height/2.0+(rand()%last_height));
  689.           x = min(x, rgbImage->width-1);
  690.           y = min(y, rgbImage->height-1);
  691.           x = max(x, 0);
  692.           y = max(y, 0);
  693.           ColorBGR* color;
  694.           GetPixel(rgbImage, x, y, &color);
  695.           if (use_prob_distr) {
  696.             prob = m_pProbDistrProvider->LookupProb(*color);
  697.           }
  698.           prob_cnt++;
  699.         } while (prob<MIN_PROB_FOREGROUND && prob_cnt<20);
  700.         features[fcnt].x = (float)x;
  701.         features[fcnt].y = (float)y;
  702.         status[fcnt] = 0;
  703.         too_close = TooCloseToOthers(features, fcnt, (int)features.size());
  704.         cnt++;
  705.       } // end while
  706.     } // end for scnd
  707.     if (centroid_index!=fcnt) {
  708.       if (round_or_square==1) {
  709.         // round
  710.         double dist = -1;
  711.         if (fcnt<centroid_index) {
  712.           dist = distances[fcnt][centroid_index];
  713.         } else if (centroid_index<fcnt) {
  714.           dist = distances[centroid_index][fcnt];
  715.         } else {
  716.           // will not enter the following loop
  717.         }
  718.         while (dist>last_width/2.0) {
  719.           features[fcnt].x = (float)((features[fcnt].x+centroid.x)/2.0);
  720.           features[fcnt].y = (float)((features[fcnt].y+centroid.y)/2.0);
  721.           m_feature_status[fcnt] = 0;
  722.           double dx = features[fcnt].x-centroid.x;
  723.           double dy = features[fcnt].y-centroid.y;
  724.           dist = sqrt(dx*dx+dy*dy);;
  725.         }
  726.       } else {
  727.         // rectangular area
  728.         // move towards horizontal center
  729.         while (features[fcnt].x<left || features[fcnt].x>right) {
  730.           features[fcnt].x = (float)((features[fcnt].x+centroid.x)/2.0);
  731.           status[fcnt] = 0;
  732.         }
  733.         // move towards vertical center
  734.         while (features[fcnt].y<top || features[fcnt].y>bottom) {
  735.           features[fcnt].y = (float)((features[fcnt].y+centroid.y)/2.0);
  736.           status[fcnt] = 0;
  737.         }
  738.       }
  739.     } // end centroid_index!=fcnt
  740.     features[fcnt].x = min(max(features[fcnt].x, .0f), rgbImage->width-1.0f);
  741.     features[fcnt].y = min(max(features[fcnt].y, .0f), rgbImage->height-1.0f);
  742.   }
  743. }
  744. /* randomly re-place one of two points that are too close to each other,
  745. * move others that are too far from the centroid towards the centroid
  746. */
  747. void OpticalFlow::AddNewFeatures(IplImage* rgbImage,
  748.                                  CPointVector& features,
  749.                                  CStatusVector& status,
  750.                                  int last_width, int last_height,
  751.                                  bool use_prob_distr)
  752. {
  753.   ASSERT(m_pProbDistrProvider);
  754.   CDoubleMatrix distances;
  755.   DistanceMatrix(features, distances);
  756.   // discard the highest 15percent of distances when computing centroid
  757.   int discard_num_distances = (int)(0.15*(double)distances.size());
  758.   int centroid_index = FindCentroid(distances, discard_num_distances);
  759.   CvPoint2D32f centroid = features[centroid_index];
  760. //  double halfwidth = last_width/2.0;
  761. //  double halfheight = last_height/2.0;
  762. //  double left = centroid.x-halfwidth;
  763. //  double right = centroid.x+halfwidth;
  764. //  double top = centroid.y-halfheight;
  765. //  double bottom = centroid.y+halfheight;
  766.   for (int fcnt=m_num_features_tracked; fcnt<m_target_num_features; fcnt++) {
  767.     // try to find a location that was segmented as foreground color
  768.     int x, y;
  769.     double prob = 1.0;
  770.     int prob_cnt = 0;
  771.     do {
  772.       x = (int) (centroid.x-last_width/2.0+(rand()%last_width));
  773.       y = (int) (centroid.y-last_height/2.0+(rand()%last_height));
  774.       x = min(x, rgbImage->width-1);
  775.       y = min(y, rgbImage->height-1);
  776.       x = max(x, 0);
  777.       y = max(y, 0);
  778.       ColorBGR* color;
  779.       GetPixel(rgbImage, x, y, &color);
  780.       if (use_prob_distr) {
  781.         prob = m_pProbDistrProvider->LookupProb(*color);
  782.       }
  783.       prob_cnt++;
  784.     } while (prob<MIN_PROB_FOREGROUND && prob_cnt<20);
  785.     features[fcnt].x = (float)x;
  786.     features[fcnt].y = (float)y;
  787.     status[fcnt] = 0;
  788.   } // end for fcnt
  789. }
  790. void OpticalFlow::GetPixel(IplImage* rgbImage, int x, int y, ColorBGR** color)
  791. {
  792.   ASSERT(rgbImage->nChannels==3);
  793.   *color = (ColorBGR*) cvPtr2D(rgbImage, y, x);
  794. }