ported changes from PR #2867
[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.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.data + i*elem.step;
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.data, 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((Ipp##data_type *)_src->data, (int)_src->step[0], (Ipp##data_type *)dst.data, (int)dst.step[0],\
1177                                             roiSize, ippBorderRepl, 0, pSpec, pBuffer));\
1178                 else\
1179                     ok = (0 <= ippiDilateBorder_##flavor((Ipp##data_type *)_src->data, (int)_src->step[0], (Ipp##data_type *)dst.data, (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.data kernelSize, &specSize))\
1196                     return false;\
1197                 bool ok = false;\
1198                 IppiMorphState* pState = (IppiMorphState*)ippMalloc(specSize);\
1199                 if (ippiMorphologyInit_##flavor(roiSize.width, kernel.data, kernelSize, point, pState) >= 0)\
1200                 {\
1201                     if (op == MORPH_ERODE)\
1202                         ok = ippiErodeBorderReplicate_##flavor((Ipp##data_type *)_src->data, (int)_src->step[0],\
1203                             (Ipp##data_type *)dst.data, (int)dst.step[0],\
1204                             roiSize, ippBorderRepl, pState ) >= 0;\
1205                     else\
1206                         ok = ippiDilateBorderReplicate_##flavor((Ipp##data_type *)_src->data, (int)_src->step[0],\
1207                             (Ipp##data_type *)dst.data, (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             return false;
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((Ipp##data_type *)_src->data, (int)_src->step[0], (Ipp##data_type *)dst.data, (int)dst.step[0], roiSize, kernelSize, point, buffer));\
1243                 return (0 <= ippiFilterMaxBorderReplicate_##flavor((Ipp##data_type *)_src->data, (int)_src->step[0], (Ipp##data_type *)dst.data, (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             return false;
1257         }
1258         #undef IPP_MORPH_CASE
1259
1260 #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8
1261         return false; /// It disables false positive warning in GCC 4.8.2
1262 #endif
1263     }
1264 }
1265
1266 static bool IPPMorphOp(int op, InputArray _src, OutputArray _dst,
1267     const Mat& _kernel, Point anchor, int iterations,
1268     int borderType, const Scalar &borderValue)
1269 {
1270     Mat src = _src.getMat(), kernel = _kernel;
1271     int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
1272
1273     if( !( depth == CV_8U || depth == CV_32F ) || !(cn == 1 || cn == 3 || cn == 4) ||
1274         !( borderType == cv::BORDER_REPLICATE || (borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue()) )
1275         || !( op == MORPH_DILATE || op == MORPH_ERODE) || _src.isSubmatrix() )
1276         return false;
1277
1278     if( borderType == cv::BORDER_CONSTANT && kernel.data )
1279     {
1280         int x, y;
1281         for( y = 0; y < kernel.rows; y++ )
1282         {
1283             if( kernel.at<uchar>(y, anchor.x) != 0 )
1284                 continue;
1285             for( x = 0; x < kernel.cols; x++ )
1286             {
1287                 if( kernel.at<uchar>(y,x) != 0 )
1288                     return false;
1289             }
1290         }
1291         for( x = 0; x < kernel.cols; x++ )
1292         {
1293             if( kernel.at<uchar>(anchor.y, x) != 0 )
1294                 continue;
1295             for( y = 0; y < kernel.rows; y++ )
1296             {
1297                 if( kernel.at<uchar>(y,x) != 0 )
1298                     return false;
1299             }
1300         }
1301
1302     }
1303     Size ksize = kernel.data ? kernel.size() : Size(3,3);
1304
1305     _dst.create( src.size(), src.type() );
1306     Mat dst = _dst.getMat();
1307
1308     if( iterations == 0 || kernel.rows*kernel.cols == 1 )
1309     {
1310         src.copyTo(dst);
1311         return true;
1312     }
1313
1314     bool rectKernel = false;
1315     if( !kernel.data )
1316     {
1317         ksize = Size(1+iterations*2,1+iterations*2);
1318         anchor = Point(iterations, iterations);
1319         rectKernel = true;
1320         iterations = 1;
1321     }
1322     else if( iterations >= 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
1323     {
1324         ksize = Size(ksize.width + (iterations-1)*(ksize.width-1),
1325              ksize.height + (iterations-1)*(ksize.height-1)),
1326         anchor = Point(anchor.x*iterations, anchor.y*iterations);
1327         kernel = Mat();
1328         rectKernel = true;
1329         iterations = 1;
1330     }
1331
1332     // TODO: implement the case of iterations > 1.
1333     if( iterations > 1 )
1334         return false;
1335
1336     if (IPPMorphReplicate( op, src, dst, kernel, ksize, anchor, rectKernel ))
1337         return true;
1338
1339     return false;
1340 }
1341 #endif
1342
1343 #ifdef HAVE_OPENCL
1344
1345 static bool ocl_morphOp(InputArray _src, OutputArray _dst, InputArray _kernel,
1346                         Point anchor, int iterations, int op, int borderType,
1347                         const Scalar& borderValue)
1348 {
1349     if (borderType != BORDER_CONSTANT)
1350         return false;
1351
1352     Mat kernel = _kernel.getMat();
1353     Size ksize = kernel.data ? kernel.size() : Size(3,3);
1354     anchor = normalizeAnchor(anchor, ksize);
1355
1356     if (iterations == 0 || kernel.rows*kernel.cols == 1)
1357     {
1358         _src.copyTo(_dst);
1359         return true;
1360     }
1361
1362     if (!kernel.data)
1363     {
1364         kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
1365         anchor = Point(iterations, iterations);
1366         iterations = 1;
1367     }
1368     else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
1369     {
1370         anchor = Point(anchor.x*iterations, anchor.y*iterations);
1371         kernel = getStructuringElement(MORPH_RECT,
1372                                        Size(ksize.width + (iterations-1)*(ksize.width-1),
1373                                             ksize.height + (iterations-1)*(ksize.height-1)),
1374                                        anchor);
1375         iterations = 1;
1376     }
1377
1378     const ocl::Device & dev = ocl::Device::getDefault();
1379     int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
1380     bool doubleSupport = dev.doubleFPConfig() > 0;
1381
1382     if (depth == CV_64F && !doubleSupport)
1383         return false;
1384
1385     UMat src = _src.getUMat();
1386
1387 #ifdef ANDROID
1388     size_t localThreads[2] = { 16, 8 };
1389 #else
1390     size_t localThreads[2] = { 16, 16 };
1391 #endif
1392     size_t globalThreads[2] = { src.cols, src.rows };
1393
1394     if (localThreads[0]*localThreads[1] * 2 < (localThreads[0] + ksize.width - 1) * (localThreads[1] + ksize.height - 1))
1395         return false;
1396
1397     // build processing
1398     String processing;
1399     Mat kernel8u;
1400     kernel.convertTo(kernel8u, CV_8U);
1401     for (int y = 0; y < kernel8u.rows; ++y)
1402         for (int x = 0; x < kernel8u.cols; ++x)
1403             if (kernel8u.at<uchar>(y, x) != 0)
1404                 processing += format("PROCESS(%d,%d)", y, x);
1405
1406     static const char * const op2str[] = { "ERODE", "DILATE" };
1407     String buildOptions = format("-D RADIUSX=%d -D RADIUSY=%d -D LSIZE0=%d -D LSIZE1=%d -D %s%s"
1408                                  " -D PROCESS_ELEMS=%s -D T=%s -D DEPTH_%d -D cn=%d -D T1=%s", anchor.x, anchor.y,
1409                                  (int)localThreads[0], (int)localThreads[1], op2str[op],
1410                                  doubleSupport ? " -D DOUBLE_SUPPORT" : "", processing.c_str(),
1411                                  ocl::typeToStr(type), depth, cn, ocl::typeToStr(depth));
1412
1413     std::vector<ocl::Kernel> kernels(iterations);
1414     for (int i = 0; i < iterations; i++)
1415     {
1416         kernels[i].create("morph", ocl::imgproc::morph_oclsrc, buildOptions);
1417         if (kernels[i].empty())
1418             return false;
1419     }
1420
1421     _dst.create(src.size(), src.type());
1422     UMat dst = _dst.getUMat();
1423
1424     if (iterations == 1 && src.u != dst.u)
1425     {
1426         Size wholesize;
1427         Point ofs;
1428         src.locateROI(wholesize, ofs);
1429         int wholecols = wholesize.width, wholerows = wholesize.height;
1430
1431         kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst),
1432                         ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows);
1433
1434         return kernels[0].run(2, globalThreads, localThreads, false);
1435     }
1436
1437     for (int i = 0; i < iterations; i++)
1438     {
1439         UMat source;
1440         Size wholesize;
1441         Point ofs;
1442
1443         if (i == 0)
1444         {
1445             int cols =  src.cols, rows = src.rows;
1446             src.locateROI(wholesize, ofs);
1447             src.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x);
1448             if(src.u != dst.u)
1449                 source = src;
1450             else
1451                 src.copyTo(source);
1452
1453             src.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
1454             source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
1455         }
1456         else
1457         {
1458             int cols =  dst.cols, rows = dst.rows;
1459             dst.locateROI(wholesize, ofs);
1460             dst.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x);
1461             dst.copyTo(source);
1462             dst.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
1463             source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
1464         }
1465         source.locateROI(wholesize, ofs);
1466
1467         kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst),
1468                         ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height);
1469
1470         if (!kernels[i].run(2, globalThreads, localThreads, false))
1471             return false;
1472     }
1473
1474     return true;
1475 }
1476
1477 #endif
1478
1479 static void morphOp( int op, InputArray _src, OutputArray _dst,
1480                      InputArray _kernel,
1481                      Point anchor, int iterations,
1482                      int borderType, const Scalar& borderValue )
1483 {
1484     CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
1485                borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() &&
1486                (op == MORPH_ERODE || op == MORPH_DILATE),
1487                ocl_morphOp(_src, _dst, _kernel, anchor, iterations, op, borderType, borderValue) )
1488
1489     Mat kernel = _kernel.getMat();
1490     Size ksize = kernel.data ? kernel.size() : Size(3,3);
1491     anchor = normalizeAnchor(anchor, ksize);
1492
1493     if (iterations == 0 || kernel.rows*kernel.cols == 1)
1494     {
1495         _src.copyTo(_dst);
1496         return;
1497     }
1498
1499     if (!kernel.data)
1500     {
1501         kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
1502         anchor = Point(iterations, iterations);
1503         iterations = 1;
1504     }
1505     else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
1506     {
1507         anchor = Point(anchor.x*iterations, anchor.y*iterations);
1508         kernel = getStructuringElement(MORPH_RECT,
1509                                        Size(ksize.width + (iterations-1)*(ksize.width-1),
1510                                             ksize.height + (iterations-1)*(ksize.height-1)),
1511                                        anchor);
1512         iterations = 1;
1513     }
1514
1515 #if IPP_VERSION_X100 >= 801
1516     if( IPPMorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue) )
1517         return;
1518 #endif
1519
1520     Mat src = _src.getMat();
1521     _dst.create( src.size(), src.type() );
1522     Mat dst = _dst.getMat();
1523
1524     int nStripes = 1;
1525 #if defined HAVE_TEGRA_OPTIMIZATION
1526     if (src.data != dst.data && iterations == 1 &&  //NOTE: threads are not used for inplace processing
1527         (borderType & BORDER_ISOLATED) == 0 && //TODO: check border types
1528         src.rows >= 64 ) //NOTE: just heuristics
1529         nStripes = 4;
1530 #endif
1531
1532     parallel_for_(Range(0, nStripes),
1533                   MorphologyRunner(src, dst, nStripes, iterations, op, kernel, anchor, borderType, borderType, borderValue));
1534 }
1535
1536 }
1537
1538 void cv::erode( InputArray src, OutputArray dst, InputArray kernel,
1539                 Point anchor, int iterations,
1540                 int borderType, const Scalar& borderValue )
1541 {
1542     morphOp( MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType, borderValue );
1543 }
1544
1545
1546 void cv::dilate( InputArray src, OutputArray dst, InputArray kernel,
1547                  Point anchor, int iterations,
1548                  int borderType, const Scalar& borderValue )
1549 {
1550     morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue );
1551 }
1552
1553 #ifdef HAVE_OPENCL
1554
1555 namespace cv {
1556
1557 static bool ocl_morphologyEx(InputArray _src, OutputArray _dst, int op,
1558                              InputArray kernel, Point anchor, int iterations,
1559                              int borderType, const Scalar& borderValue)
1560 {
1561     _dst.createSameSize(_src, _src.type());
1562     UMat temp;
1563
1564     switch( op )
1565     {
1566     case MORPH_ERODE:
1567         ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue );
1568         break;
1569     case MORPH_DILATE:
1570         ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue );
1571         break;
1572     case MORPH_OPEN:
1573         ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue );
1574         ocl_morphOp( temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue );
1575         break;
1576     case MORPH_CLOSE:
1577         ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue );
1578         ocl_morphOp( temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue );
1579         break;
1580     case MORPH_GRADIENT:
1581     // ??
1582         ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue );
1583         ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue );
1584         subtract(_dst, temp, _dst);
1585         break;
1586     case MORPH_TOPHAT:
1587     // ??
1588         ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue );
1589         ocl_morphOp( temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue );
1590         subtract(_src, _dst, _dst);
1591         break;
1592     case MORPH_BLACKHAT:
1593     // ??
1594         ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue );
1595         ocl_morphOp( temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue );
1596         subtract(_dst, _src, _dst);
1597         break;
1598     default:
1599         CV_Error( CV_StsBadArg, "unknown morphological operation" );
1600     }
1601
1602     return true;
1603 }
1604
1605 }
1606
1607 #endif
1608
1609 void cv::morphologyEx( InputArray _src, OutputArray _dst, int op,
1610                        InputArray kernel, Point anchor, int iterations,
1611                        int borderType, const Scalar& borderValue )
1612 {
1613 #ifdef HAVE_OPENCL
1614     Size ksize = kernel.size();
1615     CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
1616         anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 &&
1617         borderType == cv::BORDER_CONSTANT,
1618         ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue))
1619 #endif
1620
1621     Mat src = _src.getMat(), temp;
1622     _dst.create(src.size(), src.type());
1623     Mat dst = _dst.getMat();
1624
1625     switch( op )
1626     {
1627     case MORPH_ERODE:
1628         erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
1629         break;
1630     case MORPH_DILATE:
1631         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1632         break;
1633     case MORPH_OPEN:
1634         erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
1635         dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
1636         break;
1637     case CV_MOP_CLOSE:
1638         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1639         erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
1640         break;
1641     case CV_MOP_GRADIENT:
1642         erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
1643         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1644         dst -= temp;
1645         break;
1646     case CV_MOP_TOPHAT:
1647         if( src.data != dst.data )
1648             temp = dst;
1649         erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
1650         dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue );
1651         dst = src - temp;
1652         break;
1653     case CV_MOP_BLACKHAT:
1654         if( src.data != dst.data )
1655             temp = dst;
1656         dilate( src, temp, kernel, anchor, iterations, borderType, borderValue );
1657         erode( temp, temp, kernel, anchor, iterations, borderType, borderValue );
1658         dst = temp - src;
1659         break;
1660     default:
1661         CV_Error( CV_StsBadArg, "unknown morphological operation" );
1662     }
1663 }
1664
1665 CV_IMPL IplConvKernel *
1666 cvCreateStructuringElementEx( int cols, int rows,
1667                               int anchorX, int anchorY,
1668                               int shape, int *values )
1669 {
1670     cv::Size ksize = cv::Size(cols, rows);
1671     cv::Point anchor = cv::Point(anchorX, anchorY);
1672     CV_Assert( cols > 0 && rows > 0 && anchor.inside(cv::Rect(0,0,cols,rows)) &&
1673                (shape != CV_SHAPE_CUSTOM || values != 0));
1674
1675     int i, size = rows * cols;
1676     int element_size = sizeof(IplConvKernel) + size*sizeof(int);
1677     IplConvKernel *element = (IplConvKernel*)cvAlloc(element_size + 32);
1678
1679     element->nCols = cols;
1680     element->nRows = rows;
1681     element->anchorX = anchorX;
1682     element->anchorY = anchorY;
1683     element->nShiftR = shape < CV_SHAPE_ELLIPSE ? shape : CV_SHAPE_CUSTOM;
1684     element->values = (int*)(element + 1);
1685
1686     if( shape == CV_SHAPE_CUSTOM )
1687     {
1688         for( i = 0; i < size; i++ )
1689             element->values[i] = values[i];
1690     }
1691     else
1692     {
1693         cv::Mat elem = cv::getStructuringElement(shape, ksize, anchor);
1694         for( i = 0; i < size; i++ )
1695             element->values[i] = elem.data[i];
1696     }
1697
1698     return element;
1699 }
1700
1701
1702 CV_IMPL void
1703 cvReleaseStructuringElement( IplConvKernel ** element )
1704 {
1705     if( !element )
1706         CV_Error( CV_StsNullPtr, "" );
1707     cvFree( element );
1708 }
1709
1710
1711 static void convertConvKernel( const IplConvKernel* src, cv::Mat& dst, cv::Point& anchor )
1712 {
1713     if(!src)
1714     {
1715         anchor = cv::Point(1,1);
1716         dst.release();
1717         return;
1718     }
1719     anchor = cv::Point(src->anchorX, src->anchorY);
1720     dst.create(src->nRows, src->nCols, CV_8U);
1721
1722     int i, size = src->nRows*src->nCols;
1723     for( i = 0; i < size; i++ )
1724         dst.data[i] = (uchar)(src->values[i] != 0);
1725 }
1726
1727
1728 CV_IMPL void
1729 cvErode( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element, int iterations )
1730 {
1731     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1732     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1733     cv::Point anchor;
1734     convertConvKernel( element, kernel, anchor );
1735     cv::erode( src, dst, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1736 }
1737
1738
1739 CV_IMPL void
1740 cvDilate( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element, int iterations )
1741 {
1742     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1743     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1744     cv::Point anchor;
1745     convertConvKernel( element, kernel, anchor );
1746     cv::dilate( src, dst, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1747 }
1748
1749
1750 CV_IMPL void
1751 cvMorphologyEx( const void* srcarr, void* dstarr, void*,
1752                 IplConvKernel* element, int op, int iterations )
1753 {
1754     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1755     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1756     cv::Point anchor;
1757     IplConvKernel* temp_element = NULL;
1758     if (!element)
1759     {
1760         temp_element = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT);
1761     } else {
1762         temp_element = element;
1763     }
1764     convertConvKernel( temp_element, kernel, anchor );
1765     if (!element)
1766     {
1767         cvReleaseStructuringElement(&temp_element);
1768     }
1769     cv::morphologyEx( src, dst, op, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1770 }
1771
1772 /* End of file. */