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

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2026-03-19 09:19:35 +03:00
67 changed files with 1881 additions and 555 deletions
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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;
Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

+5 -5
View File
@@ -185,7 +185,7 @@ Z_w \\
The following figure illustrates the pinhole camera model.
![Pinhole camera model](pics/pinhole_camera_model.png)
![Pinhole camera model](pics/pinhole_camera_model.png) { 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:
![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.png){ width=50% }
![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.jpg){ 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.
![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.png){ width=50% }
![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.jpg){ 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.
![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.png){ width=50% }
![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.jpg){ 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.
![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.png){ width=50% }
![Perspective projection, from object to camera frame](pics/pinhole_homogeneous_transformation.jpg){ width=50% }
@see @ref calib3d_solvePnP
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 39 KiB

+1 -1
View File
@@ -180,7 +180,7 @@ Z_w \\
The following figure illustrates the pinhole camera model.
![Pinhole camera model](pics/pinhole_camera_model.png)
![Pinhole camera model](pics/pinhole_camera_model.png) { 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
-16
View File
@@ -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));
}
+5 -4
View File
@@ -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)
+202 -180
View File
@@ -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 )
+3 -2
View File
@@ -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;
+12
View File
@@ -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
+15
View File
@@ -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" };
+3 -3
View File
@@ -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())
+1 -1
View File
@@ -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)
{
+1 -1
View File
@@ -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());
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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;
};
+1 -1
View File
@@ -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)
{
+1 -1
View File
@@ -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,
+3 -3
View File
@@ -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()
+1 -1
View File
@@ -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();
+2 -2
View File
@@ -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
+4 -2
View File
@@ -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,
+131 -26
View File
@@ -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
+148
View File
@@ -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
+10 -10
View File
@@ -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.
+35
View File
@@ -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
+384 -102
View File
@@ -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});
+25 -2
View File
@@ -130,8 +130,31 @@ void findSecondPoint(const PT *pts, int i, Point2f &center, float &radius)
template<typename PT>
static void findMinEnclosingCircle(const PT *pts, int count, Point2f &center, float &radius)
static void findMinEnclosingCircle(const PT *pts_in, int count, Point2f &center, 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);
+3
View File
@@ -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);
+27
View File
@@ -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)
+44 -51
View File
@@ -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);
+149 -2
View File
@@ -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);
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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), \
+1 -1
View File
@@ -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
View File
@@ -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
+2 -79
View File
@@ -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(
+22
View File
@@ -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
+2
View File
@@ -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)
+118
View File
@@ -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
+12 -2
View File
@@ -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,
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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));
+30 -16
View File
@@ -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);