FoeDetector.C

00001 /*!@file Robots2/Beobot2/Navigation/FOE_Navigation/FoeDetector.C */
00002 // //////////////////////////////////////////////////////////////////// //
00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00004 // University of Southern California (USC) and the iLab at USC.         //
00005 // See http://iLab.usc.edu for information about this project.          //
00006 // //////////////////////////////////////////////////////////////////// //
00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00009 // in Visual Environments, and Applications'' by Christof Koch and      //
00010 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00011 // pending; application number 09/912,225 filed July 23, 2001; see      //
00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00013 // //////////////////////////////////////////////////////////////////// //
00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00015 //                                                                      //
00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00017 // redistribute it and/or modify it under the terms of the GNU General  //
00018 // Public License as published by the Free Software Foundation; either  //
00019 // version 2 of the License, or (at your option) any later version.     //
00020 //                                                                      //
00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00024 // PURPOSE.  See the GNU General Public License for more details.       //
00025 //                                                                      //
00026 // You should have received a copy of the GNU General Public License    //
00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00029 // Boston, MA 02111-1307 USA.                                           //
00030 // //////////////////////////////////////////////////////////////////// //
00031 //
00032 // Primary maintainer for this file: Christian Siagian <siagian@usc.edu>
00033 // $HeadURL: svn://ilab.usc.edu/trunk/saliency/src/Robots/Beobot2/Navigation/FOE_Navigation/FoeDetector.C
00034 // $ $Id: $
00035 //
00036 //////////////////////////////////////////////////////////////////////////
00037 
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 
00041 #include "Robots/Beobot2/Navigation/FOE_Navigation/FoeDetector.H"
00042 
00043 #include  <cstdio>
00044 #include "Image/Kernels.H"
00045 #include "Image/ColorOps.H"
00046 #include "Image/CutPaste.H"
00047 #include "Image/DrawOps.H"
00048 #include "Image/FilterOps.H"
00049 #include "Image/MathOps.H"
00050 #include "Image/Pixels.H"
00051 #include "Image/ShapeOps.H"
00052 #include "Image/SimpleFont.H"
00053 
00054 #include "Util/Timer.H"
00055 
00056 #define FOE_STEP                 4
00057 #define INTEGRATION_WINDOW       5
00058 
00059 // ######################################################################
00060 // ######################################################################
00061 FoeDetector::FoeDetector(OptionManager& mgr,
00062                          const std::string& descrName,
00063                          const std::string& tagName)
00064   :
00065   ModelComponent(mgr, descrName, tagName)
00066 { 
00067   // set observer rotation motion to be 0
00068   itsCurrentRotMotionSpeed = 0.0;
00069   itsCurrentRotMotionDir   = 0;
00070   itsCurrentRotMotionDi    = 0.0; 
00071   itsCurrentRotMotionDj    = 0.0;
00072   
00073   // For temporal integration of FOE computation
00074   itsCurrentFoeMapIndex = -1;
00075   itsRecentFoeMaps.resize(INTEGRATION_WINDOW);
00076 
00077   // storage for MT features
00078   itsMT.reset(new MiddleTemporal());
00079 
00080   itsWin.reset();
00081 }
00082 
00083 // ######################################################################
00084 void FoeDetector::reset
00085 (uint numPyrLevels, uint numDirs, uint numSpeeds)
00086 {
00087   itsNumPyrLevels  = numPyrLevels;
00088   itsNumDirs       = numDirs;
00089   itsNumSpeeds     = numSpeeds;
00090 
00091   itsSpatioTemporalPyrBuilders.clear();
00092   itsSpatioTemporalPyrBuilders.resize(itsNumDirs);
00093   for(uint i = 0; i < itsNumDirs; i++)
00094     itsSpatioTemporalPyrBuilders[i].resize(itsNumSpeeds);
00095 
00096   itsRawSpatioTemporalEnergy.clear();
00097   itsRawSpatioTemporalEnergy.resize(itsNumDirs);
00098   for(uint i = 0; i < itsNumDirs; i++)
00099     itsRawSpatioTemporalEnergy[i].resize(itsNumSpeeds);
00100 
00101   itsSpatioTemporalEnergy.resize(itsNumDirs);
00102   //itsSpatioTemporalEnergyOptimalShift.resize(itsNumDirs);
00103   //for(uint i = 0; i < itsNumDirs; i++)
00104   //  itsSpatioTemporalEnergyOptimalShift[i].reset(itsNumPyrLevels);
00105 
00106   itsMTfeatures.resize(itsNumDirs);
00107   itsMToptimalShift.resize(itsNumDirs);
00108 
00109   LINFO("Using %d directions spanning [0..360]deg", itsNumDirs);
00110   for (uint i = 0; i < itsNumDirs; i++)
00111     {
00112       for (uint j = 0; j < itsNumSpeeds; j++)
00113         {
00114           float speed = pow(2.0, j);
00115           
00116           itsSpatioTemporalPyrBuilders[i][j].reset
00117             (new SpatioTemporalEnergyPyrBuilder<float>
00118              (Oriented5, 
00119               360.0 * double(i)/double(itsNumDirs),
00120               speed,
00121               itsNumPyrLevels));
00122         }
00123 
00124       // FIXXX: add the 1/2, 1/2, 1/4 case as well
00125     }
00126 }
00127 
00128 // ######################################################################
00129 FoeDetector::~FoeDetector()
00130 { }
00131 
00132 // ######################################################################
00133 Point2D<int> FoeDetector::getFoe
00134 (Image<byte> lum, uint method, bool tempFilter)
00135 {
00136   // compute all Visual Cortex features
00137   uint width  = itsCurrentImage.getWidth();
00138   uint height = itsCurrentImage.getHeight(); 
00139 
00140   uint lwidth  = lum.getWidth();
00141   uint lheight = lum.getHeight(); 
00142 
00143   if(width != lwidth || height != lheight)
00144     {
00145       width  = lum.getWidth();
00146       height = lum.getHeight(); 
00147       itsFoeMap = Image<float>
00148         (width/MAX_NEIGHBORHOOD, height/MAX_NEIGHBORHOOD, ZEROS);
00149 
00150       // pre-compute direction weights for FOE detection
00151       resetDirectionWeights(width, height);
00152     }
00153 
00154   itsCurrentImage = lum;
00155 
00156   Timer tim(1000000);
00157     
00158   // compute Visual Cortex features 
00159   tim.reset();
00160   computeV1features();
00161   //uint64 t1 = tim.get();
00162   //LINFO("time V1feat : %f", t1/1000.0);
00163 
00164   Point2D<int> maxpt;
00165   if(itsRawSpatioTemporalEnergy[0][0].size() > 0) 
00166     {
00167       // compute Medial Temporal features 
00168       itsMT->computeMTfeatures(itsRawSpatioTemporalEnergy);
00169       itsMTfeatures = itsMT->getMTfeatures();
00170       itsMToptimalShift = itsMT->getMToptimalShift();
00171       
00172       // DEBUG
00173       //printItsSpatioTemporalEnergy();
00174       
00175       //uint64 t2 = tim.get();
00176       //LINFO("time MTfeat : %f", (t2 - t1)/1000.0);
00177       
00178       // detect yaw-pitch plane rotation (planar motion)
00179       // and correct the FOE templates accordingly
00180       //detectObserverRotation();
00181       //correctForObserverRotation();
00182       
00183       // compute FOE     
00184       if(method == FOE_METHOD_TEMPLATE)
00185         maxpt = computeFoeTemplate();
00186       else if(method == FOE_METHOD_AVERAGE)
00187         maxpt = computeFoeAverage();
00188       else LFATAL("Unknown Foe Method");
00189       //uint64 t3 = tim.get();  
00190       //LINFO("time MSTfeat: %f", (t3 - t2)/1000.0);
00191     }
00192 
00193   // if we want to filter the FOE locations temporally
00194   if(tempFilter)
00195     itsFoe = temporalFilterFoeComputation();
00196   else
00197     itsFoe = maxpt;
00198 
00199   return itsFoe;
00200 }
00201 
00202 // ######################################################################
00203 Image<float> FoeDetector::getFoeMap
00204 (Image<byte> lum, uint method, bool tempFilter)
00205 {
00206   Point2D<int> pt =  getFoe(lum, method, tempFilter);
00207   LDEBUG("pt: %d %d", pt.i, pt.j);
00208   
00209   return itsFoeMap;
00210 }
00211 
00212 // ######################################################################
00213 Point2D<int> FoeDetector::getFoe
00214 ( rutz::shared_ptr<OpticalFlow> flow, uint method, bool tempFilter)
00215 {
00216   Timer tim(1000000); tim.reset();
00217       
00218   // detect yaw-pitch plane rotation (planar motion)
00219   // and correct the FOE templates accordingly
00220   detectObserverRotation();
00221   correctForObserverRotation();
00222 
00223   // NOTE: can create MT features from the flow as well
00224 
00225   // compute FOE     
00226   Point2D<int> maxpt;
00227   if(method == FOE_METHOD_TEMPLATE)
00228     maxpt = computeFoeTemplate(flow);
00229   else if(method == FOE_METHOD_AVERAGE)
00230     maxpt = computeFoeAverage(flow);
00231   else LFATAL("Unknown Foe Method");
00232   //uint64 t3 = tim.get();  
00233   //LINFO("time MSTfeat: %f", t3/1000.0);
00234 
00235   // if we want to filter the FOE locations temporally
00236   if(tempFilter)
00237     itsFoe = temporalFilterFoeComputation();
00238   else
00239     itsFoe = maxpt;
00240 
00241   return itsFoe;
00242 }
00243 
00244 // ######################################################################
00245 Image<float> FoeDetector::getFoeMap
00246 ( rutz::shared_ptr<OpticalFlow> flow, uint method, bool tempFilter)
00247 {
00248   Point2D<int> pt = getFoe(flow, method, tempFilter);
00249   LDEBUG("pt: %d %d", pt.i, pt.j);
00250 
00251   return itsFoeMap;
00252 }
00253 // ######################################################################
00254 Point2D<int> FoeDetector::getFoe
00255 ( std::vector <Image<float> > mtFeatures,
00256   std::vector <Image<float> > mtOptimalShift,
00257   uint method, bool tempFilter)
00258 {
00259   itsNumDirs        = mtFeatures.size();
00260   itsMTfeatures     = mtFeatures;
00261   itsMToptimalShift = mtOptimalShift;
00262       
00263   //uint64 t2 = tim.get();
00264   //LINFO("time MTfeat : %f", (t2 - t1)/1000.0);
00265 
00266   // check whether observer is stationary
00267   float activityVal = detectObserverStationarity();
00268 
00269   // detect yaw-pitch plane rotation (planar motion)
00270   // and correct the FOE templates accordingly  
00271   detectObserverRotation();
00272   //correctForObserverRotation();
00273   
00274   // compute FOE
00275   Point2D<int> maxpt;
00276   if(method == FOE_METHOD_TEMPLATE)
00277     maxpt = computeFoeTemplate();
00278   else if(method == FOE_METHOD_AVERAGE)
00279     maxpt = computeFoeAverage();
00280   else LFATAL("Unknown Foe Method");
00281 
00282   // if we want to filter the FOE locations temporally
00283   if(tempFilter)
00284     itsFoe = temporalFilterFoeComputation();
00285   else
00286     itsFoe = maxpt;
00287 
00288   // check if the we observe stationary or radial motion
00289   // by the confidence value of the maximum location
00290   LINFO("maxpt: %d %d", maxpt.i, maxpt.j);
00291   float val = itsFoeMap.getVal(maxpt/MAX_NEIGHBORHOOD);
00292   if(val < .1)
00293     { 
00294       LINFO("max val too low: "
00295             "must be radial motion (%f) or stationary (%f)",
00296             val, activityVal);
00297       itsFoeMap.clear(0.0);
00298       itsFoe = Point2D<int>(-1,-1);
00299     }
00300 
00301   return itsFoe;
00302 }
00303 
00304 // ######################################################################
00305 Point2D<int> FoeDetector::temporalFilterFoeComputation()
00306 {
00307   Point2D<int> foe;
00308   uint width  = itsFoeMap.getWidth();
00309   uint height = itsFoeMap.getHeight();
00310 
00311   // put the current FOE map to the history
00312   itsCurrentFoeMapIndex++;
00313   uint index = (itsCurrentFoeMapIndex)%INTEGRATION_WINDOW;
00314   itsRecentFoeMaps[index] = itsFoeMap;
00315 
00316   uint numMaps = INTEGRATION_WINDOW;
00317   if (itsCurrentFoeMapIndex < INTEGRATION_WINDOW)
00318     numMaps = itsCurrentFoeMapIndex+1;
00319 
00320   LINFO("start: %d",itsCurrentFoeMapIndex);
00321   Image<float> image =  itsRecentFoeMaps[0];
00322   for(uint i = 1; i < numMaps; i++)
00323     image = image + itsRecentFoeMaps[i];
00324   itsFoeMap = image/numMaps;
00325 
00326   float max = itsFoeMap.getVal(0,0);
00327   for(uint i = 0; i < width; i++) 
00328     for(uint j = 0; j < height; j++)
00329       { 
00330         float val = itsFoeMap.getVal(i,j);
00331         if(max < val) 
00332           {
00333             foe = Point2D<int>(i*MAX_NEIGHBORHOOD,j*MAX_NEIGHBORHOOD);
00334             max = val;
00335           }
00336       }
00337 
00338   LINFO("Integrated FOE: (%3d %3d): %15.5f", foe.i, foe.j, max);
00339   // if(itsWin.is_invalid())
00340   //   itsWin.reset(new XWinManaged(Dims(width,height), 
00341   //                                width+10, 0, "FOE Detector"));
00342   // itsWin->setDims(Dims(width*MAX_NEIGHBORHOOD, height*MAX_NEIGHBORHOOD));
00343   // itsWin->drawImage(zoomXY(itsFoeMap,MAX_NEIGHBORHOOD),0,0);
00344 
00345   //Raster::waitForKey();
00346 
00347 
00348       // float midsum = 0.0;
00349       // for(uint i = width/4+1; i < 3*width/4-1; i++)
00350       //   for(uint j = height/4+1; j < 3*height/4-1; j++)
00351       //     midsum += itsFoeMap.getVal(i,j);
00352       // float total = sum(itsFoeMap);
00353 
00354       // LINFO("mid: %f sum: %f", midsum, total);
00355 
00356   // if(itsWin.is_invalid())
00357   //   itsWin.reset(new XWinManaged(Dims(width,height), 28
00358   //                                width+10, 0, "FOE Detector"));
00359   // itsWin->setDims(Dims(width*MAX_NEIGHBORHOOD, height*MAX_NEIGHBORHOOD));
00360   // itsWin->drawImage(zoomXY(itsFoeMap,MAX_NEIGHBORHOOD),0,0);
00361   
00362   // Raster::waitForKey();
00363 
00364   return foe;
00365 }
00366 
00367 // ######################################################################
00368 Image<float> FoeDetector::getFoeMap
00369 ( std::vector <Image<float> > mtFeatures,
00370   std::vector <Image<float> > mtOptimalShift,
00371   uint method, bool tempFilter)
00372 {
00373   Point2D<int> pt = getFoe(mtFeatures, mtOptimalShift, 
00374                            method, tempFilter);
00375   LDEBUG("pt: %d %d", pt.i, pt.j);
00376 
00377   return itsFoeMap;
00378 }
00379 
00380 // ######################################################################
00381 Point2D<int> FoeDetector::getFoe()
00382 {
00383   return itsFoe;
00384 }
00385 
00386 // ######################################################################
00387 Image<float> FoeDetector::getFoeMap()
00388 {
00389   return itsFoeMap;
00390 }
00391 
00392 // ######################################################################
00393 void FoeDetector::resetDirectionWeights(uint width, uint height)
00394 {
00395   float length = 1.0;
00396 
00397   itsDirWeights.clear();
00398   itsDirWeights.resize(width/MAX_NEIGHBORHOOD/FOE_STEP);
00399   for(uint i = 0; i < width/MAX_NEIGHBORHOOD/FOE_STEP; i++)
00400     itsDirWeights[i].resize(width/MAX_NEIGHBORHOOD/FOE_STEP);
00401 
00402   for(uint i = 0; i < width/MAX_NEIGHBORHOOD/FOE_STEP; i++)
00403     for(uint j = 0; j < height/MAX_NEIGHBORHOOD/FOE_STEP; j++)
00404       itsDirWeights[i][j].resize(itsNumDirs);
00405 
00406   // for each point in the FOE map that we will fill in
00407   for(uint i = 0; i < width/MAX_NEIGHBORHOOD/FOE_STEP; i++)
00408     for(uint j = 0; j < height/MAX_NEIGHBORHOOD/FOE_STEP; j++)
00409       for(uint k = 0; k < itsNumDirs; k++)
00410         {
00411           float mangle = (k*360.0)/itsNumDirs;
00412           
00413           itsDirWeights[i][j][k] = 
00414             Image<float>(width/MAX_NEIGHBORHOOD, 
00415                          height/MAX_NEIGHBORHOOD, NO_INIT);
00416 
00417           // for each point in the MT features map
00418           for(uint ii = 0; ii < width/MAX_NEIGHBORHOOD; ii++)
00419             for(uint jj = 0; jj < height/MAX_NEIGHBORHOOD; jj++)
00420               {
00421                 float val = getDirectionWeight
00422                   (Point2D<float>(ii,jj), 
00423                    Point2D<float>(i*FOE_STEP, j*FOE_STEP), length, mangle);
00424 
00425                   itsDirWeights[i][j][k].setVal(ii,jj, val);
00426               }
00427         }
00428 }
00429 
00430 // ######################################################################
00431 void FoeDetector::computeV1features()
00432 {
00433   // have 3 (1,2, and 4 pix/fr) spatial shifts
00434   // have 3 (1/2, 1/3, and 1/4 pix/fr) temporal shifs
00435   //    --> too slow or fast a movement cannot be detected by human
00436 
00437   Timer tim(1000000);
00438 
00439   // compute the motion energy for each direction and speeds
00440   for (uint i = 0; i < itsNumDirs; i++)
00441     {
00442       tim.reset();
00443       for (uint j = 0; j < itsNumSpeeds; j++)
00444         {
00445           itsRawSpatioTemporalEnergy[i][j] = 
00446             itsSpatioTemporalPyrBuilders[i][j]->build(itsCurrentImage);
00447         }
00448 
00449       uint64 t1 = tim.get();
00450       LINFO("[%3d] ste: %f", i, t1/1000.0);
00451     }
00452 }
00453 
00454 
00455 // ######################################################################
00456 float FoeDetector::detectObserverStationarity()
00457 {
00458   if(itsMTfeatures.size() == 0 ||
00459      itsMTfeatures[0].getSize() == 0) return -1.0;
00460 
00461   double max = mean(itsMTfeatures[0]);  uint maxInd = 0;
00462   std::vector<double> maxs(itsNumDirs); double total = 0.0;
00463   LDEBUG("%3d: %10.3f  max[%3d]: %10.3f ", 0, max, maxInd, max);
00464   for(uint i = 1; i < itsNumDirs; i++)
00465     {
00466       maxs[i] = mean(itsMTfeatures[i]);
00467       if(maxs[i] > max)
00468         {
00469           max = maxs[i];
00470           maxInd = i;
00471         }
00472       total += maxs[i];
00473       LDEBUG("%3d: %10.3f  max[%3d]: %10.3f ", i, maxs[i], maxInd, max);
00474     }
00475   float avg = total/itsNumDirs;
00476   LINFO("avg activity: %f", avg);
00477   return avg;
00478 }
00479 
00480 // ######################################################################
00481 void FoeDetector::detectObserverRotation()
00482 {
00483   if(itsMTfeatures.size() == 0 ||
00484      itsMTfeatures[0].getSize() == 0) return;
00485 
00486   // we do not worry about identical speed throughout the field
00487   //  to gauge for speed we look for parts that have dominant preference
00488   double max = maxMean(itsMTfeatures[0]);  uint maxInd = 0;
00489   std::vector<double> maxs(itsNumDirs); double total = 0.0;
00490   LDEBUG("%3d: %10.3f  max[%3d]: %10.3f ", 0, max, maxInd, max);
00491   for(uint i = 1; i < itsNumDirs; i++)
00492     {
00493       maxs[i] = maxMean(itsMTfeatures[i]);
00494       if(maxs[i] > max)
00495         {
00496           max = maxs[i];
00497           maxInd = i;
00498         }
00499       total += maxs[i];
00500       LDEBUG("%3d: %10.3f  max[%3d]: %10.3f ", i, maxs[i], maxInd, max);
00501     }
00502 
00503   // check ratio of maximum to total 
00504   double rat = max/total;
00505   if(rat > 3.0/itsNumDirs) LINFO("Planar motion : %f", rat);
00506 
00507   computeFoeAverage();
00508 
00509   // MAYBE THE TOTAL NEEDS TO GO ABOVE A CERTAIN THRESHOLD: .020 
00510   // to go above stationary.
00511 
00512   // motion from far away areas has little speed but mostly rotational
00513   // motion from closer areas has more speed but mostly translational
00514   //   and also more varied speed.
00515   // not FOE
00516 
00517   // compute motion opponency ? already computed
00518 
00519 
00520   // try SIFT first for correspondences
00521 
00522   //itsCurrentRotMotionDir   = maxInd;
00523   //itsCurrentRotMotionSpeed = 0.0;
00524   //LINFO("max[%3d]: %10.3f Speed: %f", maxInd, max, itsCurrentRotMotionSpeed);
00525 
00526   // LINFO("estimating ego-motion");
00527   //Raster::waitForKey();
00528 }
00529 
00530 
00531 // ######################################################################
00532 float FoeDetector::maxMean(Image<float> image)
00533 {
00534   uint width  = image.getWidth();
00535   uint height = image.getHeight();
00536 
00537   float max = 0.0;  Rectangle rMax;
00538   for(uint i = 0; i < 5; i++)
00539     {
00540       uint sI = uint(i* width/8.0);
00541       uint eI = uint((i+4)* width/8.0 -1);
00542 
00543       for(uint j = 0; j < 5; j++)
00544         {
00545           uint sJ = uint(j* height/8.0);
00546           uint eJ = uint((j+4)* height/8.0 - 1);
00547       
00548           Rectangle r = Rectangle::tlbrI(sJ, sI, eJ, eI);
00549           LDEBUG("[%3d %3d %3d %3d]", 
00550                 r.top(), r.left(), r.bottomI(), r.rightI());
00551           Image<float> result = crop(image, r);
00552           float val = mean(result); 
00553           LDEBUG("val: %f", val);
00554 
00555           // get the mean magnitude
00556           if(max < val){ max = val; rMax = r; }
00557         }
00558     }
00559   LDEBUG("[%3d %3d %3d %3d]: %10.3f", 
00560          rMax.top(), rMax.left(), rMax.bottomI(), rMax.rightI(), max);
00561 
00562   return max;
00563 }
00564 
00565 // ######################################################################
00566 void FoeDetector::setObserverRotation(uint dir, float speed)
00567 {
00568   itsCurrentRotMotionDir   = dir;
00569   itsCurrentRotMotionSpeed = speed;
00570 
00571   itsCurrentRotMotionDi = 
00572     itsCurrentRotMotionSpeed *
00573     cos((2.0*M_PI * itsCurrentRotMotionDir)/itsNumDirs); 
00574   itsCurrentRotMotionDj =
00575     itsCurrentRotMotionSpeed *
00576     sin((2.0*M_PI * itsCurrentRotMotionDir)/itsNumDirs);
00577   
00578   LINFO("di,j: %f %f", itsCurrentRotMotionDi, itsCurrentRotMotionDj);
00579 }
00580 
00581 // ######################################################################
00582 void FoeDetector::setObserverRotation(float di, float dj)
00583 {
00584   itsCurrentRotMotionDi = di;
00585   itsCurrentRotMotionDj = dj;
00586 
00587   float angle = atan2(dj,di) / M_PI * 180.0;              
00588   float nangle = fmod(angle+360.0, 360.0);
00589   
00590   float max = 360.0; uint dir = 0;
00591   for(uint i = 0; i <itsNumDirs; i++)
00592     {
00593       float mangle = (i*360.0)/itsNumDirs;
00594 
00595       float diff1 = fabs(nangle - mangle);
00596       float diff2 = 360.0 - diff1;
00597       float diff = diff1; if(diff1 > diff2) diff = diff2;
00598 
00599       if(diff < max) { max = diff; dir = i; }
00600     }
00601 
00602   itsCurrentRotMotionDir   = dir;
00603   itsCurrentRotMotionSpeed = sqrt(di*di + dj*dj);
00604 
00605   LINFO("di,j: %f %f", itsCurrentRotMotionDi, itsCurrentRotMotionDj);
00606 }
00607 
00608 // ######################################################################
00609 void FoeDetector::correctForObserverRotation()
00610 {
00611   // vector addition 
00612   // between rotational change and FOE templates
00613 
00614 
00615 //   itsCurrentRotMotionDirection;
00616 //   itsCurrentRotMotionSpeed;
00617   
00618 //   float dX = 
00619 //     itsCurrentRotMotionSpeed *
00620 //     cos((2.0*M_PI * itsCurrentRotMotionDirection)/itsNumDirs); 
00621 //   float dY = 
00622 //     itsCurrentRotMotionSpeed *
00623 //     sin((2.0*M_PI * itsCurrentRotMotionDirection)/itsNumDirs); 
00624 
00625 
00626 
00627   // NOTE: maybe try idealized rotational motion first
00628   //    moving to the left
00629   //    moving at an angle
00630   //    combine moving and FOE
00631 }
00632 
00633 // ######################################################################
00634 Point2D<int> FoeDetector::computeFoeTemplate()
00635 {
00636   Point2D<int> foeLoc(-1, -1);
00637   if(itsMTfeatures[0].size() == 0) return foeLoc;
00638 
00639   int width  = itsMTfeatures[0].getWidth();
00640   int height = itsMTfeatures[0].getHeight();
00641   itsFoeMap = Image<float>(width,height, ZEROS);
00642 
00643   int foeStep = FOE_STEP;
00644   int border = 2*foeStep;
00645   Timer tim(1000000);
00646 
00647   LDEBUG("dir: %d sp: %f", itsCurrentRotMotionDir, itsCurrentRotMotionSpeed);
00648 
00649   // compute FOE value for all desired locations
00650 
00651   // good result is about 3.6% 
00652   // max out at about 5%, will multiply by 20.0 
00653   // to make it 100% or bigger in a few cases
00654   // we divide with width * height to normalize on size of image as well
00655   float normalizer = 20.0/width/height;
00656   float max = 0.0; float min = 10000.0;
00657   for(int i = border; i < (width - border); i+=foeStep)
00658     {
00659       for(int j = border+ 4*foeStep; j < (height - border)-3*foeStep; j+=foeStep)
00660         {
00661           tim.reset();
00662           float val = computeFoeTemplateValue(i,j) * normalizer;
00663 
00664           // NOTE FIX: maybe have 0 flow threshold to say that there is no FOE
00665           if(val > max) 
00666             {
00667               foeLoc = Point2D<int>(i*MAX_NEIGHBORHOOD,j*MAX_NEIGHBORHOOD);
00668               max = val;
00669             }
00670 
00671           if(val < min) min = val;
00672           itsFoeMap.setVal(i,j,val);
00673           drawFilledRect
00674             (itsFoeMap,Rectangle::tlbrI(j,i,j+foeStep-1,i+foeStep-1), val);
00675 
00676           //LINFO("(%13.3f) [%3d %3d]: %15.5f", tim.get()/1000.0, i,j, val);
00677         }
00678     }
00679   LINFO("Current FOE: (%3d %3d): %15.5f", foeLoc.i, foeLoc.j, max);
00680 
00681   for(int i = 0; i < width; i+=foeStep)
00682     {
00683       for(int j = 0; j < height; j+=foeStep)
00684         {
00685           if((i < border) || 
00686              (j < border + 4*foeStep) ||
00687              (i >= (width - border)) || 
00688              (j >= (height - border)-3*foeStep)) 
00689             {
00690               drawFilledRect
00691                 (itsFoeMap,Rectangle::tlbrI(j,i,j+foeStep-1,i+foeStep-1), min);
00692               //LINFO("[%3d %3d]: %15.5f", i,j, min);
00693             }
00694         }
00695     }
00696 
00697  // if(itsWin.is_invalid())
00698  //   itsWin.reset(new XWinManaged(Dims(width,height), 
00699  //                                width+10, 0, "FOE Template: FOE Detector"));
00700  // itsWin->setDims(Dims(width*MAX_NEIGHBORHOOD, height*MAX_NEIGHBORHOOD));
00701  // itsWin->drawImage(zoomXY(itsFoeMap,MAX_NEIGHBORHOOD),0,0);
00702   
00703  // FILE *fp; 
00704  // LINFO("FOE%d %d", foeLoc.i, foeLoc.j);
00705  // if((fp = fopen("ST_PS_Germany.txt","at")) == NULL) LFATAL("not found");
00706  // fputs(sformat("%d %d \n", foeLoc.i, foeLoc.j).c_str(), fp);
00707  // fclose (fp);  
00708 
00709   //Raster::waitForKey();
00710 
00711   return foeLoc;
00712 }
00713 
00714 // ######################################################################
00715 float FoeDetector::computeFoeTemplateValue(uint foeI, uint foeJ)
00716 {
00717   float result = 0.0;
00718 
00719   int width  = itsFoeMap.getWidth();
00720   int height = itsFoeMap.getHeight(); 
00721   float diag = sqrt(width*width + height*height);
00722   //float range = 360.0/double(itsNumDirs)/2.0;
00723 
00724   Image<float>::iterator mtT[itsNumDirs];
00725   for (unsigned int i=0; i < itsNumDirs; i++)
00726     mtT[i] = itsMTfeatures[i].beginw();
00727 
00728   Image<float>::iterator mtosT[itsNumDirs];
00729   for (unsigned int i=0; i < itsNumDirs; i++)
00730     mtosT[i] = itsMToptimalShift[i].beginw();
00731 
00732   // go through each direction
00733   for(uint dir = 0; dir < itsNumDirs; dir++)
00734     {
00735 //       Image<float> distWeight(width, height, ZEROS);
00736 //       Image<float> dirWeight (width, height, ZEROS);
00737 //       Image<float>::iterator tmtT = distWeight.beginw();
00738 //       Image<float>::iterator tmtT = dirWeight.beginw();
00739 
00740       float mangle = (dir*360.0)/itsNumDirs;
00741 
00742       // go through each point
00743       for(int j = 0; j < height; j++)
00744         {
00745           for(int i = 0; i < width; i++)
00746             {
00747               // the vector distance from this FOE
00748               float di = float(i) - float(foeI);
00749               float dj = float(j) - float(foeJ);
00750               float dist = sqrt(pow(di, 2.0) + pow(dj, 2.0)); 
00751 
00752               // check the quadrant 
00753 //              uint quad = 0; 
00754 //               if(nangle <= range || nangle >= (360.0 - range))
00755 //                 {
00756 //                   quad = 0;
00757 //                 }
00758 //               else
00759 //                 {
00760 //                   for(uint k = 1; k < itsNumDirs; k++)
00761 //                     {
00762 //                       float lang = k * 360.0/double(itsNumDirs) - range;
00763 //                       float rang = k * 360.0/double(itsNumDirs) + range;
00764 //                       if(nangle >= lang && nangle <= rang)
00765 //                         {
00766 //                           quad = k; k = itsNumDirs;
00767 //                         }
00768 //                     }
00769 //                 }
00770 
00771               // weigh contribution with direction 
00772               float length = *mtosT[dir]++;              
00773               float wDir = getDirectionWeight
00774                 (Point2D<float>(i,j), Point2D<float>(foeI, foeJ), length, mangle);
00775 
00776               // NOTE: USING PRE-COMPUTED WEIGHTS
00777               //float wDir = itsDirWeights[foeI/FOE_STEP][foeJ/FOE_STEP][dir].getVal(i,j);
00778               
00779 
00780               //LINFO("(%d %d)(%d %d -> %d %d) %f %f", 
00781               //      i,j, foeI,foeJ,foeI/FOE_STEP, foeJ/FOE_STEP, wDir, wDir2);              
00782 
00783              // weight contribution with distance:
00784               //   the closer the location to FOE 
00785               //   the less reliable it is
00786               float wLoc = 1.0; 
00787               if(dist/diag < 1.0/8.0) wLoc = .1;
00788               
00789               //distWeight.setVal(i,j, wLoc);
00790 
00791               float val = *mtT[dir]++;
00792               //result += wDir*val;
00793               result += wDir*wLoc*val;
00794 
00795               
00796             }
00797         }
00798     }
00799 
00800   // NOTE: FIXXX: find more constraints for FOE!!!
00801 
00802   // 6DOF observers: 2trans, 1heading, 3 rot (pitch, yaw, roll) 
00803   //   Roll is minimal
00804   // speed is not recoverable from flow field
00805   
00806   // Deal with rotation in yaw --> 4 rotation presets
00807 
00808   // take care of noise in the max
00809   return result;
00810 }
00811 
00812 // ######################################################################
00813 float FoeDetector::getDirectionWeight
00814   (Point2D<float> pt, Point2D<float> foe, float length, float mangle)
00815 {  
00816   if(length == 0.0) return 0.0;
00817   //if(mangle == 0.0 || mangle == 180.0) return 0.0;
00818   //if(length*3.0 < itsCurrentRotMotionSpeed) return 0.0; 
00819 
00820   // observer rotational motion
00821   float mI = itsCurrentRotMotionDi;
00822   float mJ = itsCurrentRotMotionDj;
00823 
00824   // delta in the middle
00825   float odi = (float(pt.i) - float(foe.i));
00826   float odj = (float(pt.j) - float(foe.j));
00827   float dist = sqrt(odi*odi + odj*odj); 
00828   if(dist == 0.0) dist = 1.0;
00829 
00830   float di = length*(float(pt.i) - float(foe.i))/dist + mI;
00831   float dj = length*(float(pt.j) - float(foe.j))/dist + mJ;
00832 
00833   float angle = atan2(dj,di) / M_PI * 180.0;              
00834   float nangle = fmod(angle +360.0, 360.0);
00835 
00836   // find difference in direction
00837   float diff1 = fabs(nangle - mangle);
00838   float diff2 = 360.0 - diff1;
00839   float diff = diff1; if(diff1 > diff2) diff = diff2;
00840 
00841 //   if((foe.i == 20 && foe.j == 16) &&
00842 //      (odi == odj) && (odi > 12 && fmod(odi,4.0) == 0.0) && 
00843 //      length != 0.0)
00844 //     {
00845 //       float oangle = atan2(odj,odi) / M_PI * 180.0;              
00846 //       float onangle = fmod(oangle +360.0, 360.0);
00847       
00848 //       float odiff1 = fabs(onangle - mangle);
00849 //       float odiff2 = 360.0 - odiff1;
00850 //       float odiff = odiff1; if(odiff1 > odiff2) odiff = odiff2;
00851 
00852 //       LINFO("(%3d %3d), FOE(%3d %3d): Motion[[%8.3f %8.3f]] "
00853 //             "mangle: %8.3f, length = %8.3f " 
00854 //             "angle %8.3f -> %8.3f diff: %8.3f -> %8.3f ", 
00855 //             pt.i, pt.j, foe.i, foe.j, 
00856 //             mI, mJ, 
00857 //             mangle, length,
00858 //             onangle, nangle, odiff, diff);
00859 //     }
00860 
00861 
00862 
00863 //   float width  = itsFOEmap.getWidth();
00864 //   float height = itsFOEmap.getHeight();
00865 
00866 //   //===================================================
00867 //   // top left delta
00868 //   float tli = pt.i - 1.0F; if(tli < 0) tli = 0.0F;
00869 //   float tlj = pt.j - 1.0F; if(tlj < 0) tlj = 0.0F; 
00870 
00871 //   float tldi = tli - float(foe.i);
00872 //   float tldj = tlj - float(foe.j);
00873 
00874 //   float tlAngle = atan2(tldj,tldi) / M_PI * 180.0;              
00875 //   float tlNangle = fmod(tlAngle +360.0, 360.0);
00876 
00877 //   // angle is circular
00878 //   float tlDiff1 = fabs(tlNangle - mangle);
00879 //   float tlDiff2 = 360.0 - tlDiff1;
00880 //   float tlDiff = tlDiff1; if(tlDiff1 > tlDiff2) tlDiff = tlDiff2;
00881 
00882 //   //===================================================    
00883 //   // top right delta
00884 //   float tri = pt.i + 1.0F; if(tri > width-1) tri = width-1;
00885 //   float trj = pt.j - 1.0F; if(trj < 0) trj = 0.0F; 
00886 
00887 //   float trdi = tri - float(foe.i);
00888 //   float trdj = trj - float(foe.j);
00889 
00890 //   float trAngle = atan2(trdj,trdi) / M_PI * 180.0;              
00891 //   float trNangle = fmod(trAngle +360.0, 360.0);
00892 
00893 //   // angle is circular
00894 //   float trDiff1 = fabs(trNangle - mangle);
00895 //   float trDiff2 = 360.0 - trDiff1;
00896 //   float trDiff = trDiff1; if(trDiff1 > trDiff2) trDiff = trDiff2;
00897 
00898 //   //===================================================
00899 //   // bottom left delta
00900 //   float bli = pt.i - 1.0F; if(bli < 0) bli = 0.0F;
00901 //   float blj = pt.j + 1.0F; if(blj > height-1) blj = height-1; 
00902 
00903 //   float bldi = bli - float(foe.i);
00904 //   float bldj = blj - float(foe.j);
00905 
00906 //   float blAngle = atan2(bldj,bldi) / M_PI * 180.0;              
00907 //   float blNangle = fmod(blAngle +360.0, 360.0);
00908 
00909 //   // angle is circular
00910 //   float blDiff1 = fabs(blNangle - mangle);
00911 //   float blDiff2 = 360.0 - blDiff1;
00912 //   float blDiff  = blDiff1; if(blDiff1 > blDiff2) blDiff = blDiff2;
00913 
00914 //   //===================================================
00915 //   // bottom right delta
00916 //   float bri = pt.i - 1.0F; if(bri > width-1)  bri = width-1;
00917 //   float brj = pt.j - 1.0F; if(brj > height-1) brj = height-1; 
00918 
00919 //   float brdi = bri - float(foe.i);
00920 //   float brdj = brj - float(foe.j);
00921 
00922 //   float brAngle = atan2(brdj,brdi) / M_PI * 180.0;
00923 //   float brNangle = fmod(brAngle +360.0, 360.0);
00924 
00925 //   // angle is circular
00926 //   float brDiff1 = fabs(brNangle - mangle);
00927 //   float brDiff2 = 360.0 - brDiff1;
00928 //   float brDiff  = blDiff1; if(brDiff1 > brDiff2) brDiff = brDiff2;
00929 
00930 
00931 //   // find the smallest angles  
00932 //   diff = tlDiff;
00933 //   if(diff > trDiff) diff = trDiff;
00934 //   if(diff > blDiff) diff = blDiff;
00935 //   if(diff > brDiff) diff = brDiff;
00936 
00937 
00938   float val = 0.0;
00939   //float stdang = 30.0;
00940 
00941   // we will use a half cosine rectified weights
00942   if(diff <= 90.0) val = cos(diff/180.0*M_PI)*.4;
00943 
00944   // LINFO("cos[90]: %f cos(45): %f cos[0]: %f ",
00945   //       cos(90.0/180.0*M_PI),
00946   //       cos(45.0/180.0*M_PI),
00947   //       cos( 0.0/180.0*M_PI));
00948   // Raster::waitForKey();
00949 
00950   //  // difference between 0 and 30 degrees
00951   //  if(diff >= 0 && diff < stdang)
00952   //    val = (stdang - diff)/stdang *    0.1 + 0.3;
00953   //    //  val = .4;
00954 
00955   //  // difference between 30 and 60 degrees
00956   //  else if(diff >= stdang && diff < 2*stdang)
00957   //    val = (2*stdang - diff)/stdang *  0.2 + 0.1;
00958 
00959   //  // difference between 60 and 90 degrees
00960   //  else if(diff >= 2*stdang && diff < 3*stdang)
00961   //    val = (3*stdang - diff)/stdang *  0.1;
00962 
00963   // // difference between 150 and 180 degrees
00964   // else
00965   //   val = (6*stdang - diff)/stdang * 0.1 + -0.4;
00966 
00967   // // OR JUST:
00968   // // difference between 0 and 90 degrees
00969   // // NOTE: for aperture problem: doesn't work well, however
00970   // //if(diff >= 0 && diff < 3*stdang) val = .4;
00971 
00972   // // difference between 90 and 120 degrees
00973   // else if(diff >= 3*stdang && diff < 4*stdang)
00974   //   val = (4*stdang - diff)/stdang * 0.1 + -0.1;
00975 
00976   // // difference between 120 and 150 degrees
00977   // else if(diff >= 4*stdang && diff < 5*stdang)
00978   //   val = (5*stdang - diff)/stdang * 0.2 + -0.3;
00979 
00980   // // difference between 150 and 180 degrees
00981   // else
00982   //   val = (6*stdang - diff)/stdang * 0.1 + -0.4;
00983 
00984   return val;
00985 }
00986 
00987 // ######################################################################
00988 float FoeDetector::getDirectionWeight2(uint quad, uint dir)
00989 {
00990   if(quad == dir) return 1.00;
00991   else            return 0.25;
00992 
00993   // need more fancy computation
00994 
00995   // using distance from quadrants: min(x, x-NUM_DIR/2, x+NUM_DIR/2)
00996 }
00997 
00998 // ######################################################################
00999 Point2D<int> FoeDetector::computeFoeTemplate
01000 (rutz::shared_ptr<OpticalFlow> flow)
01001 {
01002   Point2D<int> foeLoc(-1,-1);
01003 
01004   Dims imSize = flow->getImageDims();
01005   int width  = imSize.w()/MAX_NEIGHBORHOOD;
01006   int height = imSize.h()/MAX_NEIGHBORHOOD;
01007   itsFoeMap = Image<float>(width,height, ZEROS);
01008 
01009   int foeStep = FOE_STEP;
01010   int border  = 2*foeStep;
01011   Timer tim(1000000);
01012 
01013   LDEBUG("dir: %d sp: %f", itsCurrentRotMotionDir, itsCurrentRotMotionSpeed);
01014 
01015   // compute FOE value
01016 
01017   // good result is about 3.6%_FIXXXX
01018   // max out at about 5%, will multiply by 20.0_FIXXXX 
01019   // to make it 100% or bigger in a few cases
01020   // we divide with width * height to normalize on size of image as well
01021   float normalizer = 20.0/width/height;
01022   float max = 0.0; float min = 10000.0;
01023   for(int i = border; i < (width - border); i+=foeStep)
01024     {
01025       for(int j = border; j < (height - border); j+=foeStep)
01026         {
01027           tim.reset();
01028           float val = computeFoeTemplateValue
01029             (i*MAX_NEIGHBORHOOD,j*MAX_NEIGHBORHOOD, flow) * normalizer;
01030 
01031           // NOTE FIX: have 0 flow threshold to say that there is no FOE    
01032           if(val > max) 
01033             {
01034               foeLoc = Point2D<int>(i*MAX_NEIGHBORHOOD,j*MAX_NEIGHBORHOOD);
01035               max = val;
01036             }
01037 
01038           if(val < min) min = val;
01039 
01040           itsFoeMap.setVal(i,j,val);
01041           drawFilledRect
01042             (itsFoeMap,Rectangle::tlbrI(j,i,j+foeStep-1,i+foeStep-1), val);
01043           //LINFO("(%13.3f) [%3d %3d]: %15.5f", tim.get()/1000.0, i,j, val);
01044         }
01045     }
01046   LINFO("Current FOE: (%3d %3d): %15.5f", foeLoc.i, foeLoc.j, max);
01047 
01048   for(int i = 0; i < width; i+=foeStep)
01049     {
01050       for(int j = 0; j < height; j+=foeStep)
01051         {
01052           if((i < border) || 
01053              (j < border) ||
01054              (i >= (width - border)) || 
01055              (j >= (height - border))) 
01056             {
01057               drawFilledRect
01058                 (itsFoeMap,Rectangle::tlbrI(j,i,j+foeStep-1,i+foeStep-1), min);
01059               //LINFO("[%3d %3d]: %15.5f", i,j, min);
01060             }
01061         }
01062     }
01063 
01064   // if(itsWin.is_invalid())
01065   //   itsWin.reset(new XWinManaged(Dims(width,height), 
01066   //                                width+10, 0, "FOE Template: FOE Detector"));
01067   // itsWin->setDims(Dims(width*MAX_NEIGHBORHOOD, height*MAX_NEIGHBORHOOD));
01068   // itsWin->drawImage(zoomXY(itsFoeMap,MAX_NEIGHBORHOOD),0,0);
01069   // //Raster::waitForKey();
01070 
01071   return foeLoc;
01072 }
01073 
01074 // ######################################################################
01075 float FoeDetector::computeFoeTemplateValue
01076 (uint foeI, uint foeJ, rutz::shared_ptr<OpticalFlow> flow)
01077 {
01078   float result = 0.0;
01079 
01080   Dims imSize = flow->getImageDims();
01081   int width  = imSize.w();
01082   int height = imSize.h(); 
01083   float diag = sqrt(width*width + height*height);
01084   //float range = 360.0/double(itsNumDirs)/2.0;
01085 
01086   std::vector<rutz::shared_ptr<FlowVector> > flowVectors =
01087     flow->getFlowVectors();
01088 
01089   // go through each point
01090   for(uint i = 0; i < flowVectors.size(); i++)
01091     {
01092       // FIXXX: to speed things up compute these motion vectors outside!!!
01093       // get the corresponding points
01094       Point2D<float> pt1 = flowVectors[i]->p1;
01095       //Point2D<float> pt2 = flowVectors[i]->p2;
01096       float mangle       = flowVectors[i]->angle;
01097       float length       = flowVectors[i]->mag;
01098       //float val = flowVectors[i].val; // always 1 in LucasKanade
01099 
01100       //LINFO("[%3d] flow: 1[%13.4f %13.4f] 2[%13.4f %13.4f] ang: %f mag: %f val: %f",
01101       //     i, pt1.i, pt1.j, pt2.i, pt2.j, mangle, length, val);
01102 
01103       // the vector distance from this FOE
01104       float di = float(pt1.i) - float(foeI);
01105       float dj = float(pt1.j) - float(foeJ);
01106       float dist = sqrt(pow(di, 2.0) + pow(dj, 2.0)); 
01107 
01108       // weigh contribution with direction 
01109       float wDir = getDirectionWeight
01110         (pt1, Point2D<float>(foeI, foeJ), length, mangle);
01111 
01112       // NOTE: USING PRE-COMPUTED WEIGHTS
01113       //float wDir = itsDirWeights[foeI/FOE_STEP][foeJ/FOE_STEP][dir].getVal(i,j);
01114               
01115       //LINFO("(%d %d)(%d %d -> %d %d) %f %f", 
01116       //      i,j, foeI,foeJ,foeI/FOE_STEP, foeJ/FOE_STEP, wDir, wDir2);              
01117 
01118       // weight contribution with distance:
01119       //   the closer the location to FOE 
01120       //   the less reliable it is
01121       float wLoc = 1.0; 
01122       if(dist/diag < 1.0/8.0) wLoc = .1;
01123               
01124       //distWeight.setVal(i,j, wLoc);
01125 
01126       // assume all motion detection strength to be equal
01127       //result += wDir;
01128       result += wDir*wLoc;//*val;
01129     }
01130 
01131   // NOTE: FIXXX: find more constraints for FOE!!!
01132 
01133   // 6DOF observers: 2trans, 1heading, 3 rot (pitch, yaw, roll) 
01134   //   Roll is minimal
01135   // speed is not recoverable from flow field
01136   
01137   // Deal with rotation in yaw --> 4 rotation presets
01138 
01139   // take care of noise in the max
01140   return result;
01141 }
01142 
01143 // ######################################################################
01144 Point2D<int> FoeDetector::computeFoeAverage()
01145 {
01146   Point2D<int> foeLoc(-1, -1);
01147   if(itsMTfeatures[0].size() == 0) return foeLoc;
01148 
01149   float maxShift = 16.0;
01150 
01151   uint width  = itsMTfeatures[0].getWidth();
01152   uint height = itsMTfeatures[0].getHeight();
01153 
01154   Image<float> tempH(width, height, ZEROS);
01155   Image<float> tempV(width, height, ZEROS);
01156   std::vector<Point2D<float> > hVals; std::vector<float> hW;
01157   std::vector<Point2D<float> > vVals; std::vector<float> vW;
01158 
01159   // get the horizontal projection of the y-component of each point 
01160   // and the vertical   projection of the x-component of each point 
01161   for(uint d = 0; d < itsNumDirs; d++)
01162     {
01163       float sinAng = sin((2.0*M_PI * d)/itsNumDirs); 
01164       float cosAng = cos((2.0*M_PI * d)/itsNumDirs); 
01165       //LINFO("dir[%d]: c:%10.3f s:%10.3f", d, cosAng, sinAng);
01166 
01167       for(uint i = 0; i < width; i++)
01168         for(uint j = 0; j < height; j++)
01169           {
01170             float val = itsMTfeatures[d].getVal(i,j);
01171             //LINFO("[%3d %3d]: %10.3f", i,j,val);
01172 
01173             float len = itsMToptimalShift[d].getVal(i,j);
01174             float dy = len * sinAng;
01175             float dx = len * cosAng;
01176             
01177             uint lH = uint(width/2  - dy/maxShift*width/2);
01178             uint lV = uint(height/2 - dx/maxShift*height/2);
01179             if(lH == width) lH--; if(lV == height) lV--;
01180             
01181             //                 LINFO("[%3d, %3d]:l %10.3f:(%10.3f %10.3f): tHV; %3d %13d ",
01182             //                       i,j, len, dy, dx, lH, lV);
01183             
01184             float tH = tempH.getVal(lH, j);
01185             float tV = tempV.getVal(i, lV);
01186 
01187             if(val > .20)
01188               {                
01189                 tempH.setVal(lH, j, val + tH);
01190                 tempV.setVal(i, lV, val + tV);
01191 
01192                 hVals.push_back(Point2D<float>(j,dy)); hW.push_back(val);
01193                 vVals.push_back(Point2D<float>(i,dx)); vW.push_back(val);            
01194               }
01195           }
01196     }
01197 
01198   // weighted linear regression to find zero crossings
01199   double sumXY = 0.0; double sumX  = 0.0; double sumY  = 0.0; double sumX2 = 0.0;
01200   double wsum = 0.0;
01201   for(uint i = 0; i < hVals.size(); i++)
01202     {
01203       wsum  += hW[i];
01204 
01205       // sum(xy)
01206       sumXY += hW[i] * (hVals[i].i * hVals[i].j);
01207       
01208       // sum(x)
01209       sumX  += hW[i] * hVals[i].i;
01210       
01211       // sum(y)
01212       sumY  += hW[i] * hVals[i].j;
01213       
01214       // sum(x^2)
01215       sumX2 += hW[i] * hVals[i].i*hVals[i].i;
01216     }
01217 
01218   double n = wsum;
01219   double mH = (n*sumXY - sumX*sumY)/(n*sumX2 - sumX*sumX);
01220   double bH = (sumY - mH*sumX)/n; 
01221   foeLoc.j = -bH/mH * MAX_NEIGHBORHOOD;
01222   LINFO("x = %10.5fx + %10.5f: %10.5f", mH, bH,-bH/mH);
01223 
01224   sumXY = 0.0; sumX  = 0.0; sumY  = 0.0; sumX2 = 0.0;
01225   wsum = 0.0;
01226   for(uint i = 0; i < vVals.size(); i++)
01227     {
01228       wsum  += vW[i];
01229 
01230       // sum(xy)
01231       sumXY += vW[i] * (vVals[i].i * vVals[i].j);
01232       
01233       // sum(x)
01234       sumX  += vW[i] * vVals[i].i;
01235       
01236       // sum(y)
01237       sumY  += vW[i] * vVals[i].j;
01238       
01239       // sum(x^2)
01240       sumX2 += vW[i] * vVals[i].i*vVals[i].i;
01241     }
01242 
01243   n = wsum;
01244   float mV = (n*sumXY - sumX*sumY)/(n*sumX2 - sumX*sumX);
01245   float bV = (sumY - mV*sumX)/n; 
01246   foeLoc.i = -bV/mV * MAX_NEIGHBORHOOD;
01247   LINFO("y = %10.5fx + %10.5f: %10.5f", mV, bV,-bV/mV);
01248 
01249   LINFO("FOE: (%3d %3d)", foeLoc.i, foeLoc.j);
01250 
01251   tempH = zoomXY(tempH, MAX_NEIGHBORHOOD);
01252   tempV = zoomXY(tempV, MAX_NEIGHBORHOOD);
01253   uint dispW = width*MAX_NEIGHBORHOOD;
01254   uint dispH = height*MAX_NEIGHBORHOOD;
01255 
01256   float spH = mH*0         + bH;
01257   float epH = mH*(height-1) + bH;
01258 
01259   Point2D<int> sH((width/2 - spH/8.0*width/2)*MAX_NEIGHBORHOOD,        0);
01260   Point2D<int> eH((width/2 - epH/8.0*width/2)*MAX_NEIGHBORHOOD,  dispH-1);
01261 
01262   float spV = mV*0         + bV;
01263   float epV = mV*(width-1) + bV;
01264   Point2D<int> sV(0,       (height/2 - spV/8.0*height/2)*MAX_NEIGHBORHOOD);
01265   Point2D<int> eV(dispW-1, (height/2 - epV/8.0*height/2)*MAX_NEIGHBORHOOD);
01266 
01267 
01268   float mn, mx; 
01269   getMinMax(tempH, mn,mx);
01270   drawLine(tempH, sH, eH, mx, 1);
01271   drawLine(tempH, Point2D<int>(dispW/2,0),Point2D<int>(dispW/2,dispH-1),mx);
01272   drawLine(tempH, Point2D<int>(0,dispH/2),Point2D<int>(dispW-1,dispH/2),mx);
01273   drawLine(tempH, Point2D<int>(dispW-1,0),Point2D<int>(dispW-1,dispH-1),mx);
01274   drawDisk(tempH, Point2D<int>(dispW/2, foeLoc.j), 4, mx);
01275 
01276   getMinMax(tempV, mn,mx);
01277   drawLine(tempV, sV, eV, mx, 1);
01278   drawLine(tempV, Point2D<int>(dispW/2,0),Point2D<int>(dispW/2,dispH-1),mx);
01279   drawLine(tempV, Point2D<int>(0,dispH/2),Point2D<int>(dispW-1,dispH/2),mx);
01280   drawDisk(tempV, Point2D<int>(foeLoc.i, dispH/2), 4, mx);
01281 
01282   // if(itsWin.is_invalid())
01283   //   itsWin.reset(new XWinManaged
01284   //                (Dims(2*dispW, dispH), dispW+10, 0, "FOE Detector"));
01285   // Image<float> disp(2*dispW, dispH, ZEROS);
01286   // inplacePaste(disp, tempH, Point2D<int>(0,0));
01287   // inplacePaste(disp, tempV, Point2D<int>(dispW,0));
01288   // itsWin->setDims(Dims(2*dispW, dispH));
01289   // itsWin->drawImage(disp,0,0);
01290   // LINFO("Foe Estimation: AVERAGE");
01291   // Raster::waitForKey();
01292 
01293 
01294 
01295   // FoeMap:
01296   //   just draw a gaussian blob centered at the found FOE
01297   itsFoeMap = gaussianBlob<float>(Dims(width, height),
01298                                   Point2D<int>(-bV/mV, -bH/mH), 7, 7);  
01299   inplaceNormalize(itsFoeMap, 0.0F, 1.0F);
01300 
01301 
01302   return foeLoc;
01303 
01304 //   itsWin->setDims(itsFoeMap.getDims());
01305 //   itsWin->drawImage(itsFoeMap,0,0);
01306 //   getMinMax(itsFoeMap,mn,mx);
01307 //   LINFO("Foe Map: %f %f",mn,mx);
01308 //   Raster::waitForKey();
01309 }
01310 
01311 // ######################################################################
01312 Point2D<int> FoeDetector::computeFoeAverage
01313 (rutz::shared_ptr<OpticalFlow> flow)
01314 {
01315   Point2D<int> foeLoc(-1, -1);
01316 
01317   float maxShift = 16.0;
01318 
01319   Dims imSize = flow->getImageDims();
01320   uint width  = imSize.w()/MAX_NEIGHBORHOOD;
01321   uint height = imSize.h()/MAX_NEIGHBORHOOD;
01322 
01323   Image<float> tempH(width, height, ZEROS);
01324   Image<float> tempV(width, height, ZEROS);
01325   std::vector<Point2D<float> > hVals; std::vector<float> hW;
01326   std::vector<Point2D<float> > vVals; std::vector<float> vW;
01327 
01328   std::vector<rutz::shared_ptr<FlowVector> > flowVectors =
01329     flow->getFlowVectors();
01330 
01331   // go through each point
01332   // get the horizontal projection of the y-component of each point 
01333   // and the vertical   projection of the x-component of each point 
01334   for(uint i = 0; i < flowVectors.size(); i++)
01335     {
01336       // get the corresponding points
01337       Point2D<float> pt1 = flowVectors[i]->p1;
01338       //Point2D<float> pt2 = flowVectors[i]->p2;
01339       float angle        = flowVectors[i]->angle;
01340       float length       = flowVectors[i]->mag;
01341       float val          = 1;//flowVectors[i].val; // always 1 in LucasKanade
01342 
01343       float sinAng = sin(angle/180.0*M_PI); 
01344       float cosAng = cos(angle/180.0*M_PI); 
01345       //LINFO("[%f %f] [%f %f] c:%10.3f s:%10.3f", 
01346       //      pt1.i, pt1.j, pt2.i, pt2.j, cosAng, sinAng);
01347 
01348       float dy = length * sinAng; 
01349       if(dy >  maxShift) dy = maxShift;
01350       if(dy < -maxShift) dy = -maxShift;
01351 
01352       float dx = length * cosAng; 
01353       if(dx >  maxShift) dx = maxShift;
01354       if(dx < -maxShift) dx = -maxShift;
01355 
01356       uint lH = uint(width/2  - dy/maxShift*width/2);
01357       uint lV = uint(height/2 - dx/maxShift*height/2);
01358       if(lH == width) lH--; if(lV == height) lV--;
01359       if(lH == width) lH--; if(lV == height) lV--;
01360 
01361       int pi = int(pt1.i/MAX_NEIGHBORHOOD);
01362       int pj = int(pt1.j/MAX_NEIGHBORHOOD);
01363 
01364       //LINFO("{%f %f}[%f %f]  %d %d - %d %d", 
01365       //      length, angle, dy, dx, lH, pj, pi, lV);
01366 
01367       float tH = tempH.getVal(lH, pj);
01368       float tV = tempV.getVal(pi, lV);
01369 
01370       if(val > .20)
01371         {                
01372           tempH.setVal(lH, pj, val + tH);
01373           tempV.setVal(pi, lV, val + tV);
01374           
01375           hVals.push_back(Point2D<float>(pj,dy)); hW.push_back(val);
01376           vVals.push_back(Point2D<float>(pi,dx)); vW.push_back(val);            
01377         }
01378     }
01379 
01380   //===================================================================
01381 
01382   // weighted linear regression to find zero crossings
01383   double sumXY = 0.0; double sumX  = 0.0; 
01384   double sumY  = 0.0; double sumX2 = 0.0;
01385   double wsum  = 0.0;
01386   for(uint i = 0; i < hVals.size(); i++)
01387     {
01388       wsum  += hW[i];
01389 
01390       // sum(xy)
01391       sumXY += hW[i] * (hVals[i].i * hVals[i].j);
01392       
01393       // sum(x)
01394       sumX  += hW[i] * hVals[i].i;
01395       
01396       // sum(y)
01397       sumY  += hW[i] * hVals[i].j;
01398       
01399       // sum(x^2)
01400       sumX2 += hW[i] * hVals[i].i*hVals[i].i;
01401     }
01402 
01403   double n = wsum;
01404   double mH = (n*sumXY - sumX*sumY)/(n*sumX2 - sumX*sumX);
01405   double bH = (sumY - mH*sumX)/n; 
01406   foeLoc.j = -bH/mH * MAX_NEIGHBORHOOD;
01407   LINFO("x = %10.5fx + %10.5f: %10.5f", mH, bH,-bH/mH);
01408 
01409   sumXY = 0.0; sumX  = 0.0; sumY  = 0.0; sumX2 = 0.0;
01410   wsum = 0.0;
01411   for(uint i = 0; i < vVals.size(); i++)
01412     {
01413       wsum  += vW[i];
01414 
01415       // sum(xy)
01416       sumXY += vW[i] * (vVals[i].i * vVals[i].j);
01417       
01418       // sum(x)
01419       sumX  += vW[i] * vVals[i].i;
01420       
01421       // sum(y)
01422       sumY  += vW[i] * vVals[i].j;
01423       
01424       // sum(x^2)
01425       sumX2 += vW[i] * vVals[i].i*vVals[i].i;
01426     }
01427 
01428   n = wsum;
01429   float mV = (n*sumXY - sumX*sumY)/(n*sumX2 - sumX*sumX);
01430   float bV = (sumY - mV*sumX)/n; 
01431   foeLoc.i = -bV/mV * MAX_NEIGHBORHOOD;
01432   LINFO("y = %10.5fx + %10.5f: %10.5f", mV, bV,-bV/mV);
01433 
01434   LINFO("FOE: (%3d %3d)", foeLoc.i, foeLoc.j);
01435 
01436   // FIXXXX: need to compute the quality of the fit
01437   //for(uint i = 0; i < hVals.size(); i++)
01438   //  {
01439   //   // compute the error between the point
01440   //   // and the predicted location by the equation
01441   //   
01442   //  // accumulate 
01443   //  }
01444   // divide total with number of points
01445   // normalize error and flip (1 - err)
01446   
01447   // do the same for the vertical points
01448 
01449   // average the two dimensions
01450 
01451   // display the result
01452   tempH = zoomXY(tempH, MAX_NEIGHBORHOOD);
01453   tempV = zoomXY(tempV, MAX_NEIGHBORHOOD);
01454   uint dispW = width*MAX_NEIGHBORHOOD;
01455   uint dispH = height*MAX_NEIGHBORHOOD;
01456 
01457   float spH = mH*0         + bH;
01458   float epH = mH*(height-1) + bH;
01459 
01460   Point2D<int> sH((width/2 - spH/8.0*width/2)*MAX_NEIGHBORHOOD,        0);
01461   Point2D<int> eH((width/2 - epH/8.0*width/2)*MAX_NEIGHBORHOOD,  dispH-1);
01462 
01463   float spV = mV*0         + bV;
01464   float epV = mV*(width-1) + bV;
01465   Point2D<int> sV(0,       (height/2 - spV/8.0*height/2)*MAX_NEIGHBORHOOD);
01466   Point2D<int> eV(dispW-1, (height/2 - epV/8.0*height/2)*MAX_NEIGHBORHOOD);
01467 
01468 
01469   float mn, mx; 
01470   getMinMax(tempH, mn,mx);
01471   drawLine(tempH, sH, eH, mx, 1);
01472   drawLine(tempH, Point2D<int>(dispW/2,0),Point2D<int>(dispW/2,dispH-1),mx);
01473   drawLine(tempH, Point2D<int>(0,dispH/2),Point2D<int>(dispW-1,dispH/2),mx);
01474   drawLine(tempH, Point2D<int>(dispW-1,0),Point2D<int>(dispW-1,dispH-1),mx);
01475   drawDisk(tempH, Point2D<int>(dispW/2, foeLoc.j), 4, mx);
01476 
01477   getMinMax(tempV, mn,mx);
01478   drawLine(tempV, sV, eV, mx, 1);
01479   drawLine(tempV, Point2D<int>(dispW/2,0),Point2D<int>(dispW/2,dispH-1),mx);
01480   drawLine(tempV, Point2D<int>(0,dispH/2),Point2D<int>(dispW-1,dispH/2),mx);
01481   drawDisk(tempV, Point2D<int>(foeLoc.i, dispH/2), 4, mx);
01482 
01483   // if(itsWin.is_invalid())
01484   //   itsWin.reset(new XWinManaged
01485   //                (Dims(2*dispW, dispH), dispW+10, 0, "FOE Detector: AVG"));
01486   // Image<float> disp(2*dispW, dispH, ZEROS);
01487   // inplacePaste(disp, tempH, Point2D<int>(0,0));
01488   // inplacePaste(disp, tempV, Point2D<int>(dispW,0));
01489   // itsWin->setDims(Dims(2*dispW, dispH));
01490   // itsWin->drawImage(disp,0,0);
01491   //Raster::waitForKey();
01492  
01493   // FoeMap:
01494   //   just draw a gaussian blob centered at the found FOE
01495   itsFoeMap = gaussianBlob<float>(Dims(width, height),
01496                                   Point2D<int>(-bV/mV, -bH/mH), 7, 7);  
01497   inplaceNormalize(itsFoeMap, 0.0F, 1.0F);
01498 
01499   // itsWin->setDims(itsFoeMap.getDims());
01500   // itsWin->drawImage(itsFoeMap,0,0);
01501   // Raster::waitForKey();
01502 
01503   return foeLoc;
01504 }
01505 
01506 // ######################################################################
01507 Layout<byte> FoeDetector::getMTfeaturesDisplay(Image<byte> image)
01508 {
01509   return itsMT->getMTfeaturesDisplay(image);
01510 }
01511 
01512 // ######################################################################
01513 void FoeDetector::print
01514 (Image<float> img, 
01515  uint si, uint ei, uint sj, uint ej, bool stop)
01516 {
01517   for(uint y = sj; y < ej; y++)
01518     {
01519       for(uint x = si; x < ei; x++)
01520         printf("%10.3f ", img.getVal(x,y));
01521       printf("\n");
01522     }
01523   if(stop) Raster::waitForKey();
01524 }
01525 
01526 // ######################################################################
01527 void FoeDetector::display(Image<float> img, std::string info)
01528 {
01529   LINFO("%s", info.c_str());
01530   uint w = img.getWidth();
01531   uint h = img.getHeight();
01532   itsWin->setDims(Dims(w,h));
01533   itsWin->drawImage(img,0,0);
01534   Raster::waitForKey();
01535 }
01536 
01537 // ######################################################################
01538 void FoeDetector::display(ImageSet<float> imgs, std::string info)
01539 {
01540   LINFO("%s", info.c_str());
01541   uint w = imgs[0].getWidth();
01542   uint h = imgs[0].getHeight();
01543   itsWin->setDims(Dims(w,h));
01544 
01545   for(uint i = 0; i < imgs.size(); i++)
01546     {
01547       uint scale = uint(pow(2.0, i));
01548       itsWin->drawImage(zoomXY(imgs[i], scale),0,0);
01549       Raster::waitForKey();
01550     }
01551 }
01552 
01553 // ######################################################################
01554 /* So things look consistent in everyone's emacs... */
01555 /* Local Variables: */
01556 /* indent-tabs-mode: nil */
01557 /* End: */
Generated on Sun May 8 08:41:18 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3