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 42 #include "_cv.h" 43 #include <limits.h> 44 #include <stdio.h> 45 46 #define IPCV_MORPHOLOGY_PTRS( morphtype, flavor ) \ 47 icv##morphtype##Rect_##flavor##_C1R_t \ 48 icv##morphtype##Rect_##flavor##_C1R_p = 0; \ 49 icv##morphtype##Rect_GetBufSize_##flavor##_C1R_t \ 50 icv##morphtype##Rect_GetBufSize_##flavor##_C1R_p = 0; \ 51 icv##morphtype##Rect_##flavor##_C3R_t \ 52 icv##morphtype##Rect_##flavor##_C3R_p = 0; \ 53 icv##morphtype##Rect_GetBufSize_##flavor##_C3R_t \ 54 icv##morphtype##Rect_GetBufSize_##flavor##_C3R_p = 0; \ 55 icv##morphtype##Rect_##flavor##_C4R_t \ 56 icv##morphtype##Rect_##flavor##_C4R_p = 0; \ 57 icv##morphtype##Rect_GetBufSize_##flavor##_C4R_t \ 58 icv##morphtype##Rect_GetBufSize_##flavor##_C4R_p = 0; \ 59 \ 60 icv##morphtype##_##flavor##_C1R_t \ 61 icv##morphtype##_##flavor##_C1R_p = 0; \ 62 icv##morphtype##_##flavor##_C3R_t \ 63 icv##morphtype##_##flavor##_C3R_p = 0; \ 64 icv##morphtype##_##flavor##_C4R_t \ 65 icv##morphtype##_##flavor##_C4R_p = 0; 66 67 #define IPCV_MORPHOLOGY_INITALLOC_PTRS( flavor ) \ 68 icvMorphInitAlloc_##flavor##_C1R_t \ 69 icvMorphInitAlloc_##flavor##_C1R_p = 0; \ 70 icvMorphInitAlloc_##flavor##_C3R_t \ 71 icvMorphInitAlloc_##flavor##_C3R_p = 0; \ 72 icvMorphInitAlloc_##flavor##_C4R_t \ 73 icvMorphInitAlloc_##flavor##_C4R_p = 0; 74 75 IPCV_MORPHOLOGY_PTRS( Erode, 8u ) 76 IPCV_MORPHOLOGY_PTRS( Erode, 16u ) 77 IPCV_MORPHOLOGY_PTRS( Erode, 32f ) 78 IPCV_MORPHOLOGY_PTRS( Dilate, 8u ) 79 IPCV_MORPHOLOGY_PTRS( Dilate, 16u ) 80 IPCV_MORPHOLOGY_PTRS( Dilate, 32f ) 81 IPCV_MORPHOLOGY_INITALLOC_PTRS( 8u ) 82 IPCV_MORPHOLOGY_INITALLOC_PTRS( 16u ) 83 IPCV_MORPHOLOGY_INITALLOC_PTRS( 32f ) 84 85 icvMorphFree_t icvMorphFree_p = 0; 86 87 /****************************************************************************************\ 88 Basic Morphological Operations: Erosion & Dilation 89 \****************************************************************************************/ 90 91 static void icvErodeRectRow_8u( const uchar* src, uchar* dst, void* params ); 92 static void icvErodeRectRow_16u( const ushort* src, ushort* dst, void* params ); 93 static void icvErodeRectRow_32f( const int* src, int* dst, void* params ); 94 static void icvDilateRectRow_8u( const uchar* src, uchar* dst, void* params ); 95 static void icvDilateRectRow_16u( const ushort* src, ushort* dst, void* params ); 96 static void icvDilateRectRow_32f( const int* src, int* dst, void* params ); 97 98 static void icvErodeRectCol_8u( const uchar** src, uchar* dst, int dst_step, 99 int count, void* params ); 100 static void icvErodeRectCol_16u( const ushort** src, ushort* dst, int dst_step, 101 int count, void* params ); 102 static void icvErodeRectCol_32f( const int** src, int* dst, int dst_step, 103 int count, void* params ); 104 static void icvDilateRectCol_8u( const uchar** src, uchar* dst, int dst_step, 105 int count, void* params ); 106 static void icvDilateRectCol_16u( const ushort** src, ushort* dst, int dst_step, 107 int count, void* params ); 108 static void icvDilateRectCol_32f( const int** src, int* dst, int dst_step, 109 int count, void* params ); 110 111 static void icvErodeAny_8u( const uchar** src, uchar* dst, int dst_step, 112 int count, void* params ); 113 static void icvErodeAny_16u( const ushort** src, ushort* dst, int dst_step, 114 int count, void* params ); 115 static void icvErodeAny_32f( const int** src, int* dst, int dst_step, 116 int count, void* params ); 117 static void icvDilateAny_8u( const uchar** src, uchar* dst, int dst_step, 118 int count, void* params ); 119 static void icvDilateAny_16u( const ushort** src, ushort* dst, int dst_step, 120 int count, void* params ); 121 static void icvDilateAny_32f( const int** src, int* dst, int dst_step, 122 int count, void* params ); 123 124 CvMorphology::CvMorphology() 125 { 126 element = 0; 127 el_sparse = 0; 128 } 129 130 CvMorphology::CvMorphology( int _operation, int _max_width, int _src_dst_type, 131 int _element_shape, CvMat* _element, 132 CvSize _ksize, CvPoint _anchor, 133 int _border_mode, CvScalar _border_value ) 134 { 135 element = 0; 136 el_sparse = 0; 137 init( _operation, _max_width, _src_dst_type, 138 _element_shape, _element, _ksize, _anchor, 139 _border_mode, _border_value ); 140 } 141 142 143 void CvMorphology::clear() 144 { 145 cvReleaseMat( &element ); 146 cvFree( &el_sparse ); 147 CvBaseImageFilter::clear(); 148 } 149 150 151 CvMorphology::~CvMorphology() 152 { 153 clear(); 154 } 155 156 157 void CvMorphology::init( int _operation, int _max_width, int _src_dst_type, 158 int _element_shape, CvMat* _element, 159 CvSize _ksize, CvPoint _anchor, 160 int _border_mode, CvScalar _border_value ) 161 { 162 CV_FUNCNAME( "CvMorphology::init" ); 163 164 __BEGIN__; 165 166 int depth = CV_MAT_DEPTH(_src_dst_type); 167 int el_type = 0, nz = -1; 168 169 if( _operation != ERODE && _operation != DILATE ) 170 CV_ERROR( CV_StsBadArg, "Unknown/unsupported morphological operation" ); 171 172 if( _element_shape == CUSTOM ) 173 { 174 if( !CV_IS_MAT(_element) ) 175 CV_ERROR( CV_StsBadArg, 176 "structuring element should be valid matrix if CUSTOM element shape is specified" ); 177 178 el_type = CV_MAT_TYPE(_element->type); 179 if( el_type != CV_8UC1 && el_type != CV_32SC1 ) 180 CV_ERROR( CV_StsUnsupportedFormat, "the structuring element must have 8uC1 or 32sC1 type" ); 181 182 _ksize = cvGetMatSize(_element); 183 CV_CALL( nz = cvCountNonZero(_element)); 184 if( nz == _ksize.width*_ksize.height ) 185 _element_shape = RECT; 186 } 187 188 operation = _operation; 189 el_shape = _element_shape; 190 191 CV_CALL( CvBaseImageFilter::init( _max_width, _src_dst_type, _src_dst_type, 192 _element_shape == RECT, _ksize, _anchor, _border_mode, _border_value )); 193 194 if( el_shape == RECT ) 195 { 196 if( operation == ERODE ) 197 { 198 if( depth == CV_8U ) 199 x_func = (CvRowFilterFunc)icvErodeRectRow_8u, 200 y_func = (CvColumnFilterFunc)icvErodeRectCol_8u; 201 else if( depth == CV_16U ) 202 x_func = (CvRowFilterFunc)icvErodeRectRow_16u, 203 y_func = (CvColumnFilterFunc)icvErodeRectCol_16u; 204 else if( depth == CV_32F ) 205 x_func = (CvRowFilterFunc)icvErodeRectRow_32f, 206 y_func = (CvColumnFilterFunc)icvErodeRectCol_32f; 207 } 208 else 209 { 210 assert( operation == DILATE ); 211 if( depth == CV_8U ) 212 x_func = (CvRowFilterFunc)icvDilateRectRow_8u, 213 y_func = (CvColumnFilterFunc)icvDilateRectCol_8u; 214 else if( depth == CV_16U ) 215 x_func = (CvRowFilterFunc)icvDilateRectRow_16u, 216 y_func = (CvColumnFilterFunc)icvDilateRectCol_16u; 217 else if( depth == CV_32F ) 218 x_func = (CvRowFilterFunc)icvDilateRectRow_32f, 219 y_func = (CvColumnFilterFunc)icvDilateRectCol_32f; 220 } 221 } 222 else 223 { 224 int i, j, k = 0; 225 int cn = CV_MAT_CN(src_type); 226 CvPoint* nz_loc; 227 228 if( !(element && el_sparse && 229 _ksize.width == element->cols && _ksize.height == element->rows) ) 230 { 231 cvReleaseMat( &element ); 232 cvFree( &el_sparse ); 233 CV_CALL( element = cvCreateMat( _ksize.height, _ksize.width, CV_8UC1 )); 234 CV_CALL( el_sparse = (uchar*)cvAlloc( 235 ksize.width*ksize.height*(2*sizeof(int) + sizeof(uchar*)))); 236 } 237 238 if( el_shape == CUSTOM ) 239 { 240 CV_CALL( cvConvert( _element, element )); 241 } 242 else 243 { 244 CV_CALL( init_binary_element( element, el_shape, anchor )); 245 } 246 247 if( operation == ERODE ) 248 { 249 if( depth == CV_8U ) 250 y_func = (CvColumnFilterFunc)icvErodeAny_8u; 251 else if( depth == CV_16U ) 252 y_func = (CvColumnFilterFunc)icvErodeAny_16u; 253 else if( depth == CV_32F ) 254 y_func = (CvColumnFilterFunc)icvErodeAny_32f; 255 } 256 else 257 { 258 assert( operation == DILATE ); 259 if( depth == CV_8U ) 260 y_func = (CvColumnFilterFunc)icvDilateAny_8u; 261 else if( depth == CV_16U ) 262 y_func = (CvColumnFilterFunc)icvDilateAny_16u; 263 else if( depth == CV_32F ) 264 y_func = (CvColumnFilterFunc)icvDilateAny_32f; 265 } 266 267 nz_loc = (CvPoint*)el_sparse; 268 269 for( i = 0; i < ksize.height; i++ ) 270 for( j = 0; j < ksize.width; j++ ) 271 { 272 if( element->data.ptr[i*element->step+j] ) 273 nz_loc[k++] = cvPoint(j*cn,i); 274 } 275 if( k == 0 ) 276 nz_loc[k++] = cvPoint(anchor.x*cn,anchor.y); 277 el_sparse_count = k; 278 } 279 280 if( depth == CV_32F && border_mode == IPL_BORDER_CONSTANT ) 281 { 282 int i, cn = CV_MAT_CN(src_type); 283 int* bt = (int*)border_tab; 284 for( i = 0; i < cn; i++ ) 285 bt[i] = CV_TOGGLE_FLT(bt[i]); 286 } 287 288 __END__; 289 } 290 291 292 void CvMorphology::init( int _max_width, int _src_type, int _dst_type, 293 bool _is_separable, CvSize _ksize, 294 CvPoint _anchor, int _border_mode, 295 CvScalar _border_value ) 296 { 297 CvBaseImageFilter::init( _max_width, _src_type, _dst_type, _is_separable, 298 _ksize, _anchor, _border_mode, _border_value ); 299 } 300 301 302 void CvMorphology::start_process( CvSlice x_range, int width ) 303 { 304 CvBaseImageFilter::start_process( x_range, width ); 305 if( el_shape == RECT ) 306 { 307 // cut the cyclic buffer off by 1 line if need, to make 308 // the vertical part of separable morphological filter 309 // always process 2 rows at once (except, may be, 310 // for the last one in a stripe). 311 int t = buf_max_count - max_ky*2; 312 if( t > 1 && t % 2 != 0 ) 313 { 314 buf_max_count--; 315 buf_end -= buf_step; 316 } 317 } 318 } 319 320 321 int CvMorphology::fill_cyclic_buffer( const uchar* src, int src_step, 322 int y0, int y1, int y2 ) 323 { 324 int i, y = y0, bsz1 = border_tab_sz1, bsz = border_tab_sz; 325 int pix_size = CV_ELEM_SIZE(src_type); 326 int width_n = (prev_x_range.end_index - prev_x_range.start_index)*pix_size; 327 328 if( CV_MAT_DEPTH(src_type) != CV_32F ) 329 return CvBaseImageFilter::fill_cyclic_buffer( src, src_step, y0, y1, y2 ); 330 331 // fill the cyclic buffer 332 for( ; buf_count < buf_max_count && y < y2; buf_count++, y++, src += src_step ) 333 { 334 uchar* trow = is_separable ? buf_end : buf_tail; 335 336 for( i = 0; i < width_n; i += sizeof(int) ) 337 { 338 int t = *(int*)(src + i); 339 *(int*)(trow + i + bsz1) = CV_TOGGLE_FLT(t); 340 } 341 342 if( border_mode != IPL_BORDER_CONSTANT ) 343 { 344 for( i = 0; i < bsz1; i++ ) 345 { 346 int j = border_tab[i]; 347 trow[i] = trow[j]; 348 } 349 for( ; i < bsz; i++ ) 350 { 351 int j = border_tab[i]; 352 trow[i + width_n] = trow[j]; 353 } 354 } 355 else 356 { 357 const uchar *bt = (uchar*)border_tab; 358 for( i = 0; i < bsz1; i++ ) 359 trow[i] = bt[i]; 360 361 for( ; i < bsz; i++ ) 362 trow[i + width_n] = bt[i]; 363 } 364 365 if( is_separable ) 366 x_func( trow, buf_tail, this ); 367 368 buf_tail += buf_step; 369 if( buf_tail >= buf_end ) 370 buf_tail = buf_start; 371 } 372 373 return y - y0; 374 } 375 376 377 void CvMorphology::init_binary_element( CvMat* element, int element_shape, CvPoint anchor ) 378 { 379 CV_FUNCNAME( "CvMorphology::init_binary_element" ); 380 381 __BEGIN__; 382 383 int type; 384 int i, j, cols, rows; 385 int r = 0, c = 0; 386 double inv_r2 = 0; 387 388 if( !CV_IS_MAT(element) ) 389 CV_ERROR( CV_StsBadArg, "element must be valid matrix" ); 390 391 type = CV_MAT_TYPE(element->type); 392 if( type != CV_8UC1 && type != CV_32SC1 ) 393 CV_ERROR( CV_StsUnsupportedFormat, "element must have 8uC1 or 32sC1 type" ); 394 395 if( anchor.x == -1 ) 396 anchor.x = element->cols/2; 397 398 if( anchor.y == -1 ) 399 anchor.y = element->rows/2; 400 401 if( (unsigned)anchor.x >= (unsigned)element->cols || 402 (unsigned)anchor.y >= (unsigned)element->rows ) 403 CV_ERROR( CV_StsOutOfRange, "anchor is outside of element" ); 404 405 if( element_shape != RECT && element_shape != CROSS && element_shape != ELLIPSE ) 406 CV_ERROR( CV_StsBadArg, "Unknown/unsupported element shape" ); 407 408 rows = element->rows; 409 cols = element->cols; 410 411 if( rows == 1 || cols == 1 ) 412 element_shape = RECT; 413 414 if( element_shape == ELLIPSE ) 415 { 416 r = rows/2; 417 c = cols/2; 418 inv_r2 = r ? 1./((double)r*r) : 0; 419 } 420 421 for( i = 0; i < rows; i++ ) 422 { 423 uchar* ptr = element->data.ptr + i*element->step; 424 int j1 = 0, j2 = 0, jx, t = 0; 425 426 if( element_shape == RECT || (element_shape == CROSS && i == anchor.y) ) 427 j2 = cols; 428 else if( element_shape == CROSS ) 429 j1 = anchor.x, j2 = j1 + 1; 430 else 431 { 432 int dy = i - r; 433 if( abs(dy) <= r ) 434 { 435 int dx = cvRound(c*sqrt(((double)r*r - dy*dy)*inv_r2)); 436 j1 = MAX( c - dx, 0 ); 437 j2 = MIN( c + dx + 1, cols ); 438 } 439 } 440 441 for( j = 0, jx = j1; j < cols; ) 442 { 443 for( ; j < jx; j++ ) 444 { 445 if( type == CV_8UC1 ) 446 ptr[j] = (uchar)t; 447 else 448 ((int*)ptr)[j] = t; 449 } 450 if( jx == j2 ) 451 jx = cols, t = 0; 452 else 453 jx = j2, t = 1; 454 } 455 } 456 457 __END__; 458 } 459 460 461 #define ICV_MORPH_RECT_ROW( name, flavor, arrtype, \ 462 worktype, update_extr_macro ) \ 463 static void \ 464 icv##name##RectRow_##flavor( const arrtype* src, \ 465 arrtype* dst, void* params ) \ 466 { \ 467 const CvMorphology* state = (const CvMorphology*)params;\ 468 int ksize = state->get_kernel_size().width; \ 469 int width = state->get_width(); \ 470 int cn = CV_MAT_CN(state->get_src_type()); \ 471 int i, j, k; \ 472 \ 473 width *= cn; ksize *= cn; \ 474 \ 475 if( ksize == cn ) \ 476 { \ 477 for( i = 0; i < width; i++ ) \ 478 dst[i] = src[i]; \ 479 return; \ 480 } \ 481 \ 482 for( k = 0; k < cn; k++, src++, dst++ ) \ 483 { \ 484 for( i = 0; i <= width - cn*2; i += cn*2 ) \ 485 { \ 486 const arrtype* s = src + i; \ 487 worktype m = s[cn], t; \ 488 for( j = cn*2; j < ksize; j += cn ) \ 489 { \ 490 t = s[j]; update_extr_macro(m,t); \ 491 } \ 492 t = s[0]; update_extr_macro(t,m); \ 493 dst[i] = (arrtype)t; \ 494 t = s[j]; update_extr_macro(t,m); \ 495 dst[i+cn] = (arrtype)t; \ 496 } \ 497 \ 498 for( ; i < width; i += cn ) \ 499 { \ 500 const arrtype* s = src + i; \ 501 worktype m = s[0], t; \ 502 for( j = cn; j < ksize; j += cn ) \ 503 { \ 504 t = s[j]; update_extr_macro(m,t); \ 505 } \ 506 dst[i] = (arrtype)m; \ 507 } \ 508 } \ 509 } 510 511 512 ICV_MORPH_RECT_ROW( Erode, 8u, uchar, int, CV_CALC_MIN_8U ) 513 ICV_MORPH_RECT_ROW( Dilate, 8u, uchar, int, CV_CALC_MAX_8U ) 514 ICV_MORPH_RECT_ROW( Erode, 16u, ushort, int, CV_CALC_MIN ) 515 ICV_MORPH_RECT_ROW( Dilate, 16u, ushort, int, CV_CALC_MAX ) 516 ICV_MORPH_RECT_ROW( Erode, 32f, int, int, CV_CALC_MIN ) 517 ICV_MORPH_RECT_ROW( Dilate, 32f, int, int, CV_CALC_MAX ) 518 519 520 #define ICV_MORPH_RECT_COL( name, flavor, arrtype, \ 521 worktype, update_extr_macro, toggle_macro ) \ 522 static void \ 523 icv##name##RectCol_##flavor( const arrtype** src, \ 524 arrtype* dst, int dst_step, int count, void* params ) \ 525 { \ 526 const CvMorphology* state = (const CvMorphology*)params;\ 527 int ksize = state->get_kernel_size().height; \ 528 int width = state->get_width(); \ 529 int cn = CV_MAT_CN(state->get_src_type()); \ 530 int i, k; \ 531 \ 532 width *= cn; \ 533 dst_step /= sizeof(dst[0]); \ 534 \ 535 for( ; ksize > 1 && count > 1; count -= 2, \ 536 dst += dst_step*2, src += 2 ) \ 537 { \ 538 for( i = 0; i <= width - 4; i += 4 ) \ 539 { \ 540 const arrtype* sptr = src[1] + i; \ 541 worktype s0 = sptr[0], s1 = sptr[1], \ 542 s2 = sptr[2], s3 = sptr[3], t0, t1; \ 543 \ 544 for( k = 2; k < ksize; k++ ) \ 545 { \ 546 sptr = src[k] + i; \ 547 t0 = sptr[0]; t1 = sptr[1]; \ 548 update_extr_macro(s0,t0); \ 549 update_extr_macro(s1,t1); \ 550 t0 = sptr[2]; t1 = sptr[3]; \ 551 update_extr_macro(s2,t0); \ 552 update_extr_macro(s3,t1); \ 553 } \ 554 \ 555 sptr = src[0] + i; \ 556 t0 = sptr[0]; t1 = sptr[1]; \ 557 update_extr_macro(t0,s0); \ 558 update_extr_macro(t1,s1); \ 559 dst[i] = (arrtype)toggle_macro(t0); \ 560 dst[i+1] = (arrtype)toggle_macro(t1); \ 561 t0 = sptr[2]; t1 = sptr[3]; \ 562 update_extr_macro(t0,s2); \ 563 update_extr_macro(t1,s3); \ 564 dst[i+2] = (arrtype)toggle_macro(t0); \ 565 dst[i+3] = (arrtype)toggle_macro(t1); \ 566 \ 567 sptr = src[k] + i; \ 568 t0 = sptr[0]; t1 = sptr[1]; \ 569 update_extr_macro(t0,s0); \ 570 update_extr_macro(t1,s1); \ 571 dst[i+dst_step] = (arrtype)toggle_macro(t0); \ 572 dst[i+dst_step+1] = (arrtype)toggle_macro(t1); \ 573 t0 = sptr[2]; t1 = sptr[3]; \ 574 update_extr_macro(t0,s2); \ 575 update_extr_macro(t1,s3); \ 576 dst[i+dst_step+2] = (arrtype)toggle_macro(t0); \ 577 dst[i+dst_step+3] = (arrtype)toggle_macro(t1); \ 578 } \ 579 \ 580 for( ; i < width; i++ ) \ 581 { \ 582 const arrtype* sptr = src[1] + i; \ 583 worktype s0 = sptr[0], t0; \ 584 \ 585 for( k = 2; k < ksize; k++ ) \ 586 { \ 587 sptr = src[k] + i; t0 = sptr[0]; \ 588 update_extr_macro(s0,t0); \ 589 } \ 590 \ 591 sptr = src[0] + i; t0 = sptr[0]; \ 592 update_extr_macro(t0,s0); \ 593 dst[i] = (arrtype)toggle_macro(t0); \ 594 \ 595 sptr = src[k] + i; t0 = sptr[0]; \ 596 update_extr_macro(t0,s0); \ 597 dst[i+dst_step] = (arrtype)toggle_macro(t0); \ 598 } \ 599 } \ 600 \ 601 for( ; count > 0; count--, dst += dst_step, src++ ) \ 602 { \ 603 for( i = 0; i <= width - 4; i += 4 ) \ 604 { \ 605 const arrtype* sptr = src[0] + i; \ 606 worktype s0 = sptr[0], s1 = sptr[1], \ 607 s2 = sptr[2], s3 = sptr[3], t0, t1; \ 608 \ 609 for( k = 1; k < ksize; k++ ) \ 610 { \ 611 sptr = src[k] + i; \ 612 t0 = sptr[0]; t1 = sptr[1]; \ 613 update_extr_macro(s0,t0); \ 614 update_extr_macro(s1,t1); \ 615 t0 = sptr[2]; t1 = sptr[3]; \ 616 update_extr_macro(s2,t0); \ 617 update_extr_macro(s3,t1); \ 618 } \ 619 dst[i] = (arrtype)toggle_macro(s0); \ 620 dst[i+1] = (arrtype)toggle_macro(s1); \ 621 dst[i+2] = (arrtype)toggle_macro(s2); \ 622 dst[i+3] = (arrtype)toggle_macro(s3); \ 623 } \ 624 \ 625 for( ; i < width; i++ ) \ 626 { \ 627 const arrtype* sptr = src[0] + i; \ 628 worktype s0 = sptr[0], t0; \ 629 \ 630 for( k = 1; k < ksize; k++ ) \ 631 { \ 632 sptr = src[k] + i; t0 = sptr[0]; \ 633 update_extr_macro(s0,t0); \ 634 } \ 635 dst[i] = (arrtype)toggle_macro(s0); \ 636 } \ 637 } \ 638 } 639 640 641 ICV_MORPH_RECT_COL( Erode, 8u, uchar, int, CV_CALC_MIN_8U, CV_NOP ) 642 ICV_MORPH_RECT_COL( Dilate, 8u, uchar, int, CV_CALC_MAX_8U, CV_NOP ) 643 ICV_MORPH_RECT_COL( Erode, 16u, ushort, int, CV_CALC_MIN, CV_NOP ) 644 ICV_MORPH_RECT_COL( Dilate, 16u, ushort, int, CV_CALC_MAX, CV_NOP ) 645 ICV_MORPH_RECT_COL( Erode, 32f, int, int, CV_CALC_MIN, CV_TOGGLE_FLT ) 646 ICV_MORPH_RECT_COL( Dilate, 32f, int, int, CV_CALC_MAX, CV_TOGGLE_FLT ) 647 648 649 #define ICV_MORPH_ANY( name, flavor, arrtype, worktype, \ 650 update_extr_macro, toggle_macro ) \ 651 static void \ 652 icv##name##Any_##flavor( const arrtype** src, arrtype* dst, \ 653 int dst_step, int count, void* params ) \ 654 { \ 655 CvMorphology* state = (CvMorphology*)params; \ 656 int width = state->get_width(); \ 657 int cn = CV_MAT_CN(state->get_src_type()); \ 658 int i, k; \ 659 CvPoint* el_sparse = (CvPoint*)state->get_element_sparse_buf();\ 660 int el_count = state->get_element_sparse_count(); \ 661 const arrtype** el_ptr = (const arrtype**)(el_sparse + el_count);\ 662 const arrtype** el_end = el_ptr + el_count; \ 663 \ 664 width *= cn; \ 665 dst_step /= sizeof(dst[0]); \ 666 \ 667 for( ; count > 0; count--, dst += dst_step, src++ ) \ 668 { \ 669 for( k = 0; k < el_count; k++ ) \ 670 el_ptr[k] = src[el_sparse[k].y]+el_sparse[k].x; \ 671 \ 672 for( i = 0; i <= width - 4; i += 4 ) \ 673 { \ 674 const arrtype** psptr = el_ptr; \ 675 const arrtype* sptr = *psptr++; \ 676 worktype s0 = sptr[i], s1 = sptr[i+1], \ 677 s2 = sptr[i+2], s3 = sptr[i+3], t; \ 678 \ 679 while( psptr != el_end ) \ 680 { \ 681 sptr = *psptr++; \ 682 t = sptr[i]; \ 683 update_extr_macro(s0,t); \ 684 t = sptr[i+1]; \ 685 update_extr_macro(s1,t); \ 686 t = sptr[i+2]; \ 687 update_extr_macro(s2,t); \ 688 t = sptr[i+3]; \ 689 update_extr_macro(s3,t); \ 690 } \ 691 \ 692 dst[i] = (arrtype)toggle_macro(s0); \ 693 dst[i+1] = (arrtype)toggle_macro(s1); \ 694 dst[i+2] = (arrtype)toggle_macro(s2); \ 695 dst[i+3] = (arrtype)toggle_macro(s3); \ 696 } \ 697 \ 698 for( ; i < width; i++ ) \ 699 { \ 700 const arrtype* sptr = el_ptr[0] + i; \ 701 worktype s0 = sptr[0], t0; \ 702 \ 703 for( k = 1; k < el_count; k++ ) \ 704 { \ 705 sptr = el_ptr[k] + i; \ 706 t0 = sptr[0]; \ 707 update_extr_macro(s0,t0); \ 708 } \ 709 \ 710 dst[i] = (arrtype)toggle_macro(s0); \ 711 } \ 712 } \ 713 } 714 715 ICV_MORPH_ANY( Erode, 8u, uchar, int, CV_CALC_MIN, CV_NOP ) 716 ICV_MORPH_ANY( Dilate, 8u, uchar, int, CV_CALC_MAX, CV_NOP ) 717 ICV_MORPH_ANY( Erode, 16u, ushort, int, CV_CALC_MIN, CV_NOP ) 718 ICV_MORPH_ANY( Dilate, 16u, ushort, int, CV_CALC_MAX, CV_NOP ) 719 ICV_MORPH_ANY( Erode, 32f, int, int, CV_CALC_MIN, CV_TOGGLE_FLT ) 720 ICV_MORPH_ANY( Dilate, 32f, int, int, CV_CALC_MAX, CV_TOGGLE_FLT ) 721 722 /////////////////////////////////// External Interface ///////////////////////////////////// 723 724 725 CV_IMPL IplConvKernel * 726 cvCreateStructuringElementEx( int cols, int rows, 727 int anchorX, int anchorY, 728 int shape, int *values ) 729 { 730 IplConvKernel *element = 0; 731 int i, size = rows * cols; 732 int element_size = sizeof(*element) + size*sizeof(element->values[0]); 733 734 CV_FUNCNAME( "cvCreateStructuringElementEx" ); 735 736 __BEGIN__; 737 738 if( !values && shape == CV_SHAPE_CUSTOM ) 739 CV_ERROR_FROM_STATUS( CV_NULLPTR_ERR ); 740 741 if( cols <= 0 || rows <= 0 || 742 (unsigned) anchorX >= (unsigned) cols || 743 (unsigned) anchorY >= (unsigned) rows ) 744 CV_ERROR_FROM_STATUS( CV_BADSIZE_ERR ); 745 746 CV_CALL( element = (IplConvKernel *)cvAlloc(element_size + 32)); 747 if( !element ) 748 CV_ERROR_FROM_STATUS( CV_OUTOFMEM_ERR ); 749 750 element->nCols = cols; 751 element->nRows = rows; 752 element->anchorX = anchorX; 753 element->anchorY = anchorY; 754 element->nShiftR = shape < CV_SHAPE_ELLIPSE ? shape : CV_SHAPE_CUSTOM; 755 element->values = (int*)(element + 1); 756 757 if( shape == CV_SHAPE_CUSTOM ) 758 { 759 if( !values ) 760 CV_ERROR( CV_StsNullPtr, "Null pointer to the custom element mask" ); 761 for( i = 0; i < size; i++ ) 762 element->values[i] = values[i]; 763 } 764 else 765 { 766 CvMat el_hdr = cvMat( rows, cols, CV_32SC1, element->values ); 767 CV_CALL( CvMorphology::init_binary_element(&el_hdr, 768 shape, cvPoint(anchorX,anchorY))); 769 } 770 771 __END__; 772 773 if( cvGetErrStatus() < 0 ) 774 cvReleaseStructuringElement( &element ); 775 776 return element; 777 } 778 779 780 CV_IMPL void 781 cvReleaseStructuringElement( IplConvKernel ** element ) 782 { 783 CV_FUNCNAME( "cvReleaseStructuringElement" ); 784 785 __BEGIN__; 786 787 if( !element ) 788 CV_ERROR( CV_StsNullPtr, "" ); 789 cvFree( element ); 790 791 __END__; 792 } 793 794 795 typedef CvStatus (CV_STDCALL * CvMorphRectGetBufSizeFunc_IPP) 796 ( int width, CvSize el_size, int* bufsize ); 797 798 typedef CvStatus (CV_STDCALL * CvMorphRectFunc_IPP) 799 ( const void* src, int srcstep, void* dst, int dststep, 800 CvSize roi, CvSize el_size, CvPoint el_anchor, void* buffer ); 801 802 typedef CvStatus (CV_STDCALL * CvMorphCustomInitAllocFunc_IPP) 803 ( int width, const uchar* element, CvSize el_size, 804 CvPoint el_anchor, void** morphstate ); 805 806 typedef CvStatus (CV_STDCALL * CvMorphCustomFunc_IPP) 807 ( const void* src, int srcstep, void* dst, int dststep, 808 CvSize roi, int bordertype, void* morphstate ); 809 810 static void 811 icvMorphOp( const void* srcarr, void* dstarr, IplConvKernel* element, 812 int iterations, int mop ) 813 { 814 CvMorphology morphology; 815 void* buffer = 0; 816 int local_alloc = 0; 817 void* morphstate = 0; 818 CvMat* temp = 0; 819 820 CV_FUNCNAME( "icvMorphOp" ); 821 822 __BEGIN__; 823 824 int i, coi1 = 0, coi2 = 0; 825 CvMat srcstub, *src = (CvMat*)srcarr; 826 CvMat dststub, *dst = (CvMat*)dstarr; 827 CvMat el_hdr, *el = 0; 828 CvSize size, el_size; 829 CvPoint el_anchor; 830 int el_shape; 831 int type; 832 bool inplace; 833 834 if( !CV_IS_MAT(src) ) 835 CV_CALL( src = cvGetMat( src, &srcstub, &coi1 )); 836 837 if( src != &srcstub ) 838 { 839 srcstub = *src; 840 src = &srcstub; 841 } 842 843 if( dstarr == srcarr ) 844 dst = src; 845 else 846 { 847 CV_CALL( dst = cvGetMat( dst, &dststub, &coi2 )); 848 849 if( !CV_ARE_TYPES_EQ( src, dst )) 850 CV_ERROR( CV_StsUnmatchedFormats, "" ); 851 852 if( !CV_ARE_SIZES_EQ( src, dst )) 853 CV_ERROR( CV_StsUnmatchedSizes, "" ); 854 } 855 856 if( dst != &dststub ) 857 { 858 dststub = *dst; 859 dst = &dststub; 860 } 861 862 if( coi1 != 0 || coi2 != 0 ) 863 CV_ERROR( CV_BadCOI, "" ); 864 865 type = CV_MAT_TYPE( src->type ); 866 size = cvGetMatSize( src ); 867 inplace = src->data.ptr == dst->data.ptr; 868 869 if( iterations == 0 || (element && element->nCols == 1 && element->nRows == 1)) 870 { 871 if( src->data.ptr != dst->data.ptr ) 872 cvCopy( src, dst ); 873 EXIT; 874 } 875 876 if( element ) 877 { 878 el_size = cvSize( element->nCols, element->nRows ); 879 el_anchor = cvPoint( element->anchorX, element->anchorY ); 880 el_shape = (int)(element->nShiftR); 881 el_shape = el_shape < CV_SHAPE_CUSTOM ? el_shape : CV_SHAPE_CUSTOM; 882 } 883 else 884 { 885 el_size = cvSize(3,3); 886 el_anchor = cvPoint(1,1); 887 el_shape = CV_SHAPE_RECT; 888 } 889 890 if( el_shape == CV_SHAPE_RECT && iterations > 1 ) 891 { 892 el_size.width = 1 + (el_size.width-1)*iterations; 893 el_size.height = 1 + (el_size.height-1)*iterations; 894 el_anchor.x *= iterations; 895 el_anchor.y *= iterations; 896 iterations = 1; 897 } 898 899 if( el_shape == CV_SHAPE_RECT && icvErodeRect_GetBufSize_8u_C1R_p ) 900 { 901 CvMorphRectFunc_IPP rect_func = 0; 902 CvMorphRectGetBufSizeFunc_IPP rect_getbufsize_func = 0; 903 904 if( mop == 0 ) 905 { 906 if( type == CV_8UC1 ) 907 rect_getbufsize_func = icvErodeRect_GetBufSize_8u_C1R_p, 908 rect_func = icvErodeRect_8u_C1R_p; 909 else if( type == CV_8UC3 ) 910 rect_getbufsize_func = icvErodeRect_GetBufSize_8u_C3R_p, 911 rect_func = icvErodeRect_8u_C3R_p; 912 else if( type == CV_8UC4 ) 913 rect_getbufsize_func = icvErodeRect_GetBufSize_8u_C4R_p, 914 rect_func = icvErodeRect_8u_C4R_p; 915 else if( type == CV_16UC1 ) 916 rect_getbufsize_func = icvErodeRect_GetBufSize_16u_C1R_p, 917 rect_func = icvErodeRect_16u_C1R_p; 918 else if( type == CV_16UC3 ) 919 rect_getbufsize_func = icvErodeRect_GetBufSize_16u_C3R_p, 920 rect_func = icvErodeRect_16u_C3R_p; 921 else if( type == CV_16UC4 ) 922 rect_getbufsize_func = icvErodeRect_GetBufSize_16u_C4R_p, 923 rect_func = icvErodeRect_16u_C4R_p; 924 else if( type == CV_32FC1 ) 925 rect_getbufsize_func = icvErodeRect_GetBufSize_32f_C1R_p, 926 rect_func = icvErodeRect_32f_C1R_p; 927 else if( type == CV_32FC3 ) 928 rect_getbufsize_func = icvErodeRect_GetBufSize_32f_C3R_p, 929 rect_func = icvErodeRect_32f_C3R_p; 930 else if( type == CV_32FC4 ) 931 rect_getbufsize_func = icvErodeRect_GetBufSize_32f_C4R_p, 932 rect_func = icvErodeRect_32f_C4R_p; 933 } 934 else 935 { 936 if( type == CV_8UC1 ) 937 rect_getbufsize_func = icvDilateRect_GetBufSize_8u_C1R_p, 938 rect_func = icvDilateRect_8u_C1R_p; 939 else if( type == CV_8UC3 ) 940 rect_getbufsize_func = icvDilateRect_GetBufSize_8u_C3R_p, 941 rect_func = icvDilateRect_8u_C3R_p; 942 else if( type == CV_8UC4 ) 943 rect_getbufsize_func = icvDilateRect_GetBufSize_8u_C4R_p, 944 rect_func = icvDilateRect_8u_C4R_p; 945 else if( type == CV_16UC1 ) 946 rect_getbufsize_func = icvDilateRect_GetBufSize_16u_C1R_p, 947 rect_func = icvDilateRect_16u_C1R_p; 948 else if( type == CV_16UC3 ) 949 rect_getbufsize_func = icvDilateRect_GetBufSize_16u_C3R_p, 950 rect_func = icvDilateRect_16u_C3R_p; 951 else if( type == CV_16UC4 ) 952 rect_getbufsize_func = icvDilateRect_GetBufSize_16u_C4R_p, 953 rect_func = icvDilateRect_16u_C4R_p; 954 else if( type == CV_32FC1 ) 955 rect_getbufsize_func = icvDilateRect_GetBufSize_32f_C1R_p, 956 rect_func = icvDilateRect_32f_C1R_p; 957 else if( type == CV_32FC3 ) 958 rect_getbufsize_func = icvDilateRect_GetBufSize_32f_C3R_p, 959 rect_func = icvDilateRect_32f_C3R_p; 960 else if( type == CV_32FC4 ) 961 rect_getbufsize_func = icvDilateRect_GetBufSize_32f_C4R_p, 962 rect_func = icvDilateRect_32f_C4R_p; 963 } 964 965 if( rect_getbufsize_func && rect_func ) 966 { 967 int bufsize = 0; 968 969 CvStatus status = rect_getbufsize_func( size.width, el_size, &bufsize ); 970 if( status >= 0 && bufsize > 0 ) 971 { 972 if( bufsize < CV_MAX_LOCAL_SIZE ) 973 { 974 buffer = cvStackAlloc( bufsize ); 975 local_alloc = 1; 976 } 977 else 978 CV_CALL( buffer = cvAlloc( bufsize )); 979 } 980 981 if( status >= 0 ) 982 { 983 int src_step, dst_step = dst->step ? dst->step : CV_STUB_STEP; 984 985 if( inplace ) 986 { 987 CV_CALL( temp = cvCloneMat( dst )); 988 src = temp; 989 } 990 src_step = src->step ? src->step : CV_STUB_STEP; 991 992 status = rect_func( src->data.ptr, src_step, dst->data.ptr, 993 dst_step, size, el_size, el_anchor, buffer ); 994 } 995 996 if( status >= 0 ) 997 EXIT; 998 } 999 } 1000 else if( el_shape == CV_SHAPE_CUSTOM && icvMorphInitAlloc_8u_C1R_p && icvMorphFree_p && 1001 src->data.ptr != dst->data.ptr ) 1002 { 1003 CvMorphCustomFunc_IPP custom_func = 0; 1004 CvMorphCustomInitAllocFunc_IPP custom_initalloc_func = 0; 1005 const int bordertype = 1; // replication border 1006 1007 if( type == CV_8UC1 ) 1008 custom_initalloc_func = icvMorphInitAlloc_8u_C1R_p, 1009 custom_func = mop == 0 ? icvErode_8u_C1R_p : icvDilate_8u_C1R_p; 1010 else if( type == CV_8UC3 ) 1011 custom_initalloc_func = icvMorphInitAlloc_8u_C3R_p, 1012 custom_func = mop == 0 ? icvErode_8u_C3R_p : icvDilate_8u_C3R_p; 1013 else if( type == CV_8UC4 ) 1014 custom_initalloc_func = icvMorphInitAlloc_8u_C4R_p, 1015 custom_func = mop == 0 ? icvErode_8u_C4R_p : icvDilate_8u_C4R_p; 1016 else if( type == CV_16UC1 ) 1017 custom_initalloc_func = icvMorphInitAlloc_16u_C1R_p, 1018 custom_func = mop == 0 ? icvErode_16u_C1R_p : icvDilate_16u_C1R_p; 1019 else if( type == CV_16UC3 ) 1020 custom_initalloc_func = icvMorphInitAlloc_16u_C3R_p, 1021 custom_func = mop == 0 ? icvErode_16u_C3R_p : icvDilate_16u_C3R_p; 1022 else if( type == CV_16UC4 ) 1023 custom_initalloc_func = icvMorphInitAlloc_16u_C4R_p, 1024 custom_func = mop == 0 ? icvErode_16u_C4R_p : icvDilate_16u_C4R_p; 1025 else if( type == CV_32FC1 ) 1026 custom_initalloc_func = icvMorphInitAlloc_32f_C1R_p, 1027 custom_func = mop == 0 ? icvErode_32f_C1R_p : icvDilate_32f_C1R_p; 1028 else if( type == CV_32FC3 ) 1029 custom_initalloc_func = icvMorphInitAlloc_32f_C3R_p, 1030 custom_func = mop == 0 ? icvErode_32f_C3R_p : icvDilate_32f_C3R_p; 1031 else if( type == CV_32FC4 ) 1032 custom_initalloc_func = icvMorphInitAlloc_32f_C4R_p, 1033 custom_func = mop == 0 ? icvErode_32f_C4R_p : icvDilate_32f_C4R_p; 1034 1035 if( custom_initalloc_func && custom_func ) 1036 { 1037 uchar *src_ptr, *dst_ptr = dst->data.ptr; 1038 int src_step, dst_step = dst->step ? dst->step : CV_STUB_STEP; 1039 int el_len = el_size.width*el_size.height; 1040 uchar* el_mask = (uchar*)cvStackAlloc( el_len ); 1041 CvStatus status; 1042 1043 for( i = 0; i < el_len; i++ ) 1044 el_mask[i] = (uchar)(element->values[i] != 0); 1045 1046 status = custom_initalloc_func( size.width, el_mask, el_size, 1047 el_anchor, &morphstate ); 1048 1049 if( status >= 0 && (inplace || iterations > 1) ) 1050 { 1051 CV_CALL( temp = cvCloneMat( src )); 1052 src = temp; 1053 } 1054 1055 src_ptr = src->data.ptr; 1056 src_step = src->step ? src->step : CV_STUB_STEP; 1057 1058 for( i = 0; i < iterations && status >= 0 && morphstate; i++ ) 1059 { 1060 uchar* t_ptr; 1061 int t_step; 1062 status = custom_func( src_ptr, src_step, dst_ptr, dst_step, 1063 size, bordertype, morphstate ); 1064 CV_SWAP( src_ptr, dst_ptr, t_ptr ); 1065 CV_SWAP( src_step, dst_step, t_step ); 1066 if( i == 0 && temp ) 1067 { 1068 dst_ptr = temp->data.ptr; 1069 dst_step = temp->step ? temp->step : CV_STUB_STEP; 1070 } 1071 } 1072 1073 if( status >= 0 ) 1074 { 1075 if( iterations % 2 == 0 ) 1076 cvCopy( temp, dst ); 1077 EXIT; 1078 } 1079 } 1080 } 1081 1082 if( el_shape != CV_SHAPE_RECT ) 1083 { 1084 el_hdr = cvMat( element->nRows, element->nCols, CV_32SC1, element->values ); 1085 el = &el_hdr; 1086 el_shape = CV_SHAPE_CUSTOM; 1087 } 1088 1089 CV_CALL( morphology.init( mop, src->cols, src->type, 1090 el_shape, el, el_size, el_anchor )); 1091 1092 for( i = 0; i < iterations; i++ ) 1093 { 1094 CV_CALL( morphology.process( src, dst )); 1095 src = dst; 1096 } 1097 1098 __END__; 1099 1100 if( !local_alloc ) 1101 cvFree( &buffer ); 1102 if( morphstate ) 1103 icvMorphFree_p( morphstate ); 1104 cvReleaseMat( &temp ); 1105 } 1106 1107 1108 CV_IMPL void 1109 cvErode( const void* src, void* dst, IplConvKernel* element, int iterations ) 1110 { 1111 icvMorphOp( src, dst, element, iterations, 0 ); 1112 } 1113 1114 1115 CV_IMPL void 1116 cvDilate( const void* src, void* dst, IplConvKernel* element, int iterations ) 1117 { 1118 icvMorphOp( src, dst, element, iterations, 1 ); 1119 } 1120 1121 1122 CV_IMPL void 1123 cvMorphologyEx( const void* src, void* dst, 1124 void* temp, IplConvKernel* element, int op, int iterations ) 1125 { 1126 CV_FUNCNAME( "cvMorhologyEx" ); 1127 1128 __BEGIN__; 1129 1130 if( (op == CV_MOP_GRADIENT || 1131 ((op == CV_MOP_TOPHAT || op == CV_MOP_BLACKHAT) && src == dst)) && temp == 0 ) 1132 CV_ERROR( CV_HeaderIsNull, "temp image required" ); 1133 1134 if( temp == src || temp == dst ) 1135 CV_ERROR( CV_HeaderIsNull, "temp image is equal to src or dst" ); 1136 1137 switch (op) 1138 { 1139 case CV_MOP_OPEN: 1140 CV_CALL( cvErode( src, dst, element, iterations )); 1141 CV_CALL( cvDilate( dst, dst, element, iterations )); 1142 break; 1143 case CV_MOP_CLOSE: 1144 CV_CALL( cvDilate( src, dst, element, iterations )); 1145 CV_CALL( cvErode( dst, dst, element, iterations )); 1146 break; 1147 case CV_MOP_GRADIENT: 1148 CV_CALL( cvErode( src, temp, element, iterations )); 1149 CV_CALL( cvDilate( src, dst, element, iterations )); 1150 CV_CALL( cvSub( dst, temp, dst )); 1151 break; 1152 case CV_MOP_TOPHAT: 1153 if( src != dst ) 1154 temp = dst; 1155 CV_CALL( cvErode( src, temp, element, iterations )); 1156 CV_CALL( cvDilate( temp, temp, element, iterations )); 1157 CV_CALL( cvSub( src, temp, dst )); 1158 break; 1159 case CV_MOP_BLACKHAT: 1160 if( src != dst ) 1161 temp = dst; 1162 CV_CALL( cvDilate( src, temp, element, iterations )); 1163 CV_CALL( cvErode( temp, temp, element, iterations )); 1164 CV_CALL( cvSub( temp, src, dst )); 1165 break; 1166 default: 1167 CV_ERROR( CV_StsBadArg, "unknown morphological operation" ); 1168 } 1169 1170 __END__; 1171 } 1172 1173 /* End of file. */ 1174