diff --git a/modules/fastcv/include/opencv2/fastcv.hpp b/modules/fastcv/include/opencv2/fastcv.hpp index 6ed8eba4a33..af188dfcb09 100644 --- a/modules/fastcv/include/opencv2/fastcv.hpp +++ b/modules/fastcv/include/opencv2/fastcv.hpp @@ -16,6 +16,7 @@ #include "opencv2/fastcv/edges.hpp" #include "opencv2/fastcv/fast10.hpp" #include "opencv2/fastcv/fft.hpp" +#include "opencv2/fastcv/histogram.hpp" #include "opencv2/fastcv/hough.hpp" #include "opencv2/fastcv/ipptransform.hpp" #include "opencv2/fastcv/moments.hpp" diff --git a/modules/fastcv/include/opencv2/fastcv/histogram.hpp b/modules/fastcv/include/opencv2/fastcv/histogram.hpp new file mode 100644 index 00000000000..f0bbd3c6f61 --- /dev/null +++ b/modules/fastcv/include/opencv2/fastcv/histogram.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 +*/ + +#ifndef OPENCV_FASTCV_HISTOGRAM_HPP +#define OPENCV_FASTCV_HISTOGRAM_HPP + +#include + +namespace cv { +namespace fastcv { + +//! @addtogroup fastcv +//! @{ + +/** + * @brief Calculates histogram of input image. This function implements specific use case of + * 256-bin histogram calculation for 8u single channel images in an optimized way. + * @param _src Intput image with type CV_8UC1 + * @param _hist Output histogram of type int of 256 bins + */ +CV_EXPORTS_W void calcHist( InputArray _src, OutputArray _hist ); +//! @} + +} // fastcv:: +} // cv:: + +#endif // OPENCV_FASTCV_HISTOGRAM_HPP diff --git a/modules/fastcv/include/opencv2/fastcv/moments.hpp b/modules/fastcv/include/opencv2/fastcv/moments.hpp index 90034548571..13c9019841f 100644 --- a/modules/fastcv/include/opencv2/fastcv/moments.hpp +++ b/modules/fastcv/include/opencv2/fastcv/moments.hpp @@ -16,7 +16,8 @@ namespace fastcv { /** * @brief Calculates all of the moments up to the third order of the image pixels' intensities - The results are returned in the structure cv::Moments. + * The results are returned in the structure cv::Moments. This function cv::fastcv::moments() + * calculate the moments using floating point calculations whereas cv::moments() calculate moments using double. * @param _src Input image with type CV_8UC1, CV_32SC1, CV_32FC1 * @param binary If true, assumes the image to be binary (0x00 for black, 0xff for white), otherwise assumes the image to be * grayscale. diff --git a/modules/fastcv/perf/perf_bilateral.cpp b/modules/fastcv/perf/perf_bilateral.cpp index 63323d459cc..bfeb50f288c 100644 --- a/modules/fastcv/perf/perf_bilateral.cpp +++ b/modules/fastcv/perf/perf_bilateral.cpp @@ -52,7 +52,7 @@ PERF_TEST_P(BilateralPerfTest, run, RNG& rng = cv::theRNG(); Mat src(size, CV_8UC1); - cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256)); + cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255)); Mat dst; while (next()) diff --git a/modules/fastcv/perf/perf_histogram.cpp b/modules/fastcv/perf/perf_histogram.cpp new file mode 100644 index 00000000000..1c9c9bc7e62 --- /dev/null +++ b/modules/fastcv/perf/perf_histogram.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 +*/ + +#include "perf_precomp.hpp" + +namespace opencv_test { + +typedef std::tuple HistogramPerfParams; +typedef perf::TestBaseWithParam HistogramPerfTest; + + +PERF_TEST_P(HistogramPerfTest, run, + testing::Values(perf::szQVGA, perf::szVGA, perf::sz720p, perf::sz1080p) + ) +{ + auto p = GetParam(); + cv::Size size = std::get<0>(p); + + RNG& rng = cv::theRNG(); + Mat src(size, CV_8UC1); + cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256)); + Mat hist(1, 256, CV_32SC1); + + for (; next(); ) + { + startTimer(); + cv::fastcv::calcHist(src, hist); + stopTimer(); + } + + SANITY_CHECK_NOTHING(); +} + +} // namespace diff --git a/modules/fastcv/src/bilateralFilter.cpp b/modules/fastcv/src/bilateralFilter.cpp index a0995347b24..a7c3cd52e8d 100644 --- a/modules/fastcv/src/bilateralFilter.cpp +++ b/modules/fastcv/src/bilateralFilter.cpp @@ -22,8 +22,8 @@ class FcvFilterLoop_Invoker : public cv::ParallelLoopBody { int height_ = range.end - range.start; int width_ = width; - cv::Mat src_; - int n = knl/2; + cv::Mat src_; + int n = knl/2; src_ = cv::Mat(height_ + 2 * n, width_ + 2 * n, CV_8U); if (range.start == 0 && range.end == height) @@ -43,7 +43,7 @@ class FcvFilterLoop_Invoker : public cv::ParallelLoopBody cv::copyMakeBorder(src(cv::Rect(0, range.start - n, width_, height_ + 2 * n)), src_, 0, 0, n, n, bdr); } - cv::Mat dst_padded = cv::Mat(height_ + 2*n, width_ + 2*n, CV_8U); + cv::Mat dst_padded = cv::Mat(height_ + 2*n, width_ + 2*n, CV_8U); auto func = (knl == 5) ? fcvBilateralFilter5x5u8_v3 : (knl == 7) ? fcvBilateralFilter7x7u8_v3 : @@ -52,10 +52,10 @@ class FcvFilterLoop_Invoker : public cv::ParallelLoopBody func(src_.data, width_ + 2 * n, height_ + 2 * n, width_ + 2 * n, dst_padded.data, width_ + 2 * n, sigma_color, sigma_space, 0); - cv::Mat dst_temp1 = dst_padded(cv::Rect(n, n, width_, height_)); - cv::Mat dst_temp2 = dst(cv::Rect(0, range.start, width_, height_)); - dst_temp1.copyTo(dst_temp2); - } + cv::Mat dst_temp1 = dst_padded(cv::Rect(n, n, width_, height_)); + cv::Mat dst_temp2 = dst(cv::Rect(0, range.start, width_, height_)); + dst_temp1.copyTo(dst_temp2); + } private: const size_t src_step; @@ -67,8 +67,8 @@ class FcvFilterLoop_Invoker : public cv::ParallelLoopBody float32_t sigma_color; float32_t sigma_space; int ret; - cv::Mat src; - cv::Mat dst; + cv::Mat src; + cv::Mat dst; FcvFilterLoop_Invoker(const FcvFilterLoop_Invoker &); // = delete; const FcvFilterLoop_Invoker& operator= (const FcvFilterLoop_Invoker &); // = delete; @@ -82,24 +82,20 @@ void bilateralFilter( InputArray _src, OutputArray _dst, int d, CV_Assert(!_src.empty()); int type = _src.type(); - CV_Assert(type == CV_8UC1); - CV_Assert(d == 5 || d == 7 || d == 9); + CV_Assert(type == CV_8UC1); + CV_Assert(d == 5 || d == 7 || d == 9); Size size = _src.size(); - _dst.create( size, type ); + _dst.create( size, type ); Mat src = _src.getMat(); Mat dst = _dst.getMat(); CV_Assert(src.data != dst.data); if( sigmaColor <= 0 ) - { sigmaColor = 1; - } if( sigmaSpace <= 0 ) - { sigmaSpace = 1; - } int nStripes = (src.rows / 20 == 0) ? 1 : (src.rows / 20); cv::parallel_for_(cv::Range(0, src.rows), diff --git a/modules/fastcv/src/histogram.cpp b/modules/fastcv/src/histogram.cpp new file mode 100644 index 00000000000..e3ae4c13b75 --- /dev/null +++ b/modules/fastcv/src/histogram.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 +*/ + +#include "precomp.hpp" + +namespace cv { +namespace fastcv { + +class FcvHistogramLoop_Invoker : public cv::ParallelLoopBody +{ +public: + + FcvHistogramLoop_Invoker(const uchar * src_data_, size_t src_step_, int width_, int height_, int32_t* gl_hist_, int stripeHeight_, cv::Mutex* histogramLock, int nStripes_): + cv::ParallelLoopBody(), src_data(src_data_), src_step(src_step_), width(width_), height(height_), gl_hist(gl_hist_), stripeHeight(stripeHeight_), histogramLock_(histogramLock), nStripes(nStripes_) + { + } + + virtual void operator()(const cv::Range& range) const CV_OVERRIDE + { + int height_ = stripeHeight; + if(range.end == nStripes) + height_ += (height % nStripes); + const uchar* yS = src_data; + int32_t l_hist[256] = {0}; + fcvImageIntensityHistogram(yS, src_step, 0, range.start, width, height_, l_hist); + cv::AutoLock lock(*histogramLock_); + + for( int i = 0; i < 256; i++ ) + gl_hist[i] += l_hist[i]; + } + +private: + const uchar * src_data; + const size_t src_step; + const int width; + const int height; + int32_t *gl_hist; + int ret; + int stripeHeight; + cv::Mutex* histogramLock_; + int nStripes; + + FcvHistogramLoop_Invoker(const FcvHistogramLoop_Invoker &); // = delete; + const FcvHistogramLoop_Invoker& operator= (const FcvHistogramLoop_Invoker &); // = delete; +}; + +void calcHist( InputArray _src, OutputArray _hist ) +{ + INITIALIZATION_CHECK; + + CV_Assert(!_src.empty()); + int type = _src.type(); + CV_Assert(type == CV_8UC1); + + _hist.create( cv::Size(256, 1), CV_32SC1 ); + Mat src = _src.getMat(); + Mat hist = _hist.getMat(); + + for( int i = 0; i < 256; i++ ) + hist.ptr()[i] = 0; + + cv::Mutex histogramLockInstance; + + int nStripes = cv::getNumThreads();//(src.cols*src.rows)/(1<<8); + int stripeHeight = src.rows / nStripes; + + cv::parallel_for_(cv::Range(0, nStripes), + FcvHistogramLoop_Invoker(src.data, src.step[0], src.cols, src.rows, hist.ptr(), stripeHeight, &histogramLockInstance, nStripes), nStripes); +} + +} // fastcv:: +} // cv:: diff --git a/modules/fastcv/src/moments.cpp b/modules/fastcv/src/moments.cpp index e40c85a1bc8..38bae771df3 100644 --- a/modules/fastcv/src/moments.cpp +++ b/modules/fastcv/src/moments.cpp @@ -10,11 +10,11 @@ namespace fastcv { cv::Moments moments(InputArray _src, bool binary) { - INITIALIZATION_CHECK; + INITIALIZATION_CHECK; CV_Assert(!_src.empty()); int type = _src.type(); - CV_Assert(type == CV_8UC1 || type == CV_32SC1 || type == CV_32FC1); + CV_Assert(type == CV_8UC1 || type == CV_32SC1 || type == CV_32FC1); Size size = _src.size(); Mat src = _src.getMat(); @@ -22,12 +22,12 @@ cv::Moments moments(InputArray _src, bool binary) cv::Moments m; fcvMoments mFCV; fcvStatus status = FASTCV_SUCCESS; - if(binary) + if(binary) { cv::Mat src_binary(size, CV_8UC1); cv::compare( src, 0, src_binary, cv::CMP_NE ); fcvImageMomentsu8(src_binary.data, src_binary.cols, - src_binary.rows, src_binary.step, &mFCV, binary); + src_binary.rows, src_binary.step[0], &mFCV, binary); } else { @@ -45,30 +45,28 @@ cv::Moments moments(InputArray _src, bool binary) } } - if (status != FASTCV_SUCCESS) + if (status != FASTCV_SUCCESS) { CV_Error( cv::Error::StsError, cv::format("Error occurred!") ); - delete mFCV; return m; } - m.m00 = mFCV->m00; m.m10 = mFCV->m10; m.m01 = mFCV->m01; - m.m20 = mFCV->m20; m.m11 = mFCV->m11; m.m02 = mFCV->m02; - m.m30 = mFCV->m30; m.m21 = mFCV->m21; m.m12 = mFCV->m12; - m.m03 = mFCV->m03; m.mu02 = mFCV->mu02; m.m03 = mFCV->mu03; - m.mu11 = mFCV->mu11; m.mu12 = mFCV->mu12; m.mu20 = mFCV->mu20; - m.mu21 = mFCV->mu21; m.mu30 = mFCV->mu30; + m.m00 = mFCV.m00; m.m10 = mFCV.m10; m.m01 = mFCV.m01; + m.m20 = mFCV.m20; m.m11 = mFCV.m11; m.m02 = mFCV.m02; + m.m30 = mFCV.m30; m.m21 = mFCV.m21; m.m12 = mFCV.m12; + m.m03 = mFCV.m03; m.mu02 = mFCV.mu02; m.m03 = mFCV.mu03; + m.mu11 = mFCV.mu11; m.mu12 = mFCV.mu12; m.mu20 = mFCV.mu20; + m.mu21 = mFCV.mu21; m.mu30 = mFCV.mu30; - float32_t inv_m00 = 1.0/mFCV->m00; - float32_t inv_sqrt_m00 = mFCV->inv_sqrt_m00; + float32_t inv_m00 = 1.0/mFCV.m00; + float32_t inv_sqrt_m00 = mFCV.inv_sqrt_m00; float32_t s2 = inv_m00 * inv_m00, s3 = s2 * inv_sqrt_m00; - m.nu20 = mFCV->mu20 * s2; m.nu11 = mFCV->mu11 * s2; - m.nu02 = mFCV->mu02 * s2; m.nu30 = mFCV->mu30 * s3; - m.nu21 = mFCV->mu21 * s3; m.nu12 = mFCV->mu12 * s3; - m.nu03 = mFCV->mu03 * s3; + m.nu20 = mFCV.mu20 * s2; m.nu11 = mFCV.mu11 * s2; + m.nu02 = mFCV.mu02 * s2; m.nu30 = mFCV.mu30 * s3; + m.nu21 = mFCV.mu21 * s3; m.nu12 = mFCV.mu12 * s3; + m.nu03 = mFCV.mu03 * s3; - delete mFCV; return m; } diff --git a/modules/fastcv/test/test_bilateral.cpp b/modules/fastcv/test/test_bilateral.cpp index 5c883801a92..6ee8e6c409c 100644 --- a/modules/fastcv/test/test_bilateral.cpp +++ b/modules/fastcv/test/test_bilateral.cpp @@ -12,13 +12,13 @@ typedef testing::TestWithParam> fcv_bilateralFilterTest; TEST_P(fcv_bilateralFilterTest, accuracy) { cv::Size size = get<0>(GetParam()); - int d = get<1>(GetParam()); + int d = get<1>(GetParam()); double sigmaColor = get<2>(GetParam()); double sigmaSpace = sigmaColor; RNG& rng = cv::theRNG(); Mat src(size, CV_8UC1); - cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(256)); + cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(255)); cv::Mat dst; diff --git a/modules/fastcv/test/test_moments.cpp b/modules/fastcv/test/test_moments.cpp index 50bbc812554..13e245f749d 100644 --- a/modules/fastcv/test/test_moments.cpp +++ b/modules/fastcv/test/test_moments.cpp @@ -15,19 +15,15 @@ TEST_P(fcv_momentsTest, accuracy) const Size srcSize = get<1>(GetParam()); const MatDepth srcType = get<2>(GetParam()); Mat src(srcSize, srcType); - - for(int j = 0; j < srcSize.width; ++j) - for(int i = 0; i < srcSize.height; ++i) - { - if(srcType == CV_8UC1) - src.at(i, j) = cv::randu(); - else if(srcType == CV_32SC1) - src.at(i, j) = cv::randu(); - else if(srcType == CV_32FC1) - src.at(i, j) = cv::randu(); - } - - cv::Moments m = cv::fastcv::moments(src, binaryImage); + cv::RNG& rng = cv::theRNG(); + if(srcType == CV_8UC1) + rng.fill(src, cv::RNG::UNIFORM, 0, 5); + else if(srcType == CV_32SC1) + rng.fill(src, cv::RNG::UNIFORM, 0, 5); + else if(srcType == CV_32FC1) + rng.fill(src, cv::RNG::UNIFORM, 0.f, 5.f); + + cv::Moments m = cv::fastcv::moments(src, binaryImage); cv::Scalar mean_val, stdDev; float mean_val_fcv = m.m00/(srcSize.width * srcSize.height);