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

source code of opencv/modules/imgproc/src/morph.dispatch.cpp