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 {
toRad(double a)52 double toRad(double a)
53 {
54 return a * CV_PI / 180.0;
55 }
56
notNull(float v)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();
~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
GeneralizedHoughBase()102 GeneralizedHoughBase::GeneralizedHoughBase()
103 {
104 cannyLowThresh_ = 50;
105 cannyHighThresh_ = 100;
106 minDist_ = 1.0;
107 dp_ = 1.0;
108 }
109
calcEdges(InputArray _src,Mat & edges,Mat & dx,Mat & dy)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
setTemplateImpl(InputArray templ,Point templCenter)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
setTemplateImpl(InputArray edges,InputArray dx,InputArray dy,Point templCenter)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
detectImpl(InputArray image,OutputArray positions,OutputArray votes)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
detectImpl(InputArray edges,InputArray dx,InputArray dy,OutputArray positions,OutputArray votes)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:
Vec3iGreaterThanIdx(const Vec3i * _arr)213 Vec3iGreaterThanIdx( const Vec3i* _arr ) : arr(_arr) {}
operator ()(size_t a,size_t b) const214 bool operator()(size_t a, size_t b) const { return arr[a][0] > arr[b][0]; }
215 const Vec3i* arr;
216 };
217
filterMinDist()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
convertTo(OutputArray _positions,OutputArray _votes)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
setTemplate(InputArray templ,Point templCenter)334 void setTemplate(InputArray templ, Point templCenter) { setTemplateImpl(templ, templCenter); }
setTemplate(InputArray edges,InputArray dx,InputArray dy,Point templCenter)335 void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { setTemplateImpl(edges, dx, dy, templCenter); }
336
detect(InputArray image,OutputArray positions,OutputArray votes)337 void detect(InputArray image, OutputArray positions, OutputArray votes) { detectImpl(image, positions, votes); }
detect(InputArray edges,InputArray dx,InputArray dy,OutputArray positions,OutputArray votes)338 void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { detectImpl(edges, dx, dy, positions, votes); }
339
setCannyLowThresh(int cannyLowThresh)340 void setCannyLowThresh(int cannyLowThresh) { cannyLowThresh_ = cannyLowThresh; }
getCannyLowThresh() const341 int getCannyLowThresh() const { return cannyLowThresh_; }
342
setCannyHighThresh(int cannyHighThresh)343 void setCannyHighThresh(int cannyHighThresh) { cannyHighThresh_ = cannyHighThresh; }
getCannyHighThresh() const344 int getCannyHighThresh() const { return cannyHighThresh_; }
345
setMinDist(double minDist)346 void setMinDist(double minDist) { minDist_ = minDist; }
getMinDist() const347 double getMinDist() const { return minDist_; }
348
setDp(double dp)349 void setDp(double dp) { dp_ = dp; }
getDp() const350 double getDp() const { return dp_; }
351
setMaxBufferSize(int)352 void setMaxBufferSize(int) { }
getMaxBufferSize() const353 int getMaxBufferSize() const { return 0; }
354
setLevels(int levels)355 void setLevels(int levels) { levels_ = levels; }
getLevels() const356 int getLevels() const { return levels_; }
357
setVotesThreshold(int votesThreshold)358 void setVotesThreshold(int votesThreshold) { votesThreshold_ = votesThreshold; }
getVotesThreshold() const359 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
GeneralizedHoughBallardImpl()375 GeneralizedHoughBallardImpl::GeneralizedHoughBallardImpl()
376 {
377 levels_ = 360;
378 votesThreshold_ = 100;
379 }
380
processTempl()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
processImage()410 void GeneralizedHoughBallardImpl::processImage()
411 {
412 calcHist();
413 findPosInHist();
414 }
415
calcHist()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
findPosInHist()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
createGeneralizedHoughBallard()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
setTemplate(InputArray templ,Point templCenter)506 void setTemplate(InputArray templ, Point templCenter) { setTemplateImpl(templ, templCenter); }
setTemplate(InputArray edges,InputArray dx,InputArray dy,Point templCenter)507 void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) { setTemplateImpl(edges, dx, dy, templCenter); }
508
detect(InputArray image,OutputArray positions,OutputArray votes)509 void detect(InputArray image, OutputArray positions, OutputArray votes) { detectImpl(image, positions, votes); }
detect(InputArray edges,InputArray dx,InputArray dy,OutputArray positions,OutputArray votes)510 void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) { detectImpl(edges, dx, dy, positions, votes); }
511
setCannyLowThresh(int cannyLowThresh)512 void setCannyLowThresh(int cannyLowThresh) { cannyLowThresh_ = cannyLowThresh; }
getCannyLowThresh() const513 int getCannyLowThresh() const { return cannyLowThresh_; }
514
setCannyHighThresh(int cannyHighThresh)515 void setCannyHighThresh(int cannyHighThresh) { cannyHighThresh_ = cannyHighThresh; }
getCannyHighThresh() const516 int getCannyHighThresh() const { return cannyHighThresh_; }
517
setMinDist(double minDist)518 void setMinDist(double minDist) { minDist_ = minDist; }
getMinDist() const519 double getMinDist() const { return minDist_; }
520
setDp(double dp)521 void setDp(double dp) { dp_ = dp; }
getDp() const522 double getDp() const { return dp_; }
523
setMaxBufferSize(int maxBufferSize)524 void setMaxBufferSize(int maxBufferSize) { maxBufferSize_ = maxBufferSize; }
getMaxBufferSize() const525 int getMaxBufferSize() const { return maxBufferSize_; }
526
setXi(double xi)527 void setXi(double xi) { xi_ = xi; }
getXi() const528 double getXi() const { return xi_; }
529
setLevels(int levels)530 void setLevels(int levels) { levels_ = levels; }
getLevels() const531 int getLevels() const { return levels_; }
532
setAngleEpsilon(double angleEpsilon)533 void setAngleEpsilon(double angleEpsilon) { angleEpsilon_ = angleEpsilon; }
getAngleEpsilon() const534 double getAngleEpsilon() const { return angleEpsilon_; }
535
setMinAngle(double minAngle)536 void setMinAngle(double minAngle) { minAngle_ = minAngle; }
getMinAngle() const537 double getMinAngle() const { return minAngle_; }
538
setMaxAngle(double maxAngle)539 void setMaxAngle(double maxAngle) { maxAngle_ = maxAngle; }
getMaxAngle() const540 double getMaxAngle() const { return maxAngle_; }
541
setAngleStep(double angleStep)542 void setAngleStep(double angleStep) { angleStep_ = angleStep; }
getAngleStep() const543 double getAngleStep() const { return angleStep_; }
544
setAngleThresh(int angleThresh)545 void setAngleThresh(int angleThresh) { angleThresh_ = angleThresh; }
getAngleThresh() const546 int getAngleThresh() const { return angleThresh_; }
547
setMinScale(double minScale)548 void setMinScale(double minScale) { minScale_ = minScale; }
getMinScale() const549 double getMinScale() const { return minScale_; }
550
setMaxScale(double maxScale)551 void setMaxScale(double maxScale) { maxScale_ = maxScale; }
getMaxScale() const552 double getMaxScale() const { return maxScale_; }
553
setScaleStep(double scaleStep)554 void setScaleStep(double scaleStep) { scaleStep_ = scaleStep; }
getScaleStep() const555 double getScaleStep() const { return scaleStep_; }
556
setScaleThresh(int scaleThresh)557 void setScaleThresh(int scaleThresh) { scaleThresh_ = scaleThresh; }
getScaleThresh() const558 int getScaleThresh() const { return scaleThresh_; }
559
setPosThresh(int posThresh)560 void setPosThresh(int posThresh) { posThresh_ = posThresh; }
getPosThresh() const561 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
clampAngle(double a)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
angleEq(double a,double b,double eps=1.0)628 bool angleEq(double a, double b, double eps = 1.0)
629 {
630 return (fabs(clampAngle(a - b)) <= eps);
631 }
632
GeneralizedHoughGuilImpl()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
processTempl()653 void GeneralizedHoughGuilImpl::processTempl()
654 {
655 buildFeatureList(templEdges_, templDx_, templDy_, templFeatures_, templCenter_);
656 }
657
processImage()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
buildFeatureList(const Mat & edges,const Mat & dx,const Mat & dy,std::vector<std::vector<Feature>> & features,Point2d center)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
getContourPoints(const Mat & edges,const Mat & dx,const Mat & dy,std::vector<ContourPoint> & points)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
calcOrientation()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
calcScale(double angle)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
calcPosition(double angle,int angleVotes,double scale,int scaleVotes)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
createGeneralizedHoughGuil()941 Ptr<GeneralizedHoughGuil> cv::createGeneralizedHoughGuil()
942 {
943 return makePtr<GeneralizedHoughGuilImpl>();
944 }
945