diff --git a/3rdparty/libjpeg-turbo/CMakeLists.txt b/3rdparty/libjpeg-turbo/CMakeLists.txt
index fe9b574805..b257116755 100644
--- a/3rdparty/libjpeg-turbo/CMakeLists.txt
+++ b/3rdparty/libjpeg-turbo/CMakeLists.txt
@@ -136,9 +136,6 @@ endif()
set(JPEG_LIB_VERSION 70)
-# OpenCV
-set(JPEG_LIB_VERSION "${VERSION}-${JPEG_LIB_VERSION}" PARENT_SCOPE)
-
set(THREAD_LOCAL "") # WITH_TURBOJPEG is not used
add_definitions(-DNO_GETENV -DNO_PUTENV)
diff --git a/cmake/OpenCVFindLibsGrfmt.cmake b/cmake/OpenCVFindLibsGrfmt.cmake
index 55a0a7fbde..73e23a580f 100644
--- a/cmake/OpenCVFindLibsGrfmt.cmake
+++ b/cmake/OpenCVFindLibsGrfmt.cmake
@@ -95,8 +95,23 @@ if(WITH_JPEG)
macro(ocv_detect_jpeg_version header_file)
if(NOT DEFINED JPEG_LIB_VERSION AND EXISTS "${header_file}")
ocv_parse_header("${header_file}" JPEG_VERSION_LINES JPEG_LIB_VERSION)
+
+ if(DEFINED JPEG_LIB_VERSION)
+ # Extract libjpeg-turbo version from the header file if JPEG_LIB_VERSION is found.
+ file(STRINGS "${header_file}" JPEG_TURBO_VERSION_LINE REGEX "^#define[\t ]+LIBJPEG_TURBO_VERSION[\t ]")
+
+ if(JPEG_TURBO_VERSION_LINE)
+ # Support both raw values (e.g., 3.1.2) and quoted strings (e.g., "3.1.2").
+ string(REGEX REPLACE "^#define[\t ]+LIBJPEG_TURBO_VERSION[\t ]+\"?([^\"]+)\"?.*" "\\1" JPEG_TURBO_VERSION_STRING "${JPEG_TURBO_VERSION_LINE}")
+ if(JPEG_TURBO_VERSION_STRING)
+ string(STRIP "${JPEG_TURBO_VERSION_STRING}" JPEG_TURBO_VERSION_STRING)
+ set(JPEG_LIB_VERSION "${JPEG_TURBO_VERSION_STRING}-${JPEG_LIB_VERSION}")
+ endif()
+ endif()
+ endif()
endif()
endmacro()
+
ocv_detect_jpeg_version("${JPEG_INCLUDE_DIR}/jpeglib.h")
if(DEFINED CMAKE_CXX_LIBRARY_ARCHITECTURE)
ocv_detect_jpeg_version("${JPEG_INCLUDE_DIR}/${CMAKE_CXX_LIBRARY_ARCHITECTURE}/jconfig.h")
diff --git a/cmake/OpenCVInstallLayout.cmake b/cmake/OpenCVInstallLayout.cmake
index bbe5f7b5c3..e92b5f6755 100644
--- a/cmake/OpenCVInstallLayout.cmake
+++ b/cmake/OpenCVInstallLayout.cmake
@@ -96,7 +96,11 @@ else() # UNIX
endif()
-ocv_update(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${OPENCV_LIB_INSTALL_PATH}")
+if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS)
+ ocv_update(CMAKE_INSTALL_RPATH "@executable_path/Frameworks;@loader_path/Frameworks;@executable_path/../${OPENCV_3P_LIB_INSTALL_PATH}")
+else()
+ ocv_update(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${OPENCV_LIB_INSTALL_PATH}")
+endif()
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
if(INSTALL_TO_MANGLED_PATHS)
diff --git a/cmake/platforms/OpenCV-Darwin.cmake b/cmake/platforms/OpenCV-Darwin.cmake
index 1bb8bf6d7f..5f6c32ac4d 100644
--- a/cmake/platforms/OpenCV-Darwin.cmake
+++ b/cmake/platforms/OpenCV-Darwin.cmake
@@ -1 +1,8 @@
-# empty
+# Additional flags for dynamic framework (macOS)
+if(APPLE_FRAMEWORK AND BUILD_SHARED_LIBS)
+ string(APPEND CMAKE_MODULE_LINKER_FLAGS " -rpath @executable_path/Frameworks -rpath @loader_path/Frameworks")
+ string(APPEND CMAKE_SHARED_LINKER_FLAGS " -rpath @executable_path/Frameworks -rpath @loader_path/Frameworks")
+ string(APPEND CMAKE_EXE_LINKER_FLAGS " -rpath @executable_path/Frameworks -rpath @loader_path/Frameworks")
+ set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG 1)
+ set(CMAKE_INSTALL_NAME_DIR "@rpath")
+endif()
diff --git a/cmake/templates/OpenCVConfig-CUDALanguage.cmake.in b/cmake/templates/OpenCVConfig-CUDALanguage.cmake.in
index 259141006a..44d8de8225 100644
--- a/cmake/templates/OpenCVConfig-CUDALanguage.cmake.in
+++ b/cmake/templates/OpenCVConfig-CUDALanguage.cmake.in
@@ -10,6 +10,14 @@ set(OpenCV_CUDNN_VERSION "@CUDNN_VERSION@")
set(OpenCV_USE_CUDNN "@HAVE_CUDNN@")
set(ENABLE_CUDA_FIRST_CLASS_LANGUAGE ON)
+# By default OpenCV only requires the consumer's CUDA Toolkit to match the
+# major.minor version it was built with. The CUDA runtime API is stable within
+# a minor release and patch versions change frequently, so pinning the patch
+# component forces needless rebuilds (issue #26965). Set
+# OPENCV_STRONG_CUDA_VERSION_CHECK before find_package(OpenCV) to require an
+# exact match including the patch component.
+string(REGEX MATCH "^[0-9]+\\.[0-9]+" OpenCV_CUDA_VERSION_MAJOR_MINOR "${OpenCV_CUDA_VERSION}")
+
if(NOT CUDAToolkit_FOUND)
if(NOT CMAKE_VERSION VERSION_LESS 3.18)
if(UNIX AND NOT CMAKE_CUDA_COMPILER AND NOT CUDAToolkit_ROOT)
@@ -17,15 +25,28 @@ if(NOT CUDAToolkit_FOUND)
set(CUDA_PATH "/usr/local/cuda" CACHE INTERNAL "")
set(ENV{CUDA_PATH} ${CUDA_PATH})
endif()
- find_package(CUDAToolkit ${OpenCV_CUDA_VERSION} EXACT REQUIRED)
+ if(OPENCV_STRONG_CUDA_VERSION_CHECK)
+ find_package(CUDAToolkit ${OpenCV_CUDA_VERSION} EXACT REQUIRED)
+ else()
+ find_package(CUDAToolkit ${OpenCV_CUDA_VERSION_MAJOR_MINOR} REQUIRED)
+ endif()
else()
message(FATAL_ERROR "Using OpenCV compiled with CUDA as first class language requires CMake \>= 3.18.")
endif()
-else()
- if(CUDAToolkit_FOUND)
- set(CUDA_VERSION_STRING ${CUDAToolkit_VERSION})
- endif()
- if(NOT CUDA_VERSION_STRING VERSION_EQUAL OpenCV_CUDA_VERSION)
- message(FATAL_ERROR "OpenCV library was compiled with CUDA ${OpenCV_CUDA_VERSION} support. Please, use the same version or rebuild OpenCV with CUDA ${CUDA_VERSION_STRING}")
- endif()
+endif()
+
+if(CUDAToolkit_FOUND)
+ set(CUDA_VERSION_STRING ${CUDAToolkit_VERSION})
+endif()
+
+string(REGEX MATCH "^[0-9]+\\.[0-9]+" CUDA_VERSION_STRING_MAJOR_MINOR "${CUDA_VERSION_STRING}")
+if(OPENCV_STRONG_CUDA_VERSION_CHECK)
+ set(__ocv_cuda_found "${CUDA_VERSION_STRING}")
+ set(__ocv_cuda_built "${OpenCV_CUDA_VERSION}")
+else()
+ set(__ocv_cuda_found "${CUDA_VERSION_STRING_MAJOR_MINOR}")
+ set(__ocv_cuda_built "${OpenCV_CUDA_VERSION_MAJOR_MINOR}")
+endif()
+if(NOT __ocv_cuda_found VERSION_EQUAL __ocv_cuda_built)
+ message(FATAL_ERROR "OpenCV library was compiled with CUDA ${OpenCV_CUDA_VERSION} support. Please, use the same version or rebuild OpenCV with CUDA ${CUDA_VERSION_STRING}")
endif()
diff --git a/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown b/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown
index ee110c37cb..19b7cc6157 100644
--- a/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown
+++ b/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown
@@ -105,7 +105,7 @@ let mat = new cv.Mat();
let matVec = new cv.MatVector();
// Push a Mat back into MatVector
matVec.push_back(mat);
-// Get a Mat fom MatVector
+// Get a Mat from MatVector
let cnt = matVec.get(0);
mat.delete(); matVec.delete(); cnt.delete();
@endcode
diff --git a/doc/js_tutorials/js_imgproc/js_morphological_ops/js_morphological_ops.markdown b/doc/js_tutorials/js_imgproc/js_morphological_ops/js_morphological_ops.markdown
index 09cad1003c..5564c8a6e9 100644
--- a/doc/js_tutorials/js_imgproc/js_morphological_ops/js_morphological_ops.markdown
+++ b/doc/js_tutorials/js_imgproc/js_morphological_ops/js_morphological_ops.markdown
@@ -27,7 +27,7 @@ foreground object (Always try to keep foreground in white). So what it does? The
through the image (as in 2D convolution). A pixel in the original image (either 1 or 0) will be
considered 1 only if all the pixels under the kernel is 1, otherwise it is eroded (made to zero).
-So what happends is that, all the pixels near boundary will be discarded depending upon the size of
+So what happens is that, all the pixels near boundary will be discarded depending upon the size of
kernel. So the thickness or size of the foreground object decreases or simply white region decreases
in the image. It is useful for removing small white noises (as we have seen in colorspace chapter),
detach two connected objects etc.
@@ -174,4 +174,4 @@ Try it
-\endhtmlonly
\ No newline at end of file
+\endhtmlonly
diff --git a/doc/opencv.bib b/doc/opencv.bib
index aa44dcbe1b..80e789eaf5 100644
--- a/doc/opencv.bib
+++ b/doc/opencv.bib
@@ -1265,7 +1265,7 @@
@article{TRUCO2026,
title={TRUCO: A Scalable Lock-Free Algorithm for Parallel Contour Extraction},
author={Mu\~noz-Salinas, Rafael and Romero-Ramírez, Francisco J. and Marín-Jiménez, Manuel J.},
- journal={Pattern Recognition under review},
+ journal={To be published},
year={2026}
}
diff --git a/doc/tutorials/app/highgui_wayland_ubuntu.markdown b/doc/tutorials/app/highgui_wayland_ubuntu.markdown
index 2b8020ad19..c1aa7d19c5 100644
--- a/doc/tutorials/app/highgui_wayland_ubuntu.markdown
+++ b/doc/tutorials/app/highgui_wayland_ubuntu.markdown
@@ -103,4 +103,4 @@ int main(void)
Limitation/Known problem
------------------------
-- cv::moveWindow() is not implementated. ( See. https://github.com/opencv/opencv/issues/25478 )
+- cv::moveWindow() is not implemented. ( See. https://github.com/opencv/opencv/issues/25478 )
diff --git a/doc/tutorials/app/orbbec_astra_openni.markdown b/doc/tutorials/app/orbbec_astra_openni.markdown
index 0b5ae5a8c5..6b1d8a88f3 100644
--- a/doc/tutorials/app/orbbec_astra_openni.markdown
+++ b/doc/tutorials/app/orbbec_astra_openni.markdown
@@ -72,8 +72,8 @@ In order to use the Astra camera's depth sensor with OpenCV you should do the fo
@note The last tried version `2.3.0.86_202210111154_4c8f5aa4_beta6` does not work correctly with
modern Linux, even after libusb rebuild as recommended by the instruction. The last know good
- configuration is version 2.3.0.63 (tested with Ubuntu 18.04 amd64). It's not provided officialy
- with the downloading page, but published by Orbbec technical suport on Orbbec community forum
+ configuration is version 2.3.0.63 (tested with Ubuntu 18.04 amd64). It's not provided officially
+ with the downloading page, but published by Orbbec technical support on Orbbec community forum
[here](https://3dclub.orbbec3d.com/t/universal-download-thread-for-astra-series-cameras/622).
-# Now you can configure OpenCV with OpenNI support enabled by setting the `WITH_OPENNI2` flag in CMake.
diff --git a/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown b/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown
index 94cb37e5b5..dae706d124 100644
--- a/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown
+++ b/doc/tutorials/calib3d/camera_calibration_pattern/camera_calibration_pattern.markdown
@@ -62,7 +62,7 @@ Example code to generate features coordinates for calibration with symmetric gri
}
}
```
-Example code to generate features corrdinates for calibration with asymmetic grid (object points):
+Example code to generate features coordinates for calibration with asymmetric grid (object points):
```
std::vector objectPoints;
for (int i = 0; i < boardSize.height; i++) {
@@ -83,7 +83,7 @@ about ArUco pairs. In opposite to the previous pattern partially occluded board
corners are labeled. The board is rotation invariant, but set of ArUco markers and their order
should be known to detector apriori. It cannot detect ChAruco board with predefined size and random
set of markers.
-Example code to generate features corrdinates for calibration (object points) for board size in units:
+Example code to generate features coordinates for calibration (object points) for board size in units:
```
std::vector objectPoints;
for (int i = 0; i < boardSize.height-1; ++i) {
diff --git a/doc/tutorials/dnn/dnn_text_spotting/dnn_text_spotting.markdown b/doc/tutorials/dnn/dnn_text_spotting/dnn_text_spotting.markdown
index 6948c30563..46a6dea7d8 100644
--- a/doc/tutorials/dnn/dnn_text_spotting/dnn_text_spotting.markdown
+++ b/doc/tutorials/dnn/dnn_text_spotting/dnn_text_spotting.markdown
@@ -65,25 +65,25 @@ We encourage you to add new algorithms to these APIs.
```
crnn.onnx:
-url: https://drive.google.com/uc?export=dowload&id=1ooaLR-rkTl8jdpGy1DoQs0-X0lQsB6Fj
+url: https://drive.google.com/uc?export=download&id=1ooaLR-rkTl8jdpGy1DoQs0-X0lQsB6Fj
sha: 270d92c9ccb670ada2459a25977e8deeaf8380d3,
-alphabet_36.txt: https://drive.google.com/uc?export=dowload&id=1oPOYx5rQRp8L6XQciUwmwhMCfX0KyO4b
+alphabet_36.txt: https://drive.google.com/uc?export=download&id=1oPOYx5rQRp8L6XQciUwmwhMCfX0KyO4b
parameter setting: -rgb=0;
description: The classification number of this model is 36 (0~9 + a~z).
The training dataset is MJSynth.
crnn_cs.onnx:
-url: https://drive.google.com/uc?export=dowload&id=12diBsVJrS9ZEl6BNUiRp9s0xPALBS7kt
+url: https://drive.google.com/uc?export=download&id=12diBsVJrS9ZEl6BNUiRp9s0xPALBS7kt
sha: a641e9c57a5147546f7a2dbea4fd322b47197cd5
-alphabet_94.txt: https://drive.google.com/uc?export=dowload&id=1oKXxXKusquimp7XY1mFvj9nwLzldVgBR
+alphabet_94.txt: https://drive.google.com/uc?export=download&id=1oKXxXKusquimp7XY1mFvj9nwLzldVgBR
parameter setting: -rgb=1;
description: The classification number of this model is 94 (0~9 + a~z + A~Z + punctuations).
The training datasets are MJsynth and SynthText.
crnn_cs_CN.onnx:
-url: https://drive.google.com/uc?export=dowload&id=1is4eYEUKH7HR7Gl37Sw4WPXx6Ir8oQEG
+url: https://drive.google.com/uc?export=download&id=1is4eYEUKH7HR7Gl37Sw4WPXx6Ir8oQEG
sha: 3940942b85761c7f240494cf662dcbf05dc00d14
-alphabet_3944.txt: https://drive.google.com/uc?export=dowload&id=18IZUUdNzJ44heWTndDO6NNfIpJMmN-ul
+alphabet_3944.txt: https://drive.google.com/uc?export=download&id=18IZUUdNzJ44heWTndDO6NNfIpJMmN-ul
parameter setting: -rgb=1;
description: The classification number of this model is 3944 (0~9 + a~z + A~Z + Chinese characters + special characters).
The training dataset is ReCTS (https://rrc.cvc.uab.es/?ch=12).
@@ -97,25 +97,25 @@ You can train more models by [CRNN](https://github.com/meijieru/crnn.pytorch), a
```
- DB_IC15_resnet50.onnx:
-url: https://drive.google.com/uc?export=dowload&id=17_ABp79PlFt9yPCxSaarVc_DKTmrSGGf
+url: https://drive.google.com/uc?export=download&id=17_ABp79PlFt9yPCxSaarVc_DKTmrSGGf
sha: bef233c28947ef6ec8c663d20a2b326302421fa3
recommended parameter setting: -inputHeight=736, -inputWidth=1280;
description: This model is trained on ICDAR2015, so it can only detect English text instances.
- DB_IC15_resnet18.onnx:
-url: https://drive.google.com/uc?export=dowload&id=1vY_KsDZZZb_svd5RT6pjyI8BS1nPbBSX
+url: https://drive.google.com/uc?export=download&id=1vY_KsDZZZb_svd5RT6pjyI8BS1nPbBSX
sha: 19543ce09b2efd35f49705c235cc46d0e22df30b
recommended parameter setting: -inputHeight=736, -inputWidth=1280;
description: This model is trained on ICDAR2015, so it can only detect English text instances.
- DB_TD500_resnet50.onnx:
-url: https://drive.google.com/uc?export=dowload&id=19YWhArrNccaoSza0CfkXlA8im4-lAGsR
+url: https://drive.google.com/uc?export=download&id=19YWhArrNccaoSza0CfkXlA8im4-lAGsR
sha: 1b4dd21a6baa5e3523156776970895bd3db6960a
recommended parameter setting: -inputHeight=736, -inputWidth=736;
description: This model is trained on MSRA-TD500, so it can detect both English and Chinese text instances.
- DB_TD500_resnet18.onnx:
-url: https://drive.google.com/uc?export=dowload&id=1sZszH3pEt8hliyBlTmB-iulxHP1dCQWV
+url: https://drive.google.com/uc?export=download&id=1sZszH3pEt8hliyBlTmB-iulxHP1dCQWV
sha: 8a3700bdc13e00336a815fc7afff5dcc1ce08546
recommended parameter setting: -inputHeight=736, -inputWidth=736;
description: This model is trained on MSRA-TD500, so it can detect both English and Chinese text instances.
@@ -134,11 +134,11 @@ This model is based on https://github.com/argman/EAST
```
Text Recognition:
-url: https://drive.google.com/uc?export=dowload&id=1nMcEy68zDNpIlqAn6xCk_kYcUTIeSOtN
+url: https://drive.google.com/uc?export=download&id=1nMcEy68zDNpIlqAn6xCk_kYcUTIeSOtN
sha: 89205612ce8dd2251effa16609342b69bff67ca3
Text Detection:
-url: https://drive.google.com/uc?export=dowload&id=149tAhIcvfCYeyufRoZ9tmc2mZDKE_XrF
+url: https://drive.google.com/uc?export=download&id=149tAhIcvfCYeyufRoZ9tmc2mZDKE_XrF
sha: ced3c03fb7f8d9608169a913acf7e7b93e07109b
```
diff --git a/doc/tutorials/dnn/dnn_yolo/dnn_yolo.markdown b/doc/tutorials/dnn/dnn_yolo/dnn_yolo.markdown
index 81eb67d44d..c286b5189a 100644
--- a/doc/tutorials/dnn/dnn_yolo/dnn_yolo.markdown
+++ b/doc/tutorials/dnn/dnn_yolo/dnn_yolo.markdown
@@ -128,7 +128,7 @@ than YOLOX) in case it is needed. However, usually each YOLO repository has pred
#### Exporting YOLOv10 model
-In oder to run YOLOv10 one needs to cut off postporcessing with dynamic shapes from torch and then convert it to ONNX. If someone is looking for on how to cut off the postprocessing, there is this [forked branch](https://github.com/Abdurrahheem/yolov10/tree/ash/opencv-export) from official YOLOv10. The forked branch cuts of the postprocessing by [returning output](https://github.com/Abdurrahheem/yolov10/blob/4fdaafd912c8891642bfbe85751ea66ec20f05ad/ultralytics/nn/modules/head.py#L522) of the model before postprocessing procedure itself. To convert torch model to ONNX follow this proceduce.
+In order to run YOLOv10 one needs to cut off postprocessing with dynamic shapes from torch and then convert it to ONNX. If someone is looking for on how to cut off the postprocessing, there is this [forked branch](https://github.com/Abdurrahheem/yolov10/tree/ash/opencv-export) from official YOLOv10. The forked branch cuts off the postprocessing by [returning output](https://github.com/Abdurrahheem/yolov10/blob/4fdaafd912c8891642bfbe85751ea66ec20f05ad/ultralytics/nn/modules/head.py#L522) of the model before postprocessing procedure itself. To convert torch model to ONNX follow this procedure.
@code{.bash}
git clone git@github.com:Abdurrahheem/yolov10.git
diff --git a/doc/tutorials/objdetect/aruco_faq/aruco_faq.markdown b/doc/tutorials/objdetect/aruco_faq/aruco_faq.markdown
index 232bf49839..da23634315 100644
--- a/doc/tutorials/objdetect/aruco_faq/aruco_faq.markdown
+++ b/doc/tutorials/objdetect/aruco_faq/aruco_faq.markdown
@@ -168,7 +168,7 @@ for `cv::aruco::Dictionary`. The data member of board classes are public and can
To do so, you will need to use an external rendering engine library, such as OpenGL. The aruco module
only provides the functionality to obtain the camera pose, i.e. the rotation and translation vectors,
which is necessary to create the augmented reality effect. However, you will need to adapt the rotation
-and traslation vectors from the OpenCV format to the format accepted by your 3d rendering library.
+and translation vectors from the OpenCV format to the format accepted by your 3d rendering library.
The original ArUco library contains examples of how to do it for OpenGL and Ogre3D.
diff --git a/doc/tutorials/objdetect/charuco_diamond_detection/charuco_diamond_detection.markdown b/doc/tutorials/objdetect/charuco_diamond_detection/charuco_diamond_detection.markdown
index 04ae79ded0..a962861d80 100644
--- a/doc/tutorials/objdetect/charuco_diamond_detection/charuco_diamond_detection.markdown
+++ b/doc/tutorials/objdetect/charuco_diamond_detection/charuco_diamond_detection.markdown
@@ -12,7 +12,7 @@ It is similar to a ChArUco board in appearance, however they are conceptually di
In both, ChArUco board and Diamond markers, their detection is based on the previous detected ArUco
markers. In the ChArUco case, the used markers are selected by directly looking their identifiers. This means
that if a marker (included in the board) is found on a image, it will be automatically assumed to belong to the board. Furthermore,
-if a marker board is found more than once in the image, it will produce an ambiguity since the system wont
+if a marker board is found more than once in the image, it will produce an ambiguity since the system won't
be able to know which one should be used for the Board.
On the other hand, the detection of Diamond marker is not based on the identifiers. Instead, their detection
diff --git a/hal/carotene/include/carotene/functions.hpp b/hal/carotene/include/carotene/functions.hpp
index 15a12e765b..31a5d0af94 100644
--- a/hal/carotene/include/carotene/functions.hpp
+++ b/hal/carotene/include/carotene/functions.hpp
@@ -2285,7 +2285,7 @@ namespace CAROTENE_NS {
f64 alpha, f64 beta);
/*
- Reduce matrix to a vector by calculatin given operation for each column
+ Reduce matrix to a vector by calculating given operation for each column
*/
void reduceColSum(const Size2D &size,
const u8 * srcBase, ptrdiff_t srcStride,
diff --git a/hal/carotene/src/accumulate.cpp b/hal/carotene/src/accumulate.cpp
index ee9ce22d35..6ab86ae2dd 100644
--- a/hal/carotene/src/accumulate.cpp
+++ b/hal/carotene/src/accumulate.cpp
@@ -231,7 +231,7 @@ void accumulateSquare(const Size2D &size,
internal::assertSupportedConfiguration();
#ifdef CAROTENE_NEON
- // this ugly contruction is needed to avoid:
+ // this ugly construction is needed to avoid:
// /usr/lib/gcc/arm-linux-gnueabihf/4.8/include/arm_neon.h:3581:59: error: argument must be a constant
// return (int16x8_t)__builtin_neon_vshr_nv8hi (__a, __b, 1);
diff --git a/hal/carotene/src/canny.cpp b/hal/carotene/src/canny.cpp
index f61bc23e9b..b668d85cbe 100644
--- a/hal/carotene/src/canny.cpp
+++ b/hal/carotene/src/canny.cpp
@@ -524,7 +524,7 @@ inline void Canny3x3(const Size2D &size, s32 cn,
//i == 0
normEstimator.firstRow(size, cn, srcBase, srcStride, dxBase, dxStride, dyBase, dyStride, mag_buf);
- // calculate magnitude and angle of gradient, perform non-maxima supression.
+ // calculate magnitude and angle of gradient, perform non-maxima suppression.
// fill the map with one of the following values:
// 0 - the pixel might belong to an edge
// 1 - the pixel can not belong to an edge
diff --git a/hal/carotene/src/intrinsics.hpp b/hal/carotene/src/intrinsics.hpp
index 062a3f897b..5b04bc91a1 100644
--- a/hal/carotene/src/intrinsics.hpp
+++ b/hal/carotene/src/intrinsics.hpp
@@ -66,7 +66,7 @@ inline float32x2_t vrecp_f32(float32x2_t val)
return reciprocal;
}
-// caclulate sqrt value
+// calculate sqrt value
inline float32x4_t vrsqrtq_f32(float32x4_t val)
{
diff --git a/hal/ipp/CMakeLists.txt b/hal/ipp/CMakeLists.txt
index 9318b931d1..beaa91e7f3 100644
--- a/hal/ipp/CMakeLists.txt
+++ b/hal/ipp/CMakeLists.txt
@@ -9,6 +9,7 @@ set(IPP_HAL_HEADERS
CACHE INTERNAL "")
add_library(ipphal STATIC
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/math_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/mean_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/minmax_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/norm_ipp.cpp"
@@ -16,8 +17,11 @@ add_library(ipphal STATIC
"${CMAKE_CURRENT_SOURCE_DIR}/src/color_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/transforms_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/warp_ipp.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/deriv_ipp.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/resize_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/box_filter_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/sum_ipp.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/color_ipp.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/filter_ipp.cpp"
)
diff --git a/hal/ipp/include/ipp_hal_core.hpp b/hal/ipp/include/ipp_hal_core.hpp
index 7b71c8d28a..359764a7bf 100644
--- a/hal/ipp/include/ipp_hal_core.hpp
+++ b/hal/ipp/include/ipp_hal_core.hpp
@@ -12,8 +12,10 @@
int ipp_hal_meanStdDev(const uchar* src_data, size_t src_step, int width, int height, int src_type,
double* mean_val, double* stddev_val, uchar* mask, size_t mask_step);
-#undef cv_hal_meanStdDev
-#define cv_hal_meanStdDev ipp_hal_meanStdDev
+// IPP version is less efficient than implementation with universtal intrinsics
+// See https://github.com/opencv/opencv/pull/29339
+//#undef cv_hal_meanStdDev
+//#define cv_hal_meanStdDev ipp_hal_meanStdDev
int ipp_hal_minMaxIdxMaskStep(const uchar* src_data, size_t src_step, int width, int height, int depth,
double* _minVal, double* _maxVal, int* _minIdx, int* _maxIdx, uchar* mask, size_t mask_step);
@@ -74,6 +76,33 @@ int ipp_hal_transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data,
#undef cv_hal_transpose2d
#define cv_hal_transpose2d ipp_hal_transpose2d
+int ipp_hal_invSqrt32f(const float* src, float* dst, int len);
+int ipp_hal_invSqrt64f(const double* src, double* dst, int len);
+
+#undef cv_hal_invSqrt32f
+#define cv_hal_invSqrt32f ipp_hal_invSqrt32f
+
+#undef cv_hal_invSqrt64f
+#define cv_hal_invSqrt64f ipp_hal_invSqrt64f
+
+int ipp_hal_exp32f(const float* src, float* dst, int len);
+int ipp_hal_exp64f(const double* src, double* dst, int len);
+
+#undef cv_hal_exp32f
+#define cv_hal_exp32f ipp_hal_exp32f
+
+#undef cv_hal_exp64f
+#define cv_hal_exp64f ipp_hal_exp64f
+
+int ipp_hal_log32f(const float* src, float* dst, int len);
+int ipp_hal_log64f(const double* src, double* dst, int len);
+
+#undef cv_hal_log32f
+#define cv_hal_log32f ipp_hal_log32f
+
+#undef cv_hal_log64f
+#define cv_hal_log64f ipp_hal_log64f
+
//! @endcond
#endif
diff --git a/hal/ipp/include/ipp_hal_imgproc.hpp b/hal/ipp/include/ipp_hal_imgproc.hpp
index 7272cd2fe1..3c3cf6c111 100644
--- a/hal/ipp/include/ipp_hal_imgproc.hpp
+++ b/hal/ipp/include/ipp_hal_imgproc.hpp
@@ -24,6 +24,21 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
// Does not pass tests in 5.x branch
//#undef cv_hal_warpAffine
//#define cv_hal_warpAffine ipp_hal_warpAffine
+
+int ipp_hal_sobel(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
+ int width, int height, int src_depth, int dst_depth, int cn,
+ int margin_left, int margin_top, int margin_right, int margin_bottom,
+ int dx, int dy, int ksize, double scale, double delta, int border_type);
+#undef cv_hal_sobel
+#define cv_hal_sobel ipp_hal_sobel
+
+int ipp_hal_scharr(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
+ int width, int height, int src_depth, int dst_depth, int cn,
+ int margin_left, int margin_top, int margin_right, int margin_bottom,
+ int dx, int dy, double scale, double delta, int border_type);
+#undef cv_hal_scharr
+#define cv_hal_scharr ipp_hal_scharr
+
#endif
#if IPP_VERSION_X100 >= 202600
@@ -44,6 +59,14 @@ int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int s
#undef cv_hal_remap32f
#define cv_hal_remap32f ipp_hal_remap32f
+#if defined(HAVE_IPP_IW)
+int ipp_hal_resize(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height,
+ uchar *dst_data, size_t dst_step, int dst_width, int dst_height,
+ double inv_scale_x, double inv_scale_y, int interpolation);
+#undef cv_hal_resize
+#define cv_hal_resize ipp_hal_resize
+#endif // HAVE_IPP_IW
+
#if defined(HAVE_IPP_IW) && !DISABLE_IPP_BOX_FILTER
int ipp_hal_boxFilter(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
int width, int height, int src_depth, int dst_depth, int cn,
diff --git a/hal/ipp/src/deriv_ipp.cpp b/hal/ipp/src/deriv_ipp.cpp
new file mode 100644
index 0000000000..31aa6f44bc
--- /dev/null
+++ b/hal/ipp/src/deriv_ipp.cpp
@@ -0,0 +1,247 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
+// Copyright (C) 2026, Intel Corporation, all rights reserved.
+
+#include "ipp_hal_imgproc.hpp"
+#include "precomp_ipp.hpp"
+
+#include
+#include
+
+#if IPP_VERSION_X100 >= 810
+
+#ifdef HAVE_IPP_IW
+#include "iw++/iw.hpp"
+
+static inline IppiMaskSize ippiGetMaskSize(int kx, int ky)
+{
+ return (kx == 1 && ky == 3) ? ippMskSize1x3 :
+ (kx == 1 && ky == 5) ? ippMskSize1x5 :
+ (kx == 3 && ky == 1) ? ippMskSize3x1 :
+ (kx == 3 && ky == 3) ? ippMskSize3x3 :
+ (kx == 5 && ky == 1) ? ippMskSize5x1 :
+ (kx == 5 && ky == 5) ? ippMskSize5x5 :
+ (IppiMaskSize)-1;
+}
+
+static inline IwiDerivativeType ippiGetDerivType(int dx, int dy, bool nvert)
+{
+ return (dx == 1 && dy == 0) ? ((nvert)?iwiDerivNVerFirst:iwiDerivVerFirst) :
+ (dx == 0 && dy == 1) ? iwiDerivHorFirst :
+ (dx == 2 && dy == 0) ? iwiDerivVerSecond :
+ (dx == 0 && dy == 2) ? iwiDerivHorSecond :
+ (IwiDerivativeType)-1;
+}
+
+// Build an IwiImage from a raw buffer, encoding the in-memory border (the OpenCV
+// margins around the ROI) so that BORDER_*-with-physical-pixels works as in core.
+// TODO: promote to precomp_ipp.hpp and unify with the raw-pointer ippiGetImage in
+// transforms_ipp.cpp once more filter HALs share it
+static inline ::ipp::IwiImage ippiGetImage(int depth, int channels, const uchar* data, size_t step,
+ int width, int height,
+ int margin_left, int margin_top, int margin_right, int margin_bottom)
+{
+ ::ipp::IwiImage image;
+ ::ipp::IwiBorderSize inMemBorder((IwSize)margin_left, (IwSize)margin_top, (IwSize)margin_right, (IwSize)margin_bottom);
+ image.Init({width, height}, ippiGetDataType(depth), channels, inMemBorder, (void*)data, step);
+ return image;
+}
+
+// Translate the OpenCV border type into an IPP border, accounting for in-memory pixels.
+// Returns (IppiBorderType)0 on unsupported configuration.
+// TODO: promote to precomp_ipp.hpp once shared by other filter HALs (box/gaussian/bilateral/
+// sepFilter etc.). Note the shared ippiGetBorderType there does not map BORDER_REFLECT_101;
+// widening it would also affect warp_ipp.cpp, so keep this filter-specific mapping separate
+// (or add a dedicated filter border helper) when consolidating.
+static inline IppiBorderType ippiGetBorder(::ipp::IwiImage &image, int ocvBorderType, ::ipp::IwiBorderSize &borderSize)
+{
+ int inMemFlags = 0;
+ int borderTypeNI = ocvBorderType & ~cv::BORDER_ISOLATED;
+ IppiBorderType border = borderTypeNI == cv::BORDER_CONSTANT ? ippBorderConst :
+ borderTypeNI == cv::BORDER_TRANSPARENT ? ippBorderTransp :
+ borderTypeNI == cv::BORDER_REPLICATE ? ippBorderRepl :
+ borderTypeNI == cv::BORDER_REFLECT_101 ? ippBorderMirror :
+ (IppiBorderType)-1;
+ if((int)border == -1)
+ return (IppiBorderType)0;
+
+ if(!(ocvBorderType & cv::BORDER_ISOLATED))
+ {
+ if(image.m_inMemSize.left)
+ {
+ if(image.m_inMemSize.left >= borderSize.left)
+ inMemFlags |= ippBorderInMemLeft;
+ else
+ return (IppiBorderType)0;
+ }
+ else
+ borderSize.left = 0;
+ if(image.m_inMemSize.top)
+ {
+ if(image.m_inMemSize.top >= borderSize.top)
+ inMemFlags |= ippBorderInMemTop;
+ else
+ return (IppiBorderType)0;
+ }
+ else
+ borderSize.top = 0;
+ if(image.m_inMemSize.right)
+ {
+ if(image.m_inMemSize.right >= borderSize.right)
+ inMemFlags |= ippBorderInMemRight;
+ else
+ return (IppiBorderType)0;
+ }
+ else
+ borderSize.right = 0;
+ if(image.m_inMemSize.bottom)
+ {
+ if(image.m_inMemSize.bottom >= borderSize.bottom)
+ inMemFlags |= ippBorderInMemBottom;
+ else
+ return (IppiBorderType)0;
+ }
+ else
+ borderSize.bottom = 0;
+ }
+ else
+ borderSize.left = borderSize.right = borderSize.top = borderSize.bottom = 0;
+
+ return (IppiBorderType)(border|inMemFlags);
+}
+
+// Shared worker for Sobel (useScharr == false) and Scharr (useScharr == true).
+static int ipp_Deriv(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
+ int width, int height, int src_depth, int dst_depth, int cn,
+ int margin_left, int margin_top, int margin_right, int margin_bottom,
+ int dx, int dy, int ksize, double scale, double delta, int borderType,
+ bool useScharr)
+{
+ IppDataType srcType = ippiGetDataType(src_depth);
+ IppDataType dstType = ippiGetDataType(dst_depth);
+ bool useScale = false;
+
+ if(cn < 1 || cn > 4)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+ if(src_depth < 0 || src_depth >= CV_DEPTH_MAX || dst_depth < 0 || dst_depth >= CV_DEPTH_MAX)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+ // Supported (source depth -> destination depth) x channels combinations.
+ // Index order: [src_depth][dst_depth][channel-1]; depths are 8U,8S,16U,16S,32S,32F,64F.
+ // IPP iwiFilterSobel/iwiFilterScharr provide only single-channel kernels for
+ // 8u->16s, 16s->16s and 32f->32f; 8u->8u is realized as an 8u->16s filter plus a 16s->8u scale.
+ // 8u->32f and 16s->32f are intentionally left disabled: IPP would need an extra full-image
+ // conversion pass there and is slower than OpenCV's fused sepFilter2D.
+ /* dst: 8U 8S 16U 16S 32S 32F 64F */
+#if defined(IPP_CALLS_ENFORCED)
+ const char impl[CV_DEPTH_MAX][CV_DEPTH_MAX][4] = {
+ /* src 8U */ {{1,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 8S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 16U */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 16S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 32S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 32F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0}},
+ /* src 64F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}};
+#else
+ const char impl[CV_DEPTH_MAX][CV_DEPTH_MAX][4] = {
+ /* src 8U */ {{1,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 8S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 16U */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 16S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 32S */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
+ /* src 32F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,0,0,0},{0,0,0,0}},
+ /* src 64F */ {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}};
+#endif // IPP_CALLS_ENFORCED
+ if(impl[src_depth][dst_depth][cn-1] == 0)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+ if(fabs(delta) > FLT_EPSILON || fabs(scale-1) > FLT_EPSILON)
+ useScale = true;
+
+ // cv::Sobel accepts ksize == FILTER_SCHARR (-1, i.e. ksize <= 0) which selects the 3x3
+ // Scharr derivative, so the Sobel entry point may legitimately request a Scharr filter.
+ if(ksize <= 0)
+ {
+ ksize = 3;
+ useScharr = true;
+ }
+
+ IppiMaskSize maskSize = ippiGetMaskSize(ksize, ksize);
+ if((int)maskSize < 0)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+#if IPP_VERSION_X100 <= 201703
+ // Bug with mirror wrap
+ if(borderType == cv::BORDER_REFLECT_101 && (ksize/2+1 > width || ksize/2+1 > height))
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+#endif
+
+ IwiDerivativeType derivType = ippiGetDerivType(dx, dy, (useScharr)?false:true);
+ if((int)derivType < 0)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+ try
+ {
+ ::ipp::IwiImage iwSrc = ippiGetImage(src_depth, cn, src_data, src_step, width, height,
+ margin_left, margin_top, margin_right, margin_bottom);
+ ::ipp::IwiImage iwDst = ippiGetImage(dst_depth, cn, dst_data, dst_step, width, height,
+ 0, 0, 0, 0);
+ ::ipp::IwiImage iwSrcProc = iwSrc;
+ ::ipp::IwiImage iwDstProc = iwDst;
+ ::ipp::IwiBorderSize borderSize(maskSize);
+ ::ipp::IwiBorderType ippBorder(ippiGetBorder(iwSrc, borderType, borderSize));
+ if(!ippBorder)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+ // IPP needs an extra iwiScale pass for 32f output with scale/delta, slower than OpenCV's fused approach
+ if(useScale && dstType == ipp32f)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+ if(srcType == ipp8u && dstType == ipp8u)
+ {
+ iwDstProc.Alloc(iwDst.m_size, ipp16s, cn);
+ useScale = true;
+ }
+
+ if(useScharr)
+ CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterScharr, iwSrcProc, iwDstProc, derivType, maskSize, ::ipp::IwDefault(), ippBorder);
+ else
+ CV_INSTRUMENT_FUN_IPP(::ipp::iwiFilterSobel, iwSrcProc, iwDstProc, derivType, maskSize, ::ipp::IwDefault(), ippBorder);
+
+ if(useScale)
+ CV_INSTRUMENT_FUN_IPP(::ipp::iwiScale, iwDstProc, iwDst, scale, delta, ::ipp::IwiScaleParams(ippAlgHintFast));
+ }
+ catch (const ::ipp::IwException &)
+ {
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return CV_HAL_ERROR_OK;
+}
+
+int ipp_hal_sobel(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
+ int width, int height, int src_depth, int dst_depth, int cn,
+ int margin_left, int margin_top, int margin_right, int margin_bottom,
+ int dx, int dy, int ksize, double scale, double delta, int border_type)
+{
+ CV_HAL_CHECK_USE_IPP();
+ return ipp_Deriv(src_data, src_step, dst_data, dst_step, width, height, src_depth, dst_depth, cn,
+ margin_left, margin_top, margin_right, margin_bottom,
+ dx, dy, ksize, scale, delta, border_type, false);
+}
+
+int ipp_hal_scharr(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step,
+ int width, int height, int src_depth, int dst_depth, int cn,
+ int margin_left, int margin_top, int margin_right, int margin_bottom,
+ int dx, int dy, double scale, double delta, int border_type)
+{
+ CV_HAL_CHECK_USE_IPP();
+ return ipp_Deriv(src_data, src_step, dst_data, dst_step, width, height, src_depth, dst_depth, cn,
+ margin_left, margin_top, margin_right, margin_bottom,
+ dx, dy, 0, scale, delta, border_type, true);
+}
+
+#endif // HAVE_IPP_IW
+
+#endif // IPP_VERSION_X100 >= 810
diff --git a/hal/ipp/src/math_ipp.cpp b/hal/ipp/src/math_ipp.cpp
new file mode 100644
index 0000000000..cb83a34264
--- /dev/null
+++ b/hal/ipp/src/math_ipp.cpp
@@ -0,0 +1,80 @@
+// 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
+
+#include "ipp_hal_core.hpp"
+
+#include
+#include
+
+int ipp_hal_invSqrt32f(const float* src, float* dst, int len)
+{
+ CV_HAL_CHECK_USE_IPP();
+ IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsInvSqrt_32f_A21, src, dst, len);
+ if (status >= 0)
+ {
+ return CV_HAL_ERROR_OK;
+ }
+
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+}
+
+int ipp_hal_invSqrt64f(const double* src, double* dst, int len)
+{
+ CV_HAL_CHECK_USE_IPP();
+ IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsInvSqrt_64f_A50, src, dst, len);
+ if (status >= 0)
+ {
+ return CV_HAL_ERROR_OK;
+ }
+
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+}
+
+int ipp_hal_exp32f(const float* src, float* dst, int len)
+{
+ CV_HAL_CHECK_USE_IPP();
+ IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsExp_32f_A21, src, dst, len);
+ if (status >= 0)
+ {
+ return CV_HAL_ERROR_OK;
+ }
+
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+}
+
+int ipp_hal_exp64f(const double* src, double* dst, int len)
+{
+ CV_HAL_CHECK_USE_IPP();
+ IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsExp_64f_A50, src, dst, len);
+ if (status >= 0)
+ {
+ return CV_HAL_ERROR_OK;
+ }
+
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+}
+
+int ipp_hal_log32f(const float* src, float* dst, int len)
+{
+ CV_HAL_CHECK_USE_IPP();
+ IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsLn_32f_A21, src, dst, len);
+ if (status >= 0)
+ {
+ return CV_HAL_ERROR_OK;
+ }
+
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+}
+
+int ipp_hal_log64f(const double* src, double* dst, int len)
+{
+ CV_HAL_CHECK_USE_IPP();
+ IppStatus status = CV_INSTRUMENT_FUN_IPP(ippsLn_64f_A50, src, dst, len);
+ if (status >= 0)
+ {
+ return CV_HAL_ERROR_OK;
+ }
+
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/hal/ipp/src/resize_ipp.cpp b/hal/ipp/src/resize_ipp.cpp
new file mode 100644
index 0000000000..e6d3d35ccc
--- /dev/null
+++ b/hal/ipp/src/resize_ipp.cpp
@@ -0,0 +1,196 @@
+// 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
+
+#include "ipp_hal_imgproc.hpp"
+
+#ifdef HAVE_IPP_IW
+
+#include
+#include "precomp_ipp.hpp"
+
+#include "iw++/iw.hpp"
+
+#include
+
+#define IPP_RESIZE_PARALLEL 1
+
+class ipp_resizeParallel: public cv::ParallelLoopBody
+{
+public:
+ ipp_resizeParallel(::ipp::IwiImage &src, ::ipp::IwiImage &dst, bool &ok):
+ m_src(src), m_dst(dst), m_ok(ok) {}
+ ~ipp_resizeParallel()
+ {
+ }
+
+ void Init(IppiInterpolationType inter)
+ {
+ iwiResize.InitAlloc(m_src.m_size, m_dst.m_size, m_src.m_dataType, m_src.m_channels, inter, ::ipp::IwiResizeParams(0, 0, 0.75, 4), ippBorderRepl);
+
+ m_ok = true;
+ }
+
+ virtual void operator() (const cv::Range& range) const CV_OVERRIDE
+ {
+ if(!m_ok)
+ return;
+
+ try
+ {
+ ::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
+ CV_INSTRUMENT_FUN_IPP(iwiResize, m_src, m_dst, ippBorderRepl, tile);
+ }
+ catch(const ::ipp::IwException &)
+ {
+ m_ok = false;
+ return;
+ }
+ }
+private:
+ ::ipp::IwiImage &m_src;
+ ::ipp::IwiImage &m_dst;
+
+ mutable ::ipp::IwiResize iwiResize;
+
+ volatile bool &m_ok;
+ const ipp_resizeParallel& operator= (const ipp_resizeParallel&);
+};
+
+class ipp_resizeAffineParallel: public cv::ParallelLoopBody
+{
+public:
+ ipp_resizeAffineParallel(::ipp::IwiImage &src, ::ipp::IwiImage &dst, bool &ok):
+ m_src(src), m_dst(dst), m_ok(ok) {}
+ ~ipp_resizeAffineParallel()
+ {
+ }
+
+ void Init(IppiInterpolationType inter, double scaleX, double scaleY)
+ {
+ double shift = (inter == ippNearest)?-1e-10:-0.5;
+ double coeffs[2][3] = {
+ {scaleX, 0, shift+0.5*scaleX},
+ {0, scaleY, shift+0.5*scaleY}
+ };
+
+ iwiWarpAffine.InitAlloc(m_src.m_size, m_dst.m_size, m_src.m_dataType, m_src.m_channels, coeffs, iwTransForward, inter, ::ipp::IwiWarpAffineParams(0, 0, 0.75), ippBorderRepl);
+
+ m_ok = true;
+ }
+
+ virtual void operator() (const cv::Range& range) const CV_OVERRIDE
+ {
+ if(!m_ok)
+ return;
+
+ try
+ {
+ ::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
+ CV_INSTRUMENT_FUN_IPP(iwiWarpAffine, m_src, m_dst, tile);
+ }
+ catch(const ::ipp::IwException &)
+ {
+ m_ok = false;
+ return;
+ }
+ }
+private:
+ ::ipp::IwiImage &m_src;
+ ::ipp::IwiImage &m_dst;
+
+ mutable ::ipp::IwiWarpAffine iwiWarpAffine;
+
+ volatile bool &m_ok;
+ const ipp_resizeAffineParallel& operator= (const ipp_resizeAffineParallel&);
+};
+
+int ipp_hal_resize(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height,
+ uchar *dst_data, size_t dst_step, int dst_width, int dst_height,
+ double inv_scale_x, double inv_scale_y, int interpolation)
+{
+ CV_HAL_CHECK_USE_IPP();
+
+ int depth = CV_MAT_DEPTH(src_type), channels = CV_MAT_CN(src_type);
+
+ IppDataType ippDataType = ippiGetDataType(depth);
+ IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
+ if((int)ippInter < 0)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+ // Resize which doesn't match OpenCV exactly
+ if (!cv::ipp::useIPP_NotExact())
+ {
+ if (ippInter == ippNearest || ippInter == ippSuper || (ippDataType == ipp8u && ippInter == ippLinear))
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+ }
+
+ if(ippInter != ippLinear && ippDataType == ipp64f)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+#if IPP_VERSION_X100 < 201801
+ // Degradations on int^2 linear downscale
+ if (ippDataType != ipp64f && ippInter == ippLinear && inv_scale_x < 1 && inv_scale_y < 1) // if downscale
+ {
+ int scale_x = (int)(1 / inv_scale_x);
+ int scale_y = (int)(1 / inv_scale_y);
+ if (1 / inv_scale_x - scale_x < DBL_EPSILON && 1 / inv_scale_y - scale_y < DBL_EPSILON) // if integer
+ {
+ if (!(scale_x&(scale_x - 1)) && !(scale_y&(scale_y - 1))) // if power of 2
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+ }
+ }
+#endif
+
+ bool affine = false;
+ const double IPP_RESIZE_EPS = (depth == CV_64F)?0:1e-10;
+ double ex = fabs((double)dst_width / src_width - inv_scale_x) / inv_scale_x;
+ double ey = fabs((double)dst_height / src_height - inv_scale_y) / inv_scale_y;
+
+ // Use affine transform resize to allow sub-pixel accuracy
+ if(ex > IPP_RESIZE_EPS || ey > IPP_RESIZE_EPS)
+ affine = true;
+
+ // Affine doesn't support Lanczos and Super interpolations
+ if(affine && (ippInter == ippLanczos || ippInter == ippSuper))
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+
+ try
+ {
+ ::ipp::IwiImage iwSrc(::ipp::IwiSize(src_width, src_height), ippDataType, channels, 0, (void*)src_data, src_step);
+ ::ipp::IwiImage iwDst(::ipp::IwiSize(dst_width, dst_height), ippDataType, channels, 0, (void*)dst_data, dst_step);
+
+ bool ok;
+ int threads = ippiSuggestThreadsNum(iwDst, 1+((double)(src_width*src_height)/(dst_width*dst_height)));
+ cv::Range range(0, dst_height);
+ ipp_resizeParallel invokerGeneral(iwSrc, iwDst, ok);
+ ipp_resizeAffineParallel invokerAffine(iwSrc, iwDst, ok);
+ cv::ParallelLoopBody *pInvoker = NULL;
+ if(affine)
+ {
+ pInvoker = &invokerAffine;
+ invokerAffine.Init(ippInter, inv_scale_x, inv_scale_y);
+ }
+ else
+ {
+ pInvoker = &invokerGeneral;
+ invokerGeneral.Init(ippInter);
+ }
+
+ if(IPP_RESIZE_PARALLEL && threads > 1)
+ cv::parallel_for_(range, *pInvoker, threads*4);
+ else
+ pInvoker->operator()(range);
+
+ if(!ok)
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+ }
+ catch(const ::ipp::IwException &)
+ {
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return CV_HAL_ERROR_OK;
+}
+
+#endif // HAVE_IPP_IW
diff --git a/hal/ipp/src/warp_ipp.cpp b/hal/ipp/src/warp_ipp.cpp
index b7a0093516..17885d69cd 100644
--- a/hal/ipp/src/warp_ipp.cpp
+++ b/hal/ipp/src/warp_ipp.cpp
@@ -81,6 +81,8 @@ static bool IPPSet(const double value[4], void *dataPointer, int step, IppiSize
int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step,
int dst_width, int dst_height, const double M[6], int interpolation, int borderType, const double borderValue[4])
{
+ CV_HAL_CHECK_USE_IPP();
+
//CV_INSTRUMENT_REGION_IPP();
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
@@ -188,6 +190,8 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar * dst_data, size_t dst_step,
int dst_width, int dst_height, const double M[9], int interpolation, int borderType, const double borderValue[4])
{
+ CV_HAL_CHECK_USE_IPP();
+
//CV_INSTRUMENT_REGION_IPP();
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
@@ -384,6 +388,8 @@ int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int s
float *mapx, size_t mapx_step, float *mapy, size_t mapy_step,
int interpolation, int border_type, const double border_value[4])
{
+ CV_HAL_CHECK_USE_IPP();
+
if (!((interpolation == cv::INTER_LINEAR || interpolation == cv::INTER_CUBIC || interpolation == cv::INTER_NEAREST) &&
(border_type == cv::BORDER_CONSTANT || border_type == cv::BORDER_TRANSPARENT)))
{
diff --git a/hal/ndsrvp/src/filter.cpp b/hal/ndsrvp/src/filter.cpp
index 85f02b99c0..b873b87474 100644
--- a/hal/ndsrvp/src/filter.cpp
+++ b/hal/ndsrvp/src/filter.cpp
@@ -141,7 +141,7 @@ int filter(cvhalFilter2D *context,
int cal_y = offset_y - ctx->anchor_y; // negative if top border exceeded
// calculate source border
- ctx->padding.resize(cal_width * cal_height * cnes);
+ ctx->padding.resize((size_t)cal_width * cal_height * cnes);
uchar* pad_data = &ctx->padding[0];
int pad_step = cal_width * cnes;
diff --git a/hal/riscv-rvv/src/core/mean.cpp b/hal/riscv-rvv/src/core/mean.cpp
index 2fc2f98f65..264c06e46b 100644
--- a/hal/riscv-rvv/src/core/mean.cpp
+++ b/hal/riscv-rvv/src/core/mean.cpp
@@ -19,6 +19,14 @@ inline int meanStdDev_32FC1(const uchar* src_data, size_t src_step, int width, i
int meanStdDev(const uchar* src_data, size_t src_step, int width, int height, int src_type,
double* mean_val, double* stddev_val, uchar* mask, size_t mask_step) {
+
+ // NOTE: The OpenCV imlementation with universal intrinscs is more efficient
+ // See https://github.com/opencv/opencv/pull/29339#issuecomment-4778104352
+ if (!mask && !stddev_val)
+ {
+ return CV_HAL_ERROR_NOT_IMPLEMENTED;
+ }
+
switch (src_type)
{
case CV_8UC1:
diff --git a/hal/riscv-rvv/src/features2d/fast.cpp b/hal/riscv-rvv/src/features2d/fast.cpp
index 8b7cf66e40..fd9324ac6c 100644
--- a/hal/riscv-rvv/src/features2d/fast.cpp
+++ b/hal/riscv-rvv/src/features2d/fast.cpp
@@ -56,32 +56,35 @@ inline int fast_16(const uchar* src_data, size_t src_step,
size_t vl;
for (; j < width - 3; j += vl, ptr += vl)
{
- vl = __riscv_vsetvl_e16m1(width - 3 - j);
+ /* u8mf2 pre-screen: same VL as i16m1, defer vzext until needed */
+ vl = __riscv_vsetvl_e8mf2(width - 3 - j);
+ vuint8mf2_t vcen_8 = __riscv_vle8_v_u8mf2(ptr, vl);
+ vuint8mf2_t vlo_8 = __riscv_vssubu_vx_u8mf2(vcen_8, (uint8_t)threshold, vl);
+ vuint8mf2_t vhi_8 = __riscv_vsaddu_vx_u8mf2(vcen_8, (uint8_t)threshold, vl);
+ vuint8mf2_t vk0_8 = __riscv_vle8_v_u8mf2(ptr + pixel[0], vl);
+ vuint8mf2_t vk4_8 = __riscv_vle8_v_u8mf2(ptr + pixel[4], vl);
+ vuint8mf2_t vk8_8 = __riscv_vle8_v_u8mf2(ptr + pixel[8], vl);
+ vuint8mf2_t vk12_8 = __riscv_vle8_v_u8mf2(ptr + pixel[12], vl);
- /* Load center pixel and widen to int16 */
- vint16m1_t vcen = __riscv_vreinterpret_v_u16m1_i16m1(
- __riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr, vl), vl));
- vint16m1_t vlo = __riscv_vsub_vx_i16m1(vcen, threshold, vl);
- vint16m1_t vhi = __riscv_vadd_vx_i16m1(vcen, threshold, vl);
-
- /* 4-direction quick reject */
- vint16m1_t vk0 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[0], vl), vl));
- vint16m1_t vk4 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[4], vl), vl));
- vint16m1_t vk8 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[8], vl), vl));
- vint16m1_t vk12 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[12], vl), vl));
-
- vbool16_t bright = __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vk0, vhi, vl), __riscv_vmsgt_vv_i16m1_b16(vk4, vhi, vl), vl);
- vbool16_t dark = __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vlo, vk0, vl), __riscv_vmsgt_vv_i16m1_b16(vlo, vk4, vl), vl);
- bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vk4, vhi, vl), __riscv_vmsgt_vv_i16m1_b16(vk8, vhi, vl), vl), vl);
- dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vlo, vk4, vl), __riscv_vmsgt_vv_i16m1_b16(vlo, vk8, vl), vl), vl);
- bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vk8, vhi, vl), __riscv_vmsgt_vv_i16m1_b16(vk12, vhi, vl), vl), vl);
- dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vlo, vk8, vl), __riscv_vmsgt_vv_i16m1_b16(vlo, vk12, vl), vl), vl);
- bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vk12, vhi, vl), __riscv_vmsgt_vv_i16m1_b16(vk0, vhi, vl), vl), vl);
- dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgt_vv_i16m1_b16(vlo, vk12, vl), __riscv_vmsgt_vv_i16m1_b16(vlo, vk0, vl), vl), vl);
+ vbool16_t bright = __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vk0_8, vhi_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vk4_8, vhi_8, vl), vl);
+ vbool16_t dark = __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk0_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk4_8, vl), vl);
+ bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vk4_8, vhi_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vk8_8, vhi_8, vl), vl), vl);
+ dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk4_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk8_8, vl), vl), vl);
+ bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vk8_8, vhi_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vk12_8, vhi_8, vl), vl), vl);
+ dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk8_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk12_8, vl), vl), vl);
+ bright = __riscv_vmor_mm_b16(bright, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vk12_8, vhi_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vk0_8, vhi_8, vl), vl), vl);
+ dark = __riscv_vmor_mm_b16(dark, __riscv_vmand_mm_b16(__riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk12_8, vl), __riscv_vmsgtu_vv_u8mf2_b16(vlo_8, vk0_8, vl), vl), vl);
if (__riscv_vfirst_m_b16(__riscv_vmor_mm_b16(bright, dark, vl), vl) < 0)
continue;
+ /* Widen pre-loaded u8mf2 to i16m1 for score computation */
+ vint16m1_t vcen = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vcen_8, vl));
+ vint16m1_t vk0 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vk0_8, vl));
+ vint16m1_t vk4 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vk4_8, vl));
+ vint16m1_t vk8 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vk8_8, vl));
+ vint16m1_t vk12 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(vk12_8, vl));
+
/* Load remaining 12 neighbors */
vint16m1_t vk1 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[1], vl), vl));
vint16m1_t vk2 = __riscv_vreinterpret_v_u16m1_i16m1(__riscv_vzext_vf2(__riscv_vle8_v_u8mf2(ptr + pixel[2], vl), vl));
diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt
index 8f6d51e200..ecc27202d5 100644
--- a/modules/core/CMakeLists.txt
+++ b/modules/core/CMakeLists.txt
@@ -8,14 +8,15 @@ ocv_add_dispatched_file(convert_scale SSE2 AVX2 LASX)
ocv_add_dispatched_file(count_non_zero SSE2 AVX2 LASX)
ocv_add_dispatched_file(has_non_zero SSE2 AVX2 LASX )
ocv_add_dispatched_file(matmul SSE2 SSE4_1 AVX2 AVX512_SKX NEON_DOTPROD LASX)
-ocv_add_dispatched_file(mean SSE2 AVX2 LASX)
+ocv_add_dispatched_file(mean SSE2 AVX2 AVX512_SKX AVX512_ICL LASX)
ocv_add_dispatched_file(merge SSE2 AVX2 LASX)
-ocv_add_dispatched_file(minmax SSE2 SSE4_1 AVX2 VSX3 LASX)
+ocv_add_dispatched_file(minmax SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL VSX3 LASX)
ocv_add_dispatched_file(nan_mask SSE2 AVX2 LASX)
ocv_add_dispatched_file(split SSE2 AVX2 LASX)
-ocv_add_dispatched_file(sum SSE2 AVX2 LASX)
+ocv_add_dispatched_file(sum SSE2 AVX2 AVX512_SKX AVX512_ICL LASX)
ocv_add_dispatched_file(reduce SSE2 SSSE3 AVX2 NEON_DOTPROD)
ocv_add_dispatched_file(norm SSE2 SSE4_1 AVX AVX2 NEON_DOTPROD LASX)
+ocv_add_dispatched_file(lut AVX512_ICL)
ocv_add_dispatched_file(transpose AVX AVX2 NEON RVV LASX)
# dispatching for accuracy tests
diff --git a/modules/core/include/opencv2/core/hal/intrin_avx.hpp b/modules/core/include/opencv2/core/hal/intrin_avx.hpp
index f2525f0b24..aadfa2f5f1 100644
--- a/modules/core/include/opencv2/core/hal/intrin_avx.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_avx.hpp
@@ -1603,6 +1603,20 @@ inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterp
inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); }
+inline v_uint8x32 v256_lut(const uchar* tab, const v_uint8x32& idx)
+{
+ uchar CV_DECL_ALIGNED(32) indices[32], result[32];
+ _mm256_store_si256((__m256i*)indices, idx.val);
+ for (int i = 0; i < 32; i++) result[i] = tab[indices[i]];
+ return v_uint8x32(_mm256_load_si256((const __m256i*)result));
+}
+inline v_int8x32 v256_lut(const schar* tab, const v_uint8x32& idx)
+{ return v_reinterpret_as_s8(v256_lut((const uchar *)tab, idx)); }
+
+
+inline v_uint8x32 v_lut(const uchar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); }
+inline v_int8x32 v_lut(const schar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); }
+
inline v_int16x16 v256_lut(const short* tab, const int* idx)
{
return v_int16x16(_mm256_setr_epi16(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]],
diff --git a/modules/core/include/opencv2/core/hal/intrin_avx512.hpp b/modules/core/include/opencv2/core/hal/intrin_avx512.hpp
index 077b4d17a7..e06dc05d72 100644
--- a/modules/core/include/opencv2/core/hal/intrin_avx512.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_avx512.hpp
@@ -1634,6 +1634,122 @@ inline v_uint8x64 v512_lut(const uchar* tab, const int* idx) { return v_reinterp
inline v_uint8x64 v512_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x64 v512_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_quads((const schar *)tab, idx)); }
+// Byte-indexed LUT: takes v_uint8x64 byte indices instead of int* indices
+// Table must have at least 256 entries (byte indices cover 0-255)
+// VBMI path uses vpermb (register-to-register), fallback uses gather
+inline v_uint8x64 v512_lut(const uchar* tab, const v_uint8x64& idx)
+{
+#if CV_AVX_512VBMI
+ const __m512i lut0 = _mm512_loadu_si512(tab);
+ const __m512i lut1 = _mm512_loadu_si512(tab + 64);
+ const __m512i lut2 = _mm512_loadu_si512(tab + 128);
+ const __m512i lut3 = _mm512_loadu_si512(tab + 192);
+
+ __m512i idx6 = _mm512_and_si512(idx.val, _mm512_set1_epi8(0x3F));
+
+ __m512i r0 = _mm512_permutexvar_epi8(idx6, lut0);
+ __m512i r1 = _mm512_permutexvar_epi8(idx6, lut1);
+ __m512i r2 = _mm512_permutexvar_epi8(idx6, lut2);
+ __m512i r3 = _mm512_permutexvar_epi8(idx6, lut3);
+
+ __mmask64 k6 = _mm512_test_epi8_mask(idx.val, _mm512_set1_epi8(0x40));
+ __mmask64 k7 = _mm512_test_epi8_mask(idx.val, _mm512_set1_epi8((char)0x80));
+
+ __m512i low = _mm512_mask_blend_epi8(k6, r0, r1);
+ __m512i high = _mm512_mask_blend_epi8(k6, r2, r3);
+ return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high));
+#else
+ uchar CV_DECL_ALIGNED(64) indices[64], result[64];
+ _mm512_store_si512((__m512i*)indices, idx.val);
+ for (int i = 0; i < 64; i++) result[i] = tab[indices[i]];
+ return v_uint8x64(_mm512_load_si512((const __m512i*)result));
+#endif
+}
+inline v_int8x64 v512_lut(const schar* tab, const v_uint8x64& idx)
+{ return v_reinterpret_as_s8(v512_lut((const uchar *)tab, idx)); }
+
+// Universal v_lut overloads for vector byte indices (aliases to v512_lut)
+inline v_uint8x64 v_lut(const uchar* tab, const v_uint8x64& idx)
+{ return v512_lut(tab, idx); }
+
+inline v_int8x64 v_lut(const schar* tab, const v_uint8x64& idx)
+{ return v512_lut(tab, idx); }
+
+// Byte-indexed pair LUT: 32 byte indices (lower half of idx) → 32 pairs = 64 bytes
+inline v_uint8x64 v512_lut_pairs(const uchar* tab, const v_uint8x64& idx)
+{
+#if CV_AVX_512VBMI
+ const __m512i lut0 = _mm512_loadu_si512(tab);
+ const __m512i lut1 = _mm512_loadu_si512(tab + 64);
+ const __m512i lut2 = _mm512_loadu_si512(tab + 128);
+ const __m512i lut3 = _mm512_loadu_si512(tab + 192);
+
+ // Expand 32 byte indices to interleaved pairs: (i0, i0+1, i1, i1+1, ...)
+ __m512i idx16 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(idx.val));
+ __m512i idx_dup = _mm512_or_si512(idx16, _mm512_slli_epi16(idx16, 8));
+ __m512i idx_pairs = _mm512_add_epi8(idx_dup, _mm512_set1_epi16(0x0100));
+
+ __m512i idx6 = _mm512_and_si512(idx_pairs, _mm512_set1_epi8(0x3F));
+
+ __m512i r0 = _mm512_permutexvar_epi8(idx6, lut0);
+ __m512i r1 = _mm512_permutexvar_epi8(idx6, lut1);
+ __m512i r2 = _mm512_permutexvar_epi8(idx6, lut2);
+ __m512i r3 = _mm512_permutexvar_epi8(idx6, lut3);
+
+ __mmask64 k6 = _mm512_test_epi8_mask(idx_pairs, _mm512_set1_epi8(0x40));
+ __mmask64 k7 = _mm512_test_epi8_mask(idx_pairs, _mm512_set1_epi8((char)0x80));
+
+ __m512i low = _mm512_mask_blend_epi8(k6, r0, r1);
+ __m512i high = _mm512_mask_blend_epi8(k6, r2, r3);
+ return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high));
+#else
+ uchar CV_DECL_ALIGNED(64) indices[64], result[64];
+ _mm512_store_si512((__m512i*)indices, idx.val);
+ for (int i = 0; i < 64; i++) result[i] = tab[indices[i]];
+ return v_uint8x64(_mm512_load_si512((const __m512i*)result));
+#endif
+}
+inline v_int8x64 v512_lut_pairs(const schar* tab, const v_uint8x64& idx)
+{ return v_reinterpret_as_s8(v512_lut_pairs((const uchar *)tab, idx)); }
+
+// Byte-indexed quad LUT: 16 byte indices (lower 16 of idx) → 16 quads = 64 bytes
+inline v_uint8x64 v512_lut_quads(const uchar* tab, const v_uint8x64& idx)
+{
+#if CV_AVX_512VBMI
+ const __m512i lut0 = _mm512_loadu_si512(tab);
+ const __m512i lut1 = _mm512_loadu_si512(tab + 64);
+ const __m512i lut2 = _mm512_loadu_si512(tab + 128);
+ const __m512i lut3 = _mm512_loadu_si512(tab + 192);
+
+ // Expand 16 byte indices to quad offsets: (i0, i0+1, i0+2, i0+3, i1, ...)
+ __m512i idx32 = _mm512_cvtepu8_epi32(_mm512_castsi512_si128(idx.val));
+ __m512i t1 = _mm512_or_si512(idx32, _mm512_slli_epi32(idx32, 8));
+ __m512i idx_bcast = _mm512_or_si512(t1, _mm512_slli_epi32(t1, 16));
+ __m512i idx_quads = _mm512_add_epi8(idx_bcast, _mm512_set1_epi32(0x03020100));
+
+ __m512i idx6 = _mm512_and_si512(idx_quads, _mm512_set1_epi8(0x3F));
+
+ __m512i r0 = _mm512_permutexvar_epi8(idx6, lut0);
+ __m512i r1 = _mm512_permutexvar_epi8(idx6, lut1);
+ __m512i r2 = _mm512_permutexvar_epi8(idx6, lut2);
+ __m512i r3 = _mm512_permutexvar_epi8(idx6, lut3);
+
+ __mmask64 k6 = _mm512_test_epi8_mask(idx_quads, _mm512_set1_epi8(0x40));
+ __mmask64 k7 = _mm512_test_epi8_mask(idx_quads, _mm512_set1_epi8((char)0x80));
+
+ __m512i low = _mm512_mask_blend_epi8(k6, r0, r1);
+ __m512i high = _mm512_mask_blend_epi8(k6, r2, r3);
+ return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high));
+#else
+ uchar CV_DECL_ALIGNED(64) indices[64], result[64];
+ _mm512_store_si512((__m512i*)indices, idx.val);
+ for (int i = 0; i < 64; i++) result[i] = tab[indices[i]];
+ return v_uint8x64(_mm512_load_si512((const __m512i*)result));
+#endif
+}
+inline v_int8x64 v512_lut_quads(const schar* tab, const v_uint8x64& idx)
+{ return v_reinterpret_as_s8(v512_lut_quads((const uchar *)tab, idx)); }
+
inline v_int16x32 v512_lut(const short* tab, const int* idx)
{
__m256i p0 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 2));
diff --git a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp
index 25f7c2bbbc..6e00a210e2 100644
--- a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp
@@ -14,6 +14,7 @@
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -2714,6 +2715,16 @@ template inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_lut_quad
return c;
}
+template inline v_reg v_lut(const uchar* tab, const v_reg& idx)
+{
+ v_reg c;
+ for( int i = 0; i < n; i++ )
+ c.s[i] = tab[idx.s[i]];
+ return c;
+}
+template inline v_reg v_lut(const schar* tab, const v_reg& idx)
+{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
+
template inline v_reg v_lut(const int* tab, const v_reg& idx)
{
v_reg c;
diff --git a/modules/core/include/opencv2/core/hal/intrin_lasx.hpp b/modules/core/include/opencv2/core/hal/intrin_lasx.hpp
index 97df327767..7523025fd5 100644
--- a/modules/core/include/opencv2/core/hal/intrin_lasx.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_lasx.hpp
@@ -1677,6 +1677,21 @@ inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterp
inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); }
+// Byte-indexed LUT: 32 byte indices in a vector -> 32 looked-up bytes
+inline v_uint8x32 v256_lut(const uchar* tab, const v_uint8x32& idx)
+{
+ uchar CV_DECL_ALIGNED(32) indices[32], result[32];
+ __lasx_xvst(idx.val, indices, 0);
+ for (int i = 0; i < 32; i++) result[i] = tab[indices[i]];
+ return v_uint8x32(__lasx_xvld(result, 0));
+}
+inline v_int8x32 v256_lut(const schar* tab, const v_uint8x32& idx)
+{ return v_reinterpret_as_s8(v256_lut((const uchar*)tab, idx)); }
+
+// Universal v_lut overloads for vector byte indices (aliases to v256_lut)
+inline v_uint8x32 v_lut(const uchar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); }
+inline v_int8x32 v_lut(const schar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); }
+
inline v_int16x16 v256_lut(const short* tab, const int* idx)
{
return v_int16x16(_v256_setr_h(tab[idx[ 0]], tab[idx[ 1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]],
diff --git a/modules/core/include/opencv2/core/hal/intrin_lsx.hpp b/modules/core/include/opencv2/core/hal/intrin_lsx.hpp
index bc720e0a4b..6dac0286c4 100644
--- a/modules/core/include/opencv2/core/hal/intrin_lsx.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_lsx.hpp
@@ -1447,6 +1447,16 @@ inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx)
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx)
{ return v_reinterpret_as_u8(v_lut_quads((const schar*)tab, idx)); }
+inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
+{
+ uchar CV_DECL_ALIGNED(16) indices[16], result[16];
+ __lsx_vst(idx.val, indices, 0);
+ for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
+ return v_uint8x16(__lsx_vld(result, 0));
+}
+inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
+{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
+
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
return v_int16x8(_v128_setr_h(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
diff --git a/modules/core/include/opencv2/core/hal/intrin_msa.hpp b/modules/core/include/opencv2/core/hal/intrin_msa.hpp
index 3917faa292..a1458e224d 100644
--- a/modules/core/include/opencv2/core/hal/intrin_msa.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_msa.hpp
@@ -1566,6 +1566,15 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
+inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
+{
+ uchar CV_DECL_ALIGNED(16) indices[16], result[16];
+ msa_st1q_u8(indices, idx.val);
+ for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
+ return v_uint8x16(msa_ld1q_u8(result));
+}
+inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
+{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
diff --git a/modules/core/include/opencv2/core/hal/intrin_neon.hpp b/modules/core/include/opencv2/core/hal/intrin_neon.hpp
index 044c00ca4e..9dd2957f2f 100644
--- a/modules/core/include/opencv2/core/hal/intrin_neon.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_neon.hpp
@@ -14,6 +14,7 @@
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -2707,6 +2708,48 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
+inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
+{
+#if CV_NEON_AARCH64
+ uint8x16_t index = idx.val;
+ uint8x16_t idx6 = vandq_u8(index, vdupq_n_u8(0x3F));
+
+ uint8x16x4_t t0, t1, t2, t3;
+ t0.val[0] = vld1q_u8(tab); t0.val[1] = vld1q_u8(tab + 16);
+ t0.val[2] = vld1q_u8(tab + 32); t0.val[3] = vld1q_u8(tab + 48);
+ uint8x16_t r0 = vqtbl4q_u8(t0, idx6);
+
+ t1.val[0] = vld1q_u8(tab + 64); t1.val[1] = vld1q_u8(tab + 80);
+ t1.val[2] = vld1q_u8(tab + 96); t1.val[3] = vld1q_u8(tab + 112);
+ uint8x16_t r1 = vqtbl4q_u8(t1, idx6);
+
+ t2.val[0] = vld1q_u8(tab + 128); t2.val[1] = vld1q_u8(tab + 144);
+ t2.val[2] = vld1q_u8(tab + 160); t2.val[3] = vld1q_u8(tab + 176);
+ uint8x16_t r2 = vqtbl4q_u8(t2, idx6);
+
+ t3.val[0] = vld1q_u8(tab + 192); t3.val[1] = vld1q_u8(tab + 208);
+ t3.val[2] = vld1q_u8(tab + 224); t3.val[3] = vld1q_u8(tab + 240);
+ uint8x16_t r3 = vqtbl4q_u8(t3, idx6);
+
+ uint8x16_t bit6 = vtstq_u8(index, vdupq_n_u8(0x40));
+ uint8x16_t low = vbslq_u8(bit6, r1, r0);
+ uint8x16_t high = vbslq_u8(bit6, r3, r2);
+
+ uint8x16_t bit7 = vtstq_u8(index, vdupq_n_u8(0x80));
+ return v_uint8x16(vbslq_u8(bit7, high, low));
+#else
+ uchar CV_DECL_ALIGNED(16) indices[16], result[16];
+ vst1q_u8(indices, idx.val);
+ for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
+ return v_uint8x16(vld1q_u8(result));
+#endif
+}
+
+inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
+{
+ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx));
+}
+
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
short CV_DECL_ALIGNED(32) elems[8] =
diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp
index 146335dc01..8e470b18e5 100644
--- a/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp
@@ -3,6 +3,7 @@
// of this distribution and at http://opencv.org/license.html
// Copyright (C) 2015, PingTouGe Semiconductor Co., Ltd., all rights reserved.
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#ifndef OPENCV_HAL_INTRIN_RISCVV_HPP
#define OPENCV_HAL_INTRIN_RISCVV_HPP
@@ -1625,6 +1626,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
+inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
+{
+ uchar CV_DECL_ALIGNED(16) indices[16], result[16];
+ vse8_v_u8m1(indices, idx.val, 16);
+ for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
+ return v_uint8x16(vle8_v_u8m1(result, 16));
+}
+inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
+{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
+
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
#if 0
diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp
index 7abfad01df..2afc277ee1 100644
--- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp
@@ -4,6 +4,7 @@
// The original implementation is contributed by HAN Liutong.
// Copyright (C) 2022, Institute of Software, Chinese Academy of Sciences.
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#ifndef OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP
#define OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP
@@ -703,6 +704,13 @@ inline void v_lut_deinterleave(const double* tab, const v_int32& vidx, v_float64
inline v_uint8 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); }
inline v_uint8 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
inline v_uint8 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); }
+
+// Byte-indexed LUT: vector byte indices -> looked-up bytes (uses RVV indexed load)
+inline v_uint8 v_lut(const uchar* tab, const v_uint8& idx)
+{ return __riscv_vluxei8_v_u8m2(tab, idx, VTraits::vlanes()); }
+inline v_int8 v_lut(const schar* tab, const v_uint8& idx)
+{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
+
inline v_uint16 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); }
inline v_uint16 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); }
inline v_uint16 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((short*)tab, idx)); }
diff --git a/modules/core/include/opencv2/core/hal/intrin_sse.hpp b/modules/core/include/opencv2/core/hal/intrin_sse.hpp
index 26ea340263..3cd2cbff6b 100644
--- a/modules/core/include/opencv2/core/hal/intrin_sse.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_sse.hpp
@@ -14,6 +14,7 @@
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -3070,71 +3071,55 @@ inline v_float64x2 v_cvt_f64(const v_int64x2& v)
inline v_int8x16 v_lut(const schar* tab, const int* idx)
{
-#if defined(_MSC_VER)
- return v_int8x16(_mm_setr_epi8(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]],
- tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]));
-#else
- return v_int8x16(_mm_setr_epi64(
- _mm_setr_pi8(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]]),
- _mm_setr_pi8(tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]], tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]])
- ));
-#endif
+ return v_int8x16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
+ tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]],
+ tab[idx[8]], tab[idx[9]], tab[idx[10]], tab[idx[11]],
+ tab[idx[12]], tab[idx[13]], tab[idx[14]], tab[idx[15]]);
}
inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx)
{
-#if defined(_MSC_VER)
- return v_int8x16(_mm_setr_epi16(*(const short*)(tab + idx[0]), *(const short*)(tab + idx[1]), *(const short*)(tab + idx[2]), *(const short*)(tab + idx[3]),
- *(const short*)(tab + idx[4]), *(const short*)(tab + idx[5]), *(const short*)(tab + idx[6]), *(const short*)(tab + idx[7])));
-#else
- return v_int8x16(_mm_setr_epi64(
- _mm_setr_pi16(*(const short*)(tab + idx[0]), *(const short*)(tab + idx[1]), *(const short*)(tab + idx[2]), *(const short*)(tab + idx[3])),
- _mm_setr_pi16(*(const short*)(tab + idx[4]), *(const short*)(tab + idx[5]), *(const short*)(tab + idx[6]), *(const short*)(tab + idx[7]))
- ));
-#endif
+ return v_int8x16(tab[idx[0]], tab[idx[0] + 1], tab[idx[1]], tab[idx[1] + 1],
+ tab[idx[2]], tab[idx[2] + 1], tab[idx[3]], tab[idx[3] + 1],
+ tab[idx[4]], tab[idx[4] + 1], tab[idx[5]], tab[idx[5] + 1],
+ tab[idx[6]], tab[idx[6] + 1], tab[idx[7]], tab[idx[7] + 1]);
}
inline v_int8x16 v_lut_quads(const schar* tab, const int* idx)
{
-#if defined(_MSC_VER)
- return v_int8x16(_mm_setr_epi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]),
- *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3])));
-#else
- return v_int8x16(_mm_setr_epi64(
- _mm_setr_pi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1])),
- _mm_setr_pi32(*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))
- ));
-#endif
+ return v_int8x16(
+ tab[idx[0]], tab[idx[0] + 1], tab[idx[0] + 2], tab[idx[0] + 3],
+ tab[idx[1]], tab[idx[1] + 1], tab[idx[1] + 2], tab[idx[1] + 3],
+ tab[idx[2]], tab[idx[2] + 1], tab[idx[2] + 2], tab[idx[2] + 3],
+ tab[idx[3]], tab[idx[3] + 1], tab[idx[3] + 2], tab[idx[3] + 3]);
}
inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((const schar *)tab, idx)); }
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); }
+inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
+{
+ uchar CV_DECL_ALIGNED(16) indices[16], result[16];
+ _mm_store_si128((__m128i*)indices, idx.val);
+ for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
+ return v_uint8x16(_mm_load_si128((const __m128i*)result));
+}
+inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
+{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
+
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
-#if defined(_MSC_VER)
- return v_int16x8(_mm_setr_epi16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
- tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]));
-#else
- return v_int16x8(_mm_setr_epi64(
- _mm_setr_pi16(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]),
- _mm_setr_pi16(tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]])
- ));
-#endif
+ return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
+ tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]);
}
inline v_int16x8 v_lut_pairs(const short* tab, const int* idx)
{
-#if defined(_MSC_VER)
- return v_int16x8(_mm_setr_epi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1]),
- *(const int*)(tab + idx[2]), *(const int*)(tab + idx[3])));
-#else
- return v_int16x8(_mm_setr_epi64(
- _mm_setr_pi32(*(const int*)(tab + idx[0]), *(const int*)(tab + idx[1])),
- _mm_setr_pi32(*(const int*)(tab + idx[2]), *(const int*)(tab + idx[3]))
- ));
-#endif
+ return v_int16x8(tab[idx[0]], tab[idx[0] + 1], tab[idx[1]], tab[idx[1] + 1],
+ tab[idx[2]], tab[idx[2] + 1], tab[idx[3]], tab[idx[3] + 1]);
}
inline v_int16x8 v_lut_quads(const short* tab, const int* idx)
{
- return v_int16x8(_mm_set_epi64x(*(const int64_t*)(tab + idx[1]), *(const int64_t*)(tab + idx[0])));
+ return v_int16x8(tab[idx[0]], tab[idx[0] + 1], tab[idx[0] + 2],
+ tab[idx[0] + 3], tab[idx[1]], tab[idx[1] + 1],
+ tab[idx[1] + 2], tab[idx[1] + 3]);
}
inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((const short *)tab, idx)); }
inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((const short *)tab, idx)); }
@@ -3142,15 +3127,7 @@ inline v_uint16x8 v_lut_quads(const ushort* tab, const int* idx) { return v_rein
inline v_int32x4 v_lut(const int* tab, const int* idx)
{
-#if defined(_MSC_VER)
- return v_int32x4(_mm_setr_epi32(tab[idx[0]], tab[idx[1]],
- tab[idx[2]], tab[idx[3]]));
-#else
- return v_int32x4(_mm_setr_epi64(
- _mm_setr_pi32(tab[idx[0]], tab[idx[1]]),
- _mm_setr_pi32(tab[idx[2]], tab[idx[3]])
- ));
-#endif
+ return v_int32x4(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]]);
}
inline v_int32x4 v_lut_pairs(const int* tab, const int* idx)
{
diff --git a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp
index 0a0915a22f..a0383e7352 100644
--- a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp
@@ -1175,6 +1175,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar*)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar*)tab, idx)); }
+inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
+{
+ uchar CV_DECL_ALIGNED(16) indices[16], result[16];
+ vsx_st(idx.val, 0, indices);
+ for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
+ return v_uint8x16(vsx_ld(0, result));
+}
+inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
+{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
+
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]);
diff --git a/modules/core/include/opencv2/core/hal/intrin_wasm.hpp b/modules/core/include/opencv2/core/hal/intrin_wasm.hpp
index a15a8ddef1..d6e881a1a1 100644
--- a/modules/core/include/opencv2/core/hal/intrin_wasm.hpp
+++ b/modules/core/include/opencv2/core/hal/intrin_wasm.hpp
@@ -2581,6 +2581,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); }
inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); }
+inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx)
+{
+ uchar CV_DECL_ALIGNED(16) indices[16], result[16];
+ wasm_v128_store(indices, idx.val);
+ for (int i = 0; i < 16; i++) result[i] = tab[indices[i]];
+ return v_uint8x16(wasm_v128_load(result));
+}
+inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx)
+{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
+
inline v_int16x8 v_lut(const short* tab, const int* idx)
{
return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]],
diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp
index f1afcd9843..47fa176b7f 100644
--- a/modules/core/include/opencv2/core/mat.hpp
+++ b/modules/core/include/opencv2/core/mat.hpp
@@ -687,6 +687,7 @@ public:
Mat_ m2({2, 3}, {1, 2, 3, 4, 5, 6}); // 2x3 Mat
Mat_ R({2, 2}, {a, -b, b, a}); // from example
+ \endcode
*/
template class MatCommaInitializer_
{
diff --git a/modules/core/perf/perf_arithm.cpp b/modules/core/perf/perf_arithm.cpp
index 54053fd652..a7eea48d3e 100644
--- a/modules/core/perf/perf_arithm.cpp
+++ b/modules/core/perf/perf_arithm.cpp
@@ -436,7 +436,22 @@ PERF_TEST_P_(BinaryOpTest, transpose2d)
SANITY_CHECK_NOTHING();
}
-PERF_TEST_P_(BinaryOpTest, transposeND)
+static cv::Mat makeTransposeNDOutput(const cv::Mat& src, const std::vector& order)
+{
+ std::vector new_sz(order.size());
+ for (size_t i = 0; i < order.size(); ++i)
+ new_sz[i] = src.size[order[i]];
+ return Mat(static_cast(new_sz.size()), new_sz.data(), src.type());
+}
+
+static cv::Mat makeTransposeNDInput3D(Size sz, int type)
+{
+ const int channels = CV_MAT_CN(type);
+ int dims[] = { sz.height, sz.width, channels };
+ return Mat(3, dims, CV_MAKETYPE(CV_MAT_DEPTH(type), 1));
+}
+
+PERF_TEST_P_(BinaryOpTest, transposeND_identity_order)
{
Size sz = get<0>(GetParam());
int type = get<1>(GetParam());
@@ -444,14 +459,66 @@ PERF_TEST_P_(BinaryOpTest, transposeND)
std::vector order(a.dims);
std::iota(order.begin(), order.end(), 0);
- std::reverse(order.begin(), order.end());
- std::vector new_sz(a.dims);
- std::copy(a.size.p, a.size.p + a.dims, new_sz.begin());
- std::reverse(new_sz.begin(), new_sz.end());
- cv::Mat b = Mat(new_sz, type);
+ cv::Mat b = makeTransposeNDOutput(a, order);
- declare.in(a,WARMUP_RNG).out(b);
+ declare.in(a, WARMUP_RNG).out(b);
+
+ TEST_CYCLE() cv::transposeND(a, order, b);
+
+ SANITY_CHECK_NOTHING();
+}
+
+PERF_TEST_P_(BinaryOpTest, transposeND_2d_swap_order)
+{
+ Size sz = get<0>(GetParam());
+ int type = get<1>(GetParam());
+ cv::Mat a = Mat(sz, type).reshape(1);
+
+ std::vector order{1, 0};
+
+ cv::Mat b = makeTransposeNDOutput(a, order);
+
+ declare.in(a, WARMUP_RNG).out(b);
+
+ TEST_CYCLE() cv::transposeND(a, order, b);
+
+ SANITY_CHECK_NOTHING();
+}
+
+
+PERF_TEST_P_(BinaryOpTest, transposeND_generic_keep_tail_order)
+{
+ Size sz = get<0>(GetParam());
+ int type = get<1>(GetParam());
+ cv::Mat a = makeTransposeNDInput3D(sz, type);
+
+ std::vector order{1, 0, 2};
+
+ cv::Mat b = makeTransposeNDOutput(a, order);
+
+ randu(a, 0, 255);
+ //BUG: //BUG: in/out calls do not support arrays with more than 2 dimentions
+ //declare.in(a).out(b);
+
+ TEST_CYCLE() cv::transposeND(a, order, b);
+
+ SANITY_CHECK_NOTHING();
+}
+
+PERF_TEST_P_(BinaryOpTest, transposeND_generic_move_tail_order)
+{
+ Size sz = get<0>(GetParam());
+ int type = get<1>(GetParam());
+ cv::Mat a = makeTransposeNDInput3D(sz, type);
+
+ std::vector order{2, 0, 1};
+
+ cv::Mat b = makeTransposeNDOutput(a, order);
+
+ randu(a, 0, 255);
+ //BUG: in/out calls do not support arrays with more than 2 dimentions
+ //declare.in(a).out(b);
TEST_CYCLE() cv::transposeND(a, order, b);
diff --git a/modules/core/perf/perf_gemm.cpp b/modules/core/perf/perf_gemm.cpp
new file mode 100644
index 0000000000..de8ad54815
--- /dev/null
+++ b/modules/core/perf/perf_gemm.cpp
@@ -0,0 +1,117 @@
+#include "perf_precomp.hpp"
+
+namespace opencv_test
+{
+using namespace perf;
+
+CV_FLAGS(GemmFlag, 0, GEMM_1_T, GEMM_2_T, GEMM_3_T)
+
+typedef tuple, MatType, GemmFlag> GemmTestParams_t;
+class GemmTest : public perf::TestBaseWithParam
+{
+ public:
+ void runGemmTest(const GemmTestParams_t& params)
+ {
+ int M = get<0>(get<0>(params));
+ int N = get<1>(get<0>(params));
+ int K = get<2>(get<0>(params));
+ int type = get<1>(params);
+ int flags = get<2>(params);
+
+ Size aSize = (flags & GEMM_1_T) ? Size(M, K) : Size(K, M);
+ Size bSize = (flags & GEMM_2_T) ? Size(K, N) : Size(N, K);
+
+ Mat src1(aSize, type), src2(bSize, type), src3(M, N, type), dst(M, N, type);
+ declare.in(src1, src2, src3, WARMUP_RNG).out(dst);
+
+ TEST_CYCLE() cv::gemm(src1, src2, 1.0, src3, 1.0, dst, flags);
+
+ if (dst.total() * dst.channels() < 26)
+ SANITY_CHECK_NOTHING();
+ else
+ SANITY_CHECK(dst, (CV_MAT_DEPTH(type) == CV_32F) ? 1e-4 : 1e-6, ERROR_RELATIVE);
+ };
+};
+
+// Sparse coverage: exercise tiny/small/rectangular shapes and m=1/n=1 edge cases.
+// Large square sizes (640+) are covered by opencl/perf_gemm.cpp on the CPU perf tool.
+PERF_TEST_P(GemmTest, gemmTiny,
+ testing::Combine(
+ testing::Values(
+ make_tuple(2, 2, 2),
+ make_tuple(3, 3, 3)
+ ),
+ testing::Values(CV_32FC1, CV_64FC1),
+ testing::Values(0, (int)GEMM_1_T, (int)GEMM_2_T,
+ (int)(GEMM_1_T | GEMM_2_T))
+ ))
+{
+ GemmTest::runGemmTest(GetParam());
+}
+
+PERF_TEST_P(GemmTest, gemmSmall,
+ testing::Combine(
+ testing::Values(
+ make_tuple(8, 8, 8),
+ make_tuple(32, 32, 32),
+ make_tuple(64, 64, 64),
+ make_tuple(8, 16, 32)
+ ),
+ testing::Values(CV_32FC1),
+ testing::Values(0, (int)GEMM_2_T)
+ ))
+{
+ GemmTest::runGemmTest(GetParam());
+}
+
+PERF_TEST_P(GemmTest, gemmSquare,
+ testing::Combine(
+ testing::Values(
+ make_tuple(256, 256, 256),
+ make_tuple(512, 512, 512)
+ ),
+ testing::Values(CV_32FC1),
+ testing::Values(0)
+ ))
+{
+ GemmTest::runGemmTest(GetParam());
+}
+
+PERF_TEST_P(GemmTest, gemmRect,
+ testing::Combine(
+ testing::Values(
+ make_tuple(1024, 64, 256),
+ make_tuple(256, 1024, 512)
+ ),
+ testing::Values(CV_32FC1),
+ testing::Values(0)
+ ))
+{
+ GemmTest::runGemmTest(GetParam());
+}
+
+PERF_TEST_P(GemmTest, gemmM1,
+ testing::Combine(
+ testing::Values(
+ make_tuple(1, 64, 2500)
+ ),
+ testing::Values(CV_32FC1),
+ testing::Values(0, (int)GEMM_1_T)
+ ))
+{
+ GemmTest::runGemmTest(GetParam());
+}
+
+PERF_TEST_P(GemmTest, gemmN1,
+ testing::Combine(
+ testing::Values(
+ make_tuple(256, 1, 256)
+ ),
+ testing::Values(CV_32FC1),
+ testing::Values(0)
+ ))
+{
+ GemmTest::runGemmTest(GetParam());
+}
+
+} // namespace
diff --git a/modules/core/src/hal_internal.cpp b/modules/core/src/hal_internal.cpp
index 1444f91084..2274c96f97 100644
--- a/modules/core/src/hal_internal.cpp
+++ b/modules/core/src/hal_internal.cpp
@@ -47,7 +47,6 @@
#ifdef HAVE_LAPACK
-#include
#include "opencv_lapack.h"
#include
@@ -722,4 +721,4 @@ int lapack_gemm64fc(const double *src1, size_t src1_step, const double *src2, si
#pragma clang diagnostic pop
#endif
-#endif //HAVE_LAPACK
\ No newline at end of file
+#endif //HAVE_LAPACK
diff --git a/modules/core/src/lut.cpp b/modules/core/src/lut.cpp
index b32edcad2a..80a8d20b01 100644
--- a/modules/core/src/lut.cpp
+++ b/modules/core/src/lut.cpp
@@ -1,6 +1,7 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
@@ -8,6 +9,11 @@
#include "convert.hpp"
#include
+namespace cv {
+void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn );
+void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn );
+} // namespace cv
+
/****************************************************************************************\
* LUT Transform *
\****************************************************************************************/
@@ -40,9 +46,9 @@ static LUTFunc getLUTFunc(const int srcDepth, const int dstDepth)
{
switch(dstDepth)
{
- case CV_8U: ret = (LUTFunc)LUT_; break;
+ case CV_8U: ret = (LUTFunc)LUT8u_dispatch; break;
case CV_8S: ret = (LUTFunc)LUT_; break;
- case CV_16U: ret = (LUTFunc)LUT_; break;
+ case CV_16U: ret = (LUTFunc)LUT16u_dispatch; break;
case CV_16S: ret = (LUTFunc)LUT_; break;
case CV_32S: ret = (LUTFunc)LUT_; break;
case CV_32F: ret = (LUTFunc)LUT_; break; // float
diff --git a/modules/core/src/lut.dispatch.cpp b/modules/core/src/lut.dispatch.cpp
new file mode 100644
index 0000000000..a95a54daa0
--- /dev/null
+++ b/modules/core/src/lut.dispatch.cpp
@@ -0,0 +1,56 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
+
+#include "precomp.hpp"
+
+#include "lut.simd.hpp"
+#include "lut.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX512_ICL,...,BASELINE based on CMakeLists.txt content
+
+namespace cv {
+
+namespace {
+
+typedef void (*LUT8uFunc)( const uchar*, const uchar*, uchar*, int, int, int );
+typedef void (*LUT16uFunc)( const uchar*, const ushort*, ushort*, int, int, int );
+
+static LUT8uFunc resolveLUT8uFunc()
+{
+#if CV_TRY_AVX512_ICL
+ if (cv::checkHardwareSupport(CV_CPU_AVX512_ICL))
+ return opt_AVX512_ICL::LUT8u_;
+#endif
+ return cpu_baseline::LUT8u_;
+}
+
+static LUT16uFunc resolveLUT16uFunc()
+{
+#if CV_TRY_AVX512_ICL
+ if (cv::checkHardwareSupport(CV_CPU_AVX512_ICL))
+ return opt_AVX512_ICL::LUT16u_;
+#endif
+ return cpu_baseline::LUT16u_;
+}
+
+} // namespace
+
+void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn );
+
+void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn )
+{
+ CV_INSTRUMENT_REGION();
+ static LUT8uFunc fn = resolveLUT8uFunc();
+ fn(src, lut, dst, len, cn, lutcn);
+}
+
+void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn );
+
+void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn )
+{
+ CV_INSTRUMENT_REGION();
+ static LUT16uFunc fn = resolveLUT16uFunc();
+ fn(src, lut, dst, len, cn, lutcn);
+}
+
+} // namespace cv
diff --git a/modules/core/src/lut.simd.hpp b/modules/core/src/lut.simd.hpp
new file mode 100644
index 0000000000..c8152a5ae5
--- /dev/null
+++ b/modules/core/src/lut.simd.hpp
@@ -0,0 +1,146 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
+
+#include "precomp.hpp"
+
+// VBMI vpermb is the only x86 SIMD path faster than scalar for byte LUT.
+// x86 gather (AVX2/SSE) is slower than scalar due to high gather latency.
+// Non-x86 (NEON, etc.) benefits from v_lut implementation.
+#if CV_AVX_512VBMI
+#define CV_LUT8U_SIMD 1
+#elif (defined(CV_CPU_COMPILE_SSE) || defined(CV_CPU_COMPILE_SSE2) || defined(CV_CPU_COMPILE_SSE3) || \
+ defined(CV_CPU_COMPILE_SSSE3) || defined(CV_CPU_COMPILE_SSE4_1) || defined(CV_CPU_COMPILE_SSE4_2) || \
+ defined(CV_CPU_COMPILE_AVX) || defined(CV_CPU_COMPILE_AVX2) || defined(CV_CPU_COMPILE_AVX_512F) || \
+ defined(CV_CPU_COMPILE_AVX512_COMMON) || defined(CV_CPU_COMPILE_AVX512_SKX) || \
+ defined(CV_CPU_COMPILE_AVX512_CNL) || defined(CV_CPU_COMPILE_AVX512_CLX))
+#define CV_LUT8U_SIMD 0
+#elif CV_SIMD || CV_SIMD_SCALABLE
+#define CV_LUT8U_SIMD 1
+#else
+#define CV_LUT8U_SIMD 0
+#endif
+
+namespace cv {
+CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
+
+void LUT8u_( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn );
+void LUT16u_( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn );
+
+#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
+
+void LUT8u_( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn )
+{
+ const int total = len * cn;
+
+ if( lutcn == 1 )
+ {
+ int i = 0;
+#if CV_LUT8U_SIMD
+ const int nlanes = VTraits::vlanes();
+ for( ; i <= total - nlanes; i += nlanes )
+ {
+ v_uint8 idx = vx_load(src + i);
+ v_uint8 result = v_lut(lut, idx);
+ v_store(dst + i, result);
+ }
+#endif
+ for( ; i < total; i++ )
+ dst[i] = lut[src[i]];
+ }
+#if CV_LUT8U_SIMD
+ else if( cn == 3 )
+ {
+ // Deinterleave the 3-channel LUT into per-channel tables
+ uchar CV_DECL_ALIGNED(64) lut0[256], lut1[256], lut2[256];
+ const int nlanes = VTraits::vlanes();
+ {
+ unsigned j = 0;
+ for( ; j + (unsigned)nlanes <= 256; j += (unsigned)nlanes )
+ {
+ v_uint8 a, b, c;
+ v_load_deinterleave(lut + j * 3, a, b, c);
+ v_store(lut0 + j, a);
+ v_store(lut1 + j, b);
+ v_store(lut2 + j, c);
+ }
+ for( ; j < 256; j++ )
+ {
+ const unsigned idx = j * 3;
+ lut0[j] = lut[idx];
+ lut1[j] = lut[idx + 1];
+ lut2[j] = lut[idx + 2];
+ }
+ }
+ int i = 0;
+ for( ; i <= total - nlanes*3; i += nlanes*3 )
+ {
+ v_uint8 r, g, b;
+ v_load_deinterleave(src + i, r, g, b);
+ r = v_lut(lut0, r);
+ g = v_lut(lut1, g);
+ b = v_lut(lut2, b);
+ v_store_interleave(dst + i, r, g, b);
+ }
+ for( ; i < total; i += 3 )
+ {
+ dst[i] = lut0[src[i]];
+ dst[i+1] = lut1[src[i+1]];
+ dst[i+2] = lut2[src[i+2]];
+ }
+ }
+#endif
+ else
+ {
+ for( int i = 0; i < total; i += cn )
+ for( int k = 0; k < cn; k++ )
+ dst[i+k] = lut[src[i+k]*cn + k];
+ }
+}
+
+void LUT16u_( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn )
+{
+ const int total = len * cn;
+
+ if( lutcn == 1 )
+ {
+ int i = 0;
+#if CV_LUT8U_SIMD
+ // Split ushort LUT into low-byte and high-byte tables,
+ // then use two byte v_lut (vpermb on VBMI) + interleave.
+ uchar CV_DECL_ALIGNED(64) lut_lo[256], lut_hi[256];
+ for( int j = 0; j < 256; j++ )
+ {
+ lut_lo[j] = (uchar)(lut[j]);
+ lut_hi[j] = (uchar)(lut[j] >> 8);
+ }
+ const int nlanes8 = VTraits::vlanes();
+ const int nlanes16 = VTraits::vlanes();
+ for( ; i <= total - nlanes8; i += nlanes8 )
+ {
+ v_uint8 idx = vx_load(src + i);
+ v_uint8 lo = v_lut(lut_lo, idx);
+ v_uint8 hi = v_lut(lut_hi, idx);
+ // Zip low and high bytes: [l0,h0,l1,h1,...] → ushort values on little-endian
+ v_uint8 res0, res1;
+ v_zip(lo, hi, res0, res1);
+ v_store(dst + i, v_reinterpret_as_u16(res0));
+ v_store(dst + i + nlanes16, v_reinterpret_as_u16(res1));
+ }
+#endif
+ for( ; i < total; i++ )
+ dst[i] = lut[src[i]];
+ }
+ else
+ {
+ for( int i = 0; i < total; i += cn )
+ for( int k = 0; k < cn; k++ )
+ dst[i+k] = lut[src[i+k]*cn + k];
+ }
+}
+
+#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
+
+CV_CPU_OPTIMIZATION_NAMESPACE_END
+} // namespace cv
diff --git a/modules/core/src/mathfuncs_core.dispatch.cpp b/modules/core/src/mathfuncs_core.dispatch.cpp
index 84e4e6a652..db27713c51 100644
--- a/modules/core/src/mathfuncs_core.dispatch.cpp
+++ b/modules/core/src/mathfuncs_core.dispatch.cpp
@@ -107,7 +107,6 @@ void invSqrt32f(const float* src, float* dst, int len)
CV_INSTRUMENT_REGION();
CALL_HAL(invSqrt32f, cv_hal_invSqrt32f, src, dst, len);
- CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsInvSqrt_32f_A21, src, dst, len) >= 0);
CV_CPU_DISPATCH(invSqrt32f, (src, dst, len),
CV_CPU_DISPATCH_MODES_ALL);
@@ -119,7 +118,6 @@ void invSqrt64f(const double* src, double* dst, int len)
CV_INSTRUMENT_REGION();
CALL_HAL(invSqrt64f, cv_hal_invSqrt64f, src, dst, len);
- CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsInvSqrt_64f_A50, src, dst, len) >= 0);
CV_CPU_DISPATCH(invSqrt64f, (src, dst, len),
CV_CPU_DISPATCH_MODES_ALL);
@@ -152,7 +150,6 @@ void exp32f(const float *src, float *dst, int n)
CV_INSTRUMENT_REGION();
CALL_HAL(exp32f, cv_hal_exp32f, src, dst, n);
- CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsExp_32f_A21, src, dst, n) >= 0);
CV_CPU_DISPATCH(exp32f, (src, dst, n),
CV_CPU_DISPATCH_MODES_ALL);
@@ -163,7 +160,6 @@ void exp64f(const double *src, double *dst, int n)
CV_INSTRUMENT_REGION();
CALL_HAL(exp64f, cv_hal_exp64f, src, dst, n);
- CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsExp_64f_A50, src, dst, n) >= 0);
CV_CPU_DISPATCH(exp64f, (src, dst, n),
CV_CPU_DISPATCH_MODES_ALL);
@@ -174,7 +170,6 @@ void log32f(const float *src, float *dst, int n)
CV_INSTRUMENT_REGION();
CALL_HAL(log32f, cv_hal_log32f, src, dst, n);
- CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsLn_32f_A21, src, dst, n) >= 0);
CV_CPU_DISPATCH(log32f, (src, dst, n),
CV_CPU_DISPATCH_MODES_ALL);
@@ -185,7 +180,6 @@ void log64f(const double *src, double *dst, int n)
CV_INSTRUMENT_REGION();
CALL_HAL(log64f, cv_hal_log64f, src, dst, n);
- CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippsLn_64f_A50, src, dst, n) >= 0);
CV_CPU_DISPATCH(log64f, (src, dst, n),
CV_CPU_DISPATCH_MODES_ALL);
diff --git a/modules/core/src/mathfuncs_core.simd.hpp b/modules/core/src/mathfuncs_core.simd.hpp
index d9289ecb4e..8c4dbffe1d 100644
--- a/modules/core/src/mathfuncs_core.simd.hpp
+++ b/modules/core/src/mathfuncs_core.simd.hpp
@@ -595,7 +595,7 @@ void sqrt64f(const double* src, double* dst, int len)
// Workaround for ICE in MSVS 2015 update 3 (issue #7795)
// CV_AVX is not used here, because generated code is faster in non-AVX mode.
// (tested with disabled IPP on i5-6300U)
-#if (defined _MSC_VER && _MSC_VER >= 1900) || defined(__EMSCRIPTEN__)
+#if (defined _MSC_VER && _MSC_VER >= 1900 && (defined(_M_IX86) || defined(_M_X64))) || defined(__EMSCRIPTEN__)
void exp32f(const float *src, float *dst, int n)
{
CV_INSTRUMENT_REGION();
diff --git a/modules/core/src/matrix_expressions.cpp b/modules/core/src/matrix_expressions.cpp
index ab7005de78..b9d6c3b01d 100644
--- a/modules/core/src/matrix_expressions.cpp
+++ b/modules/core/src/matrix_expressions.cpp
@@ -1735,8 +1735,16 @@ MatExpr Mat::mul(InputArray m, double scale) const
{
CV_INSTRUMENT_REGION();
+ Mat b = m.getMat();
+ // Unless the argument is a refcounted Mat/UMat, the header returned by getMat() may be
+ // a non-owning view of caller memory (e.g. a scalar bound to _InputArray(const double&),
+ // a Matx or a Vec) that the returned MatExpr can outlive, so snapshot it.
+ // See https://github.com/opencv/opencv/issues/23577
+ if( !m.isMat() && !m.isUMat() )
+ b = b.clone();
+
MatExpr e;
- MatOp_Bin::makeExpr(e, '*', *this, m.getMat(), scale);
+ MatOp_Bin::makeExpr(e, '*', *this, b, scale);
return e;
}
diff --git a/modules/core/src/matrix_operations.cpp b/modules/core/src/matrix_operations.cpp
index 9a5ae5fa01..d59b8b54c4 100644
--- a/modules/core/src/matrix_operations.cpp
+++ b/modules/core/src/matrix_operations.cpp
@@ -987,7 +987,6 @@ namespace cv
template static void sort_( const Mat& src, Mat& dst, int flags )
{
- AutoBuffer buf;
int n, len;
bool sortRows = (flags & 1) == SORT_EVERY_ROW;
bool inplace = src.data == dst.data;
@@ -996,44 +995,48 @@ template static void sort_( const Mat& src, Mat& dst, int flags )
if( sortRows )
n = src.rows, len = src.cols;
else
- {
n = src.cols, len = src.rows;
- buf.allocate(len);
- }
- T* bptr = buf.data();
-
- for( int i = 0; i < n; i++ )
+ parallel_for_(Range(0, n), [&](const Range& range)
{
- T* ptr = bptr;
- if( sortRows )
- {
- T* dptr = dst.ptr(i);
- if( !inplace )
- {
- const T* sptr = src.ptr(i);
- memcpy(dptr, sptr, sizeof(T) * len);
- }
- ptr = dptr;
- }
- else
- {
- for( int j = 0; j < len; j++ )
- ptr[j] = src.ptr(j)[i];
- }
-
- std::sort( ptr, ptr + len );
- if( sortDescending )
- {
- for( int j = 0; j < len/2; j++ )
- std::swap(ptr[j], ptr[len-1-j]);
- }
-
+ AutoBuffer buf;
if( !sortRows )
- for( int j = 0; j < len; j++ )
- dst.ptr(j)[i] = ptr[j];
- }
+ buf.allocate(len);
+ T* bptr = buf.data();
+
+ for( int i = range.start; i < range.end; i++ )
+ {
+ T* ptr = bptr;
+ if( sortRows )
+ {
+ T* dptr = dst.ptr(i);
+ if( !inplace )
+ {
+ const T* sptr = src.ptr(i);
+ memcpy(dptr, sptr, sizeof(T) * len);
+ }
+ ptr = dptr;
+ }
+ else
+ {
+ for( int j = 0; j < len; j++ )
+ ptr[j] = src.ptr(j)[i];
+ }
+
+ std::sort( ptr, ptr + len );
+ if( sortDescending )
+ {
+ for( int j = 0; j < len/2; j++ )
+ std::swap(ptr[j], ptr[len-1-j]);
+ }
+
+ if( !sortRows )
+ for( int j = 0; j < len; j++ )
+ dst.ptr(j)[i] = ptr[j];
+ }
+ });
}
+
#ifdef HAVE_IPP
typedef IppStatus (CV_STDCALL *IppSortFunc)(void *pSrcDst, int len, Ipp8u *pBuffer);
diff --git a/modules/core/src/matrix_transform.cpp b/modules/core/src/matrix_transform.cpp
index 90994fe91d..21dffcad7b 100644
--- a/modules/core/src/matrix_transform.cpp
+++ b/modules/core/src/matrix_transform.cpp
@@ -526,6 +526,33 @@ void transposeND(InputArray src_, const std::vector& order, OutputArray dst
CV_CheckEQ(inp.channels(), 1, "Input array should be single-channel");
CV_CheckEQ(order.size(), static_cast(inp.dims), "Number of dimensions shouldn't change");
+ bool isIdentityOrder = true;
+ for (size_t i = 0; i < order.size(); ++i)
+ {
+ if (order[i] != static_cast(i))
+ {
+ isIdentityOrder = false;
+ break;
+ }
+ }
+ if (isIdentityOrder)
+ {
+ dst_.create(inp.dims, inp.size.p, inp.type());
+ Mat out = dst_.getMat();
+ CV_Assert(out.isContinuous());
+
+ if (inp.data != out.data)
+ inp.copyTo(out);
+ return;
+ }
+
+ const bool is2DSwap = inp.dims == 2 && order.size() == 2 && order[0] == 1 && order[1] == 0;
+ if (is2DSwap)
+ {
+ transpose(inp, dst_);
+ return;
+ }
+
auto order_ = order;
std::sort(order_.begin(), order_.end());
for (size_t i = 0; i < order_.size(); ++i)
@@ -533,7 +560,7 @@ void transposeND(InputArray src_, const std::vector& order, OutputArray dst
CV_CheckEQ(static_cast(order_[i]), i, "New order should be a valid permutation of the old one");
}
- std::vector newShape(order.size());
+ AutoBuffer newShape(order.size());
for (size_t i = 0; i < order.size(); ++i)
{
newShape[i] = inp.size[order[i]];
@@ -557,7 +584,7 @@ void transposeND(InputArray src_, const std::vector& order, OutputArray dst
size_t continuous_size = continuous_idx == 0 ? out.total() : out.step1(continuous_idx - 1);
size_t outer_size = out.total() / continuous_size;
- std::vector steps(order.size());
+ AutoBuffer steps(order.size());
for (int i = 0; i < static_cast(steps.size()); ++i)
{
steps[i] = inp.step1(order[i]);
@@ -1206,7 +1233,7 @@ void broadcast(InputArray _src, InputArray _shape, OutputArray _dst) {
Mat dst = _dst.getMat();
if (dst.total() == 0)
return;
- std::vector is_same_shape(dims_shape, 0);
+ cv::AutoBuffer is_same_shape(dims_shape, 0);
for (int i = 0; i < static_cast(shape_src.size()); ++i) {
if (shape_src[i] == ptr_shape[i]) {
is_same_shape[i] = 1;
@@ -1313,7 +1340,7 @@ void broadcast(InputArray _src, InputArray _shape, OutputArray _dst) {
std::memcpy(p_dst + dst_offset, p_src + src_offset, dst.elemSize());
}
// broadcast copy (dst inplace)
- std::vector cumulative_shape(dims_shape, 1);
+ AutoBuffer cumulative_shape(dims_shape, 1);
int total = static_cast(dst.total());
for (int i = dims_shape - 1; i >= 0; --i) {
cumulative_shape[i] = static_cast(total / ptr_shape[i]);
diff --git a/modules/core/src/mean.simd.hpp b/modules/core/src/mean.simd.hpp
index db7b411f39..f030eed976 100644
--- a/modules/core/src/mean.simd.hpp
+++ b/modules/core/src/mean.simd.hpp
@@ -2,6 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
#include "stat.hpp"
@@ -31,7 +32,35 @@ struct SumSqr_SIMD
{
int operator () (const uchar * src0, const uchar * mask, int * sum, int * sqsum, int len, int cn) const
{
- if (mask || (cn != 1 && cn != 2 && cn != 4))
+ if (mask)
+ return 0;
+ // cn==3: deinterleave so each lane belongs to one channel, then reduce per
+ // channel. sum/sqsum stay exact in s32 within the dispatcher's 1<<15 block.
+ if (cn == 3)
+ {
+ const int vl8 = VTraits::vlanes();
+ v_int32 vs0 = vx_setzero_s32(), vs1 = vx_setzero_s32(), vs2 = vx_setzero_s32();
+ v_int32 vq0 = vx_setzero_s32(), vq1 = vx_setzero_s32(), vq2 = vx_setzero_s32();
+ auto acc = [](const v_uint8& ch, v_int32& vs, v_int32& vq)
+ {
+ v_uint16 lo, hi; v_expand(ch, lo, hi);
+ v_uint32 s0, s1, s2, s3; v_expand(lo, s0, s1); v_expand(hi, s2, s3);
+ vs = v_add(vs, v_reinterpret_as_s32(v_add(v_add(s0, s1), v_add(s2, s3))));
+ v_int16 d0 = v_reinterpret_as_s16(lo), d1 = v_reinterpret_as_s16(hi);
+ vq = v_add(vq, v_add(v_dotprod(d0, d0), v_dotprod(d1, d1)));
+ };
+ int e = 0;
+ for (; e <= len - vl8; e += vl8)
+ {
+ v_uint8 a, b, c; v_load_deinterleave(src0 + e * 3, a, b, c);
+ acc(a, vs0, vq0); acc(b, vs1, vq1); acc(c, vs2, vq2);
+ }
+ sum[0] += v_reduce_sum(vs0); sum[1] += v_reduce_sum(vs1); sum[2] += v_reduce_sum(vs2);
+ sqsum[0] += v_reduce_sum(vq0); sqsum[1] += v_reduce_sum(vq1); sqsum[2] += v_reduce_sum(vq2);
+ v_cleanup();
+ return e;
+ }
+ if (cn != 1 && cn != 2 && cn != 4)
return 0;
len *= cn;
@@ -98,7 +127,32 @@ struct SumSqr_SIMD
{
int operator () (const schar * src0, const uchar * mask, int * sum, int * sqsum, int len, int cn) const
{
- if (mask || (cn != 1 && cn != 2 && cn != 4))
+ if (mask)
+ return 0;
+ if (cn == 3)
+ {
+ const int vl8 = VTraits::vlanes();
+ v_int32 vs0 = vx_setzero_s32(), vs1 = vx_setzero_s32(), vs2 = vx_setzero_s32();
+ v_int32 vq0 = vx_setzero_s32(), vq1 = vx_setzero_s32(), vq2 = vx_setzero_s32();
+ auto acc = [](const v_int8& ch, v_int32& vs, v_int32& vq)
+ {
+ v_int16 lo, hi; v_expand(ch, lo, hi);
+ v_int32 s0, s1, s2, s3; v_expand(lo, s0, s1); v_expand(hi, s2, s3);
+ vs = v_add(vs, v_add(v_add(s0, s1), v_add(s2, s3)));
+ vq = v_add(vq, v_add(v_dotprod(lo, lo), v_dotprod(hi, hi)));
+ };
+ int e = 0;
+ for (; e <= len - vl8; e += vl8)
+ {
+ v_int8 a, b, c; v_load_deinterleave(src0 + e * 3, a, b, c);
+ acc(a, vs0, vq0); acc(b, vs1, vq1); acc(c, vs2, vq2);
+ }
+ sum[0] += v_reduce_sum(vs0); sum[1] += v_reduce_sum(vs1); sum[2] += v_reduce_sum(vs2);
+ sqsum[0] += v_reduce_sum(vq0); sqsum[1] += v_reduce_sum(vq1); sqsum[2] += v_reduce_sum(vq2);
+ v_cleanup();
+ return e;
+ }
+ if (cn != 1 && cn != 2 && cn != 4)
return 0;
len *= cn;
@@ -160,6 +214,212 @@ struct SumSqr_SIMD
}
};
+#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
+
+// Expand a 16-bit vector into two s32 halves. The unsigned overload reinterprets
+// after a widening expand (values 0..65535 stay non-negative in s32, so the
+// subsequent v_cvt_f64 is exact); the signed overload expands directly. This is
+// the only real difference between the ushort and short reductions below.
+static inline void sumSqrExpandS32(const v_uint16& v, v_int32& lo, v_int32& hi)
+{
+ v_uint32 a, b; v_expand(v, a, b);
+ lo = v_reinterpret_as_s32(a); hi = v_reinterpret_as_s32(b);
+}
+static inline void sumSqrExpandS32(const v_int16& v, v_int32& lo, v_int32& hi)
+{
+ v_expand(v, lo, hi);
+}
+
+// Shared 16-bit -> {s32 sum, f64 sqsum} reduction for ushort/short. The register
+// type is deduced from the pointer, so the body is identical for both depths.
+template
+static inline int sumSqr16ToF64(const T* src0, int* sum, double* sqsum, int len, int cn)
+{
+ typedef decltype(vx_load(src0)) VT; // v_uint16 or v_int16
+
+ if (cn == 3)
+ {
+ const int vl = VTraits::vlanes();
+ v_int32 vs0 = vx_setzero_s32(), vs1 = vx_setzero_s32(), vs2 = vx_setzero_s32();
+ v_float64 vq0 = vx_setzero_f64(), vq1 = vx_setzero_f64(), vq2 = vx_setzero_f64();
+ auto acc = [](const VT& ch, v_int32& vs, v_float64& vq)
+ {
+ v_int32 lo, hi; sumSqrExpandS32(ch, lo, hi);
+ vs = v_add(vs, v_add(lo, hi));
+ v_float64 f0 = v_cvt_f64(lo), f1 = v_cvt_f64_high(lo);
+ v_float64 f2 = v_cvt_f64(hi), f3 = v_cvt_f64_high(hi);
+ vq = v_fma(f0, f0, v_fma(f1, f1, v_fma(f2, f2, v_fma(f3, f3, vq))));
+ };
+ int e = 0;
+ for (; e <= len - vl; e += vl)
+ {
+ VT a, b, c; v_load_deinterleave(src0 + e * 3, a, b, c);
+ acc(a, vs0, vq0); acc(b, vs1, vq1); acc(c, vs2, vq2);
+ }
+ sum[0] += v_reduce_sum(vs0); sum[1] += v_reduce_sum(vs1); sum[2] += v_reduce_sum(vs2);
+ sqsum[0] += v_reduce_sum(vq0); sqsum[1] += v_reduce_sum(vq1); sqsum[2] += v_reduce_sum(vq2);
+ v_cleanup();
+ return e;
+ }
+ if (cn != 1 && cn != 2 && cn != 4)
+ return 0;
+ len *= cn;
+
+ const int vl16 = VTraits::vlanes();
+ const int vl32 = VTraits::vlanes();
+ const int vl64 = VTraits::vlanes();
+
+ // The lane->channel scatter below (i % cn) and the returned pixel count
+ // (x / cn) require the lane counts to be a multiple of cn. This always holds
+ // for fixed-width SIMD (vlanes is a power of two >= cn for cn in {1,2,4}),
+ // but scalable backends (e.g. RVV) may report a non-multiple; fall back to
+ // scalar there. vl16 == 2*vl32, so checking vl32 covers both scatter loops.
+ if (vl32 % cn != 0)
+ return 0;
+
+ v_int32 v_sum = vx_setzero_s32();
+ v_float64 v_sq0 = vx_setzero_f64(), v_sq1 = vx_setzero_f64();
+ v_float64 v_sq2 = vx_setzero_f64(), v_sq3 = vx_setzero_f64();
+
+ int x = 0;
+ for (; x <= len - vl16; x += vl16)
+ {
+ VT v_src = vx_load(src0 + x);
+ v_int32 lo, hi; sumSqrExpandS32(v_src, lo, hi);
+ v_sum = v_add(v_sum, v_add(lo, hi));
+
+ v_float64 f0 = v_cvt_f64(lo);
+ v_float64 f1 = v_cvt_f64_high(lo);
+ v_float64 f2 = v_cvt_f64(hi);
+ v_float64 f3 = v_cvt_f64_high(hi);
+ v_sq0 = v_fma(f0, f0, v_sq0);
+ v_sq1 = v_fma(f1, f1, v_sq1);
+ v_sq2 = v_fma(f2, f2, v_sq2);
+ v_sq3 = v_fma(f3, f3, v_sq3);
+ }
+
+ int CV_DECL_ALIGNED(CV_SIMD_WIDTH) sbuf[VTraits::max_nlanes];
+ double CV_DECL_ALIGNED(CV_SIMD_WIDTH) qbuf[VTraits::max_nlanes * 4];
+ v_store_aligned(sbuf, v_sum);
+ v_store_aligned(qbuf + vl64 * 0, v_sq0);
+ v_store_aligned(qbuf + vl64 * 1, v_sq1);
+ v_store_aligned(qbuf + vl64 * 2, v_sq2);
+ v_store_aligned(qbuf + vl64 * 3, v_sq3);
+
+ // sbuf lane j (j channel j%cn (vl32%cn==0).
+ // qbuf index i (i channel i%cn (vl16%cn==0).
+ for (int i = 0; i < vl32; ++i)
+ sum[i % cn] += sbuf[i];
+ for (int i = 0; i < vl16; ++i)
+ sqsum[i % cn] += qbuf[i];
+
+ v_cleanup();
+ return x / cn;
+}
+
+// Shared 32-bit -> f64 reduction for int/float. Both sum and sqsum accumulate in
+// f64 to match the scalar reference's double accumulation; values are widened to
+// f64 before squaring so the square is computed in double (same as (double)v*v).
+// The register type is deduced from the pointer, so int and float share one body.
+template
+static inline int sumSqr32ToF64(const T* src0, double* sum, double* sqsum, int len, int cn)
+{
+ typedef decltype(vx_load(src0)) VT; // v_int32 or v_float32
+
+ if (cn == 3)
+ {
+ const int vl = VTraits::vlanes();
+ v_float64 vs0 = vx_setzero_f64(), vs1 = vx_setzero_f64(), vs2 = vx_setzero_f64();
+ v_float64 vq0 = vx_setzero_f64(), vq1 = vx_setzero_f64(), vq2 = vx_setzero_f64();
+ auto acc = [](const VT& ch, v_float64& vs, v_float64& vq)
+ {
+ v_float64 f0 = v_cvt_f64(ch), f1 = v_cvt_f64_high(ch);
+ vs = v_add(vs, v_add(f0, f1));
+ vq = v_fma(f0, f0, v_fma(f1, f1, vq));
+ };
+ int e = 0;
+ for (; e <= len - vl; e += vl)
+ {
+ VT a, b, c; v_load_deinterleave(src0 + e * 3, a, b, c);
+ acc(a, vs0, vq0); acc(b, vs1, vq1); acc(c, vs2, vq2);
+ }
+ sum[0] += v_reduce_sum(vs0); sum[1] += v_reduce_sum(vs1); sum[2] += v_reduce_sum(vs2);
+ sqsum[0] += v_reduce_sum(vq0); sqsum[1] += v_reduce_sum(vq1); sqsum[2] += v_reduce_sum(vq2);
+ v_cleanup();
+ return e;
+ }
+ if (cn != 1 && cn != 2 && cn != 4)
+ return 0;
+ len *= cn;
+
+ const int vl32 = VTraits::vlanes();
+ const int vl64 = VTraits::vlanes();
+
+ // See note in sumSqr16ToF64: guard scalable backends whose lane count may
+ // not divide cn (the i % cn scatter / x / cn rely on it).
+ if (vl32 % cn != 0)
+ return 0;
+
+ v_float64 vs0 = vx_setzero_f64(), vs1 = vx_setzero_f64();
+ v_float64 vq0 = vx_setzero_f64(), vq1 = vx_setzero_f64();
+
+ int x = 0;
+ for (; x <= len - vl32; x += vl32)
+ {
+ VT v_src = vx_load(src0 + x);
+ v_float64 f0 = v_cvt_f64(v_src);
+ v_float64 f1 = v_cvt_f64_high(v_src);
+ vs0 = v_add(vs0, f0);
+ vs1 = v_add(vs1, f1);
+ vq0 = v_fma(f0, f0, vq0);
+ vq1 = v_fma(f1, f1, vq1);
+ }
+
+ double CV_DECL_ALIGNED(CV_SIMD_WIDTH) sbuf[VTraits::max_nlanes * 2];
+ double CV_DECL_ALIGNED(CV_SIMD_WIDTH) qbuf[VTraits::max_nlanes * 2];
+ v_store_aligned(sbuf, vs0); v_store_aligned(sbuf + vl64, vs1);
+ v_store_aligned(qbuf, vq0); v_store_aligned(qbuf + vl64, vq1);
+
+ for (int i = 0; i < vl32; ++i)
+ {
+ sum[i % cn] += sbuf[i];
+ sqsum[i % cn] += qbuf[i];
+ }
+
+ v_cleanup();
+ return x / cn;
+}
+
+template <>
+struct SumSqr_SIMD
+{
+ int operator () (const ushort* src0, const uchar* mask, int* sum, double* sqsum, int len, int cn) const
+ { return mask ? 0 : sumSqr16ToF64(src0, sum, sqsum, len, cn); }
+};
+
+template <>
+struct SumSqr_SIMD
+{
+ int operator () (const short* src0, const uchar* mask, int* sum, double* sqsum, int len, int cn) const
+ { return mask ? 0 : sumSqr16ToF64(src0, sum, sqsum, len, cn); }
+};
+
+template <>
+struct SumSqr_SIMD
+{
+ int operator () (const int* src0, const uchar* mask, double* sum, double* sqsum, int len, int cn) const
+ { return mask ? 0 : sumSqr32ToF64(src0, sum, sqsum, len, cn); }
+};
+
+template <>
+struct SumSqr_SIMD
+{
+ int operator () (const float* src0, const uchar* mask, double* sum, double* sqsum, int len, int cn) const
+ { return mask ? 0 : sumSqr32ToF64(src0, sum, sqsum, len, cn); }
+};
+
+#endif // CV_SIMD_64F
+
#endif
template
@@ -253,6 +513,21 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
sum[0] = s0;
sqsum[0] = sq0;
}
+ else if( cn == 2 )
+ {
+ ST s0 = sum[0], s1 = sum[1];
+ SQT sq0 = sqsum[0], sq1 = sqsum[1];
+ for( i = 0; i < len; i++, src += 2 )
+ if( mask[i] )
+ {
+ T v0 = src[0], v1 = src[1];
+ s0 += v0; sq0 += (SQT)v0*v0;
+ s1 += v1; sq1 += (SQT)v1*v1;
+ nzm++;
+ }
+ sum[0] = s0; sum[1] = s1;
+ sqsum[0] = sq0; sqsum[1] = sq1;
+ }
else if( cn == 3 )
{
ST s0 = sum[0], s1 = sum[1], s2 = sum[2];
@@ -269,6 +544,23 @@ static int sumsqr_(const T* src0, const uchar* mask, ST* sum, SQT* sqsum, int le
sum[0] = s0; sum[1] = s1; sum[2] = s2;
sqsum[0] = sq0; sqsum[1] = sq1; sqsum[2] = sq2;
}
+ else if( cn == 4 )
+ {
+ ST s0 = sum[0], s1 = sum[1], s2 = sum[2], s3 = sum[3];
+ SQT sq0 = sqsum[0], sq1 = sqsum[1], sq2 = sqsum[2], sq3 = sqsum[3];
+ for( i = 0; i < len; i++, src += 4 )
+ if( mask[i] )
+ {
+ T v0 = src[0], v1 = src[1], v2 = src[2], v3 = src[3];
+ s0 += v0; sq0 += (SQT)v0*v0;
+ s1 += v1; sq1 += (SQT)v1*v1;
+ s2 += v2; sq2 += (SQT)v2*v2;
+ s3 += v3; sq3 += (SQT)v3*v3;
+ nzm++;
+ }
+ sum[0] = s0; sum[1] = s1; sum[2] = s2; sum[3] = s3;
+ sqsum[0] = sq0; sqsum[1] = sq1; sqsum[2] = sq2; sqsum[3] = sq3;
+ }
else
{
for( i = 0; i < len; i++, src += cn )
diff --git a/modules/core/src/norm.dispatch.cpp b/modules/core/src/norm.dispatch.cpp
index 930c3b758a..ca9aac1f26 100644
--- a/modules/core/src/norm.dispatch.cpp
+++ b/modules/core/src/norm.dispatch.cpp
@@ -1,6 +1,8 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
+//
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "precomp.hpp"
@@ -155,20 +157,27 @@ float normL2Sqr_(const float* a, const float* b, int n)
{
int j = 0; float d = 0.f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vl = VTraits::vlanes();
v_float32 v_d0 = vx_setzero_f32(), v_d1 = vx_setzero_f32();
v_float32 v_d2 = vx_setzero_f32(), v_d3 = vx_setzero_f32();
- for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes())
+ for (; j <= n - 4 * vl; j += 4 * vl)
{
v_float32 t0 = v_sub(vx_load(a + j), vx_load(b + j));
- v_float32 t1 = v_sub(vx_load(a + j + VTraits::vlanes()), vx_load(b + j + VTraits::vlanes()));
+ v_float32 t1 = v_sub(vx_load(a + j + vl), vx_load(b + j + vl));
v_d0 = v_muladd(t0, t0, v_d0);
- v_float32 t2 = v_sub(vx_load(a + j + 2 * VTraits::vlanes()), vx_load(b + j + 2 * VTraits::vlanes()));
+ v_float32 t2 = v_sub(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl));
v_d1 = v_muladd(t1, t1, v_d1);
- v_float32 t3 = v_sub(vx_load(a + j + 3 * VTraits::vlanes()), vx_load(b + j + 3 * VTraits::vlanes()));
+ v_float32 t3 = v_sub(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl));
v_d2 = v_muladd(t2, t2, v_d2);
v_d3 = v_muladd(t3, t3, v_d3);
}
- d = v_reduce_sum(v_add(v_add(v_add(v_d0, v_d1), v_d2), v_d3));
+ v_d0 = v_add(v_add(v_d0, v_d1), v_add(v_d2, v_d3));
+ for (; j <= n - vl; j += vl)
+ {
+ v_float32 t0 = v_sub(vx_load(a + j), vx_load(b + j));
+ v_d0 = v_muladd(t0, t0, v_d0);
+ }
+ d = v_reduce_sum(v_d0);
#endif
for( ; j < n; j++ )
{
@@ -183,16 +192,20 @@ float normL1_(const float* a, const float* b, int n)
{
int j = 0; float d = 0.f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vl = VTraits::vlanes();
v_float32 v_d0 = vx_setzero_f32(), v_d1 = vx_setzero_f32();
v_float32 v_d2 = vx_setzero_f32(), v_d3 = vx_setzero_f32();
- for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes())
+ for (; j <= n - 4 * vl; j += 4 * vl)
{
v_d0 = v_add(v_d0, v_absdiff(vx_load(a + j), vx_load(b + j)));
- v_d1 = v_add(v_d1, v_absdiff(vx_load(a + j + VTraits::vlanes()), vx_load(b + j + VTraits::vlanes())));
- v_d2 = v_add(v_d2, v_absdiff(vx_load(a + j + 2 * VTraits::vlanes()), vx_load(b + j + 2 * VTraits::vlanes())));
- v_d3 = v_add(v_d3, v_absdiff(vx_load(a + j + 3 * VTraits::vlanes()), vx_load(b + j + 3 * VTraits::vlanes())));
+ v_d1 = v_add(v_d1, v_absdiff(vx_load(a + j + vl), vx_load(b + j + vl)));
+ v_d2 = v_add(v_d2, v_absdiff(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl)));
+ v_d3 = v_add(v_d3, v_absdiff(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl)));
}
- d = v_reduce_sum(v_add(v_add(v_add(v_d0, v_d1), v_d2), v_d3));
+ v_d0 = v_add(v_add(v_d0, v_d1), v_add(v_d2, v_d3));
+ for (; j <= n - vl; j += vl)
+ v_d0 = v_add(v_d0, v_absdiff(vx_load(a + j), vx_load(b + j)));
+ d = v_reduce_sum(v_d0);
#endif
for( ; j < n; j++ )
d += std::abs(a[j] - b[j]);
@@ -203,11 +216,14 @@ int normL1_(const uchar* a, const uchar* b, int n)
{
int j = 0, d = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
- for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes())
+ const int vl = VTraits::vlanes();
+ for (; j <= n - 4 * vl; j += 4 * vl)
d += v_reduce_sad(vx_load(a + j), vx_load(b + j)) +
- v_reduce_sad(vx_load(a + j + VTraits::vlanes()), vx_load(b + j + VTraits::vlanes())) +
- v_reduce_sad(vx_load(a + j + 2 * VTraits::vlanes()), vx_load(b + j + 2 * VTraits::vlanes())) +
- v_reduce_sad(vx_load(a + j + 3 * VTraits::vlanes()), vx_load(b + j + 3 * VTraits::vlanes()));
+ v_reduce_sad(vx_load(a + j + vl), vx_load(b + j + vl)) +
+ v_reduce_sad(vx_load(a + j + 2 * vl), vx_load(b + j + 2 * vl)) +
+ v_reduce_sad(vx_load(a + j + 3 * vl), vx_load(b + j + 3 * vl));
+ for (; j <= n - vl; j += vl)
+ d += v_reduce_sad(vx_load(a + j), vx_load(b + j));
#endif
for( ; j < n; j++ )
d += std::abs(a[j] - b[j]);
diff --git a/modules/core/src/opengl.cpp b/modules/core/src/opengl.cpp
index 83be34f147..b7a85a61a4 100644
--- a/modules/core/src/opengl.cpp
+++ b/modules/core/src/opengl.cpp
@@ -1682,27 +1682,12 @@ Context& initializeContextFromGL()
if(extensionSize > 0)
{
- char* extensions = nullptr;
-
- try {
- extensions = new char[extensionSize];
-
- status = clGetDeviceInfo(devices[j], CL_DEVICE_EXTENSIONS, extensionSize, extensions, &extensionSize);
- if (status != CL_SUCCESS)
- continue;
- } catch(...) {
- CV_Error(cv::Error::OpenCLInitError, "OpenCL: Exception thrown during device extensions gathering");
- }
-
- std::string devString;
-
- if(extensions != nullptr) {
- devString = extensions;
- delete[] extensions;
- }
- else {
- CV_Error(cv::Error::OpenCLInitError, "OpenCL: Unexpected error during device extensions gathering");
- }
+ std::string devString(extensionSize, '\0');
+ status = clGetDeviceInfo(devices[j], CL_DEVICE_EXTENSIONS, devString.size(), &devString[0], &extensionSize);
+ if (status != CL_SUCCESS)
+ continue;
+ if (extensionSize > 0 && devString.size() >= extensionSize)
+ devString.resize(extensionSize - 1);
size_t oldPos = 0;
size_t spacePos = devString.find(' ', oldPos); // extensions string is space delimited
diff --git a/modules/core/src/persistence.cpp b/modules/core/src/persistence.cpp
index abcbc15f0c..8256ebd7b5 100644
--- a/modules/core/src/persistence.cpp
+++ b/modules/core/src/persistence.cpp
@@ -268,7 +268,7 @@ int decodeFormat( const char* dt, int* fmt_pairs, int max_len )
int calcElemSize( const char* dt, int initial_size )
{
int size = 0;
- int fmt_pairs[CV_FS_MAX_FMT_PAIRS], i, fmt_pair_count;
+ int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2], i, fmt_pair_count;
int comp_size;
fmt_pair_count = decodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
@@ -323,7 +323,7 @@ int calcStructSize( const char* dt, int initial_size )
int decodeSimpleFormat( const char* dt )
{
int elem_type = -1;
- int fmt_pairs[CV_FS_MAX_FMT_PAIRS], fmt_pair_count;
+ int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2], fmt_pair_count;
fmt_pair_count = decodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
if( fmt_pair_count != 1 || fmt_pairs[0] >= CV_CN_MAX)
diff --git a/modules/core/src/persistence_base64_encoding.cpp b/modules/core/src/persistence_base64_encoding.cpp
index 149117d436..c2d81b94f6 100644
--- a/modules/core/src/persistence_base64_encoding.cpp
+++ b/modules/core/src/persistence_base64_encoding.cpp
@@ -65,15 +65,15 @@ public:
/*
* a convertor must provide :
* - `operator >> (uchar * & dst)` for writing current binary data to `dst` and moving to next data.
- * - `operator bool` for checking if current loaction is valid and not the end.
+ * - `operator bool` for checking if current location is valid and not the end.
*/
template inline
Base64ContextEmitter & write(_to_binary_convertor_t & convertor)
{
- static const size_t BUFFER_MAX_LEN = 1024U;
+ constexpr size_t BUFFER_MAX_LEN = 1024U;
- std::vector buffer(BUFFER_MAX_LEN);
- uchar * beg = buffer.data();
+ uchar buffer[BUFFER_MAX_LEN];
+ uchar * beg = buffer;
uchar * end = beg;
while (convertor) {
diff --git a/modules/core/src/persistence_types.cpp b/modules/core/src/persistence_types.cpp
index 8aa929cc1d..a1614c2c8b 100644
--- a/modules/core/src/persistence_types.cpp
+++ b/modules/core/src/persistence_types.cpp
@@ -75,7 +75,7 @@ void write( FileStorage& fs, const String& name, const SparseMat& m )
fs << "data" << "[:";
size_t i = 0, n = m.nzcount();
- std::vector elems(n);
+ AutoBuffer elems(n);
SparseMatConstIterator it = m.begin(), it_end = m.end();
for( ; it != it_end; ++it )
@@ -142,6 +142,7 @@ void read(const FileNode& node, Mat& m, const Mat& default_mat)
CV_Assert( !sizes_node.empty() );
dims = (int)sizes_node.size();
+ CV_Assert( dims >= 0 && dims <= CV_MAX_DIM );
sizes_node.readRaw("i", sizes, dims*sizeof(sizes[0]));
m.create(dims, sizes, elem_type);
@@ -180,6 +181,7 @@ void read( const FileNode& node, SparseMat& m, const SparseMat& default_mat )
CV_Assert( !sizes_node.empty() );
int dims = (int)sizes_node.size();
+ CV_Assert( dims > 0 && dims <= CV_MAX_DIM );
sizes_node.readRaw("i", sizes, dims*sizeof(sizes[0]));
m.create(dims, sizes, elem_type);
diff --git a/modules/core/src/stat.simd.hpp b/modules/core/src/stat.simd.hpp
index e363313e5b..0322c6e4e2 100644
--- a/modules/core/src/stat.simd.hpp
+++ b/modules/core/src/stat.simd.hpp
@@ -1,6 +1,8 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
+//
+// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
#include "opencv2/core/hal/intrin.hpp"
@@ -8,12 +10,18 @@ namespace cv { namespace hal {
extern const uchar popCountTable[256];
+typedef int (*NormHammingFunc)(const uchar*, int);
+typedef int (*NormHammingDiffFunc)(const uchar*, const uchar*, int);
+
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
// forward declarations
int normHamming(const uchar* a, int n);
int normHamming(const uchar* a, const uchar* b, int n);
+NormHammingFunc getNormHammingFunc();
+NormHammingDiffFunc getNormHammingDiffFunc();
+
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
#if CV_AVX2
@@ -125,6 +133,17 @@ int normHamming(const uchar* a, const uchar* b, int n)
return result;
}
+NormHammingFunc getNormHammingFunc()
+{
+ NormHammingFunc f = &normHamming; // disambiguate the (a,n) overload
+ return f;
+}
+NormHammingDiffFunc getNormHammingDiffFunc()
+{
+ NormHammingDiffFunc f = &normHamming; // disambiguate the (a,b,n) overload
+ return f;
+}
+
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
CV_CPU_OPTIMIZATION_NAMESPACE_END
diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp
index a52946b46a..c2adb5eeee 100644
--- a/modules/core/src/system.cpp
+++ b/modules/core/src/system.cpp
@@ -1176,12 +1176,19 @@ String tempfile( const char* suffix )
#else
// Use GUID-based naming to avoid race condition with GetTempFileNameA
// See issue #19648
- char temp_dir2[MAX_PATH] = { 0 };
+ wchar_t temp_dir2_w[MAX_PATH] = { 0 };
if (temp_dir.empty())
{
- ::GetTempPathA(sizeof(temp_dir2), temp_dir2);
- temp_dir = std::string(temp_dir2);
+ ::GetTempPathW(sizeof(temp_dir2_w)/sizeof(wchar_t), temp_dir2_w);
+ // Convert from UTF-16 to UTF-8 to support Unicode paths
+ int len = WideCharToMultiByte(CP_UTF8, 0, temp_dir2_w, -1, NULL, 0, NULL, NULL);
+ if (len > 0)
+ {
+ std::vector utf8_buf(len);
+ WideCharToMultiByte(CP_UTF8, 0, temp_dir2_w, -1, utf8_buf.data(), len, NULL, NULL);
+ temp_dir = std::string(utf8_buf.data());
+ }
}
GUID g;
@@ -2481,7 +2488,12 @@ public:
IPPInitSingleton()
{
useIPP = true;
+
+#if defined(OPENCV_ALGO_HINT_DEFAULT)
+ useIPP_NE = OPENCV_ALGO_HINT_DEFAULT == cv::ALGO_HINT_APPROX;
+#else
useIPP_NE = false;
+#endif
ippStatus = 0;
funcname = NULL;
filename = NULL;
diff --git a/modules/core/src/utils/filesystem.cpp b/modules/core/src/utils/filesystem.cpp
index adc5d85c6c..1fa58cf03a 100644
--- a/modules/core/src/utils/filesystem.cpp
+++ b/modules/core/src/utils/filesystem.cpp
@@ -438,11 +438,18 @@ cv::String getCacheDirectory(const char* sub_directory_name, const char* configu
{
cv::String default_cache_path;
#ifdef _WIN32
- char tmp_path_buf[MAX_PATH+1] = {0};
- DWORD res = GetTempPath(MAX_PATH, tmp_path_buf);
+ wchar_t tmp_path_buf_w[MAX_PATH+1] = {0};
+ DWORD res = GetTempPathW(MAX_PATH, tmp_path_buf_w);
if (res > 0 && res <= MAX_PATH)
{
- default_cache_path = tmp_path_buf;
+ // Convert from UTF-16 to UTF-8 to support Unicode paths
+ int len = WideCharToMultiByte(CP_UTF8, 0, tmp_path_buf_w, -1, NULL, 0, NULL, NULL);
+ if (len > 0)
+ {
+ std::vector utf8_buf(len);
+ WideCharToMultiByte(CP_UTF8, 0, tmp_path_buf_w, -1, utf8_buf.data(), len, NULL, NULL);
+ default_cache_path = std::string(utf8_buf.data());
+ }
}
#elif defined __ANDROID__
// no defaults
diff --git a/modules/core/test/test_io.cpp b/modules/core/test/test_io.cpp
index fe5e01aed3..6289db4de6 100644
--- a/modules/core/test/test_io.cpp
+++ b/modules/core/test/test_io.cpp
@@ -855,6 +855,61 @@ TEST(Core_InputOutput, filestorage_heap_overflow)
EXPECT_EQ(0, remove(name.c_str()));
}
+TEST(Core_InputOutput, filestorage_nd_matrix_too_many_dims)
+{
+ // A declared dimension count above CV_MAX_DIM used to write the sizes list
+ // past the fixed-size sizes[CV_MAX_DIM] stack buffer in cv::read().
+ std::string sizes;
+ for (int i = 0; i < CV_MAX_DIM + 8; i++)
+ sizes += "2, ";
+ sizes += "2";
+
+ const std::string content =
+ "%YAML:1.0\n---\n"
+ "m: !!opencv-nd-matrix\n"
+ " sizes: [ " + sizes + " ]\n"
+ " dt: f\n"
+ " data: [ 0., 0. ]\n"
+ "sm: !!opencv-sparse-matrix\n"
+ " sizes: [ " + sizes + " ]\n"
+ " dt: f\n"
+ " data: [ ]\n";
+
+ FileStorage fs(content, FileStorage::READ | FileStorage::MEMORY);
+
+ Mat m;
+ EXPECT_ANY_THROW(fs["m"] >> m);
+
+ SparseMat sm;
+ EXPECT_ANY_THROW(fs["sm"] >> sm);
+}
+
+TEST(Core_InputOutput, filestorage_matrix_dt_too_long)
+{
+ // A "dt" string with many distinct adjacent types makes decodeFormat()
+ // emit more pairs than the caller buffer holds. decodeSimpleFormat() sized
+ // its fmt_pairs buffer as CV_FS_MAX_FMT_PAIRS ints while decodeFormat writes
+ // up to 2*CV_FS_MAX_FMT_PAIRS ints, so a long enough dt wrote past the stack
+ // buffer before the length guard could reject it.
+ // CV_FS_MAX_FMT_PAIRS is 128; well over 2x that many distinct pairs.
+ std::string dt;
+ for (int i = 0; i < 300; i++)
+ dt += (i & 1) ? 'c' : 'u';
+
+ const std::string content =
+ "%YAML:1.0\n---\n"
+ "m: !!opencv-matrix\n"
+ " rows: 1\n"
+ " cols: 1\n"
+ " dt: \"" + dt + "\"\n"
+ " data: [ 0 ]\n";
+
+ FileStorage fs(content, FileStorage::READ | FileStorage::MEMORY);
+
+ Mat m;
+ EXPECT_ANY_THROW(fs["m"] >> m);
+}
+
TEST(Core_InputOutput, filestorage_base64_valid_call)
{
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
@@ -2222,6 +2277,24 @@ TEST(Core_InputOutput, FileStorage_int64_26829)
}
}
+TEST(Core_InputOutput, FileStorage_read_bigint_as_real_29363)
+{
+ // An integer above INT_MAX (e.g. from externally-produced json/yaml/xml) must
+ // convert to float/double without truncating to int32. Regression for #29363.
+ String content =
+ "%YAML:1.0\n"
+ "a: 6662329666\n" // ~6.6e9, exact in double
+ "b: -9876543210\n"
+ "c: 4294967296\n"; // 2^32, exact in double and float
+ FileStorage fs(content, FileStorage::READ | FileStorage::MEMORY);
+
+ EXPECT_EQ(6662329666.0, (double)fs["a"]);
+ EXPECT_EQ(-9876543210.0, (double)fs["b"]);
+ EXPECT_EQ(4294967296.0, (double)fs["c"]);
+ EXPECT_EQ(4294967296.0f, (float)fs["c"]);
+ EXPECT_EQ((int64_t)6662329666LL, (int64_t)fs["a"]); // int64 path unchanged
+}
+
template
T fsWriteRead(const T& expectedValue, const char* ext)
{
diff --git a/modules/core/test/test_operations.cpp b/modules/core/test/test_operations.cpp
index d6913fd269..bebbf137e7 100644
--- a/modules/core/test/test_operations.cpp
+++ b/modules/core/test/test_operations.cpp
@@ -1561,6 +1561,37 @@ TEST(Core_MatExpr, empty_check_15760)
EXPECT_THROW(Mat c = Mat().cross(Mat()), cv::Exception);
}
+// https://github.com/opencv/opencv/issues/23577
+// A scalar passed to Mat::mul() binds to _InputArray(const double&), i.e. to a temporary
+// double on the caller's stack. The returned MatExpr used to keep a Mat header pointing at
+// that stack slot after it died. The helpers are called through volatile function pointers
+// so they cannot be inlined, which makes the stale-stack read deterministic.
+MatExpr makeScalarMulExpr(const Mat& m, double scale)
+{
+ return m.mul(scale);
+}
+
+void overwriteStackFrame()
+{
+ volatile double buf[256];
+ for (int i = 0; i < 256; i++)
+ buf[i] = -1.0;
+ (void)buf;
+}
+
+TEST(Core_MatExpr, mul_scalar_use_after_scope_23577)
+{
+ MatExpr (*volatile makeExprFn)(const Mat&, double) = makeScalarMulExpr;
+ void (*volatile overwriteFn)() = overwriteStackFrame;
+
+ Mat m(2, 3, CV_32FC1, Scalar::all(3.0f));
+ MatExpr e = makeExprFn(m, 7.0);
+ overwriteFn();
+ Mat res = e;
+
+ EXPECT_EQ(0, cvtest::norm(res, Mat(2, 3, CV_32FC1, Scalar::all(21.0f)), NORM_INF));
+}
+
TEST(Core_Arithm, scalar_handling_19599) // https://github.com/opencv/opencv/issues/19599 (OpenCV 4.x+ only)
{
Mat a(1, 1, CV_32F, Scalar::all(1));
diff --git a/modules/dnn/perf/perf_utils.cpp b/modules/dnn/perf/perf_utils.cpp
index c6dd0bf058..4bae8b398b 100644
--- a/modules/dnn/perf/perf_utils.cpp
+++ b/modules/dnn/perf/perf_utils.cpp
@@ -69,4 +69,343 @@ INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImages,
std::vector{16, 2048, 2048})
);
+// NCHW, 8U->32F, C3, mean+scale+swapRB at 640x640
+using Utils_blobFromImage_8U_NCHW = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImage_8U_NCHW, MeanScale_SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_8UC3);
+ randu(input, 0, 255);
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImage(input, 1.0/255.0, Size(), Scalar(104, 117, 123), true, false, CV_32F);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_NCHW,
+ Values(std::vector{ 640, 640})
+);
+
+// NHWC, 8U->32F, C3
+using Utils_blobFromImage_8U_NHWC = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImage_8U_NHWC, SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_8UC3);
+ randu(input, 0, 255);
+
+ Image2BlobParams params;
+ params.scalefactor = Scalar::all(1.0);
+ params.swapRB = true;
+ params.datalayout = DNN_LAYOUT_NHWC;
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImageWithParams(input, params);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+PERF_TEST_P_(Utils_blobFromImage_8U_NHWC, MeanScale_SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_8UC3);
+ randu(input, 0, 255);
+
+ Image2BlobParams params;
+ params.scalefactor = Scalar::all(1.0/255.0);
+ params.mean = Scalar(104, 117, 123);
+ params.swapRB = true;
+ params.datalayout = DNN_LAYOUT_NHWC;
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImageWithParams(input, params);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_NHWC,
+ Values(std::vector{ 224, 224},
+ std::vector{ 640, 640})
+);
+
+// NHWC, 32F->32F, C3
+using Utils_blobFromImage_32F_NHWC = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImage_32F_NHWC, SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_32FC3);
+ randu(input, 0.0f, 1.0f);
+
+ Image2BlobParams params;
+ params.scalefactor = Scalar::all(1.0);
+ params.swapRB = true;
+ params.datalayout = DNN_LAYOUT_NHWC;
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImageWithParams(input, params);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+PERF_TEST_P_(Utils_blobFromImage_32F_NHWC, MeanScale_SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_32FC3);
+ randu(input, 0.0f, 1.0f);
+
+ Image2BlobParams params;
+ params.scalefactor = Scalar::all(1.0/0.226);
+ params.mean = Scalar(0.485, 0.456, 0.406);
+ params.swapRB = true;
+ params.datalayout = DNN_LAYOUT_NHWC;
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImageWithParams(input, params);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_32F_NHWC,
+ Values(std::vector{ 224, 224},
+ std::vector{ 640, 640})
+);
+
+// Resize+crop, 8U->32F, C3, mean+scale+swapRB to 640x640
+using Utils_blobFromImage_8U_Resize = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImage_8U_Resize, NHWC_Crop_MeanScale_SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_8UC3);
+ randu(input, 0, 255);
+
+ Image2BlobParams params;
+ params.scalefactor = Scalar::all(1.0/255.0);
+ params.size = Size(640, 640);
+ params.mean = Scalar(104, 117, 123);
+ params.swapRB = true;
+ params.datalayout = DNN_LAYOUT_NHWC;
+ params.paddingmode = DNN_PMODE_CROP_CENTER;
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImageWithParams(input, params);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+PERF_TEST_P_(Utils_blobFromImage_8U_Resize, NCHW_Crop_MeanScale_SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_8UC3);
+ randu(input, 0, 255);
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImage(input, 1.0/255.0, Size(640, 640), Scalar(104, 117, 123), true, true, CV_32F);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_Resize,
+ Values(std::vector{ 720, 1280},
+ std::vector{ 1080, 1920},
+ std::vector{ 2160, 3840})
+);
+
+// Resize+crop, NCHW, 32F->32F, C3, mean+scale+swapRB to 300x300
+using Utils_blobFromImage_32F_NCHW_Resize = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImage_32F_NCHW_Resize, Crop_MeanScale_SwapRB_To300) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_32FC3);
+ randu(input, 0.0f, 1.0f);
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImage(input, 1.0/0.226, Size(300, 300), Scalar(0.485, 0.456, 0.406), true, true, CV_32F);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_32F_NCHW_Resize,
+ Values(std::vector{ 720, 1280},
+ std::vector{ 1080, 1920})
+);
+
+// Resize+crop, NCHW, 8U->8U, C3
+using Utils_blobFromImage_8U_to_8U_Crop = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImage_8U_to_8U_Crop, NCHW_SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_8UC3);
+ randu(input, 0, 255);
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImage(input, 1.0, Size(640, 640), Scalar(), true, true, CV_8U);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+PERF_TEST_P_(Utils_blobFromImage_8U_to_8U_Crop, NCHW) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_8UC3);
+ randu(input, 0, 255);
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImage(input, 1.0, Size(640, 640), Scalar(), false, true, CV_8U);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_to_8U_Crop,
+ Values(std::vector{ 1080, 1920},
+ std::vector{ 2160, 3840})
+);
+
+// Resize, NCHW, 8U->8U, C3
+using Utils_blobFromImage_8U_to_8U_Resize = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImage_8U_to_8U_Resize, NCHW_SwapRB) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_8UC3);
+ randu(input, 0, 255);
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImage(input, 1.0, Size(640, 640), Scalar(), true, false, CV_8U);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_8U_to_8U_Resize,
+ Values(std::vector{ 1080, 1920},
+ std::vector{ 2160, 3840})
+);
+
+// Resize+crop, NCHW, 32F->32F, C1, mean
+using Utils_blobFromImage_32F_NCHW_C1 = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImage_32F_NCHW_C1, Crop_MeanScale_To224) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_32FC1);
+ randu(input, 0.0f, 1.0f);
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImage(input, 1.0/0.226, Size(224, 224), Scalar(0.5), false, true, CV_32F);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+PERF_TEST_P_(Utils_blobFromImage_32F_NCHW_C1, Crop_MeanScale_To640) {
+ std::vector input_shape = GetParam();
+
+ Mat input(input_shape, CV_32FC1);
+ randu(input, 0.0f, 1.0f);
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImage(input, 1.0/0.226, Size(640, 640), Scalar(0.5), false, true, CV_32F);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImage_32F_NCHW_C1,
+ Values(std::vector{ 1080, 1920},
+ std::vector{ 2160, 3840})
+);
+
+// Batch=8, NHWC, 8U->32F, C3, mean+scale+swapRB
+using Utils_blobFromImages_NoResize = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImages_NoResize, NHWC_MeanScale_SwapRB) {
+ std::vector input_shape = GetParam();
+ int batch = input_shape.front();
+ std::vector input_shape_no_batch(input_shape.begin()+1, input_shape.end());
+
+ std::vector inputs;
+ for (int i = 0; i < batch; i++) {
+ Mat input(input_shape_no_batch, CV_8UC3);
+ randu(input, 0, 255);
+ inputs.push_back(input);
+ }
+
+ Image2BlobParams params;
+ params.scalefactor = Scalar::all(1.0/255.0);
+ params.mean = Scalar(104, 117, 123);
+ params.swapRB = true;
+ params.datalayout = DNN_LAYOUT_NHWC;
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImagesWithParams(inputs, params);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImages_NoResize,
+ Values(std::vector{8, 640, 640})
+);
+
+// Batch=8, resize+crop to 640x640, 8U->32F, C3, mean+scale+swapRB
+using Utils_blobFromImages_Resize = TestBaseWithParam>;
+PERF_TEST_P_(Utils_blobFromImages_Resize, NHWC_Crop_MeanScale_SwapRB) {
+ std::vector input_shape = GetParam();
+ int batch = input_shape.front();
+ std::vector input_shape_no_batch(input_shape.begin()+1, input_shape.end());
+
+ std::vector inputs;
+ for (int i = 0; i < batch; i++) {
+ Mat input(input_shape_no_batch, CV_8UC3);
+ randu(input, 0, 255);
+ inputs.push_back(input);
+ }
+
+ Image2BlobParams params;
+ params.scalefactor = Scalar::all(1.0/255.0);
+ params.size = Size(640, 640);
+ params.mean = Scalar(104, 117, 123);
+ params.swapRB = true;
+ params.datalayout = DNN_LAYOUT_NHWC;
+ params.paddingmode = DNN_PMODE_CROP_CENTER;
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImagesWithParams(inputs, params);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+PERF_TEST_P_(Utils_blobFromImages_Resize, NCHW_Crop_MeanScale_SwapRB) {
+ std::vector input_shape = GetParam();
+ int batch = input_shape.front();
+ std::vector input_shape_no_batch(input_shape.begin()+1, input_shape.end());
+
+ std::vector inputs;
+ for (int i = 0; i < batch; i++) {
+ Mat input(input_shape_no_batch, CV_8UC3);
+ randu(input, 0, 255);
+ inputs.push_back(input);
+ }
+
+ TEST_CYCLE() {
+ Mat blob = blobFromImages(inputs, 1.0/255.0, Size(640, 640), Scalar(104, 117, 123), true, true, CV_32F);
+ }
+
+ SANITY_CHECK_NOTHING();
+}
+
+INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImages_Resize,
+ Values(std::vector{8, 720, 1280},
+ std::vector{8, 1080, 1920})
+);
+
}
diff --git a/modules/dnn/src/dnn_utils.cpp b/modules/dnn/src/dnn_utils.cpp
index 5cc1359b0d..29135fc6df 100644
--- a/modules/dnn/src/dnn_utils.cpp
+++ b/modules/dnn/src/dnn_utils.cpp
@@ -152,29 +152,69 @@ void blobFromImagesNCHWImpl(const std::vector& images, Mat& blob_, const Im
if (param.swapRB)
std::swap(p_blob_r, p_blob_b);
- for (size_t i = 0; i < h; ++i)
+ if (nch == 1)
{
- const Tinp* p_img_row = images[k].ptr(i);
-
- if (nch == 1)
+ for (size_t i = 0; i < (size_t)h; ++i)
{
- for (size_t j = 0; j < w; ++j)
+ const Tinp* p_img_row = images[k].ptr(i);
+ for (size_t j = 0; j < (size_t)w; ++j)
{
p_blob[i * w + j] = p_img_row[j];
}
}
- else if (nch == 3)
+ }
+ else if (nch == 3)
+ {
+ if (images[k].isContinuous())
{
- for (size_t j = 0; j < w; ++j)
+ const Tinp* p_src = images[k].ptr();
+ size_t i = 0;
+// NOTE: Visual Studio compiler is not able to vectorize the following loop on ARM64 CPU.
+// GCC and Clang does it efficiently.
+// TODO: Drop NEON block when MSVC is able to vectorize it too.
+#if CV_NEON
+ if (sizeof(Tinp) == 4 && sizeof(Tout) == 4)
{
- p_blob_r[i * w + j] = p_img_row[j * 3 ];
- p_blob_g[i * w + j] = p_img_row[j * 3 + 1];
- p_blob_b[i * w + j] = p_img_row[j * 3 + 2];
+ const float* src_f = reinterpret_cast(p_src);
+ float* dst_r = reinterpret_cast(p_blob_r);
+ float* dst_g = reinterpret_cast(p_blob_g);
+ float* dst_b = reinterpret_cast(p_blob_b);
+ for (; i + 4 <= (size_t)wh; i += 4)
+ {
+ float32x4x3_t rgb = vld3q_f32(src_f + i * 3);
+ vst1q_f32(dst_r + i, rgb.val[0]);
+ vst1q_f32(dst_g + i, rgb.val[1]);
+ vst1q_f32(dst_b + i, rgb.val[2]);
+ }
+ }
+#endif
+ for (; i < (size_t)wh; ++i)
+ {
+ p_blob_r[i] = p_src[i * 3 ];
+ p_blob_g[i] = p_src[i * 3 + 1];
+ p_blob_b[i] = p_src[i * 3 + 2];
}
}
- else // if (nch == 4)
+ else
{
- for (size_t j = 0; j < w; ++j)
+ for (size_t i = 0; i < (size_t)h; ++i)
+ {
+ const Tinp* p_img_row = images[k].ptr(i);
+ for (size_t j = 0; j < (size_t)w; ++j)
+ {
+ p_blob_r[i * w + j] = p_img_row[j * 3 ];
+ p_blob_g[i * w + j] = p_img_row[j * 3 + 1];
+ p_blob_b[i * w + j] = p_img_row[j * 3 + 2];
+ }
+ }
+ }
+ }
+ else // if (nch == 4)
+ {
+ for (size_t i = 0; i < (size_t)h; ++i)
+ {
+ const Tinp* p_img_row = images[k].ptr(i);
+ for (size_t j = 0; j < (size_t)w; ++j)
{
p_blob_r[i * w + j] = p_img_row[j * 4 ];
p_blob_g[i * w + j] = p_img_row[j * 4 + 1];
diff --git a/modules/dnn/src/layers/einsum_layer.cpp b/modules/dnn/src/layers/einsum_layer.cpp
index de26131b09..4699fa5f81 100644
--- a/modules/dnn/src/layers/einsum_layer.cpp
+++ b/modules/dnn/src/layers/einsum_layer.cpp
@@ -1056,6 +1056,8 @@ void LayerEinsumImpl::processEquation(const std::vector& inputs)
CV_CheckNE(letterIdx, -1,
"The only permissible subscript labels are lowercase letters (a-z) and uppercase letters (A-Z).");
+ CV_CheckLT(dim_count, rank,
+ "The Einsum subscripts string has an excessive number of subscript labels compared to the rank of the input.");
int dimValue = shape[dim_count];
// The subscript label was not found in the global subscript label array
@@ -1083,8 +1085,7 @@ void LayerEinsumImpl::processEquation(const std::vector& inputs)
++letter2count[letterIdx];
currTokenIndices.push_back(letter2index[letterIdx]);
- CV_CheckLE(++dim_count, rank,
- "The Einsum subscripts string has an excessive number of subscript labels compared to the rank of the input.");
+ ++dim_count;
}
}
diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp
index f11fe510f2..5405d938d2 100644
--- a/modules/dnn/src/layers/elementwise_layers.cpp
+++ b/modules/dnn/src/layers/elementwise_layers.cpp
@@ -960,6 +960,32 @@ struct GeluApproximationFunctor : public BaseDefaultFunctor::vlanes();
+ v_float32 one = vx_setall_f32(1.f), two = vx_setall_f32(2.f), half = vx_setall_f32(0.5f);
+ v_float32 c1 = vx_setall_f32(GeluApproximationConstants::sqrt_2_pi);
+ v_float32 c2 = vx_setall_f32(GeluApproximationConstants::coef_sqrt_2_pi);
+ v_float32 lo = vx_setall_f32(-88.f), hi = vx_setall_f32(88.f);
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ v_float32 u = v_mul(x, v_fma(v_mul(x, x), c2, c1));
+ v_float32 e = v_exp(v_min(v_max(v_add(u, u), lo), hi));
+ v_float32 th = v_sub(one, v_div(two, v_add(e, one)));
+ vx_store(dstptr + i, v_mul(v_mul(half, x), v_add(one, th)));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
int64 getFLOPSPerElement() const { return 100; }
};
@@ -993,6 +1019,28 @@ struct TanHFunctor : public BaseDefaultFunctor
return tanh(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 one = vx_setall_f32(1.f), two = vx_setall_f32(2.f);
+ v_float32 lo = vx_setall_f32(-88.f), hi = vx_setall_f32(88.f);
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ v_float32 e = v_exp(v_min(v_max(v_add(x, x), lo), hi)); // e^{2x}, clamped
+ vx_store(dstptr + i, v_sub(one, v_div(two, v_add(e, one)))); // 1 - 2/(e+1)
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -1517,6 +1565,27 @@ struct BNLLFunctor : public BaseDefaultFunctor
return x > 0 ? x + log(1.f + exp(-x)) : log(1.f + exp(x));
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 one = vx_setall_f32(1.f), z = vx_setzero_f32();
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ // stable single-branch form: max(x,0) + log(1 + exp(-|x|))
+ vx_store(dstptr + i, v_add(v_max(x, z), v_log(v_add(one, v_exp(v_sub(z, v_abs(x)))))));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -1671,6 +1740,22 @@ struct LogFunctor : public BaseDefaultFunctor
return log(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ for (; i <= len - vlanes; i += vlanes)
+ vx_store(dstptr + i, v_log(vx_load(srcptr + i)));
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -1801,6 +1886,26 @@ struct AcoshFunctor : public BaseDefaultFunctor
return acosh(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 one = vx_setall_f32(1.f);
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_log(v_add(x, v_sqrt(v_sub(v_mul(x, x), one)))));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -1863,6 +1968,28 @@ struct AsinhFunctor : public BaseDefaultFunctor
return asinh(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 one = vx_setall_f32(1.f), z = vx_setzero_f32();
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ v_float32 ax = v_abs(x);
+ v_float32 r = v_log(v_add(ax, v_sqrt(v_fma(ax, ax, one))));
+ vx_store(dstptr + i, v_select(v_lt(x, z), v_sub(z, r), r));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -1925,6 +2052,26 @@ struct AtanhFunctor : public BaseDefaultFunctor
return atanh(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 one = vx_setall_f32(1.f), half = vx_setall_f32(0.5f);
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_mul(half, v_log(v_div(v_add(one, x), v_sub(one, x)))));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -1960,6 +2107,25 @@ struct CosFunctor : public BaseDefaultFunctor
return cos(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_cos(x));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -1995,6 +2161,26 @@ struct CoshFunctor : public BaseDefaultFunctor
return cosh(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 half = vx_setall_f32(0.5f), z = vx_setzero_f32();
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_mul(half, v_add(v_exp(x), v_exp(v_sub(z, x)))));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -2030,6 +2216,22 @@ struct ErfFunctor : public BaseDefaultFunctor
return erf(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ for (; i <= len - vlanes; i += vlanes)
+ vx_store(dstptr + i, v_erf(vx_load(srcptr + i)));
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -2156,6 +2358,25 @@ struct SinFunctor : public BaseDefaultFunctor
return sin(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_sin(x));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -2191,6 +2412,26 @@ struct SinhFunctor : public BaseDefaultFunctor
return sinh(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 half = vx_setall_f32(0.5f), z = vx_setzero_f32();
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_mul(half, v_sub(v_exp(x), v_exp(v_sub(z, x)))));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -2226,6 +2467,26 @@ struct SoftplusFunctor : public BaseDefaultFunctor
return log1p(exp(x));
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 one = vx_setall_f32(1.f);
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_log(v_add(one, v_exp(x))));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -2288,6 +2549,25 @@ struct TanFunctor : public BaseDefaultFunctor
return tan(x);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_div(v_sin(x), v_cos(x)));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
#ifdef HAVE_CUDA
Ptr initCUDA(int target, csl::Stream stream)
{
@@ -2765,6 +3045,26 @@ struct ExpFunctor : public BaseDefaultFunctor
return exp(normScale * x + normShift);
}
+ void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const
+ {
+ CV_UNUSED(stripeStart);
+ for (int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize)
+ {
+ int i = 0;
+#if (CV_SIMD || CV_SIMD_SCALABLE)
+ const int vlanes = VTraits::vlanes();
+ v_float32 vsc = vx_setall_f32(normScale), vsh = vx_setall_f32(normShift);
+ for (; i <= len - vlanes; i += vlanes)
+ {
+ v_float32 x = vx_load(srcptr + i);
+ vx_store(dstptr + i, v_exp(v_fma(x, vsc, vsh)));
+ }
+#endif
+ for (; i < len; i++)
+ dstptr[i] = calculate(srcptr[i]);
+ }
+ }
+
inline void setKernelParams(ocl::Kernel& kernel) const
{
kernel.set(3, normScale);
diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp
index 699c44d6e0..ac8c550286 100644
--- a/modules/dnn/src/onnx/onnx_importer.cpp
+++ b/modules/dnn/src/onnx/onnx_importer.cpp
@@ -3210,13 +3210,14 @@ void ONNXImporter::parseTile(LayerParams& layerParams, const opencv_onnx::NodePr
// input2 in tile-1: axis, 1d tensor of shape [1]
Mat input2_blob = getIntBlob(node_proto, 2);
CV_CheckEQ(input2_blob.total(), 1ull, "ONNX/Tile: axis must be a 0D tensor or 1D tensor of shape [1].");
- int axis = input2_blob.at(0);
+ int axis = normalize_axis(input2_blob.at(0), input0_dims);
repeats_vec[axis] = tiles;
}
else
{
// input1 in tile>1: repeats
CV_CheckLE(input1_blob.dims, 2, "ONNX/Tile: repeats must be a 1D tensor."); // 1D tensor is represented as a 2D Mat
+ CV_CheckEQ((int)input1_blob.total(), input0_dims, "ONNX/Tile: repeats length must match the input rank.");
for (int i = 0; i < input1_blob.total(); i++)
repeats_vec[i] = input1_blob.at(i);
}
diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp
index 196a2cb54d..c69e06c2a1 100644
--- a/modules/dnn/src/tensorflow/tf_importer.cpp
+++ b/modules/dnn/src/tensorflow/tf_importer.cpp
@@ -1708,6 +1708,7 @@ void TFImporter::parseSlice(tensorflow::GraphDef& net, const tensorflow::NodeDef
CV_Assert_N(!begins.empty(), !sizes.empty());
CV_CheckTypeEQ(begins.type(), CV_32SC1, "");
CV_CheckTypeEQ(sizes.type(), CV_32SC1, "");
+ CV_CheckEQ(begins.total(), sizes.total(), "Slice: 'begin' and 'size' must have equal length");
if (begins.total() == 4 && getDataLayout(name, data_layouts) == DNN_LAYOUT_NHWC)
{
diff --git a/modules/dnn/src/tflite/tflite_importer.cpp b/modules/dnn/src/tflite/tflite_importer.cpp
index 8e495ec27e..b09c4b612d 100644
--- a/modules/dnn/src/tflite/tflite_importer.cpp
+++ b/modules/dnn/src/tflite/tflite_importer.cpp
@@ -540,6 +540,7 @@ void TFLiteImporter::parseConvolution(const Operator& op, const std::string& opc
if (filterScales->size() == 1) {
layerParams.blobs[2].setTo(inpScale * filterScales->Get(0) / outScale);
} else {
+ CV_CheckEQ((int)filterScales->size(), oc, "TFLite: number of filter quantization scales must match the number of output channels");
for (size_t i = 0; i < filterScales->size(); ++i) {
layerParams.blobs[2].at(i) = inpScale * filterScales->Get(i) / outScale;
}
@@ -605,6 +606,7 @@ void TFLiteImporter::parseDWConvolution(const Operator& op, const std::string& o
if (filterScales->size() == 1) {
layerParams.blobs[2].setTo(inpScale * filterScales->Get(0) / outScale);
} else {
+ CV_CheckEQ((int)filterScales->size(), oc, "TFLite: number of filter quantization scales must match the number of output channels");
for (size_t i = 0; i < filterScales->size(); ++i) {
layerParams.blobs[2].at(i) = inpScale * filterScales->Get(i) / outScale;
}
@@ -631,16 +633,19 @@ void TFLiteImporter::parsePadding(const Operator& op, const std::string& opcode,
Mat paddings = allTensors[op.inputs()->Get(1)].clone();
CV_CheckTypeEQ(paddings.type(), CV_32S, "");
- // N H W C
- // 0 1 2 3 4 5 6 7
- std::swap(paddings.at(2), paddings.at(6));
- std::swap(paddings.at(3), paddings.at(7));
- // N C W H
- // 0 1 2 3 4 5 6 7
- std::swap(paddings.at(4), paddings.at(6));
- std::swap(paddings.at(5), paddings.at