天天看點

【第二部分 圖像處理】第3章 Opencv圖像處理進階【4 圖像輪廓C】

3.3使用多邊形将輪廓包圍

在實際運用中,常常會有将檢測處的輪廓用多邊形包圍,本節就為讀者講解如何用多邊形包圍輪廓。

3.3.1多邊形包圍輪廓的相關API

3.3.1.1多邊形包圍輪廓的相關API講解

 傳回外圍矩形邊界:boundingRect() 函數

C++: Rect boundingRect(InputArray points)             

【參數】

唯一的參數就是輸入的二維點集。

 尋找最小包圍矩形:minAreaRect()函數

C++: RotatedRect minAreaRect(InputArray points)           

【參數】

對于輸入的二維點集,計算包圍點集的最小矩形

 尋找最小包圍圓形:minEnclosingCircle()函數

利用疊代算法,對給定的二維點集尋找計算可包圍點集的最小圓形,其定義如下

C++: void minEnclosingCircle(InputArray points, 
                             Point2f& center, 
                             float& radius)           

【參數】

第一個參數,points:輸入的二維點集,資料類型為vector<>或Mat類型

第二個參數,center:繪制圓的圓心坐标

第三個參數,radius:圓的半徑

 用橢圓拟合二維點集:fitEllipse()函數

C++: RotatedRect fitEllipse(InputArray points)           

【參數】

唯一的參數就是二維點集。

 逼近多邊形曲線: approxPolyDP()函數

C++: void approxPolyDP(InputArray curve, 
                       OutputArray approxCurve, 
                       double epsilon, 
                       bool closed)           

【參數】

第一個參數,curve:輸入的二維點集,可以是vector類型或Mat類型 ;

第二參數,approxCurve:多邊形逼近的結果,其類型與輸入的點集類型一緻;

第三個參數,epsilon:逼近的精度,為原始曲線和逼近曲線間的最大值;

第四個參數,closed:如果為true,逼近的曲線為封閉曲線,如果為false則逼近曲線不封閉。

 多邊形測試:pointPolygonTest ()函數

double pointPolygonTest(InputArray contour, 
                        Point2f pt, 
                        bool measureDist);           

【參數】

第一個參數,contour:輸入的輪廓;

第二個參數,pt:輪廓中檢測的點;

第四個參數,measureDist:如果是true:估計點到最近輪廓邊緣的距離,否則檢查點是否在輪廓中。

3.3.1.2多邊形包圍輪廓的相關API源代碼

 傳回外圍矩形邊界:boundingRect() 函數

/*【boundingRect ( )源代碼】**********************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\imgproc\src\ shapedescr.cpp
 * @起始行數:698行   
********************************************************************************/
cv::Rect cv::boundingRect(InputArray array)
{
    Mat m = array.getMat();
    return m.depth() <= CV_8U ? maskBoundingRect(m) : pointSetBoundingRect(m);
}           

從源碼可以看出,其實是調用了兩個函數maskBoundingRect()和pointSetBoundingRect(),函數源代碼如下。

/*【pointSetBoundingRect ( )源代碼】***************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\imgproc\src\ shapedescr.cpp
 * @起始行數:483行   
********************************************************************************/
static Rect pointSetBoundingRect( const Mat& points )
{
    int npoints = points.checkVector(2);
    int depth = points.depth();
    CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S));

    int  xmin = 0, ymin = 0, xmax = -1, ymax = -1, i;
    bool is_float = depth == CV_32F;

    if( npoints == 0 )
        return Rect();

    const Point* pts = points.ptr<Point>();
    Point pt = pts[0];

