www.pudn.com > OpenCV-Intel.zip > cvrotcalipers.cpp


/*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" 
 
typedef struct 
{ 
    int bottom; 
    int left; 
    float height; 
    float width; 
    float base_a; 
    float base_b; 
} 
icvMinAreaState; 
 
#define CV_CALIPERS_MAXHEIGHT      0 
#define CV_CALIPERS_MINAREARECT    1 
#define CV_CALIPERS_MAXDIST        2 
 
/*F/////////////////////////////////////////////////////////////////////////////////////// 
//    Name:    icvRotatingCalipers 
//    Purpose: 
//      Rotating calipers algorithm with some applications 
// 
//    Context: 
//    Parameters: 
//      points      - convex hull vertices ( any orientation ) 
//      n           - number of vertices 
//      mode        - concrete application of algorithm  
//                    can be  CV_CALIPERS_MAXDIST   or    
//                            CV_CALIPERS_MINAREARECT   
//      left, bottom, right, top - indexes of extremal points 
//      out         - output info. 
//                    In case CV_CALIPERS_MAXDIST it points to float value -  
//                    maximal height of polygon. 
//                    In case CV_CALIPERS_MINAREARECT 
//                    ((CvPoint2D32f*)out)[0] - corner  
//                    ((CvPoint2D32f*)out)[1] - vector1 
//                    ((CvPoint2D32f*)out)[0] - corner2 
//                       
//                      ^ 
//                      | 
//              vector2 | 
//                      | 
//                      |____________\ 
//                    corner         / 
//                               vector1 
// 
//    Returns: 
//    Notes: 
//F*/ 
 
