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:
-3
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
+2
-2
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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_
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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})
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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.;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, ¢ers[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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user