From b56f338c8b357e924ccc40b71e0e8d1dc72f312e Mon Sep 17 00:00:00 2001 From: Ola Olsson <12532102+saolaolsson@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:46:30 +0100 Subject: [PATCH 01/27] Make undistortImagePoints default criteria undistortPoints consistent --- modules/calib3d/include/opencv2/calib3d.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index f44dacbedf..e976227347 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -3770,8 +3770,7 @@ CV_64FC2) (or vector\ ). CV_EXPORTS_W void undistortImagePoints(InputArray src, OutputArray dst, InputArray cameraMatrix, InputArray distCoeffs, - TermCriteria = TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, - 0.01)); + TermCriteria = TermCriteria(TermCriteria::MAX_ITER, 5, 0.01)); //! @} calib3d From 3f603f32321947b0198b4b1473d17243a06b836c Mon Sep 17 00:00:00 2001 From: Dimitre Date: Thu, 18 Sep 2025 14:52:48 -0300 Subject: [PATCH 02/27] typo COMPENSACTION -> COMPENSATION --- modules/videoio/src/cap_gphoto2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/videoio/src/cap_gphoto2.cpp b/modules/videoio/src/cap_gphoto2.cpp index f285ba9764..acf0071b44 100644 --- a/modules/videoio/src/cap_gphoto2.cpp +++ b/modules/videoio/src/cap_gphoto2.cpp @@ -153,7 +153,7 @@ public: protected: // Known widget names - static const char * PROP_EXPOSURE_COMPENSACTION; + static const char * PROP_EXPOSURE_COMPENSATION; static const char * PROP_SELF_TIMER_DELAY; static const char * PROP_MANUALFOCUS; static const char * PROP_AUTOFOCUS; @@ -294,7 +294,7 @@ const char * DigitalCameraCapture::lineDelimiter = "\n"; * Those are actually substrings of widget name. * ie. for VIEWFINDER, Nikon uses "viewfinder", while Canon can use "eosviewfinder". */ -const char * DigitalCameraCapture::PROP_EXPOSURE_COMPENSACTION = +const char * DigitalCameraCapture::PROP_EXPOSURE_COMPENSATION = "exposurecompensation"; const char * DigitalCameraCapture::PROP_SELF_TIMER_DELAY = "selftimerdelay"; const char * DigitalCameraCapture::PROP_MANUALFOCUS = "manualfocusdrive"; @@ -555,7 +555,7 @@ CameraWidget * DigitalCameraCapture::getGenericProperty(int propertyId, return NULL; } case CAP_PROP_EXPOSURE: - return findWidgetByName(PROP_EXPOSURE_COMPENSACTION); + return findWidgetByName(PROP_EXPOSURE_COMPENSATION); case CAP_PROP_TRIGGER_DELAY: return findWidgetByName(PROP_SELF_TIMER_DELAY); case CAP_PROP_ZOOM: @@ -692,7 +692,7 @@ CameraWidget * DigitalCameraCapture::setGenericProperty(int propertyId, output = false; return NULL; case CAP_PROP_EXPOSURE: - return findWidgetByName(PROP_EXPOSURE_COMPENSACTION); + return findWidgetByName(PROP_EXPOSURE_COMPENSATION); case CAP_PROP_TRIGGER_DELAY: return findWidgetByName(PROP_SELF_TIMER_DELAY); case CAP_PROP_ZOOM: From 67bece1d9926260a32a23206d6278cc7a2e36b9e Mon Sep 17 00:00:00 2001 From: Stefania Hergane Date: Mon, 17 Nov 2025 12:28:10 +0200 Subject: [PATCH 03/27] Update usage of ov::Tensor::data in govbackend.cpp Signed-off-by: StefaniaHergane --- modules/gapi/src/backends/ov/govbackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gapi/src/backends/ov/govbackend.cpp b/modules/gapi/src/backends/ov/govbackend.cpp index 4ea1c1cc0f..6a787c03e9 100644 --- a/modules/gapi/src/backends/ov/govbackend.cpp +++ b/modules/gapi/src/backends/ov/govbackend.cpp @@ -185,7 +185,7 @@ static void copyFromOV(const ov::Tensor &tensor, cv::Mat &mat) { mat.ptr(), total); } else { - std::copy_n(reinterpret_cast(tensor.data()), + std::copy_n(reinterpret_cast(tensor.data()), tensor.get_byte_size(), mat.ptr()); } From 01cce12ce7078c77a2603b523bf03bc19b7f7acc Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 19 Nov 2025 10:20:06 +0100 Subject: [PATCH 04/27] Avoid integer overflow in BmpDecoder::readData This solves https://g-issues.oss-fuzz.com/issues/457623761 --- modules/imgcodecs/src/grfmt_bmp.cpp | 6 +++--- modules/imgcodecs/src/utils.cpp | 8 ++++---- modules/imgcodecs/src/utils.hpp | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_bmp.cpp b/modules/imgcodecs/src/grfmt_bmp.cpp index 4540d3cd93..77201b3af1 100644 --- a/modules/imgcodecs/src/grfmt_bmp.cpp +++ b/modules/imgcodecs/src/grfmt_bmp.cpp @@ -337,7 +337,7 @@ bool BmpDecoder::readData( Mat& img ) } else { - int x_shift3 = (int)(line_end - data); + ptrdiff_t x_shift3 = line_end - data; if( code == 2 ) { @@ -430,7 +430,7 @@ decode_rle4_bad: ; } else { - int x_shift3 = (int)(line_end - data); + ptrdiff_t x_shift3 = line_end - data; int y_shift = m_height - y; if( code || !line_end_flag || x_shift3 < width3 ) @@ -441,7 +441,7 @@ decode_rle4_bad: ; y_shift = m_strm.getByte(); } - x_shift3 += (y_shift * width3) & ((code == 0) - 1); + x_shift3 += ((ptrdiff_t)y_shift * width3) & ((code == 0) - 1); if( y >= m_height ) break; diff --git a/modules/imgcodecs/src/utils.cpp b/modules/imgcodecs/src/utils.cpp index 41fd9f5041..3b597fe61c 100644 --- a/modules/imgcodecs/src/utils.cpp +++ b/modules/imgcodecs/src/utils.cpp @@ -435,7 +435,7 @@ bool IsColorPalette( PaletteEntry* palette, int bpp ) uchar* FillUniColor( uchar* data, uchar*& line_end, int step, int width3, int& y, int height, - int count3, PaletteEntry clr ) + ptrdiff_t count3, PaletteEntry clr ) { do { @@ -444,7 +444,7 @@ uchar* FillUniColor( uchar* data, uchar*& line_end, if( end > line_end ) end = line_end; - count3 -= (int)(end - data); + count3 -= end - data; for( ; data < end; data += 3 ) { @@ -467,7 +467,7 @@ uchar* FillUniColor( uchar* data, uchar*& line_end, uchar* FillUniGray( uchar* data, uchar*& line_end, int step, int width, int& y, int height, - int count, uchar clr ) + ptrdiff_t count, uchar clr ) { do { @@ -476,7 +476,7 @@ uchar* FillUniGray( uchar* data, uchar*& line_end, if( end > line_end ) end = line_end; - count -= (int)(end - data); + count -= end - data; for( ; data < end; data++ ) { diff --git a/modules/imgcodecs/src/utils.hpp b/modules/imgcodecs/src/utils.hpp index 95deebde31..8b0ad1a9e9 100644 --- a/modules/imgcodecs/src/utils.hpp +++ b/modules/imgcodecs/src/utils.hpp @@ -124,9 +124,9 @@ void FillGrayPalette( PaletteEntry* palette, int bpp, bool negative = false ); bool IsColorPalette( PaletteEntry* palette, int bpp ); void CvtPaletteToGray( const PaletteEntry* palette, uchar* grayPalette, int entries ); uchar* FillUniColor( uchar* data, uchar*& line_end, int step, int width3, - int& y, int height, int count3, PaletteEntry clr ); + int& y, int height, ptrdiff_t count3, PaletteEntry clr ); uchar* FillUniGray( uchar* data, uchar*& line_end, int step, int width3, - int& y, int height, int count3, uchar clr ); + int& y, int height, ptrdiff_t count3, uchar clr ); uchar* FillColorRow8( uchar* data, uchar* indices, int len, PaletteEntry* palette ); uchar* FillGrayRow8( uchar* data, uchar* indices, int len, uchar* palette ); From 308631197f2a982b1ffdbfb418658e3909c3bba0 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 20 Nov 2025 08:55:03 +0300 Subject: [PATCH 05/27] Improved HAL status check for filter, sepFilter and morphology operations to support stateless HAL. --- modules/imgproc/src/filter.dispatch.cpp | 48 +++++++++++++++++++++---- modules/imgproc/src/morph.dispatch.cpp | 21 +++++++++-- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/modules/imgproc/src/filter.dispatch.cpp b/modules/imgproc/src/filter.dispatch.cpp index 081614c95d..a1568d249c 100644 --- a/modules/imgproc/src/filter.dispatch.cpp +++ b/modules/imgproc/src/filter.dispatch.cpp @@ -1174,13 +1174,31 @@ static bool replacementFilter2D(int stype, int dtype, int kernel_type, cvhalFilter2D* ctx; int res = cv_hal_filterInit(&ctx, kernel_data, kernel_step, kernel_type, kernel_width, kernel_height, width, height, stype, dtype, borderType, delta, anchor_x, anchor_y, isSubmatrix, src_data == dst_data); - if (res != CV_HAL_ERROR_OK) + if (res == CV_HAL_ERROR_NOT_IMPLEMENTED) + { return false; + } else if (res != CV_HAL_ERROR_OK) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation filterInit ==> " CVAUX_STR(cv_hal_filterInit) " returned %d (0x%08x)", res, res)); + } + res = cv_hal_filter(ctx, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); bool success = (res == CV_HAL_ERROR_OK); + if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED ) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation filter ==> " CVAUX_STR(cv_hal_filter) " returned %d (0x%08x)", res, res)); + } + res = cv_hal_filterFree(ctx); - if (res != CV_HAL_ERROR_OK) - return false; + success &= (res == CV_HAL_ERROR_OK); + if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED ) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation filterFree ==> " CVAUX_STR(cv_hal_filterFree) " returned %d (0x%08x)", res, res)); + } + return success; } @@ -1372,13 +1390,31 @@ static bool replacementSepFilter(int stype, int dtype, int ktype, kernelx_data, kernelx_len, kernely_data, kernely_len, anchor_x, anchor_y, delta, borderType); - if (res != CV_HAL_ERROR_OK) + if (res == CV_HAL_ERROR_NOT_IMPLEMENTED) + { return false; + } else if (res != CV_HAL_ERROR_OK) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation sepFilterInit ==> " CVAUX_STR(cv_hal_sepFilterInit) " returned %d (0x%08x)", res, res)); + } + res = cv_hal_sepFilter(ctx, src_data, src_step, dst_data, dst_step, width, height, full_width, full_height, offset_x, offset_y); bool success = (res == CV_HAL_ERROR_OK); + if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED ) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation sepFilter ==> " CVAUX_STR(cv_hal_sepFilter) " returned %d (0x%08x)", res, res)); + } + res = cv_hal_sepFilterFree(ctx); - if (res != CV_HAL_ERROR_OK) - return false; + success &= (res == CV_HAL_ERROR_OK); + if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED ) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation sepFilterFree ==> " CVAUX_STR(cv_hal_sepFilterFree) " returned %d (0x%08x)", res, res)); + } + return success; } diff --git a/modules/imgproc/src/morph.dispatch.cpp b/modules/imgproc/src/morph.dispatch.cpp index 714050ccf1..98adbf10c1 100644 --- a/modules/imgproc/src/morph.dispatch.cpp +++ b/modules/imgproc/src/morph.dispatch.cpp @@ -218,8 +218,14 @@ static bool halMorph(int op, int src_type, int dst_type, anchor_x, anchor_y, borderType, borderValue, iterations, isSubmatrix, src_data == dst_data); - if (res != CV_HAL_ERROR_OK) + if (res == CV_HAL_ERROR_NOT_IMPLEMENTED) + { return false; + } else if (res != CV_HAL_ERROR_OK) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation morphInit ==> " CVAUX_STR(cv_hal_morphInit) " returned %d (0x%08x)", res, res)); + } res = cv_hal_morph(ctx, src_data, src_step, dst_data, dst_step, width, height, roi_width, roi_height, @@ -227,10 +233,19 @@ static bool halMorph(int op, int src_type, int dst_type, roi_width2, roi_height2, roi_x2, roi_y2); bool success = (res == CV_HAL_ERROR_OK); + if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED ) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation morph ==> " CVAUX_STR(cv_hal_morph) " returned %d (0x%08x)", res, res)); + } res = cv_hal_morphFree(ctx); - if (res != CV_HAL_ERROR_OK) - return false; + success &= (res == CV_HAL_ERROR_OK); + if (res != CV_HAL_ERROR_OK && res != CV_HAL_ERROR_NOT_IMPLEMENTED ) + { + CV_Error_(cv::Error::StsInternal, + ("HAL implementation morphFree ==> " CVAUX_STR(cv_hal_morphFree) " returned %d (0x%08x)", res, res)); + } return success; } From f7d3850183a2e31e6f05bef7373bd9f75b6ee2c3 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 7 Nov 2025 12:39:28 +0300 Subject: [PATCH 06/27] Use rounding intrinsic on Windows for ARM. --- modules/core/include/opencv2/core/fast_math.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/include/opencv2/core/fast_math.hpp b/modules/core/include/opencv2/core/fast_math.hpp index e136327207..370f5b439b 100644 --- a/modules/core/include/opencv2/core/fast_math.hpp +++ b/modules/core/include/opencv2/core/fast_math.hpp @@ -201,7 +201,7 @@ cvRound( double value ) { #if defined CV_INLINE_ROUND_DBL CV_INLINE_ROUND_DBL(value); -#elif defined _MSC_VER && defined _M_ARM64 +#elif defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC)) float64x1_t v = vdup_n_f64(value); int64x1_t r = vcvtn_s64_f64(v); return static_cast(vget_lane_s64(r, 0)); @@ -327,7 +327,7 @@ CV_INLINE int cvRound(float value) { #if defined CV_INLINE_ROUND_FLT CV_INLINE_ROUND_FLT(value); -#elif defined _MSC_VER && defined _M_ARM64 +#elif defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC)) float32x2_t v = vdup_n_f32(value); int32x2_t r = vcvtn_s32_f32(v); return vget_lane_s32(r, 0); From 35f82d4599f965f170abe9eb740b06d7ee1aab9e Mon Sep 17 00:00:00 2001 From: JayPol999 Date: Thu, 20 Nov 2025 23:24:55 +0530 Subject: [PATCH 07/27] Fix OpenBLAS detection on Fedora/RHEL (Issue #28049) --- cmake/OpenCVFindOpenBLAS.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVFindOpenBLAS.cmake b/cmake/OpenCVFindOpenBLAS.cmake index 15771f7ebb..2bab4f109a 100644 --- a/cmake/OpenCVFindOpenBLAS.cmake +++ b/cmake/OpenCVFindOpenBLAS.cmake @@ -20,8 +20,10 @@ if(NOT OpenBLAS_FOUND) endif() if(NOT OpenBLAS_FOUND) - find_library(OpenBLAS_LIBRARIES NAMES openblas) - find_path(OpenBLAS_INCLUDE_DIRS NAMES cblas.h) + find_library(OpenBLAS_LIBRARIES NAMES openblasp openblas) + find_path(OpenBLAS_INCLUDE_DIRS + NAMES cblas.h + PATH_SUFFIXES openblas) find_path(OpenBLAS_LAPACKE_DIR NAMES lapacke.h PATHS "${OpenBLAS_INCLUDE_DIRS}") if(OpenBLAS_LIBRARIES AND OpenBLAS_INCLUDE_DIRS) message(STATUS "Found OpenBLAS in the system") From fdf033295428e6c7fa67335019d004f05f7a6080 Mon Sep 17 00:00:00 2001 From: Pierre Chatelier Date: Fri, 21 Nov 2025 06:41:12 +0100 Subject: [PATCH 08/27] Merge pull request #23913 from chacha21:GpuMatND_InputOutputArray make cuda::GpuMatND compatible with InputArray/OutputArray #23913 continuation of [PR#19259](https://github.com/opencv/opencv/pull/19259) Make cuda::GpuMatND wrappable in InputArray/OutputArray The goal for now is just wrapping, some functions are not supported (InputArray::size(), InputArray::convertTo(), InputArray::assign()...) No new feature for cuda::GpuMatND - [X] I agree to contribute to the project under Apache 2 License. - [X] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [X] The PR is proposed to the proper branch - [X] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/core/include/opencv2/core/base.hpp | 1 + modules/core/include/opencv2/core/mat.hpp | 11 +- modules/core/include/opencv2/core/mat.inl.hpp | 19 ++- modules/core/src/matrix_wrap.cpp | 138 ++++++++++++++++++ 4 files changed, 166 insertions(+), 3 deletions(-) diff --git a/modules/core/include/opencv2/core/base.hpp b/modules/core/include/opencv2/core/base.hpp index 63c0a01b50..e295a5f7e1 100644 --- a/modules/core/include/opencv2/core/base.hpp +++ b/modules/core/include/opencv2/core/base.hpp @@ -698,6 +698,7 @@ namespace ogl namespace cuda { class CV_EXPORTS GpuMat; + class CV_EXPORTS GpuMatND; class CV_EXPORTS HostMem; class CV_EXPORTS Stream; class CV_EXPORTS Event; diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index e8cb786e3f..7a260abe04 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -185,7 +185,8 @@ public: #if OPENCV_ABI_COMPATIBILITY < 500 STD_ARRAY =14 << KIND_SHIFT, //!< removed: https://github.com/opencv/opencv/issues/18897 #endif - STD_ARRAY_MAT =15 << KIND_SHIFT + STD_ARRAY_MAT =15 << KIND_SHIFT, + CUDA_GPU_MATND =16 << KIND_SHIFT }; _InputArray(); @@ -204,6 +205,7 @@ public: _InputArray(const double& val); _InputArray(const cuda::GpuMat& d_mat); _InputArray(const std::vector& d_mat_array); + _InputArray(const cuda::GpuMatND& d_mat); _InputArray(const ogl::Buffer& buf); _InputArray(const cuda::HostMem& cuda_mem); template _InputArray(const cudev::GpuMat_<_Tp>& m); @@ -223,6 +225,7 @@ public: void getUMatVector(std::vector& umv) const; void getGpuMatVector(std::vector& gpumv) const; cuda::GpuMat getGpuMat() const; + cuda::GpuMatND getGpuMatND() const; ogl::Buffer getOGlBuffer() const; int getFlags() const; @@ -255,6 +258,7 @@ public: bool isVector() const; bool isGpuMat() const; bool isGpuMatVector() const; + bool isGpuMatND() const; ~_InputArray(); protected: @@ -318,6 +322,7 @@ public: _OutputArray(std::vector& vec); _OutputArray(cuda::GpuMat& d_mat); _OutputArray(std::vector& d_mat); + _OutputArray(cuda::GpuMatND& d_mat); _OutputArray(ogl::Buffer& buf); _OutputArray(cuda::HostMem& cuda_mem); template _OutputArray(cudev::GpuMat_<_Tp>& m); @@ -336,6 +341,7 @@ public: _OutputArray(const std::vector& vec); _OutputArray(const cuda::GpuMat& d_mat); _OutputArray(const std::vector& d_mat); + _OutputArray(const cuda::GpuMatND& d_mat); _OutputArray(const ogl::Buffer& buf); _OutputArray(const cuda::HostMem& cuda_mem); template _OutputArray(const cudev::GpuMat_<_Tp>& m); @@ -363,6 +369,7 @@ public: UMat& getUMatRef(int i=-1) const; cuda::GpuMat& getGpuMatRef() const; std::vector& getGpuMatVecRef() const; + cuda::GpuMatND& getGpuMatNDRef() const; ogl::Buffer& getOGlBufferRef() const; cuda::HostMem& getHostMemRef() const; void create(Size sz, int type, int i=-1, bool allowTransposed=false, _OutputArray::DepthMask fixedDepthMask=static_cast<_OutputArray::DepthMask>(0)) const; @@ -393,6 +400,7 @@ public: _InputOutputArray(Mat& m); _InputOutputArray(std::vector& vec); _InputOutputArray(cuda::GpuMat& d_mat); + _InputOutputArray(cuda::GpuMatND& d_mat); _InputOutputArray(ogl::Buffer& buf); _InputOutputArray(cuda::HostMem& cuda_mem); template _InputOutputArray(cudev::GpuMat_<_Tp>& m); @@ -410,6 +418,7 @@ public: _InputOutputArray(const std::vector& vec); _InputOutputArray(const cuda::GpuMat& d_mat); _InputOutputArray(const std::vector& d_mat); + _InputOutputArray(const cuda::GpuMatND& d_mat); _InputOutputArray(const ogl::Buffer& buf); _InputOutputArray(const cuda::HostMem& cuda_mem); template _InputOutputArray(const cudev::GpuMat_<_Tp>& m); diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index d11d5051e9..f034064351 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -151,7 +151,10 @@ inline _InputArray::_InputArray(const cuda::GpuMat& d_mat) { init(+CUDA_GPU_MAT + ACCESS_READ, &d_mat); } inline _InputArray::_InputArray(const std::vector& d_mat) -{ init(+STD_VECTOR_CUDA_GPU_MAT + ACCESS_READ, &d_mat);} +{ init(+STD_VECTOR_CUDA_GPU_MAT + ACCESS_READ, &d_mat);} + +inline _InputArray::_InputArray(const cuda::GpuMatND& d_mat) +{ init(+CUDA_GPU_MATND + ACCESS_READ, &d_mat); } inline _InputArray::_InputArray(const ogl::Buffer& buf) { init(+OPENGL_BUFFER + ACCESS_READ, &buf); } @@ -197,6 +200,7 @@ inline bool _InputArray::isVector() const { return kind() == _InputArray::STD_VE (kind() == _InputArray::MATX && (sz.width <= 1 || sz.height <= 1)); } inline bool _InputArray::isGpuMat() const { return kind() == _InputArray::CUDA_GPU_MAT; } inline bool _InputArray::isGpuMatVector() const { return kind() == _InputArray::STD_VECTOR_CUDA_GPU_MAT; } +inline bool _InputArray::isGpuMatND() const { return kind() == _InputArray::CUDA_GPU_MATND; } //////////////////////////////////////////////////////////////////////////////////////// @@ -275,7 +279,10 @@ inline _OutputArray::_OutputArray(cuda::GpuMat& d_mat) { init(+CUDA_GPU_MAT + ACCESS_WRITE, &d_mat); } inline _OutputArray::_OutputArray(std::vector& d_mat) -{ init(+STD_VECTOR_CUDA_GPU_MAT + ACCESS_WRITE, &d_mat);} +{ init(+STD_VECTOR_CUDA_GPU_MAT + ACCESS_WRITE, &d_mat);} + +inline _OutputArray::_OutputArray(cuda::GpuMatND& d_mat) +{ init(+CUDA_GPU_MATND + ACCESS_WRITE, &d_mat); } inline _OutputArray::_OutputArray(ogl::Buffer& buf) { init(+OPENGL_BUFFER + ACCESS_WRITE, &buf); } @@ -298,6 +305,8 @@ inline _OutputArray::_OutputArray(const std::vector& vec) inline _OutputArray::_OutputArray(const cuda::GpuMat& d_mat) { init(FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MAT + ACCESS_WRITE, &d_mat); } +inline _OutputArray::_OutputArray(const cuda::GpuMatND& d_mat) +{ init(+FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MATND + ACCESS_WRITE, &d_mat); } inline _OutputArray::_OutputArray(const ogl::Buffer& buf) { init(FIXED_TYPE + FIXED_SIZE + OPENGL_BUFFER + ACCESS_WRITE, &buf); } @@ -400,6 +409,9 @@ _InputOutputArray::_InputOutputArray(const _Tp* vec, int n) inline _InputOutputArray::_InputOutputArray(cuda::GpuMat& d_mat) { init(+CUDA_GPU_MAT + ACCESS_RW, &d_mat); } +inline _InputOutputArray::_InputOutputArray(cuda::GpuMatND& d_mat) +{ init(+CUDA_GPU_MATND + ACCESS_RW, &d_mat); } + inline _InputOutputArray::_InputOutputArray(ogl::Buffer& buf) { init(+OPENGL_BUFFER + ACCESS_RW, &buf); } @@ -427,6 +439,9 @@ inline _InputOutputArray::_InputOutputArray(const std::vector& d_m template<> inline _InputOutputArray::_InputOutputArray(std::vector& d_mat) { init(FIXED_TYPE + FIXED_SIZE + STD_VECTOR_CUDA_GPU_MAT + ACCESS_RW, &d_mat);} +inline _InputOutputArray::_InputOutputArray(const cuda::GpuMatND& d_mat) +{ init(+FIXED_TYPE + FIXED_SIZE + CUDA_GPU_MATND + ACCESS_RW, &d_mat); } + inline _InputOutputArray::_InputOutputArray(const ogl::Buffer& buf) { init(FIXED_TYPE + FIXED_SIZE + OPENGL_BUFFER + ACCESS_RW, &buf); } diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp index 4ddb89f638..a8366a5f6c 100644 --- a/modules/core/src/matrix_wrap.cpp +++ b/modules/core/src/matrix_wrap.cpp @@ -111,6 +111,12 @@ Mat _InputArray::getMat_(int i) const CV_Error(cv::Error::StsNotImplemented, "You should explicitly call download method for cuda::GpuMat object"); } + if( k == CUDA_GPU_MATND ) + { + CV_Assert( i < 0 ); + CV_Error(cv::Error::StsNotImplemented, "You should explicitly call download method for cuda::GpuMatND object"); + } + if( k == CUDA_HOST_MEM ) { CV_Assert( i < 0 ); @@ -357,6 +363,22 @@ void _InputArray::getGpuMatVector(std::vector& gpumv) const CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)"); #endif } +cuda::GpuMatND _InputArray::getGpuMatND() const +{ +#ifdef HAVE_CUDA + _InputArray::KindFlag k = kind(); + + if (k == CUDA_GPU_MATND) + { + const cuda::GpuMatND* d_mat = (const cuda::GpuMatND*)obj; + return *d_mat; + } + + CV_Error(cv::Error::StsNotImplemented, "getGpuMatND is available only for cuda::GpuMatND"); +#else + CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)"); +#endif +} ogl::Buffer _InputArray::getOGlBuffer() const { _InputArray::KindFlag k = kind(); @@ -379,11 +401,29 @@ _InputArray::KindFlag _InputArray::kind() const int _InputArray::rows(int i) const { +#ifdef HAVE_CUDA + _InputArray::KindFlag k = kind(); + if (k == CUDA_GPU_MATND) + { + const cuda::GpuMatND& _gpuMatND = *(const cuda::GpuMatND*)obj; + return (_gpuMatND.dims < 1) ? 0 : _gpuMatND.size[0]; + } +#endif + return size(i).height; } int _InputArray::cols(int i) const { +#ifdef HAVE_CUDA + _InputArray::KindFlag k = kind(); + if (k == CUDA_GPU_MATND) + { + const cuda::GpuMatND& _gpuMatND = *(const cuda::GpuMatND*)obj; + return (_gpuMatND.dims < 2) ? 0 : _gpuMatND.size[1]; + } +#endif + return size(i).width; } @@ -604,8 +644,13 @@ bool _InputArray::sameSize(const _InputArray& arr) const return false; sz1 = m->size(); } + else if ( (k1 == CUDA_GPU_MATND) && (k2 == CUDA_GPU_MATND)) + { + return ((const cuda::GpuMatND*)obj)->size == ((const cuda::GpuMatND*)arr.obj)->size; + } else sz1 = size(); + if( arr.dims() > 2 ) return false; return sz1 == arr.size(); @@ -693,6 +738,12 @@ int _InputArray::dims(int i) const return 2; } + if( k == CUDA_GPU_MATND ) + { + CV_Assert( i < 0 ); + return ((const cuda::GpuMatND*)obj)->dims; + } + if( k == CUDA_HOST_MEM ) { CV_Assert( i < 0 ); @@ -748,6 +799,21 @@ size_t _InputArray::total(int i) const return vv[i].total(); } + if( k == CUDA_GPU_MATND ) + { + CV_Assert( i < 0 ); + size_t res = 0; + const cuda::GpuMatND& _gpuMatND = *((const cuda::GpuMatND*)obj); + if (_gpuMatND.dims > 0) + { + res = 1; + for(int d = 0 ; d<_gpuMatND.dims ; ++d) + res *= _gpuMatND.size[d]; + return res; + } + } + + return size(i).area(); } @@ -825,6 +891,9 @@ int _InputArray::type(int i) const if( k == CUDA_GPU_MAT ) return ((const cuda::GpuMat*)obj)->type(); + if( k == CUDA_GPU_MATND ) + return ((const cuda::GpuMatND*)obj)->type(); + if( k == CUDA_HOST_MEM ) return ((const cuda::HostMem*)obj)->type(); @@ -904,6 +973,9 @@ bool _InputArray::empty() const return vv.empty(); } + if( k == CUDA_GPU_MATND ) + return ((const cuda::GpuMatND*)obj)->empty(); + if( k == CUDA_HOST_MEM ) return ((const cuda::HostMem*)obj)->empty(); @@ -948,6 +1020,9 @@ bool _InputArray::isContinuous(int i) const if( k == CUDA_GPU_MAT ) return i < 0 ? ((const cuda::GpuMat*)obj)->isContinuous() : true; + if( k == CUDA_GPU_MATND ) + return i < 0 ? ((const cuda::GpuMatND*)obj)->isContinuous() : true; + CV_Error(cv::Error::StsNotImplemented, "Unknown/unsupported array type"); } @@ -986,6 +1061,11 @@ bool _InputArray::isSubmatrix(int i) const return vv[i].isSubmatrix(); } + if( k == CUDA_GPU_MATND ) + { + return ((const cuda::GpuMatND*)obj)->isSubmatrix(); + } + CV_Error(cv::Error::StsNotImplemented, ""); } @@ -1101,6 +1181,12 @@ size_t _InputArray::step(int i) const CV_Assert(i >= 0 && (size_t)i < vv.size()); return vv[i].step; } + if( k == CUDA_GPU_MATND ) + { + const cuda::GpuMatND& _gpuMatND = *(const cuda::GpuMatND*)obj; + CV_Assert( i >= _gpuMatND.dims ); + return _gpuMatND.step[i]; + } CV_Error(Error::StsNotImplemented, ""); } @@ -1183,6 +1269,18 @@ void _OutputArray::create(Size _sz, int mtype, int i, bool allowTransposed, _Out return; #else CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)"); +#endif + } + if( k == CUDA_GPU_MATND && i < 0 && !allowTransposed && fixedDepthMask == 0 ) + { + CV_Assert(!fixedSize() || ((((cuda::GpuMatND*)obj)->dims == 2) && (((cuda::GpuMatND*)obj)->size[0] == _sz.height) && (((cuda::GpuMatND*)obj)->size[1] == _sz.width))); + CV_Assert(!fixedType() || ((cuda::GpuMatND*)obj)->type() == mtype); +#ifdef HAVE_CUDA + cuda::GpuMatND::SizeArray sizes = {_sz.height, _sz.width}; + ((cuda::GpuMatND*)obj)->create(sizes, mtype); + return; +#else + CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)"); #endif } if( k == OPENGL_BUFFER && i < 0 && !allowTransposed && fixedDepthMask == 0 ) @@ -1237,6 +1335,18 @@ void _OutputArray::create(int _rows, int _cols, int mtype, int i, bool allowTran return; #else CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)"); +#endif + } + if( k == CUDA_GPU_MATND && i < 0 && !allowTransposed && fixedDepthMask == 0 ) + { + CV_Assert(!fixedSize() || ((((cuda::GpuMatND*)obj)->dims == 2) && (((cuda::GpuMatND*)obj)->size[0] == _rows) && (((cuda::GpuMatND*)obj)->size[1] == _cols))); + CV_Assert(!fixedType() || ((cuda::GpuMatND*)obj)->type() == mtype); +#ifdef HAVE_CUDA + cuda::GpuMatND::SizeArray sizes = {_rows, _cols}; + ((cuda::GpuMatND*)obj)->create(sizes, mtype); + return; +#else + CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)"); #endif } if( k == OPENGL_BUFFER && i < 0 && !allowTransposed && fixedDepthMask == 0 ) @@ -1653,6 +1763,18 @@ void _OutputArray::create(int d, const int* sizes, int mtype, int i, return; } + if( k == CUDA_GPU_MATND && d > 0 && i < 0 && !allowTransposed && fixedDepthMask == 0 ) + { +#ifdef HAVE_CUDA + cuda::GpuMatND::SizeArray sizeArray = cuda::GpuMatND::SizeArray(sizes, sizes+d); + ((cuda::GpuMatND*)obj)->create(sizeArray, mtype); + return; +#else + CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)"); +#endif + } + + CV_Error(Error::StsNotImplemented, "Unknown/unsupported array type"); } @@ -1696,6 +1818,16 @@ void _OutputArray::release() const #endif } + if( k == CUDA_GPU_MATND ) + { +#ifdef HAVE_CUDA + ((cuda::GpuMatND*)obj)->release(); + return; +#else + CV_Error(Error::StsNotImplemented, "CUDA support is not enabled in this OpenCV build (missing HAVE_CUDA)"); +#endif + } + if( k == CUDA_HOST_MEM ) { #ifdef HAVE_CUDA @@ -1827,6 +1959,12 @@ std::vector& _OutputArray::getGpuMatVecRef() const CV_Assert(k == STD_VECTOR_CUDA_GPU_MAT); return *(std::vector*)obj; } +cuda::GpuMatND& _OutputArray::getGpuMatNDRef() const +{ + _InputArray::KindFlag k = kind(); + CV_Assert( k == CUDA_GPU_MATND ); + return *(cuda::GpuMatND*)obj; +} ogl::Buffer& _OutputArray::getOGlBufferRef() const { From c0364e4e31b0d7b8bdeecb4feb68981d7d0486fe Mon Sep 17 00:00:00 2001 From: Adrian Kretz Date: Fri, 21 Nov 2025 08:52:02 +0100 Subject: [PATCH 09/27] Merge pull request #27993 from akretz:undistortPoints_convergence Undistort points convergence #27993 I have looked into the `undistortPoints()` problem of issue #27916 and have found a solution. The problem is, as @Linhuihang has correctly pointed out, that the fixed-point iterations do not converge. Here are the functions which are optimized for the undistortion problem: $$ \begin{aligned} r^2 &= x'^2 + y'^2 \\ f_1(x') &= \frac{1 + k_4 r^2 + k_5 r^4 + k_6 r^6}{1 + k_1 r^2 + k_2 r^4 + k_3 r^6} (x'' - 2p_1 x' y' - p_2(r^2 + 2 x'^2) - s_1 r^2 + s_2 r^4) = x' \\ f_2(y') &= \frac{1 + k_4 r^2 + k_5 r^4 + k_6 r^6}{1 + k_1 r^2 + k_2 r^4 + k_3 r^6} (y'' - p_1 (r^2 + 2 y'^2) - 2 p_2 x' y' - s_3 r^2 - s_4 r^4) = y' \end{aligned} $$ where $x', y'$ are the undistorted points we want to compute and and $x'', y''$ are the given distorted points. This problem is solved using fixed-point iterations like $$ x'_{k+1} = f_1(x'_k),\quad y'_{k+1} = f_2(y'_k) $$ I guess the issue here is that the distortion function does not necessarily satisfy the [Banach fixed-point theorem](https://en.wikipedia.org/wiki/Banach_fixed-point_theorem), i.e. the slope of the function can be too large. This can be seen in @Linhuihang's comment https://github.com/opencv/opencv/issues/27916#issuecomment-3417883642 - the point series jumps around and doesn't converge. A common solution is to instead do damped fixed-point iterations, so that the updates are "more smooth". $$ x'_{k+1} = (1 - \alpha) x'_k + \alpha f_1(x'_k),\quad y'_{k+1} = (1 - \alpha) y'_k + \alpha f_2(y'_k) $$ I have implemented a simple logic which starts with $\alpha = 1$ (so just like it is now) and reduces $\alpha$ whenever the optimization error would increase. This seems reasonable to me: the initial logic is to do normal fixed-point iterations and to gradually become "more damped" when we notice that we don't converge. Perhaps there is a better way to ensure convergence, but this is the most straightforward modification to the current code that I have found. This problem is not due to the $\tau_x, \tau_y$ parameters; it also occurs when they are zero. In fact, the fixed-point iterations are done when the tilt correction of $\tau_x, \tau_y$ has already been applied. I have added a test to reproduce the problem. This PR fixes #27916. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/calib3d/src/undistort.dispatch.cpp | 32 ++++++---- .../calib3d/test/test_undistort_points.cpp | 58 +++++++++++++++++++ 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/modules/calib3d/src/undistort.dispatch.cpp b/modules/calib3d/src/undistort.dispatch.cpp index cae0d094d4..0d08c4d781 100644 --- a/modules/calib3d/src/undistort.dispatch.cpp +++ b/modules/calib3d/src/undistort.dispatch.cpp @@ -445,8 +445,12 @@ static void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvM y0 = y = invProj * vecUntilt(1); double error = std::numeric_limits::max(); + double prevError = std::numeric_limits::max(); // compensate distortion iteratively using fixed-point iteration + // parameter for damped fixed-point iteration + double alpha = 1.; + for( int j = 0; ; j++ ) { if ((criteria.type & cv::TermCriteria::COUNT) && j >= criteria.maxCount) @@ -470,10 +474,11 @@ static void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvM // [x'', y'']^T = [x' / icdist + deltaX, y' / icdist + deltaY]^T => // [x', y']^T = [(x'' - deltaX) * icdist, (y'' - deltaY) * icdist]^T => // x' = f1(x') := (x'' - deltaX) * icdist, y' = f2(y') := (y'' - deltaY) * icdist - // Fixed-point iteration: - // new_x' = f1(x') = (x'' - deltaX) * icdist, new_y' = f2(y') = (y'' - deltaY) * icdist - x = (x0 - deltaX)*icdist; - y = (y0 - deltaY)*icdist; + // Damped fixed-point iteration: + // f1(x') = (x'' - deltaX) * icdist, f2(y') = (y'' - deltaY) * icdist + // new_x' = (1 - alpha) * x' + alpha * f1(x'), new_y' = (1 - alpha) * y' + alpha * f2(y') + double new_x = (1. - alpha)*x + alpha*(x0 - deltaX)*icdist; + double new_y = (1. - alpha)*y + alpha*(y0 - deltaY)*icdist; if(criteria.type & cv::TermCriteria::EPS) { @@ -482,20 +487,20 @@ static void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvM cv::Vec3d vecTilt; // r^2 = x'^2 + y'^2 - r2 = x*x + y*y; + r2 = new_x*new_x + new_y*new_y; r4 = r2*r2; r6 = r4*r2; - a1 = 2*x*y; - a2 = r2 + 2*x*x; - a3 = r2 + 2*y*y; + a1 = 2*new_x*new_y; + a2 = r2 + 2*new_x*new_x; + a3 = r2 + 2*new_y*new_y; // cdist := 1 + k1 * r^2 + k2 * r^4 + k3 * r^6 cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6; // icdist2 := 1 / (1 + k4 * r^2 + k5 * r^4 + k6 * r^6) icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6); // x'' = x' * cdist * icdist2 + 2 * p1 * x' * y' + p2 * (r^2 + 2 * x'^2) + s1 * r^2 + s2 * r^4 // y'' = y' * cdist * icdist2 + p1 * (r^2 + 2 * y'^2) + 2 * p2 * x' * y' + s3 * r^2 + s4 * r^4 - xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4; - yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4; + xd0 = new_x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4; + yd0 = new_y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4; // s * [x''', y''', 1]^T = matTilt * [x'', y'', 1]^T => // (vecTilt := matTilt * [x'', y'', 1]^T) @@ -513,6 +518,13 @@ static void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvM error = sqrt( pow(x_proj - u, 2) + pow(y_proj - v, 2) ); } + if (error > prevError) { + alpha *= .5; + } else { + x = new_x; + y = new_y; + } + prevError = error; } } diff --git a/modules/calib3d/test/test_undistort_points.cpp b/modules/calib3d/test/test_undistort_points.cpp index f92bec068b..faed9b5dcf 100644 --- a/modules/calib3d/test/test_undistort_points.cpp +++ b/modules/calib3d/test/test_undistort_points.cpp @@ -16,6 +16,7 @@ protected: void generateCameraMatrix(Mat& cameraMatrix); void generateDistCoeffs(Mat& distCoeffs, int count); cv::Mat generateRotationVector(); + std::vector distortPoints(const cv::Mat &cameraMatrix, const cv::Mat &dist, const std::vector &points); double thresh = 1.0e-2; }; @@ -60,6 +61,34 @@ cv::Mat UndistortPointsTest::generateRotationVector() return rvec; } +std::vector UndistortPointsTest::distortPoints(const cv::Mat &cameraMatrix, const cv::Mat &dist, const std::vector &points) +{ + CV_Assert(cameraMatrix.rows == 3 && cameraMatrix.cols == 3); + CV_Assert(cameraMatrix.type() == CV_64F); + CV_Assert(dist.rows * dist.cols == 12); + CV_Assert(dist.type() == CV_64F); + double *k = reinterpret_cast(dist.data); + double fx = cameraMatrix.at(0, 0); + double fy = cameraMatrix.at(1, 1); + double cx = cameraMatrix.at(0, 2); + double cy = cameraMatrix.at(1, 2); + std::vector distortedPoints; + distortedPoints.reserve(points.size()); + + for (const cv::Point2d p : points) { + double x = (p.x - cx) / fx; + double y = (p.y - cy) / fy; + double r2 = x*x + y*y; + double cdist = (1 + ((k[4]*r2 + k[1])*r2 + k[0])*r2)/(1 + ((k[7]*r2 + k[6])*r2 + k[5])*r2); + CV_Assert(cdist >= 0); + double deltaX = 2*k[2]*x*y + k[3]*(r2 + 2*x*x)+ k[8]*r2+k[9]*r2*r2; + double deltaY = k[2]*(r2 + 2*y*y) + 2*k[3]*x*y+ k[10]*r2+k[11]*r2*r2; + distortedPoints.push_back(cv::Point2d((x * cdist + deltaX) * fx + cx, (y * cdist + deltaY) * fy + cy)); + } + + return distortedPoints; +} + TEST_F(UndistortPointsTest, accuracy) { Mat intrinsics, distCoeffs; @@ -196,4 +225,33 @@ TEST_F(UndistortPointsTest, regression_14583) << "undistort point: " << undistort_pt; } +TEST_F(UndistortPointsTest, regression_27916) +{ + cv::Mat K = (cv::Mat_(3, 3) << + 1570.8956145992222, 0., 744.87337646727406, 0., + 1570.3494207432338, 575.55087456337526, 0., 0., 1.); + cv::Mat dist = (cv::Mat_(1, 12) << + -2.8247717583453804, -0.80078070764368037, + -0.014595359484103326, 0.0018820998949700702, 1.9827795585249783, + -2.7306773773930897, -1.217725820479524, 2.4052243546080136, + -0.0020670359760441713, 3.4660880793174063e-05, + 0.014100351510458799, -3.0935329736207612e-05); + + const cv::TermCriteria termCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, 100, thresh / 2); + std::vector distortedPoints, distortedPoints2; + std::vector undistortedPoints; + + for (int i = 0; i < 50; i++) + { + for (int j = 0; j < 50; j++) + { + distortedPoints.push_back(cv::Point2d(i, j)); + } + } + + cv::undistortPoints(distortedPoints, undistortedPoints, K, dist, cv::noArray(), K, termCriteria); + distortedPoints2 = distortPoints(K, dist, undistortedPoints); + EXPECT_MAT_NEAR(distortedPoints2, distortedPoints, thresh); +} + }} // namespace From 2cc5b69fd1a7a720365a8b066bbc586d06314dbc Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 21 Nov 2025 15:54:38 +0800 Subject: [PATCH 10/27] Merge pull request #28043 from dkurt:d.kurtaev/convexHull_eps Handle near-zero convexity in convexHull #28043 ### Pull Request Readiness Checklist resolves https://github.com/opencv/opencv/issues/21482 closes https://github.com/opencv/opencv/issues/14401 Also skip a code that determines orientation inside rotatingCalipers and rely on the order after convexHull See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/src/convhull.cpp | 14 +++++++---- modules/imgproc/src/rotcalipers.cpp | 10 ++++---- modules/imgproc/test/test_convhull.cpp | 33 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/modules/imgproc/src/convhull.cpp b/modules/imgproc/src/convhull.cpp index f16618ccb6..ed7b5a4a52 100644 --- a/modules/imgproc/src/convhull.cpp +++ b/modules/imgproc/src/convhull.cpp @@ -76,12 +76,16 @@ static int Sklansky_( Point_<_Tp>** array, int start, int end, int* stack, int n if( CV_SIGN( by ) != nsign ) { - _Tp ax = array[pcur]->x - array[pprev]->x; - _Tp bx = array[pnext]->x - array[pcur]->x; - _Tp ay = cury - array[pprev]->y; - _DotTp convexity = (_DotTp)ay*bx - (_DotTp)ax*by; // if >0 then convex angle + Vec<_Tp, 2> a(array[pcur]->x - array[pprev]->x, cury - array[pprev]->y); + Vec<_Tp, 2> b(array[pnext]->x - array[pcur]->x, by); + if (std::is_floating_point<_Tp>::value) + { + a = normalize(a); + b = normalize(b); + } + _DotTp convexity = (_DotTp)a[1]*b[0] - (_DotTp)a[0]*b[1]; // if >0 then convex angle - if( CV_SIGN( convexity ) == sign2 && (ax != 0 || ay != 0) ) + if( CV_SIGN( convexity ) == sign2 && (a[0] != 0 || a[1] != 0) ) { pprev = pcur; pcur = pnext; diff --git a/modules/imgproc/src/rotcalipers.cpp b/modules/imgproc/src/rotcalipers.cpp index 3bec592c9b..923013b762 100644 --- a/modules/imgproc/src/rotcalipers.cpp +++ b/modules/imgproc/src/rotcalipers.cpp @@ -64,6 +64,7 @@ enum { CALIPERS_MAXHEIGHT=0, CALIPERS_MINAREARECT=1, CALIPERS_MAXDIST=2 }; // Parameters: // points - convex hull vertices ( any orientation ) // n - number of vertices + // orientation - -1 for clockwise vertices order, 1 for CCW. 0 if unknown. // mode - concrete application of algorithm // can be CV_CALIPERS_MAXDIST or // CV_CALIPERS_MINAREARECT @@ -115,7 +116,7 @@ static bool firstVecIsRight(const cv::Point2f& vec1, const cv::Point2f &vec2) } /* we will use usual cartesian coordinates */ -static void rotatingCalipers( const Point2f* points, int n, int mode, float* out ) +static void rotatingCalipers( const Point2f* points, int n, float orientation, int mode, float* out ) { float minarea = FLT_MAX; float max_dist = 0; @@ -132,7 +133,6 @@ static void rotatingCalipers( const Point2f* points, int n, int mode, float* out (a,b) (-b,a) (-a,-b) (b, -a) */ /* this is a first base vector (a,b) initialized by (1,0) */ - float orientation = 0; float base_a; float base_b = 0; @@ -171,6 +171,7 @@ static void rotatingCalipers( const Point2f* points, int n, int mode, float* out } // find convex hull orientation + if (orientation == 0.f) { double ax = vect[n-1].x; double ay = vect[n-1].y; @@ -365,7 +366,8 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) Point2f out[3]; RotatedRect box; - convexHull(_points, hull, false, true); + static const bool clockwise = false; + convexHull(_points, hull, clockwise, true); if( hull.depth() != CV_32F ) { @@ -379,7 +381,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) if( n > 2 ) { - rotatingCalipers( hpoints, n, CALIPERS_MINAREARECT, (float*)out ); + rotatingCalipers( hpoints, n, clockwise ? -1.f : 1.f, CALIPERS_MINAREARECT, (float*)out ); box.center.x = out[0].x + (out[1].x + out[2].x)*0.5f; box.center.y = out[0].y + (out[1].y + out[2].y)*0.5f; box.size.width = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y); diff --git a/modules/imgproc/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index e6466aa283..3663ce56f6 100644 --- a/modules/imgproc/test/test_convhull.cpp +++ b/modules/imgproc/test/test_convhull.cpp @@ -1232,6 +1232,39 @@ TEST(minEnclosingPolygon, pentagon) } } +TEST(Imgproc_minAreaRect, reproducer_21482) +{ + const int N = 4; + float pts_[N][2] = { + { 188.8991f, 12.400669f }, + { 80.64467f, -49.644814f }, + { 469.59897f, 173.28242f }, + { 690.4597f, 299.86768f }, + }; + + Mat contour(N, 1, CV_32FC2, (void*)pts_); + + RotatedRect rr = cv::minAreaRect(contour); + + EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle; + EXPECT_NEAR(min(rr.size.width, rr.size.height), 0, 1e-5); + EXPECT_GE(max(rr.size.width, rr.size.height), 702); +} + +TEST(Imgproc_minAreaRect, reproducer_21482_small_values) +{ + const int N = 4; + float pts_[N][2] = { { 0.f, 0.f }, { 1e-4f, 0.f }, { 1e-4f, 1e-4f }, { 0.f, 1e-4f },}; + + Mat contour(N, 1, CV_32FC2, (void*)pts_); + + RotatedRect rr = cv::minAreaRect(contour); + + EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle; + EXPECT_EQ(rr.size.width, 1e-4f); + EXPECT_EQ(rr.size.height, 1e-4f); +} + }} // namespace /* End of file. */ From 6231b080ff8a2d5c7dc1e62178c58e44bd1985e7 Mon Sep 17 00:00:00 2001 From: Dmytro Dadyka Date: Fri, 21 Nov 2025 10:50:29 +0200 Subject: [PATCH 11/27] Merge pull request #27952 from DDmytro:ecc/template-mask-rework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add optional template mask for findTransformECC #27952 Supersedes #22997 **Summary** Add optional template mask support to findTransformECC so that only pixels valid in both the template and the image are used in ECC. Backward compatibility is preserved (existing signatures unchanged; one new overload adds templateMask). **Motivation** - Real-world frames often contain moving foreground artifacts (e.g., a football over a static field). Masking the object in one frame only is insufficient because its position changes independently of the background. Since we don’t know the warp a priori, we can’t back-project a single mask across frames. The correct approach is to supply both masks and take their intersection. - Templates may include uninformative/low-texture or noisy regions, or partial overlaps with other objects. Excluding such regions from the alignment improves robustness and convergence. This PR completes and replaces https://github.com/opencv/opencv/pull/22997 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../video/include/opencv2/video/tracking.hpp | 45 +++++++++ modules/video/src/ecc.cpp | 97 ++++++++++++++----- modules/video/test/test_ecc.cpp | 20 ++++ 3 files changed, 137 insertions(+), 25 deletions(-) diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 7fbd77d2ef..7cd04061de 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -374,6 +374,51 @@ double findTransformECC(InputArray templateImage, InputArray inputImage, TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 50, 0.001), InputArray inputMask = noArray()); +/** @brief Finds the geometric transform (warp) between two images in terms of the ECC criterion @cite EP08 +using validity masks for both the template and the input images. + +This function extends findTransformECC() by adding a mask for the template image. +The Enhanced Correlation Coefficient is evaluated only over pixels that are valid in both images: +on each iteration inputMask is warped into the template frame and combined with templateMask, and +only the intersection of these masks contributes to the objective function. + +@param templateImage 1 or 3 channel template image; CV_8U, CV_16U, CV_32F, CV_64F type. +@param inputImage input image which should be warped with the final warpMatrix in +order to provide an image similar to templateImage, same type as templateImage. +@param templateMask single-channel 8-bit mask for templateImage indicating valid pixels +to be used in the alignment. Must have the same size as templateImage. +@param inputMask single-channel 8-bit mask for inputImage indicating valid pixels +before warping. Must have the same size as inputImage. +@param warpMatrix floating-point \f$2\times 3\f$ or \f$3\times 3\f$ mapping matrix (warp). +@param motionType parameter, specifying the type of motion: + - **MOTION_TRANSLATION** sets a translational motion model; warpMatrix is \f$2\times 3\f$ with + the first \f$2\times 2\f$ part being the unity matrix and the rest two parameters being + estimated. + - **MOTION_EUCLIDEAN** sets a Euclidean (rigid) transformation as motion model; three + parameters are estimated; warpMatrix is \f$2\times 3\f$. + - **MOTION_AFFINE** sets an affine motion model (DEFAULT); six parameters are estimated; + warpMatrix is \f$2\times 3\f$. + - **MOTION_HOMOGRAPHY** sets a homography as a motion model; eight parameters are + estimated; warpMatrix is \f$3\times 3\f$. +@param criteria parameter, specifying the termination criteria of the ECC algorithm; +criteria.epsilon defines the threshold of the increment in the correlation coefficient between two +iterations (a negative criteria.epsilon makes criteria.maxcount the only termination criterion). +Default values are shown in the declaration above. +@param gaussFiltSize size of the Gaussian blur filter used for smoothing images and masks +before computing the alignment (DEFAULT: 5). + +@sa +findTransformECC, computeECC, estimateAffine2D, estimateAffinePartial2D, findHomography +*/ +CV_EXPORTS_W double findTransformECCWithMask( InputArray templateImage, + InputArray inputImage, + InputArray templateMask, + InputArray inputMask, + InputOutputArray warpMatrix, + int motionType = MOTION_AFFINE, + TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 50, 1e-6), + int gaussFiltSize = 5 ); + /** @example samples/cpp/kalman.cpp An example using the standard Kalman filter */ diff --git a/modules/video/src/ecc.cpp b/modules/video/src/ecc.cpp index 4fa45a4b32..87b3c53365 100644 --- a/modules/video/src/ecc.cpp +++ b/modules/video/src/ecc.cpp @@ -333,8 +333,15 @@ double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArra return templateImage_zeromean.dot(inputImage_zeromean) / (templateImagenorm * inputImagenorm); } -double cv::findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix, - int motionType, TermCriteria criteria, InputArray inputMask, int gaussFiltSize) { + +double cv::findTransformECCWithMask( InputArray templateImage, + InputArray inputImage, + InputArray templateMask, + InputArray inputMask, + InputOutputArray warpMatrix, + int motionType, + TermCriteria criteria, + int gaussFiltSize) { Mat src = templateImage.getMat(); // template image Mat dst = inputImage.getMat(); // input image (to be warped) Mat map = warpMatrix.getMat(); // warp (transformation) @@ -416,7 +423,7 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp Ycoord.release(); const int channels = src.channels(); - int type = CV_MAKETYPE(CV_32F, channels); // используем отдельно, если нужно явно + int type = CV_MAKETYPE(CV_32F, channels); std::vector XgridCh(channels, Xgrid); cv::merge(XgridCh, Xgrid); @@ -430,27 +437,10 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp Mat imageWarped = Mat(hs, ws, type); // to store the warped zero-mean input image Mat imageMask = Mat(hs, ws, CV_8U); // to store the final mask - Mat inputMaskMat = inputMask.getMat(); - // to use it for mask warping - Mat preMask; - if (inputMask.empty()) - preMask = Mat::ones(hd, wd, CV_8U); - else - threshold(inputMask, preMask, 0, 1, THRESH_BINARY); - // Gaussian filtering is optional src.convertTo(templateFloat, templateFloat.type()); GaussianBlur(templateFloat, templateFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0); - Mat preMaskFloat; - preMask.convertTo(preMaskFloat, type); - GaussianBlur(preMaskFloat, preMaskFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0); - // Change threshold. - preMaskFloat *= (0.5 / 0.95); - // Rounding conversion. - preMaskFloat.convertTo(preMask, preMask.type()); - preMask.convertTo(preMaskFloat, preMaskFloat.type()); - dst.convertTo(imageFloat, imageFloat.type()); GaussianBlur(imageFloat, imageFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0); @@ -466,12 +456,48 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp filter2D(imageFloat, gradientX, -1, dx); filter2D(imageFloat, gradientY, -1, dx.t()); - cv::Mat preMaskFloatNCh; - std::vector maskChannels(gradientX.channels(), preMaskFloat); - cv::merge(maskChannels, preMaskFloatNCh); + // To use in mask warping + Mat templtMask; + if(templateMask.empty()) + { + templtMask = Mat::ones(hs, ws, CV_8U); + } + else + { + threshold(templateMask, templtMask, 0, 1, THRESH_BINARY); + templtMask.convertTo(templtMask, CV_32F); + GaussianBlur(templtMask, templtMask, Size(gaussFiltSize, gaussFiltSize), 0, 0); + templtMask *= (0.5/0.95); + templtMask.convertTo(templtMask, CV_8U); + } - gradientX = gradientX.mul(preMaskFloatNCh); - gradientY = gradientY.mul(preMaskFloatNCh); + //to use it for mask warping + Mat preMask; + if(inputMask.empty()) + { + preMask = Mat::ones(hd, wd, CV_8U); + } + else + { + Mat preMaskFloat; + threshold(inputMask, preMask, 0, 1, THRESH_BINARY); + + preMask.convertTo(preMaskFloat, CV_32F); + GaussianBlur(preMaskFloat, preMaskFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0); + // Change threshold. + preMaskFloat *= (0.5/0.95); + // Rounding conversion. + preMaskFloat.convertTo(preMask, CV_8U); + + // If there's no template mask, we can apply image masks to gradients only once. + // Otherwise, we'll need to combine the template and image masks at each iteration. + if (templateMask.empty()) + { + cv::Mat zeroMask = (preMask == 0); + gradientX.setTo(0, zeroMask); + gradientY.setTo(0, zeroMask); + } + } // matrices needed for solving linear equation system for maximizing ECC Mat jacobian = Mat(hs, ws * numberOfParameters, type); @@ -505,6 +531,15 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp warpPerspective(preMask, imageMask, map, imageMask.size(), maskFlags); } + if (!templateMask.empty()) + { + cv::bitwise_and(imageMask, templtMask, imageMask); + + cv::Mat zeroMask = (imageMask == 0); + gradientXWarped.setTo(0, zeroMask); + gradientYWarped.setTo(0, zeroMask); + } + Scalar imgMean, imgStd, tmpMean, tmpStd; meanStdDev(imageWarped, imgMean, imgStd, imageMask); meanStdDev(templateFloat, tmpMean, tmpStd, imageMask); @@ -576,6 +611,18 @@ double cv::findTransformECC(InputArray templateImage, InputArray inputImage, Inp return rho; } +double cv::findTransformECC(InputArray templateImage, + InputArray inputImage, + InputOutputArray warpMatrix, + int motionType, + TermCriteria criteria, + InputArray inputMask, + int gaussFiltSize + ) { + return findTransformECCWithMask(templateImage, inputImage, noArray(), inputMask, + warpMatrix, motionType, criteria, gaussFiltSize); +} + double cv::findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix, int motionType, TermCriteria criteria, InputArray inputMask) { // Use default value of 5 for gaussFiltSize to maintain backward compatibility. diff --git a/modules/video/test/test_ecc.cpp b/modules/video/test/test_ecc.cpp index 50cd5fed7f..18438b7539 100644 --- a/modules/video/test/test_ecc.cpp +++ b/modules/video/test/test_ecc.cpp @@ -342,6 +342,26 @@ bool CV_ECC_Test_Mask::test(const Mat testImg) { // Test with non-default gaussian blur. findTransformECC(warpedImage, testImg, mapTranslation, 0, criteria, mask, 1); + if (!checkMap(mapTranslation, translationGround)) + return false; + + // Test with template mask. + Mat_ warpedMask = Mat_::ones(warpedImage.rows, warpedImage.cols); + for (int i=warpedImage.rows*1/3; i Date: Sat, 22 Nov 2025 15:15:00 -0300 Subject: [PATCH 12/27] Just original author. --- doc/tutorials/app/intelperc.markdown | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/tutorials/app/intelperc.markdown b/doc/tutorials/app/intelperc.markdown index 73d5e9867c..3e03c1d18a 100644 --- a/doc/tutorials/app/intelperc.markdown +++ b/doc/tutorials/app/intelperc.markdown @@ -6,6 +6,11 @@ Using Creative Senz3D and other Intel RealSense SDK compatible depth sensors {#t @prev_tutorial{tutorial_orbbec_uvc} @next_tutorial{tutorial_wayland_ubuntu} +| | | +| -: | :- | +| Original author | Alessandro de Oliveira Faria | +| Compatibility | OpenCV >= 4.5.5 | + ![hardwares](images/realsense.jpg) **Note**: This tutorial is partially obsolete since PerC SDK has been replaced with RealSense SDK From 79cfef49ff2d044ad838ea886f571629f2961ff7 Mon Sep 17 00:00:00 2001 From: Akash A Date: Sun, 23 Nov 2025 12:13:54 +0000 Subject: [PATCH 13/27] imgcodecs: avif: set matrixCoefficients to UNSPECIFIED for monochrome images Fixes issue with libavif >= 1.3.0 where subsampling with identity matrix coefficients is invalid. Aligns OpenCV AVIF writer with updated libavif conformance rules. --- modules/imgcodecs/src/grfmt_avif.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp index 35b4411b3b..36f7cdd58f 100644 --- a/modules/imgcodecs/src/grfmt_avif.cpp +++ b/modules/imgcodecs/src/grfmt_avif.cpp @@ -86,7 +86,7 @@ AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless, int bit_dept result->yuvFormat = AVIF_PIXEL_FORMAT_YUV400; result->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; result->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; - result->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY; + result->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; result->yuvRange = AVIF_RANGE_FULL; result->yuvPlanes[0] = img.data; result->yuvRowBytes[0] = img.step[0]; From 6617a7ca3453995676d2e051d73728356b45ea30 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Mon, 24 Nov 2025 08:19:25 +0900 Subject: [PATCH 14/27] imgcodecs: avif: support metadata for 1ch Mat --- modules/imgcodecs/src/grfmt_avif.cpp | 37 ++++++++++++++-------------- modules/imgcodecs/test/test_exif.cpp | 11 ++++++--- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp index 35b4411b3b..036cf62fef 100644 --- a/modules/imgcodecs/src/grfmt_avif.cpp +++ b/modules/imgcodecs/src/grfmt_avif.cpp @@ -91,10 +91,7 @@ AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless, int bit_dept result->yuvPlanes[0] = img.data; result->yuvRowBytes[0] = img.step[0]; result->imageOwnsYUVPlanes = AVIF_FALSE; - return AvifImageUniquePtr(result); - } - - if (lossless) { + } else if (lossless) { result = avifImageCreate(width, height, bit_depth, AVIF_PIXEL_FORMAT_YUV444); if (result == nullptr) return nullptr; @@ -139,22 +136,24 @@ AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless, int bit_dept #endif } - avifRGBImage rgba; - avifRGBImageSetDefaults(&rgba, result); - if (img.channels() == 3) { - rgba.format = AVIF_RGB_FORMAT_BGR; - } else { - CV_Assert(img.channels() == 4); - rgba.format = AVIF_RGB_FORMAT_BGRA; - } - rgba.rowBytes = (uint32_t)img.step[0]; - rgba.depth = bit_depth; - rgba.pixels = - const_cast(reinterpret_cast(img.data)); + if (img.channels() > 1) { + avifRGBImage rgba; + avifRGBImageSetDefaults(&rgba, result); + if (img.channels() == 3) { + rgba.format = AVIF_RGB_FORMAT_BGR; + } else { + CV_Assert(img.channels() == 4); + rgba.format = AVIF_RGB_FORMAT_BGRA; + } + rgba.rowBytes = (uint32_t)img.step[0]; + rgba.depth = bit_depth; + rgba.pixels = + const_cast(reinterpret_cast(img.data)); - if (avifImageRGBToYUV(result, &rgba) != AVIF_RESULT_OK) { - avifImageDestroy(result); - return nullptr; + if (avifImageRGBToYUV(result, &rgba) != AVIF_RESULT_OK) { + avifImageDestroy(result); + return nullptr; + } } return AvifImageUniquePtr(result); } diff --git a/modules/imgcodecs/test/test_exif.cpp b/modules/imgcodecs/test/test_exif.cpp index 706896fedc..bd89728ce0 100644 --- a/modules/imgcodecs/test/test_exif.cpp +++ b/modules/imgcodecs/test/test_exif.cpp @@ -296,13 +296,15 @@ INSTANTIATE_TEST_CASE_P(Imgcodecs, Exif, testing::ValuesIn(exif_files)); #ifdef HAVE_AVIF -TEST(Imgcodecs_Avif, ReadWriteWithExif) +typedef testing::TestWithParam MatChannels; + +TEST_P(MatChannels, Imgcodecs_Avif_ReadWriteWithExif) { int avif_nbits = 10; int avif_speed = 10; int avif_quality = 85; int imgdepth = avif_nbits > 8 ? CV_16U : CV_8U; - int imgtype = CV_MAKETYPE(imgdepth, 3); + int imgtype = CV_MAKETYPE(imgdepth, GetParam()); const string outputname = cv::tempfile(".avif"); Mat img = makeCirclesImage(Size(1280, 720), imgtype, avif_nbits); @@ -328,7 +330,7 @@ TEST(Imgcodecs_Avif, ReadWriteWithExif) EXPECT_EQ(img2.rows, img.rows); EXPECT_EQ(img2.type(), imgtype); EXPECT_EQ(read_metadata_types, read_metadata_types2); - EXPECT_GE(read_metadata_types.size(), 1u); + ASSERT_GE(read_metadata_types.size(), 1u); EXPECT_EQ(read_metadata, read_metadata2); EXPECT_EQ(read_metadata_types[0], IMAGE_METADATA_EXIF); EXPECT_EQ(read_metadata_types.size(), read_metadata.size()); @@ -338,6 +340,9 @@ TEST(Imgcodecs_Avif, ReadWriteWithExif) EXPECT_LT(mse, 1500); remove(outputname.c_str()); } + +INSTANTIATE_TEST_CASE_P(Imgcodecs, MatChannels, + testing::Values(1,3,4)); #endif // HAVE_AVIF #ifdef HAVE_WEBP From 3c3596c6efcabd0e5ee021e2048a30ec38a8d4e1 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Mon, 24 Nov 2025 16:46:12 +0900 Subject: [PATCH 15/27] Merge pull request #28063 from Kumataro:fix28062 imgcodecs: Fix IMWRITE_AVIF_DEPTH typo and update AVIF details on imwrite() #28063 Close https://github.com/opencv/opencv/issues/28062 - Corrected the documentation for `IMWRITE_AVIF_DEPTH`. - Added descriptions for the new AVIF encoder parameters in `imwrite()` documentation. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- modules/imgcodecs/include/opencv2/imgcodecs.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 9f6b7b6fcd..7bb14f9c7e 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -113,7 +113,7 @@ enum ImwriteFlags { IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. Default is IMWRITE_TIFF_PREDICTOR_HORIZONTAL . IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000. IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95. - IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8. + IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_16U. Default is 8. IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and 10(fastest). Default is 9. IMWRITE_JPEGXL_QUALITY = 640,//!< For JPEG XL, it can be a quality from 0 to 100 (the higher is the better). Default value is 95. If set, distance parameter is re-calicurated from quality level automatically. This parameter request libjxl v0.10 or later. IMWRITE_JPEGXL_EFFORT = 641,//!< For JPEG XL, encoder effort/speed level without affecting decoding speed; it is between 1 (fastest) and 10 (slowest). Default is 7. @@ -538,6 +538,11 @@ can be saved using this function, with these exceptions: To achieve this, create an 8-bit 4-channel (CV_8UC4) BGRA image, ensuring the alpha channel is the last component. Fully transparent pixels should have an alpha value of 0, while fully opaque pixels should have an alpha value of 255. - 8-bit single-channel images (CV_8UC1) are not supported due to GIF's limitation to indexed color formats. +- With AVIF encoder, 8-bit unsigned (CV_8U) and 16-bit unsigned (CV_16U) images can be saved. + - CV_16U images can be saved as only 10-bit or 12-bit (not 16-bit). See IMWRITE_AVIF_DEPTH. + - AVIF images with an alpha channel can be saved using this function. + To achieve this, create an 8-bit 4-channel (CV_8UC4) / 16-bit 4-channel (CV_16UC4) BGRA image, ensuring the alpha channel is the last component. + Fully transparent pixels should have an alpha value of 0, while fully opaque pixels should have an alpha value of 255 (8-bit) / 1023 (10-bit) / 4095 (12-bit) (see the code sample below). If the image format is not supported, the image will be converted to 8-bit unsigned (CV_8U) and saved that way. From 621ad482d625a75ab8c5337269a3f18b50414fac Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Tue, 25 Nov 2025 20:56:59 +0800 Subject: [PATCH 16/27] Merge pull request #28051 from dkurt:minAreaRect_angle Correct minAreaRect angle to be in range [-90, 0) #28051 ### Pull Request Readiness Checklist Box angle range over all imgproc tests is in interval `[-90, -0.0581199]` resolves https://github.com/opencv/opencv/issues/27667 resolves https://github.com/opencv/opencv/issues/19472 resolves https://github.com/opencv/opencv/issues/24436 See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake --- .../imgproc/misc/java/test/ImgprocTest.java | 4 +-- modules/imgproc/src/rotcalipers.cpp | 34 ++++++++++++++----- modules/imgproc/test/test_convhull.cpp | 18 ++++++++++ modules/js/test/test_imgproc.js | 16 ++++----- modules/python/test/test_legacy.py | 2 +- 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/modules/imgproc/misc/java/test/ImgprocTest.java b/modules/imgproc/misc/java/test/ImgprocTest.java index c9c5753da7..0e27f7d437 100644 --- a/modules/imgproc/misc/java/test/ImgprocTest.java +++ b/modules/imgproc/misc/java/test/ImgprocTest.java @@ -1344,8 +1344,8 @@ public class ImgprocTest extends OpenCVTestCase { RotatedRect rrect = Imgproc.minAreaRect(points); - assertEquals(new Size(5, 2), rrect.size); - assertEquals(0., rrect.angle); + assertEquals(new Size(2, 5), rrect.size); + assertEquals(-90., rrect.angle); assertEquals(new Point(3.5, 2), rrect.center); } diff --git a/modules/imgproc/src/rotcalipers.cpp b/modules/imgproc/src/rotcalipers.cpp index 923013b762..d906258650 100644 --- a/modules/imgproc/src/rotcalipers.cpp +++ b/modules/imgproc/src/rotcalipers.cpp @@ -365,6 +365,7 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) Mat hull; Point2f out[3]; RotatedRect box; + box.angle = -(float)CV_PI / 2; // default angle for box without rotation and single point static const bool clockwise = false; convexHull(_points, hull, clockwise, true); @@ -384,19 +385,34 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) rotatingCalipers( hpoints, n, clockwise ? -1.f : 1.f, CALIPERS_MINAREARECT, (float*)out ); box.center.x = out[0].x + (out[1].x + out[2].x)*0.5f; box.center.y = out[0].y + (out[1].y + out[2].y)*0.5f; - box.size.width = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y); - box.size.height = (float)std::sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y); - box.angle = (float)atan2( (double)out[1].y, (double)out[1].x ); + box.size.width = (float)std::sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y); + box.size.height = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y); + if (out[1].x == 0.f && out[1].y > 0.f) + std::swap(box.size.width, box.size.height); + else + box.angle += (float)atan2( (double)out[1].y, (double)out[1].x ); } else if( n == 2 ) { box.center.x = (hpoints[0].x + hpoints[1].x)*0.5f; box.center.y = (hpoints[0].y + hpoints[1].y)*0.5f; - double dx = hpoints[1].x - hpoints[0].x; - double dy = hpoints[1].y - hpoints[0].y; - box.size.width = (float)std::sqrt(dx*dx + dy*dy); - box.size.height = 0; - box.angle = (float)atan2( dy, dx ); + double dx = hpoints[0].x - hpoints[1].x; + double dy = hpoints[0].y - hpoints[1].y; + box.size.width = 0; + box.size.height = (float)std::sqrt(dx*dx + dy*dy); + if (dx == 0) + { + std::swap(box.size.width, box.size.height); + } + else if (dy < 0) + { + box.angle = (float)atan2( dy, dx ); + std::swap(box.size.width, box.size.height); + } + else if (dy > 0) + { + box.angle += (float)atan2( dy, dx ); + } } else { @@ -405,6 +421,8 @@ cv::RotatedRect cv::minAreaRect( InputArray _points ) } box.angle = (float)(box.angle*180/CV_PI); + CV_DbgCheckGE(box.angle, -90.0f, ""); + CV_DbgCheckLT(box.angle, 0.0f, ""); return box; } diff --git a/modules/imgproc/test/test_convhull.cpp b/modules/imgproc/test/test_convhull.cpp index 3663ce56f6..774bee7af6 100644 --- a/modules/imgproc/test/test_convhull.cpp +++ b/modules/imgproc/test/test_convhull.cpp @@ -1265,6 +1265,24 @@ TEST(Imgproc_minAreaRect, reproducer_21482_small_values) EXPECT_EQ(rr.size.height, 1e-4f); } +typedef testing::TestWithParam> minAreaRect_of_line; +TEST_P(minAreaRect_of_line, accuracy) +{ + Point2f p1 = get<0>(GetParam()); + Point2f p2 = get<1>(GetParam()); + RotatedRect out = minAreaRect(std::vector{p1, p2}); + EXPECT_EQ(out.center, get<2>(GetParam())); + EXPECT_EQ(out.size, get<3>(GetParam())); + EXPECT_NEAR(out.angle, get<4>(GetParam()), 1e-6); +} +INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line, + testing::Values( + std::make_tuple(Point2f(10, 15), Point2f(10, 25), Point2f(10, 20), Size2f(10, 0), -90.f), + std::make_tuple(Point2f(450, 500), Point2f(508, 500), Point2f(479, 500), Size2f(0, 58), -90.f), + std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301002f), + std::make_tuple(Point2f(9, 19), Point2f(4, 7), Point2f(6.5, 13), Size2f(0, 13), -22.6198654f) + )); + }} // namespace /* End of file. */ diff --git a/modules/js/test/test_imgproc.js b/modules/js/test/test_imgproc.js index 24da9b9266..786a7ba4d2 100644 --- a/modules/js/test/test_imgproc.js +++ b/modules/js/test/test_imgproc.js @@ -710,14 +710,14 @@ QUnit.test('test_rotatedRectangleIntersection', function(assert) { assert.deepEqual(intersectionType, cv.INTERSECT_FULL); intersectionPoints.convertTo(intersectionPoints, cv.CV_32S); let intersectionPointsData = intersectionPoints.data32S; - assert.deepEqual(intersectionPointsData[0], 30); - assert.deepEqual(intersectionPointsData[1], 40); - assert.deepEqual(intersectionPointsData[2], 40); - assert.deepEqual(intersectionPointsData[3], 30); - assert.deepEqual(intersectionPointsData[4], 50); - assert.deepEqual(intersectionPointsData[5], 40); - assert.deepEqual(intersectionPointsData[6], 40); - assert.deepEqual(intersectionPointsData[7], 50); + assert.deepEqual(intersectionPointsData[0], 40); + assert.deepEqual(intersectionPointsData[1], 50); + assert.deepEqual(intersectionPointsData[2], 30); + assert.deepEqual(intersectionPointsData[3], 40); + assert.deepEqual(intersectionPointsData[4], 40); + assert.deepEqual(intersectionPointsData[5], 30); + assert.deepEqual(intersectionPointsData[6], 50); + assert.deepEqual(intersectionPointsData[7], 40); intersectionType = cv.rotatedRectangleIntersection(rr1, rr3, intersectionPoints); diff --git a/modules/python/test/test_legacy.py b/modules/python/test/test_legacy.py index e550ab73c8..47fd4ecd13 100644 --- a/modules/python/test/test_legacy.py +++ b/modules/python/test/test_legacy.py @@ -81,7 +81,7 @@ class Hackathon244Tests(NewOpenCVTests): mc, mr = cv.minEnclosingCircle(a) be0 = ((150.2511749267578, 150.77322387695312), (158.024658203125, 197.57696533203125), 37.57804489135742) - br0 = ((161.2974090576172, 154.41793823242188), (207.7177734375, 199.2301483154297), 80.83544921875) + br0 = ((161.2974090576172, 154.41793823242188), (199.2301483154297, 207.7177734375), -9.164555549621582) mc0, mr0 = (160.41790771484375, 144.55152893066406), 136.713500977 self.check_close_boxes(be, be0, 5, 15) From 166f4591d216af0a86b73f3841231975e5625389 Mon Sep 17 00:00:00 2001 From: Anshu Date: Wed, 26 Nov 2025 14:48:42 +0530 Subject: [PATCH 17/27] Merge pull request #28047 from 0AnshuAditya0:fix-pyos-fspath-memory-leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix memory leak in pyopencv_to for path-like objects #28047 This PR fixes a memory leak in pyopencv_to when handling path-like objects (e.g., pathlib.Path). Problem: PyOS_FSPath() returns a new strong reference, but the code was not calling Py_XDECREF to decrement it, causing a memory leak on every call with path-like arguments. Solution: Store the returned reference from PyOS_FSPath() in a separate variable path_obj Call Py_XDECREF(path_obj) on all function exit paths (both success and error paths) This ensures proper reference counting without changing the function's behavior Testing: The leak can be reproduced using the steps in issue #28046 with Python built with --with-address-sanitizer. This fix ensures the reference is properly released. Fixes #28046 Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request ✓ I agree to contribute to the project under Apache 2 License. ✓ To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV ✓ The PR is proposed to the proper branch (4.x) ✓ There is a reference to the original bug report and related work (#28046) NA There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. ✓ The feature is well documented and sample code can be built with the project CMake --- modules/python/src2/cv2_convert.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/python/src2/cv2_convert.cpp b/modules/python/src2/cv2_convert.cpp index 577c079dcd..1161995495 100644 --- a/modules/python/src2/cv2_convert.cpp +++ b/modules/python/src2/cv2_convert.cpp @@ -711,28 +711,29 @@ bool pyopencv_to(PyObject* obj, String &value, const ArgInfo& info) std::string str; #if ((PY_VERSION_HEX >= 0x03060000) && !defined(Py_LIMITED_API)) || (Py_LIMITED_API >= 0x03060000) + PyObject* path_obj = NULL; if (info.pathlike) { - obj = PyOS_FSPath(obj); + path_obj = PyOS_FSPath(obj); if (PyErr_Occurred()) { failmsg("Expected '%s' to be a str or path-like object", info.name); return false; } + obj = path_obj; } #endif + + bool result = false; if (getUnicodeString(obj, str)) { value = str; - return true; + result = true; } else { - // If error hasn't been already set by Python conversion functions if (!PyErr_Occurred()) { - // Direct access to underlying slots of PyObjectType is not allowed - // when limited API is enabled #ifdef Py_LIMITED_API failmsg("Can't convert object to 'str' for '%s'", info.name); #else @@ -741,7 +742,12 @@ bool pyopencv_to(PyObject* obj, String &value, const ArgInfo& info) #endif } } - return false; + +#if ((PY_VERSION_HEX >= 0x03060000) && !defined(Py_LIMITED_API)) || (Py_LIMITED_API >= 0x03060000) + Py_XDECREF(path_obj); +#endif + + return result; } template<> From 638f372c2e1391abee24096d5eb588baa269acd1 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 26 Nov 2025 16:47:13 +0300 Subject: [PATCH 18/27] Update ARM config label to Ubuntu 24.04. --- .github/workflows/PR-4.x.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/PR-4.x.yaml b/.github/workflows/PR-4.x.yaml index 11c4cbf517..0b25554bfb 100644 --- a/.github/workflows/PR-4.x.yaml +++ b/.github/workflows/PR-4.x.yaml @@ -17,10 +17,10 @@ jobs: with: workflow_branch: main - Ubuntu2004-ARM64: + Ubuntu2404-ARM64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-ARM64.yaml@main - Ubuntu2004-ARM64-Debug: + Ubuntu2404-ARM64-Debug: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-ARM64-Debug.yaml@main Ubuntu2004-x64-OpenVINO: From 1c2cdb1818419cc91a66872ccd3ae15825a9f081 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 27 Nov 2025 09:38:43 +0300 Subject: [PATCH 19/27] Unblock system-wide installation of Aravis SDK. --- modules/videoio/cmake/detect_aravis.cmake | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/videoio/cmake/detect_aravis.cmake b/modules/videoio/cmake/detect_aravis.cmake index cf8429e5dc..62a3c6c6fe 100644 --- a/modules/videoio/cmake/detect_aravis.cmake +++ b/modules/videoio/cmake/detect_aravis.cmake @@ -9,12 +9,10 @@ endif() if(NOT HAVE_ARAVIS_API) find_path(ARAVIS_INCLUDE "arv.h" PATHS "${ARAVIS_ROOT}" ENV ARAVIS_ROOT - PATH_SUFFIXES "include/aravis-0.8" - NO_DEFAULT_PATH) + PATH_SUFFIXES "include/aravis-0.8") find_library(ARAVIS_LIBRARY "aravis-0.8" PATHS "${ARAVIS_ROOT}" ENV ARAVIS_ROOT - PATH_SUFFIXES "lib" - NO_DEFAULT_PATH) + PATH_SUFFIXES "lib") if(ARAVIS_INCLUDE AND ARAVIS_LIBRARY) set(HAVE_ARAVIS_API TRUE) file(STRINGS "${ARAVIS_INCLUDE}/arvversion.h" ver_strings REGEX "#define +ARAVIS_(MAJOR|MINOR|MICRO)_VERSION.*") From 71c759b7cda1492af9305054c56acf9cd0b13b3d Mon Sep 17 00:00:00 2001 From: Ghazi-raad Date: Thu, 27 Nov 2025 06:57:21 +0000 Subject: [PATCH 20/27] Merge pull request #28085 from Ghazi-raad:fix/multiband-blender-memory-leak-27333 Fixed multiband blender memory leak 27333 #28085 Fixes #27333 This PR fixes a memory leak in MultiBandBlender where memory from pyramid vectors was not being released when prepare() was called multiple times or when the blender object was reused. Problem: MultiBandBlender retains hundreds of MB to several GB of memory even after the blender pointer is released. The issue occurs because: 1. The resize() function on std::vector does not release memory when the new size is less than or equal to the current size 2. It only adjusts the size marker while retaining the capacity and existing data 3. When prepare() is called, the pyramid vectors are resized but old data remains allocated Example from the bug report: Blending 14 images (1920x1080) retained ~200MB after blender.release(). With larger images, several GB could be retained. Root Cause: GPU path (lines 254-256): Correctly calls clear() before operations Non-GPU path (lines 285-298): Missing clear() calls, causing resize() to retain old data Solution: Added .clear() calls before .resize() in the non-GPU path to match the GPU path behavior. This ensures: - Memory from previous blend operations is released in prepare() - Reusing a blender object doesn't accumulate memory - Behavior is consistent between GPU and non-GPU code paths Changes: modules/stitching/src/blenders.cpp: Added 2 clear() calls (dst_pyr_laplace_.clear() and dst_band_weights_.clear()) before resize() in the non-GPU path Pull Request Readiness Checklist: - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch (4.x) - [x] There is a reference to the original bug report and related work (issue #27333) - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable N/A - This is a memory management fix that doesn't affect algorithm behavior. Existing stitching tests verify correctness. - [x] The feature is well documented and sample code can be built with the project CMake The fix maintains existing API behavior, no documentation changes needed --- modules/stitching/src/blenders.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/stitching/src/blenders.cpp b/modules/stitching/src/blenders.cpp index 33a86ee360..534489de76 100644 --- a/modules/stitching/src/blenders.cpp +++ b/modules/stitching/src/blenders.cpp @@ -277,6 +277,9 @@ void MultiBandBlender::prepare(Rect dst_roi) else #endif { + dst_pyr_laplace_.clear(); + dst_band_weights_.clear(); + dst_pyr_laplace_.resize(num_bands_ + 1); dst_pyr_laplace_[0] = dst_; From 29746ab9635953fe8784d24bc2487aad5a9a91fd Mon Sep 17 00:00:00 2001 From: Haodi Yao <20B904013@stu.hit.edu.cn> Date: Thu, 27 Nov 2025 16:34:26 +0800 Subject: [PATCH 21/27] fix(features2d): Correct pointer arithmetic in BRISK corner traversal The pointer offset to move from top-right to bottom-right was incorrect. This change corrects the pointer calculation to use the proper row stride, ensuring it lands on the correct pixel. --- modules/features2d/src/brisk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features2d/src/brisk.cpp b/modules/features2d/src/brisk.cpp index 59a86470f6..a324f44718 100644 --- a/modules/features2d/src/brisk.cpp +++ b/modules/features2d/src/brisk.cpp @@ -626,7 +626,7 @@ BRISK_Impl::smoothedIntensity(const cv::Mat& image, const cv::Mat& integral, con ret_val = A * int(*ptr); ptr += dx + 1; ret_val += B * int(*ptr); - ptr += dy * imagecols + 1; + ptr += (dy + 1) * imagecols; ret_val += C * int(*ptr); ptr -= dx + 1; ret_val += D * int(*ptr); From 2cf9c68da04dd90e29304af78802000f4a0e4c01 Mon Sep 17 00:00:00 2001 From: Ghazi-raad Date: Thu, 27 Nov 2025 14:57:42 +0000 Subject: [PATCH 22/27] Merge pull request #28087 from Ghazi-raad:fix/tempfile-race-condition-19648 Fix tempfile race condition on Windows (issue #19648) #28087 Fix tempfile race condition on Windows Addresses issue #19648 Problem The cv::tempfile() function on Windows used GetTempFileNameA() followed by an immediate DeleteFileA() call. This created a race condition where multiple OpenCV processes running simultaneously could receive the same temporary filename, leading to name collisions. Root Cause The previous implementation: Called GetTempFileNameA() to generate a temp filename Immediately deleted the file to free the name Returned just the filename string Between steps 2 and 3, another process could call GetTempFileNameA() and receive the same filename, causing a collision. Solution Replaced GetTempFileNameA() with GUID-based filename generation using CoCreateGuid(), following the same approach already used in GetTempFileNameWinRT() and Microsoft's recommendations for scenarios requiring many temp files. Changes modules/core/src/system.cpp: Removed GetTempFileNameA() and DeleteFileA() calls Added CoCreateGuid() to generate unique GUID-based filenames Format: "ocv{GUID}" where GUID ensures uniqueness across processes Benefits Eliminates race condition in multi-process scenarios No file I/O overhead from creating and deleting placeholder files Consistent with WinRT implementation approach Follows Microsoft best practices Testing Standard OpenCV test suite. The change only affects Windows temp file naming and maintains the same String return type and usage pattern. --- modules/core/src/system.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 3531678f92..082e0aa804 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -1106,20 +1106,31 @@ String tempfile( const char* suffix ) fname = String(aname); } #else + // Use GUID-based naming to avoid race condition with GetTempFileNameA + // See issue #19648 char temp_dir2[MAX_PATH] = { 0 }; - char temp_file[MAX_PATH] = { 0 }; if (temp_dir.empty()) { ::GetTempPathA(sizeof(temp_dir2), temp_dir2); temp_dir = std::string(temp_dir2); } - if(0 == ::GetTempFileNameA(temp_dir.c_str(), "ocv", 0, temp_file)) + + GUID g; + HRESULT hr = CoCreateGuid(&g); + if (FAILED(hr)) return String(); + char guidStr[40]; + const char* mask = "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x"; + snprintf(guidStr, sizeof(guidStr), mask, + g.Data1, g.Data2, g.Data3, (unsigned int)g.Data4[0], (unsigned int)g.Data4[1], + (unsigned int)g.Data4[2], (unsigned int)g.Data4[3], (unsigned int)g.Data4[4], + (unsigned int)g.Data4[5], (unsigned int)g.Data4[6], (unsigned int)g.Data4[7]); - DeleteFileA(temp_file); - - fname = temp_file; + fname = temp_dir; + if (!fname.empty() && fname[fname.size()-1] != '\\' && fname[fname.size()-1] != '/') + fname += "\\"; + fname = fname + "ocv" + guidStr; #endif # else # ifdef __ANDROID__ From 895be753ac01eae9a6dba4b433dab6f663d08a21 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Thu, 27 Nov 2025 18:30:25 +0300 Subject: [PATCH 23/27] Update FlatBuffers source code to 25.9.23 --- 3rdparty/flatbuffers/README.md | 2 +- .../include/flatbuffers/allocator.h | 10 +- .../flatbuffers/include/flatbuffers/array.h | 97 ++-- .../flatbuffers/include/flatbuffers/base.h | 52 +- .../flatbuffers/include/flatbuffers/buffer.h | 104 ++-- .../include/flatbuffers/buffer_ref.h | 9 +- .../include/flatbuffers/default_allocator.h | 12 +- .../include/flatbuffers/detached_buffer.h | 31 +- .../include/flatbuffers/flatbuffer_builder.h | 508 ++++++++++-------- .../include/flatbuffers/flatbuffers.h | 71 +-- .../include/flatbuffers/stl_emulation.h | 5 +- .../flatbuffers/include/flatbuffers/string.h | 15 +- .../flatbuffers/include/flatbuffers/struct.h | 14 +- .../flatbuffers/include/flatbuffers/table.h | 68 +-- .../flatbuffers/include/flatbuffers/vector.h | 180 ++++--- .../include/flatbuffers/vector_downward.h | 72 +-- .../include/flatbuffers/verifier.h | 200 ++++--- cmake/OpenCVDetectFlatbuffers.cmake | 2 +- modules/dnn/misc/tflite/schema_generated.h | 6 +- 19 files changed, 817 insertions(+), 641 deletions(-) diff --git a/3rdparty/flatbuffers/README.md b/3rdparty/flatbuffers/README.md index 4ea040adc6..bbd293d782 100644 --- a/3rdparty/flatbuffers/README.md +++ b/3rdparty/flatbuffers/README.md @@ -1 +1 @@ -Origin: https://github.com/google/flatbuffers/tree/v23.5.9 +Origin: https://github.com/google/flatbuffers/tree/v25.9.23 diff --git a/3rdparty/flatbuffers/include/flatbuffers/allocator.h b/3rdparty/flatbuffers/include/flatbuffers/allocator.h index 30427190b6..d451818568 100644 --- a/3rdparty/flatbuffers/include/flatbuffers/allocator.h +++ b/3rdparty/flatbuffers/include/flatbuffers/allocator.h @@ -28,21 +28,21 @@ class Allocator { virtual ~Allocator() {} // Allocate `size` bytes of memory. - virtual uint8_t *allocate(size_t size) = 0; + virtual uint8_t* allocate(size_t size) = 0; // Deallocate `size` bytes of memory at `p` allocated by this allocator. - virtual void deallocate(uint8_t *p, size_t size) = 0; + virtual void deallocate(uint8_t* p, size_t size) = 0; // Reallocate `new_size` bytes of memory, replacing the old region of size // `old_size` at `p`. In contrast to a normal realloc, this grows downwards, // and is intended specifcally for `vector_downward` use. // `in_use_back` and `in_use_front` indicate how much of `old_size` is // actually in use at each end, and needs to be copied. - virtual uint8_t *reallocate_downward(uint8_t *old_p, size_t old_size, + virtual uint8_t* reallocate_downward(uint8_t* old_p, size_t old_size, size_t new_size, size_t in_use_back, size_t in_use_front) { FLATBUFFERS_ASSERT(new_size > old_size); // vector_downward only grows - uint8_t *new_p = allocate(new_size); + uint8_t* new_p = allocate(new_size); memcpy_downward(old_p, old_size, new_p, new_size, in_use_back, in_use_front); deallocate(old_p, old_size); @@ -54,7 +54,7 @@ class Allocator { // to `new_p` of `new_size`. Only memory of size `in_use_front` and // `in_use_back` will be copied from the front and back of the old memory // allocation. - void memcpy_downward(uint8_t *old_p, size_t old_size, uint8_t *new_p, + void memcpy_downward(uint8_t* old_p, size_t old_size, uint8_t* new_p, size_t new_size, size_t in_use_back, size_t in_use_front) { memcpy(new_p + new_size - in_use_back, old_p + old_size - in_use_back, diff --git a/3rdparty/flatbuffers/include/flatbuffers/array.h b/3rdparty/flatbuffers/include/flatbuffers/array.h index f4bfbf054c..914f479aab 100644 --- a/3rdparty/flatbuffers/include/flatbuffers/array.h +++ b/3rdparty/flatbuffers/include/flatbuffers/array.h @@ -27,17 +27,15 @@ namespace flatbuffers { // This is used as a helper type for accessing arrays. -template class Array { +template +class Array { // Array can carry only POD data types (scalars or structs). typedef typename flatbuffers::bool_constant::value> scalar_tag; - typedef - typename flatbuffers::conditional::type - IndirectHelperType; public: typedef uint16_t size_type; - typedef typename IndirectHelper::return_type return_type; + typedef typename IndirectHelper::return_type return_type; typedef VectorConstIterator const_iterator; typedef VectorReverseIterator const_reverse_iterator; @@ -50,7 +48,7 @@ template class Array { return_type Get(uoffset_t i) const { FLATBUFFERS_ASSERT(i < size()); - return IndirectHelper::Read(Data(), i); + return IndirectHelper::Read(Data(), i); } return_type operator[](uoffset_t i) const { return Get(i); } @@ -58,7 +56,8 @@ template class Array { // If this is a Vector of enums, T will be its storage type, not the enum // type. This function makes it convenient to retrieve value with enum // type E. - template E GetEnum(uoffset_t i) const { + template + E GetEnum(uoffset_t i) const { return static_cast(Get(i)); } @@ -83,28 +82,28 @@ template class Array { // operation. For primitive types use @p Mutate directly. // @warning Assignments and reads to/from the dereferenced pointer are not // automatically converted to the correct endianness. - typename flatbuffers::conditional::type + typename flatbuffers::conditional::type GetMutablePointer(uoffset_t i) const { FLATBUFFERS_ASSERT(i < size()); - return const_cast(&data()[i]); + return const_cast(&data()[i]); } // Change elements if you have a non-const pointer to this object. - void Mutate(uoffset_t i, const T &val) { MutateImpl(scalar_tag(), i, val); } + void Mutate(uoffset_t i, const T& val) { MutateImpl(scalar_tag(), i, val); } // The raw data in little endian format. Use with care. - const uint8_t *Data() const { return data_; } + const uint8_t* Data() const { return data_; } - uint8_t *Data() { return data_; } + uint8_t* Data() { return data_; } // Similarly, but typed, much like std::vector::data - const T *data() const { return reinterpret_cast(Data()); } - T *data() { return reinterpret_cast(Data()); } + const T* data() const { return reinterpret_cast(Data()); } + T* data() { return reinterpret_cast(Data()); } // Copy data from a span with endian conversion. // If this Array and the span overlap, the behavior is undefined. void CopyFromSpan(flatbuffers::span src) { - const auto p1 = reinterpret_cast(src.data()); + const auto p1 = reinterpret_cast(src.data()); const auto p2 = Data(); FLATBUFFERS_ASSERT(!(p1 >= p2 && p1 < (p2 + length)) && !(p2 >= p1 && p2 < (p1 + length))); @@ -114,12 +113,12 @@ template class Array { } protected: - void MutateImpl(flatbuffers::true_type, uoffset_t i, const T &val) { + void MutateImpl(flatbuffers::true_type, uoffset_t i, const T& val) { FLATBUFFERS_ASSERT(i < size()); WriteScalar(data() + i, val); } - void MutateImpl(flatbuffers::false_type, uoffset_t i, const T &val) { + void MutateImpl(flatbuffers::false_type, uoffset_t i, const T& val) { *(GetMutablePointer(i)) = val; } @@ -134,7 +133,9 @@ template class Array { // Copy data from flatbuffers::span with endian conversion. void CopyFromSpanImpl(flatbuffers::false_type, flatbuffers::span src) { - for (size_type k = 0; k < length; k++) { Mutate(k, src[k]); } + for (size_type k = 0; k < length; k++) { + Mutate(k, src[k]); + } } // This class is only used to access pre-existing data. Don't ever @@ -153,21 +154,21 @@ template class Array { private: // This class is a pointer. Copying will therefore create an invalid object. // Private and unimplemented copy constructor. - Array(const Array &); - Array &operator=(const Array &); + Array(const Array&); + Array& operator=(const Array&); }; // Specialization for Array[struct] with access using Offset pointer. // This specialization used by idl_gen_text.cpp. -template class OffsetT> +template class OffsetT> class Array, length> { static_assert(flatbuffers::is_same::value, "unexpected type T"); public: - typedef const void *return_type; + typedef const void* return_type; typedef uint16_t size_type; - const uint8_t *Data() const { return data_; } + const uint8_t* Data() const { return data_; } // Make idl_gen_text.cpp::PrintContainer happy. return_type operator[](uoffset_t) const { @@ -178,14 +179,14 @@ class Array, length> { private: // This class is only used to access pre-existing data. Array(); - Array(const Array &); - Array &operator=(const Array &); + Array(const Array&); + Array& operator=(const Array&); uint8_t data_[1]; }; -template -FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_span(Array &arr) +template +FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_span(Array& arr) FLATBUFFERS_NOEXCEPT { static_assert( Array::is_span_observable, @@ -193,26 +194,26 @@ FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_span(Array &arr) return span(arr.data(), N); } -template +template FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span make_span( - const Array &arr) FLATBUFFERS_NOEXCEPT { + const Array& arr) FLATBUFFERS_NOEXCEPT { static_assert( Array::is_span_observable, "wrong type U, only plain struct, LE-scalar, or byte types are allowed"); return span(arr.data(), N); } -template +template FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span -make_bytes_span(Array &arr) FLATBUFFERS_NOEXCEPT { +make_bytes_span(Array& arr) FLATBUFFERS_NOEXCEPT { static_assert(Array::is_span_observable, "internal error, Array might hold only scalars or structs"); return span(arr.Data(), sizeof(U) * N); } -template +template FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span -make_bytes_span(const Array &arr) FLATBUFFERS_NOEXCEPT { +make_bytes_span(const Array& arr) FLATBUFFERS_NOEXCEPT { static_assert(Array::is_span_observable, "internal error, Array might hold only scalars or structs"); return span(arr.Data(), sizeof(U) * N); @@ -221,31 +222,31 @@ make_bytes_span(const Array &arr) FLATBUFFERS_NOEXCEPT { // Cast a raw T[length] to a raw flatbuffers::Array // without endian conversion. Use with care. // TODO: move these Cast-methods to `internal` namespace. -template -Array &CastToArray(T (&arr)[length]) { - return *reinterpret_cast *>(arr); +template +Array& CastToArray(T (&arr)[length]) { + return *reinterpret_cast*>(arr); } -template -const Array &CastToArray(const T (&arr)[length]) { - return *reinterpret_cast *>(arr); +template +const Array& CastToArray(const T (&arr)[length]) { + return *reinterpret_cast*>(arr); } -template -Array &CastToArrayOfEnum(T (&arr)[length]) { +template +Array& CastToArrayOfEnum(T (&arr)[length]) { static_assert(sizeof(E) == sizeof(T), "invalid enum type E"); - return *reinterpret_cast *>(arr); + return *reinterpret_cast*>(arr); } -template -const Array &CastToArrayOfEnum(const T (&arr)[length]) { +template +const Array& CastToArrayOfEnum(const T (&arr)[length]) { static_assert(sizeof(E) == sizeof(T), "invalid enum type E"); - return *reinterpret_cast *>(arr); + return *reinterpret_cast*>(arr); } -template -bool operator==(const Array &lhs, - const Array &rhs) noexcept { +template +bool operator==(const Array& lhs, + const Array& rhs) noexcept { return std::addressof(lhs) == std::addressof(rhs) || (lhs.size() == rhs.size() && std::memcmp(lhs.Data(), rhs.Data(), rhs.size() * sizeof(T)) == 0); diff --git a/3rdparty/flatbuffers/include/flatbuffers/base.h b/3rdparty/flatbuffers/include/flatbuffers/base.h index ac8db356a4..987dcbc406 100644 --- a/3rdparty/flatbuffers/include/flatbuffers/base.h +++ b/3rdparty/flatbuffers/include/flatbuffers/base.h @@ -139,9 +139,9 @@ #endif #endif // !defined(FLATBUFFERS_LITTLEENDIAN) -#define FLATBUFFERS_VERSION_MAJOR 23 -#define FLATBUFFERS_VERSION_MINOR 5 -#define FLATBUFFERS_VERSION_REVISION 9 +#define FLATBUFFERS_VERSION_MAJOR 25 +#define FLATBUFFERS_VERSION_MINOR 9 +#define FLATBUFFERS_VERSION_REVISION 23 #define FLATBUFFERS_STRING_EXPAND(X) #X #define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X) namespace flatbuffers { @@ -155,7 +155,7 @@ namespace flatbuffers { #define FLATBUFFERS_FINAL_CLASS final #define FLATBUFFERS_OVERRIDE override #define FLATBUFFERS_EXPLICIT_CPP11 explicit - #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : flatbuffers::voffset_t + #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : ::flatbuffers::voffset_t #else #define FLATBUFFERS_FINAL_CLASS #define FLATBUFFERS_OVERRIDE @@ -279,20 +279,22 @@ namespace flatbuffers { #endif // !FLATBUFFERS_LOCALE_INDEPENDENT // Suppress Undefined Behavior Sanitizer (recoverable only). Usage: -// - __suppress_ubsan__("undefined") -// - __suppress_ubsan__("signed-integer-overflow") +// - FLATBUFFERS_SUPPRESS_UBSAN("undefined") +// - FLATBUFFERS_SUPPRESS_UBSAN("signed-integer-overflow") #if defined(__clang__) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >=7)) - #define __suppress_ubsan__(type) __attribute__((no_sanitize(type))) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) __attribute__((no_sanitize(type))) #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409) - #define __suppress_ubsan__(type) __attribute__((no_sanitize_undefined)) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) __attribute__((no_sanitize_undefined)) #else - #define __suppress_ubsan__(type) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) #endif -// This is constexpr function used for checking compile-time constants. -// Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`. -template FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) { - return !!t; +namespace flatbuffers { + // This is constexpr function used for checking compile-time constants. + // Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`. + template FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) { + return !!t; + } } // Enable C++ attribute [[]] if std:c++17 or higher. @@ -337,15 +339,15 @@ typedef uint16_t voffset_t; typedef uintmax_t largest_scalar_t; // In 32bits, this evaluates to 2GB - 1 -#define FLATBUFFERS_MAX_BUFFER_SIZE std::numeric_limits<::flatbuffers::soffset_t>::max() -#define FLATBUFFERS_MAX_64_BUFFER_SIZE std::numeric_limits<::flatbuffers::soffset64_t>::max() +#define FLATBUFFERS_MAX_BUFFER_SIZE (std::numeric_limits<::flatbuffers::soffset_t>::max)() +#define FLATBUFFERS_MAX_64_BUFFER_SIZE (std::numeric_limits<::flatbuffers::soffset64_t>::max)() // The minimum size buffer that can be a valid flatbuffer. // Includes the offset to the root table (uoffset_t), the offset to the vtable // of the root table (soffset_t), the size of the vtable (uint16_t), and the // size of the referring table (uint16_t). -#define FLATBUFFERS_MIN_BUFFER_SIZE sizeof(uoffset_t) + sizeof(soffset_t) + \ - sizeof(uint16_t) + sizeof(uint16_t) +#define FLATBUFFERS_MIN_BUFFER_SIZE sizeof(::flatbuffers::uoffset_t) + \ + sizeof(::flatbuffers::soffset_t) + sizeof(uint16_t) + sizeof(uint16_t) // We support aligning the contents of buffers up to this size. #ifndef FLATBUFFERS_MAX_ALIGNMENT @@ -361,7 +363,6 @@ inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) { } #if defined(_MSC_VER) - #pragma warning(disable: 4351) // C4351: new behavior: elements of array ... will be default initialized #pragma warning(push) #pragma warning(disable: 4127) // C4127: conditional expression is constant #endif @@ -422,7 +423,7 @@ template T EndianScalar(T t) { template // UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. -__suppress_ubsan__("alignment") +FLATBUFFERS_SUPPRESS_UBSAN("alignment") T ReadScalar(const void *p) { return EndianScalar(*reinterpret_cast(p)); } @@ -436,13 +437,13 @@ T ReadScalar(const void *p) { template // UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. -__suppress_ubsan__("alignment") +FLATBUFFERS_SUPPRESS_UBSAN("alignment") void WriteScalar(void *p, T t) { *reinterpret_cast(p) = EndianScalar(t); } template struct Offset; -template __suppress_ubsan__("alignment") void WriteScalar(void *p, Offset t) { +template FLATBUFFERS_SUPPRESS_UBSAN("alignment") void WriteScalar(void *p, Offset t) { *reinterpret_cast(p) = EndianScalar(t.o); } @@ -453,15 +454,22 @@ template __suppress_ubsan__("alignment") void WriteScalar(void *p, O // Computes how many bytes you'd have to pad to be able to write an // "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in // memory). -__suppress_ubsan__("unsigned-integer-overflow") +FLATBUFFERS_SUPPRESS_UBSAN("unsigned-integer-overflow") inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) { return ((~buf_size) + 1) & (scalar_size - 1); } +#if !defined(_MSC_VER) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif // Generic 'operator==' with conditional specialisations. // T e - new value of a scalar field. // T def - default of scalar (is known at compile-time). template inline bool IsTheSameAs(T e, T def) { return e == def; } +#if !defined(_MSC_VER) + #pragma GCC diagnostic pop +#endif #if defined(FLATBUFFERS_NAN_DEFAULTS) && \ defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) diff --git a/3rdparty/flatbuffers/include/flatbuffers/buffer.h b/3rdparty/flatbuffers/include/flatbuffers/buffer.h index 94d4f7903b..154d187ab7 100644 --- a/3rdparty/flatbuffers/include/flatbuffers/buffer.h +++ b/3rdparty/flatbuffers/include/flatbuffers/buffer.h @@ -20,12 +20,14 @@ #include #include "flatbuffers/base.h" +#include "flatbuffers/stl_emulation.h" namespace flatbuffers { // Wrapper for uoffset_t to allow safe template specialization. // Value is allowed to be 0 to indicate a null object (see e.g. AddOffset). -template struct Offset { +template +struct Offset { // The type of offset to use. typedef uoffset_t offset_type; @@ -36,8 +38,14 @@ template struct Offset { bool IsNull() const { return !o; } }; +template +struct is_specialisation_of_Offset : false_type {}; +template +struct is_specialisation_of_Offset> : true_type {}; + // Wrapper for uoffset64_t Offsets. -template struct Offset64 { +template +struct Offset64 { // The type of offset to use. typedef uoffset64_t offset_type; @@ -48,6 +56,11 @@ template struct Offset64 { bool IsNull() const { return !o; } }; +template +struct is_specialisation_of_Offset64 : false_type {}; +template +struct is_specialisation_of_Offset64> : true_type {}; + // Litmus check for ensuring the Offsets are the expected size. static_assert(sizeof(Offset<>) == 4, "Offset has wrong size"); static_assert(sizeof(Offset64<>) == 8, "Offset64 has wrong size"); @@ -55,12 +68,13 @@ static_assert(sizeof(Offset64<>) == 8, "Offset64 has wrong size"); inline void EndianCheck() { int endiantest = 1; // If this fails, see FLATBUFFERS_LITTLEENDIAN above. - FLATBUFFERS_ASSERT(*reinterpret_cast(&endiantest) == + FLATBUFFERS_ASSERT(*reinterpret_cast(&endiantest) == FLATBUFFERS_LITTLEENDIAN); (void)endiantest; } -template FLATBUFFERS_CONSTEXPR size_t AlignOf() { +template +FLATBUFFERS_CONSTEXPR size_t AlignOf() { // clang-format off #ifdef _MSC_VER return __alignof(T); @@ -76,8 +90,8 @@ template FLATBUFFERS_CONSTEXPR size_t AlignOf() { // Lexicographically compare two strings (possibly containing nulls), and // return true if the first is less than the second. -static inline bool StringLessThan(const char *a_data, uoffset_t a_size, - const char *b_data, uoffset_t b_size) { +static inline bool StringLessThan(const char* a_data, uoffset_t a_size, + const char* b_data, uoffset_t b_size) { const auto cmp = memcmp(a_data, b_data, (std::min)(a_size, b_size)); return cmp == 0 ? a_size < b_size : cmp < 0; } @@ -90,42 +104,43 @@ static inline bool StringLessThan(const char *a_data, uoffset_t a_size, // return type like this. // The typedef is for the convenience of callers of this function // (avoiding the need for a trailing return decltype) -template struct IndirectHelper { +template +struct IndirectHelper { typedef T return_type; typedef T mutable_return_type; static const size_t element_stride = sizeof(T); - static return_type Read(const uint8_t *p, const size_t i) { - return EndianScalar((reinterpret_cast(p))[i]); + static return_type Read(const uint8_t* p, const size_t i) { + return EndianScalar((reinterpret_cast(p))[i]); } - static mutable_return_type Read(uint8_t *p, const size_t i) { + static mutable_return_type Read(uint8_t* p, const size_t i) { return reinterpret_cast( - Read(const_cast(p), i)); + Read(const_cast(p), i)); } }; // For vector of Offsets. -template class OffsetT> +template class OffsetT> struct IndirectHelper> { - typedef const T *return_type; - typedef T *mutable_return_type; + typedef const T* return_type; + typedef T* mutable_return_type; typedef typename OffsetT::offset_type offset_type; static const offset_type element_stride = sizeof(offset_type); - static return_type Read(const uint8_t *const p, const offset_type i) { + static return_type Read(const uint8_t* const p, const offset_type i) { // Offsets are relative to themselves, so first update the pointer to // point to the offset location. - const uint8_t *const offset_location = p + i * element_stride; + const uint8_t* const offset_location = p + i * element_stride; // Then read the scalar value of the offset (which may be 32 or 64-bits) and // then determine the relative location from the offset location. return reinterpret_cast( offset_location + ReadScalar(offset_location)); } - static mutable_return_type Read(uint8_t *const p, const offset_type i) { + static mutable_return_type Read(uint8_t* const p, const offset_type i) { // Offsets are relative to themselves, so first update the pointer to // point to the offset location. - uint8_t *const offset_location = p + i * element_stride; + uint8_t* const offset_location = p + i * element_stride; // Then read the scalar value of the offset (which may be 32 or 64-bits) and // then determine the relative location from the offset location. @@ -135,16 +150,26 @@ struct IndirectHelper> { }; // For vector of structs. -template struct IndirectHelper { - typedef const T *return_type; - typedef T *mutable_return_type; - static const size_t element_stride = sizeof(T); +template +struct IndirectHelper< + T, typename std::enable_if< + !std::is_scalar::type>::value && + !is_specialisation_of_Offset::value && + !is_specialisation_of_Offset64::value>::type> { + private: + typedef typename std::remove_pointer::type>::type + pointee_type; - static return_type Read(const uint8_t *const p, const size_t i) { + public: + typedef const pointee_type* return_type; + typedef pointee_type* mutable_return_type; + static const size_t element_stride = sizeof(pointee_type); + + static return_type Read(const uint8_t* const p, const size_t i) { // Structs are stored inline, relative to the first struct pointer. return reinterpret_cast(p + i * element_stride); } - static mutable_return_type Read(uint8_t *const p, const size_t i) { + static mutable_return_type Read(uint8_t* const p, const size_t i) { // Structs are stored inline, relative to the first struct pointer. return reinterpret_cast(p + i * element_stride); } @@ -157,14 +182,14 @@ template struct IndirectHelper { /// This function is UNDEFINED for FlatBuffers whose schema does not include /// a file_identifier (likely points at padding or the start of a the root /// vtable). -inline const char *GetBufferIdentifier(const void *buf, +inline const char* GetBufferIdentifier(const void* buf, bool size_prefixed = false) { - return reinterpret_cast(buf) + + return reinterpret_cast(buf) + ((size_prefixed) ? 2 * sizeof(uoffset_t) : sizeof(uoffset_t)); } // Helper to see if the identifier in a buffer has the expected value. -inline bool BufferHasIdentifier(const void *buf, const char *identifier, +inline bool BufferHasIdentifier(const void* buf, const char* identifier, bool size_prefixed = false) { return strncmp(GetBufferIdentifier(buf, size_prefixed), identifier, flatbuffers::kFileIdentifierLength) == 0; @@ -172,26 +197,27 @@ inline bool BufferHasIdentifier(const void *buf, const char *identifier, /// @cond FLATBUFFERS_INTERNAL // Helpers to get a typed pointer to the root object contained in the buffer. -template T *GetMutableRoot(void *buf) { +template +T* GetMutableRoot(void* buf) { if (!buf) return nullptr; EndianCheck(); - return reinterpret_cast( - reinterpret_cast(buf) + - EndianScalar(*reinterpret_cast(buf))); + return reinterpret_cast(reinterpret_cast(buf) + + EndianScalar(*reinterpret_cast(buf))); } -template -T *GetMutableSizePrefixedRoot(void *buf) { - return GetMutableRoot(reinterpret_cast(buf) + sizeof(SizeT)); +template +T* GetMutableSizePrefixedRoot(void* buf) { + return GetMutableRoot(reinterpret_cast(buf) + sizeof(SizeT)); } -template const T *GetRoot(const void *buf) { - return GetMutableRoot(const_cast(buf)); +template +const T* GetRoot(const void* buf) { + return GetMutableRoot(const_cast(buf)); } -template -const T *GetSizePrefixedRoot(const void *buf) { - return GetRoot(reinterpret_cast(buf) + sizeof(SizeT)); +template +const T* GetSizePrefixedRoot(const void* buf) { + return GetRoot(reinterpret_cast(buf) + sizeof(SizeT)); } } // namespace flatbuffers diff --git a/3rdparty/flatbuffers/include/flatbuffers/buffer_ref.h b/3rdparty/flatbuffers/include/flatbuffers/buffer_ref.h index f70941fc64..746903eb97 100644 --- a/3rdparty/flatbuffers/include/flatbuffers/buffer_ref.h +++ b/3rdparty/flatbuffers/include/flatbuffers/buffer_ref.h @@ -27,23 +27,24 @@ namespace flatbuffers { // A BufferRef does not own its buffer. struct BufferRefBase {}; // for std::is_base_of -template struct BufferRef : BufferRefBase { +template +struct BufferRef : BufferRefBase { BufferRef() : buf(nullptr), len(0), must_free(false) {} - BufferRef(uint8_t *_buf, uoffset_t _len) + BufferRef(uint8_t* _buf, uoffset_t _len) : buf(_buf), len(_len), must_free(false) {} ~BufferRef() { if (must_free) free(buf); } - const T *GetRoot() const { return flatbuffers::GetRoot(buf); } + const T* GetRoot() const { return flatbuffers::GetRoot(buf); } bool Verify() { Verifier verifier(buf, len); return verifier.VerifyBuffer(nullptr); } - uint8_t *buf; + uint8_t* buf; uoffset_t len; bool must_free; }; diff --git a/3rdparty/flatbuffers/include/flatbuffers/default_allocator.h b/3rdparty/flatbuffers/include/flatbuffers/default_allocator.h index d4724122cb..d1cab08d74 100644 --- a/3rdparty/flatbuffers/include/flatbuffers/default_allocator.h +++ b/3rdparty/flatbuffers/include/flatbuffers/default_allocator.h @@ -25,32 +25,32 @@ namespace flatbuffers { // DefaultAllocator uses new/delete to allocate memory regions class DefaultAllocator : public Allocator { public: - uint8_t *allocate(size_t size) FLATBUFFERS_OVERRIDE { + uint8_t* allocate(size_t size) FLATBUFFERS_OVERRIDE { return new uint8_t[size]; } - void deallocate(uint8_t *p, size_t) FLATBUFFERS_OVERRIDE { delete[] p; } + void deallocate(uint8_t* p, size_t) FLATBUFFERS_OVERRIDE { delete[] p; } - static void dealloc(void *p, size_t) { delete[] static_cast(p); } + static void dealloc(void* p, size_t) { delete[] static_cast(p); } }; // These functions allow for a null allocator to mean use the default allocator, // as used by DetachedBuffer and vector_downward below. // This is to avoid having a statically or dynamically allocated default // allocator, or having to move it between the classes that may own it. -inline uint8_t *Allocate(Allocator *allocator, size_t size) { +inline uint8_t* Allocate(Allocator* allocator, size_t size) { return allocator ? allocator->allocate(size) : DefaultAllocator().allocate(size); } -inline void Deallocate(Allocator *allocator, uint8_t *p, size_t size) { +inline void Deallocate(Allocator* allocator, uint8_t* p, size_t size) { if (allocator) allocator->deallocate(p, size); else DefaultAllocator().deallocate(p, size); } -inline uint8_t *ReallocateDownward(Allocator *allocator, uint8_t *old_p, +inline uint8_t* ReallocateDownward(Allocator* allocator, uint8_t* old_p, size_t old_size, size_t new_size, size_t in_use_back, size_t in_use_front) { return allocator ? allocator->reallocate_downward(old_p, old_size, new_size, diff --git a/3rdparty/flatbuffers/include/flatbuffers/detached_buffer.h b/3rdparty/flatbuffers/include/flatbuffers/detached_buffer.h index 5e900baeb5..0577a42d96 100644 --- a/3rdparty/flatbuffers/include/flatbuffers/detached_buffer.h +++ b/3rdparty/flatbuffers/include/flatbuffers/detached_buffer.h @@ -36,8 +36,8 @@ class DetachedBuffer { cur_(nullptr), size_(0) {} - DetachedBuffer(Allocator *allocator, bool own_allocator, uint8_t *buf, - size_t reserved, uint8_t *cur, size_t sz) + DetachedBuffer(Allocator* allocator, bool own_allocator, uint8_t* buf, + size_t reserved, uint8_t* cur, size_t sz) : allocator_(allocator), own_allocator_(own_allocator), buf_(buf), @@ -45,7 +45,7 @@ class DetachedBuffer { cur_(cur), size_(sz) {} - DetachedBuffer(DetachedBuffer &&other) noexcept + DetachedBuffer(DetachedBuffer&& other) noexcept : allocator_(other.allocator_), own_allocator_(other.own_allocator_), buf_(other.buf_), @@ -55,7 +55,7 @@ class DetachedBuffer { other.reset(); } - DetachedBuffer &operator=(DetachedBuffer &&other) noexcept { + DetachedBuffer& operator=(DetachedBuffer&& other) noexcept { if (this == &other) return *this; destroy(); @@ -74,28 +74,35 @@ class DetachedBuffer { ~DetachedBuffer() { destroy(); } - const uint8_t *data() const { return cur_; } + const uint8_t* data() const { return cur_; } - uint8_t *data() { return cur_; } + uint8_t* data() { return cur_; } size_t size() const { return size_; } + uint8_t* begin() { return data(); } + const uint8_t* begin() const { return data(); } + uint8_t* end() { return data() + size(); } + const uint8_t* end() const { return data() + size(); } + // These may change access mode, leave these at end of public section - FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer &other)); + FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer& other)); FLATBUFFERS_DELETE_FUNC( - DetachedBuffer &operator=(const DetachedBuffer &other)); + DetachedBuffer& operator=(const DetachedBuffer& other)); protected: - Allocator *allocator_; + Allocator* allocator_; bool own_allocator_; - uint8_t *buf_; + uint8_t* buf_; size_t reserved_; - uint8_t *cur_; + uint8_t* cur_; size_t size_; inline void destroy() { if (buf_) Deallocate(allocator_, buf_, reserved_); - if (own_allocator_ && allocator_) { delete allocator_; } + if (own_allocator_ && allocator_) { + delete allocator_; + } reset(); } diff --git a/3rdparty/flatbuffers/include/flatbuffers/flatbuffer_builder.h b/3rdparty/flatbuffers/include/flatbuffers/flatbuffer_builder.h index ed932cd9cc..9eea6bab0c 100644 --- a/3rdparty/flatbuffers/include/flatbuffers/flatbuffer_builder.h +++ b/3rdparty/flatbuffers/include/flatbuffers/flatbuffer_builder.h @@ -45,22 +45,24 @@ inline voffset_t FieldIndexToOffset(voffset_t field_id) { // Should correspond to what EndTable() below builds up. const voffset_t fixed_fields = 2 * sizeof(voffset_t); // Vtable size and Object Size. - return fixed_fields + field_id * sizeof(voffset_t); + size_t offset = fixed_fields + field_id * sizeof(voffset_t); + FLATBUFFERS_ASSERT(offset < std::numeric_limits::max()); + return static_cast(offset); } -template> -const T *data(const std::vector &v) { +template > +const T* data(const std::vector& v) { // Eventually the returned pointer gets passed down to memcpy, so // we need it to be non-null to avoid undefined behavior. static uint8_t t; - return v.empty() ? reinterpret_cast(&t) : &v.front(); + return v.empty() ? reinterpret_cast(&t) : &v.front(); } -template> -T *data(std::vector &v) { +template > +T* data(std::vector& v) { // Eventually the returned pointer gets passed down to memcpy, so // we need it to be non-null to avoid undefined behavior. static uint8_t t; - return v.empty() ? reinterpret_cast(&t) : &v.front(); + return v.empty() ? reinterpret_cast(&t) : &v.front(); } /// @addtogroup flatbuffers_cpp_api @@ -72,7 +74,8 @@ T *data(std::vector &v) { /// `PushElement`/`AddElement`/`EndTable`, or the builtin `CreateString`/ /// `CreateVector` functions. Do this is depth-first order to build up a tree to /// the root. `Finish()` wraps up the buffer ready for transport. -template class FlatBufferBuilderImpl { +template +class FlatBufferBuilderImpl { public: // This switches the size type of the builder, based on if its 64-bit aware // (uoffset64_t) or not (uoffset_t). @@ -91,7 +94,7 @@ template class FlatBufferBuilderImpl { /// types with custom alignment AND you wish to read the buffer in-place /// directly after creation. explicit FlatBufferBuilderImpl( - size_t initial_size = 1024, Allocator *allocator = nullptr, + size_t initial_size = 1024, Allocator* allocator = nullptr, bool own_allocator = false, size_t buffer_minalign = AlignOf()) : buf_(initial_size, allocator, own_allocator, buffer_minalign, @@ -110,7 +113,7 @@ template class FlatBufferBuilderImpl { } /// @brief Move constructor for FlatBufferBuilder. - FlatBufferBuilderImpl(FlatBufferBuilderImpl &&other) noexcept + FlatBufferBuilderImpl(FlatBufferBuilderImpl&& other) noexcept : buf_(1024, nullptr, false, AlignOf(), static_cast(Is64Aware ? FLATBUFFERS_MAX_64_BUFFER_SIZE : FLATBUFFERS_MAX_BUFFER_SIZE)), @@ -131,14 +134,14 @@ template class FlatBufferBuilderImpl { } /// @brief Move assignment operator for FlatBufferBuilder. - FlatBufferBuilderImpl &operator=(FlatBufferBuilderImpl &&other) noexcept { + FlatBufferBuilderImpl& operator=(FlatBufferBuilderImpl&& other) noexcept { // Move construct a temporary and swap idiom FlatBufferBuilderImpl temp(std::move(other)); Swap(temp); return *this; } - void Swap(FlatBufferBuilderImpl &other) { + void Swap(FlatBufferBuilderImpl& other) { using std::swap; buf_.swap(other.buf_); swap(num_field_loc, other.num_field_loc); @@ -180,7 +183,7 @@ template class FlatBufferBuilderImpl { /// @brief The current size of the serialized buffer relative to the end of /// the 32-bit region. /// @return Returns an `uoffset_t` with the current size of the buffer. - template + template // Only enable this method for the 64-bit builder, as only that builder is // concerned with the 32/64-bit boundary, and should be the one to bare any // run time costs. @@ -193,7 +196,7 @@ template class FlatBufferBuilderImpl { return static_cast(GetSize() - length_of_64_bit_region_); } - template + template // Only enable this method for the 32-bit builder. typename std::enable_if::type GetSizeRelative32BitRegion() const { @@ -203,7 +206,7 @@ template class FlatBufferBuilderImpl { /// @brief Get the serialized buffer (after you call `Finish()`). /// @return Returns an `uint8_t` pointer to the FlatBuffer data inside the /// buffer. - uint8_t *GetBufferPointer() const { + uint8_t* GetBufferPointer() const { Finished(); return buf_.data(); } @@ -218,23 +221,15 @@ template class FlatBufferBuilderImpl { /// @brief Get a pointer to an unfinished buffer. /// @return Returns a `uint8_t` pointer to the unfinished buffer. - uint8_t *GetCurrentBufferPointer() const { return buf_.data(); } - - /// @brief Get the released pointer to the serialized buffer. - /// @warning Do NOT attempt to use this FlatBufferBuilder afterwards! - /// @return A `FlatBuffer` that owns the buffer and its allocator and - /// behaves similar to a `unique_ptr` with a deleter. - FLATBUFFERS_ATTRIBUTE([[deprecated("use Release() instead")]]) - DetachedBuffer ReleaseBufferPointer() { - Finished(); - return buf_.release(); - } + uint8_t* GetCurrentBufferPointer() const { return buf_.data(); } /// @brief Get the released DetachedBuffer. /// @return A `DetachedBuffer` that owns the buffer and its allocator. DetachedBuffer Release() { Finished(); - return buf_.release(); + DetachedBuffer buffer = buf_.release(); + Clear(); + return buffer; } /// @brief Get the released pointer to the serialized buffer. @@ -245,10 +240,12 @@ template class FlatBufferBuilderImpl { /// @return A raw pointer to the start of the memory block containing /// the serialized `FlatBuffer`. /// @remark If the allocator is owned, it gets deleted when the destructor is - /// called.. - uint8_t *ReleaseRaw(size_t &size, size_t &offset) { + /// called. + uint8_t* ReleaseRaw(size_t& size, size_t& offset) { Finished(); - return buf_.release_raw(size, offset); + uint8_t* raw = buf_.release_raw(size, offset); + Clear(); + return raw; } /// @brief get the minimum alignment this buffer needs to be accessed @@ -295,22 +292,23 @@ template class FlatBufferBuilderImpl { buf_.fill(PaddingBytes(buf_.size(), elem_size)); } - void PushFlatBuffer(const uint8_t *bytes, size_t size) { + void PushFlatBuffer(const uint8_t* bytes, size_t size) { PushBytes(bytes, size); finished = true; } - void PushBytes(const uint8_t *bytes, size_t size) { buf_.push(bytes, size); } + void PushBytes(const uint8_t* bytes, size_t size) { buf_.push(bytes, size); } void PopBytes(size_t amount) { buf_.pop(amount); } - template void AssertScalarT() { + template + void AssertScalarT() { // The code assumes power of 2 sizes and endian-swap-ability. static_assert(flatbuffers::is_scalar::value, "T must be a scalar type"); } // Write a single aligned scalar to the buffer - template + template ReturnT PushElement(T element) { AssertScalarT(); Align(sizeof(T)); @@ -318,7 +316,7 @@ template class FlatBufferBuilderImpl { return CalculateOffset(); } - template class OffsetT = Offset> + template class OffsetT = Offset> uoffset_t PushElement(OffsetT off) { // Special case for offsets: see ReferTo below. return PushElement(ReferTo(off.o)); @@ -327,34 +325,41 @@ template class FlatBufferBuilderImpl { // When writing fields, we track where they are, so we can create correct // vtables later. void TrackField(voffset_t field, uoffset_t off) { - FieldLoc fl = { off, field }; + FieldLoc fl = {off, field}; buf_.scratch_push_small(fl); num_field_loc++; - if (field > max_voffset_) { max_voffset_ = field; } + if (field > max_voffset_) { + max_voffset_ = field; + } } // Like PushElement, but additionally tracks the field this represents. - template void AddElement(voffset_t field, T e, T def) { + template + void AddElement(voffset_t field, T e, T def) { // We don't serialize values equal to the default. if (IsTheSameAs(e, def) && !force_defaults_) return; TrackField(field, PushElement(e)); } - template void AddElement(voffset_t field, T e) { + template + void AddElement(voffset_t field, T e) { TrackField(field, PushElement(e)); } - template void AddOffset(voffset_t field, Offset off) { + template + void AddOffset(voffset_t field, Offset off) { if (off.IsNull()) return; // Don't store. AddElement(field, ReferTo(off.o), static_cast(0)); } - template void AddOffset(voffset_t field, Offset64 off) { + template + void AddOffset(voffset_t field, Offset64 off) { if (off.IsNull()) return; // Don't store. AddElement(field, ReferTo(off.o), static_cast(0)); } - template void AddStruct(voffset_t field, const T *structptr) { + template + void AddStruct(voffset_t field, const T* structptr) { if (!structptr) return; // Default, don't store. Align(AlignOf()); buf_.push_small(*structptr); @@ -384,12 +389,14 @@ template class FlatBufferBuilderImpl { return ReferTo(off, GetSize()); } - template T ReferTo(const T off, const T2 size) { + template + T ReferTo(const T off, const T2 size) { FLATBUFFERS_ASSERT(off && off <= size); return size - off + static_cast(sizeof(T)); } - template T ReferTo(const T off, const T size) { + template + T ReferTo(const T off, const T size) { FLATBUFFERS_ASSERT(off && off <= size); return size - off + static_cast(sizeof(T)); } @@ -445,7 +452,7 @@ template class FlatBufferBuilderImpl { // Write the offsets into the table for (auto it = buf_.scratch_end() - num_field_loc * sizeof(FieldLoc); it < buf_.scratch_end(); it += sizeof(FieldLoc)) { - auto field_location = reinterpret_cast(it); + auto field_location = reinterpret_cast(it); const voffset_t pos = static_cast(vtable_offset_loc - field_location->off); // If this asserts, it means you've set a field twice. @@ -454,7 +461,7 @@ template class FlatBufferBuilderImpl { WriteScalar(buf_.data() + field_location->id, pos); } ClearOffsets(); - auto vt1 = reinterpret_cast(buf_.data()); + auto vt1 = reinterpret_cast(buf_.data()); auto vt1_size = ReadScalar(vt1); auto vt_use = GetSizeRelative32BitRegion(); // See if we already have generated a vtable with this exact same @@ -462,8 +469,8 @@ template class FlatBufferBuilderImpl { if (dedup_vtables_) { for (auto it = buf_.scratch_data(); it < buf_.scratch_end(); it += sizeof(uoffset_t)) { - auto vt_offset_ptr = reinterpret_cast(it); - auto vt2 = reinterpret_cast(buf_.data_at(*vt_offset_ptr)); + auto vt_offset_ptr = reinterpret_cast(it); + auto vt2 = reinterpret_cast(buf_.data_at(*vt_offset_ptr)); auto vt2_size = ReadScalar(vt2); if (vt1_size != vt2_size || 0 != memcmp(vt2, vt1, vt1_size)) continue; vt_use = *vt_offset_ptr; @@ -494,8 +501,9 @@ template class FlatBufferBuilderImpl { // This checks a required field has been set in a given table that has // just been constructed. - template void Required(Offset table, voffset_t field) { - auto table_ptr = reinterpret_cast(buf_.data_at(table.o)); + template + void Required(Offset table, voffset_t field) { + auto table_ptr = reinterpret_cast(buf_.data_at(table.o)); bool ok = table_ptr->GetOptionalFieldOffset(field) != 0; // If this fails, the caller will show what field needs to be set. FLATBUFFERS_ASSERT(ok); @@ -525,7 +533,8 @@ template class FlatBufferBuilderImpl { // Aligns such than when "len" bytes are written, an object of type `AlignT` // can be written after it (forward in the buffer) without padding. - template void PreAlign(size_t len) { + template + void PreAlign(size_t len) { AssertScalarT(); PreAlign(len, AlignOf()); } @@ -535,8 +544,8 @@ template class FlatBufferBuilderImpl { /// @param[in] str A const char pointer to the data to be stored as a string. /// @param[in] len The number of bytes that should be stored from `str`. /// @return Returns the offset in the buffer where the string starts. - template class OffsetT = Offset> - OffsetT CreateString(const char *str, size_t len) { + template