/*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. // // // Intel License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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 "opencl_kernels_imgproc.hpp" #include "opencv2/core/hal/intrin.hpp" #include "opencv2/core/utils/tls.hpp" namespace cv { ////////////////// Helper functions ////////////////////// #define CV_CLAMP_INT(v, vmin, vmax) (v < vmin ? vmin : (vmax < v ? vmax : v)) static const size_t OUT_OF_RANGE = (size_t)1 << (sizeof(size_t)*8 - 2); static void calcHistLookupTables_8u( const Mat& hist, const SparseMat& shist, int dims, const float** ranges, const double* uniranges, bool uniform, bool issparse, std::vector& _tab ) { const int low = 0, high = 256; int i, j; _tab.resize((high-low)*dims); size_t* tab = &_tab[0]; if( uniform ) { for( i = 0; i < dims; i++ ) { double a = uniranges[i*2]; double b = uniranges[i*2+1]; int sz = !issparse ? hist.size[i] : shist.size(i); size_t step = !issparse ? hist.step[i] : 1; double v_lo = ranges ? ranges[i][0] : 0; double v_hi = ranges ? ranges[i][1] : 256; for( j = low; j < high; j++ ) { int idx = cvFloor(j*a + b); size_t written_idx = OUT_OF_RANGE; if (j >= v_lo && j < v_hi) { idx = CV_CLAMP_INT(idx, 0, sz - 1); written_idx = idx*step; } tab[i*(high - low) + j - low] = written_idx; } } } else if (ranges) { for( i = 0; i < dims; i++ ) { int limit = std::min(cvCeil(ranges[i][0]), high); int idx = -1, sz = !issparse ? hist.size[i] : shist.size(i); size_t written_idx = OUT_OF_RANGE; size_t step = !issparse ? hist.step[i] : 1; for(j = low;;) { for( ; j < limit; j++ ) tab[i*(high - low) + j - low] = written_idx; if( (unsigned)(++idx) < (unsigned)sz ) { limit = std::min(cvCeil(ranges[i][idx+1]), high); written_idx = idx*step; } else { for( ; j < high; j++ ) tab[i*(high - low) + j - low] = OUT_OF_RANGE; break; } } } } else { CV_Error(Error::StsBadArg, "Either ranges, either uniform ranges should be provided"); } } static void histPrepareImages( const Mat* images, int nimages, const int* channels, const Mat& mask, int dims, const int* histSize, const float** ranges, bool uniform, std::vector& ptrs, std::vector& deltas, Size& imsize, std::vector& uniranges ) { int i, j, c; CV_Assert( channels != 0 || nimages == dims ); imsize = images[0].size(); int depth = images[0].depth(), esz1 = (int)images[0].elemSize1(); bool isContinuous = true; ptrs.resize(dims + 1); deltas.resize((dims + 1)*2); for( i = 0; i < dims; i++ ) { if(!channels) { j = i; c = 0; CV_Assert( images[j].channels() == 1 ); } else { c = channels[i]; CV_Assert( c >= 0 ); for( j = 0; j < nimages; c -= images[j].channels(), j++ ) if( c < images[j].channels() ) break; CV_Assert( j < nimages ); } CV_Assert( images[j].size() == imsize && images[j].depth() == depth ); if( !images[j].isContinuous() ) isContinuous = false; ptrs[i] = images[j].data + c*esz1; deltas[i*2] = images[j].channels(); deltas[i*2+1] = (int)(images[j].step/esz1 - imsize.width*deltas[i*2]); } if( !mask.empty() ) { CV_Assert( mask.size() == imsize && mask.channels() == 1 ); isContinuous = isContinuous && mask.isContinuous(); ptrs[dims] = mask.data; deltas[dims*2] = 1; deltas[dims*2 + 1] = (int)(mask.step/mask.elemSize1()); } if( isContinuous ) { imsize.width *= imsize.height; imsize.height = 1; } if( !ranges ) // implicit uniform ranges for 8U { CV_Assert( depth == CV_8U ); uniranges.resize( dims*2 ); for( i = 0; i < dims; i++ ) { uniranges[i*2] = histSize[i]/256.; uniranges[i*2+1] = 0; } } else if( uniform ) { uniranges.resize( dims*2 ); for( i = 0; i < dims; i++ ) { CV_Assert( ranges[i] && ranges[i][0] < ranges[i][1] ); double low = ranges[i][0], high = ranges[i][1]; double t = histSize[i]/(high - low); uniranges[i*2] = t; uniranges[i*2+1] = -t*low; #if 0 // This should be true by math, but it is not accurate numerically CV_Assert(cvFloor(low * uniranges[i*2] + uniranges[i*2+1]) == 0); CV_Assert((high * uniranges[i*2] + uniranges[i*2+1]) < histSize[i]); #endif } } else { for( i = 0; i < dims; i++ ) { size_t n = histSize[i]; for(size_t k = 0; k < n; k++ ) CV_Assert( ranges[i][k] < ranges[i][k+1] ); } } } ////////////////////////////////// C A L C U L A T E H I S T O G R A M //////////////////////////////////// template static void calcHist_( std::vector& _ptrs, const std::vector& _deltas, Size imsize, Mat& hist, int dims, const float** _ranges, const double* _uniranges, bool uniform ) { T** ptrs = (T**)&_ptrs[0]; const int* deltas = &_deltas[0]; uchar* H = hist.ptr(); int i, x; const uchar* mask = _ptrs[dims]; int mstep = _deltas[dims*2 + 1]; int size[CV_MAX_DIM]; size_t hstep[CV_MAX_DIM]; for( i = 0; i < dims; i++ ) { size[i] = hist.size[i]; hstep[i] = hist.step[i]; } if( uniform ) { const double* uniranges = &_uniranges[0]; if( dims == 1 ) { double a = uniranges[0], b = uniranges[1]; int sz = size[0], d0 = deltas[0], step0 = deltas[1]; const T* p0 = (const T*)ptrs[0]; double v0_lo = _ranges[0][0]; double v0_hi = _ranges[0][1]; for( ; imsize.height--; p0 += step0, mask += mstep ) { if( !mask ) for( x = 0; x < imsize.width; x++, p0 += d0 ) { double v0 = (double)*p0; int idx = cvFloor(v0*a + b); if (v0 < v0_lo || v0 >= v0_hi) continue; idx = CV_CLAMP_INT(idx, 0, sz - 1); CV_DbgAssert((unsigned)idx < (unsigned)sz); ((int*)H)[idx]++; } else for( x = 0; x < imsize.width; x++, p0 += d0 ) if( mask[x] ) { double v0 = (double)*p0; int idx = cvFloor(v0*a + b); if (v0 < v0_lo || v0 >= v0_hi) continue; idx = CV_CLAMP_INT(idx, 0, sz - 1); CV_DbgAssert((unsigned)idx < (unsigned)sz); ((int*)H)[idx]++; } } return; } else if( dims == 2 ) { double a0 = uniranges[0], b0 = uniranges[1], a1 = uniranges[2], b1 = uniranges[3]; int sz0 = size[0], sz1 = size[1]; int d0 = deltas[0], step0 = deltas[1], d1 = deltas[2], step1 = deltas[3]; size_t hstep0 = hstep[0]; const T* p0 = (const T*)ptrs[0]; const T* p1 = (const T*)ptrs[1]; double v0_lo = _ranges[0][0]; double v0_hi = _ranges[0][1]; double v1_lo = _ranges[1][0]; double v1_hi = _ranges[1][1]; for( ; imsize.height--; p0 += step0, p1 += step1, mask += mstep ) { if( !mask ) for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 ) { double v0 = (double)*p0; double v1 = (double)*p1; int idx0 = cvFloor(v0*a0 + b0); int idx1 = cvFloor(v1*a1 + b1); if (v0 < v0_lo || v0 >= v0_hi) continue; if (v1 < v1_lo || v1 >= v1_hi) continue; idx0 = CV_CLAMP_INT(idx0, 0, sz0 - 1); idx1 = CV_CLAMP_INT(idx1, 0, sz1 - 1); CV_DbgAssert((unsigned)idx0 < (unsigned)sz0 && (unsigned)idx1 < (unsigned)sz1); ((int*)(H + hstep0*idx0))[idx1]++; } else for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 ) if( mask[x] ) { double v0 = (double)*p0; double v1 = (double)*p1; int idx0 = cvFloor(v0*a0 + b0); int idx1 = cvFloor(v1*a1 + b1); if (v0 < v0_lo || v0 >= v0_hi) continue; if (v1 < v1_lo || v1 >= v1_hi) continue; idx0 = CV_CLAMP_INT(idx0, 0, sz0 - 1); idx1 = CV_CLAMP_INT(idx1, 0, sz1 - 1); CV_DbgAssert((unsigned)idx0 < (unsigned)sz0 && (unsigned)idx1 < (unsigned)sz1); ((int*)(H + hstep0*idx0))[idx1]++; } } return; } else if( dims == 3 ) { double a0 = uniranges[0], b0 = uniranges[1], a1 = uniranges[2], b1 = uniranges[3], a2 = uniranges[4], b2 = uniranges[5]; int sz0 = size[0], sz1 = size[1], sz2 = size[2]; int d0 = deltas[0], step0 = deltas[1], d1 = deltas[2], step1 = deltas[3], d2 = deltas[4], step2 = deltas[5]; size_t hstep0 = hstep[0], hstep1 = hstep[1]; const T* p0 = (const T*)ptrs[0]; const T* p1 = (const T*)ptrs[1]; const T* p2 = (const T*)ptrs[2]; double v0_lo = _ranges[0][0]; double v0_hi = _ranges[0][1]; double v1_lo = _ranges[1][0]; double v1_hi = _ranges[1][1]; double v2_lo = _ranges[2][0]; double v2_hi = _ranges[2][1]; for( ; imsize.height--; p0 += step0, p1 += step1, p2 += step2, mask += mstep ) { if( !mask ) for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 ) { double v0 = (double)*p0; double v1 = (double)*p1; double v2 = (double)*p2; int idx0 = cvFloor(v0*a0 + b0); int idx1 = cvFloor(v1*a1 + b1); int idx2 = cvFloor(v2*a2 + b2); if (v0 < v0_lo || v0 >= v0_hi) continue; if (v1 < v1_lo || v1 >= v1_hi) continue; if (v2 < v2_lo || v2 >= v2_hi) continue; idx0 = CV_CLAMP_INT(idx0, 0, sz0 - 1); idx1 = CV_CLAMP_INT(idx1, 0, sz1 - 1); idx2 = CV_CLAMP_INT(idx2, 0, sz2 - 1); CV_DbgAssert( (unsigned)idx0 < (unsigned)sz0 && (unsigned)idx1 < (unsigned)sz1 && (unsigned)idx2 < (unsigned)sz2); ((int*)(H + hstep0*idx0 + hstep1*idx1))[idx2]++; } else for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 ) if( mask[x] ) { double v0 = (double)*p0; double v1 = (double)*p1; double v2 = (double)*p2; int idx0 = cvFloor(v0*a0 + b0); int idx1 = cvFloor(v1*a1 + b1); int idx2 = cvFloor(v2*a2 + b2); if (v0 < v0_lo || v0 >= v0_hi) continue; if (v1 < v1_lo || v1 >= v1_hi) continue; if (v2 < v2_lo || v2 >= v2_hi) continue; idx0 = CV_CLAMP_INT(idx0, 0, sz0 - 1); idx1 = CV_CLAMP_INT(idx1, 0, sz1 - 1); idx2 = CV_CLAMP_INT(idx2, 0, sz2 - 1); CV_DbgAssert( (unsigned)idx0 < (unsigned)sz0 && (unsigned)idx1 < (unsigned)sz1 && (unsigned)idx2 < (unsigned)sz2); ((int*)(H + hstep0*idx0 + hstep1*idx1))[idx2]++; } } } else { for( ; imsize.height--; mask += mstep ) { if( !mask ) for( x = 0; x < imsize.width; x++ ) { uchar* Hptr = H; for( i = 0; i < dims; i++ ) { double v_lo = _ranges[i][0]; double v_hi = _ranges[i][1]; double v = *ptrs[i]; if (v < v_lo || v >= v_hi) break; int idx = cvFloor(v*uniranges[i*2] + uniranges[i*2+1]); idx = CV_CLAMP_INT(idx, 0, size[i] - 1); CV_DbgAssert((unsigned)idx < (unsigned)size[i]); ptrs[i] += deltas[i*2]; Hptr += idx*hstep[i]; } if( i == dims ) ++*((int*)Hptr); else for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } else for( x = 0; x < imsize.width; x++ ) { uchar* Hptr = H; i = 0; if( mask[x] ) for( ; i < dims; i++ ) { double v_lo = _ranges[i][0]; double v_hi = _ranges[i][1]; double v = *ptrs[i]; if (v < v_lo || v >= v_hi) break; int idx = cvFloor(v*uniranges[i*2] + uniranges[i*2+1]); idx = CV_CLAMP_INT(idx, 0, size[i] - 1); CV_DbgAssert((unsigned)idx < (unsigned)size[i]); ptrs[i] += deltas[i*2]; Hptr += idx*hstep[i]; } if( i == dims ) ++*((int*)Hptr); else for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } } else if (_ranges) { // non-uniform histogram const float* ranges[CV_MAX_DIM]; for( i = 0; i < dims; i++ ) ranges[i] = &_ranges[i][0]; for( ; imsize.height--; mask += mstep ) { for( x = 0; x < imsize.width; x++ ) { uchar* Hptr = H; i = 0; if( !mask || mask[x] ) for( ; i < dims; i++ ) { float v = (float)*ptrs[i]; const float* R = ranges[i]; int idx = -1, sz = size[i]; while( v >= R[idx+1] && ++idx < sz ) ; // nop if( (unsigned)idx >= (unsigned)sz ) break; ptrs[i] += deltas[i*2]; Hptr += idx*hstep[i]; } if( i == dims ) ++*((int*)Hptr); else for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } else { CV_Error(Error::StsBadArg, "Either ranges, either uniform ranges should be provided"); } } static void calcHist_8u( std::vector& _ptrs, const std::vector& _deltas, Size imsize, Mat& hist, int dims, const float** _ranges, const double* _uniranges, bool uniform ) { uchar** ptrs = &_ptrs[0]; const int* deltas = &_deltas[0]; uchar* H = hist.ptr(); int x; const uchar* mask = _ptrs[dims]; int mstep = _deltas[dims*2 + 1]; std::vector _tab; calcHistLookupTables_8u( hist, SparseMat(), dims, _ranges, _uniranges, uniform, false, _tab ); const size_t* tab = &_tab[0]; if( dims == 1 ) { int d0 = deltas[0], step0 = deltas[1]; int matH[256] = { 0, }; const uchar* p0 = (const uchar*)ptrs[0]; for( ; imsize.height--; p0 += step0, mask += mstep ) { if( !mask ) { if( d0 == 1 ) { for( x = 0; x <= imsize.width - 4; x += 4 ) { int t0 = p0[x], t1 = p0[x+1]; matH[t0]++; matH[t1]++; t0 = p0[x+2]; t1 = p0[x+3]; matH[t0]++; matH[t1]++; } p0 += x; } else for( x = 0; x <= imsize.width - 4; x += 4 ) { int t0 = p0[0], t1 = p0[d0]; matH[t0]++; matH[t1]++; p0 += d0*2; t0 = p0[0]; t1 = p0[d0]; matH[t0]++; matH[t1]++; p0 += d0*2; } for( ; x < imsize.width; x++, p0 += d0 ) matH[*p0]++; } else for( x = 0; x < imsize.width; x++, p0 += d0 ) if( mask[x] ) matH[*p0]++; } for(int i = 0; i < 256; i++ ) { size_t hidx = tab[i]; if( hidx < OUT_OF_RANGE ) *(int*)(H + hidx) += matH[i]; } } else if( dims == 2 ) { int d0 = deltas[0], step0 = deltas[1], d1 = deltas[2], step1 = deltas[3]; const uchar* p0 = (const uchar*)ptrs[0]; const uchar* p1 = (const uchar*)ptrs[1]; for( ; imsize.height--; p0 += step0, p1 += step1, mask += mstep ) { if( !mask ) for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 ) { size_t idx = tab[*p0] + tab[*p1 + 256]; if( idx < OUT_OF_RANGE ) ++*(int*)(H + idx); } else for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 ) { size_t idx; if( mask[x] && (idx = tab[*p0] + tab[*p1 + 256]) < OUT_OF_RANGE ) ++*(int*)(H + idx); } } } else if( dims == 3 ) { int d0 = deltas[0], step0 = deltas[1], d1 = deltas[2], step1 = deltas[3], d2 = deltas[4], step2 = deltas[5]; const uchar* p0 = (const uchar*)ptrs[0]; const uchar* p1 = (const uchar*)ptrs[1]; const uchar* p2 = (const uchar*)ptrs[2]; for( ; imsize.height--; p0 += step0, p1 += step1, p2 += step2, mask += mstep ) { if( !mask ) for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 ) { size_t idx = tab[*p0] + tab[*p1 + 256] + tab[*p2 + 512]; if( idx < OUT_OF_RANGE ) ++*(int*)(H + idx); } else for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 ) { size_t idx; if( mask[x] && (idx = tab[*p0] + tab[*p1 + 256] + tab[*p2 + 512]) < OUT_OF_RANGE ) ++*(int*)(H + idx); } } } else { for( ; imsize.height--; mask += mstep ) { if( !mask ) for( x = 0; x < imsize.width; x++ ) { uchar* Hptr = H; int i = 0; for( ; i < dims; i++ ) { size_t idx = tab[*ptrs[i] + i*256]; if( idx >= OUT_OF_RANGE ) break; Hptr += idx; ptrs[i] += deltas[i*2]; } if( i == dims ) ++*((int*)Hptr); else for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } else for( x = 0; x < imsize.width; x++ ) { uchar* Hptr = H; int i = 0; if( mask[x] ) for( ; i < dims; i++ ) { size_t idx = tab[*ptrs[i] + i*256]; if( idx >= OUT_OF_RANGE ) break; Hptr += idx; ptrs[i] += deltas[i*2]; } if( i == dims ) ++*((int*)Hptr); else for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } for(int i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } } #ifdef HAVE_IPP typedef IppStatus(CV_STDCALL * IppiHistogram_C1)(const void* pSrc, int srcStep, IppiSize roiSize, Ipp32u* pHist, const IppiHistogramSpec* pSpec, Ipp8u* pBuffer); static IppiHistogram_C1 getIppiHistogramFunction_C1(int type) { IppiHistogram_C1 ippFunction = (type == CV_8UC1) ? (IppiHistogram_C1)ippiHistogram_8u_C1R : (type == CV_16UC1) ? (IppiHistogram_C1)ippiHistogram_16u_C1R : (type == CV_32FC1) ? (IppiHistogram_C1)ippiHistogram_32f_C1R : NULL; return ippFunction; } class ipp_calcHistParallelTLS { public: ipp_calcHistParallelTLS() {} IppAutoBuffer spec; IppAutoBuffer buffer; IppAutoBuffer thist; }; class ipp_calcHistParallel: public ParallelLoopBody { public: ipp_calcHistParallel(const Mat &src, Mat &hist, Ipp32s histSize, const float *ranges, bool uniform, bool &ok): ParallelLoopBody(), m_src(src), m_hist(hist), m_ok(ok) { ok = true; m_uniform = uniform; m_ranges = ranges; m_histSize = histSize; m_type = ippiGetDataType(src.type()); m_levelsNum = histSize+1; ippiHistogram_C1 = getIppiHistogramFunction_C1(src.type()); m_fullRoi = ippiSize(src.size()); m_bufferSize = 0; m_specSize = 0; if(!ippiHistogram_C1) { ok = false; return; } if(ippiHistogramGetBufferSize(m_type, m_fullRoi, &m_levelsNum, 1, 1, &m_specSize, &m_bufferSize) < 0) { ok = false; return; } hist.setTo(0); } virtual void operator() (const Range & range) const CV_OVERRIDE { CV_INSTRUMENT_REGION_IPP(); if(!m_ok) return; ipp_calcHistParallelTLS *pTls = m_tls.get(); IppiSize roi = {m_src.cols, range.end - range.start }; bool mtLoop = false; if(m_fullRoi.height != roi.height) mtLoop = true; if(!pTls->spec) { pTls->spec.allocate(m_specSize); if(!pTls->spec.get()) { m_ok = false; return; } pTls->buffer.allocate(m_bufferSize); if(!pTls->buffer.get() && m_bufferSize) { m_ok = false; return; } if(m_uniform) { if(ippiHistogramUniformInit(m_type, (Ipp32f*)&m_ranges[0], (Ipp32f*)&m_ranges[1], (Ipp32s*)&m_levelsNum, 1, pTls->spec) < 0) { m_ok = false; return; } } else { if(ippiHistogramInit(m_type, (const Ipp32f**)&m_ranges, (Ipp32s*)&m_levelsNum, 1, pTls->spec) < 0) { m_ok = false; return; } } pTls->thist.allocate(m_histSize*sizeof(Ipp32u)); } if(CV_INSTRUMENT_FUN_IPP(ippiHistogram_C1, m_src.ptr(range.start), (int)m_src.step, roi, pTls->thist, pTls->spec, pTls->buffer) < 0) { m_ok = false; return; } if(mtLoop) { for(int i = 0; i < m_histSize; i++) CV_XADD((int*)(m_hist.ptr(i)), *(int*)((Ipp32u*)pTls->thist + i)); } else ippiCopy_32s_C1R((Ipp32s*)pTls->thist.get(), sizeof(Ipp32u), (Ipp32s*)m_hist.ptr(), (int)m_hist.step, ippiSize(1, m_histSize)); } private: const Mat &m_src; Mat &m_hist; Ipp32s m_histSize; const float *m_ranges; bool m_uniform; IppiHistogram_C1 ippiHistogram_C1; IppiSize m_fullRoi; IppDataType m_type; Ipp32s m_levelsNum; int m_bufferSize; int m_specSize; mutable Mutex m_syncMutex; TLSData m_tls; volatile bool &m_ok; const ipp_calcHistParallel & operator = (const ipp_calcHistParallel & ); }; #endif } #ifdef HAVE_IPP #define IPP_HISTOGRAM_PARALLEL 1 namespace cv { static bool ipp_calchist(const Mat &image, Mat &hist, int histSize, const float** ranges, bool uniform, bool accumulate) { CV_INSTRUMENT_REGION_IPP(); #if IPP_VERSION_X100 < 201801 // No SSE42 optimization for uniform 32f if(uniform && image.depth() == CV_32F && cv::ipp::getIppTopFeatures() == ippCPUID_SSE42) return false; #endif // IPP_DISABLE_HISTOGRAM - https://github.com/opencv/opencv/issues/11544 // and https://github.com/opencv/opencv/issues/21595 if ((uniform && (ranges[0][1] - ranges[0][0]) != histSize) || abs(ranges[0][0]) != cvFloor(ranges[0][0])) return false; Mat ihist = hist; if(accumulate) ihist.create(1, &histSize, CV_32S); bool ok = true; int threads = ippiSuggestThreadsNum(image, (1+((double)ihist.total()/image.total()))*2); Range range(0, image.rows); ipp_calcHistParallel invoker(image, ihist, histSize, ranges[0], uniform, ok); if(!ok) return false; if(IPP_HISTOGRAM_PARALLEL && threads > 1) parallel_for_(range, invoker, threads*2); else invoker(range); if(ok) { if(accumulate) { IppiSize histRoi = ippiSize(1, histSize); IppAutoBuffer fhist(histSize*sizeof(Ipp32f)); CV_INSTRUMENT_FUN_IPP(ippiConvert_32s32f_C1R, (Ipp32s*)ihist.ptr(), (int)ihist.step, (Ipp32f*)fhist, sizeof(Ipp32f), histRoi); CV_INSTRUMENT_FUN_IPP(ippiAdd_32f_C1IR, (Ipp32f*)fhist, sizeof(Ipp32f), (Ipp32f*)hist.ptr(), (int)hist.step, histRoi); } else CV_INSTRUMENT_FUN_IPP(ippiConvert_32s32f_C1R, (Ipp32s*)ihist.ptr(), (int)ihist.step, (Ipp32f*)hist.ptr(), (int)hist.step, ippiSize(1, histSize)); } return ok; } } #endif void cv::calcHist( const Mat* images, int nimages, const int* channels, InputArray _mask, OutputArray _hist, int dims, const int* histSize, const float** ranges, bool uniform, bool accumulate ) { CV_INSTRUMENT_REGION(); CV_Assert(images && nimages > 0); Mat mask = _mask.getMat(); CV_Assert(dims > 0 && histSize); const uchar* const histdata = _hist.getMat().ptr(); _hist.create(dims, histSize, CV_32F); Mat hist = _hist.getMat(); if(histdata != hist.data) accumulate = false; CV_IPP_RUN( nimages == 1 && dims == 1 && channels && channels[0] == 0 && _mask.empty() && images[0].dims <= 2 && ranges && ranges[0], ipp_calchist(images[0], hist, histSize[0], ranges, uniform, accumulate)); if (nimages == 1 && dims == 1 && channels && channels[0] == 0 && _mask.empty() && images[0].dims <= 2 && ranges && ranges[0]) { CALL_HAL(calcHist, cv_hal_calcHist, images[0].data, images[0].step, images[0].type(), images[0].cols, images[0].rows, hist.ptr(), histSize[0], ranges, uniform, accumulate); } Mat ihist = hist; ihist.flags = (ihist.flags & ~CV_MAT_TYPE_MASK)|CV_32S; if(!accumulate) hist = Scalar(0.); else hist.convertTo(ihist, CV_32S); std::vector ptrs; std::vector deltas; std::vector uniranges; Size imsize; CV_Assert( mask.empty() || mask.type() == CV_8UC1 || mask.type() == CV_BoolC1); histPrepareImages( images, nimages, channels, mask, dims, hist.size.p, ranges, uniform, ptrs, deltas, imsize, uniranges ); const double* _uniranges = uniform ? &uniranges[0] : 0; int depth = images[0].depth(); if( depth == CV_8U ) calcHist_8u(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform ); else if( depth == CV_16U ) calcHist_(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform ); else if( depth == CV_32F ) calcHist_(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform ); else CV_Error(cv::Error::StsUnsupportedFormat, ""); ihist.convertTo(hist, CV_32F); } namespace cv { template static void calcSparseHist_( std::vector& _ptrs, const std::vector& _deltas, Size imsize, SparseMat& hist, int dims, const float** _ranges, const double* _uniranges, bool uniform ) { T** ptrs = (T**)&_ptrs[0]; const int* deltas = &_deltas[0]; int i, x; const uchar* mask = _ptrs[dims]; int mstep = _deltas[dims*2 + 1]; const int* size = hist.hdr->size; int idx[CV_MAX_DIM]; if( uniform ) { const double* uniranges = &_uniranges[0]; for( ; imsize.height--; mask += mstep ) { for( x = 0; x < imsize.width; x++ ) { i = 0; if( !mask || mask[x] ) for( ; i < dims; i++ ) { idx[i] = cvFloor(*ptrs[i]*uniranges[i*2] + uniranges[i*2+1]); if( (unsigned)idx[i] >= (unsigned)size[i] ) break; ptrs[i] += deltas[i*2]; } if( i == dims ) ++*(int*)hist.ptr(idx, true); else for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } else if (_ranges) { // non-uniform histogram const float* ranges[CV_MAX_DIM]; for( i = 0; i < dims; i++ ) ranges[i] = &_ranges[i][0]; for( ; imsize.height--; mask += mstep ) { for( x = 0; x < imsize.width; x++ ) { i = 0; if( !mask || mask[x] ) for( ; i < dims; i++ ) { float v = (float)*ptrs[i]; const float* R = ranges[i]; int j = -1, sz = size[i]; while( v >= R[j+1] && ++j < sz ) ; // nop if( (unsigned)j >= (unsigned)sz ) break; ptrs[i] += deltas[i*2]; idx[i] = j; } if( i == dims ) ++*(int*)hist.ptr(idx, true); else for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } else { CV_Error(Error::StsBadArg, "Either ranges, either uniform ranges should be provided"); } } static void calcSparseHist_8u( std::vector& _ptrs, const std::vector& _deltas, Size imsize, SparseMat& hist, int dims, const float** _ranges, const double* _uniranges, bool uniform ) { uchar** ptrs = (uchar**)&_ptrs[0]; const int* deltas = &_deltas[0]; int x; const uchar* mask = _ptrs[dims]; int mstep = _deltas[dims*2 + 1]; int idx[CV_MAX_DIM]; std::vector _tab; calcHistLookupTables_8u( Mat(), hist, dims, _ranges, _uniranges, uniform, true, _tab ); const size_t* tab = &_tab[0]; for( ; imsize.height--; mask += mstep ) { for( x = 0; x < imsize.width; x++ ) { int i = 0; if( !mask || mask[x] ) for( ; i < dims; i++ ) { size_t hidx = tab[*ptrs[i] + i*256]; if( hidx >= OUT_OF_RANGE ) break; ptrs[i] += deltas[i*2]; idx[i] = (int)hidx; } if( i == dims ) ++*(int*)hist.ptr(idx,true); else for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } for(int i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } static void calcHist( const Mat* images, int nimages, const int* channels, const Mat& mask, SparseMat& hist, int dims, const int* histSize, const float** ranges, bool uniform, bool accumulate, bool keepInt ) { size_t i, N; if( !accumulate ) hist.create(dims, histSize, CV_32F); else { SparseMatIterator it = hist.begin(); for( i = 0, N = hist.nzcount(); i < N; i++, ++it ) { CV_Assert(it.ptr != NULL); Cv32suf* val = (Cv32suf*)it.ptr; val->i = cvRound(val->f); } } std::vector ptrs; std::vector deltas; std::vector uniranges; Size imsize; CV_Assert( mask.empty() || mask.type() == CV_8UC1 || mask.type() == CV_BoolC1 ); histPrepareImages( images, nimages, channels, mask, dims, hist.hdr->size, ranges, uniform, ptrs, deltas, imsize, uniranges ); const double* _uniranges = uniform ? &uniranges[0] : 0; int depth = images[0].depth(); if( depth == CV_8U ) calcSparseHist_8u(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, uniform ); else if( depth == CV_16U ) calcSparseHist_(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, uniform ); else if( depth == CV_32F ) calcSparseHist_(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, uniform ); else CV_Error(cv::Error::StsUnsupportedFormat, ""); if( !keepInt ) { SparseMatIterator it = hist.begin(); for( i = 0, N = hist.nzcount(); i < N; i++, ++it ) { CV_Assert(it.ptr != NULL); Cv32suf* val = (Cv32suf*)it.ptr; val->f = (float)val->i; } } } #ifdef HAVE_OPENCL enum { BINS = 256 }; static bool ocl_calcHist1(InputArray _src, OutputArray _hist, int ddepth = CV_32S) { const ocl::Device & dev = ocl::Device::getDefault(); int compunits = dev.maxComputeUnits(); size_t wgs = dev.maxWorkGroupSize(); Size size = _src.size(); bool use16 = size.width % 16 == 0 && _src.offset() % 16 == 0 && _src.step() % 16 == 0; int kercn = dev.isAMD() && use16 ? 16 : std::min(4, ocl::predictOptimalVectorWidth(_src)); ocl::Kernel k1("calculate_histogram", ocl::imgproc::histogram_oclsrc, format("-D BINS=%d -D HISTS_COUNT=%d -D WGS=%zu -D kercn=%d -D T=%s%s", BINS, compunits, wgs, kercn, kercn == 4 ? "int" : ocl::typeToStr(CV_8UC(kercn)), _src.isContinuous() ? " -D HAVE_SRC_CONT" : "")); if (k1.empty()) return false; int hsz = BINS; _hist.create(1, &hsz, ddepth); UMat src = _src.getUMat(), ghist(1, BINS * compunits, CV_32SC1), hist = _hist.getUMat().reshape(1, hsz); k1.args(ocl::KernelArg::ReadOnly(src), ocl::KernelArg::PtrWriteOnly(ghist), (int)src.total()); size_t globalsize = compunits * wgs; if (!k1.run(1, &globalsize, &wgs, false)) return false; wgs = std::min(ocl::Device::getDefault().maxWorkGroupSize(), BINS); char cvt[50]; ocl::Kernel k2("merge_histogram", ocl::imgproc::histogram_oclsrc, format("-D BINS=%d -D HISTS_COUNT=%d -D WGS=%d -D convertToHT=%s -D HT=%s", BINS, compunits, (int)wgs, ocl::convertTypeStr(CV_32S, ddepth, 1, cvt, sizeof(cvt)), ocl::typeToStr(ddepth))); if (k2.empty()) return false; k2.args(ocl::KernelArg::PtrReadOnly(ghist), ocl::KernelArg::WriteOnlyNoSize(hist)); return k2.run(1, &wgs, &wgs, false); } static bool ocl_calcHist(InputArrayOfArrays images, OutputArray hist) { std::vector v; images.getUMatVector(v); return ocl_calcHist1(v[0], hist, CV_32F); } #endif } void cv::calcHist( const Mat* images, int nimages, const int* channels, InputArray _mask, SparseMat& hist, int dims, const int* histSize, const float** ranges, bool uniform, bool accumulate ) { CV_INSTRUMENT_REGION(); CV_Assert(images && nimages > 0); Mat mask = _mask.getMat(); calcHist( images, nimages, channels, mask, hist, dims, histSize, ranges, uniform, accumulate, false ); } void cv::calcHist( InputArrayOfArrays images, const std::vector& channels, InputArray mask, OutputArray hist, const std::vector& histSize, const std::vector& ranges, bool accumulate ) { CV_INSTRUMENT_REGION(); CV_OCL_RUN(images.total() == 1 && channels.size() == 1 && images.channels(0) == 1 && channels[0] == 0 && images.isUMatVector() && mask.empty() && !accumulate && histSize.size() == 1 && histSize[0] == BINS && ranges.size() == 2 && ranges[0] == 0 && ranges[1] == static_cast(BINS), ocl_calcHist(images, hist)) int i, dims = (int)histSize.size(), rsz = (int)ranges.size(), csz = (int)channels.size(); int nimages = (int)images.total(); CV_Assert(nimages > 0 && dims > 0); CV_Assert(rsz == dims*2 || (rsz == 0 && images.depth(0) == CV_8U)); CV_Assert(csz == 0 || csz == dims); float* _ranges[CV_MAX_DIM]; if( rsz > 0 ) { for( i = 0; i < rsz/2; i++ ) _ranges[i] = (float*)&ranges[i*2]; } AutoBuffer buf(nimages); for( i = 0; i < nimages; i++ ) buf[i] = images.getMat(i); calcHist(&buf[0], nimages, csz ? &channels[0] : 0, mask, hist, dims, &histSize[0], rsz ? (const float**)_ranges : 0, true, accumulate); } /////////////////////////////////////// B A C K P R O J E C T //////////////////////////////////// namespace cv { template static void calcBackProj_( std::vector& _ptrs, const std::vector& _deltas, Size imsize, const Mat& hist, int dims, const float** _ranges, const double* _uniranges, float scale, bool uniform ) { T** ptrs = (T**)&_ptrs[0]; const int* deltas = &_deltas[0]; const uchar* H = hist.ptr(); int i, x; BT* bproj = (BT*)_ptrs[dims]; int bpstep = _deltas[dims*2 + 1]; int size[CV_MAX_DIM]; size_t hstep[CV_MAX_DIM]; for( i = 0; i < dims; i++ ) { size[i] = hist.size[i]; hstep[i] = hist.step[i]; } if( uniform ) { const double* uniranges = &_uniranges[0]; if( dims == 1 ) { double a = uniranges[0], b = uniranges[1]; int sz = size[0], d0 = deltas[0], step0 = deltas[1]; const T* p0 = (const T*)ptrs[0]; for( ; imsize.height--; p0 += step0, bproj += bpstep ) { for( x = 0; x < imsize.width; x++, p0 += d0 ) { int idx = cvFloor(*p0*a + b); bproj[x] = (unsigned)idx < (unsigned)sz ? saturate_cast(((const float*)H)[idx]*scale) : 0; } } } else if( dims == 2 ) { double a0 = uniranges[0], b0 = uniranges[1], a1 = uniranges[2], b1 = uniranges[3]; int sz0 = size[0], sz1 = size[1]; int d0 = deltas[0], step0 = deltas[1], d1 = deltas[2], step1 = deltas[3]; size_t hstep0 = hstep[0]; const T* p0 = (const T*)ptrs[0]; const T* p1 = (const T*)ptrs[1]; for( ; imsize.height--; p0 += step0, p1 += step1, bproj += bpstep ) { for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 ) { int idx0 = cvFloor(*p0*a0 + b0); int idx1 = cvFloor(*p1*a1 + b1); bproj[x] = (unsigned)idx0 < (unsigned)sz0 && (unsigned)idx1 < (unsigned)sz1 ? saturate_cast(((const float*)(H + hstep0*idx0))[idx1]*scale) : 0; } } } else if( dims == 3 ) { double a0 = uniranges[0], b0 = uniranges[1], a1 = uniranges[2], b1 = uniranges[3], a2 = uniranges[4], b2 = uniranges[5]; int sz0 = size[0], sz1 = size[1], sz2 = size[2]; int d0 = deltas[0], step0 = deltas[1], d1 = deltas[2], step1 = deltas[3], d2 = deltas[4], step2 = deltas[5]; size_t hstep0 = hstep[0], hstep1 = hstep[1]; const T* p0 = (const T*)ptrs[0]; const T* p1 = (const T*)ptrs[1]; const T* p2 = (const T*)ptrs[2]; for( ; imsize.height--; p0 += step0, p1 += step1, p2 += step2, bproj += bpstep ) { for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 ) { int idx0 = cvFloor(*p0*a0 + b0); int idx1 = cvFloor(*p1*a1 + b1); int idx2 = cvFloor(*p2*a2 + b2); bproj[x] = (unsigned)idx0 < (unsigned)sz0 && (unsigned)idx1 < (unsigned)sz1 && (unsigned)idx2 < (unsigned)sz2 ? saturate_cast(((const float*)(H + hstep0*idx0 + hstep1*idx1))[idx2]*scale) : 0; } } } else { for( ; imsize.height--; bproj += bpstep ) { for( x = 0; x < imsize.width; x++ ) { const uchar* Hptr = H; for( i = 0; i < dims; i++ ) { int idx = cvFloor(*ptrs[i]*uniranges[i*2] + uniranges[i*2+1]); if( (unsigned)idx >= (unsigned)size[i] || (_ranges && *ptrs[i] >= _ranges[i][1])) break; ptrs[i] += deltas[i*2]; Hptr += idx*hstep[i]; } if( i == dims ) bproj[x] = saturate_cast(*(const float*)Hptr*scale); else { bproj[x] = 0; for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } } else if (_ranges) { // non-uniform histogram const float* ranges[CV_MAX_DIM]; for( i = 0; i < dims; i++ ) ranges[i] = &_ranges[i][0]; for( ; imsize.height--; bproj += bpstep ) { for( x = 0; x < imsize.width; x++ ) { const uchar* Hptr = H; for( i = 0; i < dims; i++ ) { float v = (float)*ptrs[i]; const float* R = ranges[i]; int idx = -1, sz = size[i]; while( v >= R[idx+1] && ++idx < sz ) ; // nop if( (unsigned)idx >= (unsigned)sz ) break; ptrs[i] += deltas[i*2]; Hptr += idx*hstep[i]; } if( i == dims ) bproj[x] = saturate_cast(*(const float*)Hptr*scale); else { bproj[x] = 0; for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } else { CV_Error(Error::StsBadArg, "Either ranges, either uniform ranges should be provided"); } } static void calcBackProj_8u( std::vector& _ptrs, const std::vector& _deltas, Size imsize, const Mat& hist, int dims, const float** _ranges, const double* _uniranges, float scale, bool uniform ) { uchar** ptrs = &_ptrs[0]; const int* deltas = &_deltas[0]; const uchar* H = hist.ptr(); int i, x; uchar* bproj = _ptrs[dims]; int bpstep = _deltas[dims*2 + 1]; std::vector _tab; calcHistLookupTables_8u( hist, SparseMat(), dims, _ranges, _uniranges, uniform, false, _tab ); const size_t* tab = &_tab[0]; if( dims == 1 ) { int d0 = deltas[0], step0 = deltas[1]; uchar matH[256] = {0}; const uchar* p0 = (const uchar*)ptrs[0]; for( i = 0; i < 256; i++ ) { size_t hidx = tab[i]; if( hidx < OUT_OF_RANGE ) matH[i] = saturate_cast(*(float*)(H + hidx)*scale); } for( ; imsize.height--; p0 += step0, bproj += bpstep ) { if( d0 == 1 ) { for( x = 0; x <= imsize.width - 4; x += 4 ) { uchar t0 = matH[p0[x]], t1 = matH[p0[x+1]]; bproj[x] = t0; bproj[x+1] = t1; t0 = matH[p0[x+2]]; t1 = matH[p0[x+3]]; bproj[x+2] = t0; bproj[x+3] = t1; } p0 += x; } else for( x = 0; x <= imsize.width - 4; x += 4 ) { uchar t0 = matH[p0[0]], t1 = matH[p0[d0]]; bproj[x] = t0; bproj[x+1] = t1; p0 += d0*2; t0 = matH[p0[0]]; t1 = matH[p0[d0]]; bproj[x+2] = t0; bproj[x+3] = t1; p0 += d0*2; } for( ; x < imsize.width; x++, p0 += d0 ) bproj[x] = matH[*p0]; } } else if( dims == 2 ) { int d0 = deltas[0], step0 = deltas[1], d1 = deltas[2], step1 = deltas[3]; const uchar* p0 = (const uchar*)ptrs[0]; const uchar* p1 = (const uchar*)ptrs[1]; for( ; imsize.height--; p0 += step0, p1 += step1, bproj += bpstep ) { for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 ) { size_t idx = tab[*p0] + tab[*p1 + 256]; bproj[x] = idx < OUT_OF_RANGE ? saturate_cast(*(const float*)(H + idx)*scale) : 0; } } } else if( dims == 3 ) { int d0 = deltas[0], step0 = deltas[1], d1 = deltas[2], step1 = deltas[3], d2 = deltas[4], step2 = deltas[5]; const uchar* p0 = (const uchar*)ptrs[0]; const uchar* p1 = (const uchar*)ptrs[1]; const uchar* p2 = (const uchar*)ptrs[2]; for( ; imsize.height--; p0 += step0, p1 += step1, p2 += step2, bproj += bpstep ) { for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 ) { size_t idx = tab[*p0] + tab[*p1 + 256] + tab[*p2 + 512]; bproj[x] = idx < OUT_OF_RANGE ? saturate_cast(*(const float*)(H + idx)*scale) : 0; } } } else { for( ; imsize.height--; bproj += bpstep ) { for( x = 0; x < imsize.width; x++ ) { const uchar* Hptr = H; for( i = 0; i < dims; i++ ) { size_t idx = tab[*ptrs[i] + i*256]; if( idx >= OUT_OF_RANGE ) break; ptrs[i] += deltas[i*2]; Hptr += idx; } if( i == dims ) bproj[x] = saturate_cast(*(const float*)Hptr*scale); else { bproj[x] = 0; for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } } } void cv::calcBackProject( const Mat* images, int nimages, const int* channels, InputArray _hist, OutputArray _backProject, const float** ranges, double scale, bool uniform ) { CV_INSTRUMENT_REGION(); CV_Assert(images && nimages > 0); Mat hist = _hist.getMat(); std::vector ptrs; std::vector deltas; std::vector uniranges; Size imsize; if (hist.dims == 2 && (hist.rows == 1 || hist.cols == 1)) { CV_Assert(hist.isContinuous()); std::vector hist_size = {hist.rows + hist.cols - 1}; hist = hist.reshape(1, hist_size); } int dims = hist.dims; CV_Assert( dims > 0 && !hist.empty() ); _backProject.create( images[0].size(), images[0].depth() ); Mat backProject = _backProject.getMat(); histPrepareImages( images, nimages, channels, backProject, dims, hist.size.p, ranges, uniform, ptrs, deltas, imsize, uniranges ); const double* _uniranges = uniform ? &uniranges[0] : 0; int depth = images[0].depth(); if( depth == CV_8U ) calcBackProj_8u(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, (float)scale, uniform); else if( depth == CV_16U ) calcBackProj_(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, (float)scale, uniform ); else if( depth == CV_32F ) calcBackProj_(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, (float)scale, uniform ); else CV_Error(cv::Error::StsUnsupportedFormat, ""); } namespace cv { template static void calcSparseBackProj_( std::vector& _ptrs, const std::vector& _deltas, Size imsize, const SparseMat& hist, int dims, const float** _ranges, const double* _uniranges, float scale, bool uniform ) { T** ptrs = (T**)&_ptrs[0]; const int* deltas = &_deltas[0]; int i, x; BT* bproj = (BT*)_ptrs[dims]; int bpstep = _deltas[dims*2 + 1]; const int* size = hist.hdr->size; int idx[CV_MAX_DIM]; const SparseMat_& hist_ = (const SparseMat_&)hist; if( uniform ) { const double* uniranges = &_uniranges[0]; for( ; imsize.height--; bproj += bpstep ) { for( x = 0; x < imsize.width; x++ ) { for( i = 0; i < dims; i++ ) { idx[i] = cvFloor(*ptrs[i]*uniranges[i*2] + uniranges[i*2+1]); if( (unsigned)idx[i] >= (unsigned)size[i] ) break; ptrs[i] += deltas[i*2]; } if( i == dims ) bproj[x] = saturate_cast(hist_(idx)*scale); else { bproj[x] = 0; for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } else if (_ranges) { // non-uniform histogram const float* ranges[CV_MAX_DIM]; for( i = 0; i < dims; i++ ) ranges[i] = &_ranges[i][0]; for( ; imsize.height--; bproj += bpstep ) { for( x = 0; x < imsize.width; x++ ) { for( i = 0; i < dims; i++ ) { float v = (float)*ptrs[i]; const float* R = ranges[i]; int j = -1, sz = size[i]; while( v >= R[j+1] && ++j < sz ) ; // nop if( (unsigned)j >= (unsigned)sz ) break; idx[i] = j; ptrs[i] += deltas[i*2]; } if( i == dims ) bproj[x] = saturate_cast(hist_(idx)*scale); else { bproj[x] = 0; for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } else { CV_Error(Error::StsBadArg, "Either ranges, either uniform ranges should be provided"); } } static void calcSparseBackProj_8u( std::vector& _ptrs, const std::vector& _deltas, Size imsize, const SparseMat& hist, int dims, const float** _ranges, const double* _uniranges, float scale, bool uniform ) { uchar** ptrs = &_ptrs[0]; const int* deltas = &_deltas[0]; int i, x; uchar* bproj = _ptrs[dims]; int bpstep = _deltas[dims*2 + 1]; std::vector _tab; int idx[CV_MAX_DIM]; calcHistLookupTables_8u( Mat(), hist, dims, _ranges, _uniranges, uniform, true, _tab ); const size_t* tab = &_tab[0]; for( ; imsize.height--; bproj += bpstep ) { for( x = 0; x < imsize.width; x++ ) { for( i = 0; i < dims; i++ ) { size_t hidx = tab[*ptrs[i] + i*256]; if( hidx >= OUT_OF_RANGE ) break; idx[i] = (int)hidx; ptrs[i] += deltas[i*2]; } if( i == dims ) bproj[x] = saturate_cast(hist.value(idx)*scale); else { bproj[x] = 0; for( ; i < dims; i++ ) ptrs[i] += deltas[i*2]; } } for( i = 0; i < dims; i++ ) ptrs[i] += deltas[i*2 + 1]; } } } void cv::calcBackProject( const Mat* images, int nimages, const int* channels, const SparseMat& hist, OutputArray _backProject, const float** ranges, double scale, bool uniform ) { CV_INSTRUMENT_REGION(); CV_Assert(images && nimages > 0); std::vector ptrs; std::vector deltas; std::vector uniranges; Size imsize; int dims = hist.dims(); CV_Assert( dims > 0 ); _backProject.create( images[0].size(), images[0].depth() ); Mat backProject = _backProject.getMat(); histPrepareImages( images, nimages, channels, backProject, dims, hist.hdr->size, ranges, uniform, ptrs, deltas, imsize, uniranges ); const double* _uniranges = uniform ? &uniranges[0] : 0; int depth = images[0].depth(); if( depth == CV_8U ) calcSparseBackProj_8u(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, (float)scale, uniform); else if( depth == CV_16U ) calcSparseBackProj_(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, (float)scale, uniform ); else if( depth == CV_32F ) calcSparseBackProj_(ptrs, deltas, imsize, hist, dims, ranges, _uniranges, (float)scale, uniform ); else CV_Error(cv::Error::StsUnsupportedFormat, ""); } #ifdef HAVE_OPENCL namespace cv { static void getUMatIndex(const std::vector & um, int cn, int & idx, int & cnidx) { int totalChannels = 0; for (size_t i = 0, size = um.size(); i < size; ++i) { int ccn = um[i].channels(); totalChannels += ccn; if (totalChannels == cn) { idx = (int)(i + 1); cnidx = 0; return; } else if (totalChannels > cn) { idx = (int)i; cnidx = i == 0 ? cn : (cn - totalChannels + ccn); return; } } idx = cnidx = -1; } static bool ocl_calcBackProject( InputArrayOfArrays _images, std::vector channels, InputArray _hist, OutputArray _dst, const std::vector& ranges, float scale, size_t histdims ) { std::vector images; _images.getUMatVector(images); size_t nimages = images.size(), totalcn = images[0].channels(); CV_Assert(nimages > 0); Size size = images[0].size(); int depth = images[0].depth(); //kernels are valid for this type only if (depth != CV_8U) return false; for (size_t i = 1; i < nimages; ++i) { const UMat & m = images[i]; totalcn += m.channels(); CV_Assert(size == m.size() && depth == m.depth()); } std::sort(channels.begin(), channels.end()); for (size_t i = 0; i < histdims; ++i) CV_Assert(channels[i] < (int)totalcn); if (histdims == 1) { int idx, cnidx; getUMatIndex(images, channels[0], idx, cnidx); CV_Assert(idx >= 0); UMat im = images[idx]; String opts = format("-D histdims=1 -D scn=%d", im.channels()); ocl::Kernel lutk("calcLUT", ocl::imgproc::calc_back_project_oclsrc, opts); if (lutk.empty()) return false; size_t lsize = 256; UMat lut(1, (int)lsize, CV_32SC1); UMat hist = _hist.getUMat(); UMat uranges; Mat(ranges, false).copyTo(uranges); hist = hist.reshape(1, hist.rows + hist.cols-1); lutk.args(ocl::KernelArg::ReadOnlyNoSize(hist), hist.rows, ocl::KernelArg::PtrWriteOnly(lut), scale, ocl::KernelArg::PtrReadOnly(uranges)); if (!lutk.run(1, &lsize, NULL, false)) return false; ocl::Kernel mapk("LUT", ocl::imgproc::calc_back_project_oclsrc, opts); if (mapk.empty()) return false; _dst.create(size, depth); UMat dst = _dst.getUMat(); im.offset += cnidx; mapk.args(ocl::KernelArg::ReadOnlyNoSize(im), ocl::KernelArg::PtrReadOnly(lut), ocl::KernelArg::WriteOnly(dst)); size_t globalsize[2] = { (size_t)size.width, (size_t)size.height }; return mapk.run(2, globalsize, NULL, false); } else if (histdims == 2) { int idx0, idx1, cnidx0, cnidx1; getUMatIndex(images, channels[0], idx0, cnidx0); getUMatIndex(images, channels[1], idx1, cnidx1); CV_Assert(idx0 >= 0 && idx1 >= 0); UMat im0 = images[idx0], im1 = images[idx1]; // Lut for the first dimension String opts = format("-D histdims=2 -D scn1=%d -D scn2=%d", im0.channels(), im1.channels()); ocl::Kernel lutk1("calcLUT", ocl::imgproc::calc_back_project_oclsrc, opts); if (lutk1.empty()) return false; size_t lsize = 256; UMat lut(1, (int)lsize<<1, CV_32SC1); UMat hist = _hist.getUMat(); UMat uranges; Mat(ranges, false).copyTo(uranges); lutk1.args(hist.rows, ocl::KernelArg::PtrWriteOnly(lut), (int)0, ocl::KernelArg::PtrReadOnly(uranges), (int)0); if (!lutk1.run(1, &lsize, NULL, false)) return false; // lut for the second dimension ocl::Kernel lutk2("calcLUT", ocl::imgproc::calc_back_project_oclsrc, opts); if (lutk2.empty()) return false; lut.offset += lsize * sizeof(int); lutk2.args(hist.cols, ocl::KernelArg::PtrWriteOnly(lut), (int)256, ocl::KernelArg::PtrReadOnly(uranges), (int)2); if (!lutk2.run(1, &lsize, NULL, false)) return false; // perform lut ocl::Kernel mapk("LUT", ocl::imgproc::calc_back_project_oclsrc, opts); if (mapk.empty()) return false; _dst.create(size, depth); UMat dst = _dst.getUMat(); im0.offset += cnidx0; im1.offset += cnidx1; mapk.args(ocl::KernelArg::ReadOnlyNoSize(im0), ocl::KernelArg::ReadOnlyNoSize(im1), ocl::KernelArg::ReadOnlyNoSize(hist), ocl::KernelArg::PtrReadOnly(lut), scale, ocl::KernelArg::WriteOnly(dst)); size_t globalsize[2] = { (size_t)size.width, (size_t)size.height }; return mapk.run(2, globalsize, NULL, false); } return false; } } #endif void cv::calcBackProject( InputArrayOfArrays images, const std::vector& channels, InputArray hist, OutputArray dst, const std::vector& ranges, double scale ) { CV_INSTRUMENT_REGION(); if (hist.dims() <= 2) { #ifdef HAVE_OPENCL Size histSize = hist.size(); bool _1D = histSize.height == 1 || histSize.width == 1; size_t histdims = _1D ? 1 : hist.dims(); #endif CV_OCL_RUN(dst.isUMat() && hist.type() == CV_32FC1 && histdims <= 2 && ranges.size() == histdims * 2 && histdims == channels.size(), ocl_calcBackProject(images, channels, hist, dst, ranges, (float)scale, histdims)) } Mat H0 = hist.getMat(), H; int hcn = H0.channels(); if( hcn > 1 ) { CV_Assert( H0.isContinuous() ); int hsz[CV_CN_MAX+1]; memcpy(hsz, &H0.size[0], H0.dims*sizeof(hsz[0])); hsz[H0.dims] = hcn; H = Mat(H0.dims+1, hsz, H0.depth(), H0.ptr()); } else H = H0; bool _1d = H.rows == 1 || H.cols == 1; int i, dims = H.dims, rsz = (int)ranges.size(), csz = (int)channels.size(); int nimages = (int)images.total(); CV_Assert(nimages > 0); CV_Assert(rsz == dims*2 || (rsz == 2 && _1d) || (rsz == 0 && images.depth(0) == CV_8U)); CV_Assert(csz == 0 || csz == dims || (csz == 1 && _1d)); float* _ranges[CV_MAX_DIM]; if( rsz > 0 ) { for( i = 0; i < rsz/2; i++ ) _ranges[i] = (float*)&ranges[i*2]; } AutoBuffer buf(nimages); for( i = 0; i < nimages; i++ ) buf[i] = images.getMat(i); calcBackProject(&buf[0], nimages, csz ? &channels[0] : 0, hist, dst, rsz ? (const float**)_ranges : 0, scale, true); } ////////////////// C O M P A R E H I S T O G R A M S //////////////////////// double cv::compareHist( InputArray _H1, InputArray _H2, int method ) { CV_INSTRUMENT_REGION(); Mat H1 = _H1.getMat(), H2 = _H2.getMat(); const Mat* arrays[] = {&H1, &H2, 0}; Mat planes[2]; NAryMatIterator it(arrays, planes); double result = 0; int j; CV_Assert( H1.type() == H2.type() && H1.depth() == CV_32F ); double s1 = 0, s2 = 0, s11 = 0, s12 = 0, s22 = 0; CV_Assert( it.planes[0].isContinuous() && it.planes[1].isContinuous() ); for( size_t i = 0; i < it.nplanes; i++, ++it ) { const float* h1 = it.planes[0].ptr(); const float* h2 = it.planes[1].ptr(); const int len = it.planes[0].rows*it.planes[0].cols*H1.channels(); j = 0; if( (method == cv::HISTCMP_CHISQR) || (method == cv::HISTCMP_CHISQR_ALT)) { #if CV_SIMD_64F || CV_SIMD_SCALABLE_64F v_float64 v_eps = vx_setall_f64(DBL_EPSILON); v_float64 v_one = vx_setall_f64(1.f); v_float64 v_zero = vx_setzero_f64(); v_float64 v_res = vx_setzero_f64(); for ( ; j <= len - VTraits::vlanes(); j += VTraits::vlanes()) { v_float32 v_h1 = vx_load(h1 + j), v_h2 = vx_load(h2 + j); v_float64 v_h1_l = v_cvt_f64(v_h1), v_h1_h = v_cvt_f64_high(v_h1); v_float64 v_h2_l = v_cvt_f64(v_h2), v_h2_h = v_cvt_f64_high(v_h2); v_float64 v_a_l, v_a_h; v_a_l = v_sub(v_h1_l, v_h2_l); v_a_h = v_sub(v_h1_h, v_h2_h); v_float64 v_b_l, v_b_h; if (method == cv::HISTCMP_CHISQR) { v_b_l = v_h1_l; v_b_h = v_h1_h; } else { v_b_l = v_add(v_h1_l, v_h2_l); v_b_h = v_add(v_h1_h, v_h2_h); } // low part auto v_res_l = v_mul(v_mul(v_a_l, v_a_l), v_div(v_one, v_b_l)); auto mask = v_gt(v_abs(v_b_l), v_eps); v_res_l = v_select(mask, v_res_l, v_zero); v_res = v_add(v_res, v_res_l); // high part auto v_res_h = v_mul(v_mul(v_a_h, v_a_h), v_div(v_one, v_b_h)); mask = v_gt(v_abs(v_b_h), v_eps); v_res_h = v_select(mask, v_res_h, v_zero); v_res = v_add(v_res, v_res_h); } result += v_reduce_sum(v_res); #endif for( ; j < len; j++ ) { double a = h1[j] - h2[j]; double b = (method == cv::HISTCMP_CHISQR) ? h1[j] : h1[j] + h2[j]; if( fabs(b) > DBL_EPSILON ) result += a*a/b; } } else if( method == cv::HISTCMP_CORREL ) { #if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) v_float64 v_s1 = vx_setzero_f64(); v_float64 v_s2 = vx_setzero_f64(); v_float64 v_s11 = vx_setzero_f64(); v_float64 v_s12 = vx_setzero_f64(); v_float64 v_s22 = vx_setzero_f64(); for ( ; j <= len - VTraits::vlanes(); j += VTraits::vlanes()) { v_float32 v_a = vx_load(h1 + j); v_float32 v_b = vx_load(h2 + j); // 0-1 v_float64 v_ad = v_cvt_f64(v_a); v_float64 v_bd = v_cvt_f64(v_b); v_s12 = v_muladd(v_ad, v_bd, v_s12); v_s11 = v_muladd(v_ad, v_ad, v_s11); v_s22 = v_muladd(v_bd, v_bd, v_s22); v_s1 = v_add(v_s1, v_ad); v_s2 = v_add(v_s2, v_bd); // 2-3 v_ad = v_cvt_f64_high(v_a); v_bd = v_cvt_f64_high(v_b); v_s12 = v_muladd(v_ad, v_bd, v_s12); v_s11 = v_muladd(v_ad, v_ad, v_s11); v_s22 = v_muladd(v_bd, v_bd, v_s22); v_s1 = v_add(v_s1, v_ad); v_s2 = v_add(v_s2, v_bd); } s12 += v_reduce_sum(v_s12); s11 += v_reduce_sum(v_s11); s22 += v_reduce_sum(v_s22); s1 += v_reduce_sum(v_s1); s2 += v_reduce_sum(v_s2); #elif CV_SIMD && 0 //Disable vectorization for cv::HISTCMP_CORREL if f64 is unsupported due to low precision v_float32 v_s1 = vx_setzero_f32(); v_float32 v_s2 = vx_setzero_f32(); v_float32 v_s11 = vx_setzero_f32(); v_float32 v_s12 = vx_setzero_f32(); v_float32 v_s22 = vx_setzero_f32(); for (; j <= len - VTraits::vlanes(); j += VTraits::vlanes()) { v_float32 v_a = vx_load(h1 + j); v_float32 v_b = vx_load(h2 + j); v_s12 = v_muladd(v_a, v_b, v_s12); v_s11 = v_muladd(v_a, v_a, v_s11); v_s22 = v_muladd(v_b, v_b, v_s22); v_s1 += v_a; v_s2 += v_b; } s12 += v_reduce_sum(v_s12); s11 += v_reduce_sum(v_s11); s22 += v_reduce_sum(v_s22); s1 += v_reduce_sum(v_s1); s2 += v_reduce_sum(v_s2); #endif for( ; j < len; j++ ) { double a = h1[j]; double b = h2[j]; s12 += a*b; s1 += a; s11 += a*a; s2 += b; s22 += b*b; } } else if( method == cv::HISTCMP_INTERSECT ) { #if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) v_float64 v_result = vx_setzero_f64(); for ( ; j <= len - VTraits::vlanes(); j += VTraits::vlanes()) { v_float32 v_src = v_min(vx_load(h1 + j), vx_load(h2 + j)); v_result = v_add(v_result, v_add(v_cvt_f64(v_src), v_cvt_f64_high(v_src))); } result += v_reduce_sum(v_result); #elif CV_SIMD && 0 // Disable vectorization for CV_COMP_INTERSECT if f64 is unsupported due to low precision // See https://github.com/opencv/opencv/issues/24757 v_float32 v_result = vx_setzero_f32(); for (; j <= len - VTraits::vlanes(); j += VTraits::vlanes()) { v_float32 v_src = v_min(vx_load(h1 + j), vx_load(h2 + j)); v_result = v_add(v_result, v_src); } result += v_reduce_sum(v_result); #endif for( ; j < len; j++ ) result += std::min(h1[j], h2[j]); } else if( method == cv::HISTCMP_BHATTACHARYYA ) { #if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) v_float64 v_s1 = vx_setzero_f64(); v_float64 v_s2 = vx_setzero_f64(); v_float64 v_result = vx_setzero_f64(); for ( ; j <= len - VTraits::vlanes(); j += VTraits::vlanes()) { v_float32 v_a = vx_load(h1 + j); v_float32 v_b = vx_load(h2 + j); v_float64 v_ad = v_cvt_f64(v_a); v_float64 v_bd = v_cvt_f64(v_b); v_s1 = v_add(v_s1, v_ad); v_s2 = v_add(v_s2, v_bd); v_result = v_add(v_result, v_sqrt(v_mul(v_ad, v_bd))); v_ad = v_cvt_f64_high(v_a); v_bd = v_cvt_f64_high(v_b); v_s1 = v_add(v_s1, v_ad); v_s2 = v_add(v_s2, v_bd); v_result = v_add(v_result, v_sqrt(v_mul(v_ad, v_bd))); } s1 += v_reduce_sum(v_s1); s2 += v_reduce_sum(v_s2); result += v_reduce_sum(v_result); #elif CV_SIMD && 0 //Disable vectorization for cv::HISTCMP_BHATTACHARYYA if f64 is unsupported due to low precision v_float32 v_s1 = vx_setzero_f32(); v_float32 v_s2 = vx_setzero_f32(); v_float32 v_result = vx_setzero_f32(); for (; j <= len - VTraits::vlanes(); j += VTraits::vlanes()) { v_float32 v_a = vx_load(h1 + j); v_float32 v_b = vx_load(h2 + j); v_s1 += v_a; v_s2 += v_b; v_result += v_sqrt(v_a * v_b); } s1 += v_reduce_sum(v_s1); s2 += v_reduce_sum(v_s2); result += v_reduce_sum(v_result); #endif for( ; j < len; j++ ) { double a = h1[j]; double b = h2[j]; result += std::sqrt(a*b); s1 += a; s2 += b; } } else if( method == cv::HISTCMP_KL_DIV ) { for( ; j < len; j++ ) { double p = h1[j]; double q = h2[j]; if( fabs(p) <= DBL_EPSILON ) { continue; } if( fabs(q) <= DBL_EPSILON ) { q = 1e-10; } result += p * std::log( p / q ); } } else CV_Error( cv::Error::StsBadArg, "Unknown comparison method" ); } if( method == cv::HISTCMP_CHISQR_ALT ) result *= 2; else if( method == cv::HISTCMP_CORREL ) { size_t total = H1.total(); double scale = 1./total; double num = s12 - s1*s2*scale; double denom2 = (s11 - s1*s1*scale)*(s22 - s2*s2*scale); result = std::abs(denom2) > DBL_EPSILON ? num/std::sqrt(denom2) : 1.; } else if( method == cv::HISTCMP_BHATTACHARYYA ) { s1 *= s2; s1 = fabs(s1) > FLT_EPSILON ? 1./std::sqrt(s1) : 1.; result = std::sqrt(std::max(1. - result*s1, 0.)); } return result; } double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) { CV_INSTRUMENT_REGION(); double result = 0; int i, dims = H1.dims(); CV_Assert( dims > 0 && dims == H2.dims() && H1.type() == H2.type() && H1.type() == CV_32F ); for( i = 0; i < dims; i++ ) CV_Assert( H1.size(i) == H2.size(i) ); const SparseMat *PH1 = &H1, *PH2 = &H2; if( PH1->nzcount() > PH2->nzcount() && method != cv::HISTCMP_CHISQR && method != cv::HISTCMP_CHISQR_ALT && method != cv::HISTCMP_KL_DIV ) std::swap(PH1, PH2); SparseMatConstIterator it = PH1->begin(); int N1 = (int)PH1->nzcount(), N2 = (int)PH2->nzcount(); if( (method == cv::HISTCMP_CHISQR) || (method == cv::HISTCMP_CHISQR_ALT) ) { for( i = 0; i < N1; i++, ++it ) { CV_Assert(it.ptr != NULL); float v1 = it.value(); const SparseMat::Node* node = it.node(); float v2 = PH2->value(node->idx, (size_t*)&node->hashval); double a = v1 - v2; double b = (method == cv::HISTCMP_CHISQR) ? v1 : v1 + v2; if( fabs(b) > DBL_EPSILON ) result += a*a/b; } } else if( method == cv::HISTCMP_CORREL ) { double s1 = 0, s2 = 0, s11 = 0, s12 = 0, s22 = 0; for( i = 0; i < N1; i++, ++it ) { CV_Assert(it.ptr != NULL); double v1 = it.value(); const SparseMat::Node* node = it.node(); s12 += v1*PH2->value(node->idx, (size_t*)&node->hashval); s1 += v1; s11 += v1*v1; } it = PH2->begin(); for( i = 0; i < N2; i++, ++it ) { CV_Assert(it.ptr != NULL); double v2 = it.value(); s2 += v2; s22 += v2*v2; } size_t total = 1; for( i = 0; i < H1.dims(); i++ ) total *= H1.size(i); double scale = 1./total; double num = s12 - s1*s2*scale; double denom2 = (s11 - s1*s1*scale)*(s22 - s2*s2*scale); result = std::abs(denom2) > DBL_EPSILON ? num/std::sqrt(denom2) : 1.; } else if( method == cv::HISTCMP_INTERSECT ) { for( i = 0; i < N1; i++, ++it ) { CV_Assert(it.ptr != NULL); float v1 = it.value(); const SparseMat::Node* node = it.node(); float v2 = PH2->value(node->idx, (size_t*)&node->hashval); if( v2 ) result += std::min(v1, v2); } } else if( method == cv::HISTCMP_BHATTACHARYYA ) { double s1 = 0, s2 = 0; for( i = 0; i < N1; i++, ++it ) { CV_Assert(it.ptr != NULL); double v1 = it.value(); const SparseMat::Node* node = it.node(); double v2 = PH2->value(node->idx, (size_t*)&node->hashval); result += std::sqrt(v1*v2); s1 += v1; } it = PH2->begin(); for( i = 0; i < N2; i++, ++it ) { CV_Assert(it.ptr != NULL); s2 += it.value(); } s1 *= s2; s1 = fabs(s1) > FLT_EPSILON ? 1./std::sqrt(s1) : 1.; result = std::sqrt(std::max(1. - result*s1, 0.)); } else if( method == cv::HISTCMP_KL_DIV ) { for( i = 0; i < N1; i++, ++it ) { CV_Assert(it.ptr != NULL); double v1 = it.value(); const SparseMat::Node* node = it.node(); double v2 = PH2->value(node->idx, (size_t*)&node->hashval); if( !v2 ) v2 = 1e-10; result += v1 * std::log( v1 / v2 ); } } else CV_Error( cv::Error::StsBadArg, "Unknown comparison method" ); if( method == cv::HISTCMP_CHISQR_ALT ) result *= 2; return result; } class EqualizeHistCalcHist_Invoker : public cv::ParallelLoopBody { public: enum {HIST_SZ = 256}; EqualizeHistCalcHist_Invoker(cv::Mat& src, int* histogram, cv::Mutex* histogramLock) : src_(src), globalHistogram_(histogram), histogramLock_(histogramLock) { } void operator()( const cv::Range& rowRange ) const CV_OVERRIDE { int localHistogram[HIST_SZ] = {0, }; const size_t sstep = src_.step; int width = src_.cols; int height = rowRange.end - rowRange.start; if (src_.isContinuous()) { width *= height; height = 1; } for (const uchar* ptr = src_.ptr(rowRange.start); height--; ptr += sstep) { int x = 0; for (; x <= width - 4; x += 4) { int t0 = ptr[x], t1 = ptr[x+1]; localHistogram[t0]++; localHistogram[t1]++; t0 = ptr[x+2]; t1 = ptr[x+3]; localHistogram[t0]++; localHistogram[t1]++; } for (; x < width; ++x) localHistogram[ptr[x]]++; } cv::AutoLock lock(*histogramLock_); for( int i = 0; i < HIST_SZ; i++ ) globalHistogram_[i] += localHistogram[i]; } static bool isWorthParallel( const cv::Mat& src ) { return ( src.total() >= 640*480 ); } private: EqualizeHistCalcHist_Invoker& operator=(const EqualizeHistCalcHist_Invoker&); cv::Mat& src_; int* globalHistogram_; cv::Mutex* histogramLock_; }; namespace cv { void equalizeHistLut_dispatch( const uchar* src, uchar* dst, int len, const uchar* lut ); } class EqualizeHistLut_Invoker : public cv::ParallelLoopBody { public: EqualizeHistLut_Invoker( cv::Mat& src, cv::Mat& dst, uchar* lut ) : src_(src), dst_(dst), lut_(lut) { } void operator()( const cv::Range& rowRange ) const CV_OVERRIDE { const size_t sstep = src_.step; const size_t dstep = dst_.step; int width = src_.cols; int height = rowRange.end - rowRange.start; const uchar* lut = lut_; if (src_.isContinuous() && dst_.isContinuous()) { width *= height; height = 1; } const uchar* sptr = src_.ptr(rowRange.start); uchar* dptr = dst_.ptr(rowRange.start); for (; height--; sptr += sstep, dptr += dstep) { cv::equalizeHistLut_dispatch(sptr, dptr, width, lut); } } static bool isWorthParallel( const cv::Mat& src ) { return ( src.total() >= 640*480 ); } private: EqualizeHistLut_Invoker& operator=(const EqualizeHistLut_Invoker&); cv::Mat& src_; cv::Mat& dst_; uchar* lut_; }; #ifdef HAVE_OPENCL namespace cv { static bool ocl_equalizeHist(InputArray _src, OutputArray _dst) { const ocl::Device & dev = ocl::Device::getDefault(); int compunits = dev.maxComputeUnits(); size_t wgs = dev.maxWorkGroupSize(); Size size = _src.size(); bool use16 = size.width % 16 == 0 && _src.offset() % 16 == 0 && _src.step() % 16 == 0; int kercn = dev.isAMD() && use16 ? 16 : std::min(4, ocl::predictOptimalVectorWidth(_src)); ocl::Kernel k1("calculate_histogram", ocl::imgproc::histogram_oclsrc, format("-D BINS=%d -D HISTS_COUNT=%d -D WGS=%zu -D kercn=%d -D T=%s%s", BINS, compunits, wgs, kercn, kercn == 4 ? "int" : ocl::typeToStr(CV_8UC(kercn)), _src.isContinuous() ? " -D HAVE_SRC_CONT" : "")); if (k1.empty()) return false; UMat src = _src.getUMat(), ghist(1, BINS * compunits, CV_32SC1); k1.args(ocl::KernelArg::ReadOnly(src), ocl::KernelArg::PtrWriteOnly(ghist), (int)src.total()); size_t globalsize = compunits * wgs; if (!k1.run(1, &globalsize, &wgs, false)) return false; wgs = std::min(ocl::Device::getDefault().maxWorkGroupSize(), BINS); UMat lut(1, 256, CV_8UC1); ocl::Kernel k2("calcLUT", ocl::imgproc::histogram_oclsrc, format("-D BINS=%d -D HISTS_COUNT=%d -D WGS=%d", BINS, compunits, (int)wgs)); k2.args(ocl::KernelArg::PtrWriteOnly(lut), ocl::KernelArg::PtrReadOnly(ghist), (int)_src.total()); // calculation of LUT if (!k2.run(1, &wgs, &wgs, false)) return false; // execute LUT transparently LUT(_src, lut, _dst); return true; } } #endif void cv::equalizeHist( InputArray _src, OutputArray _dst ) { CV_INSTRUMENT_REGION(); CV_Assert( _src.type() == CV_8UC1 ); if (_src.empty()) return; CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), ocl_equalizeHist(_src, _dst)) Mat src = _src.getMat(); _dst.create( src.size(), src.type() ); Mat dst = _dst.getMat(); CALL_HAL(equalizeHist, cv_hal_equalize_hist, src.data, src.step, dst.data, dst.step, src.cols, src.rows); Mutex histogramLockInstance; const int hist_sz = EqualizeHistCalcHist_Invoker::HIST_SZ; int hist[hist_sz] = {0,}; int lut[hist_sz] = {0,}; EqualizeHistCalcHist_Invoker calcBody(src, hist, &histogramLockInstance); cv::Range heightRange(0, src.rows); if(EqualizeHistCalcHist_Invoker::isWorthParallel(src)) parallel_for_(heightRange, calcBody); else calcBody(heightRange); int i = 0; while (!hist[i]) ++i; int total = (int)src.total(); if (hist[i] == total) { dst.setTo(i); return; } float scale = (hist_sz - 1.f)/(total - hist[i]); int sum = 0; for (lut[i++] = 0; i < hist_sz; ++i) { sum += hist[i]; lut[i] = saturate_cast(sum * scale); } uchar lut_u8[hist_sz]; for (int j = 0; j < hist_sz; ++j) lut_u8[j] = (uchar)lut[j]; EqualizeHistLut_Invoker lutBody(src, dst, lut_u8); if(EqualizeHistLut_Invoker::isWorthParallel(src)) parallel_for_(heightRange, lutBody); else lutBody(heightRange); } /* End of file. */