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

视频捕捉/采集

开发平台:

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: LearnedColor.cpp,v 1.14 2005/01/18 22:02:42 matz Exp $
  22. **/
  23. #include "Common.h"
  24. #include "LearnedColor.h"
  25. #include "Exceptions.h"
  26. //
  27. // Constructor
  28. //
  29. LearnedColor::LearnedColor()
  30.   : m_truth_map(NULL),
  31.     m_truth_pos(0),
  32.     m_truth_neg(0),
  33.     m_rgb_lookup_numbins(0),
  34.     m_rgb_lookup_binsize(0)
  35. {
  36. } // (Constructor)
  37. LearnedColor::~LearnedColor()
  38. {
  39. }
  40. void LearnedColor::Initialize(int width, int height)
  41. {
  42.   m_truth_map = cvCreateImage(cvSize(width, height), IPL_DEPTH_32F, 1);
  43. }
  44. void LearnedColor::DrawOverlay(IplImage* rgbImage, int overlay_level, 
  45.                             const CRect& roi)
  46. {
  47.   if (overlay_level>=3) {
  48.     Backproject(rgbImage, roi);
  49.   }
  50. }
  51. /* draw the backprojection of the learned RGB lookup table
  52.  */
  53. void LearnedColor::Backproject(IplImage* rgbImage, const CRect& roi)
  54. {
  55.   ASSERT(rgbImage && rgbImage->imageData);
  56.   ColorBGR black; 
  57.   black.red = black.green = black.blue = 0;
  58.   int start_x = max(0, roi.left);
  59.   int start_y = max(0, roi.top);
  60.   int stop_x = min(rgbImage->width, roi.right);
  61.   int stop_y = min(rgbImage->height, roi.bottom);
  62.   for (int y=start_y; y<stop_y; y++) {
  63.     ColorBGR* prgb = (ColorBGR*) rgbImage->imageData;
  64.     prgb += y*rgbImage->width + start_x;
  65.     for (int x=start_x; x<stop_x; x++, prgb++) {
  66.       // if not foreground, set pixel to black
  67.       prgb->red = prgb->green = prgb->blue = (BYTE) (255 * LookupProb(*prgb));
  68. //      if (LookupProb(*prgb)<0.5) {
  69. //        *prgb = black;
  70. //      }
  71.     }
  72.   }
  73.   
  74.   int width = min(rgbImage->width-roi.left, 
  75.                   min(roi.right, rgbImage->width-1)-roi.left);
  76.   int height = min(rgbImage->height-roi.top, 
  77.                    min(roi.bottom, rgbImage->height-1)-roi.top);
  78.   CvRect cvBox = cvRect(roi.left, roi.top, width, height);
  79.   cvSetImageROI(rgbImage, cvBox);
  80. //  cvErode(rgbImage, rgbImage, NULL, 2);
  81.   // cvDilate(rgbImage, rgbImage, NULL, 2);
  82.   cvResetImageROI(rgbImage);
  83. }
  84. /* return (in "map") the probabilities based on the learned RGB lookup table
  85. * map must be allocated image with IPL_DEPTH_8U
  86.  */
  87. void LearnedColor::CreateMap(const IplImage* rgbImage, IplImage* mapImage, const CRect& roi) const
  88. {
  89.   ASSERT(rgbImage && rgbImage->imageData);
  90.   ASSERT(mapImage && mapImage->imageData);
  91.   ASSERT(mapImage->width==rgbImage->width && mapImage->height==rgbImage->height);
  92.   int start_x = max(0, roi.left);
  93.   int start_y = max(0, roi.top);
  94.   int stop_x = min(rgbImage->width, roi.right);
  95.   int stop_y = min(rgbImage->height, roi.bottom);
  96.   for (int y=start_y; y<stop_y; y++) {
  97.     ColorBGR* prgb = (ColorBGR*) rgbImage->imageData;
  98.     prgb += y*rgbImage->width + start_x;
  99.     BYTE* pmap = (BYTE*) mapImage->imageData;
  100.     pmap += y*mapImage->width + start_x;
  101.     for (int x=start_x; x<stop_x; x++, prgb++, pmap++) {
  102.       *pmap = (BYTE) (255*LookupProb(*prgb));
  103.     }
  104.   }
  105. }
  106. #ifdef DEBUG
  107. // non-debug version is inlined in LearnedColor.h
  108. int LearnedColor::GetRGBLookupIndex(int x, int y, int z, int size, int arraylen) const
  109. {
  110.   ASSERT(x>=0 && y>=0 && z>=0);
  111.   ASSERT(size);
  112.   int index = z*size*size + y*size + x; 
  113.   ASSERT(0<=index && index<arraylen);
  114.   return index;
  115. }
  116. #endif // DEBUG
  117. /* look up the probability for the given color sample
  118.  * in the learned RGB table
  119.  */
  120. double LearnedColor::LookupProb(ColorBGR sample) const
  121. {
  122.   ASSERT(m_rgb_lookup.size());
  123.   ASSERT(m_rgb_lookup_binsize);
  124.   int bR = sample.red/m_rgb_lookup_binsize;
  125.   int bG = sample.green/m_rgb_lookup_binsize;
  126.   int bB = sample.blue/m_rgb_lookup_binsize;
  127.   ASSERT(bR<m_rgb_lookup_numbins 
  128.          && bG<m_rgb_lookup_numbins && bB<m_rgb_lookup_numbins);
  129.   double prob =
  130.     m_rgb_lookup[GetRGBLookupIndex(bR, bG, bB, m_rgb_lookup_numbins,
  131.                                    (int) m_rgb_lookup.size())];
  132.   return prob;
  133. }
  134. void LearnedColor::TestSegmentation(IplImage* rgbImage, 
  135.                                         const CRect& roi,
  136.                                         double* fpr, double* dr, bool draw)
  137. {
  138.   // let's look at each pixel(sample)
  139.   ColorBGR black; 
  140.   black.red = black.green = black.blue = 0;
  141.   int start_x = max(0, roi.left);
  142.   int start_y = max(0, roi.top);
  143.   int stop_x = min(rgbImage->width, roi.right);
  144.   int stop_y = min(rgbImage->height, roi.bottom);
  145.   int miss_pos=0, hit_pos=0, miss_neg=0, hit_neg=0;
  146.   for (int y=start_y; y<stop_y; y++) {
  147.     ColorBGR* prgb = (ColorBGR*) rgbImage->imageData;
  148.     float* pprob = (float*) m_truth_map->imageData;
  149.     prgb += y*rgbImage->width + start_x;
  150.     pprob += y*rgbImage->width + start_x;
  151.     for (int x=start_x; x<stop_x; x++, prgb++, pprob++) {
  152.       // only consider pixels in the positive and negative areas
  153.       if (*pprob!=1.0 && *pprob!=-1.0) {
  154.         continue;
  155.       }
  156.       
  157.       bool is_fore = (LookupProb(*prgb)>=0.5);
  158.       // write to output buffer
  159.       if (is_fore) {
  160.         if (*pprob==1) {
  161.           hit_pos++;
  162.         } else {
  163.           miss_neg++;
  164.         }
  165.       } else {
  166.         if (*pprob==-1) {
  167.           hit_neg++;
  168.         } else {
  169.           miss_pos++;
  170.         }
  171.         if (draw) *prgb = black;
  172.       }
  173.     } // end x
  174.   } // end y
  175.   *fpr = (double)miss_neg/(double)(miss_neg+hit_neg);
  176.   *dr = (double)hit_pos/(double)(hit_pos+miss_pos);
  177.   if (draw) {
  178.     int height = 200;
  179.     cvRectangle(rgbImage, cvPoint(0, 0), cvPoint(20, height),
  180.                 CV_RGB(255,0,0), 1);
  181.     cvRectangle(rgbImage, cvPoint(25, 0), cvPoint(45, height),
  182.                 CV_RGB(0,255,0), 1);
  183.     cvRectangle(rgbImage, cvPoint(0, 0), cvPoint(20, (int)(height* *fpr)),
  184.                 CV_RGB(255,0,0), CV_FILLED);
  185.     cvRectangle(rgbImage, cvPoint(25, 0), cvPoint(45, (int)(height* *dr)),
  186.                 CV_RGB(0,255,0), CV_FILLED);
  187.   }
  188. }
  189. void LearnedColor::SortSamplesIntoBins(vector<ColorBGR> samples, int* cube,
  190.                                     int num_bins)
  191. {
  192.   ASSERT(num_bins);
  193.   int binsize = (int)(256.0/(double)num_bins);
  194.   ASSERT(binsize);
  195.   int num_samples = (int)samples.size();
  196.   for (int indx=0; indx<num_samples; indx++) {
  197.     int bR = samples[indx].red/binsize;
  198.     int bG = samples[indx].green/binsize;
  199.     int bB = samples[indx].blue/binsize;
  200.     ASSERT(bR<num_bins && bG<num_bins && bB<num_bins);
  201.     int index = bB*num_bins*num_bins + bG*num_bins + bR;
  202.     cube[index]++;
  203.   }
  204. }
  205. #pragma warning (disable:4786)
  206. void LearnedColor::LearnFromGroundTruth(IplImage* rgbImage,
  207.                                      const CuScanMatch& match,
  208.                                      ConstMaskIt mask)
  209. {
  210.   VERBOSE0(5, "HandVu: learning color from ground truth");
  211.   CRect bbox;
  212.   SetGroundTruth(match, mask, bbox);
  213.   LearnLookupCube(rgbImage, bbox);
  214. }
  215. #pragma warning (default:4786)
  216. void LearnedColor::LearnLookupCube(IplImage* rgbImage, const CRect& bbox)
  217. {
  218.   // positive and negative areas should be set through SetTrainingAreas
  219.   ASSERT(m_truth_pos>0 && m_truth_neg>0); 
  220.   // collect pixels; that's faster than running through the image
  221.   // multiple times
  222.   vector<ColorBGR> foreground;
  223.   vector<ColorBGR> background;
  224.   foreground.reserve(m_truth_pos);
  225.   background.reserve(m_truth_neg);
  226. /*
  227.   int start_x = 0;
  228.   int start_y = 0;
  229.   int stop_x = rgbImage->width;
  230.   int stop_y = rgbImage->height;
  231.   */
  232.   int start_x = max(0, bbox.left);
  233.   int start_y = max(0, bbox.top);
  234.   int stop_x = min(rgbImage->width, bbox.right);
  235.   int stop_y = min(rgbImage->height, bbox.bottom);
  236.   for (int y=start_y; y<stop_y; y++) {
  237.     ColorBGR* prgb = (ColorBGR*) rgbImage->imageData;
  238.     float* pprob = (float*) m_truth_map->imageData;
  239.     prgb += y*rgbImage->width + start_x;
  240.     pprob += y*rgbImage->width + start_x;
  241.     for (int x=start_x; x<stop_x; x++, prgb++, pprob++) {
  242.       // only consider pixels in the positive and negative areas
  243.       if (*pprob==1.0) {
  244.         foreground.push_back(*prgb);
  245.       } else if (*pprob==-1.0) {
  246.         background.push_back(*prgb);
  247.       }
  248.     }
  249.   }
  250.   ASSERT(m_truth_pos==(int)foreground.size()); 
  251.   ASSERT(m_truth_neg==(int)background.size()); 
  252.   // init the learning/counting
  253.   m_rgb_lookup_numbins = 2;
  254.   CFloatVector lookup_prev;
  255.   m_rgb_lookup.resize(0);
  256.   int* F_curr = NULL;
  257.   int* B_curr = NULL;
  258.   const int min_num_samples = 5; // stop breaking cells up if less
  259.   // than min_num_samples samples are in cell
  260.   // increase the number of bins per dimension until the number of samples per
  261.   // bin does not exceed a threshold
  262.   double max_r_xyz_curr;
  263.   do {
  264.     m_rgb_lookup_numbins *= 2;
  265.     int cubed_size =
  266.       m_rgb_lookup_numbins*m_rgb_lookup_numbins*m_rgb_lookup_numbins;
  267.     m_rgb_lookup.swap(lookup_prev);
  268.     m_rgb_lookup.resize(cubed_size);
  269.     delete[] F_curr;
  270.     F_curr = new int[cubed_size];
  271.     memset(F_curr, 0, cubed_size*sizeof(int));
  272.     delete[] B_curr;
  273.     B_curr = new int[cubed_size];
  274.     memset(B_curr, 0, cubed_size*sizeof(int));
  275.     SortSamplesIntoBins(foreground, F_curr, m_rgb_lookup_numbins);
  276.     SortSamplesIntoBins(background, B_curr, m_rgb_lookup_numbins);
  277.     max_r_xyz_curr = 0;
  278.     for (int z=0; z<m_rgb_lookup_numbins; z++) {
  279.       for (int y=0; y<m_rgb_lookup_numbins; y++) {
  280.         for (int x=0; x<m_rgb_lookup_numbins; x++) {
  281.           // find the probability of pixels in this bin to be foreground color
  282.           int index = GetRGBLookupIndex(x,y,z,m_rgb_lookup_numbins,
  283.                                         (int) m_rgb_lookup.size());
  284.           int F = F_curr[index];
  285.           int B = B_curr[index];
  286.           int n_xyz_curr = F + B;
  287.           if (n_xyz_curr<min_num_samples && (int)lookup_prev.size()>0) {
  288.             m_rgb_lookup[index] = 
  289.               lookup_prev[GetRGBLookupIndex(x/2,y/2,z/2,m_rgb_lookup_numbins/2,
  290.                                             (int) lookup_prev.size())];
  291.           } else {
  292.             double rc = (double)n_xyz_curr/
  293.               (double)(m_truth_pos+m_truth_neg);
  294.             max_r_xyz_curr = max(max_r_xyz_curr, rc);
  295.             double f = (double)F/(double)m_truth_pos;
  296.             double b = (double)B/(double)m_truth_neg;
  297.             if (f!=0 && b!=0) {
  298.               m_rgb_lookup[index] = (float) (f/(f+b));
  299.             } else if (f==0 && b!=0) {
  300.               m_rgb_lookup[index] = 0.0f;
  301.             } else if (f!=0 && b==0) {
  302.               m_rgb_lookup[index] = 1.0f;
  303.             } else if ((int)lookup_prev.size()>0) {
  304.               m_rgb_lookup[index] =
  305.                 lookup_prev[GetRGBLookupIndex(x/2,y/2,z/2,
  306.                                               m_rgb_lookup_numbins/2,
  307.                                               (int) lookup_prev.size())];
  308.             } else {
  309.               m_rgb_lookup[index] = 0.0f;
  310.             }
  311.           }
  312.           ASSERT(0<=m_rgb_lookup[index]);
  313.           ASSERT(m_rgb_lookup[index]<=1);
  314.         }
  315.       }
  316.     }
  317.   } while (max_r_xyz_curr>=0.05 && m_rgb_lookup_numbins<64);
  318.   m_rgb_lookup_binsize = (int)(256.0/(double)m_rgb_lookup_numbins);
  319.   delete[] F_curr;
  320.   delete[] B_curr;
  321. }
  322. void LearnedColor::GetMostRightUpBlob(IplImage* rgbImage,
  323.   const CRect& roi, CvPoint2D32f& pos)
  324. {
  325.   int start_x = max(0, roi.left);
  326.   int start_y = max(0, roi.top);
  327.   int stop_x = min(rgbImage->width, roi.right);
  328.   int stop_y = min(rgbImage->height, roi.bottom);
  329.   int width = stop_x-start_x;
  330.   int height = stop_y-start_y;
  331.   ASSERT(rgbImage && rgbImage->imageData);
  332.   int cnt_consecutive = 0;
  333.   int diag = 1;
  334.   do {
  335.     ColorBGR* prgb = (ColorBGR*) rgbImage->imageData;
  336.     prgb += start_y*rgbImage->width + stop_x-diag;
  337.     for (int x=stop_x-diag, y=start_y; x<stop_x && y<stop_y; x++, y++) {
  338.       if (LookupProb(*prgb)>=0.5) {
  339.         cnt_consecutive ++;
  340.         if (cnt_consecutive>20) {
  341.           pos.x = (float)x;
  342.           pos.y = (float)y;
  343.           cvCircle(rgbImage, cvPoint(x, y), 12, CV_RGB(0, 255, 0), 1);
  344.           return;
  345.         }
  346.       } else {
  347.         cnt_consecutive = 0;
  348.       }
  349.       prgb += rgbImage->width + 1;
  350.     }
  351.     diag++;
  352.   } while (diag<width+height);
  353.   // no blob found
  354.   pos.x = -1; pos.y = -1;
  355. }
  356. #pragma warning (disable:4786)
  357. void LearnedColor::SetGroundTruth(const CuScanMatch& match,
  358.                                ConstMaskIt mask, CRect& bbox)
  359. {
  360.   cvSet(m_truth_map, cvScalar(0));
  361.   m_truth_pos = 0;
  362.   m_truth_neg = 0;
  363.   // set positive pixels where the mask has a skin-color
  364.   // probability of at least scp_thresh (skin-color-prob)
  365.   const double scp_thresh = 0.7;
  366.   double scale_x = (double)(match.right-match.left-1)/((*mask).second.GetWidth()-1.);
  367.   double scale_y = (double)(match.bottom-match.top-1)/((*mask).second.GetHeight()-1.);
  368.   CvPixelPosition32f position;
  369.   CV_INIT_PIXEL_POS(position, 
  370.                     (float*)(m_truth_map->imageData),
  371.                     m_truth_map->widthStep, 
  372.                     cvSize(m_truth_map->width, m_truth_map->height), 0, 0,
  373.                     m_truth_map->origin);
  374.   ASSERT(m_truth_map->nChannels==1);
  375.   for (int y=match.top; y<match.bottom; y++) {
  376.     for (int x=match.left; x<match.right; x++) {
  377.       int m_x = cvRound((double)(x-match.left)/scale_x);
  378.       int m_y = cvRound((double)(y-match.top)/scale_y);
  379.       double scp = (*mask).second.GetProb(m_x, m_y);
  380.       if (scp>=scp_thresh) {
  381.         CV_MOVE_TO(position, x, y, 1);
  382.         *CV_GET_CURRENT(position, 1) = 1.0f;
  383.         m_truth_pos ++;
  384.       }
  385.     }
  386.   }
  387.   // set negative area at a certain distance from the hand region
  388.   int width = match.right-match.left;
  389.   int height = match.bottom-match.top;
  390.   int leftmost = match.left-width;
  391.   int leftmid = match.left-width/2;
  392.   int topmost = match.top-height;
  393.   int topmid = match.top-height/2;
  394.   int rightmost = match.right+width;
  395.   int rightmid = match.right+width/2;
  396.   int bottom = match.bottom;
  397.   
  398.   CRect neg_mask_left(leftmost, topmid, leftmid, bottom);
  399.   ModifyGroundTruth(neg_mask_left, -1.0);
  400.   CRect neg_mask_top(leftmost, topmost, rightmost, topmid);
  401.   ModifyGroundTruth(neg_mask_top, -1.0);
  402.   CRect neg_mask_right(rightmid, topmid, rightmost, bottom);
  403.   ModifyGroundTruth(neg_mask_right, -1.0);
  404.     
  405.   bbox = CRect(leftmost, topmost, rightmost, bottom);
  406. }
  407. #pragma warning (default:4786)
  408. /* add areas with certain probabilities to the ground truth map
  409.    with reset==true, the probability mask is zeroed out first,
  410.    otherwise it's kept.
  411.    probability==1 means foreground color for sure,
  412.    probability==0 means background color; if
  413.    probability==-1, that area is used to train a specific
  414.    background color model.
  415.    The area consists of four absolute coordinates (not width, height).
  416.    all the computation will be done the next time Transform is called.
  417. */
  418. void LearnedColor::ModifyGroundTruth(const CRect& area, double probability)
  419. {
  420.   ASSERT(m_truth_map!=NULL);
  421.   int x_to = min(area.right, m_truth_map->width);
  422.   int x_from = max(0, area.left);
  423.   int y_to = min(area.bottom, m_truth_map->height);
  424.   CvPixelPosition32f position;
  425.   CV_INIT_PIXEL_POS(position, 
  426.                     (float*)(m_truth_map->imageData),
  427.                     m_truth_map->widthStep, 
  428.                     cvSize(m_truth_map->width, m_truth_map->height), 0, 0,
  429.                     m_truth_map->origin);
  430.   ASSERT(m_truth_map->nChannels==1);
  431.   for (int y=max(0, area.top); y<y_to; y++) {
  432.     CV_MOVE_TO(position, x_from, y, 1);
  433.     for (int x=x_from; x<x_to; x++) {
  434.       *CV_GET_CURRENT(position, 1) = (float) probability;
  435.       CV_MOVE_RIGHT(position, 1);
  436.       if (probability==1) {
  437.         m_truth_pos ++;
  438.       } else if (probability==-1) {
  439.         m_truth_neg ++;
  440.       }
  441.     }
  442.   }
  443. }