347e80bfe63dc417928c57ed66f329c38001a8a1
[profile/ivi/opencv.git] / modules / imgproc / src / morph.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 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 #include "precomp.hpp"
44 #include <limits.h>
45 #include "opencl_kernels_imgproc.hpp"
46
47 /****************************************************************************************\
48                      Basic Morphological Operations: Erosion & Dilation
49 \****************************************************************************************/
50
51 namespace cv
52 {
53
54 template<typename T> struct MinOp
55 {
56     typedef T type1;
57     typedef T type2;
58     typedef T rtype;
59     T operator ()(const T a, const T b) const { return std::min(a, b); }
60 };
61
62 template<typename T> struct MaxOp
63 {
64     typedef T type1;
65     typedef T type2;
66     typedef T rtype;
67     T operator ()(const T a, const T b) const { return std::max(a, b); }
68 };
69
70 #undef CV_MIN_8U
71 #undef CV_MAX_8U
72 #define CV_MIN_8U(a,b)       ((a) - CV_FAST_CAST_8U((a) - (b)))
73 #define CV_MAX_8U(a,b)       ((a) + CV_FAST_CAST_8U((b) - (a)))
74
75 template<> inline uchar MinOp<uchar>::operator ()(const uchar a, const uchar b) const { return CV_MIN_8U(a, b); }
76 template<> inline uchar MaxOp<uchar>::operator ()(const uchar a, const uchar b) const { return CV_MAX_8U(a, b); }
77
78 struct MorphRowNoVec
79 {
80     MorphRowNoVec(int, int) {}
81     int operator()(const uchar*, uchar*, int, int) const { return 0; }
82 };
83
84 struct MorphColumnNoVec
85 {
86     MorphColumnNoVec(int, int) {}
87     int operator()(const uchar**, uchar*, int, int, int) const { return 0; }
88 };
89
90 struct MorphNoVec
91 {
92     int operator()(uchar**, int, uchar*, int) const { return 0; }
93 };
94
95 #if CV_SSE2
96
97 template<class VecUpdate> struct MorphRowIVec
98 {
99     enum { ESZ = VecUpdate::ESZ };
100
101     MorphRowIVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {}
102     int operator()(const uchar* src, uchar* dst, int width, int cn) const
103     {
104         if( !checkHardwareSupport(CV_CPU_SSE2) )
105             return 0;
106
107         cn *= ESZ;
108         int i, k, _ksize = ksize*cn;
109         width = (width & -4)*cn;
110         VecUpdate updateOp;
111
112         for( i = 0; i <= width - 16; i += 16 )
113         {
114             __m128i s = _mm_loadu_si128((const __m128i*)(src + i));
115             for( k = cn; k < _ksize; k += cn )
116             {
117                 __m128i x = _mm_loadu_si128((const __m128i*)(src + i + k));
118                 s = updateOp(s, x);
119             }
120             _mm_storeu_si128((__m128i*)(dst + i), s);
121         }
122
123         for( ; i < width; i += 4 )
124         {
125             __m128i s = _mm_cvtsi32_si128(*(const int*)(src + i));
126             for( k = cn; k < _ksize; k += cn )
127             {
128                 __m128i x = _mm_cvtsi32_si128(*(const int*)(src + i + k));
129                 s = updateOp(s, x);
130             }
131             *(int*)(dst + i) = _mm_cvtsi128_si32(s);
132         }
133
134         return i/ESZ;
135     }
136
137     int ksize, anchor;
138 };
139
140
141 template<class VecUpdate> struct MorphRowFVec
142 {
143     MorphRowFVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {}
144     int operator()(const uchar* src, uchar* dst, int width, int cn) const
145     {
146         if( !checkHardwareSupport(CV_CPU_SSE) )
147             return 0;
148
149         int i, k, _ksize = ksize*cn;
150         width = (width & -4)*cn;
151         VecUpdate updateOp;
152
153         for( i = 0; i < width; i += 4 )
154         {
155             __m128 s = _mm_loadu_ps((const float*)src + i);
156             for( k = cn; k < _ksize; k += cn )
157             {
158                 __m128 x = _mm_loadu_ps((const float*)src + i + k);
159                 s = updateOp(s, x);
160             }
161             _mm_storeu_ps((float*)dst + i, s);
162         }
163
164         return i;
165     }
166
167     int ksize, anchor;
168 };
169
170
171 template<class VecUpdate> struct MorphColumnIVec
172 {
173     enum { ESZ = VecUpdate::ESZ };
174
175     MorphColumnIVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {}
176     int operator()(const uchar** src, uchar* dst, int dststep, int count, int width) const
177     {
178         if( !checkHardwareSupport(CV_CPU_SSE2) )
179             return 0;
180
181         int i = 0, k, _ksize = ksize;
182         width *= ESZ;
183         VecUpdate updateOp;
184
185         for( i = 0; i < count + ksize - 1; i++ )
186             CV_Assert( ((size_t)src[i] & 15) == 0 );
187
188         for( ; _ksize > 1 && count > 1; count -= 2, dst += dststep*2, src += 2 )
189         {
190             for( i = 0; i <= width - 32; i += 32 )
191             {
192                 const uchar* sptr = src[1] + i;
193                 __m128i s0 = _mm_load_si128((const __m128i*)sptr);
194                 __m128i s1 = _mm_load_si128((const __m128i*)(sptr + 16));
195                 __m128i x0, x1;
196
197                 for( k = 2; k < _ksize; k++ )
198                 {
199                     sptr = src[k] + i;
200                     x0 = _mm_load_si128((const __m128i*)sptr);
201                     x1 = _mm_load_si128((const __m128i*)(sptr + 16));
202                     s0 = updateOp(s0, x0);
203                     s1 = updateOp(s1, x1);
204                 }
205
206                 sptr = src[0] + i;
207                 x0 = _mm_load_si128((const __m128i*)sptr);
208                 x1 = _mm_load_si128((const __m128i*)(sptr + 16));
209                 _mm_storeu_si128((__m128i*)(dst + i), updateOp(s0, x0));
210                 _mm_storeu_si128((__m128i*)(dst + i + 16), updateOp(s1, x1));
211
212                 sptr = src[k] + i;
213                 x0 = _mm_load_si128((const __m128i*)sptr);
214                 x1 = _mm_load_si128((const __m128i*)(sptr + 16));
215                 _mm_storeu_si128((__m128i*)(dst + dststep + i), updateOp(s0, x0));
216                 _mm_storeu_si128((__m128i*)(dst + dststep + i + 16), updateOp(s1, x1));
217             }
218
219             for( ; i <= width - 8; i += 8 )
220             {
221                 __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[1] + i)), x0;
222
223                 for( k = 2; k < _ksize; k++ )
224                 {
225                     x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i));
226                     s0 = updateOp(s0, x0);
227                 }
228
229                 x0 = _mm_loadl_epi64((const __m128i*)(src[0] + i));
230                 _mm_storel_epi64((__m128i*)(dst + i), updateOp(s0, x0));
231                 x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i));
232                 _mm_storel_epi64((__m128i*)(dst + dststep + i), updateOp(s0, x0));
233             }
234         }
235
236         for( ; count > 0; count--, dst += dststep, src++ )
237         {
238             for( i = 0; i <= width - 32; i += 32 )
239             {
240                 const uchar* sptr = src[0] + i;
241                 __m128i s0 = _mm_load_si128((const __m128i*)sptr);
242                 __m128i s1 = _mm_load_si128((const __m128i*)(sptr + 16));
243                 __m128i x0, x1;
244
245                 for( k = 1; k < _ksize; k++ )
246                 {
247                     sptr = src[k] + i;
248                     x0 = _mm_load_si128((const __m128i*)sptr);
249                     x1 = _mm_load_si128((const __m128i*)(sptr + 16));
250                     s0 = updateOp(s0, x0);
251                     s1 = updateOp(s1, x1);
252                 }
253                 _mm_storeu_si128((__m128i*)(dst + i), s0);
254                 _mm_storeu_si128((__m128i*)(dst + i + 16), s1);
255             }
256
257             for( ; i <= width - 8; i += 8 )
258             {
259                 __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[0] + i)), x0;
260
261                 for( k = 1; k < _ksize; k++ )
262                 {
263                     x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i));
264                     s0 = updateOp(s0, x0);
265                 }
266                 _mm_storel_epi64((__m128i*)(dst + i), s0);
267             }
268         }
269
270         return i/ESZ;
271     }
272
273     int ksize, anchor;
274 };
275
276
277 template<class VecUpdate> struct MorphColumnFVec
278 {
279     MorphColumnFVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {}
280     int operator()(const uchar** _src, uchar* _dst, int dststep, int count, int width) const
281     {
282         if( !checkHardwareSupport(CV_CPU_SSE) )
283             return 0;
284
285         int i = 0, k, _ksize = ksize;
286         VecUpdate updateOp;
287
288         for( i = 0; i < count + ksize - 1; i++ )
289             CV_Assert( ((size_t)_src[i] & 15) == 0 );
290
291         const float** src = (const float**)_src;
292         float* dst = (float*)_dst;
293         dststep /= sizeof(dst[0]);
294
295         for( ; _ksize > 1 && count > 1; count -= 2, dst += dststep*2, src += 2 )
296         {
297             for( i = 0; i <= width - 16; i += 16 )
298             {
299                 const float* sptr = src[1] + i;
300                 __m128 s0 = _mm_load_ps(sptr);
301                 __m128 s1 = _mm_load_ps(sptr + 4);
302                 __m128 s2 = _mm_load_ps(sptr + 8);
303                 __m128 s3 = _mm_load_ps(sptr + 12);
304                 __m128 x0, x1, x2, x3;
305
306                 for( k = 2; k < _ksize; k++ )
307                 {
308                     sptr = src[k] + i;
309                     x0 = _mm_load_ps(sptr);
310                     x1 = _mm_load_ps(sptr + 4);
311                     s0 = updateOp(s0, x0);
312                     s1 = updateOp(s1, x1);
313                     x2 = _mm_load_ps(sptr + 8);
314                     x3 = _mm_load_ps(sptr + 12);
315                     s2 = updateOp(s2, x2);
316                     s3 = updateOp(s3, x3);
317                 }
318
319                 sptr = src[0] + i;
320                 x0 = _mm_load_ps(sptr);
321                 x1 = _mm_load_ps(sptr + 4);
322                 x2 = _mm_load_ps(sptr + 8);
323                 x3 = _mm_load_ps(sptr + 12);
324                 _mm_storeu_ps(dst + i, updateOp(s0, x0));
325                 _mm_storeu_ps(dst + i + 4, updateOp(s1, x1));
326                 _mm_storeu_ps(dst + i + 8, updateOp(s2, x2));
327                 _mm_storeu_ps(dst + i + 12, updateOp(s3, x3));
328
329                 sptr = src[k] + i;
330                 x0 = _mm_load_ps(sptr);
331                 x1 = _mm_load_ps(sptr + 4);
332                 x2 = _mm_load_ps(sptr + 8);
333                 x3 = _mm_load_ps(sptr + 12);
334                 _mm_storeu_ps(dst + dststep + i, updateOp(s0, x0));
335                 _mm_storeu_ps(dst + dststep + i + 4, updateOp(s1, x1));
336                 _mm_storeu_ps(dst + dststep + i + 8, updateOp(s2, x2));
337                 _mm_storeu_ps(dst + dststep + i + 12, updateOp(s3, x3));
338             }
339
340             for( ; i <= width - 4; i += 4 )
341             {
342                 __m128 s0 = _mm_load_ps(src[1] + i), x0;
343
344                 for( k = 2; k < _ksize; k++ )
345                 {
346                     x0 = _mm_load_ps(src[k] + i);
347                     s0 = updateOp(s0, x0);
348                 }
349
350                 x0 = _mm_load_ps(src[0] + i);
351                 _mm_storeu_ps(dst + i, updateOp(s0, x0));
352                 x0 = _mm_load_ps(src[k] + i);
353                 _mm_storeu_ps(dst + dststep + i, updateOp(s0, x0));
354             }
355         }
356
357         for( ; count > 0; count--, dst += dststep, src++ )
358         {
359             for( i = 0; i <= width - 16; i += 16 )
360             {
361                 const float* sptr = src[0] + i;
362                 __m128 s0 = _mm_load_ps(sptr);
363                 __m128 s1 = _mm_load_ps(sptr + 4);
364                 __m128 s2 = _mm_load_ps(sptr + 8);
365                 __m128 s3 = _mm_load_ps(sptr + 12);
366                 __m128 x0, x1, x2, x3;
367
368                 for( k = 1; k < _ksize; k++ )
369                 {
370                     sptr = src[k] + i;
371                     x0 = _mm_load_ps(sptr);
372                     x1 = _mm_load_ps(sptr + 4);
373                     s0 = updateOp(s0, x0);
374                     s1 = updateOp(s1, x1);
375                     x2 = _mm_load_ps(sptr + 8);
376                     x3 = _mm_load_ps(sptr + 12);
377                     s2 = updateOp(s2, x2);
378                     s3 = updateOp(s3, x3);
379                 }
380                 _mm_storeu_ps(dst + i, s0);
381                 _mm_storeu_ps(dst + i + 4, s1);
382                 _mm_storeu_ps(dst + i + 8, s2);
383                 _mm_storeu_ps(dst + i + 12, s3);
384             }
385
386             for( i = 0; i <= width - 4; i += 4 )
387             {
388                 __m128 s0 = _mm_load_ps(src[0] + i), x0;
389                 for( k = 1; k < _ksize; k++ )
390                 {
391                     x0 = _mm_load_ps(src[k] + i);
392                     s0 = updateOp(s0, x0);
393                 }
394                 _mm_storeu_ps(dst + i, s0);
395             }
396         }
397
398         return i;
399     }
400
401     int ksize, anchor;
402 };
403
404
405 template<class VecUpdate> struct MorphIVec
406 {
407     enum { ESZ = VecUpdate::ESZ };
408
409     int operator()(uchar** src, int nz, uchar* dst, int width) const
410     {
411         if( !checkHardwareSupport(CV_CPU_SSE2) )
412             return 0;
413
414         int i, k;
415         width *= ESZ;
416         VecUpdate updateOp;
417
418         for( i = 0; i <= width - 32; i += 32 )
419         {
420             const uchar* sptr = src[0] + i;
421             __m128i s0 = _mm_loadu_si128((const __m128i*)sptr);
422             __m128i s1 = _mm_loadu_si128((const __m128i*)(sptr + 16));
423             __m128i x0, x1;
424
425             for( k = 1; k < nz; k++ )
426             {
427                 sptr = src[k] + i;
428                 x0 = _mm_loadu_si128((const __m128i*)sptr);
429                 x1 = _mm_loadu_si128((const __m128i*)(sptr + 16));
430                 s0 = updateOp(s0, x0);
431                 s1 = updateOp(s1, x1);
432             }
433             _mm_storeu_si128((__m128i*)(dst + i), s0);
434             _mm_storeu_si128((__m128i*)(dst + i + 16), s1);
435         }
436
437         for( ; i <= width - 8; i += 8 )
438         {
439             __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[0] + i)), x0;
440
441             for( k = 1; k < nz; k++ )
442             {
443                 x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i));
444                 s0 = updateOp(s0, x0);
445             }
446             _mm_storel_epi64((__m128i*)(dst + i), s0);
447         }
448
449         return i/ESZ;
450     }
451 };
452
453
454 template<class VecUpdate> struct MorphFVec
455 {
456     int operator()(uchar** _src, int nz, uchar* _dst, int width) const
457     {
458         if( !checkHardwareSupport(CV_CPU_SSE) )
459             return 0;
460
461         const float** src = (const float**)_src;
462         float* dst = (float*)_dst;
463         int i, k;
464         VecUpdate updateOp;
465
466         for( i = 0; i <= width - 16; i += 16 )
467         {
468             const float* sptr = src[0] + i;
469             __m128 s0 = _mm_loadu_ps(sptr);
470             __m128 s1 = _mm_loadu_ps(sptr + 4);
471             __m128 s2 = _mm_loadu_ps(sptr + 8);
472             __m128 s3 = _mm_loadu_ps(sptr + 12);
473             __m128 x0, x1, x2, x3;
474
475             for( k = 1; k < nz; k++ )
476             {
477                 sptr = src[k] + i;
478                 x0 = _mm_loadu_ps(sptr);
479                 x1 = _mm_loadu_ps(sptr + 4);
480                 x2 = _mm_loadu_ps(sptr + 8);
481                 x3 = _mm_loadu_ps(sptr + 12);
482                 s0 = updateOp(s0, x0);
483                 s1 = updateOp(s1, x1);
484                 s2 = updateOp(s2, x2);
485                 s3 = updateOp(s3, x3);
486             }
487             _mm_storeu_ps(dst + i, s0);
488             _mm_storeu_ps(dst + i + 4, s1);
489             _mm_storeu_ps(dst + i + 8, s2);
490             _mm_storeu_ps(dst + i + 12, s3);
491         }
492
493         for( ; i <= width - 4; i += 4 )
494         {
495             __m128 s0 = _mm_loadu_ps(src[0] + i), x0;
496
497             for( k = 1; k < nz; k++ )
498             {
499                 x0 = _mm_loadu_ps(src[k] + i);
500                 s0 = updateOp(s0, x0);
501             }
502             _mm_storeu_ps(dst + i, s0);
503         }
504
505         for( ; i < width; i++ )
506         {
507             __m128 s0 = _mm_load_ss(src[0] + i), x0;
508
509             for( k = 1; k < nz; k++ )
510             {
511                 x0 = _mm_load_ss(src[k] + i);
512                 s0 = updateOp(s0, x0);
513             }
514             _mm_store_ss(dst + i, s0);
515         }
516
517         return i;
518     }
519 };
520
521 struct VMin8u
522 {
523     enum { ESZ = 1 };
524     __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_min_epu8(a,b); }
525 };
526 struct VMax8u
527 {
528     enum { ESZ = 1 };
529     __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_max_epu8(a,b); }
530 };
531 struct VMin16u
532 {
533     enum { ESZ = 2 };
534     __m128i operator()(const __m128i& a, const __m128i& b) const
535     { return _mm_subs_epu16(a,_mm_subs_epu16(a,b)); }
536 };
537 struct VMax16u
538 {
539     enum { ESZ = 2 };
540     __m128i operator()(const __m128i& a, const __m128i& b) const
541     { return _mm_adds_epu16(_mm_subs_epu16(a,b), b); }
542 };
543 struct VMin16s
544 {
545     enum { ESZ = 2 };
546     __m128i operator()(const __m128i& a, const __m128i& b) const
547     { return _mm_min_epi16(a, b); }
548 };
549 struct VMax16s
550 {
551     enum { ESZ = 2 };
552     __m128i operator()(const __m128i& a, const __m128i& b) const
553     { return _mm_max_epi16(a, b); }
554 };
555 struct VMin32f { __m128 operator()(const __m128& a, const __m128& b) const { return _mm_min_ps(a,b); }};
556 struct VMax32f { __m128 operator()(const __m128& a, const __m128& b) const { return _mm_max_ps(a,b); }};
557
558 typedef MorphRowIVec<VMin8u> ErodeRowVec8u;
559 typedef MorphRowIVec<VMax8u> DilateRowVec8u;
560 typedef MorphRowIVec<VMin16u> ErodeRowVec16u;
561 typedef MorphRowIVec<VMax16u> DilateRowVec16u;
562 typedef MorphRowIVec<VMin16s> ErodeRowVec16s;
563 typedef MorphRowIVec<VMax16s> DilateRowVec16s;
564 typedef MorphRowFVec<VMin32f> ErodeRowVec32f;
565 typedef MorphRowFVec<VMax32f> DilateRowVec32f;
566
567 typedef MorphColumnIVec<VMin8u> ErodeColumnVec8u;
568 typedef MorphColumnIVec<VMax8u> DilateColumnVec8u;
569 typedef MorphColumnIVec<VMin16u> ErodeColumnVec16u;
570 typedef MorphColumnIVec<VMax16u> DilateColumnVec16u;
571 typedef MorphColumnIVec<VMin16s> ErodeColumnVec16s;
572 typedef MorphColumnIVec<VMax16s> DilateColumnVec16s;
573 typedef MorphColumnFVec<VMin32f> ErodeColumnVec32f;
574 typedef MorphColumnFVec<VMax32f> DilateColumnVec32f;
575
576 typedef MorphIVec<VMin8u> ErodeVec8u;
577 typedef MorphIVec<VMax8u> DilateVec8u;
578 typedef MorphIVec<VMin16u> ErodeVec16u;
579 typedef MorphIVec<VMax16u> DilateVec16u;
580 typedef MorphIVec<VMin16s> ErodeVec16s;
581 typedef MorphIVec<VMax16s> DilateVec16s;
582 typedef MorphFVec<VMin32f> ErodeVec32f;
583 typedef MorphFVec<VMax32f> DilateVec32f;
584
585 #else
586
587 #ifdef HAVE_TEGRA_OPTIMIZATION
588 using tegra::ErodeRowVec8u;
589 using tegra::DilateRowVec8u;
590
591 using tegra::ErodeColumnVec8u;
592 using tegra::DilateColumnVec8u;
593 #else
594 typedef MorphRowNoVec ErodeRowVec8u;
595 typedef MorphRowNoVec DilateRowVec8u;
596
597 typedef MorphColumnNoVec ErodeColumnVec8u;
598 typedef MorphColumnNoVec DilateColumnVec8u;
599 #endif
600
601 typedef MorphRowNoVec ErodeRowVec16u;
602 typedef MorphRowNoVec DilateRowVec16u;
603 typedef MorphRowNoVec ErodeRowVec16s;
604 typedef MorphRowNoVec DilateRowVec16s;
605 typedef MorphRowNoVec ErodeRowVec32f;
606 typedef MorphRowNoVec DilateRowVec32f;
607
608 typedef MorphColumnNoVec ErodeColumnVec16u;
609 typedef MorphColumnNoVec DilateColumnVec16u;
610 typedef MorphColumnNoVec ErodeColumnVec16s;
611 typedef MorphColumnNoVec DilateColumnVec16s;
612 typedef MorphColumnNoVec ErodeColumnVec32f;
613 typedef MorphColumnNoVec DilateColumnVec32f;
614
615 typedef MorphNoVec ErodeVec8u;
616 typedef MorphNoVec DilateVec8u;
617 typedef MorphNoVec ErodeVec16u;
618 typedef MorphNoVec DilateVec16u;
619 typedef MorphNoVec ErodeVec16s;
620 typedef MorphNoVec DilateVec16s;
621 typedef MorphNoVec ErodeVec32f;
622 typedef MorphNoVec DilateVec32f;
623
624 #endif
625
626 typedef MorphRowNoVec ErodeRowVec64f;
627 typedef MorphRowNoVec DilateRowVec64f;
628 typedef MorphColumnNoVec ErodeColumnVec64f;
629 typedef MorphColumnNoVec DilateColumnVec64f;
630 typedef MorphNoVec ErodeVec64f;
631 typedef MorphNoVec DilateVec64f;
632
633
634 template<class Op, class VecOp> struct MorphRowFilter : public BaseRowFilter
635 {
636     typedef typename Op::rtype T;
637
638     MorphRowFilter( int _ksize, int _anchor ) : vecOp(_ksize, _anchor)
639     {
640         ksize = _ksize;
641         anchor = _anchor;
642     }
643
644     void operator()(const uchar* src, uchar* dst, int width, int cn)
645     {
646         int i, j, k, _ksize = ksize*cn;
647         const T* S = (const T*)src;
648         Op op;
649         T* D = (T*)dst;
650
651         if( _ksize == cn )
652         {
653             for( i = 0; i < width*cn; i++ )
654                 D[i] = S[i];
655             return;
656         }
657
658         int i0 = vecOp(src, dst, width, cn);
659         width *= cn;
660
661         for( k = 0; k < cn; k++, S++, D++ )
662         {
663             for( i = i0; i <= width - cn*2; i += cn*2 )
664             {
665                 const T* s = S + i;
666                 T m = s[cn];
667                 for( j = cn*2; j < _ksize; j += cn )
668                     m = op(m, s[j]);
669                 D[i] = op(m, s[0]);
670                 D[i+cn] = op(m, s[j]);
671             }
672
673             for( ; i < width; i += cn )
674             {
675                 const T* s = S + i;
676                 T m = s[0];
677                 for( j = cn; j < _ksize; j += cn )
678                     m = op(m, s[j]);
679                 D[i] = m;
680             }
681         }
682     }
683
684     VecOp vecOp;
685 };
686
687
688 template<class Op, class VecOp> struct MorphColumnFilter : public BaseColumnFilter
689 {
690     typedef typename Op::rtype T;
691
692     MorphColumnFilter( int _ksize, int _anchor ) : vecOp(_ksize, _anchor)
693     {
694         ksize = _ksize;
695         anchor = _anchor;
696     }
697
698     void operator()(const uchar** _src, uchar* dst, int dststep, int count, int width)
699     {
700         int i, k, _ksize = ksize;
701         const T** src = (const T**)_src;
702         T* D = (T*)dst;
703         Op op;
704
705         int i0 = vecOp(_src, dst, dststep, count, width);
706         dststep /= sizeof(D[0]);
707
708         for( ; _ksize > 1 && count > 1; count -= 2, D += dststep*2, src += 2 )
709         {
710             i = i0;
711             #if CV_ENABLE_UNROLLED
712             for( ; i <= width - 4; i += 4 )
713             {
714                 const T* sptr = src[1] + i;
715                 T s0 = sptr[0], s1 = sptr[1], s2 = sptr[2], s3 = sptr[3];
716
717                 for( k = 2; k < _ksize; k++ )
718                 {
719                     sptr = src[k] + i;
720                     s0 = op(s0, sptr[0]); s1 = op(s1, sptr[1]);
721                     s2 = op(s2, sptr[2]); s3 = op(s3, sptr[3]);
722                 }
723
724                 sptr = src[0] + i;
725                 D[i] = op(s0, sptr[0]);
726                 D[i+1] = op(s1, sptr[1]);
727                 D[i+2] = op(s2, sptr[2]);
728                 D[i+3] = op(s3, sptr[3]);
729
730                 sptr = src[k] + i;
731                 D[i+dststep] = op(s0, sptr[0]);
732                 D[i+dststep+1] = op(s1, sptr[1]);
733                 D[i+dststep+2] = op(s2, sptr[2]);
734                 D[i+dststep+3] = op(s3, sptr[3]);
735             }
736             #endif
737             for( ; i < width; i++ )
738             {
739                 T s0 = src[1][i];
740
741                 for( k = 2; k < _ksize; k++ )
742                     s0 = op(s0, src[k][i]);
743
744                 D[i] = op(s0, src[0][i]);
745                 D[i+dststep] = op(s0, src[k][i]);
746             }
747         }
748
749         for( ; count > 0; count--, D += dststep, src++ )
750         {
751             i = i0;
752             #if CV_ENABLE_UNROLLED
753             for( ; i <= width - 4; i += 4 )
754             {
755                 const T* sptr = src[0] + i;
756                 T s0 = sptr[0], s1 = sptr[1], s2 = sptr[2], s3 = sptr[3];
757
758                 for( k = 1; k < _ksize; k++ )
759                 {
760                     sptr = src[k] + i;
761                     s0 = op(s0, sptr[0]); s1 = op(s1, sptr[1]);
762                     s2 = op(s2, sptr[2]); s3 = op(s3, sptr[3]);
763                 }
764
765                 D[i] = s0; D[i+1] = s1;
766                 D[i+2] = s2; D[i+3] = s3;
767             }
768             #endif
769             for( ; i < width; i++ )
770             {
771                 T s0 = src[0][i];
772                 for( k = 1; k < _ksize; k++ )
773                     s0 = op(s0, src[k][i]);
774                 D[i] = s0;
775             }
776         }
777     }
778
779     VecOp vecOp;
780 };
781
782
783 template<class Op, class VecOp> struct MorphFilter : BaseFilter
784 {
785     typedef typename Op::rtype T;
786
787     MorphFilter( const Mat& _kernel, Point _anchor )
788     {
789         anchor = _anchor;
790         ksize = _kernel.size();
791         CV_Assert( _kernel.type() == CV_8U );
792
793         std::vector<uchar> coeffs; // we do not really the values of non-zero
794         // kernel elements, just their locations
795         preprocess2DKernel( _kernel, coords, coeffs );
796         ptrs.resize( coords.size() );
797     }
798
799     void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn)
800     {
801         const Point* pt = &coords[0];
802         const T** kp = (const T**)&ptrs[0];
803         int i, k, nz = (int)coords.size();
804         Op op;
805
806         width *= cn;
807         for( ; count > 0; count--, dst += dststep, src++ )
808         {
809             T* D = (T*)dst;
810
811             for( k = 0; k < nz; k++ )
812                 kp[k] = (const T*)src[pt[k].y] + pt[k].x*cn;
813
814             i = vecOp(&ptrs[0], nz, dst, width);
815             #if CV_ENABLE_UNROLLED
816             for( ; i <= width - 4; i += 4 )
817             {
818                 const T* sptr = kp[0] + i;
819                 T s0 = sptr[0], s1 = sptr[1], s2 = sptr[2], s3 = sptr[3];
820
821                 for( k = 1; k < nz; k++ )
822                 {
823                     sptr = kp[k] + i;
824                     s0 = op(s0, sptr[0]); s1 = op(s1, sptr[1]);
825                     s2 = op(s2, sptr[2]); s3 = op(s3, sptr[3]);
826                 }
827
828                 D[i] = s0; D[i+1] = s1;
829                 D[i+2] = s2; D[i+3] = s3;
830             }
831             #endif
832             for( ; i < width; i++ )
833             {
834                 T s0 = kp[0][i];
835                 for( k = 1; k < nz; k++ )
836                     s0 = op(s0, kp[k][i]);
837                 D[i] = s0;
838             }
839         }
840     }
841
842     std::vector<Point> coords;
843     std::vector<uchar*> ptrs;
844     VecOp vecOp;
845 };
846
847 }
848
849 /////////////////////////////////// External Interface /////////////////////////////////////
850
851 cv::Ptr<cv::BaseRowFilter> cv::getMorphologyRowFilter(int op, int type, int ksize, int anchor)
852 {
853     int depth = CV_MAT_DEPTH(type);
854     if( anchor < 0 )
855         anchor = ksize/2;
856     CV_Assert( op == MORPH_ERODE || op == MORPH_DILATE );
857     if( op == MORPH_ERODE )
858     {
859         if( depth == CV_8U )
860             return makePtr<MorphRowFilter<MinOp<uchar>,
861                                       ErodeRowVec8u> >(ksize, anchor);
862         if( depth == CV_16U )
863             return makePtr<MorphRowFilter<MinOp<ushort>,
864                                       ErodeRowVec16u> >(ksize, anchor);
865         if( depth == CV_16S )
866             return makePtr<MorphRowFilter<MinOp<short>,
867                                       ErodeRowVec16s> >(ksize, anchor);
868         if( depth == CV_32F )
869             return makePtr<MorphRowFilter<MinOp<float>,
870                                       ErodeRowVec32f> >(ksize, anchor);
871         if( depth == CV_64F )
872             return makePtr<MorphRowFilter<MinOp<double>,
873                                       ErodeRowVec64f> >(ksize, anchor);
874     }
875     else
876     {
877         if( depth == CV_8U )
878             return makePtr<MorphRowFilter<MaxOp<uchar>,
879                                       DilateRowVec8u> >(ksize, anchor);
880         if( depth == CV_16U )
881             return makePtr<MorphRowFilter<MaxOp<ushort>,
882                                       DilateRowVec16u> >(ksize, anchor);
883         if( depth == CV_16S )
884             return makePtr<MorphRowFilter<MaxOp<short>,
885                                       DilateRowVec16s> >(ksize, anchor);
886         if( depth == CV_32F )
887             return makePtr<MorphRowFilter<MaxOp<float>,
888                                       DilateRowVec32f> >(ksize, anchor);
889         if( depth == CV_64F )
890             return makePtr<MorphRowFilter<MaxOp<double>,
891                                       DilateRowVec64f> >(ksize, anchor);
892     }
893
894     CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type));
895     return Ptr<BaseRowFilter>();
896 }
897
898 cv::Ptr<cv::BaseColumnFilter> cv::getMorphologyColumnFilter(int op, int type, int ksize, int anchor)
899 {
900     int depth = CV_MAT_DEPTH(type);
901     if( anchor < 0 )
902         anchor = ksize/2;
903     CV_Assert( op == MORPH_ERODE || op == MORPH_DILATE );
904     if( op == MORPH_ERODE )
905     {
906         if( depth == CV_8U )
907             return makePtr<MorphColumnFilter<MinOp<uchar>,
908                                          ErodeColumnVec8u> >(ksize, anchor);
909         if( depth == CV_16U )
910             return makePtr<MorphColumnFilter<MinOp<ushort>,
911                                          ErodeColumnVec16u> >(ksize, anchor);
912         if( depth == CV_16S )
913             return makePtr<MorphColumnFilter<MinOp<short>,
914                                          ErodeColumnVec16s> >(ksize, anchor);
915         if( depth == CV_32F )
916             return makePtr<MorphColumnFilter<MinOp<float>,
917                                          ErodeColumnVec32f> >(ksize, anchor);
918         if( depth == CV_64F )
919             return makePtr<MorphColumnFilter<MinOp<double>,
920                                          ErodeColumnVec64f> >(ksize, anchor);
921     }
922     else
923     {
924         if( depth == CV_8U )
925             return makePtr<MorphColumnFilter<MaxOp<uchar>,
926                                          DilateColumnVec8u> >(ksize, anchor);
927         if( depth == CV_16U )
928             return makePtr<MorphColumnFilter<MaxOp<ushort>,
929                                          DilateColumnVec16u> >(ksize, anchor);
930         if( depth == CV_16S )
931             return makePtr<MorphColumnFilter<MaxOp<short>,
932                                          DilateColumnVec16s> >(ksize, anchor);
933         if( depth == CV_32F )
934             return makePtr<MorphColumnFilter<MaxOp<float>,
935                                          DilateColumnVec32f> >(ksize, anchor);
936         if( depth == CV_64F )
937             return makePtr<MorphColumnFilter<MaxOp<double>,
938                                          DilateColumnVec64f> >(ksize, anchor);
939     }
940
941     CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type));
942     return Ptr<BaseColumnFilter>();
943 }
944
945
946 cv::Ptr<cv::BaseFilter> cv::getMorphologyFilter(int op, int type, InputArray _kernel, Point anchor)
947 {
948     Mat kernel = _kernel.getMat();
949     int depth = CV_MAT_DEPTH(type);
950     anchor = normalizeAnchor(anchor, kernel.size());
951     CV_Assert( op == MORPH_ERODE || op == MORPH_DILATE );
952     if( op == MORPH_ERODE )
953     {
954         if( depth == CV_8U )
955             return makePtr<MorphFilter<MinOp<uchar>, ErodeVec8u> >(kernel, anchor);
956         if( depth == CV_16U )
957             return makePtr<MorphFilter<MinOp<ushort>, ErodeVec16u> >(kernel, anchor);
958         if( depth == CV_16S )
959             return makePtr<MorphFilter<MinOp<short>, ErodeVec16s> >(kernel, anchor);
960         if( depth == CV_32F )
961             return makePtr<MorphFilter<MinOp<float>, ErodeVec32f> >(kernel, anchor);
962         if( depth == CV_64F )
963             return makePtr<MorphFilter<MinOp<double>, ErodeVec64f> >(kernel, anchor);
964     }
965     else
966     {
967         if( depth == CV_8U )
968             return makePtr<MorphFilter<MaxOp<uchar>, DilateVec8u> >(kernel, anchor);
969         if( depth == CV_16U )
970             return makePtr<MorphFilter<MaxOp<ushort>, DilateVec16u> >(kernel, anchor);
971         if( depth == CV_16S )
972             return makePtr<MorphFilter<MaxOp<short>, DilateVec16s> >(kernel, anchor);
973         if( depth == CV_32F )
974             return makePtr<MorphFilter<MaxOp<float>, DilateVec32f> >(kernel, anchor);
975         if( depth == CV_64F )
976             return makePtr<MorphFilter<MaxOp<double>, DilateVec64f> >(kernel, anchor);
977     }
978
979     CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type));
980     return Ptr<BaseFilter>();
981 }
982
983
984 cv::Ptr<cv::FilterEngine> cv::createMorphologyFilter( int op, int type, InputArray _kernel,
985                                                       Point anchor, int _rowBorderType, int _columnBorderType,
986                                                       const Scalar& _borderValue )
987 {
988     Mat kernel = _kernel.getMat();
989     anchor = normalizeAnchor(anchor, kernel.size());
990
991     Ptr<BaseRowFilter> rowFilter;
992     Ptr<BaseColumnFilter> columnFilter;
993     Ptr<BaseFilter> filter2D;
994
995     if( countNonZero(kernel) == kernel.rows*kernel.cols )
996     {
997         // rectangular structuring element
998         rowFilter = getMorphologyRowFilter(op, type, kernel.cols, anchor.x);
999         columnFilter = getMorphologyColumnFilter(op, type, kernel.rows, anchor.y);
1000     }
1001     else
1002         filter2D = getMorphologyFilter(op, type, kernel, anchor);
1003
1004     Scalar borderValue = _borderValue;
1005     if( (_rowBorderType == BORDER_CONSTANT || _columnBorderType == BORDER_CONSTANT) &&
1006             borderValue == morphologyDefaultBorderValue() )
1007     {
1008         int depth = CV_MAT_DEPTH(type);
1009         CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_16S ||
1010                    depth == CV_32F || depth == CV_64F );
1011         if( op == MORPH_ERODE )
1012             borderValue = Scalar::all( depth == CV_8U ? (double)UCHAR_MAX :
1013                                        depth == CV_16U ? (double)USHRT_MAX :
1014                                        depth == CV_16S ? (double)SHRT_MAX :
1015                                        depth == CV_32F ? (double)FLT_MAX : DBL_MAX);
1016         else
1017             borderValue = Scalar::all( depth == CV_8U || depth == CV_16U ?
1018                                            0. :
1019                                        depth == CV_16S ? (double)SHRT_MIN :
1020                                        depth == CV_32F ? (double)-FLT_MAX : -DBL_MAX);
1021     }
1022
1023     return makePtr<FilterEngine>(filter2D, rowFilter, columnFilter,
1024                                  type, type, type, _rowBorderType, _columnBorderType, borderValue );
1025 }
1026
1027
1028 cv::Mat cv::getStructuringElement(int shape, Size ksize, Point anchor)
1029 {
1030     int i, j;
1031     int r = 0, c = 0;
1032     double inv_r2 = 0;
1033
1034     CV_Assert( shape == MORPH_RECT || shape == MORPH_CROSS || shape == MORPH_ELLIPSE );
1035
1036     anchor = normalizeAnchor(anchor, ksize);
1037
1038     if( ksize == Size(1,1) )
1039         shape = MORPH_RECT;
1040
1041     if( shape == MORPH_ELLIPSE )
1042     {
1043         r = ksize.height/2;
1044         c = ksize.width/2;
1045         inv_r2 = r ? 1./((double)r*r) : 0;
1046     }
1047
1048     Mat elem(ksize, CV_8U);
1049
1050     for( i = 0; i < ksize.height; i++ )
1051     {
1052         uchar* ptr = elem.ptr(i);
1053         int j1 = 0, j2 = 0;
1054
1055         if( shape == MORPH_RECT || (shape == MORPH_CROSS && i == anchor.y) )
1056             j2 = ksize.width;
1057         else if( shape == MORPH_CROSS )
1058             j1 = anchor.x, j2 = j1 + 1;
1059         else
1060         {
1061             int dy = i - r;
1062             if( std::abs(dy) <= r )
1063             {
1064                 int dx = saturate_cast<int>(c*std::sqrt((r*r - dy*dy)*inv_r2));
1065                 j1 = std::max( c - dx, 0 );
1066                 j2 = std::min( c + dx + 1, ksize.width );
1067             }
1068         }
1069
1070         for( j = 0; j < j1; j++ )
1071             ptr[j] = 0;
1072         for( ; j < j2; j++ )
1073             ptr[j] = 1;
1074         for( ; j < ksize.width; j++ )
1075             ptr[j] = 0;
1076     }
1077
1078     return elem;
1079 }
1080
1081 namespace cv
1082 {
1083
1084 class MorphologyRunner : public ParallelLoopBody
1085 {
1086 public:
1087     MorphologyRunner(Mat _src, Mat _dst, int _nStripes, int _iterations,
1088                      int _op, Mat _kernel, Point _anchor,
1089                      int _rowBorderType, int _columnBorderType, const Scalar& _borderValue) :
1090         borderValue(_borderValue)
1091     {
1092         src = _src;
1093         dst = _dst;
1094
1095         nStripes = _nStripes;
1096         iterations = _iterations;
1097
1098         op = _op;
1099         kernel = _kernel;
1100         anchor = _anchor;
1101         rowBorderType = _rowBorderType;
1102         columnBorderType = _columnBorderType;
1103     }
1104
1105     void operator () ( const Range& range ) const
1106     {
1107         int row0 = std::min(cvRound(range.start * src.rows / nStripes), src.rows);
1108         int row1 = std::min(cvRound(range.end * src.rows / nStripes), src.rows);
1109
1110         /*if(0)
1111             printf("Size = (%d, %d), range[%d,%d), row0 = %d, row1 = %d\n",
1112                    src.rows, src.cols, range.start, range.end, row0, row1);*/
1113
1114         Mat srcStripe = src.rowRange(row0, row1);
1115         Mat dstStripe = dst.rowRange(row0, row1);
1116
1117         Ptr<FilterEngine> f = createMorphologyFilter(op, src.type(), kernel, anchor,
1118                                                      rowBorderType, columnBorderType, borderValue );
1119
1120         f->apply( srcStripe, dstStripe );
1121         for( int i = 1; i < iterations; i++ )
1122             f->apply( dstStripe, dstStripe );
1123     }
1124
1125 private:
1126     Mat src;
1127     Mat dst;
1128     int nStripes;
1129     int iterations;
1130
1131     int op;
1132     Mat kernel;
1133     Point anchor;
1134     int rowBorderType;
1135     int columnBorderType;
1136     Scalar borderValue;
1137 };
1138
1139 #if IPP_VERSION_X100 >= 801
1140 static bool IPPMorphReplicate(int op, const Mat &src, Mat &dst, const Mat &kernel,
1141                               const Size& ksize, const Point &anchor, bool rectKernel)
1142 {
1143     int type = src.type();
1144     const Mat* _src = &src;
1145     Mat temp;
1146     if (src.data == dst.data)
1147     {
1148         src.copyTo(temp);
1149         _src = &temp;
1150     }
1151
1152     IppiSize roiSize = {src.cols, src.rows};
1153     IppiSize kernelSize = {ksize.width, ksize.height};
1154
1155     if (!rectKernel)
1156     {
1157 #if 1
1158         if (((kernel.cols - 1) / 2 != anchor.x) || ((kernel.rows - 1) / 2 != anchor.y))
1159             return false;
1160         #define IPP_MORPH_CASE(cvtype, flavor, data_type) \
1161         case cvtype: \
1162             {\
1163                 int specSize = 0, bufferSize = 0;\
1164                 if (0 > ippiMorphologyBorderGetSize_##flavor(roiSize.width, kernelSize, &specSize, &bufferSize))\
1165                     return false;\
1166                 IppiMorphState *pSpec = (IppiMorphState*)ippMalloc(specSize);\
1167                 Ipp8u *pBuffer = (Ipp8u*)ippMalloc(bufferSize);\
1168                 if (0 > ippiMorphologyBorderInit_##flavor(roiSize.width, kernel.ptr(), kernelSize, pSpec, pBuffer))\
1169                 {\
1170                     ippFree(pBuffer);\
1171                     ippFree(pSpec);\
1172                     return false;\
1173                 }\
1174                 bool ok = false;\
1175                 if (op == MORPH_ERODE)\
1176                     ok = (0 <= ippiErodeBorder_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0], dst.ptr<Ipp##data_type>(), (int)dst.step[0],\
1177                                             roiSize, ippBorderRepl, 0, pSpec, pBuffer));\
1178                 else\
1179                     ok = (0 <= ippiDilateBorder_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0], dst.ptr<Ipp##data_type>(), (int)dst.step[0],\
1180                                             roiSize, ippBorderRepl, 0, pSpec, pBuffer));\
1181                 ippFree(pBuffer);\
1182                 ippFree(pSpec);\
1183                 return ok;\
1184             }\
1185             break;
1186 #else
1187         IppiPoint point = {anchor.x, anchor.y};
1188         // this is case, which can be used with the anchor not in center of the kernel, but
1189         // ippiMorphologyBorderGetSize_, ippiErodeBorderReplicate_ and ippiDilateBorderReplicate_ are deprecated.
1190         #define IPP_MORPH_CASE(cvtype, flavor, data_type) \
1191         case cvtype: \
1192             {\
1193                 int specSize = 0;\
1194                 int bufferSize = 0;\
1195                 if (0 > ippiMorphologyGetSize_##flavor( roiSize.width, kernel.ptr() kernelSize, &specSize))\
1196                     return false;\
1197                 bool ok = false;\
1198                 IppiMorphState* pState = (IppiMorphState*)ippMalloc(specSize);\
1199                 if (ippiMorphologyInit_##flavor(roiSize.width, kernel.ptr(), kernelSize, point, pState) >= 0)\
1200                 {\
1201                     if (op == MORPH_ERODE)\
1202                         ok = ippiErodeBorderReplicate_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0],\
1203                             dst.ptr<Ipp##data_type>(), (int)dst.step[0],\
1204                             roiSize, ippBorderRepl, pState ) >= 0;\
1205                     else\
1206                         ok = ippiDilateBorderReplicate_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0],\
1207                             dst.ptr<Ipp##data_type>(), (int)dst.step[0],\
1208                             roiSize, ippBorderRepl, pState ) >= 0;\
1209                 }\
1210                 ippFree(pState);\
1211                 return ok;\
1212             }\
1213             break;
1214 #endif
1215         switch (type)
1216         {
1217         IPP_MORPH_CASE(CV_8UC1, 8u_C1R, 8u);
1218         IPP_MORPH_CASE(CV_8UC3, 8u_C3R, 8u);
1219         IPP_MORPH_CASE(CV_8UC4, 8u_C4R, 8u);
1220         IPP_MORPH_CASE(CV_32FC1, 32f_C1R, 32f);
1221         IPP_MORPH_CASE(CV_32FC3, 32f_C3R, 32f);
1222         IPP_MORPH_CASE(CV_32FC4, 32f_C4R, 32f);
1223         default:
1224             ;
1225         }
1226
1227         #undef IPP_MORPH_CASE
1228     }
1229     else
1230     {
1231         IppiPoint point = {anchor.x, anchor.y};
1232
1233         #define IPP_MORPH_CASE(cvtype, flavor, data_type) \
1234         case cvtype: \
1235             {\
1236                 int bufSize = 0;\
1237                 if (0 > ippiFilterMinGetBufferSize_##flavor(src.cols, kernelSize, &bufSize))\
1238                     return false;\
1239                 AutoBuffer<uchar> buf(bufSize + 64);\
1240                 uchar* buffer = alignPtr((uchar*)buf, 32);\
1241                 if (op == MORPH_ERODE)\
1242                     return (0 <= ippiFilterMinBorderReplicate_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0], dst.ptr<Ipp##data_type>(), (int)dst.step[0], roiSize, kernelSize, point, buffer));\
1243                 return (0 <= ippiFilterMaxBorderReplicate_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0], dst.ptr<Ipp##data_type>(), (int)dst.step[0], roiSize, kernelSize, point, buffer));\
1244             }\
1245             break;
1246
1247         switch (type)
1248         {
1249         IPP_MORPH_CASE(CV_8UC1, 8u_C1R, 8u);
1250         IPP_MORPH_CASE(CV_8UC3, 8u_C3R, 8u);
1251         IPP_MORPH_CASE(CV_8UC4, 8u_C4R, 8u);
1252         IPP_MORPH_CASE(CV_32FC1, 32f_C1R, 32f);
1253         IPP_MORPH_CASE(CV_32FC3, 32f_C3R, 32f);
1254         IPP_MORPH_CASE(CV_32FC4, 32f_C4R, 32f);
1255         default:
1256             ;
1257         }
1258         #undef IPP_MORPH_CASE
1259     }
1260     return false;
1261 }
1262
1263 static bool IPPMorphOp(int op, InputArray _src, OutputArray _dst,
1264     const Mat& _kernel, Point anchor, int iterations,
1265     int borderType, const Scalar &borderValue)
1266 {
1267     Mat src = _src.getMat(), kernel = _kernel;
1268     int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
1269
1270     if( !( depth == CV_8U || depth == CV_32F ) || !(cn == 1 || cn == 3 || cn == 4) ||
1271         !( borderType == cv::BORDER_REPLICATE || (borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() &&
1272         kernel.size() == Size(3,3)) ) || !( op == MORPH_DILATE || op == MORPH_ERODE) || _src.isSubmatrix() )
1273         return false;
1274
1275     // In case BORDER_CONSTANT, IPPMorphReplicate works correct with kernels of size 3*3 only
1276     if( borderType == cv::BORDER_CONSTANT && kernel.data )
1277     {
1278         int x, y;
1279         for( y = 0; y < kernel.rows; y++ )
1280         {
1281             if( kernel.at<uchar>(y, anchor.x) != 0 )
1282                 continue;
1283             for( x = 0; x < kernel.cols; x++ )
1284             {
1285                 if( kernel.at<uchar>(y,x) != 0 )
1286                     return false;
1287             }
1288         }
1289         for( x = 0; x < kernel.cols; x++ )
1290         {
1291             if( kernel.at<uchar>(anchor.y, x) != 0 )
1292                 continue;
1293             for( y = 0; y < kernel.rows; y++ )
1294             {
1295                 if( kernel.at<uchar>(y,x) != 0 )
1296                     return false;
1297             }
1298         }
1299
1300     }
1301     Size ksize = !kernel.empty() ? kernel.size() : Size(3,3);
1302
1303     _dst.create( src.size(), src.type() );
1304     Mat dst = _dst.getMat();
1305
1306     if( iterations == 0 || kernel.rows*kernel.cols == 1 )
1307     {
1308         src.copyTo(dst);
1309         return true;
1310     }
1311
1312     bool rectKernel = false;
1313     if( kernel.empty() )
1314     {
1315         ksize = Size(1+iterations*2,1+iterations*2);
1316         anchor = Point(iterations, iterations);
1317         rectKernel = true;
1318         iterations = 1;
1319     }
1320     else if( iterations >= 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
1321     {
1322         ksize = Size(ksize.width + (iterations-1)*(ksize.width-1),
1323              ksize.height + (iterations-1)*(ksize.height-1)),
1324         anchor = Point(anchor.x*iterations, anchor.y*iterations);
1325         kernel = Mat();
1326         rectKernel = true;
1327         iterations = 1;
1328     }
1329
1330     // TODO: implement the case of iterations > 1.
1331     if( iterations > 1 )
1332         return false;
1333
1334     return IPPMorphReplicate( op, src, dst, kernel, ksize, anchor, rectKernel );
1335 }
1336 #endif
1337
1338 #ifdef HAVE_OPENCL
1339
1340 #define ROUNDUP(sz, n)      ((sz) + (n) - 1 - (((sz) + (n) - 1) % (n)))
1341
1342 static bool ocl_morphSmall( InputArray _src, OutputArray _dst, InputArray _kernel, Point anchor, int borderType,
1343                             int op, int actual_op = -1, InputArray _extraMat = noArray())
1344 {
1345     const ocl::Device & dev = ocl::Device::getDefault();
1346     int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), esz = CV_ELEM_SIZE(type);
1347     bool doubleSupport = dev.doubleFPConfig() > 0;
1348
1349     if (cn > 4 || (!doubleSupport && depth == CV_64F) ||
1350         _src.offset() % esz != 0 || _src.step() % esz != 0)
1351         return false;
1352
1353     bool haveExtraMat = !_extraMat.empty();
1354     CV_Assert(actual_op <= 3 || haveExtraMat);
1355
1356     Size ksize = _kernel.size();
1357     if (anchor.x < 0)
1358         anchor.x = ksize.width / 2;
1359     if (anchor.y < 0)
1360         anchor.y = ksize.height / 2;
1361
1362     Size size = _src.size(), wholeSize;
1363     bool isolated = (borderType & BORDER_ISOLATED) != 0;
1364     borderType &= ~BORDER_ISOLATED;
1365     int wdepth = depth, wtype = type;
1366     if (depth == CV_8U)
1367     {
1368         wdepth = CV_32S;
1369         wtype = CV_MAKETYPE(wdepth, cn);
1370     }
1371     char cvt[2][40];
1372
1373     const char * const borderMap[] = { "BORDER_CONSTANT", "BORDER_REPLICATE",
1374                                        "BORDER_REFLECT", 0, "BORDER_REFLECT_101" };
1375     size_t globalsize[2] = { size.width, size.height };
1376
1377     UMat src = _src.getUMat();
1378     if (!isolated)
1379     {
1380         Point ofs;
1381         src.locateROI(wholeSize, ofs);
1382     }
1383
1384     int h = isolated ? size.height : wholeSize.height;
1385     int w = isolated ? size.width : wholeSize.width;
1386     if (w < ksize.width || h < ksize.height)
1387         return false;
1388
1389     // Figure out what vector size to use for loading the pixels.
1390     int pxLoadNumPixels = cn != 1 || size.width % 4 ? 1 : 4;
1391     int pxLoadVecSize = cn * pxLoadNumPixels;
1392
1393     // Figure out how many pixels per work item to compute in X and Y
1394     // directions.  Too many and we run out of registers.
1395     int pxPerWorkItemX = 1, pxPerWorkItemY = 1;
1396     if (cn <= 2 && ksize.width <= 4 && ksize.height <= 4)
1397     {
1398         pxPerWorkItemX = size.width % 8 ? size.width % 4 ? size.width % 2 ? 1 : 2 : 4 : 8;
1399         pxPerWorkItemY = size.height % 2 ? 1 : 2;
1400     }
1401     else if (cn < 4 || (ksize.width <= 4 && ksize.height <= 4))
1402     {
1403         pxPerWorkItemX = size.width % 2 ? 1 : 2;
1404         pxPerWorkItemY = size.height % 2 ? 1 : 2;
1405     }
1406     globalsize[0] = size.width / pxPerWorkItemX;
1407     globalsize[1] = size.height / pxPerWorkItemY;
1408
1409     // Need some padding in the private array for pixels
1410     int privDataWidth = ROUNDUP(pxPerWorkItemX + ksize.width - 1, pxLoadNumPixels);
1411
1412     // Make the global size a nice round number so the runtime can pick
1413     // from reasonable choices for the workgroup size
1414     const int wgRound = 256;
1415     globalsize[0] = ROUNDUP(globalsize[0], wgRound);
1416
1417     if (actual_op < 0)
1418         actual_op = op;
1419
1420     // build processing
1421     String processing;
1422     Mat kernel8u;
1423     _kernel.getMat().convertTo(kernel8u, CV_8U);
1424     for (int y = 0; y < kernel8u.rows; ++y)
1425         for (int x = 0; x < kernel8u.cols; ++x)
1426             if (kernel8u.at<uchar>(y, x) != 0)
1427                 processing += format("PROCESS(%d,%d)", y, x);
1428
1429
1430     static const char * const op2str[] = { "OP_ERODE", "OP_DILATE", NULL, NULL, "OP_GRADIENT", "OP_TOPHAT", "OP_BLACKHAT" };
1431     String opts = format("-D cn=%d "
1432             "-D ANCHOR_X=%d -D ANCHOR_Y=%d -D KERNEL_SIZE_X=%d -D KERNEL_SIZE_Y=%d "
1433             "-D PX_LOAD_VEC_SIZE=%d -D PX_LOAD_NUM_PX=%d -D DEPTH_%d "
1434             "-D PX_PER_WI_X=%d -D PX_PER_WI_Y=%d -D PRIV_DATA_WIDTH=%d -D %s -D %s "
1435             "-D PX_LOAD_X_ITERATIONS=%d -D PX_LOAD_Y_ITERATIONS=%d "
1436             "-D srcT=%s -D srcT1=%s -D dstT=srcT -D dstT1=srcT1 -D WT=%s -D WT1=%s "
1437             "-D convertToWT=%s -D convertToDstT=%s -D PROCESS_ELEM_=%s -D %s%s",
1438             cn, anchor.x, anchor.y, ksize.width, ksize.height,
1439             pxLoadVecSize, pxLoadNumPixels, depth,
1440             pxPerWorkItemX, pxPerWorkItemY, privDataWidth, borderMap[borderType],
1441             isolated ? "BORDER_ISOLATED" : "NO_BORDER_ISOLATED",
1442             privDataWidth / pxLoadNumPixels, pxPerWorkItemY + ksize.height - 1,
1443             ocl::typeToStr(type), ocl::typeToStr(depth),
1444             haveExtraMat ? ocl::typeToStr(wtype):"srcT",//to prevent overflow - WT
1445             haveExtraMat ? ocl::typeToStr(wdepth):"srcT1",//to prevent overflow - WT1
1446             haveExtraMat ? ocl::convertTypeStr(depth, wdepth, cn, cvt[0]) : "noconvert",//to prevent overflow - src to WT
1447             haveExtraMat ? ocl::convertTypeStr(wdepth, depth, cn, cvt[1]) : "noconvert",//to prevent overflow - WT to dst
1448             processing.c_str(), op2str[op],
1449             actual_op == op ? "" : cv::format(" -D %s", op2str[actual_op]).c_str());
1450
1451     ocl::Kernel kernel("filterSmall", cv::ocl::imgproc::filterSmall_oclsrc, opts);
1452     if (kernel.empty())
1453         return false;
1454
1455     _dst.create(size, type);
1456     UMat dst = _dst.getUMat();
1457
1458     UMat source;
1459     if(src.u != dst.u)
1460         source = src;
1461     else
1462     {
1463         Point ofs;
1464         int cols =  src.cols, rows = src.rows;
1465         src.locateROI(wholeSize, ofs);
1466         src.adjustROI(ofs.y, wholeSize.height - rows - ofs.y, ofs.x, wholeSize.width - cols - ofs.x);
1467         src.copyTo(source);
1468
1469         src.adjustROI(-ofs.y, -wholeSize.height + rows + ofs.y, -ofs.x, -wholeSize.width + cols + ofs.x);
1470         source.adjustROI(-ofs.y, -wholeSize.height + rows + ofs.y, -ofs.x, -wholeSize.width + cols + ofs.x);
1471         source.locateROI(wholeSize, ofs);
1472     }
1473
1474     UMat extraMat = _extraMat.getUMat();
1475
1476     int idxArg = kernel.set(0, ocl::KernelArg::PtrReadOnly(source));
1477     idxArg = kernel.set(idxArg, (int)source.step);
1478     int srcOffsetX = (int)((source.offset % source.step) / source.elemSize());
1479     int srcOffsetY = (int)(source.offset / source.step);
1480     int srcEndX = isolated ? srcOffsetX + size.width : wholeSize.width;
1481     int srcEndY = isolated ? srcOffsetY + size.height : wholeSize.height;
1482     idxArg = kernel.set(idxArg, srcOffsetX);
1483     idxArg = kernel.set(idxArg, srcOffsetY);
1484     idxArg = kernel.set(idxArg, srcEndX);
1485     idxArg = kernel.set(idxArg, srcEndY);
1486     idxArg = kernel.set(idxArg, ocl::KernelArg::WriteOnly(dst));
1487
1488     if (haveExtraMat)
1489     {
1490         idxArg = kernel.set(idxArg, ocl::KernelArg::ReadOnlyNoSize(extraMat));
1491     }
1492
1493     return kernel.run(2, globalsize, NULL, false);
1494
1495 }
1496
1497 static bool ocl_morphOp(InputArray _src, OutputArray _dst, InputArray _kernel,
1498                         Point anchor, int iterations, int op, int borderType,
1499                         const Scalar &, int actual_op = -1, InputArray _extraMat = noArray())
1500 {
1501     const ocl::Device & dev = ocl::Device::getDefault();
1502     int type = _src.type(), depth = CV_MAT_DEPTH(type),
1503             cn = CV_MAT_CN(type), esz = CV_ELEM_SIZE(type);
1504     Mat kernel = _kernel.getMat();
1505     Size ksize = !kernel.empty() ? kernel.size() : Size(3, 3), ssize = _src.size();
1506
1507     bool doubleSupport = dev.doubleFPConfig() > 0;
1508     if ((depth == CV_64F && !doubleSupport) || borderType != BORDER_CONSTANT)
1509         return false;
1510
1511     bool haveExtraMat = !_extraMat.empty();
1512     CV_Assert(actual_op <= 3 || haveExtraMat);
1513
1514     if (kernel.empty())
1515     {
1516         kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
1517         anchor = Point(iterations, iterations);
1518         iterations = 1;
1519     }
1520     else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
1521     {
1522         anchor = Point(anchor.x*iterations, anchor.y*iterations);
1523         kernel = getStructuringElement(MORPH_RECT,
1524                                        Size(ksize.width + (iterations-1)*(ksize.width-1),
1525                                             ksize.height + (iterations-1)*(ksize.height-1)),
1526                                        anchor);
1527         iterations = 1;
1528     }
1529
1530     // try to use OpenCL kernel adopted for small morph kernel
1531     if (dev.isIntel() && !(dev.type() & ocl::Device::TYPE_CPU) &&
1532         ((ksize.width < 5 && ksize.height < 5 && esz <= 4) ||
1533          (ksize.width == 5 && ksize.height == 5 && cn == 1)) &&
1534          (iterations == 1) && cn == 1)
1535     {
1536         if (ocl_morphSmall(_src, _dst, kernel, anchor, borderType, op, actual_op, _extraMat))
1537             return true;
1538     }
1539
1540     if (iterations == 0 || kernel.rows*kernel.cols == 1)
1541     {
1542         _src.copyTo(_dst);
1543         return true;
1544     }
1545
1546 #if defined ANDROID
1547     size_t localThreads[2] = { 16, 8 };
1548 #else
1549     size_t localThreads[2] = { 16, 16 };
1550 #endif
1551     size_t globalThreads[2] = { ssize.width, ssize.height };
1552
1553 #ifdef __APPLE__
1554     if( actual_op != MORPH_ERODE && actual_op != MORPH_DILATE )
1555         localThreads[0] = localThreads[1] = 4;
1556 #endif
1557
1558     if (localThreads[0]*localThreads[1] * 2 < (localThreads[0] + ksize.width - 1) * (localThreads[1] + ksize.height - 1))
1559         return false;
1560
1561     // build processing
1562     String processing;
1563     Mat kernel8u;
1564     kernel.convertTo(kernel8u, CV_8U);
1565     for (int y = 0; y < kernel8u.rows; ++y)
1566         for (int x = 0; x < kernel8u.cols; ++x)
1567             if (kernel8u.at<uchar>(y, x) != 0)
1568                 processing += format("PROCESS(%d,%d)", y, x);
1569
1570     static const char * const op2str[] = { "OP_ERODE", "OP_DILATE", NULL, NULL, "OP_GRADIENT", "OP_TOPHAT", "OP_BLACKHAT" };
1571
1572     char cvt[2][50];
1573     int wdepth = std::max(depth, CV_32F), scalarcn = cn == 3 ? 4 : cn;
1574
1575     if (actual_op < 0)
1576         actual_op = op;
1577
1578     std::vector<ocl::Kernel> kernels(iterations);
1579     for (int i = 0; i < iterations; i++)
1580     {
1581         int current_op = iterations == i + 1 ? actual_op : op;
1582         String buildOptions = format("-D RADIUSX=%d -D RADIUSY=%d -D LSIZE0=%d -D LSIZE1=%d -D %s%s"
1583                                      " -D PROCESS_ELEMS=%s -D T=%s -D DEPTH_%d -D cn=%d -D T1=%s"
1584                                      " -D convertToWT=%s -D convertToT=%s -D ST=%s%s",
1585                                      anchor.x, anchor.y, (int)localThreads[0], (int)localThreads[1], op2str[op],
1586                                      doubleSupport ? " -D DOUBLE_SUPPORT" : "", processing.c_str(),
1587                                      ocl::typeToStr(type), depth, cn, ocl::typeToStr(depth),
1588                                      ocl::convertTypeStr(depth, wdepth, cn, cvt[0]),
1589                                      ocl::convertTypeStr(wdepth, depth, cn, cvt[1]),
1590                                      ocl::typeToStr(CV_MAKE_TYPE(depth, scalarcn)),
1591                                      current_op == op ? "" : cv::format(" -D %s", op2str[current_op]).c_str());
1592
1593         kernels[i].create("morph", ocl::imgproc::morph_oclsrc, buildOptions);
1594         if (kernels[i].empty())
1595             return false;
1596     }
1597
1598     UMat src = _src.getUMat(), extraMat = _extraMat.getUMat();
1599     _dst.create(src.size(), src.type());
1600     UMat dst = _dst.getUMat();
1601
1602     if (iterations == 1 && src.u != dst.u)
1603     {
1604         Size wholesize;
1605         Point ofs;
1606         src.locateROI(wholesize, ofs);
1607         int wholecols = wholesize.width, wholerows = wholesize.height;
1608
1609         if (haveExtraMat)
1610             kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst),
1611                         ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows,
1612                         ocl::KernelArg::ReadOnlyNoSize(extraMat));
1613         else
1614             kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst),
1615                         ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows);
1616
1617         return kernels[0].run(2, globalThreads, localThreads, false);
1618     }
1619
1620     for (int i = 0; i < iterations; i++)
1621     {
1622         UMat source;
1623         Size wholesize;
1624         Point ofs;
1625
1626         if (i == 0)
1627         {
1628             int cols =  src.cols, rows = src.rows;
1629             src.locateROI(wholesize, ofs);
1630             src.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x);
1631             if(src.u != dst.u)
1632                 source = src;
1633             else
1634                 src.copyTo(source);
1635
1636             src.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
1637             source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
1638         }
1639         else
1640         {
1641             int cols =  dst.cols, rows = dst.rows;
1642             dst.locateROI(wholesize, ofs);
1643             dst.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x);
1644             dst.copyTo(source);
1645             dst.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
1646             source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
1647         }
1648         source.locateROI(wholesize, ofs);
1649
1650         if (haveExtraMat && iterations == i + 1)
1651             kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst),
1652                 ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height,
1653                 ocl::KernelArg::ReadOnlyNoSize(extraMat));
1654         else
1655             kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst),
1656                 ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height);
1657
1658         if (!kernels[i].run(2, globalThreads, localThreads, false))
1659             return false;
1660     }
1661
1662     return true;
1663 }
1664
1665 #endif
1666
1667 static void morphOp( int op, InputArray _src, OutputArray _dst,
1668                      InputArray _kernel,
1669                      Point anchor, int iterations,
1670                      int borderType, const Scalar& borderValue )
1671 {
1672     Mat kernel = _kernel.getMat();
1673     Size ksize = !kernel.empty() ? kernel.size() : Size(3,3);
1674     anchor = normalizeAnchor(anchor, ksize);
1675
1676     CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
1677                borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() &&
1678                (op == MORPH_ERODE || op == MORPH_DILATE) &&
1679                anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1,
1680                ocl_morphOp(_src, _dst, kernel, anchor, iterations, op, borderType, borderValue) )
1681
1682     if (iterations == 0 || kernel.rows*kernel.cols == 1)
1683     {
1684         _src.copyTo(_dst);
1685         return;
1686     }
1687
1688     if (kernel.empty())
1689     {
1690         kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
1691         anchor = Point(iterations, iterations);
1692         iterations = 1;
1693     }
1694     else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
1695     {
1696         anchor = Point(anchor.x*iterations, anchor.y*iterations);
1697         kernel = getStructuringElement(MORPH_RECT,
1698                                        Size(ksize.width + (iterations-1)*(ksize.width-1),
1699                                             ksize.height + (iterations-1)*(ksize.height-1)),
1700                                        anchor);
1701         iterations = 1;
1702     }
1703
1704 #if IPP_VERSION_X100 >= 801
1705     if( IPPMorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue) )
1706         return;
1707 #endif
1708
1709     Mat src = _src.getMat();
1710     _dst.create( src.size(), src.type() );
1711     Mat dst = _dst.getMat();
1712
1713     int nStripes = 1;
1714 #if defined HAVE_TEGRA_OPTIMIZATION
1715     if (src.data != dst.data && iterations == 1 &&  //NOTE: threads are not used for inplace processing
1716         (borderType & BORDER_ISOLATED) == 0 && //TODO: check border types
1717         src.rows >= 64 ) //NOTE: just heuristics
1718         nStripes = 4;
1719 #endif
1720
1721     parallel_for_(Range(0, nStripes),
1722                   MorphologyRunner(src, dst, nStripes, iterations, op, kernel, anchor, borderType, borderType, borderValue));
1723 }
1724
1725 }
1726
1727 void cv::erode( InputArray src, OutputArray dst, InputArray kernel,
1728                 Point anchor, int iterations,
1729                 int borderType, const Scalar& borderValue )
1730 {
1731     morphOp( MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType, borderValue );
1732 }
1733
1734
1735 void cv::dilate( InputArray src, OutputArray dst, InputArray kernel,
1736                  Point anchor, int iterations,
1737                  int borderType, const Scalar& borderValue )
1738 {
1739     morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue );
1740 }
1741
1742 #ifdef HAVE_OPENCL
1743
1744 namespace cv {
1745
1746 static bool ocl_morphologyEx(InputArray _src, OutputArray _dst, int op,
1747                              InputArray kernel, Point anchor, int iterations,
1748                              int borderType, const Scalar& borderValue)
1749 {
1750     _dst.createSameSize(_src, _src.type());
1751     bool submat = _dst.isSubmatrix();
1752     UMat temp;
1753     _OutputArray _temp = submat ? _dst : _OutputArray(temp);
1754
1755     switch( op )
1756     {
1757     case MORPH_ERODE:
1758         if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1759             return false;
1760         break;
1761     case MORPH_DILATE:
1762         if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
1763             return false;
1764         break;
1765     case MORPH_OPEN:
1766         if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1767             return false;
1768         if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
1769             return false;
1770         break;
1771     case MORPH_CLOSE:
1772         if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
1773             return false;
1774         if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1775             return false;
1776         break;
1777     case MORPH_GRADIENT:
1778         if (!ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1779             return false;
1780         if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_GRADIENT, temp ))
1781             return false;
1782         break;
1783     case MORPH_TOPHAT:
1784         if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1785             return false;
1786         if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_TOPHAT, _src ))
1787             return false;
1788         break;
1789     case MORPH_BLACKHAT:
1790         if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
1791             return false;
1792         if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue, MORPH_BLACKHAT, _src ))
1793             return false;
1794         break;
1795     default:
1796         CV_Error( CV_StsBadArg, "unknown morphological operation" );
1797     }
1798
1799     return true;
1800 }
1801
1802 }
1803
1804 #endif
1805
1806 void cv::morphologyEx( InputArray _src, OutputArray _dst, int op,
1807                        InputArray kernel, Point anchor, int iterations,
1808                        int borderType, const Scalar& borderValue )
1809 {
1810 #ifdef HAVE_OPENCL
1811     Size ksize = kernel.size();
1812     anchor = normalizeAnchor(anchor, ksize);
1813
1814     CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
1815         anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 &&
1816         borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(),
1817         ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue))
1818 #endif
1819
1820     Mat src = _src.getMat(), temp;
1821     _dst.create(src.size(), src.type());
1822     Mat dst = _dst.getMat();
1823
1824     switch( op )
1825     {
1826     case MORPH_ERODE:
1827         erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
1828         break;
1829     case MORPH_DILATE:
1830         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1831         break;
1832     case MORPH_OPEN:
1833         erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
1834         dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
1835         break;
1836     case CV_MOP_CLOSE:
1837         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1838         erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
1839         break;
1840     case CV_MOP_GRADIENT:
1841         erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
1842         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1843         dst -= temp;
1844         break;
1845     case CV_MOP_TOPHAT:
1846         if( src.data != dst.data )
1847             temp = dst;
1848         erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
1849         dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue );
1850         dst = src - temp;
1851         break;
1852     case CV_MOP_BLACKHAT:
1853         if( src.data != dst.data )
1854             temp = dst;
1855         dilate( src, temp, kernel, anchor, iterations, borderType, borderValue );
1856         erode( temp, temp, kernel, anchor, iterations, borderType, borderValue );
1857         dst = temp - src;
1858         break;
1859     default:
1860         CV_Error( CV_StsBadArg, "unknown morphological operation" );
1861     }
1862 }
1863
1864 CV_IMPL IplConvKernel *
1865 cvCreateStructuringElementEx( int cols, int rows,
1866                               int anchorX, int anchorY,
1867                               int shape, int *values )
1868 {
1869     cv::Size ksize = cv::Size(cols, rows);
1870     cv::Point anchor = cv::Point(anchorX, anchorY);
1871     CV_Assert( cols > 0 && rows > 0 && anchor.inside(cv::Rect(0,0,cols,rows)) &&
1872                (shape != CV_SHAPE_CUSTOM || values != 0));
1873
1874     int i, size = rows * cols;
1875     int element_size = sizeof(IplConvKernel) + size*sizeof(int);
1876     IplConvKernel *element = (IplConvKernel*)cvAlloc(element_size + 32);
1877
1878     element->nCols = cols;
1879     element->nRows = rows;
1880     element->anchorX = anchorX;
1881     element->anchorY = anchorY;
1882     element->nShiftR = shape < CV_SHAPE_ELLIPSE ? shape : CV_SHAPE_CUSTOM;
1883     element->values = (int*)(element + 1);
1884
1885     if( shape == CV_SHAPE_CUSTOM )
1886     {
1887         for( i = 0; i < size; i++ )
1888             element->values[i] = values[i];
1889     }
1890     else
1891     {
1892         cv::Mat elem = cv::getStructuringElement(shape, ksize, anchor);
1893         for( i = 0; i < size; i++ )
1894             element->values[i] = elem.ptr()[i];
1895     }
1896
1897     return element;
1898 }
1899
1900
1901 CV_IMPL void
1902 cvReleaseStructuringElement( IplConvKernel ** element )
1903 {
1904     if( !element )
1905         CV_Error( CV_StsNullPtr, "" );
1906     cvFree( element );
1907 }
1908
1909
1910 static void convertConvKernel( const IplConvKernel* src, cv::Mat& dst, cv::Point& anchor )
1911 {
1912     if(!src)
1913     {
1914         anchor = cv::Point(1,1);
1915         dst.release();
1916         return;
1917     }
1918     anchor = cv::Point(src->anchorX, src->anchorY);
1919     dst.create(src->nRows, src->nCols, CV_8U);
1920
1921     int i, size = src->nRows*src->nCols;
1922     for( i = 0; i < size; i++ )
1923         dst.ptr()[i] = (uchar)(src->values[i] != 0);
1924 }
1925
1926
1927 CV_IMPL void
1928 cvErode( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element, int iterations )
1929 {
1930     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1931     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1932     cv::Point anchor;
1933     convertConvKernel( element, kernel, anchor );
1934     cv::erode( src, dst, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1935 }
1936
1937
1938 CV_IMPL void
1939 cvDilate( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element, int iterations )
1940 {
1941     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1942     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1943     cv::Point anchor;
1944     convertConvKernel( element, kernel, anchor );
1945     cv::dilate( src, dst, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1946 }
1947
1948
1949 CV_IMPL void
1950 cvMorphologyEx( const void* srcarr, void* dstarr, void*,
1951                 IplConvKernel* element, int op, int iterations )
1952 {
1953     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1954     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1955     cv::Point anchor;
1956     IplConvKernel* temp_element = NULL;
1957     if (!element)
1958     {
1959         temp_element = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT);
1960     } else {
1961         temp_element = element;
1962     }
1963     convertConvKernel( temp_element, kernel, anchor );
1964     if (!element)
1965     {
1966         cvReleaseStructuringElement(&temp_element);
1967     }
1968     cv::morphologyEx( src, dst, op, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1969 }
1970
1971 /* End of file. */