// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html #include "precomp.hpp" #include "opencl_kernels_core.hpp" #include "stat.hpp" #include "mean.simd.hpp" #include "mean.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content namespace cv { Scalar mean(InputArray _src, InputArray _mask) { CV_INSTRUMENT_REGION(); Mat src = _src.getMat(), mask = _mask.getMat(); CV_Assert( mask.empty() || mask.type() == CV_8U ); int k, cn = src.channels(), depth = src.depth(); Scalar s = Scalar::all(0.0); CV_Assert( cn <= 4 ); if (src.isContinuous() && mask.isContinuous()) { CALL_HAL_RET2(meanStdDev, cv_hal_meanStdDev, s, src.data, 0, (int)src.total(), 1, src.type(), &s[0], nullptr /*stddev*/, mask.data, 0); } else { if (src.dims <= 2) { CALL_HAL_RET2(meanStdDev, cv_hal_meanStdDev, s, src.data, src.step, src.cols, src.rows, src.type(), &s[0], nullptr, mask.data, mask.step); } } SumFunc func = getSumFunc(depth); CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &mask, 0}; uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int total = (int)it.size, blockSize = total, intSumBlockSize = 0; int j, count = 0; AutoBuffer _buf; int* buf = (int*)&s[0]; bool blockSum = depth <= CV_16S; size_t esz = 0, nz0 = 0; if( blockSum ) { intSumBlockSize = depth <= CV_8S ? (1 << 23) : (1 << 15); blockSize = std::min(blockSize, intSumBlockSize); _buf.allocate(cn); buf = _buf.data(); for( k = 0; k < cn; k++ ) buf[k] = 0; esz = src.elemSize(); } for( size_t i = 0; i < it.nplanes; i++, ++it ) { for( j = 0; j < total; j += blockSize ) { int bsz = std::min(total - j, blockSize); int nz = func( ptrs[0], ptrs[1], (uchar*)buf, bsz, cn ); count += nz; nz0 += nz; if( blockSum && (count + blockSize >= intSumBlockSize || (i+1 >= it.nplanes && j+bsz >= total)) ) { for( k = 0; k < cn; k++ ) { s[k] += buf[k]; buf[k] = 0; } count = 0; } ptrs[0] += bsz*esz; if( ptrs[1] ) ptrs[1] += bsz; } } return s*(nz0 ? 1./nz0 : 0); } static SumSqrFunc getSumSqrFunc(int depth) { CV_INSTRUMENT_REGION(); CV_CPU_DISPATCH(getSumSqrFunc, (depth), CV_CPU_DISPATCH_MODES_ALL); } #ifdef HAVE_OPENCL static bool ocl_meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, InputArray _mask ) { CV_INSTRUMENT_REGION_OPENCL(); bool haveMask = _mask.kind() != _InputArray::NONE; int nz = haveMask ? -1 : (int)_src.total(); Scalar mean(0), stddev(0); const int cn = _src.channels(); if (cn > 4) return false; { int type = _src.type(), depth = CV_MAT_DEPTH(type); bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0, isContinuous = _src.isContinuous(), isMaskContinuous = _mask.isContinuous(); const ocl::Device &defDev = ocl::Device::getDefault(); int groups = defDev.maxComputeUnits(); if (defDev.isIntel()) { static const int subSliceEUCount = 10; groups = (groups / subSliceEUCount) * 2; } size_t wgs = defDev.maxWorkGroupSize(); int ddepth = std::max(CV_32S, depth), sqddepth = std::max(CV_32F, depth), dtype = CV_MAKE_TYPE(ddepth, cn), sqdtype = CV_MAKETYPE(sqddepth, cn); CV_Assert(!haveMask || _mask.type() == CV_8UC1); int wgs2_aligned = 1; while (wgs2_aligned < (int)wgs) wgs2_aligned <<= 1; wgs2_aligned >>= 1; if ( (!doubleSupport && depth == CV_64F) ) return false; char cvt[2][50]; String opts = format("-D srcT=%s -D srcT1=%s -D dstT=%s -D dstT1=%s -D sqddepth=%d" " -D sqdstT=%s -D sqdstT1=%s -D convertToSDT=%s -D cn=%d%s%s" " -D convertToDT=%s -D WGS=%d -D WGS2_ALIGNED=%d%s%s", ocl::typeToStr(type), ocl::typeToStr(depth), ocl::typeToStr(dtype), ocl::typeToStr(ddepth), sqddepth, ocl::typeToStr(sqdtype), ocl::typeToStr(sqddepth), ocl::convertTypeStr(depth, sqddepth, cn, cvt[0], sizeof(cvt[0])), cn, isContinuous ? " -D HAVE_SRC_CONT" : "", isMaskContinuous ? " -D HAVE_MASK_CONT" : "", ocl::convertTypeStr(depth, ddepth, cn, cvt[1], sizeof(cvt[1])), (int)wgs, wgs2_aligned, haveMask ? " -D HAVE_MASK" : "", doubleSupport ? " -D DOUBLE_SUPPORT" : ""); ocl::Kernel k("meanStdDev", ocl::core::meanstddev_oclsrc, opts); if (k.empty()) return false; int dbsize = groups * ((haveMask ? CV_ELEM_SIZE1(CV_32S) : 0) + CV_ELEM_SIZE(sqdtype) + CV_ELEM_SIZE(dtype)); UMat src = _src.getUMat(), db(1, dbsize, CV_8UC1), mask = _mask.getUMat(); ocl::KernelArg srcarg = ocl::KernelArg::ReadOnlyNoSize(src), dbarg = ocl::KernelArg::PtrWriteOnly(db), maskarg = ocl::KernelArg::ReadOnlyNoSize(mask); if (haveMask) k.args(srcarg, src.cols, (int)src.total(), groups, dbarg, maskarg); else k.args(srcarg, src.cols, (int)src.total(), groups, dbarg); size_t globalsize = groups * wgs; if(!k.run(1, &globalsize, &wgs, false)) return false; typedef Scalar (* part_sum)(Mat m); part_sum funcs[3] = { ocl_part_sum, ocl_part_sum, ocl_part_sum }; Mat dbm = db.getMat(ACCESS_READ); mean = funcs[ddepth - CV_32S](Mat(1, groups, dtype, dbm.ptr())); stddev = funcs[sqddepth - CV_32S](Mat(1, groups, sqdtype, dbm.ptr() + groups * CV_ELEM_SIZE(dtype))); if (haveMask) nz = saturate_cast(funcs[0](Mat(1, groups, CV_32SC1, dbm.ptr() + groups * (CV_ELEM_SIZE(dtype) + CV_ELEM_SIZE(sqdtype))))[0]); } double total = nz != 0 ? 1.0 / nz : 0; int k, j; for (int i = 0; i < cn; ++i) { mean[i] *= total; stddev[i] = std::sqrt(std::max(stddev[i] * total - mean[i] * mean[i] , 0.)); } for( j = 0; j < 2; j++ ) { const double * const sptr = j == 0 ? &mean[0] : &stddev[0]; _OutputArray _dst = j == 0 ? _mean : _sdv; if( !_dst.needed() ) continue; if( !_dst.fixedSize() ) _dst.create(cn, 1, CV_64F, -1, true); Mat dst = _dst.getMat(); int dcn = (int)dst.total(); CV_Assert( dst.type() == CV_64F && dst.isContinuous() && (dst.cols == 1 || dst.rows == 1) && dcn >= cn ); double* dptr = dst.ptr(); for( k = 0; k < cn; k++ ) dptr[k] = sptr[k]; for( ; k < dcn; k++ ) dptr[k] = 0; } return true; } #endif void meanStdDev(InputArray _src, OutputArray _mean, OutputArray _sdv, InputArray _mask) { CV_INSTRUMENT_REGION(); CV_Assert(!_src.empty()); CV_Assert( _mask.empty() || _mask.type() == CV_8UC1 ); CV_OCL_RUN(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2, ocl_meanStdDev(_src, _mean, _sdv, _mask)) Mat src = _src.getMat(), mask = _mask.getMat(); CV_Assert(mask.empty() || src.size == mask.size); int k, cn = src.channels(), depth = src.depth(); Mat mean_mat, stddev_mat; if(_mean.needed()) { if( !_mean.fixedSize() ) _mean.create(cn, 1, CV_64F, -1, true); mean_mat = _mean.getMat(); int dcn = (int)mean_mat.total(); CV_Assert( mean_mat.type() == CV_64F && mean_mat.isContinuous() && (mean_mat.cols == 1 || mean_mat.rows == 1) && dcn >= cn ); double* dptr = mean_mat.ptr(); for(k = cn ; k < dcn; k++ ) dptr[k] = 0; } if (_sdv.needed()) { if( !_sdv.fixedSize() ) _sdv.create(cn, 1, CV_64F, -1, true); stddev_mat = _sdv.getMat(); int dcn = (int)stddev_mat.total(); CV_Assert( stddev_mat.type() == CV_64F && stddev_mat.isContinuous() && (stddev_mat.cols == 1 || stddev_mat.rows == 1) && dcn >= cn ); double* dptr = stddev_mat.ptr(); for(k = cn ; k < dcn; k++ ) dptr[k] = 0; } if (src.isContinuous() && mask.isContinuous()) { CALL_HAL(meanStdDev, cv_hal_meanStdDev, src.data, 0, (int)src.total(), 1, src.type(), _mean.needed() ? mean_mat.ptr() : nullptr, _sdv.needed() ? stddev_mat.ptr() : nullptr, mask.data, 0); } else { if (src.dims <= 2) { CALL_HAL(meanStdDev, cv_hal_meanStdDev, src.data, src.step, src.cols, src.rows, src.type(), _mean.needed() ? mean_mat.ptr() : nullptr, _sdv.needed() ? stddev_mat.ptr() : nullptr, mask.data, mask.step); } } SumSqrFunc func = getSumSqrFunc(depth); CV_Assert( func != 0 ); const Mat* arrays[] = {&src, &mask, 0}; uchar* ptrs[2] = {}; NAryMatIterator it(arrays, ptrs); int total = (int)it.size, blockSize = total, intSumBlockSize = 0; int j; int64_t count = 0, nz0 = 0; AutoBuffer _buf(cn*4); double *s = (double*)_buf.data(), *sq = s + cn; int *sbuf = (int*)s, *sqbuf = (int*)sq; bool blockSum = depth <= CV_16S, blockSqSum = depth <= CV_8S; size_t esz = 0; for( k = 0; k < cn; k++ ) s[k] = sq[k] = 0; if( blockSum ) { intSumBlockSize = 1 << 15; blockSize = std::min(blockSize, intSumBlockSize); sbuf = (int*)(sq + cn); if( blockSqSum ) sqbuf = sbuf + cn; for( k = 0; k < cn; k++ ) sbuf[k] = sqbuf[k] = 0; esz = src.elemSize(); } for( size_t i = 0; i < it.nplanes; i++, ++it ) { for( j = 0; j < total; j += blockSize ) { int bsz = std::min(total - j, blockSize); int nz = func( ptrs[0], ptrs[1], (uchar*)sbuf, (uchar*)sqbuf, bsz, cn ); count += nz; nz0 += nz; if( blockSum && (count + blockSize >= intSumBlockSize || (i+1 >= it.nplanes && j+bsz >= total)) ) { for( k = 0; k < cn; k++ ) { s[k] += sbuf[k]; sbuf[k] = 0; } if( blockSqSum ) { for( k = 0; k < cn; k++ ) { sq[k] += sqbuf[k]; sqbuf[k] = 0; } } count = 0; } ptrs[0] += bsz*esz; if( ptrs[1] ) ptrs[1] += bsz; } } double scale = nz0 ? 1./nz0 : 0.; for( k = 0; k < cn; k++ ) { s[k] *= scale; sq[k] = std::sqrt(std::max(sq[k]*scale - s[k]*s[k], 0.)); } if (_mean.needed()) { const double* sptr = s; double* dptr = mean_mat.ptr(); for( k = 0; k < cn; k++ ) dptr[k] = sptr[k]; } if (_sdv.needed()) { const double* sptr = sq; double* dptr = stddev_mat.ptr(); for( k = 0; k < cn; k++ ) dptr[k] = sptr[k]; } } } // namespace