#if CV_SSE4_2
    if(cv::checkHardwareSupport(CV_CPU_SSE4_2))
    {
        if( !is_float )
        {
            __m128i minval, maxval;
            minval = maxval = _mm_loadl_epi64((const __m128i*)(&pt)); //min[0]=pt.x, min[1]=pt.y

            for( i = 1; i < npoints; i++ )
            {
                __m128i ptXY = _mm_loadl_epi64((const __m128i*)&pts[i]);
                minval = _mm_min_epi32(ptXY, minval);
                maxval = _mm_max_epi32(ptXY, maxval);
            }
            xmin = _mm_cvtsi128_si32(minval);
            ymin = _mm_cvtsi128_si32(_mm_srli_si128(minval, 4));
            xmax = _mm_cvtsi128_si32(maxval);
            ymax = _mm_cvtsi128_si32(_mm_srli_si128(maxval, 4));
        }
        else
        {
            __m128 minvalf, maxvalf, z = _mm_setzero_ps(), ptXY = _mm_setzero_ps();
            minvalf = maxvalf = _mm_loadl_pi(z, (const __m64*)(&pt));

            for( i = 1; i < npoints; i++ )
            {
                ptXY = _mm_loadl_pi(ptXY, (const __m64*)&pts[i]);

                minvalf = _mm_min_ps(minvalf, ptXY);
                maxvalf = _mm_max_ps(maxvalf, ptXY);
            }

            float xyminf[2], xymaxf[2];
            _mm_storel_pi((__m64*)xyminf, minvalf);
            _mm_storel_pi((__m64*)xymaxf, maxvalf);
            xmin = cvFloor(xyminf[0]);
            ymin = cvFloor(xyminf[1]);
            xmax = cvFloor(xymaxf[0]);
            ymax = cvFloor(xymaxf[1]);
        }
    }
    else
#endif
    {
        if( !is_float )
        {
            xmin = xmax = pt.x;
            ymin = ymax = pt.y;

            for( i = 1; i < npoints; i++ )
            {
                pt = pts[i];

                if( xmin > pt.x )
                    xmin = pt.x;

                if( xmax < pt.x )
                    xmax = pt.x;

                if( ymin > pt.y )
                    ymin = pt.y;

                if( ymax < pt.y )
                    ymax = pt.y;
            }
        }
        else
        {
            Cv32suf v;
            // init values
            xmin = xmax = CV_TOGGLE_FLT(pt.x);
            ymin = ymax = CV_TOGGLE_FLT(pt.y);

            for( i = 1; i < npoints; i++ )
            {
                pt = pts[i];
                pt.x = CV_TOGGLE_FLT(pt.x);
                pt.y = CV_TOGGLE_FLT(pt.y);

                if( xmin > pt.x )
                    xmin = pt.x;

                if( xmax < pt.x )
                    xmax = pt.x;

                if( ymin > pt.y )
                    ymin = pt.y;

                if( ymax < pt.y )
                    ymax = pt.y;
            }

            v.i = CV_TOGGLE_FLT(xmin); xmin = cvFloor(v.f);
            v.i = CV_TOGGLE_FLT(ymin); ymin = cvFloor(v.f);
            // because right and bottom sides of the bounding rectangle are not inclusive
            // (note +1 in width and height calculation below), cvFloor is used here instead of cvCeil
            v.i = CV_TOGGLE_FLT(xmax); xmax = cvFloor(v.f);
            v.i = CV_TOGGLE_FLT(ymax); ymax = cvFloor(v.f);
        }
    }

    return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
}           
/*【maskBoundingRect ( )源代碼】******************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\imgproc\src\ shapedescr.cpp
 * @起始行數:603行   
********************************************************************************/
static Rect maskBoundingRect( const Mat& img )
{
    CV_Assert( img.depth() <= CV_8S && img.channels() == 1 );

    Size size = img.size();
    int xmin = size.width, ymin = -1, xmax = -1, ymax = -1, i, j, k;

    for( i = 0; i < size.height; i++ )
    {
        const uchar* _ptr = img.ptr(i);
        const uchar* ptr = (const uchar*)alignPtr(_ptr, 4);
        int have_nz = 0, k_min, offset = (int)(ptr - _ptr);
        j = 0;
        offset = MIN(offset, size.width);
        for( ; j < offset; j++ )
            if( _ptr[j] )
            {
                have_nz = 1;
                break;
            }
        if( j < offset )
        {
            if( j < xmin )
                xmin = j;
            if( j > xmax )
                xmax = j;
        }
        if( offset < size.width )
        {
            xmin -= offset;
            xmax -= offset;
            size.width -= offset;
            j = 0;
            for( ; j <= xmin - 4; j += 4 )
                if( *((int*)(ptr+j)) )
                    break;
            for( ; j < xmin; j++ )
                if( ptr[j] )
                {
                    xmin = j;
                    if( j > xmax )
                        xmax = j;
                    have_nz = 1;
                    break;
                }
            k_min = MAX(j-1, xmax);
            k = size.width - 1;
            for( ; k > k_min && (k&3) != 3; k-- )
                if( ptr[k] )
                    break;
            if( k > k_min && (k&3) == 3 )
            {
                for( ; k > k_min+3; k -= 4 )
                    if( *((int*)(ptr+k-3)) )
                        break;
            }
            for( ; k > k_min; k-- )
                if( ptr[k] )
                {
                    xmax = k;
                    have_nz = 1;
                    break;
                }
            if( !have_nz )
            {
                j &= ~3;
                for( ; j <= k - 3; j += 4 )
                    if( *((int*)(ptr+j)) )
                        break;
                for( ; j <= k; j++ )
                    if( ptr[j] )
                    {
                        have_nz = 1;
                        break;
                    }
            }
            xmin += offset;
            xmax += offset;
            size.width += offset;
        }
        if( have_nz )
        {
            if( ymin < 0 )
                ymin = i;
            ymax = i;
        }
    }

    if( xmin >= size.width )
        xmin = ymin = 0;
    return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
}           

 尋找最小包圍矩形:minAreaRect()函數

