1 /*M/////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. 4 // 5 // By downloading, copying, installing or using the software you agree to this license. 6 // If you do not agree to this license, do not download, install, 7 // copy or use the software. 8 // 9 // 10 // Intel License Agreement 11 // For Open Source Computer Vision Library 12 // 13 // Copyright (C) 2000, Intel Corporation, all rights reserved. 14 // Third party copyrights are property of their respective owners. 15 // 16 // Redistribution and use in source and binary forms, with or without modification, 17 // are permitted provided that the following conditions are met: 18 // 19 // * Redistribution's of source code must retain the above copyright notice, 20 // this list of conditions and the following disclaimer. 21 // 22 // * Redistribution's in binary form must reproduce the above copyright notice, 23 // this list of conditions and the following disclaimer in the documentation 24 // and/or other materials provided with the distribution. 25 // 26 // * The name of Intel Corporation may not be used to endorse or promote products 27 // derived from this software without specific prior written permission. 28 // 29 // This software is provided by the copyright holders and contributors "as is" and 30 // any express or implied warranties, including, but not limited to, the implied 31 // warranties of merchantability and fitness for a particular purpose are disclaimed. 32 // In no event shall the Intel Corporation or contributors be liable for any direct, 33 // indirect, incidental, special, exemplary, or consequential damages 34 // (including, but not limited to, procurement of substitute goods or services; 35 // loss of use, data, or profits; or business interruption) however caused 36 // and on any theory of liability, whether in contract, strict liability, 37 // or tort (including negligence or otherwise) arising in any way out of 38 // the use of this software, even if advised of the possibility of such damage. 39 // 40 //M*/ 41 42 #include "precomp.hpp" 43 #include <functional> 44 #include <limits> 45 46 using namespace cv; 47 48 // common 49 50 namespace 51 { 52 double toRad(double a) 53 { 54 return a * CV_PI / 180.0; 55 } 56 57 bool notNull(float v) 58 { 59 return fabs(v) > std::numeric_limits<float>::epsilon(); 60 } 61 62 class GeneralizedHoughBase 63 { 64 protected: 65 GeneralizedHoughBase(); 66 virtual ~GeneralizedHoughBase() {} 67 68 void setTemplateImpl(InputArray templ, Point templCenter); 69 void setTemplateImpl(InputArray edges, InputArray dx, InputArray dy, Point templCenter); 70 71 void detectImpl(InputArray image, OutputArray positions, OutputArray votes); 72 void detectImpl(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes); 73 74 virtual void processTempl() = 0; 75 virtual void processImage() = 0; 76 77 int cannyLowThresh_; 78 int cannyHighThresh_; 79 double minDist_; 80 double dp_; 81 82 Size templSize_; 83 Point templCenter_; 84 Mat templEdges_; 85 Mat templDx_; 86 Mat templDy_; 87 88 Size imageSize_; 89 Mat imageEdges_; 90 Mat imageDx_; 91 Mat imageDy_; 92 93 std::vector<Vec4f> posOutBuf_; 94 std::vector<Vec3i> voteOutBuf_; 95 96 private: 97 void calcEdges(InputArray src, Mat& edges, Mat& dx, Mat& dy); 98 void filterMinDist(); 99 void convertTo(OutputArray positions, OutputArray votes); 100 }; 101 102 GeneralizedHoughBase::GeneralizedHoughBase() 103 { 104 cannyLowThresh_ = 50; 105 cannyHighThresh_ = 100; 106 minDist_ = 1.0; 107 dp_ = 1.0; 108 } 109 110 void GeneralizedHoughBase::calcEdges(InputArray _src, Mat& edges, Mat& dx, Mat& dy) 111 { 112 Mat src = _src.getMat(); 113 114 CV_Assert( src.type() == CV_8UC1 ); 115 CV_Assert( cannyLowThresh_ > 0 && cannyLowThresh_ < cannyHighThresh_ ); 116 117 Canny(src, edges, cannyLowThresh_, cannyHighThresh_); 118 Sobel(src, dx, CV_32F, 1, 0); 119 Sobel(src, dy, CV_32F, 0, 1); 120 } 121 122 void GeneralizedHoughBase::setTemplateImpl(InputArray templ, Point templCenter) 123 { 124 calcEdges(templ, templEdges_, templDx_, templDy_); 125 126 if (templCenter == Point(-1, -1)) 127 templCenter = Point(templEdges_.cols / 2, templEdges_.rows / 2); 128 129 templSize_ = templEdges_.size(); 130 templCenter_ = templCenter; 131 132 processTempl(); 133 } 134 135 void GeneralizedHoughBase::setTemplateImpl(InputArray edges, InputArray dx, InputArray dy, Point templCenter) 136 { 137 edges.getMat().copyTo(templEdges_); 138 dx.getMat().copyTo(templDx_); 139 dy.getMat().copyTo(templDy_); 140 141 CV_Assert( templEdges_.type() == CV_8UC1 ); 142 CV_Assert( templDx_.type() == CV_32FC1 && templDx_.size() == templEdges_.size() ); 143 CV_Assert( templDy_.type() == templDx_.type() && templDy_.size() == templEdges_.size() ); 144 145 if (templCenter == Point(-1, -1)) 146 templCenter = Point(templEdges_.cols / 2, templEdges_.rows / 2); 147 148 templSize_ = templEdges_.size(); 149 templCenter_ = templCenter; 150 151 processTempl(); 152 } 153 154 void GeneralizedHoughBase::detectImpl(InputArray image, OutputArray positions, OutputArray votes) 155 { 156 calcEdges(image, imageEdges_, imageDx_, imageDy_); 157 158 imageSize_ = imageEdges_.size(); 159 160 posOutBuf_.clear(); 161 voteOutBuf_.clear(); 162 163 processImage(); 164 165 if (!posOutBuf_.empty()) 166 { 167 if (minDist_ > 1) 168 filterMinDist(); 169 convertTo(positions, votes); 170 } 171 else 172 { 173 positions.release(); 174 if (votes.needed()) 175 votes.release(); 176 } 177 } 178 179 void GeneralizedHoughBase::detectImpl(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) 180 { 181 edges.getMat().copyTo(imageEdges_); 182 dx.getMat().copyTo(imageDx_); 183 dy.getMat().copyTo(imageDy_); 184 185 CV_Assert( imageEdges_.type() == CV_8UC1 ); 186 CV_Assert( imageDx_.type() == CV_32FC1 && imageDx_.size() == imageEdges_.size() ); 187 CV_Assert( imageDy_.type() == imageDx_.type() && imageDy_.size() == imageEdges_.size() ); 188 189 imageSize_ = imageEdges_.size(); 190 191 posOutBuf_.clear(); 192 voteOutBuf_.clear(); 193 194 processImage(); 195 196 if (!posOutBuf_.empty()) 197 { 198 if (minDist_ > 1) 199 filterMinDist(); 200 convertTo(positions, votes); 201 } 202 else 203 { 204 positions.release(); 205 if (votes.needed()) 206 votes.release(); 207 } 208 } 209 210 class Vec3iGreaterThanIdx 211 { 212 public: 213 Vec3iGreaterThanIdx( const Vec3i* _arr ) : arr(_arr) {} 214 bool operator()(size_t a, size_t b) const { return arr[a][0] > arr[b][0]; } 215 const Vec3i* arr; 216 }; 217 218 void GeneralizedHoughBase::filterMinDist() 219 { 220 size_t oldSize = posOutBuf_.size(); 221 const bool hasVotes = !voteOutBuf_.empty(); 222 223 CV_Assert( !hasVotes || voteOutBuf_.size() == oldSize ); 224 225 std::vector<Vec4f> oldPosBuf(posOutBuf_); 226 std::vector<Vec3i> oldVoteBuf(voteOutBuf_); 227 228 std::vector<size_t> indexies(oldSize); 229 for (size_t i = 0; i < oldSize; ++i) 230 indexies[i] = i; 231 std::sort(indexies.begin(), indexies.end(), Vec3iGreaterThanIdx(&oldVoteBuf[0])); 232 233 posOutBuf_.clear(); 234 voteOutBuf_.clear(); 235 236 const int cellSize = cvRound(minDist_); 237 const int gridWidth = (imageSize_.width + cellSize - 1) / cellSize; 238 const int gridHeight = (imageSize_.height + cellSize - 1) / cellSize; 239 240 std::vector< std::vector<Point2f> > grid(gridWidth * gridHeight); 241 242 const double minDist2 = minDist_ * minDist_; 243 244 for (size_t i = 0; i < oldSize; ++i) 245 { 246 const size_t ind = indexies[i]; 247 248 Point2f p(oldPosBuf[ind][0], oldPosBuf[ind][1]); 249 250 bool good = true; 251 252 const int xCell = static_cast<int>(p.x / cellSize); 253 const int yCell = static_cast<int>(p.y / cellSize); 254 255 int x1 = xCell - 1; 256 int y1 = yCell - 1; 257 int x2 = xCell + 1; 258 int y2 = yCell + 1; 259 260 // boundary check 261 x1 = std::max(0, x1); 262 y1 = std::max(0, y1); 263 x2 = std::min(gridWidth - 1, x2); 264 y2 = std::min(gridHeight - 1, y2); 265 266 for (int yy = y1; yy <= y2; ++yy) 267 { 268 for (int xx = x1; xx <= x2; ++xx) 269 { 270 const std::vector<Point2f>& m = grid[yy * gridWidth + xx]; 271 272 for(size_t j = 0; j < m.size(); ++j) 273 { 274 const Point2f d = p - m[j]; 275 276 if (d.ddot(d) < minDist2) 277 { 278 good = false; 279 goto break_out; 280 } 281 } 282 } 283 } 284 285 break_out: 286 287 if(good) 288 { 289 grid[yCell * gridWidth + xCell].push_back(p); 290 291 posOutBuf_.push_back(oldPosBuf[ind]); 292 if (hasVotes) 293 voteOutBuf_.push_back(oldVoteBuf[ind]); 294 } 295 } 296 } 297 298 void GeneralizedHoughBase::convertTo(OutputArray _positions, OutputArray _votes) 299 { 300 const int total = static_cast<int>(posOutBuf_.size()); 301 const bool hasVotes = !voteOutBuf_.empty(); 302 303 CV_Assert( !hasVotes || voteOutBuf_.size() == posOutBuf_.size() ); 304 305 _positions.create(1, total, CV_32FC4); 306 Mat positions = _positions.getMat(); 307 Mat(1, total, CV_32FC4, &posOutBuf_[0]).copyTo(positions); 308 309 if (_votes.needed()) 310 { 311 if (!hasVotes) 312 { 313 _votes.release(); 314 } 315 else 316 { 317 _votes.create(1, total, CV_32SC3); 318 Mat votes = _votes.getMat(); 319 Mat(1, total, CV_32SC3, &voteOutBuf_[0]).copyTo(votes); 320 } 321 } 322 } 323 } 324 325 // GeneralizedHoughBallard 326 327 namespace 328 { 329 class GeneralizedHoughBallardImpl : public GeneralizedHoughBallard, private GeneralizedHoughBase 330 { 331 public: 332 GeneralizedHoughBallardImpl(); 333 334 void setTemplate(InputArray templ, Point templCenter) { setTemplateImpl(templ, templCenter); } 335 void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { setTemplateImpl(edges, dx, dy, templCenter); } 336 337 void detect(InputArray image, OutputArray positions, OutputArray votes) { detectImpl(image, positions, votes); } 338 void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { detectImpl(edges, dx, dy, positions, votes); } 339 340 void setCannyLowThresh(int cannyLowThresh) { cannyLowThresh_ = cannyLowThresh; } 341 int getCannyLowThresh() const { return cannyLowThresh_; } 342 343 void setCannyHighThresh(int cannyHighThresh) { cannyHighThresh_ = cannyHighThresh; } 344 int getCannyHighThresh() const { return cannyHighThresh_; } 345 346 void setMinDist(double minDist) { minDist_ = minDist; } 347 double getMinDist() const { return minDist_; } 348 349 void setDp(double dp) { dp_ = dp; } 350 double getDp() const { return dp_; } 351 352 void setMaxBufferSize(int) { } 353 int getMaxBufferSize() const { return 0; } 354 355 void setLevels(int levels) { levels_ = levels; } 356 int getLevels() const { return levels_; } 357 358 void setVotesThreshold(int votesThreshold) { votesThreshold_ = votesThreshold; } 359 int getVotesThreshold() const { return votesThreshold_; } 360 361 private: 362 void processTempl(); 363 void processImage(); 364 365 void calcHist(); 366 void findPosInHist(); 367 368 int levels_; 369 int votesThreshold_; 370 371 std::vector< std::vector<Point> > r_table_; 372 Mat hist_; 373 }; 374 375 GeneralizedHoughBallardImpl::GeneralizedHoughBallardImpl() 376 { 377 levels_ = 360; 378 votesThreshold_ = 100; 379 } 380 381 void GeneralizedHoughBallardImpl::processTempl() 382 { 383 CV_Assert( levels_ > 0 ); 384 385 const double thetaScale = levels_ / 360.0; 386 387 r_table_.resize(levels_ + 1); 388 std::for_each(r_table_.begin(), r_table_.end(), std::mem_fun_ref(&std::vector<Point>::clear)); 389 390 for (int y = 0; y < templSize_.height; ++y) 391 { 392 const uchar* edgesRow = templEdges_.ptr(y); 393 const float* dxRow = templDx_.ptr<float>(y); 394 const float* dyRow = templDy_.ptr<float>(y); 395 396 for (int x = 0; x < templSize_.width; ++x) 397 { 398 const Point p(x, y); 399 400 if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) 401 { 402 const float theta = fastAtan2(dyRow[x], dxRow[x]); 403 const int n = cvRound(theta * thetaScale); 404 r_table_[n].push_back(p - templCenter_); 405 } 406 } 407 } 408 } 409 410 void GeneralizedHoughBallardImpl::processImage() 411 { 412 calcHist(); 413 findPosInHist(); 414 } 415 416 void GeneralizedHoughBallardImpl::calcHist() 417 { 418 CV_Assert( imageEdges_.type() == CV_8UC1 ); 419 CV_Assert( imageDx_.type() == CV_32FC1 && imageDx_.size() == imageSize_); 420 CV_Assert( imageDy_.type() == imageDx_.type() && imageDy_.size() == imageSize_); 421 CV_Assert( levels_ > 0 && r_table_.size() == static_cast<size_t>(levels_ + 1) ); 422 CV_Assert( dp_ > 0.0 ); 423 424 const double thetaScale = levels_ / 360.0; 425 const double idp = 1.0 / dp_; 426 427 hist_.create(cvCeil(imageSize_.height * idp) + 2, cvCeil(imageSize_.width * idp) + 2, CV_32SC1); 428 hist_.setTo(0); 429 430 const int rows = hist_.rows - 2; 431 const int cols = hist_.cols - 2; 432 433 for (int y = 0; y < imageSize_.height; ++y) 434 { 435 const uchar* edgesRow = imageEdges_.ptr(y); 436 const float* dxRow = imageDx_.ptr<float>(y); 437 const float* dyRow = imageDy_.ptr<float>(y); 438 439 for (int x = 0; x < imageSize_.width; ++x) 440 { 441 const Point p(x, y); 442 443 if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) 444 { 445 const float theta = fastAtan2(dyRow[x], dxRow[x]); 446 const int n = cvRound(theta * thetaScale); 447 448 const std::vector<Point>& r_row = r_table_[n]; 449 450 for (size_t j = 0; j < r_row.size(); ++j) 451 { 452 Point c = p - r_row[j]; 453 454 c.x = cvRound(c.x * idp); 455 c.y = cvRound(c.y * idp); 456 457 if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows) 458 ++hist_.at<int>(c.y + 1, c.x + 1); 459 } 460 } 461 } 462 } 463 } 464 465 void GeneralizedHoughBallardImpl::findPosInHist() 466 { 467 CV_Assert( votesThreshold_ > 0 ); 468 469 const int histRows = hist_.rows - 2; 470 const int histCols = hist_.cols - 2; 471 472 for(int y = 0; y < histRows; ++y) 473 { 474 const int* prevRow = hist_.ptr<int>(y); 475 const int* curRow = hist_.ptr<int>(y + 1); 476 const int* nextRow = hist_.ptr<int>(y + 2); 477 478 for(int x = 0; x < histCols; ++x) 479 { 480 const int votes = curRow[x + 1]; 481 482 if (votes > votesThreshold_ && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1]) 483 { 484 posOutBuf_.push_back(Vec4f(static_cast<float>(x * dp_), static_cast<float>(y * dp_), 1.0f, 0.0f)); 485 voteOutBuf_.push_back(Vec3i(votes, 0, 0)); 486 } 487 } 488 } 489 } 490 } 491 492 Ptr<GeneralizedHoughBallard> cv::createGeneralizedHoughBallard() 493 { 494 return makePtr<GeneralizedHoughBallardImpl>(); 495 } 496 497 // GeneralizedHoughGuil 498 499 namespace 500 { 501 class GeneralizedHoughGuilImpl : public GeneralizedHoughGuil, private GeneralizedHoughBase 502 { 503 public: 504 GeneralizedHoughGuilImpl(); 505 506 void setTemplate(InputArray templ, Point templCenter) { setTemplateImpl(templ, templCenter); } 507 void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { setTemplateImpl(edges, dx, dy, templCenter); } 508 509 void detect(InputArray image, OutputArray positions, OutputArray votes) { detectImpl(image, positions, votes); } 510 void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { detectImpl(edges, dx, dy, positions, votes); } 511 512 void setCannyLowThresh(int cannyLowThresh) { cannyLowThresh_ = cannyLowThresh; } 513 int getCannyLowThresh() const { return cannyLowThresh_; } 514 515 void setCannyHighThresh(int cannyHighThresh) { cannyHighThresh_ = cannyHighThresh; } 516 int getCannyHighThresh() const { return cannyHighThresh_; } 517 518 void setMinDist(double minDist) { minDist_ = minDist; } 519 double getMinDist() const { return minDist_; } 520 521 void setDp(double dp) { dp_ = dp; } 522 double getDp() const { return dp_; } 523 524 void setMaxBufferSize(int maxBufferSize) { maxBufferSize_ = maxBufferSize; } 525 int getMaxBufferSize() const { return maxBufferSize_; } 526 527 void setXi(double xi) { xi_ = xi; } 528 double getXi() const { return xi_; } 529 530 void setLevels(int levels) { levels_ = levels; } 531 int getLevels() const { return levels_; } 532 533 void setAngleEpsilon(double angleEpsilon) { angleEpsilon_ = angleEpsilon; } 534 double getAngleEpsilon() const { return angleEpsilon_; } 535 536 void setMinAngle(double minAngle) { minAngle_ = minAngle; } 537 double getMinAngle() const { return minAngle_; } 538 539 void setMaxAngle(double maxAngle) { maxAngle_ = maxAngle; } 540 double getMaxAngle() const { return maxAngle_; } 541 542 void setAngleStep(double angleStep) { angleStep_ = angleStep; } 543 double getAngleStep() const { return angleStep_; } 544 545 void setAngleThresh(int angleThresh) { angleThresh_ = angleThresh; } 546 int getAngleThresh() const { return angleThresh_; } 547 548 void setMinScale(double minScale) { minScale_ = minScale; } 549 double getMinScale() const { return minScale_; } 550 551 void setMaxScale(double maxScale) { maxScale_ = maxScale; } 552 double getMaxScale() const { return maxScale_; } 553 554 void setScaleStep(double scaleStep) { scaleStep_ = scaleStep; } 555 double getScaleStep() const { return scaleStep_; } 556 557 void setScaleThresh(int scaleThresh) { scaleThresh_ = scaleThresh; } 558 int getScaleThresh() const { return scaleThresh_; } 559 560 void setPosThresh(int posThresh) { posThresh_ = posThresh; } 561 int getPosThresh() const { return posThresh_; } 562 563 private: 564 void processTempl(); 565 void processImage(); 566 567 int maxBufferSize_; 568 double xi_; 569 int levels_; 570 double angleEpsilon_; 571 572 double minAngle_; 573 double maxAngle_; 574 double angleStep_; 575 int angleThresh_; 576 577 double minScale_; 578 double maxScale_; 579 double scaleStep_; 580 int scaleThresh_; 581 582 int posThresh_; 583 584 struct ContourPoint 585 { 586 Point2d pos; 587 double theta; 588 }; 589 590 struct Feature 591 { 592 ContourPoint p1; 593 ContourPoint p2; 594 595 double alpha12; 596 double d12; 597 598 Point2d r1; 599 Point2d r2; 600 }; 601 602 void buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, std::vector< std::vector<Feature> >& features, Point2d center = Point2d()); 603 void getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, std::vector<ContourPoint>& points); 604 605 void calcOrientation(); 606 void calcScale(double angle); 607 void calcPosition(double angle, int angleVotes, double scale, int scaleVotes); 608 609 std::vector< std::vector<Feature> > templFeatures_; 610 std::vector< std::vector<Feature> > imageFeatures_; 611 612 std::vector< std::pair<double, int> > angles_; 613 std::vector< std::pair<double, int> > scales_; 614 }; 615 616 double clampAngle(double a) 617 { 618 double res = a; 619 620 while (res > 360.0) 621 res -= 360.0; 622 while (res < 0) 623 res += 360.0; 624 625 return res; 626 } 627 628 bool angleEq(double a, double b, double eps = 1.0) 629 { 630 return (fabs(clampAngle(a - b)) <= eps); 631 } 632 633 GeneralizedHoughGuilImpl::GeneralizedHoughGuilImpl() 634 { 635 maxBufferSize_ = 1000; 636 xi_ = 90.0; 637 levels_ = 360; 638 angleEpsilon_ = 1.0; 639 640 minAngle_ = 0.0; 641 maxAngle_ = 360.0; 642 angleStep_ = 1.0; 643 angleThresh_ = 15000; 644 645 minScale_ = 0.5; 646 maxScale_ = 2.0; 647 scaleStep_ = 0.05; 648 scaleThresh_ = 1000; 649 650 posThresh_ = 100; 651 } 652 653 void GeneralizedHoughGuilImpl::processTempl() 654 { 655 buildFeatureList(templEdges_, templDx_, templDy_, templFeatures_, templCenter_); 656 } 657 658 void GeneralizedHoughGuilImpl::processImage() 659 { 660 buildFeatureList(imageEdges_, imageDx_, imageDy_, imageFeatures_); 661 662 calcOrientation(); 663 664 for (size_t i = 0; i < angles_.size(); ++i) 665 { 666 const double angle = angles_[i].first; 667 const int angleVotes = angles_[i].second; 668 669 calcScale(angle); 670 671 for (size_t j = 0; j < scales_.size(); ++j) 672 { 673 const double scale = scales_[j].first; 674 const int scaleVotes = scales_[j].second; 675 676 calcPosition(angle, angleVotes, scale, scaleVotes); 677 } 678 } 679 } 680 681 void GeneralizedHoughGuilImpl::buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, std::vector< std::vector<Feature> >& features, Point2d center) 682 { 683 CV_Assert( levels_ > 0 ); 684 685 const double maxDist = sqrt((double) templSize_.width * templSize_.width + templSize_.height * templSize_.height) * maxScale_; 686 687 const double alphaScale = levels_ / 360.0; 688 689 std::vector<ContourPoint> points; 690 getContourPoints(edges, dx, dy, points); 691 692 features.resize(levels_ + 1); 693 std::for_each(features.begin(), features.end(), std::mem_fun_ref(&std::vector<Feature>::clear)); 694 std::for_each(features.begin(), features.end(), std::bind2nd(std::mem_fun_ref(&std::vector<Feature>::reserve), maxBufferSize_)); 695 696 for (size_t i = 0; i < points.size(); ++i) 697 { 698 ContourPoint p1 = points[i]; 699 700 for (size_t j = 0; j < points.size(); ++j) 701 { 702 ContourPoint p2 = points[j]; 703 704 if (angleEq(p1.theta - p2.theta, xi_, angleEpsilon_)) 705 { 706 const Point2d d = p1.pos - p2.pos; 707 708 Feature f; 709 710 f.p1 = p1; 711 f.p2 = p2; 712 713 f.alpha12 = clampAngle(fastAtan2((float)d.y, (float)d.x) - p1.theta); 714 f.d12 = norm(d); 715 716 if (f.d12 > maxDist) 717 continue; 718 719 f.r1 = p1.pos - center; 720 f.r2 = p2.pos - center; 721 722 const int n = cvRound(f.alpha12 * alphaScale); 723 724 if (features[n].size() < static_cast<size_t>(maxBufferSize_)) 725 features[n].push_back(f); 726 } 727 } 728 } 729 } 730 731 void GeneralizedHoughGuilImpl::getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, std::vector<ContourPoint>& points) 732 { 733 CV_Assert( edges.type() == CV_8UC1 ); 734 CV_Assert( dx.type() == CV_32FC1 && dx.size == edges.size ); 735 CV_Assert( dy.type() == dx.type() && dy.size == edges.size ); 736 737 points.clear(); 738 points.reserve(edges.size().area()); 739 740 for (int y = 0; y < edges.rows; ++y) 741 { 742 const uchar* edgesRow = edges.ptr(y); 743 const float* dxRow = dx.ptr<float>(y); 744 const float* dyRow = dy.ptr<float>(y); 745 746 for (int x = 0; x < edges.cols; ++x) 747 { 748 if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x]))) 749 { 750 ContourPoint p; 751 752 p.pos = Point2d(x, y); 753 p.theta = fastAtan2(dyRow[x], dxRow[x]); 754 755 points.push_back(p); 756 } 757 } 758 } 759 } 760 761 void GeneralizedHoughGuilImpl::calcOrientation() 762 { 763 CV_Assert( levels_ > 0 ); 764 CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) ); 765 CV_Assert( imageFeatures_.size() == templFeatures_.size() ); 766 CV_Assert( minAngle_ >= 0.0 && minAngle_ < maxAngle_ && maxAngle_ <= 360.0 ); 767 CV_Assert( angleStep_ > 0.0 && angleStep_ < 360.0 ); 768 CV_Assert( angleThresh_ > 0 ); 769 770 const double iAngleStep = 1.0 / angleStep_; 771 const int angleRange = cvCeil((maxAngle_ - minAngle_) * iAngleStep); 772 773 std::vector<int> OHist(angleRange + 1, 0); 774 for (int i = 0; i <= levels_; ++i) 775 { 776 const std::vector<Feature>& templRow = templFeatures_[i]; 777 const std::vector<Feature>& imageRow = imageFeatures_[i]; 778 779 for (size_t j = 0; j < templRow.size(); ++j) 780 { 781 Feature templF = templRow[j]; 782 783 for (size_t k = 0; k < imageRow.size(); ++k) 784 { 785 Feature imF = imageRow[k]; 786 787 const double angle = clampAngle(imF.p1.theta - templF.p1.theta); 788 if (angle >= minAngle_ && angle <= maxAngle_) 789 { 790 const int n = cvRound((angle - minAngle_) * iAngleStep); 791 ++OHist[n]; 792 } 793 } 794 } 795 } 796 797 angles_.clear(); 798 799 for (int n = 0; n < angleRange; ++n) 800 { 801 if (OHist[n] >= angleThresh_) 802 { 803 const double angle = minAngle_ + n * angleStep_; 804 angles_.push_back(std::make_pair(angle, OHist[n])); 805 } 806 } 807 } 808 809 void GeneralizedHoughGuilImpl::calcScale(double angle) 810 { 811 CV_Assert( levels_ > 0 ); 812 CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) ); 813 CV_Assert( imageFeatures_.size() == templFeatures_.size() ); 814 CV_Assert( minScale_ > 0.0 && minScale_ < maxScale_ ); 815 CV_Assert( scaleStep_ > 0.0 ); 816 CV_Assert( scaleThresh_ > 0 ); 817 818 const double iScaleStep = 1.0 / scaleStep_; 819 const int scaleRange = cvCeil((maxScale_ - minScale_) * iScaleStep); 820 821 std::vector<int> SHist(scaleRange + 1, 0); 822 823 for (int i = 0; i <= levels_; ++i) 824 { 825 const std::vector<Feature>& templRow = templFeatures_[i]; 826 const std::vector<Feature>& imageRow = imageFeatures_[i]; 827 828 for (size_t j = 0; j < templRow.size(); ++j) 829 { 830 Feature templF = templRow[j]; 831 832 templF.p1.theta += angle; 833 834 for (size_t k = 0; k < imageRow.size(); ++k) 835 { 836 Feature imF = imageRow[k]; 837 838 if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon_)) 839 { 840 const double scale = imF.d12 / templF.d12; 841 if (scale >= minScale_ && scale <= maxScale_) 842 { 843 const int s = cvRound((scale - minScale_) * iScaleStep); 844 ++SHist[s]; 845 } 846 } 847 } 848 } 849 } 850 851 scales_.clear(); 852 853 for (int s = 0; s < scaleRange; ++s) 854 { 855 if (SHist[s] >= scaleThresh_) 856 { 857 const double scale = minScale_ + s * scaleStep_; 858 scales_.push_back(std::make_pair(scale, SHist[s])); 859 } 860 } 861 } 862 863 void GeneralizedHoughGuilImpl::calcPosition(double angle, int angleVotes, double scale, int scaleVotes) 864 { 865 CV_Assert( levels_ > 0 ); 866 CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) ); 867 CV_Assert( imageFeatures_.size() == templFeatures_.size() ); 868 CV_Assert( dp_ > 0.0 ); 869 CV_Assert( posThresh_ > 0 ); 870 871 const double sinVal = sin(toRad(angle)); 872 const double cosVal = cos(toRad(angle)); 873 const double idp = 1.0 / dp_; 874 875 const int histRows = cvCeil(imageSize_.height * idp); 876 const int histCols = cvCeil(imageSize_.width * idp); 877 878 Mat DHist(histRows + 2, histCols + 2, CV_32SC1, Scalar::all(0)); 879 880 for (int i = 0; i <= levels_; ++i) 881 { 882 const std::vector<Feature>& templRow = templFeatures_[i]; 883 const std::vector<Feature>& imageRow = imageFeatures_[i]; 884 885 for (size_t j = 0; j < templRow.size(); ++j) 886 { 887 Feature templF = templRow[j]; 888 889 templF.p1.theta += angle; 890 891 templF.r1 *= scale; 892 templF.r2 *= scale; 893 894 templF.r1 = Point2d(cosVal * templF.r1.x - sinVal * templF.r1.y, sinVal * templF.r1.x + cosVal * templF.r1.y); 895 templF.r2 = Point2d(cosVal * templF.r2.x - sinVal * templF.r2.y, sinVal * templF.r2.x + cosVal * templF.r2.y); 896 897 for (size_t k = 0; k < imageRow.size(); ++k) 898 { 899 Feature imF = imageRow[k]; 900 901 if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon_)) 902 { 903 Point2d c1, c2; 904 905 c1 = imF.p1.pos - templF.r1; 906 c1 *= idp; 907 908 c2 = imF.p2.pos - templF.r2; 909 c2 *= idp; 910 911 if (fabs(c1.x - c2.x) > 1 || fabs(c1.y - c2.y) > 1) 912 continue; 913 914 if (c1.y >= 0 && c1.y < histRows && c1.x >= 0 && c1.x < histCols) 915 ++DHist.at<int>(cvRound(c1.y) + 1, cvRound(c1.x) + 1); 916 } 917 } 918 } 919 } 920 921 for(int y = 0; y < histRows; ++y) 922 { 923 const int* prevRow = DHist.ptr<int>(y); 924 const int* curRow = DHist.ptr<int>(y + 1); 925 const int* nextRow = DHist.ptr<int>(y + 2); 926 927 for(int x = 0; x < histCols; ++x) 928 { 929 const int votes = curRow[x + 1]; 930 931 if (votes > posThresh_ && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1]) 932 { 933 posOutBuf_.push_back(Vec4f(static_cast<float>(x * dp_), static_cast<float>(y * dp_), static_cast<float>(scale), static_cast<float>(angle))); 934 voteOutBuf_.push_back(Vec3i(votes, scaleVotes, angleVotes)); 935 } 936 } 937 } 938 } 939 } 940 941 Ptr<GeneralizedHoughGuil> cv::createGeneralizedHoughGuil() 942 { 943 return makePtr<GeneralizedHoughGuilImpl>(); 944 } 945