Home | History | Annotate | Download | only in src
      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 "_cv.h"
     42 
     43 typedef struct
     44 {
     45     int bottom;
     46     int left;
     47     float height;
     48     float width;
     49     float base_a;
     50     float base_b;
     51 }
     52 icvMinAreaState;
     53 
     54 #define CV_CALIPERS_MAXHEIGHT      0
     55 #define CV_CALIPERS_MINAREARECT    1
     56 #define CV_CALIPERS_MAXDIST        2
     57 
     58 /*F///////////////////////////////////////////////////////////////////////////////////////
     59 //    Name:    icvRotatingCalipers
     60 //    Purpose:
     61 //      Rotating calipers algorithm with some applications
     62 //
     63 //    Context:
     64 //    Parameters:
     65 //      points      - convex hull vertices ( any orientation )
     66 //      n           - number of vertices
     67 //      mode        - concrete application of algorithm
     68 //                    can be  CV_CALIPERS_MAXDIST   or
     69 //                            CV_CALIPERS_MINAREARECT
     70 //      left, bottom, right, top - indexes of extremal points
     71 //      out         - output info.
     72 //                    In case CV_CALIPERS_MAXDIST it points to float value -
     73 //                    maximal height of polygon.
     74 //                    In case CV_CALIPERS_MINAREARECT
     75 //                    ((CvPoint2D32f*)out)[0] - corner
     76 //                    ((CvPoint2D32f*)out)[1] - vector1
     77 //                    ((CvPoint2D32f*)out)[0] - corner2
     78 //
     79 //                      ^
     80 //                      |
     81 //              vector2 |
     82 //                      |
     83 //                      |____________\
     84 //                    corner         /
     85 //                               vector1
     86 //
     87 //    Returns:
     88 //    Notes:
     89 //F*/
     90 
     91 /* we will use usual cartesian coordinates */
     92 static void
     93 icvRotatingCalipers( CvPoint2D32f* points, int n, int mode, float* out )
     94 {
     95     float minarea = FLT_MAX;
     96     float max_dist = 0;
     97     char buffer[32];
     98     int i, k;
     99     CvPoint2D32f* vect = (CvPoint2D32f*)cvAlloc( n * sizeof(vect[0]) );
    100     float* inv_vect_length = (float*)cvAlloc( n * sizeof(inv_vect_length[0]) );
    101     int left = 0, bottom = 0, right = 0, top = 0;
    102     int seq[4] = { -1, -1, -1, -1 };
    103 
    104     /* rotating calipers sides will always have coordinates
    105        (a,b) (-b,a) (-a,-b) (b, -a)
    106      */
    107     /* this is a first base bector (a,b) initialized by (1,0) */
    108     float orientation = 0;
    109     float base_a;
    110     float base_b = 0;
    111 
    112     float left_x, right_x, top_y, bottom_y;
    113     CvPoint2D32f pt0 = points[0];
    114 
    115     left_x = right_x = pt0.x;
    116     top_y = bottom_y = pt0.y;
    117 
    118     for( i = 0; i < n; i++ )
    119     {
    120         double dx, dy;
    121 
    122         if( pt0.x < left_x )
    123             left_x = pt0.x, left = i;
    124 
    125         if( pt0.x > right_x )
    126             right_x = pt0.x, right = i;
    127 
    128         if( pt0.y > top_y )
    129             top_y = pt0.y, top = i;
    130 
    131         if( pt0.y < bottom_y )
    132             bottom_y = pt0.y, bottom = i;
    133 
    134         CvPoint2D32f pt = points[(i+1) & (i+1 < n ? -1 : 0)];
    135 
    136         dx = pt.x - pt0.x;
    137         dy = pt.y - pt0.y;
    138 
    139         vect[i].x = (float)dx;
    140         vect[i].y = (float)dy;
    141         inv_vect_length[i] = (float)(1./sqrt(dx*dx + dy*dy));
    142 
    143         pt0 = pt;
    144     }
    145 
    146     //cvbInvSqrt( inv_vect_length, inv_vect_length, n );
    147 
    148     /* find convex hull orientation */
    149     {
    150         double ax = vect[n-1].x;
    151         double ay = vect[n-1].y;
    152 
    153         for( i = 0; i < n; i++ )
    154         {
    155             double bx = vect[i].x;
    156             double by = vect[i].y;
    157 
    158             double convexity = ax * by - ay * bx;
    159 
    160             if( convexity != 0 )
    161             {
    162                 orientation = (convexity > 0) ? 1.f : (-1.f);
    163                 break;
    164             }
    165             ax = bx;
    166             ay = by;
    167         }
    168         assert( orientation != 0 );
    169     }
    170     base_a = orientation;
    171 
    172 /*****************************************************************************************/
    173 /*                         init calipers position                                        */
    174     seq[0] = bottom;
    175     seq[1] = right;
    176     seq[2] = top;
    177     seq[3] = left;
    178 /*****************************************************************************************/
    179 /*                         Main loop - evaluate angles and rotate calipers               */
    180 
    181     /* all of edges will be checked while rotating calipers by 90 degrees */
    182     for( k = 0; k < n; k++ )
    183     {
    184         /* sinus of minimal angle */
    185         /*float sinus;*/
    186 
    187         /* compute cosine of angle between calipers side and polygon edge */
    188         /* dp - dot product */
    189         float dp0 = base_a * vect[seq[0]].x + base_b * vect[seq[0]].y;
    190         float dp1 = -base_b * vect[seq[1]].x + base_a * vect[seq[1]].y;
    191         float dp2 = -base_a * vect[seq[2]].x - base_b * vect[seq[2]].y;
    192         float dp3 = base_b * vect[seq[3]].x - base_a * vect[seq[3]].y;
    193 
    194         float cosalpha = dp0 * inv_vect_length[seq[0]];
    195         float maxcos = cosalpha;
    196 
    197         /* number of calipers edges, that has minimal angle with edge */
    198         int main_element = 0;
    199 
    200         /* choose minimal angle */
    201         cosalpha = dp1 * inv_vect_length[seq[1]];
    202         maxcos = (cosalpha > maxcos) ? (main_element = 1, cosalpha) : maxcos;
    203         cosalpha = dp2 * inv_vect_length[seq[2]];
    204         maxcos = (cosalpha > maxcos) ? (main_element = 2, cosalpha) : maxcos;
    205         cosalpha = dp3 * inv_vect_length[seq[3]];
    206         maxcos = (cosalpha > maxcos) ? (main_element = 3, cosalpha) : maxcos;
    207 
    208         /*rotate calipers*/
    209         {
    210             //get next base
    211             int pindex = seq[main_element];
    212             float lead_x = vect[pindex].x*inv_vect_length[pindex];
    213             float lead_y = vect[pindex].y*inv_vect_length[pindex];
    214             switch( main_element )
    215             {
    216             case 0:
    217                 base_a = lead_x;
    218                 base_b = lead_y;
    219                 break;
    220             case 1:
    221                 base_a = lead_y;
    222                 base_b = -lead_x;
    223                 break;
    224             case 2:
    225                 base_a = -lead_x;
    226                 base_b = -lead_y;
    227                 break;
    228             case 3:
    229                 base_a = -lead_y;
    230                 base_b = lead_x;
    231                 break;
    232             default: assert(0);
    233             }
    234         }
    235         /* change base point of main edge */
    236         seq[main_element] += 1;
    237         seq[main_element] = (seq[main_element] == n) ? 0 : seq[main_element];
    238 
    239 
    240         switch (mode)
    241         {
    242         case CV_CALIPERS_MAXHEIGHT:
    243             {
    244                 /* now main element lies on edge alligned to calipers side */
    245 
    246                 /* find opposite element i.e. transform  */
    247                 /* 0->2, 1->3, 2->0, 3->1                */
    248                 int opposite_el = main_element ^ 2;
    249 
    250                 float dx = points[seq[opposite_el]].x - points[seq[main_element]].x;
    251                 float dy = points[seq[opposite_el]].y - points[seq[main_element]].y;
    252                 float dist;
    253 
    254                 if( main_element & 1 )
    255                     dist = (float)fabs(dx * base_a + dy * base_b);
    256                 else
    257                     dist = (float)fabs(dx * (-base_b) + dy * base_a);
    258 
    259                 if( dist > max_dist )
    260                     max_dist = dist;
    261 
    262                 break;
    263             }
    264         case CV_CALIPERS_MINAREARECT:
    265             /* find area of rectangle */
    266             {
    267                 float height;
    268                 float area;
    269 
    270                 /* find vector left-right */
    271                 float dx = points[seq[1]].x - points[seq[3]].x;
    272                 float dy = points[seq[1]].y - points[seq[3]].y;
    273 
    274                 /* dotproduct */
    275                 float width = dx * base_a + dy * base_b;
    276 
    277                 /* find vector left-right */
    278                 dx = points[seq[2]].x - points[seq[0]].x;
    279                 dy = points[seq[2]].y - points[seq[0]].y;
    280 
    281                 /* dotproduct */
    282                 height = -dx * base_b + dy * base_a;
    283 
    284                 area = width * height;
    285                 if( area <= minarea )
    286                 {
    287                     float *buf = (float *) buffer;
    288 
    289                     minarea = area;
    290                     /* leftist point */
    291                     ((int *) buf)[0] = seq[3];
    292                     buf[1] = base_a;
    293                     buf[2] = width;
    294                     buf[3] = base_b;
    295                     buf[4] = height;
    296                     /* bottom point */
    297                     ((int *) buf)[5] = seq[0];
    298                     buf[6] = area;
    299                 }
    300                 break;
    301             }
    302         }                       /*switch */
    303     }                           /* for */
    304 
    305     switch (mode)
    306     {
    307     case CV_CALIPERS_MINAREARECT:
    308         {
    309             float *buf = (float *) buffer;
    310 
    311             float A1 = buf[1];
    312             float B1 = buf[3];
    313 
    314             float A2 = -buf[3];
    315             float B2 = buf[1];
    316 
    317             float C1 = A1 * points[((int *) buf)[0]].x + points[((int *) buf)[0]].y * B1;
    318             float C2 = A2 * points[((int *) buf)[5]].x + points[((int *) buf)[5]].y * B2;
    319 
    320             float idet = 1.f / (A1 * B2 - A2 * B1);
    321 
    322             float px = (C1 * B2 - C2 * B1) * idet;
    323             float py = (A1 * C2 - A2 * C1) * idet;
    324 
    325             out[0] = px;
    326             out[1] = py;
    327 
    328             out[2] = A1 * buf[2];
    329             out[3] = B1 * buf[2];
    330 
    331             out[4] = A2 * buf[4];
    332             out[5] = B2 * buf[4];
    333         }
    334         break;
    335     case CV_CALIPERS_MAXHEIGHT:
    336         {
    337             out[0] = max_dist;
    338         }
    339         break;
    340     }
    341 
    342     cvFree( &vect );
    343     cvFree( &inv_vect_length );
    344 }
    345 
    346 
    347 CV_IMPL  CvBox2D
    348 cvMinAreaRect2( const CvArr* array, CvMemStorage* storage )
    349 {
    350     CvMemStorage* temp_storage = 0;
    351     CvBox2D box;
    352     CvPoint2D32f* points = 0;
    353 
    354     CV_FUNCNAME( "cvMinAreaRect2" );
    355 
    356     memset(&box, 0, sizeof(box));
    357 
    358     __BEGIN__;
    359 
    360     int i, n;
    361     CvSeqReader reader;
    362     CvContour contour_header;
    363     CvSeqBlock block;
    364     CvSeq* ptseq = (CvSeq*)array;
    365     CvPoint2D32f out[3];
    366 
    367     if( CV_IS_SEQ(ptseq) )
    368     {
    369         if( !CV_IS_SEQ_POINT_SET(ptseq) &&
    370             (CV_SEQ_KIND(ptseq) != CV_SEQ_KIND_CURVE || !CV_IS_SEQ_CONVEX(ptseq) ||
    371             CV_SEQ_ELTYPE(ptseq) != CV_SEQ_ELTYPE_PPOINT ))
    372             CV_ERROR( CV_StsUnsupportedFormat,
    373                 "Input sequence must consist of 2d points or pointers to 2d points" );
    374         if( !storage )
    375             storage = ptseq->storage;
    376     }
    377     else
    378     {
    379         CV_CALL( ptseq = cvPointSeqFromMat(
    380             CV_SEQ_KIND_GENERIC, array, &contour_header, &block ));
    381     }
    382 
    383     if( storage )
    384     {
    385         CV_CALL( temp_storage = cvCreateChildMemStorage( storage ));
    386     }
    387     else
    388     {
    389         CV_CALL( temp_storage = cvCreateMemStorage(1 << 10));
    390     }
    391 
    392     if( !CV_IS_SEQ_CONVEX( ptseq ))
    393     {
    394         CV_CALL( ptseq = cvConvexHull2( ptseq, temp_storage, CV_CLOCKWISE, 1 ));
    395     }
    396     else if( !CV_IS_SEQ_POINT_SET( ptseq ))
    397     {
    398         CvSeqWriter writer;
    399 
    400         if( !CV_IS_SEQ(ptseq->v_prev) || !CV_IS_SEQ_POINT_SET(ptseq->v_prev))
    401             CV_ERROR( CV_StsBadArg,
    402             "Convex hull must have valid pointer to point sequence stored in v_prev" );
    403         cvStartReadSeq( ptseq, &reader );
    404         cvStartWriteSeq( CV_SEQ_KIND_CURVE|CV_SEQ_FLAG_CONVEX|CV_SEQ_ELTYPE(ptseq->v_prev),
    405                          sizeof(CvContour), CV_ELEM_SIZE(ptseq->v_prev->flags),
    406                          temp_storage, &writer );
    407 
    408         for( i = 0; i < ptseq->total; i++ )
    409         {
    410             CvPoint pt = **(CvPoint**)(reader.ptr);
    411             CV_WRITE_SEQ_ELEM( pt, writer );
    412         }
    413 
    414         ptseq = cvEndWriteSeq( &writer );
    415     }
    416 
    417     n = ptseq->total;
    418 
    419     CV_CALL( points = (CvPoint2D32f*)cvAlloc( n*sizeof(points[0]) ));
    420     cvStartReadSeq( ptseq, &reader );
    421 
    422     if( CV_SEQ_ELTYPE( ptseq ) == CV_32SC2 )
    423     {
    424         for( i = 0; i < n; i++ )
    425         {
    426             CvPoint pt;
    427             CV_READ_SEQ_ELEM( pt, reader );
    428             points[i].x = (float)pt.x;
    429             points[i].y = (float)pt.y;
    430         }
    431     }
    432     else
    433     {
    434         for( i = 0; i < n; i++ )
    435         {
    436             CV_READ_SEQ_ELEM( points[i], reader );
    437         }
    438     }
    439 
    440     if( n > 2 )
    441     {
    442         icvRotatingCalipers( points, n, CV_CALIPERS_MINAREARECT, (float*)out );
    443         box.center.x = out[0].x + (out[1].x + out[2].x)*0.5f;
    444         box.center.y = out[0].y + (out[1].y + out[2].y)*0.5f;
    445         box.size.height = (float)sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y);
    446         box.size.width = (float)sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y);
    447         box.angle = (float)atan2( -(double)out[1].y, (double)out[1].x );
    448     }
    449     else if( n == 2 )
    450     {
    451         box.center.x = (points[0].x + points[1].x)*0.5f;
    452         box.center.y = (points[0].y + points[1].y)*0.5f;
    453         double dx = points[1].x - points[0].x;
    454         double dy = points[1].y - points[0].y;
    455         box.size.height = (float)sqrt(dx*dx + dy*dy);
    456         box.size.width = 0;
    457         box.angle = (float)atan2( -dy, dx );
    458     }
    459     else
    460     {
    461         if( n == 1 )
    462             box.center = points[0];
    463     }
    464 
    465     box.angle = (float)(box.angle*180/CV_PI);
    466 
    467     __END__;
    468 
    469     cvReleaseMemStorage( &temp_storage );
    470     cvFree( &points );
    471 
    472     return box;
    473 }
    474 
    475