/*【minAreaRect ( )源代碼】***********************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\imgproc\src\ rotcalipers.cpp
 * @起始行數:343行   
********************************************************************************/
cv::RotatedRect cv::minAreaRect( InputArray _points )
{
    Mat hull;
    Point2f out[3];
    RotatedRect box;

    convexHull(_points, hull, true, true);

    if( hull.depth() != CV_32F )
    {
        Mat temp;
        hull.convertTo(temp, CV_32F);
        hull = temp;
    }

    int n = hull.checkVector(2);
    const Point2f* hpoints = hull.ptr<Point2f>();

    if( n > 2 )
    {
        rotatingCalipers( hpoints, n, CALIPERS_MINAREARECT, (float*)out );
        box.center.x = out[0].x + (out[1].x + out[2].x)*0.5f;
        box.center.y = out[0].y + (out[1].y + out[2].y)*0.5f;
        box.size.width = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y);
        box.size.height = (float)std::sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y);
        box.angle = (float)atan2( (double)out[1].y, (double)out[1].x );
    }
    else if( n == 2 )
    {
        box.center.x = (hpoints[0].x + hpoints[1].x)*0.5f;
        box.center.y = (hpoints[0].y + hpoints[1].y)*0.5f;
        double dx = hpoints[1].x - hpoints[0].x;
        double dy = hpoints[1].y - hpoints[0].y;
        box.size.width = (float)std::sqrt(dx*dx + dy*dy);
        box.size.height = 0;
        box.angle = (float)atan2( dy, dx );
    }
    else
    {
        if( n == 1 )
            box.center = hpoints[0];
    }

    box.angle = (float)(box.angle*180/CV_PI);
    return box;
}
           

 尋找最小包圍圓形:minEnclosingCircle()函數

/*【minEnclosingCircle ( )源代碼】******************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\imgproc\src\ shapedescr.cpp
 * @起始行數:196行   
********************************************************************************/
void cv::minEnclosingCircle( InputArray _points, Point2f& _center, float& _radius )
{
    int max_iters = 100;
    const float eps = FLT_EPSILON*2;
    bool result = false;
    Mat points = _points.getMat();
    int i, j, k, count = points.checkVector(2);
    int depth = points.depth();
    Point2f center;
    float radius = 0.f;
    CV_Assert(count >= 0 && (depth == CV_32F || depth == CV_32S));

    _center.x = _center.y = 0.f;
    _radius = 0.f;

    if( count == 0 )
        return;

    bool is_float = depth == CV_32F;
    const Point* ptsi = points.ptr<Point>();
    const Point2f* ptsf = points.ptr<Point2f>();

    Point2f pt = is_float ? ptsf[0] : Point2f((float)ptsi[0].x,(float)ptsi[0].y);
    Point2f pts[4] = {pt, pt, pt, pt};

    for( i = 1; i < count; i++ )
    {
        pt = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);

        if( pt.x < pts[0].x )
            pts[0] = pt;
        if( pt.x > pts[1].x )
            pts[1] = pt;
        if( pt.y < pts[2].y )
            pts[2] = pt;
        if( pt.y > pts[3].y )
            pts[3] = pt;
    }

    for( k = 0; k < max_iters; k++ )
    {
        double min_delta = 0, delta;
        Point2f farAway(0,0);
        /*only for first iteration because the alg is repared at the loop's foot*/
        if( k == 0 )
            findEnslosingCicle4pts_32f( pts, center, radius );

        for( i = 0; i < count; i++ )
        {
            pt = is_float ? ptsf[i] : Point2f((float)ptsi[i].x,(float)ptsi[i].y);

            delta = pointInCircle( pt, center, radius );
            if( delta < min_delta )
            {
                min_delta = delta;
                farAway = pt;
            }
        }
        result = min_delta >= 0;
        if( result )
            break;

        Point2f ptsCopy[4];
        // find good replacement partner for the point which is at most far away,
        // starting with the one that lays in the actual circle (i=3)
        for( i = 3; i >= 0; i-- )
        {
            for( j = 0; j < 4; j++ )
                ptsCopy[j] = i != j ? pts[j] : farAway;

            findEnslosingCicle4pts_32f( ptsCopy, center, radius );
            if( pointInCircle( pts[i], center, radius ) >= 0)
            {
                // replaced one again in the new circle?
                pts[i] = farAway;
                break;
            }
        }
    }

    if( !result )
    {
        radius = 0.f;
        for( i = 0; i < count; i++ )
        {
            pt = is_float ? ptsf[i] : Point2f((float)ptsi[i].x,(float)ptsi[i].y);
            float dx = center.x - pt.x, dy = center.y - pt.y;
            float t = dx*dx + dy*dy;
            radius = MAX(radius, t);
        }

        radius = (float)(std::sqrt(radius)*(1 + eps));
    }

    _center = center;
    _radius = radius;
}
           

 用橢圓拟合二維點集:fitEllipse()函數

