00001 /*!@file Gist/ContourBoundaryDetector.C Detect meaningful contours by 00002 weighting longer countour chains more */ 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Christian Siagian <siagian@usc.edu> 00034 // $HeadURL: svn://ilab.usc.edu/trunk/saliency/src/Robots/Beobot2/Navigation/FOE_Navigation/MiddleTemporal.C 00035 // $ $Id: $ 00036 // 00037 ////////////////////////////////////////////////////////////////////////// 00038 00039 #include "Gist/ContourBoundaryDetector.H" 00040 #include "Image/ColorOps.H" 00041 #include "Image/MathOps.H" 00042 #include "Image/DrawOps.H" 00043 #include <cstdio> 00044 00045 // ###################################################################### 00046 // ###################################################################### 00047 00048 // ###################################################################### 00049 ContourBoundaryDetector::ContourBoundaryDetector() 00050 { 00051 itsWin.reset(); 00052 } 00053 00054 // ###################################################################### 00055 ContourBoundaryDetector::~ContourBoundaryDetector() 00056 { } 00057 00058 // ###################################################################### 00059 void ContourBoundaryDetector::computeContourBoundary 00060 (Image<PixRGB<byte> > ima, int r) 00061 { 00062 itsImage = ima; 00063 if(r == -1) 00064 itsRadius = DEFAULT_NEIGHBORHOOD_RAD; 00065 00066 // execute each step 00067 computeVarianceRidgeBoundaryMap(); 00068 computeNmsBoundaryMap(); 00069 computeContourBoundaryEdgels(); 00070 computeContourBoundaryMap(); 00071 } 00072 00073 // ###################################################################### 00074 void ContourBoundaryDetector::computeVarianceRidgeBoundaryMap() 00075 { 00076 // get the CIELab variance of the input image 00077 Image<float> varImg = getLabStandardDeviation(itsImage, itsRadius); 00078 00079 // get the gradient of the variance of the image 00080 std::vector<Image<float> > gradImg = calculateGradient(varImg, itsRadius); 00081 00082 // get the ridges in the gradient of the variance image 00083 Image<float> ridgeImg = getRidge(gradImg, itsRadius); 00084 00085 // substract the gradient from the ridge image 00086 itsBoundaryImage = substractGradImg(ridgeImg, gradImg); 00087 } 00088 00089 // ###################################################################### 00090 void ContourBoundaryDetector::computeNmsBoundaryMap() 00091 { 00092 // run non-max suppression to thin out image 00093 itsBoundaryImageNMS = getNonMaxSuppression(itsBoundaryImage); 00094 //bImgNMS = getNonMaxSuppression(bImgNMS); 00095 } 00096 00097 // ###################################################################### 00098 void ContourBoundaryDetector::computeContourBoundaryEdgels() 00099 { 00100 // compute the contour edgels 00101 itsEdgelBoundaryImage = getContourBoundaryEdgels(); 00102 } 00103 00104 // ###################################################################### 00105 void ContourBoundaryDetector::computeContourBoundaryMap() 00106 { 00107 // connect the contour edgels 00108 connectContourEdgels(); 00109 00110 itsContourBoundaryImage = getContourBoundaryImage(); 00111 00112 //displayContourBoundary(); 00113 } 00114 00115 // ###################################################################### 00116 Image<float> ContourBoundaryDetector::getVarianceRidgeBoundaryMap 00117 (Image<PixRGB<byte> > ima, int r) 00118 { 00119 itsImage = ima; 00120 if(r == -1) 00121 itsRadius = DEFAULT_NEIGHBORHOOD_RAD; 00122 00123 computeVarianceRidgeBoundaryMap(); 00124 00125 return itsBoundaryImage; 00126 } 00127 00128 // ###################################################################### 00129 Image<float> ContourBoundaryDetector::getVarianceRidgeBoundaryMap() 00130 { 00131 return itsBoundaryImage; 00132 } 00133 00134 // ###################################################################### 00135 Image<float> ContourBoundaryDetector::getNmsBoundaryMap 00136 (Image<PixRGB<byte> > ima, int r) 00137 { 00138 // assume that we need to recompute everything 00139 itsImage = ima; 00140 if(r == -1) 00141 itsRadius = DEFAULT_NEIGHBORHOOD_RAD; 00142 00143 computeVarianceRidgeBoundaryMap(); 00144 computeNmsBoundaryMap(); 00145 00146 return itsBoundaryImageNMS; 00147 } 00148 00149 // ###################################################################### 00150 Image<float> ContourBoundaryDetector::getNmsBoundaryMap() 00151 { 00152 return itsBoundaryImageNMS; 00153 } 00154 00155 // ###################################################################### 00156 Image<float> ContourBoundaryDetector::getEdgelBoundaryMap 00157 (Image<PixRGB<byte> > ima, int r) 00158 { 00159 // assume that we need to recompute everything 00160 itsImage = ima; 00161 if(r == -1) 00162 itsRadius = DEFAULT_NEIGHBORHOOD_RAD; 00163 00164 computeVarianceRidgeBoundaryMap(); 00165 computeNmsBoundaryMap(); 00166 computeContourBoundaryEdgels(); 00167 00168 return itsEdgelBoundaryImage; 00169 } 00170 00171 // ###################################################################### 00172 Image<float> ContourBoundaryDetector::getEdgelBoundaryMap() 00173 { 00174 return itsEdgelBoundaryImage; 00175 } 00176 00177 // ###################################################################### 00178 Image<PixRGB<byte> > ContourBoundaryDetector::getContourBoundaryMap 00179 (Image<PixRGB<byte> > ima, int r) 00180 { 00181 // assume that we need to recompute everything 00182 itsImage = ima; 00183 if(r == -1) 00184 itsRadius = DEFAULT_NEIGHBORHOOD_RAD; 00185 00186 computeVarianceRidgeBoundaryMap(); 00187 computeNmsBoundaryMap(); 00188 computeContourBoundaryEdgels(); 00189 computeContourBoundaryMap(); 00190 00191 return itsContourBoundaryImage; 00192 } 00193 00194 // ###################################################################### 00195 Image<PixRGB<byte> > ContourBoundaryDetector::getContourBoundaryMap() 00196 { 00197 return itsContourBoundaryImage; 00198 } 00199 00200 // ###################################################################### 00201 Image<float> ContourBoundaryDetector::getLabStandardDeviation 00202 (Image<PixRGB<byte> > ima, int r) 00203 { 00204 // convert to CIElab color space 00205 Image<float> lImg; 00206 Image<float> aImg; 00207 Image<float> bImg; 00208 getLAB(ima, lImg, aImg, bImg); 00209 00210 // compute squares of each channel 00211 Image<float> lSqImg = lImg * lImg; 00212 Image<float> aSqImg = aImg * aImg; 00213 Image<float> bSqImg = bImg * bImg; 00214 00215 // compute box blur for each channel 00216 Image<float> lBbImg = boxBlur(lImg, r); 00217 Image<float> aBbImg = boxBlur(aImg, r); 00218 Image<float> bBbImg = boxBlur(bImg, r); 00219 00220 Image<float> lSqBbImg = boxBlur(lSqImg, r); 00221 Image<float> aSqBbImg = boxBlur(aSqImg, r); 00222 Image<float> bSqBbImg = boxBlur(bSqImg, r); 00223 00224 //calculate standard deviation of each l a b channel 00225 Image<float> vl = lBbImg*lBbImg - lSqBbImg; 00226 Image<float> va = aBbImg*aBbImg - aSqBbImg; 00227 Image<float> vb = bBbImg*bBbImg - bSqBbImg; 00228 Image<float> varImg = sqCombine(vl, va, vb); 00229 00230 return varImg; 00231 } 00232 00233 // ###################################################################### 00234 Image<float> ContourBoundaryDetector::boxBlur(Image<float> img, int rad) 00235 { 00236 int w = img.getWidth(); 00237 int h = img.getHeight(); 00238 00239 Image<float> ret(w,h,NO_INIT); 00240 00241 int r = rad; 00242 00243 // go through each location 00244 for(int i = 0; i < w; i++) 00245 { 00246 for(int j = 0; j < h; j++) 00247 { 00248 float sum = 0.0; 00249 uint count = 0; 00250 00251 for(int di = -r; di <= r; di++) 00252 { 00253 for(int dj = -r; dj <= r; dj++) 00254 { 00255 int ii = i + di; 00256 int jj = j + dj; 00257 00258 if(ii < 0) ii = -ii; 00259 if(jj < 0) jj = -jj; 00260 if(ii >= w) ii = 2*w - 2 - ii; 00261 if(jj >= h) jj = 2*h - 2 - jj; 00262 00263 //if(img.coordsOk(ii,jj)) 00264 // { 00265 sum += img.getVal(ii,jj); 00266 count++; 00267 // } 00268 } 00269 } 00270 ret.setVal(i,j, sum/count); 00271 } 00272 } 00273 00274 return ret; 00275 } 00276 00277 // ###################################################################### 00278 Image<float> ContourBoundaryDetector::sqCombine 00279 (Image<float> a, Image<float> b, Image<float> c) 00280 { 00281 ASSERT(a.isSameSize(b)); 00282 ASSERT(b.isSameSize(c)); 00283 00284 int w = a.getWidth(); 00285 int h = a.getHeight(); 00286 Image<float> ret(w,h,NO_INIT); 00287 00288 Image<float>::const_iterator aitr = a.begin(), stop = a.end(); 00289 Image<float>::const_iterator bitr = b.begin(); 00290 Image<float>::const_iterator citr = c.begin(); 00291 Image<float>::iterator ritr = ret.beginw(); 00292 00293 while (aitr != stop) 00294 { 00295 float av = *aitr++; 00296 float bv = *bitr++; 00297 float cv = *citr++; 00298 00299 *ritr++ = pow(av*av + bv*bv + cv*cv, 0.5); 00300 } 00301 00302 return ret; 00303 } 00304 00305 // ###################################################################### 00306 std::vector<Image<float> > ContourBoundaryDetector::calculateGradient 00307 (Image<float> varImg, int r) 00308 { 00309 int w = varImg.getWidth(); 00310 int h = varImg.getHeight(); 00311 00312 Image<float> gradImgX(w,h,NO_INIT); 00313 Image<float> gradImgY(w,h,NO_INIT); 00314 00315 // smooth the variance Image using BoxBlur 00316 Image<float> varBbImg = boxBlur(varImg, r); 00317 00318 std::vector<float> dx(NUM_GRADIENT_DIRECTIONS); 00319 std::vector<float> dy(NUM_GRADIENT_DIRECTIONS); 00320 for(uint k = 0; k < NUM_GRADIENT_DIRECTIONS; k++) 00321 { 00322 dx[k] = cos(k*2*M_PI/NUM_GRADIENT_DIRECTIONS); 00323 dy[k] = sin(k*2*M_PI/NUM_GRADIENT_DIRECTIONS); 00324 LDEBUG("%d %f %f", k, dx[k], dy[k]); 00325 } 00326 00327 // go through each location 00328 for(int i = 0; i < w; i++) 00329 { 00330 for(int j = 0; j < h; j++) 00331 { 00332 float sumX = 0.0; 00333 float sumY = 0.0; 00334 for(uint k = 0; k < NUM_GRADIENT_DIRECTIONS; k++) 00335 { 00336 int i1 = i + r*dx[k]; 00337 int j1 = j + r*dy[k]; 00338 00339 int i2 = i - r*dx[k]; 00340 int j2 = j - r*dy[k]; 00341 00342 if(i1 < 0) i1 = -i1; 00343 if(j1 < 0) j1 = -j1; 00344 if(i1 >= w) i1 = 2*w - 2 - i1; 00345 if(j1 >= h) j1 = 2*h - 2 - j1; 00346 00347 if(i2 < 0) i2 = -i2; 00348 if(j2 < 0) j2 = -j2; 00349 if(i2 >= w) i2 = 2*w - 2 - i2; 00350 if(j2 >= h) j2 = 2*h - 2 - j2; 00351 00352 float val = varBbImg.getVal(i1,j1) - varBbImg.getVal(i2,j2); 00353 00354 sumX += val * dx[k]; 00355 sumY += val * dy[k]; 00356 00357 // LINFO("%d %d %d| val: %f, (%f %f) %f %f", 00358 // i,j,k, val, val*dx[k], val*dy[k], sumX, sumY); 00359 } 00360 00361 gradImgX.setVal(i,j, sumX); 00362 gradImgY.setVal(i,j, sumY); 00363 } 00364 } 00365 00366 std::vector<Image<float> > gradImg(2); 00367 gradImg[0] = gradImgX; 00368 gradImg[1] = gradImgY; 00369 00370 return gradImg; 00371 } 00372 00373 // ###################################################################### 00374 Image<float> ContourBoundaryDetector::getRidge 00375 (std::vector<Image<float> > gradImg, int r) 00376 { 00377 Image<float> gradImgX = gradImg[0]; 00378 Image<float> gradImgY = gradImg[1]; 00379 00380 int w = gradImg[0].getWidth(); 00381 int h = gradImg[0].getHeight(); 00382 Image<float> ridgeImg(w,h,NO_INIT); 00383 00384 std::vector<float> dx(NUM_GRADIENT_DIRECTIONS); 00385 std::vector<float> dy(NUM_GRADIENT_DIRECTIONS); 00386 for(uint k = 0; k < NUM_GRADIENT_DIRECTIONS; k++) 00387 { 00388 dx[k] = cos(k*2*M_PI/NUM_GRADIENT_DIRECTIONS); 00389 dy[k] = sin(k*2*M_PI/NUM_GRADIENT_DIRECTIONS); 00390 } 00391 00392 // threshold the gradient image 00393 std::vector<std::vector<Image<float> > > dVin(NUM_GRADIENT_DIRECTIONS); 00394 for(uint k = 0; k < NUM_GRADIENT_DIRECTIONS; k++) 00395 dVin[k] = std::vector<Image<float> >(2); 00396 00397 for(uint k = 0; k < NUM_GRADIENT_DIRECTIONS; k++) 00398 { 00399 dVin[k][0] = Image<float>(w,h,NO_INIT); 00400 dVin[k][1] = Image<float>(w,h,NO_INIT); 00401 00402 for(int i = 0; i < w; i++) 00403 { 00404 for(int j = 0; j < h; j++) 00405 { 00406 float x = 0.0; 00407 float y = 0.0; 00408 00409 int ii = int(i + r*dx[k]); 00410 int jj = int(j + r*dy[k]); 00411 00412 if(ii < 0) ii = -ii; 00413 if(jj < 0) jj = -jj; 00414 if(ii >= w) ii = 2*w - 2 - ii; 00415 if(jj >= h) jj = 2*h - 2 - jj; 00416 00417 float vX = gradImgX.getVal(ii,jj); 00418 float vY = gradImgY.getVal(ii,jj); 00419 if((vX*dx[k] + vY*dy[k]) < 0.0) 00420 { 00421 x = vX; 00422 y = vY; 00423 } 00424 00425 dVin[k][0].setVal(i,j, x); 00426 dVin[k][1].setVal(i,j, y); 00427 00428 // if(i == 100 && j == 100) 00429 // LINFO("[%3d %3d %d %d]: %3d %3d: " 00430 // "%10.5f %10.5f %10.5f %10.5f %10.5f %10.5f", 00431 // i,j,k, r, ii, jj, 00432 // vX, vY, dx[k], dy[k], x, y); 00433 } 00434 } 00435 } 00436 itsDVin = dVin; 00437 00438 std::vector<Image<float> > rDir (NUM_GRADIENT_DIRECTIONS); 00439 Image<float> rDirIndex(w,h,NO_INIT); 00440 00441 // calculate the geometric and arithmetic ridge direction 00442 // and sum the two together 00443 for(uint k = 0; k < NUM_GRADIENT_DIRECTIONS; k++) 00444 { 00445 rDir[k] = Image<float>(w,h,NO_INIT); 00446 00447 for(int i = 0; i < w; i++) 00448 { 00449 for(int j = 0; j < h; j++) 00450 { 00451 float x1 = dVin[k][0].getVal(i,j); 00452 float y1 = dVin[k][1].getVal(i,j); 00453 00454 uint k2 = k + NUM_RIDGE_DIRECTIONS; 00455 if(k >= NUM_RIDGE_DIRECTIONS) 00456 k2 = k - NUM_RIDGE_DIRECTIONS/2; 00457 00458 float x2 = dVin[k2][0].getVal(i,j); 00459 float y2 = dVin[k2][1].getVal(i,j); 00460 00461 float gVal = -(x1*x2 + y1*y2); 00462 if(gVal < 0.0) 00463 gVal = 0.0; 00464 else 00465 gVal = pow(gVal, 0.5); 00466 00467 // float ax = x2 - x1; 00468 // float ay = y2 - y1; 00469 // float aVal = pow(ax*ax + ay*ay, 0.5)/ 2.0; 00470 // rDir[k].setVal(i,j, gVal + aVal); 00471 00472 rDir[k].setVal(i,j, gVal); 00473 00474 // if(i == 100 && j == 100) 00475 // LINFO("[%3d %3d %d %d %d]: " 00476 // "%10.5f %10.5f %10.5f %10.5f || " 00477 // "%10.5f %10.5f = %10.5f", 00478 // i,j,k, k2, r, x1, y1, x2, y2, 00479 // gVal, aVal, gVal+aVal); 00480 } 00481 } 00482 } 00483 00484 itsRidgeDirection = rDir; 00485 00486 std::vector<Image<float> > rDirMax(NUM_RIDGE_DIRECTIONS); 00487 for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00488 rDirMax[k] = Image<float>(w,h,ZEROS); 00489 00490 // get the maximum among the directions 00491 for(int i = 0; i < w; i++) 00492 { 00493 for(int j = 0; j < h; j++) 00494 { 00495 float max = 0.0; int maxk = -1; 00496 for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00497 { 00498 float val = rDir[k].getVal(i,j); 00499 if(val > max) { max = val; maxk = k; } 00500 } 00501 ridgeImg.setVal(i,j, max); 00502 rDirIndex.setVal(i,j, maxk); 00503 if(maxk != -1) 00504 rDirMax[maxk].setVal(i,j, max); 00505 } 00506 } 00507 00508 itsRidgeDirectionIndex = rDirIndex; 00509 itsRidgeDirectionMax = rDirMax; 00510 00511 return ridgeImg; 00512 } 00513 00514 // ###################################################################### 00515 Image<float> ContourBoundaryDetector::substractGradImg 00516 (Image<float> ridgeImg, std::vector<Image<float> > gradImg) 00517 { 00518 Image<float> gradImgX = gradImg[0]; 00519 Image<float> gradImgY = gradImg[1]; 00520 00521 Image<float> res = 00522 //clampedDiff(ridgeImg, sqrt(gradImgX*gradImgX + gradImgY*gradImgY)); 00523 ridgeImg - sqrt(gradImgX*gradImgX + gradImgY*gradImgY); 00524 00525 return res; 00526 } 00527 00528 // ###################################################################### 00529 Image<float> ContourBoundaryDetector::getNonMaxSuppression 00530 (Image<float> bImg) 00531 { 00532 int w = bImg.getWidth(); 00533 int h = bImg.getHeight(); 00534 Image<float> bImgNMS(w,h,ZEROS); 00535 00536 // LINFO("ridge image"); 00537 // int lb = 152, rb = lb+16, tb = 64, bb = tb+16; 00538 // for(int j = tb; j < bb; j++) 00539 // { 00540 // for(int i = lb; i < rb; i++) 00541 // { 00542 // printf("%10.5f ", bImg.getVal(i,j)); 00543 // } 00544 // printf("\n"); 00545 // } 00546 00547 // set up kernel for non-max suppression 00548 int wSize = BOUNDARY_STEP_SIZE+1; 00549 std::vector<std::vector<Point2D<int> > > sCoordsL(NUM_RIDGE_DIRECTIONS); 00550 std::vector<std::vector<Point2D<int> > > sCoordsR(NUM_RIDGE_DIRECTIONS); 00551 std::vector<std::vector<Point2D<int> > > cCoords (NUM_RIDGE_DIRECTIONS); 00552 00553 00554 for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00555 { 00556 cCoords [k] = std::vector<Point2D<int> >(); 00557 sCoordsL[k] = std::vector<Point2D<int> >(); 00558 sCoordsR[k] = std::vector<Point2D<int> >(); 00559 } 00560 00561 // Non-max suppressed values for each direction 00562 itsRidgeDirectionNMS.clear(); 00563 for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00564 itsRidgeDirectionNMS.push_back( Image<float>(w,h,ZEROS)); 00565 00566 for(int i = -wSize/2; i <= wSize/2; i++) 00567 { 00568 for(int j = -wSize/2; j <= wSize/2; j++) 00569 { 00570 if( i == 0) cCoords [0].push_back(Point2D<int>(i,j)); 00571 if( i < 0) sCoordsL[0].push_back(Point2D<int>(i,j)); 00572 if( i > 0) sCoordsR[0].push_back(Point2D<int>(i,j)); 00573 00574 if(-j == i) cCoords [1].push_back(Point2D<int>(i,j)); 00575 if(-j > i) sCoordsL[1].push_back(Point2D<int>(i,j)); 00576 if( i > -j) sCoordsR[1].push_back(Point2D<int>(i,j)); 00577 00578 if( j == 0) cCoords [2].push_back(Point2D<int>(i,j)); 00579 if( j < 0) sCoordsL[2].push_back(Point2D<int>(i,j)); 00580 if( j > 0) sCoordsR[2].push_back(Point2D<int>(i,j)); 00581 00582 if( i == j) cCoords [3].push_back(Point2D<int>(i,j)); 00583 if( j > i) sCoordsL[3].push_back(Point2D<int>(i,j)); 00584 if( i > j) sCoordsR[3].push_back(Point2D<int>(i,j)); 00585 } 00586 } 00587 00588 // go through each point 00589 for(int i = 0; i < w; i++) 00590 { 00591 for(int j = 0; j < h; j++) 00592 { 00593 // get the value 00594 float val = bImg.getVal(i,j); 00595 Point2D<int> cpt(i,j); 00596 00597 // if(i == 160 && j == 70) 00598 // LINFO("%3d %3d: %f", i, j, val); 00599 00600 for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00601 { 00602 float totalC = 0.0; uint ctC = 0; 00603 for(uint cc = 0; cc < cCoords[k].size(); cc++) 00604 { 00605 Point2D<int> pt = cCoords[k][cc] + cpt; 00606 if(bImg.coordsOk(pt)) 00607 { 00608 totalC += bImg.getVal(pt); 00609 ctC++; 00610 } 00611 // if(i == 160 && j == 70 && k == 1) 00612 // { 00613 // LINFO("center: %3d %3d: %f", pt.i, pt.j, bImg.getVal(pt)); 00614 // } 00615 } 00616 00617 float totalL = 0.0; uint ctL = 0; 00618 for(uint cl = 0; cl < sCoordsL[k].size(); cl++) 00619 { 00620 Point2D<int> pt = sCoordsL[k][cl] + cpt; 00621 if(bImg.coordsOk(pt)) 00622 { 00623 totalL += bImg.getVal(pt); ctL++; 00624 } 00625 // if(i == 160 && j == 70 && k == 1) 00626 // { 00627 // LINFO("left: %3d %3d: %f", pt.i, pt.j, bImg.getVal(pt)); 00628 // } 00629 } 00630 00631 float totalR = 0.0; uint ctR = 0; 00632 for(uint cr = 0; cr < sCoordsR[k].size(); cr++) 00633 { 00634 Point2D<int> pt = sCoordsR[k][cr] + cpt; 00635 if(bImg.coordsOk(pt)) 00636 { 00637 totalR += bImg.getVal(pt); ctR++; 00638 } 00639 // if(i == 160 && j == 70 && k == 1) 00640 // { 00641 // LINFO("right: %3d %3d: %f", pt.i, pt.j, bImg.getVal(pt)); 00642 // } 00643 } 00644 00645 if(totalC/ctC > totalR/ctR && totalC/ctC > totalL/ctL 00646 && val > 0.0) 00647 { 00648 bImgNMS.setVal(i,j, val); 00649 00650 // if(i == 160 && j == 70) 00651 // { 00652 // LINFO("K: %d in: %f %f %f | %d %d %d| %f %f %f --> %f", 00653 // k, totalC, totalR, totalL, 00654 // ctC, ctR, ctL, 00655 // totalC/ctC, totalR/ctR, totalL/ctL, 00656 // totalC/ctC*2 - totalR/ctR - totalL/ctL); 00657 // } 00658 itsRidgeDirectionNMS[k].setVal 00659 (i,j, totalC/ctC*2 - totalR/ctR - totalL/ctL); 00660 } 00661 } 00662 } 00663 } 00664 00665 return bImgNMS; 00666 } 00667 00668 // ###################################################################### 00669 Image<float> ContourBoundaryDetector::getContourBoundaryEdgels() 00670 { 00671 // NOTE: FIXXXX: TENSOR-VOTING IS PROBABLY A BETTER IDEA 00672 00673 int w = itsImage.getWidth(); 00674 int h = itsImage.getHeight(); 00675 Image<float> edgelBoundaryImage(w,h,ZEROS); 00676 00677 // LINFO("ridge NMS image"); 00678 // int lb = 152, rb = lb+16, tb = 64, bb = tb+16; 00679 // for(int j = tb; j < bb; j++) 00680 // { 00681 // for(int i = lb; i < rb; i++) 00682 // { 00683 // printf("%10.5f ", itsBoundaryImageNMS.getVal(i,j)); 00684 // } 00685 // printf("\n"); 00686 // } 00687 00688 // LINFO("dir ridge NMS "); 00689 00690 // for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00691 // { 00692 // LINFO("dir: %d", k); 00693 // for(int j = tb; j < bb; j++) 00694 // { 00695 // for(int i = lb; i < rb; i++) 00696 // { 00697 // printf("%10.5f ", itsRidgeDirectionNMS[k].getVal(i,j)); 00698 // } 00699 // printf("\n"); 00700 // } 00701 // } 00702 00703 int step = BOUNDARY_STEP_SIZE; 00704 //int wSize = BOUNDARY_STEP_SIZE+1; 00705 00706 // set up the center and surround opponency locations 00707 std::vector<std::vector<Point2D<int> > > cCoords(NUM_RIDGE_DIRECTIONS); 00708 std::vector<std::vector<Point2D<int> > > sCoords(NUM_RIDGE_DIRECTIONS); 00709 std::vector<float> angles; 00710 00711 for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00712 { 00713 cCoords[k] = std::vector<Point2D<int> >(); 00714 sCoords[k] = std::vector<Point2D<int> >(); 00715 00716 angles.push_back(k*2.0*M_PI/float(NUM_RIDGE_DIRECTIONS)); 00717 } 00718 00719 // fill the center coordinates 00720 for(int i = -step/2; i < step/2; i++) 00721 { 00722 cCoords[0].push_back(Point2D<int>( i, 0)); 00723 cCoords[1].push_back(Point2D<int>( i, i)); 00724 cCoords[2].push_back(Point2D<int>( 0, i)); 00725 cCoords[3].push_back(Point2D<int>( i,-i)); 00726 } 00727 00728 int hstep = step/2; 00729 // fill the surround coordinates (bottom or right) 00730 for(int i = 0; i < hstep; i++) 00731 { 00732 sCoords[0].push_back(Point2D<int>( i+hstep, 0)); 00733 sCoords[0].push_back(Point2D<int>( i+hstep, hstep)); 00734 sCoords[0].push_back(Point2D<int>( i+hstep, -hstep)); 00735 00736 sCoords[1].push_back(Point2D<int>( i+hstep, i+hstep)); 00737 sCoords[1].push_back(Point2D<int>( i+step , i )); 00738 sCoords[1].push_back(Point2D<int>( i ,-i-step )); 00739 00740 sCoords[2].push_back(Point2D<int>( 0, i+hstep)); 00741 sCoords[2].push_back(Point2D<int>( hstep, i+hstep)); 00742 sCoords[2].push_back(Point2D<int>( -hstep, i+hstep)); 00743 00744 sCoords[3].push_back(Point2D<int>( i+hstep,-i-hstep)); 00745 sCoords[3].push_back(Point2D<int>( i ,-i-step )); 00746 sCoords[3].push_back(Point2D<int>( i+step ,-i )); 00747 } 00748 00749 // fill the surround coordinates (top or right) 00750 for(int i = -hstep; i < 0; i++) 00751 { 00752 sCoords[0].push_back(Point2D<int>( i-hstep, 0)); 00753 sCoords[0].push_back(Point2D<int>( i-hstep, hstep)); 00754 sCoords[0].push_back(Point2D<int>( i-hstep, -hstep)); 00755 00756 sCoords[1].push_back(Point2D<int>( i-hstep, i-hstep)); 00757 sCoords[1].push_back(Point2D<int>( i-step , i )); 00758 sCoords[1].push_back(Point2D<int>( i , i-step )); 00759 00760 sCoords[2].push_back(Point2D<int>( 0, i-hstep)); 00761 sCoords[2].push_back(Point2D<int>( hstep, i-hstep)); 00762 sCoords[2].push_back(Point2D<int>( -hstep, i-hstep)); 00763 00764 sCoords[3].push_back(Point2D<int>( i-hstep,-i+hstep)); 00765 sCoords[3].push_back(Point2D<int>( i ,-i+step )); 00766 sCoords[3].push_back(Point2D<int>( i-step ,-i )); 00767 } 00768 00769 // for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00770 // { 00771 // LINFO("c Dir: %d", k); 00772 // for(uint i = 0; i < cCoords[k].size(); i++) 00773 // { 00774 // LINFO("%3d: %5d %5d", i, cCoords[k][i].i, cCoords[k][i].j); 00775 // } 00776 // LINFO("s "); 00777 // for(uint i = 0; i < sCoords[k].size(); i++) 00778 // { 00779 // LINFO("%3d: %5d %5d", i, sCoords[k][i].i, sCoords[k][i].j); 00780 // if(i%3 == 2) LINFO(" "); 00781 // } 00782 // Raster::waitForKey(); 00783 // } 00784 // Raster::waitForKey(); 00785 00786 // reset the edgel storage 00787 // NOTE: we will keep edgel at index 0 empty 00788 int wEdgel = w/step; 00789 int hEdgel = h/step; 00790 itsEdgels = 00791 Image<std::vector<rutz::shared_ptr<Edgel> > >(wEdgel, hEdgel, ZEROS); 00792 00793 // go through each point 00794 // with the specified step size 00795 int wLimit = (w/step)*step; 00796 int hLimit = (h/step)*step; 00797 for(int j = step; j < hLimit; j+= step) 00798 { 00799 for(int i = step; i < wLimit; i+= step) 00800 { 00801 Point2D<int> cpt(i,j); 00802 00803 int maxk = -1; 00804 Point2D<int> maxPt(-1,-1); 00805 float maxVal = -1.0F; 00806 00807 // for each direction 00808 for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++) 00809 { 00810 Point2D<int> maxCKpt(-1,-1); 00811 float maxCKval = 0.0; 00812 00813 // get maximum contour value for the center size 00814 // to make the contour detector phase invariant 00815 for(uint ci = 0; ci < cCoords[k].size(); ci++) 00816 { 00817 Point2D<int> pt = cCoords[k][ci] + cpt; 00818 if(edgelBoundaryImage.coordsOk(pt)) 00819 { 00820 float val = itsRidgeDirectionNMS[k].getVal(pt); 00821 if(maxCKval < val) 00822 { 00823 maxCKval = val; maxCKpt = pt; 00824 } 00825 } 00826 } 00827 00828 float maxSKval = 0.0; 00829 00830 // get the maximum value for the surround 00831 for(uint si = 0; si < sCoords[k].size(); si++) 00832 { 00833 Point2D<int> pt = sCoords[k][si] + cpt; 00834 if(edgelBoundaryImage.coordsOk(pt)) 00835 { 00836 float val = itsRidgeDirectionNMS[k].getVal(pt); 00837 if(maxSKval < val) maxSKval = val; 00838 } 00839 } 00840 00841 // if center > 0 and wins 00842 if(maxCKval > 0.0F && maxCKval > maxSKval) 00843 { 00844 if(maxCKval > maxVal) 00845 { 00846 maxPt = maxCKpt; 00847 maxVal = maxCKval; 00848 maxk = k; 00849 } 00850 00851 uint iEdgel = i/step; 00852 uint jEdgel = j/step; 00853 00854 // put the new edgel in the right position 00855 rutz::shared_ptr<Edgel> 00856 edgel(new Edgel(maxCKpt, angles[k], k, maxCKval)); 00857 00858 std::vector<rutz::shared_ptr<Edgel> > 00859 cEdgelList = itsEdgels.getVal(iEdgel,jEdgel); 00860 00861 uint eNum = cEdgelList.size(); 00862 uint place = 0; 00863 for(uint ce = 0; ce < eNum; ce++) 00864 { 00865 if(cEdgelList[ce]->val < maxCKval) 00866 { 00867 place = ce; ce = eNum; 00868 } 00869 else place = ce+1; 00870 } 00871 //LINFO("place: %d | eNum: %d", place, eNum); 00872 00873 cEdgelList.push_back(edgel); 00874 00875 // for(uint ce = 0; ce < cEdgelList.size(); ce++) 00876 // LINFO("%12.5f %3d", cEdgelList[ce]->val, 00877 // cEdgelList[ce]->angleIndex); 00878 00879 if(place != eNum) 00880 { 00881 // LINFO("move one"); 00882 00883 for(int ce = int(eNum-1); ce >= int(place); ce--) 00884 { 00885 cEdgelList[ce+1] = cEdgelList[ce]; 00886 } 00887 00888 // for(uint ce = 0; ce < cEdgelList.size(); ce++) 00889 // LINFO("%12.5f %3d", cEdgelList[ce]->val, 00890 // cEdgelList[ce]->angleIndex); 00891 00892 cEdgelList[place] = edgel; 00893 00894 // LINFO("place the new one properly"); 00895 00896 // for(uint ce = 0; ce < cEdgelList.size(); ce++) 00897 // LINFO("%12.5f %3d", cEdgelList[ce]->val, 00898 // cEdgelList[ce]->angleIndex); 00899 } 00900 // else LINFO("last place"); 00901 00902 itsEdgels.setVal(iEdgel,jEdgel, cEdgelList); 00903 00904 00905 // LINFO("%d %d: size: %d ", i,j, 00906 // int(itsEdgels.getVal(iEdgel,jEdgel).size())); 00907 00908 // for(uint ce = 0; ce < cEdgelList.size(); ce++) 00909 // LINFO("%12.5f %3d", cEdgelList[ce]->val, 00910 // cEdgelList[ce]->angleIndex); 00911 } 00912 } 00913 00914 // if there is a winner 00915 if(maxk != -1) 00916 { 00917 float borderK = 00918 fmod((maxk+(NUM_RIDGE_DIRECTIONS/2)),NUM_RIDGE_DIRECTIONS); 00919 00920 float dx = cos(borderK * M_PI/4.0) * hstep; 00921 float dy = sin(borderK * M_PI/4.0) * hstep; 00922 00923 Point2D<int> pt = maxPt; 00924 Point2D<int> p1 = pt + Point2D<int>( dx+.5, dy+.5); 00925 Point2D<int> p2 = pt + Point2D<int>(-dx-.5, -dy-.5); 00926 00927 //uint iEdgel = i/step; 00928 //uint jEdgel = j/step; 00929 //if(iEdgel >= 10 && iEdgel <= 25 && jEdgel >= 1 && jEdgel <= 14) 00930 // { 00931 // LINFO("maxk: %d -> %d -> %f %f (%f %f %f %f) |%f %f", 00932 // maxk, (maxk+(NUM_RIDGE_DIRECTIONS/2)), 00933 // (borderK*M_PI)/float(NUM_RIDGE_DIRECTIONS), borderK, 00934 // cos(0), cos(M_PI/4.0), cos(M_PI/2.0), cos(M_PI*.75), 00935 // dx, dy); 00936 00937 00938 00939 //LINFO("%d %d | %d %d | %d %d::::: %d %d %d", 00940 // pt.i, pt.j, p1.i, p1.j, p2.i, p2.j, iEdgel, jEdgel, maxk); 00941 00942 // draw the straightline contour in the image 00943 // for visualization 00944 drawLine(edgelBoundaryImage, p1, p2, 255.0F); 00945 //drawDisk(edgelBoundaryImage, pt, 2, 255.0F); 00946 // } 00947 00948 } 00949 } 00950 } 00951 00952 return edgelBoundaryImage; 00953 } 00954 00955 // ###################################################################### 00956 void ContourBoundaryDetector::connectContourEdgels() 00957 { 00958 int w = itsImage.getWidth(); 00959 int h = itsImage.getHeight(); 00960 00961 // if contour map don't have a contour on the location 00962 // that means the edgel on that location 00963 // do not belong to any contours yet 00964 00965 // reset the contours storage 00966 // NOTE: we will keep edgel at index 0 empty 00967 int step = BOUNDARY_STEP_SIZE; 00968 int wEdgel = w/step; 00969 int hEdgel = h/step; 00970 itsContourMap = 00971 Image<rutz::shared_ptr<Contour> >(wEdgel, hEdgel, ZEROS); 00972 00973 // for each edgel RF location: we move horizontally first 00974 for(int j = 0; j < hEdgel; j++) 00975 { 00976 for(int i = 0; i < wEdgel; i++) 00977 { 00978 std::vector<rutz::shared_ptr<Edgel> > 00979 cEdgelList = itsEdgels.getVal(i,j); 00980 uint eNum = cEdgelList.size(); 00981 00982 // skip if there is no edgel in the location 00983 if(eNum == 0) continue; 00984 00985 // skip if the edgel already belong to a contours 00986 // FIXXX: ADD THE JUNCTION RECOGNITION AS WELL HERE 00987 if(itsContourMap.getVal(i,j).is_valid()) continue; 00988 00989 // start growing: 00990 rutz::shared_ptr<Edgel> edgel = cEdgelList[0]; 00991 rutz::shared_ptr<Contour> contour(new Contour()); 00992 contour->edgels.push_back(edgel); 00993 itsContourBoundaries.push_back(contour); 00994 itsContourMap.setVal(i, j, contour); 00995 00996 //LINFO("edgel: %3d %3d: ang: %d", i,j, edgel->angleIndex); 00997 00998 // while can add an edgel 00999 // to the right (and/or bottom) of the contour 01000 bool isAddingRight = true; 01001 int ci = i, cj = j; 01002 //LINFO("start: ci: %3d cj: %3d: cd: %3d sign: %d", 01003 // ci, cj, edgel->angleIndex, 1); 01004 while(isAddingRight) 01005 { 01006 isAddingRight = addEdgelToContour(ci, cj, edgel, contour, 1); 01007 if(isAddingRight) 01008 itsContourMap.setVal(ci, cj, contour); 01009 //LINFO("[%3d %3d]: %3d %3d %3d", ci,cj, 01010 // edgel->pt.i, edgel->pt.j, edgel->angleIndex); 01011 } 01012 01013 //LINFO("Final Size: %d", 01014 // int(itsContourMap.getVal(i,j)->edgels.size())); 01015 //Raster::waitForKey(); 01016 01017 // // while can add an edgel 01018 // // to the left (and/or top) 01019 // bool isAddingLeft = true; 01020 // while(isAddingLeft) 01021 // { 01022 // isAddingLeft = addEdgelToContour(i,j, edgel, contour, -1); 01023 // } 01024 01025 01026 01027 } 01028 } 01029 01030 // go through the contours 01031 01032 // only link with negative edgels if the two contours are 01033 01034 // report the contours from the most salient first 01035 // using equation that includes both length of contours and magnitude 01036 01037 // better graphical drawing 01038 // --> probably need the stdev gradient info 01039 01040 01041 } 01042 01043 // ###################################################################### 01044 bool ContourBoundaryDetector::addEdgelToContour 01045 (int &ci, int &cj, 01046 rutz::shared_ptr<Edgel> &edgel, 01047 rutz::shared_ptr<Contour> &contour, 01048 int sign) 01049 { 01050 bool isAdding = false; 01051 01052 // direction of the current edgel 01053 int cd = edgel->angleIndex; 01054 01055 // get the neighborhood of edgel 01056 // depending on the direction 01057 int ni1, nj1, ni2, nj2, ni3, nj3; 01058 getEdgelDirectionNeighborhood 01059 (ci, cj, cd, sign, ni1, nj1, ni2, nj2, ni3, nj3); 01060 01061 // LINFO("ni1: %3d nj1: %3d", ni1, nj1); 01062 // LINFO("ni2: %3d nj2: %3d", ni2, nj2); 01063 // LINFO("ni3: %3d nj3: %3d", ni3, nj3); 01064 01065 // only examine if coordinate is within image 01066 // and there is an edgel in the coordinate 01067 uint bEdge1 = 0; uint bEdge2 = 0; uint bEdge3 = 0; 01068 01069 if(itsEdgels.coordsOk(ni1, nj1) && 01070 itsEdgels.getVal(ni1, nj1).size() != 0) bEdge1 = 1; 01071 01072 if(itsEdgels.coordsOk(ni2, nj2) && 01073 itsEdgels.getVal(ni2, nj2).size() != 0) bEdge2 = 1; 01074 01075 if(itsEdgels.coordsOk(ni3, nj3) && 01076 itsEdgels.getVal(ni3, nj3).size() != 0) bEdge3 = 1; 01077 01078 uint totalEdgels = bEdge1+bEdge2+bEdge3; 01079 01080 //LINFO(" have %d neighbors: " 01081 // "(%d %d %d) (%d %d %d) (%d %d %d)", totalEdgels, 01082 // ni1, nj1, bEdge1, ni2, nj2, bEdge2, ni3, nj3, bEdge3); 01083 01084 // if no edgels to continue, we're done 01085 if(totalEdgels == 0) 01086 return false; 01087 01088 // if there is only 1 edgel to continue to 01089 else if(totalEdgels == 1) 01090 { 01091 // get the next edgel 01092 int ni = -1; int nj = -1; int nd = -1; 01093 if(bEdge1 == 1) { ni = ni1; nj = nj1; } 01094 else if(bEdge2 == 1) { ni = ni2; nj = nj2; } 01095 else if(bEdge3 == 1) { ni = ni3; nj = nj3; } 01096 01097 std::vector<rutz::shared_ptr<Edgel> > 01098 nEdgelList = itsEdgels.getVal(ni,nj); 01099 rutz::shared_ptr<Edgel> nEdgel = nEdgelList[0]; 01100 01101 // get the next direction 01102 nd = nEdgel->angleIndex; 01103 01104 // if unclaimed and legal direction to add 01105 bool legalDirection = 01106 isLegalDirection(ci, cj, cd, sign, ni, nj, nd); 01107 if(itsContourMap.getVal(ni,nj).is_invalid() && 01108 legalDirection) 01109 { 01110 // add and advance the end of contour 01111 isAdding = true; 01112 contour->edgels.push_back(nEdgel); 01113 edgel = nEdgel; 01114 ci = ni; cj = nj; cd = edgel->angleIndex; 01115 01116 //LINFO(" --> [1] adding ci: %3d cj: %3d: cd: %3d sign: %d", 01117 // ci, cj, cd, sign); 01118 01119 // FIXXX: maybe there is a second go round 01120 // to connect even more 01121 // so may not want to return right away 01122 return isAdding; 01123 } 01124 else return false; 01125 } 01126 else if(totalEdgels == 2) 01127 { 01128 // FIXXX: have to check for junction example: 01129 // where there are more than 1 valid direction 01130 01131 // priority on what?? 01132 int n1i = -1, n1j = -1, n2i = -1, n2j = -1; 01133 if(bEdge1 == 0) 01134 { 01135 n1i = ni2; n1j = nj2; n2i = ni3; n2j = nj3; 01136 } 01137 else if(bEdge2 == 0) 01138 { 01139 n1i = ni1; n1j = nj1; n2i = ni3; n2j = nj3; 01140 01141 } 01142 else if(bEdge3 == 0) 01143 { 01144 n1i = ni1; n1j = nj1; n2i = ni2; n2j = nj2; 01145 } 01146 01147 // get the first next edgel 01148 std::vector<rutz::shared_ptr<Edgel> > 01149 nEdgelList1 = itsEdgels.getVal(n1i,n1j); 01150 rutz::shared_ptr<Edgel> nEdgel1 = nEdgelList1[0]; 01151 int n1d = nEdgel1->angleIndex; 01152 01153 // get the second next edgel 01154 std::vector<rutz::shared_ptr<Edgel> > 01155 nEdgelList2 = itsEdgels.getVal(n2i,n2j); 01156 rutz::shared_ptr<Edgel> nEdgel2 = nEdgelList2[0]; 01157 int n2d = nEdgel2->angleIndex; 01158 01159 // just deal with the diagonal first 01160 if(cd == 1) 01161 { 01162 if(n1d == 1 && n2d == 1) 01163 { 01164 // if edgel unclaimed 01165 if(itsContourMap.getVal(n2i,n2j).is_invalid()) 01166 { 01167 // add and advance the end of contour 01168 isAdding = true; 01169 contour->edgels.push_back(nEdgel2); 01170 edgel = nEdgel2; 01171 ci = n2i; cj = n2j; 01172 01173 //LINFO(" --> [2a] adding ci: %3d cj: %3d: cd: %3d sign: %d", 01174 // ci, cj, cd, sign); 01175 // FIXXX: maybe there is a second go round 01176 // to connect even more 01177 // so may not want to return right away 01178 return isAdding; 01179 } 01180 else 01181 { 01182 //LINFO(" CLAIMED"); 01183 return false; 01184 } 01185 } 01186 else 01187 { 01188 //LINFO(" --> [21a]NOT 1(%3d %3d %3d) 2(%3d %3d %3d)", 01189 // n1i, n1j, n1d, n2i, n2j, n2d); 01190 return false; 01191 } 01192 } 01193 if(cd == 3) 01194 { 01195 if(n1d == 3 && n2d == 3) 01196 { 01197 // if edgel unclaimed 01198 if(itsContourMap.getVal(n2i,n2j).is_invalid()) 01199 { 01200 // add and advance the end of contour 01201 isAdding = true; 01202 contour->edgels.push_back(nEdgel2); 01203 edgel = nEdgel2; 01204 ci = n2i; cj = n2j; cd = edgel->angleIndex; 01205 01206 //LINFO(" --> [23b] adding ci: %3d cj: %3d: cd: %3d sign: %d", 01207 // ci, cj, cd, sign); 01208 01209 // FIXXX: maybe there is a second go round 01210 // to connect even more 01211 // so may not want to return right away 01212 return isAdding; 01213 } 01214 else 01215 { 01216 //LINFO(" CLAIMED"); 01217 return false; 01218 } 01219 } 01220 else 01221 { 01222 //LINFO(" --> [2b]NOT 1(%3d %3d %3d) 2(%3d %3d %3d)", 01223 // n1i, n1j, n1d, n2i, n2j, n2d); 01224 return false; 01225 } 01226 } 01227 01228 if(cd == 0) 01229 { 01230 if(n1d == 3 && n2d == 3) 01231 { 01232 // if edgel unclaimed 01233 if(itsContourMap.getVal(n1i,n1j).is_invalid()) 01234 { 01235 // add and advance the end of contour 01236 isAdding = true; 01237 contour->edgels.push_back(nEdgel1); 01238 edgel = nEdgel1; 01239 ci = n1i; cj = n1j; cd = edgel->angleIndex; 01240 01241 //LINFO(" --> [20a] adding ci: %3d cj: %3d: cd: %3d sign: %d", 01242 // ci, cj, cd, sign); 01243 01244 // FIXXX: maybe there is a second go round 01245 // to connect even more 01246 // so may not want to return right away 01247 return isAdding; 01248 } 01249 } 01250 if(n1d == 1 && n2d == 1) 01251 { 01252 // if edgel unclaimed 01253 if(itsContourMap.getVal(n1i,n1j).is_invalid()) 01254 { 01255 // add and advance the end of contour 01256 isAdding = true; 01257 contour->edgels.push_back(nEdgel1); 01258 edgel = nEdgel1; 01259 ci = n1i; cj = n1j; cd = edgel->angleIndex; 01260 01261 //LINFO(" --> [20b] adding ci: %3d cj: %3d: cd: %3d sign: %d", 01262 // ci, cj, cd, sign); 01263 // FIXXX: maybe there is a second go round 01264 // to connect even more 01265 // so may not want to return right away 01266 return isAdding; 01267 } 01268 } 01269 01270 // FIXXX: pick the better of the two 01271 //LINFO(" Branch:0: junction"); 01272 //LINFO(" --> [2_0]NOT 1(%3d %3d %3d) 2(%3d %3d %3d)", 01273 // n1i, n1j, n1d, n2i, n2j, n2d); 01274 01275 return false; 01276 } 01277 01278 if(cd == 2) 01279 { 01280 if(n1d == 3 && n2d == 3) 01281 { 01282 // if edgel unclaimed 01283 if(itsContourMap.getVal(n1i,n1j).is_invalid()) 01284 { 01285 // add and advance the end of contour 01286 isAdding = true; 01287 contour->edgels.push_back(nEdgel1); 01288 edgel = nEdgel1; 01289 ci = n1i; cj = n1j; cd = edgel->angleIndex; 01290 01291 //LINFO(" --> [20a] adding ci: %3d cj: %3d: cd: %3d sign: %d", 01292 // ci, cj, cd, sign); 01293 01294 // FIXXX: maybe there is a second go round 01295 // to connect even more 01296 // so may not want to return right away 01297 return isAdding; 01298 } 01299 } 01300 if(n1d == 1 && n2d == 1) 01301 { 01302 // if edgel unclaimed 01303 if(itsContourMap.getVal(n1i,n1j).is_invalid()) 01304 { 01305 // add and advance the end of contour 01306 isAdding = true; 01307 contour->edgels.push_back(nEdgel1); 01308 edgel = nEdgel1; 01309 ci = n1i; cj = n1j; cd = edgel->angleIndex; 01310 01311 //LINFO(" --> [20b] adding ci: %3d cj: %3d: cd: %3d sign: %d", 01312 // ci, cj, cd, sign); 01313 // FIXXX: maybe there is a second go round 01314 // to connect even more 01315 // so may not want to return right away 01316 return isAdding; 01317 } 01318 } 01319 01320 // FIXXX: pick the better of the two 01321 //LINFO(" Branch:2: junction"); 01322 //LINFO(" --> [2_2]NOT 1(%3d %3d %3d) 2(%3d %3d %3d)", 01323 // n1i, n1j, n1d, n2i, n2j, n2d); 01324 return false; 01325 } 01326 01327 01328 // if we can still include with the rules 01329 // (in this order): 01330 // 1 space lookahead 01331 // 2 space lookahead 01332 // [3 space lookahead]??? 01333 // note 3 space lookahead means 01334 // a gap of 16 pixels to interpolate 01335 // --> probably an absolute max 01336 01337 // in any of these lookahead 01338 // only consider positive NMS difference 01339 01340 01341 // if there is branching: also split (it's a junction) 01342 01343 // if it's a junction 01344 // stop a contour on the end 01345 // split if it hit the middle 01346 01347 // split if the NMS difference is very not similar 01348 // remember merging is easy - but spliting is hard 01349 01350 // add the edgel to the contour 01351 01352 //LINFO("ci: %3d cj: %3d", ci, cj); 01353 01354 01355 // for now return false 01356 return false; 01357 } 01358 else if(totalEdgels == 3) 01359 { 01360 // more than likely it's too ambiguous to decide what to do 01361 LINFO("3 edgels. May want to take a look at it"); 01362 //Raster::waitForKey(); 01363 return false; 01364 } 01365 01366 01367 // if(itsEdgels.coordsOk(ni1, nj1) && 01368 // itsEdgels.getVal(ni1, nj1).size() != 0 && 01369 // itsContourMap.getVal(ni1,nj1).is_invalid()) 01370 // { 01371 // std::vector<rutz::shared_ptr<Edgel> > 01372 // nEdgelList1 = itsEdgels.getVal(ni1,nj1); 01373 01374 // rutz::shared_ptr<Edgel> nEdgel1 = nEdgelList1[0]; 01375 01376 // //LINFO("e1: %d %d %d %f", nEdgel1->pt.i, nEdgel1->pt.j, nEdgel1->angleIndex, nEdgel1->val); 01377 01378 // // if the next edgel can be added to the contour 01379 // if(nEdgel1->angleIndex == aind) 01380 // { 01381 // // add and advance the end of contour 01382 // isAdding = true; 01383 // contour->edgels.push_back(nEdgel1); 01384 // edgel = nEdgel1; 01385 01386 // //LINFO("in1"); 01387 01388 // ci = ni1; cj = nj1; 01389 // } 01390 // } 01391 // else if(itsEdgels.coordsOk(ni2, nj2) && 01392 // itsEdgels.getVal(ni2, nj2).size() != 0 && 01393 // itsContourMap.getVal(ni2,nj2).is_invalid()) 01394 // { 01395 // std::vector<rutz::shared_ptr<Edgel> > 01396 // nEdgelList2 = itsEdgels.getVal(ni2,nj2); 01397 01398 // rutz::shared_ptr<Edgel> nEdgel2 = nEdgelList2[0]; 01399 01400 // //LINFO("e2: %d %d %d %f", nEdgel2->pt.i, nEdgel2->pt.j, nEdgel2->angleIndex, nEdgel2->val); 01401 01402 // // if the next edgel can be added to the contour 01403 // if(nEdgel2->angleIndex == aind) 01404 // { 01405 // // add and advance the end of contour 01406 // isAdding = true; 01407 // contour->edgels.push_back(nEdgel2); 01408 // edgel = nEdgel2; 01409 01410 // //LINFO("in2"); 01411 01412 // ci = ni2; cj = nj2; 01413 // } 01414 // } 01415 // else if(itsEdgels.coordsOk(ni3, nj3) && 01416 // itsEdgels.getVal(ni3, nj3).size() != 0 && 01417 // itsContourMap.getVal(ni3,nj3).is_invalid()) 01418 // { 01419 // std::vector<rutz::shared_ptr<Edgel> > 01420 // nEdgelList3 = itsEdgels.getVal(ni3,nj3); 01421 01422 // rutz::shared_ptr<Edgel> nEdgel3 = nEdgelList3[0]; 01423 01424 // //LINFO("e3: %d %d %d %f", nEdgel3->pt.i, nEdgel3->pt.j, nEdgel3->angleIndex, nEdgel3->val); 01425 01426 // // if the next edgel can be added to the contour 01427 // if(nEdgel3->angleIndex == aind) 01428 // { 01429 // // add and advance the end of contour 01430 // isAdding = true; 01431 // contour->edgels.push_back(nEdgel3); 01432 // edgel = nEdgel3; 01433 01434 // //LINFO("in3"); 01435 01436 // ci = ni3; cj = nj3; 01437 // } 01438 // } 01439 01440 01441 01442 01443 return isAdding; 01444 } 01445 01446 // ###################################################################### 01447 void ContourBoundaryDetector::getEdgelDirectionNeighborhood 01448 (int ci, int cj, int dir, int sign, 01449 int &ni1, int &nj1, int &ni2, int &nj2, int &ni3, int &nj3) 01450 { 01451 // 0 degrees: | boundary 01452 if(dir == 0) 01453 { 01454 ni1 = ci; nj1 = cj + sign; 01455 ni2 = ci-sign; nj2 = cj + sign; 01456 ni3 = ci+sign; nj3 = cj + sign; 01457 } 01458 // 45 degrees: / boundary 01459 else if(dir == 1) 01460 { 01461 ni1 = ci-sign; nj1 = cj + sign; 01462 ni2 = ci-sign; nj2 = cj; 01463 ni3 = ci; nj3 = cj + sign; 01464 } 01465 01466 // 90 degrees: -- boundary 01467 else if(dir == 2) 01468 { 01469 ni1 = ci+sign; nj1 = cj; 01470 ni2 = ci+sign; nj2 = cj - sign; 01471 ni3 = ci+sign; nj3 = cj + sign; 01472 } 01473 01474 // 135 degrees: \ boundary 01475 else if(dir == 3) 01476 { 01477 ni1 = ci+sign; nj1 = cj + sign; 01478 ni2 = ci+sign; nj2 = cj; 01479 ni3 = ci; nj3 = cj + sign; 01480 } 01481 } 01482 01483 // ###################################################################### 01484 bool ContourBoundaryDetector::isLegalDirection 01485 (int ci, int cj, int cd, int sign, int ni, int nj, int nd) 01486 { 01487 // important that this operation symmetric!!! 01488 // that is: (a isLegalDirection b) == (b isLegalDirection a) 01489 01490 int di = sign*(ni - ci); 01491 int dj = sign*(nj - cj); 01492 01493 // LINFO("%d %d %d - %d - %d %d %d ", ci, cj, cd, sign, ni, nj, nd); 01494 // LINFO("di dj: %d %d", di, dj); 01495 01496 // 0 degrees: | boundary: 01497 if(cd == 0) 01498 { 01499 // loc n1 01500 if (di == 0 && dj == 1) 01501 { 01502 return (nd != 2); 01503 } 01504 // loc n2 01505 else if(di == -1 && dj == 1) 01506 { 01507 return (nd != 3); 01508 } 01509 // loc n3 01510 else if(di == 1 && dj == 1) 01511 { 01512 return (nd != 1); 01513 } 01514 else 01515 { 01516 LFATAL("invalid dir. loc:" 01517 "%d %d %d - %d - %d %d %d " 01518 "di dj: %d %d", 01519 ci, cj, cd, sign, ni, nj, nd, di, dj); 01520 return false; 01521 } 01522 } 01523 // 45 degrees: / boundary 01524 else if(cd == 1) 01525 { 01526 // loc n1 01527 if (di == -1 && dj == 1) 01528 { 01529 return (nd != 3); 01530 } 01531 // loc n2 01532 else if(di == -1 && dj == 0) 01533 { 01534 return (nd != 0); 01535 } 01536 // loc n3 01537 else if(di == 0 && dj == 1) 01538 { 01539 return (nd != 2); 01540 } 01541 else 01542 { 01543 LFATAL("invalid dir. loc:" 01544 "%d %d %d - %d - %d %d %d " 01545 "di dj: %d %d", 01546 ci, cj, cd, sign, ni, nj, nd, di, dj); 01547 return false; 01548 } 01549 } 01550 01551 // 90 degrees: -- boundary 01552 else if(cd == 2) 01553 { 01554 // loc n1 01555 if (di == 1 && dj == 0) 01556 { 01557 return (nd != 0); 01558 } 01559 // loc n2 01560 else if(di == 1 && dj == -1) 01561 { 01562 return (nd != 3); 01563 } 01564 // loc n3 01565 else if(di == 1 && dj == 1) 01566 { 01567 return (nd != 1); 01568 } 01569 else 01570 { 01571 LFATAL("invalid dir. loc:" 01572 "%d %d %d - %d - %d %d %d " 01573 "di dj: %d %d", 01574 ci, cj, cd, sign, ni, nj, nd, di, dj); 01575 return false; 01576 } 01577 } 01578 01579 // 135 degrees: \ boundary 01580 else if(cd == 3) 01581 { 01582 // loc n1 01583 if (di == 1 && dj == 1) 01584 { 01585 return (nd != 1); 01586 } 01587 // loc n2 01588 else if(di == 1 && dj == 0) 01589 { 01590 return (nd != 0); 01591 } 01592 // loc n3 01593 else if(di == 0 && dj == 1) 01594 { 01595 return (nd != 2); 01596 } 01597 else 01598 { 01599 LFATAL("invalid dir. loc:" 01600 "%d %d %d - %d - %d %d %d " 01601 "di dj: %d %d", 01602 ci, cj, cd, sign, ni, nj, nd, di, dj); 01603 return false; 01604 } 01605 } 01606 else LFATAL("invalid direction"); 01607 return false; 01608 } 01609 01610 // ###################################################################### 01611 Image<PixRGB<byte> > ContourBoundaryDetector::getContourBoundaryImage() 01612 { 01613 int w = itsImage.getWidth(); 01614 int h = itsImage.getHeight(); 01615 01616 Image<PixRGB<byte> > image = itsImage; 01617 float mVal = 32; 01618 float bVal = 255 - mVal; 01619 Image<byte> dImaR, dImaG, dImaB; 01620 getComponents(image, dImaR, dImaG, dImaB); 01621 inplaceNormalize(dImaR, byte(0), byte(mVal)); 01622 inplaceNormalize(dImaG, byte(0), byte(mVal)); 01623 inplaceNormalize(dImaB, byte(0), byte(mVal)); 01624 Image<PixRGB<byte> > dIma = makeRGB(dImaR,dImaG,dImaB); 01625 01626 int hstep = BOUNDARY_STEP_SIZE/2; 01627 01628 // clean image 01629 Image<byte> dCBmapR(w,h,ZEROS); 01630 Image<byte> dCBmapG(w,h,ZEROS); 01631 Image<byte> dCBmapB(w,h,ZEROS); 01632 01633 for(uint i = 0; i < itsContourBoundaries.size(); i++) 01634 { 01635 rutz::shared_ptr<Contour> contour = itsContourBoundaries[i]; 01636 01637 byte rcVal = bVal*rand()/(RAND_MAX+1.0); 01638 byte gcVal = bVal*rand()/(RAND_MAX+1.0); 01639 byte bcVal = bVal*rand()/(RAND_MAX+1.0); 01640 01641 if(contour->edgels.size() < 5) continue; 01642 01643 for(uint j = 0; j < contour->edgels.size(); j++) 01644 { 01645 rutz::shared_ptr<Edgel> edgel = contour->edgels[j]; 01646 01647 uint aind = edgel->angleIndex; 01648 float baind = 01649 fmod((aind+(NUM_RIDGE_DIRECTIONS/2)),NUM_RIDGE_DIRECTIONS); 01650 01651 float dx = cos(baind * M_PI/4.0) * hstep; 01652 float dy = sin(baind * M_PI/4.0) * hstep; 01653 01654 Point2D<int> pt = edgel->pt; 01655 Point2D<int> p1 = pt + Point2D<int>( dx+.5, dy+.5); 01656 Point2D<int> p2 = pt + Point2D<int>(-dx-.5, -dy-.5); 01657 01658 //uint iEdgel = i/step; 01659 //uint jEdgel = j/step; 01660 //if(iEdgel >= 10 && iEdgel <= 25 && jEdgel >= 1 && jEdgel <= 14) 01661 // { 01662 // LINFO("maxk: %d -> %d -> %f %f (%f %f %f %f) |%f %f", 01663 // maxk, (maxk+(NUM_RIDGE_DIRECTIONS/2)), 01664 // (borderK*M_PI)/float(NUM_RIDGE_DIRECTIONS), borderK, 01665 // cos(0), cos(M_PI/4.0), cos(M_PI/2.0), cos(M_PI*.75), 01666 // dx, dy); 01667 01668 //LINFO("%d %d | %d %d | %d %d::::: %d %d %d", 01669 // pt.i, pt.j, p1.i, p1.j, p2.i, p2.j, iEdgel, jEdgel, maxk); 01670 01671 // draw the edgel 01672 01673 // draw the straightline contour in the image 01674 // for visualization 01675 drawLine(dCBmapR, p1, p2, byte(rcVal)); 01676 drawLine(dCBmapG, p1, p2, byte(gcVal)); 01677 drawLine(dCBmapB, p1, p2, byte(bcVal)); 01678 //drawDisk(itsContourBoundaryImage, pt, 2, 255.0F); 01679 } 01680 } 01681 01682 Image<PixRGB<byte> > dCBmap = makeRGB(dCBmapR,dCBmapG,dCBmapB); 01683 return Image<PixRGB<byte> >(dIma+dCBmap); 01684 } 01685 01686 // ###################################################################### 01687 void ContourBoundaryDetector::displayContourBoundary() 01688 { 01689 int w = itsImage.getWidth(); 01690 int h = itsImage.getHeight(); 01691 01692 if(itsWin.is_invalid()) 01693 itsWin.reset(new XWinManaged(Dims(w,h), w+10, 0, "Contours")); 01694 else itsWin->setDims(Dims(w,h)); 01695 01696 // display the contour 01697 itsWin->drawImage(getContourBoundaryImage(),0,0); 01698 //Raster::waitForKey(); 01699 } 01700 01701 // ###################################################################### 01702 void ContourBoundaryDetector::displayGradImage 01703 (std::vector<Image<float> > gradImg) 01704 { 01705 Image<float> gradImgX = gradImg[0]; 01706 Image<float> gradImgY = gradImg[1]; 01707 01708 uint w = gradImgX.getWidth(); 01709 uint h = gradImgX.getHeight(); 01710 01711 Image<PixRGB<byte> > dGradImg(w, h, NO_INIT); 01712 01713 float mn, mx; 01714 Image<float> temp = gradImgX*gradImgX + gradImgY*gradImgY; 01715 getMinMax(temp, mn,mx); 01716 float max = pow(mx,.5); 01717 01718 for(uint i = 0; i < w; i++) 01719 { 01720 for(uint j = 0; j < h; j++) 01721 { 01722 float x = gradImgX.getVal(i,j); 01723 float y = gradImgY.getVal(i,j); 01724 01725 float dir = atan2(y,x) * 180.0 / M_PI; 01726 if(dir < 0.0) dir = 360.0 + dir; 01727 float mag = pow(x*x + y*y, 0.5); 01728 01729 // PixHSV<byte> hsvp(byte(dir/360.0 * 255.0), 01730 // 100, 01731 // byte(max/max* 255.0)); 01732 01733 PixHSV<byte> hsvp(180,100,50); 01734 01735 //PixRGB<byte> rgbp(hsvp); 01736 byte m = byte(mag/max * 255.0); 01737 //byte d = byte(dir/360.0 * 255.0); 01738 PixRGB<byte> rgbp(m, m, m); 01739 dGradImg.setVal(i,j, rgbp); 01740 01741 // PixRGB<byte> a(0,255,255); 01742 // PixHSV<byte> b(a); 01743 // PixRGB<byte> c(b); 01744 // LINFO("%d %d %d ==> %d %d %d ==> %d %d %d", 01745 // a.red(), a.green(), a.blue(), 01746 // b.H(), b.S(), b.V(), 01747 // c.red(), c.green(), c.blue()); 01748 // Raster::waitForKey(); 01749 01750 // LINFO("%f %f || %f %f (%f): %d %d %d ==> %d %d %d", 01751 // x,y, dir, mag, mag/max* 255.0, 01752 // hsvp.H(), hsvp.S(), hsvp.V(), 01753 // rgbp.red(), rgbp.green(), rgbp.blue()); 01754 } 01755 } 01756 01757 itsWin->drawImage(dGradImg, 0,0); 01758 Raster::waitForKey(); 01759 } 01760 01761 01762 // ###################################################################### 01763 /* So things look consistent in everyone's emacs... */ 01764 /* Local Variables: */ 01765 /* indent-tabs-mode: nil */ 01766 /* End: */