Add multi-channel mask support to mean, meanStdDev and setTo

This adds the possibility to use multi-channel masks for the functions
cv::mean, cv::meanStdDev and the method Mat::setTo. The tests have now a
probability to use multi-channel masks for operations that support them.
This also includes Mat::copyTo, which supported multi-channel masks
before, but there was no test confirming this.
pull/9551/head
Christof Kaufmann 8 years ago
parent 791a11f949
commit 46a668c565
  1. 4
      modules/core/include/opencv2/core.hpp
  2. 7
      modules/core/include/opencv2/core/mat.hpp
  3. 13
      modules/core/src/copy.cpp
  4. 304
      modules/core/src/stat.cpp
  5. 23
      modules/core/test/test_arithm.cpp
  6. 92
      modules/ts/src/ts_func.cpp

@ -608,7 +608,7 @@ CV_EXPORTS_W void findNonZero( InputArray src, OutputArray idx );
The function cv::mean calculates the mean value M of array elements, The function cv::mean calculates the mean value M of array elements,
independently for each channel, and return it: independently for each channel, and return it:
\f[\begin{array}{l} N = \sum _{I: \; \texttt{mask} (I) \ne 0} 1 \\ M_c = \left ( \sum _{I: \; \texttt{mask} (I) \ne 0}{ \texttt{mtx} (I)_c} \right )/N \end{array}\f] \f[\begin{array}{l} N_c = \sum _{I: \; {\texttt{mask} (I)_c} \ne 0} 1 \\ M_c = \left ( \sum _{I: \; {\texttt{mask} (I)_c} \ne 0}{ \texttt{src} (I)_c} \right )/N_c \end{array}\f]
When all the mask elements are 0's, the function returns Scalar::all(0) When all the mask elements are 0's, the function returns Scalar::all(0)
@param src input array that should have from 1 to 4 channels so that the result can be stored in @param src input array that should have from 1 to 4 channels so that the result can be stored in
Scalar_ . Scalar_ .
@ -622,7 +622,7 @@ CV_EXPORTS_W Scalar mean(InputArray src, InputArray mask = noArray());
The function cv::meanStdDev calculates the mean and the standard deviation M The function cv::meanStdDev calculates the mean and the standard deviation M
of array elements independently for each channel and returns it via the of array elements independently for each channel and returns it via the
output parameters: output parameters:
\f[\begin{array}{l} N = \sum _{I, \texttt{mask} (I) \ne 0} 1 \\ \texttt{mean} _c = \frac{\sum_{ I: \; \texttt{mask}(I) \ne 0} \texttt{src} (I)_c}{N} \\ \texttt{stddev} _c = \sqrt{\frac{\sum_{ I: \; \texttt{mask}(I) \ne 0} \left ( \texttt{src} (I)_c - \texttt{mean} _c \right )^2}{N}} \end{array}\f] \f[\begin{array}{l} N_c = \sum _{I, {\texttt{mask} (I)_c} \ne 0} 1 \\ \texttt{mean} _c = \frac{\sum_{ I: \; {\texttt{mask} (I)_c} \ne 0} \texttt{src} (I)_c}{N_c} \\ \texttt{stddev} _c = \sqrt{\frac{\sum_{ I: \; {\texttt{mask} (I)_c} \ne 0} \left ( \texttt{src} (I)_c - \texttt{mean} _c \right )^2}{N_c}} \end{array}\f]
When all the mask elements are 0's, the function returns When all the mask elements are 0's, the function returns
mean=stddev=Scalar::all(0). mean=stddev=Scalar::all(0).
@note The calculated standard deviation is only the diagonal of the @note The calculated standard deviation is only the diagonal of the

@ -1192,8 +1192,8 @@ public:
/** @overload /** @overload
@param m Destination matrix. If it does not have a proper size or type before the operation, it is @param m Destination matrix. If it does not have a proper size or type before the operation, it is
reallocated. reallocated.
@param mask Operation mask. Its non-zero elements indicate which matrix elements need to be copied. @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix
The mask has to be of type CV_8U and can have 1 or multiple channels. elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels.
*/ */
void copyTo( OutputArray m, InputArray mask ) const; void copyTo( OutputArray m, InputArray mask ) const;
@ -1229,7 +1229,8 @@ public:
This is an advanced variant of the Mat::operator=(const Scalar& s) operator. This is an advanced variant of the Mat::operator=(const Scalar& s) operator.
@param value Assigned scalar converted to the actual array type. @param value Assigned scalar converted to the actual array type.
@param mask Operation mask of the same size as \*this. @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix
elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels
*/ */
Mat& setTo(InputArray value, InputArray mask=noArray()); Mat& setTo(InputArray value, InputArray mask=noArray());