/*【fitEllipse ( )源代碼】**************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\imgproc\src\ shapedescr.cpp
 * @起始行數:369行   
********************************************************************************/
cv::RotatedRect cv::fitEllipse( InputArray _points )
{
    Mat points = _points.getMat();
    int i, n = points.checkVector(2);
    int depth = points.depth();
    CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S));

    RotatedRect box;

    if( n < 5 )
        CV_Error( CV_StsBadSize, "There should be at least 5 points to fit the ellipse" );

    // New fitellipse algorithm, contributed by Dr. Daniel Weiss
    Point2f c(0,0);
    double gfp[5], rp[5], t;
    const double min_eps = 1e-8;
    bool is_float = depth == CV_32F;
    const Point* ptsi = points.ptr<Point>();
    const Point2f* ptsf = points.ptr<Point2f>();

    AutoBuffer<double> _Ad(n*5), _bd(n);
    double *Ad = _Ad, *bd = _bd;

    // first fit for parameters A - E
    Mat A( n, 5, CV_64F, Ad );
    Mat b( n, 1, CV_64F, bd );
    Mat x( 5, 1, CV_64F, gfp );

    for( i = 0; i < n; i++ )
    {
        Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
        c += p;
    }
    c.x /= n;
    c.y /= n;

    for( i = 0; i < n; i++ )
    {
        Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
        p -= c;

        bd[i] = 10000.0; // 1.0?
        Ad[i*5] = -(double)p.x * p.x; // A - C signs inverted as proposed by APP
        Ad[i*5 + 1] = -(double)p.y * p.y;
        Ad[i*5 + 2] = -(double)p.x * p.y;
        Ad[i*5 + 3] = p.x;
        Ad[i*5 + 4] = p.y;
    }

    solve(A, b, x, DECOMP_SVD);

    // now use general-form parameters A - E to find the ellipse center:
    // differentiate general form wrt x/y to get two equations for cx and cy
    A = Mat( 2, 2, CV_64F, Ad );
    b = Mat( 2, 1, CV_64F, bd );
    x = Mat( 2, 1, CV_64F, rp );
    Ad[0] = 2 * gfp[0];
    Ad[1] = Ad[2] = gfp[2];
    Ad[3] = 2 * gfp[1];
    bd[0] = gfp[3];
    bd[1] = gfp[4];
    solve( A, b, x, DECOMP_SVD );

    // re-fit for parameters A - C with those center coordinates
    A = Mat( n, 3, CV_64F, Ad );
    b = Mat( n, 1, CV_64F, bd );
    x = Mat( 3, 1, CV_64F, gfp );
    for( i = 0; i < n; i++ )
    {
        Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
        p -= c;
        bd[i] = 1.0;
        Ad[i * 3] = (p.x - rp[0]) * (p.x - rp[0]);
        Ad[i * 3 + 1] = (p.y - rp[1]) * (p.y - rp[1]);
        Ad[i * 3 + 2] = (p.x - rp[0]) * (p.y - rp[1]);
    }
    solve(A, b, x, DECOMP_SVD);

    // store angle and radii
    rp[4] = -0.5 * atan2(gfp[2], gfp[1] - gfp[0]); // convert from APP angle usage
    if( fabs(gfp[2]) > min_eps )
        t = gfp[2]/sin(-2.0 * rp[4]);
    else // ellipse is rotated by an integer multiple of pi/2
        t = gfp[1] - gfp[0];
    rp[2] = fabs(gfp[0] + gfp[1] - t);
    if( rp[2] > min_eps )
        rp[2] = std::sqrt(2.0 / rp[2]);
    rp[3] = fabs(gfp[0] + gfp[1] + t);
    if( rp[3] > min_eps )
        rp[3] = std::sqrt(2.0 / rp[3]);

    box.center.x = (float)rp[0] + c.x;
    box.center.y = (float)rp[1] + c.y;
    box.size.width = (float)(rp[2]*2);
    box.size.height = (float)(rp[3]*2);
    if( box.size.width > box.size.height )
    {
        float tmp;
        CV_SWAP( box.size.width, box.size.height, tmp );
        box.angle = (float)(90 + rp[4]*180/CV_PI);
    }
    if( box.angle < -180 )
        box.angle += 360;
    if( box.angle > 360 )
        box.angle -= 360;

    return box;
}           

 逼近多邊形曲線:approxPolyDP()函數

