Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/src/linefit.cpp
16354 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// Intel License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
14
// Third party copyrights are property of their respective owners.
15
//
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
18
//
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
21
//
22
// * Redistribution's in binary form must reproduce the above copyright notice,
23
// this list of conditions and the following disclaimer in the documentation
24
// and/or other materials provided with the distribution.
25
//
26
// * The name of Intel Corporation may not be used to endorse or promote products
27
// derived from this software without specific prior written permission.
28
//
29
// This software is provided by the copyright holders and contributors "as is" and
30
// any express or implied warranties, including, but not limited to, the implied
31
// warranties of merchantability and fitness for a particular purpose are disclaimed.
32
// In no event shall the Intel Corporation or contributors be liable for any direct,
33
// indirect, incidental, special, exemplary, or consequential damages
34
// (including, but not limited to, procurement of substitute goods or services;
35
// loss of use, data, or profits; or business interruption) however caused
36
// and on any theory of liability, whether in contract, strict liability,
37
// or tort (including negligence or otherwise) arising in any way out of
38
// the use of this software, even if advised of the possibility of such damage.
39
//
40
//M*/
41
#include "precomp.hpp"
42
43
namespace cv
44
{
45
46
static const double eps = 1e-6;
47
48
static void fitLine2D_wods( const Point2f* points, int count, float *weights, float *line )
49
{
50
CV_Assert(count > 0);
51
double x = 0, y = 0, x2 = 0, y2 = 0, xy = 0, w = 0;
52
double dx2, dy2, dxy;
53
int i;
54
float t;
55
56
// Calculating the average of x and y...
57
if( weights == 0 )
58
{
59
for( i = 0; i < count; i += 1 )
60
{
61
x += points[i].x;
62
y += points[i].y;
63
x2 += points[i].x * points[i].x;
64
y2 += points[i].y * points[i].y;
65
xy += points[i].x * points[i].y;
66
}
67
w = (float) count;
68
}
69
else
70
{
71
for( i = 0; i < count; i += 1 )
72
{
73
x += weights[i] * points[i].x;
74
y += weights[i] * points[i].y;
75
x2 += weights[i] * points[i].x * points[i].x;
76
y2 += weights[i] * points[i].y * points[i].y;
77
xy += weights[i] * points[i].x * points[i].y;
78
w += weights[i];
79
}
80
}
81
82
x /= w;
83
y /= w;
84
x2 /= w;
85
y2 /= w;
86
xy /= w;
87
88
dx2 = x2 - x * x;
89
dy2 = y2 - y * y;
90
dxy = xy - x * y;
91
92
t = (float) atan2( 2 * dxy, dx2 - dy2 ) / 2;
93
line[0] = (float) cos( t );
94
line[1] = (float) sin( t );
95
96
line[2] = (float) x;
97
line[3] = (float) y;
98
}
99
100
static void fitLine3D_wods( const Point3f * points, int count, float *weights, float *line )
101
{
102
CV_Assert(count > 0);
103
int i;
104
float w0 = 0;
105
float x0 = 0, y0 = 0, z0 = 0;
106
float x2 = 0, y2 = 0, z2 = 0, xy = 0, yz = 0, xz = 0;
107
float dx2, dy2, dz2, dxy, dxz, dyz;
108
float *v;
109
float n;
110
float det[9], evc[9], evl[3];
111
112
memset( evl, 0, 3*sizeof(evl[0]));
113
memset( evc, 0, 9*sizeof(evl[0]));
114
115
if( weights )
116
{
117
for( i = 0; i < count; i++ )
118
{
119
float x = points[i].x;
120
float y = points[i].y;
121
float z = points[i].z;
122
float w = weights[i];
123
124
125
x2 += x * x * w;
126
xy += x * y * w;
127
xz += x * z * w;
128
y2 += y * y * w;
129
yz += y * z * w;
130
z2 += z * z * w;
131
x0 += x * w;
132
y0 += y * w;
133
z0 += z * w;
134
w0 += w;
135
}
136
}
137
else
138
{
139
for( i = 0; i < count; i++ )
140
{
141
float x = points[i].x;
142
float y = points[i].y;
143
float z = points[i].z;
144
145
x2 += x * x;
146
xy += x * y;
147
xz += x * z;
148
y2 += y * y;
149
yz += y * z;
150
z2 += z * z;
151
x0 += x;
152
y0 += y;
153
z0 += z;
154
}
155
w0 = (float) count;
156
}
157
158
x2 /= w0;
159
xy /= w0;
160
xz /= w0;
161
y2 /= w0;
162
yz /= w0;
163
z2 /= w0;
164
165
x0 /= w0;
166
y0 /= w0;
167
z0 /= w0;
168
169
dx2 = x2 - x0 * x0;
170
dxy = xy - x0 * y0;
171
dxz = xz - x0 * z0;
172
dy2 = y2 - y0 * y0;
173
dyz = yz - y0 * z0;
174
dz2 = z2 - z0 * z0;
175
176
det[0] = dz2 + dy2;
177
det[1] = -dxy;
178
det[2] = -dxz;
179
det[3] = det[1];
180
det[4] = dx2 + dz2;
181
det[5] = -dyz;
182
det[6] = det[2];
183
det[7] = det[5];
184
det[8] = dy2 + dx2;
185
186
// Searching for a eigenvector of det corresponding to the minimal eigenvalue
187
Mat _det( 3, 3, CV_32F, det );
188
Mat _evc( 3, 3, CV_32F, evc );
189
Mat _evl( 3, 1, CV_32F, evl );
190
eigen( _det, _evl, _evc );
191
i = evl[0] < evl[1] ? (evl[0] < evl[2] ? 0 : 2) : (evl[1] < evl[2] ? 1 : 2);
192
193
v = &evc[i * 3];
194
n = (float) std::sqrt( (double)v[0] * v[0] + (double)v[1] * v[1] + (double)v[2] * v[2] );
195
n = (float)MAX(n, eps);
196
line[0] = v[0] / n;
197
line[1] = v[1] / n;
198
line[2] = v[2] / n;
199
line[3] = x0;
200
line[4] = y0;
201
line[5] = z0;
202
}
203
204
static double calcDist2D( const Point2f* points, int count, float *_line, float *dist )
205
{
206
int j;
207
float px = _line[2], py = _line[3];
208
float nx = _line[1], ny = -_line[0];
209
double sum_dist = 0.;
210
211
for( j = 0; j < count; j++ )
212
{
213
float x, y;
214
215
x = points[j].x - px;
216
y = points[j].y - py;
217
218
dist[j] = (float) fabs( nx * x + ny * y );
219
sum_dist += dist[j];
220
}
221
222
return sum_dist;
223
}
224
225
static double calcDist3D( const Point3f* points, int count, float *_line, float *dist )
226
{
227
int j;
228
float px = _line[3], py = _line[4], pz = _line[5];
229
float vx = _line[0], vy = _line[1], vz = _line[2];
230
double sum_dist = 0.;
231
232
for( j = 0; j < count; j++ )
233
{
234
float x, y, z;
235
double p1, p2, p3;
236
237
x = points[j].x - px;
238
y = points[j].y - py;
239
z = points[j].z - pz;
240
241
p1 = vy * z - vz * y;
242
p2 = vz * x - vx * z;
243
p3 = vx * y - vy * x;
244
245
dist[j] = (float) std::sqrt( p1*p1 + p2*p2 + p3*p3 );
246
sum_dist += dist[j];
247
}
248
249
return sum_dist;
250
}
251
252
static void weightL1( float *d, int count, float *w )
253
{
254
int i;
255
256
for( i = 0; i < count; i++ )
257
{
258
double t = fabs( (double) d[i] );
259
w[i] = (float)(1. / MAX(t, eps));
260
}
261
}
262
263
static void weightL12( float *d, int count, float *w )
264
{
265
int i;
266
267
for( i = 0; i < count; i++ )
268
{
269
w[i] = 1.0f / (float) std::sqrt( 1 + (double) (d[i] * d[i] * 0.5) );
270
}
271
}
272
273
274
static void weightHuber( float *d, int count, float *w, float _c )
275
{
276
int i;
277
const float c = _c <= 0 ? 1.345f : _c;
278
279
for( i = 0; i < count; i++ )
280
{
281
if( d[i] < c )
282
w[i] = 1.0f;
283
else
284
w[i] = c/d[i];
285
}
286
}
287
288
289
static void weightFair( float *d, int count, float *w, float _c )
290
{
291
int i;
292
const float c = _c == 0 ? 1 / 1.3998f : 1 / _c;
293
294
for( i = 0; i < count; i++ )
295
{
296
w[i] = 1 / (1 + d[i] * c);
297
}
298
}
299
300
static void weightWelsch( float *d, int count, float *w, float _c )
301
{
302
int i;
303
const float c = _c == 0 ? 1 / 2.9846f : 1 / _c;
304
305
for( i = 0; i < count; i++ )
306
{
307
w[i] = (float) std::exp( -d[i] * d[i] * c * c );
308
}
309
}
310
311
312
/* Takes an array of 2D points, type of distance (including user-defined
313
distance specified by callbacks, fills the array of four floats with line
314
parameters A, B, C, D, where (A, B) is the normalized direction vector,
315
(C, D) is the point that belongs to the line. */
316
317
static void fitLine2D( const Point2f * points, int count, int dist,
318
float _param, float reps, float aeps, float *line )
319
{
320
double EPS = count*FLT_EPSILON;
321
void (*calc_weights) (float *, int, float *) = 0;
322
void (*calc_weights_param) (float *, int, float *, float) = 0;
323
int i, j, k;
324
float _line[4], _lineprev[4];
325
float rdelta = reps != 0 ? reps : 1.0f;
326
float adelta = aeps != 0 ? aeps : 0.01f;
327
double min_err = DBL_MAX, err = 0;
328
RNG rng((uint64)-1);
329
330
memset( line, 0, 4*sizeof(line[0]) );
331
332
switch (dist)
333
{
334
case CV_DIST_L2:
335
return fitLine2D_wods( points, count, 0, line );
336
337
case CV_DIST_L1:
338
calc_weights = weightL1;
339
break;
340
341
case CV_DIST_L12:
342
calc_weights = weightL12;
343
break;
344
345
case CV_DIST_FAIR:
346
calc_weights_param = weightFair;
347
break;
348
349
case CV_DIST_WELSCH:
350
calc_weights_param = weightWelsch;
351
break;
352
353
case CV_DIST_HUBER:
354
calc_weights_param = weightHuber;
355
break;
356
357
/*case DIST_USER:
358
calc_weights = (void ( * )(float *, int, float *)) _PFP.fp;
359
break;*/
360
default:
361
CV_Error(CV_StsBadArg, "Unknown distance type");
362
}
363
364
AutoBuffer<float> wr(count*2);
365
float *w = wr.data(), *r = w + count;
366
367
for( k = 0; k < 20; k++ )
368
{
369
int first = 1;
370
for( i = 0; i < count; i++ )
371
w[i] = 0.f;
372
373
for( i = 0; i < MIN(count,10); )
374
{
375
j = rng.uniform(0, count);
376
if( w[j] < FLT_EPSILON )
377
{
378
w[j] = 1.f;
379
i++;
380
}
381
}
382
383
fitLine2D_wods( points, count, w, _line );
384
for( i = 0; i < 30; i++ )
385
{
386
double sum_w = 0;
387
388
if( first )
389
{
390
first = 0;
391
}
392
else
393
{
394
double t = _line[0] * _lineprev[0] + _line[1] * _lineprev[1];
395
t = MAX(t,-1.);
396
t = MIN(t,1.);
397
if( fabs(acos(t)) < adelta )
398
{
399
float x, y, d;
400
401
x = (float) fabs( _line[2] - _lineprev[2] );
402
y = (float) fabs( _line[3] - _lineprev[3] );
403
404
d = x > y ? x : y;
405
if( d < rdelta )
406
break;
407
}
408
}
409
/* calculate distances */
410
err = calcDist2D( points, count, _line, r );
411
if( err < EPS )
412
break;
413
414
/* calculate weights */
415
if( calc_weights )
416
calc_weights( r, count, w );
417
else
418
calc_weights_param( r, count, w, _param );
419
420
for( j = 0; j < count; j++ )
421
sum_w += w[j];
422
423
if( fabs(sum_w) > FLT_EPSILON )
424
{
425
sum_w = 1./sum_w;
426
for( j = 0; j < count; j++ )
427
w[j] = (float)(w[j]*sum_w);
428
}
429
else
430
{
431
for( j = 0; j < count; j++ )
432
w[j] = 1.f;
433
}
434
435
/* save the line parameters */
436
memcpy( _lineprev, _line, 4 * sizeof( float ));
437
438
/* Run again... */
439
fitLine2D_wods( points, count, w, _line );
440
}
441
442
if( err < min_err )
443
{
444
min_err = err;
445
memcpy( line, _line, 4 * sizeof(line[0]));
446
if( err < EPS )
447
break;
448
}
449
}
450
}
451
452
453
/* Takes an array of 3D points, type of distance (including user-defined
454
distance specified by callbacks, fills the array of four floats with line
455
parameters A, B, C, D, E, F, where (A, B, C) is the normalized direction vector,
456
(D, E, F) is the point that belongs to the line. */
457
static void fitLine3D( Point3f * points, int count, int dist,
458
float _param, float reps, float aeps, float *line )
459
{
460
double EPS = count*FLT_EPSILON;
461
void (*calc_weights) (float *, int, float *) = 0;
462
void (*calc_weights_param) (float *, int, float *, float) = 0;
463
int i, j, k;
464
float _line[6]={0,0,0,0,0,0}, _lineprev[6]={0,0,0,0,0,0};
465
float rdelta = reps != 0 ? reps : 1.0f;
466
float adelta = aeps != 0 ? aeps : 0.01f;
467
double min_err = DBL_MAX, err = 0;
468
RNG rng((uint64)-1);
469
470
switch (dist)
471
{
472
case CV_DIST_L2:
473
return fitLine3D_wods( points, count, 0, line );
474
475
case CV_DIST_L1:
476
calc_weights = weightL1;
477
break;
478
479
case CV_DIST_L12:
480
calc_weights = weightL12;
481
break;
482
483
case CV_DIST_FAIR:
484
calc_weights_param = weightFair;
485
break;
486
487
case CV_DIST_WELSCH:
488
calc_weights_param = weightWelsch;
489
break;
490
491
case CV_DIST_HUBER:
492
calc_weights_param = weightHuber;
493
break;
494
495
default:
496
CV_Error(CV_StsBadArg, "Unknown distance");
497
}
498
499
AutoBuffer<float> buf(count*2);
500
float *w = buf.data(), *r = w + count;
501
502
for( k = 0; k < 20; k++ )
503
{
504
int first = 1;
505
for( i = 0; i < count; i++ )
506
w[i] = 0.f;
507
508
for( i = 0; i < MIN(count,10); )
509
{
510
j = rng.uniform(0, count);
511
if( w[j] < FLT_EPSILON )
512
{
513
w[j] = 1.f;
514
i++;
515
}
516
}
517
518
fitLine3D_wods( points, count, w, _line );
519
for( i = 0; i < 30; i++ )
520
{
521
double sum_w = 0;
522
523
if( first )
524
{
525
first = 0;
526
}
527
else
528
{
529
double t = _line[0] * _lineprev[0] + _line[1] * _lineprev[1] + _line[2] * _lineprev[2];
530
t = MAX(t,-1.);
531
t = MIN(t,1.);
532
if( fabs(acos(t)) < adelta )
533
{
534
float x, y, z, ax, ay, az, dx, dy, dz, d;
535
536
x = _line[3] - _lineprev[3];
537
y = _line[4] - _lineprev[4];
538
z = _line[5] - _lineprev[5];
539
ax = _line[0] - _lineprev[0];
540
ay = _line[1] - _lineprev[1];
541
az = _line[2] - _lineprev[2];
542
dx = (float) fabs( y * az - z * ay );
543
dy = (float) fabs( z * ax - x * az );
544
dz = (float) fabs( x * ay - y * ax );
545
546
d = dx > dy ? (dx > dz ? dx : dz) : (dy > dz ? dy : dz);
547
if( d < rdelta )
548
break;
549
}
550
}
551
/* calculate distances */
552
err = calcDist3D( points, count, _line, r );
553
//if( err < FLT_EPSILON*count )
554
// break;
555
556
/* calculate weights */
557
if( calc_weights )
558
calc_weights( r, count, w );
559
else
560
calc_weights_param( r, count, w, _param );
561
562
for( j = 0; j < count; j++ )
563
sum_w += w[j];
564
565
if( fabs(sum_w) > FLT_EPSILON )
566
{
567
sum_w = 1./sum_w;
568
for( j = 0; j < count; j++ )
569
w[j] = (float)(w[j]*sum_w);
570
}
571
else
572
{
573
for( j = 0; j < count; j++ )
574
w[j] = 1.f;
575
}
576
577
/* save the line parameters */
578
memcpy( _lineprev, _line, 6 * sizeof( float ));
579
580
/* Run again... */
581
fitLine3D_wods( points, count, w, _line );
582
}
583
584
if( err < min_err )
585
{
586
min_err = err;
587
memcpy( line, _line, 6 * sizeof(line[0]));
588
if( err < EPS )
589
break;
590
}
591
}
592
}
593
594
}
595
596
void cv::fitLine( InputArray _points, OutputArray _line, int distType,
597
double param, double reps, double aeps )
598
{
599
CV_INSTRUMENT_REGION();
600
601
Mat points = _points.getMat();
602
603
float linebuf[6]={0.f};
604
int npoints2 = points.checkVector(2, -1, false);
605
int npoints3 = points.checkVector(3, -1, false);
606
607
CV_Assert( npoints2 >= 0 || npoints3 >= 0 );
608
609
if( points.depth() != CV_32F || !points.isContinuous() )
610
{
611
Mat temp;
612
points.convertTo(temp, CV_32F);
613
points = temp;
614
}
615
616
if( npoints2 >= 0 )
617
fitLine2D( points.ptr<Point2f>(), npoints2, distType,
618
(float)param, (float)reps, (float)aeps, linebuf);
619
else
620
fitLine3D( points.ptr<Point3f>(), npoints3, distType,
621
(float)param, (float)reps, (float)aeps, linebuf);
622
623
Mat(npoints2 >= 0 ? 4 : 6, 1, CV_32F, linebuf).copyTo(_line);
624
}
625
626
627
CV_IMPL void
628
cvFitLine( const CvArr* array, int dist, double param,
629
double reps, double aeps, float *line )
630
{
631
CV_Assert(line != 0);
632
633
cv::AutoBuffer<double> buf;
634
cv::Mat points = cv::cvarrToMat(array, false, false, 0, &buf);
635
cv::Mat linemat(points.checkVector(2) >= 0 ? 4 : 6, 1, CV_32F, line);
636
637
cv::fitLine(points, linemat, dist, param, reps, aeps);
638
}
639
640
/* End of file. */
641
642