diff --git a/hal/ipp/CMakeLists.txt b/hal/ipp/CMakeLists.txt index fb4e9ad312..4774cf55fb 100644 --- a/hal/ipp/CMakeLists.txt +++ b/hal/ipp/CMakeLists.txt @@ -26,6 +26,7 @@ add_library(ipphal STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/canny_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/threshold_ipp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/distancetransform_ipp.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/histogram_ipp.cpp" ) #TODO: HAVE_IPP_ICV and HAVE_IPP_IW added as private macro till OpenCV itself is diff --git a/hal/ipp/include/ipp_hal_imgproc.hpp b/hal/ipp/include/ipp_hal_imgproc.hpp index 5c86281817..25ffc31003 100644 --- a/hal/ipp/include/ipp_hal_imgproc.hpp +++ b/hal/ipp/include/ipp_hal_imgproc.hpp @@ -154,6 +154,11 @@ int ipp_hal_distanceTransform(const uchar* src_data, size_t src_step, uchar* dst #undef cv_hal_distanceTransform #define cv_hal_distanceTransform ipp_hal_distanceTransform +int ipp_hal_calcHist(const uchar* src_data, size_t src_step, int src_type, int src_width, int src_height, + float* hist_data, int hist_size, const float** ranges, bool uniform, bool accumulate); +#undef cv_hal_calcHist +#define cv_hal_calcHist ipp_hal_calcHist + #endif // IPP_VERSION_X100 >= 700 #define IPP_DISABLE_PERF_CANNY_MT 1 // cv::Canny OpenCV MT performance is better diff --git a/hal/ipp/src/histogram_ipp.cpp b/hal/ipp/src/histogram_ipp.cpp new file mode 100644 index 0000000000..7ff64e5f5e --- /dev/null +++ b/hal/ipp/src/histogram_ipp.cpp @@ -0,0 +1,223 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// Copyright (C) 2026, BigVision LLC, all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "ipp_hal_imgproc.hpp" + +#include +#include +#include "precomp_ipp.hpp" + +#if IPP_VERSION_X100 >= 700 + +#define IPP_HISTOGRAM_PARALLEL 1 + +namespace cv { namespace ipp { unsigned long long getIppTopFeatures(); } } + +using namespace cv; + +namespace { + +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 + { + 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 & ); +}; + +static bool ipp_calchist(const Mat &image, Mat &hist, int histSize, const float** ranges, bool uniform, bool accumulate) +{ +#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.cols, image.rows, image.elemSize(), (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; +} + +} // namespace + +int ipp_hal_calcHist(const uchar* src_data, size_t src_step, int src_type, int src_width, int src_height, + float* hist_data, int hist_size, const float** ranges, bool uniform, bool accumulate) +{ + CV_HAL_CHECK_USE_IPP(); + + Mat image(src_height, src_width, src_type, (void*)src_data, src_step); + Mat hist(1, &hist_size, CV_32F, (void*)hist_data); + + if(ipp_calchist(image, hist, hist_size, ranges, uniform, accumulate)) + return CV_HAL_ERROR_OK; + + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +#endif diff --git a/hal/ipp/src/precomp_ipp.hpp b/hal/ipp/src/precomp_ipp.hpp index 0d68a5a40e..7b3bfc2ae6 100644 --- a/hal/ipp/src/precomp_ipp.hpp +++ b/hal/ipp/src/precomp_ipp.hpp @@ -120,4 +120,30 @@ static inline int ippiSuggestRowThreadsNum(const ::ipp::IwiImage &image, size_t } #endif +#if IPP_VERSION_X100 >= 201700 +#define CV_IPP_MALLOC(SIZE) ippMalloc_L(SIZE) +#else +#define CV_IPP_MALLOC(SIZE) ippMalloc((int)SIZE) +#endif + +template +class IppAutoBuffer +{ +public: + IppAutoBuffer() { m_size = 0; m_pBuffer = NULL; } + explicit IppAutoBuffer(size_t size) { m_size = 0; m_pBuffer = NULL; allocate(size); } + ~IppAutoBuffer() { deallocate(); } + T* allocate(size_t size) { if(m_size < size) { deallocate(); m_pBuffer = (T*)CV_IPP_MALLOC(size); m_size = size; } return m_pBuffer; } + void deallocate() { if(m_pBuffer) { ippFree(m_pBuffer); m_pBuffer = NULL; } m_size = 0; } + inline T* get() { return (T*)m_pBuffer;} + inline operator T* () { return (T*)m_pBuffer;} + inline operator const T* () const { return (const T*)m_pBuffer;} +private: + IppAutoBuffer(IppAutoBuffer &) {} + IppAutoBuffer& operator =(const IppAutoBuffer &) {return *this;} + + size_t m_size; + T* m_pBuffer; +}; + #endif //__PRECOMP_IPP_HPP__ diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index 9691f61fa3..123cb83556 100644 --- a/modules/imgproc/src/histogram.cpp +++ b/modules/imgproc/src/histogram.cpp @@ -688,152 +688,6 @@ calcHist_8u( std::vector& _ptrs, const std::vector& _deltas, } } -#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_OPENVX @@ -894,58 +748,6 @@ namespace cv } #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 ) @@ -973,11 +775,6 @@ void cv::calcHist( const Mat* images, int nimages, const int* channels, 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);