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: */