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

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

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