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/* initializes 8-element array for fast access to 3x3 neighborhood of a pixel */ 44#define CV_INIT_3X3_DELTAS( deltas, step, nch ) \ 45 ((deltas)[0] = (nch), (deltas)[1] = -(step) + (nch), \ 46 (deltas)[2] = -(step), (deltas)[3] = -(step) - (nch), \ 47 (deltas)[4] = -(nch), (deltas)[5] = (step) - (nch), \ 48 (deltas)[6] = (step), (deltas)[7] = (step) + (nch)) 49 50static const CvPoint icvCodeDeltas[8] = 51 { {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1} }; 52 53CV_IMPL void 54cvStartReadChainPoints( CvChain * chain, CvChainPtReader * reader ) 55{ 56 int i; 57 58 CV_FUNCNAME( "cvStartReadChainPoints" ); 59 60 __BEGIN__; 61 62 if( !chain || !reader ) 63 CV_ERROR( CV_StsNullPtr, "" ); 64 65 if( chain->elem_size != 1 || chain->header_size < (int)sizeof(CvChain)) 66 CV_ERROR_FROM_STATUS( CV_BADSIZE_ERR ); 67 68 cvStartReadSeq( (CvSeq *) chain, (CvSeqReader *) reader, 0 ); 69 CV_CHECK(); 70 71 reader->pt = chain->origin; 72 73 for( i = 0; i < 8; i++ ) 74 { 75 reader->deltas[i][0] = (schar) icvCodeDeltas[i].x; 76 reader->deltas[i][1] = (schar) icvCodeDeltas[i].y; 77 } 78 79 __END__; 80} 81 82 83/* retrieves next point of the chain curve and updates reader */ 84CV_IMPL CvPoint 85cvReadChainPoint( CvChainPtReader * reader ) 86{ 87 schar *ptr; 88 int code; 89 CvPoint pt = { 0, 0 }; 90 91 CV_FUNCNAME( "cvReadChainPoint" ); 92 93 __BEGIN__; 94 95 if( !reader ) 96 CV_ERROR( CV_StsNullPtr, "" ); 97 98 pt = reader->pt; 99 100 ptr = reader->ptr; 101 if( ptr ) 102 { 103 code = *ptr++; 104 105 if( ptr >= reader->block_max ) 106 { 107 cvChangeSeqBlock( (CvSeqReader *) reader, 1 ); 108 ptr = reader->ptr; 109 } 110 111 reader->ptr = ptr; 112 reader->code = (schar)code; 113 assert( (code & ~7) == 0 ); 114 reader->pt.x = pt.x + icvCodeDeltas[code].x; 115 reader->pt.y = pt.y + icvCodeDeltas[code].y; 116 } 117 118 __END__; 119 120 return pt; 121} 122 123 124/****************************************************************************************\ 125* Raster->Chain Tree (Suzuki algorithms) * 126\****************************************************************************************/ 127 128typedef struct _CvContourInfo 129{ 130 int flags; 131 struct _CvContourInfo *next; /* next contour with the same mark value */ 132 struct _CvContourInfo *parent; /* information about parent contour */ 133 CvSeq *contour; /* corresponding contour (may be 0, if rejected) */ 134 CvRect rect; /* bounding rectangle */ 135 CvPoint origin; /* origin point (where the contour was traced from) */ 136 int is_hole; /* hole flag */ 137} 138_CvContourInfo; 139 140 141/* 142 Structure that is used for sequental retrieving contours from the image. 143 It supports both hierarchical and plane variants of Suzuki algorithm. 144*/ 145typedef struct _CvContourScanner 146{ 147 CvMemStorage *storage1; /* contains fetched contours */ 148 CvMemStorage *storage2; /* contains approximated contours 149 (!=storage1 if approx_method2 != approx_method1) */ 150 CvMemStorage *cinfo_storage; /* contains _CvContourInfo nodes */ 151 CvSet *cinfo_set; /* set of _CvContourInfo nodes */ 152 CvMemStoragePos initial_pos; /* starting storage pos */ 153 CvMemStoragePos backup_pos; /* beginning of the latest approx. contour */ 154 CvMemStoragePos backup_pos2; /* ending of the latest approx. contour */ 155 schar *img0; /* image origin */ 156 schar *img; /* current image row */ 157 int img_step; /* image step */ 158 CvSize img_size; /* ROI size */ 159 CvPoint offset; /* ROI offset: coordinates, added to each contour point */ 160 CvPoint pt; /* current scanner position */ 161 CvPoint lnbd; /* position of the last met contour */ 162 int nbd; /* current mark val */ 163 _CvContourInfo *l_cinfo; /* information about latest approx. contour */ 164 _CvContourInfo cinfo_temp; /* temporary var which is used in simple modes */ 165 _CvContourInfo frame_info; /* information about frame */ 166 CvSeq frame; /* frame itself */ 167 int approx_method1; /* approx method when tracing */ 168 int approx_method2; /* final approx method */ 169 int mode; /* contour scanning mode: 170 0 - external only 171 1 - all the contours w/o any hierarchy 172 2 - connected components (i.e. two-level structure - 173 external contours and holes) */ 174 int subst_flag; 175 int seq_type1; /* type of fetched contours */ 176 int header_size1; /* hdr size of fetched contours */ 177 int elem_size1; /* elem size of fetched contours */ 178 int seq_type2; /* */ 179 int header_size2; /* the same for approx. contours */ 180 int elem_size2; /* */ 181 _CvContourInfo *cinfo_table[126]; 182} 183_CvContourScanner; 184 185#define _CV_FIND_CONTOURS_FLAGS_EXTERNAL_ONLY 1 186#define _CV_FIND_CONTOURS_FLAGS_HIERARCHIC 2 187 188/* 189 Initializes scanner structure. 190 Prepare image for scanning ( clear borders and convert all pixels to 0-1. 191*/ 192CV_IMPL CvContourScanner 193cvStartFindContours( void* _img, CvMemStorage* storage, 194 int header_size, int mode, 195 int method, CvPoint offset ) 196{ 197 int y; 198 int step; 199 CvSize size; 200 uchar *img = 0; 201 CvContourScanner scanner = 0; 202 CvMat stub, *mat = (CvMat*)_img; 203 204 CV_FUNCNAME( "cvStartFindContours" ); 205 206 __BEGIN__; 207 208 if( !storage ) 209 CV_ERROR( CV_StsNullPtr, "" ); 210 211 CV_CALL( mat = cvGetMat( mat, &stub )); 212 213 if( !CV_IS_MASK_ARR( mat )) 214 CV_ERROR( CV_StsUnsupportedFormat, "[Start]FindContours support only 8uC1 images" ); 215 216 size = cvSize( mat->width, mat->height ); 217 step = mat->step; 218 img = (uchar*)(mat->data.ptr); 219 220 if( method < 0 || method > CV_CHAIN_APPROX_TC89_KCOS ) 221 CV_ERROR_FROM_STATUS( CV_BADRANGE_ERR ); 222 223 if( header_size < (int) (method == CV_CHAIN_CODE ? sizeof( CvChain ) : sizeof( CvContour ))) 224 CV_ERROR_FROM_STATUS( CV_BADSIZE_ERR ); 225 226 scanner = (CvContourScanner)cvAlloc( sizeof( *scanner )); 227 if( !scanner ) 228 CV_ERROR_FROM_STATUS( CV_OUTOFMEM_ERR ); 229 230 memset( scanner, 0, sizeof( *scanner )); 231 232 scanner->storage1 = scanner->storage2 = storage; 233 scanner->img0 = (schar *) img; 234 scanner->img = (schar *) (img + step); 235 scanner->img_step = step; 236 scanner->img_size.width = size.width - 1; /* exclude rightest column */ 237 scanner->img_size.height = size.height - 1; /* exclude bottomost row */ 238 scanner->mode = mode; 239 scanner->offset = offset; 240 scanner->pt.x = scanner->pt.y = 1; 241 scanner->lnbd.x = 0; 242 scanner->lnbd.y = 1; 243 scanner->nbd = 2; 244 scanner->mode = (int) mode; 245 scanner->frame_info.contour = &(scanner->frame); 246 scanner->frame_info.is_hole = 1; 247 scanner->frame_info.next = 0; 248 scanner->frame_info.parent = 0; 249 scanner->frame_info.rect = cvRect( 0, 0, size.width, size.height ); 250 scanner->l_cinfo = 0; 251 scanner->subst_flag = 0; 252 253 scanner->frame.flags = CV_SEQ_FLAG_HOLE; 254 255 scanner->approx_method2 = scanner->approx_method1 = method; 256 257 if( method == CV_CHAIN_APPROX_TC89_L1 || method == CV_CHAIN_APPROX_TC89_KCOS ) 258 scanner->approx_method1 = CV_CHAIN_CODE; 259 260 if( scanner->approx_method1 == CV_CHAIN_CODE ) 261 { 262 scanner->seq_type1 = CV_SEQ_CHAIN_CONTOUR; 263 scanner->header_size1 = scanner->approx_method1 == scanner->approx_method2 ? 264 header_size : sizeof( CvChain ); 265 scanner->elem_size1 = sizeof( char ); 266 } 267 else 268 { 269 scanner->seq_type1 = CV_SEQ_POLYGON; 270 scanner->header_size1 = scanner->approx_method1 == scanner->approx_method2 ? 271 header_size : sizeof( CvContour ); 272 scanner->elem_size1 = sizeof( CvPoint ); 273 } 274 275 scanner->header_size2 = header_size; 276 277 if( scanner->approx_method2 == CV_CHAIN_CODE ) 278 { 279 scanner->seq_type2 = scanner->seq_type1; 280 scanner->elem_size2 = scanner->elem_size1; 281 } 282 else 283 { 284 scanner->seq_type2 = CV_SEQ_POLYGON; 285 scanner->elem_size2 = sizeof( CvPoint ); 286 } 287 288 scanner->seq_type1 = scanner->approx_method1 == CV_CHAIN_CODE ? 289 CV_SEQ_CHAIN_CONTOUR : CV_SEQ_POLYGON; 290 291 scanner->seq_type2 = scanner->approx_method2 == CV_CHAIN_CODE ? 292 CV_SEQ_CHAIN_CONTOUR : CV_SEQ_POLYGON; 293 294 cvSaveMemStoragePos( storage, &(scanner->initial_pos) ); 295 296 if( method > CV_CHAIN_APPROX_SIMPLE ) 297 { 298 scanner->storage1 = cvCreateChildMemStorage( scanner->storage2 ); 299 } 300 301 if( mode > CV_RETR_LIST ) 302 { 303 scanner->cinfo_storage = cvCreateChildMemStorage( scanner->storage2 ); 304 scanner->cinfo_set = cvCreateSet( 0, sizeof( CvSet ), sizeof( _CvContourInfo ), 305 scanner->cinfo_storage ); 306 if( scanner->cinfo_storage == 0 || scanner->cinfo_set == 0 ) 307 CV_ERROR_FROM_STATUS( CV_OUTOFMEM_ERR ); 308 } 309 310 /* make zero borders */ 311 memset( img, 0, size.width ); 312 memset( img + step * (size.height - 1), 0, size.width ); 313 314 for( y = 1, img += step; y < size.height - 1; y++, img += step ) 315 { 316 img[0] = img[size.width - 1] = 0; 317 } 318 319 /* converts all pixels to 0 or 1 */ 320 cvThreshold( mat, mat, 0, 1, CV_THRESH_BINARY ); 321 CV_CHECK(); 322 323 __END__; 324 325 if( cvGetErrStatus() < 0 ) 326 cvFree( &scanner ); 327 328 return scanner; 329} 330 331/* 332 Final stage of contour processing. 333 Three variants possible: 334 1. Contour, which was retrieved using border following, is added to 335 the contour tree. It is the case when the icvSubstituteContour function 336 was not called after retrieving the contour. 337 338 2. New contour, assigned by icvSubstituteContour function, is added to the 339 tree. The retrieved contour itself is removed from the storage. 340 Here two cases are possible: 341 2a. If one deals with plane variant of algorithm 342 (hierarchical strucutre is not reconstructed), 343 the contour is removed completely. 344 2b. In hierarchical case, the header of the contour is not removed. 345 It's marked as "link to contour" and h_next pointer of it is set to 346 new, substituting contour. 347 348 3. The similar to 2, but when NULL pointer was assigned by 349 icvSubstituteContour function. In this case, the function removes 350 retrieved contour completely if plane case and 351 leaves header if hierarchical (but doesn't mark header as "link"). 352 ------------------------------------------------------------------------ 353 The 1st variant can be used to retrieve and store all the contours from the image 354 (with optional convertion from chains to contours using some approximation from 355 restriced set of methods). Some characteristics of contour can be computed in the 356 same pass. 357 358 The usage scheme can look like: 359 360 icvContourScanner scanner; 361 CvMemStorage* contour_storage; 362 CvSeq* first_contour; 363 CvStatus result; 364 365 ... 366 367 icvCreateMemStorage( &contour_storage, block_size/0 ); 368 369 ... 370 371 cvStartFindContours 372 ( img, contour_storage, 373 header_size, approx_method, 374 [external_only,] 375 &scanner ); 376 377 for(;;) 378 { 379 [CvSeq* contour;] 380 result = icvFindNextContour( &scanner, &contour/0 ); 381 382 if( result != CV_OK ) break; 383 384 // calculate some characteristics 385 ... 386 } 387 388 if( result < 0 ) goto error_processing; 389 390 cvEndFindContours( &scanner, &first_contour ); 391 ... 392 393 ----------------------------------------------------------------- 394 395 Second variant is more complex and can be used when someone wants store not 396 the retrieved contours but transformed ones. (e.g. approximated with some 397 non-default algorithm ). 398 399 The scheme can be the as following: 400 401 icvContourScanner scanner; 402 CvMemStorage* contour_storage; 403 CvMemStorage* temp_storage; 404 CvSeq* first_contour; 405 CvStatus result; 406 407 ... 408 409 icvCreateMemStorage( &contour_storage, block_size/0 ); 410 icvCreateMemStorage( &temp_storage, block_size/0 ); 411 412 ... 413 414 icvStartFindContours8uC1R 415 ( <img_params>, temp_storage, 416 header_size, approx_method, 417 [retrival_mode], 418 &scanner ); 419 420 for(;;) 421 { 422 CvSeq* temp_contour; 423 CvSeq* new_contour; 424 result = icvFindNextContour( scanner, &temp_contour ); 425 426 if( result != CV_OK ) break; 427 428 <approximation_function>( temp_contour, contour_storage, 429 &new_contour, <parameters...> ); 430 431 icvSubstituteContour( scanner, new_contour ); 432 ... 433 } 434 435 if( result < 0 ) goto error_processing; 436 437 cvEndFindContours( &scanner, &first_contour ); 438 ... 439 440 ---------------------------------------------------------------------------- 441 Third method to retrieve contours may be applied if contours are irrelevant 442 themselves but some characteristics of them are used only. 443 The usage is similar to second except slightly different internal loop 444 445 for(;;) 446 { 447 CvSeq* temp_contour; 448 result = icvFindNextContour( &scanner, &temp_contour ); 449 450 if( result != CV_OK ) break; 451 452 // calculate some characteristics of temp_contour 453 454 icvSubstituteContour( scanner, 0 ); 455 ... 456 } 457 458 new_storage variable is not needed here. 459 460 Two notes. 461 1. Second and third method can interleave. I.e. it is possible to 462 remain contours that satisfy with some criteria and reject others. 463 In hierarchic case the resulting tree is the part of original tree with 464 some nodes absent. But in the resulting tree the contour1 is a child 465 (may be indirect) of contour2 iff in the original tree the contour1 466 is a child (may be indirect) of contour2. 467*/ 468static void 469icvEndProcessContour( CvContourScanner scanner ) 470{ 471 _CvContourInfo *l_cinfo = scanner->l_cinfo; 472 473 if( l_cinfo ) 474 { 475 if( scanner->subst_flag ) 476 { 477 CvMemStoragePos temp; 478 479 cvSaveMemStoragePos( scanner->storage2, &temp ); 480 481 if( temp.top == scanner->backup_pos2.top && 482 temp.free_space == scanner->backup_pos2.free_space ) 483 { 484 cvRestoreMemStoragePos( scanner->storage2, &scanner->backup_pos ); 485 } 486 scanner->subst_flag = 0; 487 } 488 489 if( l_cinfo->contour ) 490 { 491 cvInsertNodeIntoTree( l_cinfo->contour, l_cinfo->parent->contour, 492 &(scanner->frame) ); 493 } 494 scanner->l_cinfo = 0; 495 } 496} 497 498/* replaces one contour with another */ 499CV_IMPL void 500cvSubstituteContour( CvContourScanner scanner, CvSeq * new_contour ) 501{ 502 _CvContourInfo *l_cinfo; 503 504 CV_FUNCNAME( "cvSubstituteContour" ); 505 506 __BEGIN__; 507 508 if( !scanner ) 509 CV_ERROR( CV_StsNullPtr, "" ); 510 511 l_cinfo = scanner->l_cinfo; 512 if( l_cinfo && l_cinfo->contour && l_cinfo->contour != new_contour ) 513 { 514 l_cinfo->contour = new_contour; 515 scanner->subst_flag = 1; 516 } 517 518 __END__; 519} 520 521 522/* 523 marks domain border with +/-<constant> and stores the contour into CvSeq. 524 method: 525 <0 - chain 526 ==0 - direct 527 >0 - simple approximation 528*/ 529static CvStatus 530icvFetchContour( schar *ptr, 531 int step, 532 CvPoint pt, 533 CvSeq* contour, 534 int _method ) 535{ 536 const schar nbd = 2; 537 int deltas[16]; 538 CvSeqWriter writer; 539 schar *i0 = ptr, *i1, *i3, *i4 = 0; 540 int prev_s = -1, s, s_end; 541 int method = _method - 1; 542 543 assert( (unsigned) _method <= CV_CHAIN_APPROX_SIMPLE ); 544 545 /* initialize local state */ 546 CV_INIT_3X3_DELTAS( deltas, step, 1 ); 547 memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] )); 548 549 /* initialize writer */ 550 cvStartAppendToSeq( contour, &writer ); 551 552 if( method < 0 ) 553 ((CvChain *) contour)->origin = pt; 554 555 s_end = s = CV_IS_SEQ_HOLE( contour ) ? 0 : 4; 556 557 do 558 { 559 s = (s - 1) & 7; 560 i1 = i0 + deltas[s]; 561 if( *i1 != 0 ) 562 break; 563 } 564 while( s != s_end ); 565 566 if( s == s_end ) /* single pixel domain */ 567 { 568 *i0 = (schar) (nbd | -128); 569 if( method >= 0 ) 570 { 571 CV_WRITE_SEQ_ELEM( pt, writer ); 572 } 573 } 574 else 575 { 576 i3 = i0; 577 prev_s = s ^ 4; 578 579 /* follow border */ 580 for( ;; ) 581 { 582 s_end = s; 583 584 for( ;; ) 585 { 586 i4 = i3 + deltas[++s]; 587 if( *i4 != 0 ) 588 break; 589 } 590 s &= 7; 591 592 /* check "right" bound */ 593 if( (unsigned) (s - 1) < (unsigned) s_end ) 594 { 595 *i3 = (schar) (nbd | -128); 596 } 597 else if( *i3 == 1 ) 598 { 599 *i3 = nbd; 600 } 601 602 if( method < 0 ) 603 { 604 schar _s = (schar) s; 605 606 CV_WRITE_SEQ_ELEM( _s, writer ); 607 } 608 else 609 { 610 if( s != prev_s || method == 0 ) 611 { 612 CV_WRITE_SEQ_ELEM( pt, writer ); 613 prev_s = s; 614 } 615 616 pt.x += icvCodeDeltas[s].x; 617 pt.y += icvCodeDeltas[s].y; 618 619 } 620 621 if( i4 == i0 && i3 == i1 ) 622 break; 623 624 i3 = i4; 625 s = (s + 4) & 7; 626 } /* end of border following loop */ 627 } 628 629 cvEndWriteSeq( &writer ); 630 631 if( _method != CV_CHAIN_CODE ) 632 cvBoundingRect( contour, 1 ); 633 634 assert( writer.seq->total == 0 && writer.seq->first == 0 || 635 writer.seq->total > writer.seq->first->count || 636 (writer.seq->first->prev == writer.seq->first && 637 writer.seq->first->next == writer.seq->first) ); 638 639 return CV_OK; 640} 641 642 643 644/* 645 trace contour until certain point is met. 646 returns 1 if met, 0 else. 647*/ 648static int 649icvTraceContour( schar *ptr, int step, schar *stop_ptr, int is_hole ) 650{ 651 int deltas[16]; 652 schar *i0 = ptr, *i1, *i3, *i4; 653 int s, s_end; 654 655 /* initialize local state */ 656 CV_INIT_3X3_DELTAS( deltas, step, 1 ); 657 memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] )); 658 659 assert( (*i0 & -2) != 0 ); 660 661 s_end = s = is_hole ? 0 : 4; 662 663 do 664 { 665 s = (s - 1) & 7; 666 i1 = i0 + deltas[s]; 667 if( *i1 != 0 ) 668 break; 669 } 670 while( s != s_end ); 671 672 i3 = i0; 673 674 /* check single pixel domain */ 675 if( s != s_end ) 676 { 677 /* follow border */ 678 for( ;; ) 679 { 680 s_end = s; 681 682 for( ;; ) 683 { 684 i4 = i3 + deltas[++s]; 685 if( *i4 != 0 ) 686 break; 687 } 688 689 if( i3 == stop_ptr || (i4 == i0 && i3 == i1) ) 690 break; 691 692 i3 = i4; 693 s = (s + 4) & 7; 694 } /* end of border following loop */ 695 } 696 return i3 == stop_ptr; 697} 698 699 700static CvStatus 701icvFetchContourEx( schar* ptr, 702 int step, 703 CvPoint pt, 704 CvSeq* contour, 705 int _method, 706 int nbd, 707 CvRect* _rect ) 708{ 709 int deltas[16]; 710 CvSeqWriter writer; 711 schar *i0 = ptr, *i1, *i3, *i4; 712 CvRect rect; 713 int prev_s = -1, s, s_end; 714 int method = _method - 1; 715 716 assert( (unsigned) _method <= CV_CHAIN_APPROX_SIMPLE ); 717 assert( 1 < nbd && nbd < 128 ); 718 719 /* initialize local state */ 720 CV_INIT_3X3_DELTAS( deltas, step, 1 ); 721 memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] )); 722 723 /* initialize writer */ 724 cvStartAppendToSeq( contour, &writer ); 725 726 if( method < 0 ) 727 ((CvChain *)contour)->origin = pt; 728 729 rect.x = rect.width = pt.x; 730 rect.y = rect.height = pt.y; 731 732 s_end = s = CV_IS_SEQ_HOLE( contour ) ? 0 : 4; 733 734 do 735 { 736 s = (s - 1) & 7; 737 i1 = i0 + deltas[s]; 738 if( *i1 != 0 ) 739 break; 740 } 741 while( s != s_end ); 742 743 if( s == s_end ) /* single pixel domain */ 744 { 745 *i0 = (schar) (nbd | 0x80); 746 if( method >= 0 ) 747 { 748 CV_WRITE_SEQ_ELEM( pt, writer ); 749 } 750 } 751 else 752 { 753 i3 = i0; 754 755 prev_s = s ^ 4; 756 757 /* follow border */ 758 for( ;; ) 759 { 760 s_end = s; 761 762 for( ;; ) 763 { 764 i4 = i3 + deltas[++s]; 765 if( *i4 != 0 ) 766 break; 767 } 768 s &= 7; 769 770 /* check "right" bound */ 771 if( (unsigned) (s - 1) < (unsigned) s_end ) 772 { 773 *i3 = (schar) (nbd | 0x80); 774 } 775 else if( *i3 == 1 ) 776 { 777 *i3 = (schar) nbd; 778 } 779 780 if( method < 0 ) 781 { 782 schar _s = (schar) s; 783 CV_WRITE_SEQ_ELEM( _s, writer ); 784 } 785 else if( s != prev_s || method == 0 ) 786 { 787 CV_WRITE_SEQ_ELEM( pt, writer ); 788 } 789 790 if( s != prev_s ) 791 { 792 /* update bounds */ 793 if( pt.x < rect.x ) 794 rect.x = pt.x; 795 else if( pt.x > rect.width ) 796 rect.width = pt.x; 797 798 if( pt.y < rect.y ) 799 rect.y = pt.y; 800 else if( pt.y > rect.height ) 801 rect.height = pt.y; 802 } 803 804 prev_s = s; 805 pt.x += icvCodeDeltas[s].x; 806 pt.y += icvCodeDeltas[s].y; 807 808 if( i4 == i0 && i3 == i1 ) break; 809 810 i3 = i4; 811 s = (s + 4) & 7; 812 } /* end of border following loop */ 813 } 814 815 rect.width -= rect.x - 1; 816 rect.height -= rect.y - 1; 817 818 cvEndWriteSeq( &writer ); 819 820 if( _method != CV_CHAIN_CODE ) 821 ((CvContour*)contour)->rect = rect; 822 823 assert( writer.seq->total == 0 && writer.seq->first == 0 || 824 writer.seq->total > writer.seq->first->count || 825 (writer.seq->first->prev == writer.seq->first && 826 writer.seq->first->next == writer.seq->first) ); 827 828 if( _rect ) *_rect = rect; 829 830 return CV_OK; 831} 832 833 834CvSeq * 835cvFindNextContour( CvContourScanner scanner ) 836{ 837 schar *img0; 838 schar *img; 839 int step; 840 int width, height; 841 int x, y; 842 int prev; 843 CvPoint lnbd; 844 CvSeq *contour = 0; 845 int nbd; 846 int mode; 847 CvStatus result = (CvStatus) 1; 848 849 CV_FUNCNAME( "cvFindNextContour" ); 850 851 __BEGIN__; 852 853 if( !scanner ) 854 CV_ERROR( CV_StsNullPtr, "" ); 855 icvEndProcessContour( scanner ); 856 857 /* initialize local state */ 858 img0 = scanner->img0; 859 img = scanner->img; 860 step = scanner->img_step; 861 x = scanner->pt.x; 862 y = scanner->pt.y; 863 width = scanner->img_size.width; 864 height = scanner->img_size.height; 865 mode = scanner->mode; 866 lnbd = scanner->lnbd; 867 nbd = scanner->nbd; 868 869 prev = img[x - 1]; 870 871 for( ; y < height; y++, img += step ) 872 { 873 for( ; x < width; x++ ) 874 { 875 int p = img[x]; 876 877 if( p != prev ) 878 { 879 _CvContourInfo *par_info = 0; 880 _CvContourInfo *l_cinfo = 0; 881 CvSeq *seq = 0; 882 int is_hole = 0; 883 CvPoint origin; 884 885 if( !(prev == 0 && p == 1) ) /* if not external contour */ 886 { 887 /* check hole */ 888 if( p != 0 || prev < 1 ) 889 goto resume_scan; 890 891 if( prev & -2 ) 892 { 893 lnbd.x = x - 1; 894 } 895 is_hole = 1; 896 } 897 898 if( mode == 0 && (is_hole || img0[lnbd.y * step + lnbd.x] > 0) ) 899 goto resume_scan; 900 901 origin.y = y; 902 origin.x = x - is_hole; 903 904 /* find contour parent */ 905 if( mode <= 1 || (!is_hole && mode == 2) || lnbd.x <= 0 ) 906 { 907 par_info = &(scanner->frame_info); 908 } 909 else 910 { 911 int lval = img0[lnbd.y * step + lnbd.x] & 0x7f; 912 _CvContourInfo *cur = scanner->cinfo_table[lval - 2]; 913 914 assert( lval >= 2 ); 915 916 /* find the first bounding contour */ 917 while( cur ) 918 { 919 if( (unsigned) (lnbd.x - cur->rect.x) < (unsigned) cur->rect.width && 920 (unsigned) (lnbd.y - cur->rect.y) < (unsigned) cur->rect.height ) 921 { 922 if( par_info ) 923 { 924 if( icvTraceContour( scanner->img0 + 925 par_info->origin.y * step + 926 par_info->origin.x, step, img + lnbd.x, 927 par_info->is_hole ) > 0 ) 928 break; 929 } 930 par_info = cur; 931 } 932 cur = cur->next; 933 } 934 935 assert( par_info != 0 ); 936 937 /* if current contour is a hole and previous contour is a hole or 938 current contour is external and previous contour is external then 939 the parent of the contour is the parent of the previous contour else 940 the parent is the previous contour itself. */ 941 if( par_info->is_hole == is_hole ) 942 { 943 par_info = par_info->parent; 944 /* every contour must have a parent 945 (at least, the frame of the image) */ 946 if( !par_info ) 947 par_info = &(scanner->frame_info); 948 } 949 950 /* hole flag of the parent must differ from the flag of the contour */ 951 assert( par_info->is_hole != is_hole ); 952 if( par_info->contour == 0 ) /* removed contour */ 953 goto resume_scan; 954 } 955 956 lnbd.x = x - is_hole; 957 958 cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos) ); 959 960 seq = cvCreateSeq( scanner->seq_type1, scanner->header_size1, 961 scanner->elem_size1, scanner->storage1 ); 962 if( !seq ) 963 { 964 result = CV_OUTOFMEM_ERR; 965 goto exit_func; 966 } 967 seq->flags |= is_hole ? CV_SEQ_FLAG_HOLE : 0; 968 969 /* initialize header */ 970 if( mode <= 1 ) 971 { 972 l_cinfo = &(scanner->cinfo_temp); 973 result = icvFetchContour( img + x - is_hole, step, 974 cvPoint( origin.x + scanner->offset.x, 975 origin.y + scanner->offset.y), 976 seq, scanner->approx_method1 ); 977 if( result < 0 ) 978 goto exit_func; 979 } 980 else 981 { 982 union { _CvContourInfo* ci; CvSetElem* se; } v; 983 v.ci = l_cinfo; 984 cvSetAdd( scanner->cinfo_set, 0, &v.se ); 985 l_cinfo = v.ci; 986 987 result = icvFetchContourEx( img + x - is_hole, step, 988 cvPoint( origin.x + scanner->offset.x, 989 origin.y + scanner->offset.y), 990 seq, scanner->approx_method1, 991 nbd, &(l_cinfo->rect) ); 992 if( result < 0 ) 993 goto exit_func; 994 l_cinfo->rect.x -= scanner->offset.x; 995 l_cinfo->rect.y -= scanner->offset.y; 996 997 l_cinfo->next = scanner->cinfo_table[nbd - 2]; 998 scanner->cinfo_table[nbd - 2] = l_cinfo; 999 1000 /* change nbd */ 1001 nbd = (nbd + 1) & 127; 1002 nbd += nbd == 0 ? 3 : 0; 1003 } 1004 1005 l_cinfo->is_hole = is_hole; 1006 l_cinfo->contour = seq; 1007 l_cinfo->origin = origin; 1008 l_cinfo->parent = par_info; 1009 1010 if( scanner->approx_method1 != scanner->approx_method2 ) 1011 { 1012 result = icvApproximateChainTC89( (CvChain *) seq, 1013 scanner->header_size2, 1014 scanner->storage2, 1015 &(l_cinfo->contour), 1016 scanner->approx_method2 ); 1017 if( result < 0 ) 1018 goto exit_func; 1019 cvClearMemStorage( scanner->storage1 ); 1020 } 1021 1022 l_cinfo->contour->v_prev = l_cinfo->parent->contour; 1023 1024 if( par_info->contour == 0 ) 1025 { 1026 l_cinfo->contour = 0; 1027 if( scanner->storage1 == scanner->storage2 ) 1028 { 1029 cvRestoreMemStoragePos( scanner->storage1, &(scanner->backup_pos) ); 1030 } 1031 else 1032 { 1033 cvClearMemStorage( scanner->storage1 ); 1034 } 1035 p = img[x]; 1036 goto resume_scan; 1037 } 1038 1039 cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos2) ); 1040 scanner->l_cinfo = l_cinfo; 1041 scanner->pt.x = x + 1; 1042 scanner->pt.y = y; 1043 scanner->lnbd = lnbd; 1044 scanner->img = (schar *) img; 1045 scanner->nbd = nbd; 1046 contour = l_cinfo->contour; 1047 1048 result = CV_OK; 1049 goto exit_func; 1050 resume_scan: 1051 prev = p; 1052 /* update lnbd */ 1053 if( prev & -2 ) 1054 { 1055 lnbd.x = x; 1056 } 1057 } /* end of prev != p */ 1058 } /* end of loop on x */ 1059 1060 lnbd.x = 0; 1061 lnbd.y = y + 1; 1062 x = 1; 1063 prev = 0; 1064 1065 } /* end of loop on y */ 1066 1067 exit_func: 1068 1069 if( result != 0 ) 1070 contour = 0; 1071 if( result < 0 ) 1072 CV_ERROR_FROM_STATUS( result ); 1073 1074 __END__; 1075 1076 return contour; 1077} 1078 1079 1080/* 1081 The function add to tree the last retrieved/substituted contour, 1082 releases temp_storage, restores state of dst_storage (if needed), and 1083 returns pointer to root of the contour tree */ 1084CV_IMPL CvSeq * 1085cvEndFindContours( CvContourScanner * _scanner ) 1086{ 1087 CvContourScanner scanner; 1088 CvSeq *first = 0; 1089 1090 CV_FUNCNAME( "cvFindNextContour" ); 1091 1092 __BEGIN__; 1093 1094 if( !_scanner ) 1095 CV_ERROR( CV_StsNullPtr, "" ); 1096 scanner = *_scanner; 1097 1098 if( scanner ) 1099 { 1100 icvEndProcessContour( scanner ); 1101 1102 if( scanner->storage1 != scanner->storage2 ) 1103 cvReleaseMemStorage( &(scanner->storage1) ); 1104 1105 if( scanner->cinfo_storage ) 1106 cvReleaseMemStorage( &(scanner->cinfo_storage) ); 1107 1108 first = scanner->frame.v_next; 1109 cvFree( _scanner ); 1110 } 1111 1112 __END__; 1113 1114 return first; 1115} 1116 1117 1118#define ICV_SINGLE 0 1119#define ICV_CONNECTING_ABOVE 1 1120#define ICV_CONNECTING_BELOW -1 1121#define ICV_IS_COMPONENT_POINT(val) ((val) != 0) 1122 1123#define CV_GET_WRITTEN_ELEM( writer ) ((writer).ptr - (writer).seq->elem_size) 1124 1125typedef struct CvLinkedRunPoint 1126{ 1127 struct CvLinkedRunPoint* link; 1128 struct CvLinkedRunPoint* next; 1129 CvPoint pt; 1130} 1131CvLinkedRunPoint; 1132 1133 1134static int 1135icvFindContoursInInterval( const CvArr* src, 1136 /*int minValue, int maxValue,*/ 1137 CvMemStorage* storage, 1138 CvSeq** result, 1139 int contourHeaderSize ) 1140{ 1141 int count = 0; 1142 CvMemStorage* storage00 = 0; 1143 CvMemStorage* storage01 = 0; 1144 CvSeq* first = 0; 1145 1146 CV_FUNCNAME( "icvFindContoursInInterval" ); 1147 1148 __BEGIN__; 1149 1150 int i, j, k, n; 1151 1152 uchar* src_data = 0; 1153 int img_step = 0; 1154 CvSize img_size; 1155 1156 int connect_flag; 1157 int lower_total; 1158 int upper_total; 1159 int all_total; 1160 1161 CvSeq* runs; 1162 CvLinkedRunPoint tmp; 1163 CvLinkedRunPoint* tmp_prev; 1164 CvLinkedRunPoint* upper_line = 0; 1165 CvLinkedRunPoint* lower_line = 0; 1166 CvLinkedRunPoint* last_elem; 1167 1168 CvLinkedRunPoint* upper_run = 0; 1169 CvLinkedRunPoint* lower_run = 0; 1170 CvLinkedRunPoint* prev_point = 0; 1171 1172 CvSeqWriter writer_ext; 1173 CvSeqWriter writer_int; 1174 CvSeqWriter writer; 1175 CvSeqReader reader; 1176 1177 CvSeq* external_contours; 1178 CvSeq* internal_contours; 1179 CvSeq* prev = 0; 1180 1181 if( !storage ) 1182 CV_ERROR( CV_StsNullPtr, "NULL storage pointer" ); 1183 1184 if( !result ) 1185 CV_ERROR( CV_StsNullPtr, "NULL double CvSeq pointer" ); 1186 1187 if( contourHeaderSize < (int)sizeof(CvContour)) 1188 CV_ERROR( CV_StsBadSize, "Contour header size must be >= sizeof(CvContour)" ); 1189 1190 CV_CALL( storage00 = cvCreateChildMemStorage(storage)); 1191 CV_CALL( storage01 = cvCreateChildMemStorage(storage)); 1192 1193 { 1194 CvMat stub, *mat; 1195 1196 CV_CALL( mat = cvGetMat( src, &stub )); 1197 if( !CV_IS_MASK_ARR(mat)) 1198 CV_ERROR( CV_StsBadArg, "Input array must be 8uC1 or 8sC1" ); 1199 src_data = mat->data.ptr; 1200 img_step = mat->step; 1201 img_size = cvGetMatSize( mat ); 1202 } 1203 1204 // Create temporary sequences 1205 runs = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvLinkedRunPoint), storage00 ); 1206 cvStartAppendToSeq( runs, &writer ); 1207 1208 cvStartWriteSeq( 0, sizeof(CvSeq), sizeof(CvLinkedRunPoint*), storage01, &writer_ext ); 1209 cvStartWriteSeq( 0, sizeof(CvSeq), sizeof(CvLinkedRunPoint*), storage01, &writer_int ); 1210 1211 tmp_prev = &(tmp); 1212 tmp_prev->next = 0; 1213 tmp_prev->link = 0; 1214 1215 // First line. None of runs is binded 1216 tmp.pt.y = 0; 1217 i = 0; 1218 CV_WRITE_SEQ_ELEM( tmp, writer ); 1219 upper_line = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer ); 1220 1221 tmp_prev = upper_line; 1222 for( j = 0; j < img_size.width; ) 1223 { 1224 for( ; j < img_size.width && !ICV_IS_COMPONENT_POINT(src_data[j]); j++ ) 1225 ; 1226 if( j == img_size.width ) 1227 break; 1228 1229 tmp.pt.x = j; 1230 CV_WRITE_SEQ_ELEM( tmp, writer ); 1231 tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer ); 1232 tmp_prev = tmp_prev->next; 1233 1234 for( ; j < img_size.width && ICV_IS_COMPONENT_POINT(src_data[j]); j++ ) 1235 ; 1236 1237 tmp.pt.x = j-1; 1238 CV_WRITE_SEQ_ELEM( tmp, writer ); 1239 tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer ); 1240 tmp_prev->link = tmp_prev->next; 1241 // First point of contour 1242 CV_WRITE_SEQ_ELEM( tmp_prev, writer_ext ); 1243 tmp_prev = tmp_prev->next; 1244 } 1245 cvFlushSeqWriter( &writer ); 1246 upper_line = upper_line->next; 1247 upper_total = runs->total - 1; 1248 last_elem = tmp_prev; 1249 tmp_prev->next = 0; 1250 1251 for( i = 1; i < img_size.height; i++ ) 1252 { 1253//------// Find runs in next line 1254 src_data += img_step; 1255 tmp.pt.y = i; 1256 all_total = runs->total; 1257 for( j = 0; j < img_size.width; ) 1258 { 1259 for( ; j < img_size.width && !ICV_IS_COMPONENT_POINT(src_data[j]); j++ ) 1260 ; 1261 if( j == img_size.width ) break; 1262 1263 tmp.pt.x = j; 1264 CV_WRITE_SEQ_ELEM( tmp, writer ); 1265 tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer ); 1266 tmp_prev = tmp_prev->next; 1267 1268 for( ; j < img_size.width && ICV_IS_COMPONENT_POINT(src_data[j]); j++ ) 1269 ; 1270 1271 tmp.pt.x = j-1; 1272 CV_WRITE_SEQ_ELEM( tmp, writer ); 1273 tmp_prev = tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer ); 1274 }//j 1275 cvFlushSeqWriter( &writer ); 1276 lower_line = last_elem->next; 1277 lower_total = runs->total - all_total; 1278 last_elem = tmp_prev; 1279 tmp_prev->next = 0; 1280//------// 1281//------// Find links between runs of lower_line and upper_line 1282 upper_run = upper_line; 1283 lower_run = lower_line; 1284 connect_flag = ICV_SINGLE; 1285 1286 for( k = 0, n = 0; k < upper_total/2 && n < lower_total/2; ) 1287 { 1288 switch( connect_flag ) 1289 { 1290 case ICV_SINGLE: 1291 if( upper_run->next->pt.x < lower_run->next->pt.x ) 1292 { 1293 if( upper_run->next->pt.x >= lower_run->pt.x -1 ) 1294 { 1295 lower_run->link = upper_run; 1296 connect_flag = ICV_CONNECTING_ABOVE; 1297 prev_point = upper_run->next; 1298 } 1299 else 1300 upper_run->next->link = upper_run; 1301 k++; 1302 upper_run = upper_run->next->next; 1303 } 1304 else 1305 { 1306 if( upper_run->pt.x <= lower_run->next->pt.x +1 ) 1307 { 1308 lower_run->link = upper_run; 1309 connect_flag = ICV_CONNECTING_BELOW; 1310 prev_point = lower_run->next; 1311 } 1312 else 1313 { 1314 lower_run->link = lower_run->next; 1315 // First point of contour 1316 CV_WRITE_SEQ_ELEM( lower_run, writer_ext ); 1317 } 1318 n++; 1319 lower_run = lower_run->next->next; 1320 } 1321 break; 1322 case ICV_CONNECTING_ABOVE: 1323 if( upper_run->pt.x > lower_run->next->pt.x +1 ) 1324 { 1325 prev_point->link = lower_run->next; 1326 connect_flag = ICV_SINGLE; 1327 n++; 1328 lower_run = lower_run->next->next; 1329 } 1330 else 1331 { 1332 prev_point->link = upper_run; 1333 if( upper_run->next->pt.x < lower_run->next->pt.x ) 1334 { 1335 k++; 1336 prev_point = upper_run->next; 1337 upper_run = upper_run->next->next; 1338 } 1339 else 1340 { 1341 connect_flag = ICV_CONNECTING_BELOW; 1342 prev_point = lower_run->next; 1343 n++; 1344 lower_run = lower_run->next->next; 1345 } 1346 } 1347 break; 1348 case ICV_CONNECTING_BELOW: 1349 if( lower_run->pt.x > upper_run->next->pt.x +1 ) 1350 { 1351 upper_run->next->link = prev_point; 1352 connect_flag = ICV_SINGLE; 1353 k++; 1354 upper_run = upper_run->next->next; 1355 } 1356 else 1357 { 1358 // First point of contour 1359 CV_WRITE_SEQ_ELEM( lower_run, writer_int ); 1360 1361 lower_run->link = prev_point; 1362 if( lower_run->next->pt.x < upper_run->next->pt.x ) 1363 { 1364 n++; 1365 prev_point = lower_run->next; 1366 lower_run = lower_run->next->next; 1367 } 1368 else 1369 { 1370 connect_flag = ICV_CONNECTING_ABOVE; 1371 k++; 1372 prev_point = upper_run->next; 1373 upper_run = upper_run->next->next; 1374 } 1375 } 1376 break; 1377 } 1378 }// k, n 1379 1380 for( ; n < lower_total/2; n++ ) 1381 { 1382 if( connect_flag != ICV_SINGLE ) 1383 { 1384 prev_point->link = lower_run->next; 1385 connect_flag = ICV_SINGLE; 1386 lower_run = lower_run->next->next; 1387 continue; 1388 } 1389 lower_run->link = lower_run->next; 1390 1391 //First point of contour 1392 CV_WRITE_SEQ_ELEM( lower_run, writer_ext ); 1393 1394 lower_run = lower_run->next->next; 1395 } 1396 1397 for( ; k < upper_total/2; k++ ) 1398 { 1399 if( connect_flag != ICV_SINGLE ) 1400 { 1401 upper_run->next->link = prev_point; 1402 connect_flag = ICV_SINGLE; 1403 upper_run = upper_run->next->next; 1404 continue; 1405 } 1406 upper_run->next->link = upper_run; 1407 upper_run = upper_run->next->next; 1408 } 1409 upper_line = lower_line; 1410 upper_total = lower_total; 1411 }//i 1412 1413 upper_run = upper_line; 1414 1415 //the last line of image 1416 for( k = 0; k < upper_total/2; k++ ) 1417 { 1418 upper_run->next->link = upper_run; 1419 upper_run = upper_run->next->next; 1420 } 1421 1422//------// 1423//------//Find end read contours 1424 external_contours = cvEndWriteSeq( &writer_ext ); 1425 internal_contours = cvEndWriteSeq( &writer_int ); 1426 1427 for( k = 0; k < 2; k++ ) 1428 { 1429 CvSeq* contours = k == 0 ? external_contours : internal_contours; 1430 1431 cvStartReadSeq( contours, &reader ); 1432 1433 for( j = 0; j < contours->total; j++, count++ ) 1434 { 1435 CvLinkedRunPoint* p_temp; 1436 CvLinkedRunPoint* p00; 1437 CvLinkedRunPoint* p01; 1438 CvSeq* contour; 1439 1440 CV_READ_SEQ_ELEM( p00, reader ); 1441 p01 = p00; 1442 1443 if( !p00->link ) 1444 continue; 1445 1446 cvStartWriteSeq( CV_SEQ_ELTYPE_POINT | CV_SEQ_POLYLINE | CV_SEQ_FLAG_CLOSED, 1447 contourHeaderSize, sizeof(CvPoint), storage, &writer ); 1448 do 1449 { 1450 CV_WRITE_SEQ_ELEM( p00->pt, writer ); 1451 p_temp = p00; 1452 p00 = p00->link; 1453 p_temp->link = 0; 1454 } 1455 while( p00 != p01 ); 1456 1457 contour = cvEndWriteSeq( &writer ); 1458 cvBoundingRect( contour, 1 ); 1459 1460 if( k != 0 ) 1461 contour->flags |= CV_SEQ_FLAG_HOLE; 1462 1463 if( !first ) 1464 prev = first = contour; 1465 else 1466 { 1467 contour->h_prev = prev; 1468 prev = prev->h_next = contour; 1469 } 1470 } 1471 } 1472 1473 __END__; 1474 1475 if( !first ) 1476 count = -1; 1477 1478 if( result ) 1479 *result = first; 1480 1481 cvReleaseMemStorage(&storage00); 1482 cvReleaseMemStorage(&storage01); 1483 1484 return count; 1485} 1486 1487 1488 1489/*F/////////////////////////////////////////////////////////////////////////////////////// 1490// Name: cvFindContours 1491// Purpose: 1492// Finds all the contours on the bi-level image. 1493// Context: 1494// Parameters: 1495// img - source image. 1496// Non-zero pixels are considered as 1-pixels 1497// and zero pixels as 0-pixels. 1498// step - full width of source image in bytes. 1499// size - width and height of the image in pixels 1500// storage - pointer to storage where will the output contours be placed. 1501// header_size - header size of resulting contours 1502// mode - mode of contour retrieval. 1503// method - method of approximation that is applied to contours 1504// first_contour - pointer to first contour pointer 1505// Returns: 1506// CV_OK or error code 1507// Notes: 1508//F*/ 1509CV_IMPL int 1510cvFindContours( void* img, CvMemStorage* storage, 1511 CvSeq** firstContour, int cntHeaderSize, 1512 int mode, 1513 int method, CvPoint offset ) 1514{ 1515 CvContourScanner scanner = 0; 1516 CvSeq *contour = 0; 1517 int count = -1; 1518 1519 CV_FUNCNAME( "cvFindContours" ); 1520 1521 __BEGIN__; 1522 1523 if( !firstContour ) 1524 CV_ERROR( CV_StsNullPtr, "NULL double CvSeq pointer" ); 1525 1526 if( method == CV_LINK_RUNS ) 1527 { 1528 if( offset.x != 0 || offset.y != 0 ) 1529 CV_ERROR( CV_StsOutOfRange, 1530 "Nonzero offset is not supported in CV_LINK_RUNS yet" ); 1531 1532 CV_CALL( count = icvFindContoursInInterval( img, storage, 1533 firstContour, cntHeaderSize )); 1534 } 1535 else 1536 { 1537 CV_CALL( scanner = cvStartFindContours( img, storage, 1538 cntHeaderSize, mode, method, offset )); 1539 assert( scanner ); 1540 1541 do 1542 { 1543 count++; 1544 contour = cvFindNextContour( scanner ); 1545 } 1546 while( contour != 0 ); 1547 1548 *firstContour = cvEndFindContours( &scanner ); 1549 } 1550 1551 __END__; 1552 1553 return count; 1554} 1555 1556 1557/* End of file. */ 1558