@ -334,7 +334,7 @@ static bool ipp_copyTo(const Mat &src, Mat &dst, const Mat &mask)
#ifdef HAVE_IPP_IW #ifdef HAVE_IPP_IW
CV_INSTRUMENT_REGION_IPP() CV_INSTRUMENT_REGION_IPP()
if(mask.channels() > 1 && mask.depth() != CV_8U) if(mask.channels() > 1 || mask.depth() != CV_8U)
return false; return false;
if (src.dims <= 2) if (src.dims <= 2)
@ -510,20 +510,23 @@ Mat& Mat::setTo(InputArray _value, InputArray _mask)
Mat value = _value.getMat(), mask = _mask.getMat(); Mat value = _value.getMat(), mask = _mask.getMat();
CV_Assert( checkScalar(value, type(), _value.kind(), _InputArray::MAT )); CV_Assert( checkScalar(value, type(), _value.kind(), _InputArray::MAT ));
CV_Assert( mask.empty() || (mask.type() == CV_8U && size == mask.size) ); int cn = channels(), mcn = mask.channels();
CV_Assert( mask.empty() || (mask.depth() == CV_8U && (mcn == 1 || mcn == cn) && size == mask.size) );
CV_IPP_RUN_FAST(ipp_Mat_setTo_Mat(*this, value, mask), *this) CV_IPP_RUN_FAST(ipp_Mat_setTo_Mat(*this, value, mask), *this)
size_t esz = elemSize(); size_t esz = mcn > 1 ? elemSize1() : elemSize();
BinaryFunc copymask = getCopyMaskFunc(esz); BinaryFunc copymask = getCopyMaskFunc(esz);
const Mat* arrays[] = { this, !mask.empty() ? &mask : 0, 0 }; const Mat* arrays[] = { this, !mask.empty() ? &mask : 0, 0 };
uchar* ptrs[2]={0,0}; uchar* ptrs[2]={0,0};
NAryMatIterator it(arrays, ptrs); NAryMatIterator it(arrays, ptrs);
int totalsz = (int)it.size, blockSize0 = std::min(totalsz, (int)((BLOCK_SIZE + esz-1)/esz)); int totalsz = (int)it.size*mcn;
int blockSize0 = std::min(totalsz, (int)((BLOCK_SIZE + esz-1)/esz));
blockSize0 -= blockSize0 % mcn; // must be divisible without remainder for unrolling and advancing
AutoBuffer<uchar> _scbuf(blockSize0*esz + 32); AutoBuffer<uchar> _scbuf(blockSize0*esz + 32);
uchar* scbuf = alignPtr((uchar*)_scbuf, (int)sizeof(double)); uchar* scbuf = alignPtr((uchar*)_scbuf, (int)sizeof(double));
convertAndUnrollScalar( value, type(), scbuf, blockSize0 ); convertAndUnrollScalar( value, type(), scbuf, blockSize0/mcn );
for( size_t i = 0; i < it.nplanes; i++, ++it ) for( size_t i = 0; i < it.nplanes; i++, ++it )
{ {

@ -323,8 +323,11 @@ struct Sum_SIMD<short, int>
#endif #endif
template<typename T, typename ST> template<typename T, typename ST>
static int sum_(const T* src0, const uchar* mask, ST* dst, int len, int cn ) static void sum_(const T* src0, const uchar* mask, ST* dst, int* nzm, int len, int cn, int mcn )
{ {
for( int k = 0; k < mcn; k++ )
nzm[k] = 0;
const T* src = src0; const T* src = src0;
if( !mask ) if( !mask )
{ {
@ -383,10 +386,14 @@ static int sum_(const T* src0, const uchar* mask, ST* dst, int len, int cn )
dst[k+2] = s2; dst[k+2] = s2;
dst[k+3] = s3; dst[k+3] = s3;
} }
return len;
if (nzm)
nzm[0] = len;
return;
} }
int i, nzm = 0; CV_Assert(mcn >= 1 && nzm);
int i;
if( cn == 1 ) if( cn == 1 )
{ {
ST s = dst[0]; ST s = dst[0];
@ -394,26 +401,53 @@ static int sum_(const T* src0, const uchar* mask, ST* dst, int len, int cn )
if( mask[i] ) if( mask[i] )
{ {
s += src[i]; s += src[i];
nzm++; nzm[0]++;
} }
dst[0] = s; dst[0] = s;
} }
else if( cn == 3 ) else if( cn == 3 )
{ {
ST s0 = dst[0], s1 = dst[1], s2 = dst[2]; ST s0 = dst[0], s1 = dst[1], s2 = dst[2];
if( mcn == 1 )
{
for( i = 0; i < len; i++, src += 3 ) for( i = 0; i < len; i++, src += 3 )
if( mask[i] ) if( mask[i] )
{ {
s0 += src[0]; s0 += src[0];
s1 += src[1]; s1 += src[1];
s2 += src[2]; s2 += src[2];
nzm++; nzm[0]++;
}
}
else
{
CV_Assert(mcn == cn);
for( i = 0; i < len; i++, src += 3, mask += 3 )
{
if( mask[0] )
{
s0 += src[0];
nzm[0]++;
}
if( mask[1] )
{
s1 += src[1];
nzm[1]++;
}
if( mask[2] )
{
s2 += src[2];
nzm[2]++;
}
}
} }
dst[0] = s0; dst[0] = s0;
dst[1] = s1; dst[1] = s1;
dst[2] = s2; dst[2] = s2;
} }
else else
{
if( mcn == 1 )
{ {
for( i = 0; i < len; i++, src += cn ) for( i = 0; i < len; i++, src += cn )
if( mask[i] ) if( mask[i] )
@ -433,35 +467,46 @@ static int sum_(const T* src0, const uchar* mask, ST* dst, int len, int cn )
#endif #endif
for( ; k < cn; k++ ) for( ; k < cn; k++ )
dst[k] += src[k]; dst[k] += src[k];
nzm++; nzm[0]++;
}
}
else
{
CV_Assert(mcn == cn);
for( i = 0; i < len; i++, src += cn, mask += cn )
for( int k = 0; k < cn; k++ )
if( mask[k] )
{
dst[k] += src[k];
nzm[k]++;
}
} }
} }
return nzm;
} }
static int sum8u( const uchar* src, const uchar* mask, int* dst, int len, int cn ) static void sum8u( const uchar* src, const uchar* mask, int* dst, int* nzm, int len, int cn, int mcn )
{ return sum_(src, mask, dst, len, cn); } { sum_(src, mask, dst, nzm, len, cn, mcn); }
static int sum8s( const schar* src, const uchar* mask, int* dst, int len, int cn ) static void sum8s( const schar* src, const uchar* mask, int* dst, int* nzm, int len, int cn, int mcn )
{ return sum_(src, mask, dst, len, cn); } { sum_(src, mask, dst, nzm, len, cn, mcn); }
static int sum16u( const ushort* src, const uchar* mask, int* dst, int len, int cn ) static void sum16u( const ushort* src, const uchar* mask, int* dst, int* nzm, int len, int cn, int mcn )
{ return sum_(src, mask, dst, len, cn); } { sum_(src, mask, dst, nzm, len, cn, mcn); }
static int sum16s( const short* src, const uchar* mask, int* dst, int len, int cn ) static void sum16s( const short* src, const uchar* mask, int* dst, int* nzm, int len, int cn, int mcn )
{ return sum_(src, mask, dst, len, cn); } { sum_(src, mask, dst, nzm, len, cn, mcn); }
static int sum32s( const int* src, const uchar* mask, double* dst, int len, int cn ) static void sum32s( const int* src, const uchar* mask, double* dst, int* nzm, int len, int cn, int mcn )
{ return sum_(src, mask, dst, len, cn); } { sum_(src, mask, dst, nzm, len, cn, mcn); }
static int sum32f( const float* src, const uchar* mask, double* dst, int len, int cn ) static void sum32f( const float* src, const uchar* mask, double* dst, int* nzm, int len, int cn, int mcn )
{ return sum_(src, mask, dst, len, cn); } { sum_(src, mask, dst, nzm, len, cn, mcn); }
static int sum64f( const double* src, const uchar* mask, double* dst, int len, int cn ) static void sum64f( const double* src, const uchar* mask, double* dst, int* nzm, int len, int cn, int mcn )
{ return sum_(src, mask, dst, len, cn); } { sum_(src, mask, dst, nzm, len, cn, mcn); }
typedef int (*SumFunc)(const uchar*, const uchar* mask, uchar*, int, int); typedef void (*SumFunc)(const uchar*, const uchar* mask, uchar*, int*, int, int, int);
static SumFunc getSumFunc(int depth) static SumFunc getSumFunc(int depth)
{ {
@ -850,10 +895,12 @@ struct SumSqr_SIMD<schar, int, int>
#endif #endif
template<typename T, typename ST, typename SQT> template<typename T, typename ST, typename SQT>
static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int len, int cn ) static void sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int* nzm, int len, int cn, int mcn )
{ {
const T* src = src0; for( int k = 0; k < mcn; k++ )
nzm[k] = 0;
const T* src = src0;
if( !mask ) if( !mask )
{ {
SumSqr_SIMD<T, ST, SQT> vop; SumSqr_SIMD<T, ST, SQT> vop;
@ -920,11 +967,14 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
sqsum[k] = sq0; sqsum[k+1] = sq1; sqsum[k] = sq0; sqsum[k+1] = sq1;
sqsum[k+2] = sq2; sqsum[k+3] = sq3; sqsum[k+2] = sq2; sqsum[k+3] = sq3;
} }
return len;
}
int i, nzm = 0; if (nzm)
nzm[0] = len;
return;
}
CV_Assert(mcn >= 1 && nzm);
int i;
if( cn == 1 ) if( cn == 1 )
{ {
ST s0 = sum[0]; ST s0 = sum[0];
@ -934,7 +984,7 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
{ {
T v = src[i]; T v = src[i];
s0 += v; sq0 += (SQT)v*v; s0 += v; sq0 += (SQT)v*v;
nzm++; nzm[0]++;
} }
sum[0] = s0; sum[0] = s0;
sqsum[0] = sq0; sqsum[0] = sq0;
@ -943,6 +993,8 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
{ {
ST s0 = sum[0], s1 = sum[1], s2 = sum[2]; ST s0 = sum[0], s1 = sum[1], s2 = sum[2];
SQT sq0 = sqsum[0], sq1 = sqsum[1], sq2 = sqsum[2]; SQT sq0 = sqsum[0], sq1 = sqsum[1], sq2 = sqsum[2];
if( mcn == 1 )
{
for( i = 0; i < len; i++, src += 3 ) for( i = 0; i < len; i++, src += 3 )
if( mask[i] ) if( mask[i] )
{ {
@ -950,12 +1002,40 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
s0 += v0; sq0 += (SQT)v0*v0; s0 += v0; sq0 += (SQT)v0*v0;
s1 += v1; sq1 += (SQT)v1*v1; s1 += v1; sq1 += (SQT)v1*v1;
s2 += v2; sq2 += (SQT)v2*v2; s2 += v2; sq2 += (SQT)v2*v2;
nzm++; nzm[0]++;
}
}
else
{
CV_Assert(mcn == cn);
for( i = 0; i < len; i++, src += 3, mask += 3 )
{
if( mask[0] )
{
T v0 = src[0];
s0 += v0; sq0 += (SQT)v0*v0;
nzm[0]++;
}
if( mask[1] )
{
T v1 = src[1];
s1 += v1; sq1 += (SQT)v1*v1;
nzm[1]++;
}
if( mask[2] )
{
T v2 = src[2];
s2 += v2; sq2 += (SQT)v2*v2;
nzm[2]++;
}
}
} }
sum[0] = s0; sum[1] = s1; sum[2] = s2; sum[0] = s0; sum[1] = s1; sum[2] = s2;
sqsum[0] = sq0; sqsum[1] = sq1; sqsum[2] = sq2; sqsum[0] = sq0; sqsum[1] = sq1; sqsum[2] = sq2;
} }
else else
{
if( mcn == 1 )
{ {
for( i = 0; i < len; i++, src += cn ) for( i = 0; i < len; i++, src += cn )
if( mask[i] ) if( mask[i] )
@ -967,42 +1047,59 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
SQT sq = sqsum[k] + (SQT)v*v; SQT sq = sqsum[k] + (SQT)v*v;
sum[k] = s; sqsum[k] = sq; sum[k] = s; sqsum[k] = sq;
} }
nzm++; nzm[0]++;
}
}
else
{
CV_Assert(mcn == cn);
for( i = 0; i < len; i++, src += cn, mask += cn )
for( int k = 0; k < cn; k++ )
if( mask[k] )
{
T v = src[k];
ST s = sum[k] + v;
SQT sq = sqsum[k] + (SQT)v*v;
sum[k] = s; sqsum[k] = sq;
nzm[k]++;
}
} }
} }
return nzm;
} }
static int sqsum8u( const uchar* src, const uchar* mask, int* sum, int* sqsum, int len, int cn ) static void sqsum8u( const uchar* src, const uchar* mask, int* sum, int* sqsum, int* nzm, int len, int cn, int mcn )
{ return sumsqr_(src, mask, sum, sqsum, len, cn); } { sumsqr_(src, mask, sum, sqsum, nzm, len, cn, mcn); }
static int sqsum8s( const schar* src, const uchar* mask, int* sum, int* sqsum, int len, int cn ) static void sqsum8s( const schar* src, const uchar* mask, int* sum, int* sqsum, int* nzm, int len, int cn, int mcn )
{ return sumsqr_(src, mask, sum, sqsum, len, cn); } { sumsqr_(src, mask, sum, sqsum, nzm, len, cn, mcn); }
static int sqsum16u( const ushort* src, const uchar* mask, int* sum, double* sqsum, int len, int cn ) static void sqsum16u( const ushort* src, const uchar* mask, int* sum, double* sqsum, int* nzm, int len, int cn, int mcn )
{ return sumsqr_(src, mask, sum, sqsum, len, cn); } { sumsqr_(src, mask, sum, sqsum, nzm, len, cn, mcn); }
static int sqsum16s( const short* src, const uchar* mask, int* sum, double* sqsum, int len, int cn ) static void sqsum16s( const short* src, const uchar* mask, int* sum, double* sqsum, int* nzm, int len, int cn, int mcn )
{ return sumsqr_(src, mask, sum, sqsum, len, cn); } { sumsqr_(src, mask, sum, sqsum, nzm, len, cn, mcn); }
static int sqsum32s( const int* src, const uchar* mask, double* sum, double* sqsum, int len, int cn ) static void sqsum32s( const int* src, const uchar* mask, double* sum, double* sqsum, int* nzm, int len, int cn, int mcn )
{ return sumsqr_(src, mask, sum, sqsum, len, cn); } { sumsqr_(src, mask, sum, sqsum, nzm, len, cn, mcn); }
static int sqsum32f( const float* src, const uchar* mask, double* sum, double* sqsum, int len, int cn ) static void sqsum32f( const float* src, const uchar* mask, double* sum, double* sqsum, int* nzm, int len, int cn, int mcn )
{ return sumsqr_(src, mask, sum, sqsum, len, cn); } { sumsqr_(src, mask, sum, sqsum, nzm, len, cn, mcn); }
static int sqsum64f( const double* src, const uchar* mask, double* sum, double* sqsum, int len, int cn ) static void sqsum64f( const double* src, const uchar* mask, double* sum, double* sqsum, int* nzm, int len, int cn, int mcn )
{ return sumsqr_(src, mask, sum, sqsum, len, cn); } { sumsqr_(src, mask, sum, sqsum, nzm, len, cn, mcn); }
typedef int (*SumSqrFunc)(const uchar*, const uchar* mask, uchar*, uchar*, int, int); typedef int (*SumSqrFunc)(const uchar*, const uchar* mask, uchar*, uchar*, int*, int, int, int);
static SumSqrFunc getSumSqrTab(int depth) static SumSqrFunc getSumSqrTab(int depth)
{ {
static SumSqrFunc sumSqrTab[] = static SumSqrFunc sumSqrTab[] =
{ {
(SumSqrFunc)GET_OPTIMIZED(sqsum8u), (SumSqrFunc)sqsum8s, (SumSqrFunc)sqsum16u, (SumSqrFunc)sqsum16s, (SumSqrFunc)GET_OPTIMIZED(sqsum8u), (SumSqrFunc)sqsum8s,
(SumSqrFunc)sqsum32s, (SumSqrFunc)GET_OPTIMIZED(sqsum32f), (SumSqrFunc)sqsum64f, 0 (SumSqrFunc)sqsum16u, (SumSqrFunc)sqsum16s,
(SumSqrFunc)sqsum32s,
(SumSqrFunc)GET_OPTIMIZED(sqsum32f), (SumSqrFunc)sqsum64f,
0
}; };
return sumSqrTab[depth]; return sumSqrTab[depth];
@ -1226,7 +1323,7 @@ cv::Scalar cv::sum( InputArray _src )
for( j = 0; j < total; j += blockSize ) for( j = 0; j < total; j += blockSize )
{ {
int bsz = std::min(total - j, blockSize); int bsz = std::min(total - j, blockSize);
func( ptrs[0], 0, (uchar*)buf, bsz, cn ); func( ptrs[0], 0, (uchar*)buf, 0, bsz, cn, 0 );
count += bsz; count += bsz;
if( blockSum && (count + blockSize >= intSumBlockSize || (i+1 >= it.nplanes && j+bsz >= total)) ) if( blockSum && (count + blockSize >= intSumBlockSize || (i+1 >= it.nplanes && j+bsz >= total)) )
{ {
@ -1390,6 +1487,8 @@ namespace cv
static bool ipp_mean( Mat &src, Mat &mask, Scalar &ret ) static bool ipp_mean( Mat &src, Mat &mask, Scalar &ret )
{ {
CV_INSTRUMENT_REGION_IPP() CV_INSTRUMENT_REGION_IPP()
if( mask.channels() > 1 )
return false;
#if IPP_VERSION_X100 >= 700 #if IPP_VERSION_X100 >= 700
size_t total_size = src.total(); size_t total_size = src.total();
@ -1485,11 +1584,10 @@ cv::Scalar cv::mean( InputArray _src, InputArray _mask )
CV_INSTRUMENT_REGION() CV_INSTRUMENT_REGION()
Mat src = _src.getMat(), mask = _mask.getMat(); Mat src = _src.getMat(), mask = _mask.getMat();
CV_Assert( mask.empty() || mask.type() == CV_8U ); int k, cn = src.channels(), depth = src.depth(), mcn = mask.channels();
CV_Assert( mask.empty() || (mask.depth() == CV_8U && (mcn == 1 || mcn == cn)) );
int k, cn = src.channels(), depth = src.depth();
Scalar s; Scalar s;
CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_mean(src, mask, s), s) CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_mean(src, mask, s), s)
SumFunc func = getSumFunc(depth); SumFunc func = getSumFunc(depth);
@ -1500,11 +1598,22 @@ cv::Scalar cv::mean( InputArray _src, InputArray _mask )
uchar* ptrs[2]; uchar* ptrs[2];
NAryMatIterator it(arrays, ptrs); NAryMatIterator it(arrays, ptrs);
int total = (int)it.size, blockSize = total, intSumBlockSize = 0; int total = (int)it.size, blockSize = total, intSumBlockSize = 0;
int j, count = 0; int j;
AutoBuffer<int> _buf; AutoBuffer<int, 4> _count(mcn), _nz(mcn);
int* count = _count;
int* nz = _nz;
AutoBuffer<int, 4> _buf;
int* buf = (int*)&s[0]; int* buf = (int*)&s[0];
bool blockSum = depth <= CV_16S; bool blockSum = depth <= CV_16S;
size_t esz = 0, nz0 = 0; size_t esz = 0, mesz = 0;
AutoBuffer<size_t, 4> _nz0(mcn);
size_t* nz0 = _nz0;
for( k = 0; k < mcn; k++ )
{
count[k] = 0;
nz0[k] = 0;
}
if( blockSum ) if( blockSum )
{ {
@ -1516,6 +1625,7 @@ cv::Scalar cv::mean( InputArray _src, InputArray _mask )
for( k = 0; k < cn; k++ ) for( k = 0; k < cn; k++ )
buf[k] = 0; buf[k] = 0;
esz = src.elemSize(); esz = src.elemSize();
mesz = mask.elemSize();
} }
for( size_t i = 0; i < it.nplanes; i++, ++it ) for( size_t i = 0; i < it.nplanes; i++, ++it )
@ -1523,24 +1633,38 @@ cv::Scalar cv::mean( InputArray _src, InputArray _mask )
for( j = 0; j < total; j += blockSize ) for( j = 0; j < total; j += blockSize )
{ {
int bsz = std::min(total - j, blockSize); int bsz = std::min(total - j, blockSize);
int nz = func( ptrs[0], ptrs[1], (uchar*)buf, bsz, cn ); func( ptrs[0], ptrs[1], (uchar*)buf, nz, bsz, cn, mcn );
count += nz;
nz0 += nz; bool doCommit = false;
if( blockSum && (count + blockSize >= intSumBlockSize || (i+1 >= it.nplanes && j+bsz >= total)) ) for( k = 0; k < mcn; k++ )
{
nz0[k] += nz[k];
count[k] += nz[k];
if( count[k] + blockSize >= intSumBlockSize )
doCommit = true;
}
if( blockSum && (doCommit || (i+1 >= it.nplanes && j+bsz >= total)) )
{ {
for( k = 0; k < cn; k++ ) for( k = 0; k < cn; k++ )
{ {
s[k] += buf[k]; s[k] += buf[k];
buf[k] = 0; buf[k] = 0;
} }
count = 0; for( k = 0; k < mcn; k++ )
count[k] = 0;
} }
ptrs[0] += bsz*esz; ptrs[0] += bsz*esz;
if( ptrs[1] ) if( ptrs[1] )
ptrs[1] += bsz; ptrs[1] += bsz*mesz;
} }
} }
return s*(nz0 ? 1./nz0 : 0);
if( mcn == cn )
for( k = 0; k < cn; k++ )
s[k] *= nz0[k] ? 1./nz0[k] : 0;
else
s *= nz0[0] ? 1./nz0[0] : 0;
return s;
} }
#ifdef HAVE_OPENCL #ifdef HAVE_OPENCL
@ -1557,6 +1681,8 @@ static bool ocl_meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv
const int cn = _src.channels(); const int cn = _src.channels();
if (cn > 4) if (cn > 4)
return false; return false;
if (_mask.channels() > 1)
return false;
{ {
int type = _src.type(), depth = CV_MAT_DEPTH(type); int type = _src.type(), depth = CV_MAT_DEPTH(type);
@ -1743,6 +1869,9 @@ static bool ipp_meanStdDev(Mat& src, OutputArray _mean, OutputArray _sdv, Mat& m
{ {
CV_INSTRUMENT_REGION_IPP() CV_INSTRUMENT_REGION_IPP()
if( mask.channels() > 1 )
return false;
#if IPP_VERSION_X100 >= 700 #if IPP_VERSION_X100 >= 700
int cn = src.channels(); int cn = src.channels();
@ -1866,14 +1995,14 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input
ocl_meanStdDev(_src, _mean, _sdv, _mask)) ocl_meanStdDev(_src, _mean, _sdv, _mask))
Mat src = _src.getMat(), mask = _mask.getMat(); Mat src = _src.getMat(), mask = _mask.getMat();
CV_Assert( mask.empty() || mask.type() == CV_8UC1 ); int k, cn = src.channels(), depth = src.depth(), mcn = mask.channels();
CV_Assert( mask.empty() || (mask.depth() == CV_8U && (mcn == 1 || mcn == cn)) );
CV_OVX_RUN(!ovx::skipSmallImages<VX_KERNEL_MEAN_STDDEV>(src.cols, src.rows), CV_OVX_RUN(!ovx::skipSmallImages<VX_KERNEL_MEAN_STDDEV>(src.cols, src.rows),
openvx_meanStdDev(src, _mean, _sdv, mask)) openvx_meanStdDev(src, _mean, _sdv, mask))
CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_meanStdDev(src, _mean, _sdv, mask)); CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_meanStdDev(src, _mean, _sdv, mask));
int k, cn = src.channels(), depth = src.depth();
SumSqrFunc func = getSumSqrTab(depth); SumSqrFunc func = getSumSqrTab(depth);
@ -1883,12 +2012,23 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input
uchar* ptrs[2]; uchar* ptrs[2];
NAryMatIterator it(arrays, ptrs); NAryMatIterator it(arrays, ptrs);
int total = (int)it.size, blockSize = total, intSumBlockSize = 0; int total = (int)it.size, blockSize = total, intSumBlockSize = 0;
int j, count = 0, nz0 = 0; int j;
AutoBuffer<int> _count(mcn), _nz(mcn);
int* count = _count;
int* nz = _nz;
AutoBuffer<double> _buf(cn*4); AutoBuffer<double> _buf(cn*4);
double *s = (double*)_buf, *sq = s + cn; double *s = (double*)_buf, *sq = s + cn;
int *sbuf = (int*)s, *sqbuf = (int*)sq; int *sbuf = (int*)s, *sqbuf = (int*)sq;
bool blockSum = depth <= CV_16S, blockSqSum = depth <= CV_8S; bool blockSum = depth <= CV_16S, blockSqSum = depth <= CV_8S;
size_t esz = 0; size_t esz = 0, mesz = 0;
AutoBuffer<size_t> _nz0(mcn);
size_t* nz0 = _nz0;
for( k = 0; k < mcn; k++ )
{
count[k] = 0;
nz0[k] = 0;
}
for( k = 0; k < cn; k++ ) for( k = 0; k < cn; k++ )
s[k] = sq[k] = 0; s[k] = sq[k] = 0;
@ -1903,6 +2043,7 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input
for( k = 0; k < cn; k++ ) for( k = 0; k < cn; k++ )
sbuf[k] = sqbuf[k] = 0; sbuf[k] = sqbuf[k] = 0;
esz = src.elemSize(); esz = src.elemSize();
mesz = mask.elemSize();
} }
for( size_t i = 0; i < it.nplanes; i++, ++it ) for( size_t i = 0; i < it.nplanes; i++, ++it )
@ -1910,10 +2051,17 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input
for( j = 0; j < total; j += blockSize ) for( j = 0; j < total; j += blockSize )
{ {
int bsz = std::min(total - j, blockSize); int bsz = std::min(total - j, blockSize);
int nz = func( ptrs[0], ptrs[1], (uchar*)sbuf, (uchar*)sqbuf, bsz, cn ); func( ptrs[0], ptrs[1], (uchar*)sbuf, (uchar*)sqbuf, nz, bsz, cn, mcn );
count += nz;
nz0 += nz; bool doCommit = false;
if( blockSum && (count + blockSize >= intSumBlockSize || (i+1 >= it.nplanes && j+bsz >= total)) ) for( k = 0; k < mcn; k++ )
{
nz0[k] += nz[k];
count[k] += nz[k];
if( count[k] + blockSize >= intSumBlockSize )
doCommit = true;
}
if( blockSum && (doCommit || (i+1 >= it.nplanes && j+bsz >= total)) )
{ {
for( k = 0; k < cn; k++ ) for( k = 0; k < cn; k++ )
{ {
@ -1928,20 +2076,30 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input
sqbuf[k] = 0; sqbuf[k] = 0;
} }
} }
count = 0; for( k = 0; k < mcn; k++ )
count[k] = 0;
} }
ptrs[0] += bsz*esz; ptrs[0] += bsz*esz;
if( ptrs[1] ) if( ptrs[1] )
ptrs[1] += bsz; ptrs[1] += bsz*mesz;
} }
} }
double scale = nz0 ? 1./nz0 : 0.; if( mcn == cn )
for( k = 0; k < cn; k++ ) {
double scale = nz0[k] ? 1./nz0[k] : 0;
s[k] *= scale;
sq[k] = std::sqrt(std::max(sq[k]*scale - s[k]*s[k], 0.));
}
else
{
double scale = nz0[0] ? 1./nz0[0] : 0.;
for( k = 0; k < cn; k++ ) for( k = 0; k < cn; k++ )
{ {
s[k] *= scale; s[k] *= scale;
sq[k] = std::sqrt(std::max(sq[k]*scale - s[k]*s[k], 0.)); sq[k] = std::sqrt(std::max(sq[k]*scale - s[k]*s[k], 0.));
} }
}
for( j = 0; j < 2; j++ ) for( j = 0; j < 2; j++ )
{ {

@ -1,4 +1,4 @@
#include "test_precomp.hpp" #include "test_precomp.hpp"
#include <cmath> #include <cmath>
using namespace cv; using namespace cv;
@ -15,7 +15,7 @@ const int ARITHM_MAX_SIZE_LOG = 10;
struct BaseElemWiseOp struct BaseElemWiseOp
{ {
enum { FIX_ALPHA=1, FIX_BETA=2, FIX_GAMMA=4, REAL_GAMMA=8, SUPPORT_MASK=16, SCALAR_OUTPUT=32 }; enum { FIX_ALPHA=1, FIX_BETA=2, FIX_GAMMA=4, REAL_GAMMA=8, SUPPORT_MASK=16, SCALAR_OUTPUT=32, SUPPORT_MULTICHANNELMASK=64 };
BaseElemWiseOp(int _ninputs, int _flags, double _alpha, double _beta, BaseElemWiseOp(int _ninputs, int _flags, double _alpha, double _beta,
Scalar _gamma=Scalar::all(0), int _context=1) Scalar _gamma=Scalar::all(0), int _context=1)
: ninputs(_ninputs), flags(_flags), alpha(_alpha), beta(_beta), gamma(_gamma), context(_context) {} : ninputs(_ninputs), flags(_flags), alpha(_alpha), beta(_beta), gamma(_gamma), context(_context) {}
@ -467,7 +467,7 @@ struct CmpSOp : public BaseElemWiseOp
struct CopyOp : public BaseElemWiseOp struct CopyOp : public BaseElemWiseOp
{ {
CopyOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) { } CopyOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SUPPORT_MULTICHANNELMASK, 1, 1, Scalar::all(0)) { }
void op(const vector<Mat>& src, Mat& dst, const Mat& mask) void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
{ {
src[0].copyTo(dst, mask); src[0].copyTo(dst, mask);
@ -489,7 +489,7 @@ struct CopyOp : public BaseElemWiseOp
struct SetOp : public BaseElemWiseOp struct SetOp : public BaseElemWiseOp
{ {
SetOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {} SetOp() : BaseElemWiseOp(0, FIX_ALPHA+FIX_BETA+SUPPORT_MASK+SUPPORT_MULTICHANNELMASK, 1, 1, Scalar::all(0)) {}
void op(const vector<Mat>&, Mat& dst, const Mat& mask) void op(const vector<Mat>&, Mat& dst, const Mat& mask)
{ {
dst.setTo(gamma, mask); dst.setTo(gamma, mask);
@ -1162,7 +1162,7 @@ struct CartToPolarToCartOp : public BaseElemWiseOp
struct MeanOp : public BaseElemWiseOp struct MeanOp : public BaseElemWiseOp
{ {
MeanOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0)) MeanOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SUPPORT_MULTICHANNELMASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0))
{ {
context = 3; context = 3;
}; };
@ -1244,7 +1244,7 @@ struct MeanStdDevOp : public BaseElemWiseOp
Scalar sqmeanRef; Scalar sqmeanRef;
int cn; int cn;
MeanStdDevOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0)) MeanStdDevOp() : BaseElemWiseOp(1, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK+SUPPORT_MULTICHANNELMASK+SCALAR_OUTPUT, 1, 1, Scalar::all(0))
{ {
cn = 0; cn = 0;
context = 7; context = 7;
@ -1394,7 +1394,8 @@ TEST_P(ElemWiseTest, accuracy)
op->getRandomSize(rng, size); op->getRandomSize(rng, size);
int type = op->getRandomType(rng); int type = op->getRandomType(rng);
int depth = CV_MAT_DEPTH(type); int depth = CV_MAT_DEPTH(type);
bool haveMask = (op->flags & cvtest::BaseElemWiseOp::SUPPORT_MASK) != 0 && rng.uniform(0, 4) == 0; bool haveMask = ((op->flags & cvtest::BaseElemWiseOp::SUPPORT_MASK) != 0
|| (op->flags & cvtest::BaseElemWiseOp::SUPPORT_MULTICHANNELMASK) != 0) && rng.uniform(0, 4) == 0;
double minval=0, maxval=0; double minval=0, maxval=0;
op->getValueRange(depth, minval, maxval); op->getValueRange(depth, minval, maxval);
@ -1403,8 +1404,12 @@ TEST_P(ElemWiseTest, accuracy)
for( i = 0; i < ninputs; i++ ) for( i = 0; i < ninputs; i++ )
src[i] = cvtest::randomMat(rng, size, type, minval, maxval, true); src[i] = cvtest::randomMat(rng, size, type, minval, maxval, true);
Mat dst0, dst, mask; Mat dst0, dst, mask;
if( haveMask ) if( haveMask ) {
mask = cvtest::randomMat(rng, size, CV_8U, 0, 2, true); bool multiChannelMask = (op->flags & cvtest::BaseElemWiseOp::SUPPORT_MULTICHANNELMASK) != 0
&& rng.uniform(0, 2) == 0;
int masktype = CV_8UC(multiChannelMask ? CV_MAT_CN(type) : 1);
mask = cvtest::randomMat(rng, size, masktype, 0, 2, true);
}
if( (haveMask || ninputs == 0) && !(op->flags & cvtest::BaseElemWiseOp::SCALAR_OUTPUT)) if( (haveMask || ninputs == 0) && !(op->flags & cvtest::BaseElemWiseOp::SCALAR_OUTPUT))
{ {

@ -353,27 +353,39 @@ void copy(const Mat& src, Mat& dst, const Mat& mask, bool invertMask)
return; return;
} }
CV_Assert( src.size == mask.size && mask.type() == CV_8U ); int mcn = mask.channels();
CV_Assert( src.size == mask.size && mask.depth() == CV_8U
&& (mcn == 1 || mcn == src.channels()) );
const Mat *arrays[]={&src, &dst, &mask, 0}; const Mat *arrays[]={&src, &dst, &mask, 0};
Mat planes[3]; Mat planes[3];
NAryMatIterator it(arrays, planes); NAryMatIterator it(arrays, planes);
size_t j, k, elemSize = src.elemSize(), total = planes[0].total(); size_t j, k, elemSize = src.elemSize(), maskElemSize = mask.elemSize(), total = planes[0].total();
size_t i, nplanes = it.nplanes; size_t i, nplanes = it.nplanes;
size_t elemSize1 = src.elemSize1();
for( i = 0; i < nplanes; i++, ++it) for( i = 0; i < nplanes; i++, ++it)
{ {
const uchar* sptr = planes[0].ptr(); const uchar* sptr = planes[0].ptr();
uchar* dptr = planes[1].ptr(); uchar* dptr = planes[1].ptr();
const uchar* mptr = planes[2].ptr(); const uchar* mptr = planes[2].ptr();
for( j = 0; j < total; j++, sptr += elemSize, dptr += elemSize, mptr += maskElemSize )
for( j = 0; j < total; j++, sptr += elemSize, dptr += elemSize ) {
if( mcn == 1)
{ {
if( (mptr[j] != 0) ^ invertMask ) if( (mptr[0] != 0) ^ invertMask )
for( k = 0; k < elemSize; k++ ) for( k = 0; k < elemSize; k++ )
dptr[k] = sptr[k]; dptr[k] = sptr[k];
} }
else
{
for( int c = 0; c < mcn; c++ )
if( (mptr[c] != 0) ^ invertMask )
for( k = 0; k < elemSize1; k++ )
dptr[k + c * elemSize1] = sptr[k + c * elemSize1];
}
}
} }
} }
@ -414,26 +426,38 @@ void set(Mat& dst, const Scalar& gamma, const Mat& mask)
return; return;
} }
CV_Assert( dst.size == mask.size && mask.type() == CV_8U ); int cn = dst.channels(), mcn = mask.channels();
CV_Assert( dst.size == mask.size && (mcn == 1 || mcn == cn) );
const Mat *arrays[]={&dst, &mask, 0}; const Mat *arrays[]={&dst, &mask, 0};
Mat planes[2]; Mat planes[2];
NAryMatIterator it(arrays, planes); NAryMatIterator it(arrays, planes);
size_t j, k, elemSize = dst.elemSize(), total = planes[0].total(); size_t j, k, elemSize = dst.elemSize(), maskElemSize = mask.elemSize(), total = planes[0].total();
size_t i, nplanes = it.nplanes; size_t i, nplanes = it.nplanes;
size_t elemSize1 = dst.elemSize1();
for( i = 0; i < nplanes; i++, ++it) for( i = 0; i < nplanes; i++, ++it)
{ {
uchar* dptr = planes[0].ptr(); uchar* dptr = planes[0].ptr();
const uchar* mptr = planes[1].ptr(); const uchar* mptr = planes[1].ptr();
for( j = 0; j < total; j++, dptr += elemSize ) for( j = 0; j < total; j++, dptr += elemSize, mptr += maskElemSize )
{
if( mcn == 1)
{ {
if( mptr[j] ) if( mptr[0] )
for( k = 0; k < elemSize; k++ ) for( k = 0; k < elemSize; k++ )
dptr[k] = gptr[k]; dptr[k] = gptr[k];
} }
else
{
for( int c = 0; c < mcn; c++ )
if( mptr[c] )
for( k = 0; k < elemSize1; k++ )
dptr[k + c * elemSize1] = gptr[k + c * elemSize1];
}
}
} }
} }
@ -2574,11 +2598,11 @@ void divide(const Mat& src1, const Mat& src2, Mat& dst, double scale)
template<typename _Tp> static void template<typename _Tp> static void
mean_(const _Tp* src, const uchar* mask, size_t total, int cn, Scalar& sum, int& nz) mean_(const _Tp* src, const uchar* mask, size_t total, int cn, int mcn, Scalar& sum, Scalar_<int>& nz)
{ {
if( !mask ) if( !mask )
{ {
nz += (int)total; nz += Scalar_<int>::all((int)total);
total *= cn; total *= cn;
for( size_t i = 0; i < total; i += cn ) for( size_t i = 0; i < total; i += cn )
{ {
@ -2586,23 +2610,41 @@ mean_(const _Tp* src, const uchar* mask, size_t total, int cn, Scalar& sum, int&
sum[c] += src[i + c]; sum[c] += src[i + c];
} }
} }
else else if( mcn == 1 )
{ {
for( size_t i = 0; i < total; i++ ) for( size_t i = 0; i < total; i++ )
if( mask[i] ) if( mask[i] )
{ {
nz++;
for( int c = 0; c < cn; c++ ) for( int c = 0; c < cn; c++ )
{
nz[c]++;
sum[c] += src[i*cn + c]; sum[c] += src[i*cn + c];
} }
} }
}
else
{
total *= cn;
for( size_t i = 0; i < total; i += cn )
{
for( int c = 0; c < cn; c++ )
{
if( mask[i + c] )
{
nz[c]++;
sum[c] += src[i + c];
}
}
}
}
} }
Scalar mean(const Mat& src, const Mat& mask) Scalar mean(const Mat& src, const Mat& mask)
{ {
CV_Assert(mask.empty() || (mask.type() == CV_8U && mask.size == src.size)); CV_Assert(mask.empty() || (mask.depth() == CV_8U && mask.size == src.size &&
(mask.channels() == 1 || mask.channels() == src.channels())));
Scalar sum; Scalar sum;
int nz = 0; Scalar_<int> nz = Scalar_<int>::all(0);
const Mat *arrays[]={&src, &mask, 0}; const Mat *arrays[]={&src, &mask, 0};
Mat planes[2]; Mat planes[2];
@ -2610,7 +2652,7 @@ Scalar mean(const Mat& src, const Mat& mask)
NAryMatIterator it(arrays, planes); NAryMatIterator it(arrays, planes);
size_t total = planes[0].total(); size_t total = planes[0].total();
size_t i, nplanes = it.nplanes; size_t i, nplanes = it.nplanes;
int depth = src.depth(), cn = src.channels(); int c, depth = src.depth(), cn = src.channels(), mcn = mask.channels();
for( i = 0; i < nplanes; i++, ++it ) for( i = 0; i < nplanes; i++, ++it )
{ {
@ -2620,32 +2662,34 @@ Scalar mean(const Mat& src, const Mat& mask)
switch( depth ) switch( depth )
{ {
case CV_8U: case CV_8U:
mean_((const uchar*)sptr, mptr, total, cn, sum, nz); mean_((const uchar*)sptr, mptr, total, cn, mcn, sum, nz);
break; break;
case CV_8S: case CV_8S:
mean_((const schar*)sptr, mptr, total, cn, sum, nz); mean_((const schar*)sptr, mptr, total, cn, mcn, sum, nz);
break; break;
case CV_16U: case CV_16U:
mean_((const ushort*)sptr, mptr, total, cn, sum, nz); mean_((const ushort*)sptr, mptr, total, cn, mcn, sum, nz);
break; break;
case CV_16S: case CV_16S:
mean_((const short*)sptr, mptr, total, cn, sum, nz); mean_((const short*)sptr, mptr, total, cn, mcn, sum, nz);
break; break;
case CV_32S: case CV_32S:
mean_((const int*)sptr, mptr, total, cn, sum, nz); mean_((const int*)sptr, mptr, total, cn, mcn, sum, nz);
break; break;
case CV_32F: case CV_32F:
mean_((const float*)sptr, mptr, total, cn, sum, nz); mean_((const float*)sptr, mptr, total, cn, mcn, sum, nz);
break; break;
case CV_64F: case CV_64F:
mean_((const double*)sptr, mptr, total, cn, sum, nz); mean_((const double*)sptr, mptr, total, cn, mcn, sum, nz);
break; break;
default: default:
CV_Error(Error::StsUnsupportedFormat, ""); CV_Error(Error::StsUnsupportedFormat, "");
} }
} }
return sum * (1./std::max(nz, 1)); for( c = 0; c < cn; c++ )
sum[c] *= (1./std::max(nz[c], 1));
return sum;
} }

Loading…
Cancel
Save