Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/src/lsd.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) 2013, OpenCV Foundation, 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
// * Redistributions of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
21
//
22
// * Redistributions 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 the copyright holders 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 <vector>
44
45
/////////////////////////////////////////////////////////////////////////////////////////
46
// Default LSD parameters
47
// SIGMA_SCALE 0.6 - Sigma for Gaussian filter is computed as sigma = sigma_scale/scale.
48
// QUANT 2.0 - Bound to the quantization error on the gradient norm.
49
// ANG_TH 22.5 - Gradient angle tolerance in degrees.
50
// LOG_EPS 0.0 - Detection threshold: -log10(NFA) > log_eps
51
// DENSITY_TH 0.7 - Minimal density of region points in rectangle.
52
// N_BINS 1024 - Number of bins in pseudo-ordering of gradient modulus.
53
54
#define M_3_2_PI (3 * CV_PI) / 2 // 3/2 pi
55
#define M_2__PI (2 * CV_PI) // 2 pi
56
57
#ifndef M_LN10
58
#define M_LN10 2.30258509299404568402
59
#endif
60
61
#define NOTDEF double(-1024.0) // Label for pixels with undefined gradient.
62
63
#define NOTUSED 0 // Label for pixels not used in yet.
64
#define USED 1 // Label for pixels already used in detection.
65
66
#define RELATIVE_ERROR_FACTOR 100.0
67
68
const double DEG_TO_RADS = CV_PI / 180;
69
70
#define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x))
71
72
struct edge
73
{
74
cv::Point p;
75
bool taken;
76
};
77
78
/////////////////////////////////////////////////////////////////////////////////////////
79
80
inline double distSq(const double x1, const double y1,
81
const double x2, const double y2)
82
{
83
return (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1);
84
}
85
86
inline double dist(const double x1, const double y1,
87
const double x2, const double y2)
88
{
89
return sqrt(distSq(x1, y1, x2, y2));
90
}
91
92
// Signed angle difference
93
inline double angle_diff_signed(const double& a, const double& b)
94
{
95
double diff = a - b;
96
while(diff <= -CV_PI) diff += M_2__PI;
97
while(diff > CV_PI) diff -= M_2__PI;
98
return diff;
99
}
100
101
// Absolute value angle difference
102
inline double angle_diff(const double& a, const double& b)
103
{
104
return std::fabs(angle_diff_signed(a, b));
105
}
106
107
// Compare doubles by relative error.
108
inline bool double_equal(const double& a, const double& b)
109
{
110
// trivial case
111
if(a == b) return true;
112
113
double abs_diff = fabs(a - b);
114
double aa = fabs(a);
115
double bb = fabs(b);
116
double abs_max = (aa > bb)? aa : bb;
117
118
if(abs_max < DBL_MIN) abs_max = DBL_MIN;
119
120
return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON);
121
}
122
123
inline bool AsmallerB_XoverY(const edge& a, const edge& b)
124
{
125
if (a.p.x == b.p.x) return a.p.y < b.p.y;
126
else return a.p.x < b.p.x;
127
}
128
129
/**
130
* Computes the natural logarithm of the absolute value of
131
* the gamma function of x using Windschitl method.
132
* See http://www.rskey.org/gamma.htm
133
*/
134
inline double log_gamma_windschitl(const double& x)
135
{
136
return 0.918938533204673 + (x-0.5)*log(x) - x
137
+ 0.5*x*log(x*sinh(1/x) + 1/(810.0*pow(x, 6.0)));
138
}
139
140
/**
141
* Computes the natural logarithm of the absolute value of
142
* the gamma function of x using the Lanczos approximation.
143
* See http://www.rskey.org/gamma.htm
144
*/
145
inline double log_gamma_lanczos(const double& x)
146
{
147
static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477,
148
8687.24529705, 1168.92649479, 83.8676043424,
149
2.50662827511 };
150
double a = (x + 0.5) * log(x + 5.5) - (x + 5.5);
151
double b = 0;
152
for(int n = 0; n < 7; ++n)
153
{
154
a -= log(x + double(n));
155
b += q[n] * pow(x, double(n));
156
}
157
return a + log(b);
158
}
159
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
160
161
namespace cv{
162
163
class LineSegmentDetectorImpl CV_FINAL : public LineSegmentDetector
164
{
165
public:
166
167
/**
168
* Create a LineSegmentDetectorImpl object. Specifying scale, number of subdivisions for the image, should the lines be refined and other constants as follows:
169
*
170
* @param _refine How should the lines found be refined?
171
* LSD_REFINE_NONE - No refinement applied.
172
* LSD_REFINE_STD - Standard refinement is applied. E.g. breaking arches into smaller line approximations.
173
* LSD_REFINE_ADV - Advanced refinement. Number of false alarms is calculated,
174
* lines are refined through increase of precision, decrement in size, etc.
175
* @param _scale The scale of the image that will be used to find the lines. Range (0..1].
176
* @param _sigma_scale Sigma for Gaussian filter is computed as sigma = _sigma_scale/_scale.
177
* @param _quant Bound to the quantization error on the gradient norm.
178
* @param _ang_th Gradient angle tolerance in degrees.
179
* @param _log_eps Detection threshold: -log10(NFA) > _log_eps
180
* @param _density_th Minimal density of aligned region points in rectangle.
181
* @param _n_bins Number of bins in pseudo-ordering of gradient modulus.
182
*/
183
LineSegmentDetectorImpl(int _refine = LSD_REFINE_STD, double _scale = 0.8,
184
double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5,
185
double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024);
186
187
/**
188
* Detect lines in the input image.
189
*
190
* @param _image A grayscale(CV_8UC1) input image.
191
* If only a roi needs to be selected, use
192
* lsd_ptr->detect(image(roi), ..., lines);
193
* lines += Scalar(roi.x, roi.y, roi.x, roi.y);
194
* @param _lines Return: A vector of Vec4i or Vec4f elements specifying the beginning and ending point of a line.
195
* Where Vec4i/Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end.
196
* Returned lines are strictly oriented depending on the gradient.
197
* @param width Return: Vector of widths of the regions, where the lines are found. E.g. Width of line.
198
* @param prec Return: Vector of precisions with which the lines are found.
199
* @param nfa Return: Vector containing number of false alarms in the line region, with precision of 10%.
200
* The bigger the value, logarithmically better the detection.
201
* * -1 corresponds to 10 mean false alarms
202
* * 0 corresponds to 1 mean false alarm
203
* * 1 corresponds to 0.1 mean false alarms
204
* This vector will be calculated _only_ when the objects type is REFINE_ADV
205
*/
206
void detect(InputArray _image, OutputArray _lines,
207
OutputArray width = noArray(), OutputArray prec = noArray(),
208
OutputArray nfa = noArray()) CV_OVERRIDE;
209
210
/**
211
* Draw lines on the given canvas.
212
*
213
* @param image The image, where lines will be drawn.
214
* Should have the size of the image, where the lines were found
215
* @param lines The lines that need to be drawn
216
*/
217
void drawSegments(InputOutputArray _image, InputArray lines) CV_OVERRIDE;
218
219
/**
220
* Draw both vectors on the image canvas. Uses blue for lines 1 and red for lines 2.
221
*
222
* @param size The size of the image, where lines1 and lines2 were found.
223
* @param lines1 The first lines that need to be drawn. Color - Blue.
224
* @param lines2 The second lines that need to be drawn. Color - Red.
225
* @param image An optional image, where lines will be drawn.
226
* Should have the size of the image, where the lines were found
227
* @return The number of mismatching pixels between lines1 and lines2.
228
*/
229
int compareSegments(const Size& size, InputArray lines1, InputArray lines2, InputOutputArray _image = noArray()) CV_OVERRIDE;
230
231
private:
232
Mat image;
233
Mat scaled_image;
234
Mat_<double> angles; // in rads
235
Mat_<double> modgrad;
236
Mat_<uchar> used;
237
238
int img_width;
239
int img_height;
240
double LOG_NT;
241
242
bool w_needed;
243
bool p_needed;
244
bool n_needed;
245
246
const double SCALE;
247
const int doRefine;
248
const double SIGMA_SCALE;
249
const double QUANT;
250
const double ANG_TH;
251
const double LOG_EPS;
252
const double DENSITY_TH;
253
const int N_BINS;
254
255
struct RegionPoint {
256
int x;
257
int y;
258
uchar* used;
259
double angle;
260
double modgrad;
261
};
262
263
struct normPoint
264
{
265
Point2i p;
266
int norm;
267
};
268
269
std::vector<normPoint> ordered_points;
270
271
struct rect
272
{
273
double x1, y1, x2, y2; // first and second point of the line segment
274
double width; // rectangle width
275
double x, y; // center of the rectangle
276
double theta; // angle
277
double dx,dy; // (dx,dy) is vector oriented as the line segment
278
double prec; // tolerance angle
279
double p; // probability of a point with angle within 'prec'
280
};
281
282
LineSegmentDetectorImpl& operator= (const LineSegmentDetectorImpl&); // to quiet MSVC
283
284
/**
285
* Detect lines in the whole input image.
286
*
287
* @param lines Return: A vector of Vec4f elements specifying the beginning and ending point of a line.
288
* Where Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end.
289
* Returned lines are strictly oriented depending on the gradient.
290
* @param widths Return: Vector of widths of the regions, where the lines are found. E.g. Width of line.
291
* @param precisions Return: Vector of precisions with which the lines are found.
292
* @param nfas Return: Vector containing number of false alarms in the line region, with precision of 10%.
293
* The bigger the value, logarithmically better the detection.
294
* * -1 corresponds to 10 mean false alarms
295
* * 0 corresponds to 1 mean false alarm
296
* * 1 corresponds to 0.1 mean false alarms
297
*/
298
void flsd(std::vector<Vec4f>& lines,
299
std::vector<double>& widths, std::vector<double>& precisions,
300
std::vector<double>& nfas);
301
302
/**
303
* Finds the angles and the gradients of the image. Generates a list of pseudo ordered points.
304
*
305
* @param threshold The minimum value of the angle that is considered defined, otherwise NOTDEF
306
* @param n_bins The number of bins with which gradients are ordered by, using bucket sort.
307
* @param ordered_points Return: Vector of coordinate points that are pseudo ordered by magnitude.
308
* Pixels would be ordered by norm value, up to a precision given by max_grad/n_bins.
309
*/
310
void ll_angle(const double& threshold, const unsigned int& n_bins);
311
312
/**
313
* Grow a region starting from point s with a defined precision,
314
* returning the containing points size and the angle of the gradients.
315
*
316
* @param s Starting point for the region.
317
* @param reg Return: Vector of points, that are part of the region
318
* @param reg_angle Return: The mean angle of the region.
319
* @param prec The precision by which each region angle should be aligned to the mean.
320
*/
321
void region_grow(const Point2i& s, std::vector<RegionPoint>& reg,
322
double& reg_angle, const double& prec);
323
324
/**
325
* Finds the bounding rotated rectangle of a region.
326
*
327
* @param reg The region of points, from which the rectangle to be constructed from.
328
* @param reg_angle The mean angle of the region.
329
* @param prec The precision by which points were found.
330
* @param p Probability of a point with angle within 'prec'.
331
* @param rec Return: The generated rectangle.
332
*/
333
void region2rect(const std::vector<RegionPoint>& reg, const double reg_angle,
334
const double prec, const double p, rect& rec) const;
335
336
/**
337
* Compute region's angle as the principal inertia axis of the region.
338
* @return Regions angle.
339
*/
340
double get_theta(const std::vector<RegionPoint>& reg, const double& x,
341
const double& y, const double& reg_angle, const double& prec) const;
342
343
/**
344
* An estimation of the angle tolerance is performed by the standard deviation of the angle at points
345
* near the region's starting point. Then, a new region is grown starting from the same point, but using the
346
* estimated angle tolerance. If this fails to produce a rectangle with the right density of region points,
347
* 'reduce_region_radius' is called to try to satisfy this condition.
348
*/
349
bool refine(std::vector<RegionPoint>& reg, double reg_angle,
350
const double prec, double p, rect& rec, const double& density_th);
351
352
/**
353
* Reduce the region size, by elimination the points far from the starting point, until that leads to
354
* rectangle with the right density of region points or to discard the region if too small.
355
*/
356
bool reduce_region_radius(std::vector<RegionPoint>& reg, double reg_angle,
357
const double prec, double p, rect& rec, double density, const double& density_th);
358
359
/**
360
* Try some rectangles variations to improve NFA value. Only if the rectangle is not meaningful (i.e., log_nfa <= log_eps).
361
* @return The new NFA value.
362
*/
363
double rect_improve(rect& rec) const;
364
365
/**
366
* Calculates the number of correctly aligned points within the rectangle.
367
* @return The new NFA value.
368
*/
369
double rect_nfa(const rect& rec) const;
370
371
/**
372
* Computes the NFA values based on the total number of points, points that agree.
373
* n, k, p are the binomial parameters.
374
* @return The new NFA value.
375
*/
376
double nfa(const int& n, const int& k, const double& p) const;
377
378
/**
379
* Is the point at place 'address' aligned to angle theta, up to precision 'prec'?
380
* @return Whether the point is aligned.
381
*/
382
bool isAligned(int x, int y, const double& theta, const double& prec) const;
383
384
public:
385
// Compare norm
386
static inline bool compare_norm( const normPoint& n1, const normPoint& n2 )
387
{
388
return (n1.norm > n2.norm);
389
}
390
};
391
392
/////////////////////////////////////////////////////////////////////////////////////////
393
394
CV_EXPORTS Ptr<LineSegmentDetector> createLineSegmentDetector(
395
int _refine, double _scale, double _sigma_scale, double _quant, double _ang_th,
396
double _log_eps, double _density_th, int _n_bins)
397
{
398
return makePtr<LineSegmentDetectorImpl>(
399
_refine, _scale, _sigma_scale, _quant, _ang_th,
400
_log_eps, _density_th, _n_bins);
401
}
402
403
/////////////////////////////////////////////////////////////////////////////////////////
404
405
LineSegmentDetectorImpl::LineSegmentDetectorImpl(int _refine, double _scale, double _sigma_scale, double _quant,
406
double _ang_th, double _log_eps, double _density_th, int _n_bins)
407
: img_width(0), img_height(0), LOG_NT(0), w_needed(false), p_needed(false), n_needed(false),
408
SCALE(_scale), doRefine(_refine), SIGMA_SCALE(_sigma_scale), QUANT(_quant),
409
ANG_TH(_ang_th), LOG_EPS(_log_eps), DENSITY_TH(_density_th), N_BINS(_n_bins)
410
{
411
CV_Assert(_scale > 0 && _sigma_scale > 0 && _quant >= 0 &&
412
_ang_th > 0 && _ang_th < 180 && _density_th >= 0 && _density_th < 1 &&
413
_n_bins > 0);
414
}
415
416
void LineSegmentDetectorImpl::detect(InputArray _image, OutputArray _lines,
417
OutputArray _width, OutputArray _prec, OutputArray _nfa)
418
{
419
CV_INSTRUMENT_REGION();
420
421
image = _image.getMat();
422
CV_Assert(!image.empty() && image.type() == CV_8UC1);
423
424
std::vector<Vec4f> lines;
425
std::vector<double> w, p, n;
426
w_needed = _width.needed();
427
p_needed = _prec.needed();
428
if (doRefine < LSD_REFINE_ADV)
429
n_needed = false;
430
else
431
n_needed = _nfa.needed();
432
433
flsd(lines, w, p, n);
434
435
Mat(lines).copyTo(_lines);
436
if(w_needed) Mat(w).copyTo(_width);
437
if(p_needed) Mat(p).copyTo(_prec);
438
if(n_needed) Mat(n).copyTo(_nfa);
439
440
// Clear used structures
441
ordered_points.clear();
442
}
443
444
void LineSegmentDetectorImpl::flsd(std::vector<Vec4f>& lines,
445
std::vector<double>& widths, std::vector<double>& precisions,
446
std::vector<double>& nfas)
447
{
448
// Angle tolerance
449
const double prec = CV_PI * ANG_TH / 180;
450
const double p = ANG_TH / 180;
451
const double rho = QUANT / sin(prec); // gradient magnitude threshold
452
453
if(SCALE != 1)
454
{
455
Mat gaussian_img;
456
const double sigma = (SCALE < 1)?(SIGMA_SCALE / SCALE):(SIGMA_SCALE);
457
const double sprec = 3;
458
const unsigned int h = (unsigned int)(ceil(sigma * sqrt(2 * sprec * log(10.0))));
459
Size ksize(1 + 2 * h, 1 + 2 * h); // kernel size
460
GaussianBlur(image, gaussian_img, ksize, sigma);
461
// Scale image to needed size
462
resize(gaussian_img, scaled_image, Size(), SCALE, SCALE, INTER_LINEAR_EXACT);
463
ll_angle(rho, N_BINS);
464
}
465
else
466
{
467
scaled_image = image;
468
ll_angle(rho, N_BINS);
469
}
470
471
LOG_NT = 5 * (log10(double(img_width)) + log10(double(img_height))) / 2 + log10(11.0);
472
const size_t min_reg_size = size_t(-LOG_NT/log10(p)); // minimal number of points in region that can give a meaningful event
473
474
// // Initialize region only when needed
475
// Mat region = Mat::zeros(scaled_image.size(), CV_8UC1);
476
used = Mat_<uchar>::zeros(scaled_image.size()); // zeros = NOTUSED
477
std::vector<RegionPoint> reg;
478
479
// Search for line segments
480
for(size_t i = 0, points_size = ordered_points.size(); i < points_size; ++i)
481
{
482
const Point2i& point = ordered_points[i].p;
483
if((used.at<uchar>(point) == NOTUSED) && (angles.at<double>(point) != NOTDEF))
484
{
485
double reg_angle;
486
region_grow(ordered_points[i].p, reg, reg_angle, prec);
487
488
// Ignore small regions
489
if(reg.size() < min_reg_size) { continue; }
490
491
// Construct rectangular approximation for the region
492
rect rec;
493
region2rect(reg, reg_angle, prec, p, rec);
494
495
double log_nfa = -1;
496
if(doRefine > LSD_REFINE_NONE)
497
{
498
// At least REFINE_STANDARD lvl.
499
if(!refine(reg, reg_angle, prec, p, rec, DENSITY_TH)) { continue; }
500
501
if(doRefine >= LSD_REFINE_ADV)
502
{
503
// Compute NFA
504
log_nfa = rect_improve(rec);
505
if(log_nfa <= LOG_EPS) { continue; }
506
}
507
}
508
// Found new line
509
510
// Add the offset
511
rec.x1 += 0.5; rec.y1 += 0.5;
512
rec.x2 += 0.5; rec.y2 += 0.5;
513
514
// scale the result values if a sub-sampling was performed
515
if(SCALE != 1)
516
{
517
rec.x1 /= SCALE; rec.y1 /= SCALE;
518
rec.x2 /= SCALE; rec.y2 /= SCALE;
519
rec.width /= SCALE;
520
}
521
522
//Store the relevant data
523
lines.push_back(Vec4f(float(rec.x1), float(rec.y1), float(rec.x2), float(rec.y2)));
524
if(w_needed) widths.push_back(rec.width);
525
if(p_needed) precisions.push_back(rec.p);
526
if(n_needed && doRefine >= LSD_REFINE_ADV) nfas.push_back(log_nfa);
527
}
528
}
529
}
530
531
void LineSegmentDetectorImpl::ll_angle(const double& threshold,
532
const unsigned int& n_bins)
533
{
534
//Initialize data
535
angles = Mat_<double>(scaled_image.size());
536
modgrad = Mat_<double>(scaled_image.size());
537
538
img_width = scaled_image.cols;
539
img_height = scaled_image.rows;
540
541
// Undefined the down and right boundaries
542
angles.row(img_height - 1).setTo(NOTDEF);
543
angles.col(img_width - 1).setTo(NOTDEF);
544
545
// Computing gradient for remaining pixels
546
double max_grad = -1;
547
for(int y = 0; y < img_height - 1; ++y)
548
{
549
const uchar* scaled_image_row = scaled_image.ptr<uchar>(y);
550
const uchar* next_scaled_image_row = scaled_image.ptr<uchar>(y+1);
551
double* angles_row = angles.ptr<double>(y);
552
double* modgrad_row = modgrad.ptr<double>(y);
553
for(int x = 0; x < img_width-1; ++x)
554
{
555
int DA = next_scaled_image_row[x + 1] - scaled_image_row[x];
556
int BC = scaled_image_row[x + 1] - next_scaled_image_row[x];
557
int gx = DA + BC; // gradient x component
558
int gy = DA - BC; // gradient y component
559
double norm = std::sqrt((gx * gx + gy * gy) / 4.0); // gradient norm
560
561
modgrad_row[x] = norm; // store gradient
562
563
if (norm <= threshold) // norm too small, gradient no defined
564
{
565
angles_row[x] = NOTDEF;
566
}
567
else
568
{
569
angles_row[x] = fastAtan2(float(gx), float(-gy)) * DEG_TO_RADS; // gradient angle computation
570
if (norm > max_grad) { max_grad = norm; }
571
}
572
573
}
574
}
575
576
// Compute histogram of gradient values
577
double bin_coef = (max_grad > 0) ? double(n_bins - 1) / max_grad : 0; // If all image is smooth, max_grad <= 0
578
for(int y = 0; y < img_height - 1; ++y)
579
{
580
const double* modgrad_row = modgrad.ptr<double>(y);
581
for(int x = 0; x < img_width - 1; ++x)
582
{
583
normPoint _point;
584
int i = int(modgrad_row[x] * bin_coef);
585
_point.p = Point(x, y);
586
_point.norm = i;
587
ordered_points.push_back(_point);
588
}
589
}
590
591
// Sort
592
std::sort(ordered_points.begin(), ordered_points.end(), compare_norm);
593
}
594
595
void LineSegmentDetectorImpl::region_grow(const Point2i& s, std::vector<RegionPoint>& reg,
596
double& reg_angle, const double& prec)
597
{
598
reg.clear();
599
600
// Point to this region
601
RegionPoint seed;
602
seed.x = s.x;
603
seed.y = s.y;
604
seed.used = &used.at<uchar>(s);
605
reg_angle = angles.at<double>(s);
606
seed.angle = reg_angle;
607
seed.modgrad = modgrad.at<double>(s);
608
reg.push_back(seed);
609
610
float sumdx = float(std::cos(reg_angle));
611
float sumdy = float(std::sin(reg_angle));
612
*seed.used = USED;
613
614
//Try neighboring regions
615
for (size_t i = 0;i<reg.size();i++)
616
{
617
const RegionPoint& rpoint = reg[i];
618
int xx_min = std::max(rpoint.x - 1, 0), xx_max = std::min(rpoint.x + 1, img_width - 1);
619
int yy_min = std::max(rpoint.y - 1, 0), yy_max = std::min(rpoint.y + 1, img_height - 1);
620
for(int yy = yy_min; yy <= yy_max; ++yy)
621
{
622
uchar* used_row = used.ptr<uchar>(yy);
623
const double* angles_row = angles.ptr<double>(yy);
624
const double* modgrad_row = modgrad.ptr<double>(yy);
625
for(int xx = xx_min; xx <= xx_max; ++xx)
626
{
627
uchar& is_used = used_row[xx];
628
if(is_used != USED &&
629
(isAligned(xx, yy, reg_angle, prec)))
630
{
631
const double& angle = angles_row[xx];
632
// Add point
633
is_used = USED;
634
RegionPoint region_point;
635
region_point.x = xx;
636
region_point.y = yy;
637
region_point.used = &is_used;
638
region_point.modgrad = modgrad_row[xx];
639
region_point.angle = angle;
640
reg.push_back(region_point);
641
642
// Update region's angle
643
sumdx += cos(float(angle));
644
sumdy += sin(float(angle));
645
// reg_angle is used in the isAligned, so it needs to be updates?
646
reg_angle = fastAtan2(sumdy, sumdx) * DEG_TO_RADS;
647
}
648
}
649
}
650
}
651
}
652
653
void LineSegmentDetectorImpl::region2rect(const std::vector<RegionPoint>& reg,
654
const double reg_angle, const double prec, const double p, rect& rec) const
655
{
656
double x = 0, y = 0, sum = 0;
657
for(size_t i = 0; i < reg.size(); ++i)
658
{
659
const RegionPoint& pnt = reg[i];
660
const double& weight = pnt.modgrad;
661
x += double(pnt.x) * weight;
662
y += double(pnt.y) * weight;
663
sum += weight;
664
}
665
666
// Weighted sum must differ from 0
667
CV_Assert(sum > 0);
668
669
x /= sum;
670
y /= sum;
671
672
double theta = get_theta(reg, x, y, reg_angle, prec);
673
674
// Find length and width
675
double dx = cos(theta);
676
double dy = sin(theta);
677
double l_min = 0, l_max = 0, w_min = 0, w_max = 0;
678
679
for(size_t i = 0; i < reg.size(); ++i)
680
{
681
double regdx = double(reg[i].x) - x;
682
double regdy = double(reg[i].y) - y;
683
684
double l = regdx * dx + regdy * dy;
685
double w = -regdx * dy + regdy * dx;
686
687
if(l > l_max) l_max = l;
688
else if(l < l_min) l_min = l;
689
if(w > w_max) w_max = w;
690
else if(w < w_min) w_min = w;
691
}
692
693
// Store values
694
rec.x1 = x + l_min * dx;
695
rec.y1 = y + l_min * dy;
696
rec.x2 = x + l_max * dx;
697
rec.y2 = y + l_max * dy;
698
rec.width = w_max - w_min;
699
rec.x = x;
700
rec.y = y;
701
rec.theta = theta;
702
rec.dx = dx;
703
rec.dy = dy;
704
rec.prec = prec;
705
rec.p = p;
706
707
// Min width of 1 pixel
708
if(rec.width < 1.0) rec.width = 1.0;
709
}
710
711
double LineSegmentDetectorImpl::get_theta(const std::vector<RegionPoint>& reg, const double& x,
712
const double& y, const double& reg_angle, const double& prec) const
713
{
714
double Ixx = 0.0;
715
double Iyy = 0.0;
716
double Ixy = 0.0;
717
718
// Compute inertia matrix
719
for(size_t i = 0; i < reg.size(); ++i)
720
{
721
const double& regx = reg[i].x;
722
const double& regy = reg[i].y;
723
const double& weight = reg[i].modgrad;
724
double dx = regx - x;
725
double dy = regy - y;
726
Ixx += dy * dy * weight;
727
Iyy += dx * dx * weight;
728
Ixy -= dx * dy * weight;
729
}
730
731
// Check if inertia matrix is null
732
CV_Assert(!(double_equal(Ixx, 0) && double_equal(Iyy, 0) && double_equal(Ixy, 0)));
733
734
// Compute smallest eigenvalue
735
double lambda = 0.5 * (Ixx + Iyy - sqrt((Ixx - Iyy) * (Ixx - Iyy) + 4.0 * Ixy * Ixy));
736
737
// Compute angle
738
double theta = (fabs(Ixx)>fabs(Iyy))?
739
double(fastAtan2(float(lambda - Ixx), float(Ixy))):
740
double(fastAtan2(float(Ixy), float(lambda - Iyy))); // in degs
741
theta *= DEG_TO_RADS;
742
743
// Correct angle by 180 deg if necessary
744
if(angle_diff(theta, reg_angle) > prec) { theta += CV_PI; }
745
746
return theta;
747
}
748
749
bool LineSegmentDetectorImpl::refine(std::vector<RegionPoint>& reg, double reg_angle,
750
const double prec, double p, rect& rec, const double& density_th)
751
{
752
double density = double(reg.size()) / (dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width);
753
754
if (density >= density_th) { return true; }
755
756
// Try to reduce angle tolerance
757
double xc = double(reg[0].x);
758
double yc = double(reg[0].y);
759
const double& ang_c = reg[0].angle;
760
double sum = 0, s_sum = 0;
761
int n = 0;
762
763
for (size_t i = 0; i < reg.size(); ++i)
764
{
765
*(reg[i].used) = NOTUSED;
766
if (dist(xc, yc, reg[i].x, reg[i].y) < rec.width)
767
{
768
const double& angle = reg[i].angle;
769
double ang_d = angle_diff_signed(angle, ang_c);
770
sum += ang_d;
771
s_sum += ang_d * ang_d;
772
++n;
773
}
774
}
775
CV_Assert(n > 0);
776
double mean_angle = sum / double(n);
777
// 2 * standard deviation
778
double tau = 2.0 * sqrt((s_sum - 2.0 * mean_angle * sum) / double(n) + mean_angle * mean_angle);
779
780
// Try new region
781
region_grow(Point(reg[0].x, reg[0].y), reg, reg_angle, tau);
782
783
if (reg.size() < 2) { return false; }
784
785
region2rect(reg, reg_angle, prec, p, rec);
786
density = double(reg.size()) / (dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width);
787
788
if (density < density_th)
789
{
790
return reduce_region_radius(reg, reg_angle, prec, p, rec, density, density_th);
791
}
792
else
793
{
794
return true;
795
}
796
}
797
798
bool LineSegmentDetectorImpl::reduce_region_radius(std::vector<RegionPoint>& reg, double reg_angle,
799
const double prec, double p, rect& rec, double density, const double& density_th)
800
{
801
// Compute region's radius
802
double xc = double(reg[0].x);
803
double yc = double(reg[0].y);
804
double radSq1 = distSq(xc, yc, rec.x1, rec.y1);
805
double radSq2 = distSq(xc, yc, rec.x2, rec.y2);
806
double radSq = radSq1 > radSq2 ? radSq1 : radSq2;
807
808
while(density < density_th)
809
{
810
radSq *= 0.75*0.75; // Reduce region's radius to 75% of its value
811
// Remove points from the region and update 'used' map
812
for (size_t i = 0; i < reg.size(); ++i)
813
{
814
if(distSq(xc, yc, double(reg[i].x), double(reg[i].y)) > radSq)
815
{
816
// Remove point from the region
817
*(reg[i].used) = NOTUSED;
818
std::swap(reg[i], reg[reg.size() - 1]);
819
reg.pop_back();
820
--i; // To avoid skipping one point
821
}
822
}
823
824
if(reg.size() < 2) { return false; }
825
826
// Re-compute rectangle
827
region2rect(reg ,reg_angle, prec, p, rec);
828
829
// Re-compute region points density
830
density = double(reg.size()) /
831
(dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width);
832
}
833
834
return true;
835
}
836
837
double LineSegmentDetectorImpl::rect_improve(rect& rec) const
838
{
839
double delta = 0.5;
840
double delta_2 = delta / 2.0;
841
842
double log_nfa = rect_nfa(rec);
843
844
if(log_nfa > LOG_EPS) return log_nfa; // Good rectangle
845
846
// Try to improve
847
// Finer precision
848
rect r = rect(rec); // Copy
849
for(int n = 0; n < 5; ++n)
850
{
851
r.p /= 2;
852
r.prec = r.p * CV_PI;
853
double log_nfa_new = rect_nfa(r);
854
if(log_nfa_new > log_nfa)
855
{
856
log_nfa = log_nfa_new;
857
rec = rect(r);
858
}
859
}
860
if(log_nfa > LOG_EPS) return log_nfa;
861
862
// Try to reduce width
863
r = rect(rec);
864
for(unsigned int n = 0; n < 5; ++n)
865
{
866
if((r.width - delta) >= 0.5)
867
{
868
r.width -= delta;
869
double log_nfa_new = rect_nfa(r);
870
if(log_nfa_new > log_nfa)
871
{
872
rec = rect(r);
873
log_nfa = log_nfa_new;
874
}
875
}
876
}
877
if(log_nfa > LOG_EPS) return log_nfa;
878
879
// Try to reduce one side of rectangle
880
r = rect(rec);
881
for(unsigned int n = 0; n < 5; ++n)
882
{
883
if((r.width - delta) >= 0.5)
884
{
885
r.x1 += -r.dy * delta_2;
886
r.y1 += r.dx * delta_2;
887
r.x2 += -r.dy * delta_2;
888
r.y2 += r.dx * delta_2;
889
r.width -= delta;
890
double log_nfa_new = rect_nfa(r);
891
if(log_nfa_new > log_nfa)
892
{
893
rec = rect(r);
894
log_nfa = log_nfa_new;
895
}
896
}
897
}
898
if(log_nfa > LOG_EPS) return log_nfa;
899
900
// Try to reduce other side of rectangle
901
r = rect(rec);
902
for(unsigned int n = 0; n < 5; ++n)
903
{
904
if((r.width - delta) >= 0.5)
905
{
906
r.x1 -= -r.dy * delta_2;
907
r.y1 -= r.dx * delta_2;
908
r.x2 -= -r.dy * delta_2;
909
r.y2 -= r.dx * delta_2;
910
r.width -= delta;
911
double log_nfa_new = rect_nfa(r);
912
if(log_nfa_new > log_nfa)
913
{
914
rec = rect(r);
915
log_nfa = log_nfa_new;
916
}
917
}
918
}
919
if(log_nfa > LOG_EPS) return log_nfa;
920
921
// Try finer precision
922
r = rect(rec);
923
for(unsigned int n = 0; n < 5; ++n)
924
{
925
if((r.width - delta) >= 0.5)
926
{
927
r.p /= 2;
928
r.prec = r.p * CV_PI;
929
double log_nfa_new = rect_nfa(r);
930
if(log_nfa_new > log_nfa)
931
{
932
rec = rect(r);
933
log_nfa = log_nfa_new;
934
}
935
}
936
}
937
938
return log_nfa;
939
}
940
941
double LineSegmentDetectorImpl::rect_nfa(const rect& rec) const
942
{
943
int total_pts = 0, alg_pts = 0;
944
double half_width = rec.width / 2.0;
945
double dyhw = rec.dy * half_width;
946
double dxhw = rec.dx * half_width;
947
948
edge ordered_x[4];
949
edge* min_y = &ordered_x[0];
950
edge* max_y = &ordered_x[0]; // Will be used for loop range
951
952
ordered_x[0].p.x = int(rec.x1 - dyhw); ordered_x[0].p.y = int(rec.y1 + dxhw); ordered_x[0].taken = false;
953
ordered_x[1].p.x = int(rec.x2 - dyhw); ordered_x[1].p.y = int(rec.y2 + dxhw); ordered_x[1].taken = false;
954
ordered_x[2].p.x = int(rec.x2 + dyhw); ordered_x[2].p.y = int(rec.y2 - dxhw); ordered_x[2].taken = false;
955
ordered_x[3].p.x = int(rec.x1 + dyhw); ordered_x[3].p.y = int(rec.y1 - dxhw); ordered_x[3].taken = false;
956
957
std::sort(ordered_x, ordered_x + 4, AsmallerB_XoverY);
958
959
// Find min y. And mark as taken. find max y.
960
for(unsigned int i = 1; i < 4; ++i)
961
{
962
if(min_y->p.y > ordered_x[i].p.y) {min_y = &ordered_x[i]; }
963
if(max_y->p.y < ordered_x[i].p.y) {max_y = &ordered_x[i]; }
964
}
965
min_y->taken = true;
966
967
// Find leftmost untaken point;
968
edge* leftmost = 0;
969
for(unsigned int i = 0; i < 4; ++i)
970
{
971
if(!ordered_x[i].taken)
972
{
973
if(!leftmost) // if uninitialized
974
{
975
leftmost = &ordered_x[i];
976
}
977
else if (leftmost->p.x > ordered_x[i].p.x)
978
{
979
leftmost = &ordered_x[i];
980
}
981
}
982
}
983
CV_Assert(leftmost != NULL);
984
leftmost->taken = true;
985
986
// Find rightmost untaken point;
987
edge* rightmost = 0;
988
for(unsigned int i = 0; i < 4; ++i)
989
{
990
if(!ordered_x[i].taken)
991
{
992
if(!rightmost) // if uninitialized
993
{
994
rightmost = &ordered_x[i];
995
}
996
else if (rightmost->p.x < ordered_x[i].p.x)
997
{
998
rightmost = &ordered_x[i];
999
}
1000
}
1001
}
1002
CV_Assert(rightmost != NULL);
1003
rightmost->taken = true;
1004
1005
// Find last untaken point;
1006
edge* tailp = 0;
1007
for(unsigned int i = 0; i < 4; ++i)
1008
{
1009
if(!ordered_x[i].taken)
1010
{
1011
if(!tailp) // if uninitialized
1012
{
1013
tailp = &ordered_x[i];
1014
}
1015
else if (tailp->p.x > ordered_x[i].p.x)
1016
{
1017
tailp = &ordered_x[i];
1018
}
1019
}
1020
}
1021
CV_Assert(tailp != NULL);
1022
tailp->taken = true;
1023
1024
double flstep = (min_y->p.y != leftmost->p.y) ?
1025
(min_y->p.x - leftmost->p.x) / (min_y->p.y - leftmost->p.y) : 0; //first left step
1026
double slstep = (leftmost->p.y != tailp->p.x) ?
1027
(leftmost->p.x - tailp->p.x) / (leftmost->p.y - tailp->p.x) : 0; //second left step
1028
1029
double frstep = (min_y->p.y != rightmost->p.y) ?
1030
(min_y->p.x - rightmost->p.x) / (min_y->p.y - rightmost->p.y) : 0; //first right step
1031
double srstep = (rightmost->p.y != tailp->p.x) ?
1032
(rightmost->p.x - tailp->p.x) / (rightmost->p.y - tailp->p.x) : 0; //second right step
1033
1034
double lstep = flstep, rstep = frstep;
1035
1036
double left_x = min_y->p.x, right_x = min_y->p.x;
1037
1038
// Loop around all points in the region and count those that are aligned.
1039
int min_iter = min_y->p.y;
1040
int max_iter = max_y->p.y;
1041
for(int y = min_iter; y <= max_iter; ++y)
1042
{
1043
if (y < 0 || y >= img_height) continue;
1044
1045
for(int x = int(left_x); x <= int(right_x); ++x)
1046
{
1047
if (x < 0 || x >= img_width) continue;
1048
1049
++total_pts;
1050
if(isAligned(x, y, rec.theta, rec.prec))
1051
{
1052
++alg_pts;
1053
}
1054
}
1055
1056
if(y >= leftmost->p.y) { lstep = slstep; }
1057
if(y >= rightmost->p.y) { rstep = srstep; }
1058
1059
left_x += lstep;
1060
right_x += rstep;
1061
}
1062
1063
return nfa(total_pts, alg_pts, rec.p);
1064
}
1065
1066
double LineSegmentDetectorImpl::nfa(const int& n, const int& k, const double& p) const
1067
{
1068
// Trivial cases
1069
if(n == 0 || k == 0) { return -LOG_NT; }
1070
if(n == k) { return -LOG_NT - double(n) * log10(p); }
1071
1072
double p_term = p / (1 - p);
1073
1074
double log1term = (double(n) + 1) - log_gamma(double(k) + 1)
1075
- log_gamma(double(n-k) + 1)
1076
+ double(k) * log(p) + double(n-k) * log(1.0 - p);
1077
double term = exp(log1term);
1078
1079
if(double_equal(term, 0))
1080
{
1081
if(k > n * p) return -log1term / M_LN10 - LOG_NT;
1082
else return -LOG_NT;
1083
}
1084
1085
// Compute more terms if needed
1086
double bin_tail = term;
1087
double tolerance = 0.1; // an error of 10% in the result is accepted
1088
for(int i = k + 1; i <= n; ++i)
1089
{
1090
double bin_term = double(n - i + 1) / double(i);
1091
double mult_term = bin_term * p_term;
1092
term *= mult_term;
1093
bin_tail += term;
1094
if(bin_term < 1)
1095
{
1096
double err = term * ((1 - pow(mult_term, double(n-i+1))) / (1 - mult_term) - 1);
1097
if(err < tolerance * fabs(-log10(bin_tail) - LOG_NT) * bin_tail) break;
1098
}
1099
1100
}
1101
return -log10(bin_tail) - LOG_NT;
1102
}
1103
1104
inline bool LineSegmentDetectorImpl::isAligned(int x, int y, const double& theta, const double& prec) const
1105
{
1106
if(x < 0 || y < 0 || x >= angles.cols || y >= angles.rows) { return false; }
1107
const double& a = angles.at<double>(y, x);
1108
if(a == NOTDEF) { return false; }
1109
1110
// It is assumed that 'theta' and 'a' are in the range [-pi,pi]
1111
double n_theta = theta - a;
1112
if(n_theta < 0) { n_theta = -n_theta; }
1113
if(n_theta > M_3_2_PI)
1114
{
1115
n_theta -= M_2__PI;
1116
if(n_theta < 0) n_theta = -n_theta;
1117
}
1118
1119
return n_theta <= prec;
1120
}
1121
1122
1123
void LineSegmentDetectorImpl::drawSegments(InputOutputArray _image, InputArray lines)
1124
{
1125
CV_INSTRUMENT_REGION();
1126
1127
CV_Assert(!_image.empty() && (_image.channels() == 1 || _image.channels() == 3));
1128
1129
if (_image.channels() == 1)
1130
{
1131
cvtColor(_image, _image, COLOR_GRAY2BGR);
1132
}
1133
1134
Mat _lines = lines.getMat();
1135
const int N = _lines.checkVector(4);
1136
1137
CV_Assert(_lines.depth() == CV_32F || _lines.depth() == CV_32S);
1138
1139
// Draw segments
1140
if (_lines.depth() == CV_32F)
1141
{
1142
for (int i = 0; i < N; ++i)
1143
{
1144
const Vec4f& v = _lines.at<Vec4f>(i);
1145
const Point2f b(v[0], v[1]);
1146
const Point2f e(v[2], v[3]);
1147
line(_image, b, e, Scalar(0, 0, 255), 1);
1148
}
1149
}
1150
else
1151
{
1152
for (int i = 0; i < N; ++i)
1153
{
1154
const Vec4i& v = _lines.at<Vec4i>(i);
1155
const Point2i b(v[0], v[1]);
1156
const Point2i e(v[2], v[3]);
1157
line(_image, b, e, Scalar(0, 0, 255), 1);
1158
}
1159
}
1160
}
1161
1162
1163
int LineSegmentDetectorImpl::compareSegments(const Size& size, InputArray lines1, InputArray lines2, InputOutputArray _image)
1164
{
1165
CV_INSTRUMENT_REGION();
1166
1167
Size sz = size;
1168
if (_image.needed() && _image.size() != size) sz = _image.size();
1169
CV_Assert(!sz.empty());
1170
1171
Mat_<uchar> I1 = Mat_<uchar>::zeros(sz);
1172
Mat_<uchar> I2 = Mat_<uchar>::zeros(sz);
1173
1174
Mat _lines1 = lines1.getMat();
1175
Mat _lines2 = lines2.getMat();
1176
const int N1 = _lines1.checkVector(4);
1177
const int N2 = _lines2.checkVector(4);
1178
1179
CV_Assert(_lines1.depth() == CV_32F || _lines1.depth() == CV_32S);
1180
CV_Assert(_lines2.depth() == CV_32F || _lines2.depth() == CV_32S);
1181
1182
if (_lines1.depth() == CV_32S)
1183
_lines1.convertTo(_lines1, CV_32F);
1184
if (_lines2.depth() == CV_32S)
1185
_lines2.convertTo(_lines2, CV_32F);
1186
1187
// Draw segments
1188
for(int i = 0; i < N1; ++i)
1189
{
1190
const Point2f b(_lines1.at<Vec4f>(i)[0], _lines1.at<Vec4f>(i)[1]);
1191
const Point2f e(_lines1.at<Vec4f>(i)[2], _lines1.at<Vec4f>(i)[3]);
1192
line(I1, b, e, Scalar::all(255), 1);
1193
}
1194
for(int i = 0; i < N2; ++i)
1195
{
1196
const Point2f b(_lines2.at<Vec4f>(i)[0], _lines2.at<Vec4f>(i)[1]);
1197
const Point2f e(_lines2.at<Vec4f>(i)[2], _lines2.at<Vec4f>(i)[3]);
1198
line(I2, b, e, Scalar::all(255), 1);
1199
}
1200
1201
// Count the pixels that don't agree
1202
Mat Ixor;
1203
bitwise_xor(I1, I2, Ixor);
1204
int N = countNonZero(Ixor);
1205
1206
if (_image.needed())
1207
{
1208
CV_Assert(_image.channels() == 3);
1209
Mat img = _image.getMatRef();
1210
CV_Assert(img.isContinuous() && I1.isContinuous() && I2.isContinuous());
1211
1212
for (unsigned int i = 0; i < I1.total(); ++i)
1213
{
1214
uchar i1 = I1.ptr()[i];
1215
uchar i2 = I2.ptr()[i];
1216
if (i1 || i2)
1217
{
1218
unsigned int base_idx = i * 3;
1219
if (i1) img.ptr()[base_idx] = 255;
1220
else img.ptr()[base_idx] = 0;
1221
img.ptr()[base_idx + 1] = 0;
1222
if (i2) img.ptr()[base_idx + 2] = 255;
1223
else img.ptr()[base_idx + 2] = 0;
1224
}
1225
}
1226
}
1227
1228
return N;
1229
}
1230
1231
} // namespace cv
1232
1233