/* we will use usual cartesian coordinates */ 
static void 
icvRotatingCalipers( CvPoint2D32f* points, int n, int mode, float* out ) 
{ 
    float minarea = FLT_MAX; 
    float max_dist = 0; 
    char buffer[32]; 
    int i, k; 
    CvPoint2D32f* vect = (CvPoint2D32f*)cvAlloc( n * sizeof(vect[0]) ); 
    float* inv_vect_length = (float*)cvAlloc( n * sizeof(inv_vect_length[0]) ); 
    int left = 0, bottom = 0, right = 0, top = 0; 
    int seq[4] = { -1, -1, -1, -1 }; 
 
    /* rotating calipers sides will always have coordinates     
       (a,b) (-b,a) (-a,-b) (b, -a)      
     */ 
    /* this is a first base bector (a,b) initialized by (1,0) */ 
    float orientation = 0; 
    float base_a; 
    float base_b = 0; 
 
    float left_x, right_x, top_y, bottom_y; 
    CvPoint2D32f pt0 = points[0]; 
     
    left_x = right_x = pt0.x; 
    top_y = bottom_y = pt0.y; 
     
    for( i = 0; i < n; i++ ) 
    { 
        double dx, dy; 
         
        if( pt0.x < left_x ) 
            left_x = pt0.x, left = i; 
 
        if( pt0.x > right_x ) 
            right_x = pt0.x, right = i; 
 
        if( pt0.y > top_y ) 
            top_y = pt0.y, top = i; 
 
        if( pt0.y < bottom_y ) 
            bottom_y = pt0.y, bottom = i; 
 
        CvPoint2D32f pt = points[(i+1) & (i+1 < n ? -1 : 0)]; 
         
        dx = pt.x - pt0.x; 
        dy = pt.y - pt0.y; 
 
        vect[i].x = (float)dx; 
        vect[i].y = (float)dy; 
        inv_vect_length[i] = (float)(1./sqrt(dx*dx + dy*dy)); 
 
        pt0 = pt; 
    } 
 
    //cvbInvSqrt( inv_vect_length, inv_vect_length, n ); 
 
    /* find convex hull orientation */ 
    { 
        double ax = vect[n-1].x; 
        double ay = vect[n-1].y; 
         
        for( i = 0; i < n; i++ ) 
        { 
            double bx = vect[i].x; 
            double by = vect[i].y; 
 
            double convexity = ax * by - ay * bx; 
 
            if( convexity != 0 ) 
            { 
                orientation = (convexity > 0) ? 1.f : (-1.f); 
                break; 
            } 
            ax = bx; 
            ay = by; 
        } 
        assert( orientation != 0 ); 
    } 
    base_a = orientation; 
 
/*****************************************************************************************/ 
/*                         init calipers position                                        */ 
    seq[0] = bottom; 
    seq[1] = right; 
    seq[2] = top; 
    seq[3] = left; 
/*****************************************************************************************/ 
/*                         Main loop - evaluate angles and rotate calipers               */ 
 
    /* all of edges will be checked while rotating calipers by 90 degrees */ 
    for( k = 0; k < n; k++ ) 
    { 
        /* sinus of minimal angle */ 
        /*float sinus;*/ 
 
        /* compute cosine of angle between calipers side and polygon edge */ 
        /* dp - dot product */ 
        float dp0 = base_a * vect[seq[0]].x + base_b * vect[seq[0]].y; 
        float dp1 = -base_b * vect[seq[1]].x + base_a * vect[seq[1]].y; 
        float dp2 = -base_a * vect[seq[2]].x - base_b * vect[seq[2]].y; 
        float dp3 = base_b * vect[seq[3]].x - base_a * vect[seq[3]].y; 
 
        float cosalpha = dp0 * inv_vect_length[seq[0]]; 
        float maxcos = cosalpha; 
 
        /* number of calipers edges, that has minimal angle with edge */ 
        int main_element = 0; 
 
        /* choose minimal angle */ 
        cosalpha = dp1 * inv_vect_length[seq[1]]; 
        maxcos = (cosalpha > maxcos) ? (main_element = 1, cosalpha) : maxcos; 
        cosalpha = dp2 * inv_vect_length[seq[2]]; 
        maxcos = (cosalpha > maxcos) ? (main_element = 2, cosalpha) : maxcos; 
        cosalpha = dp3 * inv_vect_length[seq[3]]; 
        maxcos = (cosalpha > maxcos) ? (main_element = 3, cosalpha) : maxcos; 
 
        /*rotate calipers*/ 
        { 
            //get next base 
            int pindex = seq[main_element]; 
            float lead_x = vect[pindex].x*inv_vect_length[pindex]; 
            float lead_y = vect[pindex].y*inv_vect_length[pindex]; 
            switch( main_element ) 
            { 
            case 0: 
                base_a = lead_x; 
                base_b = lead_y; 
                break; 
            case 1: 
                base_a = lead_y;  
                base_b = -lead_x; 
                break; 
            case 2: 
                base_a = -lead_x; 
                base_b = -lead_y; 
                break; 
            case 3: 
                base_a = -lead_y; 
                base_b = lead_x; 
                break; 
            default: assert(0); 
            } 
        }                         
        /* change base point of main edge */ 
        seq[main_element] += 1; 
        seq[main_element] = (seq[main_element] == n) ? 0 : seq[main_element]; 
 
         
        switch (mode) 
        { 
        case CV_CALIPERS_MAXHEIGHT: 
            { 
                /* now main element lies on edge alligned to calipers side */ 
 
                /* find opposite element i.e. transform  */ 
                /* 0->2, 1->3, 2->0, 3->1                */ 
                int opposite_el = main_element ^ 2; 
 
                float dx = points[seq[opposite_el]].x - points[seq[main_element]].x; 
                float dy = points[seq[opposite_el]].y - points[seq[main_element]].y; 
                float dist; 
 
                if( main_element & 1 ) 
                    dist = (float)fabs(dx * base_a + dy * base_b); 
                else 
                    dist = (float)fabs(dx * (-base_b) + dy * base_a); 
 
                if( dist > max_dist ) 
                    max_dist = dist; 
 
                break; 
            } 
        case CV_CALIPERS_MINAREARECT: 
            /* find area of rectangle */ 
            { 
                float height; 
                float area; 
 
                /* find vector left-right */ 
                float dx = points[seq[1]].x - points[seq[3]].x; 
                float dy = points[seq[1]].y - points[seq[3]].y; 
 
                /* dotproduct */ 
                float width = dx * base_a + dy * base_b; 
 
                /* find vector left-right */ 
                dx = points[seq[2]].x - points[seq[0]].x; 
                dy = points[seq[2]].y - points[seq[0]].y; 
 
                /* dotproduct */ 
                height = -dx * base_b + dy * base_a; 
 
                area = width * height; 
                if( area <= minarea ) 
                { 
                    float *buf = (float *) buffer; 
 
                    minarea = area; 
                    /* leftist point */ 
                    ((int *) buf)[0] = seq[3]; 
                    buf[1] = base_a; 
                    buf[2] = width; 
                    buf[3] = base_b; 
                    buf[4] = height; 
                    /* bottom point */ 
                    ((int *) buf)[5] = seq[0]; 
                    buf[6] = area; 
                } 
                break; 
            } 
        }                       /*switch */ 
    }                           /* for */ 
 
    switch (mode) 
    { 
    case CV_CALIPERS_MINAREARECT: 
        { 
            float *buf = (float *) buffer; 
 
            float A1 = buf[1]; 
            float B1 = buf[3]; 
 
            float A2 = -buf[3]; 
            float B2 = buf[1]; 
 
            float C1 = A1 * points[((int *) buf)[0]].x + points[((int *) buf)[0]].y * B1; 
            float C2 = A2 * points[((int *) buf)[5]].x + points[((int *) buf)[5]].y * B2; 
 
            float idet = 1.f / (A1 * B2 - A2 * B1); 
 
            float px = (C1 * B2 - C2 * B1) * idet; 
            float py = (A1 * C2 - A2 * C1) * idet; 
 
            out[0] = px; 
            out[1] = py; 
 
            out[2] = A1 * buf[2]; 
            out[3] = B1 * buf[2]; 
 
            out[4] = A2 * buf[4]; 
            out[5] = B2 * buf[4]; 
        } 
        break; 
    case CV_CALIPERS_MAXHEIGHT: 
        { 
            out[0] = max_dist; 
        } 
        break; 
    } 
 
    cvFree( (void**)&vect ); 
    cvFree( (void**)&inv_vect_length ); 
} 
 
 
CV_IMPL  CvBox2D 
cvMinAreaRect2( const CvArr* array, CvMemStorage* storage ) 
{ 
    CvMemStorage* temp_storage = 0; 
    CvBox2D box; 
    CvPoint2D32f* points = 0; 
     
    CV_FUNCNAME( "cvMinAreaRect2" ); 
 
    memset(&box, 0, sizeof(box)); 
 
    __BEGIN__; 
 
    int i, n; 
    CvSeqReader reader; 
    CvContour contour_header; 
    CvSeqBlock block; 
    CvSeq* ptseq = (CvSeq*)array; 
    CvPoint2D32f out[3]; 
 
    if( CV_IS_SEQ(ptseq) ) 
    { 
        if( !CV_IS_SEQ_POINT_SET(ptseq) && 
            (CV_SEQ_KIND(ptseq) != CV_SEQ_KIND_CURVE || !CV_IS_SEQ_CONVEX(ptseq) || 
            CV_SEQ_ELTYPE(ptseq) != CV_SEQ_ELTYPE_PPOINT )) 
            CV_ERROR( CV_StsUnsupportedFormat, 
                "Input sequence must consist of 2d points or pointers to 2d points" ); 
        if( !storage ) 
            storage = ptseq->storage; 
    } 
    else 
    { 
        CV_CALL( ptseq = cvPointSeqFromMat( 
            CV_SEQ_KIND_GENERIC, array, &contour_header, &block )); 
    } 
 
    if( storage ) 
    { 
        CV_CALL( temp_storage = cvCreateChildMemStorage( storage )); 
    } 
    else 
    { 
        CV_CALL( temp_storage = cvCreateMemStorage(1 << 10)); 
    } 
 
    if( !CV_IS_SEQ_CONVEX( ptseq )) 
    { 
        CV_CALL( ptseq = cvConvexHull2( ptseq, temp_storage, CV_CLOCKWISE, 1 )); 
    } 
    else if( !CV_IS_SEQ_POINT_SET( ptseq )) 
    { 
        CvSeqWriter writer; 
         
        if( !CV_IS_SEQ(ptseq->v_prev) || !CV_IS_SEQ_POINT_SET(ptseq->v_prev)) 
            CV_ERROR( CV_StsBadArg, 
            "Convex hull must have valid pointer to point sequence stored in v_prev" ); 
        cvStartReadSeq( ptseq, &reader ); 
        cvStartWriteSeq( CV_SEQ_KIND_CURVE|CV_SEQ_FLAG_CONVEX|CV_SEQ_ELTYPE(ptseq->v_prev), 
                         sizeof(CvContour), CV_ELEM_SIZE(ptseq->v_prev->flags), 
                         temp_storage, &writer ); 
             
        for( i = 0; i < ptseq->total; i++ ) 
        { 
            CvPoint pt = **(CvPoint**)(reader.ptr); 
            CV_WRITE_SEQ_ELEM( pt, writer ); 
        } 
 
        ptseq = cvEndWriteSeq( &writer ); 
    } 
 
    n = ptseq->total; 
 
    CV_CALL( points = (CvPoint2D32f*)cvAlloc( n*sizeof(points[0]) )); 
    cvStartReadSeq( ptseq, &reader ); 
 
    if( CV_SEQ_ELTYPE( ptseq ) == CV_32SC2 ) 
    { 
        for( i = 0; i < n; i++ ) 
        { 
            CvPoint pt; 
            CV_READ_SEQ_ELEM( pt, reader ); 
            points[i].x = (float)pt.x; 
            points[i].y = (float)pt.y; 
        } 
    } 
    else 
    { 
        for( i = 0; i < n; i++ ) 
        { 
            CV_READ_SEQ_ELEM( points[i], reader ); 
        } 
    } 
     
    if( n > 2 ) 
    { 
        icvRotatingCalipers( points, n, CV_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.height = (float)sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y); 
        box.size.width = (float)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 = (points[0].x + points[1].x)*0.5f; 
        box.center.y = (points[0].y + points[1].y)*0.5f; 
        double dx = points[1].x - points[0].x; 
        double dy = points[1].y - points[0].y; 
        box.size.height = (float)sqrt(dx*dx + dy*dy); 
        box.size.width = 0; 
        box.angle = (float)atan2( -dy, dx ); 
    } 
    else 
    { 
        if( n == 1 ) 
            box.center = points[0]; 
    } 
 
    __END__;  
 
    cvReleaseMemStorage( &temp_storage ); 
    cvFree( (void**)&points ); 
 
    return box; 
}