/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #include "precomp.hpp" #include <limits.h> #include "opencl_kernels_imgproc.hpp" /****************************************************************************************\ Basic Morphological Operations: Erosion & Dilation \****************************************************************************************/ namespace cv { template<typename T> struct MinOp { typedef T type1; typedef T type2; typedef T rtype; T operator ()(const T a, const T b) const { return std::min(a, b); } }; template<typename T> struct MaxOp { typedef T type1; typedef T type2; typedef T rtype; T operator ()(const T a, const T b) const { return std::max(a, b); } }; #undef CV_MIN_8U #undef CV_MAX_8U #define CV_MIN_8U(a,b) ((a) - CV_FAST_CAST_8U((a) - (b))) #define CV_MAX_8U(a,b) ((a) + CV_FAST_CAST_8U((b) - (a))) template<> inline uchar MinOp<uchar>::operator ()(const uchar a, const uchar b) const { return CV_MIN_8U(a, b); } template<> inline uchar MaxOp<uchar>::operator ()(const uchar a, const uchar b) const { return CV_MAX_8U(a, b); } struct MorphRowNoVec { MorphRowNoVec(int, int) {} int operator()(const uchar*, uchar*, int, int) const { return 0; } }; struct MorphColumnNoVec { MorphColumnNoVec(int, int) {} int operator()(const uchar**, uchar*, int, int, int) const { return 0; } }; struct MorphNoVec { int operator()(uchar**, int, uchar*, int) const { return 0; } }; #if CV_SSE2 template<class VecUpdate> struct MorphRowIVec { enum { ESZ = VecUpdate::ESZ }; MorphRowIVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} int operator()(const uchar* src, uchar* dst, int width, int cn) const { if( !checkHardwareSupport(CV_CPU_SSE2) ) return 0; cn *= ESZ; int i, k, _ksize = ksize*cn; width = (width & -4)*cn; VecUpdate updateOp; for( i = 0; i <= width - 16; i += 16 ) { __m128i s = _mm_loadu_si128((const __m128i*)(src + i)); for( k = cn; k < _ksize; k += cn ) { __m128i x = _mm_loadu_si128((const __m128i*)(src + i + k)); s = updateOp(s, x); } _mm_storeu_si128((__m128i*)(dst + i), s); } for( ; i < width; i += 4 ) { __m128i s = _mm_cvtsi32_si128(*(const int*)(src + i)); for( k = cn; k < _ksize; k += cn ) { __m128i x = _mm_cvtsi32_si128(*(const int*)(src + i + k)); s = updateOp(s, x); } *(int*)(dst + i) = _mm_cvtsi128_si32(s); } return i/ESZ; } int ksize, anchor; }; template<class VecUpdate> struct MorphRowFVec { MorphRowFVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} int operator()(const uchar* src, uchar* dst, int width, int cn) const { if( !checkHardwareSupport(CV_CPU_SSE) ) return 0; int i, k, _ksize = ksize*cn; width = (width & -4)*cn; VecUpdate updateOp; for( i = 0; i < width; i += 4 ) { __m128 s = _mm_loadu_ps((const float*)src + i); for( k = cn; k < _ksize; k += cn ) { __m128 x = _mm_loadu_ps((const float*)src + i + k); s = updateOp(s, x); } _mm_storeu_ps((float*)dst + i, s); } return i; } int ksize, anchor; }; template<class VecUpdate> struct MorphColumnIVec { enum { ESZ = VecUpdate::ESZ }; MorphColumnIVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} int operator()(const uchar** src, uchar* dst, int dststep, int count, int width) const { if( !checkHardwareSupport(CV_CPU_SSE2) ) return 0; int i = 0, k, _ksize = ksize; width *= ESZ; VecUpdate updateOp; for( i = 0; i < count + ksize - 1; i++ ) CV_Assert( ((size_t)src[i] & 15) == 0 ); for( ; _ksize > 1 && count > 1; count -= 2, dst += dststep*2, src += 2 ) { for( i = 0; i <= width - 32; i += 32 ) { const uchar* sptr = src[1] + i; __m128i s0 = _mm_load_si128((const __m128i*)sptr); __m128i s1 = _mm_load_si128((const __m128i*)(sptr + 16)); __m128i x0, x1; for( k = 2; k < _ksize; k++ ) { sptr = src[k] + i; x0 = _mm_load_si128((const __m128i*)sptr); x1 = _mm_load_si128((const __m128i*)(sptr + 16)); s0 = updateOp(s0, x0); s1 = updateOp(s1, x1); } sptr = src[0] + i; x0 = _mm_load_si128((const __m128i*)sptr); x1 = _mm_load_si128((const __m128i*)(sptr + 16)); _mm_storeu_si128((__m128i*)(dst + i), updateOp(s0, x0)); _mm_storeu_si128((__m128i*)(dst + i + 16), updateOp(s1, x1)); sptr = src[k] + i; x0 = _mm_load_si128((const __m128i*)sptr); x1 = _mm_load_si128((const __m128i*)(sptr + 16)); _mm_storeu_si128((__m128i*)(dst + dststep + i), updateOp(s0, x0)); _mm_storeu_si128((__m128i*)(dst + dststep + i + 16), updateOp(s1, x1)); } for( ; i <= width - 8; i += 8 ) { __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[1] + i)), x0; for( k = 2; k < _ksize; k++ ) { x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i)); s0 = updateOp(s0, x0); } x0 = _mm_loadl_epi64((const __m128i*)(src[0] + i)); _mm_storel_epi64((__m128i*)(dst + i), updateOp(s0, x0)); x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i)); _mm_storel_epi64((__m128i*)(dst + dststep + i), updateOp(s0, x0)); } } for( ; count > 0; count--, dst += dststep, src++ ) { for( i = 0; i <= width - 32; i += 32 ) { const uchar* sptr = src[0] + i; __m128i s0 = _mm_load_si128((const __m128i*)sptr); __m128i s1 = _mm_load_si128((const __m128i*)(sptr + 16)); __m128i x0, x1; for( k = 1; k < _ksize; k++ ) { sptr = src[k] + i; x0 = _mm_load_si128((const __m128i*)sptr); x1 = _mm_load_si128((const __m128i*)(sptr + 16)); s0 = updateOp(s0, x0); s1 = updateOp(s1, x1); } _mm_storeu_si128((__m128i*)(dst + i), s0); _mm_storeu_si128((__m128i*)(dst + i + 16), s1); } for( ; i <= width - 8; i += 8 ) { __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[0] + i)), x0; for( k = 1; k < _ksize; k++ ) { x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i)); s0 = updateOp(s0, x0); } _mm_storel_epi64((__m128i*)(dst + i), s0); } } return i/ESZ; } int ksize, anchor; }; template<class VecUpdate> struct MorphColumnFVec { MorphColumnFVec(int _ksize, int _anchor) : ksize(_ksize), anchor(_anchor) {} int operator()(const uchar** _src, uchar* _dst, int dststep, int count, int width) const { if( !checkHardwareSupport(CV_CPU_SSE) ) return 0; int i = 0, k, _ksize = ksize; VecUpdate updateOp; for( i = 0; i < count + ksize - 1; i++ ) CV_Assert( ((size_t)_src[i] & 15) == 0 ); const float** src = (const float**)_src; float* dst = (float*)_dst; dststep /= sizeof(dst[0]); for( ; _ksize > 1 && count > 1; count -= 2, dst += dststep*2, src += 2 ) { for( i = 0; i <= width - 16; i += 16 ) { const float* sptr = src[1] + i; __m128 s0 = _mm_load_ps(sptr); __m128 s1 = _mm_load_ps(sptr + 4); __m128 s2 = _mm_load_ps(sptr + 8); __m128 s3 = _mm_load_ps(sptr + 12); __m128 x0, x1, x2, x3; for( k = 2; k < _ksize; k++ ) { sptr = src[k] + i; x0 = _mm_load_ps(sptr); x1 = _mm_load_ps(sptr + 4); s0 = updateOp(s0, x0); s1 = updateOp(s1, x1); x2 = _mm_load_ps(sptr + 8); x3 = _mm_load_ps(sptr + 12); s2 = updateOp(s2, x2); s3 = updateOp(s3, x3); } sptr = src[0] + i; x0 = _mm_load_ps(sptr); x1 = _mm_load_ps(sptr + 4); x2 = _mm_load_ps(sptr + 8); x3 = _mm_load_ps(sptr + 12); _mm_storeu_ps(dst + i, updateOp(s0, x0)); _mm_storeu_ps(dst + i + 4, updateOp(s1, x1)); _mm_storeu_ps(dst + i + 8, updateOp(s2, x2)); _mm_storeu_ps(dst + i + 12, updateOp(s3, x3)); sptr = src[k] + i; x0 = _mm_load_ps(sptr); x1 = _mm_load_ps(sptr + 4); x2 = _mm_load_ps(sptr + 8); x3 = _mm_load_ps(sptr + 12); _mm_storeu_ps(dst + dststep + i, updateOp(s0, x0)); _mm_storeu_ps(dst + dststep + i + 4, updateOp(s1, x1)); _mm_storeu_ps(dst + dststep + i + 8, updateOp(s2, x2)); _mm_storeu_ps(dst + dststep + i + 12, updateOp(s3, x3)); } for( ; i <= width - 4; i += 4 ) { __m128 s0 = _mm_load_ps(src[1] + i), x0; for( k = 2; k < _ksize; k++ ) { x0 = _mm_load_ps(src[k] + i); s0 = updateOp(s0, x0); } x0 = _mm_load_ps(src[0] + i); _mm_storeu_ps(dst + i, updateOp(s0, x0)); x0 = _mm_load_ps(src[k] + i); _mm_storeu_ps(dst + dststep + i, updateOp(s0, x0)); } } for( ; count > 0; count--, dst += dststep, src++ ) { for( i = 0; i <= width - 16; i += 16 ) { const float* sptr = src[0] + i; __m128 s0 = _mm_load_ps(sptr); __m128 s1 = _mm_load_ps(sptr + 4); __m128 s2 = _mm_load_ps(sptr + 8); __m128 s3 = _mm_load_ps(sptr + 12); __m128 x0, x1, x2, x3; for( k = 1; k < _ksize; k++ ) { sptr = src[k] + i; x0 = _mm_load_ps(sptr); x1 = _mm_load_ps(sptr + 4); s0 = updateOp(s0, x0); s1 = updateOp(s1, x1); x2 = _mm_load_ps(sptr + 8); x3 = _mm_load_ps(sptr + 12); s2 = updateOp(s2, x2); s3 = updateOp(s3, x3); } _mm_storeu_ps(dst + i, s0); _mm_storeu_ps(dst + i + 4, s1); _mm_storeu_ps(dst + i + 8, s2); _mm_storeu_ps(dst + i + 12, s3); } for( i = 0; i <= width - 4; i += 4 ) { __m128 s0 = _mm_load_ps(src[0] + i), x0; for( k = 1; k < _ksize; k++ ) { x0 = _mm_load_ps(src[k] + i); s0 = updateOp(s0, x0); } _mm_storeu_ps(dst + i, s0); } } return i; } int ksize, anchor; }; template<class VecUpdate> struct MorphIVec { enum { ESZ = VecUpdate::ESZ }; int operator()(uchar** src, int nz, uchar* dst, int width) const { if( !checkHardwareSupport(CV_CPU_SSE2) ) return 0; int i, k; width *= ESZ; VecUpdate updateOp; for( i = 0; i <= width - 32; i += 32 ) { const uchar* sptr = src[0] + i; __m128i s0 = _mm_loadu_si128((const __m128i*)sptr); __m128i s1 = _mm_loadu_si128((const __m128i*)(sptr + 16)); __m128i x0, x1; for( k = 1; k < nz; k++ ) { sptr = src[k] + i; x0 = _mm_loadu_si128((const __m128i*)sptr); x1 = _mm_loadu_si128((const __m128i*)(sptr + 16)); s0 = updateOp(s0, x0); s1 = updateOp(s1, x1); } _mm_storeu_si128((__m128i*)(dst + i), s0); _mm_storeu_si128((__m128i*)(dst + i + 16), s1); } for( ; i <= width - 8; i += 8 ) { __m128i s0 = _mm_loadl_epi64((const __m128i*)(src[0] + i)), x0; for( k = 1; k < nz; k++ ) { x0 = _mm_loadl_epi64((const __m128i*)(src[k] + i)); s0 = updateOp(s0, x0); } _mm_storel_epi64((__m128i*)(dst + i), s0); } return i/ESZ; } }; template<class VecUpdate> struct MorphFVec { int operator()(uchar** _src, int nz, uchar* _dst, int width) const { if( !checkHardwareSupport(CV_CPU_SSE) ) return 0; const float** src = (const float**)_src; float* dst = (float*)_dst; int i, k; VecUpdate updateOp; for( i = 0; i <= width - 16; i += 16 ) { const float* sptr = src[0] + i; __m128 s0 = _mm_loadu_ps(sptr); __m128 s1 = _mm_loadu_ps(sptr + 4); __m128 s2 = _mm_loadu_ps(sptr + 8); __m128 s3 = _mm_loadu_ps(sptr + 12); __m128 x0, x1, x2, x3; for( k = 1; k < nz; k++ ) { sptr = src[k] + i; x0 = _mm_loadu_ps(sptr); x1 = _mm_loadu_ps(sptr + 4); x2 = _mm_loadu_ps(sptr + 8); x3 = _mm_loadu_ps(sptr + 12); s0 = updateOp(s0, x0); s1 = updateOp(s1, x1); s2 = updateOp(s2, x2); s3 = updateOp(s3, x3); } _mm_storeu_ps(dst + i, s0); _mm_storeu_ps(dst + i + 4, s1); _mm_storeu_ps(dst + i + 8, s2); _mm_storeu_ps(dst + i + 12, s3); } for( ; i <= width - 4; i += 4 ) { __m128 s0 = _mm_loadu_ps(src[0] + i), x0; for( k = 1; k < nz; k++ ) { x0 = _mm_loadu_ps(src[k] + i); s0 = updateOp(s0, x0); } _mm_storeu_ps(dst + i, s0); } for( ; i < width; i++ ) { __m128 s0 = _mm_load_ss(src[0] + i), x0; for( k = 1; k < nz; k++ ) { x0 = _mm_load_ss(src[k] + i); s0 = updateOp(s0, x0); } _mm_store_ss(dst + i, s0); } return i; } }; struct VMin8u { enum { ESZ = 1 }; __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_min_epu8(a,b); } }; struct VMax8u { enum { ESZ = 1 }; __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_max_epu8(a,b); } }; struct VMin16u { enum { ESZ = 2 }; __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_subs_epu16(a,_mm_subs_epu16(a,b)); } }; struct VMax16u { enum { ESZ = 2 }; __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_adds_epu16(_mm_subs_epu16(a,b), b); } }; struct VMin16s { enum { ESZ = 2 }; __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_min_epi16(a, b); } }; struct VMax16s { enum { ESZ = 2 }; __m128i operator()(const __m128i& a, const __m128i& b) const { return _mm_max_epi16(a, b); } }; struct VMin32f { __m128 operator()(const __m128& a, const __m128& b) const { return _mm_min_ps(a,b); }}; struct VMax32f { __m128 operator()(const __m128& a, const __m128& b) const { return _mm_max_ps(a,b); }}; typedef MorphRowIVec<VMin8u> ErodeRowVec8u; typedef MorphRowIVec<VMax8u> DilateRowVec8u; typedef MorphRowIVec<VMin16u> ErodeRowVec16u; typedef MorphRowIVec<VMax16u> DilateRowVec16u; typedef MorphRowIVec<VMin16s> ErodeRowVec16s; typedef MorphRowIVec<VMax16s> DilateRowVec16s; typedef MorphRowFVec<VMin32f> ErodeRowVec32f; typedef MorphRowFVec<VMax32f> DilateRowVec32f; typedef MorphColumnIVec<VMin8u> ErodeColumnVec8u; typedef MorphColumnIVec<VMax8u> DilateColumnVec8u; typedef MorphColumnIVec<VMin16u> ErodeColumnVec16u; typedef MorphColumnIVec<VMax16u> DilateColumnVec16u; typedef MorphColumnIVec<VMin16s> ErodeColumnVec16s; typedef MorphColumnIVec<VMax16s> DilateColumnVec16s; typedef MorphColumnFVec<VMin32f> ErodeColumnVec32f; typedef MorphColumnFVec<VMax32f> DilateColumnVec32f; typedef MorphIVec<VMin8u> ErodeVec8u; typedef MorphIVec<VMax8u> DilateVec8u; typedef MorphIVec<VMin16u> ErodeVec16u; typedef MorphIVec<VMax16u> DilateVec16u; typedef MorphIVec<VMin16s> ErodeVec16s; typedef MorphIVec<VMax16s> DilateVec16s; typedef MorphFVec<VMin32f> ErodeVec32f; typedef MorphFVec<VMax32f> DilateVec32f; #else #ifdef HAVE_TEGRA_OPTIMIZATION using tegra::ErodeRowVec8u; using tegra::DilateRowVec8u; using tegra::ErodeColumnVec8u; using tegra::DilateColumnVec8u; #else typedef MorphRowNoVec ErodeRowVec8u; typedef MorphRowNoVec DilateRowVec8u; typedef MorphColumnNoVec ErodeColumnVec8u; typedef MorphColumnNoVec DilateColumnVec8u; #endif typedef MorphRowNoVec ErodeRowVec16u; typedef MorphRowNoVec DilateRowVec16u; typedef MorphRowNoVec ErodeRowVec16s; typedef MorphRowNoVec DilateRowVec16s; typedef MorphRowNoVec ErodeRowVec32f; typedef MorphRowNoVec DilateRowVec32f; typedef MorphColumnNoVec ErodeColumnVec16u; typedef MorphColumnNoVec DilateColumnVec16u; typedef MorphColumnNoVec ErodeColumnVec16s; typedef MorphColumnNoVec DilateColumnVec16s; typedef MorphColumnNoVec ErodeColumnVec32f; typedef MorphColumnNoVec DilateColumnVec32f; typedef MorphNoVec ErodeVec8u; typedef MorphNoVec DilateVec8u; typedef MorphNoVec ErodeVec16u; typedef MorphNoVec DilateVec16u; typedef MorphNoVec ErodeVec16s; typedef MorphNoVec DilateVec16s; typedef MorphNoVec ErodeVec32f; typedef MorphNoVec DilateVec32f; #endif typedef MorphRowNoVec ErodeRowVec64f; typedef MorphRowNoVec DilateRowVec64f; typedef MorphColumnNoVec ErodeColumnVec64f; typedef MorphColumnNoVec DilateColumnVec64f; typedef MorphNoVec ErodeVec64f; typedef MorphNoVec DilateVec64f; template<class Op, class VecOp> struct MorphRowFilter : public BaseRowFilter { typedef typename Op::rtype T; MorphRowFilter( int _ksize, int _anchor ) : vecOp(_ksize, _anchor) { ksize = _ksize; anchor = _anchor; } void operator()(const uchar* src, uchar* dst, int width, int cn) { int i, j, k, _ksize = ksize*cn; const T* S = (const T*)src; Op op; T* D = (T*)dst; if( _ksize == cn ) { for( i = 0; i < width*cn; i++ ) D[i] = S[i]; return; } int i0 = vecOp(src, dst, width, cn); width *= cn; for( k = 0; k < cn; k++, S++, D++ ) { for( i = i0; i <= width - cn*2; i += cn*2 ) { const T* s = S + i; T m = s[cn]; for( j = cn*2; j < _ksize; j += cn ) m = op(m, s[j]); D[i] = op(m, s[0]); D[i+cn] = op(m, s[j]); } for( ; i < width; i += cn ) { const T* s = S + i; T m = s[0]; for( j = cn; j < _ksize; j += cn ) m = op(m, s[j]); D[i] = m; } } } VecOp vecOp; }; template<class Op, class VecOp> struct MorphColumnFilter : public BaseColumnFilter { typedef typename Op::rtype T; MorphColumnFilter( int _ksize, int _anchor ) : vecOp(_ksize, _anchor) { ksize = _ksize; anchor = _anchor; } void operator()(const uchar** _src, uchar* dst, int dststep, int count, int width) { int i, k, _ksize = ksize; const T** src = (const T**)_src; T* D = (T*)dst; Op op; int i0 = vecOp(_src, dst, dststep, count, width); dststep /= sizeof(D[0]); for( ; _ksize > 1 && count > 1; count -= 2, D += dststep*2, src += 2 ) { i = i0; #if CV_ENABLE_UNROLLED for( ; i <= width - 4; i += 4 ) { const T* sptr = src[1] + i; T s0 = sptr[0], s1 = sptr[1], s2 = sptr[2], s3 = sptr[3]; for( k = 2; k < _ksize; k++ ) { sptr = src[k] + i; s0 = op(s0, sptr[0]); s1 = op(s1, sptr[1]); s2 = op(s2, sptr[2]); s3 = op(s3, sptr[3]); } sptr = src[0] + i; D[i] = op(s0, sptr[0]); D[i+1] = op(s1, sptr[1]); D[i+2] = op(s2, sptr[2]); D[i+3] = op(s3, sptr[3]); sptr = src[k] + i; D[i+dststep] = op(s0, sptr[0]); D[i+dststep+1] = op(s1, sptr[1]); D[i+dststep+2] = op(s2, sptr[2]); D[i+dststep+3] = op(s3, sptr[3]); } #endif for( ; i < width; i++ ) { T s0 = src[1][i]; for( k = 2; k < _ksize; k++ ) s0 = op(s0, src[k][i]); D[i] = op(s0, src[0][i]); D[i+dststep] = op(s0, src[k][i]); } } for( ; count > 0; count--, D += dststep, src++ ) { i = i0; #if CV_ENABLE_UNROLLED for( ; i <= width - 4; i += 4 ) { const T* sptr = src[0] + i; T s0 = sptr[0], s1 = sptr[1], s2 = sptr[2], s3 = sptr[3]; for( k = 1; k < _ksize; k++ ) { sptr = src[k] + i; s0 = op(s0, sptr[0]); s1 = op(s1, sptr[1]); s2 = op(s2, sptr[2]); s3 = op(s3, sptr[3]); } D[i] = s0; D[i+1] = s1; D[i+2] = s2; D[i+3] = s3; } #endif for( ; i < width; i++ ) { T s0 = src[0][i]; for( k = 1; k < _ksize; k++ ) s0 = op(s0, src[k][i]); D[i] = s0; } } } VecOp vecOp; }; template<class Op, class VecOp> struct MorphFilter : BaseFilter { typedef typename Op::rtype T; MorphFilter( const Mat& _kernel, Point _anchor ) { anchor = _anchor; ksize = _kernel.size(); CV_Assert( _kernel.type() == CV_8U ); std::vector<uchar> coeffs; // we do not really the values of non-zero // kernel elements, just their locations preprocess2DKernel( _kernel, coords, coeffs ); ptrs.resize( coords.size() ); } void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn) { const Point* pt = &coords[0]; const T** kp = (const T**)&ptrs[0]; int i, k, nz = (int)coords.size(); Op op; width *= cn; for( ; count > 0; count--, dst += dststep, src++ ) { T* D = (T*)dst; for( k = 0; k < nz; k++ ) kp[k] = (const T*)src[pt[k].y] + pt[k].x*cn; i = vecOp(&ptrs[0], nz, dst, width); #if CV_ENABLE_UNROLLED for( ; i <= width - 4; i += 4 ) { const T* sptr = kp[0] + i; T s0 = sptr[0], s1 = sptr[1], s2 = sptr[2], s3 = sptr[3]; for( k = 1; k < nz; k++ ) { sptr = kp[k] + i; s0 = op(s0, sptr[0]); s1 = op(s1, sptr[1]); s2 = op(s2, sptr[2]); s3 = op(s3, sptr[3]); } D[i] = s0; D[i+1] = s1; D[i+2] = s2; D[i+3] = s3; } #endif for( ; i < width; i++ ) { T s0 = kp[0][i]; for( k = 1; k < nz; k++ ) s0 = op(s0, kp[k][i]); D[i] = s0; } } } std::vector<Point> coords; std::vector<uchar*> ptrs; VecOp vecOp; }; } /////////////////////////////////// External Interface ///////////////////////////////////// cv::Ptr<cv::BaseRowFilter> cv::getMorphologyRowFilter(int op, int type, int ksize, int anchor) { int depth = CV_MAT_DEPTH(type); if( anchor < 0 ) anchor = ksize/2; CV_Assert( op == MORPH_ERODE || op == MORPH_DILATE ); if( op == MORPH_ERODE ) { if( depth == CV_8U ) return makePtr<MorphRowFilter<MinOp<uchar>, ErodeRowVec8u> >(ksize, anchor); if( depth == CV_16U ) return makePtr<MorphRowFilter<MinOp<ushort>, ErodeRowVec16u> >(ksize, anchor); if( depth == CV_16S ) return makePtr<MorphRowFilter<MinOp<short>, ErodeRowVec16s> >(ksize, anchor); if( depth == CV_32F ) return makePtr<MorphRowFilter<MinOp<float>, ErodeRowVec32f> >(ksize, anchor); if( depth == CV_64F ) return makePtr<MorphRowFilter<MinOp<double>, ErodeRowVec64f> >(ksize, anchor); } else { if( depth == CV_8U ) return makePtr<MorphRowFilter<MaxOp<uchar>, DilateRowVec8u> >(ksize, anchor); if( depth == CV_16U ) return makePtr<MorphRowFilter<MaxOp<ushort>, DilateRowVec16u> >(ksize, anchor); if( depth == CV_16S ) return makePtr<MorphRowFilter<MaxOp<short>, DilateRowVec16s> >(ksize, anchor); if( depth == CV_32F ) return makePtr<MorphRowFilter<MaxOp<float>, DilateRowVec32f> >(ksize, anchor); if( depth == CV_64F ) return makePtr<MorphRowFilter<MaxOp<double>, DilateRowVec64f> >(ksize, anchor); } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); return Ptr<BaseRowFilter>(); } cv::Ptr<cv::BaseColumnFilter> cv::getMorphologyColumnFilter(int op, int type, int ksize, int anchor) { int depth = CV_MAT_DEPTH(type); if( anchor < 0 ) anchor = ksize/2; CV_Assert( op == MORPH_ERODE || op == MORPH_DILATE ); if( op == MORPH_ERODE ) { if( depth == CV_8U ) return makePtr<MorphColumnFilter<MinOp<uchar>, ErodeColumnVec8u> >(ksize, anchor); if( depth == CV_16U ) return makePtr<MorphColumnFilter<MinOp<ushort>, ErodeColumnVec16u> >(ksize, anchor); if( depth == CV_16S ) return makePtr<MorphColumnFilter<MinOp<short>, ErodeColumnVec16s> >(ksize, anchor); if( depth == CV_32F ) return makePtr<MorphColumnFilter<MinOp<float>, ErodeColumnVec32f> >(ksize, anchor); if( depth == CV_64F ) return makePtr<MorphColumnFilter<MinOp<double>, ErodeColumnVec64f> >(ksize, anchor); } else { if( depth == CV_8U ) return makePtr<MorphColumnFilter<MaxOp<uchar>, DilateColumnVec8u> >(ksize, anchor); if( depth == CV_16U ) return makePtr<MorphColumnFilter<MaxOp<ushort>, DilateColumnVec16u> >(ksize, anchor); if( depth == CV_16S ) return makePtr<MorphColumnFilter<MaxOp<short>, DilateColumnVec16s> >(ksize, anchor); if( depth == CV_32F ) return makePtr<MorphColumnFilter<MaxOp<float>, DilateColumnVec32f> >(ksize, anchor); if( depth == CV_64F ) return makePtr<MorphColumnFilter<MaxOp<double>, DilateColumnVec64f> >(ksize, anchor); } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); return Ptr<BaseColumnFilter>(); } cv::Ptr<cv::BaseFilter> cv::getMorphologyFilter(int op, int type, InputArray _kernel, Point anchor) { Mat kernel = _kernel.getMat(); int depth = CV_MAT_DEPTH(type); anchor = normalizeAnchor(anchor, kernel.size()); CV_Assert( op == MORPH_ERODE || op == MORPH_DILATE ); if( op == MORPH_ERODE ) { if( depth == CV_8U ) return makePtr<MorphFilter<MinOp<uchar>, ErodeVec8u> >(kernel, anchor); if( depth == CV_16U ) return makePtr<MorphFilter<MinOp<ushort>, ErodeVec16u> >(kernel, anchor); if( depth == CV_16S ) return makePtr<MorphFilter<MinOp<short>, ErodeVec16s> >(kernel, anchor); if( depth == CV_32F ) return makePtr<MorphFilter<MinOp<float>, ErodeVec32f> >(kernel, anchor); if( depth == CV_64F ) return makePtr<MorphFilter<MinOp<double>, ErodeVec64f> >(kernel, anchor); } else { if( depth == CV_8U ) return makePtr<MorphFilter<MaxOp<uchar>, DilateVec8u> >(kernel, anchor); if( depth == CV_16U ) return makePtr<MorphFilter<MaxOp<ushort>, DilateVec16u> >(kernel, anchor); if( depth == CV_16S ) return makePtr<MorphFilter<MaxOp<short>, DilateVec16s> >(kernel, anchor); if( depth == CV_32F ) return makePtr<MorphFilter<MaxOp<float>, DilateVec32f> >(kernel, anchor); if( depth == CV_64F ) return makePtr<MorphFilter<MaxOp<double>, DilateVec64f> >(kernel, anchor); } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); return Ptr<BaseFilter>(); } cv::Ptr<cv::FilterEngine> cv::createMorphologyFilter( int op, int type, InputArray _kernel, Point anchor, int _rowBorderType, int _columnBorderType, const Scalar& _borderValue ) { Mat kernel = _kernel.getMat(); anchor = normalizeAnchor(anchor, kernel.size()); Ptr<BaseRowFilter> rowFilter; Ptr<BaseColumnFilter> columnFilter; Ptr<BaseFilter> filter2D; if( countNonZero(kernel) == kernel.rows*kernel.cols ) { // rectangular structuring element rowFilter = getMorphologyRowFilter(op, type, kernel.cols, anchor.x); columnFilter = getMorphologyColumnFilter(op, type, kernel.rows, anchor.y); } else filter2D = getMorphologyFilter(op, type, kernel, anchor); Scalar borderValue = _borderValue; if( (_rowBorderType == BORDER_CONSTANT || _columnBorderType == BORDER_CONSTANT) && borderValue == morphologyDefaultBorderValue() ) { int depth = CV_MAT_DEPTH(type); CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_16S || depth == CV_32F || depth == CV_64F ); if( op == MORPH_ERODE ) borderValue = Scalar::all( depth == CV_8U ? (double)UCHAR_MAX : depth == CV_16U ? (double)USHRT_MAX : depth == CV_16S ? (double)SHRT_MAX : depth == CV_32F ? (double)FLT_MAX : DBL_MAX); else borderValue = Scalar::all( depth == CV_8U || depth == CV_16U ? 0. : depth == CV_16S ? (double)SHRT_MIN : depth == CV_32F ? (double)-FLT_MAX : -DBL_MAX); } return makePtr<FilterEngine>(filter2D, rowFilter, columnFilter, type, type, type, _rowBorderType, _columnBorderType, borderValue ); } cv::Mat cv::getStructuringElement(int shape, Size ksize, Point anchor) { int i, j; int r = 0, c = 0; double inv_r2 = 0; CV_Assert( shape == MORPH_RECT || shape == MORPH_CROSS || shape == MORPH_ELLIPSE ); anchor = normalizeAnchor(anchor, ksize); if( ksize == Size(1,1) ) shape = MORPH_RECT; if( shape == MORPH_ELLIPSE ) { r = ksize.height/2; c = ksize.width/2; inv_r2 = r ? 1./((double)r*r) : 0; } Mat elem(ksize, CV_8U); for( i = 0; i < ksize.height; i++ ) { uchar* ptr = elem.ptr(i); int j1 = 0, j2 = 0; if( shape == MORPH_RECT || (shape == MORPH_CROSS && i == anchor.y) ) j2 = ksize.width; else if( shape == MORPH_CROSS ) j1 = anchor.x, j2 = j1 + 1; else { int dy = i - r; if( std::abs(dy) <= r ) { int dx = saturate_cast<int>(c*std::sqrt((r*r - dy*dy)*inv_r2)); j1 = std::max( c - dx, 0 ); j2 = std::min( c + dx + 1, ksize.width ); } } for( j = 0; j < j1; j++ ) ptr[j] = 0; for( ; j < j2; j++ ) ptr[j] = 1; for( ; j < ksize.width; j++ ) ptr[j] = 0; } return elem; } namespace cv { class MorphologyRunner : public ParallelLoopBody { public: MorphologyRunner(Mat _src, Mat _dst, int _nStripes, int _iterations, int _op, Mat _kernel, Point _anchor, int _rowBorderType, int _columnBorderType, const Scalar& _borderValue) : borderValue(_borderValue) { src = _src; dst = _dst; nStripes = _nStripes; iterations = _iterations; op = _op; kernel = _kernel; anchor = _anchor; rowBorderType = _rowBorderType; columnBorderType = _columnBorderType; } void operator () ( const Range& range ) const { int row0 = std::min(cvRound(range.start * src.rows / nStripes), src.rows); int row1 = std::min(cvRound(range.end * src.rows / nStripes), src.rows); /*if(0) printf("Size = (%d, %d), range[%d,%d), row0 = %d, row1 = %d\n", src.rows, src.cols, range.start, range.end, row0, row1);*/ Mat srcStripe = src.rowRange(row0, row1); Mat dstStripe = dst.rowRange(row0, row1); Ptr<FilterEngine> f = createMorphologyFilter(op, src.type(), kernel, anchor, rowBorderType, columnBorderType, borderValue ); f->apply( srcStripe, dstStripe ); for( int i = 1; i < iterations; i++ ) f->apply( dstStripe, dstStripe ); } private: Mat src; Mat dst; int nStripes; int iterations; int op; Mat kernel; Point anchor; int rowBorderType; int columnBorderType; Scalar borderValue; }; #if IPP_VERSION_X100 >= 801 static bool IPPMorphReplicate(int op, const Mat &src, Mat &dst, const Mat &kernel, const Size& ksize, const Point &anchor, bool rectKernel) { int type = src.type(); const Mat* _src = &src; Mat temp; if (src.data == dst.data) { src.copyTo(temp); _src = &temp; } IppiSize roiSize = {src.cols, src.rows}; IppiSize kernelSize = {ksize.width, ksize.height}; if (!rectKernel) { #if 1 if (((kernel.cols - 1) / 2 != anchor.x) || ((kernel.rows - 1) / 2 != anchor.y)) return false; #define IPP_MORPH_CASE(cvtype, flavor, data_type) \ case cvtype: \ {\ int specSize = 0, bufferSize = 0;\ if (0 > ippiMorphologyBorderGetSize_##flavor(roiSize.width, kernelSize, &specSize, &bufferSize))\ return false;\ IppiMorphState *pSpec = (IppiMorphState*)ippMalloc(specSize);\ Ipp8u *pBuffer = (Ipp8u*)ippMalloc(bufferSize);\ if (0 > ippiMorphologyBorderInit_##flavor(roiSize.width, kernel.ptr(), kernelSize, pSpec, pBuffer))\ {\ ippFree(pBuffer);\ ippFree(pSpec);\ return false;\ }\ bool ok = false;\ if (op == MORPH_ERODE)\ ok = (0 <= ippiErodeBorder_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0], dst.ptr<Ipp##data_type>(), (int)dst.step[0],\ roiSize, ippBorderRepl, 0, pSpec, pBuffer));\ else\ ok = (0 <= ippiDilateBorder_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0], dst.ptr<Ipp##data_type>(), (int)dst.step[0],\ roiSize, ippBorderRepl, 0, pSpec, pBuffer));\ ippFree(pBuffer);\ ippFree(pSpec);\ return ok;\ }\ break; #else IppiPoint point = {anchor.x, anchor.y}; // this is case, which can be used with the anchor not in center of the kernel, but // ippiMorphologyBorderGetSize_, ippiErodeBorderReplicate_ and ippiDilateBorderReplicate_ are deprecated. #define IPP_MORPH_CASE(cvtype, flavor, data_type) \ case cvtype: \ {\ int specSize = 0;\ int bufferSize = 0;\ if (0 > ippiMorphologyGetSize_##flavor( roiSize.width, kernel.ptr() kernelSize, &specSize))\ return false;\ bool ok = false;\ IppiMorphState* pState = (IppiMorphState*)ippMalloc(specSize);\ if (ippiMorphologyInit_##flavor(roiSize.width, kernel.ptr(), kernelSize, point, pState) >= 0)\ {\ if (op == MORPH_ERODE)\ ok = ippiErodeBorderReplicate_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0],\ dst.ptr<Ipp##data_type>(), (int)dst.step[0],\ roiSize, ippBorderRepl, pState ) >= 0;\ else\ ok = ippiDilateBorderReplicate_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0],\ dst.ptr<Ipp##data_type>(), (int)dst.step[0],\ roiSize, ippBorderRepl, pState ) >= 0;\ }\ ippFree(pState);\ return ok;\ }\ break; #endif switch (type) { IPP_MORPH_CASE(CV_8UC1, 8u_C1R, 8u); IPP_MORPH_CASE(CV_8UC3, 8u_C3R, 8u); IPP_MORPH_CASE(CV_8UC4, 8u_C4R, 8u); IPP_MORPH_CASE(CV_32FC1, 32f_C1R, 32f); IPP_MORPH_CASE(CV_32FC3, 32f_C3R, 32f); IPP_MORPH_CASE(CV_32FC4, 32f_C4R, 32f); default: ; } #undef IPP_MORPH_CASE } else { IppiPoint point = {anchor.x, anchor.y}; #define IPP_MORPH_CASE(cvtype, flavor, data_type) \ case cvtype: \ {\ int bufSize = 0;\ if (0 > ippiFilterMinGetBufferSize_##flavor(src.cols, kernelSize, &bufSize))\ return false;\ AutoBuffer<uchar> buf(bufSize + 64);\ uchar* buffer = alignPtr((uchar*)buf, 32);\ if (op == MORPH_ERODE)\ return (0 <= ippiFilterMinBorderReplicate_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0], dst.ptr<Ipp##data_type>(), (int)dst.step[0], roiSize, kernelSize, point, buffer));\ return (0 <= ippiFilterMaxBorderReplicate_##flavor(_src->ptr<Ipp##data_type>(), (int)_src->step[0], dst.ptr<Ipp##data_type>(), (int)dst.step[0], roiSize, kernelSize, point, buffer));\ }\ break; switch (type) { IPP_MORPH_CASE(CV_8UC1, 8u_C1R, 8u); IPP_MORPH_CASE(CV_8UC3, 8u_C3R, 8u); IPP_MORPH_CASE(CV_8UC4, 8u_C4R, 8u); IPP_MORPH_CASE(CV_32FC1, 32f_C1R, 32f); IPP_MORPH_CASE(CV_32FC3, 32f_C3R, 32f); IPP_MORPH_CASE(CV_32FC4, 32f_C4R, 32f); default: ; } #undef IPP_MORPH_CASE } return false; } static bool IPPMorphOp(int op, InputArray _src, OutputArray _dst, const Mat& _kernel, Point anchor, int iterations, int borderType, const Scalar &borderValue) { Mat src = _src.getMat(), kernel = _kernel; int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); if( !( depth == CV_8U || depth == CV_32F ) || !(cn == 1 || cn == 3 || cn == 4) || !( borderType == cv::BORDER_REPLICATE || (borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() && kernel.size() == Size(3,3)) ) || !( op == MORPH_DILATE || op == MORPH_ERODE) || _src.isSubmatrix() ) return false; // In case BORDER_CONSTANT, IPPMorphReplicate works correct with kernels of size 3*3 only if( borderType == cv::BORDER_CONSTANT && kernel.data ) { int x, y; for( y = 0; y < kernel.rows; y++ ) { if( kernel.at<uchar>(y, anchor.x) != 0 ) continue; for( x = 0; x < kernel.cols; x++ ) { if( kernel.at<uchar>(y,x) != 0 ) return false; } } for( x = 0; x < kernel.cols; x++ ) { if( kernel.at<uchar>(anchor.y, x) != 0 ) continue; for( y = 0; y < kernel.rows; y++ ) { if( kernel.at<uchar>(y,x) != 0 ) return false; } } } Size ksize = !kernel.empty() ? kernel.size() : Size(3,3); _dst.create( src.size(), src.type() ); Mat dst = _dst.getMat(); if( iterations == 0 || kernel.rows*kernel.cols == 1 ) { src.copyTo(dst); return true; } bool rectKernel = false; if( kernel.empty() ) { ksize = Size(1+iterations*2,1+iterations*2); anchor = Point(iterations, iterations); rectKernel = true; iterations = 1; } else if( iterations >= 1 && countNonZero(kernel) == kernel.rows*kernel.cols ) { ksize = Size(ksize.width + (iterations-1)*(ksize.width-1), ksize.height + (iterations-1)*(ksize.height-1)), anchor = Point(anchor.x*iterations, anchor.y*iterations); kernel = Mat(); rectKernel = true; iterations = 1; } // TODO: implement the case of iterations > 1. if( iterations > 1 ) return false; return IPPMorphReplicate( op, src, dst, kernel, ksize, anchor, rectKernel ); } #endif #ifdef HAVE_OPENCL #define ROUNDUP(sz, n) ((sz) + (n) - 1 - (((sz) + (n) - 1) % (n))) static bool ocl_morphSmall( InputArray _src, OutputArray _dst, InputArray _kernel, Point anchor, int borderType, int op, int actual_op = -1, InputArray _extraMat = noArray()) { const ocl::Device & dev = ocl::Device::getDefault(); int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), esz = CV_ELEM_SIZE(type); bool doubleSupport = dev.doubleFPConfig() > 0; if (cn > 4 || (!doubleSupport && depth == CV_64F) || _src.offset() % esz != 0 || _src.step() % esz != 0) return false; bool haveExtraMat = !_extraMat.empty(); CV_Assert(actual_op <= 3 || haveExtraMat); Size ksize = _kernel.size(); if (anchor.x < 0) anchor.x = ksize.width / 2; if (anchor.y < 0) anchor.y = ksize.height / 2; Size size = _src.size(), wholeSize; bool isolated = (borderType & BORDER_ISOLATED) != 0; borderType &= ~BORDER_ISOLATED; int wdepth = depth, wtype = type; if (depth == CV_8U) { wdepth = CV_32S; wtype = CV_MAKETYPE(wdepth, cn); } char cvt[2][40]; const char * const borderMap[] = { "BORDER_CONSTANT", "BORDER_REPLICATE", "BORDER_REFLECT", 0, "BORDER_REFLECT_101" }; size_t globalsize[2] = { size.width, size.height }; UMat src = _src.getUMat(); if (!isolated) { Point ofs; src.locateROI(wholeSize, ofs); } int h = isolated ? size.height : wholeSize.height; int w = isolated ? size.width : wholeSize.width; if (w < ksize.width || h < ksize.height) return false; // Figure out what vector size to use for loading the pixels. int pxLoadNumPixels = cn != 1 || size.width % 4 ? 1 : 4; int pxLoadVecSize = cn * pxLoadNumPixels; // Figure out how many pixels per work item to compute in X and Y // directions. Too many and we run out of registers. int pxPerWorkItemX = 1, pxPerWorkItemY = 1; if (cn <= 2 && ksize.width <= 4 && ksize.height <= 4) { pxPerWorkItemX = size.width % 8 ? size.width % 4 ? size.width % 2 ? 1 : 2 : 4 : 8; pxPerWorkItemY = size.height % 2 ? 1 : 2; } else if (cn < 4 || (ksize.width <= 4 && ksize.height <= 4)) { pxPerWorkItemX = size.width % 2 ? 1 : 2; pxPerWorkItemY = size.height % 2 ? 1 : 2; } globalsize[0] = size.width / pxPerWorkItemX; globalsize[1] = size.height / pxPerWorkItemY; // Need some padding in the private array for pixels int privDataWidth = ROUNDUP(pxPerWorkItemX + ksize.width - 1, pxLoadNumPixels); // Make the global size a nice round number so the runtime can pick // from reasonable choices for the workgroup size const int wgRound = 256; globalsize[0] = ROUNDUP(globalsize[0], wgRound); if (actual_op < 0) actual_op = op; // build processing String processing; Mat kernel8u; _kernel.getMat().convertTo(kernel8u, CV_8U); for (int y = 0; y < kernel8u.rows; ++y) for (int x = 0; x < kernel8u.cols; ++x) if (kernel8u.at<uchar>(y, x) != 0) processing += format("PROCESS(%d,%d)", y, x); static const char * const op2str[] = { "OP_ERODE", "OP_DILATE", NULL, NULL, "OP_GRADIENT", "OP_TOPHAT", "OP_BLACKHAT" }; String opts = format("-D cn=%d " "-D ANCHOR_X=%d -D ANCHOR_Y=%d -D KERNEL_SIZE_X=%d -D KERNEL_SIZE_Y=%d " "-D PX_LOAD_VEC_SIZE=%d -D PX_LOAD_NUM_PX=%d -D DEPTH_%d " "-D PX_PER_WI_X=%d -D PX_PER_WI_Y=%d -D PRIV_DATA_WIDTH=%d -D %s -D %s " "-D PX_LOAD_X_ITERATIONS=%d -D PX_LOAD_Y_ITERATIONS=%d " "-D srcT=%s -D srcT1=%s -D dstT=srcT -D dstT1=srcT1 -D WT=%s -D WT1=%s " "-D convertToWT=%s -D convertToDstT=%s -D PX_LOAD_FLOAT_VEC_CONV=convert_%s -D PROCESS_ELEM_=%s -D %s%s", cn, anchor.x, anchor.y, ksize.width, ksize.height, pxLoadVecSize, pxLoadNumPixels, depth, pxPerWorkItemX, pxPerWorkItemY, privDataWidth, borderMap[borderType], isolated ? "BORDER_ISOLATED" : "NO_BORDER_ISOLATED", privDataWidth / pxLoadNumPixels, pxPerWorkItemY + ksize.height - 1, ocl::typeToStr(type), ocl::typeToStr(depth), haveExtraMat ? ocl::typeToStr(wtype):"srcT",//to prevent overflow - WT haveExtraMat ? ocl::typeToStr(wdepth):"srcT1",//to prevent overflow - WT1 haveExtraMat ? ocl::convertTypeStr(depth, wdepth, cn, cvt[0]) : "noconvert",//to prevent overflow - src to WT haveExtraMat ? ocl::convertTypeStr(wdepth, depth, cn, cvt[1]) : "noconvert",//to prevent overflow - WT to dst ocl::typeToStr(CV_MAKE_TYPE(haveExtraMat ? wdepth : depth, pxLoadVecSize)), //PX_LOAD_FLOAT_VEC_CONV processing.c_str(), op2str[op], actual_op == op ? "" : cv::format(" -D %s", op2str[actual_op]).c_str()); ocl::Kernel kernel("filterSmall", cv::ocl::imgproc::filterSmall_oclsrc, opts); if (kernel.empty()) return false; _dst.create(size, type); UMat dst = _dst.getUMat(); UMat source; if(src.u != dst.u) source = src; else { Point ofs; int cols = src.cols, rows = src.rows; src.locateROI(wholeSize, ofs); src.adjustROI(ofs.y, wholeSize.height - rows - ofs.y, ofs.x, wholeSize.width - cols - ofs.x); src.copyTo(source); src.adjustROI(-ofs.y, -wholeSize.height + rows + ofs.y, -ofs.x, -wholeSize.width + cols + ofs.x); source.adjustROI(-ofs.y, -wholeSize.height + rows + ofs.y, -ofs.x, -wholeSize.width + cols + ofs.x); source.locateROI(wholeSize, ofs); } UMat extraMat = _extraMat.getUMat(); int idxArg = kernel.set(0, ocl::KernelArg::PtrReadOnly(source)); idxArg = kernel.set(idxArg, (int)source.step); int srcOffsetX = (int)((source.offset % source.step) / source.elemSize()); int srcOffsetY = (int)(source.offset / source.step); int srcEndX = isolated ? srcOffsetX + size.width : wholeSize.width; int srcEndY = isolated ? srcOffsetY + size.height : wholeSize.height; idxArg = kernel.set(idxArg, srcOffsetX); idxArg = kernel.set(idxArg, srcOffsetY); idxArg = kernel.set(idxArg, srcEndX); idxArg = kernel.set(idxArg, srcEndY); idxArg = kernel.set(idxArg, ocl::KernelArg::WriteOnly(dst)); if (haveExtraMat) { idxArg = kernel.set(idxArg, ocl::KernelArg::ReadOnlyNoSize(extraMat)); } return kernel.run(2, globalsize, NULL, false); } static bool ocl_morphOp(InputArray _src, OutputArray _dst, InputArray _kernel, Point anchor, int iterations, int op, int borderType, const Scalar &, int actual_op = -1, InputArray _extraMat = noArray()) { const ocl::Device & dev = ocl::Device::getDefault(); int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), esz = CV_ELEM_SIZE(type); Mat kernel = _kernel.getMat(); Size ksize = !kernel.empty() ? kernel.size() : Size(3, 3), ssize = _src.size(); bool doubleSupport = dev.doubleFPConfig() > 0; if ((depth == CV_64F && !doubleSupport) || borderType != BORDER_CONSTANT) return false; bool haveExtraMat = !_extraMat.empty(); CV_Assert(actual_op <= 3 || haveExtraMat); if (kernel.empty()) { kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2)); anchor = Point(iterations, iterations); iterations = 1; } else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols ) { anchor = Point(anchor.x*iterations, anchor.y*iterations); kernel = getStructuringElement(MORPH_RECT, Size(ksize.width + (iterations-1)*(ksize.width-1), ksize.height + (iterations-1)*(ksize.height-1)), anchor); iterations = 1; } // try to use OpenCL kernel adopted for small morph kernel if (dev.isIntel() && !(dev.type() & ocl::Device::TYPE_CPU) && ((ksize.width < 5 && ksize.height < 5 && esz <= 4) || (ksize.width == 5 && ksize.height == 5 && cn == 1)) && (iterations == 1) #if defined __APPLE__ && cn == 1 #endif ) { if (ocl_morphSmall(_src, _dst, kernel, anchor, borderType, op, actual_op, _extraMat)) return true; } if (iterations == 0 || kernel.rows*kernel.cols == 1) { _src.copyTo(_dst); return true; } #ifdef ANDROID size_t localThreads[2] = { 16, 8 }; #else size_t localThreads[2] = { 16, 16 }; #endif size_t globalThreads[2] = { ssize.width, ssize.height }; #ifdef __APPLE__ if( actual_op != MORPH_ERODE && actual_op != MORPH_DILATE ) localThreads[0] = localThreads[1] = 4; #endif if (localThreads[0]*localThreads[1] * 2 < (localThreads[0] + ksize.width - 1) * (localThreads[1] + ksize.height - 1)) return false; #ifdef ANDROID if (dev.isNVidia()) return false; #endif // build processing String processing; Mat kernel8u; kernel.convertTo(kernel8u, CV_8U); for (int y = 0; y < kernel8u.rows; ++y) for (int x = 0; x < kernel8u.cols; ++x) if (kernel8u.at<uchar>(y, x) != 0) processing += format("PROCESS(%d,%d)", y, x); static const char * const op2str[] = { "OP_ERODE", "OP_DILATE", NULL, NULL, "OP_GRADIENT", "OP_TOPHAT", "OP_BLACKHAT" }; char cvt[2][50]; int wdepth = std::max(depth, CV_32F), scalarcn = cn == 3 ? 4 : cn; if (actual_op < 0) actual_op = op; std::vector<ocl::Kernel> kernels(iterations); for (int i = 0; i < iterations; i++) { int current_op = iterations == i + 1 ? actual_op : op; String buildOptions = format("-D RADIUSX=%d -D RADIUSY=%d -D LSIZE0=%d -D LSIZE1=%d -D %s%s" " -D PROCESS_ELEMS=%s -D T=%s -D DEPTH_%d -D cn=%d -D T1=%s" " -D convertToWT=%s -D convertToT=%s -D ST=%s%s", anchor.x, anchor.y, (int)localThreads[0], (int)localThreads[1], op2str[op], doubleSupport ? " -D DOUBLE_SUPPORT" : "", processing.c_str(), ocl::typeToStr(type), depth, cn, ocl::typeToStr(depth), ocl::convertTypeStr(depth, wdepth, cn, cvt[0]), ocl::convertTypeStr(wdepth, depth, cn, cvt[1]), ocl::typeToStr(CV_MAKE_TYPE(depth, scalarcn)), current_op == op ? "" : cv::format(" -D %s", op2str[current_op]).c_str()); kernels[i].create("morph", ocl::imgproc::morph_oclsrc, buildOptions); if (kernels[i].empty()) return false; } UMat src = _src.getUMat(), extraMat = _extraMat.getUMat(); _dst.create(src.size(), src.type()); UMat dst = _dst.getUMat(); if (iterations == 1 && src.u != dst.u) { Size wholesize; Point ofs; src.locateROI(wholesize, ofs); int wholecols = wholesize.width, wholerows = wholesize.height; if (haveExtraMat) kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst), ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows, ocl::KernelArg::ReadOnlyNoSize(extraMat)); else kernels[0].args(ocl::KernelArg::ReadOnlyNoSize(src), ocl::KernelArg::WriteOnlyNoSize(dst), ofs.x, ofs.y, src.cols, src.rows, wholecols, wholerows); return kernels[0].run(2, globalThreads, localThreads, false); } for (int i = 0; i < iterations; i++) { UMat source; Size wholesize; Point ofs; if (i == 0) { int cols = src.cols, rows = src.rows; src.locateROI(wholesize, ofs); src.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x); if(src.u != dst.u) source = src; else src.copyTo(source); src.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x); source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x); } else { int cols = dst.cols, rows = dst.rows; dst.locateROI(wholesize, ofs); dst.adjustROI(ofs.y, wholesize.height - rows - ofs.y, ofs.x, wholesize.width - cols - ofs.x); dst.copyTo(source); dst.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x); source.adjustROI(-ofs.y, -wholesize.height + rows + ofs.y, -ofs.x, -wholesize.width + cols + ofs.x); } source.locateROI(wholesize, ofs); if (haveExtraMat && iterations == i + 1) kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst), ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height, ocl::KernelArg::ReadOnlyNoSize(extraMat)); else kernels[i].args(ocl::KernelArg::ReadOnlyNoSize(source), ocl::KernelArg::WriteOnlyNoSize(dst), ofs.x, ofs.y, source.cols, source.rows, wholesize.width, wholesize.height); if (!kernels[i].run(2, globalThreads, localThreads, false)) return false; } return true; } #endif static void morphOp( int op, InputArray _src, OutputArray _dst, InputArray _kernel, Point anchor, int iterations, int borderType, const Scalar& borderValue ) { Mat kernel = _kernel.getMat(); Size ksize = !kernel.empty() ? kernel.size() : Size(3,3); anchor = normalizeAnchor(anchor, ksize); CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 && borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() && (op == MORPH_ERODE || op == MORPH_DILATE) && anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1, ocl_morphOp(_src, _dst, kernel, anchor, iterations, op, borderType, borderValue) ) if (iterations == 0 || kernel.rows*kernel.cols == 1) { _src.copyTo(_dst); return; } if (kernel.empty()) { kernel = getStructuringElement(MORPH_RECT, Size(1+iterations*2,1+iterations*2)); anchor = Point(iterations, iterations); iterations = 1; } else if( iterations > 1 && countNonZero(kernel) == kernel.rows*kernel.cols ) { anchor = Point(anchor.x*iterations, anchor.y*iterations); kernel = getStructuringElement(MORPH_RECT, Size(ksize.width + (iterations-1)*(ksize.width-1), ksize.height + (iterations-1)*(ksize.height-1)), anchor); iterations = 1; } #if IPP_VERSION_X100 >= 801 CV_IPP_CHECK() { if( IPPMorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue) ) { CV_IMPL_ADD(CV_IMPL_IPP); return; } } #endif Mat src = _src.getMat(); _dst.create( src.size(), src.type() ); Mat dst = _dst.getMat(); int nStripes = 1; #if defined HAVE_TEGRA_OPTIMIZATION if (src.data != dst.data && iterations == 1 && //NOTE: threads are not used for inplace processing (borderType & BORDER_ISOLATED) == 0 && //TODO: check border types src.rows >= 64 ) //NOTE: just heuristics nStripes = 4; #endif parallel_for_(Range(0, nStripes), MorphologyRunner(src, dst, nStripes, iterations, op, kernel, anchor, borderType, borderType, borderValue)); } } void cv::erode( InputArray src, OutputArray dst, InputArray kernel, Point anchor, int iterations, int borderType, const Scalar& borderValue ) { morphOp( MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType, borderValue ); } void cv::dilate( InputArray src, OutputArray dst, InputArray kernel, Point anchor, int iterations, int borderType, const Scalar& borderValue ) { morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue ); } #ifdef HAVE_OPENCL namespace cv { static bool ocl_morphologyEx(InputArray _src, OutputArray _dst, int op, InputArray kernel, Point anchor, int iterations, int borderType, const Scalar& borderValue) { _dst.createSameSize(_src, _src.type()); bool submat = _dst.isSubmatrix(); UMat temp; _OutputArray _temp = submat ? _dst : _OutputArray(temp); switch( op ) { case MORPH_ERODE: if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) return false; break; case MORPH_DILATE: if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue )) return false; break; case MORPH_OPEN: if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) return false; if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue )) return false; break; case MORPH_CLOSE: if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue )) return false; if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) return false; break; case MORPH_GRADIENT: if (!ocl_morphOp( _src, temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) return false; if (!ocl_morphOp( _src, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_GRADIENT, temp )) return false; break; case MORPH_TOPHAT: if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue )) return false; if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue, MORPH_TOPHAT, _src )) return false; break; case MORPH_BLACKHAT: if (!ocl_morphOp( _src, _temp, kernel, anchor, iterations, MORPH_DILATE, borderType, borderValue )) return false; if (!ocl_morphOp( _temp, _dst, kernel, anchor, iterations, MORPH_ERODE, borderType, borderValue, MORPH_BLACKHAT, _src )) return false; break; default: CV_Error( CV_StsBadArg, "unknown morphological operation" ); } return true; } } #endif void cv::morphologyEx( InputArray _src, OutputArray _dst, int op, InputArray _kernel, Point anchor, int iterations, int borderType, const Scalar& borderValue ) { Mat kernel = _kernel.getMat(); if (kernel.empty()) { kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(1,1)); } #ifdef HAVE_OPENCL Size ksize = kernel.size(); anchor = normalizeAnchor(anchor, ksize); CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 && anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 && borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(), ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue)) #endif Mat src = _src.getMat(), temp; _dst.create(src.size(), src.type()); Mat dst = _dst.getMat(); switch( op ) { case MORPH_ERODE: erode( src, dst, kernel, anchor, iterations, borderType, borderValue ); break; case MORPH_DILATE: dilate( src, dst, kernel, anchor, iterations, borderType, borderValue ); break; case MORPH_OPEN: erode( src, dst, kernel, anchor, iterations, borderType, borderValue ); dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue ); break; case CV_MOP_CLOSE: dilate( src, dst, kernel, anchor, iterations, borderType, borderValue ); erode( dst, dst, kernel, anchor, iterations, borderType, borderValue ); break; case CV_MOP_GRADIENT: erode( src, temp, kernel, anchor, iterations, borderType, borderValue ); dilate( src, dst, kernel, anchor, iterations, borderType, borderValue ); dst -= temp; break; case CV_MOP_TOPHAT: if( src.data != dst.data ) temp = dst; erode( src, temp, kernel, anchor, iterations, borderType, borderValue ); dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue ); dst = src - temp; break; case CV_MOP_BLACKHAT: if( src.data != dst.data ) temp = dst; dilate( src, temp, kernel, anchor, iterations, borderType, borderValue ); erode( temp, temp, kernel, anchor, iterations, borderType, borderValue ); dst = temp - src; break; default: CV_Error( CV_StsBadArg, "unknown morphological operation" ); } } CV_IMPL IplConvKernel * cvCreateStructuringElementEx( int cols, int rows, int anchorX, int anchorY, int shape, int *values ) { cv::Size ksize = cv::Size(cols, rows); cv::Point anchor = cv::Point(anchorX, anchorY); CV_Assert( cols > 0 && rows > 0 && anchor.inside(cv::Rect(0,0,cols,rows)) && (shape != CV_SHAPE_CUSTOM || values != 0)); int i, size = rows * cols; int element_size = sizeof(IplConvKernel) + size*sizeof(int); IplConvKernel *element = (IplConvKernel*)cvAlloc(element_size + 32); element->nCols = cols; element->nRows = rows; element->anchorX = anchorX; element->anchorY = anchorY; element->nShiftR = shape < CV_SHAPE_ELLIPSE ? shape : CV_SHAPE_CUSTOM; element->values = (int*)(element + 1); if( shape == CV_SHAPE_CUSTOM ) { for( i = 0; i < size; i++ ) element->values[i] = values[i]; } else { cv::Mat elem = cv::getStructuringElement(shape, ksize, anchor); for( i = 0; i < size; i++ ) element->values[i] = elem.ptr()[i]; } return element; } CV_IMPL void cvReleaseStructuringElement( IplConvKernel ** element ) { if( !element ) CV_Error( CV_StsNullPtr, "" ); cvFree( element ); } static void convertConvKernel( const IplConvKernel* src, cv::Mat& dst, cv::Point& anchor ) { if(!src) { anchor = cv::Point(1,1); dst.release(); return; } anchor = cv::Point(src->anchorX, src->anchorY); dst.create(src->nRows, src->nCols, CV_8U); int i, size = src->nRows*src->nCols; for( i = 0; i < size; i++ ) dst.ptr()[i] = (uchar)(src->values[i] != 0); } CV_IMPL void cvErode( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element, int iterations ) { cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel; CV_Assert( src.size() == dst.size() && src.type() == dst.type() ); cv::Point anchor; convertConvKernel( element, kernel, anchor ); cv::erode( src, dst, kernel, anchor, iterations, cv::BORDER_REPLICATE ); } CV_IMPL void cvDilate( const CvArr* srcarr, CvArr* dstarr, IplConvKernel* element, int iterations ) { cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel; CV_Assert( src.size() == dst.size() && src.type() == dst.type() ); cv::Point anchor; convertConvKernel( element, kernel, anchor ); cv::dilate( src, dst, kernel, anchor, iterations, cv::BORDER_REPLICATE ); } CV_IMPL void cvMorphologyEx( const void* srcarr, void* dstarr, void*, IplConvKernel* element, int op, int iterations ) { cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr), kernel; CV_Assert( src.size() == dst.size() && src.type() == dst.type() ); cv::Point anchor; IplConvKernel* temp_element = NULL; if (!element) { temp_element = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT); } else { temp_element = element; } convertConvKernel( temp_element, kernel, anchor ); if (!element) { cvReleaseStructuringElement(&temp_element); } cv::morphologyEx( src, dst, op, kernel, anchor, iterations, cv::BORDER_REPLICATE ); } /* End of file. */