The Machine Perception Toolbox

[Introduction]- [News]- [Download]- [Screenshots]- [Manual (pdf)]- [Forums]- [API Reference]- [Repository ]

 

Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

src/mpisearch.h

Go to the documentation of this file.
00001 /*  
00002  *  mpisearch.h
00003  *
00004  *  Created by Ian Fasel on Feb 02, 2003. (mpisearch)
00005  *    Based on code written by Ryan Dahl, Apr 2, 2002, (viola++)
00006  *      which was based on matlab code by Ian Fasel August 2001, (violaSearch.m) 
00007  *      which was based on matlab code written by John Hershey, May 2001, (viola.m)
00008  *      which was based on a talk and paper by Paul Viola and Michael Jones, 2001.
00009  *  Fixes: 
00010  * 
00011  *  Copyright (c) 2003 Machine Perception Laboratory 
00012  *  University of California San Diego.
00013  * 
00014  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
00015  * 
00016  *    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
00017  *    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
00018  *    3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
00019  * 
00020  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00021  *  
00022  */
00023 #ifndef _MPISEARCH_H_
00024 #define _MPISEARCH_H_
00025 
00026 #include "mpiimage.h"
00027 #include "featuredata.h"
00028 #include "faceboxlist.h"
00029 #include <string>
00030 #include <list>
00031 #include <string>
00032 #include <iostream>
00033 #include <fstream>
00034 
00035 extern "C" {
00036 #include <math.h>
00037 #include <string.h>
00038 #include <stdlib.h>
00039 }
00040 
00041 
00042 #define sqr(x) ((x)*(x))
00043 #define cube(x) ((x)*(x)*(x))
00044 #ifndef PI
00045 #define PI 3.141592654f
00046 #endif
00047 
00048 using namespace std;
00049 
00050 
00051 #define TRUE 1
00052 #define FALSE 0
00053 // set maximum number of patches to return
00054 #define MAX_NUM_PATCHES 5000
00055 
00056 const float SCALE_FACTOR = 1.2f;
00057 
00058 template < class T > 
00059 class CornerCache{
00060 public:
00061   int scaledCornerX;
00062   int scaledCornerY;
00063   int scaledIndex;
00064   T value;
00065 };
00066 
00067 /***  Provides an architecture for performing operations on every
00068 *  sub-window of an image at multiple scales.
00069 */
00070 template < class T > class MPISearchObjectDetector; // forward declaration
00071 
00072 // MPISearchStream: holds all the memory and caches for the ObjectDetector  
00073 template < class T > 
00074 class MPISearchStream {
00075   // friend class MPISearchObjectDetector;  // C++ classes don't inherit friends -- so how can I do data hiding?
00076     // Probably the only real way is to make Stream a class that only has scope within ObjectDetector
00077 public:
00078   // Member Functions
00079   MPISearchStream();
00080   ~MPISearchStream();
00081   void init(const int width, const int height, FeatureData &data, double WINSHIFT=1.25);
00082   void reset(const int width, const int height, FeatureData &data, double WINSHIFT=1.25);
00083   void init(const MPISearchStream &s, FeatureData &data, double WINSHIFT=1.25);
00084   void reset(const MPISearchStream &s, FeatureData &data, double WINSHIFT=1.25);
00085   void release();
00086   ROI getROI() const;
00087   void SetROI(ROI &theroi);
00088   void SetROI(const int &minx,const int &maxx,const int &miny,const int &maxy,const int &minsc,const int &macsc);
00089 //private:
00090   void makeNormWindow_MPI_CacheCorners(FeatureData &thedata, double WINSHIFT_);
00091   CornerCache< T >** cacheCorners (float &scale_factor, FeatureData &thedata,  T * &fns, int &image_width,
00092                               Corner norm_window[4], CornerCache< T > nwc[4],  T  &nw_fn);
00093   void deleteCacheCorners ( CornerCache< T >** &pcc,  T * &fns );
00094   void makeNormWindow( Corner norm_window[4], FeatureData &thedata);
00095   
00096   // Member data
00097   FeatureData *m_data;
00098   RImage< T > *pixels2;
00099   vector< RImage< T > *> images;
00100   Corner norm_window[4];
00101   int height, width, block_height, block_width;
00102   vector< typename MPIScaledImage< T >::const_iterator > block_windows;
00103   int FailedCycles;
00104   MPIImagePyramid< T > *mpi;
00105   vector< CornerCache< T > *> nw_c;     // normalization window corner cache (for scales);
00106   vector< T > nw_fn;
00107   int numfeatures;
00108   vector< CornerCache< T > **> corners; // feature corner cache (for scales);
00109   vector< T* >  fns;
00110   bool do_integral;
00111 //  my_memory keeps track of use of the image. The destructor will only
00112  //erase the image if my_memory == 0
00113   bool my_memory; 
00114   bool allocated;
00115 };
00116 
00117 // virtual class, requires that a base class be derived that handles loading FeatureData
00118 template <class T>
00119 class MPISearchObjectDetector {
00120  public:
00121   MPISearchObjectDetector ( );
00122   virtual ~MPISearchObjectDetector ( );
00123 
00124   // pixels --                  the image to be scanned
00125   // faces  --                  structure to store the detected objects (faces)
00126   // flags  --                  an array with a value corresponding to each window at all scales
00127   // output_values --           Stick the output of the classifier up to this point here
00128   // box --                     flag to return the found face boxes.  Set to 0 to get activation only.
00129   // Note: flags and output_values are being used during learning, only used from Matlab right now
00130   int search (const RImage< T > &pixels, FaceBoxList &faces, int USE_BLOCK_FLAGS = 1,
00131             float WINSHIFT = 1.25, double* index_flags = NULL,
00132             double* output_values = NULL, int box = 1);
00133   void initStream(const int width, const int height, double WINSHIFT=1.25);
00134   void resetStream(const int width, const int height, double WINSHIFT=1.25);
00135   void releaseStream();
00136 
00137   bool DataLoaded();
00138   void setDebug(const bool val);
00139   void setDebug2(const bool val);
00140 
00141   void AdjSearchWindow(Square &Front); //note only works if FailedCycles are updated
00142   void AdjSearchWindow(TSquare<float> &Front); //note only works if FailedCycles are updated
00143         void setPixelMax(T maxpixval);
00144   int FailedCycles();
00145   bool allocated();
00146  protected:
00147 
00148   double classifyWindow(typename MPIScaledImage< T >::const_iterator &window,  FeatureData &thedata, 
00149            CornerCache< T >** &corners,  T * &fns, Corner norm_window[4], CornerCache< T > nw_c[4],
00150         T  nw_fn, float scale_factor,  T  sf2, double* &activation);
00151   //void AdjSearchWindow(Square &Front, int Width, int Height);//note only works if FailedCycles are updated
00152   void printData (FeatureData &thedata, int ind);
00153   virtual void processFace(typename MPIScaledImage< T >::const_iterator &window,
00154                            const  T  &one_over__sf2_times_std,
00155                            const  T  & mean_over_std, const int &numFaces);
00156 
00157   void integrateImages(const RImage<  T >& p, MPISearchStream< T > &stream);
00158   //void integrateImages(const RImage<  T > &p, vector< RImage<  T >* > &i, const int D);
00159   bool debug, debug2;
00160   MPISearchStream< T > stream;
00161   FeatureData data;
00162         T minvariance;
00163 };
00164 
00165 
00166 template <class T>
00167 MPISearchObjectDetector<T>::MPISearchObjectDetector():debug(false),debug2(false),
00168         minvariance((T)0.000729) {}
00169 
00170 // These are the virtual functions that derived classes should modify
00171 template < class T >
00172 MPISearchObjectDetector<T>::~MPISearchObjectDetector () {  }
00173 
00174 template < class T >
00175 void MPISearchObjectDetector<T>::processFace(typename MPIScaledImage< T >::const_iterator &window,
00176                                           const  T  &a, const  T  &b, const int &numFaces){}
00177 
00178 template < class T >
00179 void MPISearchObjectDetector<T>::initStream(const int width, const int height, double WINSHIFT){
00180   stream.init(width, height, data, WINSHIFT);
00181 }
00182 template < class T >
00183 void MPISearchObjectDetector<T>::resetStream(const int width, const int height, double WINSHIFT){
00184   stream.reset(width, height, data, WINSHIFT);
00185 }
00186 template < class T >
00187 void MPISearchObjectDetector<T>::releaseStream(){
00188   stream.release();
00189 }
00190 
00191 template < class T >
00192 void MPISearchObjectDetector<T>::setPixelMax(T maxpixval){
00193         minvariance = sqr(maxpixval * static_cast<T>(0.027));
00194 }
00195 // This search function is somewhat specific to faces, but most of the internals are portable
00196 template < class T >
00197 int MPISearchObjectDetector<T>::search (const RImage< T > &pixels, FaceBoxList &faces, int USE_BLOCK_FLAGS, float WINSHIFT, double * index_flags, double * output_values, int box)
00198 {
00199   if(!stream.allocated){
00200                 std::cout << "MPISearchObjectDetector<T>::search: stream not allocated. Allocating data" << endl;
00201     stream.init(pixels.width, pixels.height, data,  WINSHIFT);
00202   } else
00203     stream.mpi->m_stride = WINSHIFT;
00204   //cout << "mpisearch::search: " << stream.mpi->m_roi << endl;
00205   //cout << "MPISearchObjectDetector<T>::search: "<< stream.roi << endl;
00206   //cout << "About to enter integrateImages" << endl;
00207   integrateImages(pixels, stream);
00208   //if(debug){
00209     //cout << "num_cascades: " << data.numcascades << endl; 
00210     //printData(data, 1);
00211     //  cout << "thedata.real_fun: " << thedata.real_fun << endl;
00212   //}
00213 
00214   int numWindows = 0, scale_index;
00215   float scale_factor;
00216   bool check_it;
00217   double default_cascade = 1;
00218   double default_activation = 0;
00219   double *cascade_level = &default_cascade;
00220   double *activation = &default_activation;
00221   MPIImagePyramid<float>::const_iterator scale = stream.mpi->begin();
00222   MPIImagePyramid<float>::const_iterator last_scale = stream.mpi->end();
00223   //if(debug)
00224   //  stream.images[2]->print(20);
00225   for( ; scale != last_scale; ++scale){
00226     // get pointers to cached values for this scale
00227     scale_index = scale.getScale(scale_factor);
00228     //cout << "Scale number: " << scale_index << ", scale_factor = " << scale_factor << endl;
00229      T  sf2 = scale_factor * scale_factor;
00230     CornerCache< T > **corners = stream.corners[scale_index];
00231      T  *fns = stream.fns[scale_index];
00232     CornerCache< T > *nw_c = stream.nw_c[scale_index];
00233      T  nw_fn = stream.nw_fn[scale_index];
00234     typename MPIScaledImage< T >::const_iterator window = (*scale).begin(), last_window = (*scale).end();
00235     for( ; window != last_window; ++window, ++numWindows){
00236       //if(debug)
00237       //if( numWindows == (66052 - 1) ){
00238       //  cout << "Setting debug"<< endl;
00239       //  setDebug(true);
00240       //}
00241       //else
00242       //  setDebug(false);
00243       // A little logic here to skip a window for various reasons
00244       check_it = true;
00245       if(index_flags){
00246         //if(debug2)
00247         //  cout << "index_flags IS NOT NULL!!! " << endl;
00248         cascade_level = &(index_flags[numWindows]);
00249         if(*cascade_level != 1)
00250           check_it = false;
00251       }
00252       //if(debug2)
00253       //cout << "USE_BLOCK_FLAGS="<< USE_BLOCK_FLAGS << endl;
00254       if(USE_BLOCK_FLAGS){
00255         //if(debug2)
00256         //cout << "the BLOCK_FLAG ="<< window.getPixel(2,0) << endl;
00257         if( window.getPixel(2,0) )
00258           check_it = false;
00259       }
00260       int x, y;
00261       window.getCoords(x,y);
00262       //cout << "(" << x << ","<< y<< ") ";
00263       //if(debug2)
00264       //cout << "check_it = " << check_it << endl;
00265       if(check_it){
00266         if(output_values)
00267           activation = output_values + numWindows;
00268         *cascade_level = classifyWindow(window, data, corners, fns, stream.norm_window, nw_c, nw_fn, scale_factor, sf2, activation);
00269         //if(debug)
00270         //  cout << "activation: " << *activation << endl; 
00271         if(*cascade_level > 0) {
00272           //if(debug){
00273           //cout<< "Found a face" << endl;
00274           // cout << "numWindows = " << numWindows << endl;
00275           //}
00276           if(box){
00277             faces.push_front(window.getSquare());
00278             //cout << "Face at: (" << faces.front().x << ", " << faces.front().y << ", " << faces.front().scale << ")" << endl;
00279             if(USE_BLOCK_FLAGS){
00280               for(int by = 0; by < stream.block_height; ++by)
00281                 for(int bx = -stream.block_width; bx < stream.block_width; ++bx)
00282                   window.setShiftPixel(2, bx, by, 1);
00283               stream.block_windows.push_back(window);
00284             }
00285           }
00286         }
00287       }
00288     } // end window loop
00289     //cout << endl;
00290     if(USE_BLOCK_FLAGS){
00291       while(stream.block_windows.size() > 0){
00292         typename MPIScaledImage< T >::const_iterator window = stream.block_windows[0];
00293         for(int by = 0; by < stream.block_height; ++by)
00294           for(int bx = -stream.block_width; bx < stream.block_width; ++bx)
00295             window.setShiftPixel(2, bx, by, 0);
00296         stream.block_windows.pop_back();
00297       }
00298     }
00299 
00300   }  // end scale loop
00301 
00302   /**************added*****************/
00303   if (!faces.empty())
00304     stream.FailedCycles = 0;
00305   else{
00306     //cout << "Failed" << endl;
00307     stream.FailedCycles++;
00308   }
00309   /**************end*****************/
00310   return numWindows;
00311 }
00312 
00313 template < class T >
00314 double MPISearchObjectDetector<T>::classifyWindow(typename MPIScaledImage< T >::const_iterator &window, FeatureData &thedata,
00315                                                   CornerCache< T >** &corners,  T* &fns, Corner norm_window[4], CornerCache< T > nw_c[4],
00316                                                   T nw_fn, float scale_factor, T sf2, double* &activation){
00317 
00318   T mean = 0.0, mean2 = 0.0, standard_deviation, one_over__sf2_times_std, mean_over_std, f, std2;
00319   
00320   //cout << "Classifying window" << endl;
00321   // calculate the statistics for this sub window
00322   for(int corner = 0; corner < 4; corner++) {
00323     mean  += window.getPixel0( nw_c[corner].scaledIndex) * norm_window[corner].value;
00324     mean2 += window.getPixel1( nw_c[corner].scaledIndex) * norm_window[corner].value;
00325   }
00326   mean /= nw_fn;
00327   mean2 /= nw_fn;
00328   std2 = mean2 - mean*mean;
00329 
00330   // if the std deviation is below some point then ignore
00331   //if(standard_deviation < 0.027)
00332   if(std2 < minvariance)
00333     return -99999; // special flag for std bailout
00334   standard_deviation = sqrt(std2)*thedata.stdAdjusts[static_cast<int>(scale_factor+0.5f)-1];
00335   // cout << "mean2 - mean*mean: " << (mean2 - mean*mean);
00336   //cout << ", mean: " << mean << ", mean2: " << mean2 << ", std: " << standard_deviation << endl;
00337 
00338   // cache a few values used in normalization
00339   // one_over__sf2_times_std = 1/(sf2*standard_deviation);
00340   one_over__sf2_times_std = 1.0/(sf2*standard_deviation);
00341   mean_over_std = mean/standard_deviation;
00342   T aH = 0.0f, a,b,c,d;
00343   
00345   // run through each stage in the cascade
00346   for (int cascade_iter = 0; cascade_iter < thedata.numcascades; cascade_iter++){
00347     Cascade &cascade = thedata.cascades[cascade_iter];
00348     if(!thedata.preserve_aH)
00349       aH = 0.0;
00350 
00351     if(!thedata.real_fun){
00352       // run through the features of each cascade stage
00353       for (int feature_iter = cascade.start; feature_iter <= cascade.end; feature_iter++){
00354         Feature &feature = thedata.features[feature_iter];
00355         f = 0.0f;
00356         // for each corner in the feature
00357         CornerCache< T > *feature_corners = corners[feature_iter];
00358         //for(int corner = 0; corner < feature.numcorners; corner++) {
00359         //  f+= window.getPixel0( feature_corners[corner].scaledIndex ) * feature.corners[corner].value;
00360         //}
00361         for(int corner = 0; corner < feature.numcorners; corner+=4) {
00362           a = window.getPixel0( feature_corners[corner].scaledIndex ) * feature_corners[corner].value;
00363           b = window.getPixel0( feature_corners[corner+1].scaledIndex ) * feature_corners[corner+1].value;
00364           c = window.getPixel0( feature_corners[corner+2].scaledIndex ) * feature_corners[corner+2].value;
00365           d = window.getPixel0( feature_corners[corner+3].scaledIndex ) * feature_corners[corner+3].value;
00366           f+= a + b + c + d;
00367         }
00368         // Note: if T is float, there is floating point roundoff error here, but that should be ok.
00369         
00370         // normalize..
00371         //f = (f/sf2 - fns[feature_iter] * mean) / standard_deviation;
00372         f = f*one_over__sf2_times_std - fns[feature_iter]*mean_over_std;
00373         
00375         // Threshold classifier, using AdaBoost
00376         // 1) maybe take absolute value if indicated
00377         // 2) Threshold (weight is in {-1, +1}, so its above *or below* thresh)
00378         if(f < 0) if(feature.abs) f = -f;
00379         if(thedata.plus_minus_one)
00380           if(feature.weight * f > feature.bias) aH += feature.alpha; else aH -= feature.alpha;
00381         else
00382           if(feature.weight * f > -feature.bias) aH += feature.alpha;
00384         //if(debug2){
00385         // cout << "f = " << f << endl;
00386         // cout << "aH = " << aH << endl;
00387         //}
00388       }
00389     } else {
00390       // run through the features of each cascade stage
00391       for (int feature_iter = cascade.start; feature_iter <= cascade.end; feature_iter++){
00392         Feature &feature = thedata.features[feature_iter];
00393         f = 0.0f;
00394         // for each corner in the feature
00395         CornerCache< T > *feature_corners = corners[feature_iter];
00396         //for(int corner = 0; corner < feature.numcorners; corner++) {
00397         //  f+= window.getPixel0( feature_corners[corner].scaledIndex ) * feature.corners[corner].value;
00398         //}
00399         for(int corner = 0; corner < feature.numcorners; corner+=4) {
00400           a = window.getPixel0( feature_corners[corner].scaledIndex ) * feature_corners[corner].value;
00401           b = window.getPixel0( feature_corners[corner+1].scaledIndex ) * feature_corners[corner+1].value;
00402           c = window.getPixel0( feature_corners[corner+2].scaledIndex ) * feature_corners[corner+2].value;
00403           d = window.getPixel0( feature_corners[corner+3].scaledIndex ) * feature_corners[corner+3].value;
00404           f+= a + b + c + d;
00405         }
00406         // Note: I think there is numerical error here because of floating point roundoff error.  But that realy should be ok.
00407         
00408         // normalize..
00409         //f = (f/sf2 - fns[feature_iter] * mean) / standard_deviation;
00410         //if(debug){
00411         //  cout << "f (before normalization): " << f << endl;
00412         //}
00413         f = f*one_over__sf2_times_std - fns[feature_iter]*mean_over_std;
00414         //if(debug){
00415         //  cout << "f (after normalization): " << f << endl;
00416         //}
00417         
00419         // Using some real-valued WeakLearner, perhaps learned via GentleBoost
00420         // 1) scale feature output to 1:nl
00421         // 2) round and use as index into tuning curve
00422         f = (f - feature.bias) * feature.nl_over_range;
00423         //if(debug){
00424         //  cout << "feature.nl_over_range: " << feature.nl_over_range << endl;
00425         //  cout << "f: " << f << endl;
00426         //  cout << "feature.tuning_curve: ";
00427         //  for(int tc =0; tc <= thedata.nl; ++tc)
00428         //    cout << feature.tuning_curve[tc] << " " ;
00429         //  cout << endl;
00430         //}
00431         if(f<1)
00432           aH += feature.tuning_curve[1];
00433         else if(f>thedata.nl)
00434           aH += feature.tuning_curve[thedata.nl];
00435         else
00436           aH += feature.tuning_curve[static_cast<int>(f+0.5f)];
00437         //if(debug){
00438         //  cout << "f: " << f << ", aH: " << aH << endl;
00439         //}
00440       }
00441     }
00442     
00444     if(aH < cascade.thresh){
00445       //cout << "exit the cascade, there is no face after cascade " << cascade_iter << endl;
00446       *activation = static_cast<double>(aH);
00447       //if(debug){
00448       // cout << "aH = " << aH << endl;
00449       // cout << "*activation = " << *activation << endl;
00450       //}
00451       return static_cast<double>(-cascade_iter);
00452     }
00453   }
00454   *activation = static_cast<double>(aH);
00455   //if(debug){
00456   //  cout << "aH = " << aH << endl;
00457   //  cout << "*activation = " << *activation << endl;
00458   //}
00459   return 1.0;
00460 }
00461 
00462 template < class T >
00463 void MPISearchObjectDetector<T>::integrateImages(const RImage<  T  >& pixels, MPISearchStream<T> &thestream){
00464    T  temp;
00465    T  *p, *q;
00466   ROI roi = thestream.mpi->getROI();
00467   // The ROI in the MPIImage Pyramid is too large by 1 because it is looking at integral images.
00468   roi.m_max_x -= 1;  roi.m_max_y -= 1; // sorry about this, I know its confusing
00469   for(int y = roi.m_min_y; y < roi.m_max_y; y++) {
00470     p = pixels.array + (pixels.width*y + roi.m_min_x);
00471     q = thestream.pixels2->array + (thestream.pixels2->width*y + roi.m_min_x);
00472     for(int x = roi.m_min_x; x < roi.m_max_x; x++) {
00473       //temp = pixels.getPixel(x,y);
00474       temp = *p++;
00475       //thestream.pixels2->setPixel(x, y, temp * temp);
00476       *q++ = temp*temp; 
00477     }
00478   }
00479   // create integral images
00480   static_cast<RIntegral< T  >* >(thestream.images[0])->integrate(pixels, roi);
00481   static_cast<RIntegral< T  >* >(thestream.images[1])->integrate(*(thestream.pixels2), roi);
00482 }
00483 
00484 template < class T >
00485 void MPISearchObjectDetector<T>::printData (FeatureData &thedata, int ind) {
00486         std::cerr << "patchsize = " << thedata.patchsize << endl;
00487   std::cerr << "patch_width = " << thedata.patch_width << endl;
00488   std::cerr << "patch_height = " << thedata.patch_height << endl;
00489   std::cerr << "numfeatures = " << thedata.numfeatures << endl;
00490   std::cerr << "numcascades = " << thedata.numcascades << endl;
00491   std::cerr << "normOffset.top = " << thedata.normOffset.top << endl;
00492   std::cerr << "normOffset.left = " << thedata.normOffset.left << endl;
00493   std::cerr << "normOffset.right = " << thedata.normOffset.right << endl;
00494   std::cerr << "normOffset.bottom = " << thedata.normOffset.bottom << endl;
00495   std::cerr << "numStdAdjusts = " << thedata.numStdAdjusts << endl;
00496 
00497   std::cerr << "feature[" << ind << "]:" << endl;
00498   std::cerr << "numcorners = " << thedata.features[ind].numcorners << endl;
00499   std::cerr << "real_fun" << thedata.real_fun << endl;
00500   if(!thedata.real_fun){
00501     std::cerr << "alpha = " << thedata.features[ind].alpha << endl;
00502     std::cerr << "bias = " << thedata.features[ind].bias << endl;
00503     std::cerr << "weight = " << thedata.features[ind].weight << endl;
00504     std::cerr << "abs = " << thedata.features[ind].abs << endl;
00505   } else {
00506     std::cerr << "bias = " << thedata.features[ind].bias << endl;
00507     std::cout << "Weights:";
00508     for(int j = 0; j <= thedata.nl; ++j)
00509       std::cout << " " << thedata.features[ind].tuning_curve[j];
00510     std::cout << endl;
00511   }
00512   //cerr << "range = " << thedata.features[ind].range << endl;
00513   for (int i = 0; i<thedata.features[ind].numcorners; ++i) {
00514     std::cerr << "corners_x[" << i << "] = " << thedata.features[ind].corners[i].x << endl;
00515     std::cerr << "corners_y[" << i << "] = " << thedata.features[ind].corners[i].y << endl;
00516     std::cerr << "corners_value[" << i << "] = " << thedata.features[ind].corners[i].value << endl;
00517   }
00518 
00519   if(thedata.numStdAdjusts){
00520     std::cerr << "stdAdjusts:" << endl;
00521     for (int j = 0; j < thedata.numStdAdjusts; ++j)
00522       std::cerr << thedata.stdAdjusts[j] << " ";
00523     std::cerr << endl;
00524   }
00525 
00526   std::cerr << "Cascades:" << endl;
00527   for (int k = 0; k < thedata.numcascades; ++k) 
00528     std::cerr << thedata.cascades[k].start << "\t" << thedata.cascades[k].end << "\t" << thedata.cascades[k].thresh << endl; 
00529 }
00530 
00531 template < class T >
00532 void MPISearchObjectDetector<T>::setDebug(const bool val){ debug = val; }
00533 
00534 template < class T >
00535 void MPISearchObjectDetector<T>::setDebug2(const bool val){ debug2 = val; }
00536 
00537 template < class T >
00538 int MPISearchObjectDetector<T>::FailedCycles(){ return stream.FailedCycles; };
00539 
00540 template < class T >
00541 bool MPISearchObjectDetector<T>::DataLoaded(){ return ( data.numfeatures > 0 );}
00542 
00543 template < class T >
00544 bool MPISearchObjectDetector<T>::allocated(){ return stream.allocated;}
00545 
00546 /*adjusts window for faster search.  Note if FailedCycles are not used it will not readjust to 
00547 span a larger window to search for a face*/
00548 template < class T >
00549 void MPISearchObjectDetector<T>::AdjSearchWindow(TSquare< float > &F) // Width and Height are not needed as arguments
00550 {
00551   Square S(static_cast<int>(F.size), static_cast<int>(F.x), static_cast<int>(F.y), static_cast<int>(F.scale));
00552   AdjSearchWindow(S);
00553 }
00554 template < class T >
00555 void MPISearchObjectDetector<T>::AdjSearchWindow(Square &Front)
00556 {
00557   //std::cout << "MPISearchObjectDetector<T>::AdjSearchWindow" << stream.mpi->getROI() << endl;
00558         int midSize;
00559         int minX, maxX, minY, maxY, minscale, maxscale;
00560          static Square oldFront;
00561          
00562          //std::cout << Front << endl;
00563 
00564      // IMPORTANT NOTE:
00565      // stream.SetROI keeps itself consistent: i.e., it keeps the ROI within the bounds of the image pyramid.
00566      // Therefore, it is not necessary to do bounds checking yourself.
00567 
00568      if (!Front.size) //set up roi for next image, makes it smaller to speed up processing
00569          Front = oldFront;
00570      else
00571          oldFront = Front;
00572      
00573 
00574      if (stream.FailedCycles > 5)
00575      {
00576        ROI theroi;
00577        stream.SetROI(theroi);
00578      }
00579      else
00580      {
00581                 //each failed cycle roi increases by size of facebox in X and Y direction
00582                 //until it reaches size of image
00583                 midSize = static_cast<int>(ceil((float)Front.size/2.0f));
00584                 minX = Front.x - (midSize * (stream.FailedCycles+1));
00585                 maxX = Front.x + Front.size + (midSize * (stream.FailedCycles+1));
00586                 minY = Front.y - (midSize * (stream.FailedCycles+1));
00587                 maxY = Front.y + Front.size + (midSize * (stream.FailedCycles+1));
00588                 minscale = Front.scale-stream.FailedCycles;
00589                 maxscale = Front.scale+stream.FailedCycles+1;
00590                 stream.SetROI(minX,maxX,minY,maxY,minscale,maxscale);
00591      }
00592 }
00593 
00594 
00595 template< class T >
00596 MPISearchStream< T >::MPISearchStream() : FailedCycles(0), my_memory(false), allocated(false) {}
00597 template< class T >
00598 MPISearchStream< T >::~MPISearchStream(){ if(allocated) release(); }
00599 template< class T >
00600 void MPISearchStream< T >::init(const int width_, const int height_, FeatureData &thedata, double WINSHIFT_){
00601   if(allocated){
00602     //cout << "MPISearchStream< T >::init:  Tried to init stream, but it thinks its already allocated" << endl;
00603     release();
00604   }
00605   //cout << "MPISearchStream< T >::init: Allocating data for width_ = " << width_ << ", height_ = " << height_ << endl;
00606   m_data = &thedata;
00607   width = width_; height = height_;
00608   do_integral = true;
00609   allocated = true;
00610   pixels2 = new RImage< T >(width, height);
00611   // create integral images
00612   my_memory = true;
00613   RIntegral< T > *ii = new RIntegral< T >(width, height);
00614   RIntegral< T > *ii2 = new RIntegral< T >(width, height);
00615   RImage< T > * BlockFlag = new RImage< T >(width+1, height+1, 0);
00616   images.push_back(ii);
00617   images.push_back(ii2);
00618   images.push_back(BlockFlag);
00619   block_height = static_cast<int>(m_data->patch_height/3);
00620   block_width = static_cast<int>(m_data->patch_width/3);
00621   makeNormWindow_MPI_CacheCorners(thedata, WINSHIFT_);
00622 }
00623 template< class T >
00624 void MPISearchStream< T >::init(const MPISearchStream &s, FeatureData &thedata, double WINSHIFT_){
00625   my_memory = false;
00626   m_data = &thedata;
00627   width = s.width; height = s.height;
00628   do_integral = false;
00629   pixels2 = s.pixels2;
00630   images = s.images;
00631   block_height = static_cast<int>(m_data->patch_height/3);
00632   block_width = static_cast<int>(m_data->patch_width/3);
00633   makeNormWindow_MPI_CacheCorners(thedata, WINSHIFT_);
00634   allocated = true;  
00635 }
00636 template< class T >
00637 void MPISearchStream< T >::makeNormWindow_MPI_CacheCorners(FeatureData &thedata, double WINSHIFT_){
00638   // Create the normalization window feature
00639   makeNormWindow( norm_window, thedata);
00640 
00641   // create image pyramid object
00642   mpi = new MPIImagePyramid< T >(images, 1.2f, m_data->patch_width-1, m_data->patch_height-1, WINSHIFT_);
00643   typename MPIImagePyramid< T >::const_iterator scale = mpi->begin(), last_scale = mpi->end();
00644   int scale_index; 
00645   float scale_factor;
00646   numfeatures = thedata.numfeatures;
00647   // Cache all the corner calculations for each scale
00648   for( ; scale != last_scale; ++scale){
00649     scale_index = scale.getScale(scale_factor);
00650      T  *fns_;
00651      T  nw_fn_;
00652     CornerCache< T > *nw_c_ = new CornerCache< T >[4];
00653     CornerCache< T > **corners_ = cacheCorners(scale_factor, thedata, fns_, images[0]->width, norm_window, nw_c_, nw_fn_);
00654     nw_fn.push_back(nw_fn_);
00655     nw_c.push_back(nw_c_);
00656     fns.push_back(fns_);
00657     corners.push_back(corners_);
00658   }
00659 }
00660 
00661 template< class T >
00662 void MPISearchStream< T >::release(){
00663   unsigned int i;
00664   allocated = false;
00665   if(my_memory){
00666     delete pixels2;
00667     for(i = 0; i < images.size(); ++i){
00668       delete images[i];
00669     }
00670     images.clear();
00671   }
00672   for(i = 0 ; i < corners.size(); ++i){
00673     //cout << "MPISearchStream< T >::release(): calling deleteCacheCorners()" << endl;
00674     deleteCacheCorners(corners[i], fns[i]);
00675     delete [] nw_c[i];
00676   }
00677   nw_c.clear(); // normalization window corner cache (for scales);
00678   nw_fn.clear();
00679   corners.clear(); // feature corner cache (for scales);
00680   fns.clear();
00681   delete mpi;
00682   return;
00683 }
00684 template< class T >
00685 void MPISearchStream< T >::reset(const int width_, const int height_, FeatureData &thedata, double WINSHIFT_){
00686   if(allocated)
00687     release();
00688   init(width_, height_, thedata, WINSHIFT_);
00689 }
00690 
00691 template< class T >
00692 void MPISearchStream< T >::reset(const MPISearchStream &s, FeatureData &thedata, double WINSHIFT_){
00693   if(allocated)
00694     release();
00695   init(s, thedata, WINSHIFT_);
00696 }
00697 template< class T >
00698 ROI MPISearchStream< T >::getROI() const { return mpi->getROI(); }
00699 template< class T >
00700 void MPISearchStream< T >::SetROI(ROI &theroi){
00701   mpi->SetROI(theroi);
00702 }
00703 template< class T >
00704 void MPISearchStream< T >::SetROI(const int &minx,const int &maxx,const int &miny,const int &maxy,const int &minsc,const int &maxsc){
00705   ROI theroi(minx, maxx, miny, maxy, minsc, maxsc);
00706   mpi->SetROI(theroi);
00707   //cout << "MPISearchStream< T >::SetROI " << theroi << endl;
00708 }
00709 
00710 template< class T >
00711 CornerCache< T >** MPISearchStream< T >::cacheCorners (float &scale_factor, FeatureData &thedata,  T * &fns,
00712                                                      int &image_width, Corner norm_window[4], CornerCache< T > nwc[4],  T  &nw_fn)
00713 {
00714   //if(debug){
00715   //cout << "scale_factor = " << scale_factor << endl;
00716   //for(int j = 0; j < thedata.features[0].numcorners; ++j) {
00717   //  cout << "features[0].corners x y = (" << scale_factor * thedata.features[0].corners[j].x;
00718   //  cout << ", " << scale_factor * thedata.features[0].corners[j].y << ")" << endl;
00719   //}
00720   //}
00721   bool make16bytes = true;
00722 
00723   //CornerCache< T > **pcc = (CornerCache< T >**)malloc(thedata.numfeatures*sizeof(CornerCache< T >*));
00724   CornerCache< T > **pcc = new CornerCache< T >* [thedata.numfeatures];
00725   //fns = ( T  *)malloc(thedata.numfeatures*sizeof( T ));
00726   fns = new  T  [thedata.numfeatures];
00727   int i, newnc = 0;
00728   for(i = 0; i < thedata.numfeatures; ++i) {
00729     CornerCache< T > *cc;
00730     if(make16bytes){
00731         newnc = thedata.features[i].numcorners + (((4 - (thedata.features[i].numcorners % 4)) % 4));
00732         cc = new CornerCache< T > [newnc];
00733     } else {
00734         cc = new CornerCache< T > [thedata.features[i].numcorners];
00735     }
00736 
00737     int fn = 0;
00738     float scaledCornerFloatX;
00739     float scaledCornerFloatY;
00740     float correction;
00741     for(int j = 0; j < thedata.features[i].numcorners; ++j) {
00742       scaledCornerFloatX = scale_factor * thedata.features[i].corners[j].x;
00743       scaledCornerFloatY = scale_factor * thedata.features[i].corners[j].y;
00744       cc[j].scaledCornerX = static_cast<int>(scaledCornerFloatX+0.5);
00745       cc[j].scaledCornerY = static_cast<int>(scaledCornerFloatY+0.5);
00746       //if(cc[j].scaledCornerX * cc[j].scaledCornerY > 0.000001)
00747       //  correction = (scaledCornerFloatX * scaledCornerFloatY) /(cc[j].scaledCornerX * cc[j].scaledCornerY);
00748       //else
00749         correction = 1;  
00750       cc[j].scaledIndex = cc[j].scaledCornerY * image_width + cc[j].scaledCornerX;
00751       cc[j].value = thedata.features[i].corners[j].value * correction;
00752       //cout << "(" <<  scaledCornerFloatX << ", " << scaledCornerFloatY << ") --> (";
00753       //cout <<  cc[j].scaledCornerX << ", " << cc[j].scaledCornerY << "). ";
00754       //cout << "correction: "<< correction << endl;
00755       fn += thedata.features[i].corners[j].value *
00756         thedata.features[i].corners[j].x *
00757         thedata.features[i].corners[j].y;
00758     }
00759     if(make16bytes && (thedata.features[i].numcorners < newnc)){
00760         for(int j = thedata.features[i].numcorners; j < newnc; ++j) {
00761             cc[j].scaledCornerX = 0;
00762             cc[j].scaledCornerY = 0;
00763             cc[j].scaledIndex = 0;
00764             cc[j].value = 0;
00765         }
00766     }
00767     
00768     pcc[i] = cc;
00769     fns[i] = static_cast< T >(fn);
00770   }
00771 
00772   nw_fn = 0;
00773   for(i = 0; i < 4; ++i){
00774     nwc[i].scaledCornerX = static_cast<int>(scale_factor * norm_window[i].x);
00775     nwc[i].scaledCornerY = static_cast<int>(scale_factor * norm_window[i].y);
00776     nwc[i].scaledIndex = nwc[i].scaledCornerY * image_width + nwc[i].scaledCornerX;
00777     nw_fn += norm_window[i].value * nwc[i].scaledCornerX * nwc[i].scaledCornerY;
00778   }
00779 
00780   return pcc;
00781 }
00782 template< class T >
00783 void MPISearchStream< T >::deleteCacheCorners ( CornerCache< T > ** &pcc,  T * &fns )
00784 {
00785   for(int i = 0; i < numfeatures; i++) {
00786     //free(pcc[i]);
00787     delete [] pcc[i];
00788   }
00789   //free(pcc);
00790   delete [] pcc;
00791   // free(fns);
00792   delete [] fns;
00793   // Note: someone replaced with xmlFree for windows changes, which makes no sense to me
00794 }
00795 template< class T >
00796 void MPISearchStream< T >::makeNormWindow( Corner norm_window[4], FeatureData &thedata){
00797   // Create the normalization window feature
00798   norm_window[0].x = thedata.normOffset.top; norm_window[0].y = thedata.normOffset.left; norm_window[0].value = 1;
00799   norm_window[1].x = thedata.normOffset.top; norm_window[1].y = thedata.patch_height-1 - thedata.normOffset.right; norm_window[1].value = -1;
00800   norm_window[2].x = thedata.patch_width-1 - thedata.normOffset.bottom; norm_window[2].y = thedata.normOffset.left; norm_window[2].value = -1;
00801   norm_window[3].x = thedata.patch_width-1 - thedata.normOffset.bottom; norm_window[3].y = thedata.patch_height-1 - thedata.normOffset.right;
00802   norm_window[3].value = 1;
00803 }
00804 
00805 
00806 #endif
00807 
00808 /*
00809  * 
00810  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
00811  * 
00812  *    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
00813  *    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
00814  *    3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
00815  * 
00816  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00817  * 
00818  */
00819 

Generated on Mon Nov 8 17:07:45 2004 for MPT by  doxygen 1.3.9.1