diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index beb2a9ad2d..83ae132e24 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -30,6 +30,7 @@ #include "hal_rvv_1p0/atan.hpp" // core #include "hal_rvv_1p0/split.hpp" // core #include "hal_rvv_1p0/flip.hpp" // core +#include "hal_rvv_1p0/lut.hpp" // core #include "hal_rvv_1p0/pyramids.hpp" // imgproc #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp new file mode 100644 index 0000000000..b5c4008186 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/lut.hpp @@ -0,0 +1,218 @@ +// 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. +#pragma once + +#include +#include +#include + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_lut +#define cv_hal_lut cv::cv_hal_rvv::lut + +// need vlen >= 256 +struct LUTCacheU8 +{ + using ElemType = uchar; + constexpr static size_t elem_size = sizeof(ElemType); + + static inline vuint8m8_t loadTable(const ElemType* lut_data) + { + return __riscv_vle8_v_u8m8(lut_data, 256); + } + + static inline size_t setvl(size_t len) + { + return __riscv_vsetvl_e8m8(len); + } + + static inline size_t setvlmax() + { + return __riscv_vsetvlmax_e8m8(); + } + + static inline void lut(const uchar* src, vuint8m8_t lut_v, ElemType* dst, size_t vl) + { + auto src_v = __riscv_vle8_v_u8m8(src, vl); + auto dst_v = __riscv_vrgather(lut_v, src_v, vl); + __riscv_vse8(dst, dst_v, vl); + } +}; + +// need vlen >= 512 +struct LUTCacheU16 +{ + using ElemType = uint16_t; + constexpr static size_t elem_size = sizeof(ElemType); + + static inline vuint16m8_t loadTable(const ElemType* lut_data) + { + return __riscv_vle16_v_u16m8(lut_data, 256); + } + + static inline size_t setvl(size_t len) + { + return __riscv_vsetvl_e16m8(len); + } + + static inline size_t setvlmax() + { + return __riscv_vsetvlmax_e16m8(); + } + + static inline void lut(const uchar* src, vuint16m8_t lut_v, ElemType* dst, size_t vl) + { + auto src_v = __riscv_vzext_vf2(__riscv_vle8_v_u8m4(src, vl), vl); + auto dst_v = __riscv_vrgather(lut_v, src_v, vl); + __riscv_vse16(dst, dst_v, vl); + } +}; + +// need vlen >= 1024 +struct LUTCacheU32 +{ + using ElemType = uint32_t; + constexpr static size_t elem_size = sizeof(ElemType); + + static inline vuint32m8_t loadTable(const ElemType* lut_data) + { + return __riscv_vle32_v_u32m8(lut_data, 256); + } + + static inline size_t setvl(size_t len) + { + return __riscv_vsetvl_e32m8(len); + } + + static inline size_t setvlmax() + { + return __riscv_vsetvlmax_e32m8(); + } + + static inline void lut(const uchar* src, vuint32m8_t lut_v, ElemType* dst, size_t vl) + { + auto src_v = __riscv_vzext_vf2(__riscv_vle8_v_u8m2(src, vl), vl); + auto dst_v = __riscv_vrgatherei16(lut_v, src_v, vl); + __riscv_vse32(dst, dst_v, vl); + } +}; + +template +class LUTParallelBody : public cv::ParallelLoopBody +{ + using ElemType = typename LUT_TYPE::ElemType; + +public: + const uchar* src_data; + const uchar* lut_data; + uchar* dst_data; + size_t src_step; + size_t dst_step; + size_t width; + + LUTParallelBody(const uchar* src_data, + size_t src_step, + const uchar* lut_data, + uchar* dst_data, + size_t dst_step, + size_t width) : + src_data(src_data), lut_data(lut_data), dst_data(dst_data), src_step(src_step), + dst_step(dst_step), width(width) + { + } + + void operator()(const cv::Range& range) const CV_OVERRIDE + { + auto src = src_data + range.start * src_step; + auto dst = dst_data + range.start * dst_step; + size_t h = range.size(); + size_t w = width; + if (w == src_step && w * LUT_TYPE::elem_size == dst_step) + { + w = w * h; + h = 1; + } + auto lut = LUT_TYPE::loadTable((ElemType*)lut_data); + size_t maxlv = LUT_TYPE::setvlmax(); + for (; h; h--, src += src_step, dst += dst_step) + { + size_t vl = maxlv; + size_t l = w; + auto s = src; + auto d = (ElemType*)dst; + for (; l >= vl; l -= vl, s += vl, d += vl) + { + LUT_TYPE::lut(s, lut, d, vl); + } + for (; l > 0; l -= vl, s += vl, d += vl) + { + vl = LUT_TYPE::setvl(l); + LUT_TYPE::lut(s, lut, d, vl); + } + } + } + +private: + LUTParallelBody(const LUTParallelBody&); + LUTParallelBody& operator=(const LUTParallelBody&); +}; + +inline int lut(const uchar* src_data, + size_t src_step, + size_t src_type, + const uchar* lut_data, + size_t lut_channel_size, + size_t lut_channels, + uchar* dst_data, + size_t dst_step, + int width, + int height) +{ + if (width <= 0 || height <= 0) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + size_t w = width; + size_t h = height; + size_t vlen = __riscv_vsetvlmax_e8m8(); + if (lut_channels == 1) + { + w *= CV_MAT_CN(src_type); + + // Actually, vlen 128 can use four u8m4 vectors to load the whole table, gather and merge + // the result, but the performance is almost the same as the scalar. + if (lut_channel_size == 1 && vlen >= 256) + { + LUTParallelBody body(src_data, src_step, lut_data, dst_data, dst_step, w); + Range all(0, height); + if (w * h >= (1 << 18)) + cv::parallel_for_(all, body); + else + body(all); + return CV_HAL_ERROR_OK; + } + else if (lut_channel_size == 2 && vlen >= 512) + { + LUTParallelBody body(src_data, src_step, lut_data, dst_data, dst_step, w); + Range all(0, height); + if (w * h >= (1 << 18)) + cv::parallel_for_(all, body); + else + body(all); + return CV_HAL_ERROR_OK; + } + else if (lut_channel_size == 4 && vlen >= 1024) + { + LUTParallelBody body(src_data, src_step, lut_data, dst_data, dst_step, w); + Range all(0, height); + if (w * h >= (1 << 18)) + cv::parallel_for_(all, body); + else + body(all); + return CV_HAL_ERROR_OK; + } + } + return CV_HAL_ERROR_NOT_IMPLEMENTED; +} + +}} // namespace cv::cv_hal_rvv diff --git a/modules/core/perf/perf_lut.cpp b/modules/core/perf/perf_lut.cpp index 0b791e8aad..6f185ac782 100644 --- a/modules/core/perf/perf_lut.cpp +++ b/modules/core/perf/perf_lut.cpp @@ -24,4 +24,23 @@ PERF_TEST_P( SizePrm, LUT, SANITY_CHECK(dst, 0.1); } +PERF_TEST_P( SizePrm, LUT_multi, + testing::Values(szQVGA, szVGA, sz1080p) + ) +{ + Size sz = GetParam(); + + int maxValue = 255; + + Mat src(sz, CV_8UC3); + randu(src, 0, maxValue); + Mat lut(1, 256, CV_8UC1); + randu(lut, 0, maxValue); + Mat dst(sz, CV_8UC3); + + TEST_CYCLE() LUT(src, lut, dst); + + SANITY_CHECK_NOTHING(); +} + }} // namespace diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index 137f30a13c..721cf70b6d 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -3225,10 +3225,10 @@ CV_ENUM(LutMatType, CV_8U, CV_16U, CV_16F, CV_32S, CV_32F, CV_64F) struct Core_LUT: public testing::TestWithParam { - template + template cv::Mat referenceWithType(cv::Mat input, cv::Mat table) { - cv::Mat ref(input.size(), CV_MAKE_TYPE(table.type(), ch)); + cv::Mat ref(input.size(), CV_MAKE_TYPE(table.depth(), ch)); for (int i = 0; i < input.rows; i++) { for (int j = 0; j < input.cols; j++) @@ -3242,7 +3242,14 @@ struct Core_LUT: public testing::TestWithParam Vec val; for (int k = 0; k < ch; k++) { - val[k] = table.at(input.at>(i, j)[k]); + if (same_cn) + { + val[k] = table.at>(input.at>(i, j)[k])[k]; + } + else + { + val[k] = table.at(input.at>(i, j)[k]); + } } ref.at>(i, j) = val; } @@ -3251,32 +3258,32 @@ struct Core_LUT: public testing::TestWithParam return ref; } - template + template cv::Mat reference(cv::Mat input, cv::Mat table) { - if (table.type() == CV_8U) + if (table.depth() == CV_8U) { - return referenceWithType(input, table); + return referenceWithType(input, table); } - else if (table.type() == CV_16U) + else if (table.depth() == CV_16U) { - return referenceWithType(input, table); + return referenceWithType(input, table); } - else if (table.type() == CV_16F) + else if (table.depth() == CV_16F) { - return referenceWithType(input, table); + return referenceWithType(input, table); } - else if (table.type() == CV_32S) + else if (table.depth() == CV_32S) { - return referenceWithType(input, table); + return referenceWithType(input, table); } - else if (table.type() == CV_32F) + else if (table.depth() == CV_32F) { - return referenceWithType(input, table); + return referenceWithType(input, table); } - else if (table.type() == CV_64F) + else if (table.depth() == CV_64F) { - return referenceWithType(input, table); + return referenceWithType(input, table); } return cv::Mat(); @@ -3290,7 +3297,7 @@ TEST_P(Core_LUT, accuracy) randu(input, 0, 256); cv::Mat table(1, 256, CV_MAKE_TYPE(type, 1)); - randu(table, 0, 127); + randu(table, 0, getMaxVal(type)); cv::Mat output; cv::LUT(input, table, output); @@ -3307,7 +3314,7 @@ TEST_P(Core_LUT, accuracy_multi) randu(input, 0, 256); cv::Mat table(1, 256, CV_MAKE_TYPE(type, 1)); - randu(table, 0, 127); + randu(table, 0, getMaxVal(type)); cv::Mat output; cv::LUT(input, table, output); @@ -3317,6 +3324,22 @@ TEST_P(Core_LUT, accuracy_multi) ASSERT_EQ(0, cv::norm(output, gt, cv::NORM_INF)); } +TEST_P(Core_LUT, accuracy_multi2) +{ + int type = (int)GetParam(); + cv::Mat input(117, 113, CV_8UC3); + randu(input, 0, 256); + + cv::Mat table(1, 256, CV_MAKE_TYPE(type, 3)); + randu(table, 0, getMaxVal(type)); + + cv::Mat output; + cv::LUT(input, table, output); + + cv::Mat gt = reference<3, true>(input, table); + + ASSERT_EQ(0, cv::norm(output, gt, cv::NORM_INF)); +} INSTANTIATE_TEST_CASE_P(/**/, Core_LUT, LutMatType::all());