From ced60b74983645b6f943332b0ef46a58fe3eeae8 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 12 Oct 2010 06:37:44 +0000 Subject: [PATCH] added histograms calculation to gpu module --- modules/gpu/include/opencv2/gpu/gpu.hpp | 25 +++ modules/gpu/src/imgproc_gpu.cpp | 228 ++++++++++++++++++++++++ tests/gpu/src/imgproc_gpu.cpp | 77 +++++++- 3 files changed, 329 insertions(+), 1 deletion(-) diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index 8bb7c2a17..041f6759c 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -700,6 +700,31 @@ namespace cv //!performs labeling via graph cuts CV_EXPORTS void graphcut(GpuMat& terminals, GpuMat& leftTransp, GpuMat& rightTransp, GpuMat& top, GpuMat& bottom, GpuMat& labels, GpuMat& buf); + ////////////////////////////////// Histograms ////////////////////////////////// + + //! Compute levels with even distribution. levels will have 1 row and nLevels cols and CV_32SC1 type. + CV_EXPORTS void evenLevels(GpuMat& levels, int nLevels, int lowerLevel, int upperLevel); + //! Calculates histogram with evenly distributed bins for signle channel source. + //! Supports CV_8UC1, CV_16UC1 and CV_16SC1 source types. + //! Output hist will have one row and histSize cols and CV_32SC1 type. + CV_EXPORTS void histEven(const GpuMat& src, GpuMat& hist, int histSize, int lowerLevel, int upperLevel); + //! Calculates histogram with evenly distributed bins for four-channel source. + //! All channels of source are processed separately. + //! Supports CV_8UC4, CV_16UC4 and CV_16SC4 source types. + //! Output hist[i] will have one row and histSize[i] cols and CV_32SC1 type. + CV_EXPORTS void histEven(const GpuMat& src, GpuMat hist[4], int histSize[4], int lowerLevel[4], int upperLevel[4]); + //! Calculates histogram with bins determined by levels array. + //! levels must have one row and CV_32SC1 type. + //! Supports CV_8UC1, CV_16UC1 and CV_16SC1 source types. + //! Output hist will have one row and (levels.cols-1) cols and CV_32SC1 type. + CV_EXPORTS void histRange(const GpuMat& src, GpuMat& hist, const GpuMat& levels); + //! Calculates histogram with bins determined by levels array. + //! All levels must have one row and CV_32SC1 type. + //! All channels of source are processed separately. + //! Supports CV_8UC4, CV_16UC4 and CV_16SC4 source types. + //! Output hist[i] will have one row and (levels[i].cols-1) cols and CV_32SC1 type. + CV_EXPORTS void histRange(const GpuMat& src, GpuMat hist[4], const GpuMat levels[4]); + //////////////////////////////// StereoBM_GPU //////////////////////////////// class CV_EXPORTS StereoBM_GPU diff --git a/modules/gpu/src/imgproc_gpu.cpp b/modules/gpu/src/imgproc_gpu.cpp index 33bf5b5b3..84d951665 100644 --- a/modules/gpu/src/imgproc_gpu.cpp +++ b/modules/gpu/src/imgproc_gpu.cpp @@ -64,6 +64,11 @@ void cv::gpu::warpPerspective(const GpuMat&, GpuMat&, const Mat&, Size, int) { t void cv::gpu::rotate(const GpuMat&, GpuMat&, Size, double, double, double, int) { throw_nogpu(); } void cv::gpu::integral(GpuMat&, GpuMat&, GpuMat&) { throw_nogpu(); } void cv::gpu::Canny(const GpuMat&, GpuMat&, double, double, int) { throw_nogpu(); } +void cv::gpu::evenLevels(GpuMat&, int, int, int) { throw_nogpu(); } +void cv::gpu::histEven(const GpuMat&, GpuMat&, int, int, int) { throw_nogpu(); } +void cv::gpu::histEven(const GpuMat&, GpuMat*, int*, int*, int*) { throw_nogpu(); } +void cv::gpu::histRange(const GpuMat&, GpuMat&, const GpuMat&) { throw_nogpu(); } +void cv::gpu::histRange(const GpuMat&, GpuMat*, const GpuMat*) { throw_nogpu(); } #else /* !defined (HAVE_CUDA) */ @@ -1048,4 +1053,227 @@ void cv::gpu::Canny(const GpuMat& image, GpuMat& edges, double threshold1, doubl edges.ptr(), edges.step, sz, (Npp32f)threshold1, (Npp32f)threshold2, buf.ptr()) ); } +//////////////////////////////////////////////////////////////////////// +// Histogram + +namespace +{ + template struct NPPTypeTraits; + template<> struct NPPTypeTraits { typedef Npp8u npp_type; }; + template<> struct NPPTypeTraits { typedef Npp16u npp_type; }; + template<> struct NPPTypeTraits { typedef Npp16s npp_type; }; + + typedef NppStatus (*get_buf_size_c1_t)(NppiSize oSizeROI, int nLevels, int* hpBufferSize); + typedef NppStatus (*get_buf_size_c4_t)(NppiSize oSizeROI, int nLevels[], int* hpBufferSize); + + template struct NppHistogramEvenFuncC1 + { + typedef typename NPPTypeTraits::npp_type src_t; + + typedef NppStatus (*func_ptr)(const src_t* pSrc, int nSrcStep, NppiSize oSizeROI, Npp32s * pHist, + int nLevels, Npp32s nLowerLevel, Npp32s nUpperLevel, Npp8u * pBuffer); + }; + template struct NppHistogramEvenFuncC4 + { + typedef typename NPPTypeTraits::npp_type src_t; + + typedef NppStatus (*func_ptr)(const src_t* pSrc, int nSrcStep, NppiSize oSizeROI, + Npp32s * pHist[4], int nLevels[4], Npp32s nLowerLevel[4], Npp32s nUpperLevel[4], Npp8u * pBuffer); + }; + + template::func_ptr func, get_buf_size_c1_t get_buf_size> + struct NppHistogramEvenC1 + { + typedef typename NppHistogramEvenFuncC1::src_t src_t; + + static void hist(const GpuMat& src, GpuMat& hist, int histSize, int lowerLevel, int upperLevel) + { + int levels = histSize + 1; + hist.create(1, histSize, CV_32S); + + NppiSize sz; + sz.width = src.cols; + sz.height = src.rows; + + GpuMat buffer; + int buf_size; + + get_buf_size(sz, levels, &buf_size); + buffer.create(1, buf_size, CV_8U); + nppSafeCall( func(src.ptr(), src.step, sz, hist.ptr(), levels, + lowerLevel, upperLevel, buffer.ptr()) ); + } + }; + template::func_ptr func, get_buf_size_c4_t get_buf_size> + struct NppHistogramEvenC4 + { + typedef typename NppHistogramEvenFuncC4::src_t src_t; + + static void hist(const GpuMat& src, GpuMat hist[4], int histSize[4], int lowerLevel[4], int upperLevel[4]) + { + int levels[] = {histSize[0] + 1, histSize[1] + 1, histSize[2] + 1, histSize[3] + 1}; + hist[0].create(1, histSize[0], CV_32S); + hist[1].create(1, histSize[1], CV_32S); + hist[2].create(1, histSize[2], CV_32S); + hist[3].create(1, histSize[3], CV_32S); + + NppiSize sz; + sz.width = src.cols; + sz.height = src.rows; + + Npp32s* pHist[] = {hist[0].ptr(), hist[1].ptr(), hist[2].ptr(), hist[3].ptr()}; + + GpuMat buffer; + int buf_size; + + get_buf_size(sz, levels, &buf_size); + buffer.create(1, buf_size, CV_8U); + nppSafeCall( func(src.ptr(), src.step, sz, pHist, levels, lowerLevel, upperLevel, buffer.ptr()) ); + } + }; + + template struct NppHistogramRangeFuncC1 + { + typedef typename NPPTypeTraits::npp_type src_t; + + typedef NppStatus (*func_ptr)(const src_t* pSrc, int nSrcStep, NppiSize oSizeROI, Npp32s* pHist, + const Npp32s* pLevels, int nLevels, Npp8u* pBuffer); + }; + template struct NppHistogramRangeFuncC4 + { + typedef typename NPPTypeTraits::npp_type src_t; + + typedef NppStatus (*func_ptr)(const src_t* pSrc, int nSrcStep, NppiSize oSizeROI, Npp32s* pHist[4], + const Npp32s* pLevels[4], int nLevels[4], Npp8u* pBuffer); + }; + + template::func_ptr func, get_buf_size_c1_t get_buf_size> + struct NppHistogramRangeC1 + { + typedef typename NppHistogramRangeFuncC1::src_t src_t; + + static void hist(const GpuMat& src, GpuMat& hist, const GpuMat& levels) + { + CV_Assert(levels.type() == CV_32SC1 && levels.rows == 1); + + hist.create(1, levels.cols - 1, CV_32S); + + NppiSize sz; + sz.width = src.cols; + sz.height = src.rows; + + GpuMat buffer; + int buf_size; + + get_buf_size(sz, levels.cols, &buf_size); + buffer.create(1, buf_size, CV_8U); + nppSafeCall( func(src.ptr(), src.step, sz, hist.ptr(), levels.ptr(), levels.cols, buffer.ptr()) ); + } + }; + template::func_ptr func, get_buf_size_c4_t get_buf_size> + struct NppHistogramRangeC4 + { + typedef typename NppHistogramRangeFuncC4::src_t src_t; + + static void hist(const GpuMat& src, GpuMat hist[4], const GpuMat levels[4]) + { + CV_Assert(levels[0].type() == CV_32SC1 && levels[0].rows == 1); + CV_Assert(levels[1].type() == CV_32SC1 && levels[1].rows == 1); + CV_Assert(levels[2].type() == CV_32SC1 && levels[2].rows == 1); + CV_Assert(levels[3].type() == CV_32SC1 && levels[3].rows == 1); + + hist[0].create(1, levels[0].cols - 1, CV_32S); + hist[1].create(1, levels[1].cols - 1, CV_32S); + hist[2].create(1, levels[2].cols - 1, CV_32S); + hist[3].create(1, levels[3].cols - 1, CV_32S); + + Npp32s* pHist[] = {hist[0].ptr(), hist[1].ptr(), hist[2].ptr(), hist[3].ptr()}; + int nLevels[] = {levels[0].cols, levels[1].cols, levels[2].cols, levels[3].cols}; + const Npp32s* pLevels[] = {levels[0].ptr(), levels[1].ptr(), levels[2].ptr(), levels[3].ptr()}; + + NppiSize sz; + sz.width = src.cols; + sz.height = src.rows; + + GpuMat buffer; + int buf_size; + + get_buf_size(sz, nLevels, &buf_size); + buffer.create(1, buf_size, CV_8U); + nppSafeCall( func(src.ptr(), src.step, sz, pHist, pLevels, nLevels, buffer.ptr()) ); + } + }; +} + +void cv::gpu::evenLevels(GpuMat& levels, int nLevels, int lowerLevel, int upperLevel) +{ + Mat host_levels(1, nLevels, CV_32SC1); + nppSafeCall( nppiEvenLevelsHost_32s(host_levels.ptr(), nLevels, lowerLevel, upperLevel) ); + levels.upload(host_levels); +} + +void cv::gpu::histEven(const GpuMat& src, GpuMat& hist, int histSize, int lowerLevel, int upperLevel) +{ + CV_Assert(src.type() == CV_8UC1 || src.type() == CV_16UC1 || src.type() == CV_16SC1 ); + + typedef void (*hist_t)(const GpuMat& src, GpuMat& hist, int levels, int lowerLevel, int upperLevel); + static const hist_t hist_callers[] = + { + NppHistogramEvenC1::hist, + 0, + NppHistogramEvenC1::hist, + NppHistogramEvenC1::hist + }; + + hist_callers[src.depth()](src, hist, histSize, lowerLevel, upperLevel); +} + +void cv::gpu::histEven(const GpuMat& src, GpuMat hist[4], int histSize[4], int lowerLevel[4], int upperLevel[4]) +{ + CV_Assert(src.type() == CV_8UC4 || src.type() == CV_16UC4 || src.type() == CV_16SC4 ); + + typedef void (*hist_t)(const GpuMat& src, GpuMat hist[4], int levels[4], int lowerLevel[4], int upperLevel[4]); + static const hist_t hist_callers[] = + { + NppHistogramEvenC4::hist, + 0, + NppHistogramEvenC4::hist, + NppHistogramEvenC4::hist + }; + + hist_callers[src.depth()](src, hist, histSize, lowerLevel, upperLevel); +} + +void cv::gpu::histRange(const GpuMat& src, GpuMat& hist, const GpuMat& levels) +{ + CV_Assert(src.type() == CV_8UC1 || src.type() == CV_16UC1 || src.type() == CV_16SC1); + + typedef void (*hist_t)(const GpuMat& src, GpuMat& hist, const GpuMat& levels); + static const hist_t hist_callers[] = + { + NppHistogramRangeC1::hist, + 0, + NppHistogramRangeC1::hist, + NppHistogramRangeC1::hist + }; + + hist_callers[src.depth()](src, hist, levels); +} + +void cv::gpu::histRange(const GpuMat& src, GpuMat hist[4], const GpuMat levels[4]) +{ + CV_Assert(src.type() == CV_8UC4 || src.type() == CV_16UC4 || src.type() == CV_16SC4); + + typedef void (*hist_t)(const GpuMat& src, GpuMat hist[4], const GpuMat levels[4]); + static const hist_t hist_callers[] = + { + NppHistogramRangeC4::hist, + 0, + NppHistogramRangeC4::hist, + NppHistogramRangeC4::hist + }; + + hist_callers[src.depth()](src, hist, levels); +} + #endif /* !defined (HAVE_CUDA) */ diff --git a/tests/gpu/src/imgproc_gpu.cpp b/tests/gpu/src/imgproc_gpu.cpp index b199acb82..c7fc60d9c 100644 --- a/tests/gpu/src/imgproc_gpu.cpp +++ b/tests/gpu/src/imgproc_gpu.cpp @@ -530,6 +530,80 @@ void CV_GpuCvtColorTest::run( int ) ts->set_failed_test_info(testResult); } +//////////////////////////////////////////////////////////////////////////////// +// Histograms +class CV_GpuHistogramsTest : public CvTest +{ +public: + CV_GpuHistogramsTest() : CvTest("GPU-Histograms", "histEven") {} + ~CV_GpuHistogramsTest() {}; + +protected: + void run(int); + + int CheckNorm(const Mat& m1, const Mat& m2) + { + double ret = norm(m1, m2, NORM_INF); + + if (ret < std::numeric_limits::epsilon()) + { + return CvTS::OK; + } + else + { + ts->printf(CvTS::LOG, "\nNorm: %f\n", ret); + return CvTS::FAIL_GENERIC; + } + } +}; + +void CV_GpuHistogramsTest::run( int ) +{ + //load image + cv::Mat img = cv::imread(std::string(ts->get_data_path()) + "stereobp/aloe-L.png"); + + if (img.empty()) + { + ts->set_failed_test_info(CvTS::FAIL_MISSING_TEST_DATA); + return; + } + + try + { + Mat hsv; + cv::cvtColor(img, hsv, CV_BGR2HSV); + + int hbins = 30; + int histSize[] = {hbins}; + + float hranges[] = {0, 180}; + const float* ranges[] = {hranges}; + + MatND hist; + + int channels[] = {0}; + calcHist(&hsv, 1, channels, Mat(), hist, 1, histSize, ranges); + + GpuMat gpuHsv(hsv); + std::vector srcs; + cv::gpu::split(gpuHsv, srcs); + GpuMat gpuHist; + histEven(srcs[0], gpuHist, hbins, (int)hranges[0], (int)hranges[1]); + + Mat cpuHist = hist; + cpuHist = cpuHist.t(); + cpuHist.convertTo(cpuHist, CV_32S); + + ts->set_failed_test_info(CheckNorm(cpuHist, gpuHist)); + } + catch(const cv::Exception& e) + { + if (!check_and_treat_gpu_exception(e, ts)) + throw; + return; + } +} + ///////////////////////////////////////////////////////////////////////////// /////////////////// tests registration ///////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -545,4 +619,5 @@ CV_GpuNppImageWarpAffineTest CV_GpuNppImageWarpAffine_test; CV_GpuNppImageWarpPerspectiveTest CV_GpuNppImageWarpPerspective_test; CV_GpuNppImageIntegralTest CV_GpuNppImageIntegral_test; CV_GpuNppImageCannyTest CV_GpuNppImageCanny_test; -CV_GpuCvtColorTest CV_GpuCvtColor_test; \ No newline at end of file +CV_GpuCvtColorTest CV_GpuCvtColor_test; +CV_GpuHistogramsTest CV_GpuHistograms_test; \ No newline at end of file