/*【approxPolyDP ( )源代碼】**********************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\imgproc\src\ approx.cpp
 * @起始行數:674行   
********************************************************************************/
void cv::approxPolyDP( InputArray _curve, OutputArray _approxCurve,
                      double epsilon, bool closed )
{
    Mat curve = _curve.getMat();
    int npoints = curve.checkVector(2), depth = curve.depth();
    CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F));

    if( npoints == 0 )
    {
        _approxCurve.release();
        return;
    }

    AutoBuffer<Point> _buf(npoints);
    AutoBuffer<Range> _stack(npoints);
    Point* buf = _buf;
    int nout = 0;

    if( depth == CV_32S )
        nout = approxPolyDP_(curve.ptr<Point>(), npoints, buf, closed, epsilon, &_stack);
    else if( depth == CV_32F )
        nout = approxPolyDP_(curve.ptr<Point2f>(), npoints, (Point2f*)buf, closed, epsilon, &_stack);
    else
        CV_Error( CV_StsUnsupportedFormat, "" );

    Mat(nout, 1, CV_MAKETYPE(depth, 2), buf).copyTo(_approxCurve);
}           

 多邊形測試:pointPolygonTest ()函數

/*【pointPolygonTest ( )源代碼】*******************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\imgproc\src\ geometry.cpp
 * @起始行數:95行   
********************************************************************************/
double cv::pointPolygonTest( InputArray _contour, Point2f pt, bool measureDist )
{
    double result = 0;
    Mat contour = _contour.getMat();
    int i, total = contour.checkVector(2), counter = 0;
    int depth = contour.depth();
    CV_Assert( total >= 0 && (depth == CV_32S || depth == CV_32F));

    bool is_float = depth == CV_32F;
    double min_dist_num = FLT_MAX, min_dist_denom = 1;
    Point ip(cvRound(pt.x), cvRound(pt.y));

    if( total == 0 )
        return measureDist ? -DBL_MAX : -1;

    const Point* cnt = contour.ptr<Point>();
    const Point2f* cntf = (const Point2f*)cnt;

    if( !is_float && !measureDist && ip.x == pt.x && ip.y == pt.y )
    {
        // the fastest "purely integer" branch
        Point v0, v = cnt[total-1];

        for( i = 0; i < total; i++ )
        {
            int dist;
            v0 = v;
            v = cnt[i];

            if( (v0.y <= ip.y && v.y <= ip.y) ||
               (v0.y > ip.y && v.y > ip.y) ||
               (v0.x < ip.x && v.x < ip.x) )
            {
                if( ip.y == v.y && (ip.x == v.x || (ip.y == v0.y &&
                    ((v0.x <= ip.x && ip.x <= v.x) || (v.x <= ip.x && ip.x <= v0.x)))) )
                    return 0;
                continue;
            }

            dist = (ip.y - v0.y)*(v.x - v0.x) - (ip.x - v0.x)*(v.y - v0.y);
            if( dist == 0 )
                return 0;
            if( v.y < v0.y )
                dist = -dist;
            counter += dist > 0;
        }

        result = counter % 2 == 0 ? -1 : 1;
    }
    else
    {
        Point2f v0, v;
        Point iv;

        if( is_float )
        {
            v = cntf[total-1];
        }
        else
        {
            v = cnt[total-1];
        }

        if( !measureDist )
        {
            for( i = 0; i < total; i++ )
            {
                double dist;
                v0 = v;
                if( is_float )
                    v = cntf[i];
                else
                    v = cnt[i];

                if( (v0.y <= pt.y && v.y <= pt.y) ||
                   (v0.y > pt.y && v.y > pt.y) ||
                   (v0.x < pt.x && v.x < pt.x) )
                {
                    if( pt.y == v.y && (pt.x == v.x || (pt.y == v0.y &&
                        ((v0.x <= pt.x && pt.x <= v.x) || (v.x <= pt.x && pt.x <= v0.x)))) )
                        return 0;
                    continue;
                }

                dist = (double)(pt.y - v0.y)*(v.x - v0.x) - (double)(pt.x - v0.x)*(v.y - v0.y);
                if( dist == 0 )
                    return 0;
                if( v.y < v0.y )
                    dist = -dist;
                counter += dist > 0;
            }

            result = counter % 2 == 0 ? -1 : 1;
        }
        else
        {
            for( i = 0; i < total; i++ )
            {
                double dx, dy, dx1, dy1, dx2, dy2, dist_num, dist_denom = 1;

                v0 = v;
                if( is_float )
                    v = cntf[i];
                else
                    v = cnt[i];

                dx = v.x - v0.x; dy = v.y - v0.y;
                dx1 = pt.x - v0.x; dy1 = pt.y - v0.y;
                dx2 = pt.x - v.x; dy2 = pt.y - v.y;

                if( dx1*dx + dy1*dy <= 0 )
                    dist_num = dx1*dx1 + dy1*dy1;
                else if( dx2*dx + dy2*dy >= 0 )
                    dist_num = dx2*dx2 + dy2*dy2;
                else
                {
                    dist_num = (dy1*dx - dx1*dy);
                    dist_num *= dist_num;
                    dist_denom = dx*dx + dy*dy;
                }

                if( dist_num*min_dist_denom < min_dist_num*dist_denom )
                {
                    min_dist_num = dist_num;
                    min_dist_denom = dist_denom;
                    if( min_dist_num == 0 )
                        break;
                }

                if( (v0.y <= pt.y && v.y <= pt.y) ||
                   (v0.y > pt.y && v.y > pt.y) ||
                   (v0.x < pt.x && v.x < pt.x) )
                    continue;

                dist_num = dy1*dx - dx1*dy;
                if( dy < 0 )
                    dist_num = -dist_num;
                counter += dist_num > 0;
            }

            result = std::sqrt(min_dist_num/min_dist_denom);
            if( counter % 2 == 0 )
                result = -result;
        }
    }

    return result;
}           

