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
fitLine2D_wods(const Point2f * points,int count,float * weights,float * line)48 static void fitLine2D_wods( const Point2f* points, int count, float *weights, float *line )
49 {
50 double x = 0, y = 0, x2 = 0, y2 = 0, xy = 0, w = 0;
51 double dx2, dy2, dxy;
52 int i;
53 float t;
54
55 // Calculating the average of x and y...
56 if( weights == 0 )
57 {
58 for( i = 0; i < count; i += 1 )
59 {
60 x += points[i].x;
61 y += points[i].y;
62 x2 += points[i].x * points[i].x;
63 y2 += points[i].y * points[i].y;
64 xy += points[i].x * points[i].y;
65 }
66 w = (float) count;
67 }
68 else
69 {
70 for( i = 0; i < count; i += 1 )
71 {
72 x += weights[i] * points[i].x;
73 y += weights[i] * points[i].y;
74 x2 += weights[i] * points[i].x * points[i].x;
75 y2 += weights[i] * points[i].y * points[i].y;
76 xy += weights[i] * points[i].x * points[i].y;
77 w += weights[i];
78 }
79 }
80
81 x /= w;
82 y /= w;
83 x2 /= w;
84 y2 /= w;
85 xy /= w;
86
87 dx2 = x2 - x * x;
88 dy2 = y2 - y * y;
89 dxy = xy - x * y;
90
91 t = (float) atan2( 2 * dxy, dx2 - dy2 ) / 2;
92 line[0] = (float) cos( t );
93 line[1] = (float) sin( t );
94
95 line[2] = (float) x;
96 line[3] = (float) y;
97 }
98
fitLine3D_wods(const Point3f * points,int count,float * weights,float * line)99 static void fitLine3D_wods( const Point3f * points, int count, float *weights, float *line )
100 {
101 int i;
102 float w0 = 0;
103 float x0 = 0, y0 = 0, z0 = 0;
104 float x2 = 0, y2 = 0, z2 = 0, xy = 0, yz = 0, xz = 0;
105 float dx2, dy2, dz2, dxy, dxz, dyz;
106 float *v;
107 float n;
108 float det[9], evc[9], evl[3];
109
110 memset( evl, 0, 3*sizeof(evl[0]));
111 memset( evc, 0, 9*sizeof(evl[0]));
112
113 if( weights )
114 {
115 for( i = 0; i < count; i++ )
116 {
117 float x = points[i].x;
118 float y = points[i].y;
119 float z = points[i].z;
120 float w = weights[i];
121
122
123 x2 += x * x * w;
124 xy += x * y * w;
125 xz += x * z * w;
126 y2 += y * y * w;
127 yz += y * z * w;
128 z2 += z * z * w;
129 x0 += x * w;
130 y0 += y * w;
131 z0 += z * w;
132 w0 += w;
133 }
134 }
135 else
136 {
137 for( i = 0; i < count; i++ )
138 {
139 float x = points[i].x;
140 float y = points[i].y;
141 float z = points[i].z;
142
143 x2 += x * x;
144 xy += x * y;
145 xz += x * z;
146 y2 += y * y;
147 yz += y * z;
148 z2 += z * z;
149 x0 += x;
150 y0 += y;
151 z0 += z;
152 }
153 w0 = (float) count;
154 }
155
156 x2 /= w0;
157 xy /= w0;
158 xz /= w0;
159 y2 /= w0;
160 yz /= w0;
161 z2 /= w0;
162
163 x0 /= w0;
164 y0 /= w0;
165 z0 /= w0;
166
167 dx2 = x2 - x0 * x0;
168 dxy = xy - x0 * y0;
169 dxz = xz - x0 * z0;
170 dy2 = y2 - y0 * y0;
171 dyz = yz - y0 * z0;
172 dz2 = z2 - z0 * z0;
173
174 det[0] = dz2 + dy2;
175 det[1] = -dxy;
176 det[2] = -dxz;
177 det[3] = det[1];
178 det[4] = dx2 + dz2;
179 det[5] = -dyz;
180 det[6] = det[2];
181 det[7] = det[5];
182 det[8] = dy2 + dx2;
183
184 // Searching for a eigenvector of det corresponding to the minimal eigenvalue
185 Mat _det( 3, 3, CV_32F, det );
186 Mat _evc( 3, 3, CV_32F, evc );
187 Mat _evl( 3, 1, CV_32F, evl );
188 eigen( _det, _evl, _evc );
189 i = evl[0] < evl[1] ? (evl[0] < evl[2] ? 0 : 2) : (evl[1] < evl[2] ? 1 : 2);
190
191 v = &evc[i * 3];
192 n = (float) std::sqrt( (double)v[0] * v[0] + (double)v[1] * v[1] + (double)v[2] * v[2] );
193 n = (float)MAX(n, eps);
194 line[0] = v[0] / n;
195 line[1] = v[1] / n;
196 line[2] = v[2] / n;
197 line[3] = x0;
198 line[4] = y0;
199 line[5] = z0;
200 }
201
calcDist2D(const Point2f * points,int count,float * _line,float * dist)202 static double calcDist2D( const Point2f* points, int count, float *_line, float *dist )
203 {
204 int j;
205 float px = _line[2], py = _line[3];
206 float nx = _line[1], ny = -_line[0];
207 double sum_dist = 0.;
208
209 for( j = 0; j < count; j++ )
210 {
211 float x, y;
212
213 x = points[j].x - px;
214 y = points[j].y - py;
215
216 dist[j] = (float) fabs( nx * x + ny * y );
217 sum_dist += dist[j];
218 }
219
220 return sum_dist;
221 }
222
calcDist3D(const Point3f * points,int count,float * _line,float * dist)223 static double calcDist3D( const Point3f* points, int count, float *_line, float *dist )
224 {
225 int j;
226 float px = _line[3], py = _line[4], pz = _line[5];
227 float vx = _line[0], vy = _line[1], vz = _line[2];
228 double sum_dist = 0.;
229
230 for( j = 0; j < count; j++ )
231 {
232 float x, y, z;
233 double p1, p2, p3;
234
235 x = points[j].x - px;
236 y = points[j].y - py;
237 z = points[j].z - pz;
238
239 p1 = vy * z - vz * y;
240 p2 = vz * x - vx * z;
241 p3 = vx * y - vy * x;
242
243 dist[j] = (float) std::sqrt( p1*p1 + p2*p2 + p3*p3 );
244 sum_dist += dist[j];
245 }
246
247 return sum_dist;
248 }
249
weightL1(float * d,int count,float * w)250 static void weightL1( float *d, int count, float *w )
251 {
252 int i;
253
254 for( i = 0; i < count; i++ )
255 {
256 double t = fabs( (double) d[i] );
257 w[i] = (float)(1. / MAX(t, eps));
258 }
259 }
260
weightL12(float * d,int count,float * w)261 static void weightL12( float *d, int count, float *w )
262 {
263 int i;
264
265 for( i = 0; i < count; i++ )
266 {
267 w[i] = 1.0f / (float) std::sqrt( 1 + (double) (d[i] * d[i] * 0.5) );
268 }
269 }
270
271
weightHuber(float * d,int count,float * w,float _c)272 static void weightHuber( float *d, int count, float *w, float _c )
273 {
274 int i;
275 const float c = _c <= 0 ? 1.345f : _c;
276
277 for( i = 0; i < count; i++ )
278 {
279 if( d[i] < c )
280 w[i] = 1.0f;
281 else
282 w[i] = c/d[i];
283 }
284 }
285
286
weightFair(float * d,int count,float * w,float _c)287 static void weightFair( float *d, int count, float *w, float _c )
288 {
289 int i;
290 const float c = _c == 0 ? 1 / 1.3998f : 1 / _c;
291
292 for( i = 0; i < count; i++ )
293 {
294 w[i] = 1 / (1 + d[i] * c);
295 }
296 }
297
weightWelsch(float * d,int count,float * w,float _c)298 static void weightWelsch( float *d, int count, float *w, float _c )
299 {
300 int i;
301 const float c = _c == 0 ? 1 / 2.9846f : 1 / _c;
302
303 for( i = 0; i < count; i++ )
304 {
305 w[i] = (float) std::exp( -d[i] * d[i] * c * c );
306 }
307 }
308
309
310 /* Takes an array of 2D points, type of distance (including user-defined
311 distance specified by callbacks, fills the array of four floats with line
312 parameters A, B, C, D, where (A, B) is the normalized direction vector,
313 (C, D) is the point that belongs to the line. */
314
fitLine2D(const Point2f * points,int count,int dist,float _param,float reps,float aeps,float * line)315 static void fitLine2D( const Point2f * points, int count, int dist,
316 float _param, float reps, float aeps, float *line )
317 {
318 double EPS = count*FLT_EPSILON;
319 void (*calc_weights) (float *, int, float *) = 0;
320 void (*calc_weights_param) (float *, int, float *, float) = 0;
321 int i, j, k;
322 float _line[6], _lineprev[6];
323 float rdelta = reps != 0 ? reps : 1.0f;
324 float adelta = aeps != 0 ? aeps : 0.01f;
325 double min_err = DBL_MAX, err = 0;
326 RNG rng((uint64)-1);
327
328 memset( line, 0, 4*sizeof(line[0]) );
329
330 switch (dist)
331 {
332 case CV_DIST_L2:
333 return fitLine2D_wods( points, count, 0, line );
334
335 case CV_DIST_L1:
336 calc_weights = weightL1;
337 break;
338
339 case CV_DIST_L12:
340 calc_weights = weightL12;
341 break;
342
343 case CV_DIST_FAIR:
344 calc_weights_param = weightFair;
345 break;
346
347 case CV_DIST_WELSCH:
348 calc_weights_param = weightWelsch;
349 break;
350
351 case CV_DIST_HUBER:
352 calc_weights_param = weightHuber;
353 break;
354
355 /*case DIST_USER:
356 calc_weights = (void ( * )(float *, int, float *)) _PFP.fp;
357 break;*/
358 default:
359 CV_Error(CV_StsBadArg, "Unknown distance type");
360 }
361
362 AutoBuffer<float> wr(count*2);
363 float *w = wr, *r = w + count;
364
365 for( k = 0; k < 20; k++ )
366 {
367 int first = 1;
368 for( i = 0; i < count; i++ )
369 w[i] = 0.f;
370
371 for( i = 0; i < MIN(count,10); )
372 {
373 j = rng.uniform(0, count);
374 if( w[j] < FLT_EPSILON )
375 {
376 w[j] = 1.f;
377 i++;
378 }
379 }
380
381 fitLine2D_wods( points, count, w, _line );
382 for( i = 0; i < 30; i++ )
383 {
384 double sum_w = 0;
385
386 if( first )
387 {
388 first = 0;
389 }
390 else
391 {
392 double t = _line[0] * _lineprev[0] + _line[1] * _lineprev[1];
393 t = MAX(t,-1.);
394 t = MIN(t,1.);
395 if( fabs(acos(t)) < adelta )
396 {
397 float x, y, d;
398
399 x = (float) fabs( _line[2] - _lineprev[2] );
400 y = (float) fabs( _line[3] - _lineprev[3] );
401
402 d = x > y ? x : y;
403 if( d < rdelta )
404 break;
405 }
406 }
407 /* calculate distances */
408 err = calcDist2D( points, count, _line, r );
409 if( err < EPS )
410 break;
411
412 /* calculate weights */
413 if( calc_weights )
414 calc_weights( r, count, w );
415 else
416 calc_weights_param( r, count, w, _param );
417
418 for( j = 0; j < count; j++ )
419 sum_w += w[j];
420
421 if( fabs(sum_w) > FLT_EPSILON )
422 {
423 sum_w = 1./sum_w;
424 for( j = 0; j < count; j++ )
425 w[j] = (float)(w[j]*sum_w);
426 }
427 else
428 {
429 for( j = 0; j < count; j++ )
430 w[j] = 1.f;
431 }
432
433 /* save the line parameters */
434 memcpy( _lineprev, _line, 4 * sizeof( float ));
435
436 /* Run again... */
437 fitLine2D_wods( points, count, w, _line );
438 }
439
440 if( err < min_err )
441 {
442 min_err = err;
443 memcpy( line, _line, 4 * sizeof(line[0]));
444 if( err < EPS )
445 break;
446 }
447 }
448 }
449
450
451 /* Takes an array of 3D points, type of distance (including user-defined
452 distance specified by callbacks, fills the array of four floats with line
453 parameters A, B, C, D, E, F, where (A, B, C) is the normalized direction vector,
454 (D, E, F) is the point that belongs to the line. */
fitLine3D(Point3f * points,int count,int dist,float _param,float reps,float aeps,float * line)455 static void fitLine3D( Point3f * points, int count, int dist,
456 float _param, float reps, float aeps, float *line )
457 {
458 double EPS = count*FLT_EPSILON;
459 void (*calc_weights) (float *, int, float *) = 0;
460 void (*calc_weights_param) (float *, int, float *, float) = 0;
461 int i, j, k;
462 float _line[6]={0,0,0,0,0,0}, _lineprev[6]={0,0,0,0,0,0};
463 float rdelta = reps != 0 ? reps : 1.0f;
464 float adelta = aeps != 0 ? aeps : 0.01f;
465 double min_err = DBL_MAX, err = 0;
466 RNG rng((uint64)-1);
467
468 switch (dist)
469 {
470 case CV_DIST_L2:
471 return fitLine3D_wods( points, count, 0, line );
472
473 case CV_DIST_L1:
474 calc_weights = weightL1;
475 break;
476
477 case CV_DIST_L12:
478 calc_weights = weightL12;
479 break;
480
481 case CV_DIST_FAIR:
482 calc_weights_param = weightFair;
483 break;
484
485 case CV_DIST_WELSCH:
486 calc_weights_param = weightWelsch;
487 break;
488
489 case CV_DIST_HUBER:
490 calc_weights_param = weightHuber;
491 break;
492
493 default:
494 CV_Error(CV_StsBadArg, "Unknown distance");
495 }
496
497 AutoBuffer<float> buf(count*2);
498 float *w = buf, *r = w + count;
499
500 for( k = 0; k < 20; k++ )
501 {
502 int first = 1;
503 for( i = 0; i < count; i++ )
504 w[i] = 0.f;
505
506 for( i = 0; i < MIN(count,10); )
507 {
508 j = rng.uniform(0, count);
509 if( w[j] < FLT_EPSILON )
510 {
511 w[j] = 1.f;
512 i++;
513 }
514 }
515
516 fitLine3D_wods( points, count, w, _line );
517 for( i = 0; i < 30; i++ )
518 {
519 double sum_w = 0;
520
521 if( first )
522 {
523 first = 0;
524 }
525 else
526 {
527 double t = _line[0] * _lineprev[0] + _line[1] * _lineprev[1] + _line[2] * _lineprev[2];
528 t = MAX(t,-1.);
529 t = MIN(t,1.);
530 if( fabs(acos(t)) < adelta )
531 {
532 float x, y, z, ax, ay, az, dx, dy, dz, d;
533
534 x = _line[3] - _lineprev[3];
535 y = _line[4] - _lineprev[4];
536 z = _line[5] - _lineprev[5];
537 ax = _line[0] - _lineprev[0];
538 ay = _line[1] - _lineprev[1];
539 az = _line[2] - _lineprev[2];
540 dx = (float) fabs( y * az - z * ay );
541 dy = (float) fabs( z * ax - x * az );
542 dz = (float) fabs( x * ay - y * ax );
543
544 d = dx > dy ? (dx > dz ? dx : dz) : (dy > dz ? dy : dz);
545 if( d < rdelta )
546 break;
547 }
548 }
549 /* calculate distances */
550 err = calcDist3D( points, count, _line, r );
551 //if( err < FLT_EPSILON*count )
552 // break;
553
554 /* calculate weights */
555 if( calc_weights )
556 calc_weights( r, count, w );
557 else
558 calc_weights_param( r, count, w, _param );
559
560 for( j = 0; j < count; j++ )
561 sum_w += w[j];
562
563 if( fabs(sum_w) > FLT_EPSILON )
564 {
565 sum_w = 1./sum_w;
566 for( j = 0; j < count; j++ )
567 w[j] = (float)(w[j]*sum_w);
568 }
569 else
570 {
571 for( j = 0; j < count; j++ )
572 w[j] = 1.f;
573 }
574
575 /* save the line parameters */
576 memcpy( _lineprev, _line, 6 * sizeof( float ));
577
578 /* Run again... */
579 fitLine3D_wods( points, count, w, _line );
580 }
581
582 if( err < min_err )
583 {
584 min_err = err;
585 memcpy( line, _line, 6 * sizeof(line[0]));
586 if( err < EPS )
587 break;
588 }
589 }
590 }
591
592 }
593
fitLine(InputArray _points,OutputArray _line,int distType,double param,double reps,double aeps)594 void cv::fitLine( InputArray _points, OutputArray _line, int distType,
595 double param, double reps, double aeps )
596 {
597 Mat points = _points.getMat();
598
599 float linebuf[6]={0.f};
600 int npoints2 = points.checkVector(2, -1, false);
601 int npoints3 = points.checkVector(3, -1, false);
602
603 CV_Assert( npoints2 >= 0 || npoints3 >= 0 );
604
605 if( points.depth() != CV_32F || !points.isContinuous() )
606 {
607 Mat temp;
608 points.convertTo(temp, CV_32F);
609 points = temp;
610 }
611
612 if( npoints2 >= 0 )
613 fitLine2D( points.ptr<Point2f>(), npoints2, distType,
614 (float)param, (float)reps, (float)aeps, linebuf);
615 else
616 fitLine3D( points.ptr<Point3f>(), npoints3, distType,
617 (float)param, (float)reps, (float)aeps, linebuf);
618
619 Mat(npoints2 >= 0 ? 4 : 6, 1, CV_32F, linebuf).copyTo(_line);
620 }
621
622
623 CV_IMPL void
cvFitLine(const CvArr * array,int dist,double param,double reps,double aeps,float * line)624 cvFitLine( const CvArr* array, int dist, double param,
625 double reps, double aeps, float *line )
626 {
627 CV_Assert(line != 0);
628
629 cv::AutoBuffer<double> buf;
630 cv::Mat points = cv::cvarrToMat(array, false, false, 0, &buf);
631 cv::Mat linemat(points.checkVector(2) >= 0 ? 4 : 6, 1, CV_32F, line);
632
633 cv::fitLine(points, linemat, dist, param, reps, aeps);
634 }
635
636 /* End of file. */
637