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