Merge branch 4.x
@@ -437,7 +437,7 @@ endmacro()
|
||||
macro(ocv_check_cuda_delayed_load cuda_toolkit_root_dir)
|
||||
if(MSVC AND CUDA_ENABLE_DELAYLOAD)
|
||||
set(DELAYFLAGS "delayimp.lib")
|
||||
file(GLOB CUDA_DLLS "${cuda_toolkit_root_dir}/bin/*.dll")
|
||||
file(GLOB_RECURSE CUDA_DLLS "${cuda_toolkit_root_dir}/bin/*.dll")
|
||||
foreach(d ${CUDA_DLLS})
|
||||
cmake_path(GET "d" FILENAME DLL_NAME)
|
||||
if(NOT ${DLL_NAME} MATCHES "cudart")
|
||||
|
||||
@@ -52,7 +52,7 @@ cv.circle(img,(447,63), 63, (0,0,255), -1)
|
||||
### Drawing Ellipse
|
||||
|
||||
To draw the ellipse, we need to pass several arguments. One argument is the center location (x,y).
|
||||
Next argument is axes lengths (major axis length, minor axis length). angle is the angle of rotation
|
||||
Next argument is axes lengths (semi-major axis length, semi-minor axis length). angle is the angle of rotation
|
||||
of ellipse in anti-clockwise direction. startAngle and endAngle denotes the starting and ending of
|
||||
ellipse arc measured in clockwise direction from major axis. i.e. giving values 0 and 360 gives the
|
||||
full ellipse. For more details, check the documentation of **cv.ellipse()**. Below example draws a
|
||||
|
||||
@@ -18,6 +18,7 @@ Introduction to OpenCV {#tutorial_table_of_content_introduction}
|
||||
- @subpage tutorial_windows_install
|
||||
- @subpage tutorial_windows_visual_studio_opencv
|
||||
- @subpage tutorial_windows_visual_studio_image_watch
|
||||
- @subpage tutorial_windows_msys2_vscode
|
||||
|
||||
##### Java & Android
|
||||
- @subpage tutorial_java_dev_intro
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
Building OpenCV on Windows from Source Code using MSYS2 UCRT64 and VS Code (C++) {#tutorial_windows_msys2_vscode}
|
||||
=====================================================================
|
||||
|
||||
| | |
|
||||
| -: | :- |
|
||||
| Original author | Mahadev Kumar |
|
||||
| Compatibility | OpenCV >= 4.x |
|
||||
|
||||
@tableofcontents
|
||||
|
||||
@note This tutorial was tested on Windows >= 7 using the MSYS2 UCRT64 environment.
|
||||
@note The OpenCV team does not maintain MSYS/Cygwin configuration and does not regularly test it with continuous integration.
|
||||
|
||||
---
|
||||
|
||||
## Introduction {#tutorial_windows_msys2_install_intro}
|
||||
|
||||
This tutorial explains how to build OpenCV from source on Windows using the **MSYS2 UCRT64 toolchain** and use it inside **Visual Studio Code with C++**.
|
||||
|
||||
The build configuration uses:
|
||||
|
||||
- **MSYS2 (UCRT64 shell)**
|
||||
- **GCC (UCRT toolchain)**
|
||||
- **CMake**
|
||||
- **mingw32-make**
|
||||
- **VS Code**
|
||||
|
||||
This method produces **native Windows binaries linked against the Universal C Runtime (UCRT)**.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites {#tutorial_windows_install_msys2_prerequisites}
|
||||
|
||||
Install the following software before proceeding:
|
||||
|
||||
- [MSYS2](https://www.msys2.org)
|
||||
- [Git](https://git-scm.com/install)
|
||||
- [Visual Studio Code](https://code.visualstudio.com)
|
||||
|
||||
After installing MSYS2, always open the:
|
||||
|
||||
**MSYS2 UCRT64 Terminal**
|
||||
|
||||
@note Do not use the `MSYS`, `MinGW64`, or `CLANG64` shells for this build.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Update MSYS2 {#tutorial_windows_msys2_install_update}
|
||||
|
||||
Open the **MSYS2 UCRT64** terminal and update the system.
|
||||
|
||||
@code{.bash}
|
||||
pacman -Syu
|
||||
@endcode
|
||||
|
||||
Restart the terminal if prompted.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Install Required Packages {#tutorial_windows_msys2_install_packages}
|
||||
|
||||
Install the required compiler and build tools.
|
||||
|
||||
@code{.bash}
|
||||
pacman -S mingw-w64-ucrt-x86_64-gcc \
|
||||
mingw-w64-ucrt-x86_64-cmake \
|
||||
mingw-w64-ucrt-x86_64-make
|
||||
@endcode
|
||||
|
||||
Verify installation:
|
||||
|
||||
@code{.bash}
|
||||
gcc --version
|
||||
cmake --version
|
||||
mingw32-make --version
|
||||
@endcode
|
||||
|
||||
@note **Add the following directory to your `Windows PATH`:**
|
||||
@code{.bash}
|
||||
C:\msys64\ucrt64\bin
|
||||
@endcode
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Clone OpenCV Source Code {#tutorial_windows_msys2_install_clone}
|
||||
|
||||
Clone `OpenCV` and optional `contrib modules`.
|
||||
|
||||
@code{.bash}
|
||||
git clone https://github.com/opencv/opencv.git
|
||||
git clone https://github.com/opencv/opencv_contrib.git
|
||||
@endcode
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Create Build Directory {#tutorial_windows_msys2_install_build_dir}
|
||||
|
||||
@code{.bash}
|
||||
cd opencv
|
||||
mkdir build
|
||||
cd build
|
||||
@endcode
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Configure the Build with CMake {#tutorial_windows_msys2_install_configure}
|
||||
|
||||
Run CMake using the **MinGW Makefiles generator**.
|
||||
|
||||
@code{.bash}
|
||||
cmake -G "MinGW Makefiles" ../
|
||||
@endcode
|
||||
|
||||
@note If you do not want to use **opencv_contrib**, remove the `OPENCV_EXTRA_MODULES_PATH` option.
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Build OpenCV {#tutorial_windows_msys2_install_build}
|
||||
|
||||
Compile OpenCV.
|
||||
|
||||
@code{.bash}
|
||||
mingw32-make -j6
|
||||
@endcode
|
||||
|
||||
@note If the build fails due to memory limitations, reduce the job count.
|
||||
|
||||
Example:
|
||||
|
||||
@code{.bash}
|
||||
mingw32-make -j4
|
||||
@endcode
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Install OpenCV {#tutorial_windows_msys2_install_install}
|
||||
|
||||
Install the compiled libraries.
|
||||
|
||||
@code{.bash}
|
||||
mingw32-make install
|
||||
@endcode
|
||||
|
||||
After installation, OpenCV will be located in:
|
||||
`opencv/build/install`
|
||||
|
||||
Add the following directory to your **Windows PATH**:
|
||||
@code{.bash}
|
||||
C:\path\to\opencv\build\install\x64\mingw\bin
|
||||
@endcode
|
||||
|
||||
---
|
||||
|
||||
## Step 8: Verify with a C++ Example {#tutorial_windows_msys2_install_verify}
|
||||
|
||||
- Create a folder outside the OpenCV source directory.
|
||||
Example: `first-project`
|
||||
- Inside the folder create **main.cpp**
|
||||
|
||||
@code{.cpp}
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << "OpenCV Version: " << CV_VERSION << std::endl;
|
||||
|
||||
cv::Mat img = cv::Mat::zeros(400, 400, CV_8UC3);
|
||||
|
||||
cv::imshow("OpenCV Test", img);
|
||||
cv::waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@endcode
|
||||
|
||||
- Create **CMakeLists.txt**
|
||||
|
||||
@code{.cmake}
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(OpenCVApp)
|
||||
|
||||
set(OpenCV_DIR "C:/path/to/opencv/build/install/lib/cmake/opencv4")
|
||||
|
||||
find_package(OpenCV REQUIRED)
|
||||
|
||||
add_executable(app main.cpp)
|
||||
|
||||
target_link_libraries(app ${OpenCV_LIBS})
|
||||
@endcode
|
||||
|
||||
- Build the project.
|
||||
|
||||
@code{.bash}
|
||||
mkdir build && cd build
|
||||
cmake -G "MinGW Makefiles" ..
|
||||
mingw32-make
|
||||
@endcode
|
||||
|
||||
- Run the program.
|
||||
|
||||
@code{.bash}
|
||||
./app.exe
|
||||
@endcode
|
||||
|
||||
@note If everything is configured correctly, a window should appear displaying a blank image.
|
||||
|
||||
---
|
||||
|
||||
## Using OpenCV in Visual Studio Code {#tutorial_windows_msys2_vscode_section}
|
||||
|
||||
Install the following extensions in **VS Code**:
|
||||
|
||||
- C/C++
|
||||
- CMake Tools
|
||||
|
||||
Open your project folder in VS Code.
|
||||
|
||||
Configure the project using CMake.
|
||||
|
||||
@code{.bash}
|
||||
cmake -G "MinGW Makefiles" ..
|
||||
@endcode
|
||||
|
||||
Build the project.
|
||||
|
||||
@code{.bash}
|
||||
mingw32-make
|
||||
@endcode
|
||||
|
||||
Run the executable.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting {#tutorial_windows_msys2_install_troubleshoot}
|
||||
|
||||
### CMake cannot find OpenCV
|
||||
|
||||
Ensure that `OpenCV_DIR` is set correctly.
|
||||
|
||||
Example:
|
||||
|
||||
@code{.cmake}
|
||||
set(OpenCV_DIR "C:/path/to/opencv/build/install/lib/cmake/opencv4")
|
||||
@endcode
|
||||
|
||||
---
|
||||
|
||||
### mingw32-make not found
|
||||
|
||||
Make sure the following path is added to the Windows environment variable **PATH**: `C:\msys64\ucrt64\bin`
|
||||
|
||||
---
|
||||
|
||||
### Build fails due to memory limits
|
||||
|
||||
Reduce parallel build jobs.
|
||||
|
||||
@code{.bash}
|
||||
mingw32-make -j2
|
||||
@endcode
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
You have successfully built OpenCV from source using **MSYS2 UCRT64** and verified it with a **C++ project in VS Code**.
|
||||
|
||||
This setup allows you to develop OpenCV-based C++ applications using a fully open-source toolchain on Windows.
|
||||
@@ -112,7 +112,7 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
|
||||
iwSrc.Init({src_width, src_height}, ippiGetDataType(src_type), CV_MAT_CN(src_type), NULL, src_data, IwSize(src_step));
|
||||
::ipp::IwiImage iwDst({dst_width, dst_height}, ippiGetDataType(src_type), CV_MAT_CN(src_type), NULL, dst_data, dst_step);
|
||||
::ipp::IwiBorderType ippBorder(ippiGetBorderType(borderType), {borderValue[0], borderValue[1], borderValue[2], borderValue[3]});
|
||||
IwTransDirection iwTransDirection = iwTransForward;
|
||||
IwTransDirection iwTransDirection = iwTransInverse;
|
||||
|
||||
if((int)ippBorder == -1)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
|
After Width: | Height: | Size: 78 KiB |
@@ -185,7 +185,7 @@ Z_w \\
|
||||
|
||||
The following figure illustrates the pinhole camera model.
|
||||
|
||||

|
||||
 { width=70% }
|
||||
|
||||
Real lenses usually have some distortion, mostly radial distortion, and slight tangential distortion.
|
||||
So, the above model is extended as:
|
||||
@@ -979,7 +979,7 @@ Check @ref tutorial_homography "the corresponding tutorial" for more details
|
||||
|
||||
/** @brief Finds an object pose \f$ {}^{c}\mathbf{T}_o \f$ from 3D-2D point correspondences:
|
||||
|
||||
{ width=50% }
|
||||
{ width=50% }
|
||||
|
||||
@see @ref calib3d_solvePnP
|
||||
|
||||
@@ -1049,7 +1049,7 @@ CV_EXPORTS_W bool solvePnP( InputArray objectPoints, InputArray imagePoints,
|
||||
|
||||
/** @brief Finds an object pose \f$ {}^{c}\mathbf{T}_o \f$ from 3D-2D point correspondences using the RANSAC scheme to deal with bad matches.
|
||||
|
||||
{ width=50% }
|
||||
{ width=50% }
|
||||
|
||||
@see @ref calib3d_solvePnP
|
||||
|
||||
@@ -1110,7 +1110,7 @@ CV_EXPORTS_W bool solvePnPRansac( InputArray objectPoints, InputArray imagePoint
|
||||
|
||||
/** @brief Finds an object pose \f$ {}^{c}\mathbf{T}_o \f$ from **3** 3D-2D point correspondences.
|
||||
|
||||
{ width=50% }
|
||||
{ width=50% }
|
||||
|
||||
@see @ref calib3d_solvePnP
|
||||
|
||||
@@ -1207,7 +1207,7 @@ CV_EXPORTS_W void solvePnPRefineVVS( InputArray objectPoints, InputArray imagePo
|
||||
|
||||
/** @brief Finds an object pose \f$ {}^{c}\mathbf{T}_o \f$ from 3D-2D point correspondences.
|
||||
|
||||
{ width=50% }
|
||||
{ width=50% }
|
||||
|
||||
@see @ref calib3d_solvePnP
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 39 KiB |
@@ -180,7 +180,7 @@ Z_w \\
|
||||
|
||||
The following figure illustrates the pinhole camera model.
|
||||
|
||||

|
||||
 { width=70% }
|
||||
|
||||
Real lenses usually have some distortion, mostly radial distortion, and slight tangential distortion.
|
||||
So, the above model is extended as:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#endif
|
||||
#include "tbb/tbb.h"
|
||||
#if !defined(TBB_INTERFACE_VERSION)
|
||||
#error "Unknows/unsupported TBB version"
|
||||
#error "Unknown/unsupported TBB version"
|
||||
#endif
|
||||
|
||||
#if TBB_INTERFACE_VERSION >= 8000
|
||||
|
||||
@@ -283,40 +283,24 @@ inline IppCmpOp arithm_ipp_convert_cmp(int cmpop)
|
||||
inline int arithm_ipp_cmp8u(const uchar* src1, size_t step1, const uchar* src2, size_t step2,
|
||||
uchar* dst, size_t step, int width, int height, int cmpop)
|
||||
{
|
||||
// perf regression with AVX512: https://github.com/opencv/opencv/issues/28251
|
||||
if (ippCPUID_AVX512F&cv::ipp::getIppFeatures())
|
||||
return 0;
|
||||
|
||||
ARITHM_IPP_CMP(ippiCompare_8u_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height));
|
||||
}
|
||||
|
||||
inline int arithm_ipp_cmp16u(const ushort* src1, size_t step1, const ushort* src2, size_t step2,
|
||||
uchar* dst, size_t step, int width, int height, int cmpop)
|
||||
{
|
||||
// perf regression with AVX512: https://github.com/opencv/opencv/issues/28251
|
||||
if (ippCPUID_AVX512F&cv::ipp::getIppFeatures())
|
||||
return 0;
|
||||
|
||||
ARITHM_IPP_CMP(ippiCompare_16u_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height));
|
||||
}
|
||||
|
||||
inline int arithm_ipp_cmp16s(const short* src1, size_t step1, const short* src2, size_t step2,
|
||||
uchar* dst, size_t step, int width, int height, int cmpop)
|
||||
{
|
||||
// perf regression with AVX512: https://github.com/opencv/opencv/issues/28251
|
||||
if (ippCPUID_AVX512F&cv::ipp::getIppFeatures())
|
||||
return 0;
|
||||
|
||||
ARITHM_IPP_CMP(ippiCompare_16s_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height));
|
||||
}
|
||||
|
||||
inline int arithm_ipp_cmp32f(const float* src1, size_t step1, const float* src2, size_t step2,
|
||||
uchar* dst, size_t step, int width, int height, int cmpop)
|
||||
{
|
||||
// perf regression with AVX512: https://github.com/opencv/opencv/issues/28251
|
||||
if (ippCPUID_AVX512F&cv::ipp::getIppFeatures())
|
||||
return 0;
|
||||
|
||||
ARITHM_IPP_CMP(ippiCompare_32f_C1R, src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(width, height));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace cv {
|
||||
|
||||
typedef int (*CountNonZeroFunc)(const uchar*, int);
|
||||
typedef int (*CountNonZeroFunc)(const void*, int);
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
|
||||
|
||||
@@ -32,8 +32,9 @@ static int countNonZero_(const T* src, int len )
|
||||
|
||||
#undef DEFINE_NONZERO_FUNC
|
||||
#define DEFINE_NONZERO_FUNC(funcname, suffix, ssuffix, T, VT, ST, cmp_op, add_op, update_sum, scalar_cmp_op) \
|
||||
static int funcname( const T* src, int len ) \
|
||||
static int funcname( const void* src_ptr, int len ) \
|
||||
{ \
|
||||
const T* src = static_cast<const T*>(src_ptr); \
|
||||
int i = 0, nz = 0; \
|
||||
SIMD_ONLY( \
|
||||
const int vlanes = VTraits<VT>::vlanes(); \
|
||||
@@ -119,9 +120,9 @@ DEFINE_NONZERO_FUNC(countNonZero16f, u16, u32, ushort, v_uint16, v_uint32, VEC_C
|
||||
|
||||
#undef DEFINE_NONZERO_FUNC_NOSIMD
|
||||
#define DEFINE_NONZERO_FUNC_NOSIMD(funcname, T) \
|
||||
static int funcname(const T* src, int len) \
|
||||
static int funcname(const void* src, int len) \
|
||||
{ \
|
||||
return countNonZero_(src, len); \
|
||||
return countNonZero_(static_cast<const T*>(src), len); \
|
||||
}
|
||||
|
||||
DEFINE_NONZERO_FUNC_NOSIMD(countNonZero64s, int64)
|
||||
|
||||
@@ -1,7 +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"
|
||||
#include "opencl_kernels_core.hpp"
|
||||
#include "hal_replacement.hpp"
|
||||
@@ -285,40 +285,63 @@ void transposeND(InputArray src_, const std::vector<int>& order, OutputArray dst
|
||||
}
|
||||
|
||||
|
||||
#if CV_SIMD128
|
||||
template<typename V> CV_ALWAYS_INLINE void flipHoriz_single( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size, size_t esz )
|
||||
// Generic scalar fallback
|
||||
CV_ALWAYS_INLINE void flipHoriz_generic( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size, size_t esz )
|
||||
{
|
||||
int i, j, limit = (int)(((size.width + 1)/2)*esz);
|
||||
int height = size.height;
|
||||
AutoBuffer<int> _tab(size.width*esz);
|
||||
int* tab = _tab.data();
|
||||
|
||||
for( i = 0; i < size.width; i++ )
|
||||
for( size_t k = 0; k < esz; k++ )
|
||||
tab[i*esz + k] = (int)((size.width - i - 1)*esz + k);
|
||||
|
||||
for( ; height--; src += sstep, dst += dstep )
|
||||
{
|
||||
for( i = 0; i < limit; i++ )
|
||||
{
|
||||
j = tab[i];
|
||||
uchar t0 = src[i], t1 = src[j];
|
||||
dst[i] = t1; dst[j] = t0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
template<typename V> CV_ALWAYS_INLINE void flipHoriz_single( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size )
|
||||
{
|
||||
typedef typename VTraits<V>::lane_type T;
|
||||
int end = (int)(size.width*esz);
|
||||
int width = (end + 1)/2;
|
||||
int width_1 = width & -VTraits<v_uint8x16>::vlanes();
|
||||
int i, j;
|
||||
const int vlanes = VTraits<v_uint8>::vlanes();
|
||||
int end = (int)(size.width * sizeof(T));
|
||||
int width = (end + 1) / 2;
|
||||
int width_simd = width & -vlanes;
|
||||
int height = size.height;
|
||||
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
CV_Assert(isAligned<sizeof(T)>(src, dst));
|
||||
#endif
|
||||
|
||||
for( ; size.height--; src += sstep, dst += dstep )
|
||||
for( ; height--; src += sstep, dst += dstep )
|
||||
{
|
||||
for( i = 0, j = end; i < width_1; i += VTraits<v_uint8x16>::vlanes(), j -= VTraits<v_uint8x16>::vlanes() )
|
||||
int i = 0, j = end;
|
||||
for( ; i < width_simd; i += vlanes, j -= vlanes )
|
||||
{
|
||||
V t0, t1;
|
||||
|
||||
t0 = v_load((T*)((uchar*)src + i));
|
||||
t1 = v_load((T*)((uchar*)src + j - VTraits<v_uint8x16>::vlanes()));
|
||||
V t0 = vx_load((const T*)(src + i));
|
||||
V t1 = vx_load((const T*)(src + j - vlanes));
|
||||
t0 = v_reverse(t0);
|
||||
t1 = v_reverse(t1);
|
||||
v_store((T*)(dst + j - VTraits<v_uint8x16>::vlanes()), t0);
|
||||
v_store((T*)(dst + j - vlanes), t0);
|
||||
v_store((T*)(dst + i), t1);
|
||||
}
|
||||
|
||||
// Scalar tail loop
|
||||
if (isAligned<sizeof(T)>(src, dst))
|
||||
{
|
||||
for ( ; i < width; i += sizeof(T), j -= sizeof(T) )
|
||||
{
|
||||
T t0, t1;
|
||||
|
||||
t0 = *((T*)((uchar*)src + i));
|
||||
t1 = *((T*)((uchar*)src + j - sizeof(T)));
|
||||
T t0 = *((const T*)(src + i));
|
||||
T t1 = *((const T*)(src + j - sizeof(T)));
|
||||
*((T*)(dst + j - sizeof(T))) = t0;
|
||||
*((T*)(dst + i)) = t1;
|
||||
}
|
||||
@@ -329,195 +352,194 @@ template<typename V> CV_ALWAYS_INLINE void flipHoriz_single( const uchar* src, s
|
||||
{
|
||||
for (int k = 0; k < (int)sizeof(T); k++)
|
||||
{
|
||||
uchar t0, t1;
|
||||
|
||||
t0 = *((uchar*)src + i + k);
|
||||
t1 = *((uchar*)src + j + k - sizeof(T));
|
||||
*(dst + j + k - sizeof(T)) = t0;
|
||||
*(dst + i + k) = t1;
|
||||
uchar t0 = src[i + k];
|
||||
uchar t1 = src[j + k - sizeof(T)];
|
||||
dst[j + k - sizeof(T)] = t0;
|
||||
dst[i + k] = t1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T1, typename T2> CV_ALWAYS_INLINE void flipHoriz_double( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size, size_t esz )
|
||||
// SIMD for C3
|
||||
template<typename V>
|
||||
CV_ALWAYS_INLINE void flipHoriz_c3( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size )
|
||||
{
|
||||
int end = (int)(size.width*esz);
|
||||
int width = (end + 1)/2;
|
||||
typedef typename VTraits<V>::lane_type T;
|
||||
const int vlanes = VTraits<V>::vlanes();
|
||||
const int stride = 3 * sizeof(T);
|
||||
int width = size.width;
|
||||
int centre = (width + 1) / 2;
|
||||
int width_simd = (centre / vlanes) * vlanes;
|
||||
int height = size.height;
|
||||
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
CV_Assert(isAligned<sizeof(T1)>(src, dst));
|
||||
CV_Assert(isAligned<sizeof(T2)>(src, dst));
|
||||
#endif
|
||||
|
||||
for( ; size.height--; src += sstep, dst += dstep )
|
||||
for( ; height--; src += sstep, dst += dstep )
|
||||
{
|
||||
for ( int i = 0, j = end; i < width; i += sizeof(T1) + sizeof(T2), j -= sizeof(T1) + sizeof(T2) )
|
||||
int i = 0;
|
||||
for( ; i < width_simd; i += vlanes )
|
||||
{
|
||||
T1 t0, t1;
|
||||
T2 t2, t3;
|
||||
|
||||
t0 = *((T1*)((uchar*)src + i));
|
||||
t2 = *((T2*)((uchar*)src + i + sizeof(T1)));
|
||||
t1 = *((T1*)((uchar*)src + j - sizeof(T1) - sizeof(T2)));
|
||||
t3 = *((T2*)((uchar*)src + j - sizeof(T2)));
|
||||
*((T1*)(dst + j - sizeof(T1) - sizeof(T2))) = t0;
|
||||
*((T2*)(dst + j - sizeof(T2))) = t2;
|
||||
*((T1*)(dst + i)) = t1;
|
||||
*((T2*)(dst + i + sizeof(T1))) = t3;
|
||||
V r0, g0, b0;
|
||||
v_load_deinterleave((const T*)(src + i * stride), r0, g0, b0);
|
||||
V r1, g1, b1;
|
||||
v_load_deinterleave((const T*)(src + (width - i - vlanes) * stride), r1, g1, b1);
|
||||
r0 = v_reverse(r0);
|
||||
g0 = v_reverse(g0);
|
||||
b0 = v_reverse(b0);
|
||||
r1 = v_reverse(r1);
|
||||
g1 = v_reverse(g1);
|
||||
b1 = v_reverse(b1);
|
||||
v_store_interleave((T*)(dst + (width - i - vlanes) * stride), r0, g0, b0);
|
||||
v_store_interleave((T*)(dst + i * stride), r1, g1, b1);
|
||||
}
|
||||
// Scalar tail loop for remaining pixels
|
||||
for( ; i < centre; i++ )
|
||||
{
|
||||
int j = width - i - 1;
|
||||
T c0 = ((const T*)(src + i * stride))[0];
|
||||
T c1 = ((const T*)(src + i * stride))[1];
|
||||
T c2 = ((const T*)(src + i * stride))[2];
|
||||
T c3 = ((const T*)(src + j * stride))[0];
|
||||
T c4 = ((const T*)(src + j * stride))[1];
|
||||
T c5 = ((const T*)(src + j * stride))[2];
|
||||
((T*)(dst + j * stride))[0] = c0;
|
||||
((T*)(dst + j * stride))[1] = c1;
|
||||
((T*)(dst + j * stride))[2] = c2;
|
||||
((T*)(dst + i * stride))[0] = c3;
|
||||
((T*)(dst + i * stride))[1] = c4;
|
||||
((T*)(dst + i * stride))[2] = c5;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
flipHoriz( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size, size_t esz )
|
||||
// SIMD flip when ESZ multiple of vlanes
|
||||
template<size_t ESZ>
|
||||
CV_ALWAYS_INLINE void flipHoriz_vlanes_match( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size)
|
||||
{
|
||||
const int vlanes = VTraits<v_uint8>::vlanes();
|
||||
int end = (int)(size.width * ESZ);
|
||||
int width = end / 2;
|
||||
int height = size.height;
|
||||
int eSize = (int)ESZ;
|
||||
for( ; height--; src += sstep, dst += dstep )
|
||||
{
|
||||
for( int i = 0, j = end - eSize; i < width; i += eSize, j -= eSize )
|
||||
{
|
||||
for( int k = 0; k < eSize; k += vlanes )
|
||||
{
|
||||
v_uint8 t0 = vx_load(src + i + k);
|
||||
v_uint8 t1 = vx_load(src + j + k);
|
||||
v_store(dst + j + k, t0);
|
||||
v_store(dst + i + k, t1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if CV_SIMD128
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
size_t alignmentMark = ((size_t)src)|((size_t)dst)|sstep|dstep;
|
||||
#endif
|
||||
if (esz == 2 * (size_t)VTraits<v_uint8x16>::vlanes())
|
||||
// SIMD flip when ESZ=16 (128-bit)
|
||||
template<size_t ESZ>
|
||||
CV_ALWAYS_INLINE void flipHoriz_vlanes_match_128( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size)
|
||||
{
|
||||
const int vlanes16 = VTraits<v_uint8x16>::vlanes();
|
||||
int end = (int)(size.width * ESZ);
|
||||
int width = end / 2;
|
||||
int height = size.height;
|
||||
for( ; height--; src += sstep, dst += dstep )
|
||||
{
|
||||
int end = (int)(size.width*esz);
|
||||
int width = end/2;
|
||||
|
||||
for( ; size.height--; src += sstep, dst += dstep )
|
||||
for( int i = 0, j = end - vlanes16; i < width; i += vlanes16, j -= vlanes16 )
|
||||
{
|
||||
for( int i = 0, j = end - 2 * VTraits<v_uint8x16>::vlanes(); i < width; i += 2 * VTraits<v_uint8x16>::vlanes(), j -= 2 * VTraits<v_uint8x16>::vlanes() )
|
||||
{
|
||||
#if CV_SIMD256
|
||||
v_uint8x32 t0, t1;
|
||||
|
||||
t0 = v256_load((uchar*)src + i);
|
||||
t1 = v256_load((uchar*)src + j);
|
||||
v_store(dst + j, t0);
|
||||
v_store(dst + i, t1);
|
||||
#else
|
||||
v_uint8x16 t0, t1, t2, t3;
|
||||
|
||||
t0 = v_load((uchar*)src + i);
|
||||
t1 = v_load((uchar*)src + i + VTraits<v_uint8x16>::vlanes());
|
||||
t2 = v_load((uchar*)src + j);
|
||||
t3 = v_load((uchar*)src + j + VTraits<v_uint8x16>::vlanes());
|
||||
v_store(dst + j, t0);
|
||||
v_store(dst + j + VTraits<v_uint8x16>::vlanes(), t1);
|
||||
v_store(dst + i, t2);
|
||||
v_store(dst + i + VTraits<v_uint8x16>::vlanes(), t3);
|
||||
#endif
|
||||
}
|
||||
v_uint8x16 t0 = v_load(src + i);
|
||||
v_uint8x16 t1 = v_load(src + j);
|
||||
v_store(dst + j, t0);
|
||||
v_store(dst + i, t1);
|
||||
}
|
||||
}
|
||||
else if (esz == (size_t)VTraits<v_uint8x16>::vlanes())
|
||||
{
|
||||
int end = (int)(size.width*esz);
|
||||
int width = end/2;
|
||||
|
||||
for( ; size.height--; src += sstep, dst += dstep )
|
||||
{
|
||||
for( int i = 0, j = end - VTraits<v_uint8x16>::vlanes(); i < width; i += VTraits<v_uint8x16>::vlanes(), j -= VTraits<v_uint8x16>::vlanes() )
|
||||
{
|
||||
v_uint8x16 t0, t1;
|
||||
|
||||
t0 = v_load((uchar*)src + i);
|
||||
t1 = v_load((uchar*)src + j);
|
||||
v_store(dst + j, t0);
|
||||
v_store(dst + i, t1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (esz == 8
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
&& isAligned<sizeof(uint64)>(alignmentMark)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
flipHoriz_single<v_uint64x2>(src, sstep, dst, dstep, size, esz);
|
||||
}
|
||||
else if (esz == 4
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
&& isAligned<sizeof(unsigned)>(alignmentMark)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
flipHoriz_single<v_uint32x4>(src, sstep, dst, dstep, size, esz);
|
||||
}
|
||||
else if (esz == 2
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
&& isAligned<sizeof(ushort)>(alignmentMark)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
flipHoriz_single<v_uint16x8>(src, sstep, dst, dstep, size, esz);
|
||||
}
|
||||
else if (esz == 1)
|
||||
{
|
||||
flipHoriz_single<v_uint8x16>(src, sstep, dst, dstep, size, esz);
|
||||
}
|
||||
else if (esz == 24
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
&& isAligned<sizeof(uint64_t)>(alignmentMark)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int end = (int)(size.width*esz);
|
||||
int width = (end + 1)/2;
|
||||
|
||||
for( ; size.height--; src += sstep, dst += dstep )
|
||||
{
|
||||
for ( int i = 0, j = end; i < width; i += VTraits<v_uint8x16>::vlanes() + sizeof(uint64_t), j -= VTraits<v_uint8x16>::vlanes() + sizeof(uint64_t) )
|
||||
{
|
||||
v_uint8x16 t0, t1;
|
||||
uint64_t t2, t3;
|
||||
|
||||
t0 = v_load((uchar*)src + i);
|
||||
t2 = *((uint64_t*)((uchar*)src + i + VTraits<v_uint8x16>::vlanes()));
|
||||
t1 = v_load((uchar*)src + j - VTraits<v_uint8x16>::vlanes() - sizeof(uint64_t));
|
||||
t3 = *((uint64_t*)((uchar*)src + j - sizeof(uint64_t)));
|
||||
v_store(dst + j - VTraits<v_uint8x16>::vlanes() - sizeof(uint64_t), t0);
|
||||
*((uint64_t*)(dst + j - sizeof(uint64_t))) = t2;
|
||||
v_store(dst + i, t1);
|
||||
*((uint64_t*)(dst + i + VTraits<v_uint8x16>::vlanes())) = t3;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if !CV_STRONG_ALIGNMENT
|
||||
else if (esz == 12)
|
||||
{
|
||||
flipHoriz_double<uint64_t,uint>(src, sstep, dst, dstep, size, esz);
|
||||
}
|
||||
else if (esz == 6)
|
||||
{
|
||||
flipHoriz_double<uint,ushort>(src, sstep, dst, dstep, size, esz);
|
||||
}
|
||||
else if (esz == 3)
|
||||
{
|
||||
flipHoriz_double<ushort,uchar>(src, sstep, dst, dstep, size, esz);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
}
|
||||
#endif // CV_SIMD128
|
||||
|
||||
// SIMD flip for ESZ=16,32
|
||||
template<size_t ESZ>
|
||||
CV_ALWAYS_INLINE void flipHoriz_vlanes_dispatch( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size )
|
||||
{
|
||||
const int vlanes = VTraits<v_uint8>::vlanes();
|
||||
#if CV_SIMD128
|
||||
const int vlanes16 = VTraits<v_uint8x16>::vlanes();
|
||||
#endif
|
||||
if ( (ESZ == (size_t)vlanes) || (ESZ == 2 * (size_t)vlanes))
|
||||
{
|
||||
int i, j, limit = (int)(((size.width + 1)/2)*esz);
|
||||
AutoBuffer<int> _tab(size.width*esz);
|
||||
int* tab = _tab.data();
|
||||
flipHoriz_vlanes_match<ESZ>(src, sstep, dst, dstep, size);
|
||||
return;
|
||||
}
|
||||
#if CV_SIMD128
|
||||
else if (ESZ == vlanes16)
|
||||
{
|
||||
flipHoriz_vlanes_match_128<ESZ>(src, sstep, dst, dstep, size);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
flipHoriz_generic(src, sstep, dst, dstep, size, ESZ);
|
||||
}
|
||||
|
||||
for( i = 0; i < size.width; i++ )
|
||||
for( size_t k = 0; k < esz; k++ )
|
||||
tab[i*esz + k] = (int)((size.width - i - 1)*esz + k);
|
||||
|
||||
for( ; size.height--; src += sstep, dst += dstep )
|
||||
#if CV_SIMD128
|
||||
// SIMD flip for ESZ=24 (CV_64FC3)
|
||||
CV_ALWAYS_INLINE void flipHoriz_24( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size )
|
||||
{
|
||||
#if CV_STRONG_ALIGNMENT
|
||||
// This kernel performs 64-bit scalar loads/stores, so require 8-byte alignment.
|
||||
if (!isAligned<8>(((size_t)src)|((size_t)dst)|sstep|dstep))
|
||||
{
|
||||
flipHoriz_generic(src, sstep, dst, dstep, size, 24);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
const int lanes16 = 16;
|
||||
int end = (int)(size.width * 24);
|
||||
int width = (end + 1) / 2;
|
||||
int height = size.height;
|
||||
for( ; height--; src += sstep, dst += dstep )
|
||||
{
|
||||
for ( int i = 0, j = end; i < width; i += lanes16 + 8, j -= lanes16 + 8 )
|
||||
{
|
||||
for( i = 0; i < limit; i++ )
|
||||
{
|
||||
j = tab[i];
|
||||
uchar t0 = src[i], t1 = src[j];
|
||||
dst[i] = t1; dst[j] = t0;
|
||||
}
|
||||
v_uint8x16 t0 = v_load(src + i);
|
||||
uint64_t t2 = *reinterpret_cast<const uint64_t*>(src + i + lanes16);
|
||||
v_uint8x16 t1 = v_load(src + j - lanes16 - 8);
|
||||
uint64_t t3 = *reinterpret_cast<const uint64_t*>(src + j - 8);
|
||||
v_store(dst + j - lanes16 - 8, t0);
|
||||
*reinterpret_cast<uint64_t*>(dst + j - 8) = t2;
|
||||
v_store(dst + i, t1);
|
||||
*reinterpret_cast<uint64_t*>(dst + i + lanes16) = t3;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // CV_SIMD128
|
||||
#endif // CV_SIMD || CV_SIMD_SCALABLE
|
||||
|
||||
static void flipHoriz( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size, size_t esz )
|
||||
{
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
// SIMD-optimized dispatch
|
||||
switch(esz)
|
||||
{
|
||||
case 1: flipHoriz_single<v_uint8>(src, sstep, dst, dstep, size); return; // CV_8UC1: 8-bit, 1 channel
|
||||
case 2: flipHoriz_single<v_uint16>(src, sstep, dst, dstep, size); return; // CV_8UC2, CV_16UC1: 8-bit 2-channel or 16-bit 1-channel
|
||||
case 3: flipHoriz_c3<v_uint8>(src, sstep, dst, dstep, size); return; // CV_8UC3: 8-bit, 3 channels
|
||||
case 4: flipHoriz_single<v_uint32>(src, sstep, dst, dstep, size); return; // CV_8UC4, CV_16UC2, CV_32SC1, CV_32FC1: 8-bit 4-channel, 16-bit 2-channel, or 32-bit 1-channel
|
||||
case 6: flipHoriz_c3<v_uint16>(src, sstep, dst, dstep, size); return; // CV_16UC3, CV_16SC3: 16-bit, 3 channels
|
||||
case 8: flipHoriz_single<v_uint64>(src, sstep, dst, dstep, size); return; // CV_16UC4, CV_32SC2, CV_32FC2, CV_64FC1: 16-bit 4-channel, 32-bit 2-channel, or 64-bit 1-channel
|
||||
case 12: flipHoriz_c3<v_uint32>(src, sstep, dst, dstep, size); return; // CV_32SC3, CV_32FC3: 32-bit, 3 channels
|
||||
case 16: flipHoriz_vlanes_dispatch<16>(src, sstep, dst, dstep, size); return; // CV_32SC4, CV_32FC4, CV_64FC2: 32-bit 4-channel or 64-bit 2-channel
|
||||
#if CV_SIMD128
|
||||
case 24: flipHoriz_24(src, sstep, dst, dstep, size); return; // CV_64FC3: 64-bit, 3 channels
|
||||
#endif
|
||||
case 32: flipHoriz_vlanes_dispatch<32>(src, sstep, dst, dstep, size); return; // CV_64FC4: 64-bit, 4 channels
|
||||
default:
|
||||
break; // Fall through to generic implementation
|
||||
}
|
||||
#endif
|
||||
// Fallback: generic scalar
|
||||
flipHoriz_generic(src, sstep, dst, dstep, size, esz);
|
||||
}
|
||||
|
||||
static void
|
||||
flipVert( const uchar* src0, size_t sstep, uchar* dst0, size_t dstep, Size size, size_t esz )
|
||||
|
||||
@@ -432,12 +432,13 @@ public:
|
||||
CV_PARSE_ERROR_CPP( "Missing \':\'" );
|
||||
|
||||
saveptr = endptr + 1;
|
||||
if( endptr == ptr )
|
||||
CV_PARSE_ERROR_CPP( "An empty key" );
|
||||
|
||||
do c = *--endptr;
|
||||
while( c == ' ' );
|
||||
|
||||
++endptr;
|
||||
if( endptr == ptr )
|
||||
CV_PARSE_ERROR_CPP( "An empty key" );
|
||||
|
||||
value_placeholder = fs->addNode(map_node, std::string(ptr, endptr - ptr), FileNode::NONE);
|
||||
ptr = saveptr;
|
||||
|
||||
@@ -3925,5 +3925,17 @@ TEST_P(Core_MaskTypeTest, MeanStdDev)
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/**/, Core_MaskTypeTest, MaskType::all());
|
||||
|
||||
// Still fails in 5.x: https://github.com/opencv/opencv/issues/28557
|
||||
TEST(Core_Arithm, DISABLED_mul_overflow_28557)
|
||||
{
|
||||
uint16_t data[] = {5000, 60000, 5000, 60000, 5000, 60000};
|
||||
cv::Mat m(1, 6, CV_16U, data);
|
||||
cv::Mat res = m.mul(m);
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
EXPECT_EQ(65535, res.at<uint16_t>(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -1707,6 +1707,21 @@ TEST(Core_InputOutput, FileStorage_free_file_after_exception)
|
||||
ASSERT_EQ(0, std::remove(fileName.c_str()));
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, FileStorage_YAML_empty_key)
|
||||
{
|
||||
const std::string fileName = cv::tempfile("FileStorage_YAML_empty_key_test.yml");
|
||||
const std::string content = "%YAML:1.0\n---\nkey1: value1\n: 10\n";
|
||||
|
||||
std::fstream testFile;
|
||||
testFile.open(fileName.c_str(), std::fstream::out);
|
||||
if(!testFile.is_open()) FAIL();
|
||||
testFile << content;
|
||||
testFile.close();
|
||||
|
||||
EXPECT_THROW(FileStorage(fileName, FileStorage::READ), cv::Exception);
|
||||
ASSERT_EQ(0, std::remove(fileName.c_str()));
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, FileStorage_write_to_sequence)
|
||||
{
|
||||
const std::vector<std::string> formatExts = { ".yml", ".json", ".xml" };
|
||||
|
||||
@@ -69,7 +69,7 @@ protected:
|
||||
bool TestSparseMat();
|
||||
bool TestVec();
|
||||
bool TestMatxMultiplication();
|
||||
bool TestMatxElementwiseDivison();
|
||||
bool TestMatxElementwiseDivision();
|
||||
bool TestDivisionByValue();
|
||||
bool TestInplaceDivisionByValue();
|
||||
bool TestMatMatxCastSum();
|
||||
@@ -958,7 +958,7 @@ bool CV_OperationsTest::TestMatMatxCastSum()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CV_OperationsTest::TestMatxElementwiseDivison()
|
||||
bool CV_OperationsTest::TestMatxElementwiseDivision()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1248,7 +1248,7 @@ void CV_OperationsTest::run( int /* start_from */)
|
||||
if (!TestMatxMultiplication())
|
||||
return;
|
||||
|
||||
if (!TestMatxElementwiseDivison())
|
||||
if (!TestMatxElementwiseDivision())
|
||||
return;
|
||||
|
||||
if (!TestDivisionByValue())
|
||||
|
||||
@@ -187,7 +187,7 @@ void blobFromImagesNCHWImpl(const std::vector<Mat>& images, Mat& blob_, const Im
|
||||
|
||||
if (param.mean == Scalar() && param.scalefactor == Scalar::all(1.0))
|
||||
return;
|
||||
CV_CheckTypeEQ(param.ddepth, CV_32F, "Scaling and mean substraction is supported only for CV_32F blob depth");
|
||||
CV_CheckTypeEQ(param.ddepth, CV_32F, "Scaling and mean subtraction is supported only for CV_32F blob depth");
|
||||
|
||||
for (size_t k = 0; k < images.size(); ++k)
|
||||
{
|
||||
|
||||
@@ -1282,7 +1282,7 @@ Mat LayerEinsumImpl::pairwiseOperandProcess(
|
||||
shape(currentLeft),
|
||||
reshaped_dims))
|
||||
{
|
||||
// This can be done because curent_* tensors (if they exist) and output tensors are
|
||||
// This can be done because current_* tensors (if they exist) and output tensors are
|
||||
// intermediate tensors and cannot be input tensors to the Einsum node itself
|
||||
// (which are immutable).
|
||||
currentLeft = currentLeft.reshape(1, reshaped_dims.size(), reshaped_dims.data());
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
else if (reduction_name == "min")
|
||||
reduction = REDUCTION::MIN;
|
||||
else
|
||||
CV_Error(cv::Error::StsBadArg, "Unkown reduction \"" + reduction_name + "\"");
|
||||
CV_Error(cv::Error::StsBadArg, "Unknown reduction \"" + reduction_name + "\"");
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
else if (reduction_name == "min")
|
||||
reduction = REDUCTION::MIN;
|
||||
else
|
||||
CV_Error(cv::Error::StsBadArg, "Unkown reduction \"" + reduction_name + "\"");
|
||||
CV_Error(cv::Error::StsBadArg, "Unknown reduction \"" + reduction_name + "\"");
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||
|
||||
@@ -1282,7 +1282,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
// extract axis from original Gather node
|
||||
// extract axis from original Gather node
|
||||
axis = 0;
|
||||
opencv_onnx::NodeProto* origGatherNode =
|
||||
inpNode.dynamicCast<ONNXNodeWrapper>()->node;
|
||||
@@ -1306,6 +1306,7 @@ public:
|
||||
new_attr->set_name("axis");
|
||||
new_attr->set_i(axis);
|
||||
}
|
||||
|
||||
private:
|
||||
int cast, gather, axis;
|
||||
};
|
||||
|
||||
@@ -215,7 +215,7 @@ void CannNet::forward()
|
||||
ACL_CHECK_RET(aclmdlExecute(model_id, inputs, outputs));
|
||||
CV_LOG_DEBUG(NULL, "DNN/CANN: finished network forward");
|
||||
|
||||
// fetch ouputs from device to host
|
||||
// fetch outputs from device to host
|
||||
CV_LOG_DEBUG(NULL, "DNN/CANN: start fetching outputs to host");
|
||||
for (size_t i = 0; i < output_wrappers.size(); ++i)
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@ Mat infEngineBlobToMat(const ov::Tensor& blob)
|
||||
default:
|
||||
CV_Error(Error::StsNotImplemented, "Unsupported blob precision");
|
||||
}
|
||||
return Mat(size, type, blob.data());
|
||||
return Mat(size, type, const_cast<void*>(blob.data()));
|
||||
}
|
||||
|
||||
void infEngineBlobsToMats(const ov::TensorVector& blobs,
|
||||
|
||||
@@ -535,7 +535,7 @@ TimVXBackendNode::TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph_,
|
||||
}
|
||||
|
||||
TimVXBackendNode::TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph_, std::shared_ptr<tim::vx::Operation>& op_,
|
||||
std::vector<int>& inputsIndex, std::vector<int>& outpusIndex)
|
||||
std::vector<int>& inputsIndex, std::vector<int>& outputsIndex)
|
||||
:BackendNode(DNN_BACKEND_TIMVX)
|
||||
{
|
||||
tvGraph = tvGraph_;
|
||||
@@ -545,8 +545,8 @@ TimVXBackendNode::TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph_, std::shared_
|
||||
if (!inputsIndex.empty())
|
||||
inputIndexList.assign(inputsIndex.begin(), inputsIndex.end());
|
||||
|
||||
if (!outpusIndex.empty())
|
||||
outputIndexList.assign(outpusIndex.begin(), outpusIndex.end());
|
||||
if (!outputsIndex.empty())
|
||||
outputIndexList.assign(outputsIndex.begin(), outputsIndex.end());
|
||||
}
|
||||
|
||||
bool TimVXBackendNode::opBinding()
|
||||
|
||||
@@ -96,7 +96,7 @@ public:
|
||||
TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph);
|
||||
TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph, const std::shared_ptr<tim::vx::Operation>& op);
|
||||
TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph, std::shared_ptr<tim::vx::Operation>& op,
|
||||
std::vector<int>& inputsIndex, std::vector<int>& outpusIndex);
|
||||
std::vector<int>& inputsIndex, std::vector<int>& outputsIndex);
|
||||
|
||||
void setInputTensor();
|
||||
bool opBinding();
|
||||
|
||||
@@ -175,9 +175,9 @@ public:
|
||||
|
||||
/** @brief Constructs a nearest neighbor search index for a given dataset.
|
||||
|
||||
@param features Matrix of containing the features(points) to index. The size of the matrix is
|
||||
@param features Matrix containing the features(points) to index. The size of the matrix is
|
||||
num_features x feature_dimensionality and the data type of the elements in the matrix must
|
||||
coincide with the type of the index.
|
||||
match the type of the index.
|
||||
@param params Structure containing the index parameters. The type of index that will be
|
||||
constructed depends on the type of this parameter. See the description.
|
||||
@param distance
|
||||
|
||||
@@ -236,7 +236,8 @@ void showImageImpl( const char* name, cv::InputArray arr)
|
||||
int y = [window y0];
|
||||
if(x >= 0 && y >= 0)
|
||||
{
|
||||
y = [[window screen] visibleFrame].size.height - y;
|
||||
NSRect visibleFrame = [[window screen] visibleFrame];
|
||||
y = NSMaxY(visibleFrame) - y;
|
||||
[window setFrameTopLeftPoint:NSMakePoint(x, y)];
|
||||
}
|
||||
}
|
||||
@@ -273,7 +274,8 @@ void moveWindowImpl( const char* name, int x, int y)
|
||||
[window setY0:y];
|
||||
}
|
||||
else {
|
||||
y = [[window screen] visibleFrame].size.height - y;
|
||||
NSRect visibleFrame = [[window screen] visibleFrame];
|
||||
y = NSMaxY(visibleFrame) - y;
|
||||
[window setFrameTopLeftPoint:NSMakePoint(x, y)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,8 @@ enum ImwriteFlags {
|
||||
IMWRITE_EXR_TYPE = (3 << 4) + 0 /* 48 */, //!< override EXR storage type (FLOAT (FP32) is default)
|
||||
IMWRITE_EXR_COMPRESSION = (3 << 4) + 1 /* 49 */, //!< override EXR compression type (ZIP_COMPRESSION = 3 is default)
|
||||
IMWRITE_EXR_DWA_COMPRESSION_LEVEL = (3 << 4) + 2 /* 50 */, //!< override EXR DWA compression level (45 is default)
|
||||
IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used.
|
||||
IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a lossy quality from 1 to 100 (the higher is the better) for IMWRITE_WEBP_LOSSLESS_OFF. By default (without this parameter) or if quality > 100, IMWRITE_WEBP_LOSSLESS_ON is used instead.
|
||||
IMWRITE_WEBP_LOSSLESS_MODE = 65, //!< For WEBP, it can be a lossless compression strategy. See cv::ImwriteWEBPLosslessMode. Default is IMWRITE_WEBP_LOSSLESS_OFF. For Animated WEBP, it is not supported.
|
||||
IMWRITE_HDR_COMPRESSION = (5 << 4) + 0 /* 80 */, //!< specify HDR compression
|
||||
IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format
|
||||
IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set. See ImwriteTiffResolutionUnitFlags. Default is IMWRITE_TIFF_RESOLUTION_UNIT_INCH.
|
||||
@@ -240,6 +241,14 @@ enum ImwritePAMFlags {
|
||||
IMWRITE_PAM_FORMAT_RGB_ALPHA = 5
|
||||
};
|
||||
|
||||
//! Imwrite WEBP specific values for IMWRITE_WEBP_LOSSLESS_MODE parameter key.
|
||||
enum ImwriteWEBPLosslessMode {
|
||||
IMWRITE_WEBP_LOSSLESS_OFF = 0, //!< Lossy compression mode. Uses IMWRITE_WEBP_QUALITY to control compression. (Default)
|
||||
//!< @note If IMWRITE_WEBP_QUALITY is not specified, it falls back to IMWRITE_WEBP_LOSSLESS_ON to maintain backward compatibility.
|
||||
IMWRITE_WEBP_LOSSLESS_ON = 1, //!< Standard lossless compression. May modify or discard RGB values of fully transparent pixels to improve compression ratio.
|
||||
IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR = 2, //!< Exact lossless compression. Preserves all RGB data even for pixels with 0 alpha (equivalent to WebP's exact flag).
|
||||
};
|
||||
|
||||
//! Imwrite HDR specific values for IMWRITE_HDR_COMPRESSION parameter key
|
||||
enum ImwriteHDRCompressionFlags {
|
||||
IMWRITE_HDR_COMPRESSION_NONE = 0,
|
||||
|
||||
@@ -338,7 +338,7 @@ WebPEncoder::WebPEncoder()
|
||||
m_support_metadata[IMAGE_METADATA_EXIF] = true;
|
||||
m_support_metadata[IMAGE_METADATA_XMP] = true;
|
||||
m_support_metadata[IMAGE_METADATA_ICCP] = true;
|
||||
m_supported_encode_key = {IMWRITE_WEBP_QUALITY};
|
||||
m_supported_encode_key = {IMWRITE_WEBP_QUALITY, IMWRITE_WEBP_LOSSLESS_MODE};
|
||||
}
|
||||
|
||||
WebPEncoder::~WebPEncoder() { }
|
||||
@@ -348,32 +348,121 @@ ImageEncoder WebPEncoder::newEncoder() const
|
||||
return makePtr<WebPEncoder>();
|
||||
}
|
||||
|
||||
// Simple API style
|
||||
static size_t cvEncodeLosslessExactBGRA(const uint8_t* rgba, int width, int height, int stride, uint8_t** output)
|
||||
{
|
||||
WebPConfig config;
|
||||
WebPPicture pic;
|
||||
WebPMemoryWriter wrt;
|
||||
|
||||
// 6 is the default value for speed/compression balance in lossless mode.
|
||||
// It doesn't affect visual quality, only file size and encoding time.
|
||||
if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, 6))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
config.exact = 1;
|
||||
|
||||
if (!WebPPictureInit(&pic))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
pic.width = width;
|
||||
pic.height = height;
|
||||
pic.use_argb = 1; // BGRA
|
||||
if (!WebPPictureImportBGRA(&pic, rgba, stride))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebPMemoryWriterInit(&wrt);
|
||||
pic.writer = WebPMemoryWrite;
|
||||
pic.custom_ptr = &wrt;
|
||||
|
||||
if (!WebPEncode(&config, &pic))
|
||||
{
|
||||
WebPMemoryWriterClear(&wrt);
|
||||
WebPPictureFree(&pic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*output = wrt.mem;
|
||||
size_t size = wrt.size;
|
||||
WebPPictureFree(&pic);
|
||||
return size;
|
||||
}
|
||||
|
||||
bool WebPEncoder::write(const Mat& img, const std::vector<int>& params)
|
||||
{
|
||||
CV_CheckDepthEQ(img.depth(), CV_8U, "WebP codec supports 8U images only");
|
||||
|
||||
const int width = img.cols, height = img.rows;
|
||||
|
||||
bool comp_lossless = true;
|
||||
float quality = 100.0f;
|
||||
int lossless_mode = -1; // not specified
|
||||
float quality = 0.0f; // not specified
|
||||
|
||||
for(size_t i = 0; i < params.size(); i += 2)
|
||||
{
|
||||
const int value = params[i+1];
|
||||
if (params[i] == IMWRITE_WEBP_QUALITY)
|
||||
if (params[i] == IMWRITE_WEBP_LOSSLESS_MODE)
|
||||
{
|
||||
comp_lossless = false;
|
||||
quality = static_cast<float>(value);
|
||||
if (quality < 1.0f)
|
||||
switch(value)
|
||||
{
|
||||
quality = 1.0f;
|
||||
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_WEBP_QUALITY must be between 1 to 100(lossy) or more(lossless). It is fallbacked to 1", value));
|
||||
}
|
||||
if (quality > 100.0f)
|
||||
{
|
||||
comp_lossless = true;
|
||||
case IMWRITE_WEBP_LOSSLESS_ON:
|
||||
case IMWRITE_WEBP_LOSSLESS_OFF:
|
||||
case IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR:
|
||||
lossless_mode = value;
|
||||
break;
|
||||
default:
|
||||
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_WEBP_LOSSLESS_MODE must be one of ImwriteWEBPLosslessMode. It is ignored", value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (params[i] == IMWRITE_WEBP_QUALITY)
|
||||
{
|
||||
if (value < 1)
|
||||
{
|
||||
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_WEBP_QUALITY must be between 1 to 100(lossy) or more(lossless). It is fallbacked to 1", value));
|
||||
quality = 1.0f;
|
||||
}
|
||||
else if (value > 100)
|
||||
{
|
||||
quality = 101.0f;
|
||||
}
|
||||
else // value is 1 to 100
|
||||
{
|
||||
quality = static_cast<float>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(lossless_mode)
|
||||
{
|
||||
case -1: // not specified by user
|
||||
case IMWRITE_WEBP_LOSSLESS_OFF:
|
||||
// Fallback to lossless if quality is not specified (-1.0f) or out of lossy range (>100.0f).
|
||||
// This maintains backward compatibility where WebP defaults to lossless.
|
||||
if ((quality < 1.0f) || (quality > 100.0f))
|
||||
{
|
||||
lossless_mode = IMWRITE_WEBP_LOSSLESS_ON;
|
||||
quality = 101.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
lossless_mode = IMWRITE_WEBP_LOSSLESS_OFF;
|
||||
// Use specified quality for lossy compression.
|
||||
}
|
||||
break;
|
||||
|
||||
case IMWRITE_WEBP_LOSSLESS_ON:
|
||||
case IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR:
|
||||
// Force quality value to lossless range when explicit lossless mode is selected.
|
||||
quality = 101.0f;
|
||||
break;
|
||||
|
||||
default:
|
||||
CV_Error(Error::StsError, cv::format("Unexpected lossless_mode(%d)", lossless_mode));
|
||||
break;
|
||||
}
|
||||
|
||||
int channels = img.channels();
|
||||
@@ -391,29 +480,45 @@ bool WebPEncoder::write(const Mat& img, const std::vector<int>& params)
|
||||
|
||||
uint8_t *encoder_out = NULL;
|
||||
size_t size = 0;
|
||||
if (comp_lossless)
|
||||
if (channels == 3)
|
||||
{
|
||||
if (channels == 3)
|
||||
switch(lossless_mode)
|
||||
{
|
||||
size = WebPEncodeLosslessBGR(image->ptr(), width, height, (int)image->step, &encoder_out);
|
||||
}
|
||||
else if (channels == 4)
|
||||
{
|
||||
size = WebPEncodeLosslessBGRA(image->ptr(), width, height, (int)image->step, &encoder_out);
|
||||
case IMWRITE_WEBP_LOSSLESS_OFF:
|
||||
size = WebPEncodeBGR(image->ptr(), width, height, (int)image->step, quality, &encoder_out);
|
||||
break;
|
||||
case IMWRITE_WEBP_LOSSLESS_ON:
|
||||
case IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR:
|
||||
size = WebPEncodeLosslessBGR(image->ptr(), width, height, (int)image->step, &encoder_out);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsError, cv::format("Unexcepted lossless_mode(%d)", lossless_mode));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (channels == 3)
|
||||
CV_CheckEQ(channels, 4, "Unexpected channels is used");
|
||||
switch(lossless_mode)
|
||||
{
|
||||
size = WebPEncodeBGR(image->ptr(), width, height, (int)image->step, quality, &encoder_out);
|
||||
}
|
||||
else if (channels == 4)
|
||||
{
|
||||
size = WebPEncodeBGRA(image->ptr(), width, height, (int)image->step, quality, &encoder_out);
|
||||
case IMWRITE_WEBP_LOSSLESS_OFF:
|
||||
size = WebPEncodeBGRA(image->ptr(), width, height, (int)image->step, quality, &encoder_out);
|
||||
break;
|
||||
case IMWRITE_WEBP_LOSSLESS_ON:
|
||||
size = WebPEncodeLosslessBGRA(image->ptr(), width, height, (int)image->step, &encoder_out);
|
||||
break;
|
||||
case IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR:
|
||||
size = cvEncodeLosslessExactBGRA(image->ptr(), width, height, (int)image->step, &encoder_out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, cv::format("WebP encoding failed with lossless_mode=%d", lossless_mode));
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WEBP_DECODER_ABI_VERSION >= 0x0206
|
||||
Ptr<uint8_t> out_cleaner(encoder_out, WebPFree);
|
||||
#else
|
||||
|
||||
@@ -114,6 +114,154 @@ TEST(Imgcodecs_WebP, encode_decode_with_alpha_webp)
|
||||
EXPECT_EQ(512, img_webp_bgr.rows);
|
||||
}
|
||||
|
||||
|
||||
// See https://github.com/opencv/opencv/issues/28503
|
||||
TEST(Imgcodecs_WebP, encode_decode_LOSSLESS_MODE)
|
||||
{
|
||||
cv::Mat img(cv::Size(64,4), CV_8UC4, cv::Scalar(124,64,67,0) );
|
||||
for(int ix = 0; ix < img.size().width; ix++)
|
||||
{
|
||||
img.at<Vec4b>(0, ix)[3] = 0; // Transpacency pixel
|
||||
img.at<Vec4b>(1, ix)[3] = 1;
|
||||
img.at<Vec4b>(2, ix)[3] = 254;
|
||||
img.at<Vec4b>(3, ix)[3] = 255;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> work;
|
||||
EXPECT_NO_THROW(cv::imencode(".webp", img, work, {IMWRITE_WEBP_LOSSLESS_MODE, IMWRITE_WEBP_LOSSLESS_ON}));
|
||||
cv::Mat img_ON = cv::imdecode(work, IMREAD_UNCHANGED);
|
||||
EXPECT_NO_THROW(cv::imencode(".webp", img, work, {IMWRITE_WEBP_LOSSLESS_MODE, IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR}));
|
||||
cv::Mat img_PRESERVE_COLOR = cv::imdecode(work, IMREAD_UNCHANGED);
|
||||
|
||||
for(int ix = 0; ix < img.size().width; ix++)
|
||||
{
|
||||
EXPECT_EQ(img_ON.at<Vec4b>(0, ix), Vec4b(0, 0, 0, 0)); // LOSSLESS_ON -> COLOR will be optimized/dropped.
|
||||
EXPECT_EQ(img_PRESERVE_COLOR.at<Vec4b>(0, ix), Vec4b(124,64,67,0)); // PRESERVE_COLOR
|
||||
|
||||
EXPECT_EQ(img_ON.at<Vec4b>(1, ix), img_PRESERVE_COLOR.at<Vec4b>(1, ix) );
|
||||
EXPECT_EQ(img_ON.at<Vec4b>(2, ix), img_PRESERVE_COLOR.at<Vec4b>(2, ix) );
|
||||
EXPECT_EQ(img_ON.at<Vec4b>(3, ix), img_PRESERVE_COLOR.at<Vec4b>(3, ix) );
|
||||
}
|
||||
}
|
||||
|
||||
// Expected result categories for WebP encoding tests.
|
||||
enum ImencodeLosslessResult {
|
||||
LOSSY, // Expect lossy compression (pixel differences allowed)
|
||||
LOSSLESS, // Expect standard lossless compression (pixel values match)
|
||||
EXACT // Expect exact lossless (preserving RGB values of transparent pixels)
|
||||
};
|
||||
|
||||
typedef std::tuple<int, int, ImencodeLosslessResult> WebPModePriorityParams;
|
||||
class Imgcodecs_WebP_Mode_Priority : public testing::TestWithParam<WebPModePriorityParams> {};
|
||||
|
||||
TEST_P(Imgcodecs_WebP_Mode_Priority, encode_webp_mode_priority)
|
||||
{
|
||||
const int mode = std::get<0>(GetParam());
|
||||
const int quality = std::get<1>(GetParam());
|
||||
const ImencodeLosslessResult expected = std::get<2>(GetParam());
|
||||
|
||||
// Generate a 100x100 RGBA test image.
|
||||
// Set a transparent pixel with specific color (Blue) to verify EXACT mode.
|
||||
Mat src(100, 100, CV_8UC4, Scalar(255, 255, 255, 255));
|
||||
src.at<Vec4b>(0, 0) = Vec4b(255, 0, 0, 0); // Transparent Blue (B:255, G:0, R:0, A:0)
|
||||
|
||||
// Build the imwrite parameter vector dynamically.
|
||||
std::vector<int> params;
|
||||
if (mode != -1) {
|
||||
params.push_back(IMWRITE_WEBP_LOSSLESS_MODE);
|
||||
params.push_back(mode);
|
||||
}
|
||||
if (quality != -1) {
|
||||
params.push_back(IMWRITE_WEBP_QUALITY);
|
||||
params.push_back(quality);
|
||||
}
|
||||
|
||||
// Encode to memory and decode back.
|
||||
std::vector<uchar> buf;
|
||||
ASSERT_TRUE(imencode(".webp", src, buf, params));
|
||||
Mat dst = imdecode(buf, IMREAD_UNCHANGED);
|
||||
ASSERT_FALSE(dst.empty());
|
||||
|
||||
// Validation logic
|
||||
if (expected == LOSSY) {
|
||||
// We expect some differences in lossy mode
|
||||
double diff = cv::norm(src, dst, NORM_INF);
|
||||
EXPECT_GT(diff, 0) << "Should be lossy (Quality: " << quality << ")";
|
||||
}
|
||||
else if (expected == LOSSLESS) {
|
||||
// Standard lossless: we allow the library to modify RGB values
|
||||
// of fully transparent pixels (A=0) to improve compression ratio.
|
||||
// Thus, we compare only visible pixels or check with a slightly relaxed condition.
|
||||
|
||||
// Option A: If you want to allow RGB changes on A=0:
|
||||
// We can't use cv::norm directly if A=0 pixels are modified.
|
||||
// Let's check if they are identical except for the transparent pixel.
|
||||
Mat diff;
|
||||
absdiff(src, dst, diff);
|
||||
Scalar total_diff = sum(diff);
|
||||
// If only the (0,0) pixel changed from (255,0,0,0) to (0,0,0,0),
|
||||
// total_diff will be 255.
|
||||
EXPECT_LE(total_diff[0] + total_diff[1] + total_diff[2], 255)
|
||||
<< "Standard lossless should not have significant pixel differences";
|
||||
EXPECT_EQ(src.at<Vec4b>(0,0)[3], dst.at<Vec4b>(0,0)[3]) << "Alpha must be preserved";
|
||||
}
|
||||
else if (expected == EXACT) {
|
||||
// Exact lossless: Every single bit must match, including transparent pixels.
|
||||
double diff = cv::norm(src, dst, NORM_INF);
|
||||
EXPECT_EQ(0, diff) << "Exact mode must preserve all pixel values perfectly";
|
||||
EXPECT_EQ(src.at<Vec4b>(0, 0), dst.at<Vec4b>(0, 0))
|
||||
<< "RGB values of transparent pixels must be preserved in EXACT mode";
|
||||
}
|
||||
else {
|
||||
FAIL() << "Unknown expectation type";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate human-readable test names in gtest output.
|
||||
*/
|
||||
static std::string getModeStr(int m) {
|
||||
if (m == -1) return "OMIT";
|
||||
if (m == IMWRITE_WEBP_LOSSLESS_OFF) return "OFF";
|
||||
if (m == IMWRITE_WEBP_LOSSLESS_ON) return "ON";
|
||||
if (m == IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR) return "PRESERVE";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static std::string getExpectStr(ImencodeLosslessResult r) {
|
||||
return (r == LOSSY) ? "LOSSY" : (r == EXACT) ? "EXACT" : "LOSSLESS";
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Imgcodecs, Imgcodecs_WebP_Mode_Priority,
|
||||
testing::Values(
|
||||
// Default (OMIT mode) cases
|
||||
WebPModePriorityParams(-1, -1, LOSSLESS),
|
||||
WebPModePriorityParams(-1, 80, LOSSY),
|
||||
WebPModePriorityParams(-1, 101, LOSSLESS),
|
||||
|
||||
// LOSSLESS_OFF (Explicitly off)
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_OFF, -1, LOSSLESS),
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_OFF, 80, LOSSY),
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_OFF, 101, LOSSLESS),
|
||||
|
||||
// LOSSLESS_ON (Force lossless)
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_ON, -1, LOSSLESS),
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_ON, 80, LOSSLESS),
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_ON, 101, LOSSLESS),
|
||||
|
||||
// PRESERVE_COLOR (Exact lossless)
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR, -1, EXACT),
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR, 80, EXACT),
|
||||
WebPModePriorityParams(IMWRITE_WEBP_LOSSLESS_PRESERVE_COLOR, 101, EXACT)
|
||||
),
|
||||
[](const testing::TestParamInfo<WebPModePriorityParams>& info_) {
|
||||
std::string mode = getModeStr(std::get<0>(info_.param));
|
||||
int q = std::get<1>(info_.param);
|
||||
std::string q_str = (q == -1) ? "omit" : std::to_string(q);
|
||||
return mode + "_q" + q_str + "_" + getExpectStr(std::get<2>(info_.param));
|
||||
}
|
||||
);
|
||||
|
||||
#endif // HAVE_WEBP
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -217,15 +217,15 @@ enum MorphTypes{
|
||||
MORPH_ERODE = 0, //!< see #erode
|
||||
MORPH_DILATE = 1, //!< see #dilate
|
||||
MORPH_OPEN = 2, //!< an opening operation
|
||||
//!< \f[\texttt{dst} = \mathrm{open} ( \texttt{src} , \texttt{element} )= \mathrm{dilate} ( \mathrm{erode} ( \texttt{src} , \texttt{element} ))\f]
|
||||
//!< \f[\texttt{dst} = \mathrm{open} ( \texttt{src} , \texttt{kernel} )= \mathrm{dilate} ( \mathrm{erode} ( \texttt{src} , \texttt{kernel} ))\f]
|
||||
MORPH_CLOSE = 3, //!< a closing operation
|
||||
//!< \f[\texttt{dst} = \mathrm{close} ( \texttt{src} , \texttt{element} )= \mathrm{erode} ( \mathrm{dilate} ( \texttt{src} , \texttt{element} ))\f]
|
||||
//!< \f[\texttt{dst} = \mathrm{close} ( \texttt{src} , \texttt{kernel} )= \mathrm{erode} ( \mathrm{dilate} ( \texttt{src} , \texttt{kernel} ))\f]
|
||||
MORPH_GRADIENT = 4, //!< a morphological gradient
|
||||
//!< \f[\texttt{dst} = \mathrm{morph\_grad} ( \texttt{src} , \texttt{element} )= \mathrm{dilate} ( \texttt{src} , \texttt{element} )- \mathrm{erode} ( \texttt{src} , \texttt{element} )\f]
|
||||
//!< \f[\texttt{dst} = \mathrm{morph\_grad} ( \texttt{src} , \texttt{kernel} )= \mathrm{dilate} ( \texttt{src} , \texttt{kernel} )- \mathrm{erode} ( \texttt{src} , \texttt{kernel} )\f]
|
||||
MORPH_TOPHAT = 5, //!< "top hat"
|
||||
//!< \f[\texttt{dst} = \mathrm{tophat} ( \texttt{src} , \texttt{element} )= \texttt{src} - \mathrm{open} ( \texttt{src} , \texttt{element} )\f]
|
||||
//!< \f[\texttt{dst} = \mathrm{tophat} ( \texttt{src} , \texttt{kernel} )= \texttt{src} - \mathrm{open} ( \texttt{src} , \texttt{kernel} )\f]
|
||||
MORPH_BLACKHAT = 6, //!< "black hat"
|
||||
//!< \f[\texttt{dst} = \mathrm{blackhat} ( \texttt{src} , \texttt{element} )= \mathrm{close} ( \texttt{src} , \texttt{element} )- \texttt{src}\f]
|
||||
//!< \f[\texttt{dst} = \mathrm{blackhat} ( \texttt{src} , \texttt{kernel} )= \mathrm{close} ( \texttt{src} , \texttt{kernel} )- \texttt{src}\f]
|
||||
MORPH_HITMISS = 7 //!< "hit or miss"
|
||||
//!< .- Only supported for CV_8UC1 binary images. A tutorial can be found in the documentation
|
||||
};
|
||||
@@ -2356,7 +2356,7 @@ Check @ref tutorial_opening_closing_hats "the corresponding tutorial" for more d
|
||||
The function erodes the source image using the specified structuring element that determines the
|
||||
shape of a pixel neighborhood over which the minimum is taken:
|
||||
|
||||
\f[\texttt{dst} (x,y) = \min _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
|
||||
\f[\texttt{dst} (x,y) = \min _{(x',y'): \, \texttt{kernel} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
|
||||
|
||||
The function supports the in-place mode. Erosion can be applied several ( iterations ) times. In
|
||||
case of multi-channel images, each channel is processed independently.
|
||||
@@ -2364,7 +2364,7 @@ case of multi-channel images, each channel is processed independently.
|
||||
@param src input image; the number of channels can be arbitrary, but the depth should be one of
|
||||
CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
|
||||
@param dst output image of the same size and type as src.
|
||||
@param kernel structuring element used for erosion; if `element=Mat()`, a `3 x 3` rectangular
|
||||
@param kernel structuring element used for erosion; if `kernel=Mat()`, a `3 x 3` rectangular
|
||||
structuring element is used. Kernel can be created using #getStructuringElement.
|
||||
@param anchor position of the anchor within the element; default value (-1, -1) means that the
|
||||
anchor is at the element center.
|
||||
@@ -2388,7 +2388,7 @@ Check @ref tutorial_erosion_dilatation "the corresponding tutorial" for more det
|
||||
|
||||
The function dilates the source image using the specified structuring element that determines the
|
||||
shape of a pixel neighborhood over which the maximum is taken:
|
||||
\f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
|
||||
\f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{kernel} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
|
||||
|
||||
The function supports the in-place mode. Dilation can be applied several ( iterations ) times. In
|
||||
case of multi-channel images, each channel is processed independently.
|
||||
@@ -2396,7 +2396,7 @@ case of multi-channel images, each channel is processed independently.
|
||||
@param src input image; the number of channels can be arbitrary, but the depth should be one of
|
||||
CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
|
||||
@param dst output image of the same size and type as src.
|
||||
@param kernel structuring element used for dilation; if element=Mat(), a 3 x 3 rectangular
|
||||
@param kernel structuring element used for dilation; if `kernel=Mat()`, a `3 x 3` rectangular
|
||||
structuring element is used. Kernel can be created using #getStructuringElement
|
||||
@param anchor position of the anchor within the element; default value (-1, -1) means that the
|
||||
anchor is at the element center.
|
||||
@@ -3826,7 +3826,7 @@ CV_EXPORTS_W void demosaicing(InputArray src, OutputArray dst, int code, int dst
|
||||
The function computes moments, up to the 3rd order, of a vector shape or a rasterized shape. The
|
||||
results are returned in the structure cv::Moments.
|
||||
|
||||
@param array Single chanel raster image (CV_8U, CV_16U, CV_16S, CV_32F, CV_64F) or an array (
|
||||
@param array Single channel raster image (CV_8U, CV_16U, CV_16S, CV_32F, CV_64F) or an array (
|
||||
\f$1 \times N\f$ or \f$N \times 1\f$ ) of 2D points (Point or Point2f).
|
||||
@param binaryImage If it is true, all non-zero image pixels are treated as 1's. The parameter is
|
||||
used for images only.
|
||||
|
||||
@@ -106,4 +106,39 @@ PERF_TEST_P(TestBoundingRect, BoundingRect,
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
typedef TestBaseWithParam< tuple<MatDepth, int> > TestMinEnclosingCircle;
|
||||
PERF_TEST_P(TestMinEnclosingCircle, minEnclosingCircle,
|
||||
Combine(
|
||||
testing::Values(CV_32S, CV_32F),
|
||||
Values(400, 1000, 10000, 100000)
|
||||
))
|
||||
{
|
||||
int ptType = get<0>(GetParam());
|
||||
int n = get<1>(GetParam());
|
||||
Mat pts(n, 2, ptType);
|
||||
declare.in(pts, WARMUP_RNG);
|
||||
|
||||
Point2f center;
|
||||
float radius;
|
||||
TEST_CYCLE() minEnclosingCircle(pts, center, radius);
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
typedef TestBaseWithParam<int> TestMinEnclosingCircleWorstCase;
|
||||
PERF_TEST_P(TestMinEnclosingCircleWorstCase, minEnclosingCircle_sequential,
|
||||
Values(400, 1000, 5000, 10000))
|
||||
{
|
||||
int n = GetParam();
|
||||
vector<Point2f> contour;
|
||||
for(int i = 0; i < n; ++i) {
|
||||
float angle = (float)(i * 2 * CV_PI / n);
|
||||
contour.push_back(Point2f(cos(angle) * 100, sin(angle) * 100));
|
||||
}
|
||||
|
||||
Point2f center;
|
||||
float radius;
|
||||
TEST_CYCLE() minEnclosingCircle(contour, center, radius);
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} } // namespace
|
||||
|
||||
@@ -70,7 +70,7 @@ static void
|
||||
distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* metrics )
|
||||
{
|
||||
const int BORDER = 1;
|
||||
int i, j;
|
||||
|
||||
const unsigned int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT );
|
||||
const unsigned int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT );
|
||||
const unsigned int DIST_MAX = UINT_MAX - DIAG_DIST;
|
||||
@@ -79,65 +79,190 @@ distanceTransform_3x3( const Mat& _src, Mat& _temp, Mat& _dist, const float* met
|
||||
const uchar* src = _src.ptr();
|
||||
int* temp = _temp.ptr<int>();
|
||||
float* dist = _dist.ptr<float>(_dist.rows - 1);
|
||||
int srcstep = (int)(_src.step/sizeof(src[0]));
|
||||
int step = (int)(_temp.step/sizeof(temp[0]));
|
||||
int dststep = (int)(_dist.step/sizeof(dist[0]));
|
||||
Size size = _src.size();
|
||||
|
||||
const int srcstep = (int)_src.step;
|
||||
const int step = (int)(_temp.step / sizeof(int));
|
||||
const int dststep = (int)(_dist.step / sizeof(float));
|
||||
|
||||
const int width = _src.cols;
|
||||
const int height = _src.rows;
|
||||
|
||||
initTopBottom( _temp, BORDER, DIST_MAX );
|
||||
|
||||
// forward pass
|
||||
unsigned int* tmp = (unsigned int*)(temp + BORDER*step) + BORDER;
|
||||
const uchar* s = src;
|
||||
for( i = 0; i < size.height; i++ )
|
||||
{
|
||||
for( j = 0; j < BORDER; j++ )
|
||||
tmp[-j-1] = tmp[size.width + j] = DIST_MAX;
|
||||
unsigned int* row = (unsigned int*)(temp + BORDER * step) + BORDER;
|
||||
const uchar* srow = src;
|
||||
|
||||
for( j = 0; j < size.width; j++ )
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
unsigned int* cur = row;
|
||||
const uchar* s = srow;
|
||||
const unsigned int* top = (const unsigned int*)(cur - step);
|
||||
|
||||
// set horizontal border sentinels
|
||||
cur[-1] = DIST_MAX;
|
||||
cur[width] = DIST_MAX;
|
||||
|
||||
unsigned int left = cur[-1];
|
||||
|
||||
#if defined(CV_SIMD)
|
||||
const int BLOCK = 8;
|
||||
int j = 0;
|
||||
const v_uint32 v_diag = vx_setall<v_uint32>(DIAG_DIST);
|
||||
const v_uint32 v_hv = vx_setall<v_uint32>(HV_DIST);
|
||||
|
||||
for (; j <= width - BLOCK; j += BLOCK)
|
||||
{
|
||||
if( !s[j] )
|
||||
tmp[j] = 0;
|
||||
else
|
||||
v_uint32 tl0 = vx_load(top + j - 1);
|
||||
v_uint32 t0 = vx_load(top + j);
|
||||
v_uint32 tr0 = vx_load(top + j + 1);
|
||||
v_uint32 tl1 = vx_load(top + j + 3);
|
||||
v_uint32 t1 = vx_load(top + j + 4);
|
||||
v_uint32 tr1 = vx_load(top + j + 5);
|
||||
tl0 = v_add(tl0, v_diag);
|
||||
t0 = v_add(t0, v_hv);
|
||||
tr0 = v_add(tr0, v_diag);
|
||||
tl1 = v_add(tl1, v_diag);
|
||||
t1 = v_add(t1, v_hv);
|
||||
tr1 = v_add(tr1, v_diag);
|
||||
v_uint32 m0 = v_min(tl0, t0);
|
||||
m0 = v_min(m0, tr0);
|
||||
v_uint32 m1 = v_min(tl1, t1);
|
||||
m1 = v_min(m1, tr1);
|
||||
v_store(cur + j, m0);
|
||||
v_store(cur + j + 4, m1);
|
||||
|
||||
for (int k = 0; k < BLOCK; k++)
|
||||
{
|
||||
unsigned int t0 = tmp[j-step-1] + DIAG_DIST;
|
||||
unsigned int t = tmp[j-step] + HV_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j-step+1] + DIAG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j-1] + HV_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
tmp[j] = (t0 > DIST_MAX) ? DIST_MAX : t0;
|
||||
int idx = j + k;
|
||||
if (!s[idx])
|
||||
{
|
||||
cur[idx] = 0;
|
||||
left = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int m = cur[idx];
|
||||
unsigned int lm = left + HV_DIST;
|
||||
if (lm < m) m = lm;
|
||||
if (m > DIST_MAX) m = DIST_MAX;
|
||||
cur[idx] = m;
|
||||
left = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp += step;
|
||||
s += srcstep;
|
||||
|
||||
for (; j < width; j++)
|
||||
{
|
||||
if (!s[j])
|
||||
{
|
||||
cur[j] = 0;
|
||||
left = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int t0 = top[j - 1] + DIAG_DIST;
|
||||
unsigned int t1 = top[j] + HV_DIST;
|
||||
unsigned int t2 = top[j + 1] + DIAG_DIST;
|
||||
unsigned int t3 = left + HV_DIST;
|
||||
|
||||
unsigned int m = t0 < t1 ? t0 : t1;
|
||||
m = m < t2 ? m : t2;
|
||||
m = m < t3 ? m : t3;
|
||||
|
||||
cur[j] = m > DIST_MAX ? DIST_MAX : m;
|
||||
left = cur[j];
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
if (!s[j])
|
||||
{
|
||||
cur[j] = 0;
|
||||
left = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int t0 = top[j - 1] + DIAG_DIST;
|
||||
unsigned int t1 = top[j] + HV_DIST;
|
||||
unsigned int t2 = top[j + 1] + DIAG_DIST;
|
||||
unsigned int t3 = left + HV_DIST;
|
||||
|
||||
unsigned int m = t0 < t1 ? t0 : t1;
|
||||
m = m < t2 ? m : t2;
|
||||
m = m < t3 ? m : t3;
|
||||
|
||||
cur[j] = m > DIST_MAX ? DIST_MAX : m;
|
||||
left = cur[j];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
row += step;
|
||||
srow += srcstep;
|
||||
}
|
||||
|
||||
// backward pass
|
||||
float* d = (float*)dist;
|
||||
for( i = size.height - 1; i >= 0; i-- )
|
||||
float* drow = dist;
|
||||
unsigned int* cur = row - step;
|
||||
#if defined(CV_SIMD)
|
||||
const v_float32 scale_v = vx_setall<v_float32>(scale);
|
||||
#endif
|
||||
for (int i = height - 1; i >= 0; i--)
|
||||
{
|
||||
tmp -= step;
|
||||
unsigned int* bottom = cur + step;
|
||||
|
||||
for( j = size.width - 1; j >= 0; j-- )
|
||||
unsigned int right = cur[width];
|
||||
for (int j = width - 1; j >= 0; j--)
|
||||
{
|
||||
unsigned int t0 = tmp[j];
|
||||
if( t0 > HV_DIST )
|
||||
unsigned int t0 = cur[j];
|
||||
|
||||
if (t0 > HV_DIST)
|
||||
{
|
||||
unsigned int t = tmp[j+step+1] + DIAG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+step] + HV_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+step-1] + DIAG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+1] + HV_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
tmp[j] = t0;
|
||||
unsigned int b1 = bottom[j + 1] + DIAG_DIST;
|
||||
unsigned int b2 = bottom[j] + HV_DIST;
|
||||
unsigned int b3 = bottom[j - 1] + DIAG_DIST;
|
||||
unsigned int r = right + HV_DIST;
|
||||
|
||||
unsigned int t = b1 < b2 ? b1 : b2;
|
||||
t = t < b3 ? t : b3;
|
||||
t = t < r ? t : r;
|
||||
|
||||
if (t < t0)
|
||||
{
|
||||
t0 = t;
|
||||
cur[j] = t0;
|
||||
}
|
||||
}
|
||||
d[j] = (float)(t0 * scale);
|
||||
|
||||
right = cur[j];
|
||||
}
|
||||
d -= dststep;
|
||||
|
||||
#if defined(CV_SIMD)
|
||||
int j = 0;
|
||||
const uint32_t* tptr = (const uint32_t*)cur;
|
||||
float* dptr = drow;
|
||||
|
||||
for (; j <= width - 8; j += 8)
|
||||
{
|
||||
v_uint32 v0 = vx_load(tptr + j);
|
||||
v_uint32 v1 = vx_load(tptr + j + 4);
|
||||
v_float32 f0 = v_mul(v_cvt_f32(v0), scale_v);
|
||||
v_float32 f1 = v_mul(v_cvt_f32(v1), scale_v);
|
||||
v_store(dptr + j, f0);
|
||||
v_store(dptr + j + 4, f1);
|
||||
}
|
||||
|
||||
for (; j < width; j++)
|
||||
dptr[j] = (float)(tptr[j] * scale);
|
||||
#else
|
||||
for (int j = 0; j < width; j++)
|
||||
drow[j] = (float)(cur[j] * scale);
|
||||
#endif
|
||||
|
||||
cur -= step;
|
||||
drow -= dststep;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,91 +271,248 @@ static void
|
||||
distanceTransform_5x5( const Mat& _src, Mat& _temp, Mat& _dist, const float* metrics )
|
||||
{
|
||||
const int BORDER = 2;
|
||||
int i, j;
|
||||
|
||||
const unsigned int HV_DIST = CV_FLT_TO_FIX( metrics[0], DIST_SHIFT );
|
||||
const unsigned int DIAG_DIST = CV_FLT_TO_FIX( metrics[1], DIST_SHIFT );
|
||||
const unsigned int LONG_DIST = CV_FLT_TO_FIX( metrics[2], DIST_SHIFT );
|
||||
const unsigned int DIST_MAX = UINT_MAX - LONG_DIST;
|
||||
const float scale = 1.f/(1 << DIST_SHIFT);
|
||||
|
||||
const uchar* src = _src.ptr();
|
||||
const uchar* src = _src.ptr<uchar>();
|
||||
int* temp = _temp.ptr<int>();
|
||||
float* dist = _dist.ptr<float>(_dist.rows - 1);
|
||||
int srcstep = (int)(_src.step/sizeof(src[0]));
|
||||
int step = (int)(_temp.step/sizeof(temp[0]));
|
||||
int dststep = (int)(_dist.step/sizeof(dist[0]));
|
||||
Size size = _src.size();
|
||||
|
||||
initTopBottom( _temp, BORDER, DIST_MAX );
|
||||
const int srcstep = (int)_src.step;
|
||||
const int step = (int)(_temp.step / sizeof(int));
|
||||
const int dststep = (int)(_dist.step / sizeof(float));
|
||||
|
||||
const int width = _src.cols;
|
||||
const int height = _src.rows;
|
||||
|
||||
initTopBottom(_temp, BORDER, DIST_MAX);
|
||||
|
||||
// forward pass
|
||||
unsigned int* tmp = (unsigned int*)(temp + BORDER*step) + BORDER;
|
||||
const uchar* s = src;
|
||||
for( i = 0; i < size.height; i++ )
|
||||
{
|
||||
for( j = 0; j < BORDER; j++ )
|
||||
tmp[-j-1] = tmp[size.width + j] = DIST_MAX;
|
||||
unsigned int* row = (unsigned int*)(temp + BORDER * step) + BORDER;
|
||||
const uchar* srow = src;
|
||||
|
||||
for( j = 0; j < size.width; j++ )
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
unsigned int* cur = row;
|
||||
const uchar* s = srow;
|
||||
const unsigned int* top1 = cur - step;
|
||||
const unsigned int* top2 = cur - step * 2;
|
||||
cur[-1] = cur[-2] = DIST_MAX;
|
||||
cur[width] = cur[width + 1] = DIST_MAX;
|
||||
#if defined(CV_SIMD)
|
||||
unsigned int left1 = DIST_MAX;
|
||||
unsigned int left2 = DIST_MAX;
|
||||
const int BLOCK = 8;
|
||||
int j = 0;
|
||||
const v_uint32 v_hv = vx_setall<v_uint32>(HV_DIST);
|
||||
const v_uint32 v_diag = vx_setall<v_uint32>(DIAG_DIST);
|
||||
const v_uint32 v_long = vx_setall<v_uint32>(LONG_DIST);
|
||||
const v_uint32 v_max = vx_setall<v_uint32>(DIST_MAX);
|
||||
|
||||
for (; j <= width - BLOCK; j += BLOCK)
|
||||
{
|
||||
if( !s[j] )
|
||||
tmp[j] = 0;
|
||||
else
|
||||
v_uint32 t2m1_0 = vx_load(top2 + j - 1);
|
||||
v_uint32 t2p1_0 = vx_load(top2 + j + 1);
|
||||
v_uint32 t1m2_0 = vx_load(top1 + j - 2);
|
||||
v_uint32 t1m1_0 = vx_load(top1 + j - 1);
|
||||
v_uint32 t1_0 = vx_load(top1 + j);
|
||||
v_uint32 t1p1_0 = vx_load(top1 + j + 1);
|
||||
v_uint32 t1p2_0 = vx_load(top1 + j + 2);
|
||||
|
||||
t2m1_0 = v_add(t2m1_0, v_long);
|
||||
t2p1_0 = v_add(t2p1_0, v_long);
|
||||
t1m2_0 = v_add(t1m2_0, v_long);
|
||||
t1m1_0 = v_add(t1m1_0, v_diag);
|
||||
t1_0 = v_add(t1_0, v_hv);
|
||||
t1p1_0 = v_add(t1p1_0, v_diag);
|
||||
t1p2_0 = v_add(t1p2_0, v_long);
|
||||
|
||||
v_uint32 m0 = v_min(t2m1_0, t2p1_0);
|
||||
v_uint32 m0_1 = v_min(t1m2_0, t1m1_0);
|
||||
v_uint32 m0_2 = v_min(t1p1_0, t1p2_0);
|
||||
m0 = v_min(m0, m0_1);
|
||||
m0_2 = v_min(m0_2, t1_0);
|
||||
m0 = v_min(m0, m0_2);
|
||||
m0 = v_min(m0, v_max);
|
||||
|
||||
v_store(cur + j, m0);
|
||||
|
||||
v_uint32 t2m1_1 = vx_load(top2 + j + 3);
|
||||
v_uint32 t2p1_1 = vx_load(top2 + j + 5);
|
||||
v_uint32 t1m2_1 = vx_load(top1 + j + 2);
|
||||
v_uint32 t1m1_1 = vx_load(top1 + j + 3);
|
||||
v_uint32 t1_1 = vx_load(top1 + j + 4);
|
||||
v_uint32 t1p1_1 = vx_load(top1 + j + 5);
|
||||
v_uint32 t1p2_1 = vx_load(top1 + j + 6);
|
||||
|
||||
t2m1_1 = v_add(t2m1_1, v_long);
|
||||
t2p1_1 = v_add(t2p1_1, v_long);
|
||||
t1m2_1 = v_add(t1m2_1, v_long);
|
||||
t1m1_1 = v_add(t1m1_1, v_diag);
|
||||
t1_1 = v_add(t1_1, v_hv);
|
||||
t1p1_1 = v_add(t1p1_1, v_diag);
|
||||
t1p2_1 = v_add(t1p2_1, v_long);
|
||||
|
||||
v_uint32 m1 = v_min(t2m1_1, t2p1_1);
|
||||
v_uint32 m1_1 = v_min(t1m2_1, t1m1_1);
|
||||
v_uint32 m1_2 = v_min(t1p1_1, t1p2_1);
|
||||
m1 = v_min(m1, m1_1);
|
||||
m1_2 = v_min(m1_2, t1_1);
|
||||
m1 = v_min(m1, m1_2);
|
||||
m1 = v_min(m1, v_max);
|
||||
|
||||
v_store(cur + j + 4, m1);
|
||||
|
||||
for (int k = 0; k < BLOCK; k++)
|
||||
{
|
||||
unsigned int t0 = tmp[j-step*2-1] + LONG_DIST;
|
||||
unsigned int t = tmp[j-step*2+1] + LONG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j-step-2] + LONG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j-step-1] + DIAG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j-step] + HV_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j-step+1] + DIAG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j-step+2] + LONG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j-1] + HV_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
tmp[j] = (t0 > DIST_MAX) ? DIST_MAX : t0;
|
||||
int idx = j + k;
|
||||
if (!s[idx])
|
||||
{
|
||||
cur[idx] = 0;
|
||||
left1 = 0;
|
||||
left2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int m = cur[idx];
|
||||
unsigned int lm = left1 + HV_DIST;
|
||||
if (lm < m) m = lm;
|
||||
if (m > DIST_MAX) m = DIST_MAX;
|
||||
cur[idx] = m;
|
||||
left2 = left1;
|
||||
left1 = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp += step;
|
||||
s += srcstep;
|
||||
|
||||
for (; j < width; j++)
|
||||
{
|
||||
if (!s[j])
|
||||
{
|
||||
cur[j] = 0;
|
||||
left1 = left2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int t0 = top2[j - 1] + LONG_DIST;
|
||||
unsigned int t = top2[j + 1] + LONG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = top1[j - 2] + LONG_DIST; if (t < t0) t0 = t;
|
||||
t = top1[j - 1] + DIAG_DIST; if (t < t0) t0 = t;
|
||||
t = top1[j] + HV_DIST; if (t < t0) t0 = t;
|
||||
t = top1[j + 1] + DIAG_DIST; if (t < t0) t0 = t;
|
||||
t = top1[j + 2] + LONG_DIST; if (t < t0) t0 = t;
|
||||
t = left1 + HV_DIST; if (t < t0) t0 = t;
|
||||
|
||||
cur[j] = t0 > DIST_MAX ? DIST_MAX : t0;
|
||||
left2 = left1;
|
||||
left1 = cur[j];
|
||||
}
|
||||
}
|
||||
#else
|
||||
unsigned int left = DIST_MAX;
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
if (!s[j])
|
||||
{
|
||||
cur[j] = 0;
|
||||
left = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int t0 = top2[j - 1] + LONG_DIST;
|
||||
unsigned int t = top2[j + 1] + LONG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = top1[j - 2] + LONG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = top1[j - 1] + DIAG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = top1[j] + HV_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = top1[j + 1] + DIAG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = top1[j + 2] + LONG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = left + HV_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
cur[j] = t0 > DIST_MAX ? DIST_MAX : t0;
|
||||
left = cur[j];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
row += step;
|
||||
srow += srcstep;
|
||||
}
|
||||
|
||||
// backward pass
|
||||
float* d = (float*)dist;
|
||||
for( i = size.height - 1; i >= 0; i-- )
|
||||
{
|
||||
tmp -= step;
|
||||
float* drow = dist;
|
||||
unsigned int* cur = row - step;
|
||||
|
||||
for( j = size.width - 1; j >= 0; j-- )
|
||||
#if defined(CV_SIMD)
|
||||
const v_float32 scale_v = vx_setall<v_float32>(scale);
|
||||
#endif
|
||||
|
||||
for (int i = height - 1; i >= 0; i--)
|
||||
{
|
||||
unsigned int* bot1 = cur + step;
|
||||
unsigned int* bot2 = cur + step * 2;
|
||||
unsigned int right = DIST_MAX;
|
||||
|
||||
for (int j = width - 1; j >= 0; j--)
|
||||
{
|
||||
unsigned int t0 = tmp[j];
|
||||
unsigned int t0 = cur[j];
|
||||
if( t0 > HV_DIST )
|
||||
{
|
||||
unsigned int t = tmp[j+step*2+1] + LONG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+step*2-1] + LONG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+step+2] + LONG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+step+1] + DIAG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+step] + HV_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+step-1] + DIAG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+step-2] + LONG_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
t = tmp[j+1] + HV_DIST;
|
||||
if( t0 > t ) t0 = t;
|
||||
tmp[j] = t0;
|
||||
unsigned int t = bot2[j + 1] + LONG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = bot2[j - 1] + LONG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = bot1[j + 2] + LONG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = bot1[j + 1] + DIAG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = bot1[j] + HV_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = bot1[j - 1] + DIAG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = bot1[j - 2] + LONG_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
t = right + HV_DIST;
|
||||
if (t < t0) t0 = t;
|
||||
|
||||
cur[j] = t0;
|
||||
}
|
||||
d[j] = (float)(t0 * scale);
|
||||
right = cur[j];
|
||||
}
|
||||
d -= dststep;
|
||||
|
||||
#if defined(CV_SIMD)
|
||||
int j = 0;
|
||||
const uint32_t* tptr = (const uint32_t*)cur;
|
||||
float* dptr = drow;
|
||||
|
||||
for (; j <= width - 8; j += 8)
|
||||
{
|
||||
v_uint32 v0 = vx_load(tptr + j);
|
||||
v_uint32 v1 = vx_load(tptr + j + 4);
|
||||
v_float32 f0 = v_mul(v_cvt_f32(v0), scale_v);
|
||||
v_float32 f1 = v_mul(v_cvt_f32(v1), scale_v);
|
||||
v_store(dptr + j, f0);
|
||||
v_store(dptr + j + 4, f1);
|
||||
}
|
||||
|
||||
for (; j < width; j++)
|
||||
dptr[j] = (float)(tptr[j] * scale);
|
||||
#else
|
||||
for (int j = 0; j < width; j++)
|
||||
drow[j] = (float)(cur[j] * scale);
|
||||
#endif
|
||||
|
||||
cur -= step;
|
||||
drow -= dststep;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -913,6 +913,7 @@ std::vector<Side> Chains::reconstructHSidedChain(int h, int i, int j)
|
||||
std::vector<Side> Chains::findKSides(int k, int i, int j)
|
||||
{
|
||||
std::vector<Side> sides;
|
||||
sides.reserve(k); // Optimization and avoiding GCC's false positive warning (-Wstringop-overflow)
|
||||
sides.push_back({i, true});
|
||||
sides.push_back({j, true});
|
||||
|
||||
|
||||
@@ -130,8 +130,31 @@ void findSecondPoint(const PT *pts, int i, Point2f ¢er, float &radius)
|
||||
|
||||
|
||||
template<typename PT>
|
||||
static void findMinEnclosingCircle(const PT *pts, int count, Point2f ¢er, float &radius)
|
||||
static void findMinEnclosingCircle(const PT *pts_in, int count, Point2f ¢er, float &radius)
|
||||
{
|
||||
// Welzl's algorithm requires random permutation for expected O(n) time.
|
||||
// Without shuffling, sorted inputs trigger O(n^3) worst case.
|
||||
cv::AutoBuffer<PT, 1024> pts_buf(count);
|
||||
std::copy(pts_in, pts_in + count, pts_buf.data());
|
||||
PT* pts = pts_buf.data();
|
||||
if (count > 10)
|
||||
{
|
||||
Cv32suf x0, y0, xn, yn;
|
||||
x0.f = (float)pts[0].x;
|
||||
y0.f = (float)pts[0].y;
|
||||
xn.f = (float)pts[count-1].x;
|
||||
yn.f = (float)pts[count-1].y;
|
||||
|
||||
uint32_t seed = (uint32_t)count ^ x0.u ^ (y0.u << 8) ^ (xn.u << 16) ^ (yn.u << 24);
|
||||
cv::RNG rng(seed);
|
||||
|
||||
for (int i = 1; i < count; ++i)
|
||||
{
|
||||
int j = rng.uniform(0, i + 1);
|
||||
std::swap(pts[i], pts[j]);
|
||||
}
|
||||
}
|
||||
|
||||
center.x = (float)(pts[0].x + pts[1].x) / 2.0f;
|
||||
center.y = (float)(pts[0].y + pts[1].y) / 2.0f;
|
||||
float dx = (float)(pts[0].x - pts[1].x);
|
||||
@@ -750,7 +773,7 @@ cv::RotatedRect cv::fitEllipseDirect( InputArray _points )
|
||||
M(2,1) = (DM(0,1) + (DM(0,3)*TM(0,1) + DM(0,4)*TM(1,1) + DM(0,5)*TM(2,1))/Ts)/2.;
|
||||
M(2,2) = (DM(0,2) + (DM(0,3)*TM(0,2) + DM(0,4)*TM(1,2) + DM(0,5)*TM(2,2))/Ts)/2.;
|
||||
|
||||
double det = fabs(cv::determinant(M));
|
||||
double det = cv::determinant(M);
|
||||
if (fabs(det) > 1.0e-10)
|
||||
break;
|
||||
eps = (float)(s/(n*2)*1e-2);
|
||||
|
||||
@@ -620,6 +620,8 @@ void inline smooth3N121Impl(const ET* src, int cn, ET *dst, int ito, int idst,
|
||||
int len = (width - offset) * cn;
|
||||
int x = offset * cn;
|
||||
int maxRow = min((ito - vOffset),height-vOffset);
|
||||
if (v >= maxRow) return;
|
||||
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
VFT v_8 = vx_setall((WET)8);
|
||||
const int VECSZ = VTraits<VET>::vlanes();
|
||||
@@ -713,6 +715,7 @@ void inline smooth5N14641Impl(const ET* src, int cn, ET* dst, int ito, int idst,
|
||||
int len = (width - offset) * cn;
|
||||
int x = offset * cn;
|
||||
int maxRow = min((ito - vOffset),height-vOffset);
|
||||
if (v >= maxRow) return;
|
||||
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
VFT v_6 = vx_setall((WET)6);
|
||||
|
||||
@@ -996,6 +996,33 @@ TEST(Imgproc_Warp, regression_19566) // valgrind should detect problem if any
|
||||
}
|
||||
|
||||
|
||||
TEST(Imgproc_Warp, regression_28554)
|
||||
{
|
||||
const Size inSize(128, 128);
|
||||
const Size outSize(256, 256);
|
||||
|
||||
Mat inMat = Mat::ones(inSize, CV_16S);
|
||||
Mat outMat = Mat(outSize, CV_16S);
|
||||
Mat coeffs = Mat::eye(2, 3, CV_64F);
|
||||
coeffs.at<double>(0, 2) = 64.;
|
||||
coeffs.at<double>(1, 2) = 64.;
|
||||
|
||||
warpAffine(
|
||||
inMat,
|
||||
outMat,
|
||||
coeffs,
|
||||
outSize,
|
||||
INTER_NEAREST,
|
||||
cv::BORDER_CONSTANT,
|
||||
0.0
|
||||
);
|
||||
|
||||
Mat reference = Mat::zeros(outSize, CV_16S);
|
||||
reference(cv::Rect(64, 64, 128, 128)) = 1;
|
||||
ASSERT_EQ(0.0, cvtest::norm(reference, outMat, NORM_INF));
|
||||
}
|
||||
|
||||
|
||||
TEST(Imgproc_GetAffineTransform, singularity)
|
||||
{
|
||||
Point2f A_sample[3];
|
||||
|
||||
@@ -20,6 +20,8 @@ enum CornerRefineMethod{
|
||||
CORNER_REFINE_APRILTAG, ///< Tag and corners detection based on the AprilTag 2 approach @cite wang2016iros
|
||||
};
|
||||
|
||||
static constexpr float DEFAULT_VALID_BIT_ID_THRESHOLD{0.49f};
|
||||
|
||||
/** @brief struct DetectorParameters is used by ArucoDetector
|
||||
*/
|
||||
struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
||||
@@ -49,7 +51,7 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
||||
aprilTagQuadSigma = 0.0;
|
||||
aprilTagMinClusterPixels = 5;
|
||||
aprilTagMaxNmaxima = 10;
|
||||
aprilTagCriticalRad = (float)(10* CV_PI /180);
|
||||
aprilTagCriticalRad = (float)(10 * CV_PI / 180);
|
||||
aprilTagMaxLineFitMse = 10.0;
|
||||
aprilTagMinWhiteBlackDiff = 5;
|
||||
aprilTagDeglitch = 0;
|
||||
@@ -57,6 +59,7 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
||||
useAruco3Detection = false;
|
||||
minSideLengthCanonicalImg = 32;
|
||||
minMarkerLengthRatioOriginalImg = 0.0;
|
||||
validBitIdThreshold = DEFAULT_VALID_BIT_ID_THRESHOLD;
|
||||
}
|
||||
|
||||
/** @brief Read a new set of DetectorParameters from FileNode (use FileStorage.root()).
|
||||
@@ -168,12 +171,12 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
||||
*/
|
||||
CV_PROP_RW double maxErroneousBitsInBorderRate;
|
||||
|
||||
/** @brief minimun standard deviation in pixels values during the decodification step to apply Otsu
|
||||
/** @brief minimum standard deviation in pixels values during the decodification step to apply Otsu
|
||||
* thresholding (otherwise, all the bits are set to 0 or 1 depending on mean higher than 128 or not) (default 5.0)
|
||||
*/
|
||||
CV_PROP_RW double minOtsuStdDev;
|
||||
|
||||
/// error correction rate respect to the maximun error correction capability for each dictionary (default 0.6).
|
||||
/// error correction rate respect to the maximum error correction capability for each dictionary (default 0.6).
|
||||
CV_PROP_RW double errorCorrectionRate;
|
||||
|
||||
/** @brief April :: User-configurable parameters.
|
||||
@@ -231,6 +234,9 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
||||
|
||||
/// range [0,1], eq (2) from paper. The parameter tau_i has a direct influence on the processing speed.
|
||||
CV_PROP_RW float minMarkerLengthRatioOriginalImg;
|
||||
|
||||
/// range [0,1], define the acceptable threshold when comparing the detected marker to the dictionary during marker identification.
|
||||
CV_PROP_RW float validBitIdThreshold;
|
||||
};
|
||||
|
||||
/** @brief struct RefineParameters is used by ArucoDetector
|
||||
|
||||
@@ -65,6 +65,12 @@ class CV_EXPORTS_W_SIMPLE Dictionary {
|
||||
*/
|
||||
CV_WRAP bool identify(const Mat &onlyBits, CV_OUT int &idx, CV_OUT int &rotation, double maxCorrectionRate) const;
|
||||
|
||||
/** @brief Given a matrix of pixel ratio raging from 0 to 1. Returns whether if marker is identified or not.
|
||||
*
|
||||
* Returns reference to the marker id in the dictionary (if any) and its rotation.
|
||||
*/
|
||||
CV_WRAP bool identify(const Mat &onlyCellPixelRatio, CV_OUT int &idx, CV_OUT int &rotation, double maxCorrectionRate, float validBitIdThreshold) const;
|
||||
|
||||
/** @brief Returns Hamming distance of the input bits to the specific id.
|
||||
*
|
||||
* If `allRotations` flag is set, the four possible marker rotations are considered
|
||||
@@ -84,6 +90,10 @@ class CV_EXPORTS_W_SIMPLE Dictionary {
|
||||
/** @brief Transform list of bytes to matrix of bits
|
||||
*/
|
||||
CV_WRAP static Mat getBitsFromByteList(const Mat &byteList, int markerSize, int rotationId = 0);
|
||||
|
||||
/** @brief Get ground truth bits float
|
||||
*/
|
||||
CV_WRAP Mat getMarkerBits(int markerId, int rotationId = 0) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -323,6 +323,7 @@ class aruco_objdetect_test(NewOpenCVTests):
|
||||
imgSize = (500, 500)
|
||||
params = cv.aruco.DetectorParameters()
|
||||
params.minDistanceToBorder = 3
|
||||
params.validBitIdThreshold = 0.5
|
||||
|
||||
board = cv.aruco.CharucoBoard((4, 4), 0.03, 0.015, cv.aruco.getPredefinedDictionary(cv.aruco.DICT_6X6_250))
|
||||
detector = cv.aruco.CharucoDetector(board, detectorParams=params)
|
||||
|
||||
@@ -310,12 +310,11 @@ static void _detectInitialCandidates(const Mat &grey, vector<vector<Point2f> > &
|
||||
|
||||
|
||||
/**
|
||||
* @brief Given an input image and candidate corners, extract the bits of the candidate, including
|
||||
* @brief Given an input image and candidate corners, extract the cell pixel ratio of the candidate, including
|
||||
* the border bits
|
||||
*/
|
||||
static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int markerSize,
|
||||
int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu,
|
||||
OutputArray _cellPixelRatio = noArray()) {
|
||||
static Mat _extractCellPixelRatio(InputArray _image, const vector<Point2f>& corners, int markerSize,
|
||||
int markerBorderBits, int cellSize, double cellMarginRate, double minStdDevOtsu) {
|
||||
CV_Assert(_image.getMat().channels() == 1);
|
||||
CV_Assert(corners.size() == 4ull);
|
||||
CV_Assert(markerBorderBits > 0 && cellSize > 0 && cellMarginRate >= 0 && cellMarginRate <= 0.5);
|
||||
@@ -339,11 +338,11 @@ static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int m
|
||||
warpPerspective(_image, resultImg, transformation, Size(resultImgSize, resultImgSize),
|
||||
INTER_NEAREST);
|
||||
|
||||
// output image containing the bits
|
||||
Mat bits(markerSizeWithBorders, markerSizeWithBorders, CV_8UC1, Scalar::all(0));
|
||||
// output image containing the ratio of white pixels in each cell
|
||||
Mat cellPixelRatio(markerSizeWithBorders, markerSizeWithBorders, CV_32FC1, Scalar::all(0));
|
||||
|
||||
// check if standard deviation is enough to apply Otsu
|
||||
// if not enough, it probably means all bits are the same color (black or white)
|
||||
// if not enough, it probably means all pixels are the same color (black or white)
|
||||
Mat mean, stddev;
|
||||
// Remove some border just to avoid border noise from perspective transformation
|
||||
Mat innerRegion = resultImg.colRange(cellSize / 2, resultImg.cols - cellSize / 2)
|
||||
@@ -351,18 +350,13 @@ static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int m
|
||||
meanStdDev(innerRegion, mean, stddev);
|
||||
if(stddev.ptr< double >(0)[0] < minStdDevOtsu) {
|
||||
// all black or all white, depending on mean value
|
||||
if(mean.ptr< double >(0)[0] > 127)
|
||||
bits.setTo(1);
|
||||
else
|
||||
bits.setTo(0);
|
||||
if(_cellPixelRatio.needed()) bits.convertTo(_cellPixelRatio, CV_32F);
|
||||
return bits;
|
||||
}
|
||||
if(mean.ptr< double >(0)[0] > 127){
|
||||
cellPixelRatio.setTo(1);
|
||||
} else {
|
||||
cellPixelRatio.setTo(0);
|
||||
}
|
||||
|
||||
Mat cellPixelRatio;
|
||||
if (_cellPixelRatio.needed()) {
|
||||
_cellPixelRatio.create(markerSizeWithBorders, markerSizeWithBorders, CV_32FC1);
|
||||
cellPixelRatio = _cellPixelRatio.getMatRef();
|
||||
return cellPixelRatio;
|
||||
}
|
||||
|
||||
// now extract code, first threshold using Otsu
|
||||
@@ -377,38 +371,40 @@ static Mat _extractBits(InputArray _image, const vector<Point2f>& corners, int m
|
||||
cellSize - 2 * cellMarginPixels));
|
||||
// count white pixels on each cell to assign its value
|
||||
size_t nZ = (size_t) countNonZero(square);
|
||||
if(nZ > square.total() / 2) bits.at<unsigned char>(y, x) = 1;
|
||||
|
||||
// define the cell pixel ratio as the ratio of the white pixels. For inverted markers, the ratio will be inverted.
|
||||
if(_cellPixelRatio.needed()) cellPixelRatio.at<float>(y, x) = (nZ / (float)square.total());
|
||||
cellPixelRatio.at<float>(y, x) = (nZ / (float)square.total());
|
||||
}
|
||||
}
|
||||
|
||||
return bits;
|
||||
return cellPixelRatio;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Return number of erroneous bits in border, i.e. number of white bits in border.
|
||||
* @brief Return number of erroneous bits in border, i.e. bits for which pixel ratio > validBitIdThreshold.
|
||||
*/
|
||||
static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) {
|
||||
static int _getBorderErrors(const Mat &cellPixelRatio, int markerSize, int borderSize, float validBitIdThreshold) {
|
||||
|
||||
int sizeWithBorders = markerSize + 2 * borderSize;
|
||||
|
||||
CV_Assert(markerSize > 0 && bits.cols == sizeWithBorders && bits.rows == sizeWithBorders);
|
||||
CV_Assert(markerSize > 0 && cellPixelRatio.cols == sizeWithBorders && cellPixelRatio.rows == sizeWithBorders);
|
||||
|
||||
// Get border error. cellPixelRatio has the opposite color as the borders.
|
||||
int totalErrors = 0;
|
||||
for(int y = 0; y < sizeWithBorders; y++) {
|
||||
for(int k = 0; k < borderSize; k++) {
|
||||
if(bits.ptr<unsigned char>(y)[k] != 0) totalErrors++;
|
||||
if(bits.ptr<unsigned char>(y)[sizeWithBorders - 1 - k] != 0) totalErrors++;
|
||||
// Left and right vertical sides
|
||||
if(cellPixelRatio.ptr<float>(y)[k] > validBitIdThreshold) totalErrors++;
|
||||
if(cellPixelRatio.ptr<float>(y)[sizeWithBorders - 1 - k] > validBitIdThreshold) totalErrors++;
|
||||
}
|
||||
}
|
||||
for(int x = borderSize; x < sizeWithBorders - borderSize; x++) {
|
||||
for(int k = 0; k < borderSize; k++) {
|
||||
if(bits.ptr<unsigned char>(k)[x] != 0) totalErrors++;
|
||||
if(bits.ptr<unsigned char>(sizeWithBorders - 1 - k)[x] != 0) totalErrors++;
|
||||
// Top and bottom horizontal sides
|
||||
if(cellPixelRatio.ptr<float>(k)[x] > validBitIdThreshold) totalErrors++;
|
||||
if(cellPixelRatio.ptr<float>(sizeWithBorders - 1 - k)[x] > validBitIdThreshold) totalErrors++;
|
||||
}
|
||||
}
|
||||
return totalErrors;
|
||||
@@ -482,49 +478,43 @@ static uint8_t _identifyOneCandidate(const Dictionary& dictionary, const Mat& _i
|
||||
scaled_corners[i].y = _corners[i].y * scale;
|
||||
}
|
||||
|
||||
Mat cellPixelRatio;
|
||||
Mat candidateBits =
|
||||
_extractBits(_image, scaled_corners, dictionary.markerSize, params.markerBorderBits,
|
||||
params.perspectiveRemovePixelPerCell,
|
||||
params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev,
|
||||
cellPixelRatio);
|
||||
Mat cellPixelRatio =
|
||||
_extractCellPixelRatio(_image, scaled_corners, dictionary.markerSize, params.markerBorderBits,
|
||||
params.perspectiveRemovePixelPerCell,
|
||||
params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev);
|
||||
|
||||
// analyze border bits
|
||||
int maximumErrorsInBorder =
|
||||
int(dictionary.markerSize * dictionary.markerSize * params.maxErroneousBitsInBorderRate);
|
||||
int(dictionary.markerSize * dictionary.markerSize * params.maxErroneousBitsInBorderRate);
|
||||
int borderErrors =
|
||||
_getBorderErrors(candidateBits, dictionary.markerSize, params.markerBorderBits);
|
||||
_getBorderErrors(cellPixelRatio, dictionary.markerSize, params.markerBorderBits, params.validBitIdThreshold);
|
||||
|
||||
// check if it is a white marker
|
||||
if(params.detectInvertedMarker){
|
||||
// to get from 255 to 1
|
||||
Mat invertedImg = ~candidateBits-254;
|
||||
int invBError = _getBorderErrors(invertedImg, dictionary.markerSize, params.markerBorderBits);
|
||||
Mat invCellPixelRatio = 1.f - cellPixelRatio;
|
||||
int invBError = _getBorderErrors(invCellPixelRatio, dictionary.markerSize, params.markerBorderBits, params.validBitIdThreshold);
|
||||
// white marker
|
||||
if(invBError<borderErrors){
|
||||
cellPixelRatio = 1.f - cellPixelRatio;
|
||||
borderErrors = invBError;
|
||||
invertedImg.copyTo(candidateBits);
|
||||
invCellPixelRatio.copyTo(cellPixelRatio);
|
||||
typ=2;
|
||||
}
|
||||
}
|
||||
if(borderErrors > maximumErrorsInBorder) return 0; // border is wrong
|
||||
|
||||
// take only inner bits
|
||||
Mat onlyBits =
|
||||
candidateBits.rowRange(params.markerBorderBits,
|
||||
candidateBits.rows - params.markerBorderBits)
|
||||
.colRange(params.markerBorderBits, candidateBits.cols - params.markerBorderBits);
|
||||
Mat onlyCellPixelRatio =
|
||||
cellPixelRatio.rowRange(params.markerBorderBits,
|
||||
cellPixelRatio.rows - params.markerBorderBits)
|
||||
.colRange(params.markerBorderBits, cellPixelRatio.cols - params.markerBorderBits);
|
||||
|
||||
// try to indentify the marker
|
||||
if(!dictionary.identify(onlyBits, idx, rotation, params.errorCorrectionRate))
|
||||
// try to identify the marker
|
||||
if(!dictionary.identify(onlyCellPixelRatio, idx, rotation, params.errorCorrectionRate, params.validBitIdThreshold))
|
||||
return 0;
|
||||
|
||||
// compute the candidate's confidence
|
||||
if(confidenceNeeded) {
|
||||
Mat groundTruthbits;
|
||||
Mat bitsUints = dictionary.getBitsFromByteList(dictionary.bytesList.rowRange(idx, idx + 1), dictionary.markerSize, rotation);
|
||||
bitsUints.convertTo(groundTruthbits, CV_32F);
|
||||
Mat groundTruthbits = dictionary.getMarkerBits(idx, rotation);
|
||||
markerConfidence = _getMarkerConfidence(groundTruthbits, cellPixelRatio, dictionary.markerSize, params.markerBorderBits);
|
||||
}
|
||||
|
||||
@@ -1403,11 +1393,14 @@ void ArucoDetector::refineDetectedMarkers(InputArray _image, const Board& _board
|
||||
if(refineParams.errorCorrectionRate >= 0) {
|
||||
|
||||
// extract bits
|
||||
Mat bits = _extractBits(
|
||||
Mat cellPixelRatio = _extractCellPixelRatio(
|
||||
grey, rotatedMarker, dictionary.markerSize, detectorParams.markerBorderBits,
|
||||
detectorParams.perspectiveRemovePixelPerCell,
|
||||
detectorParams.perspectiveRemoveIgnoredMarginPerCell, detectorParams.minOtsuStdDev);
|
||||
|
||||
Mat bits;
|
||||
cellPixelRatio.convertTo(bits, CV_8UC1);
|
||||
|
||||
Mat onlyBits =
|
||||
bits.rowRange(detectorParams.markerBorderBits, bits.rows - detectorParams.markerBorderBits)
|
||||
.colRange(detectorParams.markerBorderBits, bits.rows - detectorParams.markerBorderBits);
|
||||
|
||||
@@ -73,25 +73,32 @@ void Dictionary::writeDictionary(FileStorage& fs, const String &name)
|
||||
}
|
||||
|
||||
|
||||
bool Dictionary::identify(const Mat &onlyBits, int &idx, int &rotation, double maxCorrectionRate) const {
|
||||
CV_Assert(onlyBits.rows == markerSize && onlyBits.cols == markerSize);
|
||||
bool Dictionary::identify(const Mat &onlyCellPixelRatio, CV_OUT int &idx, CV_OUT int &rotation, double maxCorrectionRate, float validBitIdThreshold) const {
|
||||
CV_Assert(onlyCellPixelRatio.rows == markerSize && onlyCellPixelRatio.cols == markerSize);
|
||||
|
||||
int maxCorrectionRecalculed = int(double(maxCorrectionBits) * maxCorrectionRate);
|
||||
|
||||
// get as a byte list
|
||||
Mat candidateBytes = getByteListFromBits(onlyBits);
|
||||
|
||||
idx = -1; // by default, not found
|
||||
|
||||
// search closest marker in dict
|
||||
for(int m = 0; m < bytesList.rows; m++) {
|
||||
int currentMinDistance = markerSize * markerSize + 1;
|
||||
int currentRotation = -1;
|
||||
for(unsigned int r = 0; r < 4; r++) {
|
||||
int currentHamming = cv::hal::normHamming(
|
||||
bytesList.ptr(m)+r*candidateBytes.cols,
|
||||
candidateBytes.ptr(),
|
||||
candidateBytes.cols);
|
||||
for(int r = 0; r < 4; r++) {
|
||||
|
||||
Mat bitsRot = getBitsFromByteList(bytesList.rowRange(m, m + 1), markerSize, r);
|
||||
bitsRot.convertTo(bitsRot, CV_32F);
|
||||
|
||||
// Loop over all bits dictBitsList [m, markerSize * markerSize, 4]; onlyCellPixelRatio [markerSize, markerSize]
|
||||
int currentHamming = 0;
|
||||
for(int i = 0; i < markerSize; i++) {
|
||||
for(int j = 0; j < markerSize; j++) {
|
||||
// If detected bit is too far from the ground truth, consider it false.
|
||||
if(fabs(onlyCellPixelRatio.at<float>(i, j) - static_cast<float>(bitsRot.at<float>(i, j))) > validBitIdThreshold){
|
||||
currentHamming++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(currentHamming < currentMinDistance) {
|
||||
currentMinDistance = currentHamming;
|
||||
@@ -111,6 +118,16 @@ bool Dictionary::identify(const Mat &onlyBits, int &idx, int &rotation, double m
|
||||
}
|
||||
|
||||
|
||||
bool Dictionary::identify(const Mat &onlyBits, CV_OUT int &idx, CV_OUT int &rotation, double maxCorrectionRate) const {
|
||||
CV_Assert(onlyBits.rows == markerSize && onlyBits.cols == markerSize);
|
||||
|
||||
Mat candidateBitRatio;
|
||||
onlyBits.convertTo(candidateBitRatio, CV_32F);
|
||||
const float validBitIdThreshold = DEFAULT_VALID_BIT_ID_THRESHOLD;
|
||||
return identify(candidateBitRatio, idx, rotation, maxCorrectionRate, validBitIdThreshold);
|
||||
}
|
||||
|
||||
|
||||
int Dictionary::getDistanceToId(InputArray bits, int id, bool allRotations) const {
|
||||
|
||||
CV_Assert(id >= 0 && id < bytesList.rows);
|
||||
@@ -147,7 +164,8 @@ void Dictionary::generateImageMarker(int id, int sidePixels, OutputArray _img, i
|
||||
Mat innerRegion = tinyMarker.rowRange(borderBits, tinyMarker.rows - borderBits)
|
||||
.colRange(borderBits, tinyMarker.cols - borderBits);
|
||||
// put inner bits
|
||||
Mat bits = 255 * getBitsFromByteList(bytesList.rowRange(id, id + 1), markerSize);
|
||||
Mat bits = getMarkerBits(id);
|
||||
bits.convertTo(bits, CV_8U, 255.0);
|
||||
CV_Assert(innerRegion.total() == bits.total());
|
||||
bits.copyTo(innerRegion);
|
||||
|
||||
@@ -194,12 +212,28 @@ Mat Dictionary::getByteListFromBits(const Mat &bits) {
|
||||
}
|
||||
|
||||
|
||||
Mat Dictionary::getMarkerBits(int markerId, int rotationId) const {
|
||||
|
||||
const int nbRotations = 4;
|
||||
CV_Assert(markerId < bytesList.rows);
|
||||
CV_Assert(rotationId < nbRotations);
|
||||
|
||||
Mat bits(markerSize, markerSize, CV_32F, Scalar::all(0));
|
||||
Mat bitsUints = getBitsFromByteList(bytesList.rowRange(markerId, markerId + 1), markerSize, rotationId);
|
||||
bitsUints.convertTo(bits, CV_32F);
|
||||
|
||||
CV_Assert(bits.rows == markerSize && bits.cols == markerSize);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
Mat Dictionary::getBitsFromByteList(const Mat &byteList, int markerSize, int rotationId) {
|
||||
CV_Assert(byteList.total() > 0 &&
|
||||
byteList.total() >= (unsigned int)markerSize * markerSize / 8 &&
|
||||
byteList.total() <= (unsigned int)markerSize * markerSize / 8 + 1);
|
||||
|
||||
CV_Assert(rotationId >=0 && rotationId < 4);
|
||||
CV_Assert(byteList.channels() >= 4);
|
||||
CV_Assert(rotationId >= 0 && rotationId < 4);
|
||||
|
||||
Mat bits(markerSize, markerSize, CV_8UC1, Scalar::all(0));
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ TEST(CV_ArucoTutorial, can_find_diamondmarkers)
|
||||
aruco::DetectorParameters detectorParams;
|
||||
detectorParams.readDetectorParameters(fs.root());
|
||||
detectorParams.cornerRefinementMethod = aruco::CORNER_REFINE_APRILTAG;
|
||||
detectorParams.validBitIdThreshold = 0.5f;
|
||||
|
||||
aruco::CharucoBoard charucoBoard(Size(3, 3), 0.4f, 0.25f, dictionary);
|
||||
aruco::CharucoDetector detector(charucoBoard, aruco::CharucoParameters(), detectorParams);
|
||||
|
||||
@@ -473,7 +473,7 @@ markerDetectionGT applyTemperingToMarkerCells(cv::Mat &marker,
|
||||
++cellsTempered;
|
||||
|
||||
// cell too tempered, no detection expected
|
||||
if(cellTempConfig.cellRatioToTemper > 0.5f) {
|
||||
if(cellTempConfig.cellRatioToTemper > params.validBitIdThreshold) {
|
||||
if(isBorder){
|
||||
++borderErrors;
|
||||
} else {
|
||||
@@ -556,7 +556,7 @@ static void runArucoDetectionConfidence(ArucoAlgParams arucoAlgParam) {
|
||||
// make sure there are no bits have any detection errors
|
||||
params.maxErroneousBitsInBorderRate = 0.0;
|
||||
params.errorCorrectionRate = 0.0;
|
||||
params.perspectiveRemovePixelPerCell = 8; // esnsure that there is enough resolution to properly handle distortions
|
||||
params.perspectiveRemovePixelPerCell = 8; // ensure that there is enough resolution to properly handle distortions
|
||||
aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_6X6_250), params);
|
||||
|
||||
const bool detectInvertedMarker = (arucoAlgParam == ArucoAlgParams::DETECT_INVERTED_MARKER);
|
||||
@@ -674,6 +674,144 @@ static void runArucoDetectionConfidence(ArucoAlgParams arucoAlgParam) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper struc and functions for CV_ArucoDetectionUnc
|
||||
struct ArucoThresholdTestConfig {
|
||||
MarkerTemperingConfig markerTemperingConfig; // Configuration of cells to invert (percentage, number and markerRegionToTemper)
|
||||
float validBitIdThreshold; // range [0,1], define the acceptable threshold when comparing the detected marker to the dictionary during marker identification.
|
||||
float perspectiveRemoveIgnoredMarginPerCell; // Width of the margin of pixels on each cell not considered for the marker identification
|
||||
int markerBorderBits; // Number of bits of the marker border
|
||||
float distortionRatio; // Percentage of offset used for perspective distortion, bigger means more distorted
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Test the param validBitIdThreshold
|
||||
* Loops over a set of detector configurations (validBitIdThreshold, distortion, DetectorParameters such as markerBorderBits)
|
||||
* For each configuration, it creates a synthetic image containing four markers arranged in a 2x2 grid.
|
||||
* Each marker is generated with its own configuration (id, size, rotation).
|
||||
* Make sure that markers are detected or not based on validBitIdThreshold and percentage of tempering.
|
||||
* Finally, it runs the detector and checks that each marker is detected or not based on the threshold.
|
||||
*
|
||||
*/
|
||||
static void runArucoDetectionThreshold(ArucoAlgParams arucoAlgParam) {
|
||||
|
||||
aruco::DetectorParameters params;
|
||||
// make sure there are no bits have any detection errors
|
||||
params.perspectiveRemovePixelPerCell = 20; // ensure that there is enough resolution to properly handle distortions
|
||||
params.maxErroneousBitsInBorderRate = 0.f;
|
||||
params.errorCorrectionRate = 0.f;
|
||||
aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_5X5_250), params); // Max correction: 6bits
|
||||
|
||||
const bool detectInvertedMarker = (arucoAlgParam == ArucoAlgParams::DETECT_INVERTED_MARKER);
|
||||
|
||||
// define several detector configurations to test different settings
|
||||
// {{MarkerTemperingConfig}, validBitIdThreshold, perspectiveRemoveIgnoredMarginPerCell, markerBorderBits, distortionRatio}
|
||||
|
||||
vector<ArucoThresholdTestConfig> detectorConfigs = {
|
||||
// No tempering, expect detection for every threshold
|
||||
{{0.f, 0, MarkerRegionToTemper::ALL}, 0.3f, 0.f, 1, 0.f},
|
||||
{{0.f, 0, MarkerRegionToTemper::ALL}, 0.5f, 0.f, 1, 0.f},
|
||||
{{0.f, 0, MarkerRegionToTemper::ALL}, 0.9f, 0.f, 1, 0.f},
|
||||
// Include distortions
|
||||
{{0.f, 0, MarkerRegionToTemper::ALL}, 0.3f, 0.f, 1, 0.05f},
|
||||
{{0.f, 0, MarkerRegionToTemper::ALL}, 0.5f, 0.f, 1, 0.1f},
|
||||
{{0.f, 0, MarkerRegionToTemper::ALL}, 0.9f, 0.f, 1, 0.2f},
|
||||
|
||||
// 20% temper, expect detection with threshold above 0.2
|
||||
{{0.2f, 5, MarkerRegionToTemper::BORDER}, 0.30f, 0.f, 1, 0.f}, // Detection
|
||||
|
||||
{{0.2f, 1, MarkerRegionToTemper::BORDER}, 0.18f, 0.f, 1, 0.f}, // No detection
|
||||
{{0.2f, 1, MarkerRegionToTemper::BORDER}, 0.18f, 0.f, 1, 0.f}, // No detection
|
||||
{{0.2f, 10, MarkerRegionToTemper::INNER}, 0.22f, 0.f, 1, 0.f}, // Detection
|
||||
{{0.2f, 1, MarkerRegionToTemper::INNER}, 0.18f, 0.f, 1, 0.f} // No detection
|
||||
|
||||
// distortions
|
||||
};
|
||||
|
||||
// define marker configurations for the 4 markers in each image
|
||||
const int markerSidePixels = 700; // To simplify the cell division, markerSidePixels is a multiple of 7. (5x5 dict + 2 border bits)
|
||||
vector<MarkerCreationConfig> markerCreationConfig = {
|
||||
{0, markerSidePixels, markerRot::ROT_90}, // {id, markerSidePixels, rotation}
|
||||
{1, markerSidePixels, markerRot::ROT_270},
|
||||
{2, markerSidePixels, markerRot::NONE},
|
||||
{3, markerSidePixels, markerRot::ROT_180}
|
||||
};
|
||||
|
||||
// loop over each detector configuration
|
||||
for (size_t cfgIdx = 0; cfgIdx < detectorConfigs.size(); cfgIdx++) {
|
||||
ArucoThresholdTestConfig detCfg = detectorConfigs[cfgIdx];
|
||||
|
||||
// update detector parameters
|
||||
params.validBitIdThreshold =detCfg.validBitIdThreshold;
|
||||
params.perspectiveRemoveIgnoredMarginPerCell = detCfg.perspectiveRemoveIgnoredMarginPerCell;
|
||||
params.markerBorderBits = detCfg.markerBorderBits;
|
||||
params.detectInvertedMarker = detectInvertedMarker;
|
||||
detector.setDetectorParameters(params);
|
||||
|
||||
// create a blank image large enough to hold 4 markers in a 2x2 grid
|
||||
const int margin = markerSidePixels / 2;
|
||||
const int imageSize = (markerSidePixels * 2) + margin * 3;
|
||||
Mat img(imageSize, imageSize, CV_8UC1, Scalar(255));
|
||||
|
||||
vector<markerDetectionGT> groundTruths;
|
||||
const aruco::Dictionary &dictionary = detector.getDictionary();
|
||||
|
||||
// place each marker into the image
|
||||
for (int row = 0; row < 2; row++) {
|
||||
for (int col = 0; col < 2; col++) {
|
||||
int index = row * 2 + col;
|
||||
MarkerCreationConfig markerCfg = markerCreationConfig[index];
|
||||
// adjust marker id to be unique for each detector configuration
|
||||
markerCfg.id += static_cast<int>(cfgIdx * markerCreationConfig.size());
|
||||
|
||||
// generate img
|
||||
Mat markerImg;
|
||||
markerDetectionGT gt = generateTemperedMarkerImage(markerImg, markerCfg, detCfg.markerTemperingConfig, params, dictionary, detCfg.distortionRatio);
|
||||
|
||||
groundTruths.push_back(gt);
|
||||
|
||||
// place marker in the image
|
||||
Point2f topLeft(static_cast<float>(margin + col * (markerSidePixels + margin)),
|
||||
static_cast<float>(margin + row * (markerSidePixels + margin)));
|
||||
placeMarker(img, markerImg, topLeft);
|
||||
}
|
||||
}
|
||||
|
||||
// if testing inverted markers globally, invert the whole image
|
||||
if (detectInvertedMarker) {
|
||||
bitwise_not(img, img);
|
||||
}
|
||||
|
||||
// run detection.
|
||||
vector<vector<Point2f>> corners, rejected;
|
||||
vector<int> ids;
|
||||
vector<float> markerConfidence;
|
||||
detector.detectMarkersWithConfidence(img, corners, ids, markerConfidence, rejected);
|
||||
|
||||
ASSERT_EQ(ids.size(), corners.size());
|
||||
ASSERT_EQ(ids.size(), markerConfidence.size());
|
||||
|
||||
std::map<int, float> confidenceById;
|
||||
for (size_t i = 0; i < ids.size(); i++) {
|
||||
confidenceById[ids[i]] = markerConfidence[i];
|
||||
}
|
||||
|
||||
// verify that every marker is detected and its confidence is within tolerance
|
||||
for (const auto& currentGT : groundTruths) {
|
||||
const auto it = confidenceById.find(currentGT.id);
|
||||
const bool detected = it != confidenceById.end();
|
||||
EXPECT_EQ(currentGT.expectDetection, detected)
|
||||
<< "Marker id: " << currentGT.id << " (detector config " << cfgIdx << ")";
|
||||
|
||||
if (currentGT.expectDetection && detected) {
|
||||
EXPECT_NEAR(currentGT.confidence, it->second, 0.05)
|
||||
<< "Marker id: " << currentGT.id << " (detector config " << cfgIdx << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Check max and min size in marker detection parameters
|
||||
*/
|
||||
@@ -981,6 +1119,14 @@ TEST(CV_InvertedFlagArucoDetectionConfidence, algorithmic) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CV_ArucoDetectionThreshold, algorithmic) {
|
||||
runArucoDetectionThreshold(ArucoAlgParams::USE_DEFAULT);
|
||||
}
|
||||
|
||||
TEST(CV_InvertedArucoDetectionThreshold, algorithmic) {
|
||||
runArucoDetectionThreshold(ArucoAlgParams::DETECT_INVERTED_MARKER);
|
||||
}
|
||||
|
||||
TEST(CV_ArucoDetectMarkers, regression_3192)
|
||||
{
|
||||
aruco::ArucoDetector detector(aruco::getPredefinedDictionary(aruco::DICT_4X4_50));
|
||||
@@ -1304,6 +1450,7 @@ TEST_P(ArucoThreading, number_of_threads_does_not_change_results)
|
||||
|
||||
aruco::DetectorParameters detectorParameters = detector.getDetectorParameters();
|
||||
detectorParameters.cornerRefinementMethod = (int)GetParam();
|
||||
detectorParameters.validBitIdThreshold = 0.5f;
|
||||
detector.setDetectorParameters(detectorParameters);
|
||||
|
||||
vector<vector<Point2f> > original_corners;
|
||||
|
||||
@@ -66,6 +66,7 @@ void CV_ArucoBoardPose::run(int) {
|
||||
vector<vector<Point2f> > corners;
|
||||
vector<int> ids;
|
||||
detectorParameters.markerBorderBits = markerBorder;
|
||||
detectorParameters.validBitIdThreshold = 0.5f;
|
||||
detector.setDetectorParameters(detectorParameters);
|
||||
detector.detectMarkers(img, corners, ids);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ def __load_extra_py_code_for_module(base, name, enable_debug_print=False):
|
||||
native_module = sys.modules.pop(module_name, None)
|
||||
try:
|
||||
py_module = importlib.import_module(module_name)
|
||||
except ImportError as err:
|
||||
except (ImportError, AttributeError) as err:
|
||||
if enable_debug_print:
|
||||
print("Can't load Python code for module:", module_name,
|
||||
". Reason:", err)
|
||||
|
||||
@@ -13,6 +13,21 @@ else:
|
||||
from distutils.dir_util import copy_tree
|
||||
|
||||
|
||||
def _remove_stale_pyi_files(directory):
|
||||
"""Remove .pyi files and py.typed markers from the directory tree.
|
||||
|
||||
During incremental builds, disabling a previously enabled module leaves
|
||||
stale typing stubs in the loader directory from a previous copy. Since
|
||||
copy_tree merges rather than replaces, those stale files persist.
|
||||
Removing all stub files before copying ensures only stubs for currently
|
||||
enabled modules are present. Runtime .py files are not affected.
|
||||
"""
|
||||
for dirpath, dirnames, filenames in os.walk(directory):
|
||||
for fname in filenames:
|
||||
if fname.endswith('.pyi') or fname == 'py.typed':
|
||||
os.remove(os.path.join(dirpath, fname))
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_arguments()
|
||||
py_typed_path = os.path.join(args.stubs_dir, 'py.typed')
|
||||
@@ -24,6 +39,8 @@ def main():
|
||||
'generation phase.'.format(py_typed_path)
|
||||
)
|
||||
return
|
||||
if os.path.isdir(args.output_dir):
|
||||
_remove_stale_pyi_files(args.output_dir)
|
||||
copy_tree(args.stubs_dir, args.output_dir)
|
||||
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ static PyObject* createSubmodule(PyObject* parent_module, const std::string& nam
|
||||
}
|
||||
/// Populates parent module dictionary. Submodule lifetime should be managed
|
||||
/// by the global modules dictionary and parent module dictionary, so Py_DECREF after
|
||||
/// successfull call to the `PyDict_SetItemString` is redundant.
|
||||
/// successful call to the `PyDict_SetItemString` is redundant.
|
||||
if (PyDict_SetItemString(parent_module_dict, submodule_name.c_str(), submodule) < 0) {
|
||||
return PyErr_Format(PyExc_ImportError,
|
||||
"Can't register a submodule '%s' (full name: '%s')",
|
||||
|
||||
@@ -8,7 +8,7 @@ from .nodes import (NamespaceNode, FunctionNode, OptionalTypeNode, TypeNode,
|
||||
ClassProperty, PrimitiveTypeNode, ASTNodeTypeNode,
|
||||
AggregatedTypeNode, CallableTypeNode, AnyTypeNode,
|
||||
TupleTypeNode, UnionTypeNode, ProtocolClassNode,
|
||||
DictTypeNode, ClassTypeNode)
|
||||
DictTypeNode, ClassTypeNode, AliasRefTypeNode)
|
||||
from .ast_utils import (find_function_node, SymbolName,
|
||||
for_each_function_overload)
|
||||
from .types_conversion import create_type_node
|
||||
@@ -376,6 +376,62 @@ def _find_argument_index(arguments: Sequence[FunctionNode.Arg],
|
||||
return None
|
||||
|
||||
|
||||
def make_matlike_or_scalar_arg(*arg_names: str) -> Callable[[NamespaceNode, SymbolName], None]:
|
||||
"""Make arguments accept both MatLike and Scalar types.
|
||||
|
||||
This is used for functions like inRange where the C++ InputArray parameter
|
||||
can accept both Mat objects and Scalar values (tuples, floats, etc.).
|
||||
|
||||
Example: cv2.inRange(img, (0, 0, 0), (255, 255, 255)) should be valid.
|
||||
"""
|
||||
def _make_matlike_or_scalar_arg(root_node: NamespaceNode,
|
||||
function_symbol_name: SymbolName) -> None:
|
||||
from .predefined_types import PREDEFINED_TYPES
|
||||
|
||||
function = find_function_node(root_node, function_symbol_name)
|
||||
for arg_name in arg_names:
|
||||
found_overload_with_arg = False
|
||||
|
||||
for overload in function.overloads:
|
||||
arg_idx = _find_argument_index(overload.arguments, arg_name)
|
||||
|
||||
# skip overloads without this argument
|
||||
if arg_idx is None:
|
||||
continue
|
||||
|
||||
current_type = overload.arguments[arg_idx].type_node
|
||||
|
||||
# Check if it's already a union or if it already includes Scalar
|
||||
if isinstance(current_type, UnionTypeNode):
|
||||
# Check if Scalar is already in the union
|
||||
has_scalar = any(
|
||||
isinstance(item, AliasRefTypeNode) and item.typename == "Scalar"
|
||||
for item in current_type.items
|
||||
)
|
||||
if has_scalar:
|
||||
continue
|
||||
# Add Scalar to existing union
|
||||
scalar_ref = AliasRefTypeNode("Scalar")
|
||||
current_type.items = current_type.items + (scalar_ref,)
|
||||
else:
|
||||
# Create a union of current type and Scalar
|
||||
scalar_ref = AliasRefTypeNode("Scalar")
|
||||
overload.arguments[arg_idx].type_node = UnionTypeNode(
|
||||
f"{arg_name}_type",
|
||||
(cast(TypeNode, current_type), scalar_ref)
|
||||
)
|
||||
|
||||
found_overload_with_arg = True
|
||||
|
||||
if not found_overload_with_arg:
|
||||
raise RuntimeError(
|
||||
f"Failed to find argument with name: '{arg_name}'"
|
||||
f" in '{function_symbol_name.name}' overloads"
|
||||
)
|
||||
|
||||
return _make_matlike_or_scalar_arg
|
||||
|
||||
|
||||
NODES_TO_REFINE = {
|
||||
SymbolName(("cv", ), (), "resize"): make_optional_arg("dsize"),
|
||||
SymbolName(("cv", ), (), "calcHist"): make_optional_arg("mask"),
|
||||
@@ -401,6 +457,11 @@ NODES_TO_REFINE = {
|
||||
SymbolName(("cv", "fisheye"), (), "initUndistortRectifyMap"): make_optional_arg("D"),
|
||||
SymbolName(("cv", ), (), "imread"): make_optional_none_return,
|
||||
SymbolName(("cv", ), (), "imdecode"): make_optional_none_return,
|
||||
SymbolName(("cv", ), (), "HoughCircles"): make_optional_none_return,
|
||||
SymbolName(("cv", ), (), "HoughLines"): make_optional_none_return,
|
||||
SymbolName(("cv", ), (), "HoughLinesP"): make_optional_none_return,
|
||||
# Fix for issue #28534: inRange should accept Scalar for lowerb and upperb
|
||||
SymbolName(("cv", ), (), "inRange"): make_matlike_or_scalar_arg("lowerb", "upperb"),
|
||||
}
|
||||
|
||||
ERROR_CLASS_PROPERTIES = (
|
||||
|
||||
@@ -3,6 +3,7 @@ __all__ = ("generate_typing_stubs", )
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
from typing import (Callable, NamedTuple, Union, Set, Dict,
|
||||
Collection, Tuple, List)
|
||||
import warnings
|
||||
@@ -24,6 +25,22 @@ from .nodes.type_node import (TypeNode, AliasTypeNode, AliasRefTypeNode,
|
||||
ConditionalAliasTypeNode, PrimitiveTypeNode)
|
||||
|
||||
|
||||
def _clean_stale_stubs_dirs(stubs_root: Path) -> None:
|
||||
"""Remove all subdirectories under stubs_root.
|
||||
|
||||
During incremental builds, disabling a previously enabled module leaves
|
||||
behind its typing stub directory (e.g. cv2/gapi/). Removing all
|
||||
subdirectories before regeneration ensures only stubs for currently
|
||||
enabled modules are present. Top-level files (py.typed, __init__.pyi)
|
||||
are kept because they are managed separately.
|
||||
"""
|
||||
if not stubs_root.is_dir():
|
||||
return
|
||||
for item in stubs_root.iterdir():
|
||||
if item.is_dir():
|
||||
shutil.rmtree(item)
|
||||
|
||||
|
||||
def generate_typing_stubs(root: NamespaceNode, output_path: Path):
|
||||
"""Generates typing stubs for the AST with root `root` and outputs
|
||||
created files tree to directory pointed by `output_path`.
|
||||
@@ -88,6 +105,12 @@ def generate_typing_stubs(root: NamespaceNode, output_path: Path):
|
||||
# The whole process should fail !only! when all possible scopes are
|
||||
# checked and at least 1 node is still unresolved.
|
||||
root.resolve_type_nodes()
|
||||
# Remove stale typing stub subdirectories from previous builds.
|
||||
# In incremental builds, disabling a module (e.g. -DBUILD_opencv_gapi=OFF)
|
||||
# no longer generates its stubs, but leftover directories from a previous
|
||||
# build persist and propagate through the copy/install steps, causing
|
||||
# type-checker errors for stubs referencing unavailable modules.
|
||||
_clean_stale_stubs_dirs(Path(output_path) / root.export_name)
|
||||
_generate_typing_module(root, output_path)
|
||||
_populate_reexported_symbols(root)
|
||||
_generate_typing_stubs(root, output_path)
|
||||
@@ -708,7 +731,7 @@ def _generate_typing_module(root: NamespaceNode, output_path: Path) -> None:
|
||||
"""
|
||||
|
||||
def has_all_required_modules(type_node: TypeNode) -> bool:
|
||||
return all(em in root.namespaces for em in node.required_modules)
|
||||
return all(em in root.namespaces for em in type_node.required_modules)
|
||||
|
||||
def register_alias_links_from_aggregated_type(type_node: TypeNode) -> None:
|
||||
assert isinstance(type_node, AggregatedTypeNode), \
|
||||
|
||||
@@ -219,7 +219,7 @@ void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
|
||||
Calculate depth discontinuity regions: pixels whose neighboring disparities differ by more than
|
||||
dispGap, dilated by window of width discontWidth.
|
||||
*/
|
||||
void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
set(the_description "Video Analysis")
|
||||
ocv_add_dispatched_file(lkpyramid SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL)
|
||||
ocv_define_module(video
|
||||
opencv_imgproc
|
||||
OPTIONAL
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, 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,
|
||||
@@ -72,85 +73,7 @@ static void calcScharrDeriv(const cv::Mat& src, cv::Mat& dst)
|
||||
|
||||
void cv::detail::ScharrDerivInvoker::operator()(const Range& range) const
|
||||
{
|
||||
using cv::detail::deriv_type;
|
||||
int rows = src.rows, cols = src.cols, cn = src.channels(), colsn = cols*cn;
|
||||
|
||||
int x, y, delta = (int)alignSize((cols + 2)*cn, 16);
|
||||
AutoBuffer<deriv_type> _tempBuf(delta*2 + 64);
|
||||
deriv_type *trow0 = alignPtr(_tempBuf.data() + cn, 16), *trow1 = alignPtr(trow0 + delta, 16);
|
||||
|
||||
#if CV_SIMD128
|
||||
v_int16x8 c3 = v_setall_s16(3), c10 = v_setall_s16(10);
|
||||
#endif
|
||||
|
||||
for( y = range.start; y < range.end; y++ )
|
||||
{
|
||||
const uchar* srow0 = src.ptr<uchar>(y > 0 ? y-1 : rows > 1 ? 1 : 0);
|
||||
const uchar* srow1 = src.ptr<uchar>(y);
|
||||
const uchar* srow2 = src.ptr<uchar>(y < rows-1 ? y+1 : rows > 1 ? rows-2 : 0);
|
||||
deriv_type* drow = (deriv_type *)dst.ptr<deriv_type>(y);
|
||||
|
||||
// do vertical convolution
|
||||
x = 0;
|
||||
#if CV_SIMD128
|
||||
{
|
||||
for( ; x <= colsn - 8; x += 8 )
|
||||
{
|
||||
v_int16x8 s0 = v_reinterpret_as_s16(v_load_expand(srow0 + x));
|
||||
v_int16x8 s1 = v_reinterpret_as_s16(v_load_expand(srow1 + x));
|
||||
v_int16x8 s2 = v_reinterpret_as_s16(v_load_expand(srow2 + x));
|
||||
|
||||
v_int16x8 t1 = v_sub(s2, s0);
|
||||
v_int16x8 t0 = v_add(v_mul_wrap(v_add(s0, s2), c3), v_mul_wrap(s1, c10));
|
||||
|
||||
v_store(trow0 + x, t0);
|
||||
v_store(trow1 + x, t1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for( ; x < colsn; x++ )
|
||||
{
|
||||
int t0 = (srow0[x] + srow2[x])*3 + srow1[x]*10;
|
||||
int t1 = srow2[x] - srow0[x];
|
||||
trow0[x] = (deriv_type)t0;
|
||||
trow1[x] = (deriv_type)t1;
|
||||
}
|
||||
|
||||
// make border
|
||||
int x0 = (cols > 1 ? 1 : 0)*cn, x1 = (cols > 1 ? cols-2 : 0)*cn;
|
||||
for( int k = 0; k < cn; k++ )
|
||||
{
|
||||
trow0[-cn + k] = trow0[x0 + k]; trow0[colsn + k] = trow0[x1 + k];
|
||||
trow1[-cn + k] = trow1[x0 + k]; trow1[colsn + k] = trow1[x1 + k];
|
||||
}
|
||||
|
||||
// do horizontal convolution, interleave the results and store them to dst
|
||||
x = 0;
|
||||
#if CV_SIMD128
|
||||
{
|
||||
for( ; x <= colsn - 8; x += 8 )
|
||||
{
|
||||
v_int16x8 s0 = v_load(trow0 + x - cn);
|
||||
v_int16x8 s1 = v_load(trow0 + x + cn);
|
||||
v_int16x8 s2 = v_load(trow1 + x - cn);
|
||||
v_int16x8 s3 = v_load(trow1 + x);
|
||||
v_int16x8 s4 = v_load(trow1 + x + cn);
|
||||
|
||||
v_int16x8 t0 = v_sub(s1, s0);
|
||||
v_int16x8 t1 = v_add(v_mul_wrap(v_add(s2, s4), c3), v_mul_wrap(s3, c10));
|
||||
|
||||
v_store_interleave((drow + x*2), t0, t1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for( ; x < colsn; x++ )
|
||||
{
|
||||
deriv_type t0 = (deriv_type)(trow0[x+cn] - trow0[x-cn]);
|
||||
deriv_type t1 = (deriv_type)((trow1[x+cn] + trow1[x-cn])*3 + trow1[x]*10);
|
||||
drow[x*2] = t0; drow[x*2+1] = t1;
|
||||
}
|
||||
}
|
||||
ScharrDerivInvoker_impl(src, const_cast<Mat&>(dst), range);
|
||||
}
|
||||
|
||||
cv::detail::LKTrackerInvoker::LKTrackerInvoker(
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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 "lkpyramid.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
#include "lkpyramid.simd.hpp"
|
||||
#include "lkpyramid.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL based on CMakeLists.txt
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
|
||||
void ScharrDerivInvoker_impl(const Mat& src, Mat& dst, const Range& range)
|
||||
{
|
||||
CV_CPU_DISPATCH(ScharrDerivInvoker_SIMD, (src, dst, range), CV_CPU_DISPATCH_MODES_ALL);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cv
|
||||
@@ -7,6 +7,8 @@ namespace detail
|
||||
|
||||
typedef short deriv_type;
|
||||
|
||||
void ScharrDerivInvoker_impl(const Mat& src, Mat& dst, const Range& range);
|
||||
|
||||
struct ScharrDerivInvoker : ParallelLoopBody
|
||||
{
|
||||
ScharrDerivInvoker(const Mat& _src, const Mat& _dst)
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
// 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 "lkpyramid.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
|
||||
|
||||
// SIMD-optimized implementation
|
||||
void ScharrDerivInvoker_SIMD(const Mat& src, Mat& dst, const Range& range);
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_END
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cv
|
||||
|
||||
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
|
||||
|
||||
void ScharrDerivInvoker_SIMD(const Mat& src, Mat& dst, const Range& range)
|
||||
{
|
||||
typedef short deriv_type;
|
||||
int rows = src.rows, cols = src.cols, cn = src.channels(), colsn = cols*cn;
|
||||
|
||||
int x, y, delta = (int)alignSize((cols + 2)*cn, 16);
|
||||
AutoBuffer<deriv_type> _tempBuf(delta*2 + 64);
|
||||
deriv_type *trow0 = alignPtr(_tempBuf.data() + cn, 16), *trow1 = alignPtr(trow0 + delta, 16);
|
||||
|
||||
#if (CV_SIMD)
|
||||
const int vlanes = VTraits<v_int16>::vlanes();
|
||||
v_int16 c3 = vx_setall_s16(3), c10 = vx_setall_s16(10);
|
||||
#endif
|
||||
|
||||
for( y = range.start; y < range.end; y++ )
|
||||
{
|
||||
const uchar* srow0 = src.ptr<uchar>(y > 0 ? y-1 : rows > 1 ? 1 : 0);
|
||||
const uchar* srow1 = src.ptr<uchar>(y);
|
||||
const uchar* srow2 = src.ptr<uchar>(y < rows-1 ? y+1 : rows > 1 ? rows-2 : 0);
|
||||
deriv_type* drow = (deriv_type *)dst.ptr<deriv_type>(y);
|
||||
|
||||
// do vertical convolution
|
||||
x = 0;
|
||||
#if (CV_SIMD)
|
||||
{
|
||||
for( ; x <= colsn - vlanes; x += vlanes )
|
||||
{
|
||||
v_int16 s0 = v_reinterpret_as_s16(vx_load_expand(srow0 + x));
|
||||
v_int16 s1 = v_reinterpret_as_s16(vx_load_expand(srow1 + x));
|
||||
v_int16 s2 = v_reinterpret_as_s16(vx_load_expand(srow2 + x));
|
||||
|
||||
v_int16 t1 = v_sub(s2, s0);
|
||||
v_int16 t0 = v_add(v_mul_wrap(v_add(s0, s2), c3), v_mul_wrap(s1, c10));
|
||||
|
||||
v_store(trow0 + x, t0);
|
||||
v_store(trow1 + x, t1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for( ; x < colsn; x++ )
|
||||
{
|
||||
int t0 = (srow0[x] + srow2[x])*3 + srow1[x]*10;
|
||||
int t1 = srow2[x] - srow0[x];
|
||||
trow0[x] = (deriv_type)t0;
|
||||
trow1[x] = (deriv_type)t1;
|
||||
}
|
||||
|
||||
// make border
|
||||
int x0 = (cols > 1 ? 1 : 0)*cn, x1 = (cols > 1 ? cols-2 : 0)*cn;
|
||||
for( int k = 0; k < cn; k++ )
|
||||
{
|
||||
trow0[-cn + k] = trow0[x0 + k]; trow0[colsn + k] = trow0[x1 + k];
|
||||
trow1[-cn + k] = trow1[x0 + k]; trow1[colsn + k] = trow1[x1 + k];
|
||||
}
|
||||
|
||||
// do horizontal convolution, interleave the results and store them to dst
|
||||
x = 0;
|
||||
#if (CV_SIMD)
|
||||
{
|
||||
for( ; x <= colsn - vlanes; x += vlanes )
|
||||
{
|
||||
v_int16 s0 = vx_load(trow0 + x - cn);
|
||||
v_int16 s1 = vx_load(trow0 + x + cn);
|
||||
v_int16 s2 = vx_load(trow1 + x - cn);
|
||||
v_int16 s3 = vx_load(trow1 + x);
|
||||
v_int16 s4 = vx_load(trow1 + x + cn);
|
||||
|
||||
v_int16 t0 = v_sub(s1, s0);
|
||||
v_int16 t1 = v_add(v_mul_wrap(v_add(s2, s4), c3), v_mul_wrap(s3, c10));
|
||||
|
||||
v_store_interleave((drow + x*2), t0, t1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for( ; x < colsn; x++ )
|
||||
{
|
||||
deriv_type t0 = (deriv_type)(trow0[x+cn] - trow0[x-cn]);
|
||||
deriv_type t1 = (deriv_type)((trow1[x+cn] + trow1[x-cn])*3 + trow1[x]*10);
|
||||
drow[x*2] = t0; drow[x*2+1] = t1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_END
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cv
|
||||
|
||||
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||
@@ -607,9 +607,19 @@ enum { CAP_PROP_IOS_DEVICE_FOCUS = 9001,
|
||||
enum { CAP_PROP_GIGA_FRAME_OFFSET_X = 10001,
|
||||
CAP_PROP_GIGA_FRAME_OFFSET_Y = 10002,
|
||||
CAP_PROP_GIGA_FRAME_WIDTH_MAX = 10003,
|
||||
CAP_PROP_GIGA_FRAME_HEIGH_MAX = 10004,
|
||||
CAP_PROP_GIGA_FRAME_HEIGHT_MAX = 10004,
|
||||
// Typo in pre-5.x sources. Remain for source compatibility
|
||||
#if CV_VERSION_MAJOR <= 4
|
||||
CAP_PROP_GIGA_FRAME_HEIGH_MAX = CAP_PROP_GIGA_FRAME_HEIGHT_MAX, //!< @deprecated
|
||||
#endif
|
||||
|
||||
CAP_PROP_GIGA_FRAME_SENS_WIDTH = 10005,
|
||||
CAP_PROP_GIGA_FRAME_SENS_HEIGH = 10006
|
||||
CAP_PROP_GIGA_FRAME_SENS_HEIGHT = 10006
|
||||
// Typo in pre-5.x sources. Remain for source compatibility
|
||||
#if CV_VERSION_MAJOR <= 4
|
||||
,
|
||||
CAP_PROP_GIGA_FRAME_SENS_HEIGH = CAP_PROP_GIGA_FRAME_SENS_HEIGHT //!< @deprecated
|
||||
#endif
|
||||
};
|
||||
|
||||
//! @} Smartek
|
||||
|
||||
@@ -373,7 +373,7 @@ void MSMFStreamChannel::start(const StreamProfile& profile, FrameCallback frameC
|
||||
break;
|
||||
}
|
||||
}
|
||||
streamState_ = quit ? streamState_ : STREAM_STOPED;
|
||||
streamState_ = quit ? streamState_ : STREAM_STOPPED;
|
||||
}
|
||||
|
||||
void MSMFStreamChannel::stop()
|
||||
@@ -385,7 +385,7 @@ void MSMFStreamChannel::stop()
|
||||
streamReader_->Flush(currentStreamIndex_);
|
||||
std::unique_lock<std::mutex> lk(streamStateMutex_);
|
||||
streamStateCv_.wait_for(lk, std::chrono::milliseconds(1000), [&]() {
|
||||
return streamState_ == STREAM_STOPED;
|
||||
return streamState_ == STREAM_STOPPED;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -474,7 +474,7 @@ STDMETHODIMP MSMFStreamChannel::OnReadSample(HRESULT hrStatus, DWORD dwStreamInd
|
||||
streamStateCv_.notify_all();
|
||||
}
|
||||
|
||||
if (streamState_ != STREAM_STOPPING && streamState_ != STREAM_STOPED)
|
||||
if (streamState_ != STREAM_STOPPING && streamState_ != STREAM_STOPPED)
|
||||
{
|
||||
HR_FAILED_LOG(streamReader_->ReadSample(dwStreamIndex, 0, nullptr, nullptr, nullptr, nullptr));
|
||||
if (sample)
|
||||
@@ -505,10 +505,10 @@ STDMETHODIMP MSMFStreamChannel::OnEvent(DWORD /*sidx*/, IMFMediaEvent* /*event*/
|
||||
|
||||
STDMETHODIMP MSMFStreamChannel::OnFlush(DWORD)
|
||||
{
|
||||
if (streamState_ != STREAM_STOPED)
|
||||
if (streamState_ != STREAM_STOPPED)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(streamStateMutex_);
|
||||
streamState_ = STREAM_STOPED;
|
||||
streamState_ = STREAM_STOPPED;
|
||||
streamStateCv_.notify_all();
|
||||
}
|
||||
return S_OK;
|
||||
|
||||
@@ -176,7 +176,7 @@ Ptr<IStreamChannel> V4L2Context::createStreamChannel(const UvcDeviceInfo& devInf
|
||||
|
||||
V4L2StreamChannel::V4L2StreamChannel(const UvcDeviceInfo &devInfo) : IUvcStreamChannel(devInfo),
|
||||
devFd_(-1),
|
||||
streamState_(STREAM_STOPED)
|
||||
streamState_(STREAM_STOPPED)
|
||||
{
|
||||
|
||||
devFd_ = open(devInfo_.id.c_str(), O_RDWR | O_NONBLOCK, 0);
|
||||
@@ -203,7 +203,7 @@ V4L2StreamChannel::~V4L2StreamChannel() noexcept
|
||||
|
||||
void V4L2StreamChannel::start(const StreamProfile& profile, FrameCallback frameCallback)
|
||||
{
|
||||
if (streamState_ != STREAM_STOPED)
|
||||
if (streamState_ != STREAM_STOPPED)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, devInfo_.id << ": repetitive operation!")
|
||||
return;
|
||||
@@ -248,7 +248,7 @@ void V4L2StreamChannel::start(const StreamProfile& profile, FrameCallback frameC
|
||||
streamState_ = STREAM_STARTING;
|
||||
uint32_t type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
IOCTL_FAILED_EXEC(xioctl(devFd_, VIDIOC_STREAMON, &type), {
|
||||
streamState_ = STREAM_STOPED;
|
||||
streamState_ = STREAM_STOPPED;
|
||||
for (uint32_t i = 0; i < MAX_FRAME_BUFFER_NUM; i++)
|
||||
{
|
||||
if (frameBuffList[i].ptr)
|
||||
@@ -279,7 +279,7 @@ void V4L2StreamChannel::grabFrame()
|
||||
|
||||
IOCTL_FAILED_EXEC(xioctl(devFd_, VIDIOC_QBUF, &buf), {
|
||||
std::unique_lock<std::mutex> lk(streamStateMutex_);
|
||||
streamState_ = STREAM_STOPED;
|
||||
streamState_ = STREAM_STOPPED;
|
||||
streamStateCv_.notify_all();
|
||||
return;
|
||||
});
|
||||
@@ -303,7 +303,7 @@ void V4L2StreamChannel::grabFrame()
|
||||
IOCTL_FAILED_CONTINUE(xioctl(devFd_, VIDIOC_QBUF, &buf));
|
||||
}
|
||||
std::unique_lock<std::mutex> lk(streamStateMutex_);
|
||||
streamState_ = STREAM_STOPED;
|
||||
streamState_ = STREAM_STOPPED;
|
||||
streamStateCv_.notify_all();
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ void V4L2StreamChannel::stop()
|
||||
streamState_ = STREAM_STOPPING;
|
||||
std::unique_lock<std::mutex> lk(streamStateMutex_);
|
||||
streamStateCv_.wait_for(lk, std::chrono::milliseconds(1000), [&](){
|
||||
return streamState_ == STREAM_STOPED;
|
||||
return streamState_ == STREAM_STOPPED;
|
||||
});
|
||||
uint32_t type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
IOCTL_FAILED_LOG(xioctl(devFd_, VIDIOC_STREAMOFF, &type));
|
||||
|
||||
@@ -42,7 +42,7 @@ struct UvcDeviceInfo
|
||||
|
||||
enum StreamState
|
||||
{
|
||||
STREAM_STOPED = 0, // stoped or ready
|
||||
STREAM_STOPPED = 0, // stopped or ready
|
||||
STREAM_STARTING = 1,
|
||||
STREAM_STARTED = 2,
|
||||
STREAM_STOPPING = 3,
|
||||
|
||||
@@ -55,7 +55,7 @@ static void test_readFrames(/*const*/ VideoCapture& capture, const int N = 100,
|
||||
// Check that the time between two camera frames and two system time calls
|
||||
// are within 1.5 frame periods of one another.
|
||||
//
|
||||
// 1.5x is chosen to accomodate for a dropped frame, and an additional 50%
|
||||
// 1.5x is chosen to accommodate for a dropped frame, and an additional 50%
|
||||
// to account for drift in the scale of the camera and system time domains.
|
||||
EXPECT_NEAR(sysTimeElapsedSecs, camTimeElapsedSecs, framePeriod * 1.5);
|
||||
}
|
||||
|
||||
@@ -851,7 +851,7 @@ TEST(videoio_ffmpeg, DISABLED_open_from_web)
|
||||
if (!videoio_registry::hasBackend(CAP_FFMPEG))
|
||||
throw SkipTestException("FFmpeg backend was not found");
|
||||
|
||||
string video_file = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
|
||||
string video_file = "https://dl.opencv.org/data/BigBuckBunny.mp4";
|
||||
VideoCapture cap(video_file, CAP_FFMPEG);
|
||||
int n_frames = -1;
|
||||
EXPECT_NO_THROW(n_frames = (int)cap.get(CAP_PROP_FRAME_COUNT));
|
||||
|
||||
@@ -91,6 +91,7 @@ static void help(char** argv)
|
||||
" # the calibration grid. If this parameter is specified, a more\n"
|
||||
" # accurate calibration method will be used which may be better\n"
|
||||
" # with inaccurate, roughly planar target.\n"
|
||||
" [-imread-apply-exif-rot] # use this flag to apply the exif orientation when reading images from list\n"
|
||||
" [input_data] # input data, one of the following:\n"
|
||||
" # - text file with a list of the images of the board\n"
|
||||
" # the text file can be generated with imagelist_creator\n"
|
||||
@@ -409,6 +410,7 @@ int main( int argc, char** argv )
|
||||
"{oo||}{ws|11|}{dt||}"
|
||||
"{fx||}{fy||}{cx||}{cy||}"
|
||||
"{imshow-scale|1|}{enable-k3|0|}"
|
||||
"{imread-apply-exif-rot||}"
|
||||
"{@input_data|0|}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
@@ -498,11 +500,16 @@ int main( int argc, char** argv )
|
||||
}
|
||||
int viewScaleFactor = parser.get<int>("imshow-scale");
|
||||
bool useK3 = parser.get<bool>("enable-k3");
|
||||
std::cout << "Use K3 distortion coefficient? " << useK3 << std::endl;
|
||||
std::cout << "Use K3 distortion coefficient? " << (useK3 ? "yes" : "no") << std::endl;
|
||||
if (!useK3)
|
||||
{
|
||||
flags |= CALIB_FIX_K3;
|
||||
}
|
||||
int imread_flag = IMREAD_COLOR;
|
||||
if (!parser.has("imread-apply-exif-rot"))
|
||||
{
|
||||
imread_flag |= IMREAD_IGNORE_ORIENTATION;
|
||||
}
|
||||
|
||||
float grid_width = squareSize *(pattern != CHARUCOBOARD ? (boardSize.width - 1): (boardSize.width - 2) );
|
||||
bool release_object = false;
|
||||
@@ -530,20 +537,27 @@ int main( int argc, char** argv )
|
||||
return fprintf( stderr, "Invalid board height\n" ), -1;
|
||||
|
||||
cv::aruco::Dictionary dictionary;
|
||||
if (dictFilename == "None") {
|
||||
std::cout << "Using predefined dictionary with id: " << arucoDict << std::endl;
|
||||
dictionary = aruco::getPredefinedDictionary(arucoDict);
|
||||
}
|
||||
else {
|
||||
std::cout << "Using custom dictionary from file: " << dictFilename << std::endl;
|
||||
cv::FileStorage dict_file(dictFilename, cv::FileStorage::Mode::READ);
|
||||
cv::FileNode fn(dict_file.root());
|
||||
dictionary.readDictionary(fn);
|
||||
if (pattern == CHARUCOBOARD) {
|
||||
if (dictFilename == "None") {
|
||||
std::cout << "Using predefined dictionary with id: " << arucoDict << std::endl;
|
||||
dictionary = aruco::getPredefinedDictionary(arucoDict);
|
||||
}
|
||||
else {
|
||||
std::cout << "Using custom dictionary from file: " << dictFilename << std::endl;
|
||||
cv::FileStorage dict_file(dictFilename, cv::FileStorage::Mode::READ);
|
||||
cv::FileNode fn(dict_file.root());
|
||||
dictionary.readDictionary(fn);
|
||||
}
|
||||
}
|
||||
|
||||
cv::aruco::CharucoBoard ch_board(boardSize, squareSize, markerSize, dictionary);
|
||||
cv::Ptr<cv::aruco::CharucoBoard> ch_board;
|
||||
std::vector<int> markerIds;
|
||||
cv::aruco::CharucoDetector ch_detector(ch_board);
|
||||
cv::Ptr<cv::aruco::CharucoDetector> ch_detector;
|
||||
if (pattern == CHARUCOBOARD) {
|
||||
ch_board = cv::makePtr<cv::aruco::CharucoBoard>(boardSize, squareSize, markerSize, dictionary);
|
||||
ch_detector = cv::makePtr<cv::aruco::CharucoDetector>(cv::aruco::CharucoDetector(*ch_board));
|
||||
}
|
||||
|
||||
|
||||
if( !inputFilename.empty() )
|
||||
{
|
||||
@@ -564,7 +578,7 @@ int main( int argc, char** argv )
|
||||
if( capture.isOpened() )
|
||||
printf( "%s", liveCaptureHelp );
|
||||
|
||||
namedWindow( "Image View", 1 );
|
||||
namedWindow( "Image View", cv::WINDOW_AUTOSIZE );
|
||||
|
||||
for(i = 0;;i++)
|
||||
{
|
||||
@@ -578,7 +592,7 @@ int main( int argc, char** argv )
|
||||
view0.copyTo(view);
|
||||
}
|
||||
else if( i < (int)imageList.size() )
|
||||
view = imread(imageList[i], IMREAD_COLOR);
|
||||
view = imread(imageList[i], imread_flag);
|
||||
|
||||
if(view.empty())
|
||||
{
|
||||
@@ -613,7 +627,7 @@ int main( int argc, char** argv )
|
||||
break;
|
||||
case CHARUCOBOARD:
|
||||
{
|
||||
ch_detector.detectBoard(view, pointbuf, markerIds);
|
||||
ch_detector->detectBoard(view, pointbuf, markerIds);
|
||||
found = pointbuf.size() == (size_t)(boardSize.width-1)*(boardSize.height-1);
|
||||
break;
|
||||
}
|
||||
@@ -714,7 +728,7 @@ int main( int argc, char** argv )
|
||||
|
||||
for( i = 0; i < (int)imageList.size(); i++ )
|
||||
{
|
||||
view = imread(imageList[i], IMREAD_COLOR);
|
||||
view = imread(imageList[i], imread_flag);
|
||||
if(view.empty())
|
||||
continue;
|
||||
remap(view, rview, map1, map2, INTER_LINEAR);
|
||||
|
||||