1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2026-07-08 11:31:07 +03:00
144 changed files with 4751 additions and 1058 deletions
-3
View File
@@ -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)
+15
View File
@@ -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")
+5 -1
View File
@@ -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)
+8 -1
View File
@@ -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()
@@ -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()
@@ -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
@@ -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
<iframe src="../../js_morphological_ops_getStructuringElement.html" width="100%"
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
</iframe>
\endhtmlonly
\endhtmlonly
+1 -1
View File
@@ -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}
}
@@ -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 )
@@ -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.
@@ -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<cv::Point3f> 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<cv::Point3f> objectPoints;
for (int i = 0; i < boardSize.height-1; ++i) {
@@ -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
```
+1 -1
View File
@@ -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
@@ -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.
@@ -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
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
{
+4
View File
@@ -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"
)
+31 -2
View File
@@ -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
+23
View File
@@ -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,
+247
View File
@@ -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 <opencv2/core.hpp>
#include <opencv2/core/base.hpp>
#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
+80
View File
@@ -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 <opencv2/core/core.hpp>
#include <opencv2/core/base.hpp>
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;
}
+196
View File
@@ -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 <opencv2/core.hpp>
#include "precomp_ipp.hpp"
#include "iw++/iw.hpp"
#include <cfloat>
#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
+6
View File
@@ -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)))
{
+1 -1
View File
@@ -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;
+8
View File
@@ -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:
+24 -21
View File
@@ -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));
+4 -3
View File
@@ -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
@@ -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]],
@@ -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));
@@ -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<typename _Tp> inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_lut_quad
return c;
}
template<int n> inline v_reg<uchar, n> v_lut(const uchar* tab, const v_reg<uchar, n>& idx)
{
v_reg<uchar, n> c;
for( int i = 0; i < n; i++ )
c.s[i] = tab[idx.s[i]];
return c;
}
template<int n> inline v_reg<schar, n> v_lut(const schar* tab, const v_reg<uchar, n>& idx)
{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); }
template<int n> inline v_reg<int, n> v_lut(const int* tab, const v_reg<int, n>& idx)
{
v_reg<int, n> c;
@@ -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]],
@@ -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]],
@@ -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)
{
@@ -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] =
@@ -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
@@ -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<v_uint8>::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)); }
@@ -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)
{
@@ -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]]);
@@ -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]],
@@ -687,6 +687,7 @@ public:
Mat_<uchar> m2({2, 3}, {1, 2, 3, 4, 5, 6}); // 2x3 Mat
Mat_<double> R({2, 2}, {a, -b, b, a}); // from example
\endcode
*/
template<typename _Tp> class MatCommaInitializer_
{
+74 -7
View File
@@ -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<int>& order)
{
std::vector<int> new_sz(order.size());
for (size_t i = 0; i < order.size(); ++i)
new_sz[i] = src.size[order[i]];
return Mat(static_cast<int>(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<int> order(a.dims);
std::iota(order.begin(), order.end(), 0);
std::reverse(order.begin(), order.end());
std::vector<int> 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<int> 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<int> 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<int> 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);
+117
View File
@@ -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<tuple<int, int, int>, MatType, GemmFlag> GemmTestParams_t;
class GemmTest : public perf::TestBaseWithParam<GemmTestParams_t>
{
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
+1 -2
View File
@@ -47,7 +47,6 @@
#ifdef HAVE_LAPACK
#include <complex.h>
#include "opencv_lapack.h"
#include <cmath>
@@ -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
#endif //HAVE_LAPACK
+8 -2
View File
@@ -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 <sys/types.h>
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_<uint8_t, uint8_t>; break;
case CV_8U: ret = (LUTFunc)LUT8u_dispatch; break;
case CV_8S: ret = (LUTFunc)LUT_<uint8_t, int8_t>; break;
case CV_16U: ret = (LUTFunc)LUT_<uint8_t, uint16_t>; break;
case CV_16U: ret = (LUTFunc)LUT16u_dispatch; break;
case CV_16S: ret = (LUTFunc)LUT_<uint8_t, int16_t>; break;
case CV_32S: ret = (LUTFunc)LUT_<uint8_t, int32_t>; break;
case CV_32F: ret = (LUTFunc)LUT_<uint8_t, int32_t>; break; // float
+56
View File
@@ -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
+146
View File
@@ -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<v_uint8>::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<v_uint8>::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<v_uint8>::vlanes();
const int nlanes16 = VTraits<v_uint16>::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
@@ -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);
+1 -1
View File
@@ -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();
+9 -1
View File
@@ -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;
}
+37 -34
View File
@@ -987,7 +987,6 @@ namespace cv
template<typename T> static void sort_( const Mat& src, Mat& dst, int flags )
{
AutoBuffer<T> buf;
int n, len;
bool sortRows = (flags & 1) == SORT_EVERY_ROW;
bool inplace = src.data == dst.data;
@@ -996,44 +995,48 @@ template<typename T> 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<T>(i);
if( !inplace )
{
const T* sptr = src.ptr<T>(i);
memcpy(dptr, sptr, sizeof(T) * len);
}
ptr = dptr;
}
else
{
for( int j = 0; j < len; j++ )
ptr[j] = src.ptr<T>(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<T> buf;
if( !sortRows )
for( int j = 0; j < len; j++ )
dst.ptr<T>(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<T>(i);
if( !inplace )
{
const T* sptr = src.ptr<T>(i);
memcpy(dptr, sptr, sizeof(T) * len);
}
ptr = dptr;
}
else
{
for( int j = 0; j < len; j++ )
ptr[j] = src.ptr<T>(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<T>(j)[i] = ptr[j];
}
});
}
#ifdef HAVE_IPP
typedef IppStatus (CV_STDCALL *IppSortFunc)(void *pSrcDst, int len, Ipp8u *pBuffer);
+31 -4
View File
@@ -526,6 +526,33 @@ void transposeND(InputArray src_, const std::vector<int>& order, OutputArray dst
CV_CheckEQ(inp.channels(), 1, "Input array should be single-channel");
CV_CheckEQ(order.size(), static_cast<size_t>(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<int>(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<int>& order, OutputArray dst
CV_CheckEQ(static_cast<size_t>(order_[i]), i, "New order should be a valid permutation of the old one");
}
std::vector<int> newShape(order.size());
AutoBuffer<int> 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<int>& 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<size_t> steps(order.size());
AutoBuffer<size_t> steps(order.size());
for (int i = 0; i < static_cast<int>(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<int> is_same_shape(dims_shape, 0);
cv::AutoBuffer<int> is_same_shape(dims_shape, 0);
for (int i = 0; i < static_cast<int>(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<int> cumulative_shape(dims_shape, 1);
AutoBuffer<int> cumulative_shape(dims_shape, 1);
int total = static_cast<int>(dst.total());
for (int i = dims_shape - 1; i >= 0; --i) {
cumulative_shape[i] = static_cast<int>(total / ptr_shape[i]);
+294 -2
View File
@@ -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<uchar, int, int>
{
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<v_uint8>::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<schar, int, int>
{
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<v_int8>::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<schar, int, int>
}
};
#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 <typename T>
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<VT>::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<VT>::vlanes();
const int vl32 = VTraits<v_int32>::vlanes();
const int vl64 = VTraits<v_float64>::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<v_int32>::max_nlanes];
double CV_DECL_ALIGNED(CV_SIMD_WIDTH) qbuf[VTraits<v_float64>::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<vl32) holds samples j and j+vl32 -> channel j%cn (vl32%cn==0).
// qbuf index i (i<vl16) holds sample lane 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 <typename T>
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<VT>::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<VT>::vlanes();
const int vl64 = VTraits<v_float64>::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<v_float64>::max_nlanes * 2];
double CV_DECL_ALIGNED(CV_SIMD_WIDTH) qbuf[VTraits<v_float64>::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<ushort, int, double>
{
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<short, int, double>
{
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, double, double>
{
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<float, double, double>
{
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<typename T, typename ST, typename SQT>
@@ -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 )
+30 -14
View File
@@ -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<v_float32>::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<v_float32>::vlanes(); j += 4 * VTraits<v_float32>::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<v_float32>::vlanes()), vx_load(b + j + VTraits<v_float32>::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<v_float32>::vlanes()), vx_load(b + j + 2 * VTraits<v_float32>::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<v_float32>::vlanes()), vx_load(b + j + 3 * VTraits<v_float32>::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<v_float32>::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<v_float32>::vlanes(); j += 4 * VTraits<v_float32>::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<v_float32>::vlanes()), vx_load(b + j + VTraits<v_float32>::vlanes())));
v_d2 = v_add(v_d2, v_absdiff(vx_load(a + j + 2 * VTraits<v_float32>::vlanes()), vx_load(b + j + 2 * VTraits<v_float32>::vlanes())));
v_d3 = v_add(v_d3, v_absdiff(vx_load(a + j + 3 * VTraits<v_float32>::vlanes()), vx_load(b + j + 3 * VTraits<v_float32>::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<v_uint8>::vlanes(); j += 4 * VTraits<v_uint8>::vlanes())
const int vl = VTraits<v_uint8>::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<v_uint8>::vlanes()), vx_load(b + j + VTraits<v_uint8>::vlanes())) +
v_reduce_sad(vx_load(a + j + 2 * VTraits<v_uint8>::vlanes()), vx_load(b + j + 2 * VTraits<v_uint8>::vlanes())) +
v_reduce_sad(vx_load(a + j + 3 * VTraits<v_uint8>::vlanes()), vx_load(b + j + 3 * VTraits<v_uint8>::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]);
+6 -21
View File
@@ -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
+2 -2
View File
@@ -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)
@@ -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<typename _to_binary_convertor_t> 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<uchar> buffer(BUFFER_MAX_LEN);
uchar * beg = buffer.data();
uchar buffer[BUFFER_MAX_LEN];
uchar * beg = buffer;
uchar * end = beg;
while (convertor) {
+3 -1
View File
@@ -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<const SparseMat::Node*> elems(n);
AutoBuffer<const SparseMat::Node*> 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);
+19
View File
@@ -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
+15 -3
View File
@@ -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<char> 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;
+10 -3
View File
@@ -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<char> 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
+73
View File
@@ -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 <typename T>
T fsWriteRead(const T& expectedValue, const char* ext)
{
+31
View File
@@ -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));
+339
View File
@@ -69,4 +69,343 @@ INSTANTIATE_TEST_CASE_P(/**/, Utils_blobFromImages,
std::vector<int>{16, 2048, 2048})
);
// NCHW, 8U->32F, C3, mean+scale+swapRB at 640x640
using Utils_blobFromImage_8U_NCHW = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_NCHW, MeanScale_SwapRB) {
std::vector<int> 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<int>{ 640, 640})
);
// NHWC, 8U->32F, C3
using Utils_blobFromImage_8U_NHWC = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_NHWC, SwapRB) {
std::vector<int> 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<int> 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<int>{ 224, 224},
std::vector<int>{ 640, 640})
);
// NHWC, 32F->32F, C3
using Utils_blobFromImage_32F_NHWC = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_32F_NHWC, SwapRB) {
std::vector<int> 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<int> 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<int>{ 224, 224},
std::vector<int>{ 640, 640})
);
// Resize+crop, 8U->32F, C3, mean+scale+swapRB to 640x640
using Utils_blobFromImage_8U_Resize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_Resize, NHWC_Crop_MeanScale_SwapRB) {
std::vector<int> 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<int> 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<int>{ 720, 1280},
std::vector<int>{ 1080, 1920},
std::vector<int>{ 2160, 3840})
);
// Resize+crop, NCHW, 32F->32F, C3, mean+scale+swapRB to 300x300
using Utils_blobFromImage_32F_NCHW_Resize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_32F_NCHW_Resize, Crop_MeanScale_SwapRB_To300) {
std::vector<int> 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<int>{ 720, 1280},
std::vector<int>{ 1080, 1920})
);
// Resize+crop, NCHW, 8U->8U, C3
using Utils_blobFromImage_8U_to_8U_Crop = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_to_8U_Crop, NCHW_SwapRB) {
std::vector<int> 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<int> 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<int>{ 1080, 1920},
std::vector<int>{ 2160, 3840})
);
// Resize, NCHW, 8U->8U, C3
using Utils_blobFromImage_8U_to_8U_Resize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_8U_to_8U_Resize, NCHW_SwapRB) {
std::vector<int> 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<int>{ 1080, 1920},
std::vector<int>{ 2160, 3840})
);
// Resize+crop, NCHW, 32F->32F, C1, mean
using Utils_blobFromImage_32F_NCHW_C1 = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImage_32F_NCHW_C1, Crop_MeanScale_To224) {
std::vector<int> 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<int> 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<int>{ 1080, 1920},
std::vector<int>{ 2160, 3840})
);
// Batch=8, NHWC, 8U->32F, C3, mean+scale+swapRB
using Utils_blobFromImages_NoResize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImages_NoResize, NHWC_MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
int batch = input_shape.front();
std::vector<int> input_shape_no_batch(input_shape.begin()+1, input_shape.end());
std::vector<Mat> 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<int>{8, 640, 640})
);
// Batch=8, resize+crop to 640x640, 8U->32F, C3, mean+scale+swapRB
using Utils_blobFromImages_Resize = TestBaseWithParam<std::vector<int>>;
PERF_TEST_P_(Utils_blobFromImages_Resize, NHWC_Crop_MeanScale_SwapRB) {
std::vector<int> input_shape = GetParam();
int batch = input_shape.front();
std::vector<int> input_shape_no_batch(input_shape.begin()+1, input_shape.end());
std::vector<Mat> 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<int> input_shape = GetParam();
int batch = input_shape.front();
std::vector<int> input_shape_no_batch(input_shape.begin()+1, input_shape.end());
std::vector<Mat> 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<int>{8, 720, 1280},
std::vector<int>{8, 1080, 1920})
);
}
+52 -12
View File
@@ -152,29 +152,69 @@ void blobFromImagesNCHWImpl(const std::vector<Mat>& 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<Tinp>(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<Tinp>(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<Tinp>();
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<const float*>(p_src);
float* dst_r = reinterpret_cast<float*>(p_blob_r);
float* dst_g = reinterpret_cast<float*>(p_blob_g);
float* dst_b = reinterpret_cast<float*>(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<Tinp>(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<Tinp>(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];
+3 -2
View File
@@ -1056,6 +1056,8 @@ void LayerEinsumImpl::processEquation(const std::vector<MatShape>& 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<MatShape>& 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;
}
}
@@ -960,6 +960,32 @@ struct GeluApproximationFunctor : public BaseDefaultFunctor<GeluApproximationFun
GeluApproximationConstants::coef_sqrt_2_pi * x * 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<v_float32>::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<TanHFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -1517,6 +1565,27 @@ struct BNLLFunctor : public BaseDefaultFunctor<BNLLFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -1671,6 +1740,22 @@ struct LogFunctor : public BaseDefaultFunctor<LogFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -1801,6 +1886,26 @@ struct AcoshFunctor : public BaseDefaultFunctor<AcoshFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -1863,6 +1968,28 @@ struct AsinhFunctor : public BaseDefaultFunctor<AsinhFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -1925,6 +2052,26 @@ struct AtanhFunctor : public BaseDefaultFunctor<AtanhFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -1960,6 +2107,25 @@ struct CosFunctor : public BaseDefaultFunctor<CosFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -1995,6 +2161,26 @@ struct CoshFunctor : public BaseDefaultFunctor<CoshFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -2030,6 +2216,22 @@ struct ErfFunctor : public BaseDefaultFunctor<ErfFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -2156,6 +2358,25 @@ struct SinFunctor : public BaseDefaultFunctor<SinFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -2191,6 +2412,26 @@ struct SinhFunctor : public BaseDefaultFunctor<SinhFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -2226,6 +2467,26 @@ struct SoftplusFunctor : public BaseDefaultFunctor<SoftplusFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -2288,6 +2549,25 @@ struct TanFunctor : public BaseDefaultFunctor<TanFunctor>
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<v_float32>::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<BackendNode> initCUDA(int target, csl::Stream stream)
{
@@ -2765,6 +3045,26 @@ struct ExpFunctor : public BaseDefaultFunctor<ExpFunctor>
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<v_float32>::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);
+2 -1
View File
@@ -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<int>(0);
int axis = normalize_axis(input2_blob.at<int>(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<int>(i);
}
@@ -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)
{
+16 -10
View File
@@ -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<float>(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<float>(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<int32_t>(2), paddings.at<int32_t>(6));
std::swap(paddings.at<int32_t>(3), paddings.at<int32_t>(7));
// N C W H
// 0 1 2 3 4 5 6 7
std::swap(paddings.at<int32_t>(4), paddings.at<int32_t>(6));
std::swap(paddings.at<int32_t>(5), paddings.at<int32_t>(7));
// N C H W
// 0 1 2 3 4 5 6 7
if (paddings.total() == 8)
{
// N H W C
// 0 1 2 3 4 5 6 7
std::swap(paddings.at<int32_t>(2), paddings.at<int32_t>(6));
std::swap(paddings.at<int32_t>(3), paddings.at<int32_t>(7));
// N C W H
// 0 1 2 3 4 5 6 7
std::swap(paddings.at<int32_t>(4), paddings.at<int32_t>(6));
std::swap(paddings.at<int32_t>(5), paddings.at<int32_t>(7));
// N C H W
// 0 1 2 3 4 5 6 7
}
layerParams.set("paddings", DictValue::arrayInt<int32_t*>((int32_t*)paddings.data, paddings.total()));
addLayer(layerParams, op);
@@ -946,6 +951,7 @@ void TFLiteImporter::parseResize(const Operator& op, const std::string& opcode,
layerParams.set("half_pixel_centers", options->half_pixel_centers());
}
Mat shape = allTensors[op.inputs()->Get(1)].reshape(1, 1);
CV_CheckGE(shape.total(), (size_t)2, "TFLite Resize: size tensor must hold height and width");
layerParams.set("height", shape.at<int>(0, 0));
layerParams.set("width", shape.at<int>(0, 1));
addLayer(layerParams, op);
+3
View File
@@ -3427,6 +3427,9 @@ TEST_P(Test_ONNX_nets, YOLOv5n)
TEST_P(Test_ONNX_layers, Tile)
{
testONNXModels("tile", pb);
// tile-1 (opset 1) form with a negative axis; the parser must normalize it
// against the input rank instead of indexing the repeats buffer directly.
testONNXModels("tile_neg_axis");
}
TEST_P(Test_ONNX_layers, Gelu)
+91
View File
@@ -0,0 +1,91 @@
// 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.
// Brute-force descriptor matching perf tests. These exercise the core distance
// kernels (hal::normL2Sqr_ / normL1_ / normHamming via cv::batchDistance) that
// back BFMatcher, which is how float descriptors (SIFT 128-d, SURF 64-d) and
// binary descriptors (ORB 32-byte) are matched.
#include "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
// (descriptor dimension, query/train descriptor count)
typedef tuple<int, int> Dim_Count_t;
typedef TestBaseWithParam<Dim_Count_t> DescriptorMatcherFixture;
// Float descriptors matched with L2 (SIFT=128, SURF=64) — uses hal::normL2Sqr_.
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_L2_float,
testing::Combine(
testing::Values(64, 128), // SURF, SIFT descriptor sizes
testing::Values(512, 1000)
))
{
const int dim = get<0>(GetParam());
const int count = get<1>(GetParam());
Mat query(count, dim, CV_32F);
Mat train(count, dim, CV_32F);
declare.in(query, train, WARMUP_RNG);
declare.time(60);
BFMatcher matcher(NORM_L2, false);
std::vector<std::vector<DMatch> > matches;
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
SANITY_CHECK_NOTHING();
}
// Float descriptors matched with L1 — uses hal::normL1_.
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_L1_float,
testing::Combine(
testing::Values(64, 128),
testing::Values(512, 1000)
))
{
const int dim = get<0>(GetParam());
const int count = get<1>(GetParam());
Mat query(count, dim, CV_32F);
Mat train(count, dim, CV_32F);
declare.in(query, train, WARMUP_RNG);
declare.time(60);
BFMatcher matcher(NORM_L1, false);
std::vector<std::vector<DMatch> > matches;
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
SANITY_CHECK_NOTHING();
}
// Binary descriptors matched with Hamming (ORB/BRISK=32 bytes) — uses hal::normHamming.
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_Hamming_binary,
testing::Combine(
testing::Values(32, 64), // ORB (32), BRISK/FREAK (64) byte sizes
testing::Values(512, 1000)
))
{
const int bytes = get<0>(GetParam());
const int count = get<1>(GetParam());
Mat query(count, bytes, CV_8U);
Mat train(count, bytes, CV_8U);
declare.in(query, train, WARMUP_RNG);
declare.time(60);
BFMatcher matcher(NORM_HAMMING, false);
std::vector<std::vector<DMatch> > matches;
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
SANITY_CHECK_NOTHING();
}
} // namespace
+6 -4
View File
@@ -326,11 +326,13 @@ void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImag
//compute blob radius
{
std::vector<double> dists;
for (size_t pointIdx = 0; pointIdx < contours[contourIdx].size(); pointIdx++)
const std::vector<cv::Point>& contour = contours[contourIdx];
const size_t contourSize = contour.size();
AutoBuffer<double> dists(contourSize);
for (size_t pointIdx = 0; pointIdx < contourSize; pointIdx++)
{
Point2d pt = contours[contourIdx][pointIdx];
dists.push_back(norm(center.location - pt));
const Point2d& pt = contour[pointIdx];
dists[pointIdx] = norm(center.location - pt);
}
std::sort(dists.begin(), dists.end());
center.radius = (dists[(dists.size() - 1) / 2] + dists[dists.size() / 2]) / 2.;
+10 -2
View File
@@ -441,6 +441,14 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
{
CV_INSTRUMENT_REGION();
if (type != FastFeatureDetector::TYPE_5_8 &&
type != FastFeatureDetector::TYPE_7_12 &&
type != FastFeatureDetector::TYPE_9_16)
{
CV_Error_(Error::StsBadArg,
("Unknown FastFeatureDetector detector type: %d", static_cast<int>(type)));
}
const size_t max_fast_features = std::max(_img.total()/100, size_t(1000)); // Simple heuristic that depends on resolution.
CV_OCL_RUN(_img.isUMat() && type == FastFeatureDetector::TYPE_9_16,
@@ -541,7 +549,7 @@ public:
else if(prop == FAST_N)
type = static_cast<FastFeatureDetector::DetectorType>(cvRound(value));
else
CV_Error(Error::StsBadArg, "");
CV_Error_(Error::StsBadArg, ("Unknown FastFeatureDetector property: %d", prop));
}
double get(int prop) const
@@ -552,7 +560,7 @@ public:
return nonmaxSuppression;
if(prop == FAST_N)
return static_cast<int>(type);
CV_Error(Error::StsBadArg, "");
CV_Error_(Error::StsBadArg, ("Unknown FastFeatureDetector property: %d", prop));
return 0;
}
+2 -2
View File
@@ -221,8 +221,8 @@ struct KeyPoint_LessThan
void KeyPointsFilter::removeDuplicated( std::vector<KeyPoint>& keypoints )
{
int i, j, n = (int)keypoints.size();
std::vector<int> kpidx(n);
std::vector<uchar> mask(n, (uchar)1);
AutoBuffer<int> kpidx(n);
AutoBuffer<uchar> mask(n, (uchar)1);
for( i = 0; i < n; i++ )
kpidx[i] = i;
+21 -3
View File
@@ -839,7 +839,7 @@ static void computeKeyPoints(const Mat& imagePyramid,
#endif
int i, nkeypoints, level, nlevels = (int)layerInfo.size();
std::vector<int> nfeaturesPerLevel(nlevels);
AutoBuffer<int> nfeaturesPerLevel(nlevels);
// fill the extractors and descriptors for the corresponding scales
float factor = (float)(1.0 / scaleFactor);
@@ -877,7 +877,7 @@ static void computeKeyPoints(const Mat& imagePyramid,
allKeypoints.clear();
std::vector<KeyPoint> keypoints;
std::vector<int> counters(nlevels);
AutoBuffer<int> counters(nlevels);
keypoints.reserve(nfeaturesPerLevel[0]*2);
for( level = 0; level < nlevels; level++ )
@@ -1266,7 +1266,25 @@ void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask,
Ptr<ORB> ORB::create(int nfeatures, float scaleFactor, int nlevels, int edgeThreshold,
int firstLevel, int wta_k, ORB::ScoreType scoreType, int patchSize, int fastThreshold)
{
CV_Assert(firstLevel >= 0);
CV_CheckGE(nfeatures, 0, "nfeatures must be non-negative");
CV_CheckGT(scaleFactor, 1.f, "scaleFactor must be greater than 1");
CV_CheckGT(nlevels, 0, "nlevels must be positive");
CV_CheckGE(edgeThreshold, 0, "edgeThreshold must be non-negative");
CV_CheckGE(firstLevel, 0, "firstLevel must be non-negative");
CV_CheckGE(patchSize, 2, "patchSize must be at least 2");
if (wta_k != 2 && wta_k != 3 && wta_k != 4)
{
CV_Error_(Error::StsBadArg,
("wta_k must be 2, 3, or 4, but got %d", wta_k));
}
if (scoreType != ORB::HARRIS_SCORE && scoreType != ORB::FAST_SCORE)
{
CV_Error_(Error::StsBadArg,
("Unknown ORB score type: %d", static_cast<int>(scoreType)));
}
return makePtr<ORB_Impl>(nfeatures, scaleFactor, nlevels, edgeThreshold,
firstLevel, wta_k, scoreType, patchSize, fastThreshold);
}
+1 -1
View File
@@ -225,7 +225,7 @@ void SIFT_Impl::buildGaussianPyramid( const Mat& base, std::vector<Mat>& pyr, in
{
CV_TRACE_FUNCTION();
std::vector<double> sig(nOctaveLayers + 3);
AutoBuffer<double> sig(nOctaveLayers + 3);
pyr.resize(nOctaves*(nOctaveLayers + 3));
// precompute Gaussian sigmas using the following formula:
+10
View File
@@ -165,4 +165,14 @@ TEST(Features2d_FAST, noNMS)
ASSERT_EQ( 0, cvtest::norm(gt_kps, kps, NORM_L2));
}
TEST(Features2d_FAST, invalidDetectorType)
{
Mat img = Mat::zeros(16, 16, CV_8UC1);
vector<KeyPoint> keypoints;
EXPECT_THROW(FAST(img, keypoints, 10, true,
static_cast<FastFeatureDetector::DetectorType>(-1)),
cv::Exception);
}
}} // namespace
+11
View File
@@ -195,4 +195,15 @@ TEST(Features2D_ORB, MaskValue)
ASSERT_EQ(countNonZero(diff), 0);
}
TEST(Features2D_ORB, invalidCreateParameters)
{
EXPECT_THROW(ORB::create(500, 1.0f), cv::Exception);
EXPECT_THROW(ORB::create(500, 1.2f, 0), cv::Exception);
EXPECT_THROW(ORB::create(500, 1.2f, 8, 31, 0, 5), cv::Exception);
EXPECT_THROW(ORB::create(500, 1.2f, 8, 31, 0, 2,
static_cast<ORB::ScoreType>(-1)), cv::Exception);
EXPECT_THROW(ORB::create(500, 1.2f, 8, 31, 0, 2,
ORB::HARRIS_SCORE, 1), cv::Exception);
}
}} // namespace
@@ -47,8 +47,8 @@ void find_nearest(const Matrix<typename Distance::ElementType>& dataset, typenam
typedef typename Distance::ResultType DistanceType;
int n = nn + skip;
std::vector<int> match(n);
std::vector<DistanceType> dists(n);
cv::AutoBuffer<int> match(n);
cv::AutoBuffer<DistanceType> dists(n);
dists[0] = distance(dataset[0], query, dataset.cols);
match[0] = 0;
@@ -532,7 +532,11 @@ public:
const bool explore_all_trees = get_param(searchParams,"explore_all_trees",false);
// Priority queue storing intermediate branches in the best-bin-first search
const cv::Ptr<Heap<BranchSt>>& heap = Heap<BranchSt>::getPooledInstance(cv::utils::getThreadID(), (int)size_);
// Kept in thread_local storage so each thread owns an independent heap
// and no process-wide lock is taken on the search hot path (issue #25281).
thread_local cv::Ptr<Heap<BranchSt>> heap = cv::makePtr<Heap<BranchSt>>((int)size_);
heap->clear();
heap->reserve((int)size_);
std::vector<bool> checked(size_,false);
int checks = 0;
@@ -686,8 +690,8 @@ private:
return;
}
std::vector<int> centers(branching);
std::vector<int> labels(indices_length);
cv::AutoBuffer<int> centers(branching);
cv::AutoBuffer<int> labels(indices_length);
int centers_length;
(this->*chooseCenters)(branching, dsindices, indices_length, &centers[0], centers_length);
@@ -99,8 +99,8 @@ float search_with_ground_truth(NNIndex<Distance>& index, const Matrix<typename D
KNNResultSet<DistanceType> resultSet(nn+skipMatches);
SearchParams searchParams(checks);
std::vector<int> indices(nn+skipMatches);
std::vector<DistanceType> dists(nn+skipMatches);
cv::AutoBuffer<int> indices(nn+skipMatches);
cv::AutoBuffer<DistanceType> dists(nn+skipMatches);
int* neighbors = &indices[skipMatches];
int correct = 0;
@@ -449,7 +449,11 @@ private:
DynamicBitset checked(size_);
// Priority queue storing intermediate branches in the best-bin-first search
const cv::Ptr<Heap<BranchSt>>& heap = Heap<BranchSt>::getPooledInstance(cv::utils::getThreadID(), (int)size_);
// Kept in thread_local storage so each thread owns an independent heap
// and no process-wide lock is taken on the search hot path (issue #25281).
thread_local cv::Ptr<Heap<BranchSt>> heap = cv::makePtr<Heap<BranchSt>>((int)size_);
heap->clear();
heap->reserve((int)size_);
/* Search once through each tree down to root. */
for (i = 0; i < trees_; ++i) {
@@ -528,7 +528,11 @@ public:
}
else {
// Priority queue storing intermediate branches in the best-bin-first search
const cv::Ptr<Heap<BranchSt>>& heap = Heap<BranchSt>::getPooledInstance(cv::utils::getThreadID(), (int)size_);
// Kept in thread_local storage so each thread owns an independent heap
// and no process-wide lock is taken on the search hot path (issue #25281).
thread_local cv::Ptr<Heap<BranchSt>> heap = cv::makePtr<Heap<BranchSt>>((int)size_);
heap->clear();
heap->reserve((int)size_);
int checks = 0;
for (int i=0; i<trees_; ++i) {
@@ -490,7 +490,7 @@ inline LshStats LshTable<unsigned char>::getStats() const
!= end; )
if (*iterator < bin_end) {
if (is_new_bin) {
stats.size_histogram_.push_back(std::vector<unsigned int>(3, 0));
stats.size_histogram_.emplace_back(3, 0);
stats.size_histogram_.back()[0] = bin_start;
stats.size_histogram_.back()[1] = bin_end - 1;
is_new_bin = false;
+100
View File
@@ -0,0 +1,100 @@
// 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 "test_precomp.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/flann/heap.h"
#include <vector>
#if !defined(OPENCV_DISABLE_THREAD_SUPPORT)
#include <thread>
#endif
namespace opencv_test { namespace {
using cvflann::Heap;
// Basic single-threaded behaviour: popMin returns the stored elements in
// ascending order and reports emptiness correctly.
TEST(Flann_Heap, popMin_returns_sorted)
{
const int capacity = 8;
Heap<int> heap(capacity);
const int values[] = {5, 1, 4, 2, 8, 3, 7, 6};
for (int v : values)
heap.insert(v);
EXPECT_EQ(heap.size(), capacity);
int prev = -1, out = 0;
int popped = 0;
while (heap.popMin(out))
{
EXPECT_GT(out, prev); // strictly increasing
prev = out;
++popped;
}
EXPECT_EQ(popped, capacity);
EXPECT_TRUE(heap.empty());
}
// Capacity is a hard limit: extra inserts are dropped, not stored.
TEST(Flann_Heap, insert_respects_capacity)
{
const int capacity = 3;
Heap<int> heap(capacity);
for (int i = 0; i < 10; ++i)
heap.insert(i);
EXPECT_EQ(heap.size(), capacity);
}
#if !defined(OPENCV_DISABLE_THREAD_SUPPORT)
// getPooledInstance() is no longer used on the FLANN search hot path, but it
// remains public API, so keep it covered: many threads each key the shared
// pool with their own thread id and must get usable, correctly ordered heaps
// without tripping the internal use_count guard.
TEST(Flann_Heap, getPooledInstance_concurrent_usage)
{
const int numThreads = 8;
const int capacity = 32;
std::vector<std::thread> threads;
std::vector<char> ok(numThreads, 0);
for (int t = 0; t < numThreads; ++t)
{
threads.emplace_back([&, t]()
{
bool good = true;
for (int iter = 0; iter < 200 && good; ++iter)
{
cv::Ptr<Heap<int>> heap =
Heap<int>::getPooledInstance(cv::utils::getThreadID(), capacity);
for (int v = capacity - 1; v >= 0; --v)
heap->insert(v);
int prev = -1, out = 0, count = 0;
while (heap->popMin(out))
{
if (out <= prev) { good = false; break; }
prev = out;
++count;
}
if (count != capacity) good = false;
}
ok[t] = good ? 1 : 0;
});
}
for (auto& th : threads)
th.join();
for (int t = 0; t < numThreads; ++t)
EXPECT_EQ((int)ok[t], 1) << "thread " << t << " produced incorrect heap results";
}
#endif // !OPENCV_DISABLE_THREAD_SUPPORT
}} // namespace
+3 -2
View File
@@ -516,7 +516,7 @@ void filterHomographyDecompByVisibleRefpoints(InputArrayOfArrays _rotations,
CV_Assert(pointsMask.empty() || pointsMask.checkVector(1, CV_8U) == npoints);
const uchar* pointsMaskPtr = pointsMask.data;
std::vector<uchar> solutionMask(nsolutions, (uchar)1);
AutoBuffer<uchar> solutionMask(nsolutions, (uchar)1);
std::vector<Mat> normals(nsolutions);
std::vector<Mat> rotnorm(nsolutions);
Mat R;
@@ -558,7 +558,8 @@ void filterHomographyDecompByVisibleRefpoints(InputArrayOfArrays _rotations,
if( solutionMask[i] )
possibleSolutions.push_back(i);
Mat(possibleSolutions).copyTo(_possibleSolutions);
constexpr int cvType = traits::Type<decltype(possibleSolutions)::value_type>::value;
Mat(static_cast<int>(possibleSolutions.size()), 1, cvType, possibleSolutions.data()).copyTo(_possibleSolutions);
}
} //namespace cv
+40 -31
View File
@@ -256,56 +256,65 @@ struct MomentsInTile_SIMD<uchar, int, int>
}
};
#endif // CV_SIMD128
#if (CV_SIMD || CV_SIMD_SCALABLE)
namespace {
template <typename T, int N>
struct IotaInit {
T CV_DECL_ALIGNED(CV_SIMD_WIDTH) data[N];
IotaInit() { for (int i = 0; i < N; i++) data[i] = (T)i; }
};
static const IotaInit<int, VTraits<v_int32>::max_nlanes> g_ix0_init;
}
template <>
struct MomentsInTile_SIMD<ushort, int, int64>
{
MomentsInTile_SIMD()
{
// nothing
}
MomentsInTile_SIMD() {}
int operator() (const ushort * ptr, int len, int & x0, int & x1, int & x2, int64 & x3)
{
int x = 0;
const int vlanes32 = VTraits<v_int32>::vlanes();
v_int32 v_delta = vx_setall_s32(vlanes32);
v_int32 v_ix0 = vx_load(g_ix0_init.data);
v_uint32 z = vx_setzero_u32();
v_uint32 v_x0 = z, v_x1 = z, v_x2 = z;
v_uint64 v_x3 = vx_setzero_u64();
for ( ; x <= len - vlanes32; x += vlanes32 )
{
v_int32x4 v_delta = v_setall_s32(4), v_ix0 = v_int32x4(0, 1, 2, 3);
v_uint32x4 z = v_setzero_u32(), v_x0 = z, v_x1 = z, v_x2 = z;
v_uint64x2 v_x3 = v_reinterpret_as_u64(z);
v_int32 v_src = v_reinterpret_as_s32(vx_load_expand(ptr + x));
for( ; x <= len - 4; x += 4 )
{
v_int32x4 v_src = v_reinterpret_as_s32(v_load_expand(ptr + x));
v_x0 = v_add(v_x0, v_reinterpret_as_u32(v_src));
v_x1 = v_add(v_x1, v_reinterpret_as_u32(v_mul(v_src, v_ix0)));
v_x0 = v_add(v_x0, v_reinterpret_as_u32(v_src));
v_x1 = v_add(v_x1, v_reinterpret_as_u32(v_mul(v_src, v_ix0)));
v_int32 v_ix1 = v_mul(v_ix0, v_ix0);
v_x2 = v_add(v_x2, v_reinterpret_as_u32(v_mul(v_src, v_ix1)));
v_int32x4 v_ix1 = v_mul(v_ix0, v_ix0);
v_x2 = v_add(v_x2, v_reinterpret_as_u32(v_mul(v_src, v_ix1)));
v_ix1 = v_mul(v_ix0, v_ix1);
v_src = v_mul(v_src, v_ix1);
v_uint64 v_lo, v_hi;
v_expand(v_reinterpret_as_u32(v_src), v_lo, v_hi);
v_x3 = v_add(v_x3, v_add(v_lo, v_hi));
v_ix1 = v_mul(v_ix0, v_ix1);
v_src = v_mul(v_src, v_ix1);
v_uint64x2 v_lo, v_hi;
v_expand(v_reinterpret_as_u32(v_src), v_lo, v_hi);
v_x3 = v_add(v_x3, v_add(v_lo, v_hi));
v_ix0 = v_add(v_ix0, v_delta);
}
x0 = v_reduce_sum(v_x0);
x1 = v_reduce_sum(v_x1);
x2 = v_reduce_sum(v_x2);
v_store_aligned(buf64, v_reinterpret_as_s64(v_x3));
x3 = buf64[0] + buf64[1];
v_ix0 = v_add(v_ix0, v_delta);
}
x0 = v_reduce_sum(v_x0);
x1 = v_reduce_sum(v_x1);
x2 = v_reduce_sum(v_x2);
x3 = (int64)v_reduce_sum(v_x3);
vx_cleanup();
return x;
}
int64 CV_DECL_ALIGNED(16) buf64[2];
};
#endif
#endif // CV_SIMD || CV_SIMD_SCALABLE
template<typename T, typename WT, typename MT>
#if defined __GNUC__ && __GNUC__ == 4 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 9
+2
View File
@@ -793,6 +793,7 @@ void Subdiv2D::getLeadingEdgeList(std::vector<int>& leadingEdgeList) const
{
leadingEdgeList.clear();
int i, total = (int)(qedges.size()*4);
//use a std::vector<bool> to benefit from the "bitset size/8" implementation
std::vector<bool> edgemask(total, false);
for( i = 4; i < total; i += 2 )
@@ -813,6 +814,7 @@ void Subdiv2D::getTriangleList(std::vector<Vec6f>& triangleList) const
{
triangleList.clear();
int i, total = (int)(qedges.size()*4);
//use a std::vector<bool> to benefit from the "bitset size/8" implementation
std::vector<bool> edgemask(total, false);
Rect2f rect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
+5 -5
View File
@@ -28,7 +28,7 @@ CGImageRef MatToCGImage(const cv::Mat& image) {
// Preserve alpha transparency, if exists
bool alpha = image.channels() == 4;
CGBitmapInfo bitmapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault;
CGBitmapInfo bitmapInfo = (CGBitmapInfo)(alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | (CGBitmapInfo)kCGBitmapByteOrderDefault;
// Creating CGImage from cv::Mat
CGImageRef imageRef = CGImageCreate(image.cols,
@@ -73,8 +73,8 @@ void CGImageToMat(const CGImageRef image, cv::Mat& m, bool alphaExist) {
colorSpace = CGColorSpaceCreateDeviceRGB();
m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
if (!alphaExist)
bitmapInfo = kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault;
bitmapInfo = (CGBitmapInfo)kCGImageAlphaNoneSkipLast |
(CGBitmapInfo)kCGBitmapByteOrderDefault;
else
m = cv::Scalar(0);
contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8,
@@ -86,8 +86,8 @@ void CGImageToMat(const CGImageRef image, cv::Mat& m, bool alphaExist) {
{
m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
if (!alphaExist)
bitmapInfo = kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault;
bitmapInfo = (CGBitmapInfo)kCGImageAlphaNoneSkipLast |
(CGBitmapInfo)kCGBitmapByteOrderDefault;
else
m = cv::Scalar(0);
contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8,
+63 -27
View File
@@ -54,34 +54,37 @@ namespace {
namespace cv
{
static std::string HexStringToBytes(const char* hexstring, size_t expected_length);
static std::string HexStringToBytes(const char* hexstring, size_t hexstring_len, size_t expected_length);
// Converts the NULL terminated 'hexstring' which contains 2-byte character
// representations of hex values to raw data.
// Converts the 'hexstring' (of at most 'hexstring_len' bytes) which contains
// 2-byte character representations of hex values to raw data.
// 'hexstring' may contain values consisting of [A-F][a-f][0-9] in pairs,
// e.g., 7af2..., separated by any number of newlines.
// 'expected_length' is the anticipated processed size.
// On success the raw buffer is returned with its length equivalent to
// 'expected_length'. NULL is returned if the processed length is less than
// 'expected_length' or any character aside from those above is encountered.
// The returned buffer must be freed by the caller.
// 'expected_length'. An empty buffer is returned if the processed length is
// less than 'expected_length' or any character aside from those above is
// encountered. All reads are bounded by 'hexstring_len'.
static std::string HexStringToBytes(const char* hexstring,
size_t expected_length) {
size_t hexstring_len, size_t expected_length) {
const char* src = hexstring;
const char* const src_end = hexstring + hexstring_len;
size_t actual_length = 0;
std::string raw_data;
raw_data.resize(expected_length);
char* dst = const_cast<char*>(raw_data.data());
for (; actual_length < expected_length && *src != '\0'; ++src) {
char* end;
while (actual_length < expected_length && src < src_end) {
if (*src == '\n') { ++src; continue; }
if (src + 1 >= src_end) break; // need two characters to form a byte
char val[3];
if (*src == '\n') continue;
val[0] = *src++;
val[1] = *src;
char* end;
val[0] = src[0];
val[1] = src[1];
val[2] = '\0';
*dst++ = static_cast<uint8_t>(strtol(val, &end, 16));
if (end != val + 2) break;
src += 2;
++actual_length;
}
@@ -138,31 +141,64 @@ const std::vector<unsigned char>& ExifReader::getData() const
}
bool ExifReader::processRawProfile(const char* profile, size_t profile_len) {
const char* src = profile;
char* end;
int expected_length;
if (profile == nullptr || profile_len == 0) return false;
const char* src = profile;
const char* const profile_end = profile + profile_len;
// ImageMagick formats 'raw profiles' as
// '\n<name>\n<length>(%8lu)\n<hex payload>\n'.
// '\n<name>\n<length>(%8lu)\n<hex payload>\n'. Every scan below is bounded by
// 'profile_end' so that a malformed (e.g. not NUL-terminated, or missing a
// newline) profile can never be read past its buffer.
if (*src != '\n') {
CV_LOG_WARNING(NULL, cv::format("Malformed raw profile, expected '\\n' got '\\x%.2X'", *src));
CV_LOG_WARNING(NULL, cv::format("Malformed raw profile, expected '\\n' got '\\x%.2X'", (unsigned char)*src));
return false;
}
++src;
// skip the profile name and extract the length.
while (*src != '\0' && *src++ != '\n') {}
expected_length = static_cast<int>(strtol(src, &end, 10));
if (*end != '\n') {
CV_LOG_WARNING(NULL, cv::format("Malformed raw profile, expected '\\n' got '\\x%.2X'", *src));
// skip the profile name up to the next newline
while (src < profile_end && *src != '\n') ++src;
if (src >= profile_end) { // name not terminated within the buffer
CV_LOG_WARNING(NULL, "Malformed raw profile, profile name is not terminated");
return false;
}
++end;
++src;
// 'end' now points to the profile payload.
std::string payload = HexStringToBytes(end, expected_length);
if (payload.size() == 0) return false;
// the length token runs up to the next newline; parse it from a bounded,
// NUL-terminated copy (strtol handles the leading spaces of the %8lu field).
const char* len_begin = src;
while (src < profile_end && *src != '\n') ++src;
if (src >= profile_end) { // length not terminated within the buffer
CV_LOG_WARNING(NULL, "Malformed raw profile, length field is not terminated");
return false;
}
const std::string len_str(len_begin, src);
++src; // 'src' now points to the hex payload
char* parse_end = nullptr;
const long declared_length = strtol(len_str.c_str(), &parse_end, 10);
if (parse_end == len_str.c_str() || *parse_end != '\0') { // not a clean decimal number
CV_LOG_WARNING(NULL, "Malformed raw profile, length field is not a valid number");
return false;
}
// the payload starts with a 6-byte "Exif\0\0" header, so a shorter declared
// length underflows the size and pointer handed to parseExif() below. it also
// cannot be larger than the profile text that carries it (two hex characters
// encode one byte), so reject an over-large value to avoid a huge speculative
// allocation in HexStringToBytes().
if (declared_length < 6 || static_cast<size_t>(declared_length) > profile_len) {
CV_LOG_WARNING(NULL, cv::format("Malformed raw profile, declared length %ld is out of range (profile size %llu)",
declared_length, (unsigned long long)profile_len));
return false;
}
const size_t expected_length = static_cast<size_t>(declared_length);
std::string payload = HexStringToBytes(src, (size_t)(profile_end - src), expected_length);
if (payload.size() == 0) { // fewer hex bytes than the declared length
CV_LOG_WARNING(NULL, "Malformed raw profile, hex payload shorter than declared length");
return false;
}
return parseExif((unsigned char*)payload.c_str() + 6, expected_length - 6);
}
+11 -11
View File
@@ -175,28 +175,28 @@ rgb_convert (void *src, void *target, int width, int target_channels, int target
*/
static void
basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_size,
basic_conversion (void *src, const struct channel_layout *layout, int src_sample_size,
int src_width, void *target, int target_channels, int target_depth, bool use_rgb)
{
switch (target_depth) {
case CV_8U:
{
uchar *d = (uchar *)target, *s = (uchar *)src,
*end = ((uchar *)src) + src_width;
*end = ((uchar *)src) + src_width * src_sample_size;
switch (target_channels) {
case 1:
for( ; s < end; d += 3, s += src_sampe_size )
d[0] = d[1] = d[2] = s[layout->graychan];
for( ; s < end; d += 1, s += src_sample_size )
d[0] = s[layout->graychan];
break;
case 3:
if (use_rgb)
for( ; s < end; d += 3, s += src_sampe_size ) {
for( ; s < end; d += 3, s += src_sample_size ) {
d[0] = s[layout->rchan];
d[1] = s[layout->gchan];
d[2] = s[layout->bchan];
}
else
for( ; s < end; d += 3, s += src_sampe_size ) {
for( ; s < end; d += 3, s += src_sample_size ) {
d[0] = s[layout->bchan];
d[1] = s[layout->gchan];
d[2] = s[layout->rchan];
@@ -210,21 +210,21 @@ basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_
case CV_16U:
{
ushort *d = (ushort *)target, *s = (ushort *)src,
*end = ((ushort *)src) + src_width;
*end = ((ushort *)src) + src_width * src_sample_size;
switch (target_channels) {
case 1:
for( ; s < end; d += 3, s += src_sampe_size )
d[0] = d[1] = d[2] = s[layout->graychan];
for( ; s < end; d += 1, s += src_sample_size )
d[0] = s[layout->graychan];
break;
case 3:
if (use_rgb)
for( ; s < end; d += 3, s += src_sampe_size ) {
for( ; s < end; d += 3, s += src_sample_size ) {
d[0] = s[layout->rchan];
d[1] = s[layout->gchan];
d[2] = s[layout->bchan];
}
else
for( ; s < end; d += 3, s += src_sampe_size ) {
for( ; s < end; d += 3, s += src_sample_size ) {
d[0] = s[layout->bchan];
d[1] = s[layout->gchan];
d[2] = s[layout->rchan];
+59
View File
@@ -566,6 +566,65 @@ TEST(Imgcodecs_Png, Read_Exif_From_Text)
EXPECT_EQ(read_metadata[0], exif_data);
}
static uint32_t pngCrc32(const uchar* data, size_t len)
{
uint32_t crc = 0xFFFFFFFFu;
for (size_t i = 0; i < len; i++)
{
crc ^= data[i];
for (int k = 0; k < 8; k++)
crc = (crc & 1u) ? ((crc >> 1) ^ 0xEDB88320u) : (crc >> 1);
}
return crc ^ 0xFFFFFFFFu;
}
static void pngAppendBE32(std::vector<uchar>& v, uint32_t x)
{
v.push_back((uchar)(x >> 24)); v.push_back((uchar)(x >> 16));
v.push_back((uchar)(x >> 8)); v.push_back((uchar)x);
}
// Regression: a PNG "Raw profile type exif" text chunk whose declared length is
// far larger than the payload it carries must be rejected (no multi-GB
// speculative allocation / no out-of-bounds read in ExifReader::processRawProfile)
// while the image itself still decodes.
TEST(Imgcodecs_Png, Read_Exif_From_Text_oversized_length_rejected)
{
Mat img(8, 8, CV_8UC3, Scalar(10, 20, 30));
std::vector<uchar> png;
ASSERT_TRUE(imencode(".png", img, png));
ASSERT_GT(png.size(), 33u); // 8-byte signature + 25-byte IHDR chunk
const std::string keyword = "Raw profile type exif";
const std::string profile = "\nexif\n999999999\n41414141\n"; // 9e8 declared, tiny payload
std::vector<uchar> data(keyword.begin(), keyword.end());
data.push_back(0); // keyword / text separator
data.insert(data.end(), profile.begin(), profile.end());
std::vector<uchar> chunk;
pngAppendBE32(chunk, (uint32_t)data.size());
const char type[4] = { 't', 'E', 'X', 't' };
chunk.insert(chunk.end(), type, type + 4);
chunk.insert(chunk.end(), data.begin(), data.end());
std::vector<uchar> crc_input(type, type + 4);
crc_input.insert(crc_input.end(), data.begin(), data.end());
pngAppendBE32(chunk, pngCrc32(crc_input.data(), crc_input.size()));
// splice the tEXt chunk right after IHDR (valid placement for ancillary chunks)
png.insert(png.begin() + 33, chunk.begin(), chunk.end());
std::vector<int> metadata_types;
std::vector<std::vector<uchar> > metadata;
Mat decoded;
ASSERT_NO_THROW(decoded = imdecodeWithMetadata(png, metadata_types, metadata, IMREAD_COLOR));
ASSERT_FALSE(decoded.empty());
EXPECT_EQ(decoded.rows, 8);
EXPECT_EQ(decoded.cols, 8);
// the malformed profile must not produce EXIF metadata
for (size_t i = 0; i < metadata_types.size(); i++)
EXPECT_NE(metadata_types[i], IMAGE_METADATA_EXIF);
}
static size_t locateString(const uchar* exif, size_t exif_size, const std::string& pattern)
{
size_t plen = pattern.size();
+31
View File
@@ -564,6 +564,37 @@ TEST(Imgcodecs_Pam, read_write)
remove(writefile.c_str());
remove(writefile_no_param.c_str());
}
// Regression test: a 2-channel (GRAYSCALE_ALPHA) PAM decoded as single channel
// used to overflow the output row in basic_conversion() (3 bytes written per
// source pixel into a 1-channel row). Verify it decodes safely and correctly.
TEST(Imgcodecs_Pam, decode_graya_as_gray)
{
const int width = 9, height = 3; // odd width to expose off-by-row overflow
std::string header = cv::format(
"P7\nWIDTH %d\nHEIGHT %d\nDEPTH 2\nMAXVAL 255\n"
"TUPLTYPE GRAYSCALE_ALPHA\nENDHDR\n", width, height);
std::vector<uchar> buf(header.begin(), header.end());
Mat gray_ref(height, width, CV_8UC1);
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
uchar gray = (uchar)((y * width + x) * 7 + 1);
uchar alpha = (uchar)(255 - gray);
gray_ref.at<uchar>(y, x) = gray;
buf.push_back(gray); // channel 0: gray
buf.push_back(alpha); // channel 1: alpha (must be ignored)
}
Mat decoded;
ASSERT_NO_THROW(decoded = imdecode(buf, IMREAD_GRAYSCALE));
ASSERT_FALSE(decoded.empty());
EXPECT_EQ(width, decoded.cols);
EXPECT_EQ(height, decoded.rows);
EXPECT_EQ(1, decoded.channels());
EXPECT_EQ(0, cvtest::norm(gray_ref, decoded, NORM_INF));
}
#endif
#ifdef HAVE_IMGCODEC_PFM
+4 -3
View File
@@ -4,15 +4,16 @@ ocv_add_dispatched_file(accum SSE4_1 AVX AVX2)
ocv_add_dispatched_file(bilateral_filter SSE2 AVX2 AVX512_SKX AVX512_ICL)
ocv_add_dispatched_file(box_filter SSE2 SSE4_1 AVX2 AVX512_SKX)
ocv_add_dispatched_file(filter SSE2 SSE4_1 AVX2)
ocv_add_dispatched_file(color_hsv SSE2 SSE4_1 AVX2)
ocv_add_dispatched_file(color_rgb SSE2 SSE4_1 AVX2)
ocv_add_dispatched_file(color_yuv SSE2 SSE4_1 AVX2)
ocv_add_dispatched_file(color_hsv SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL)
ocv_add_dispatched_file(color_rgb SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL)
ocv_add_dispatched_file(color_yuv SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL)
ocv_add_dispatched_file(median_blur SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL)
ocv_add_dispatched_file(morph SSE2 SSE4_1 AVX2)
ocv_add_dispatched_file(smooth SSE2 SSE4_1 AVX2 AVX512_ICL)
ocv_add_dispatched_file(sumpixels SSE2 AVX2 AVX512_SKX)
ocv_add_dispatched_file(warp_kernels SSE2 SSE4_1 AVX2 NEON NEON_FP16 RVV LASX)
ocv_add_dispatched_file(undistort SSE2 AVX2)
ocv_add_dispatched_file(equalize_hist AVX512_ICL)
ocv_define_module(imgproc opencv_core opencv_geometry WRAP java objc python js)
+37
View File
@@ -0,0 +1,37 @@
// 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 "perf_precomp.hpp"
#include <cmath>
namespace opencv_test { namespace {
typedef tuple<int, int> EMD_Size_Dim_t;
typedef perf::TestBaseWithParam<EMD_Size_Dim_t> EMD_Fixture;
PERF_TEST_P(EMD_Fixture, L1_Distance, testing::Combine(
testing::Values(100, 500, 1000),
testing::Values(3, 64)
))
{
int size = get<0>(GetParam());
int dims = get<1>(GetParam());
Mat sign1(size, dims + 1, CV_32FC1);
Mat sign2(size, dims + 1, CV_32FC1);
theRNG().fill(sign1, RNG::UNIFORM, 0.1, 1.0);
theRNG().fill(sign2, RNG::UNIFORM, 0.1, 1.0);
declare.in(sign1, sign2);
TEST_CYCLE()
{
cv::EMD(sign1, sign2, cv::DIST_L1);
}
SANITY_CHECK_NOTHING();
}
}}
@@ -98,8 +98,8 @@ static bool ocl_bilateralFilter_8u(InputArray _src, OutputArray _dst, int d,
return false;
copyMakeBorder(src, temp, radius, radius, radius, radius, borderType);
std::vector<float> _space_weight(d * d);
std::vector<int> _space_ofs(d * d);
AutoBuffer<float> _space_weight(d * d);
AutoBuffer<int> _space_ofs(d * d);
float * const space_weight = &_space_weight[0];
int * const space_ofs = &_space_ofs[0];
@@ -188,9 +188,9 @@ bilateralFilter_8u( const Mat& src, Mat& dst, int d,
Mat temp;
copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );
std::vector<float> _color_weight(cn*256);
std::vector<float> _space_weight(d*d);
std::vector<int> _space_ofs(d*d);
AutoBuffer<float> _color_weight(cn*256);
AutoBuffer<float> _space_weight(d*d);
AutoBuffer<int> _space_ofs(d*d);
float* color_weight = &_color_weight[0];
float* space_weight = &_space_weight[0];
int* space_ofs = &_space_ofs[0];
@@ -283,15 +283,15 @@ bilateralFilter_32f( const Mat& src, Mat& dst, int d,
copyMakeBorder( src, temp, radius, radius, radius, radius, borderType );
// allocate lookup tables
std::vector<float> _space_weight(d*d);
std::vector<int> _space_ofs(d*d);
AutoBuffer<float> _space_weight(d*d);
AutoBuffer<int> _space_ofs(d*d);
float* space_weight = &_space_weight[0];
int* space_ofs = &_space_ofs[0];
// assign a length which is slightly more than needed
len = (float)(maxValSrc - minValSrc) * cn;
kExpNumBins = kExpNumBinsPerChannel * cn;
std::vector<float> _expLUT(kExpNumBins+2);
AutoBuffer<float> _expLUT(kExpNumBins+2);
float* expLUT = &_expLUT[0];
scale_index = kExpNumBins/len;
+50 -5
View File
@@ -87,9 +87,18 @@ struct RGB2HSV_b
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vsize = VTraits<v_uint8>::vlanes();
for ( ; i <= n - vsize;
for ( ; i < n;
i += vsize, src += scn*vsize, dst += 3*vsize)
{
if ( i > n - vsize ) {
if (i == 0 || src == dst) {
break;
}
int backup = i - (n - vsize);
i = n - vsize;
src -= backup * scn;
dst -= backup * 3;
}
v_uint8 b, g, r;
if(scn == 4)
{
@@ -297,8 +306,17 @@ struct RGB2HSV_f
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vsize = VTraits<v_float32>::vlanes();
for ( ; i <= n - 3*vsize; i += 3*vsize, src += scn * vsize)
const float* const src0 = src;
for ( ; i < n; i += 3*vsize, src += scn * vsize)
{
if ( i > n - 3*vsize ) {
if (i == 0 || src0 == dst) {
break;
}
int backup = (i - (n - 3*vsize)) / 3;
i = n - 3*vsize;
src -= backup * scn;
}
v_float32 r, g, b, a;
if(scn == 4)
{
@@ -463,8 +481,17 @@ struct HSV2RGB_f
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int vsize = VTraits<v_float32>::vlanes();
v_float32 valpha = vx_setall_f32(alpha);
for (; i <= n - vsize*3; i += vsize*3, dst += dcn * vsize)
float* const dst0 = dst;
for (; i < n; i += vsize*3, dst += dcn * vsize)
{
if ( i > n - vsize*3 ) {
if (i == 0 || src == dst0) {
break;
}
int backup = (i - (n - vsize*3)) / 3;
i = n - vsize*3;
dst -= backup * dcn;
}
v_float32 h, s, v, b, g, r;
v_load_deinterleave(src + i, h, s, v);
@@ -709,9 +736,18 @@ struct RGB2HLS_f
const int vsize = VTraits<v_float32>::vlanes();
v_float32 vhscale = vx_setall_f32(hscale);
for ( ; i <= n - vsize;
for ( ; i < n;
i += vsize, src += scn * vsize, dst += 3 * vsize)
{
if ( i > n - vsize ) {
if (i == 0 || src == dst) {
break;
}
int backup = i - (n - vsize);
i = n - vsize;
src -= backup * scn;
dst -= backup * 3;
}
v_float32 r, g, b, h, l, s;
if(scn == 4)
@@ -1005,8 +1041,17 @@ struct HLS2RGB_f
#if (CV_SIMD || CV_SIMD_SCALABLE)
static const int vsize = VTraits<v_float32>::vlanes();
for (; i <= n - vsize; i += vsize, src += 3*vsize, dst += dcn*vsize)
for (; i < n; i += vsize, src += 3*vsize, dst += dcn*vsize)
{
if ( i > n - vsize ) {
if (i == 0 || src == dst) {
break;
}
int backup = i - (n - vsize);
i = n - vsize;
src -= backup * 3;
dst -= backup * dcn;
}
v_float32 h, l, s, r, g, b;
v_load_deinterleave(src, h, l, s);

Some files were not shown because too many files have changed in this diff Show More