Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/src/generalized_hough.cpp
16354 views
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 CV_FINAL : public GeneralizedHoughBallard, private GeneralizedHoughBase
330
{
331
public:
332
GeneralizedHoughBallardImpl();
333
334
void setTemplate(InputArray templ, Point templCenter) CV_OVERRIDE { setTemplateImpl(templ, templCenter); }
335
void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) CV_OVERRIDE { setTemplateImpl(edges, dx, dy, templCenter); }
336
337
void detect(InputArray image, OutputArray positions, OutputArray votes) CV_OVERRIDE { detectImpl(image, positions, votes); }
338
void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) CV_OVERRIDE { detectImpl(edges, dx, dy, positions, votes); }
339
340
void setCannyLowThresh(int cannyLowThresh) CV_OVERRIDE { cannyLowThresh_ = cannyLowThresh; }
341
int getCannyLowThresh() const CV_OVERRIDE { return cannyLowThresh_; }
342
343
void setCannyHighThresh(int cannyHighThresh) CV_OVERRIDE { cannyHighThresh_ = cannyHighThresh; }
344
int getCannyHighThresh() const CV_OVERRIDE { return cannyHighThresh_; }
345
346
void setMinDist(double minDist) CV_OVERRIDE { minDist_ = minDist; }
347
double getMinDist() const CV_OVERRIDE { return minDist_; }
348
349
void setDp(double dp) CV_OVERRIDE { dp_ = dp; }
350
double getDp() const CV_OVERRIDE { return dp_; }
351
352
void setMaxBufferSize(int) CV_OVERRIDE { }
353
int getMaxBufferSize() const CV_OVERRIDE { return 0; }
354
355
void setLevels(int levels) CV_OVERRIDE { levels_ = levels; }
356
int getLevels() const CV_OVERRIDE { return levels_; }
357
358
void setVotesThreshold(int votesThreshold) CV_OVERRIDE { votesThreshold_ = votesThreshold; }
359
int getVotesThreshold() const CV_OVERRIDE { return votesThreshold_; }
360
361
private:
362
void processTempl() CV_OVERRIDE;
363
void processImage() CV_OVERRIDE;
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::vector<Point>& e)->void { e.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_INSTRUMENT_REGION();
419
420
CV_Assert( imageEdges_.type() == CV_8UC1 );
421
CV_Assert( imageDx_.type() == CV_32FC1 && imageDx_.size() == imageSize_);
422
CV_Assert( imageDy_.type() == imageDx_.type() && imageDy_.size() == imageSize_);
423
CV_Assert( levels_ > 0 && r_table_.size() == static_cast<size_t>(levels_ + 1) );
424
CV_Assert( dp_ > 0.0 );
425
426
const double thetaScale = levels_ / 360.0;
427
const double idp = 1.0 / dp_;
428
429
hist_.create(cvCeil(imageSize_.height * idp) + 2, cvCeil(imageSize_.width * idp) + 2, CV_32SC1);
430
hist_.setTo(0);
431
432
const int rows = hist_.rows - 2;
433
const int cols = hist_.cols - 2;
434
435
for (int y = 0; y < imageSize_.height; ++y)
436
{
437
const uchar* edgesRow = imageEdges_.ptr(y);
438
const float* dxRow = imageDx_.ptr<float>(y);
439
const float* dyRow = imageDy_.ptr<float>(y);
440
441
for (int x = 0; x < imageSize_.width; ++x)
442
{
443
const Point p(x, y);
444
445
if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x])))
446
{
447
const float theta = fastAtan2(dyRow[x], dxRow[x]);
448
const int n = cvRound(theta * thetaScale);
449
450
const std::vector<Point>& r_row = r_table_[n];
451
452
for (size_t j = 0; j < r_row.size(); ++j)
453
{
454
Point c = p - r_row[j];
455
456
c.x = cvRound(c.x * idp);
457
c.y = cvRound(c.y * idp);
458
459
if (c.x >= 0 && c.x < cols && c.y >= 0 && c.y < rows)
460
++hist_.at<int>(c.y + 1, c.x + 1);
461
}
462
}
463
}
464
}
465
}
466
467
void GeneralizedHoughBallardImpl::findPosInHist()
468
{
469
CV_Assert( votesThreshold_ > 0 );
470
471
const int histRows = hist_.rows - 2;
472
const int histCols = hist_.cols - 2;
473
474
for(int y = 0; y < histRows; ++y)
475
{
476
const int* prevRow = hist_.ptr<int>(y);
477
const int* curRow = hist_.ptr<int>(y + 1);
478
const int* nextRow = hist_.ptr<int>(y + 2);
479
480
for(int x = 0; x < histCols; ++x)
481
{
482
const int votes = curRow[x + 1];
483
484
if (votes > votesThreshold_ && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1])
485
{
486
posOutBuf_.push_back(Vec4f(static_cast<float>(x * dp_), static_cast<float>(y * dp_), 1.0f, 0.0f));
487
voteOutBuf_.push_back(Vec3i(votes, 0, 0));
488
}
489
}
490
}
491
}
492
}
493
494
Ptr<GeneralizedHoughBallard> cv::createGeneralizedHoughBallard()
495
{
496
return makePtr<GeneralizedHoughBallardImpl>();
497
}
498
499
// GeneralizedHoughGuil
500
501
namespace
502
{
503
class GeneralizedHoughGuilImpl CV_FINAL : public GeneralizedHoughGuil, private GeneralizedHoughBase
504
{
505
public:
506
GeneralizedHoughGuilImpl();
507
508
void setTemplate(InputArray templ, Point templCenter) CV_OVERRIDE { setTemplateImpl(templ, templCenter); }
509
void setTemplate(InputArray edges, InputArray dx, InputArray dy, Point templCenter) CV_OVERRIDE { setTemplateImpl(edges, dx, dy, templCenter); }
510
511
void detect(InputArray image, OutputArray positions, OutputArray votes) CV_OVERRIDE { detectImpl(image, positions, votes); }
512
void detect(InputArray edges, InputArray dx, InputArray dy, OutputArray positions, OutputArray votes) CV_OVERRIDE { detectImpl(edges, dx, dy, positions, votes); }
513
514
void setCannyLowThresh(int cannyLowThresh) CV_OVERRIDE { cannyLowThresh_ = cannyLowThresh; }
515
int getCannyLowThresh() const CV_OVERRIDE { return cannyLowThresh_; }
516
517
void setCannyHighThresh(int cannyHighThresh) CV_OVERRIDE { cannyHighThresh_ = cannyHighThresh; }
518
int getCannyHighThresh() const CV_OVERRIDE { return cannyHighThresh_; }
519
520
void setMinDist(double minDist) CV_OVERRIDE { minDist_ = minDist; }
521
double getMinDist() const CV_OVERRIDE { return minDist_; }
522
523
void setDp(double dp) CV_OVERRIDE { dp_ = dp; }
524
double getDp() const CV_OVERRIDE { return dp_; }
525
526
void setMaxBufferSize(int maxBufferSize) CV_OVERRIDE { maxBufferSize_ = maxBufferSize; }
527
int getMaxBufferSize() const CV_OVERRIDE { return maxBufferSize_; }
528
529
void setXi(double xi) CV_OVERRIDE { xi_ = xi; }
530
double getXi() const CV_OVERRIDE { return xi_; }
531
532
void setLevels(int levels) CV_OVERRIDE { levels_ = levels; }
533
int getLevels() const CV_OVERRIDE { return levels_; }
534
535
void setAngleEpsilon(double angleEpsilon) CV_OVERRIDE { angleEpsilon_ = angleEpsilon; }
536
double getAngleEpsilon() const CV_OVERRIDE { return angleEpsilon_; }
537
538
void setMinAngle(double minAngle) CV_OVERRIDE { minAngle_ = minAngle; }
539
double getMinAngle() const CV_OVERRIDE { return minAngle_; }
540
541
void setMaxAngle(double maxAngle) CV_OVERRIDE { maxAngle_ = maxAngle; }
542
double getMaxAngle() const CV_OVERRIDE { return maxAngle_; }
543
544
void setAngleStep(double angleStep) CV_OVERRIDE { angleStep_ = angleStep; }
545
double getAngleStep() const CV_OVERRIDE { return angleStep_; }
546
547
void setAngleThresh(int angleThresh) CV_OVERRIDE { angleThresh_ = angleThresh; }
548
int getAngleThresh() const CV_OVERRIDE { return angleThresh_; }
549
550
void setMinScale(double minScale) CV_OVERRIDE { minScale_ = minScale; }
551
double getMinScale() const CV_OVERRIDE { return minScale_; }
552
553
void setMaxScale(double maxScale) CV_OVERRIDE { maxScale_ = maxScale; }
554
double getMaxScale() const CV_OVERRIDE { return maxScale_; }
555
556
void setScaleStep(double scaleStep) CV_OVERRIDE { scaleStep_ = scaleStep; }
557
double getScaleStep() const CV_OVERRIDE { return scaleStep_; }
558
559
void setScaleThresh(int scaleThresh) CV_OVERRIDE { scaleThresh_ = scaleThresh; }
560
int getScaleThresh() const CV_OVERRIDE { return scaleThresh_; }
561
562
void setPosThresh(int posThresh) CV_OVERRIDE { posThresh_ = posThresh; }
563
int getPosThresh() const CV_OVERRIDE { return posThresh_; }
564
565
private:
566
void processTempl() CV_OVERRIDE;
567
void processImage() CV_OVERRIDE;
568
569
int maxBufferSize_;
570
double xi_;
571
int levels_;
572
double angleEpsilon_;
573
574
double minAngle_;
575
double maxAngle_;
576
double angleStep_;
577
int angleThresh_;
578
579
double minScale_;
580
double maxScale_;
581
double scaleStep_;
582
int scaleThresh_;
583
584
int posThresh_;
585
586
struct ContourPoint
587
{
588
Point2d pos;
589
double theta;
590
};
591
592
struct Feature
593
{
594
ContourPoint p1;
595
ContourPoint p2;
596
597
double alpha12;
598
double d12;
599
600
Point2d r1;
601
Point2d r2;
602
};
603
604
void buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, std::vector< std::vector<Feature> >& features, Point2d center = Point2d());
605
void getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, std::vector<ContourPoint>& points);
606
607
void calcOrientation();
608
void calcScale(double angle);
609
void calcPosition(double angle, int angleVotes, double scale, int scaleVotes);
610
611
std::vector< std::vector<Feature> > templFeatures_;
612
std::vector< std::vector<Feature> > imageFeatures_;
613
614
std::vector< std::pair<double, int> > angles_;
615
std::vector< std::pair<double, int> > scales_;
616
};
617
618
double clampAngle(double a)
619
{
620
double res = a;
621
622
while (res > 360.0)
623
res -= 360.0;
624
while (res < 0)
625
res += 360.0;
626
627
return res;
628
}
629
630
bool angleEq(double a, double b, double eps = 1.0)
631
{
632
return (fabs(clampAngle(a - b)) <= eps);
633
}
634
635
GeneralizedHoughGuilImpl::GeneralizedHoughGuilImpl()
636
{
637
maxBufferSize_ = 1000;
638
xi_ = 90.0;
639
levels_ = 360;
640
angleEpsilon_ = 1.0;
641
642
minAngle_ = 0.0;
643
maxAngle_ = 360.0;
644
angleStep_ = 1.0;
645
angleThresh_ = 15000;
646
647
minScale_ = 0.5;
648
maxScale_ = 2.0;
649
scaleStep_ = 0.05;
650
scaleThresh_ = 1000;
651
652
posThresh_ = 100;
653
}
654
655
void GeneralizedHoughGuilImpl::processTempl()
656
{
657
buildFeatureList(templEdges_, templDx_, templDy_, templFeatures_, templCenter_);
658
}
659
660
void GeneralizedHoughGuilImpl::processImage()
661
{
662
buildFeatureList(imageEdges_, imageDx_, imageDy_, imageFeatures_);
663
664
calcOrientation();
665
666
for (size_t i = 0; i < angles_.size(); ++i)
667
{
668
const double angle = angles_[i].first;
669
const int angleVotes = angles_[i].second;
670
671
calcScale(angle);
672
673
for (size_t j = 0; j < scales_.size(); ++j)
674
{
675
const double scale = scales_[j].first;
676
const int scaleVotes = scales_[j].second;
677
678
calcPosition(angle, angleVotes, scale, scaleVotes);
679
}
680
}
681
}
682
683
void GeneralizedHoughGuilImpl::buildFeatureList(const Mat& edges, const Mat& dx, const Mat& dy, std::vector< std::vector<Feature> >& features, Point2d center)
684
{
685
CV_Assert( levels_ > 0 );
686
687
const double maxDist = sqrt((double) templSize_.width * templSize_.width + templSize_.height * templSize_.height) * maxScale_;
688
689
const double alphaScale = levels_ / 360.0;
690
691
std::vector<ContourPoint> points;
692
getContourPoints(edges, dx, dy, points);
693
694
features.resize(levels_ + 1);
695
std::for_each(features.begin(), features.end(), [=](std::vector<Feature>& e) { e.clear(); e.reserve(maxBufferSize_); });
696
697
for (size_t i = 0; i < points.size(); ++i)
698
{
699
ContourPoint p1 = points[i];
700
701
for (size_t j = 0; j < points.size(); ++j)
702
{
703
ContourPoint p2 = points[j];
704
705
if (angleEq(p1.theta - p2.theta, xi_, angleEpsilon_))
706
{
707
const Point2d d = p1.pos - p2.pos;
708
709
Feature f;
710
711
f.p1 = p1;
712
f.p2 = p2;
713
714
f.alpha12 = clampAngle(fastAtan2((float)d.y, (float)d.x) - p1.theta);
715
f.d12 = norm(d);
716
717
if (f.d12 > maxDist)
718
continue;
719
720
f.r1 = p1.pos - center;
721
f.r2 = p2.pos - center;
722
723
const int n = cvRound(f.alpha12 * alphaScale);
724
725
if (features[n].size() < static_cast<size_t>(maxBufferSize_))
726
features[n].push_back(f);
727
}
728
}
729
}
730
}
731
732
void GeneralizedHoughGuilImpl::getContourPoints(const Mat& edges, const Mat& dx, const Mat& dy, std::vector<ContourPoint>& points)
733
{
734
CV_Assert( edges.type() == CV_8UC1 );
735
CV_Assert( dx.type() == CV_32FC1 && dx.size == edges.size );
736
CV_Assert( dy.type() == dx.type() && dy.size == edges.size );
737
738
points.clear();
739
points.reserve(edges.size().area());
740
741
for (int y = 0; y < edges.rows; ++y)
742
{
743
const uchar* edgesRow = edges.ptr(y);
744
const float* dxRow = dx.ptr<float>(y);
745
const float* dyRow = dy.ptr<float>(y);
746
747
for (int x = 0; x < edges.cols; ++x)
748
{
749
if (edgesRow[x] && (notNull(dyRow[x]) || notNull(dxRow[x])))
750
{
751
ContourPoint p;
752
753
p.pos = Point2d(x, y);
754
p.theta = fastAtan2(dyRow[x], dxRow[x]);
755
756
points.push_back(p);
757
}
758
}
759
}
760
}
761
762
void GeneralizedHoughGuilImpl::calcOrientation()
763
{
764
CV_Assert( levels_ > 0 );
765
CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) );
766
CV_Assert( imageFeatures_.size() == templFeatures_.size() );
767
CV_Assert( minAngle_ >= 0.0 && minAngle_ < maxAngle_ && maxAngle_ <= 360.0 );
768
CV_Assert( angleStep_ > 0.0 && angleStep_ < 360.0 );
769
CV_Assert( angleThresh_ > 0 );
770
771
const double iAngleStep = 1.0 / angleStep_;
772
const int angleRange = cvCeil((maxAngle_ - minAngle_) * iAngleStep);
773
774
std::vector<int> OHist(angleRange + 1, 0);
775
for (int i = 0; i <= levels_; ++i)
776
{
777
const std::vector<Feature>& templRow = templFeatures_[i];
778
const std::vector<Feature>& imageRow = imageFeatures_[i];
779
780
for (size_t j = 0; j < templRow.size(); ++j)
781
{
782
Feature templF = templRow[j];
783
784
for (size_t k = 0; k < imageRow.size(); ++k)
785
{
786
Feature imF = imageRow[k];
787
788
const double angle = clampAngle(imF.p1.theta - templF.p1.theta);
789
if (angle >= minAngle_ && angle <= maxAngle_)
790
{
791
const int n = cvRound((angle - minAngle_) * iAngleStep);
792
++OHist[n];
793
}
794
}
795
}
796
}
797
798
angles_.clear();
799
800
for (int n = 0; n < angleRange; ++n)
801
{
802
if (OHist[n] >= angleThresh_)
803
{
804
const double angle = minAngle_ + n * angleStep_;
805
angles_.push_back(std::make_pair(angle, OHist[n]));
806
}
807
}
808
}
809
810
void GeneralizedHoughGuilImpl::calcScale(double angle)
811
{
812
CV_Assert( levels_ > 0 );
813
CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) );
814
CV_Assert( imageFeatures_.size() == templFeatures_.size() );
815
CV_Assert( minScale_ > 0.0 && minScale_ < maxScale_ );
816
CV_Assert( scaleStep_ > 0.0 );
817
CV_Assert( scaleThresh_ > 0 );
818
819
const double iScaleStep = 1.0 / scaleStep_;
820
const int scaleRange = cvCeil((maxScale_ - minScale_) * iScaleStep);
821
822
std::vector<int> SHist(scaleRange + 1, 0);
823
824
for (int i = 0; i <= levels_; ++i)
825
{
826
const std::vector<Feature>& templRow = templFeatures_[i];
827
const std::vector<Feature>& imageRow = imageFeatures_[i];
828
829
for (size_t j = 0; j < templRow.size(); ++j)
830
{
831
Feature templF = templRow[j];
832
833
templF.p1.theta += angle;
834
835
for (size_t k = 0; k < imageRow.size(); ++k)
836
{
837
Feature imF = imageRow[k];
838
839
if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon_))
840
{
841
const double scale = imF.d12 / templF.d12;
842
if (scale >= minScale_ && scale <= maxScale_)
843
{
844
const int s = cvRound((scale - minScale_) * iScaleStep);
845
++SHist[s];
846
}
847
}
848
}
849
}
850
}
851
852
scales_.clear();
853
854
for (int s = 0; s < scaleRange; ++s)
855
{
856
if (SHist[s] >= scaleThresh_)
857
{
858
const double scale = minScale_ + s * scaleStep_;
859
scales_.push_back(std::make_pair(scale, SHist[s]));
860
}
861
}
862
}
863
864
void GeneralizedHoughGuilImpl::calcPosition(double angle, int angleVotes, double scale, int scaleVotes)
865
{
866
CV_Assert( levels_ > 0 );
867
CV_Assert( templFeatures_.size() == static_cast<size_t>(levels_ + 1) );
868
CV_Assert( imageFeatures_.size() == templFeatures_.size() );
869
CV_Assert( dp_ > 0.0 );
870
CV_Assert( posThresh_ > 0 );
871
872
const double sinVal = sin(toRad(angle));
873
const double cosVal = cos(toRad(angle));
874
const double idp = 1.0 / dp_;
875
876
const int histRows = cvCeil(imageSize_.height * idp);
877
const int histCols = cvCeil(imageSize_.width * idp);
878
879
Mat DHist(histRows + 2, histCols + 2, CV_32SC1, Scalar::all(0));
880
881
for (int i = 0; i <= levels_; ++i)
882
{
883
const std::vector<Feature>& templRow = templFeatures_[i];
884
const std::vector<Feature>& imageRow = imageFeatures_[i];
885
886
for (size_t j = 0; j < templRow.size(); ++j)
887
{
888
Feature templF = templRow[j];
889
890
templF.p1.theta += angle;
891
892
templF.r1 *= scale;
893
templF.r2 *= scale;
894
895
templF.r1 = Point2d(cosVal * templF.r1.x - sinVal * templF.r1.y, sinVal * templF.r1.x + cosVal * templF.r1.y);
896
templF.r2 = Point2d(cosVal * templF.r2.x - sinVal * templF.r2.y, sinVal * templF.r2.x + cosVal * templF.r2.y);
897
898
for (size_t k = 0; k < imageRow.size(); ++k)
899
{
900
Feature imF = imageRow[k];
901
902
if (angleEq(imF.p1.theta, templF.p1.theta, angleEpsilon_))
903
{
904
Point2d c1, c2;
905
906
c1 = imF.p1.pos - templF.r1;
907
c1 *= idp;
908
909
c2 = imF.p2.pos - templF.r2;
910
c2 *= idp;
911
912
if (fabs(c1.x - c2.x) > 1 || fabs(c1.y - c2.y) > 1)
913
continue;
914
915
if (c1.y >= 0 && c1.y < histRows && c1.x >= 0 && c1.x < histCols)
916
++DHist.at<int>(cvRound(c1.y) + 1, cvRound(c1.x) + 1);
917
}
918
}
919
}
920
}
921
922
for(int y = 0; y < histRows; ++y)
923
{
924
const int* prevRow = DHist.ptr<int>(y);
925
const int* curRow = DHist.ptr<int>(y + 1);
926
const int* nextRow = DHist.ptr<int>(y + 2);
927
928
for(int x = 0; x < histCols; ++x)
929
{
930
const int votes = curRow[x + 1];
931
932
if (votes > posThresh_ && votes > curRow[x] && votes >= curRow[x + 2] && votes > prevRow[x + 1] && votes >= nextRow[x + 1])
933
{
934
posOutBuf_.push_back(Vec4f(static_cast<float>(x * dp_), static_cast<float>(y * dp_), static_cast<float>(scale), static_cast<float>(angle)));
935
voteOutBuf_.push_back(Vec3i(votes, scaleVotes, angleVotes));
936
}
937
}
938
}
939
}
940
}
941
942
Ptr<GeneralizedHoughGuil> cv::createGeneralizedHoughGuil()
943
{
944
return makePtr<GeneralizedHoughGuilImpl>();
945
}
946
947