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 #include <iostream>
47 #include "hal_replacement.hpp"
48 #include "opencv2/core/hal/intrin.hpp"
49 #include <opencv2/core/utils/configuration.private.hpp>
50 
51 #include "morph.simd.hpp"
52 #include "morph.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content
53 
54 
55 /****************************************************************************************\
56                      Basic Morphological Operations: Erosion & Dilation
57 \****************************************************************************************/
58 
59 namespace cv {
60 
61 /////////////////////////////////// External Interface /////////////////////////////////////
62 
getMorphologyRowFilter(int op,int type,int ksize,int anchor)63 Ptr<BaseRowFilter> getMorphologyRowFilter(int op, int type, int ksize, int anchor)
64 {
65     CV_INSTRUMENT_REGION();
66 
67     CV_CPU_DISPATCH(getMorphologyRowFilter, (op, type, ksize, anchor),
68         CV_CPU_DISPATCH_MODES_ALL);
69 }
70 
getMorphologyColumnFilter(int op,int type,int ksize,int anchor)71 Ptr<BaseColumnFilter> getMorphologyColumnFilter(int op, int type, int ksize, int anchor)
72 {
73     CV_INSTRUMENT_REGION();
74 
75     CV_CPU_DISPATCH(getMorphologyColumnFilter, (op, type, ksize, anchor),
76         CV_CPU_DISPATCH_MODES_ALL);
77 }
78 
79 
getMorphologyFilter(int op,int type,InputArray _kernel,Point anchor)80 Ptr<BaseFilter> getMorphologyFilter(int op, int type, InputArray _kernel, Point anchor)
81 {
82     CV_INSTRUMENT_REGION();
83 
84     Mat kernel = _kernel.getMat();
85     CV_CPU_DISPATCH(getMorphologyFilter, (op, type, kernel, anchor),
86         CV_CPU_DISPATCH_MODES_ALL);
87 }
88 
89 
createMorphologyFilter(int op,int type,InputArray _kernel,Point anchor,int _rowBorderType,int _columnBorderType,const Scalar & _borderValue)90 Ptr<FilterEngine> createMorphologyFilter(
91         int op, int type, InputArray _kernel,
92         Point anchor, int _rowBorderType, int _columnBorderType,
93         const Scalar& _borderValue)
94 {
95     Mat kernel = _kernel.getMat();
96     anchor = normalizeAnchor(anchor, kernel.size());
97 
98     Ptr<BaseRowFilter> rowFilter;
99     Ptr<BaseColumnFilter> columnFilter;
100     Ptr<BaseFilter> filter2D;
101 
102     if( countNonZero(kernel) == kernel.rows*kernel.cols )
103     {
104         // rectangular structuring element
105         rowFilter = getMorphologyRowFilter(op, type, kernel.cols, anchor.x);
106         columnFilter = getMorphologyColumnFilter(op, type, kernel.rows, anchor.y);
107     }
108     else
109         filter2D = getMorphologyFilter(op, type, kernel, anchor);
110 
111     Scalar borderValue = _borderValue;
112     if( (_rowBorderType == BORDER_CONSTANT || _columnBorderType == BORDER_CONSTANT) &&
113             borderValue == morphologyDefaultBorderValue() )
114     {
115         int depth = CV_MAT_DEPTH(type);
116         CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_16S ||
117                    depth == CV_32F || depth == CV_64F );
118         if( op == MORPH_ERODE )
119             borderValue = Scalar::all( depth == CV_8U ? (double)UCHAR_MAX :
120                                        depth == CV_16U ? (double)USHRT_MAX :
121                                        depth == CV_16S ? (double)SHRT_MAX :
122                                        depth == CV_32F ? (double)FLT_MAX : DBL_MAX);
123         else
124             borderValue = Scalar::all( depth == CV_8U || depth == CV_16U ?
125                                            0. :
126                                        depth == CV_16S ? (double)SHRT_MIN :
127                                        depth == CV_32F ? (double)-FLT_MAX : -DBL_MAX);
128     }
129 
130     return makePtr<FilterEngine>(filter2D, rowFilter, columnFilter,
131                                  type, type, type, _rowBorderType, _columnBorderType, borderValue );
132 }
133 
134 
getStructuringElement(int shape,Size ksize,Point anchor)135 Mat getStructuringElement(int shape, Size ksize, Point anchor)
136 {
137     int i, j;
138     int r = 0, c = 0;
139     double inv_r2 = 0;
140 
141     CV_Assert( shape == MORPH_RECT || shape == MORPH_CROSS || shape == MORPH_ELLIPSE );
142 
143     anchor = normalizeAnchor(anchor, ksize);
144 
145     if( ksize == Size(1,1) )
146         shape = MORPH_RECT;
147 
148     if( shape == MORPH_ELLIPSE )
149     {
150         r = ksize.height/2;
151         c = ksize.width/2;
152         inv_r2 = r ? 1./((double)r*r) : 0;
153     }
154 
155     Mat elem(ksize, CV_8U);
156 
157     for( i = 0; i < ksize.height; i++ )
158     {
159         uchar* ptr = elem.ptr(i);
160         int j1 = 0, j2 = 0;
161 
162         if( shape == MORPH_RECT || (shape == MORPH_CROSS && i == anchor.y) )
163             j2 = ksize.width;
164         else if( shape == MORPH_CROSS )
165             j1 = anchor.x, j2 = j1 + 1;
166         else
167         {
168             int dy = i - r;
169             if( std::abs(dy) <= r )
170             {
171                 int dx = saturate_cast<int>(c*std::sqrt((r*r - dy*dy)*inv_r2));
172                 j1 = std::max( c - dx, 0 );
173                 j2 = std::min( c + dx + 1, ksize.width );
174             }
175         }
176 
177         for( j = 0; j < j1; j++ )
178             ptr[j] = 0;
179         for( ; j < j2; j++ )
180             ptr[j] = 1;
181         for( ; j < ksize.width; j++ )
182             ptr[j] = 0;
183     }
184 
185     return elem;
186 }
187 
188 // ===== 1. replacement implementation
189 
halMorph(int op,int src_type,int dst_type,uchar * src_data,size_t src_step,uchar * dst_data,size_t dst_step,int width,int height,int roi_width,int roi_height,int roi_x,int roi_y,int roi_width2,int roi_height2,int roi_x2,int roi_y2,int kernel_type,uchar * kernel_data,size_t kernel_step,int kernel_width,int kernel_height,int anchor_x,int anchor_y,int borderType,const double borderValue[4],int iterations,bool isSubmatrix)190 static bool halMorph(int op, int src_type, int dst_type,
191               uchar * src_data, size_t src_step,
192               uchar * dst_data, size_t dst_step,
193               int width, int height,
194               int roi_width, int roi_height, int roi_x, int roi_y,
195               int roi_width2, int roi_height2, int roi_x2, int roi_y2,
196               int kernel_type, uchar * kernel_data, size_t kernel_step,
197               int kernel_width, int kernel_height, int anchor_x, int anchor_y,
198               int borderType, const double borderValue[4], int iterations, bool isSubmatrix)
199 {
200     cvhalFilter2D * ctx;
201     int res = cv_hal_morphInit(&ctx, op, src_type, dst_type, width, height,
202                                kernel_type, kernel_data, kernel_step, kernel_width, kernel_height,
203                                anchor_x, anchor_y,
204                                borderType, borderValue,
205                                iterations, isSubmatrix, src_data == dst_data);
206     if (res != CV_HAL_ERROR_OK)
207         return false;
208 
209     res = cv_hal_morph(ctx, src_data, src_step, dst_data, dst_step, width, height,
210                        roi_width, roi_height,
211                        roi_x, roi_y,
212                        roi_width2, roi_height2,
213                        roi_x2, roi_y2);
214     bool success = (res == CV_HAL_ERROR_OK);
215 
216     res = cv_hal_morphFree(ctx);
217     if (res != CV_HAL_ERROR_OK)
218         return false;
219 
220     return success;
221 }
222 
223 // ===== 2. IPP implementation
224 #if 0 //defined HAVE_IPP
225 #ifdef HAVE_IPP_IW
226 static inline IwiMorphologyType ippiGetMorphologyType(int morphOp)
227 {
228     return morphOp == MORPH_ERODE ? iwiMorphErode :
229         morphOp == MORPH_DILATE   ? iwiMorphDilate :
230         morphOp == MORPH_OPEN     ? iwiMorphOpen :
231         morphOp == MORPH_CLOSE    ? iwiMorphClose :
232         morphOp == MORPH_GRADIENT ? iwiMorphGradient :
233         morphOp == MORPH_TOPHAT   ? iwiMorphTophat :
234         morphOp == MORPH_BLACKHAT ? iwiMorphBlackhat : (IwiMorphologyType)-1;
235 }
236 #endif
237 
238 static bool ippMorph(int op, int src_type, int dst_type,
239               const uchar * src_data, size_t src_step,
240               uchar * dst_data, size_t dst_step,
241               int width, int height,
242               int roi_width, int roi_height, int roi_x, int roi_y,
243               int roi_width2, int roi_height2, int roi_x2, int roi_y2,
244               int kernel_type, uchar * kernel_data, size_t kernel_step,
245               int kernel_width, int kernel_height, int anchor_x, int anchor_y,
246               int borderType, const double borderValue[4], int iterations, bool isSubmatrix)
247 {
248 #ifdef HAVE_IPP_IW
249     CV_INSTRUMENT_REGION_IPP();
250 
251 #if IPP_VERSION_X100 < 201800
252     // Problem with SSE42 optimizations performance
253     if(cv::ipp::getIppTopFeatures() == ippCPUID_SSE42)
254         return false;
255 
256     // Different mask flipping
257     if(op == MORPH_GRADIENT)
258         return false;
259 
260     // Integer overflow bug
261     if(src_step >= IPP_MAX_32S ||
262        src_step*height >= IPP_MAX_32S)
263         return false;
264 #endif
265 
266 #if IPP_VERSION_X100 < 201801
267     // Problem with AVX512 optimizations performance
268     if(cv::ipp::getIppTopFeatures()&ippCPUID_AVX512F)
269         return false;
270 
271     // Multiple iterations on small mask is not effective in current integration
272     // Inplace imitation for 3x3 kernel is not efficient
273     // Advanced morphology for small mask introduces degradations
274     if((iterations > 1 || src_data == dst_data || (op != MORPH_ERODE && op != MORPH_DILATE)) && kernel_width*kernel_height < 25)
275         return false;
276 
277     // Skip even mask sizes for advanced morphology since they can produce out of spec writes
278     if((op != MORPH_ERODE && op != MORPH_DILATE) && (!(kernel_width&1) || !(kernel_height&1)))
279         return false;
280 #endif
281 
282     IppAutoBuffer<Ipp8u>        kernelTempBuffer;
283     ::ipp::IwiBorderSize        iwBorderSize;
284     ::ipp::IwiBorderSize        iwBorderSize2;
285     ::ipp::IwiBorderType        iwBorderType;
286     ::ipp::IwiBorderType        iwBorderType2;
287     ::ipp::IwiImage             iwMask;
288     ::ipp::IwiImage             iwInter;
289     ::ipp::IwiSize              initSize(width, height);
290     ::ipp::IwiSize              kernelSize(kernel_width, kernel_height);
291     IppDataType                 type        = ippiGetDataType(CV_MAT_DEPTH(src_type));
292     int                         channels    = CV_MAT_CN(src_type);
293     IwiMorphologyType           morphType   = ippiGetMorphologyType(op);
294 
295     CV_UNUSED(isSubmatrix);
296 
297     if((int)morphType < 0)
298         return false;
299 
300     if(iterations > 1 && morphType != iwiMorphErode && morphType != iwiMorphDilate)
301         return false;
302 
303     if(src_type != dst_type)
304         return false;
305 
306     if(!ippiCheckAnchor(anchor_x, anchor_y, kernel_width, kernel_height))
307         return false;
308 
309     try
310     {
311         ::ipp::IwiImage iwSrc(initSize, type, channels, ::ipp::IwiBorderSize(roi_x, roi_y, roi_width-roi_x-width, roi_height-roi_y-height), (void*)src_data, src_step);
312         ::ipp::IwiImage iwDst(initSize, type, channels, ::ipp::IwiBorderSize(roi_x2, roi_y2, roi_width2-roi_x2-width, roi_height2-roi_y2-height), (void*)dst_data, dst_step);
313 
314         iwBorderSize = ::ipp::iwiSizeToBorderSize(kernelSize);
315         iwBorderType = ippiGetBorder(iwSrc, borderType, iwBorderSize);
316         if(!iwBorderType)
317             return false;
318         if(iterations > 1)
319         {
320             // Check dst border for second and later iterations
321             iwBorderSize2 = ::ipp::iwiSizeToBorderSize(kernelSize);
322             iwBorderType2 = ippiGetBorder(iwDst, borderType, iwBorderSize2);
323             if(!iwBorderType2)
324                 return false;
325         }
326 
327         if(morphType != iwiMorphErode && morphType != iwiMorphDilate && morphType != iwiMorphGradient)
328         {
329             // For now complex morphology support only InMem around all sides. This will be improved later.
330             if((iwBorderType&ippBorderInMem) && (iwBorderType&ippBorderInMem) != ippBorderInMem)
331                 return false;
332 
333             if((iwBorderType&ippBorderInMem) == ippBorderInMem)
334             {
335                 iwBorderType &= ~ippBorderInMem;
336                 iwBorderType &=  ippBorderFirstStageInMem;
337             }
338         }
339 
340         if(iwBorderType.StripFlags() == ippBorderConst)
341         {
342             if(Vec<double, 4>(borderValue) == morphologyDefaultBorderValue())
343                 iwBorderType.SetType(ippBorderDefault);
344             else
345                 iwBorderType.m_value = ::ipp::IwValueFloat(borderValue[0], borderValue[1], borderValue[2], borderValue[3]);
346         }
347 
348         iwMask.Init(ippiSize(kernel_width, kernel_height), ippiGetDataType(CV_MAT_DEPTH(kernel_type)), CV_MAT_CN(kernel_type), 0, kernel_data, kernel_step);
349 
350         ::ipp::IwiImage iwMaskLoc = iwMask;
351         if(morphType == iwiMorphDilate)
352         {
353             iwMaskLoc.Alloc(iwMask.m_size, iwMask.m_dataType, iwMask.m_channels);
354             ::ipp::iwiMirror(iwMask, iwMaskLoc, ippAxsBoth);
355             iwMask = iwMaskLoc;
356         }
357 
358         if(iterations > 1)
359         {
360             // OpenCV uses in mem border from dst for two and more iterations, so we need to keep this border in intermediate image
361             iwInter.Alloc(initSize, type, channels, iwBorderSize2);
362 
363             ::ipp::IwiImage *pSwap[2] = {&iwInter, &iwDst};
364             CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterMorphology, iwSrc, iwInter, morphType, iwMask, ::ipp::IwDefault(), iwBorderType);
365 
366             // Copy border only
367             {
368                 if(iwBorderSize2.top)
369                 {
370                     ::ipp::IwiRoi   borderRoi(-iwBorderSize2.left, -iwBorderSize2.top, iwDst.m_size.width+iwBorderSize2.left+iwBorderSize2.right, iwBorderSize2.top);
371                     ::ipp::IwiImage iwInterRoi = iwInter.GetRoiImage(borderRoi);
372                     ::ipp::iwiCopy(iwDst.GetRoiImage(borderRoi), iwInterRoi);
373                 }
374                 if(iwBorderSize2.bottom)
375                 {
376                     ::ipp::IwiRoi   borderRoi(-iwBorderSize2.left, iwDst.m_size.height, iwDst.m_size.width+iwBorderSize2.left+iwBorderSize2.right, iwBorderSize2.bottom);
377                     ::ipp::IwiImage iwInterRoi = iwInter.GetRoiImage(borderRoi);
378                     ::ipp::iwiCopy(iwDst.GetRoiImage(borderRoi), iwInterRoi);
379                 }
380                 if(iwBorderSize2.left)
381                 {
382                     ::ipp::IwiRoi   borderRoi(-iwBorderSize2.left, 0, iwBorderSize2.left, iwDst.m_size.height);
383                     ::ipp::IwiImage iwInterRoi = iwInter.GetRoiImage(borderRoi);
384                     ::ipp::iwiCopy(iwDst.GetRoiImage(borderRoi), iwInterRoi);
385                 }
386                 if(iwBorderSize2.right)
387                 {
388                     ::ipp::IwiRoi   borderRoi(iwDst.m_size.width, 0, iwBorderSize2.left, iwDst.m_size.height);
389                     ::ipp::IwiImage iwInterRoi = iwInter.GetRoiImage(borderRoi);
390                     ::ipp::iwiCopy(iwDst.GetRoiImage(borderRoi), iwInterRoi);
391                 }
392             }
393 
394             iwBorderType2.SetType(iwBorderType);
395             for(int i = 0; i < iterations-1; i++)
396                 CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterMorphology, *pSwap[i&0x1], *pSwap[(i+1)&0x1], morphType, iwMask, ::ipp::IwDefault(), iwBorderType2);
397             if(iterations&0x1)
398                 CV_INSTRUMENT_FUN_IPP(::ipp::iwiCopy, iwInter, iwDst);
399         }
400         else
401         {
402             if(src_data == dst_data)
403             {
404                 iwInter.Alloc(initSize, type, channels);
405 
406                 CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterMorphology, iwSrc, iwInter, morphType, iwMask, ::ipp::IwDefault(), iwBorderType);
407                 CV_INSTRUMENT_FUN_IPP(::ipp::iwiCopy, iwInter, iwDst);
408             }
409             else
410                 CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterMorphology, iwSrc, iwDst, morphType, iwMask, ::ipp::IwDefault(), iwBorderType);
411         }
412     }
413     catch(const ::ipp::IwException &)
414     {
415         return false;
416     }
417 
418     return true;
419 #else
420     CV_UNUSED(op); CV_UNUSED(src_type); CV_UNUSED(dst_type); CV_UNUSED(src_data); CV_UNUSED(src_step); CV_UNUSED(dst_data);
421     CV_UNUSED(dst_step); CV_UNUSED(width); CV_UNUSED(height); CV_UNUSED(roi_width); CV_UNUSED(roi_height);
422     CV_UNUSED(roi_x); CV_UNUSED(roi_y); CV_UNUSED(roi_width2); CV_UNUSED(roi_height2); CV_UNUSED(roi_x2); CV_UNUSED(roi_y2);
423     CV_UNUSED(kernel_type); CV_UNUSED(kernel_data); CV_UNUSED(kernel_step); CV_UNUSED(kernel_width); CV_UNUSED(kernel_height);
424     CV_UNUSED(anchor_x); CV_UNUSED(anchor_y); CV_UNUSED(borderType); CV_UNUSED(borderValue); CV_UNUSED(iterations);
425     CV_UNUSED(isSubmatrix);
426     return false;
427 #endif
428 };
429 
430 #endif // HAVE_IPP
431 
432 // ===== 3. Fallback implementation
433 
ocvMorph(int op,int src_type,int dst_type,uchar * src_data,size_t src_step,uchar * dst_data,size_t dst_step,int width,int height,int roi_width,int roi_height,int roi_x,int roi_y,int roi_width2,int roi_height2,int roi_x2,int roi_y2,int kernel_type,uchar * kernel_data,size_t kernel_step,int kernel_width,int kernel_height,int anchor_x,int anchor_y,int borderType,const double borderValue[4],int iterations)434 static void ocvMorph(int op, int src_type, int dst_type,
435                      uchar * src_data, size_t src_step,
436                      uchar * dst_data, size_t dst_step,
437                      int width, int height,
438                      int roi_width, int roi_height, int roi_x, int roi_y,
439                      int roi_width2, int roi_height2, int roi_x2, int roi_y2,
440                      int kernel_type, uchar * kernel_data, size_t kernel_step,
441                      int kernel_width, int kernel_height, int anchor_x, int anchor_y,
442                      int borderType, const double borderValue[4], int iterations)
443 {
444     Mat kernel(Size(kernel_width, kernel_height), kernel_type, kernel_data, kernel_step);
445     Point anchor(anchor_x, anchor_y);
446     Vec<double, 4> borderVal(borderValue);
447     Ptr<FilterEngine> f = createMorphologyFilter(op, src_type, kernel, anchor, borderType, borderType, borderVal);
448     Mat src(Size(width, height), src_type, src_data, src_step);
449     Mat dst(Size(width, height), dst_type, dst_data, dst_step);
450     {
451         Point ofs(roi_x, roi_y);
452         Size wsz(roi_width, roi_height);
453         f->apply( src, dst, wsz, ofs );
454     }
455     {
456         Point ofs(roi_x2, roi_y2);
457         Size wsz(roi_width2, roi_height2);
458         for( int i = 1; i < iterations; i++ )
459             f->apply( dst, dst, wsz, ofs );
460     }
461 }
462 
463 
464 // ===== HAL interface implementation
465 
466 namespace hal {
467 
468 
create(int,int,int,int,int,int,uchar *,size_t,int,int,int,int,int,const double *,int,bool,bool)469 CV_DEPRECATED Ptr<Morph> Morph::create(int , int , int , int , int ,
470                                 int , uchar * , size_t ,
471                                 int , int ,
472                                 int , int ,
473                                 int , const double *,
474                                 int , bool , bool )  { return Ptr<hal::Morph>(); }
475 
476 
morph(int op,int src_type,int dst_type,uchar * src_data,size_t src_step,uchar * dst_data,size_t dst_step,int width,int height,int roi_width,int roi_height,int roi_x,int roi_y,int roi_width2,int roi_height2,int roi_x2,int roi_y2,int kernel_type,uchar * kernel_data,size_t kernel_step,int kernel_width,int kernel_height,int anchor_x,int anchor_y,int borderType,const double borderValue[4],int iterations,bool isSubmatrix)477 void morph(int op, int src_type, int dst_type,
478            uchar * src_data, size_t src_step,
479            uchar * dst_data, size_t dst_step,
480            int width, int height,
481            int roi_width, int roi_height, int roi_x, int roi_y,
482            int roi_width2, int roi_height2, int roi_x2, int roi_y2,
483            int kernel_type, uchar * kernel_data, size_t kernel_step,
484            int kernel_width, int kernel_height, int anchor_x, int anchor_y,
485            int borderType, const double borderValue[4], int iterations, bool isSubmatrix)
486 {
487     {
488         bool res = halMorph(op, src_type, dst_type, src_data, src_step, dst_data, dst_step, width, height,
489                             roi_width, roi_height, roi_x, roi_y,
490                             roi_width2, roi_height2, roi_x2, roi_y2,
491                             kernel_type, kernel_data, kernel_step,
492                             kernel_width, kernel_height, anchor_x, anchor_y,
493                             borderType, borderValue, iterations, isSubmatrix);
494         if (res)
495             return;
496     }
497 
498     /*CV_IPP_RUN_FAST(ippMorph(op, src_type, dst_type, src_data, src_step, dst_data, dst_step, width, height,
499                             roi_width, roi_height, roi_x, roi_y,
500                             roi_width2, roi_height2, roi_x2, roi_y2,
501                             kernel_type, kernel_data, kernel_step,
502                             kernel_width, kernel_height, anchor_x, anchor_y,
503                             borderType, borderValue, iterations, isSubmatrix));*/
504 
505     ocvMorph(op, src_type, dst_type, src_data, src_step, dst_data, dst_step, width, height,
506              roi_width, roi_height, roi_x, roi_y,
507              roi_width2, roi_height2, roi_x2, roi_y2,
508              kernel_type, kernel_data, kernel_step,
509              kernel_width, kernel_height, anchor_x, anchor_y,
510              borderType, borderValue, iterations);
511 }
512 
513 } // cv::hal
514 
515 #ifdef HAVE_OPENCL
516 
517 #define ROUNDUP(sz, n)      ((sz) + (n) - 1 - (((sz) + (n) - 1) % (n)))
518 
ocl_morph3x3_8UC1(InputArray _src,OutputArray _dst,InputArray _kernel,Point anchor,int op,int actual_op=-1,InputArray _extraMat=noArray ())519 static bool ocl_morph3x3_8UC1( InputArray _src, OutputArray _dst, InputArray _kernel, Point anchor,
520                                int op, int actual_op = -1, InputArray _extraMat = noArray())
521 {
522     int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
523     Size ksize = _kernel.size();
524 
525     Mat kernel8u;
526     String processing;
527 
528     bool haveExtraMat = !_extraMat.empty();
529     CV_Assert(actual_op <= 3 || haveExtraMat);
530 
531     _kernel.getMat().convertTo(kernel8u, CV_8U);
532     for (int y = 0; y < kernel8u.rows; ++y)
533         for (int x = 0; x < kernel8u.cols; ++x)
534             if (kernel8u.at<uchar>(y, x) != 0)
535                 processing += format("PROCESS(%d,%d)", y, x);
536 
537     if (anchor.x < 0)
538         anchor.x = ksize.width / 2;
539     if (anchor.y < 0)
540         anchor.y = ksize.height / 2;
541 
542     if (actual_op < 0)
543         actual_op = op;
544 
545     if (type != CV_8UC1 ||
546         !((_src.offset() == 0) && (_src.step() % 4 == 0)) ||
547         !((_src.cols() % 16 == 0) && (_src.rows() % 2 == 0)) ||
548         !(anchor.x == 1 && anchor.y == 1) ||
549         !(ksize.width == 3 && ksize.height == 3))
550         return false;
551 
552     Size size = _src.size();
553     size_t globalsize[2] = { 0, 0 };
554     size_t localsize[2] = { 0, 0 };
555 
556     globalsize[0] = size.width / 16;
557     globalsize[1] = size.height / 2;
558 
559     static const char * const op2str[] = { "OP_ERODE", "OP_DILATE", NULL, NULL, "OP_GRADIENT", "OP_TOPHAT", "OP_BLACKHAT" };
560     String opts = format("-D PROCESS_ELEM_=%s -D %s%s", processing.c_str(), op2str[op],
561                          actual_op == op ? "" : cv::format(" -D %s", op2str[actual_op]).c_str());
562 
563     ocl::Kernel k;
564     k.create("morph3x3_8UC1_cols16_rows2", cv::ocl::imgproc::morph3x3_oclsrc, opts);
565 
566     if (k.empty())
567         return false;
568 
569     UMat src = _src.getUMat();
570     _dst.create(size, CV_MAKETYPE(depth, cn));
571     if (!(_dst.offset() == 0 && _dst.step() % 4 == 0))
572         return false;
573     UMat dst = _dst.getUMat();
574     UMat extraMat = _extraMat.getUMat();
575 
576     int idxArg = k.set(0, ocl::KernelArg::PtrReadOnly(src));
577     idxArg = k.set(idxArg, (int)src.step);
578     idxArg = k.set(idxArg, ocl::KernelArg::PtrWriteOnly(dst));
579     idxArg = k.set(idxArg, (int)dst.step);
580     idxArg = k.set(idxArg, (int)dst.rows);
581     idxArg = k.set(idxArg, (int)dst.cols);
582 
583     if (haveExtraMat)
584     {
585         idxArg = k.set(idxArg, ocl::KernelArg::ReadOnlyNoSize(extraMat));
586     }
587 
588     return k.run(2, globalsize, (localsize[0] == 0) ? NULL : localsize, false);
589 }
590 
ocl_morphSmall(InputArray _src,OutputArray _dst,InputArray _kernel,Point anchor,int borderType,int op,int actual_op=-1,InputArray _extraMat=noArray ())591 static bool ocl_morphSmall( InputArray _src, OutputArray _dst, InputArray _kernel, Point anchor, int borderType,
592                             int op, int actual_op = -1, InputArray _extraMat = noArray())
593 {
594     const ocl::Device & dev = ocl::Device::getDefault();
595     int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), esz = CV_ELEM_SIZE(type);
596     bool doubleSupport = dev.doubleFPConfig() > 0;
597 
598     if (cn > 4 || (!doubleSupport && depth == CV_64F) ||
599         _src.offset() % esz != 0 || _src.step() % esz != 0)
600         return false;
601 
602     bool haveExtraMat = !_extraMat.empty();
603     CV_Assert(actual_op <= 3 || haveExtraMat);
604 
605     Size ksize = _kernel.size();
606     if (anchor.x < 0)
607         anchor.x = ksize.width / 2;
608     if (anchor.y < 0)
609         anchor.y = ksize.height / 2;
610 
611     Size size = _src.size(), wholeSize;
612     bool isolated = (borderType & BORDER_ISOLATED) != 0;
613     borderType &= ~BORDER_ISOLATED;
614     int wdepth = depth, wtype = type;
615     if (depth == CV_8U)
616     {
617         wdepth = CV_32S;
618         wtype = CV_MAKETYPE(wdepth, cn);
619     }
620     char cvt[2][40];
621 
622     const char * const borderMap[] = { "BORDER_CONSTANT", "BORDER_REPLICATE",
623                                        "BORDER_REFLECT", 0, "BORDER_REFLECT_101" };
624     size_t globalsize[2] = { (size_t)size.width, (size_t)size.height };
625 
626     UMat src = _src.getUMat();
627     if (!isolated)
628     {
629         Point ofs;
630         src.locateROI(wholeSize, ofs);
631     }
632 
633     int h = isolated ? size.height : wholeSize.height;
634     int w = isolated ? size.width : wholeSize.width;
635     if (w < ksize.width || h < ksize.height)
636         return false;
637 
638     // Figure out what vector size to use for loading the pixels.
639     int pxLoadNumPixels = cn != 1 || size.width % 4 ? 1 : 4;
640     int pxLoadVecSize = cn * pxLoadNumPixels;
641 
642     // Figure out how many pixels per work item to compute in X and Y
643     // directions.  Too many and we run out of registers.
644     int pxPerWorkItemX = 1, pxPerWorkItemY = 1;
645     if (cn <= 2 && ksize.width <= 4 && ksize.height <= 4)
646     {
647         pxPerWorkItemX = size.width % 8 ? size.width % 4 ? size.width % 2 ? 1 : 2 : 4 : 8;
648         pxPerWorkItemY = size.height % 2 ? 1 : 2;
649     }
650     else if (cn < 4 || (ksize.width <= 4 && ksize.height <= 4))
651     {
652         pxPerWorkItemX = size.width % 2 ? 1 : 2;
653         pxPerWorkItemY = size.height % 2 ? 1 : 2;
654     }
655     globalsize[0] = size.width / pxPerWorkItemX;
656     globalsize[1] = size.height / pxPerWorkItemY;
657 
658     // Need some padding in the private array for pixels
659     int privDataWidth = ROUNDUP(pxPerWorkItemX + ksize.width - 1, pxLoadNumPixels);
660 
661     // Make the global size a nice round number so the runtime can pick
662     // from reasonable choices for the workgroup size
663     const int wgRound = 256;
664     globalsize[0] = ROUNDUP(globalsize[0], wgRound);
665 
666     if (actual_op < 0)
667         actual_op = op;
668 
669     // build processing
670     String processing;
671     Mat kernel8u;
672     _kernel.getMat().convertTo(kernel8u, CV_8U);
673     for (int y = 0; y < kernel8u.rows; ++y)
674         for (int x = 0; x < kernel8u.cols; ++x)
675             if (kernel8u.at<uchar>(y, x) != 0)
676                 processing += format("PROCESS(%d,%d)", y, x);
677 
678 
679     static const char * const op2str[] = { "OP_ERODE", "OP_DILATE", NULL, NULL, "OP_GRADIENT", "OP_TOPHAT", "OP_BLACKHAT" };
680     String opts = format("-D cn=%d "
681             "-D ANCHOR_X=%d -D ANCHOR_Y=%d -D KERNEL_SIZE_X=%d -D KERNEL_SIZE_Y=%d "
682             "-D PX_LOAD_VEC_SIZE=%d -D PX_LOAD_NUM_PX=%d -D DEPTH_%d "
683             "-D PX_PER_WI_X=%d -D PX_PER_WI_Y=%d -D PRIV_DATA_WIDTH=%d -D %s -D %s "
684             "-D PX_LOAD_X_ITERATIONS=%d -D PX_LOAD_Y_ITERATIONS=%d "
685             "-D srcT=%s -D srcT1=%s -D dstT=srcT -D dstT1=srcT1 -D WT=%s -D WT1=%s "
686             "-D convertToWT=%s -D convertToDstT=%s -D PX_LOAD_FLOAT_VEC_CONV=convert_%s -D PROCESS_ELEM_=%s -D %s%s",
687             cn, anchor.x, anchor.y, ksize.width, ksize.height,
688             pxLoadVecSize, pxLoadNumPixels, depth,
689             pxPerWorkItemX, pxPerWorkItemY, privDataWidth, borderMap[borderType],
690             isolated ? "BORDER_ISOLATED" : "NO_BORDER_ISOLATED",
691             privDataWidth / pxLoadNumPixels, pxPerWorkItemY + ksize.height - 1,
692             ocl::typeToStr(type), ocl::typeToStr(depth),
693             haveExtraMat ? ocl::typeToStr(wtype):"srcT",//to prevent overflow - WT
694             haveExtraMat ? ocl::typeToStr(wdepth):"srcT1",//to prevent overflow - WT1
695             haveExtraMat ? ocl::convertTypeStr(depth, wdepth, cn, cvt[0]) : "noconvert",//to prevent overflow - src to WT
696             haveExtraMat ? ocl::convertTypeStr(wdepth, depth, cn, cvt[1]) : "noconvert",//to prevent overflow - WT to dst
697             ocl::typeToStr(CV_MAKE_TYPE(haveExtraMat ? wdepth : depth, pxLoadVecSize)), //PX_LOAD_FLOAT_VEC_CONV
698             processing.c_str(), op2str[op],
699             actual_op == op ? "" : cv::format(" -D %s", op2str[actual_op]).c_str());
700 
701     ocl::Kernel kernel("filterSmall", cv::ocl::imgproc::filterSmall_oclsrc, opts);
702     if (kernel.empty())
703         return false;
704 
705     _dst.create(size, type);
706     UMat dst = _dst.getUMat();
707 
708     UMat source;
709     if(src.u != dst.u)
710         source = src;
711     else
712     {
713         Point ofs;
714         int cols =  src.cols, rows = src.rows;
715         src.locateROI(wholeSize, ofs);
716         src.adjustROI(ofs.y, wholeSize.height - rows - ofs.y, ofs.x, wholeSize.width - cols - ofs.x);
717         src.copyTo(source);
718 
719         src.adjustROI(-ofs.y, -wholeSize.height + rows + ofs.y, -ofs.x, -wholeSize.width + cols + ofs.x);
720         source.adjustROI(-ofs.y, -wholeSize.height + rows + ofs.y, -ofs.x, -wholeSize.width + cols + ofs.x);
721         source.locateROI(wholeSize, ofs);
722     }
723 
724     UMat extraMat = _extraMat.getUMat();
725 
726     int idxArg = kernel.set(0, ocl::KernelArg::PtrReadOnly(source));
727     idxArg = kernel.set(idxArg, (int)source.step);
728     int srcOffsetX = (int)((source.offset % source.step) / source.elemSize());
729     int srcOffsetY = (int)(source.offset / source.step);
730     int srcEndX = isolated ? srcOffsetX + size.width : wholeSize.width;
731     int srcEndY = isolated ? srcOffsetY + size.height : wholeSize.height;
732     idxArg = kernel.set(idxArg, srcOffsetX);
733     idxArg = kernel.set(idxArg, srcOffsetY);
734     idxArg = kernel.set(idxArg, srcEndX);
735     idxArg = kernel.set(idxArg, srcEndY);
736     idxArg = kernel.set(idxArg, ocl::KernelArg::WriteOnly(dst));
737 
738     if (haveExtraMat)
739     {
740         idxArg = kernel.set(idxArg, ocl::KernelArg::ReadOnlyNoSize(extraMat));
741     }
742 
743     return kernel.run(2, globalsize, NULL, false);
744 }
745 
ocl_morphOp(InputArray _src,OutputArray _dst,InputArray _kernel,Point anchor,int iterations,int op,int borderType,const Scalar &,int actual_op=-1,InputArray _extraMat=noArray ())746 static bool ocl_morphOp(InputArray _src, OutputArray _dst, InputArray _kernel,
747                         Point anchor, int iterations, int op, int borderType,
748                         const Scalar &, int actual_op = -1, InputArray _extraMat = noArray())
749 {
750     const ocl::Device & dev = ocl::Device::getDefault();
751     int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
752     Mat kernel = _kernel.getMat();
753     Size ksize = !kernel.empty() ? kernel.size() : Size(3, 3), ssize = _src.size();
754 
755     bool doubleSupport = dev.doubleFPConfig() > 0;
756     if ((depth == CV_64F && !doubleSupport) || borderType != BORDER_CONSTANT)
757         return false;
758 
759     bool haveExtraMat = !_extraMat.empty();
760     CV_Assert(actual_op <= 3 || haveExtraMat);
761 
762     if (kernel.empty())
763     {
764         ksize = Size(1+iterations*2,1+iterations*2);
765         kernel = getStructuringElement(MORPH_RECT, ksize);
766         anchor = Point(iterations, iterations);
767         iterations = 1;
768         CV_DbgAssert(ksize == kernel.size());
769     }
770     else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
771     {
772         ksize = Size(ksize.width + (iterations-1)*(ksize.width-1),
773                      ksize.height + (iterations-1)*(ksize.height-1));
774         anchor = Point(anchor.x*iterations, anchor.y*iterations);
775         kernel = getStructuringElement(MORPH_RECT, ksize, anchor);
776         iterations = 1;
777         CV_DbgAssert(ksize == kernel.size());
778     }
779 
780     static bool param_use_morph_special_kernels = utils::getConfigurationParameterBool("OPENCV_OPENCL_IMGPROC_MORPH_SPECIAL_KERNEL",
781 #ifndef __APPLE__
782         true
783 #else
784         false
785 #endif
786         );
787 
788     int esz = CV_ELEM_SIZE(type);
789     // try to use OpenCL kernel adopted for small morph kernel
790     if (param_use_morph_special_kernels && dev.isIntel() &&
791         ((ksize.width < 5 && ksize.height < 5 && esz <= 4) ||
792          (ksize.width == 5 && ksize.height == 5 && cn == 1)) &&
793          (iterations == 1)
794          )
795     {
796         if (ocl_morph3x3_8UC1(_src, _dst, kernel, anchor, op, actual_op, _extraMat))
797             return true;
798 
799         if (ocl_morphSmall(_src, _dst, kernel, anchor, borderType, op, actual_op, _extraMat))
800             return true;
801     }
802 
803     if (iterations == 0 || kernel.rows*kernel.cols == 1)
804     {
805         _src.copyTo(_dst);
806         return true;
807     }
808 
809 #ifdef __ANDROID__
810     size_t localThreads[2] = { 16, 8 };
811 #else
812     size_t localThreads[2] = { 16, 16 };
813 #endif
814     size_t globalThreads[2] = { (size_t)ssize.width, (size_t)ssize.height };
815 
816 #ifdef __APPLE__
817     if( actual_op != MORPH_ERODE && actual_op != MORPH_DILATE )
818         localThreads[0] = localThreads[1] = 4;
819 #endif
820 
821     if (localThreads[0]*localThreads[1] * 2 < (localThreads[0] + ksize.width - 1) * (localThreads[1] + ksize.height - 1))
822         return false;
823 
824 #ifdef __ANDROID__
825     if (dev.isNVidia())
826         return false;
827 #endif
828 
829     // build processing
830     String processing;
831     Mat kernel8u;
832     kernel.convertTo(kernel8u, CV_8U);
833     for (int y = 0; y < kernel8u.rows; ++y)
834         for (int x = 0; x < kernel8u.cols; ++x)
835             if (kernel8u.at<uchar>(y, x) != 0)
836                 processing += format("PROCESS(%d,%d)", y, x);
837 
838     static const char * const op2str[] = { "OP_ERODE", "OP_DILATE", NULL, NULL, "OP_GRADIENT", "OP_TOPHAT", "OP_BLACKHAT" };
839 
840     char cvt[2][50];
841     int wdepth = std::max(depth, CV_32F), scalarcn = cn == 3 ? 4 : cn;
842 
843     if (actual_op < 0)
844         actual_op = op;
845 
846     std::vector<ocl::Kernel> kernels(iterations);
847     for (int i = 0; i < iterations; i++)
848     {
849         int current_op = iterations == i + 1 ? actual_op : op;
850         String buildOptions = format("-D RADIUSX=%d -D RADIUSY=%d -D LSIZE0=%d -D LSIZE1=%d -D %s%s"
851                                      " -D PROCESS_ELEMS=%s -D T=%s -D DEPTH_%d -D cn=%d -D T1=%s"
852                                      " -D convertToWT=%s -D convertToT=%s -D ST=%s%s",
853                                      anchor.x, anchor.y, (int)localThreads[0], (int)localThreads[1], op2str[op],
854                                      doubleSupport ? " -D DOUBLE_SUPPORT" : "", processing.c_str(),
855                                      ocl::typeToStr(type), depth, cn, ocl::typeToStr(depth),
856                                      ocl::convertTypeStr(depth, wdepth, cn, cvt[0]),
857                                      ocl::convertTypeStr(wdepth, depth, cn, cvt[1]),
858                                      ocl::typeToStr(CV_MAKE_TYPE(depth, scalarcn)),
859                                      current_op == op ? "" : cv::format(" -D %s", op2str[current_op]).c_str());
860 
861         kernels[i].create("morph", ocl::imgproc::morph_oclsrc, buildOptions);
862         if (kernels[i].empty())
863             return false;
864     }
865 
866     UMat src = _src.getUMat(), extraMat = _extraMat.getUMat();
867     _dst.create(src.size(), src.type());
868     UMat dst = _dst.getUMat();
869 
870     if (iterations == 1 && src.u != dst.u)
871     {
872         Size wholesize;
873         Point ofs;
874         src.locateROI(wholesize, ofs);
875         int wholecols = wholesize.width, wholerows = wholesize.height;
876 
877         if (haveExtraMat)
878             kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst),
879                         ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows,
880                         ocl::KernelArg::ReadOnlyNoSize(extraMat));
881         else
882             kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst),
883                         ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows);
884 
885         return kernels[0].run(2, globalThreads, localThreads, false);
886     }
887 
888     for (int i = 0; i < iterations; i++)
889     {
890         UMat source;
891         Size wholesize;
892         Point ofs;
893 
894         if (i == 0)
895         {
896             int cols =  src.cols, rows = src.rows;
897             src.locateROI(wholesize, ofs);
898             src.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x);
899             if(src.u != dst.u)
900                 source = src;
901             else
902                 src.copyTo(source);
903 
904             src.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
905             source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
906         }
907         else
908         {
909             int cols =  dst.cols, rows = dst.rows;
910             dst.locateROI(wholesize, ofs);
911             dst.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x);
912             dst.copyTo(source);
913             dst.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
914             source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x);
915         }
916         source.locateROI(wholesize, ofs);
917 
918         if (haveExtraMat && iterations == i + 1)
919             kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst),
920                 ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height,
921                 ocl::KernelArg::ReadOnlyNoSize(extraMat));
922         else
923             kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst),
924                 ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height);
925 
926         if (!kernels[i].run(2, globalThreads, localThreads, false))
927             return false;
928     }
929 
930     return true;
931 }
932 
933 #endif
934 
morphOp(int op,InputArray _src,OutputArray _dst,InputArray _kernel,Point anchor,int iterations,int borderType,const Scalar & borderValue)935 static void morphOp( int op, InputArray _src, OutputArray _dst,
936                      InputArray _kernel,
937                      Point anchor, int iterations,
938                      int borderType, const Scalar& borderValue )
939 {
940     CV_INSTRUMENT_REGION();
941 
942     CV_Assert(!_src.empty());
943 
944     Mat kernel = _kernel.getMat();
945     Size ksize = !kernel.empty() ? kernel.size() : Size(3,3);
946     anchor = normalizeAnchor(anchor, ksize);
947 
948     CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
949                borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() &&
950                (op == MORPH_ERODE || op == MORPH_DILATE) &&
951                anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1,
952                ocl_morphOp(_src, _dst, kernel, anchor, iterations, op, borderType, borderValue) )
953 
954     if (iterations == 0 || kernel.rows*kernel.cols == 1)
955     {
956         _src.copyTo(_dst);
957         return;
958     }
959 
960     if (kernel.empty())
961     {
962         kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
963         anchor = Point(iterations, iterations);
964         iterations = 1;
965     }
966     else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
967     {
968         anchor = Point(anchor.x*iterations, anchor.y*iterations);
969         kernel = getStructuringElement(MORPH_RECT,
970                                        Size(ksize.width + (iterations-1)*(ksize.width-1),
971                                             ksize.height + (iterations-1)*(ksize.height-1)),
972                                        anchor);
973         iterations = 1;
974     }
975 
976     Mat src = _src.getMat();
977     _dst.create( src.size(), src.type() );
978     Mat dst = _dst.getMat();
979 
980     Point s_ofs;
981     Size s_wsz(src.cols, src.rows);
982     Point d_ofs;
983     Size d_wsz(dst.cols, dst.rows);
984     bool isolated = (borderType&BORDER_ISOLATED)?true:false;
985     borderType = (borderType&~BORDER_ISOLATED);
986 
987     if(!isolated)
988     {
989         src.locateROI(s_wsz, s_ofs);
990         dst.locateROI(d_wsz, d_ofs);
991     }
992 
993     hal::morph(op, src.type(), dst.type(),
994                src.data, src.step,
995                dst.data, dst.step,
996                src.cols, src.rows,
997                s_wsz.width, s_wsz.height, s_ofs.x, s_ofs.y,
998                d_wsz.width, d_wsz.height, d_ofs.x, d_ofs.y,
999                kernel.type(), kernel.data, kernel.step, kernel.cols, kernel.rows, anchor.x, anchor.y,
1000                borderType, borderValue.val, iterations,
1001                (src.isSubmatrix() && !isolated));
1002 }
1003 
erode(InputArray src,OutputArray dst,InputArray kernel,Point anchor,int iterations,int borderType,const Scalar & borderValue)1004 void erode( InputArray src, OutputArray dst, InputArray kernel,
1005                 Point anchor, int iterations,
1006                 int borderType, const Scalar& borderValue )
1007 {
1008     CV_INSTRUMENT_REGION();
1009 
1010     CV_Assert(!src.empty());
1011 
1012     morphOp( MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType, borderValue );
1013 }
1014 
1015 
dilate(InputArray src,OutputArray dst,InputArray kernel,Point anchor,int iterations,int borderType,const Scalar & borderValue)1016 void dilate( InputArray src, OutputArray dst, InputArray kernel,
1017                  Point anchor, int iterations,
1018                  int borderType, const Scalar& borderValue )
1019 {
1020     CV_INSTRUMENT_REGION();
1021 
1022     CV_Assert(!src.empty());
1023 
1024     morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue );
1025 }
1026 
1027 #ifdef HAVE_OPENCL
1028 
ocl_morphologyEx(InputArray _src,OutputArray _dst,int op,InputArray kernel,Point anchor,int iterations,int borderType,const Scalar & borderValue)1029 static bool ocl_morphologyEx(InputArray _src, OutputArray _dst, int op,
1030                              InputArray kernel, Point anchor, int iterations,
1031                              int borderType, const Scalar& borderValue)
1032 {
1033     _dst.createSameSize(_src, _src.type());
1034     bool submat = _dst.isSubmatrix();
1035     UMat temp;
1036     _OutputArray _temp = submat ? _dst : _OutputArray(temp);
1037 
1038     switch( op )
1039     {
1040     case MORPH_ERODE:
1041         if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1042             return false;
1043         break;
1044     case MORPH_DILATE:
1045         if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
1046             return false;
1047         break;
1048     case MORPH_OPEN:
1049         if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1050             return false;
1051         if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
1052             return false;
1053         break;
1054     case MORPH_CLOSE:
1055         if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
1056             return false;
1057         if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1058             return false;
1059         break;
1060     case MORPH_GRADIENT:
1061         if (!ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1062             return false;
1063         if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_GRADIENT, temp ))
1064             return false;
1065         break;
1066     case MORPH_TOPHAT:
1067         if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue ))
1068             return false;
1069         if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_TOPHAT, _src ))
1070             return false;
1071         break;
1072     case MORPH_BLACKHAT:
1073         if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue ))
1074             return false;
1075         if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue, MORPH_BLACKHAT, _src ))
1076             return false;
1077         break;
1078     default:
1079         CV_Error( CV_StsBadArg, "unknown morphological operation" );
1080     }
1081 
1082     return true;
1083 }
1084 
1085 #endif
1086 
1087 #define IPP_DISABLE_MORPH_ADV 1
1088 #if 0 //defined HAVE_IPP
1089 #if !IPP_DISABLE_MORPH_ADV
1090 static bool ipp_morphologyEx(int op, InputArray _src, OutputArray _dst,
1091                      InputArray _kernel,
1092                      Point anchor, int iterations,
1093                      int borderType, const Scalar& borderValue)
1094 {
1095 #if defined HAVE_IPP_IW
1096     Mat kernel = _kernel.getMat();
1097     Size ksize = !kernel.empty() ? kernel.size() : Size(3,3);
1098     anchor = normalizeAnchor(anchor, ksize);
1099 
1100     if (iterations == 0 || kernel.rows*kernel.cols == 1)
1101     {
1102         _src.copyTo(_dst);
1103         return true;
1104     }
1105 
1106     if (kernel.empty())
1107     {
1108         kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2));
1109         anchor = Point(iterations, iterations);
1110         iterations = 1;
1111     }
1112     else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols )
1113     {
1114         anchor = Point(anchor.x*iterations, anchor.y*iterations);
1115         kernel = getStructuringElement(MORPH_RECT,
1116                                        Size(ksize.width + (iterations-1)*(ksize.width-1),
1117                                             ksize.height + (iterations-1)*(ksize.height-1)),
1118                                        anchor);
1119         iterations = 1;
1120     }
1121 
1122     Mat src = _src.getMat();
1123     _dst.create( src.size(), src.type() );
1124     Mat dst = _dst.getMat();
1125 
1126     Point s_ofs;
1127     Size s_wsz(src.cols, src.rows);
1128     Point d_ofs;
1129     Size d_wsz(dst.cols, dst.rows);
1130     bool isolated = (borderType&BORDER_ISOLATED)?true:false;
1131     borderType = (borderType&~BORDER_ISOLATED);
1132 
1133     if(!isolated)
1134     {
1135         src.locateROI(s_wsz, s_ofs);
1136         dst.locateROI(d_wsz, d_ofs);
1137     }
1138 
1139     return ippMorph(op, src.type(), dst.type(),
1140                src.data, src.step,
1141                dst.data, dst.step,
1142                src.cols, src.rows,
1143                s_wsz.width, s_wsz.height, s_ofs.x, s_ofs.y,
1144                d_wsz.width, d_wsz.height, d_ofs.x, d_ofs.y,
1145                kernel.type(), kernel.data, kernel.step, kernel.cols, kernel.rows, anchor.x, anchor.y,
1146                borderType, borderValue.val, iterations,
1147                (src.isSubmatrix() && !isolated));
1148 #else
1149     CV_UNUSED(op); CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(_kernel); CV_UNUSED(anchor);
1150     CV_UNUSED(iterations); CV_UNUSED(borderType); CV_UNUSED(borderValue);
1151     return false;
1152 #endif
1153 }
1154 #endif
1155 #endif
1156 
morphologyEx(InputArray _src,OutputArray _dst,int op,InputArray _kernel,Point anchor,int iterations,int borderType,const Scalar & borderValue)1157 void morphologyEx( InputArray _src, OutputArray _dst, int op,
1158                        InputArray _kernel, Point anchor, int iterations,
1159                        int borderType, const Scalar& borderValue )
1160 {
1161     CV_INSTRUMENT_REGION();
1162 
1163     CV_Assert(!_src.empty());
1164 
1165     Mat kernel = _kernel.getMat();
1166     if (kernel.empty())
1167     {
1168         kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(1,1));
1169     }
1170 #ifdef HAVE_OPENCL
1171     Size ksize = kernel.size();
1172     anchor = normalizeAnchor(anchor, ksize);
1173 
1174     CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
1175         anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 &&
1176         borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(),
1177         ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue))
1178 #endif
1179 
1180     Mat src = _src.getMat(), temp;
1181     _dst.create(src.size(), src.type());
1182     Mat dst = _dst.getMat();
1183 
1184 #if !IPP_DISABLE_MORPH_ADV
1185     //CV_IPP_RUN_FAST(ipp_morphologyEx(op, src, dst, kernel, anchor, iterations, borderType, borderValue));
1186 #endif
1187 
1188     switch( op )
1189     {
1190     case MORPH_ERODE:
1191         erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
1192         break;
1193     case MORPH_DILATE:
1194         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1195         break;
1196     case MORPH_OPEN:
1197         erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
1198         dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
1199         break;
1200     case MORPH_CLOSE:
1201         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1202         erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
1203         break;
1204     case MORPH_GRADIENT:
1205         erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
1206         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
1207         dst -= temp;
1208         break;
1209     case MORPH_TOPHAT:
1210         if( src.data != dst.data )
1211             temp = dst;
1212         erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
1213         dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue );
1214         dst = src - temp;
1215         break;
1216     case MORPH_BLACKHAT:
1217         if( src.data != dst.data )
1218             temp = dst;
1219         dilate( src, temp, kernel, anchor, iterations, borderType, borderValue );
1220         erode( temp, temp, kernel, anchor, iterations, borderType, borderValue );
1221         dst = temp - src;
1222         break;
1223     case MORPH_HITMISS:
1224         CV_Assert(src.type() == CV_8UC1);
1225         if(countNonZero(kernel) <=0)
1226         {
1227             src.copyTo(dst);
1228             break;
1229         }
1230         {
1231             Mat k1, k2, e1, e2;
1232             k1 = (kernel == 1);
1233             k2 = (kernel == -1);
1234 
1235             if (countNonZero(k1) <= 0)
1236                 e1 = Mat(src.size(), src.type(), Scalar(255));
1237             else
1238                 erode(src, e1, k1, anchor, iterations, borderType, borderValue);
1239 
1240             if (countNonZero(k2) <= 0)
1241                 e2 = Mat(src.size(), src.type(), Scalar(255));
1242             else
1243             {
1244                 Mat src_complement;
1245                 bitwise_not(src, src_complement);
1246                 erode(src_complement, e2, k2, anchor, iterations, borderType, borderValue);
1247             }
1248             dst = e1 & e2;
1249         }
1250         break;
1251     default:
1252         CV_Error( CV_StsBadArg, "unknown morphological operation" );
1253     }
1254 }
1255 
1256 } // namespace cv
1257 
1258 CV_IMPL IplConvKernel *
cvCreateStructuringElementEx(int cols,int rows,int anchorX,int anchorY,int shape,int * values)1259 cvCreateStructuringElementEx( int cols, int rows,
1260                               int anchorX, int anchorY,
1261                               int shape, int *values )
1262 {
1263     cv::Size ksize = cv::Size(cols, rows);
1264     cv::Point anchor = cv::Point(anchorX, anchorY);
1265     CV_Assert( cols > 0 && rows > 0 && anchor.inside(cv::Rect(0,0,cols,rows)) &&
1266                (shape != CV_SHAPE_CUSTOM || values != 0));
1267 
1268     int i, size = rows * cols;
1269     int element_size = sizeof(IplConvKernel) + size*sizeof(int);
1270     IplConvKernel *element = (IplConvKernel*)cvAlloc(element_size + 32);
1271 
1272     element->nCols = cols;
1273     element->nRows = rows;
1274     element->anchorX = anchorX;
1275     element->anchorY = anchorY;
1276     element->nShiftR = shape < CV_SHAPE_ELLIPSE ? shape : CV_SHAPE_CUSTOM;
1277     element->values = (int*)(element + 1);
1278 
1279     if( shape == CV_SHAPE_CUSTOM )
1280     {
1281         for( i = 0; i < size; i++ )
1282             element->values[i] = values[i];
1283     }
1284     else
1285     {
1286         cv::Mat elem = cv::getStructuringElement(shape, ksize, anchor);
1287         for( i = 0; i < size; i++ )
1288             element->values[i] = elem.ptr()[i];
1289     }
1290 
1291     return element;
1292 }
1293 
1294 
1295 CV_IMPL void
cvReleaseStructuringElement(IplConvKernel ** element)1296 cvReleaseStructuringElement( IplConvKernel ** element )
1297 {
1298     if( !element )
1299         CV_Error( CV_StsNullPtr, "" );
1300     cvFree( element );
1301 }
1302 
1303 
convertConvKernel(const IplConvKernel * src,cv::Mat & dst,cv::Point & anchor)1304 static void convertConvKernel( const IplConvKernel* src, cv::Mat& dst, cv::Point& anchor )
1305 {
1306     if(!src)
1307     {
1308         anchor = cv::Point(1,1);
1309         dst.release();
1310         return;
1311     }
1312     anchor = cv::Point(src->anchorX, src->anchorY);
1313     dst.create(src->nRows, src->nCols, CV_8U);
1314 
1315     int i, size = src->nRows*src->nCols;
1316     for( i = 0; i < size; i++ )
1317         dst.ptr()[i] = (uchar)(src->values[i] != 0);
1318 }
1319 
1320 
1321 CV_IMPL void
cvErode(const CvArr * srcarr,CvArr * dstarr,IplConvKernel * element,int iterations)1322 cvErode( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element, int iterations )
1323 {
1324     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1325     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1326     cv::Point anchor;
1327     convertConvKernel( element, kernel, anchor );
1328     cv::erode( src, dst, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1329 }
1330 
1331 
1332 CV_IMPL void
cvDilate(const CvArr * srcarr,CvArr * dstarr,IplConvKernel * element,int iterations)1333 cvDilate( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element, int iterations )
1334 {
1335     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1336     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1337     cv::Point anchor;
1338     convertConvKernel( element, kernel, anchor );
1339     cv::dilate( src, dst, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1340 }
1341 
1342 
1343 CV_IMPL void
cvMorphologyEx(const void * srcarr,void * dstarr,void *,IplConvKernel * element,int op,int iterations)1344 cvMorphologyEx( const void* srcarr, void* dstarr, void*,
1345                 IplConvKernel* element, int op, int iterations )
1346 {
1347     cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel;
1348     CV_Assert( src.size() == dst.size() && src.type() == dst.type() );
1349     cv::Point anchor;
1350     IplConvKernel* temp_element = NULL;
1351     if (!element)
1352     {
1353         temp_element = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT);
1354     } else {
1355         temp_element = element;
1356     }
1357     convertConvKernel( temp_element, kernel, anchor );
1358     if (!element)
1359     {
1360         cvReleaseStructuringElement(&temp_element);
1361     }
1362     cv::morphologyEx( src, dst, op, kernel, anchor, iterations, cv::BORDER_REPLICATE );
1363 }
1364 
1365 /* End of file. */
1366