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