00001
00015 #include <dlrComputerVision/getEuclideanDistance.h>
00016 #include <dlrComputerVision/naiveSnake.h>
00017 #include <dlrComputerVision/sobel.h>
00018 #include <dlrLinearAlgebra/linearAlgebra.h>
00019 #include <dlrNumeric/bilinearInterpolator.h>
00020 #include <dlrNumeric/utilities.h>
00021
00022 namespace dlr {
00023
00024 namespace computerVision {
00025
00026 Snake::
00027 Snake()
00028 : m_alpha(0.1),
00029 m_beta(0.0),
00030 m_betaVector(),
00031 m_contourIterations(10),
00032 m_cornerAdditionThreshold(-2.0),
00033 m_cornerDeletionThreshold(2.0),
00034 m_externalForceGradientX(),
00035 m_externalForceGradientY(),
00036 m_forceBalanceMatrix(),
00037 m_gamma(1.0),
00038 m_isClosed(true),
00039 m_isConverged(false),
00040 m_isFixed(true),
00041 m_kappa(1.0),
00042 m_maxIterations(100),
00043 m_maxSnakeSize(500),
00044 m_maxSpanLength(3.0),
00045 m_minSpanLength(0.5),
00046 m_snake()
00047 {
00048
00049 }
00050
00051
00052 void
00053 Snake::
00054 enableCornerAdditionAndDeletion(bool enableFlag)
00055 {
00056 if(!enableFlag) {
00057
00058
00059 m_cornerAdditionThreshold = -2.0;
00060 m_cornerDeletionThreshold = 2.0;
00061 }
00062 }
00063
00064
00065 std::vector<Vector2D>
00066 Snake::
00067 run()
00068 {
00069
00070 if(m_externalForceGradientX.size() == 0) {
00071 DLR_THROW(StateException, "Snake::run()",
00072 "Interest image has not been set. Please call "
00073 "setInterestImage() before calling run().");
00074 }
00075
00076
00077 if(m_snake.size() == 0) {
00078 size_t rows = m_externalForceGradientX.rows();
00079 size_t columns = m_externalForceGradientX.columns();
00080 m_snake.push_back(Vector2D(columns / 4.0, rows / 4.0));
00081 m_betaVector.push_back(m_beta);
00082 m_snake.push_back(Vector2D(columns / 4.0, 3 * rows / 4.0));
00083 m_betaVector.push_back(m_beta);
00084 m_snake.push_back(Vector2D(3 * columns / 4.0, 3 * rows / 4.0));
00085 m_betaVector.push_back(m_beta);
00086 m_snake.push_back(Vector2D(3 * columns / 4.0, rows / 4.0));
00087 m_betaVector.push_back(m_beta);
00088 }
00089
00090 size_t iterationNumber = 0;
00091 while(!this->isConverged()) {
00092 if(iterationNumber >= m_maxIterations) {
00093 break;
00094 }
00095 this->runOneIteration();
00096 ++iterationNumber;
00097 }
00098 return m_snake;
00099 }
00100
00101
00102 std::vector<Vector2D>
00103 Snake::
00104 runOneIteration()
00105 {
00106
00107 if(m_isConverged) {
00108 return m_snake;
00109 }
00110
00111
00112 if(m_externalForceGradientX.size() == 0) {
00113 DLR_THROW(StateException, "Snake::runOneIteration()",
00114 "Interest image has not been set. Please call "
00115 "setInterestImage() before calling runOneIteration().");
00116 }
00117
00118
00119 if(m_snake.size() == 0) {
00120 DLR_THROW(StateException, "Snake::runOneIteration()",
00121 "Snake has not been initialized. Please call "
00122 "setSeedPoints() before calling runOneIteration().");
00123 }
00124
00125 if(m_snake.size() < 4) {
00126 DLR_THROW(StateException, "Snake::runOneIteration()",
00127 "Snake is too short.");
00128 }
00129
00130 if(m_snake.size() > m_maxSnakeSize) {
00131 DLR_THROW(StateException, "Snake::runOneIteration()",
00132 "Snake is too long.");
00133 }
00134
00135 this->adjustBetas(m_snake, m_betaVector);
00136
00137 std::pair< std::vector<Vector2D>, std::vector<double> >
00138 snake_betaVector = this->resampleSnake();
00139 m_snake = snake_betaVector.first;
00140 m_betaVector = snake_betaVector.second;
00141
00142 for(size_t updateNumber = 0; updateNumber < m_contourIterations;
00143 ++updateNumber) {
00144 std::vector<Vector2D> oldSnake = m_snake;
00145 this->updateSnakePosition(m_snake);
00146 if(this->isConverged(m_snake, oldSnake)) {
00147 m_isConverged = true;
00148 break;
00149 }
00150 }
00151 return m_snake;
00152 }
00153
00154
00155 void
00156 Snake::
00157 setBendingConstant(double beta)
00158 {
00159 m_beta = beta;
00160 for(size_t index0 = 0; index0 < m_betaVector.size(); ++index0) {
00161 if(m_betaVector[index0] != 0.0) {
00162 m_betaVector[index0] = m_beta;
00163 }
00164 }
00165 m_isConverged = false;
00166 }
00167
00168
00169 void
00170 Snake::
00171 setCornerAdditionAngle(double theta)
00172 {
00173 if(theta < 0.0 || theta > 3.15) {
00174 DLR_THROW(ValueException, "Snake::setCornerAdditionAngle",
00175 "Argument theta should be in the range [0.0, Pi].");
00176 }
00177 m_cornerAdditionThreshold = std::cos(theta);
00178 }
00179
00180
00181 void
00182 Snake::
00183 setCornerDeletionAngle(double theta)
00184 {
00185 if(theta < 0.0 || theta > 3.15) {
00186 DLR_THROW(ValueException, "Snake::setCornerDeletionAngle",
00187 "Argument theta should be in the range [0.0, Pi].");
00188 }
00189 m_cornerDeletionThreshold = std::cos(theta);
00190 }
00191
00192
00193 void
00194 Snake::
00195 setInterestImage(const Image<GRAY1>& interestImage)
00196 {
00197 size_t numberOfPassesUsed;
00198 Array2D<double> distanceMatrix =
00199 getEuclideanDistance(interestImage, 10, numberOfPassesUsed);
00200 std::cout << "Euclidean distance calculation took " << numberOfPassesUsed
00201 << " passes." << std::endl;
00202 m_externalForceGradientX =
00203 applySobelX(Image<GRAY_FLOAT64>(distanceMatrix)) / -8.0;
00204 m_externalForceGradientY =
00205 applySobelY(Image<GRAY_FLOAT64>(distanceMatrix)) / -8.0;
00206 m_isConverged = false;
00207 }
00208
00209
00210 void
00211 Snake::
00212 setSeedPoints(const std::vector<Vector2D>& seedPoints)
00213 {
00214 std::vector<bool> cornerFlags(seedPoints.size(), false);
00215 this->setSeedPoints(seedPoints, cornerFlags);
00216 }
00217
00218
00219 void
00220 Snake::
00221 setSeedPoints(const std::vector<Vector2D>& seedPoints,
00222 const std::vector<bool> cornerFlags)
00223 {
00224 m_snake = seedPoints;
00225 m_betaVector = std::vector<double>(cornerFlags.size());
00226 for(size_t index0 = 0; index0 < m_betaVector.size(); ++index0) {
00227 if(cornerFlags[index0]) {
00228 m_betaVector[index0] = 0.0;
00229 } else {
00230 m_betaVector[index0] = m_beta;
00231 }
00232 }
00233 m_isConverged = false;
00234 }
00235
00236
00237 void
00238 Snake::
00239 addResampledSpan(std::vector<Vector2D>& snake,
00240 std::vector<double>& betaVector,
00241 const Vector2D& newPoint,
00242 double newBeta,
00243 bool isLast)
00244 {
00245
00246
00247
00248
00249
00250
00251
00252 size_t snakeSize = snake.size();
00253 double spanLength = 0.0;
00254 while(1) {
00255 if(snakeSize == 0) {
00256 snake.push_back(newPoint);
00257 betaVector.push_back(newBeta);
00258 return;
00259 }
00260
00261
00262
00263 spanLength = magnitude(newPoint - snake[snakeSize - 1]);
00264 if(spanLength >= m_minSpanLength) {
00265 break;
00266 }
00267
00268
00269
00270
00271
00272
00273
00274 if((newBeta <= betaVector[betaVector.size() - 1])
00275 || (isLast && !m_isClosed)) {
00276 snake.pop_back();
00277 betaVector.pop_back();
00278 --snakeSize;
00279 } else {
00280
00281
00282
00283
00284 return;
00285 }
00286 }
00287
00288
00289
00290 if(spanLength >= m_maxSpanLength) {
00291 int numberToAdd = int(spanLength / m_maxSpanLength);
00292 Vector2D previousPoint = snake[snakeSize - 1];
00293 for(int extraPointIndex = 0; extraPointIndex < numberToAdd;
00294 ++extraPointIndex) {
00295 double fraction =
00296 double(extraPointIndex + 1) / double(numberToAdd + 1);
00297 double extraX = (fraction * newPoint.x()
00298 + (1.0 - fraction) * previousPoint.x());
00299 double extraY = (fraction * newPoint.y()
00300 + (1.0 - fraction) * previousPoint.y());
00301 snake.push_back(Vector2D(extraX, extraY));
00302 betaVector.push_back(m_beta);
00303 }
00304 }
00305
00306
00307
00308
00309 if(!(isLast && m_isClosed)) {
00310 snake.push_back(newPoint);
00311 betaVector.push_back(newBeta);
00312 }
00313 }
00314
00315
00316 void
00317 Snake::
00318 adjustBetas(const std::vector<Vector2D>& snake,
00319 std::vector<double>& betaVector)
00320 {
00321 if((m_cornerAdditionThreshold < -1.0)
00322 && (m_cornerDeletionThreshold >= 1.0)) {
00323 return;
00324 }
00325
00326 size_t startIndex = 0;
00327 size_t stopIndex = snake.size();
00328 if(!m_isClosed) {
00329 betaVector[0] = 0.0;
00330 betaVector[snake.size() - 1] = 0.0;
00331 startIndex = 1;
00332 stopIndex = snake.size() - 1;
00333 }
00334
00335 for(size_t snakeIndex = 1; snakeIndex < snake.size() - 1; ++snakeIndex) {
00336 size_t previousIndex =
00337 (snakeIndex == 0) ? snake.size() - 1 : snakeIndex - 1;
00338 size_t nextIndex =
00339 (snakeIndex == snake.size() - 1) ? 0 : snakeIndex + 1;
00340
00341 const Vector2D& currentPoint = snake[snakeIndex];
00342 const Vector2D& previousPoint = snake[previousIndex];
00343 const Vector2D& nextPoint = snake[nextIndex];
00344
00345 Vector2D v0 = currentPoint - previousPoint;
00346 Vector2D v1 = nextPoint - currentPoint;
00347 v0 /= magnitude(v0);
00348 v1 /= magnitude(v1);
00349 double dotProduct = dot(v0, v1);
00350 if(dotProduct <= m_cornerAdditionThreshold) {
00351 betaVector[snakeIndex] = 0.0;
00352 } else if(dotProduct > m_cornerDeletionThreshold) {
00353 betaVector[snakeIndex] = m_beta;
00354 }
00355 }
00356 }
00357
00358
00359 Array2D<double>
00360 Snake::
00361 buildForceBalanceMatrix(size_t numberOfSnakePoints)
00362 {
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449 size_t N = numberOfSnakePoints;
00450
00451
00452
00453 Array2D<double> AMatrix(N, N);
00454 AMatrix = 0.0;
00455
00456
00457 size_t loopStartRow = 0;
00458 size_t loopStopRow = N;
00459 if((!m_isClosed) && m_isFixed) {
00460 AMatrix(0, 0) = 1.0;
00461 AMatrix(N - 1, N - 1) = 1.0;
00462 loopStartRow = 1;
00463 loopStopRow = N - 1;
00464 }
00465
00466
00467 for(size_t rowIndex = loopStartRow; rowIndex < loopStopRow;
00468 ++rowIndex) {
00469
00470 size_t currentJ = rowIndex;
00471 size_t previousJ = (rowIndex == 0) ? (N - 1) : (rowIndex - 1);
00472 size_t prepreviousJ = (previousJ == 0) ? (N - 1) : (previousJ - 1);
00473 size_t nextJ = (rowIndex == N - 1) ? 0 : (rowIndex + 1);
00474 size_t nextnextJ = (nextJ == N - 1) ? 0 : (nextJ + 1);
00475
00476
00477
00478 double currentBeta = m_betaVector[currentJ];
00479 double previousBeta = m_betaVector[previousJ];
00480 double nextBeta = m_betaVector[nextJ];
00481
00482
00483 AMatrix(currentJ, prepreviousJ) = 2.0 * previousBeta;
00484 AMatrix(currentJ, previousJ) = (
00485 -2.0 * m_alpha - 4.0 * currentBeta - 4.0 * previousBeta);
00486 AMatrix(currentJ, currentJ) = (
00487 4.0 * m_alpha + 8.0 * currentBeta + 2.0 * previousBeta
00488 + 2.0 * nextBeta + m_gamma);
00489 AMatrix(currentJ, nextJ) = (
00490 -2.0 * m_alpha - 4.0 * currentBeta - 4.0 * nextBeta);
00491 AMatrix(currentJ, nextnextJ) = 2.0 * nextBeta;
00492 }
00493
00494
00495 return inverse(AMatrix);
00496 }
00497
00498
00499 void
00500 Snake::
00501 buildForceBalanceRHS(const std::vector<Vector2D>& snake,
00502 Array1D<double>& xRHS,
00503 Array1D<double>& yRHS)
00504 {
00505
00506
00507
00508
00509 if(xRHS.size() != snake.size()) {
00510 xRHS.reinit(snake.size());
00511 }
00512 if(yRHS.size() != snake.size()) {
00513 yRHS.reinit(snake.size());
00514 }
00515
00516
00517
00518 size_t N = snake.size();
00519
00520 BilinearInterpolator<double> xInterp(m_externalForceGradientX);
00521 BilinearInterpolator<double> yInterp(m_externalForceGradientY);
00522 if((!m_isClosed) && m_isFixed) {
00523 xRHS[0] = snake[0].x();
00524 yRHS[0] = snake[0].y();
00525 xRHS[N - 1] = snake[N - 1].x();
00526 yRHS[N - 1] = snake[N - 1].y();
00527 } else {
00528 xRHS[0] = (m_gamma * snake[0].x()
00529 + m_kappa * xInterp(snake[0].y(), snake[0].x()));
00530 yRHS[0] = (m_gamma * snake[0].y()
00531 + m_kappa * yInterp(snake[0].y(), snake[0].x()));
00532 xRHS[N - 1] = (
00533 m_gamma * snake[N - 1].x()
00534 + m_kappa * xInterp(snake[N - 1].y(), snake[N - 1].x()));
00535 yRHS[N - 1] = (
00536 m_gamma * snake[N - 1].y()
00537 + m_kappa * yInterp(snake[N - 1].y(), snake[N - 1].x()));
00538 }
00539
00540 for(size_t rowIndex = 1; rowIndex < N - 1; ++rowIndex) {
00541 const Vector2D& snakePoint = snake[rowIndex];
00542 xRHS[rowIndex] = (m_gamma * snakePoint.x()
00543 + m_kappa * xInterp(snakePoint.y(), snakePoint.x()));
00544 yRHS[rowIndex] = (m_gamma * snakePoint.y()
00545 + m_kappa * yInterp(snakePoint.y(), snakePoint.x()));
00546 }
00547 }
00548
00549
00550 bool
00551 Snake::
00552 isConverged(const std::vector<Vector2D>& snake,
00553 const std::vector<Vector2D>& oldSnake)
00554 {
00555 bool convergedFlag = true;
00556 for(size_t pointNumber = 0; pointNumber < snake.size();
00557 ++pointNumber) {
00558 if(magnitude(snake[pointNumber] - oldSnake[pointNumber]) >= 0.5) {
00559 convergedFlag = false;
00560 break;
00561 }
00562 }
00563 return convergedFlag;
00564 }
00565
00566
00567 std::pair< std::vector<Vector2D>, std::vector<double> >
00568 Snake::
00569 resampleSnake()
00570 {
00571 std::vector<Vector2D> newSnake;
00572 std::vector<double> newBetaVector;
00573
00574 size_t snakeIndex = 0;
00575 for(; snakeIndex < m_snake.size() - 1; ++snakeIndex) {
00576 this->addResampledSpan(newSnake, newBetaVector,
00577 m_snake[snakeIndex], m_betaVector[snakeIndex]);
00578 }
00579
00580 if(m_isClosed) {
00581 this->addResampledSpan(newSnake, newBetaVector,
00582 m_snake[snakeIndex], m_betaVector[snakeIndex]);
00583 this->addResampledSpan(newSnake, newBetaVector,
00584 m_snake[0], m_betaVector[0], true);
00585 } else {
00586 this->addResampledSpan(newSnake, newBetaVector, m_snake[snakeIndex],
00587 m_betaVector[snakeIndex], true);
00588 }
00589
00590 return std::make_pair(newSnake, newBetaVector);
00591 }
00592
00593
00594 void
00595 Snake::
00596 updateSnakePosition(std::vector<Vector2D>& snake)
00597 {
00598 if(snake.size() != m_forceBalanceMatrix.size()) {
00599 m_forceBalanceMatrix = this->buildForceBalanceMatrix(snake.size());
00600 }
00601 Array1D<double> xRHS;
00602 Array1D<double> yRHS;
00603 this->buildForceBalanceRHS(snake, xRHS, yRHS);
00604 Array1D<double> newX = matrixMultiply(m_forceBalanceMatrix, xRHS);
00605 Array1D<double> newY = matrixMultiply(m_forceBalanceMatrix, yRHS);
00606 for(size_t pointNum = 0; pointNum < snake.size(); ++pointNum) {
00607 snake[pointNum].setValue(newX[pointNum], newY[pointNum]);
00608 }
00609 }
00610
00611 }
00612
00613 }