00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
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
00067 computeVarianceRidgeBoundaryMap();
00068 computeNmsBoundaryMap();
00069 computeContourBoundaryEdgels();
00070 computeContourBoundaryMap();
00071 }
00072
00073
00074 void ContourBoundaryDetector::computeVarianceRidgeBoundaryMap()
00075 {
00076
00077 Image<float> varImg = getLabStandardDeviation(itsImage, itsRadius);
00078
00079
00080 std::vector<Image<float> > gradImg = calculateGradient(varImg, itsRadius);
00081
00082
00083 Image<float> ridgeImg = getRidge(gradImg, itsRadius);
00084
00085
00086 itsBoundaryImage = substractGradImg(ridgeImg, gradImg);
00087 }
00088
00089
00090 void ContourBoundaryDetector::computeNmsBoundaryMap()
00091 {
00092
00093 itsBoundaryImageNMS = getNonMaxSuppression(itsBoundaryImage);
00094
00095 }
00096
00097
00098 void ContourBoundaryDetector::computeContourBoundaryEdgels()
00099 {
00100
00101 itsEdgelBoundaryImage = getContourBoundaryEdgels();
00102 }
00103
00104
00105 void ContourBoundaryDetector::computeContourBoundaryMap()
00106 {
00107
00108 connectContourEdgels();
00109
00110 itsContourBoundaryImage = getContourBoundaryImage();
00111
00112
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
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
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
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
00205 Image<float> lImg;
00206 Image<float> aImg;
00207 Image<float> bImg;
00208 getLAB(ima, lImg, aImg, bImg);
00209
00210
00211 Image<float> lSqImg = lImg * lImg;
00212 Image<float> aSqImg = aImg * aImg;
00213 Image<float> bSqImg = bImg * bImg;
00214
00215
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
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
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
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
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
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
00358
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
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
00429
00430
00431
00432
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
00442
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
00468
00469
00470
00471
00472 rDir[k].setVal(i,j, gVal);
00473
00474
00475
00476
00477
00478
00479
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
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
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
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
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
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
00589 for(int i = 0; i < w; i++)
00590 {
00591 for(int j = 0; j < h; j++)
00592 {
00593
00594 float val = bImg.getVal(i,j);
00595 Point2D<int> cpt(i,j);
00596
00597
00598
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
00612
00613
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
00626
00627
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
00640
00641
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
00651
00652
00653
00654
00655
00656
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
00672
00673 int w = itsImage.getWidth();
00674 int h = itsImage.getHeight();
00675 Image<float> edgelBoundaryImage(w,h,ZEROS);
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703 int step = BOUNDARY_STEP_SIZE;
00704
00705
00706
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
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
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
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
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
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
00794
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
00808 for(uint k = 0; k < NUM_RIDGE_DIRECTIONS; k++)
00809 {
00810 Point2D<int> maxCKpt(-1,-1);
00811 float maxCKval = 0.0;
00812
00813
00814
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
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
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
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
00872
00873 cEdgelList.push_back(edgel);
00874
00875
00876
00877
00878
00879 if(place != eNum)
00880 {
00881
00882
00883 for(int ce = int(eNum-1); ce >= int(place); ce--)
00884 {
00885 cEdgelList[ce+1] = cEdgelList[ce];
00886 }
00887
00888
00889
00890
00891
00892 cEdgelList[place] = edgel;
00893
00894
00895
00896
00897
00898
00899 }
00900
00901
00902 itsEdgels.setVal(iEdgel,jEdgel, cEdgelList);
00903
00904
00905
00906
00907
00908
00909
00910
00911 }
00912 }
00913
00914
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
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944 drawLine(edgelBoundaryImage, p1, p2, 255.0F);
00945
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
00962
00963
00964
00965
00966
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
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
00983 if(eNum == 0) continue;
00984
00985
00986
00987 if(itsContourMap.getVal(i,j).is_valid()) continue;
00988
00989
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
00997
00998
00999
01000 bool isAddingRight = true;
01001 int ci = i, cj = j;
01002
01003
01004 while(isAddingRight)
01005 {
01006 isAddingRight = addEdgelToContour(ci, cj, edgel, contour, 1);
01007 if(isAddingRight)
01008 itsContourMap.setVal(ci, cj, contour);
01009
01010
01011 }
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027 }
01028 }
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038
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
01053 int cd = edgel->angleIndex;
01054
01055
01056
01057 int ni1, nj1, ni2, nj2, ni3, nj3;
01058 getEdgelDirectionNeighborhood
01059 (ci, cj, cd, sign, ni1, nj1, ni2, nj2, ni3, nj3);
01060
01061
01062
01063
01064
01065
01066
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
01081
01082
01083
01084
01085 if(totalEdgels == 0)
01086 return false;
01087
01088
01089 else if(totalEdgels == 1)
01090 {
01091
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
01102 nd = nEdgel->angleIndex;
01103
01104
01105 bool legalDirection =
01106 isLegalDirection(ci, cj, cd, sign, ni, nj, nd);
01107 if(itsContourMap.getVal(ni,nj).is_invalid() &&
01108 legalDirection)
01109 {
01110
01111 isAdding = true;
01112 contour->edgels.push_back(nEdgel);
01113 edgel = nEdgel;
01114 ci = ni; cj = nj; cd = edgel->angleIndex;
01115
01116
01117
01118
01119
01120
01121
01122 return isAdding;
01123 }
01124 else return false;
01125 }
01126 else if(totalEdgels == 2)
01127 {
01128
01129
01130
01131
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
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
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
01160 if(cd == 1)
01161 {
01162 if(n1d == 1 && n2d == 1)
01163 {
01164
01165 if(itsContourMap.getVal(n2i,n2j).is_invalid())
01166 {
01167
01168 isAdding = true;
01169 contour->edgels.push_back(nEdgel2);
01170 edgel = nEdgel2;
01171 ci = n2i; cj = n2j;
01172
01173
01174
01175
01176
01177
01178 return isAdding;
01179 }
01180 else
01181 {
01182
01183 return false;
01184 }
01185 }
01186 else
01187 {
01188
01189
01190 return false;
01191 }
01192 }
01193 if(cd == 3)
01194 {
01195 if(n1d == 3 && n2d == 3)
01196 {
01197
01198 if(itsContourMap.getVal(n2i,n2j).is_invalid())
01199 {
01200
01201 isAdding = true;
01202 contour->edgels.push_back(nEdgel2);
01203 edgel = nEdgel2;
01204 ci = n2i; cj = n2j; cd = edgel->angleIndex;
01205
01206
01207
01208
01209
01210
01211
01212 return isAdding;
01213 }
01214 else
01215 {
01216
01217 return false;
01218 }
01219 }
01220 else
01221 {
01222
01223
01224 return false;
01225 }
01226 }
01227
01228 if(cd == 0)
01229 {
01230 if(n1d == 3 && n2d == 3)
01231 {
01232
01233 if(itsContourMap.getVal(n1i,n1j).is_invalid())
01234 {
01235
01236 isAdding = true;
01237 contour->edgels.push_back(nEdgel1);
01238 edgel = nEdgel1;
01239 ci = n1i; cj = n1j; cd = edgel->angleIndex;
01240
01241
01242
01243
01244
01245
01246
01247 return isAdding;
01248 }
01249 }
01250 if(n1d == 1 && n2d == 1)
01251 {
01252
01253 if(itsContourMap.getVal(n1i,n1j).is_invalid())
01254 {
01255
01256 isAdding = true;
01257 contour->edgels.push_back(nEdgel1);
01258 edgel = nEdgel1;
01259 ci = n1i; cj = n1j; cd = edgel->angleIndex;
01260
01261
01262
01263
01264
01265
01266 return isAdding;
01267 }
01268 }
01269
01270
01271
01272
01273
01274
01275 return false;
01276 }
01277
01278 if(cd == 2)
01279 {
01280 if(n1d == 3 && n2d == 3)
01281 {
01282
01283 if(itsContourMap.getVal(n1i,n1j).is_invalid())
01284 {
01285
01286 isAdding = true;
01287 contour->edgels.push_back(nEdgel1);
01288 edgel = nEdgel1;
01289 ci = n1i; cj = n1j; cd = edgel->angleIndex;
01290
01291
01292
01293
01294
01295
01296
01297 return isAdding;
01298 }
01299 }
01300 if(n1d == 1 && n2d == 1)
01301 {
01302
01303 if(itsContourMap.getVal(n1i,n1j).is_invalid())
01304 {
01305
01306 isAdding = true;
01307 contour->edgels.push_back(nEdgel1);
01308 edgel = nEdgel1;
01309 ci = n1i; cj = n1j; cd = edgel->angleIndex;
01310
01311
01312
01313
01314
01315
01316 return isAdding;
01317 }
01318 }
01319
01320
01321
01322
01323
01324 return false;
01325 }
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356 return false;
01357 }
01358 else if(totalEdgels == 3)
01359 {
01360
01361 LINFO("3 edgels. May want to take a look at it");
01362
01363 return false;
01364 }
01365
01366
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
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
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
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
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
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
01488
01489
01490 int di = sign*(ni - ci);
01491 int dj = sign*(nj - cj);
01492
01493
01494
01495
01496
01497 if(cd == 0)
01498 {
01499
01500 if (di == 0 && dj == 1)
01501 {
01502 return (nd != 2);
01503 }
01504
01505 else if(di == -1 && dj == 1)
01506 {
01507 return (nd != 3);
01508 }
01509
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
01524 else if(cd == 1)
01525 {
01526
01527 if (di == -1 && dj == 1)
01528 {
01529 return (nd != 3);
01530 }
01531
01532 else if(di == -1 && dj == 0)
01533 {
01534 return (nd != 0);
01535 }
01536
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
01552 else if(cd == 2)
01553 {
01554
01555 if (di == 1 && dj == 0)
01556 {
01557 return (nd != 0);
01558 }
01559
01560 else if(di == 1 && dj == -1)
01561 {
01562 return (nd != 3);
01563 }
01564
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
01580 else if(cd == 3)
01581 {
01582
01583 if (di == 1 && dj == 1)
01584 {
01585 return (nd != 1);
01586 }
01587
01588 else if(di == 1 && dj == 0)
01589 {
01590 return (nd != 0);
01591 }
01592
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
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
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675 drawLine(dCBmapR, p1, p2, byte(rcVal));
01676 drawLine(dCBmapG, p1, p2, byte(gcVal));
01677 drawLine(dCBmapB, p1, p2, byte(bcVal));
01678
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
01697 itsWin->drawImage(getContourBoundaryImage(),0,0);
01698
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
01730
01731
01732
01733 PixHSV<byte> hsvp(180,100,50);
01734
01735
01736 byte m = byte(mag/max * 255.0);
01737
01738 PixRGB<byte> rgbp(m, m, m);
01739 dGradImg.setVal(i,j, rgbp);
01740
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754 }
01755 }
01756
01757 itsWin->drawImage(dGradImg, 0,0);
01758 Raster::waitForKey();
01759 }
01760
01761
01762
01763
01764
01765
01766