Update to 2.0.0 tree from current Fremantle build
[opencv] / src / cv / cvlinefit.cpp
diff --git a/src/cv/cvlinefit.cpp b/src/cv/cvlinefit.cpp
new file mode 100644 (file)
index 0000000..4095c80
--- /dev/null
@@ -0,0 +1,727 @@
+/*M///////////////////////////////////////////////////////////////////////////////////////
+//
+//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+//
+//  By downloading, copying, installing or using the software you agree to this license.
+//  If you do not agree to this license, do not download, install,
+//  copy or use the software.
+//
+//
+//                        Intel License Agreement
+//                For Open Source Computer Vision Library
+//
+// Copyright (C) 2000, Intel Corporation, all rights reserved.
+// Third party copyrights are property of their respective owners.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+//   * Redistribution's of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//
+//   * Redistribution's in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//
+//   * The name of Intel Corporation may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// This software is provided by the copyright holders and contributors "as is" and
+// any express or implied warranties, including, but not limited to, the implied
+// warranties of merchantability and fitness for a particular purpose are disclaimed.
+// In no event shall the Intel Corporation or contributors be liable for any direct,
+// indirect, incidental, special, exemplary, or consequential damages
+// (including, but not limited to, procurement of substitute goods or services;
+// loss of use, data, or profits; or business interruption) however caused
+// and on any theory of liability, whether in contract, strict liability,
+// or tort (including negligence or otherwise) arising in any way out of
+// the use of this software, even if advised of the possibility of such damage.
+//
+//M*/
+#include "_cv.h"
+
+static const double eps = 1e-6;
+
+static CvStatus
+icvFitLine2D_wods( CvPoint2D32f * points, int _count, float *weights, float *line )
+{
+    double x = 0, y = 0, x2 = 0, y2 = 0, xy = 0, w = 0;
+    double dx2, dy2, dxy;
+    int i;
+    int count = _count;
+    float t;
+
+    /* Calculating the average of x and y... */
+
+    if( weights == 0 )
+    {
+        for( i = 0; i < count; i += 1 )
+        {
+            x += points[i].x;
+            y += points[i].y;
+            x2 += points[i].x * points[i].x;
+            y2 += points[i].y * points[i].y;
+            xy += points[i].x * points[i].y;
+        }
+        w = (float) count;
+    }
+    else
+    {
+        for( i = 0; i < count; i += 1 )
+        {
+            x += weights[i] * points[i].x;
+            y += weights[i] * points[i].y;
+            x2 += weights[i] * points[i].x * points[i].x;
+            y2 += weights[i] * points[i].y * points[i].y;
+            xy += weights[i] * points[i].x * points[i].y;
+            w += weights[i];
+        }
+    }
+
+    x /= w;
+    y /= w;
+    x2 /= w;
+    y2 /= w;
+    xy /= w;
+
+    dx2 = x2 - x * x;
+    dy2 = y2 - y * y;
+    dxy = xy - x * y;
+
+    t = (float) atan2( 2 * dxy, dx2 - dy2 ) / 2;
+    line[0] = (float) cos( t );
+    line[1] = (float) sin( t );
+
+    line[2] = (float) x;
+    line[3] = (float) y;
+
+    return CV_NO_ERR;
+}
+
+static CvStatus
+icvFitLine3D_wods( CvPoint3D32f * points, int count, float *weights, float *line )
+{
+    int i;
+    float w0 = 0;
+    float x0 = 0, y0 = 0, z0 = 0;
+    float x2 = 0, y2 = 0, z2 = 0, xy = 0, yz = 0, xz = 0;
+    float dx2, dy2, dz2, dxy, dxz, dyz;
+    float *v;
+    float n;
+    float det[9], evc[9], evl[3];
+
+    memset( evl, 0, 3*sizeof(evl[0]));
+    memset( evc, 0, 9*sizeof(evl[0]));
+
+    if( weights )
+    {
+        for( i = 0; i < count; i++ )
+        {
+            float x = points[i].x;
+            float y = points[i].y;
+            float z = points[i].z;
+            float w = weights[i];
+
+
+            x2 += x * x * w;
+            xy += x * y * w;
+            xz += x * z * w;
+            y2 += y * y * w;
+            yz += y * z * w;
+            z2 += z * z * w;
+            x0 += x * w;
+            y0 += y * w;
+            z0 += z * w;
+            w0 += w;
+        }
+    }
+    else
+    {
+        for( i = 0; i < count; i++ )
+        {
+            float x = points[i].x;
+            float y = points[i].y;
+            float z = points[i].z;
+
+            x2 += x * x;
+            xy += x * y;
+            xz += x * z;
+            y2 += y * y;
+            yz += y * z;
+            z2 += z * z;
+            x0 += x;
+            y0 += y;
+            z0 += z;
+        }
+        w0 = (float) count;
+    }
+
+    x2 /= w0;
+    xy /= w0;
+    xz /= w0;
+    y2 /= w0;
+    yz /= w0;
+    z2 /= w0;
+
+    x0 /= w0;
+    y0 /= w0;
+    z0 /= w0;
+
+    dx2 = x2 - x0 * x0;
+    dxy = xy - x0 * y0;
+    dxz = xz - x0 * z0;
+    dy2 = y2 - y0 * y0;
+    dyz = yz - y0 * z0;
+    dz2 = z2 - z0 * z0;
+
+    det[0] = dz2 + dy2;
+    det[1] = -dxy;
+    det[2] = -dxz;
+    det[3] = det[1];
+    det[4] = dx2 + dz2;
+    det[5] = -dyz;
+    det[6] = det[2];
+    det[7] = det[5];
+    det[8] = dy2 + dx2;
+
+    /* Searching for a eigenvector of det corresponding to the minimal eigenvalue */
+#if 1
+    {
+    CvMat _det = cvMat( 3, 3, CV_32F, det );
+    CvMat _evc = cvMat( 3, 3, CV_32F, evc );
+    CvMat _evl = cvMat( 3, 1, CV_32F, evl );
+    cvEigenVV( &_det, &_evc, &_evl, 0 );
+    i = evl[0] < evl[1] ? (evl[0] < evl[2] ? 0 : 2) : (evl[1] < evl[2] ? 1 : 2);
+    }
+#else
+    {
+        CvMat _det = cvMat( 3, 3, CV_32F, det );
+        CvMat _evc = cvMat( 3, 3, CV_32F, evc );
+        CvMat _evl = cvMat( 1, 3, CV_32F, evl );
+
+        cvSVD( &_det, &_evl, &_evc, 0, CV_SVD_MODIFY_A+CV_SVD_U_T );
+    }
+    i = 2;
+#endif
+    v = &evc[i * 3];
+    n = (float) sqrt( (double)v[0] * v[0] + (double)v[1] * v[1] + (double)v[2] * v[2] );
+    n = (float)MAX(n, eps);
+    line[0] = v[0] / n;
+    line[1] = v[1] / n;
+    line[2] = v[2] / n;
+    line[3] = x0;
+    line[4] = y0;
+    line[5] = z0;
+
+    return CV_NO_ERR;
+}
+
+static double
+icvCalcDist2D( CvPoint2D32f * points, int count, float *_line, float *dist )
+{
+    int j;
+    float px = _line[2], py = _line[3];
+    float nx = _line[1], ny = -_line[0];
+    double sum_dist = 0.;
+
+    for( j = 0; j < count; j++ )
+    {
+        float x, y;
+
+        x = points[j].x - px;
+        y = points[j].y - py;
+
+        dist[j] = (float) fabs( nx * x + ny * y );
+        sum_dist += dist[j];
+    }
+
+    return sum_dist;
+}
+
+static double
+icvCalcDist3D( CvPoint3D32f * points, int count, float *_line, float *dist )
+{
+    int j;
+    float px = _line[3], py = _line[4], pz = _line[5];
+    float vx = _line[0], vy = _line[1], vz = _line[2];
+    double sum_dist = 0.;
+
+    for( j = 0; j < count; j++ )
+    {
+        float x, y, z;
+        double p1, p2, p3;
+
+        x = points[j].x - px;
+        y = points[j].y - py;
+        z = points[j].z - pz;
+
+        p1 = vy * z - vz * y;
+        p2 = vz * x - vx * z;
+        p3 = vx * y - vy * x;
+
+        dist[j] = (float) sqrt( p1*p1 + p2*p2 + p3*p3 );
+        sum_dist += dist[j];
+    }
+
+    return sum_dist;
+}
+
+static void
+icvWeightL1( float *d, int count, float *w )
+{
+    int i;
+
+    for( i = 0; i < count; i++ )
+    {
+        double t = fabs( (double) d[i] );
+        w[i] = (float)(1. / MAX(t, eps));
+    }
+}
+
+static void
+icvWeightL12( float *d, int count, float *w )
+{
+    int i;
+
+    for( i = 0; i < count; i++ )
+    {
+        w[i] = 1.0f / (float) sqrt( 1 + (double) (d[i] * d[i] * 0.5) );
+    }
+}
+
+
+static void
+icvWeightHuber( float *d, int count, float *w, float _c )
+{
+    int i;
+    const float c = _c <= 0 ? 1.345f : _c;
+
+    for( i = 0; i < count; i++ )
+    {
+        if( d[i] < c )
+            w[i] = 1.0f;
+        else
+            w[i] = c/d[i];
+    }
+}
+
+
+static void
+icvWeightFair( float *d, int count, float *w, float _c )
+{
+    int i;
+    const float c = _c == 0 ? 1 / 1.3998f : 1 / _c;
+
+    for( i = 0; i < count; i++ )
+    {
+        w[i] = 1 / (1 + d[i] * c);
+    }
+}
+
+static void
+icvWeightWelsch( float *d, int count, float *w, float _c )
+{
+    int i;
+    const float c = _c == 0 ? 1 / 2.9846f : 1 / _c;
+
+    for( i = 0; i < count; i++ )
+    {
+        w[i] = (float) exp( -d[i] * d[i] * c * c );
+    }
+}
+
+
+/* Takes an array of 2D points, type of distance (including user-defined
+distance specified by callbacks, fills the array of four floats with line
+parameters A, B, C, D, where (A, B) is the normalized direction vector,
+(C, D) is the point that belongs to the line. */
+
+static CvStatus  icvFitLine2D( CvPoint2D32f * points, int count, int dist,
+                               float _param, float reps, float aeps, float *line )
+{
+    double EPS = count*FLT_EPSILON;
+    void (*calc_weights) (float *, int, float *) = 0;
+    void (*calc_weights_param) (float *, int, float *, float) = 0;
+    float *w;                   /* weights */
+    float *r;                   /* square distances */
+    int i, j, k;
+    float _line[6], _lineprev[6];
+    float rdelta = reps != 0 ? reps : 1.0f;
+    float adelta = aeps != 0 ? aeps : 0.01f;
+    double min_err = DBL_MAX, err = 0;
+    CvRNG rng = cvRNG(-1);
+
+    memset( line, 0, 4*sizeof(line[0]) );
+
+    switch (dist)
+    {
+    case CV_DIST_L2:
+        return icvFitLine2D_wods( points, count, 0, line );
+
+    case CV_DIST_L1:
+        calc_weights = icvWeightL1;
+        break;
+
+    case CV_DIST_L12:
+        calc_weights = icvWeightL12;
+        break;
+
+    case CV_DIST_FAIR:
+        calc_weights_param = icvWeightFair;
+        break;
+
+    case CV_DIST_WELSCH:
+        calc_weights_param = icvWeightWelsch;
+        break;
+
+    case CV_DIST_HUBER:
+        calc_weights_param = icvWeightHuber;
+        break;
+
+    /*case CV_DIST_USER:
+        calc_weights = (void ( * )(float *, int, float *)) _PFP.fp;
+        break;*/
+
+    default:
+        return CV_BADFACTOR_ERR;
+    }
+
+    w = (float *) cvAlloc( count * sizeof( float ));
+    r = (float *) cvAlloc( count * sizeof( float ));
+
+    for( k = 0; k < 20; k++ )
+    {
+        int first = 1;
+        for( i = 0; i < count; i++ )
+            w[i] = 0.f;
+        
+        for( i = 0; i < MIN(count,10); )
+        {
+            j = cvRandInt(&rng) % count;
+            if( w[j] < FLT_EPSILON )
+            {
+                w[j] = 1.f;
+                i++;
+            }
+        }
+
+        icvFitLine2D_wods( points, count, w, _line );
+        for( i = 0; i < 30; i++ )
+        {
+            double sum_w = 0;
+
+            if( first )
+            {
+                first = 0;
+            }
+            else
+            {
+                double t = _line[0] * _lineprev[0] + _line[1] * _lineprev[1];
+                t = MAX(t,-1.);
+                t = MIN(t,1.);
+                if( fabs(acos(t)) < adelta )
+                {
+                    float x, y, d;
+
+                    x = (float) fabs( _line[2] - _lineprev[2] );
+                    y = (float) fabs( _line[3] - _lineprev[3] );
+
+                    d = x > y ? x : y;
+                    if( d < rdelta )
+                        break;
+                }
+            }
+            /* calculate distances */
+            err = icvCalcDist2D( points, count, _line, r );
+            if( err < EPS )
+                break;
+
+            /* calculate weights */
+            if( calc_weights )
+                calc_weights( r, count, w );
+            else
+                calc_weights_param( r, count, w, _param );
+
+            for( j = 0; j < count; j++ )
+                sum_w += w[j];
+
+            if( fabs(sum_w) > FLT_EPSILON )
+            {
+                sum_w = 1./sum_w;
+                for( j = 0; j < count; j++ )
+                    w[j] = (float)(w[j]*sum_w);
+            }
+            else
+            {
+                for( j = 0; j < count; j++ )
+                    w[j] = 1.f;
+            }
+
+            /* save the line parameters */
+            memcpy( _lineprev, _line, 4 * sizeof( float ));
+
+            /* Run again... */
+            icvFitLine2D_wods( points, count, w, _line );
+        }
+
+        if( err < min_err )
+        {
+            min_err = err;
+            memcpy( line, _line, 4 * sizeof(line[0]));
+            if( err < EPS )
+                break;
+        }
+    }
+
+    cvFree( &w );
+    cvFree( &r );
+    return CV_OK;
+}
+
+
+/* Takes an array of 3D points, type of distance (including user-defined
+distance specified by callbacks, fills the array of four floats with line
+parameters A, B, C, D, E, F, where (A, B, C) is the normalized direction vector,
+(D, E, F) is the point that belongs to the line. */
+
+static CvStatus
+icvFitLine3D( CvPoint3D32f * points, int count, int dist,
+              float _param, float reps, float aeps, float *line )
+{
+    double EPS = count*FLT_EPSILON;
+    void (*calc_weights) (float *, int, float *) = 0;
+    void (*calc_weights_param) (float *, int, float *, float) = 0;
+    float *w;                   /* weights */
+    float *r;                   /* square distances */
+    int i, j, k;
+    float _line[6], _lineprev[6];
+    float rdelta = reps != 0 ? reps : 1.0f;
+    float adelta = aeps != 0 ? aeps : 0.01f;
+    double min_err = DBL_MAX, err = 0;
+    CvRNG rng = cvRNG(-1);
+
+    memset( line, 0, 6*sizeof(line[0]) );
+
+    switch (dist)
+    {
+    case CV_DIST_L2:
+        return icvFitLine3D_wods( points, count, 0, line );
+
+    case CV_DIST_L1:
+        calc_weights = icvWeightL1;
+        break;
+
+    case CV_DIST_L12:
+        calc_weights = icvWeightL12;
+        break;
+
+    case CV_DIST_FAIR:
+        calc_weights_param = icvWeightFair;
+        break;
+
+    case CV_DIST_WELSCH:
+        calc_weights_param = icvWeightWelsch;
+        break;
+
+    case CV_DIST_HUBER:
+        calc_weights_param = icvWeightHuber;
+        break;
+
+    /*case CV_DIST_USER:
+        _PFP.p = param;
+        calc_weights = (void ( * )(float *, int, float *)) _PFP.fp;
+        break;*/
+
+    default:
+        return CV_BADFACTOR_ERR;
+    }
+
+    w = (float *) cvAlloc( count * sizeof( float ));
+    r = (float *) cvAlloc( count * sizeof( float ));
+
+    for( k = 0; k < 20; k++ )
+    {
+        int first = 1;
+        for( i = 0; i < count; i++ )
+            w[i] = 0.f;
+        
+        for( i = 0; i < MIN(count,10); )
+        {
+            j = cvRandInt(&rng) % count;
+            if( w[j] < FLT_EPSILON )
+            {
+                w[j] = 1.f;
+                i++;
+            }
+        }
+
+        icvFitLine3D_wods( points, count, w, _line );
+        for( i = 0; i < 30; i++ )
+        {
+            double sum_w = 0;
+
+            if( first )
+            {
+                first = 0;
+            }
+            else
+            {
+                double t = _line[0] * _lineprev[0] + _line[1] * _lineprev[1] + _line[2] * _lineprev[2];
+                t = MAX(t,-1.);
+                t = MIN(t,1.);
+                if( fabs(acos(t)) < adelta )
+                {
+                    float x, y, z, ax, ay, az, dx, dy, dz, d;
+
+                    x = _line[3] - _lineprev[3];
+                    y = _line[4] - _lineprev[4];
+                    z = _line[5] - _lineprev[5];
+                    ax = _line[0] - _lineprev[0];
+                    ay = _line[1] - _lineprev[1];
+                    az = _line[2] - _lineprev[2];
+                    dx = (float) fabs( y * az - z * ay );
+                    dy = (float) fabs( z * ax - x * az );
+                    dz = (float) fabs( x * ay - y * ax );
+
+                    d = dx > dy ? (dx > dz ? dx : dz) : (dy > dz ? dy : dz);
+                    if( d < rdelta )
+                        break;
+                }
+            }
+            /* calculate distances */
+            if( icvCalcDist3D( points, count, _line, r ) < FLT_EPSILON*count )
+                break;
+
+            /* calculate weights */
+            if( calc_weights )
+                calc_weights( r, count, w );
+            else
+                calc_weights_param( r, count, w, _param );
+
+            for( j = 0; j < count; j++ )
+                sum_w += w[j];
+
+            if( fabs(sum_w) > FLT_EPSILON )
+            {
+                sum_w = 1./sum_w;
+                for( j = 0; j < count; j++ )
+                    w[j] = (float)(w[j]*sum_w);
+            }
+            else
+            {
+                for( j = 0; j < count; j++ )
+                    w[j] = 1.f;
+            }
+
+            /* save the line parameters */
+            memcpy( _lineprev, _line, 6 * sizeof( float ));
+
+            /* Run again... */
+            icvFitLine3D_wods( points, count, w, _line );
+        }
+
+        if( err < min_err )
+        {
+            min_err = err;
+            memcpy( line, _line, 6 * sizeof(line[0]));
+            if( err < EPS )
+                break;
+        }
+    }
+
+    // Return...
+    cvFree( &w );
+    cvFree( &r );
+    return CV_OK;
+}
+
+
+CV_IMPL void
+cvFitLine( const CvArr* array, int dist, double param,
+           double reps, double aeps, float *line )
+{
+    schar* buffer = 0;
+    CV_FUNCNAME( "cvFitLine" );
+
+    __BEGIN__;
+
+    schar* points = 0;
+    union { CvContour contour; CvSeq seq; } header;
+    CvSeqBlock block;
+    CvSeq* ptseq = (CvSeq*)array;
+    int type;
+
+    if( !line )
+        CV_ERROR( CV_StsNullPtr, "NULL pointer to line parameters" );
+
+    if( CV_IS_SEQ(ptseq) )
+    {
+        type = CV_SEQ_ELTYPE(ptseq);
+        if( ptseq->total == 0 )
+            CV_ERROR( CV_StsBadSize, "The sequence has no points" );
+        if( (type!=CV_32FC2 && type!=CV_32FC3 && type!=CV_32SC2 && type!=CV_32SC3) ||
+            CV_ELEM_SIZE(type) != ptseq->elem_size )
+            CV_ERROR( CV_StsUnsupportedFormat,
+                "Input sequence must consist of 2d points or 3d points" );
+    }
+    else
+    {
+        CvMat* mat = (CvMat*)array;
+        type = CV_MAT_TYPE(mat->type);
+        if( !CV_IS_MAT(mat))
+            CV_ERROR( CV_StsBadArg, "Input array is not a sequence nor matrix" );
+
+        if( !CV_IS_MAT_CONT(mat->type) ||
+            (type!=CV_32FC2 && type!=CV_32FC3 && type!=CV_32SC2 && type!=CV_32SC3) ||
+            (mat->width != 1 && mat->height != 1))
+            CV_ERROR( CV_StsBadArg,
+            "Input array must be 1d continuous array of 2d or 3d points" );
+
+        CV_CALL( ptseq = cvMakeSeqHeaderForArray(
+            CV_SEQ_KIND_GENERIC|type, sizeof(CvContour), CV_ELEM_SIZE(type), mat->data.ptr,
+            mat->width + mat->height - 1, &header.seq, &block ));
+    }
+
+    if( reps < 0 || aeps < 0 )
+        CV_ERROR( CV_StsOutOfRange, "Both reps and aeps must be non-negative" );
+
+    if( CV_MAT_DEPTH(type) == CV_32F && ptseq->first->next == ptseq->first )
+    {
+        /* no need to copy data in this case */
+        points = ptseq->first->data;
+    }
+    else
+    {
+        CV_CALL( buffer = points = (schar*)cvAlloc( ptseq->total*CV_ELEM_SIZE(type) ));
+        CV_CALL( cvCvtSeqToArray( ptseq, points, CV_WHOLE_SEQ ));
+
+        if( CV_MAT_DEPTH(type) != CV_32F )
+        {
+            int i, total = ptseq->total*CV_MAT_CN(type);
+            assert( CV_MAT_DEPTH(type) == CV_32S );
+
+            for( i = 0; i < total; i++ )
+                ((float*)points)[i] = (float)((int*)points)[i];
+        }
+    }
+
+    if( dist == CV_DIST_USER )
+        CV_ERROR( CV_StsBadArg, "User-defined distance is not allowed" );
+
+    if( CV_MAT_CN(type) == 2 )
+    {
+        IPPI_CALL( icvFitLine2D( (CvPoint2D32f*)points, ptseq->total,
+                                 dist, (float)param, (float)reps, (float)aeps, line ));
+    }
+    else
+    {
+        IPPI_CALL( icvFitLine3D( (CvPoint3D32f*)points, ptseq->total,
+                                 dist, (float)param, (float)reps, (float)aeps, line ));
+    }
+
+    __END__;
+
+    cvFree( &buffer );
+}
+
+/* End of file. */