3.3.2多邊形包圍輪廓執行個體

3.3.2.1建立包圍輪廓的矩形邊界執行個體

代碼參看附件【demo1】。

【第二部分 圖像處理】第3章 Opencv圖像處理進階【4 圖像輪廓C】

圖1

3.3.2.2建立包圍輪廓的最小矩形邊界執行個體

代碼參看附件【demo2】。

【第二部分 圖像處理】第3章 Opencv圖像處理進階【4 圖像輪廓C】

圖2

3.3.2.3建立包圍輪廓的圓形邊界執行個體

代碼參看附件【demo3】。

【第二部分 圖像處理】第3章 Opencv圖像處理進階【4 圖像輪廓C】

圖3

3.3.2.4建立包圍輪廓的橢圓邊界執行個體

代碼參看附件【demo4】。

【第二部分 圖像處理】第3章 Opencv圖像處理進階【4 圖像輪廓C】

圖4

3.3.2.5使用多邊形包圍輪廓執行個體

代碼參看附件【demo5】。

【第二部分 圖像處理】第3章 Opencv圖像處理進階【4 圖像輪廓C】

圖5

3.3.2.6使用多邊形測試執行個體

代碼參看附件【demo6】。

【第二部分 圖像處理】第3章 Opencv圖像處理進階【4 圖像輪廓C】

圖6

本章參考代碼