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 | |
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 | |
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 | |
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 | |
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, 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 | |
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 || 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 | |
205 | static 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 |
241 | static 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 | |
253 | static 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 | |
449 | static 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 | |
481 | namespace hal { |
482 | |
483 | |
484 | CV_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 | |
492 | void 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 | |
534 | static bool ocl_morph3x3_8UC1( InputArray _src, OutputArray _dst, InputArray _kernel, Point anchor, |
535 | int op, int actual_op = -1, InputArray = 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 = !_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.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 | |
606 | static bool ocl_morphSmall( InputArray _src, OutputArray _dst, InputArray _kernel, Point anchor, int borderType, |
607 | int op, int actual_op = -1, InputArray = 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 = !_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.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 | |
761 | static 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 = 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 = !_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.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 | |
950 | static 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 | |
1019 | void 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 | |
1031 | void 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 | |
1044 | static 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 |
1105 | static 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 | |
1172 | void 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 | |
1273 | CV_IMPL IplConvKernel * |
1274 | cvCreateStructuringElementEx( 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 | |
1310 | CV_IMPL void |
1311 | cvReleaseStructuringElement( IplConvKernel ** element ) |
1312 | { |
1313 | if( !element ) |
1314 | CV_Error( cv::Error::StsNullPtr, "" ); |
1315 | cvFree( element ); |
1316 | } |
1317 | |
1318 | |
1319 | static 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 | |
1336 | CV_IMPL void |
1337 | cvErode( 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 | |
1347 | CV_IMPL void |
1348 | cvDilate( 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 | |
1358 | CV_IMPL void |
1359 | cvMorphologyEx( 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 | |