Update the changelog
[opencv] / cv / src / cvmorph.cpp
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. */