Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/src/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
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
14
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
15
// Copyright (C) 2014, Itseez, Inc, all rights reserved.
16
// Third party copyrights are property of their respective owners.
17
//
18
// Redistribution and use in source and binary forms, with or without modification,
19
// are permitted provided that the following conditions are met:
20
//
21
// * Redistribution's of source code must retain the above copyright notice,
22
// this list of conditions and the following disclaimer.
23
//
24
// * Redistribution's in binary form must reproduce the above copyright notice,
25
// this list of conditions and the following disclaimer in the documentation
26
// and/or other materials provided with the distribution.
27
//
28
// * The name of the copyright holders may not be used to endorse or promote products
29
// derived from this software without specific prior written permission.
30
//
31
// This software is provided by the copyright holders and contributors "as is" and
32
// any express or implied warranties, including, but not limited to, the implied
33
// warranties of merchantability and fitness for a particular purpose are disclaimed.
34
// In no event shall the Intel Corporation or contributors be liable for any direct,
35
// indirect, incidental, special, exemplary, or consequential damages
36
// (including, but not limited to, procurement of substitute goods or services;
37
// loss of use, data, or profits; or business interruption) however caused
38
// and on any theory of liability, whether in contract, strict liability,
39
// or tort (including negligence or otherwise) arising in any way out of
40
// the use of this software, even if advised of the possibility of such damage.
41
//
42
//M*/
43
44
#include "precomp.hpp"
45
#include "opencl_kernels_imgproc.hpp"
46
#include "opencv2/core/hal/intrin.hpp"
47
#include <algorithm>
48
#include <iterator>
49
50
namespace cv
51
{
52
53
// Classical Hough Transform
54
struct LinePolar
55
{
56
float rho;
57
float angle;
58
};
59
60
61
struct hough_cmp_gt
62
{
63
hough_cmp_gt(const int* _aux) : aux(_aux) {}
64
inline bool operator()(int l1, int l2) const
65
{
66
return aux[l1] > aux[l2] || (aux[l1] == aux[l2] && l1 < l2);
67
}
68
const int* aux;
69
};
70
71
static void
72
createTrigTable( int numangle, double min_theta, double theta_step,
73
float irho, float *tabSin, float *tabCos )
74
{
75
float ang = static_cast<float>(min_theta);
76
for(int n = 0; n < numangle; ang += (float)theta_step, n++ )
77
{
78
tabSin[n] = (float)(sin((double)ang) * irho);
79
tabCos[n] = (float)(cos((double)ang) * irho);
80
}
81
}
82
83
static void
84
findLocalMaximums( int numrho, int numangle, int threshold,
85
const int *accum, std::vector<int>& sort_buf )
86
{
87
for(int r = 0; r < numrho; r++ )
88
for(int n = 0; n < numangle; n++ )
89
{
90
int base = (n+1) * (numrho+2) + r+1;
91
if( accum[base] > threshold &&
92
accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&
93
accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )
94
sort_buf.push_back(base);
95
}
96
}
97
98
/*
99
Here image is an input raster;
100
step is it's step; size characterizes it's ROI;
101
rho and theta are discretization steps (in pixels and radians correspondingly).
102
threshold is the minimum number of pixels in the feature for it
103
to be a candidate for line. lines is the output
104
array of (rho, theta) pairs. linesMax is the buffer size (number of pairs).
105
Functions return the actual number of found lines.
106
*/
107
static void
108
HoughLinesStandard( InputArray src, OutputArray lines, int type,
109
float rho, float theta,
110
int threshold, int linesMax,
111
double min_theta, double max_theta )
112
{
113
CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Internal error");
114
115
Mat img = src.getMat();
116
117
int i, j;
118
float irho = 1 / rho;
119
120
CV_Assert( img.type() == CV_8UC1 );
121
CV_Assert( linesMax > 0 );
122
123
const uchar* image = img.ptr();
124
int step = (int)img.step;
125
int width = img.cols;
126
int height = img.rows;
127
128
int max_rho = width + height;
129
int min_rho = -max_rho;
130
131
CV_CheckGE(max_theta, min_theta, "max_theta must be greater than min_theta");
132
133
int numangle = cvRound((max_theta - min_theta) / theta);
134
int numrho = cvRound(((max_rho - min_rho) + 1) / rho);
135
136
#if defined HAVE_IPP && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_HOUGH
137
if (type == CV_32FC2 && CV_IPP_CHECK_COND)
138
{
139
IppiSize srcSize = { width, height };
140
IppPointPolar delta = { rho, theta };
141
IppPointPolar dstRoi[2] = {{(Ipp32f) min_rho, (Ipp32f) min_theta},{(Ipp32f) max_rho, (Ipp32f) max_theta}};
142
int bufferSize;
143
int nz = countNonZero(img);
144
int ipp_linesMax = std::min(linesMax, nz*numangle/threshold);
145
int linesCount = 0;
146
std::vector<Vec2f> _lines(ipp_linesMax);
147
IppStatus ok = ippiHoughLineGetSize_8u_C1R(srcSize, delta, ipp_linesMax, &bufferSize);
148
Ipp8u* buffer = ippsMalloc_8u_L(bufferSize);
149
if (ok >= 0) {ok = CV_INSTRUMENT_FUN_IPP(ippiHoughLine_Region_8u32f_C1R, image, step, srcSize, (IppPointPolar*) &_lines[0], dstRoi, ipp_linesMax, &linesCount, delta, threshold, buffer);};
150
ippsFree(buffer);
151
if (ok >= 0)
152
{
153
lines.create(linesCount, 1, CV_32FC2);
154
Mat(linesCount, 1, CV_32FC2, &_lines[0]).copyTo(lines);
155
CV_IMPL_ADD(CV_IMPL_IPP);
156
return;
157
}
158
setIppErrorStatus();
159
}
160
#endif
161
162
163
Mat _accum = Mat::zeros( (numangle+2), (numrho+2), CV_32SC1 );
164
std::vector<int> _sort_buf;
165
AutoBuffer<float> _tabSin(numangle);
166
AutoBuffer<float> _tabCos(numangle);
167
int *accum = _accum.ptr<int>();
168
float *tabSin = _tabSin.data(), *tabCos = _tabCos.data();
169
170
// create sin and cos table
171
createTrigTable( numangle, min_theta, theta,
172
irho, tabSin, tabCos);
173
174
// stage 1. fill accumulator
175
for( i = 0; i < height; i++ )
176
for( j = 0; j < width; j++ )
177
{
178
if( image[i * step + j] != 0 )
179
for(int n = 0; n < numangle; n++ )
180
{
181
int r = cvRound( j * tabCos[n] + i * tabSin[n] );
182
r += (numrho - 1) / 2;
183
accum[(n+1) * (numrho+2) + r+1]++;
184
}
185
}
186
187
// stage 2. find local maximums
188
findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf );
189
190
// stage 3. sort the detected lines by accumulator value
191
std::sort(_sort_buf.begin(), _sort_buf.end(), hough_cmp_gt(accum));
192
193
// stage 4. store the first min(total,linesMax) lines to the output buffer
194
linesMax = std::min(linesMax, (int)_sort_buf.size());
195
double scale = 1./(numrho+2);
196
197
lines.create(linesMax, 1, type);
198
Mat _lines = lines.getMat();
199
for( i = 0; i < linesMax; i++ )
200
{
201
LinePolar line;
202
int idx = _sort_buf[i];
203
int n = cvFloor(idx*scale) - 1;
204
int r = idx - (n+1)*(numrho+2) - 1;
205
line.rho = (r - (numrho - 1)*0.5f) * rho;
206
line.angle = static_cast<float>(min_theta) + n * theta;
207
if (type == CV_32FC2)
208
{
209
_lines.at<Vec2f>(i) = Vec2f(line.rho, line.angle);
210
}
211
else
212
{
213
CV_DbgAssert(type == CV_32FC3);
214
_lines.at<Vec3f>(i) = Vec3f(line.rho, line.angle, (float)accum[idx]);
215
}
216
}
217
}
218
219
220
// Multi-Scale variant of Classical Hough Transform
221
222
struct hough_index
223
{
224
hough_index() : value(0), rho(0.f), theta(0.f) {}
225
hough_index(int _val, float _rho, float _theta)
226
: value(_val), rho(_rho), theta(_theta) {}
227
228
int value;
229
float rho, theta;
230
};
231
232
233
static void
234
HoughLinesSDiv( InputArray image, OutputArray lines, int type,
235
float rho, float theta, int threshold,
236
int srn, int stn, int linesMax,
237
double min_theta, double max_theta )
238
{
239
CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Internal error");
240
241
#define _POINT(row, column)\
242
(image_src[(row)*step+(column)])
243
244
Mat img = image.getMat();
245
int index, i;
246
int ri, ti, ti1, ti0;
247
int row, col;
248
float r, t; /* Current rho and theta */
249
float rv; /* Some temporary rho value */
250
251
int fn = 0;
252
float xc, yc;
253
254
const float d2r = (float)(CV_PI / 180);
255
int sfn = srn * stn;
256
int fi;
257
int count;
258
int cmax = 0;
259
260
std::vector<hough_index> lst;
261
262
CV_Assert( img.type() == CV_8UC1 );
263
CV_Assert( linesMax > 0 );
264
265
threshold = MIN( threshold, 255 );
266
267
const uchar* image_src = img.ptr();
268
int step = (int)img.step;
269
int w = img.cols;
270
int h = img.rows;
271
272
float irho = 1 / rho;
273
float itheta = 1 / theta;
274
float srho = rho / srn;
275
float stheta = theta / stn;
276
float isrho = 1 / srho;
277
float istheta = 1 / stheta;
278
279
int rn = cvFloor( std::sqrt( (double)w * w + (double)h * h ) * irho );
280
int tn = cvFloor( 2 * CV_PI * itheta );
281
282
lst.push_back(hough_index(threshold, -1.f, 0.f));
283
284
// Precalculate sin table
285
std::vector<float> _sinTable( 5 * tn * stn );
286
float* sinTable = &_sinTable[0];
287
288
for( index = 0; index < 5 * tn * stn; index++ )
289
sinTable[index] = (float)cos( stheta * index * 0.2f );
290
291
std::vector<uchar> _caccum(rn * tn, (uchar)0);
292
uchar* caccum = &_caccum[0];
293
294
// Counting all feature pixels
295
for( row = 0; row < h; row++ )
296
for( col = 0; col < w; col++ )
297
fn += _POINT( row, col ) != 0;
298
299
std::vector<int> _x(fn), _y(fn);
300
int* x = &_x[0], *y = &_y[0];
301
302
// Full Hough Transform (it's accumulator update part)
303
fi = 0;
304
for( row = 0; row < h; row++ )
305
{
306
for( col = 0; col < w; col++ )
307
{
308
if( _POINT( row, col ))
309
{
310
int halftn;
311
float r0;
312
float scale_factor;
313
int iprev = -1;
314
float phi, phi1;
315
float theta_it; // Value of theta for iterating
316
317
// Remember the feature point
318
x[fi] = col;
319
y[fi] = row;
320
fi++;
321
322
yc = (float) row + 0.5f;
323
xc = (float) col + 0.5f;
324
325
/* Update the accumulator */
326
t = (float) fabs( cvFastArctan( yc, xc ) * d2r );
327
r = (float) std::sqrt( (double)xc * xc + (double)yc * yc );
328
r0 = r * irho;
329
ti0 = cvFloor( (t + CV_PI*0.5) * itheta );
330
331
caccum[ti0]++;
332
333
theta_it = rho / r;
334
theta_it = theta_it < theta ? theta_it : theta;
335
scale_factor = theta_it * itheta;
336
halftn = cvFloor( CV_PI / theta_it );
337
for( ti1 = 1, phi = theta_it - (float)(CV_PI*0.5), phi1 = (theta_it + t) * itheta;
338
ti1 < halftn; ti1++, phi += theta_it, phi1 += scale_factor )
339
{
340
rv = r0 * std::cos( phi );
341
i = (int)rv * tn;
342
i += cvFloor( phi1 );
343
assert( i >= 0 );
344
assert( i < rn * tn );
345
caccum[i] = (uchar) (caccum[i] + ((i ^ iprev) != 0));
346
iprev = i;
347
if( cmax < caccum[i] )
348
cmax = caccum[i];
349
}
350
}
351
}
352
}
353
354
// Starting additional analysis
355
count = 0;
356
for( ri = 0; ri < rn; ri++ )
357
{
358
for( ti = 0; ti < tn; ti++ )
359
{
360
if( caccum[ri * tn + ti] > threshold )
361
count++;
362
}
363
}
364
365
if( count * 100 > rn * tn )
366
{
367
HoughLinesStandard( image, lines, type, rho, theta, threshold, linesMax, min_theta, max_theta );
368
return;
369
}
370
371
std::vector<uchar> _buffer(srn * stn + 2);
372
uchar* buffer = &_buffer[0];
373
uchar* mcaccum = buffer + 1;
374
375
count = 0;
376
for( ri = 0; ri < rn; ri++ )
377
{
378
for( ti = 0; ti < tn; ti++ )
379
{
380
if( caccum[ri * tn + ti] > threshold )
381
{
382
count++;
383
memset( mcaccum, 0, sfn * sizeof( uchar ));
384
385
for( index = 0; index < fn; index++ )
386
{
387
int ti2;
388
float r0;
389
390
yc = (float) y[index] + 0.5f;
391
xc = (float) x[index] + 0.5f;
392
393
// Update the accumulator
394
t = (float) fabs( cvFastArctan( yc, xc ) * d2r );
395
r = (float) std::sqrt( (double)xc * xc + (double)yc * yc ) * isrho;
396
ti0 = cvFloor( (t + CV_PI * 0.5) * istheta );
397
ti2 = (ti * stn - ti0) * 5;
398
r0 = (float) ri *srn;
399
400
for( ti1 = 0; ti1 < stn; ti1++, ti2 += 5 )
401
{
402
rv = r * sinTable[(int) (std::abs( ti2 ))] - r0;
403
i = cvFloor( rv ) * stn + ti1;
404
405
i = CV_IMAX( i, -1 );
406
i = CV_IMIN( i, sfn );
407
mcaccum[i]++;
408
assert( i >= -1 );
409
assert( i <= sfn );
410
}
411
}
412
413
// Find peaks in maccum...
414
for( index = 0; index < sfn; index++ )
415
{
416
int pos = (int)(lst.size() - 1);
417
if( pos < 0 || lst[pos].value < mcaccum[index] )
418
{
419
hough_index vi(mcaccum[index],
420
index / stn * srho + ri * rho,
421
index % stn * stheta + ti * theta - (float)(CV_PI*0.5));
422
lst.push_back(vi);
423
for( ; pos >= 0; pos-- )
424
{
425
if( lst[pos].value > vi.value )
426
break;
427
lst[pos+1] = lst[pos];
428
}
429
lst[pos+1] = vi;
430
if( (int)lst.size() > linesMax )
431
lst.pop_back();
432
}
433
}
434
}
435
}
436
}
437
438
lines.create((int)lst.size(), 1, type);
439
Mat _lines = lines.getMat();
440
for( size_t idx = 0; idx < lst.size(); idx++ )
441
{
442
if( lst[idx].rho < 0 )
443
continue;
444
if (type == CV_32FC2)
445
{
446
_lines.at<Vec2f>((int)idx) = Vec2f(lst[idx].rho, lst[idx].theta);
447
}
448
else
449
{
450
CV_DbgAssert(type == CV_32FC3);
451
_lines.at<Vec3f>((int)idx) = Vec3f(lst[idx].rho, lst[idx].theta, (float)lst[idx].value);
452
}
453
}
454
}
455
456
457
/****************************************************************************************\
458
* Probabilistic Hough Transform *
459
\****************************************************************************************/
460
461
static void
462
HoughLinesProbabilistic( Mat& image,
463
float rho, float theta, int threshold,
464
int lineLength, int lineGap,
465
std::vector<Vec4i>& lines, int linesMax )
466
{
467
Point pt;
468
float irho = 1 / rho;
469
RNG rng((uint64)-1);
470
471
CV_Assert( image.type() == CV_8UC1 );
472
473
int width = image.cols;
474
int height = image.rows;
475
476
int numangle = cvRound(CV_PI / theta);
477
int numrho = cvRound(((width + height) * 2 + 1) / rho);
478
479
#if defined HAVE_IPP && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_HOUGH
480
CV_IPP_CHECK()
481
{
482
IppiSize srcSize = { width, height };
483
IppPointPolar delta = { rho, theta };
484
IppiHoughProbSpec* pSpec;
485
int bufferSize, specSize;
486
int ipp_linesMax = std::min(linesMax, numangle*numrho);
487
int linesCount = 0;
488
lines.resize(ipp_linesMax);
489
IppStatus ok = ippiHoughProbLineGetSize_8u_C1R(srcSize, delta, &specSize, &bufferSize);
490
Ipp8u* buffer = ippsMalloc_8u_L(bufferSize);
491
pSpec = (IppiHoughProbSpec*) ippsMalloc_8u_L(specSize);
492
if (ok >= 0) ok = ippiHoughProbLineInit_8u32f_C1R(srcSize, delta, ippAlgHintNone, pSpec);
493
if (ok >= 0) {ok = CV_INSTRUMENT_FUN_IPP(ippiHoughProbLine_8u32f_C1R, image.data, (int)image.step, srcSize, threshold, lineLength, lineGap, (IppiPoint*) &lines[0], ipp_linesMax, &linesCount, buffer, pSpec);};
494
495
ippsFree(pSpec);
496
ippsFree(buffer);
497
if (ok >= 0)
498
{
499
lines.resize(linesCount);
500
CV_IMPL_ADD(CV_IMPL_IPP);
501
return;
502
}
503
lines.clear();
504
setIppErrorStatus();
505
}
506
#endif
507
508
Mat accum = Mat::zeros( numangle, numrho, CV_32SC1 );
509
Mat mask( height, width, CV_8UC1 );
510
std::vector<float> trigtab(numangle*2);
511
512
for( int n = 0; n < numangle; n++ )
513
{
514
trigtab[n*2] = (float)(cos((double)n*theta) * irho);
515
trigtab[n*2+1] = (float)(sin((double)n*theta) * irho);
516
}
517
const float* ttab = &trigtab[0];
518
uchar* mdata0 = mask.ptr();
519
std::vector<Point> nzloc;
520
521
// stage 1. collect non-zero image points
522
for( pt.y = 0; pt.y < height; pt.y++ )
523
{
524
const uchar* data = image.ptr(pt.y);
525
uchar* mdata = mask.ptr(pt.y);
526
for( pt.x = 0; pt.x < width; pt.x++ )
527
{
528
if( data[pt.x] )
529
{
530
mdata[pt.x] = (uchar)1;
531
nzloc.push_back(pt);
532
}
533
else
534
mdata[pt.x] = 0;
535
}
536
}
537
538
int count = (int)nzloc.size();
539
540
// stage 2. process all the points in random order
541
for( ; count > 0; count-- )
542
{
543
// choose random point out of the remaining ones
544
int idx = rng.uniform(0, count);
545
int max_val = threshold-1, max_n = 0;
546
Point point = nzloc[idx];
547
Point line_end[2];
548
float a, b;
549
int* adata = accum.ptr<int>();
550
int i = point.y, j = point.x, k, x0, y0, dx0, dy0, xflag;
551
int good_line;
552
const int shift = 16;
553
554
// "remove" it by overriding it with the last element
555
nzloc[idx] = nzloc[count-1];
556
557
// check if it has been excluded already (i.e. belongs to some other line)
558
if( !mdata0[i*width + j] )
559
continue;
560
561
// update accumulator, find the most probable line
562
for( int n = 0; n < numangle; n++, adata += numrho )
563
{
564
int r = cvRound( j * ttab[n*2] + i * ttab[n*2+1] );
565
r += (numrho - 1) / 2;
566
int val = ++adata[r];
567
if( max_val < val )
568
{
569
max_val = val;
570
max_n = n;
571
}
572
}
573
574
// if it is too "weak" candidate, continue with another point
575
if( max_val < threshold )
576
continue;
577
578
// from the current point walk in each direction
579
// along the found line and extract the line segment
580
a = -ttab[max_n*2+1];
581
b = ttab[max_n*2];
582
x0 = j;
583
y0 = i;
584
if( fabs(a) > fabs(b) )
585
{
586
xflag = 1;
587
dx0 = a > 0 ? 1 : -1;
588
dy0 = cvRound( b*(1 << shift)/fabs(a) );
589
y0 = (y0 << shift) + (1 << (shift-1));
590
}
591
else
592
{
593
xflag = 0;
594
dy0 = b > 0 ? 1 : -1;
595
dx0 = cvRound( a*(1 << shift)/fabs(b) );
596
x0 = (x0 << shift) + (1 << (shift-1));
597
}
598
599
for( k = 0; k < 2; k++ )
600
{
601
int gap = 0, x = x0, y = y0, dx = dx0, dy = dy0;
602
603
if( k > 0 )
604
dx = -dx, dy = -dy;
605
606
// walk along the line using fixed-point arithmetics,
607
// stop at the image border or in case of too big gap
608
for( ;; x += dx, y += dy )
609
{
610
uchar* mdata;
611
int i1, j1;
612
613
if( xflag )
614
{
615
j1 = x;
616
i1 = y >> shift;
617
}
618
else
619
{
620
j1 = x >> shift;
621
i1 = y;
622
}
623
624
if( j1 < 0 || j1 >= width || i1 < 0 || i1 >= height )
625
break;
626
627
mdata = mdata0 + i1*width + j1;
628
629
// for each non-zero point:
630
// update line end,
631
// clear the mask element
632
// reset the gap
633
if( *mdata )
634
{
635
gap = 0;
636
line_end[k].y = i1;
637
line_end[k].x = j1;
638
}
639
else if( ++gap > lineGap )
640
break;
641
}
642
}
643
644
good_line = std::abs(line_end[1].x - line_end[0].x) >= lineLength ||
645
std::abs(line_end[1].y - line_end[0].y) >= lineLength;
646
647
for( k = 0; k < 2; k++ )
648
{
649
int x = x0, y = y0, dx = dx0, dy = dy0;
650
651
if( k > 0 )
652
dx = -dx, dy = -dy;
653
654
// walk along the line using fixed-point arithmetics,
655
// stop at the image border or in case of too big gap
656
for( ;; x += dx, y += dy )
657
{
658
uchar* mdata;
659
int i1, j1;
660
661
if( xflag )
662
{
663
j1 = x;
664
i1 = y >> shift;
665
}
666
else
667
{
668
j1 = x >> shift;
669
i1 = y;
670
}
671
672
mdata = mdata0 + i1*width + j1;
673
674
// for each non-zero point:
675
// update line end,
676
// clear the mask element
677
// reset the gap
678
if( *mdata )
679
{
680
if( good_line )
681
{
682
adata = accum.ptr<int>();
683
for( int n = 0; n < numangle; n++, adata += numrho )
684
{
685
int r = cvRound( j1 * ttab[n*2] + i1 * ttab[n*2+1] );
686
r += (numrho - 1) / 2;
687
adata[r]--;
688
}
689
}
690
*mdata = 0;
691
}
692
693
if( i1 == line_end[k].y && j1 == line_end[k].x )
694
break;
695
}
696
}
697
698
if( good_line )
699
{
700
Vec4i lr(line_end[0].x, line_end[0].y, line_end[1].x, line_end[1].y);
701
lines.push_back(lr);
702
if( (int)lines.size() >= linesMax )
703
return;
704
}
705
}
706
}
707
708
#ifdef HAVE_OPENCL
709
710
#define OCL_MAX_LINES 4096
711
712
static bool ocl_makePointsList(InputArray _src, OutputArray _pointsList, InputOutputArray _counters)
713
{
714
UMat src = _src.getUMat();
715
_pointsList.create(1, (int) src.total(), CV_32SC1);
716
UMat pointsList = _pointsList.getUMat();
717
UMat counters = _counters.getUMat();
718
ocl::Device dev = ocl::Device::getDefault();
719
720
const int pixPerWI = 16;
721
int workgroup_size = min((int) dev.maxWorkGroupSize(), (src.cols + pixPerWI - 1)/pixPerWI);
722
ocl::Kernel pointListKernel("make_point_list", ocl::imgproc::hough_lines_oclsrc,
723
format("-D MAKE_POINTS_LIST -D GROUP_SIZE=%d -D LOCAL_SIZE=%d", workgroup_size, src.cols));
724
if (pointListKernel.empty())
725
return false;
726
727
pointListKernel.args(ocl::KernelArg::ReadOnly(src), ocl::KernelArg::WriteOnlyNoSize(pointsList),
728
ocl::KernelArg::PtrWriteOnly(counters));
729
730
size_t localThreads[2] = { (size_t)workgroup_size, 1 };
731
size_t globalThreads[2] = { (size_t)workgroup_size, (size_t)src.rows };
732
733
return pointListKernel.run(2, globalThreads, localThreads, false);
734
}
735
736
static bool ocl_fillAccum(InputArray _pointsList, OutputArray _accum, int total_points, double rho, double theta, int numrho, int numangle)
737
{
738
UMat pointsList = _pointsList.getUMat();
739
_accum.create(numangle + 2, numrho + 2, CV_32SC1);
740
UMat accum = _accum.getUMat();
741
ocl::Device dev = ocl::Device::getDefault();
742
743
float irho = (float) (1 / rho);
744
int workgroup_size = min((int) dev.maxWorkGroupSize(), total_points);
745
746
ocl::Kernel fillAccumKernel;
747
size_t localThreads[2];
748
size_t globalThreads[2];
749
750
size_t local_memory_needed = (numrho + 2)*sizeof(int);
751
if (local_memory_needed > dev.localMemSize())
752
{
753
accum.setTo(Scalar::all(0));
754
fillAccumKernel.create("fill_accum_global", ocl::imgproc::hough_lines_oclsrc,
755
format("-D FILL_ACCUM_GLOBAL"));
756
if (fillAccumKernel.empty())
757
return false;
758
globalThreads[0] = workgroup_size; globalThreads[1] = numangle;
759
fillAccumKernel.args(ocl::KernelArg::ReadOnlyNoSize(pointsList), ocl::KernelArg::WriteOnlyNoSize(accum),
760
total_points, irho, (float) theta, numrho, numangle);
761
return fillAccumKernel.run(2, globalThreads, NULL, false);
762
}
763
else
764
{
765
fillAccumKernel.create("fill_accum_local", ocl::imgproc::hough_lines_oclsrc,
766
format("-D FILL_ACCUM_LOCAL -D LOCAL_SIZE=%d -D BUFFER_SIZE=%d", workgroup_size, numrho + 2));
767
if (fillAccumKernel.empty())
768
return false;
769
localThreads[0] = workgroup_size; localThreads[1] = 1;
770
globalThreads[0] = workgroup_size; globalThreads[1] = numangle+2;
771
fillAccumKernel.args(ocl::KernelArg::ReadOnlyNoSize(pointsList), ocl::KernelArg::WriteOnlyNoSize(accum),
772
total_points, irho, (float) theta, numrho, numangle);
773
return fillAccumKernel.run(2, globalThreads, localThreads, false);
774
}
775
}
776
777
static bool ocl_HoughLines(InputArray _src, OutputArray _lines, double rho, double theta, int threshold,
778
double min_theta, double max_theta)
779
{
780
CV_Assert(_src.type() == CV_8UC1);
781
782
if (max_theta < 0 || max_theta > CV_PI ) {
783
CV_Error( Error::StsBadArg, "max_theta must fall between 0 and pi" );
784
}
785
if (min_theta < 0 || min_theta > max_theta ) {
786
CV_Error( Error::StsBadArg, "min_theta must fall between 0 and max_theta" );
787
}
788
if (!(rho > 0 && theta > 0)) {
789
CV_Error( Error::StsBadArg, "rho and theta must be greater 0" );
790
}
791
792
UMat src = _src.getUMat();
793
int numangle = cvRound((max_theta - min_theta) / theta);
794
int numrho = cvRound(((src.cols + src.rows) * 2 + 1) / rho);
795
796
UMat pointsList;
797
UMat counters(1, 2, CV_32SC1, Scalar::all(0));
798
799
if (!ocl_makePointsList(src, pointsList, counters))
800
return false;
801
802
int total_points = counters.getMat(ACCESS_READ).at<int>(0, 0);
803
if (total_points <= 0)
804
{
805
_lines.release();
806
return true;
807
}
808
809
UMat accum;
810
if (!ocl_fillAccum(pointsList, accum, total_points, rho, theta, numrho, numangle))
811
return false;
812
813
const int pixPerWI = 8;
814
ocl::Kernel getLinesKernel("get_lines", ocl::imgproc::hough_lines_oclsrc,
815
format("-D GET_LINES"));
816
if (getLinesKernel.empty())
817
return false;
818
819
int linesMax = threshold > 0 ? min(total_points*numangle/threshold, OCL_MAX_LINES) : OCL_MAX_LINES;
820
UMat lines(linesMax, 1, CV_32FC2);
821
822
getLinesKernel.args(ocl::KernelArg::ReadOnly(accum), ocl::KernelArg::WriteOnlyNoSize(lines),
823
ocl::KernelArg::PtrWriteOnly(counters), linesMax, threshold, (float) rho, (float) theta);
824
825
size_t globalThreads[2] = { ((size_t)numrho + pixPerWI - 1)/pixPerWI, (size_t)numangle };
826
if (!getLinesKernel.run(2, globalThreads, NULL, false))
827
return false;
828
829
int total_lines = min(counters.getMat(ACCESS_READ).at<int>(0, 1), linesMax);
830
if (total_lines > 0)
831
_lines.assign(lines.rowRange(Range(0, total_lines)));
832
else
833
_lines.release();
834
return true;
835
}
836
837
static bool ocl_HoughLinesP(InputArray _src, OutputArray _lines, double rho, double theta, int threshold,
838
double minLineLength, double maxGap)
839
{
840
CV_Assert(_src.type() == CV_8UC1);
841
842
if (!(rho > 0 && theta > 0)) {
843
CV_Error( Error::StsBadArg, "rho and theta must be greater 0" );
844
}
845
846
UMat src = _src.getUMat();
847
int numangle = cvRound(CV_PI / theta);
848
int numrho = cvRound(((src.cols + src.rows) * 2 + 1) / rho);
849
850
UMat pointsList;
851
UMat counters(1, 2, CV_32SC1, Scalar::all(0));
852
853
if (!ocl_makePointsList(src, pointsList, counters))
854
return false;
855
856
int total_points = counters.getMat(ACCESS_READ).at<int>(0, 0);
857
if (total_points <= 0)
858
{
859
_lines.release();
860
return true;
861
}
862
863
UMat accum;
864
if (!ocl_fillAccum(pointsList, accum, total_points, rho, theta, numrho, numangle))
865
return false;
866
867
ocl::Kernel getLinesKernel("get_lines", ocl::imgproc::hough_lines_oclsrc,
868
format("-D GET_LINES_PROBABOLISTIC"));
869
if (getLinesKernel.empty())
870
return false;
871
872
int linesMax = threshold > 0 ? min(total_points*numangle/threshold, OCL_MAX_LINES) : OCL_MAX_LINES;
873
UMat lines(linesMax, 1, CV_32SC4);
874
875
getLinesKernel.args(ocl::KernelArg::ReadOnly(accum), ocl::KernelArg::ReadOnly(src),
876
ocl::KernelArg::WriteOnlyNoSize(lines), ocl::KernelArg::PtrWriteOnly(counters),
877
linesMax, threshold, (int) minLineLength, (int) maxGap, (float) rho, (float) theta);
878
879
size_t globalThreads[2] = { (size_t)numrho, (size_t)numangle };
880
if (!getLinesKernel.run(2, globalThreads, NULL, false))
881
return false;
882
883
int total_lines = min(counters.getMat(ACCESS_READ).at<int>(0, 1), linesMax);
884
if (total_lines > 0)
885
_lines.assign(lines.rowRange(Range(0, total_lines)));
886
else
887
_lines.release();
888
889
return true;
890
}
891
892
#endif /* HAVE_OPENCL */
893
894
void HoughLines( InputArray _image, OutputArray lines,
895
double rho, double theta, int threshold,
896
double srn, double stn, double min_theta, double max_theta )
897
{
898
CV_INSTRUMENT_REGION();
899
900
int type = CV_32FC2;
901
if (lines.fixedType())
902
{
903
type = lines.type();
904
CV_CheckType(type, type == CV_32FC2 || type == CV_32FC3, "Wrong type of output lines");
905
}
906
907
CV_OCL_RUN(srn == 0 && stn == 0 && _image.isUMat() && lines.isUMat() && type == CV_32FC2,
908
ocl_HoughLines(_image, lines, rho, theta, threshold, min_theta, max_theta));
909
910
if( srn == 0 && stn == 0 )
911
HoughLinesStandard(_image, lines, type, (float)rho, (float)theta, threshold, INT_MAX, min_theta, max_theta );
912
else
913
HoughLinesSDiv(_image, lines, type, (float)rho, (float)theta, threshold, cvRound(srn), cvRound(stn), INT_MAX, min_theta, max_theta);
914
}
915
916
917
void HoughLinesP(InputArray _image, OutputArray _lines,
918
double rho, double theta, int threshold,
919
double minLineLength, double maxGap )
920
{
921
CV_INSTRUMENT_REGION();
922
923
CV_OCL_RUN(_image.isUMat() && _lines.isUMat(),
924
ocl_HoughLinesP(_image, _lines, rho, theta, threshold, minLineLength, maxGap));
925
926
Mat image = _image.getMat();
927
std::vector<Vec4i> lines;
928
HoughLinesProbabilistic(image, (float)rho, (float)theta, threshold, cvRound(minLineLength), cvRound(maxGap), lines, INT_MAX);
929
Mat(lines).copyTo(_lines);
930
}
931
932
void HoughLinesPointSet( InputArray _point, OutputArray _lines, int lines_max, int threshold,
933
double min_rho, double max_rho, double rho_step,
934
double min_theta, double max_theta, double theta_step )
935
{
936
std::vector<Vec3d> lines;
937
std::vector<Point2f> point;
938
_point.copyTo(point);
939
940
CV_Assert( _point.type() == CV_32FC2 || _point.type() == CV_32SC2 );
941
if( lines_max <= 0 ) {
942
CV_Error( Error::StsBadArg, "lines_max must be greater than 0" );
943
}
944
if( threshold < 0) {
945
CV_Error( Error::StsBadArg, "threshold must be greater than 0" );
946
}
947
if( ((max_rho - min_rho) <= 0) || ((max_theta - min_theta) <= 0) ) {
948
CV_Error( Error::StsBadArg, "max must be greater than min" );
949
}
950
if( ((rho_step <= 0)) || ((theta_step <= 0)) ) {
951
CV_Error( Error::StsBadArg, "step must be greater than 0" );
952
}
953
954
int i;
955
float irho = 1 / (float)rho_step;
956
float irho_min = ((float)min_rho * irho);
957
int numangle = cvRound((max_theta - min_theta) / theta_step);
958
int numrho = cvRound((max_rho - min_rho + 1) / rho_step);
959
960
Mat _accum = Mat::zeros( (numangle+2), (numrho+2), CV_32SC1 );
961
std::vector<int> _sort_buf;
962
AutoBuffer<float> _tabSin(numangle);
963
AutoBuffer<float> _tabCos(numangle);
964
int *accum = _accum.ptr<int>();
965
float *tabSin = _tabSin.data(), *tabCos = _tabCos.data();
966
967
// create sin and cos table
968
createTrigTable( numangle, min_theta, theta_step,
969
irho, tabSin, tabCos );
970
971
// stage 1. fill accumlator
972
for( i = 0; i < (int)point.size(); i++ )
973
for(int n = 0; n < numangle; n++ )
974
{
975
int r = cvRound( point.at(i).x * tabCos[n] + point.at(i).y * tabSin[n] - irho_min);
976
accum[(n+1) * (numrho+2) + r+1]++;
977
}
978
979
// stage 2. find local maximums
980
findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf );
981
982
// stage 3. sort the detected lines by accumulator value
983
std::sort(_sort_buf.begin(), _sort_buf.end(), hough_cmp_gt(accum));
984
985
// stage 4. store the first min(total,linesMax) lines to the output buffer
986
lines_max = std::min(lines_max, (int)_sort_buf.size());
987
double scale = 1./(numrho+2);
988
for( i = 0; i < lines_max; i++ )
989
{
990
LinePolar line;
991
int idx = _sort_buf[i];
992
int n = cvFloor(idx*scale) - 1;
993
int r = idx - (n+1)*(numrho+2) - 1;
994
line.rho = static_cast<float>(min_rho) + r * (float)rho_step;
995
line.angle = static_cast<float>(min_theta) + n * (float)theta_step;
996
lines.push_back(Vec3d((double)accum[idx], (double)line.rho, (double)line.angle));
997
}
998
999
Mat(lines).copyTo(_lines);
1000
}
1001
1002
/****************************************************************************************\
1003
* Circle Detection *
1004
\****************************************************************************************/
1005
1006
struct EstimatedCircle
1007
{
1008
EstimatedCircle(Vec3f _c, int _accum) :
1009
c(_c), accum(_accum) {}
1010
Vec3f c;
1011
int accum;
1012
};
1013
1014
static bool cmpAccum(const EstimatedCircle& left, const EstimatedCircle& right)
1015
{
1016
// Compare everything so the order is completely deterministic
1017
// Larger accum first
1018
if (left.accum > right.accum)
1019
return true;
1020
else if (left.accum < right.accum)
1021
return false;
1022
// Larger radius first
1023
else if (left.c[2] > right.c[2])
1024
return true;
1025
else if (left.c[2] < right.c[2])
1026
return false;
1027
// Smaller X
1028
else if (left.c[0] < right.c[0])
1029
return true;
1030
else if (left.c[0] > right.c[0])
1031
return false;
1032
// Smaller Y
1033
else if (left.c[1] < right.c[1])
1034
return true;
1035
else if (left.c[1] > right.c[1])
1036
return false;
1037
// Identical - neither object is less than the other
1038
else
1039
return false;
1040
}
1041
1042
static inline Vec3f GetCircle(const EstimatedCircle& est)
1043
{
1044
return est.c;
1045
}
1046
1047
static inline Vec4f GetCircle4f(const EstimatedCircle& est)
1048
{
1049
return Vec4f(est.c[0], est.c[1], est.c[2], (float)est.accum);
1050
}
1051
1052
class NZPointList : public std::vector<Point>
1053
{
1054
private:
1055
NZPointList(const NZPointList& other); // non-copyable
1056
1057
public:
1058
NZPointList(int reserveSize = 256)
1059
{
1060
reserve(reserveSize);
1061
}
1062
};
1063
1064
class NZPointSet
1065
{
1066
private:
1067
NZPointSet(const NZPointSet& other); // non-copyable
1068
1069
public:
1070
Mat_<uchar> positions;
1071
1072
NZPointSet(int rows, int cols) :
1073
positions(rows, cols, (uchar)0)
1074
{
1075
}
1076
1077
void insert(const Point& pt)
1078
{
1079
positions(pt) = 1;
1080
}
1081
1082
void insert(const NZPointSet& from)
1083
{
1084
cv::bitwise_or(from.positions, positions, positions);
1085
}
1086
1087
void toList(NZPointList& list) const
1088
{
1089
for (int y = 0; y < positions.rows; y++)
1090
{
1091
const uchar *ptr = positions.ptr<uchar>(y, 0);
1092
for (int x = 0; x < positions.cols; x++)
1093
{
1094
if (ptr[x])
1095
{
1096
list.push_back(Point(x, y));
1097
}
1098
}
1099
}
1100
}
1101
};
1102
1103
class HoughCirclesAccumInvoker : public ParallelLoopBody
1104
{
1105
public:
1106
HoughCirclesAccumInvoker(const Mat &_edges, const Mat &_dx, const Mat &_dy, int _minRadius, int _maxRadius, float _idp,
1107
std::vector<Mat>& _accumVec, NZPointSet& _nz, Mutex& _mtx) :
1108
edges(_edges), dx(_dx), dy(_dy), minRadius(_minRadius), maxRadius(_maxRadius), idp(_idp),
1109
accumVec(_accumVec), nz(_nz), mutex(_mtx)
1110
{
1111
acols = cvCeil(edges.cols * idp), arows = cvCeil(edges.rows * idp);
1112
astep = acols + 2;
1113
}
1114
1115
~HoughCirclesAccumInvoker() { }
1116
1117
void operator()(const Range &boundaries) const CV_OVERRIDE
1118
{
1119
Mat accumLocal = Mat(arows + 2, acols + 2, CV_32SC1, Scalar::all(0));
1120
int *adataLocal = accumLocal.ptr<int>();
1121
NZPointSet nzLocal(nz.positions.rows, nz.positions.cols);
1122
int startRow = boundaries.start;
1123
int endRow = boundaries.end;
1124
int numCols = edges.cols;
1125
1126
if(edges.isContinuous() && dx.isContinuous() && dy.isContinuous())
1127
{
1128
numCols *= (boundaries.end - boundaries.start);
1129
endRow = boundaries.start + 1;
1130
}
1131
1132
// Accumulate circle evidence for each edge pixel
1133
for(int y = startRow; y < endRow; ++y )
1134
{
1135
const uchar* edgeData = edges.ptr<const uchar>(y);
1136
const short* dxData = dx.ptr<const short>(y);
1137
const short* dyData = dy.ptr<const short>(y);
1138
int x = 0;
1139
1140
for(; x < numCols; ++x )
1141
{
1142
#if CV_SIMD128
1143
{
1144
v_uint8x16 v_zero = v_setzero_u8();
1145
1146
for(; x <= numCols - 32; x += 32) {
1147
v_uint8x16 v_edge1 = v_load(edgeData + x);
1148
v_uint8x16 v_edge2 = v_load(edgeData + x + 16);
1149
1150
v_uint8x16 v_cmp1 = (v_edge1 == v_zero);
1151
v_uint8x16 v_cmp2 = (v_edge2 == v_zero);
1152
1153
unsigned int mask1 = v_signmask(v_cmp1);
1154
unsigned int mask2 = v_signmask(v_cmp2);
1155
1156
mask1 ^= 0x0000ffff;
1157
mask2 ^= 0x0000ffff;
1158
1159
if(mask1)
1160
{
1161
x += trailingZeros32(mask1);
1162
goto _next_step;
1163
}
1164
1165
if(mask2)
1166
{
1167
x += trailingZeros32(mask2 << 16);
1168
goto _next_step;
1169
}
1170
}
1171
}
1172
#endif
1173
for(; x < numCols && !edgeData[x]; ++x)
1174
;
1175
1176
if(x == numCols)
1177
continue;
1178
#if CV_SIMD128
1179
_next_step:
1180
#endif
1181
float vx, vy;
1182
int sx, sy, x0, y0, x1, y1;
1183
1184
vx = dxData[x];
1185
vy = dyData[x];
1186
1187
if(vx == 0 && vy == 0)
1188
continue;
1189
1190
float mag = std::sqrt(vx*vx+vy*vy);
1191
1192
if(mag < 1.0f)
1193
continue;
1194
1195
Point pt = Point(x % edges.cols, y + x / edges.cols);
1196
nzLocal.insert(pt);
1197
1198
sx = cvRound((vx * idp) * 1024 / mag);
1199
sy = cvRound((vy * idp) * 1024 / mag);
1200
1201
x0 = cvRound((pt.x * idp) * 1024);
1202
y0 = cvRound((pt.y * idp) * 1024);
1203
1204
// Step from min_radius to max_radius in both directions of the gradient
1205
for(int k1 = 0; k1 < 2; k1++ )
1206
{
1207
x1 = x0 + minRadius * sx;
1208
y1 = y0 + minRadius * sy;
1209
1210
for(int r = minRadius; r <= maxRadius; x1 += sx, y1 += sy, r++ )
1211
{
1212
int x2 = x1 >> 10, y2 = y1 >> 10;
1213
if( (unsigned)x2 >= (unsigned)acols ||
1214
(unsigned)y2 >= (unsigned)arows )
1215
break;
1216
1217
adataLocal[y2*astep + x2]++;
1218
}
1219
1220
sx = -sx; sy = -sy;
1221
}
1222
}
1223
}
1224
1225
{ // TODO Try using TLSContainers
1226
AutoLock lock(mutex);
1227
accumVec.push_back(accumLocal);
1228
nz.insert(nzLocal);
1229
}
1230
}
1231
1232
private:
1233
const Mat &edges, &dx, &dy;
1234
int minRadius, maxRadius;
1235
float idp;
1236
std::vector<Mat>& accumVec;
1237
NZPointSet& nz;
1238
1239
int acols, arows, astep;
1240
1241
Mutex& mutex;
1242
};
1243
1244
class HoughCirclesFindCentersInvoker : public ParallelLoopBody
1245
{
1246
public:
1247
HoughCirclesFindCentersInvoker(const Mat &_accum, std::vector<int> &_centers, int _accThreshold, Mutex& _mutex) :
1248
accum(_accum), centers(_centers), accThreshold(_accThreshold), _lock(_mutex)
1249
{
1250
acols = accum.cols;
1251
arows = accum.rows;
1252
adata = accum.ptr<int>();
1253
}
1254
1255
~HoughCirclesFindCentersInvoker() {}
1256
1257
void operator()(const Range &boundaries) const CV_OVERRIDE
1258
{
1259
int startRow = boundaries.start;
1260
int endRow = boundaries.end;
1261
std::vector<int> centersLocal;
1262
bool singleThread = (boundaries == Range(1, accum.rows - 1));
1263
1264
startRow = max(1, startRow);
1265
endRow = min(arows - 1, endRow);
1266
1267
//Find possible circle centers
1268
for(int y = startRow; y < endRow; ++y )
1269
{
1270
int x = 1;
1271
int base = y * acols + x;
1272
1273
for(; x < acols - 1; ++x, ++base )
1274
{
1275
if( adata[base] > accThreshold &&
1276
adata[base] > adata[base-1] && adata[base] >= adata[base+1] &&
1277
adata[base] > adata[base-acols] && adata[base] >= adata[base+acols] )
1278
centersLocal.push_back(base);
1279
}
1280
}
1281
1282
if(!centersLocal.empty())
1283
{
1284
if(singleThread)
1285
centers = centersLocal;
1286
else
1287
{
1288
AutoLock alock(_lock);
1289
centers.insert(centers.end(), centersLocal.begin(), centersLocal.end());
1290
}
1291
}
1292
}
1293
1294
private:
1295
const Mat &accum;
1296
std::vector<int> &centers;
1297
int accThreshold;
1298
1299
int acols, arows;
1300
const int *adata;
1301
Mutex& _lock;
1302
};
1303
1304
template<typename T>
1305
static bool CheckDistance(const std::vector<T> &circles, size_t endIdx, const T& circle, float minDist2)
1306
{
1307
bool goodPoint = true;
1308
for (uint j = 0; j < endIdx; ++j)
1309
{
1310
T pt = circles[j];
1311
float distX = circle[0] - pt[0], distY = circle[1] - pt[1];
1312
if (distX * distX + distY * distY < minDist2)
1313
{
1314
goodPoint = false;
1315
break;
1316
}
1317
}
1318
return goodPoint;
1319
}
1320
1321
static void GetCircleCenters(const std::vector<int> &centers, std::vector<Vec3f> &circles, int acols, float minDist, float dr)
1322
{
1323
size_t centerCnt = centers.size();
1324
float minDist2 = minDist * minDist;
1325
for (size_t i = 0; i < centerCnt; ++i)
1326
{
1327
int center = centers[i];
1328
int y = center / acols;
1329
int x = center - y * acols;
1330
Vec3f circle = Vec3f((x + 0.5f) * dr, (y + 0.5f) * dr, 0);
1331
1332
bool goodPoint = CheckDistance(circles, circles.size(), circle, minDist2);
1333
if (goodPoint)
1334
circles.push_back(circle);
1335
}
1336
}
1337
1338
static void GetCircleCenters(const std::vector<int> &centers, std::vector<Vec4f> &circles, int acols, float minDist, float dr)
1339
{
1340
size_t centerCnt = centers.size();
1341
float minDist2 = minDist * minDist;
1342
for (size_t i = 0; i < centerCnt; ++i)
1343
{
1344
int center = centers[i];
1345
int y = center / acols;
1346
int x = center - y * acols;
1347
Vec4f circle = Vec4f((x + 0.5f) * dr, (y + 0.5f) * dr, 0, (float)center);
1348
1349
bool goodPoint = CheckDistance(circles, circles.size(), circle, minDist2);
1350
if (goodPoint)
1351
circles.push_back(circle);
1352
}
1353
}
1354
1355
template<typename T>
1356
static void RemoveOverlaps(std::vector<T>& circles, float minDist)
1357
{
1358
float minDist2 = minDist * minDist;
1359
size_t endIdx = 1;
1360
for (size_t i = 1; i < circles.size(); ++i)
1361
{
1362
T circle = circles[i];
1363
if (CheckDistance(circles, endIdx, circle, minDist2))
1364
{
1365
circles[endIdx] = circle;
1366
++endIdx;
1367
}
1368
}
1369
circles.resize(endIdx);
1370
}
1371
1372
static void CreateCircles(const std::vector<EstimatedCircle>& circlesEst, std::vector<Vec3f>& circles)
1373
{
1374
std::transform(circlesEst.begin(), circlesEst.end(), std::back_inserter(circles), GetCircle);
1375
}
1376
1377
static void CreateCircles(const std::vector<EstimatedCircle>& circlesEst, std::vector<Vec4f>& circles)
1378
{
1379
std::transform(circlesEst.begin(), circlesEst.end(), std::back_inserter(circles), GetCircle4f);
1380
}
1381
1382
template<class NZPoints>
1383
class HoughCircleEstimateRadiusInvoker : public ParallelLoopBody
1384
{
1385
public:
1386
HoughCircleEstimateRadiusInvoker(const NZPoints &_nz, int _nzSz, const std::vector<int> &_centers, std::vector<EstimatedCircle> &_circlesEst,
1387
int _acols, int _accThreshold, int _minRadius, int _maxRadius,
1388
float _dp, Mutex& _mutex) :
1389
nz(_nz), nzSz(_nzSz), centers(_centers), circlesEst(_circlesEst), acols(_acols), accThreshold(_accThreshold),
1390
minRadius(_minRadius), maxRadius(_maxRadius), dr(_dp), _lock(_mutex)
1391
{
1392
minRadius2 = (float)minRadius * minRadius;
1393
maxRadius2 = (float)maxRadius * maxRadius;
1394
centerSz = (int)centers.size();
1395
CV_Assert(nzSz > 0);
1396
}
1397
1398
~HoughCircleEstimateRadiusInvoker() {}
1399
1400
protected:
1401
inline int filterCircles(const Point2f& curCenter, float* ddata) const;
1402
1403
void operator()(const Range &boundaries) const CV_OVERRIDE
1404
{
1405
std::vector<EstimatedCircle> circlesLocal;
1406
const int nBinsPerDr = 10;
1407
int nBins = cvRound((maxRadius - minRadius)/dr*nBinsPerDr);
1408
AutoBuffer<int> bins(nBins);
1409
AutoBuffer<float> distBuf(nzSz), distSqrtBuf(nzSz);
1410
float *ddata = distBuf.data();
1411
float *dSqrtData = distSqrtBuf.data();
1412
1413
bool singleThread = (boundaries == Range(0, centerSz));
1414
int i = boundaries.start;
1415
1416
// For each found possible center
1417
// Estimate radius and check support
1418
for(; i < boundaries.end; ++i)
1419
{
1420
int ofs = centers[i];
1421
int y = ofs / acols;
1422
int x = ofs - y * acols;
1423
1424
//Calculate circle's center in pixels
1425
Point2f curCenter = Point2f((x + 0.5f) * dr, (y + 0.5f) * dr);
1426
int nzCount = filterCircles(curCenter, ddata);
1427
1428
int maxCount = 0;
1429
float rBest = 0;
1430
if(nzCount)
1431
{
1432
Mat_<float> distMat(1, nzCount, ddata);
1433
Mat_<float> distSqrtMat(1, nzCount, dSqrtData);
1434
sqrt(distMat, distSqrtMat);
1435
1436
memset(bins.data(), 0, sizeof(bins[0])*bins.size());
1437
for(int k = 0; k < nzCount; k++)
1438
{
1439
int bin = std::max(0, std::min(nBins-1, cvRound((dSqrtData[k] - minRadius)/dr*nBinsPerDr)));
1440
bins[bin]++;
1441
}
1442
1443
for(int j = nBins - 1; j > 0; j--)
1444
{
1445
if(bins[j])
1446
{
1447
int upbin = j;
1448
int curCount = 0;
1449
for(; j > upbin - nBinsPerDr && j >= 0; j--)
1450
{
1451
curCount += bins[j];
1452
}
1453
float rCur = (upbin + j)/2.f /nBinsPerDr * dr + minRadius;
1454
if((curCount * rBest >= maxCount * rCur) ||
1455
(rBest < FLT_EPSILON && curCount >= maxCount))
1456
{
1457
rBest = rCur;
1458
maxCount = curCount;
1459
}
1460
}
1461
}
1462
}
1463
1464
// Check if the circle has enough support
1465
if(maxCount > accThreshold)
1466
{
1467
circlesLocal.push_back(EstimatedCircle(Vec3f(curCenter.x, curCenter.y, rBest), maxCount));
1468
}
1469
}
1470
1471
if(!circlesLocal.empty())
1472
{
1473
std::sort(circlesLocal.begin(), circlesLocal.end(), cmpAccum);
1474
if(singleThread)
1475
{
1476
std::swap(circlesEst, circlesLocal);
1477
}
1478
else
1479
{
1480
AutoLock alock(_lock);
1481
if (circlesEst.empty())
1482
std::swap(circlesEst, circlesLocal);
1483
else
1484
circlesEst.insert(circlesEst.end(), circlesLocal.begin(), circlesLocal.end());
1485
}
1486
}
1487
}
1488
1489
private:
1490
const NZPoints &nz;
1491
int nzSz;
1492
const std::vector<int> &centers;
1493
std::vector<EstimatedCircle> &circlesEst;
1494
int acols, accThreshold, minRadius, maxRadius;
1495
float dr;
1496
int centerSz;
1497
float minRadius2, maxRadius2;
1498
Mutex& _lock;
1499
};
1500
1501
template<>
1502
inline int HoughCircleEstimateRadiusInvoker<NZPointList>::filterCircles(const Point2f& curCenter, float* ddata) const
1503
{
1504
int nzCount = 0;
1505
const Point* nz_ = &nz[0];
1506
int j = 0;
1507
#if CV_SIMD128
1508
{
1509
const v_float32x4 v_minRadius2 = v_setall_f32(minRadius2);
1510
const v_float32x4 v_maxRadius2 = v_setall_f32(maxRadius2);
1511
1512
v_float32x4 v_curCenterX = v_setall_f32(curCenter.x);
1513
v_float32x4 v_curCenterY = v_setall_f32(curCenter.y);
1514
1515
float CV_DECL_ALIGNED(16) rbuf[4];
1516
for(; j <= nzSz - 4; j += 4)
1517
{
1518
v_float32x4 v_nzX, v_nzY;
1519
v_load_deinterleave((const float*)&nz_[j], v_nzX, v_nzY); // FIXIT use proper datatype
1520
1521
v_float32x4 v_x = v_cvt_f32(v_reinterpret_as_s32(v_nzX));
1522
v_float32x4 v_y = v_cvt_f32(v_reinterpret_as_s32(v_nzY));
1523
1524
v_float32x4 v_dx = v_x - v_curCenterX;
1525
v_float32x4 v_dy = v_y - v_curCenterY;
1526
1527
v_float32x4 v_r2 = (v_dx * v_dx) + (v_dy * v_dy);
1528
v_float32x4 vmask = (v_minRadius2 <= v_r2) & (v_r2 <= v_maxRadius2);
1529
unsigned int mask = v_signmask(vmask);
1530
if (mask)
1531
{
1532
v_store_aligned(rbuf, v_r2);
1533
if (mask & 1) ddata[nzCount++] = rbuf[0];
1534
if (mask & 2) ddata[nzCount++] = rbuf[1];
1535
if (mask & 4) ddata[nzCount++] = rbuf[2];
1536
if (mask & 8) ddata[nzCount++] = rbuf[3];
1537
}
1538
}
1539
}
1540
#endif
1541
1542
// Estimate best radius
1543
for(; j < nzSz; ++j)
1544
{
1545
const Point pt = nz_[j];
1546
float _dx = curCenter.x - pt.x, _dy = curCenter.y - pt.y;
1547
float _r2 = _dx * _dx + _dy * _dy;
1548
1549
if(minRadius2 <= _r2 && _r2 <= maxRadius2)
1550
{
1551
ddata[nzCount++] = _r2;
1552
}
1553
}
1554
return nzCount;
1555
}
1556
1557
template<>
1558
inline int HoughCircleEstimateRadiusInvoker<NZPointSet>::filterCircles(const Point2f& curCenter, float* ddata) const
1559
{
1560
int nzCount = 0;
1561
const Mat_<uchar>& positions = nz.positions;
1562
1563
const int rOuter = maxRadius + 1;
1564
const Range xOuter = Range(std::max(int(curCenter.x - rOuter), 0), std::min(int(curCenter.x + rOuter), positions.cols));
1565
const Range yOuter = Range(std::max(int(curCenter.y - rOuter), 0), std::min(int(curCenter.y + rOuter), positions.rows));
1566
1567
#if CV_SIMD128
1568
const int numSIMDPoints = 4;
1569
1570
const v_float32x4 v_minRadius2 = v_setall_f32(minRadius2);
1571
const v_float32x4 v_maxRadius2 = v_setall_f32(maxRadius2);
1572
const v_float32x4 v_curCenterX_0123 = v_setall_f32(curCenter.x) - v_float32x4(0.0f, 1.0f, 2.0f, 3.0f);
1573
#endif
1574
1575
for (int y = yOuter.start; y < yOuter.end; y++)
1576
{
1577
const uchar* ptr = positions.ptr(y, 0);
1578
float dy = curCenter.y - y;
1579
float dy2 = dy * dy;
1580
1581
int x = xOuter.start;
1582
#if CV_SIMD128
1583
{
1584
const v_float32x4 v_dy2 = v_setall_f32(dy2);
1585
const v_uint32x4 v_zero_u32 = v_setall_u32(0);
1586
float CV_DECL_ALIGNED(16) rbuf[4];
1587
for (; x <= xOuter.end - 4; x += numSIMDPoints)
1588
{
1589
v_uint32x4 v_mask = v_load_expand_q(ptr + x);
1590
v_mask = v_mask != v_zero_u32;
1591
1592
v_float32x4 v_x = v_cvt_f32(v_setall_s32(x));
1593
v_float32x4 v_dx = v_x - v_curCenterX_0123;
1594
1595
v_float32x4 v_r2 = (v_dx * v_dx) + v_dy2;
1596
v_float32x4 vmask = (v_minRadius2 <= v_r2) & (v_r2 <= v_maxRadius2) & v_reinterpret_as_f32(v_mask);
1597
unsigned int mask = v_signmask(vmask);
1598
if (mask)
1599
{
1600
v_store_aligned(rbuf, v_r2);
1601
if (mask & 1) ddata[nzCount++] = rbuf[0];
1602
if (mask & 2) ddata[nzCount++] = rbuf[1];
1603
if (mask & 4) ddata[nzCount++] = rbuf[2];
1604
if (mask & 8) ddata[nzCount++] = rbuf[3];
1605
}
1606
}
1607
}
1608
#endif
1609
for (; x < xOuter.end; x++)
1610
{
1611
if (ptr[x])
1612
{
1613
float _dx = curCenter.x - x;
1614
float _r2 = _dx * _dx + dy2;
1615
if(minRadius2 <= _r2 && _r2 <= maxRadius2)
1616
{
1617
ddata[nzCount++] = _r2;
1618
}
1619
}
1620
}
1621
}
1622
return nzCount;
1623
}
1624
1625
template <typename CircleType>
1626
static void HoughCirclesGradient(InputArray _image, OutputArray _circles,
1627
float dp, float minDist,
1628
int minRadius, int maxRadius, int cannyThreshold,
1629
int accThreshold, int maxCircles, int kernelSize, bool centersOnly)
1630
{
1631
CV_Assert(kernelSize == -1 || kernelSize == 3 || kernelSize == 5 || kernelSize == 7);
1632
1633
dp = max(dp, 1.f);
1634
float idp = 1.f/dp;
1635
1636
Mat edges, dx, dy;
1637
1638
Sobel(_image, dx, CV_16S, 1, 0, kernelSize, 1, 0, BORDER_REPLICATE);
1639
Sobel(_image, dy, CV_16S, 0, 1, kernelSize, 1, 0, BORDER_REPLICATE);
1640
Canny(dx, dy, edges, std::max(1, cannyThreshold / 2), cannyThreshold, false);
1641
1642
Mutex mtx;
1643
int numThreads = std::max(1, getNumThreads());
1644
std::vector<Mat> accumVec;
1645
NZPointSet nz(_image.rows(), _image.cols());
1646
parallel_for_(Range(0, edges.rows),
1647
HoughCirclesAccumInvoker(edges, dx, dy, minRadius, maxRadius, idp, accumVec, nz, mtx),
1648
numThreads);
1649
int nzSz = cv::countNonZero(nz.positions);
1650
if(nzSz <= 0)
1651
return;
1652
1653
Mat accum = accumVec[0];
1654
for(size_t i = 1; i < accumVec.size(); i++)
1655
{
1656
accum += accumVec[i];
1657
}
1658
accumVec.clear();
1659
1660
std::vector<int> centers;
1661
1662
// 4 rows when multithreaded because there is a bit overhead
1663
// and on the other side there are some row ranges where centers are concentrated
1664
parallel_for_(Range(1, accum.rows - 1),
1665
HoughCirclesFindCentersInvoker(accum, centers, accThreshold, mtx),
1666
(numThreads > 1) ? ((accum.rows - 2) / 4) : 1);
1667
1668
int centerCnt = (int)centers.size();
1669
if(centerCnt == 0)
1670
return;
1671
1672
std::sort(centers.begin(), centers.end(), hough_cmp_gt(accum.ptr<int>()));
1673
1674
std::vector<CircleType> circles;
1675
circles.reserve(256);
1676
if (centersOnly)
1677
{
1678
// Just get the circle centers
1679
GetCircleCenters(centers, circles, accum.cols, minDist, dp);
1680
}
1681
else
1682
{
1683
std::vector<EstimatedCircle> circlesEst;
1684
if (nzSz < maxRadius * maxRadius)
1685
{
1686
// Faster to use a list
1687
NZPointList nzList(nzSz);
1688
nz.toList(nzList);
1689
// One loop iteration per thread if multithreaded.
1690
parallel_for_(Range(0, centerCnt),
1691
HoughCircleEstimateRadiusInvoker<NZPointList>(nzList, nzSz, centers, circlesEst, accum.cols,
1692
accThreshold, minRadius, maxRadius, dp, mtx),
1693
numThreads);
1694
}
1695
else
1696
{
1697
// Faster to use a matrix
1698
// One loop iteration per thread if multithreaded.
1699
parallel_for_(Range(0, centerCnt),
1700
HoughCircleEstimateRadiusInvoker<NZPointSet>(nz, nzSz, centers, circlesEst, accum.cols,
1701
accThreshold, minRadius, maxRadius, dp, mtx),
1702
numThreads);
1703
}
1704
1705
// Sort by accumulator value
1706
std::sort(circlesEst.begin(), circlesEst.end(), cmpAccum);
1707
1708
// Create Circles
1709
CreateCircles(circlesEst, circles);
1710
RemoveOverlaps(circles, minDist);
1711
}
1712
1713
if (circles.size() > 0)
1714
{
1715
int numCircles = std::min(maxCircles, int(circles.size()));
1716
Mat(1, numCircles, cv::traits::Type<CircleType>::value, &circles[0]).copyTo(_circles);
1717
return;
1718
}
1719
}
1720
1721
static void HoughCircles( InputArray _image, OutputArray _circles,
1722
int method, double dp, double minDist,
1723
double param1, double param2,
1724
int minRadius, int maxRadius,
1725
int maxCircles, double param3 )
1726
{
1727
CV_INSTRUMENT_REGION();
1728
1729
int type = CV_32FC3;
1730
if( _circles.fixedType() )
1731
{
1732
type = _circles.type();
1733
CV_CheckType(type, type == CV_32FC3 || type == CV_32FC4, "Wrong type of output circles");
1734
}
1735
1736
CV_Assert(!_image.empty() && _image.type() == CV_8UC1 && (_image.isMat() || _image.isUMat()));
1737
CV_Assert(_circles.isMat() || _circles.isVector());
1738
1739
if( dp <= 0 || minDist <= 0 || param1 <= 0 || param2 <= 0)
1740
CV_Error( Error::StsOutOfRange, "dp, min_dist, canny_threshold and acc_threshold must be all positive numbers" );
1741
1742
int cannyThresh = cvRound(param1), accThresh = cvRound(param2), kernelSize = cvRound(param3);
1743
1744
minRadius = std::max(0, minRadius);
1745
1746
if(maxCircles < 0)
1747
maxCircles = INT_MAX;
1748
1749
bool centersOnly = (maxRadius < 0);
1750
1751
if( maxRadius <= 0 )
1752
maxRadius = std::max( _image.rows(), _image.cols() );
1753
else if( maxRadius <= minRadius )
1754
maxRadius = minRadius + 2;
1755
1756
switch( method )
1757
{
1758
case CV_HOUGH_GRADIENT:
1759
if (type == CV_32FC3)
1760
HoughCirclesGradient<Vec3f>(_image, _circles, (float)dp, (float)minDist,
1761
minRadius, maxRadius, cannyThresh,
1762
accThresh, maxCircles, kernelSize, centersOnly);
1763
else if (type == CV_32FC4)
1764
HoughCirclesGradient<Vec4f>(_image, _circles, (float)dp, (float)minDist,
1765
minRadius, maxRadius, cannyThresh,
1766
accThresh, maxCircles, kernelSize, centersOnly);
1767
else
1768
CV_Error(Error::StsError, "Internal error");
1769
break;
1770
default:
1771
CV_Error( Error::StsBadArg, "Unrecognized method id. Actually only CV_HOUGH_GRADIENT is supported." );
1772
}
1773
}
1774
1775
void HoughCircles( InputArray _image, OutputArray _circles,
1776
int method, double dp, double minDist,
1777
double param1, double param2,
1778
int minRadius, int maxRadius )
1779
{
1780
HoughCircles(_image, _circles, method, dp, minDist, param1, param2, minRadius, maxRadius, -1, 3);
1781
}
1782
} // \namespace cv
1783
1784
1785
/* Wrapper function for standard hough transform */
1786
CV_IMPL CvSeq*
1787
cvHoughLines2( CvArr* src_image, void* lineStorage, int method,
1788
double rho, double theta, int threshold,
1789
double param1, double param2,
1790
double min_theta, double max_theta )
1791
{
1792
cv::Mat image = cv::cvarrToMat(src_image);
1793
std::vector<cv::Vec2f> l2;
1794
std::vector<cv::Vec4i> l4;
1795
1796
CvMat* mat = 0;
1797
CvSeq* lines = 0;
1798
CvSeq lines_header;
1799
CvSeqBlock lines_block;
1800
int lineType, elemSize;
1801
int linesMax = INT_MAX;
1802
int iparam1, iparam2;
1803
1804
if( !lineStorage )
1805
CV_Error(cv::Error::StsNullPtr, "NULL destination" );
1806
1807
if( rho <= 0 || theta <= 0 || threshold <= 0 )
1808
CV_Error( cv::Error::StsOutOfRange, "rho, theta and threshold must be positive" );
1809
1810
if( method != CV_HOUGH_PROBABILISTIC )
1811
{
1812
lineType = CV_32FC2;
1813
elemSize = sizeof(float)*2;
1814
}
1815
else
1816
{
1817
lineType = CV_32SC4;
1818
elemSize = sizeof(int)*4;
1819
}
1820
1821
bool isStorage = isStorageOrMat(lineStorage);
1822
1823
if( isStorage )
1824
{
1825
lines = cvCreateSeq( lineType, sizeof(CvSeq), elemSize, (CvMemStorage*)lineStorage );
1826
}
1827
else
1828
{
1829
mat = (CvMat*)lineStorage;
1830
1831
if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 && mat->cols != 1) )
1832
CV_Error( CV_StsBadArg,
1833
"The destination matrix should be continuous and have a single row or a single column" );
1834
1835
if( CV_MAT_TYPE( mat->type ) != lineType )
1836
CV_Error( CV_StsBadArg,
1837
"The destination matrix data type is inappropriate, see the manual" );
1838
1839
lines = cvMakeSeqHeaderForArray( lineType, sizeof(CvSeq), elemSize, mat->data.ptr,
1840
mat->rows + mat->cols - 1, &lines_header, &lines_block );
1841
linesMax = lines->total;
1842
cvClearSeq( lines );
1843
}
1844
1845
iparam1 = cvRound(param1);
1846
iparam2 = cvRound(param2);
1847
1848
switch( method )
1849
{
1850
case CV_HOUGH_STANDARD:
1851
HoughLinesStandard( image, l2, CV_32FC2, (float)rho,
1852
(float)theta, threshold, linesMax, min_theta, max_theta );
1853
break;
1854
case CV_HOUGH_MULTI_SCALE:
1855
HoughLinesSDiv( image, l2, CV_32FC2, (float)rho, (float)theta,
1856
threshold, iparam1, iparam2, linesMax, min_theta, max_theta );
1857
break;
1858
case CV_HOUGH_PROBABILISTIC:
1859
HoughLinesProbabilistic( image, (float)rho, (float)theta,
1860
threshold, iparam1, iparam2, l4, linesMax );
1861
break;
1862
default:
1863
CV_Error( CV_StsBadArg, "Unrecognized method id" );
1864
}
1865
1866
int nlines = (int)(l2.size() + l4.size());
1867
1868
if( !isStorage )
1869
{
1870
if( mat->cols > mat->rows )
1871
mat->cols = nlines;
1872
else
1873
mat->rows = nlines;
1874
}
1875
1876
if( nlines )
1877
{
1878
cv::Mat lx = method == CV_HOUGH_STANDARD || method == CV_HOUGH_MULTI_SCALE ?
1879
cv::Mat(nlines, 1, CV_32FC2, &l2[0]) : cv::Mat(nlines, 1, CV_32SC4, &l4[0]);
1880
1881
if (isStorage)
1882
{
1883
cvSeqPushMulti(lines, lx.ptr(), nlines);
1884
}
1885
else
1886
{
1887
cv::Mat dst(nlines, 1, lx.type(), mat->data.ptr);
1888
lx.copyTo(dst);
1889
}
1890
}
1891
1892
if( isStorage )
1893
return lines;
1894
return 0;
1895
}
1896
1897
1898
CV_IMPL CvSeq*
1899
cvHoughCircles( CvArr* src_image, void* circle_storage,
1900
int method, double dp, double min_dist,
1901
double param1, double param2,
1902
int min_radius, int max_radius )
1903
{
1904
CvSeq* circles = NULL;
1905
int circles_max = INT_MAX;
1906
cv::Mat src = cv::cvarrToMat(src_image), circles_mat;
1907
1908
if( !circle_storage )
1909
CV_Error( CV_StsNullPtr, "NULL destination" );
1910
1911
bool isStorage = isStorageOrMat(circle_storage);
1912
1913
if(isStorage)
1914
{
1915
circles = cvCreateSeq( CV_32FC3, sizeof(CvSeq),
1916
sizeof(float)*3, (CvMemStorage*)circle_storage );
1917
}
1918
else
1919
{
1920
CvSeq circles_header;
1921
CvSeqBlock circles_block;
1922
CvMat *mat = (CvMat*)circle_storage;
1923
1924
if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 && mat->cols != 1) ||
1925
CV_MAT_TYPE(mat->type) != CV_32FC3 )
1926
CV_Error( CV_StsBadArg,
1927
"The destination matrix should be continuous and have a single row or a single column" );
1928
1929
circles = cvMakeSeqHeaderForArray( CV_32FC3, sizeof(CvSeq), sizeof(float)*3,
1930
mat->data.ptr, mat->rows + mat->cols - 1, &circles_header, &circles_block );
1931
circles_max = circles->total;
1932
cvClearSeq( circles );
1933
}
1934
1935
cv::HoughCircles(src, circles_mat, method, dp, min_dist, param1, param2, min_radius, max_radius, circles_max, 3);
1936
cvSeqPushMulti(circles, circles_mat.data, (int)circles_mat.total());
1937
return circles;
1938
}
1939
1940
/* End of file. */
1941
1942