![]() |
The Machine Perception Toolbox |
|
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