diff --git a/apps/interactive-calibration/frameProcessor.cpp b/apps/interactive-calibration/frameProcessor.cpp index 6004cb71ed..5ef950757c 100644 --- a/apps/interactive-calibration/frameProcessor.cpp +++ b/apps/interactive-calibration/frameProcessor.cpp @@ -309,7 +309,10 @@ cv::Mat CalibProcessor::processFrame(const cv::Mat &frame) { cv::Mat frameCopy; cv::Mat frameCopyToSave; - frame.copyTo(frameCopy); + if (frame.channels() == 1) + cv::cvtColor(frame, frameCopy, cv::COLOR_GRAY2BGR); + else + frame.copyTo(frameCopy); bool isTemplateFound = false; mCurrentImagePoints.clear(); diff --git a/doc/py_tutorials/py_setup/py_intro/py_intro.markdown b/doc/py_tutorials/py_setup/py_intro/py_intro.markdown index ca05a8643d..21af3b7a8e 100644 --- a/doc/py_tutorials/py_setup/py_intro/py_intro.markdown +++ b/doc/py_tutorials/py_setup/py_intro/py_intro.markdown @@ -66,7 +66,7 @@ As new modules are added to OpenCV-Python, this tutorial will have to be expande familiar with a particular algorithm and can write up a tutorial including basic theory of the algorithm and code showing example usage, please do so. -Remember, we **together** can make this project a great success !!! +Remember,**together** we can make this project a great success !!! Contributors ------------ diff --git a/hal/riscv-rvv/CMakeLists.txt b/hal/riscv-rvv/CMakeLists.txt index a0c9e628b3..82cbcbb4c6 100644 --- a/hal/riscv-rvv/CMakeLists.txt +++ b/hal/riscv-rvv/CMakeLists.txt @@ -17,7 +17,8 @@ endif() target_include_directories(${HAL_LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/modules/core/include - ${CMAKE_SOURCE_DIR}/modules/imgproc/include) # ${CMAKE_SOURCE_DIR}/modules/features2d/include + ${CMAKE_SOURCE_DIR}/modules/imgproc/include + ${CMAKE_SOURCE_DIR}/modules/features/include) set(RVV_HAL_FOUND TRUE CACHE INTERNAL "") set(RVV_HAL_VERSION "0.0.1" CACHE INTERNAL "") diff --git a/hal/riscv-rvv/include/features2d.hpp b/hal/riscv-rvv/include/features2d.hpp new file mode 100644 index 0000000000..6e16b618a4 --- /dev/null +++ b/hal/riscv-rvv/include/features2d.hpp @@ -0,0 +1,26 @@ +// 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. + +#ifndef OPENCV_RVV_HAL_FEATURES2D_HPP +#define OPENCV_RVV_HAL_FEATURES2D_HPP + +struct cvhalFilter2D; + +namespace cv { namespace rvv_hal { namespace features2d { + +#if CV_HAL_RVV_1P0_ENABLED + +int FAST(const uchar* src_data, size_t src_step, int width, int height, + void** keypoints_data, size_t* keypoints_count, + int threshold, bool nonmax_suppression, int detector_type, void* (*realloc_func)(void*, size_t)); + +#undef cv_hal_FASTv2 +#define cv_hal_FASTv2 cv::rvv_hal::features2d::FAST + +#endif // CV_HAL_RVV_1P0_ENABLED + + +}}} // cv::rvv_hal::features2d + +#endif // OPENCV_RVV_HAL_IMGPROC_HPP diff --git a/hal/riscv-rvv/rvv_hal.hpp b/hal/riscv-rvv/rvv_hal.hpp index 88989aaeb8..327f0af1e4 100644 --- a/hal/riscv-rvv/rvv_hal.hpp +++ b/hal/riscv-rvv/rvv_hal.hpp @@ -27,5 +27,6 @@ #include "include/types.hpp" #include "include/core.hpp" #include "include/imgproc.hpp" +#include "include/features2d.hpp" #endif // OPENCV_HAL_RVV_HPP_INCLUDED diff --git a/hal/riscv-rvv/src/features2d/common.hpp b/hal/riscv-rvv/src/features2d/common.hpp new file mode 100644 index 0000000000..65ef4f935e --- /dev/null +++ b/hal/riscv-rvv/src/features2d/common.hpp @@ -0,0 +1,23 @@ +// 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) 2025, SpaceMIT Inc., all rights reserved. +// Copyright (C) 2025, Institute of Software, Chinese Academy of Sciences. +// Third party copyrights are property of their respective owners. + +#ifndef OPENCV_HAL_RVV_FEATURES2D_COMMON_HPP_INCLUDED +#define OPENCV_HAL_RVV_FEATURES2D_COMMON_HPP_INCLUDED + +#include +#include "opencv2/features/hal/interface.h" + +namespace cv { namespace rvv_hal { namespace features2d { namespace common { + +#if CV_HAL_RVV_1P0_ENABLED + +#endif // CV_HAL_RVV_1P0_ENABLED + +}}}} // cv::rvv_hal::core::common + +#endif // OPENCV_HAL_RVV_CORE_COMMON_HPP_INCLUDED diff --git a/hal/riscv-rvv/src/features2d/fast.cpp b/hal/riscv-rvv/src/features2d/fast.cpp new file mode 100644 index 0000000000..fbe5813c59 --- /dev/null +++ b/hal/riscv-rvv/src/features2d/fast.cpp @@ -0,0 +1,256 @@ +#include "rvv_hal.hpp" +#include "common.hpp" +#include + +namespace cv { namespace rvv_hal { namespace features2d { + +static inline uint8_t cornerScore(const uint8_t* ptr, const int* pixel) +{ + constexpr int K = 8, N = 16 + K + 1; + int v = ptr[0]; + int16_t d[32] = {0}; + for (int k = 0; k < N; k++) + d[k] = (int16_t)(v - ptr[pixel[k]]); + auto vlenb = __riscv_vlenb(); + switch (vlenb) { + #define CV_RVV_HAL_FAST_CORNERSOCRE16_CASE(lmul) \ + size_t vl = __riscv_vsetvl_e16m##lmul(N); \ + vint16m##lmul##_t vd = __riscv_vle16_v_i16m##lmul(d, vl); \ + vint16m##lmul##_t q0 = __riscv_vmv_v_x_i16m##lmul((int16_t)(-1000), vl); \ + vint16m##lmul##_t q1 = __riscv_vmv_v_x_i16m##lmul((int16_t)(1000), vl); \ + vint16m##lmul##_t vds = vd, ak0 = vd, bk0 = vd; \ + for (int i = 0; i < 8; i++) { \ + vds = __riscv_vslide1down(vds, 0, vl); \ + ak0 = __riscv_vmin(ak0, vds, vl); \ + bk0 = __riscv_vmax(bk0, vds, vl); \ + } \ + q0 = __riscv_vmax(q0, __riscv_vmin(ak0, vd, vl), vl); \ + q1 = __riscv_vmin(q1, __riscv_vmax(bk0, vd, vl), vl); \ + vds = __riscv_vslide1down(vds, 0, vl); \ + q0 = __riscv_vmax(q0, __riscv_vmin(ak0, vds, vl), vl); \ + q1 = __riscv_vmin(q1, __riscv_vmax(bk0, vds, vl), vl); \ + q0 = __riscv_vmax(q0, __riscv_vrsub(q1, 0, vl), vl); \ + return (uint8_t)(__riscv_vmv_x(__riscv_vredmax(q0, __riscv_vmv_s_x_i16m1(0, vl), vl)) - 1); + case 16: { // 128-bit + CV_RVV_HAL_FAST_CORNERSOCRE16_CASE(4) + } break; + case 32: { // 256-bit + CV_RVV_HAL_FAST_CORNERSOCRE16_CASE(2) + } break; + default: { // >=512-bit + CV_RVV_HAL_FAST_CORNERSOCRE16_CASE(1) + } + } +} + + +inline int fast_16(const uchar* src_data, size_t src_step, + int width, int height, + std::vector &keypoints, + int threshold, bool nonmax_suppression) +{ + + constexpr int patternSize = 16; + constexpr int K = patternSize/2, N = patternSize + K + 1; + constexpr int quarterPatternSize = patternSize/4; + + int i, j, k; + int pixel[N] = {0}; + pixel[0] = 0 + (int)src_step * 3; + pixel[1] = 1 + (int)src_step * 3; + pixel[2] = 2 + (int)src_step * 2; + pixel[3] = 3 + (int)src_step * 1; + pixel[4] = 3 + (int)src_step * 0; + pixel[5] = 3 + (int)src_step * -1; + pixel[6] = 2 + (int)src_step * -2; + pixel[7] = 1 + (int)src_step * -3; + pixel[8] = 0 + (int)src_step * -3; + pixel[9] = -1 + (int)src_step * -3; + pixel[10] = -2 + (int)src_step * -2; + pixel[11] = -3 + (int)src_step * -1; + pixel[12] = -3 + (int)src_step * 0; + pixel[13] = -3 + (int)src_step * 1; + pixel[14] = -2 + (int)src_step * 2; + pixel[15] = -1 + (int)src_step * 3; + for (k = 16; k < N; k++) + { + pixel[k] = pixel[k - 16]; + } + + std::vector _buf((width+16)*3*(sizeof(ptrdiff_t) + sizeof(uchar)) + 128); + uchar* buf[3]; + buf[0] = &_buf[0]; buf[1] = buf[0] + width; buf[2] = buf[1] + width; + ptrdiff_t* cpbuf[3]; + cpbuf[0] = (ptrdiff_t*)alignPtr(buf[2] + width, sizeof(ptrdiff_t)) + 1; + cpbuf[1] = cpbuf[0] + width + 1; + cpbuf[2] = cpbuf[1] + width + 1; + memset(buf[0], 0, width*3); + + int vlmax = __riscv_vsetvlmax_e8m4(); + vuint8m4_t v_c_delta = __riscv_vmv_v_x_u8m4(0x80, vlmax); + vuint8m4_t v_c_threshold = __riscv_vmv_v_x_u8m4((char) threshold, vlmax); + vuint8m4_t v_c_k = __riscv_vmv_v_x_u8m4((uint8_t)K, vlmax); + vuint8m4_t v_c_zero = __riscv_vmv_v_x_u8m4(0, vlmax); + + for( i = 3; i < height - 2; i++) + { + + const uchar* ptr = src_data + i * src_step + 3; + uchar* curr = buf[(i - 3)%3]; + ptrdiff_t* cornerpos = cpbuf[(i - 3)%3]; + memset(curr, 0, width); + ptrdiff_t ncorners = 0; + + if( i < height - 3 ) + { + j = 3; + { + int margin = width - 3; + int vl = __riscv_vsetvl_e8m4(margin - j); + for (; j < margin; j += vl, ptr += vl) + { + vl = __riscv_vsetvl_e8m4(margin - j); + vuint8m4_t v_pixels = __riscv_vle8_v_u8m4(ptr, vl); + // pixels add threshold + vuint8m4_t v_pat = __riscv_vsaddu(v_pixels, v_c_threshold, vl); + // pixels sub threshold + vuint8m4_t v_pst = __riscv_vssubu(v_pixels, v_c_threshold, vl); + vint8m4_t v0 = __riscv_vreinterpret_i8m4(__riscv_vxor(v_pat, v_c_delta, vl)); + vint8m4_t v1 = __riscv_vreinterpret_i8m4(__riscv_vxor(v_pst, v_c_delta, vl)); + + v_pixels = __riscv_vle8_v_u8m4(ptr + pixel[0], vl); + vint8m4_t x0 = __riscv_vreinterpret_i8m4(__riscv_vsub(v_pixels, v_c_delta, vl)); + v_pixels = __riscv_vle8_v_u8m4(ptr + pixel[quarterPatternSize], vl); + vint8m4_t x1 = __riscv_vreinterpret_i8m4(__riscv_vsub(v_pixels, v_c_delta, vl)); + v_pixels = __riscv_vle8_v_u8m4(ptr + pixel[2*quarterPatternSize], vl); + vint8m4_t x2 = __riscv_vreinterpret_i8m4(__riscv_vsub(v_pixels, v_c_delta, vl)); + v_pixels = __riscv_vle8_v_u8m4(ptr + pixel[3*quarterPatternSize], vl); + vint8m4_t x3 = __riscv_vreinterpret_i8m4(__riscv_vsub(v_pixels, v_c_delta, vl)); + + vbool2_t m0, m1; + m0 = __riscv_vmand(__riscv_vmslt(v0, x0, vl), __riscv_vmslt(v0, x1, vl), vl); + m1 = __riscv_vmand(__riscv_vmslt(x0, v1, vl), __riscv_vmslt(x1, v1, vl), vl); + m0 = __riscv_vmor(m0, __riscv_vmand(__riscv_vmslt(v0, x1, vl), __riscv_vmslt(v0, x2, vl), vl), vl); + m1 = __riscv_vmor(m1, __riscv_vmand(__riscv_vmslt(x1, v1, vl), __riscv_vmslt(x2, v1, vl), vl), vl); + m0 = __riscv_vmor(m0, __riscv_vmand(__riscv_vmslt(v0, x2, vl), __riscv_vmslt(v0, x3, vl), vl), vl); + m1 = __riscv_vmor(m1, __riscv_vmand(__riscv_vmslt(x2, v1, vl), __riscv_vmslt(x3, v1, vl), vl), vl); + m0 = __riscv_vmor(m0, __riscv_vmand(__riscv_vmslt(v0, x3, vl), __riscv_vmslt(v0, x0, vl), vl), vl); + m1 = __riscv_vmor(m1, __riscv_vmand(__riscv_vmslt(x3, v1, vl), __riscv_vmslt(x0, v1, vl), vl), vl); + m0 = __riscv_vmor(m0, m1, vl); + + unsigned long mask_cnt = __riscv_vcpop(m0, vl); + if(!mask_cnt) + continue; + + // TODO: Test if skipping to the first possible key point pixel if faster + // Memory access maybe expensive since the data is not aligned + // long first_set = __riscv_vfirst(m0, vl); + // if( first_set == -1 ) { + // j -= first_set; + // ptr -= first_set; + // } + + vuint8m4_t c0 = __riscv_vmv_v_x_u8m4(0, vl); + vuint8m4_t c1 = __riscv_vmv_v_x_u8m4(0, vl); + vuint8m4_t max0 = __riscv_vmv_v_x_u8m4(0, vl); + vuint8m4_t max1 = __riscv_vmv_v_x_u8m4(0, vl); + + for( k = 0; k < N; k++ ) + { + vint8m4_t x = __riscv_vreinterpret_i8m4(__riscv_vxor(__riscv_vle8_v_u8m4(ptr + pixel[k], vl), v_c_delta, vl)); + + m0 = __riscv_vmslt(v0, x, vl); + m1 = __riscv_vmslt(x, v1, vl); + + c0 = __riscv_vadd_mu(m0, c0, c0, (uint8_t)1, vl); + c1 = __riscv_vadd_mu(m1, c1, c1, (uint8_t)1, vl); + c0 = __riscv_vmerge(v_c_zero, c0, m0, vl); + c1 = __riscv_vmerge(v_c_zero, c1, m1, vl); + + max0 = __riscv_vmaxu(max0, c0, vl); + max1 = __riscv_vmaxu(max1, c1, vl); + } + + vbool2_t v_comparek = __riscv_vmsltu(v_c_k, __riscv_vmaxu(max0, max1, vl), vl); + uint8_t m[64]; + __riscv_vse8(m, __riscv_vreinterpret_u8m1(v_comparek), vl); + + for( k = 0; k < vl; k++ ) + { + if( (m[k / 8] >> (k % 8)) & 1 ) + { + cornerpos[ncorners++] = j + k; + if(nonmax_suppression) { + curr[j + k] = (uchar)cornerScore(ptr + k, pixel); + } + } + } + } + } + } + + cornerpos[-1] = ncorners; + + if( i == 3 ) continue; + + const uchar* prev = buf[(i - 4 + 3)%3]; + const uchar* pprev = buf[(i - 5 + 3)%3]; + cornerpos = cpbuf[(i - 4 + 3)%3]; // cornerpos[-1] is used to store a value + ncorners = cornerpos[-1]; + for( k = 0; k < ncorners; k++ ) + { + j = cornerpos[k]; + int score = prev[j]; + if(!nonmax_suppression || + (score > prev[j+1] && score > prev[j-1] && + score > pprev[j-1] && score > pprev[j] && score > pprev[j+1] && + score > curr[j-1] && score > curr[j] && score > curr[j+1]) ) + { + cvhalKeyPoint kp; + kp.x = (float)j; + kp.y = (float)(i-1); + kp.size = 7.f; + kp.angle = -1.f; + kp.response = (float)score; + kp.octave = 0; // Not used in FAST + kp.class_id = -1; // Not used in FAST + keypoints.push_back(kp); + } + } + } + return CV_HAL_ERROR_OK; +} + +int FAST(const uchar* src_data, size_t src_step, + int width, int height, void** keypoints_data, + size_t* keypoints_count, int threshold, + bool nonmax_suppression, int detector_type, void* (*realloc_func)(void*, size_t)) +{ + int res = CV_HAL_ERROR_UNKNOWN; + switch(detector_type) { + case CV_HAL_TYPE_5_8: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + case CV_HAL_TYPE_7_12: + return CV_HAL_ERROR_NOT_IMPLEMENTED; + case CV_HAL_TYPE_9_16: { + std::vector keypoints; + res = fast_16(src_data, src_step, width, height, keypoints, threshold, nonmax_suppression); + if (res == CV_HAL_ERROR_OK) { + if (keypoints.size() > *keypoints_count) { + *keypoints_count = keypoints.size(); + uchar *tmp = (uchar*)realloc_func(*keypoints_data, sizeof(cvhalKeyPoint)*(*keypoints_count)); + memcpy(tmp, (uchar*)keypoints.data(), sizeof(cvhalKeyPoint)*(*keypoints_count)); + *keypoints_data = tmp; + } else { + *keypoints_count = keypoints.size(); + memcpy(*keypoints_data, (uchar*)keypoints.data(), sizeof(cvhalKeyPoint)*(*keypoints_count)); + } + } + return res; + } + default: + return res; + } +} + +}}} // namespace cv::rvv_hal::features2d diff --git a/modules/core/src/cuda_info.cpp b/modules/core/src/cuda_info.cpp index 2558ec8ea5..d97fadd4ec 100644 --- a/modules/core/src/cuda_info.cpp +++ b/modules/core/src/cuda_info.cpp @@ -424,7 +424,9 @@ int cv::cuda::DeviceInfo::clockRate() const #ifndef HAVE_CUDA throw_no_cuda(); #else - return deviceProps().get(device_id_)->clockRate; + int clockRate; + cudaSafeCall(cudaDeviceGetAttribute(&clockRate, cudaDevAttrClockRate, device_id_)); + return clockRate; #endif } @@ -487,7 +489,9 @@ bool cv::cuda::DeviceInfo::kernelExecTimeoutEnabled() const #ifndef HAVE_CUDA throw_no_cuda(); #else - return deviceProps().get(device_id_)->kernelExecTimeoutEnabled != 0; + int kernelExecTimeoutEnabled; + cudaSafeCall(cudaDeviceGetAttribute(&kernelExecTimeoutEnabled, cudaDevAttrKernelExecTimeout, device_id_)); + return kernelExecTimeoutEnabled != 0; #endif } @@ -522,7 +526,9 @@ DeviceInfo::ComputeMode cv::cuda::DeviceInfo::computeMode() const ComputeModeExclusiveProcess }; - return tbl[deviceProps().get(device_id_)->computeMode]; + int computeMode; + cudaSafeCall(cudaDeviceGetAttribute(&computeMode, cudaDevAttrComputeMode, device_id_)); + return tbl[computeMode]; #endif } @@ -554,7 +560,14 @@ int cv::cuda::DeviceInfo::maxTexture1DLinear() const #ifndef HAVE_CUDA throw_no_cuda(); #else - return deviceProps().get(device_id_)->maxTexture1DLinear; + #if CUDA_VERSION >= 13000 + size_t maxWidthInElements; + cudaChannelFormatDesc fmtDesc = cudaCreateChannelDesc(); + cudaSafeCall(cudaDeviceGetTexture1DLinearMaxWidth(&maxWidthInElements, &fmtDesc, device_id_)); + return maxWidthInElements; + #else + return deviceProps().get(device_id_)->maxTexture1DLinear; + #endif #endif } @@ -793,7 +806,9 @@ int cv::cuda::DeviceInfo::memoryClockRate() const #ifndef HAVE_CUDA throw_no_cuda(); #else - return deviceProps().get(device_id_)->memoryClockRate; + int memoryClockRate; + cudaSafeCall(cudaDeviceGetAttribute(&memoryClockRate, cudaDevAttrMemoryClockRate, device_id_)); + return memoryClockRate; #endif } @@ -933,7 +948,9 @@ void cv::cuda::printCudaDeviceInfo(int device) if (cores > 0) printf(" (%2d) Multiprocessors x (%2d) CUDA Cores/MP: %d CUDA Cores\n", prop.multiProcessorCount, cores, cores * prop.multiProcessorCount); - printf(" GPU Clock Speed: %.2f GHz\n", prop.clockRate * 1e-6f); + int clockRate; + cudaSafeCall(cudaDeviceGetAttribute(&clockRate, cudaDevAttrClockRate, dev)); + printf(" GPU Clock Speed: %.2f GHz\n", clockRate * 1e-6f); printf(" Max Texture Dimension Size (x,y,z) 1D=(%d), 2D=(%d,%d), 3D=(%d,%d,%d)\n", prop.maxTexture1D, prop.maxTexture2D[0], prop.maxTexture2D[1], @@ -952,8 +969,10 @@ void cv::cuda::printCudaDeviceInfo(int device) printf(" Maximum memory pitch: %u bytes\n", (int)prop.memPitch); printf(" Texture alignment: %u bytes\n", (int)prop.textureAlignment); - printf(" Concurrent copy and execution: %s with %d copy engine(s)\n", (prop.deviceOverlap ? "Yes" : "No"), prop.asyncEngineCount); - printf(" Run time limit on kernels: %s\n", prop.kernelExecTimeoutEnabled ? "Yes" : "No"); + printf(" Concurrent copy and execution: %s with %d copy engine(s)\n", (prop.asyncEngineCount ? "Yes" : "No"), prop.asyncEngineCount); + int kernelExecTimeoutEnabled; + cudaSafeCall(cudaDeviceGetAttribute(&kernelExecTimeoutEnabled, cudaDevAttrKernelExecTimeout, dev)); + printf(" Run time limit on kernels: %s\n", kernelExecTimeoutEnabled ? "Yes" : "No"); printf(" Integrated GPU sharing Host Memory: %s\n", prop.integrated ? "Yes" : "No"); printf(" Support host page-locked memory mapping: %s\n", prop.canMapHostMemory ? "Yes" : "No"); @@ -963,8 +982,11 @@ void cv::cuda::printCudaDeviceInfo(int device) printf(" Device is using TCC driver mode: %s\n", prop.tccDriver ? "Yes" : "No"); printf(" Device supports Unified Addressing (UVA): %s\n", prop.unifiedAddressing ? "Yes" : "No"); printf(" Device PCI Bus ID / PCI location ID: %d / %d\n", prop.pciBusID, prop.pciDeviceID ); + + int propComputeMode; + cudaSafeCall(cudaDeviceGetAttribute(&propComputeMode, cudaDevAttrComputeMode, dev)); printf(" Compute Mode:\n"); - printf(" %s \n", computeMode[prop.computeMode]); + printf(" %s \n", computeMode[propComputeMode]); } printf("\n"); diff --git a/modules/features/perf/perf_fast.cpp b/modules/features/perf/perf_fast.cpp new file mode 100644 index 0000000000..31da48b8f1 --- /dev/null +++ b/modules/features/perf/perf_fast.cpp @@ -0,0 +1,43 @@ +#include "perf_precomp.hpp" +#include "perf_feature2d.hpp" + +namespace opencv_test +{ +using namespace perf; + +typedef tuple Fast_Params_t; +typedef perf::TestBaseWithParam Fast_Params; + +PERF_TEST_P(Fast_Params, detect, + testing::Combine( + testing::Values(20,30,100), // threshold + testing::Values( + // (int)FastFeatureDetector::TYPE_5_8, + // (int)FastFeatureDetector::TYPE_7_12, + (int)FastFeatureDetector::TYPE_9_16 // detector_type + ), + testing::Bool(), // nonmaxSuppression + testing::Values("cv/inpaint/orig.png", + "cv/cameracalibration/chess9.png") + )) +{ + int threshold_p = get<0>(GetParam()); + int type_p = get<1>(GetParam()); + bool nonmaxSuppression_p = get<2>(GetParam()); + string filename = getDataPath(get<3>(GetParam())); + + Mat img = imread(filename, IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()) << "Failed to load image: " << filename; + + vector keypoints; + + declare.in(img); + TEST_CYCLE() + { + FAST(img, keypoints, threshold_p, nonmaxSuppression_p, (FastFeatureDetector::DetectorType)type_p); + } + + SANITY_CHECK_NOTHING(); +} + +} // namespace opencv_test diff --git a/modules/features/src/fast.cpp b/modules/features/src/fast.cpp index 793999073b..a85965732f 100644 --- a/modules/features/src/fast.cpp +++ b/modules/features/src/fast.cpp @@ -429,17 +429,31 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool { CV_INSTRUMENT_REGION(); + const size_t max_fast_features = std::max(_img.total()/100, size_t(1000)); // Simple heuristic that depends on resolution. + CV_OCL_RUN(_img.isUMat() && type == FastFeatureDetector::TYPE_9_16, - ocl_FAST(_img, keypoints, threshold, nonmax_suppression, 10000)); + ocl_FAST(_img, keypoints, threshold, nonmax_suppression, (int)max_fast_features)); cv::Mat img = _img.getMat(); CALL_HAL(fast_dense, hal_FAST, img, keypoints, threshold, nonmax_suppression, type); - size_t keypoints_count = 10000; + size_t keypoints_count = 1; keypoints.clear(); - keypoints.resize(keypoints_count); - CALL_HAL(fast, cv_hal_FAST, img.data, img.step, img.cols, img.rows, - (uchar*)(keypoints.data()), &keypoints_count, threshold, nonmax_suppression, type); + KeyPoint* kps = (KeyPoint*)malloc(sizeof(KeyPoint) * keypoints_count); + int hal_ret = cv_hal_FASTv2(img.data, img.step, img.cols, img.rows, (void**)&kps, + &keypoints_count, threshold, nonmax_suppression, type, realloc); + if (hal_ret == CV_HAL_ERROR_OK) { + keypoints.assign(kps, kps + keypoints_count); + free(kps); + return; + } else { + free(kps); + keypoints_count = max_fast_features; + keypoints.clear(); + keypoints.resize(keypoints_count); + CALL_HAL(fast, cv_hal_FAST, img.data, img.step, img.cols, img.rows, + (uchar*)(keypoints.data()), &keypoints_count, threshold, nonmax_suppression, type); + } switch(type) { case FastFeatureDetector::TYPE_5_8: diff --git a/modules/features/src/hal_replacement.hpp b/modules/features/src/hal_replacement.hpp index 44ddbc51b8..7053f939a5 100644 --- a/modules/features/src/hal_replacement.hpp +++ b/modules/features/src/hal_replacement.hpp @@ -104,8 +104,24 @@ inline int hal_ni_FAST_NMS(const uchar* src_data, size_t src_step, uchar* dst_da */ inline int hal_ni_FAST(const uchar* src_data, size_t src_step, int width, int height, uchar* keypoints_data, size_t* keypoints_count, int threshold, bool nonmax_suppression, int /*cv::FastFeatureDetector::DetectorType*/ type) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } +/** + @brief Detects corners using the FAST algorithm. + @param src_data Source image data + @param src_step Source image step + @param width Source image width + @param height Source image height + @param keypoints_data Pointer to keypoints + @param keypoints_count Count of keypoints + @param threshold Threshold for keypoint + @param nonmax_suppression Indicates if make nonmaxima suppression or not. + @param type FAST type + @param realloc_func function for reallocation +*/ +inline int hal_ni_FASTv2(const uchar* src_data, size_t src_step, int width, int height, void** keypoints_data, size_t* keypoints_count, int threshold, bool nonmax_suppression, int /*cv::FastFeatureDetector::DetectorType*/ type, void* (*realloc_func)(void*, size_t)) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } + //! @cond IGNORED #define cv_hal_FAST hal_ni_FAST +#define cv_hal_FASTv2 hal_ni_FASTv2 //! @endcond //! @} diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index a2cab7b8c2..09b4a2715c 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -105,16 +105,16 @@ enum ImwriteFlags { IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used. IMWRITE_HDR_COMPRESSION = (5 << 4) + 0 /* 80 */, //!< specify HDR compression IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format - IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values + IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set. See ImwriteTiffResolutionUnitFlags. Default is IMWRITE_TIFF_RESOLUTION_UNIT_INCH. IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI IMWRITE_TIFF_YDPI = 258,//!< For TIFF, use to specify the Y direction DPI IMWRITE_TIFF_COMPRESSION = 259,//!< For TIFF, use to specify the image compression scheme. See cv::ImwriteTiffCompressionFlags. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default. IMWRITE_TIFF_ROWSPERSTRIP = 278,//!< For TIFF, use to specify the number of rows per strip. - IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. + IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. Default is IMWRITE_TIFF_PREDICTOR_HORIZONTAL . IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000. IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95. IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8. - IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and (fastest). Default is 9. + IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and 10(fastest). Default is 9. IMWRITE_JPEGXL_QUALITY = 640,//!< For JPEG XL, it can be a quality from 0 to 100 (the higher is the better). Default value is 95. If set, distance parameter is re-calicurated from quality level automatically. This parameter request libjxl v0.10 or later. IMWRITE_JPEGXL_EFFORT = 641,//!< For JPEG XL, encoder effort/speed level without affecting decoding speed; it is between 1 (fastest) and 10 (slowest). Default is 7. IMWRITE_JPEGXL_DISTANCE = 642,//!< For JPEG XL, distance level for lossy compression: target max butteraugli distance, lower = higher quality, 0 = lossless; range: 0 .. 25. Default is 1. @@ -175,7 +175,12 @@ enum ImwriteTiffPredictorFlags { IMWRITE_TIFF_PREDICTOR_NONE = 1, //!< no prediction scheme used IMWRITE_TIFF_PREDICTOR_HORIZONTAL = 2, //!< horizontal differencing IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT = 3 //!< floating point predictor +}; +enum ImwriteTiffResolutionUnitFlags { + IMWRITE_TIFF_RESOLUTION_UNIT_NONE = 1, //!< no absolute unit of measurement. + IMWRITE_TIFF_RESOLUTION_UNIT_INCH = 2, //!< inch + IMWRITE_TIFF_RESOLUTION_UNIT_CENTIMETER = 3, //!< centimeter }; enum ImwriteEXRTypeFlags { diff --git a/modules/imgcodecs/perf/perf_decode_encode.cpp b/modules/imgcodecs/perf/perf_decode_encode.cpp index 5f675f7a44..bdef7882ee 100644 --- a/modules/imgcodecs/perf/perf_decode_encode.cpp +++ b/modules/imgcodecs/perf/perf_decode_encode.cpp @@ -7,10 +7,82 @@ namespace opencv_test { -#ifdef HAVE_PNG - using namespace perf; +static Animation makeCirclesAnimation(Size size = Size(320, 240), int type = CV_8UC4, int nbits = 8, int frameCount = 40) +{ + struct AnimatedCircle { + cv::Point2f pos; + cv::Point2f velocity; + float radius; + float radius_speed; + cv::Scalar color; + cv::Scalar border_color; + }; + + const int numCircles = 80; + const int maxval = (1 << nbits) - 1; + + cv::RNG rng = theRNG(); + std::vector circles; + Animation animation; + + // Initialize animated circles + for (int i = 0; i < numCircles; ++i) { + AnimatedCircle c; + c.pos = cv::Point2f(rng.uniform(0.f, (float)size.width), + rng.uniform(0.f, (float)size.height)); + c.velocity = cv::Point2f(rng.uniform(-2.f, 2.f), + rng.uniform(-2.f, 2.f)); + c.radius = rng.uniform(10.f, 40.f); + c.radius_speed = rng.uniform(-0.5f, 0.5f); + c.color = cv::Scalar(rng.uniform(0, maxval), + rng.uniform(0, maxval), + rng.uniform(0, maxval), + rng.uniform(230, maxval)); + c.border_color = c.color; + circles.push_back(c); + } + + // Generate frames + for (int frame = 0; frame < frameCount; ++frame) { + cv::Mat img(size, type, cv::Scalar(20, 0, 10, 128)); + + for (size_t i = 0; i < circles.size(); ++i) { + AnimatedCircle& c = circles[i]; + + // Update position + c.pos += c.velocity; + + // Bounce on edges + if (c.pos.x < 0 || c.pos.x > size.width) c.velocity.x *= -1; + if (c.pos.y < 0 || c.pos.y > size.height) c.velocity.y *= -1; + + // Update radius + c.radius += c.radius_speed; + if (c.radius < 10.f || c.radius > 80.f) { + c.radius_speed *= -1; + c.radius = std::max(10.f, std::min(c.radius, 80.f)); + } + + c.color = c.color - Scalar(c.velocity.x, 0, c.velocity.y, rng.uniform(1, 4)); + + // Draw + cv::circle(img, c.pos, (int)c.radius, c.color, cv::FILLED, cv::LINE_AA); + cv::circle(img, c.pos, (int)c.radius, c.border_color, 1, cv::LINE_AA); + } + + animation.frames.push_back(img); + animation.durations.push_back(20); // milliseconds + } + + for (int i = (int)animation.frames.size() - 1; i >= 0; --i) { + animation.frames.push_back(animation.frames[i].clone()); + animation.durations.push_back(15); + } + return animation; +} + typedef perf::TestBaseWithParam Decode; typedef perf::TestBaseWithParam Encode; @@ -32,7 +104,9 @@ const string exts[] = { #ifdef HAVE_JPEGXL ".jxl", #endif +#ifdef HAVE_PNG ".png", +#endif #ifdef HAVE_IMGCODEC_PXM ".ppm", #endif @@ -54,7 +128,9 @@ const string exts_multi[] = { #ifdef HAVE_IMGCODEC_GIF ".gif", #endif +#ifdef HAVE_PNG ".png", +#endif #ifdef HAVE_TIFF ".tiff", #endif @@ -63,6 +139,23 @@ const string exts_multi[] = { #endif }; +const string exts_anim[] = { +#ifdef HAVE_AVIF + ".avif", +#endif +#ifdef HAVE_IMGCODEC_GIF + ".gif", +#endif +#ifdef HAVE_PNG + ".png", +#endif +#ifdef HAVE_WEBP + ".webp", +#endif +}; + +#ifdef HAVE_PNG + PERF_TEST_P(Decode, bgr, testing::ValuesIn(exts)) { String filename = getDataPath("perf/1920x1080.png"); @@ -126,6 +219,63 @@ PERF_TEST_P(Encode, multi, testing::ValuesIn(exts_multi)) SANITY_CHECK_NOTHING(); } + #endif // HAVE_PNG +PERF_TEST_P(Encode, animation, testing::ValuesIn(exts_anim)) +{ + Animation animation = makeCirclesAnimation(); + + TEST_CYCLE() + { + vector buf; + EXPECT_TRUE(imencodeanimation(GetParam().c_str(), animation, buf)); + } + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Encode, multi_page, testing::ValuesIn(exts_multi)) +{ + Animation animation = makeCirclesAnimation(); + + TEST_CYCLE() + { + vector buf; + EXPECT_TRUE(imencodemulti(GetParam().c_str(), animation.frames, buf)); + } + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Decode, animation, testing::ValuesIn(exts_anim)) +{ + Animation animation = makeCirclesAnimation(); + vector buf; + ASSERT_TRUE(imencodeanimation(GetParam().c_str(), animation, buf)); + + TEST_CYCLE() + { + Animation tmp_animation; + EXPECT_TRUE(imdecodeanimation(buf, tmp_animation)); + } + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Decode, multi_page, testing::ValuesIn(exts_multi)) +{ + Animation animation = makeCirclesAnimation(); + vector buf; + ASSERT_TRUE(imencodemulti(GetParam().c_str(), animation.frames, buf)); + + TEST_CYCLE() + { + vector tmp_frames; + EXPECT_TRUE(imdecodemulti(buf, IMREAD_UNCHANGED, tmp_frames)); + } + + SANITY_CHECK_NOTHING(); +} + } // namespace diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp index c1b86362e0..35b4411b3b 100644 --- a/modules/imgcodecs/src/grfmt_avif.cpp +++ b/modules/imgcodecs/src/grfmt_avif.cpp @@ -319,6 +319,7 @@ AvifEncoder::AvifEncoder() { m_support_metadata[(size_t)IMAGE_METADATA_XMP] = true; m_support_metadata[(size_t)IMAGE_METADATA_ICCP] = true; encoder_ = avifEncoderCreate(); + m_supported_encode_key = { IMWRITE_AVIF_QUALITY, IMWRITE_AVIF_DEPTH, IMWRITE_AVIF_SPEED }; } AvifEncoder::~AvifEncoder() { @@ -334,9 +335,13 @@ bool AvifEncoder::writeanimation(const Animation& animation, int bit_depth = 8; int speed = AVIF_SPEED_FASTEST; for (size_t i = 0; i < params.size(); i += 2) { + const int value = params[i + 1]; if (params[i] == IMWRITE_AVIF_QUALITY) { - const int quality = std::min(std::max(params[i + 1], AVIF_QUALITY_WORST), + const int quality = std::min(std::max(value, AVIF_QUALITY_WORST), AVIF_QUALITY_BEST); + if (value != quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_QUALITY must be between 0 to 100. It is fallbacked to %d", value, quality)); + } #if CV_AVIF_USE_QUALITY encoder_->quality = quality; #else @@ -346,9 +351,17 @@ bool AvifEncoder::writeanimation(const Animation& animation, AVIF_QUANTIZER_WORST_QUALITY; #endif } else if (params[i] == IMWRITE_AVIF_DEPTH) { - bit_depth = params[i + 1]; + bit_depth = value; + if ((bit_depth != 8) && (bit_depth !=10) && (bit_depth !=12)) + { + bit_depth = 8; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_DEPTH must be 8, 10 or 12. It is fallbacked to %d", value, bit_depth)); + } } else if (params[i] == IMWRITE_AVIF_SPEED) { - speed = params[i + 1]; + speed = std::min(std::max(value,0),10); + if (value != speed) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_SPEED must be between 0 to 10. It is fallbacked to %d", value, speed)); + } } } diff --git a/modules/imgcodecs/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp index 9c9dead740..0cd372ec28 100644 --- a/modules/imgcodecs/src/grfmt_base.cpp +++ b/modules/imgcodecs/src/grfmt_base.cpp @@ -155,6 +155,14 @@ bool BaseImageEncoder::isFormatSupported( int depth ) const return depth == CV_8U; } +bool BaseImageEncoder::isValidEncodeKey(const int key) const +{ + auto first = m_supported_encode_key.begin(); + auto last = m_supported_encode_key.end(); + + return (std::find(first, last, key) != last); +} + String BaseImageEncoder::getDescription() const { return m_description; diff --git a/modules/imgcodecs/src/grfmt_base.hpp b/modules/imgcodecs/src/grfmt_base.hpp index 889fdd0a1d..3fec6055c2 100644 --- a/modules/imgcodecs/src/grfmt_base.hpp +++ b/modules/imgcodecs/src/grfmt_base.hpp @@ -224,6 +224,13 @@ public: */ virtual bool isFormatSupported(int depth) const; + /** + * @brief Validates if the encoder supports a specific key. + * @param key The key of the encode parameter. + * @return true if key is supported for this encoder, false otherwise. + */ + virtual bool isValidEncodeKey(const int key) const; + /** * @brief Set the destination for encoding as a file. * @param filename The name of the file to which the image will be written. @@ -290,6 +297,7 @@ protected: std::vector* m_buf; ///< Pointer to the buffer for encoded data if using memory-based destination. bool m_buf_supported; ///< Flag indicating whether buffer-based encoding is supported. String m_last_error; ///< Stores the last error message encountered during encoding. + std::vector m_supported_encode_key; ///< Supported encode key list }; } diff --git a/modules/imgcodecs/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp index 1fd1de1557..a727b36a2f 100644 --- a/modules/imgcodecs/src/grfmt_exr.cpp +++ b/modules/imgcodecs/src/grfmt_exr.cpp @@ -719,6 +719,7 @@ ImageDecoder ExrDecoder::newDecoder() const ExrEncoder::ExrEncoder() { m_description = "OpenEXR Image files (*.exr)"; + m_supported_encode_key = {IMWRITE_EXR_TYPE, IMWRITE_EXR_COMPRESSION, IMWRITE_EXR_DWA_COMPRESSION_LEVEL}; } @@ -745,9 +746,10 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_EXR_TYPE ) { - switch( params[i+1] ) + switch( value ) { case IMWRITE_EXR_TYPE_HALF: type = HALF; @@ -756,12 +758,14 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) type = FLOAT; break; default: - CV_Error(Error::StsBadArg, "IMWRITE_EXR_TYPE is invalid or not supported"); + type = FLOAT; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_TYPE must be one of ImwriteEXRTypeFlags. It is fallbacked to IMWRITE_EXR_TYPE_FLOAT", value)); + break; } } if ( params[i] == IMWRITE_EXR_COMPRESSION ) { - switch ( params[i + 1] ) + switch ( value ) { case IMWRITE_EXR_COMPRESSION_NO: header.compression() = NO_COMPRESSION; @@ -799,7 +803,9 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) break; #endif default: - CV_Error(Error::StsBadArg, "IMWRITE_EXR_COMPRESSION is invalid or not supported"); + header.compression() = ZIP_COMPRESSION; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_COMPRESSION must be one of ImwriteEXRCompressionFlags. It is fallbacked to IMWRITE_EXR_COMPRESSION_ZIP", value)); + break; } } if (params[i] == IMWRITE_EXR_DWA_COMPRESSION_LEVEL) @@ -809,7 +815,11 @@ bool ExrEncoder::write( const Mat& img, const std::vector& params ) #elif OPENEXR_VERSION_MAJOR < 3 CV_LOG_ONCE_WARNING(NULL, "Setting `IMWRITE_EXR_DWA_COMPRESSION_LEVEL` not supported in OpenEXR version " + std::to_string(OPENEXR_VERSION_MAJOR) + " (version 3 is required)"); #else - header.dwaCompressionLevel() = params[i + 1]; + // See https://github.com/AcademySoftwareFoundation/openexr/blob/v3.3.5/src/lib/OpenEXR/ImfDwaCompressor.cpp#L85 + header.dwaCompressionLevel() = std::max(params[i + 1], 0); + if(value < 0) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_DWA_COMPRESSION_LEVEL must be 0 or more. It is fallbacked to 0", value)); + } #endif } } diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 1954e2d501..f5307ca39b 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -554,6 +554,8 @@ GifEncoder::GifEncoder() { colorNum = 256; // the number of colors in the color table, default 256 dithering = 0; // the level dithering, default 0 globalColorTableSize = 256, localColorTableSize = 0; + + m_supported_encode_key = {IMWRITE_GIF_QUALITY, IMWRITE_GIF_DITHER, IMWRITE_GIF_TRANSPARENCY, IMWRITE_GIF_COLORTABLE}; } GifEncoder::~GifEncoder() { @@ -576,6 +578,7 @@ bool GifEncoder::writeanimation(const Animation& animation, const std::vector& params ) int compression = IMWRITE_HDR_COMPRESSION_RLE; for (size_t i = 0; i + 1 < params.size(); i += 2) { + const int value = params[i+1]; switch (params[i]) { case IMWRITE_HDR_COMPRESSION: - compression = params[i + 1]; + switch(value) + { + case IMWRITE_HDR_COMPRESSION_NONE: + case IMWRITE_HDR_COMPRESSION_RLE: + compression = value; + break; + default: + compression = IMWRITE_HDR_COMPRESSION_RLE; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_HDR_COMPRESSION must be one of ImwriteHDRCompressionFlags. It is fallbacked to IMWRITE_HDR_COMPRESSION_RLE", value)); + break; + } break; default: break; diff --git a/modules/imgcodecs/src/grfmt_jpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg.cpp index 810ae1d63b..499036cedf 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg.cpp @@ -616,6 +616,7 @@ JpegEncoder::JpegEncoder() m_support_metadata[(size_t)IMAGE_METADATA_EXIF] = true; m_support_metadata[(size_t)IMAGE_METADATA_XMP] = true; m_support_metadata[(size_t)IMAGE_METADATA_ICCP] = true; + m_supported_encode_key = {IMWRITE_JPEG_QUALITY, IMWRITE_JPEG_PROGRESSIVE, IMWRITE_JPEG_OPTIMIZE, IMWRITE_JPEG_RST_INTERVAL, IMWRITE_JPEG_LUMA_QUALITY, IMWRITE_JPEG_CHROMA_QUALITY, IMWRITE_JPEG_SAMPLING_FACTOR}; } @@ -724,27 +725,39 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_JPEG_QUALITY ) { - quality = params[i+1]; - quality = MIN(MAX(quality, 0), 100); + quality = MIN(MAX(value, 0), 100); + if(value != quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_QUALITY must be between 0 to 100. It is fallbacked to %d", value, quality)); + } } if( params[i] == IMWRITE_JPEG_PROGRESSIVE ) { - progressive = params[i+1]; + progressive = MIN(MAX(value,0), 1); + if(value != progressive) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_PROGRESSIVE must be 0 or 1. It is fallbacked to %d", value, progressive)); + } } if( params[i] == IMWRITE_JPEG_OPTIMIZE ) { - optimize = params[i+1]; + optimize = MIN(MAX(value,0), 1); + if(value != optimize) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_OPTIMIZE must be 0 or 1. It is fallbacked to %d", value, optimize)); + } } if( params[i] == IMWRITE_JPEG_LUMA_QUALITY ) { - if (params[i+1] >= 0) + if (value >= 0) { - luma_quality = MIN(MAX(params[i+1], 0), 100); + luma_quality = MIN(MAX(value, 0), 100); + if(value != luma_quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_LUMA_QUALITY must be between 0 to 100. It is fallbacked to %d.", value, luma_quality)); + } quality = luma_quality; @@ -752,6 +765,8 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) { chroma_quality = luma_quality; } + } else { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_LUMA_QUALITY must be between 0 to 100. It is ignored.", value)); } } @@ -759,19 +774,26 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) { if (params[i+1] >= 0) { - chroma_quality = MIN(MAX(params[i+1], 0), 100); + chroma_quality = MIN(MAX(value, 0), 100); + if(value != chroma_quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_CHROMA_QUALITY must be between 0 to 100. It is fallbacked to %d.", value, chroma_quality)); + } + } else { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_CHROMA_QUALITY must be between 0 to 100. It is ignored.", value)); } } if( params[i] == IMWRITE_JPEG_RST_INTERVAL ) { - rst_interval = params[i+1]; - rst_interval = MIN(MAX(rst_interval, 0), 65535L); + rst_interval = MIN(MAX(value, 0), 65535L); + if(value != rst_interval) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_RST_INTERVAL must be between 0 to 65535. It is fallbacked to %d.", value, rst_interval)); + } } if( params[i] == IMWRITE_JPEG_SAMPLING_FACTOR ) { - sampling_factor = static_cast(params[i+1]); + sampling_factor = static_cast(value); switch ( sampling_factor ) { @@ -784,7 +806,7 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) break; default: - CV_LOG_WARNING(NULL, cv::format("Unknown value for IMWRITE_JPEG_SAMPLING_FACTOR: 0x%06x", sampling_factor ) ); + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_SAMPLING_FACTOR must be one of ImwriteJPEGSamplingFactorParams. It is fallbacked to IMWRITE_JPEG_SAMPLING_FACTOR_420.", value)); sampling_factor = 0; break; } diff --git a/modules/imgcodecs/src/grfmt_jpeg2000.cpp b/modules/imgcodecs/src/grfmt_jpeg2000.cpp index 9b8680ac1f..9611012c32 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000.cpp @@ -493,6 +493,7 @@ bool Jpeg2KDecoder::readComponent16u( unsigned short *data, void *_buffer, Jpeg2KEncoder::Jpeg2KEncoder() { m_description = "JPEG-2000 files (*.jp2)"; + m_supported_encode_key = {IMWRITE_JPEG2000_COMPRESSION_X1000}; } @@ -526,10 +527,17 @@ bool Jpeg2KEncoder::write( const Mat& _img, const std::vector& params ) double target_compression_rate = 1.0; for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; switch(params[i]) { case cv::IMWRITE_JPEG2000_COMPRESSION_X1000: - target_compression_rate = std::min(std::max(params[i+1], 0), 1000) / 1000.0; + { + const int compression = std::min(std::max(value, 0), 1000); + target_compression_rate = static_cast(compression) / 1000.0; + if(value != compression){ + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG2000_COMPRESSION_X1000 must be between 0 to 1000. It is fallbacked to %d", value, compression)); + } + } break; } } diff --git a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp index 70832277ef..047a522eca 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp @@ -330,14 +330,20 @@ opj_cparameters setupEncoderParameters(const std::vector& params) bool rate_is_specified = false; for (size_t i = 0; i < params.size(); i += 2) { + const int value = params[i + 1]; switch (params[i]) { case cv::IMWRITE_JPEG2000_COMPRESSION_X1000: - parameters.tcp_rates[0] = 1000.f / std::min(std::max(params[i + 1], 1), 1000); - rate_is_specified = true; + { + const int compression = std::min(std::max(value, 1), 1000); + parameters.tcp_rates[0] = 1000.f / static_cast(compression); + if(value != compression) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG2000_COMPRESSION_X1000 must be between 1 to 1000. It is fallbacked to 1", value)); + } + rate_is_specified = true; + } break; default: - CV_LOG_WARNING(NULL, "OpenJPEG2000(encoder): skip unsupported parameter: " << params[i]); break; } } @@ -685,6 +691,7 @@ ImageDecoder Jpeg2KJ2KOpjDecoder::newDecoder() const Jpeg2KOpjEncoder::Jpeg2KOpjEncoder() { m_description = "JPEG-2000 files (*.jp2)"; + m_supported_encode_key = {IMWRITE_JPEG2000_COMPRESSION_X1000}; } ImageEncoder Jpeg2KOpjEncoder::newEncoder() const diff --git a/modules/imgcodecs/src/grfmt_jpegxl.cpp b/modules/imgcodecs/src/grfmt_jpegxl.cpp index 72118184f6..0d8feb878a 100644 --- a/modules/imgcodecs/src/grfmt_jpegxl.cpp +++ b/modules/imgcodecs/src/grfmt_jpegxl.cpp @@ -473,6 +473,7 @@ JpegXLEncoder::JpegXLEncoder() { m_description = "JPEG XL files (*.jxl)"; m_buf_supported = true; + m_supported_encode_key = {IMWRITE_JPEGXL_QUALITY, IMWRITE_JPEGXL_EFFORT, IMWRITE_JPEGXL_DISTANCE, IMWRITE_JPEGXL_DECODING_SPEED}; } JpegXLEncoder::~JpegXLEncoder() @@ -522,11 +523,14 @@ bool JpegXLEncoder::write(const Mat& img, const std::vector& params) float distance = -1.0; // Negative means not set for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_JPEGXL_QUALITY ) { #if JPEGXL_MAJOR_VERSION > 0 || JPEGXL_MINOR_VERSION >= 10 - int quality = params[i+1]; - quality = MIN(MAX(quality, 0), 100); + const int quality = MIN(MAX(value, 0), 100); + if(value != quality) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEGXL_QUALITY must be between 0 to 100, It is fallbacked to %d", value, quality)); + } distance = JxlEncoderDistanceFromQuality(static_cast(quality)); #else CV_LOG_ONCE_WARNING(NULL, "Quality parameter is supported with libjxl v0.10.0 or later"); @@ -534,8 +538,10 @@ bool JpegXLEncoder::write(const Mat& img, const std::vector& params) } if( params[i] == IMWRITE_JPEGXL_DISTANCE ) { - int distanceInt = params[i+1]; - distanceInt = MIN(MAX(distanceInt, 0), 25); + const int distanceInt = MIN(MAX(value, 0), 25); + if(value != distanceInt) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEGXL_DISTANCE must be between 0 to 25, It is fallbacked to %d", value, distanceInt)); + } distance = static_cast(distanceInt); } } diff --git a/modules/imgcodecs/src/grfmt_pam.cpp b/modules/imgcodecs/src/grfmt_pam.cpp index 2c15ab244c..12371e1f5d 100644 --- a/modules/imgcodecs/src/grfmt_pam.cpp +++ b/modules/imgcodecs/src/grfmt_pam.cpp @@ -55,6 +55,7 @@ #include "utils.hpp" #include "grfmt_pam.hpp" +#include "opencv2/core/utils/logger.hpp" namespace cv { @@ -657,6 +658,7 @@ PAMEncoder::PAMEncoder() { m_description = "Portable arbitrary format (*.pam)"; m_buf_supported = true; + m_supported_encode_key = {IMWRITE_PAM_TUPLETYPE}; } @@ -689,12 +691,25 @@ bool PAMEncoder::write( const Mat& img, const std::vector& params ) int x, y, tmp, bufsize = 256; /* parse save file type */ - for( size_t i = 0; i < params.size(); i += 2 ) + for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_PAM_TUPLETYPE ) { - if ( params[i+1] > IMWRITE_PAM_FORMAT_NULL && - params[i+1] < (int) PAM_FORMATS_NO) - fmt = &formats[params[i+1]]; + switch(value) { + case IMWRITE_PAM_FORMAT_NULL: + case IMWRITE_PAM_FORMAT_BLACKANDWHITE: + case IMWRITE_PAM_FORMAT_GRAYSCALE: + case IMWRITE_PAM_FORMAT_GRAYSCALE_ALPHA: + case IMWRITE_PAM_FORMAT_RGB: + case IMWRITE_PAM_FORMAT_RGB_ALPHA: + fmt = &formats[value]; + break; + default: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PAM_TUPLETYPE must be one of ImwritePAMFlags. It is ignored", value)); + fmt = nullptr; + break; + } } + } if( m_buf ) { diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index 4d3d8f9d8d..9a93f68b70 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -913,6 +913,7 @@ PngEncoder::PngEncoder() memset(palette, 0, sizeof(palette)); memset(trns, 0, sizeof(trns)); memset(op, 0, sizeof(op)); + m_supported_encode_key = {IMWRITE_PNG_COMPRESSION, IMWRITE_PNG_STRATEGY, IMWRITE_PNG_BILEVEL, IMWRITE_PNG_FILTER, IMWRITE_PNG_ZLIBBUFFER_SIZE}; } PngEncoder::~PngEncoder() @@ -983,26 +984,61 @@ bool PngEncoder::write( const Mat& img, const std::vector& params ) for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; switch (params[i]) { case IMWRITE_PNG_COMPRESSION: m_compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy - m_compression_level = params[i+1]; - m_compression_level = MIN(MAX(m_compression_level, 0), Z_BEST_COMPRESSION); + m_compression_level = MIN(MAX(value, 0), Z_BEST_COMPRESSION); + if(value != m_compression_level) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_COMPRESSION must be between 0 to 9. It is fallbacked to %d", value, m_compression_level)); + } set_compression_level = true; break; case IMWRITE_PNG_STRATEGY: - m_compression_strategy = params[i+1]; - m_compression_strategy = MIN(MAX(m_compression_strategy, 0), Z_FIXED); + { + switch(value) { + case IMWRITE_PNG_STRATEGY_DEFAULT: + case IMWRITE_PNG_STRATEGY_FILTERED: + case IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY: + case IMWRITE_PNG_STRATEGY_RLE: + case IMWRITE_PNG_STRATEGY_FIXED: + m_compression_strategy = value; + break; + default: + m_compression_strategy = IMWRITE_PNG_STRATEGY_RLE; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_STRATEGY must be one of ImwritePNGFlags. It is fallbacked to IMWRITE_PNG_STRATEGY_RLE", value)); + break; + } + } break; case IMWRITE_PNG_BILEVEL: - m_isBilevel = params[i+1] != 0; + m_isBilevel = value != 0; + if((value != 0) && (value != 1)) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_BILEVEL must be 0 or 1. It is fallbacked to 1", value )); + } break; case IMWRITE_PNG_FILTER: - m_filter = params[i+1]; + { + switch(value) { + case IMWRITE_PNG_FILTER_NONE: + case IMWRITE_PNG_FILTER_SUB: + case IMWRITE_PNG_FILTER_UP: + case IMWRITE_PNG_FILTER_AVG: + case IMWRITE_PNG_FILTER_PAETH: + case IMWRITE_PNG_FAST_FILTERS: + case IMWRITE_PNG_ALL_FILTERS: + m_filter = value; + break; + default: + m_filter = IMWRITE_PNG_FILTER_SUB; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_FILTER must be one of ImwritePNGFilterFlags. It is fallbacked to IMWRITE_PNG_FILTER_SUB", value )); + break; + } + } set_filter = true; break; @@ -1011,11 +1047,16 @@ bool PngEncoder::write( const Mat& img, const std::vector& params ) // The minimum limit is 6, which is from from https://github.com/opencv/opencv/blob/4.12.0/3rdparty/libpng/pngset.c#L1600 . // The maximum limit is 1 MiB, which has been provisionally set. libpng limitation is 2 GiB(INT32_MAX), but it is too large. // For normal use, 128 or 256 KiB may be sufficient. See https://zlib.net/zlib_how.html . - png_set_compression_buffer_size(png_ptr, MIN(MAX(params[i+1],6), 1024*1024)); + { + const int zlen = MIN(MAX(value, 6), 1024*1024); + if(value != zlen) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_ZLIBBUFFER_SIZE must be between 6 to 1024*1024. It is fallbacked to %d", value , zlen)); + } + png_set_compression_buffer_size(png_ptr, zlen); + } break; default: - CV_LOG_WARNING(NULL, "An unknown or unsupported ImwriteFlags value was specified and has been ignored."); break; } } diff --git a/modules/imgcodecs/src/grfmt_pxm.cpp b/modules/imgcodecs/src/grfmt_pxm.cpp index 20c815e833..3ddab22b56 100644 --- a/modules/imgcodecs/src/grfmt_pxm.cpp +++ b/modules/imgcodecs/src/grfmt_pxm.cpp @@ -389,6 +389,7 @@ PxMEncoder::PxMEncoder(PxMMode mode) : CV_Error(Error::StsInternal, ""); } m_buf_supported = true; + m_supported_encode_key = {IMWRITE_PXM_BINARY}; } PxMEncoder::~PxMEncoder() @@ -414,8 +415,14 @@ bool PxMEncoder::write(const Mat& img, const std::vector& params) for( size_t i = 0; i < params.size(); i += 2 ) { + const int value = params[i+1]; if( params[i] == IMWRITE_PXM_BINARY ) - isBinary = params[i+1] != 0; + { + isBinary = value != 0; + if((value != 0) && (value != 1)) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PXM_BINARY must be 0 or 1. It is fallbacked to 1", value)); + } + } } int mode = mode_; diff --git a/modules/imgcodecs/src/grfmt_spng.cpp b/modules/imgcodecs/src/grfmt_spng.cpp index e2f6f02bb9..1311757b44 100644 --- a/modules/imgcodecs/src/grfmt_spng.cpp +++ b/modules/imgcodecs/src/grfmt_spng.cpp @@ -482,6 +482,7 @@ SPngEncoder::SPngEncoder() m_support_metadata[IMAGE_METADATA_EXIF] = true; m_support_metadata[IMAGE_METADATA_XMP] = true; m_support_metadata[IMAGE_METADATA_ICCP] = true; + m_supported_encode_key = {IMWRITE_PNG_COMPRESSION, IMWRITE_PNG_STRATEGY, IMWRITE_PNG_BILEVEL, IMWRITE_PNG_FILTER, IMWRITE_PNG_ZLIBBUFFER_SIZE}; } SPngEncoder::~SPngEncoder() @@ -534,25 +535,56 @@ bool SPngEncoder::write(const Mat &img, const std::vector ¶ms) for (size_t i = 0; i < params.size(); i += 2) { + const int value = params[i+1]; if (params[i] == IMWRITE_PNG_COMPRESSION) { compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy - compression_level = params[i + 1]; - compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION); + compression_level = MIN(MAX(value, 0), Z_BEST_COMPRESSION); + if(value != m_compression_level) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_COMPRESSION must be between 0 to 9. It is fallbacked to %d", value, m_compression_level)); + } set_compression_level = true; } if (params[i] == IMWRITE_PNG_STRATEGY) { - compression_strategy = params[i + 1]; - compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED); + switch(value) { + case IMWRITE_PNG_STRATEGY_DEFAULT: + case IMWRITE_PNG_STRATEGY_FILTERED: + case IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY: + case IMWRITE_PNG_STRATEGY_RLE: + case IMWRITE_PNG_STRATEGY_FIXED: + compression_strategy = value; + break; + default: + compression_strategy = IMWRITE_PNG_STRATEGY_RLE: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_STRATEGY must be one of ImwritePNGFlags. It is fallbacked to IMWRITE_PNG_STRATEGY_RLE", value)); + break; + } } if (params[i] == IMWRITE_PNG_BILEVEL) { - isBilevel = params[i + 1] != 0; + isBilevel = value != 0; + if((value != 0) && (value != 1)) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_BILEVEL must be 0 or 1. It is fallbacked to 1", value )); + } } if( params[i] == IMWRITE_PNG_FILTER ) { - filter = params[i+1]; + switch(value) { + case IMWRITE_PNG_FILTER_NONE: + case IMWRITE_PNG_FILTER_SUB: + case IMWRITE_PNG_FILTER_UP: + case IMWRITE_PNG_FILTER_AVG: + case IMWRITE_PNG_FILTER_PAETH: + case IMWRITE_PNG_FAST_FILTERS: + case IMWRITE_PNG_ALL_FILTERS: + filter = value; + break; + default: + filter = IMWRITE_PNG_FILTER_SUB; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_FILTER must be one of ImwritePNGFilterFlags. It is fallbacked to IMWRITE_PNG_FILTER_SUB", value )); + break; + } set_filter = true; } if( params[i] == IMWRITE_PNG_ZLIBBUFFER_SIZE ) diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index 99f1840fbf..3d8d6a4603 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -1090,6 +1090,7 @@ TiffEncoder::TiffEncoder() { m_description = "TIFF Files (*.tiff;*.tif)"; m_buf_supported = true; + m_supported_encode_key = {IMWRITE_TIFF_RESUNIT, IMWRITE_TIFF_XDPI, IMWRITE_TIFF_YDPI, IMWRITE_TIFF_COMPRESSION, IMWRITE_TIFF_ROWSPERSTRIP, IMWRITE_TIFF_PREDICTOR}; } TiffEncoder::~TiffEncoder() @@ -1229,15 +1230,92 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect cv::Ptr tif_cleanup(tif, cv_tiffCloseHandle); //Settings that matter to all images - int compression = COMPRESSION_LZW; - int predictor = PREDICTOR_HORIZONTAL; + int compression = IMWRITE_TIFF_COMPRESSION_LZW; + int predictor = IMWRITE_TIFF_PREDICTOR_HORIZONTAL; int resUnit = -1, dpiX = -1, dpiY = -1; - readParam(params, IMWRITE_TIFF_COMPRESSION, compression); - readParam(params, IMWRITE_TIFF_PREDICTOR, predictor); - readParam(params, IMWRITE_TIFF_RESUNIT, resUnit); - readParam(params, IMWRITE_TIFF_XDPI, dpiX); - readParam(params, IMWRITE_TIFF_YDPI, dpiY); + if(readParam(params, IMWRITE_TIFF_COMPRESSION, compression)) + { + switch(compression) { + case IMWRITE_TIFF_COMPRESSION_NONE: + case IMWRITE_TIFF_COMPRESSION_CCITTRLE: + case IMWRITE_TIFF_COMPRESSION_CCITTFAX3: // IMWRITE_TIFF_COMPRESSION_CCITT_T4: + case IMWRITE_TIFF_COMPRESSION_CCITTFAX4: // IMWRITE_TIFF_COMPRESSION_CCITT_T6: + case IMWRITE_TIFF_COMPRESSION_LZW: + case IMWRITE_TIFF_COMPRESSION_OJPEG: + case IMWRITE_TIFF_COMPRESSION_JPEG: + case IMWRITE_TIFF_COMPRESSION_T85: + case IMWRITE_TIFF_COMPRESSION_T43: + case IMWRITE_TIFF_COMPRESSION_NEXT: + case IMWRITE_TIFF_COMPRESSION_CCITTRLEW: + case IMWRITE_TIFF_COMPRESSION_PACKBITS: + case IMWRITE_TIFF_COMPRESSION_THUNDERSCAN: + case IMWRITE_TIFF_COMPRESSION_IT8CTPAD: + case IMWRITE_TIFF_COMPRESSION_IT8LW: + case IMWRITE_TIFF_COMPRESSION_IT8MP: + case IMWRITE_TIFF_COMPRESSION_IT8BL: + case IMWRITE_TIFF_COMPRESSION_PIXARFILM: + case IMWRITE_TIFF_COMPRESSION_PIXARLOG: + case IMWRITE_TIFF_COMPRESSION_DEFLATE: + case IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE: + case IMWRITE_TIFF_COMPRESSION_DCS: + case IMWRITE_TIFF_COMPRESSION_JBIG: + case IMWRITE_TIFF_COMPRESSION_SGILOG: + case IMWRITE_TIFF_COMPRESSION_SGILOG24: + case IMWRITE_TIFF_COMPRESSION_JP2000: + case IMWRITE_TIFF_COMPRESSION_LERC: + case IMWRITE_TIFF_COMPRESSION_LZMA: + case IMWRITE_TIFF_COMPRESSION_ZSTD: + case IMWRITE_TIFF_COMPRESSION_WEBP: + case IMWRITE_TIFF_COMPRESSION_JXL: + break; + default: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_COMPRESSION must be one of ImwriteTiffCompressionFlags. It is fallbacked to IMWRITE_TIFF_COMPRESSION_LZW", compression)); + compression = IMWRITE_TIFF_COMPRESSION_LZW; + break; + } + } + if(readParam(params, IMWRITE_TIFF_PREDICTOR, predictor)) + { + switch(predictor) { + case IMWRITE_TIFF_PREDICTOR_NONE: + case IMWRITE_TIFF_PREDICTOR_HORIZONTAL: + case IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT: + break; + default: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_PREDICTOR must be one of ImwriteTiffPredictorFlags. It is fallbacked to IMWRITE_TIFF_PREDICTOR_HORIZONTAL", predictor)); + predictor = IMWRITE_TIFF_PREDICTOR_HORIZONTAL; + break; + } + } + if(readParam(params, IMWRITE_TIFF_RESUNIT, resUnit)) + { + switch(resUnit) { + case IMWRITE_TIFF_RESOLUTION_UNIT_NONE: + case IMWRITE_TIFF_RESOLUTION_UNIT_INCH: + case IMWRITE_TIFF_RESOLUTION_UNIT_CENTIMETER: + break; + default: + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_RESUNIT must be one of ImwriteTiffResolutionUnitFlags. It is fallbacked to IMWRITE_TIFF_RESOLUTION_UNIT_INCH", resUnit)); + resUnit = IMWRITE_TIFF_RESOLUTION_UNIT_INCH; + break; + } + } + + if(readParam(params, IMWRITE_TIFF_XDPI, dpiX)) + { + if(dpiX <= 0) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_XDPI must be positive. It is ignored.", dpiX)); + dpiX = -1; + } + } + if(readParam(params, IMWRITE_TIFF_YDPI, dpiY)) + { + if(dpiY <= 0) { + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_YDPI must be positive. It is ignored.", dpiX)); + dpiY = -1; + } + } //Iterate through each image in the vector and write them out as Tiff directories for (size_t page = 0; page < img_vec.size(); page++) @@ -1260,8 +1338,7 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PAGENUMBER, page, img_vec.size())); } - int compression_param = -1; // OPENCV_FUTURE - if (type == CV_32FC3 && (!readParam(params, IMWRITE_TIFF_COMPRESSION, compression_param) || compression_param == COMPRESSION_SGILOG)) + if (type == CV_32FC3 && compression == COMPRESSION_SGILOG) { if (!write_32FC3_SGILOG(img, tif)) return false; diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index c82750020a..ae11c7ce2c 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -338,6 +338,7 @@ WebPEncoder::WebPEncoder() m_support_metadata[IMAGE_METADATA_EXIF] = true; m_support_metadata[IMAGE_METADATA_XMP] = true; m_support_metadata[IMAGE_METADATA_ICCP] = true; + m_supported_encode_key = {IMWRITE_WEBP_QUALITY}; } WebPEncoder::~WebPEncoder() { } @@ -356,15 +357,17 @@ bool WebPEncoder::write(const Mat& img, const std::vector& params) bool comp_lossless = true; float quality = 100.0f; - if (params.size() > 1) + for(size_t i = 0; i < params.size(); i += 2) { - if (params[0] == IMWRITE_WEBP_QUALITY) + const int value = params[i+1]; + if (params[i] == IMWRITE_WEBP_QUALITY) { comp_lossless = false; - quality = static_cast(params[1]); + quality = static_cast(value); if (quality < 1.0f) { quality = 1.0f; + CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_WEBP_QUALITY must be between 1 to 100(lossy) or more(lossless). It is fallbacked to 1", value)); } if (quality > 100.0f) { diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 2a2a287ae2..cd31608740 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -364,6 +364,20 @@ static ImageEncoder findEncoder( const String& _ext ) return ImageEncoder(); } +static bool isValidEncodeKeyAtAll(const int key) +{ + bool ret = false; + ImageCodecInitializer& codecs = getCodecs(); + for( size_t i = 0; i < codecs.encoders.size(); i++ ) + { + if( codecs.encoders[i]->isValidEncodeKey(key) == true ) + { + ret = true; + break; + } + } + return ret; +} static void ExifTransform(int orientation, OutputArray img) { @@ -1091,6 +1105,27 @@ static bool imwrite_( const String& filename, const std::vector& img_vec, CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs"); CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), ""); + + for(size_t v = 0; v < params.size(); v+= 2) + { + const int key = params[v]; + if(encoder->isValidEncodeKey(key)) + { + // Current encoder supports specified key. + // Do nothing. + } + else if(isValidEncodeKeyAtAll(key)) + { + // Current encoder does not support specified key, but some encoder supports it. + CV_LOG_WARNING(nullptr, cv::format("An unsupported key(%d) was specified and has been ignored.", key)); + } + else + { + // No encoder supports specified key. + CV_LOG_WARNING(nullptr, cv::format("An unknown key(%d) was specified and has been ignored.", key)); + } + } + bool code = false; try { @@ -1625,6 +1660,26 @@ bool imencodeWithMetadata( const String& ext, InputArray _img, CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs"); CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), ""); + for(size_t v = 0; v < params.size(); v+= 2) + { + const int key = params[v]; + if(encoder->isValidEncodeKey(key)) + { + // Current encoder supports specified key. + // Do nothing. + } + else if(isValidEncodeKeyAtAll(key)) + { + // Current encoder does not support specified key, but some encoder supports it. + CV_LOG_WARNING(nullptr, cv::format("An unsupported key(%d) was specified and has been ignored.", key)); + } + else + { + // No encoder supports specified key. + CV_LOG_WARNING(nullptr, cv::format("An unknown key(%d) was specified and has been ignored.", key)); + } + } + bool code = false; String filename; if( !encoder->setDestination(buf) ) diff --git a/modules/imgcodecs/test/test_avif.cpp b/modules/imgcodecs/test/test_avif.cpp index 5b82452049..81cbfe9c66 100644 --- a/modules/imgcodecs/test/test_avif.cpp +++ b/modules/imgcodecs/test/test_avif.cpp @@ -128,12 +128,6 @@ TEST_P(Imgcodecs_Avif_Image_WriteReadSuite, imwrite_imread) { // Encode. const string output = cv::tempfile(".avif"); - if (!IsBitDepthValid()) { - EXPECT_NO_FATAL_FAILURE( - cv::imwrite(output, img_original, encoding_params_)); - EXPECT_NE(0, remove(output.c_str())); - return; - } EXPECT_NO_THROW(cv::imwrite(output, img_original, encoding_params_)); // Read from file. @@ -164,11 +158,6 @@ TEST_P(Imgcodecs_Avif_Image_EncodeDecodeSuite, imencode_imdecode) { bool result = true; EXPECT_NO_THROW( result = cv::imencode(".avif", img_original, buf, encoding_params_);); - - if (!IsBitDepthValid()) { - EXPECT_FALSE(result); - return; - } EXPECT_TRUE(result); // Read back. diff --git a/modules/imgcodecs/test/test_gif.cpp b/modules/imgcodecs/test/test_gif.cpp index 1ceef24637..fcf5954aa3 100644 --- a/modules/imgcodecs/test/test_gif.cpp +++ b/modules/imgcodecs/test/test_gif.cpp @@ -257,20 +257,16 @@ TEST(Imgcodecs_Gif, read_gif_special){ } TEST(Imgcodecs_Gif,write_gif_flags){ - const string root = cvtest::TS::ptr()->get_data_path(); - const string png_filename = root + "gifsuite/special1.png"; vector buff; const int expected_rows=611; const int expected_cols=293; - Mat img_gt = Mat::ones(expected_rows, expected_cols, CV_8UC1); - vector param; - param.push_back(IMWRITE_GIF_QUALITY); - param.push_back(7); - param.push_back(IMWRITE_GIF_DITHER); - param.push_back(2); - EXPECT_NO_THROW(imencode(".png", img_gt, buff, param)); + Mat img_gt = Mat::ones(expected_rows, expected_cols, CV_8UC3); + const vector param = { IMWRITE_GIF_QUALITY, IMWRITE_GIF_FAST_NO_DITHER, IMWRITE_GIF_DITHER, 3}; + bool ret = false; + EXPECT_NO_THROW(ret = imencode(".gif", img_gt, buff, param)); + EXPECT_TRUE(ret); Mat img; - EXPECT_NO_THROW(img = imdecode(buff, IMREAD_ANYDEPTH)); // hang + EXPECT_NO_THROW(img = imdecode(buff, IMREAD_COLOR)); EXPECT_FALSE(img.empty()); EXPECT_EQ(img.cols, expected_cols); EXPECT_EQ(img.rows, expected_rows); @@ -280,7 +276,7 @@ TEST(Imgcodecs_Gif,write_gif_flags){ TEST(Imgcodecs_Gif, write_gif_big) { const string root = cvtest::TS::ptr()->get_data_path(); const string png_filename = root + "gifsuite/gif_big.png"; - const string gif_filename = cv::tempfile(".png"); + const string gif_filename = cv::tempfile(".gif"); cv::Mat img; ASSERT_NO_THROW(img = cv::imread(png_filename, IMREAD_UNCHANGED)); ASSERT_FALSE(img.empty()); diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index 201dac2414..ac1ecb4b8e 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -166,6 +166,8 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq) continue; if (cn == 1 && ext == ".gif") continue; + if (cn == 1 && ext == ".webp") + continue; string filename = cv::tempfile(format("%d%s", cn, ext.c_str()).c_str()); Mat img_gt(size, CV_MAKETYPE(CV_8U, cn), Scalar::all(0)); @@ -257,6 +259,9 @@ const string all_exts[] = #ifdef HAVE_IMGCODEC_GIF ".gif", #endif +#ifdef HAVE_WEBP + ".webp", +#endif }; vector all_sizes() @@ -307,6 +312,35 @@ TEST_P(Imgcodecs_pbm, write_read) INSTANTIATE_TEST_CASE_P(All, Imgcodecs_pbm, testing::Bool()); #endif +// See https://github.com/opencv/opencv/issues/27557 +typedef testing::TestWithParam Imgcodecs_invalid_key; + +TEST_P(Imgcodecs_invalid_key, encode_regression27557) +{ + const string ext = GetParam(); + const int matType = ((ext == ".pbm") || (ext == ".pgm"))? CV_8UC1 : CV_8UC3; + Mat src(100, 100, matType, Scalar(0, 255, 0)); + std::vector buf; + bool status = false; + EXPECT_NO_THROW(status = imencode(ext, src, buf, { -1, -1 })); + EXPECT_TRUE(status); +} + +TEST_P(Imgcodecs_invalid_key, write_regression27557) +{ + const string ext = GetParam(); + string fname = tempfile(ext.c_str()); + + const int matType = ((ext == ".pbm") || (ext == ".pgm"))? CV_8UC1 : CV_8UC3; + Mat src(100, 100, matType, Scalar(0, 255, 0)); + std::vector buf; + bool status = false; + EXPECT_NO_THROW(status = imwrite(fname, src, { -1, -1 })); + EXPECT_TRUE(status); + remove(fname.c_str()); +} + +INSTANTIATE_TEST_CASE_P(All, Imgcodecs_invalid_key, testing::ValuesIn(all_exts)); //================================================================================================== diff --git a/modules/imgcodecs/test/test_webp.cpp b/modules/imgcodecs/test/test_webp.cpp index 6414e60469..b3fe9b2caf 100644 --- a/modules/imgcodecs/test/test_webp.cpp +++ b/modules/imgcodecs/test/test_webp.cpp @@ -72,7 +72,7 @@ TEST(Imgcodecs_WebP, encode_decode_lossy_webp) { std::vector params; params.push_back(IMWRITE_WEBP_QUALITY); - params.push_back(q); + params.push_back(MAX(q,1)); string output = cv::tempfile(".webp"); EXPECT_NO_THROW(cv::imwrite(output, img, params)); diff --git a/modules/imgproc/src/bilateral_filter.dispatch.cpp b/modules/imgproc/src/bilateral_filter.dispatch.cpp index 7b992303b6..9ac499f685 100644 --- a/modules/imgproc/src/bilateral_filter.dispatch.cpp +++ b/modules/imgproc/src/bilateral_filter.dispatch.cpp @@ -13,6 +13,7 @@ // Copyright (C) 2000-2008, 2018, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2014-2015, Itseez Inc., all rights reserved. +// Copyright (C) 2025, Advanced Micro Devices, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -174,8 +175,8 @@ bilateralFilter_8u( const Mat& src, Mat& dst, int d, return; } - double gauss_color_coeff = -0.5/(sigma_color*sigma_color); - double gauss_space_coeff = -0.5/(sigma_space*sigma_space); + float gauss_color_coeff = (float)(-0.5/(sigma_color*sigma_color)); + float gauss_space_coeff = (float)(-0.5/(sigma_space*sigma_space)); if( d <= 0 ) radius = cvRound(sigma_space*1.5); @@ -195,15 +196,31 @@ bilateralFilter_8u( const Mat& src, Mat& dst, int d, int* space_ofs = &_space_ofs[0]; // initialize color-related bilateral filter coefficients + i = 0; +#if (CV_SIMD || CV_SIMD_SCALABLE) + int nlanes = VTraits::vlanes(); + v_float32 v_gauss_color_coeff = vx_setall_f32(gauss_color_coeff); + float counter[16] = {0.0, 1., 2., 3., 4., 5., 6., 7., + 8., 9., 10., 11., 12., 13., 14., 15.}; + v_float32 v_i = vx_load(counter); + v_float32 v_inc = vx_setall_f32(float(nlanes)); - for( i = 0; i < 256*cn; i++ ) + for( ; i < (256*cn) - nlanes; i += nlanes ) + { + v_float32 v_color_weight = v_mul(v_mul(v_i,v_i), v_gauss_color_coeff); + v_store(color_weight + i, v_exp(v_color_weight)); + v_i = v_add(v_i, v_inc); + } +#endif + for(; i < 256*cn; i++ ) + { color_weight[i] = (float)std::exp(i*i*gauss_color_coeff); + } // initialize space-related bilateral filter coefficients for( i = -radius, maxk = 0; i <= radius; i++ ) { j = -radius; - for( ; j <= radius; j++ ) { double r = std::sqrt((double)i*i + (double)j*j); diff --git a/modules/imgproc/src/bilateral_filter.simd.hpp b/modules/imgproc/src/bilateral_filter.simd.hpp index 77e0328678..ab718eac91 100644 --- a/modules/imgproc/src/bilateral_filter.simd.hpp +++ b/modules/imgproc/src/bilateral_filter.simd.hpp @@ -13,6 +13,7 @@ // Copyright (C) 2000-2008, 2018, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2014-2015, Itseez Inc., all rights reserved. +// Copyright (C) 2025, Advanced Micro Devices, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -73,13 +74,71 @@ public: { } +#if (CV_SIMD || CV_SIMD_SCALABLE) + static void expand(const v_uint8& v_input, v_uint32& v_out0, v_uint32& v_out1, v_uint32& v_out2, v_uint32& v_out3) + { + v_uint16 d0, d1; + v_expand(v_input, d0, d1); + v_expand(d0, v_out0, v_out1); + v_expand(d1, v_out2, v_out3); + } + + static void computeBilateral(const v_uint8& v_val_u8,const v_uint8& v_abs_diff, v_float32& kweight, const float* color_weight, + v_float32& v_wsum0, v_float32& v_sum0, v_float32& v_wsum1, v_float32& v_sum1, v_float32& v_wsum2, v_float32& v_sum2, v_float32& v_wsum3, v_float32& v_sum3) + { + v_uint32 d0, d1, d2, d3; + v_float32 w0, w1, w2, w3; + v_uint32 val0, val1, val2, val3; + expand(v_abs_diff, d0, d1, d2, d3); + expand(v_val_u8, val0, val1, val2, val3); + w0 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(d0))); + w1 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(d1))); + w2 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(d2))); + w3 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(d3))); + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum0); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum1); + v_wsum2 = v_add(v_wsum2, w2); + v_sum2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum2); + v_wsum3 = v_add(v_wsum3, w3); + v_sum3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum3); + } + + static void computeBilateral(const v_uint8& v_val_u8,const v_uint8& v_abs_diff, const float* color_weight, + v_float32& v_wsum0, v_float32& v_sum0, v_float32& v_wsum1, v_float32& v_sum1, v_float32& v_wsum2, v_float32& v_sum2, v_float32& v_wsum3, v_float32& v_sum3) + { + v_uint32 d0, d1, d2, d3; + v_float32 w0, w1, w2, w3; + v_uint32 val0, val1, val2, val3; + expand(v_abs_diff, d0, d1, d2, d3); + expand(v_val_u8, val0, val1, val2, val3); + w0 = v_lut(color_weight, v_reinterpret_as_s32(d0)); + w1 = v_lut(color_weight, v_reinterpret_as_s32(d1)); + w2 = v_lut(color_weight, v_reinterpret_as_s32(d2)); + w3 = v_lut(color_weight, v_reinterpret_as_s32(d3)); + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum0); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum1); + v_wsum2 = v_add(v_wsum2, w2); + v_sum2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum2); + v_wsum3 = v_add(v_wsum3, w3); + v_sum3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum3); + } +#endif + virtual void operator() (const Range& range) const CV_OVERRIDE { CV_INSTRUMENT_REGION(); int i, j, cn = dest->channels(), k; Size size = dest->size(); - +#if (CV_SIMD || CV_SIMD_SCALABLE) + int nlanes = VTraits::vlanes(); + int nlanes_2 = 2*nlanes; + int nlanes_4 = 4*nlanes; +#endif for( i = range.start; i < range.end; i++ ) { const uchar* sptr = temp->ptr(i+radius) + radius*cn; @@ -87,365 +146,233 @@ public: if( cn == 1 ) { - AutoBuffer buf(alignSize(size.width, CV_SIMD_WIDTH) + size.width + CV_SIMD_WIDTH - 1); - memset(buf.data(), 0, buf.size() * sizeof(float)); - float *sum = alignPtr(buf.data(), CV_SIMD_WIDTH); - float *wsum = sum + alignSize(size.width, CV_SIMD_WIDTH); - k = 0; - for(; k <= maxk-4; k+=4) + k = 0; j=0; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + for ( ;j <= size.width - nlanes_4; j += nlanes_4) { - const uchar* ksptr0 = sptr + space_ofs[k]; - const uchar* ksptr1 = sptr + space_ofs[k+1]; - const uchar* ksptr2 = sptr + space_ofs[k+2]; - const uchar* ksptr3 = sptr + space_ofs[k+3]; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight0 = vx_setall_f32(space_weight[k]); - v_float32 kweight1 = vx_setall_f32(space_weight[k+1]); - v_float32 kweight2 = vx_setall_f32(space_weight[k+2]); - v_float32 kweight3 = vx_setall_f32(space_weight[k+3]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) + const uchar* sptr_j = sptr + j; + v_float32 v_wsum0 = vx_setzero_f32(); + v_float32 v_wsum1 = vx_setzero_f32(); + v_float32 v_wsum2 = vx_setzero_f32(); + v_float32 v_wsum3 = vx_setzero_f32(); + v_float32 v_sum0 = vx_setzero_f32(); + v_float32 v_sum1 = vx_setzero_f32(); + v_float32 v_sum2 = vx_setzero_f32(); + v_float32 v_sum3 = vx_setzero_f32(); + v_uint8 v_sptr8 = vx_load(sptr_j); + + k=0; + if(maxk==5) { - v_uint32 rval = vx_load_expand_q(sptr + j); + const uchar* ksptrline1 = sptr_j + space_ofs[0]; + const uchar* ksptrline2 = sptr_j + space_ofs[1]; + const uchar* ksptrline3 = sptr_j + space_ofs[4]; - v_uint32 val = vx_load_expand_q(ksptr0 + j); - v_float32 w = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - v_float32 v_wsum = v_add(vx_load_aligned(wsum + j), w); - v_float32 v_sum = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, vx_load_aligned(sum + j)); + v_float32 kweight = vx_setall_f32(space_weight[0]);//same weight for all, expect centre one - val = vx_load_expand_q(ksptr1 + j); - w = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, v_sum); + v_uint8 v_val_u8_line1 = vx_load(ksptrline1); + v_uint8 v_val_u8_line2_0 = vx_load(ksptrline2); + v_uint8 v_val_u8_line2_1 = vx_load(ksptrline2 + 1); + v_uint8 v_val_u8_line2_2 = vx_load(ksptrline2 + 2); + v_uint8 v_val_u8_line3 = vx_load(ksptrline3); - val = vx_load_expand_q(ksptr2 + j); - w = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, v_sum); + //compute abs diff + v_uint8 v_abs_diff_line1 = v_absdiff(v_val_u8_line1, v_sptr8); + v_uint8 v_abs_diff_line2_0 = v_absdiff(v_val_u8_line2_0, v_sptr8); + v_uint8 v_abs_diff_line2_1 = v_absdiff(v_val_u8_line2_1, v_sptr8); + v_uint8 v_abs_diff_line2_2 = v_absdiff(v_val_u8_line2_2, v_sptr8); + v_uint8 v_abs_diff_line3 = v_absdiff(v_val_u8_line3, v_sptr8); - val = vx_load_expand_q(ksptr3 + j); - w = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, v_sum); + computeBilateral( v_val_u8_line1, v_abs_diff_line1, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_0, v_abs_diff_line2_0, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_1, v_abs_diff_line2_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_2, v_abs_diff_line2_2, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3, v_abs_diff_line3, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); - v_store_aligned(wsum + j, v_wsum); - v_store_aligned(sum + j, v_sum); + k = maxk; } -#endif -#if CV_SIMD128 - v_float32x4 kweight4 = v_load(space_weight + k); -#endif - for (; j < size.width; j++) + else if(maxk==13) { -#if CV_SIMD128 - v_uint32x4 rval = v_setall_u32(sptr[j]); - v_uint32x4 val(ksptr0[j], ksptr1[j], ksptr2[j], ksptr3[j]); - v_float32x4 w = v_mul(kweight4, v_lut(this->color_weight, v_reinterpret_as_s32(v_absdiff(val, rval)))); - wsum[j] += v_reduce_sum(w); - sum[j] += v_reduce_sum(v_mul(v_cvt_f32(v_reinterpret_as_s32(val)), w)); -#else - int rval = sptr[j]; + const uchar* ksptrline1 = sptr_j + space_ofs[0]; + const uchar* ksptrline5 = sptr_j + space_ofs[12];//last element + const uchar* ksptrline2 = sptr_j + space_ofs[1]; + const uchar* ksptrline3 = sptr_j + space_ofs[4]; + const uchar* ksptrline4 = sptr_j + space_ofs[9]; - int val = ksptr0[j]; - float w = space_weight[k] * color_weight[std::abs(val - rval)]; - wsum[j] += w; - sum[j] += val * w; + v_float32 kweight = vx_setall_f32(space_weight[0]); - val = ksptr1[j]; - w = space_weight[k+1] * color_weight[std::abs(val - rval)]; - wsum[j] += w; - sum[j] += val * w; + //compute line 1 and 5 + v_uint8 v_val_u8_line1 = vx_load(ksptrline1); + v_uint8 v_val_u8_line5 = vx_load(ksptrline5); + v_uint8 v_abs_diff_line1 = v_absdiff(v_val_u8_line1, v_sptr8); + v_uint8 v_abs_diff_line5 = v_absdiff(v_val_u8_line5, v_sptr8); + computeBilateral( v_val_u8_line1, v_abs_diff_line1, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line5, v_abs_diff_line5, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); - val = ksptr2[j]; - w = space_weight[k+2] * color_weight[std::abs(val - rval)]; - wsum[j] += w; - sum[j] += val * w; + //compute line 2 and 4 + v_uint8 v_val_u8_line2_0 = vx_load(ksptrline2); + v_uint8 v_val_u8_line2_1 = vx_load(ksptrline2 + 1); + v_uint8 v_val_u8_line2_2 = vx_load(ksptrline2 + 2); - val = ksptr3[j]; - w = space_weight[k+3] * color_weight[std::abs(val - rval)]; - wsum[j] += w; - sum[j] += val * w; -#endif + v_uint8 v_val_u8_line4_0 = vx_load(ksptrline4); + v_uint8 v_val_u8_line4_1 = vx_load(ksptrline4 + 1); + v_uint8 v_val_u8_line4_2 = vx_load(ksptrline4 + 2); + + v_uint8 v_abs_diff_line2_0 = v_absdiff(v_val_u8_line2_0, v_sptr8); + v_uint8 v_abs_diff_line2_1 = v_absdiff(v_val_u8_line2_1, v_sptr8); + v_uint8 v_abs_diff_line2_2 = v_absdiff(v_val_u8_line2_2, v_sptr8); + v_uint8 v_abs_diff_line4_0 = v_absdiff(v_val_u8_line4_0, v_sptr8); + v_uint8 v_abs_diff_line4_1 = v_absdiff(v_val_u8_line4_1, v_sptr8); + v_uint8 v_abs_diff_line4_2 = v_absdiff(v_val_u8_line4_2, v_sptr8); + v_float32 kweight_1 = vx_setall_f32(space_weight[1]); + v_float32 kweight_2 = vx_setall_f32(space_weight[2]); + computeBilateral( v_val_u8_line2_0, v_abs_diff_line2_0, kweight_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_1, v_abs_diff_line2_1, kweight_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line2_2, v_abs_diff_line2_2, kweight_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + + computeBilateral( v_val_u8_line4_0, v_abs_diff_line4_0, kweight_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line4_1, v_abs_diff_line4_1, kweight_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line4_2, v_abs_diff_line4_2, kweight_1, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + + //compute line 3 + v_uint8 v_val_u8_line3_0 = vx_load(ksptrline3); + v_uint8 v_val_u8_line3_1 = vx_load(ksptrline3 + 1); + v_uint8 v_val_u8_line3_2 = vx_load(ksptrline3 + 2); + v_uint8 v_val_u8_line3_3 = vx_load(ksptrline3 + 3); + v_uint8 v_val_u8_line3_4 = vx_load(ksptrline3 + 4); + + v_uint8 v_abs_diff_line3_0 = v_absdiff(v_val_u8_line3_0, v_sptr8); + v_uint8 v_abs_diff_line3_1 = v_absdiff(v_val_u8_line3_1, v_sptr8); + v_uint8 v_abs_diff_line3_2 = v_absdiff(v_val_u8_line3_2, v_sptr8); + v_uint8 v_abs_diff_line3_3 = v_absdiff(v_val_u8_line3_3, v_sptr8); + v_uint8 v_abs_diff_line3_4 = v_absdiff(v_val_u8_line3_4, v_sptr8); + + computeBilateral( v_val_u8_line3_0, v_abs_diff_line3_0, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3_1, v_abs_diff_line3_1, kweight_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3_2, v_abs_diff_line3_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3_3, v_abs_diff_line3_3, kweight_2, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + computeBilateral( v_val_u8_line3_4, v_abs_diff_line3_4, kweight, color_weight, + v_wsum0, v_sum0, v_wsum1, v_sum1, v_wsum2, v_sum2, v_wsum3, v_sum3); + + k = maxk; } + + for(; k < maxk; k++) + { + const uchar* ksptr = sptr_j + space_ofs[k]; + v_float32 kweight = vx_setall_f32(space_weight[k]); + + v_uint8 v_val_u8 = vx_load(ksptr); + v_uint8 v_abs_diff = v_absdiff(v_val_u8, v_sptr8); + + v_uint16 d0, d1; + v_uint32 diff0, diff1, diff2, diff3; + v_expand(v_abs_diff, d0, d1); + v_expand(d0, diff0, diff1); + v_expand(d1, diff2, diff3); + + v_uint16 v0, v1; + v_uint32 val0, val1, val2, val3; + v_expand(v_val_u8, v0, v1); + v_expand(v0, val0, val1); + v_expand(v1, val2, val3); + + v_float32 w0 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(diff0))); + v_float32 w1 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(diff1))); + v_float32 w2 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(diff2))); + v_float32 w3 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(diff3))); + + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum0); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum1); + v_wsum2 = v_add(v_wsum2, w2); + v_sum2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum2); + v_wsum3 = v_add(v_wsum3, w3); + v_sum3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum3); + } + v_pack_u_store(dptr + j, v_pack(v_round(v_div(v_sum0, v_wsum0)), + v_round(v_div(v_sum1, v_wsum1)))); + + v_pack_u_store(dptr + j + nlanes_2, v_pack(v_round(v_div(v_sum2, v_wsum2)), + v_round(v_div(v_sum3, v_wsum3)))); } - for(; k < maxk; k++) - { - const uchar* ksptr = sptr + space_ofs[k]; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight = vx_setall_f32(space_weight[k]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) - { - v_uint32 val = vx_load_expand_q(ksptr + j); - v_float32 w = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(v_absdiff(val, vx_load_expand_q(sptr + j))))); - v_store_aligned(wsum + j, v_add(vx_load_aligned(wsum + j), w)); - v_store_aligned(sum + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(val)), w, vx_load_aligned(sum + j))); - } -#endif - for (; j < size.width; j++) - { - int val = ksptr[j]; - float w = space_weight[k] * color_weight[std::abs(val - sptr[j])]; - wsum[j] += w; - sum[j] += val * w; - } - } - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - for (; j <= size.width - 2*VTraits::vlanes(); j += 2*VTraits::vlanes()) - v_pack_u_store(dptr + j, v_pack(v_round(v_div(vx_load_aligned(sum + j), vx_load_aligned(wsum + j))), - v_round(v_div(vx_load_aligned(sum + j + VTraits::vlanes()), vx_load_aligned(wsum + j + VTraits::vlanes()))))); #endif for (; j < size.width; j++) { + uchar val0 = sptr[j]; + float wsumT = 0; + float sumT = 0; + for(k=0; k < maxk; k++) + { + const uchar* ksptr = sptr + space_ofs[k]; + uchar val = ksptr[j]; + float w = space_weight[k] * color_weight[std::abs(val - val0)]; + wsumT += w; + sumT += val * w; + } + // overflow is not possible here => there is no need to use cv::saturate_cast - CV_DbgAssert(fabs(wsum[j]) > 0); - dptr[j] = (uchar)cvRound(sum[j]/wsum[j]); + CV_DbgAssert(fabs(wsumT) > 0); + dptr[j] = (uchar)cvRound(sumT/wsumT); } + } else { CV_Assert( cn == 3 ); - AutoBuffer buf(alignSize(size.width, CV_SIMD_WIDTH)*3 + size.width + CV_SIMD_WIDTH - 1); - memset(buf.data(), 0, buf.size() * sizeof(float)); - float *sum_b = alignPtr(buf.data(), CV_SIMD_WIDTH); - float *sum_g = sum_b + alignSize(size.width, CV_SIMD_WIDTH); - float *sum_r = sum_g + alignSize(size.width, CV_SIMD_WIDTH); - float *wsum = sum_r + alignSize(size.width, CV_SIMD_WIDTH); - k = 0; - for(; k <= maxk-4; k+=4) - { - const uchar* ksptr0 = sptr + space_ofs[k]; - const uchar* ksptr1 = sptr + space_ofs[k+1]; - const uchar* ksptr2 = sptr + space_ofs[k+2]; - const uchar* ksptr3 = sptr + space_ofs[k+3]; - const uchar* rsptr = sptr; - j = 0; + j = 0; + const uchar* sptr_j = sptr; #if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight0 = vx_setall_f32(space_weight[k]); - v_float32 kweight1 = vx_setall_f32(space_weight[k+1]); - v_float32 kweight2 = vx_setall_f32(space_weight[k+2]); - v_float32 kweight3 = vx_setall_f32(space_weight[k+3]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), rsptr += 3*VTraits::vlanes(), - ksptr0 += 3*VTraits::vlanes(), ksptr1 += 3*VTraits::vlanes(), ksptr2 += 3*VTraits::vlanes(), ksptr3 += 3*VTraits::vlanes()) - { - v_uint8 kb, kg, kr, rb, rg, rr; - v_load_deinterleave(rsptr, rb, rg, rr); - - v_load_deinterleave(ksptr0, kb, kg, kr); - v_uint16 val0, val1, val2, val3, val4; - v_expand(v_absdiff(kb, rb), val0, val1); - v_expand(v_absdiff(kg, rg), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - v_expand(v_absdiff(kr, rr), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - - v_uint32 vall, valh; - v_expand(val0, vall, valh); - v_float32 w0 = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(vall))); - v_float32 w1 = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j, v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_expand(kb, val0, val2); - v_expand(val0, vall, valh); - v_store_aligned(sum_b + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_expand(kg, val0, val3); - v_expand(val0, vall, valh); - v_store_aligned(sum_g + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_expand(kr, val0, val4); - v_expand(val0, vall, valh); - v_store_aligned(sum_r + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - - v_expand(val1, vall, valh); - w0 = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight0, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j + 2 * VTraits::vlanes(), v_add(w0, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3 * VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); - v_expand(val2, vall, valh); - v_store_aligned(sum_b + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))); - v_expand(val3, vall, valh); - v_store_aligned(sum_g + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))); - v_expand(val4, vall, valh); - v_store_aligned(sum_r + j + 2*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j + 2*VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + 3*VTraits::vlanes()))); - - v_load_deinterleave(ksptr1, kb, kg, kr); - v_expand(v_absdiff(kb, rb), val0, val1); - v_expand(v_absdiff(kg, rg), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - v_expand(v_absdiff(kr, rr), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - - v_expand(val0, vall, valh); - w0 = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j, v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_expand(kb, val0, val2); - v_expand(val0, vall, valh); - v_store_aligned(sum_b + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_expand(kg, val0, val3); - v_expand(val0, vall, valh); - v_store_aligned(sum_g + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_expand(kr, val0, val4); - v_expand(val0, vall, valh); - v_store_aligned(sum_r + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - - v_expand(val1, vall, valh); - w0 = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight1, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j + 2 * VTraits::vlanes(), v_add(w0, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3 * VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); - v_expand(val2, vall, valh); - v_store_aligned(sum_b + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))); - v_expand(val3, vall, valh); - v_store_aligned(sum_g + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))); - v_expand(val4, vall, valh); - v_store_aligned(sum_r + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + 3 * VTraits::vlanes()))); - - v_load_deinterleave(ksptr2, kb, kg, kr); - v_expand(v_absdiff(kb, rb), val0, val1); - v_expand(v_absdiff(kg, rg), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - v_expand(v_absdiff(kr, rr), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - - v_expand(val0, vall, valh); - w0 = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j, v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_expand(kb, val0, val2); - v_expand(val0, vall, valh); - v_store_aligned(sum_b + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_expand(kg, val0, val3); - v_expand(val0, vall, valh); - v_store_aligned(sum_g + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_expand(kr, val0, val4); - v_expand(val0, vall, valh); - v_store_aligned(sum_r + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - - v_expand(val1, vall, valh); - w0 = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight2, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j + 2 * VTraits::vlanes(), v_add(w0, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3 * VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); - v_expand(val2, vall, valh); - v_store_aligned(sum_b + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))); - v_expand(val3, vall, valh); - v_store_aligned(sum_g + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))); - v_expand(val4, vall, valh); - v_store_aligned(sum_r + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + 3 * VTraits::vlanes()))); - - v_load_deinterleave(ksptr3, kb, kg, kr); - v_expand(v_absdiff(kb, rb), val0, val1); - v_expand(v_absdiff(kg, rg), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - v_expand(v_absdiff(kr, rr), val2, val3); - val0 = v_add(val0, val2); val1 = v_add(val1, val3); - - v_expand(val0, vall, valh); - w0 = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j, v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_expand(kb, val0, val2); - v_expand(val0, vall, valh); - v_store_aligned(sum_b + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_expand(kg, val0, val3); - v_expand(val0, vall, valh); - v_store_aligned(sum_g + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_expand(kr, val0, val4); - v_expand(val0, vall, valh); - v_store_aligned(sum_r + j, v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - - v_expand(val1, vall, valh); - w0 = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(vall))); - w1 = v_mul(kweight3, v_lut(color_weight, v_reinterpret_as_s32(valh))); - v_store_aligned(wsum + j + 2 * VTraits::vlanes(), v_add(w0, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3 * VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); - v_expand(val2, vall, valh); - v_store_aligned(sum_b + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))); - v_expand(val3, vall, valh); - v_store_aligned(sum_g + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))); - v_expand(val4, vall, valh); - v_store_aligned(sum_r + j + 2 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(vall)), w0, vx_load_aligned(sum_r + j + 2 * VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3 * VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(valh)), w1, vx_load_aligned(sum_r + j + 3 * VTraits::vlanes()))); - } -#endif -#if CV_SIMD128 - v_float32x4 kweight4 = v_load(space_weight + k); -#endif - for(; j < size.width; j++, rsptr += 3, ksptr0 += 3, ksptr1 += 3, ksptr2 += 3, ksptr3 += 3) - { -#if CV_SIMD128 - v_uint32x4 rb = v_setall_u32(rsptr[0]); - v_uint32x4 rg = v_setall_u32(rsptr[1]); - v_uint32x4 rr = v_setall_u32(rsptr[2]); - v_uint32x4 b(ksptr0[0], ksptr1[0], ksptr2[0], ksptr3[0]); - v_uint32x4 g(ksptr0[1], ksptr1[1], ksptr2[1], ksptr3[1]); - v_uint32x4 r(ksptr0[2], ksptr1[2], ksptr2[2], ksptr3[2]); - v_float32x4 w = v_mul(kweight4, v_lut(this->color_weight, v_reinterpret_as_s32(v_add(v_add(v_absdiff(b, rb), v_absdiff(g, rg)), v_absdiff(r, rr))))); - wsum[j] += v_reduce_sum(w); - sum_b[j] += v_reduce_sum(v_mul(v_cvt_f32(v_reinterpret_as_s32(b)), w)); - sum_g[j] += v_reduce_sum(v_mul(v_cvt_f32(v_reinterpret_as_s32(g)), w)); - sum_r[j] += v_reduce_sum(v_mul(v_cvt_f32(v_reinterpret_as_s32(r)), w)); -#else - int rb = rsptr[0], rg = rsptr[1], rr = rsptr[2]; - - int b = ksptr0[0], g = ksptr0[1], r = ksptr0[2]; - float w = space_weight[k]*color_weight[std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; - - b = ksptr1[0]; g = ksptr1[1]; r = ksptr1[2]; - w = space_weight[k+1] * color_weight[std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; - - b = ksptr2[0]; g = ksptr2[1]; r = ksptr2[2]; - w = space_weight[k+2] * color_weight[std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; - - b = ksptr3[0]; g = ksptr3[1]; r = ksptr3[2]; - w = space_weight[k+3] * color_weight[std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; -#endif - } - } - for(; k < maxk; k++) + int n_8_lanes = VTraits::vlanes(); + for (; j <= size.width - n_8_lanes; j += n_8_lanes, sptr_j += 3*n_8_lanes, dptr += 3*n_8_lanes) { - const uchar* ksptr = sptr + space_ofs[k]; - const uchar* rsptr = sptr; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight = vx_setall_f32(space_weight[k]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), ksptr += 3*VTraits::vlanes(), rsptr += 3*VTraits::vlanes()) + const uchar* rsptr = sptr_j; + v_float32 v_wsum_0 = vx_setzero_f32(); + v_float32 v_wsum_1 = vx_setzero_f32(); + v_float32 v_wsum_2 = vx_setzero_f32(); + v_float32 v_wsum_3 = vx_setzero_f32(); + + v_float32 v_sum_b_0 = vx_setzero_f32(); + v_float32 v_sum_b_1 = vx_setzero_f32(); + v_float32 v_sum_b_2 = vx_setzero_f32(); + v_float32 v_sum_b_3 = vx_setzero_f32(); + + v_float32 v_sum_g_0 = vx_setzero_f32(); + v_float32 v_sum_g_1 = vx_setzero_f32(); + v_float32 v_sum_g_2 = vx_setzero_f32(); + v_float32 v_sum_g_3 = vx_setzero_f32(); + + v_float32 v_sum_r_0 = vx_setzero_f32(); + v_float32 v_sum_r_1 = vx_setzero_f32(); + v_float32 v_sum_r_2 = vx_setzero_f32(); + v_float32 v_sum_r_3 = vx_setzero_f32(); + + v_float32 v_one = vx_setall_f32(1.f); + + for(k=0; k < maxk; k++) { + const uchar* ksptr = sptr_j + space_ofs[k]; + v_float32 kweight = vx_setall_f32(space_weight[k]); + v_uint8 kb, kg, kr, rb, rg, rr; v_load_deinterleave(ksptr, kb, kg, kr); v_load_deinterleave(rsptr, rb, rg, rr); @@ -467,69 +394,71 @@ public: v_float32 w1 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(val1))); v_float32 w2 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(val2))); v_float32 w3 = v_mul(kweight, v_lut(color_weight, v_reinterpret_as_s32(val3))); - v_store_aligned(wsum + j , v_add(w0, vx_load_aligned(wsum + j))); - v_store_aligned(wsum + j + VTraits::vlanes(), v_add(w1, vx_load_aligned(wsum + j + VTraits::vlanes()))); - v_store_aligned(wsum + j + 2*VTraits::vlanes(), v_add(w2, vx_load_aligned(wsum + j + 2 * VTraits::vlanes()))); - v_store_aligned(wsum + j + 3*VTraits::vlanes(), v_add(w3, vx_load_aligned(wsum + j + 3 * VTraits::vlanes()))); + v_wsum_0 = v_add(w0, v_wsum_0); + v_wsum_1 = v_add(w1, v_wsum_1); + v_wsum_2 = v_add(w2, v_wsum_2); + v_wsum_3 = v_add(w3, v_wsum_3); + v_expand(b_l, val0, val1); v_expand(b_h, val2, val3); - v_store_aligned(sum_b + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_b + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, vx_load_aligned(sum_b + j + VTraits::vlanes()))); - v_store_aligned(sum_b + j + 2*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, vx_load_aligned(sum_b + j + 2*VTraits::vlanes()))); - v_store_aligned(sum_b + j + 3*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, vx_load_aligned(sum_b + j + 3*VTraits::vlanes()))); + v_sum_b_0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum_b_0); + v_sum_b_1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum_b_1); + v_sum_b_2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum_b_2); + v_sum_b_3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum_b_3); + v_expand(g_l, val0, val1); v_expand(g_h, val2, val3); - v_store_aligned(sum_g + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_g + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, vx_load_aligned(sum_g + j + VTraits::vlanes()))); - v_store_aligned(sum_g + j + 2*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, vx_load_aligned(sum_g + j + 2*VTraits::vlanes()))); - v_store_aligned(sum_g + j + 3*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, vx_load_aligned(sum_g + j + 3*VTraits::vlanes()))); + v_sum_g_0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum_g_0); + v_sum_g_1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum_g_1); + v_sum_g_2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum_g_2); + v_sum_g_3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum_g_3); + v_expand(r_l, val0, val1); v_expand(r_h, val2, val3); - v_store_aligned(sum_r + j , v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, vx_load_aligned(sum_r + j))); - v_store_aligned(sum_r + j + VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, vx_load_aligned(sum_r + j + VTraits::vlanes()))); - v_store_aligned(sum_r + j + 2*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, vx_load_aligned(sum_r + j + 2*VTraits::vlanes()))); - v_store_aligned(sum_r + j + 3*VTraits::vlanes(), v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, vx_load_aligned(sum_r + j + 3*VTraits::vlanes()))); + v_sum_r_0 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val0)), w0, v_sum_r_0); + v_sum_r_1 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val1)), w1, v_sum_r_1); + v_sum_r_2 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val2)), w2, v_sum_r_2); + v_sum_r_3 = v_muladd(v_cvt_f32(v_reinterpret_as_s32(val3)), w3, v_sum_r_3); } + v_float32 w0 = v_div(v_one, v_wsum_0); + v_float32 w1 = v_div(v_one, v_wsum_1); + v_float32 w2 = v_div(v_one, v_wsum_2); + v_float32 w3 = v_div(v_one, v_wsum_3); + + v_store_interleave(dptr, v_pack_u(v_pack(v_round(v_mul(w0, v_sum_b_0)), + v_round(v_mul(w1, v_sum_b_1))), + v_pack(v_round(v_mul(w2, v_sum_b_2)), + v_round(v_mul(w3, v_sum_b_3)))), + v_pack_u(v_pack(v_round(v_mul(w0, v_sum_g_0)), + v_round(v_mul(w1, v_sum_g_1))), + v_pack(v_round(v_mul(w2, v_sum_g_2)), + v_round(v_mul(w3, v_sum_g_3)))), + v_pack_u(v_pack(v_round(v_mul(w0, v_sum_r_0)), + v_round(v_mul(w1, v_sum_r_1))), + v_pack(v_round(v_mul(w2, v_sum_r_2)), + v_round(v_mul(w3, v_sum_r_3))))); + } #endif - for(; j < size.width; j++, ksptr += 3, rsptr += 3) + for(; j < size.width; j++, sptr_j += 3) + { + const uchar* rsptr = sptr_j; + float wsum = 0.f; + float sum_b = 0.f, sum_g = 0.f, sum_r = 0.f; + for(k=0; k < maxk; k++) { + const uchar* ksptr = sptr_j + space_ofs[k]; + int b = ksptr[0], g = ksptr[1], r = ksptr[2]; float w = space_weight[k]*color_weight[std::abs(b - rsptr[0]) + std::abs(g - rsptr[1]) + std::abs(r - rsptr[2])]; - wsum[j] += w; - sum_b[j] += b*w; sum_g[j] += g*w; sum_r[j] += r*w; + wsum += w; + sum_b += b*w; sum_g += g*w; sum_r += r*w; } - } - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 v_one = vx_setall_f32(1.f); - for(; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), dptr += 3*VTraits::vlanes()) - { - v_float32 w0 = v_div(v_one, vx_load_aligned(wsum + j)); - v_float32 w1 = v_div(v_one, vx_load_aligned(wsum + j + VTraits::vlanes())); - v_float32 w2 = v_div(v_one, vx_load_aligned(wsum + j + 2 * VTraits::vlanes())); - v_float32 w3 = v_div(v_one, vx_load_aligned(wsum + j + 3 * VTraits::vlanes())); - v_store_interleave(dptr, v_pack_u(v_pack(v_round(v_mul(w0, vx_load_aligned(sum_b + j))), - v_round(v_mul(w1, vx_load_aligned(sum_b + j + VTraits::vlanes())))), - v_pack(v_round(v_mul(w2, vx_load_aligned(sum_b + j + 2 * VTraits::vlanes()))), - v_round(v_mul(w3, vx_load_aligned(sum_b + j + 3 * VTraits::vlanes()))))), - v_pack_u(v_pack(v_round(v_mul(w0, vx_load_aligned(sum_g + j))), - v_round(v_mul(w1, vx_load_aligned(sum_g + j + VTraits::vlanes())))), - v_pack(v_round(v_mul(w2, vx_load_aligned(sum_g + j + 2 * VTraits::vlanes()))), - v_round(v_mul(w3, vx_load_aligned(sum_g + j + 3 * VTraits::vlanes()))))), - v_pack_u(v_pack(v_round(v_mul(w0, vx_load_aligned(sum_r + j))), - v_round(v_mul(w1, vx_load_aligned(sum_r + j + VTraits::vlanes())))), - v_pack(v_round(v_mul(w2, vx_load_aligned(sum_r + j + 2 * VTraits::vlanes()))), - v_round(v_mul(w3, vx_load_aligned(sum_r + j + 3 * VTraits::vlanes())))))); - } -#endif - for(; j < size.width; j++) - { - CV_DbgAssert(fabs(wsum[j]) > 0); - wsum[j] = 1.f/wsum[j]; - *(dptr++) = (uchar)cvRound(sum_b[j]*wsum[j]); - *(dptr++) = (uchar)cvRound(sum_g[j]*wsum[j]); - *(dptr++) = (uchar)cvRound(sum_r[j]*wsum[j]); + CV_DbgAssert(fabs(wsum) > 0); + wsum = 1.f/wsum; + *(dptr++) = (uchar)cvRound(sum_b*wsum); + *(dptr++) = (uchar)cvRound(sum_g*wsum); + *(dptr++) = (uchar)cvRound(sum_r*wsum); } } } @@ -552,6 +481,7 @@ void bilateralFilterInvoker_8u( int* space_ofs, float *space_weight, float *color_weight) { CV_INSTRUMENT_REGION(); + BilateralFilter_8u_Invoker body(dst, temp, radius, maxk, space_ofs, space_weight, color_weight); parallel_for_(Range(0, dst.rows), body, dst.total()/(double)(1<<16)); } @@ -577,7 +507,12 @@ public: int i, j, k; Size size = dest->size(); - +#if (CV_SIMD || CV_SIMD_SCALABLE) + int nlanes = VTraits::vlanes(); + int nlanes_2 = 2 * nlanes; + int nlanes_3 = 3 * nlanes; + int nlanes_4 = 4 * nlanes; +#endif for( i = range.start; i < range.end; i++ ) { const float* sptr = temp->ptr(i+radius) + radius*cn; @@ -585,363 +520,161 @@ public: if( cn == 1 ) { - AutoBuffer buf(alignSize(size.width, CV_SIMD_WIDTH) + size.width + CV_SIMD_WIDTH - 1); - memset(buf.data(), 0, buf.size() * sizeof(float)); - float *sum = alignPtr(buf.data(), CV_SIMD_WIDTH); - float *wsum = sum + alignSize(size.width, CV_SIMD_WIDTH); + j = 0; + const float* sptr_j = sptr; #if (CV_SIMD || CV_SIMD_SCALABLE) v_float32 v_one = vx_setall_f32(1.f); v_float32 sindex = vx_setall_f32(scale_index); -#endif - k = 0; - for(; k <= maxk - 4; k+=4) + + for(; j <= size.width - nlanes_4; j += nlanes_4, sptr_j += nlanes_4, dptr += nlanes_4) { - const float* ksptr0 = sptr + space_ofs[k]; - const float* ksptr1 = sptr + space_ofs[k + 1]; - const float* ksptr2 = sptr + space_ofs[k + 2]; - const float* ksptr3 = sptr + space_ofs[k + 3]; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight0 = vx_setall_f32(space_weight[k]); - v_float32 kweight1 = vx_setall_f32(space_weight[k+1]); - v_float32 kweight2 = vx_setall_f32(space_weight[k+2]); - v_float32 kweight3 = vx_setall_f32(space_weight[k+3]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) + v_float32 v_wsum0 = vx_setzero_f32(); + v_float32 v_wsum1 = vx_setzero_f32(); + v_float32 v_wsum2 = vx_setzero_f32(); + v_float32 v_wsum3 = vx_setzero_f32(); + v_float32 v_sum0 = vx_setzero_f32(); + v_float32 v_sum1 = vx_setzero_f32(); + v_float32 v_sum2 = vx_setzero_f32(); + v_float32 v_sum3 = vx_setzero_f32(); + + v_float32 rval0 = vx_load(sptr_j); + v_float32 rval1 = vx_load(sptr_j + nlanes); + v_float32 rval2 = vx_load(sptr_j + nlanes_2); + v_float32 rval3 = vx_load(sptr_j + nlanes_3); + for(k = 0; k < maxk; k++) { - v_float32 rval = vx_load(sptr + j); + const float* ksptr = sptr_j + space_ofs[k]; + v_float32 kweight = vx_setall_f32(space_weight[k]); - v_float32 val = vx_load(ksptr0 + j); - v_float32 knan = v_not_nan(val); - v_float32 alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - v_int32 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - v_float32 w = v_and(v_mul(kweight0, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_float32 v_wsum = v_add(vx_load_aligned(wsum + j), w); - v_float32 v_sum = v_muladd(v_and(val, knan), w, vx_load_aligned(sum + j)); + //0th + v_float32 val0 = vx_load(ksptr); + v_float32 knan0 = v_not_nan(val0); + v_float32 alpha0 = v_and(v_and(v_mul(v_absdiff(val0, rval0), sindex), v_not_nan(rval0)), knan0); + v_int32 idx0 = v_trunc(alpha0); + alpha0 = v_sub(alpha0, v_cvt_f32(idx0)); + v_float32 w0 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx0), alpha0, v_mul(v_lut(this->expLUT, idx0), v_sub(v_one, alpha0)))), knan0); + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_and(val0, knan0), w0, v_sum0); - val = vx_load(ksptr1 + j); - knan = v_not_nan(val); - alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight1, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_and(val, knan), w, v_sum); + //1st + v_float32 val1 = vx_load(ksptr + nlanes); + v_float32 knan1 = v_not_nan(val1); + v_float32 alpha1 = v_and(v_and(v_mul(v_absdiff(val1, rval1), sindex), v_not_nan(rval1)), knan1); + v_int32 idx1 = v_trunc(alpha1); + alpha1 = v_sub(alpha1, v_cvt_f32(idx1)); + v_float32 w1 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx1), alpha1, v_mul(v_lut(this->expLUT, idx1), v_sub(v_one, alpha1)))), knan1); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_and(val1, knan1), w1, v_sum1); - val = vx_load(ksptr2 + j); - knan = v_not_nan(val); - alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight2, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_and(val, knan), w, v_sum); + //2nd + v_float32 val2 = vx_load(ksptr + nlanes_2); + v_float32 knan2 = v_not_nan(val2); + v_float32 alpha2 = v_and(v_and(v_mul(v_absdiff(val2, rval2), sindex), v_not_nan(rval2)), knan2); + v_int32 idx2 = v_trunc(alpha2); + alpha2 = v_sub(alpha2, v_cvt_f32(idx2)); + v_float32 w2 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx2), alpha2, v_mul(v_lut(this->expLUT, idx2), v_sub(v_one, alpha2)))), knan2); + v_wsum2 = v_add(v_wsum2, w2); + v_sum2 = v_muladd(v_and(val2, knan2), w2, v_sum2); - val = vx_load(ksptr3 + j); - knan = v_not_nan(val); - alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight3, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum = v_muladd(v_and(val, knan), w, v_sum); - - v_store_aligned(wsum + j, v_wsum); - v_store_aligned(sum + j, v_sum); + //3rd + v_float32 val3 = vx_load(ksptr + nlanes_3); + v_float32 knan3 = v_not_nan(val3); + v_float32 alpha3 = v_and(v_and(v_mul(v_absdiff(val3, rval3), sindex), v_not_nan(rval3)), knan3); + v_int32 idx3 = v_trunc(alpha3); + alpha3 = v_sub(alpha3, v_cvt_f32(idx3)); + v_float32 w3 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx3), alpha3, v_mul(v_lut(this->expLUT, idx3), v_sub(v_one, alpha3)))), knan3); + v_wsum3 = v_add(v_wsum3, w3); + v_sum3 = v_muladd(v_and(val3, knan3), w3, v_sum3); } -#endif -#if CV_SIMD128 - v_float32x4 v_one4 = v_setall_f32(1.f); - v_float32x4 sindex4 = v_setall_f32(scale_index); - v_float32x4 kweight4 = v_load(space_weight + k); -#endif - for (; j < size.width; j++) - { -#if CV_SIMD128 - v_float32x4 rval = v_setall_f32(sptr[j]); - v_float32x4 val(ksptr0[j], ksptr1[j], ksptr2[j], ksptr3[j]); - v_float32x4 knan = v_not_nan(val); - v_float32x4 alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex4), v_not_nan(rval)), knan); - v_int32x4 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - v_float32x4 w = v_and(v_mul(kweight4, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one4, alpha)))), knan); - wsum[j] += v_reduce_sum(w); - sum[j] += v_reduce_sum(v_mul(v_and(val, knan), w)); -#else - float rval = sptr[j]; + v_store(dptr , v_div(v_add(v_sum0, v_and(rval0, v_not_nan(rval0))), v_add(v_wsum0, v_and(v_one, v_not_nan(rval0))))); + v_store(dptr + nlanes, v_div(v_add(v_sum1, v_and(rval1, v_not_nan(rval1))), v_add(v_wsum1, v_and(v_one, v_not_nan(rval1))))); + v_store(dptr + nlanes_2, v_div(v_add(v_sum2, v_and(rval2, v_not_nan(rval2))), v_add(v_wsum2, v_and(v_one, v_not_nan(rval2))))); + v_store(dptr + nlanes_3, v_div(v_add(v_sum3, v_and(rval3, v_not_nan(rval3))), v_add(v_wsum3, v_and(v_one, v_not_nan(rval3))))); + } + for (; j <= size.width - nlanes_2; j += nlanes_2, sptr_j += nlanes_2, dptr += nlanes_2) + { + v_float32 v_wsum0 = vx_setzero_f32(); + v_float32 v_wsum1 = vx_setzero_f32(); + v_float32 v_sum0 = vx_setzero_f32(); + v_float32 v_sum1 = vx_setzero_f32(); + v_float32 rval0 = vx_load(sptr_j); + v_float32 rval1 = vx_load(sptr_j + nlanes); + v_float32 rval0_not_nan = v_not_nan(rval0); + v_float32 rval1_not_nan = v_not_nan(rval1); - float val = ksptr0[j]; + for (k = 0; k < maxk; k++) + { + v_float32 kweight = vx_setall_f32(space_weight[k]); + const float* ksptr = sptr_j + space_ofs[k]; + + //0th + v_float32 val0 = vx_load(ksptr); + v_float32 knan0 = v_not_nan(val0); + v_float32 alpha0 = v_and(v_and(v_mul(v_absdiff(val0, rval0), sindex), rval0_not_nan), knan0); + v_int32 idx0 = v_trunc(alpha0); + alpha0 = v_sub(alpha0, v_cvt_f32(idx0)); + v_float32 w0 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx0), alpha0, v_mul(v_lut(this->expLUT, idx0), v_sub(v_one, alpha0)))), knan0); + v_wsum0 = v_add(v_wsum0, w0); + v_sum0 = v_muladd(v_and(val0, knan0), w0, v_sum0); + + //1st + v_float32 val1 = vx_load(ksptr + nlanes); + v_float32 knan1 = v_not_nan(val1); + v_float32 alpha1 = v_and(v_and(v_mul(v_absdiff(val1, rval1), sindex), rval1_not_nan), knan1); + v_int32 idx1 = v_trunc(alpha1); + alpha1 = v_sub(alpha1, v_cvt_f32(idx1)); + v_float32 w1 = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx1), alpha1, v_mul(v_lut(this->expLUT, idx1), v_sub(v_one, alpha1)))), knan1); + v_wsum1 = v_add(v_wsum1, w1); + v_sum1 = v_muladd(v_and(val1, knan1), w1, v_sum1); + } + v_store(dptr, v_div(v_add(v_sum0, v_and(rval0, rval0_not_nan)), v_add(v_wsum0, v_and(v_one, rval0_not_nan)))); + v_store(dptr + nlanes, v_div(v_add(v_sum1, v_and(rval1, rval1_not_nan)), v_add(v_wsum1, v_and(v_one, rval1_not_nan)))); + } +#endif + for (; j < size.width; j++, sptr_j++, dptr++) + { + float rval = *sptr_j; + float wsum = 0.f; + float sum = 0.f; + for (k = 0; k < maxk; k++) + { + const float* ksptr = sptr_j + space_ofs[k]; + float val = *ksptr; float alpha = std::abs(val - rval) * scale_index; int idx = cvFloor(alpha); alpha -= idx; if (!cvIsNaN(val)) { - float w = space_weight[k] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; - } - - val = ksptr1[j]; - alpha = std::abs(val - rval) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!cvIsNaN(val)) - { - float w = space_weight[k+1] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; - } - - val = ksptr2[j]; - alpha = std::abs(val - rval) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!cvIsNaN(val)) - { - float w = space_weight[k+2] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; - } - - val = ksptr3[j]; - alpha = std::abs(val - rval) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!cvIsNaN(val)) - { - float w = space_weight[k+3] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; - } -#endif - } - } - for(; k < maxk; k++) - { - const float* ksptr = sptr + space_ofs[k]; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight = vx_setall_f32(space_weight[k]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) - { - v_float32 val = vx_load(ksptr + j); - v_float32 rval = vx_load(sptr + j); - v_float32 knan = v_not_nan(val); - v_float32 alpha = v_and(v_and(v_mul(v_absdiff(val, rval), sindex), v_not_nan(rval)), knan); - v_int32 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - - v_float32 w = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_store_aligned(wsum + j, v_add(vx_load_aligned(wsum + j), w)); - v_store_aligned(sum + j, v_muladd(v_and(val, knan), w, vx_load_aligned(sum + j))); - } -#endif - for (; j < size.width; j++) - { - float val = ksptr[j]; - float rval = sptr[j]; - float alpha = std::abs(val - rval) * scale_index; - int idx = cvFloor(alpha); - alpha -= idx; - if (!cvIsNaN(val)) - { - float w = space_weight[k] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum[j] += val * w; + float w = space_weight[k] * (cvIsNaN(rval) ? 1.f : (expLUT[idx] + alpha * (expLUT[idx + 1] - expLUT[idx]))); + wsum += w; + sum += val * w; } } - } - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes()) - { - v_float32 v_val = vx_load(sptr + j); - v_store(dptr + j, v_div(v_add(vx_load_aligned(sum + j), v_and(v_val, v_not_nan(v_val))), v_add(vx_load_aligned(wsum + j), v_and(v_one, v_not_nan(v_val))))); - } -#endif - for (; j < size.width; j++) - { - CV_DbgAssert(fabs(wsum[j]) >= 0); - dptr[j] = cvIsNaN(sptr[j]) ? sum[j] / wsum[j] : (sum[j] + sptr[j]) / (wsum[j] + 1.f); + CV_DbgAssert(fabs(wsum) >= 0); + *dptr = cvIsNaN(rval) ? sum / wsum : (sum + rval) / (wsum + 1.f); } } else { - CV_Assert( cn == 3 ); - AutoBuffer buf(alignSize(size.width, CV_SIMD_WIDTH)*3 + size.width + CV_SIMD_WIDTH - 1); - memset(buf.data(), 0, buf.size() * sizeof(float)); - float *sum_b = alignPtr(buf.data(), CV_SIMD_WIDTH); - float *sum_g = sum_b + alignSize(size.width, CV_SIMD_WIDTH); - float *sum_r = sum_g + alignSize(size.width, CV_SIMD_WIDTH); - float *wsum = sum_r + alignSize(size.width, CV_SIMD_WIDTH); + CV_Assert(cn == 3); + j = 0; + const float* sptr_j = sptr; #if (CV_SIMD || CV_SIMD_SCALABLE) v_float32 v_one = vx_setall_f32(1.f); v_float32 sindex = vx_setall_f32(scale_index); -#endif - k = 0; - for (; k <= maxk-4; k+=4) + + for (; j <= size.width - nlanes; j += nlanes, sptr_j += nlanes_3, dptr += nlanes_3) { - const float* ksptr0 = sptr + space_ofs[k]; - const float* ksptr1 = sptr + space_ofs[k+1]; - const float* ksptr2 = sptr + space_ofs[k+2]; - const float* ksptr3 = sptr + space_ofs[k+3]; - const float* rsptr = sptr; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight0 = vx_setall_f32(space_weight[k]); - v_float32 kweight1 = vx_setall_f32(space_weight[k+1]); - v_float32 kweight2 = vx_setall_f32(space_weight[k+2]); - v_float32 kweight3 = vx_setall_f32(space_weight[k+3]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), rsptr += 3 * VTraits::vlanes(), - ksptr0 += 3 * VTraits::vlanes(), ksptr1 += 3 * VTraits::vlanes(), ksptr2 += 3 * VTraits::vlanes(), ksptr3 += 3 * VTraits::vlanes()) + const float* rsptr = sptr_j; + v_float32 v_wsum = vx_setzero_f32(); + v_float32 v_sum_b = vx_setzero_f32(); + v_float32 v_sum_g = vx_setzero_f32(); + v_float32 v_sum_r = vx_setzero_f32(); + for (k = 0; k < maxk; k++) { - v_float32 kb, kg, kr, rb, rg, rr; - v_load_deinterleave(rsptr, rb, rg, rr); + const float* ksptr = sptr_j + space_ofs[k]; + v_float32 kweight = vx_setall_f32(space_weight[k]); - v_load_deinterleave(ksptr0, kb, kg, kr); - v_float32 knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - v_float32 alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - v_int32 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - v_float32 w = v_and(v_mul(kweight0, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_float32 v_wsum = v_add(vx_load_aligned(wsum + j), w); - v_float32 v_sum_b = v_muladd(v_and(kb, knan), w, vx_load_aligned(sum_b + j)); - v_float32 v_sum_g = v_muladd(v_and(kg, knan), w, vx_load_aligned(sum_g + j)); - v_float32 v_sum_r = v_muladd(v_and(kr, knan), w, vx_load_aligned(sum_r + j)); - - v_load_deinterleave(ksptr1, kb, kg, kr); - knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight1, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum_b = v_muladd(v_and(kb, knan), w, v_sum_b); - v_sum_g = v_muladd(v_and(kg, knan), w, v_sum_g); - v_sum_r = v_muladd(v_and(kr, knan), w, v_sum_r); - - v_load_deinterleave(ksptr2, kb, kg, kr); - knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight2, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum_b = v_muladd(v_and(kb, knan), w, v_sum_b); - v_sum_g = v_muladd(v_and(kg, knan), w, v_sum_g); - v_sum_r = v_muladd(v_and(kr, knan), w, v_sum_r); - - v_load_deinterleave(ksptr3, kb, kg, kr); - knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - w = v_and(v_mul(kweight3, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_wsum = v_add(v_wsum, w); - v_sum_b = v_muladd(v_and(kb, knan), w, v_sum_b); - v_sum_g = v_muladd(v_and(kg, knan), w, v_sum_g); - v_sum_r = v_muladd(v_and(kr, knan), w, v_sum_r); - - v_store_aligned(wsum + j, v_wsum); - v_store_aligned(sum_b + j, v_sum_b); - v_store_aligned(sum_g + j, v_sum_g); - v_store_aligned(sum_r + j, v_sum_r); - } -#endif -#if CV_SIMD128 - v_float32x4 v_one4 = v_setall_f32(1.f); - v_float32x4 sindex4 = v_setall_f32(scale_index); - v_float32x4 kweight4 = v_load(space_weight + k); -#endif - for (; j < size.width; j++, rsptr += 3, ksptr0 += 3, ksptr1 += 3, ksptr2 += 3, ksptr3 += 3) - { -#if CV_SIMD128 - v_float32x4 rb = v_setall_f32(rsptr[0]); - v_float32x4 rg = v_setall_f32(rsptr[1]); - v_float32x4 rr = v_setall_f32(rsptr[2]); - v_float32x4 kb(ksptr0[0], ksptr1[0], ksptr2[0], ksptr3[0]); - v_float32x4 kg(ksptr0[1], ksptr1[1], ksptr2[1], ksptr3[1]); - v_float32x4 kr(ksptr0[2], ksptr1[2], ksptr2[2], ksptr3[2]); - v_float32x4 knan = v_and(v_and(v_not_nan(kb), v_not_nan(kg)), v_not_nan(kr)); - v_float32x4 alpha = v_and(v_and(v_and(v_and(v_mul(v_add(v_add(v_absdiff(kb, rb), v_absdiff(kg, rg)), v_absdiff(kr, rr)), sindex4), v_not_nan(rb)), v_not_nan(rg)), v_not_nan(rr)), knan); - v_int32x4 idx = v_trunc(alpha); - alpha = v_sub(alpha, v_cvt_f32(idx)); - v_float32x4 w = v_and(v_mul(kweight4, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one4, alpha)))), knan); - wsum[j] += v_reduce_sum(w); - sum_b[j] += v_reduce_sum(v_mul(v_and(kb, knan), w)); - sum_g[j] += v_reduce_sum(v_mul(v_and(kg, knan), w)); - sum_r[j] += v_reduce_sum(v_mul(v_and(kr, knan), w)); -#else - float rb = rsptr[0], rg = rsptr[1], rr = rsptr[2]; - bool r_NAN = cvIsNaN(rb) || cvIsNaN(rg) || cvIsNaN(rr); - - float b = ksptr0[0], g = ksptr0[1], r = ksptr0[2]; - bool v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); - float alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; - int idx = cvFloor(alpha); - alpha -= idx; - if (!v_NAN) - { - float w = space_weight[k] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; - } - - b = ksptr1[0]; g = ksptr1[1]; r = ksptr1[2]; - v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); - alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!v_NAN) - { - float w = space_weight[k+1] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; - } - - b = ksptr2[0]; g = ksptr2[1]; r = ksptr2[2]; - v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); - alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!v_NAN) - { - float w = space_weight[k+2] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; - } - - b = ksptr3[0]; g = ksptr3[1]; r = ksptr3[2]; - v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); - alpha = (std::abs(b - rb) + std::abs(g - rg) + std::abs(r - rr)) * scale_index; - idx = cvFloor(alpha); - alpha -= idx; - if (!v_NAN) - { - float w = space_weight[k+3] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; - } -#endif - } - } - for (; k < maxk; k++) - { - const float* ksptr = sptr + space_ofs[k]; - const float* rsptr = sptr; - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - v_float32 kweight = vx_setall_f32(space_weight[k]); - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), ksptr += 3*VTraits::vlanes(), rsptr += 3*VTraits::vlanes()) - { v_float32 kb, kg, kr, rb, rg, rr; v_load_deinterleave(ksptr, kb, kg, kr); v_load_deinterleave(rsptr, rb, rg, rr); @@ -952,14 +685,26 @@ public: alpha = v_sub(alpha, v_cvt_f32(idx)); v_float32 w = v_and(v_mul(kweight, v_muladd(v_lut(this->expLUT + 1, idx), alpha, v_mul(v_lut(this->expLUT, idx), v_sub(v_one, alpha)))), knan); - v_store_aligned(wsum + j, v_add(vx_load_aligned(wsum + j), w)); - v_store_aligned(sum_b + j, v_muladd(v_and(kb, knan), w, vx_load_aligned(sum_b + j))); - v_store_aligned(sum_g + j, v_muladd(v_and(kg, knan), w, vx_load_aligned(sum_g + j))); - v_store_aligned(sum_r + j, v_muladd(v_and(kr, knan), w, vx_load_aligned(sum_r + j))); + v_wsum = v_add(v_wsum, w); + v_sum_b = v_muladd(v_and(kb, knan), w, v_sum_b); + v_sum_g = v_muladd(v_and(kg, knan), w, v_sum_g); + v_sum_r = v_muladd(v_and(kr, knan), w, v_sum_r); } + + v_float32 b, g, r; + v_load_deinterleave(sptr_j, b, g, r); + v_float32 mask = v_and(v_and(v_not_nan(b), v_not_nan(g)), v_not_nan(r)); + v_float32 w = v_div(v_one, v_add(v_wsum, v_and(v_one, mask))); + v_store_interleave(dptr, v_mul(v_add(v_sum_b, v_and(b, mask)), w), v_mul(v_add(v_sum_g, v_and(g, mask)), w), v_mul(v_add(v_sum_r, v_and(r, mask)), w)); + } #endif - for (; j < size.width; j++, ksptr += 3, rsptr += 3) + for (; j < size.width; j++, sptr_j += 3) + { + const float* rsptr = sptr_j; + float wsum = 0.f, sum_b = 0.f, sum_g = 0.f, sum_r = 0.f; + for (k = 0; k < maxk; k++) { + const float* ksptr = sptr_j + space_ofs[k]; float b = ksptr[0], g = ksptr[1], r = ksptr[2]; bool v_NAN = cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r); float rb = rsptr[0], rg = rsptr[1], rr = rsptr[2]; @@ -969,44 +714,31 @@ public: alpha -= idx; if (!v_NAN) { - float w = space_weight[k] * (r_NAN ? 1.f : (expLUT[idx] + alpha*(expLUT[idx + 1] - expLUT[idx]))); - wsum[j] += w; - sum_b[j] += b*w; - sum_g[j] += g*w; - sum_r[j] += r*w; + float w = space_weight[k] * (r_NAN ? 1.f : (expLUT[idx] + alpha * (expLUT[idx + 1] - expLUT[idx]))); + wsum += w; + sum_b += b * w; + sum_g += g * w; + sum_r += r * w; } } - } - j = 0; -#if (CV_SIMD || CV_SIMD_SCALABLE) - for (; j <= size.width - VTraits::vlanes(); j += VTraits::vlanes(), sptr += 3*VTraits::vlanes(), dptr += 3*VTraits::vlanes()) - { - v_float32 b, g, r; - v_load_deinterleave(sptr, b, g, r); - v_float32 mask = v_and(v_and(v_not_nan(b), v_not_nan(g)), v_not_nan(r)); - v_float32 w = v_div(v_one, v_add(vx_load_aligned(wsum + j), v_and(v_one, mask))); - v_store_interleave(dptr, v_mul(v_add(vx_load_aligned(sum_b + j), v_and(b, mask)), w), v_mul(v_add(vx_load_aligned(sum_g + j), v_and(g, mask)), w), v_mul(v_add(vx_load_aligned(sum_r + j), v_and(r, mask)), w)); - } -#endif - for (; j < size.width; j++) - { - CV_DbgAssert(fabs(wsum[j]) >= 0); - float b = *(sptr++); - float g = *(sptr++); - float r = *(sptr++); + + CV_DbgAssert(fabs(wsum) >= 0); + float b = *(sptr_j); + float g = *(sptr_j+1); + float r = *(sptr_j+2); if (cvIsNaN(b) || cvIsNaN(g) || cvIsNaN(r)) { - wsum[j] = 1.f / wsum[j]; - *(dptr++) = sum_b[j] * wsum[j]; - *(dptr++) = sum_g[j] * wsum[j]; - *(dptr++) = sum_r[j] * wsum[j]; + wsum = 1.f / wsum; + *(dptr++) = sum_b * wsum; + *(dptr++) = sum_g * wsum; + *(dptr++) = sum_r * wsum; } else { - wsum[j] = 1.f / (wsum[j] + 1.f); - *(dptr++) = (sum_b[j] + b) * wsum[j]; - *(dptr++) = (sum_g[j] + g) * wsum[j]; - *(dptr++) = (sum_r[j] + r) * wsum[j]; + wsum = 1.f / (wsum + 1.f); + *(dptr++) = (sum_b + b) * wsum; + *(dptr++) = (sum_g + g) * wsum; + *(dptr++) = (sum_r + r) * wsum; } } } diff --git a/modules/imgproc/src/drawing.cpp b/modules/imgproc/src/drawing.cpp index 5e2906a171..3f4f62046c 100644 --- a/modules/imgproc/src/drawing.cpp +++ b/modules/imgproc/src/drawing.cpp @@ -2035,9 +2035,12 @@ void fillPoly( InputOutputArray _img, const Point** pts, const int* npts, int nc edges.reserve( total + 1 ); for (i = 0; i < ncontours; i++) { - if (npts[i] > 0 && pts[i]) + const Point* currentContour = pts[i]; + const int currentContourLength = npts[i]; + if ( (currentContourLength > 0) && currentContour ) { - std::vector _pts(pts[i], pts[i] + npts[i]); + AutoBuffer _pts(currentContourLength); + std::copy(currentContour, currentContour+currentContourLength, _pts.data()); CollectPolyEdges(img, _pts.data(), npts[i], edges, buf, line_type, shift, offset); } } @@ -2064,8 +2067,11 @@ void polylines( InputOutputArray _img, const Point* const* pts, const int* npts, for( int i = 0; i < ncontours; i++ ) { - std::vector _pts(pts[i], pts[i]+npts[i]); - PolyLine( img, _pts.data(), npts[i], isClosed, buf, thickness, line_type, shift ); + const Point* currentContour = pts[i]; + const int currentContourLength = npts[i]; + AutoBuffer _pts(currentContourLength); + std::copy(currentContour, currentContour+currentContourLength, _pts.data()); + PolyLine( img, _pts.data(), currentContourLength, isClosed, buf, thickness, line_type, shift ); } } diff --git a/modules/java/jar/MANIFEST.MF.in b/modules/java/jar/MANIFEST.MF.in index 07d2fda79b..b26719ce22 100644 --- a/modules/java/jar/MANIFEST.MF.in +++ b/modules/java/jar/MANIFEST.MF.in @@ -3,3 +3,4 @@ Specification-Version: @OPENCV_VERSION@ Implementation-Title: OpenCV Implementation-Version: @OPENCV_VCSVERSION@ Implementation-Date: @OPENCV_TIMESTAMP@ +Automatic-Module-Name: org.opencv diff --git a/modules/java/jar/build.xml.in b/modules/java/jar/build.xml.in index 41ef1d55ba..58bd27727b 100644 --- a/modules/java/jar/build.xml.in +++ b/modules/java/jar/build.xml.in @@ -24,6 +24,7 @@ + diff --git a/modules/js/src/helpers.js b/modules/js/src/helpers.js index d89414209a..ab88d18eec 100644 --- a/modules/js/src/helpers.js +++ b/modules/js/src/helpers.js @@ -397,3 +397,16 @@ Module['matFromImageData'] = function(imageData) { mat.data.set(imageData.data); return mat; }; +// Add Symbol.dispose support for using declaration in TypeScript 5.2+ and future JS +if ( + typeof Symbol !== "undefined" && + Symbol.dispose && + typeof cv !== "undefined" && + cv.Mat && + typeof cv.Mat.prototype.delete === "function" +) { + cv.Mat.prototype[Symbol.dispose] = cv.Mat.prototype.delete; + // Optionally repeat for other types that require manual cleanup: + if (cv.UMat) cv.UMat.prototype[Symbol.dispose] = cv.UMat.prototype.delete; + // Add more as OpenCV gains new manual-cleanup classes +} diff --git a/modules/videoio/src/cap_dshow.hpp b/modules/videoio/src/cap_dshow.hpp index 732ba81d8d..21af1ea399 100644 --- a/modules/videoio/src/cap_dshow.hpp +++ b/modules/videoio/src/cap_dshow.hpp @@ -30,7 +30,7 @@ public: virtual bool grabFrame() CV_OVERRIDE; virtual bool retrieveFrame(int outputType, OutputArray frame) CV_OVERRIDE; virtual int getCaptureDomain() CV_OVERRIDE; - virtual bool isOpened() const; + virtual bool isOpened() const CV_OVERRIDE; protected: void open(int index); void close(); diff --git a/modules/videoio/src/cap_images.cpp b/modules/videoio/src/cap_images.cpp index f472ff711b..1d7190003e 100644 --- a/modules/videoio/src/cap_images.cpp +++ b/modules/videoio/src/cap_images.cpp @@ -389,12 +389,8 @@ void CvVideoWriter_Images::write(InputArray image) cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe); CV_Assert(!filename.empty()); - std::vector image_params = params; - image_params.push_back(0); // append parameters 'stop' mark - image_params.push_back(0); - cv::Mat img = image.getMat(); - cv::imwrite(filename, img, image_params); + cv::imwrite(filename, img); currentframe++; }