From 1691a2355c8eff94b43ba8473ef49477a1d4e6c2 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Mon, 3 Nov 2025 15:18:43 +0100 Subject: [PATCH 01/32] Allow protobuffer message to be compiled with LITE_RUNTIME When adding "option optimize_for = LITE_RUNTIME;" to proto messages, they are compiled as lighter MessageLite (the base class of Message). Those lighter messages do not allow for reflection though: https://developers.google.com/protocol-buffers/docs/reference/cpp-generated This fixes https://github.com/opencv/opencv/issues/20275 --- modules/dnn/src/caffe/caffe_importer.cpp | 26 +++++++++++- modules/dnn/src/caffe/caffe_io.cpp | 46 ++++++++++++++++++---- modules/dnn/src/caffe/caffe_io.hpp | 6 ++- modules/dnn/src/tensorflow/tf_importer.cpp | 16 +++++++- 4 files changed, 82 insertions(+), 12 deletions(-) diff --git a/modules/dnn/src/caffe/caffe_importer.cpp b/modules/dnn/src/caffe/caffe_importer.cpp index a4afec136a..c92ebea619 100644 --- a/modules/dnn/src/caffe/caffe_importer.cpp +++ b/modules/dnn/src/caffe/caffe_importer.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -203,7 +204,9 @@ public: return (str.size() >= _param.size()) && str.compare(str.size() - _param.size(), _param.size(), _param) == 0; } - void extractLayerParams(const Message &msg, cv::dnn::LayerParams ¶ms, bool isInternal = false) + template + typename std::enable_if::value, void>::type + extractLayerParams(const MSG &msg, cv::dnn::LayerParams ¶ms, bool isInternal = false) { const Descriptor *msgDesc = msg.GetDescriptor(); const Reflection *msgRefl = msg.GetReflection(); @@ -238,6 +241,13 @@ public: } } + template + typename std::enable_if::value, void>::type + extractLayerParams(const MSG &msg, cv::dnn::LayerParams ¶ms, bool isInternal = false) + { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); + } + void blobShapeFromProto(const caffe::BlobProto &pbBlob, MatShape& shape) { shape.clear(); @@ -256,6 +266,18 @@ public: shape.resize(1, 1); // Is a scalar. } + template + typename std::enable_if::value, void>::type + AssertBlobProtoIsFloat(const BLOB_PROTO &pbBlob) { + CV_DbgAssert(pbBlob.GetDescriptor()->FindFieldByLowercaseName("data")->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT); + } + + template + typename std::enable_if::value, void>::type + AssertBlobProtoIsFloat(const BLOB_PROTO &pbBlob) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); + } + void blobFromProto(const caffe::BlobProto &pbBlob, cv::Mat &dstBlob) { MatShape shape; @@ -267,7 +289,7 @@ public: // Single precision floats. CV_Assert(pbBlob.data_size() == (int)dstBlob.total()); - CV_DbgAssert(pbBlob.GetDescriptor()->FindFieldByLowercaseName("data")->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT); + AssertBlobProtoIsFloat(pbBlob); Mat(dstBlob.dims, &dstBlob.size[0], CV_32F, (void*)pbBlob.data().data()).copyTo(dstBlob); } else diff --git a/modules/dnn/src/caffe/caffe_io.cpp b/modules/dnn/src/caffe/caffe_io.cpp index ebecf95eea..8897e70d67 100644 --- a/modules/dnn/src/caffe/caffe_io.cpp +++ b/modules/dnn/src/caffe/caffe_io.cpp @@ -101,6 +101,7 @@ #include #include #include +#include #include "caffe_io.hpp" #include "glog_emulator.hpp" @@ -1110,7 +1111,7 @@ const char* UpgradeV1LayerType(const V1LayerParameter_LayerType type) { static const int kProtoReadBytesLimit = INT_MAX; // Max size of 2 GB minus 1 byte. -bool ReadProtoFromBinary(ZeroCopyInputStream* input, Message *proto) { +bool ReadProtoFromBinary(ZeroCopyInputStream* input, MessageLite *proto) { CodedInputStream coded_input(input); #if GOOGLE_PROTOBUF_VERSION >= 3006000 coded_input.SetTotalBytesLimit(kProtoReadBytesLimit); @@ -1133,7 +1134,12 @@ bool ReadProtoFromTextFile(const char* filename, Message* proto) { return parser.Parse(&input, proto); } -bool ReadProtoFromBinaryFile(const char* filename, Message* proto) { +bool ReadProtoFromTextFile(const char* filename, MessageLite* proto) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); + return false; +} + +bool ReadProtoFromBinaryFile(const char* filename, MessageLite* proto) { std::ifstream fs(filename, std::ifstream::in | std::ifstream::binary); CHECK(fs.is_open()) << "Can't open \"" << filename << "\""; IstreamInputStream raw_input(&fs); @@ -1151,26 +1157,52 @@ bool ReadProtoFromTextBuffer(const char* data, size_t len, Message* proto) { return parser.Parse(&input, proto); } +bool ReadProtoFromTextBuffer(const char* data, size_t len, MessageLite* proto) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); + return false; +} -bool ReadProtoFromBinaryBuffer(const char* data, size_t len, Message* proto) { +bool ReadProtoFromBinaryBuffer(const char* data, size_t len, MessageLite* proto) { ArrayInputStream raw_input(data, len); return ReadProtoFromBinary(&raw_input, proto); } -void ReadNetParamsFromTextFileOrDie(const char* param_file, - NetParameter* param) { +template +typename std::enable_if::value, void>::type +ReadNetParamsFromTextFileOrDieImpl(const char* param_file, MESSAGE* param) { CHECK(ReadProtoFromTextFile(param_file, param)) << "Failed to parse NetParameter file: " << param_file; UpgradeNetAsNeeded(param_file, param); } -void ReadNetParamsFromTextBufferOrDie(const char* data, size_t len, - NetParameter* param) { +template +typename std::enable_if::value, void>::type +ReadNetParamsFromTextFileOrDieImpl(const char* param_file, MESSAGE* param) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); +} + +void ReadNetParamsFromTextFileOrDie(const char* param_file, NetParameter* param) { + ReadNetParamsFromTextFileOrDieImpl(param_file, param); +} + +template +typename std::enable_if::value, void>::type +ReadNetParamsFromTextBufferOrDieImpl(const char* data, size_t len, MESSAGE* param) { CHECK(ReadProtoFromTextBuffer(data, len, param)) << "Failed to parse NetParameter buffer"; UpgradeNetAsNeeded("memory buffer", param); } +template +typename std::enable_if::value, void>::type +ReadNetParamsFromTextBufferOrDieImpl(const char* data, size_t len, MESSAGE* param) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); +} + +void ReadNetParamsFromTextBufferOrDie(const char* data, size_t len, NetParameter* param) { + ReadNetParamsFromTextBufferOrDieImpl(data, len, param); +} + void ReadNetParamsFromBinaryFileOrDie(const char* param_file, NetParameter* param) { CHECK(ReadProtoFromBinaryFile(param_file, param)) diff --git a/modules/dnn/src/caffe/caffe_io.hpp b/modules/dnn/src/caffe/caffe_io.hpp index 544db1c3ed..71897d8af4 100644 --- a/modules/dnn/src/caffe/caffe_io.hpp +++ b/modules/dnn/src/caffe/caffe_io.hpp @@ -119,9 +119,11 @@ void ReadNetParamsFromTextBufferOrDie(const char* data, size_t len, // Utility functions used internally by Caffe and TensorFlow loaders bool ReadProtoFromTextFile(const char* filename, ::google::protobuf::Message* proto); -bool ReadProtoFromBinaryFile(const char* filename, ::google::protobuf::Message* proto); +bool ReadProtoFromTextFile(const char* filename, ::google::protobuf::MessageLite* proto); +bool ReadProtoFromBinaryFile(const char* filename, ::google::protobuf::MessageLite* proto); bool ReadProtoFromTextBuffer(const char* data, size_t len, ::google::protobuf::Message* proto); -bool ReadProtoFromBinaryBuffer(const char* data, size_t len, ::google::protobuf::Message* proto); +bool ReadProtoFromTextBuffer(const char* data, size_t len, ::google::protobuf::MessageLite* proto); +bool ReadProtoFromBinaryBuffer(const char* data, size_t len, ::google::protobuf::MessageLite* proto); } } diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index b000b2a71b..3bd7e63b11 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -27,6 +27,7 @@ Implementation of Tensorflow models parser #include #include #include +#include #include "tf_graph_simplifier.hpp" #endif @@ -3290,6 +3291,19 @@ Net readNetFromTensorflow(const std::vector& bufferModel, const std::vect bufferConfigPtr, bufferConfig.size()); } + +template +typename std::enable_if::value, void>::type +PrintToStringImpl(const GRAPH_DEF& net, std::string* content) { + google::protobuf::TextFormat::PrintToString(net, content); +} + +template +typename std::enable_if::value, void>::type +PrintToStringImpl(const GRAPH_DEF& net, std::string* content) { + CV_Error(Error::StsError, "DNN/TF: do not have your message be a MessageLite"); +} + void writeTextGraph(const String& _model, const String& output) { String model = _model; @@ -3312,7 +3326,7 @@ void writeTextGraph(const String& _model, const String& output) } std::string content; - google::protobuf::TextFormat::PrintToString(net, &content); + PrintToStringImpl(net, &content); std::ofstream ofs(output.c_str()); ofs << content; From 9fc556a83e770be777e69d2b4085a652469dccc4 Mon Sep 17 00:00:00 2001 From: Pierre Chatelier Date: Fri, 7 Nov 2025 08:34:52 +0100 Subject: [PATCH 02/32] Merge pull request #27366 from chacha21:arrowedLine_clipped Try to fix distant points to save time when ThickLine() calls FillConvexPoly() #27366 Proposal for #27365 cv::clipLine() is useful, but one should take care of a margin to preserve line caps. ### 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. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/src/drawing.cpp | 12 ++++++++++++ modules/imgproc/test/test_drawing.cpp | 2 +- modules/js/test/test_features2d.js | 16 ++++++++-------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/modules/imgproc/src/drawing.cpp b/modules/imgproc/src/drawing.cpp index e3158750c5..8214b17013 100644 --- a/modules/imgproc/src/drawing.cpp +++ b/modules/imgproc/src/drawing.cpp @@ -1646,6 +1646,18 @@ ThickLine( Mat& img, Point2l p0, Point2l p1, const void* color, { static const double INV_XY_ONE = 1./static_cast(XY_ONE); + Rect_ boundingRect(Point2l(0, 0), (Size2l)img.size()); + if( (thickness > 1) && (shift == 0) && ( !boundingRect.contains(p0) || !boundingRect.contains(p1) ) ) + { + const int margin = thickness; + const Point2l offset(margin, margin); + p0 += offset; + p1 += offset; + clipLine(Size2l(boundingRect.width+2*margin, boundingRect.height+2*margin), p0, p1); + p0 -= offset; + p1 -= offset; + } + p0.x <<= XY_SHIFT - shift; p0.y <<= XY_SHIFT - shift; p1.x <<= XY_SHIFT - shift; diff --git a/modules/imgproc/test/test_drawing.cpp b/modules/imgproc/test/test_drawing.cpp index 80e01794a1..50c146baad 100644 --- a/modules/imgproc/test/test_drawing.cpp +++ b/modules/imgproc/test/test_drawing.cpp @@ -589,7 +589,7 @@ TEST(Drawing, longline) Mat mat = Mat::zeros(256, 256, CV_8UC1); line(mat, cv::Point(34, 204), cv::Point(46400, 47400), cv::Scalar(255), 3); - EXPECT_EQ(310, cv::countNonZero(mat)); + EXPECT_EQ(264, cv::countNonZero(mat)); Point pt[6]; pt[0].x = 32; diff --git a/modules/js/test/test_features2d.js b/modules/js/test/test_features2d.js index 7844da3146..c49ec7ea90 100644 --- a/modules/js/test/test_features2d.js +++ b/modules/js/test/test_features2d.js @@ -26,7 +26,7 @@ QUnit.test('Detectors', function(assert) { let orb = new cv.ORB(); orb.detect(image, kp); - assert.equal(kp.size(), 67, 'ORB'); + assert.equal(kp.size(), 68, 'ORB'); let mser = new cv.MSER(); mser.detect(image, kp); @@ -34,7 +34,7 @@ QUnit.test('Detectors', function(assert) { let brisk = new cv.BRISK(); brisk.detect(image, kp); - assert.equal(kp.size(), 191, 'BRISK'); + assert.equal(kp.size(), 187, 'BRISK'); let ffd = new cv.FastFeatureDetector(); ffd.detect(image, kp); @@ -54,7 +54,7 @@ QUnit.test('Detectors', function(assert) { let akaze = new cv.AKAZE(); akaze.detect(image, kp); - assert.equal(kp.size(), 53, 'AKAZE'); + assert.equal(kp.size(), 52, 'AKAZE'); }); QUnit.test('SimpleBlobDetector', function(assert) { @@ -75,14 +75,14 @@ QUnit.test('BFMatcher', function(assert) { let orb = new cv.ORB(); orb.detectAndCompute(image, new cv.Mat(), kp, descriptors); - assert.equal(kp.size(), 67); + assert.equal(kp.size(), 68); // Run a matcher. let dm = new cv.DMatchVector(); let matcher = new cv.BFMatcher(); matcher.match(descriptors, descriptors, dm); - assert.equal(dm.size(), 67); + assert.equal(dm.size(), 68); }); QUnit.test('Drawing', function(assert) { @@ -93,7 +93,7 @@ QUnit.test('Drawing', function(assert) { let descriptors = new cv.Mat(); let orb = new cv.ORB(); orb.detectAndCompute(image, new cv.Mat(), kp, descriptors); - assert.equal(kp.size(), 67); + assert.equal(kp.size(), 68); let dst = new cv.Mat(); cv.drawKeypoints(image, kp, dst); @@ -104,7 +104,7 @@ QUnit.test('Drawing', function(assert) { let dm = new cv.DMatchVector(); let matcher = new cv.BFMatcher(); matcher.match(descriptors, descriptors, dm); - assert.equal(dm.size(), 67); + assert.equal(dm.size(), 68); cv.drawMatches(image, kp, image, kp, dm, dst); assert.equal(dst.rows, image.rows); @@ -112,7 +112,7 @@ QUnit.test('Drawing', function(assert) { dm = new cv.DMatchVectorVector(); matcher.knnMatch(descriptors, descriptors, dm, 2); - assert.equal(dm.size(), 67); + assert.equal(dm.size(), 68); cv.drawMatchesKnn(image, kp, image, kp, dm, dst); assert.equal(dst.rows, image.rows); assert.equal(dst.cols, 2 * image.cols); From 881774d8725212395d99db5e2167cce821596cb5 Mon Sep 17 00:00:00 2001 From: sirudoi <107246340+sirudoi@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:36:11 +0800 Subject: [PATCH 03/32] Merge pull request #27930 from sirudoi:4.x Add macOS support for Orbbec Gemini330 camera #27930 ### Description of Changes Adds macOS support for Orbbec Gemini330 in videoio by integrating OrbbecSDK v2. Completes the non-macOS work in [#27230](https://github.com/opencv/opencv/pull/27230). #### Motivation [#27230](https://github.com/opencv/opencv/pull/27230) skipped macOS. On macOS, UVC alone lacks required device controls; OrbbecSDK v2 provides them. #### Key Change - macOS: fetch/link OrbbecSDK v2.5.5 (replaces v1.9.4). - videoio (macOS): switch OrbbecSDK API usage from v1 to v2. ### 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] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/orbbecsdk/orbbecsdk.cmake | 34 ++++++++++++++----- doc/tutorials/app/orbbec_uvc.markdown | 9 +++++ modules/videoio/cmake/detect_obsensor.cmake | 6 +++- .../videoio/src/cap_obsensor_liborbbec.cpp | 16 +++++++++ .../videoio/src/cap_obsensor_liborbbec.hpp | 3 ++ 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/3rdparty/orbbecsdk/orbbecsdk.cmake b/3rdparty/orbbecsdk/orbbecsdk.cmake index db51aee9c4..8833a30d5a 100644 --- a/3rdparty/orbbecsdk/orbbecsdk.cmake +++ b/3rdparty/orbbecsdk/orbbecsdk.cmake @@ -1,18 +1,34 @@ +ocv_update(ORBBEC_SDK_VERSION "2") +ocv_update(ORBBEC_SDK_DOWNLOAD_DIR "${OpenCV_BINARY_DIR}/3rdparty/orbbecsdk") + function(download_orbbec_sdk root_var) - set(ORBBECSDK_DOWNLOAD_DIR "${OpenCV_BINARY_DIR}/3rdparty/orbbecsdk") - set(ORBBECSDK_FILE_HASH_CMAKE "e7566fa915a1b0c02640df41891916fe") - ocv_download(FILENAME "v1.9.4.tar.gz" - HASH ${ORBBECSDK_FILE_HASH_CMAKE} - URL "https://github.com/orbbec/OrbbecSDK/archive/refs/tags/v1.9.4/" - DESTINATION_DIR ${ORBBECSDK_DOWNLOAD_DIR} + if(ORBBEC_SDK_VERSION STREQUAL "1") + set(ORBBEC_SDK_FILE_HASH_CMAKE "e7566fa915a1b0c02640df41891916fe") + set(ORBBEC_SDK_GIT_TAG "1.9.4") + add_definitions(-DORBBEC_SDK_VERSION_MAJOR=1) + elseif(ORBBEC_SDK_VERSION STREQUAL "2") + set(ORBBEC_SDK_FILE_HASH_CMAKE "d828ac15618a56b9ae325bada8676e28") + set(ORBBEC_SDK_GIT_TAG "2.5.5") + add_definitions(-DORBBEC_SDK_VERSION_MAJOR=2) + else() + message(STATUS "Unsupported OrbbecSDK version: ${ORBBEC_SDK_VERSION}, use default version 2") + set(ORBBEC_SDK_FILE_HASH_CMAKE "d828ac15618a56b9ae325bada8676e28") + set(ORBBEC_SDK_GIT_TAG "2.5.5") + add_definitions(-DORBBEC_SDK_VERSION_MAJOR=2) + endif() + + ocv_download(FILENAME "v${ORBBEC_SDK_GIT_TAG}.tar.gz" + HASH ${ORBBEC_SDK_FILE_HASH_CMAKE} + URL "https://github.com/orbbec/OrbbecSDK/archive/refs/tags/v${ORBBEC_SDK_GIT_TAG}/" + DESTINATION_DIR ${ORBBEC_SDK_DOWNLOAD_DIR} ID OrbbecSDK STATUS res UNPACK RELATIVE_URL ) if(${res}) - message(STATUS "orbbec sdk downloaded to: ${ORBBECSDK_DOWNLOAD_DIR}") - set(${root_var} "${ORBBECSDK_DOWNLOAD_DIR}/OrbbecSDK-1.9.4" PARENT_SCOPE) + message(STATUS "OrbbecSDK downloaded to: ${ORBBEC_SDK_DOWNLOAD_DIR}") + set(${root_var} "${ORBBEC_SDK_DOWNLOAD_DIR}/OrbbecSDK-${ORBBEC_SDK_GIT_TAG}" PARENT_SCOPE) else() - message(FATAL_ERROR "Failed to download orbbec sdk") + message(FATAL_ERROR "Failed to download OrbbecSDK") endif() endfunction() \ No newline at end of file diff --git a/doc/tutorials/app/orbbec_uvc.markdown b/doc/tutorials/app/orbbec_uvc.markdown index 214ef6afc7..a5d62c5ec3 100644 --- a/doc/tutorials/app/orbbec_uvc.markdown +++ b/doc/tutorials/app/orbbec_uvc.markdown @@ -34,6 +34,15 @@ make sudo make install ``` +By default, when `-DOBSENSOR_USE_ORBBEC_SDK=ON` is enabled, OrbbecSDK v2 is used (i.e., `ORBBEC_SDK_VERSION` defaults to `2`); it supports the entire Orbbec Gemini 330 series. + +If you need legacy cameras such as Orbbec Femto, Gemini2XL, or Astra+, switch to OrbbecSDK v1 with the flag `-DORBBEC_SDK_VERSION=1`: +```bash + cmake -DOBSENSOR_USE_ORBBEC_SDK=ON -DORBBEC_SDK_VERSION=1 .. + make -j + sudo make install +``` + Code ---- diff --git a/modules/videoio/cmake/detect_obsensor.cmake b/modules/videoio/cmake/detect_obsensor.cmake index c7e6164c0f..0fa41481d9 100644 --- a/modules/videoio/cmake/detect_obsensor.cmake +++ b/modules/videoio/cmake/detect_obsensor.cmake @@ -13,8 +13,12 @@ if(NOT HAVE_OBSENSOR) set(HAVE_OBSENSOR TRUE) set(HAVE_OBSENSOR_ORBBEC_SDK TRUE) ocv_add_external_target(obsensor "${OrbbecSDK_INCLUDE_DIRS}" "${OrbbecSDK_LIBRARY}" "HAVE_OBSENSOR;HAVE_OBSENSOR_ORBBEC_SDK") - file(COPY ${OrbbecSDK_DLL_FILES} DESTINATION ${CMAKE_BINARY_DIR}/bin) file(COPY ${OrbbecSDK_DLL_FILES} DESTINATION ${CMAKE_BINARY_DIR}/lib) + if(NOT ORBBEC_SDK_VERSION STREQUAL "1") + # OrbbecSDK v2 loads some libraries at runtime. + file(COPY ${OrbbecSDK_RUNTIME_RESOURCE_FILES} DESTINATION ${CMAKE_BINARY_DIR}/lib) + install(DIRECTORY ${OrbbecSDK_RUNTIME_RESOURCE_FILES} DESTINATION ${OPENCV_LIB_INSTALL_PATH}) + endif() install(FILES ${OrbbecSDK_DLL_FILES} DESTINATION ${OPENCV_LIB_INSTALL_PATH}) ocv_install_3rdparty_licenses(OrbbecSDK ${OrbbecSDK_DIR}/LICENSE.txt) endif() diff --git a/modules/videoio/src/cap_obsensor_liborbbec.cpp b/modules/videoio/src/cap_obsensor_liborbbec.cpp index 3337d6f3ba..d8b44cf196 100644 --- a/modules/videoio/src/cap_obsensor_liborbbec.cpp +++ b/modules/videoio/src/cap_obsensor_liborbbec.cpp @@ -38,6 +38,9 @@ VideoCapture_obsensor::VideoCapture_obsensor(int, const cv::VideoCaptureParamete ob::Context::setLoggerToFile(OB_LOG_SEVERITY_OFF, ""); config = std::make_shared(); pipe = std::make_shared(); +#if ORBBEC_SDK_VERSION_MAJOR != 1 + alignFilter = std::make_shared(OB_STREAM_COLOR); +#endif int color_width = params.get(CAP_PROP_FRAME_WIDTH, OB_WIDTH_ANY); int color_height = params.get(CAP_PROP_FRAME_HEIGHT, OB_HEIGHT_ANY); @@ -75,12 +78,25 @@ VideoCapture_obsensor::VideoCapture_obsensor(int, const cv::VideoCaptureParamete config->enableStream(depthProfile->as()); } +#if ORBBEC_SDK_VERSION_MAJOR == 1 config->setAlignMode(ALIGN_D2C_SW_MODE); +#else + config->setFrameAggregateOutputMode(OB_FRAME_AGGREGATE_OUTPUT_ALL_TYPE_FRAME_REQUIRE); + pipe->enableFrameSync(); +#endif pipe->start(config, [&](std::shared_ptr frameset) { std::unique_lock lk(videoFrameMutex); +#if ORBBEC_SDK_VERSION_MAJOR == 1 colorFrame = frameset->colorFrame(); depthFrame = frameset->depthFrame(); +#else + auto alignFrameSet = alignFilter->process(frameset); + if (alignFrameSet) { + colorFrame = alignFrameSet->as()->colorFrame(); + depthFrame = alignFrameSet->as()->depthFrame(); + } +#endif }); auto param = pipe->getCameraParam(); diff --git a/modules/videoio/src/cap_obsensor_liborbbec.hpp b/modules/videoio/src/cap_obsensor_liborbbec.hpp index aa8c1d4728..d280d846e5 100644 --- a/modules/videoio/src/cap_obsensor_liborbbec.hpp +++ b/modules/videoio/src/cap_obsensor_liborbbec.hpp @@ -59,6 +59,9 @@ protected: std::shared_ptr grabbedDepthFrame; std::shared_ptr pipe; std::shared_ptr config; +#if ORBBEC_SDK_VERSION_MAJOR != 1 + std::shared_ptr alignFilter; +#endif CameraParam camParam; }; From 6862afb2c1578e25ff952de245ab4618428792b1 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Sat, 8 Nov 2025 11:24:24 +0300 Subject: [PATCH 04/32] Merge pull request #27977 from asmorkalov:as/proper_ffmpeg_diagnostic_windows_arm Fixed pre-built ffmpeg diagnostics on Windows for ARM. #27977 ### 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 --- CMakeLists.txt | 2 +- modules/videoio/cmake/detect_ffmpeg.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0338b14feb..54223fb1db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1627,7 +1627,7 @@ endif() if(WITH_FFMPEG OR HAVE_FFMPEG) if(OPENCV_FFMPEG_USE_FIND_PACKAGE) status(" FFMPEG:" HAVE_FFMPEG THEN "YES (find_package)" ELSE "NO (find_package)") - elseif(WIN32) + elseif(WIN32 AND NOT ARM AND NOT AARCH64) status(" FFMPEG:" HAVE_FFMPEG THEN "YES (prebuilt binaries)" ELSE NO) else() status(" FFMPEG:" HAVE_FFMPEG THEN YES ELSE NO) diff --git a/modules/videoio/cmake/detect_ffmpeg.cmake b/modules/videoio/cmake/detect_ffmpeg.cmake index dd5eefaac9..7ad5ecb950 100644 --- a/modules/videoio/cmake/detect_ffmpeg.cmake +++ b/modules/videoio/cmake/detect_ffmpeg.cmake @@ -12,7 +12,7 @@ if(NOT HAVE_FFMPEG AND OPENCV_FFMPEG_USE_FIND_PACKAGE) endif() endif() -if(NOT HAVE_FFMPEG AND WIN32 AND NOT ARM AND NOT OPENCV_FFMPEG_SKIP_DOWNLOAD) +if(NOT HAVE_FFMPEG AND WIN32 AND NOT ARM AND NOT AARCH64 AND NOT OPENCV_FFMPEG_SKIP_DOWNLOAD) include("${OpenCV_SOURCE_DIR}/3rdparty/ffmpeg/ffmpeg.cmake") download_win_ffmpeg(FFMPEG_CMAKE_SCRIPT) if(FFMPEG_CMAKE_SCRIPT) From 9921531fa476556e80e0c3d847cb83f0245353d1 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:07:36 +0300 Subject: [PATCH 05/32] Merge pull request #27976 from asmorkalov:as/size_t_fix_win32 Fixed ptrdiff_t cast on windows 32-bit #27976 ### 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 - [ ] 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/imgcodecs/src/bitstrm.cpp | 2 +- modules/imgcodecs/src/utils.cpp | 7 ------- modules/imgcodecs/src/utils.hpp | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/imgcodecs/src/bitstrm.cpp b/modules/imgcodecs/src/bitstrm.cpp index 12d9c1bcae..a7e6380dfb 100644 --- a/modules/imgcodecs/src/bitstrm.cpp +++ b/modules/imgcodecs/src/bitstrm.cpp @@ -153,7 +153,7 @@ void RBaseStream::setPos( int64_t pos ) int64_t RBaseStream::getPos() { CV_Assert(isOpened()); - int64_t pos = validateToInt64((m_current - m_start) + m_block_pos); + int64_t pos = static_cast((m_current - m_start) + m_block_pos); CV_Assert(pos >= m_block_pos); // overflow check CV_Assert(pos >= 0); // overflow check return pos; diff --git a/modules/imgcodecs/src/utils.cpp b/modules/imgcodecs/src/utils.cpp index cfbd62b7bc..41fd9f5041 100644 --- a/modules/imgcodecs/src/utils.cpp +++ b/modules/imgcodecs/src/utils.cpp @@ -51,13 +51,6 @@ int validateToInt(size_t sz) return valueInt; } -int64_t validateToInt64(ptrdiff_t sz) -{ - int64_t valueInt = static_cast(sz); - CV_Assert((ptrdiff_t)valueInt == sz); - return valueInt; -} - #define SCALE 14 #define cR (int)(0.299*(1 << SCALE) + 0.5) #define cG (int)(0.587*(1 << SCALE) + 0.5) diff --git a/modules/imgcodecs/src/utils.hpp b/modules/imgcodecs/src/utils.hpp index f2649f0ecd..95deebde31 100644 --- a/modules/imgcodecs/src/utils.hpp +++ b/modules/imgcodecs/src/utils.hpp @@ -45,7 +45,6 @@ namespace cv { int validateToInt(size_t step); -int64_t validateToInt64(ptrdiff_t step); template static inline size_t safeCastToSizeT(const _Tp v_origin, const char* msg) From 5fb4ce482c861f3613a82acde3509b5bf8d45e86 Mon Sep 17 00:00:00 2001 From: Ismail Abou Zeid <100245285+ismailabouzeidx@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:27:54 +0200 Subject: [PATCH 06/32] Merge pull request #27950 from ismailabouzeidx:feat/estimate-translation2d MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [calib3d] Add estimateTranslation2D() #27950 Merge with opencv_extra PR: opencv/opencv_extra#1286 ### **Description** This PR adds a new API, `cv::estimateTranslation2D()`, to the **calib3d** module. It computes a **pure 2D translation** between two sets of corresponding points using robust methods (`RANSAC` and `LMedS`). The function mirrors the interface and behavior of `estimateAffine2D()` and `estimateAffinePartial2D()`, but constrains the transformation to translation only. This model is particularly useful for cases where the motion between images is purely translational, such as: - Aerial stitching and planar mosaics. - Image alignment in fixed-camera systems. - Lightweight pipelines where affine or homography models are unnecessarily complex. The implementation introduces a new internal class `Translation2DEstimatorCallback` and integrates seamlessly into OpenCV’s existing robust estimation framework (`PointSetRegistrator`). --- ### **Key Features** - Implements `cv::estimateTranslation2D()` in the `calib3d` module. - Supports robust methods **RANSAC** and **LMedS**. - Adds accuracy and performance tests. - Provides full **C++ and Python bindings**. - Includes **Doxygen documentation** consistent with OpenCV’s standards. - Verified correctness across noise, outlier, and datatype variations. --- ### **Testing & Verification** **Unit Tests** (`modules/calib3d/`) - **Minimal sample:** `test1Point` validates that a single correspondence recovers the correct translation under both **RANSAC** and **LMedS** across 500 randomized trials. - **Robustness to noise and outliers:** `testNPoints` generates 100 correspondences, injects noise and outliers (≤40% for RANSAC, ≤50% for LMedS), and verifies that: - Estimated **T** closely matches ground truth (`cvtest::norm(..., NORM_L2)`). - Inlier mask consistency and correctness are maintained. - **Datatype conversion:** `testConversion` checks mixed input datatypes (integer → float) to ensure correct conversion and consistent results. - **Input immutability:** `dont_change_inputs` confirms that input arrays remain unchanged after function execution, mirroring affine behavior. **Performance Tests** (`modules/calib3d/`) - `EstimateTranslation2DPerf` benchmarks **RANSAC** and **LMedS** using: - Point counts: 1000 - Confidence levels: 0.95 - Refinement iterations: 10, 0 These tests confirm **numerical stability**, **performance scaling**, and **consistency** across datatypes and noise levels. --- ### 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 (`4.x`) - [x] There is a clear description, motivation, and validation summary in this PR - [x] There are accuracy and performance tests in the calib3d module - [x] The feature is well documented and sample code can be built with CMake - [x] The feature has Python bindings and verified documentation output - [x] There is test data or sample code in the opencv_extra repository (if applicable) - [ ] There is a reference to the original bug report or related issue (if applicable) --- modules/calib3d/include/opencv2/calib3d.hpp | 72 ++++++ modules/calib3d/perf/perf_translation2d.cpp | 124 ++++++++++ modules/calib3d/src/ptsetreg.cpp | 233 ++++++++++++++++++ .../test/test_translation_2d_estimator.cpp | 211 ++++++++++++++++ 4 files changed, 640 insertions(+) create mode 100644 modules/calib3d/perf/perf_translation2d.cpp create mode 100644 modules/calib3d/test/test_translation_2d_estimator.cpp diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 60b2c07c12..052a7712be 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -3362,6 +3362,78 @@ CV_EXPORTS_W cv::Mat estimateAffinePartial2D(InputArray from, InputArray to, Out size_t maxIters = 2000, double confidence = 0.99, size_t refineIters = 10); +/** @brief Computes a pure 2D translation between two 2D point sets. + +It computes +\f[ +\begin{bmatrix} +x\\ +y +\end{bmatrix} += +\begin{bmatrix} +1 & 0\\ +0 & 1 +\end{bmatrix} +\begin{bmatrix} +X\\ +Y +\end{bmatrix} ++ +\begin{bmatrix} +t_x\\ +t_y +\end{bmatrix}. +\f] + +@param from First input 2D point set containing \f$(X,Y)\f$. +@param to Second input 2D point set containing \f$(x,y)\f$. +@param inliers Output vector indicating which points are inliers (1-inlier, 0-outlier). +@param method Robust method used to compute the transformation. The following methods are possible: +- @ref RANSAC - RANSAC-based robust method +- @ref LMEDS - Least-Median robust method +RANSAC is the default method. +@param ransacReprojThreshold Maximum reprojection error in the RANSAC algorithm to consider +a point as an inlier. Applies only to RANSAC. +@param maxIters The maximum number of robust method iterations. +@param confidence Confidence level, between 0 and 1, for the estimated transformation. Anything +between 0.95 and 0.99 is usually good enough. Values too close to 1 can slow down the estimation +significantly. Values lower than 0.8–0.9 can result in an incorrectly estimated transformation. +@param refineIters Maximum number of iterations of the refining algorithm. For pure translation +the least-squares solution on inliers is closed-form, so passing 0 is recommended (no additional refine). + +@return A 2D translation vector \f$[t_x, t_y]^T\f$ as `cv::Vec2d`. If the translation could not be +estimated, both components are set to NaN and, if @p inliers is provided, the mask is filled with zeros. + +\par Converting to a 2x3 transformation matrix: +\f[ +\begin{bmatrix} +1 & 0 & t_x\\ +0 & 1 & t_y +\end{bmatrix} +\f] + +@code{.cpp} +cv::Vec2d t = cv::estimateTranslation2D(from, to, inliers); +cv::Mat T = (cv::Mat_(2,3) << 1,0,t[0], 0,1,t[1]); +@endcode + +The function estimates a pure 2D translation between two 2D point sets using the selected robust +algorithm. Inliers are determined by the reprojection error threshold. + +@note +The RANSAC method can handle practically any ratio of outliers but needs a threshold to +distinguish inliers from outliers. The method LMeDS does not need any threshold but works +correctly only when there are more than 50% inliers. + +@sa estimateAffine2D, estimateAffinePartial2D, getAffineTransform +*/ +CV_EXPORTS_W cv::Vec2d estimateTranslation2D(InputArray from, InputArray to, OutputArray inliers = noArray(), + int method = RANSAC, + double ransacReprojThreshold = 3, + size_t maxIters = 2000, double confidence = 0.99, + size_t refineIters = 0); + /** @example samples/cpp/tutorial_code/features2D/Homography/decompose_homography.cpp An example program with homography decomposition. diff --git a/modules/calib3d/perf/perf_translation2d.cpp b/modules/calib3d/perf/perf_translation2d.cpp new file mode 100644 index 0000000000..5ce0133386 --- /dev/null +++ b/modules/calib3d/perf/perf_translation2d.cpp @@ -0,0 +1,124 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// (3-clause BSD License) +// +// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the names of the copyright holders nor the names of the contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall copyright holders or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "perf_precomp.hpp" +#include +#include + +namespace opencv_test +{ +using namespace perf; + +CV_ENUM(Method, RANSAC, LMEDS) +typedef tuple TranslationParams; +typedef TestBaseWithParam EstimateTranslation2DPerf; +#define ESTIMATE_PARAMS Combine(Values(1000), Values(0.95), Method::all(), Values(10, 0)) + +static float rngIn(float from, float to) { return from + (to - from) * (float)theRNG(); } + +static cv::Mat rngTranslationMat() +{ + double tx = rngIn(-2.f, 2.f); + double ty = rngIn(-2.f, 2.f); + double t[2*3] = { 1.0, 0.0, tx, + 0.0, 1.0, ty }; + return cv::Mat(2, 3, CV_64F, t).clone(); +} + +PERF_TEST_P(EstimateTranslation2DPerf, EstimateTranslation2D, ESTIMATE_PARAMS) +{ + TranslationParams params = GetParam(); + const int n = get<0>(params); + const double confidence = get<1>(params); + const int method = get<2>(params); + const size_t refining = get<3>(params); + + //fixed seed so the generated data are deterministic + cv::theRNG().state = 0x12345678; + // ground-truth pure translation + cv::Mat T = rngTranslationMat(); + + // LMEDS can't handle more than 50% outliers (by design) + int m; + if (method == LMEDS) + m = 3*n/5; + else + m = 2*n/5; + + const float shift_outl = 15.f; + const float noise_level = 20.f; + + cv::Mat fpts(1, n, CV_32FC2); + cv::Mat tpts(1, n, CV_32FC2); + + randu(fpts, 0.f, 100.f); + transform(fpts, tpts, T); + + // add outliers to the tail [m, n) + cv::Mat outliers = tpts.colRange(m, n); + outliers.reshape(1) += shift_outl; + + cv::Mat noise(outliers.size(), outliers.type()); + randu(noise, 0.f, noise_level); + outliers += noise; + + cv::Vec2d T_est; + std::vector inliers(n); + + warmup(inliers, WARMUP_WRITE); + warmup(fpts, WARMUP_READ); + warmup(tpts, WARMUP_READ); + + TEST_CYCLE() + { + T_est = estimateTranslation2D(fpts, tpts, inliers, method, + /*ransacReprojThreshold=*/3.0, + /*maxIters=*/2000, + /*confidence=*/confidence, + /*refineIters=*/refining); + } + + // Convert to Mat for SANITY_CHECK consistency + cv::Mat T_est_mat = (cv::Mat_(2,1) << T_est[0], T_est[1]); + SANITY_CHECK(T_est_mat, 1e-6); +} + +} // namespace opencv_test diff --git a/modules/calib3d/src/ptsetreg.cpp b/modules/calib3d/src/ptsetreg.cpp index c0132a6832..1b2f44777a 100644 --- a/modules/calib3d/src/ptsetreg.cpp +++ b/modules/calib3d/src/ptsetreg.cpp @@ -762,6 +762,74 @@ public: } }; +/* + * Compute + * x 1 0 X t_x + * = * + + * y 0 1 Y t_y + * + * - every element in _m1 contains (X, Y), which are called source points + * - every element in _m2 contains (x, y), which are called destination points + * - _model is of size 2x3, which contains + * 1 0 t_x + * 0 1 t_y + * + * Minimal sample size: 1 + * - For a single correspondence, the optimal translation equals the pointwise + * displacement (to - from). The robust framework (RANSAC/LMEDS) selects + * inlier sets using the reprojection error and refits the model. + */ +class Translation2DEstimatorCallback : public cv::PointSetRegistrator::Callback +{ +public: + // Fit translation using the minimal subset (1 sample): displacement of the first pair. + // The robust framework ensures consensus on larger sets; a closed-form LS refit + // (mean displacement) can be applied after inlier selection. + int runKernel(cv::InputArray _m1, cv::InputArray _m2, cv::OutputArray _model) const CV_OVERRIDE + { + cv::Mat m1 = _m1.getMat(), m2 = _m2.getMat(); + const auto* from = m1.ptr(); + const auto* to = m2.ptr(); + const int n = m1.checkVector(2); + if (n < 1) return 0; + + // Minimal model from a single correspondence + const double tx = (double)to[0].x - (double)from[0].x; + const double ty = (double)to[0].y - (double)from[0].y; + + _model.create(2, 3, CV_64F); + double* H = _model.getMat().ptr(); + H[0]=1.0; H[1]=0.0; H[2]=tx; + H[3]=0.0; H[4]=1.0; H[5]=ty; + return 1; + } + + // Return squared L2 reprojection error (pixels^2), as used by affine estimators. + void computeError(cv::InputArray _m1, cv::InputArray _m2, + cv::InputArray _model, cv::OutputArray _err) const CV_OVERRIDE + { + cv::Mat m1 = _m1.getMat(), m2 = _m2.getMat(); + const auto* from = m1.ptr(); + const auto* to = m2.ptr(); + const int n = m1.checkVector(2); + + const double* H = _model.getMat().ptr(); // [1 0 tx; 0 1 ty] + // Cast once to float for inner loop speed + const float tx = (float)H[2]; + const float ty = (float)H[5]; + + _err.create(n, 1, CV_32F); + float* e = _err.getMat().ptr(); + + for (int i = 0; i < n; ++i) { + // residual = (from + t) - to + const float rx = (float)from[i].x + tx - (float)to[i].x; + const float ry = (float)from[i].y + ty - (float)to[i].y; + e[i] = rx*rx + ry*ry; // squared L2 (pixels^2) + } + } +}; + class Affine2DRefineCallback : public LMSolver::Callback { public: @@ -876,6 +944,59 @@ public: Mat src, dst; }; +class Translation2DRefineCallback : public cv::LMSolver::Callback { +public: + Translation2DRefineCallback(cv::InputArray _src, cv::InputArray _dst) { + src = _src.getMat(); + dst = _dst.getMat(); + } + + // param: 6x1 [a b c d e f], only c (tx) and f (ty) are used + bool compute(cv::InputArray _param, cv::OutputArray _err, cv::OutputArray _Jac) const CV_OVERRIDE + { + const int count = src.checkVector(2); + CV_Assert(count > 0); + + cv::Mat param = _param.getMat(); // CV_64F, 6x1 + const double* h = param.ptr(); + const float tx = (float)h[2]; + const float ty = (float)h[5]; + + _err.create(count * 2, 1, CV_64F); + cv::Mat err = _err.getMat(); + double* errptr = err.ptr(); + + cv::Mat J; + double* Jptr = nullptr; + if (_Jac.needed()) { + _Jac.create(count * 2, 6, CV_64F); + J = _Jac.getMat(); + Jptr = J.ptr(); + } + + const cv::Point2f* M = src.ptr(); + const cv::Point2f* m = dst.ptr(); + + for (int i = 0; i < count; ++i) + { + // residuals + errptr[2*i + 0] = (double)M[i].x + tx - (double)m[i].x; + errptr[2*i + 1] = (double)M[i].y + ty - (double)m[i].y; + + // Jacobian (only translation terms vary) + if (Jptr) { + Jptr[0] = 0.; Jptr[1] = 0.; Jptr[2] = 1.; Jptr[3] = 0.; Jptr[4] = 0.; Jptr[5] = 0.; + Jptr[6] = 0.; Jptr[7] = 0.; Jptr[8] = 0.; Jptr[9] = 0.; Jptr[10]= 0.; Jptr[11]= 1.; + Jptr += 12; // 2 rows × 6 cols + } + } + return true; + } + +private: + cv::Mat src, dst; // N×1 CV_32FC2 +}; + int estimateAffine3D(InputArray _from, InputArray _to, OutputArray _out, OutputArray _inliers, double ransacThreshold, double confidence) @@ -1182,4 +1303,116 @@ Mat estimateAffinePartial2D(InputArray _from, InputArray _to, OutputArray _inlie return H; } +cv::Vec2d estimateTranslation2D(cv::InputArray _from, cv::InputArray _to, + cv::OutputArray _inliers, + int method, + double ransacReprojThreshold, + size_t maxIters, double confidence, + size_t refineIters) +{ + CV_INSTRUMENT_REGION(); + + using std::numeric_limits; + const double NaN = numeric_limits::quiet_NaN(); + cv::Vec2d tvec(NaN, NaN); + + // Normalize input layout and type: + // - Accepts various shapes (Nx2, 1xN, etc.); force to CV_32FC2 for the registrator. + // - Keep local copies to allow reshaping into N x 1 vectors of Point2f. + cv::Mat from = _from.getMat(), to = _to.getMat(); + int count = from.checkVector(2); + bool result = false; + CV_Assert(count >= 0 && to.checkVector(2) == count); + + if (from.type() != CV_32FC2 || to.type() != CV_32FC2) { + cv::Mat tmp1, tmp2; + from.convertTo(tmp1, CV_32FC2); from = tmp1; + to.convertTo(tmp2, CV_32FC2); to = tmp2; + } else { + from = from.clone(); + to = to.clone(); + } + + // Convert to N x 1 vectors of Point2f (matches registrator expectations). + from = from.reshape(2, count); + to = to.reshape(2, count); + + // Optional inlier mask (1-inlier, 0-outlier). Only allocate if requested. + cv::Mat inliers; + if (_inliers.needed()) { + _inliers.create(count, 1, CV_8U, -1, true); + inliers = _inliers.getMat(); + } + + // Build translation model callback. Minimal sample size is 1. + cv::Mat T; // 2x3 output (CV_64F) + cv::Ptr cb = cv::makePtr(); + + // Create robust estimators with the same semantics as affine functions. + if (method == RANSAC) + result = createRANSACPointSetRegistrator(cb, 1, + ransacReprojThreshold, confidence, (int)maxIters)->run(from, to, T, inliers); + else if (method == LMEDS) + result = createLMeDSPointSetRegistrator(cb, 1, + confidence, (int)maxIters)->run(from, to, T, inliers); + else + CV_Error(Error::StsBadArg, "Unknown or unsupported robust estimation method"); + + // Estimation failure: return NaNs and zero inlier mask (if requested). + if (!result) { + if (_inliers.needed()) + inliers.setTo(cv::Scalar(0)); + return tvec; + } + + // Post-process: compress inliers to the front (same pattern as affine), + // then optionally refine or compute closed-form LS (mean displacement) on inliers. + if (count > 0) { + // Reorder: pack inliers to the front + compressElems(from.ptr(), inliers.ptr(), 1, count); + int nin = compressElems(to.ptr(), inliers.ptr(), 1, count); + + if (nin > 0) { + cv::Mat src = from.rowRange(0, nin); + cv::Mat dst = to.rowRange(0, nin); + + if (refineIters > 0) { + if (T.empty()) + T = (cv::Mat_(2,3) << 1,0,0, 0,1,0); + // LM refine on translation only: + // T is 2x3; represent as 6x1 vector [a b c d e f]^T. + // We only update the translation entries (c, f). + cv::Mat Hvec = T.reshape(1, 6); + cv::Ptr solver = cv::LMSolver::create( + cv::makePtr(src, dst), + (int)refineIters + ); + solver->run(Hvec); + } else { + // Closed-form LS on inliers: mean displacement (optimal in L2 sense). + const auto* f = src.ptr(); + const auto* t = dst.ptr(); + double sx = 0.0, sy = 0.0; + for (int i = 0; i < nin; ++i) { + sx += (double)t[i].x - (double)f[i].x; + sy += (double)t[i].y - (double)f[i].y; + } + if (T.empty()) + T = (cv::Mat_(2,3) << 1,0,0, 0,1,0); + double* H = T.ptr(); + H[2] = sx / nin; // t_x + H[5] = sy / nin; // t_y + } + } + + // Extract translation components + if (!T.empty()) { + tvec[0] = T.at(0, 2); + tvec[1] = T.at(1, 2); + } + } + + return tvec; +} + } // namespace cv diff --git a/modules/calib3d/test/test_translation_2d_estimator.cpp b/modules/calib3d/test/test_translation_2d_estimator.cpp new file mode 100644 index 0000000000..7afd20aafe --- /dev/null +++ b/modules/calib3d/test/test_translation_2d_estimator.cpp @@ -0,0 +1,211 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// (3-clause BSD License) +// +// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the names of the copyright holders nor the names of the contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall copyright holders or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" + +namespace opencv_test { namespace { + +CV_ENUM(Method, RANSAC, LMEDS) +typedef TestWithParam EstimateTranslation2D; + +static float rngIn(float from, float to) { return from + (to - from) * (float)theRNG(); } + +// build a pure translation 2x3 matrix +static cv::Mat rngTranslationMat() +{ + double tx = rngIn(-20.f, 20.f); + double ty = rngIn(-20.f, 20.f); + double t[2*3] = { 1.0, 0.0, tx, + 0.0, 1.0, ty }; + return cv::Mat(2, 3, CV_64F, t).clone(); +} + +static inline cv::Vec2d getTxTy(const cv::Mat& T) +{ + CV_Assert(T.rows == 2 && T.cols == 3 && T.type() == CV_64F); + return cv::Vec2d(T.at(0,2), T.at(1,2)); +} + +TEST_P(EstimateTranslation2D, test1Point) +{ + // minimal sample is 1 point + for (size_t i = 0; i < 500; ++i) + { + cv::Mat T = rngTranslationMat(); + cv::Vec2d T_ref = getTxTy(T); + + cv::Mat fpts(1, 1, CV_32FC2); + cv::Mat tpts(1, 1, CV_32FC2); + + fpts.at(0) = cv::Point2f(rngIn(1,2), rngIn(5,6)); + transform(fpts, tpts, T); + + std::vector inliers; + cv::Vec2d T_est = estimateTranslation2D(fpts, tpts, inliers, GetParam() /* method */); + + EXPECT_NEAR(T_est[0], T_ref[0], 1e-6); + EXPECT_NEAR(T_est[1], T_ref[1], 1e-6); + EXPECT_EQ((int)inliers.size(), 1); + EXPECT_EQ((int)inliers[0], 1); + } +} + +TEST_P(EstimateTranslation2D, testNPoints) +{ + for (size_t i = 0; i < 500; ++i) + { + cv::Mat T = rngTranslationMat(); + cv::Vec2d T_ref = getTxTy(T); + + const int method = GetParam(); + const int n = 100; + int m; + // LMEDS can't handle more than 50% outliers (by design) + if (method == LMEDS) + m = 3*n/5; + else + m = 2*n/5; + + const float shift_outl = 15.f; + const float noise_level = 20.f; + + cv::Mat fpts(1, n, CV_32FC2); + cv::Mat tpts(1, n, CV_32FC2); + + randu(fpts, 0.f, 100.f); + transform(fpts, tpts, T); + + /* adding noise to some points (make last n-m points outliers) */ + cv::Mat outliers = tpts.colRange(m, n); + outliers.reshape(1) += shift_outl; + + cv::Mat noise(outliers.size(), outliers.type()); + randu(noise, 0.f, noise_level); + outliers += noise; + + std::vector inliers; + cv::Vec2d T_est = estimateTranslation2D(fpts, tpts, inliers, method); + + // Check estimation produced finite values + ASSERT_TRUE(std::isfinite(T_est[0]) && std::isfinite(T_est[1])); + + EXPECT_NEAR(T_est[0], T_ref[0], 1e-4); + EXPECT_NEAR(T_est[1], T_ref[1], 1e-4); + + bool inliers_good = std::count(inliers.begin(), inliers.end(), 1) == m && + m == std::accumulate(inliers.begin(), inliers.begin() + m, 0); + EXPECT_TRUE(inliers_good); + } +} + +// test conversion from other datatypes than float +TEST_P(EstimateTranslation2D, testConversion) +{ + cv::Mat T = rngTranslationMat(); + T.convertTo(T, CV_32S); // convert to int to transform ints properly + + std::vector fpts(3); + std::vector tpts(3); + + fpts[0] = cv::Point2f(rngIn(1,2), rngIn(5,6)); + fpts[1] = cv::Point2f(rngIn(3,4), rngIn(3,4)); + fpts[2] = cv::Point2f(rngIn(1,2), rngIn(3,4)); + + transform(fpts, tpts, T); + + std::vector inliers; + cv::Vec2d T_est = estimateTranslation2D(fpts, tpts, inliers, GetParam() /* method */); + + ASSERT_TRUE(std::isfinite(T_est[0]) && std::isfinite(T_est[1])); + + T.convertTo(T, CV_64F); // convert back for reference extraction + cv::Vec2d T_ref = getTxTy(T); + + EXPECT_NEAR(T_est[0], T_ref[0], 1e-3); + EXPECT_NEAR(T_est[1], T_ref[1], 1e-3); + + // all must be inliers + EXPECT_EQ(countNonZero(inliers), 3); +} + +INSTANTIATE_TEST_CASE_P(Calib3d, EstimateTranslation2D, Method::all()); + +// "don't change inputs" regression, mirroring affine partial test +TEST(EstimateTranslation2D, dont_change_inputs) +{ + /*const static*/ float pts0_[10] = { + 0.0f, 0.0f, + 0.0f, 8.0f, + 4.0f, 0.0f, // outlier + 8.0f, 8.0f, + 8.0f, 0.0f + }; + /*const static*/ float pts1_[10] = { + 0.1f, 0.1f, + 0.1f, 8.1f, + 0.0f, 4.0f, // outlier + 8.1f, 8.1f, + 8.1f, 0.1f + }; + + cv::Mat pts0(cv::Size(1, 5), CV_32FC2, (void*)pts0_); + cv::Mat pts1(cv::Size(1, 5), CV_32FC2, (void*)pts1_); + + cv::Mat pts0_copy = pts0.clone(); + cv::Mat pts1_copy = pts1.clone(); + + cv::Mat inliers; + + cv::Vec2d T = cv::estimateTranslation2D(pts0, pts1, inliers); + + for (int i = 0; i < pts0.rows; ++i) + EXPECT_EQ(pts0_copy.at(i), pts0.at(i)) << "pts0: i=" << i; + + for (int i = 0; i < pts1.rows; ++i) + EXPECT_EQ(pts1_copy.at(i), pts1.at(i)) << "pts1: i=" << i; + + EXPECT_EQ(0, (int)inliers.at(2)); + + // sanity: estimated translation should be finite + EXPECT_TRUE(std::isfinite(T[0]) && std::isfinite(T[1])); +} + +}} // namespace From 7224bced8bff9d16d5e869d44f90f95ad8fdfe25 Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Sat, 8 Nov 2025 09:16:11 -0600 Subject: [PATCH 07/32] Fix #27961: Support reproducible builds by making host system version optional Add BUILD_INFO_SKIP_SYSTEM_VERSION option to exclude the host kernel version from build outputs, enabling reproducible builds across systems with different kernel versions. Changes: - Modified CMakeLists.txt to conditionally include CMAKE_HOST_SYSTEM_VERSION in the build platform status based on BUILD_INFO_SKIP_SYSTEM_VERSION flag - Updated 3rdparty/tbb/CMakeLists.txt to set TBB_HOST_VERSION conditionally - Modified 3rdparty/tbb/version_string.ver.cmakein to use the new variable When BUILD_INFO_SKIP_SYSTEM_VERSION=ON is set, the host system version is excluded from both the CMake status output and the TBB version strings, producing identical binaries on equivalent build systems regardless of kernel version differences. Default behavior is unchanged (version included) for backward compatibility. Fixes #27961 --- 3rdparty/tbb/CMakeLists.txt | 5 +++++ 3rdparty/tbb/version_string.ver.cmakein | 2 +- CMakeLists.txt | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/3rdparty/tbb/CMakeLists.txt b/3rdparty/tbb/CMakeLists.txt index 10f60094ae..2d25a3a08e 100644 --- a/3rdparty/tbb/CMakeLists.txt +++ b/3rdparty/tbb/CMakeLists.txt @@ -106,6 +106,11 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS set(TBB_SOURCE_FILES ${lib_srcs} ${lib_hdrs}) set(tbb_version_file "version_string.ver") +if(NOT BUILD_INFO_SKIP_SYSTEM_VERSION) + set(TBB_HOST_VERSION " ${CMAKE_HOST_SYSTEM_VERSION}") +else() + set(TBB_HOST_VERSION "") +endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${tbb_version_file}.cmakein" "${CMAKE_CURRENT_BINARY_DIR}/${tbb_version_file}" @ONLY) list(APPEND TBB_SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/${tbb_version_file}") diff --git a/3rdparty/tbb/version_string.ver.cmakein b/3rdparty/tbb/version_string.ver.cmakein index 1f8f0b818d..2953c1bc0c 100644 --- a/3rdparty/tbb/version_string.ver.cmakein +++ b/3rdparty/tbb/version_string.ver.cmakein @@ -1,6 +1,6 @@ #define __TBB_VERSION_STRINGS(N) \ #N": BUILD_PACKAGE OpenCV @OPENCV_VERSION@" ENDL \ -#N": BUILD_HOST @CMAKE_HOST_SYSTEM_NAME@ @CMAKE_HOST_SYSTEM_VERSION@ @CMAKE_HOST_SYSTEM_PROCESSOR@" ENDL \ +#N": BUILD_HOST @CMAKE_HOST_SYSTEM_NAME@@TBB_HOST_VERSION@ @CMAKE_HOST_SYSTEM_PROCESSOR@" ENDL \ #N": BUILD_TARGET @CMAKE_SYSTEM_NAME@ @CMAKE_SYSTEM_VERSION@ @CMAKE_SYSTEM_PROCESSOR@" ENDL \ #N": BUILD_COMPILER @CMAKE_CXX_COMPILER@ (ver @CMAKE_CXX_COMPILER_VERSION@)" ENDL \ #N": BUILD_COMMAND use cv::getBuildInformation() for details" ENDL diff --git a/CMakeLists.txt b/CMakeLists.txt index 54223fb1db..cc93888c93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1240,7 +1240,11 @@ endif() if(OPENCV_TIMESTAMP) status(" Timestamp:" ${OPENCV_TIMESTAMP}) endif() -status(" Host:" ${CMAKE_HOST_SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_VERSION} ${CMAKE_HOST_SYSTEM_PROCESSOR}) +if(NOT BUILD_INFO_SKIP_SYSTEM_VERSION) + status(" Host:" ${CMAKE_HOST_SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_VERSION} ${CMAKE_HOST_SYSTEM_PROCESSOR}) +else() + status(" Host:" ${CMAKE_HOST_SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_PROCESSOR}) +endif() if(CMAKE_CROSSCOMPILING) status(" Target:" ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION} ${CMAKE_SYSTEM_PROCESSOR}) endif() From 603bc58d3bd7bcb7386adc40c094b911a9199d1d Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Sat, 8 Nov 2025 13:16:01 -0600 Subject: [PATCH 08/32] Fix #27968: Eliminate unnecessary type conversions in GrabCut initGMMs Remove performance bottleneck caused by redundant type conversions in the initGMMs() function's tight nested loops that iterate over all image pixels. Changes: - Changed storage vectors from Vec3f to Vec3b to store pixel data directly without intermediate conversion - Modified kmeans preparation to convert Vec3b data to CV_32FC1 only when creating the Mat for clustering (using convertTo instead of per-pixel cast) - Explicitly convert Vec3b to Vec3d only when calling addSample() method Performance Impact: Previously: Vec3b -> Vec3f (in loop) -> Vec3d (implicit in addSample) Now: Vec3b (in loop) -> Vec3d (explicit, only in addSample call) This eliminates one unnecessary type conversion per pixel in the tight loop, reducing computational overhead especially for large images. Fixes #27968 --- modules/imgproc/src/grabcut.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/imgproc/src/grabcut.cpp b/modules/imgproc/src/grabcut.cpp index 358747843e..115f346e99 100644 --- a/modules/imgproc/src/grabcut.cpp +++ b/modules/imgproc/src/grabcut.cpp @@ -371,28 +371,30 @@ static void initGMMs( const Mat& img, const Mat& mask, GMM& bgdGMM, GMM& fgdGMM const int kMeansType = KMEANS_PP_CENTERS; Mat bgdLabels, fgdLabels; - std::vector bgdSamples, fgdSamples; + std::vector bgdSamples, fgdSamples; Point p; for( p.y = 0; p.y < img.rows; p.y++ ) { for( p.x = 0; p.x < img.cols; p.x++ ) { if( mask.at(p) == GC_BGD || mask.at(p) == GC_PR_BGD ) - bgdSamples.push_back( (Vec3f)img.at(p) ); + bgdSamples.push_back( img.at(p) ); else // GC_FGD | GC_PR_FGD - fgdSamples.push_back( (Vec3f)img.at(p) ); + fgdSamples.push_back( img.at(p) ); } } CV_Assert( !bgdSamples.empty() && !fgdSamples.empty() ); { - Mat _bgdSamples( (int)bgdSamples.size(), 3, CV_32FC1, &bgdSamples[0][0] ); + Mat _bgdSamples( (int)bgdSamples.size(), 3, CV_8UC1, &bgdSamples[0][0] ); + _bgdSamples.convertTo(_bgdSamples, CV_32FC1); int num_clusters = GMM::componentsCount; num_clusters = std::min(num_clusters, (int)bgdSamples.size()); kmeans( _bgdSamples, num_clusters, bgdLabels, TermCriteria( TermCriteria::MAX_ITER, kMeansItCount, 0.0), 0, kMeansType ); } { - Mat _fgdSamples( (int)fgdSamples.size(), 3, CV_32FC1, &fgdSamples[0][0] ); + Mat _fgdSamples( (int)fgdSamples.size(), 3, CV_8UC1, &fgdSamples[0][0] ); + _fgdSamples.convertTo(_fgdSamples, CV_32FC1); int num_clusters = GMM::componentsCount; num_clusters = std::min(num_clusters, (int)fgdSamples.size()); kmeans( _fgdSamples, num_clusters, fgdLabels, @@ -401,12 +403,12 @@ static void initGMMs( const Mat& img, const Mat& mask, GMM& bgdGMM, GMM& fgdGMM bgdGMM.initLearning(); for( int i = 0; i < (int)bgdSamples.size(); i++ ) - bgdGMM.addSample( bgdLabels.at(i,0), bgdSamples[i] ); + bgdGMM.addSample( bgdLabels.at(i,0), Vec3d(bgdSamples[i]) ); bgdGMM.endLearning(); fgdGMM.initLearning(); for( int i = 0; i < (int)fgdSamples.size(); i++ ) - fgdGMM.addSample( fgdLabels.at(i,0), fgdSamples[i] ); + fgdGMM.addSample( fgdLabels.at(i,0), Vec3d(fgdSamples[i]) ); fgdGMM.endLearning(); } From 3402fc1f6d348e11a2341c0ce8068d07ec0fb81b Mon Sep 17 00:00:00 2001 From: Kumataro Date: Sun, 9 Nov 2025 09:59:55 +0900 Subject: [PATCH 09/32] imgcodecs: Suppress unknown metadata type warning --- modules/imgcodecs/src/grfmt_base.cpp | 1 + modules/imgcodecs/src/loadsave.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/imgcodecs/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp index 0cd372ec28..4aaa9bbc7f 100644 --- a/modules/imgcodecs/src/grfmt_base.cpp +++ b/modules/imgcodecs/src/grfmt_base.cpp @@ -79,6 +79,7 @@ Mat BaseImageDecoder::getMetadata(ImageMetadataType type) const case IMAGE_METADATA_XMP: case IMAGE_METADATA_ICCP: + case IMAGE_METADATA_CICP: return makeMat(m_metadata[type]); default: diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 31e7e4c709..dadabb5f1c 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -476,7 +476,8 @@ static const char* metadataTypeToString(ImageMetadataType type) { return type == IMAGE_METADATA_EXIF ? "Exif" : type == IMAGE_METADATA_XMP ? "XMP" : - type == IMAGE_METADATA_ICCP ? "ICC Profile" : "???"; + type == IMAGE_METADATA_ICCP ? "ICC Profile" : + type == IMAGE_METADATA_CICP ? "cICP" : "???"; } static void addMetadata(ImageEncoder& encoder, From 386be68f9361feb9375fd1563fc4b8671aaea7b2 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Sun, 9 Nov 2025 18:23:13 +0300 Subject: [PATCH 10/32] Merge pull request #27981 from sturkmen72:fix_alpha_handling Fix alpha handling for blending in PNG format #27981 ### Pull Request Readiness Checklist closes #27974 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. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgcodecs/src/grfmt_png.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index dbb3c41cf2..a505266a8b 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -715,7 +715,7 @@ void PngDecoder::compose_frame(std::vector& rows_dst, const std::vect // Blending mode for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) { - uint16_t alpha = sp[3]; + uint16_t alpha = channels < 4 ? 0 : sp[3]; if (channels < 4 || alpha == 65535 || dp[3] == 0) { // Fully opaque OR destination fully transparent: direct copy @@ -745,7 +745,7 @@ void PngDecoder::compose_frame(std::vector& rows_dst, const std::vect // Blending mode for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) { - uint8_t alpha = sp[3]; + uint8_t alpha = channels < 4 ? 0 : sp[3]; if (channels < 4 || alpha == 255 || dp[3] == 0) { // Fully opaque OR destination fully transparent: direct copy From 64bbaa9f41be3e96a883cdbc11635eb84f18140c Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Mon, 10 Nov 2025 08:00:17 +0300 Subject: [PATCH 11/32] Merge pull request #27972 from asmorkalov:as/dst_mat_type Force empty output type where it's defined in API #27972 The PR replaces: - https://github.com/opencv/opencv/pull/27936 - https://github.com/opencv/opencv/pull/21059 Empty matrix has undefined type, so user code should not relay on the output type, if it's empty. The PR introduces some exceptions: - copyTo documentation defines, that the method re-create output buffer and set it's type. - convertTo has output type as parameter and output type is defined and expected. --- modules/core/src/arithm.cpp | 12 ++++++++++++ modules/core/src/convert.dispatch.cpp | 2 ++ modules/core/src/copy.cpp | 1 + modules/core/src/umatrix.cpp | 1 + modules/core/test/test_mat.cpp | 9 +++++++++ modules/core/test/test_umat.cpp | 8 ++++++++ 6 files changed, 33 insertions(+) diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index 5c1033be81..ccb104cebe 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -1113,6 +1113,10 @@ void cv::add( InputArray src1, InputArray src2, OutputArray dst, if (src1.empty() && src2.empty()) { dst.release(); + if (dtype >= 0) + { + dst.create(0, 0, dtype); + } return; } @@ -1140,6 +1144,10 @@ void cv::subtract( InputArray _src1, InputArray _src2, OutputArray _dst, if (_src1.empty() && _src2.empty()) { _dst.release(); + if (dtype >= 0) + { + _dst.create(0, 0, dtype); + } return; } @@ -1345,6 +1353,10 @@ void cv::addWeighted( InputArray src1, double alpha, InputArray src2, if (src1.empty() && src2.empty()) { dst.release(); + if (dtype >= 0) + { + dst.create(0, 0, dtype); + } return; } diff --git a/modules/core/src/convert.dispatch.cpp b/modules/core/src/convert.dispatch.cpp index a33708a557..0df033af6a 100644 --- a/modules/core/src/convert.dispatch.cpp +++ b/modules/core/src/convert.dispatch.cpp @@ -252,6 +252,7 @@ void Mat::convertTo(OutputArray dst, int type_, double alpha, double beta) const if (empty()) { dst.release(); + dst.create(size(), type_ >= 0 ? type_ : type()); return; } @@ -318,6 +319,7 @@ void UMat::convertTo(OutputArray dst, int type_, double alpha, double beta) cons if (empty()) { dst.release(); + dst.create(size(), type_ >= 0 ? type_ : type()); return; } diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 68d6b938e7..43f477aeae 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -447,6 +447,7 @@ void Mat::copyTo( OutputArray _dst ) const if( empty() ) { _dst.release(); + _dst.create(size(), type()); return; } diff --git a/modules/core/src/umatrix.cpp b/modules/core/src/umatrix.cpp index 65d50458dc..ceb4c064ec 100644 --- a/modules/core/src/umatrix.cpp +++ b/modules/core/src/umatrix.cpp @@ -1153,6 +1153,7 @@ void UMat::copyTo(OutputArray _dst) const if( empty() ) { _dst.release(); + _dst.create(size(), type()); return; } diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index ae8d5ddfda..9ba47755b4 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1385,6 +1385,15 @@ TEST(Core_Mat, push_back) } } +TEST(Core_Mat, copyToConvertTo_Empty) +{ + cv::Mat A(0, 0, CV_16SC2), B, C; + A.copyTo(B); + ASSERT_EQ(A.type(), B.type()); + A.convertTo(C, CV_32SC2); + ASSERT_EQ(C.type(), CV_32SC2); +} + TEST(Core_Mat, copyNx1ToVector) { cv::Mat_ src(5, 1); diff --git a/modules/core/test/test_umat.cpp b/modules/core/test/test_umat.cpp index 34e6d2be75..a1fc434dc6 100644 --- a/modules/core/test/test_umat.cpp +++ b/modules/core/test/test_umat.cpp @@ -1199,6 +1199,14 @@ TEST(UMat, async_cleanup_without_call_chain_warning) } } +TEST(UMat, copyToConvertTo_Empty) +{ + cv::UMat A(0, 0, CV_16SC2), B, C; + A.copyTo(B); + ASSERT_EQ(A.type(), B.type()); + A.convertTo(C, CV_32SC2); + ASSERT_EQ(C.type(), CV_32SC2); +} ///////////// oclCleanupCallback threadsafe check (#5062) ///////////////////// From db207c88b0d3949354f0125cc718962e69b7a637 Mon Sep 17 00:00:00 2001 From: happy-capybara-man Date: Mon, 10 Nov 2025 15:47:40 +0800 Subject: [PATCH 12/32] Merge pull request #27985 from happy-capybara-man:docs/fix-mat-clone-js docs(js): Fix Mat.clone() documentation to use mat_clone() for deep copy #27985 - Update code example to use ```mat_clone()``` instead of ```clone()``` - Add explanatory note about shallow copy issue due to Emscripten embind Problem - OpenCV.js documentation shows ```Mat.clone()``` usage, but this method performs shallow copy instead of deep copy due to Emscripten embind limitations, causing unexpected behavior where modifications to cloned matrices affect the original. Related Issues and PRs - Fixes documentation aspect of issue #27572 - Related to PR #26643 (js_clone_fix) ### 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. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../js_assets/js_image_arithmetics_bitwise.html | 2 +- doc/js_tutorials/js_assets/js_imgproc_camera.html | 2 +- doc/js_tutorials/js_assets/js_intelligent_scissors.html | 2 +- doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown | 6 ++++-- samples/dnn/js_face_recognition.html | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/js_tutorials/js_assets/js_image_arithmetics_bitwise.html b/doc/js_tutorials/js_assets/js_image_arithmetics_bitwise.html index 05c0ffd730..ccf0bbbda7 100644 --- a/doc/js_tutorials/js_assets/js_image_arithmetics_bitwise.html +++ b/doc/js_tutorials/js_assets/js_image_arithmetics_bitwise.html @@ -82,7 +82,7 @@ cv.bitwise_and(logo, logo, imgFg, mask); // Put logo in ROI and modify the main image cv.add(imgBg, imgFg, sum); -dst = src.clone(); +dst = src.mat_clone(); for (let i = 0; i < logo.rows; i++) { for (let j = 0; j < logo.cols; j++) { dst.ucharPtr(i, j)[0] = sum.ucharPtr(i, j)[0]; diff --git a/doc/js_tutorials/js_assets/js_imgproc_camera.html b/doc/js_tutorials/js_assets/js_imgproc_camera.html index 2df68d7f33..273b11ff57 100644 --- a/doc/js_tutorials/js_assets/js_imgproc_camera.html +++ b/doc/js_tutorials/js_assets/js_imgproc_camera.html @@ -248,7 +248,7 @@ function backprojection(src) { if (base instanceof cv.Mat) { base.delete(); } - base = src.clone(); + base = src.mat_clone(); cv.cvtColor(base, base, cv.COLOR_RGB2HSV, 0); } cv.cvtColor(src, dstC3, cv.COLOR_RGB2HSV, 0); diff --git a/doc/js_tutorials/js_assets/js_intelligent_scissors.html b/doc/js_tutorials/js_assets/js_intelligent_scissors.html index 1782dc6f03..f48dd07e4b 100644 --- a/doc/js_tutorials/js_assets/js_intelligent_scissors.html +++ b/doc/js_tutorials/js_assets/js_intelligent_scissors.html @@ -53,7 +53,7 @@ canvas.addEventListener('click', e => { }); canvas.addEventListener('mousemove', e => { let x = e.offsetX, y = e.offsetY; //console.log(x, y); - let dst = src.clone(); + let dst = src.mat_clone(); if (hasMap && x >= 0 && x < src.cols && y >= 0 && y < src.rows) { let contour = new cv.Mat(); diff --git a/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown b/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown index ee110c37cb..74c3b9cc86 100644 --- a/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown +++ b/doc/js_tutorials/js_core/js_basic_ops/js_basic_ops.markdown @@ -77,12 +77,14 @@ How to copy Mat There are 2 ways to copy a Mat: @code{.js} -// 1. Clone -let dst = src.clone(); +// 1. Clone (deep copy) +let dst = src.mat_clone(); // 2. CopyTo(only entries indicated in the mask are copied) src.copyTo(dst, mask); @endcode +@note In OpenCV.js, use `mat_clone()` instead of `clone()` to ensure deep copy behavior. The `clone()` method may perform shallow copy due to Emscripten embind limitations. + How to convert the type of Mat ------------------------------ diff --git a/samples/dnn/js_face_recognition.html b/samples/dnn/js_face_recognition.html index 3b4ed390d7..aaee89327f 100644 --- a/samples/dnn/js_face_recognition.html +++ b/samples/dnn/js_face_recognition.html @@ -132,7 +132,7 @@ function main() { var cell = document.getElementById("targetNames").insertCell(0); cell.innerHTML = name; - persons[name] = face2vec(face).clone(); + persons[name] = face2vec(face).mat_clone(); var canvas = document.createElement("canvas"); canvas.setAttribute("width", 112); From 83026bfe871bb9b575f62df03743704a01c13fcd Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:11:24 +0300 Subject: [PATCH 13/32] Merge pull request #27990 from asmorkalov:as/power9_build_fix Fix missing vec_cvfo on IBM POWER9 due to unavailable VSX float64 conversion #27990 Replaces https://github.com/opencv/opencv/pull/27633 Closes https://github.com/opencv/opencv/issues/27635 ### 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 --- .../core/include/opencv2/core/vsx_utils.hpp | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/modules/core/include/opencv2/core/vsx_utils.hpp b/modules/core/include/opencv2/core/vsx_utils.hpp index 4d5a694bae..aafc6c07b1 100644 --- a/modules/core/include/opencv2/core/vsx_utils.hpp +++ b/modules/core/include/opencv2/core/vsx_utils.hpp @@ -257,8 +257,27 @@ VSX_IMPL_1VRG(vec_udword2, vec_udword2, vpopcntd, vec_popcntu) VSX_IMPL_1VRG(vec_udword2, vec_dword2, vpopcntd, vec_popcntu) // converts between single and double-precision -VSX_REDIRECT_1RG(vec_float4, vec_double2, vec_cvfo, vec_floate) -VSX_REDIRECT_1RG(vec_double2, vec_float4, vec_cvfo, vec_doubleo) +// vec_floate and vec_doubleo are available since Power10 and z14 +#if defined(__POWER10__) || (defined(__powerpc64__) && defined(__ARCH_PWR10__) +// Use VSX double<->float conversion instructions (if supported by the architecture) + VSX_REDIRECT_1RG(vec_float4, vec_double2, vec_cvfo, vec_floate) + VSX_REDIRECT_1RG(vec_double2, vec_float4, vec_cvfo, vec_doubleo) +#else +// Fallback: implement vec_cvfo using scalar operations (to ensure successful linking) + static inline vec_float4 vec_cvfo(const vec_double2& a) + { + float r0 = static_cast(reinterpret_cast(&a)[0]); + float r1 = static_cast(reinterpret_cast(&a)[1]); + return (vec_float4){r0, 0.f, r1, 0.f}; + } + + static inline vec_double2 vec_cvfo(const vec_float4& a) + { + double r0 = static_cast(reinterpret_cast(&a)[0]); + double r1 = static_cast(reinterpret_cast(&a)[2]); + return (vec_double2){r0, r1}; + } +#endif // converts word and doubleword to double-precision #undef vec_ctd From 2dd0ac3122dafeeafd63e632df409c2156282110 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:26:07 +0300 Subject: [PATCH 14/32] Merge pull request #27991 from asmorkalov:as/gapi_unrich_ctor Fixed unrichable code with MSVC on x86 32-bit systems. #27991 Suppress the following warnings: ``` Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\onevpl\file_data_provider.cpp(131): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\onevpl\source.cpp(77): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\onevpl\source.cpp(82): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\onevpl\source.cpp(103): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\onevpl\source.cpp(90): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\onevpl\source.cpp(94): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\onevpl\source.cpp(86): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\onevpl\source.cpp(99): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\gstreamer\gstreamersource.cpp(366): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\gstreamer\gstreamersource.cpp(360): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] Warning: C:\GHA-OCV-5\_work\opencv\opencv\opencv\modules\gapi\src\streaming\gstreamer\gstreamerpipeline.cpp(77): warning C4702: unreachable code [C:\GHA-OCV-5\_work\opencv\opencv\build\modules\gapi\opencv_gapi.vcxproj] ``` ### 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 --- modules/gapi/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index a2aec0b1b8..0182384f1c 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -41,9 +41,7 @@ if(MSVC) # and IE deprecated code warning C4996 ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4503 /wd4996) endif() - if((MSVC_VERSION LESS 1920) OR ARM OR AARCH64) # MSVS 2015/2017 on x86 and ARM - ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4702) # 'unreachable code' - endif() + ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4702) # MSVS 2015/2017/2019 on x86 and ARM endif() file(GLOB gapi_ext_hdrs From 5b2976f27beb00608e1b5b9b4bbd79253837e789 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 9 Oct 2025 08:19:54 +0300 Subject: [PATCH 15/32] Switch to new Windows CI pipelines. --- .github/workflows/PR-4.x.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/PR-4.x.yaml b/.github/workflows/PR-4.x.yaml index b31be220f8..11c4cbf517 100644 --- a/.github/workflows/PR-4.x.yaml +++ b/.github/workflows/PR-4.x.yaml @@ -12,6 +12,11 @@ jobs: with: workflow_branch: main + Windows: + uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-Windows.yaml@main + with: + workflow_branch: main + Ubuntu2004-ARM64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-ARM64.yaml@main @@ -25,12 +30,6 @@ jobs: if: "${{ contains(github.event.pull_request.labels.*.name, 'category: dnn') }} || ${{ contains(github.event.pull_request.labels.*.name, 'category: dnn (onnx)') }}" uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-U20-Cuda.yaml@main - Windows10-x64: - uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-W10.yaml@main - - Windows10-x64-Vulkan: - uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-W10-Vulkan.yaml@main - macOS-ARM64: uses: opencv/ci-gha-workflow/.github/workflows/OCV-PR-4.x-macOS-ARM64.yaml@main From 3e49b6fa93235d9cc3296e42bd92c0179d9d4278 Mon Sep 17 00:00:00 2001 From: victorget Date: Tue, 11 Nov 2025 11:30:47 +0100 Subject: [PATCH 16/32] Merge pull request #27925 from intel-staging:dev/enforce_ipp_define Added CMake define option to enforce IPP calls in IPP HAL when building with IPP integration #27925 Extra build option needed to simplify custom builds with IPP integration to ensure the IPP calls done even in cases when the results are not bitwise compliant to the reference OpenCV implementation. Option name is ```WITH_IPP_CALLS_ENFORCED```, and it is disabled by default. Requested by some IPP customers and might be used in general as a way providing calls with better performance for the cases the aligned precision is not as important aspect. Supposed to be used in HAL only, so added only in IPP HAL CMake, currently it affects only Warp Affine, Warp Perspective and Remap integrations since the results may vary depending on HW and algorithm implementations. ### 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 - [ ] 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 --- hal/ipp/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hal/ipp/CMakeLists.txt b/hal/ipp/CMakeLists.txt index 1aa8e6abd0..5ff630c8c0 100644 --- a/hal/ipp/CMakeLists.txt +++ b/hal/ipp/CMakeLists.txt @@ -21,7 +21,7 @@ add_library(ipphal STATIC #TODO: HAVE_IPP_ICV and HAVE_IPP_IW added as private macro till OpenCV itself is # source of IPP and public definitions lead to redefinition warning # The macro should be redefined as PUBLIC when IPP part is removed from core -# to make HAL the source of IPP integration +# to make HAL the source of IPP integration. The same is true for WITH_IPP_CALLS_ENFORCED if(HAVE_IPP_ICV) target_compile_definitions(ipphal PRIVATE HAVE_IPP_ICV) endif() @@ -30,6 +30,11 @@ if(HAVE_IPP_IW) target_compile_definitions(ipphal PRIVATE HAVE_IPP_IW) endif() +if(WITH_IPP_CALLS_ENFORCED) + target_compile_definitions(ipphal PRIVATE IPP_CALLS_ENFORCED) + message("WITH_IPP_CALLS_ENFORCED=${WITH_IPP_CALLS_ENFORCED}: enforced IPP calls are enabled in IPP HAL") +endif() + target_include_directories(ipphal PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include") ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-suggest-override) From 17ce5a290b7afb856d039fdef637fbe5646fe31a Mon Sep 17 00:00:00 2001 From: Vadim Levin Date: Tue, 11 Nov 2025 19:11:16 +0300 Subject: [PATCH 17/32] fix: use export_name as resolved typename for AliasTypeNode Use ctype_name as export name for AliasTypeNode if export name is not explicitly provided. --- .../typing_stubs_generation/nodes/type_node.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/python/src2/typing_stubs_generation/nodes/type_node.py b/modules/python/src2/typing_stubs_generation/nodes/type_node.py index 86bd744a1c..f6ba773654 100644 --- a/modules/python/src2/typing_stubs_generation/nodes/type_node.py +++ b/modules/python/src2/typing_stubs_generation/nodes/type_node.py @@ -274,14 +274,21 @@ class AliasTypeNode(TypeNode): required_modules: Tuple[str, ...] = ()) -> None: super().__init__(ctype_name, required_modules) self.value = value - self._export_name = export_name + # If alias is exported as is - use its ctype_name + if export_name is None: + forbidden_symbols = (":", "*", "&") + assert all(symbol not in ctype_name for symbol in forbidden_symbols), ( + "Failed to create AliasTypeNode without export_name. " + f"'{ctype_name}' should not contain any of {forbidden_symbols}" + ) + self._export_name = ctype_name + else: + self._export_name = export_name self.doc = doc @property def typename(self) -> str: - if self._export_name is not None: - return self._export_name - return self.ctype_name + return self._export_name @property def full_typename(self) -> str: From a31a52adefb488f5b8c2f661525f90b87f35a22b Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:30:14 +0300 Subject: [PATCH 18/32] Merge pull request #27992 from asmorkalov:as/HoughLines_bias Fixed standard HoughLines output shift for rho. #27992 Closes: https://github.com/opencv/opencv/issues/25038 Replaces: https://github.com/opencv/opencv/pull/25043 Merge with https://github.com/opencv/opencv_extra/pull/1288 The original implementation introduces systematic shift (-rho/2) for odd indexes. Integer division just gives proper rounding. ### 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 --- modules/imgproc/src/hough.cpp | 2 +- modules/imgproc/src/opencl/hough_lines.cl | 2 +- modules/imgproc/test/test_houghlines.cpp | 63 +++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index 39bdecf2ea..b418b046bc 100644 --- a/modules/imgproc/src/hough.cpp +++ b/modules/imgproc/src/hough.cpp @@ -228,7 +228,7 @@ HoughLinesStandard( InputArray src, OutputArray lines, int type, int idx = _sort_buf[i]; int n = cvFloor(idx*scale) - 1; int r = idx - (n+1)*(numrho+2) - 1; - line.rho = (r - (numrho - 1)*0.5f) * rho; + line.rho = (r - (numrho - 1)/2) * rho; line.angle = static_cast(min_theta) + n * theta; if (type == CV_32FC2) { diff --git a/modules/imgproc/src/opencl/hough_lines.cl b/modules/imgproc/src/opencl/hough_lines.cl index 907811cded..5ae4171fcb 100644 --- a/modules/imgproc/src/opencl/hough_lines.cl +++ b/modules/imgproc/src/opencl/hough_lines.cl @@ -162,7 +162,7 @@ __kernel void get_lines(__global uchar * accum_ptr, int accum_step, int accum_of if (index < linesMax) { - float radius = (x - (accum_cols - 3) * 0.5f) * rho; + float radius = (x - (accum_cols - 3) / 2) * rho; float angle = y * theta; lines[index] = (float2)(radius, angle); diff --git a/modules/imgproc/test/test_houghlines.cpp b/modules/imgproc/test/test_houghlines.cpp index 02eb2d4379..82b2864b13 100644 --- a/modules/imgproc/test/test_houghlines.cpp +++ b/modules/imgproc/test/test_houghlines.cpp @@ -340,6 +340,69 @@ TEST(HoughLines, regression_21983) EXPECT_NEAR(lines[0][1], 1.57179642, 1e-4); } +TEST(HoughLines, regression_25038_vertical) +{ + cv::Mat img = cv::Mat::zeros(8, 8, CV_8UC1); + img.col(3).setTo(255); + + cv::Mat lines; + cv::HoughLines(img, lines, 0.5, CV_PI/4., 2); + EXPECT_EQ(1, lines.cols); + EXPECT_EQ(1, lines.rows); + EXPECT_EQ(2, lines.channels()); + EXPECT_NEAR(3, lines.at(0)[0], 1e-5); + EXPECT_NEAR(0, lines.at(0)[1], 1e-5); + + cv::HoughLines(img, lines, 0.05, CV_PI/4., 2); + EXPECT_EQ(1, lines.cols); + EXPECT_EQ(1, lines.rows); + EXPECT_EQ(2, lines.channels()); + EXPECT_NEAR(3, lines.at(0)[0], 1e-5); + EXPECT_NEAR(0, lines.at(0)[1], 1e-5); +} + +TEST(HoughLines, regression_25038_even) +{ + cv::Mat img = cv::Mat::zeros(8, 8, CV_8UC1); + img.col(4).setTo(255); + + cv::Mat lines; + cv::HoughLines(img, lines, 0.5, CV_PI/4., 2); + EXPECT_EQ(1, lines.cols); + EXPECT_EQ(1, lines.rows); + EXPECT_EQ(2, lines.channels()); + EXPECT_NEAR(4, lines.at(0)[0], 1e-5); + EXPECT_NEAR(0, lines.at(0)[1], 1e-5); + + cv::HoughLines(img, lines, 0.05, CV_PI/4., 2); + EXPECT_EQ(1, lines.cols); + EXPECT_EQ(1, lines.rows); + EXPECT_EQ(2, lines.channels()); + EXPECT_NEAR(4, lines.at(0)[0], 1e-5); + EXPECT_NEAR(0, lines.at(0)[1], 1e-5); +} + +TEST(HoughLines, regression_25038_horizontal) +{ + cv::Mat img = cv::Mat::zeros(8, 8, CV_8UC1); + img.row(3).setTo(255); + + cv::Mat lines; + cv::HoughLines(img, lines, 0.5, CV_PI/4., 2); + EXPECT_EQ(1, lines.cols); + EXPECT_EQ(1, lines.rows); + EXPECT_EQ(2, lines.channels()); + EXPECT_NEAR(3, lines.at(0)[0], 1e-5); + EXPECT_NEAR(CV_PI/2., lines.at(0)[1], 1e-5); + + cv::HoughLines(img, lines, 0.05, CV_PI/4., 2); + EXPECT_EQ(1, lines.cols); + EXPECT_EQ(1, lines.rows); + EXPECT_EQ(2, lines.channels()); + EXPECT_NEAR(3, lines.at(0)[0], 1e-5); + EXPECT_NEAR(CV_PI/2., lines.at(0)[1], 1e-5); +} + TEST(WeightedHoughLines, horizontal) { Mat img(25, 25, CV_8UC1, Scalar(0)); From 78adab13e9de0a6c532d2103f2e9b7ff3d2f5caf Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Wed, 12 Nov 2025 23:44:41 +0800 Subject: [PATCH 19/32] Merge pull request #27479 from fengyuentau:4x/imgproc/boundingRect-simd imgproc: supports CV_SIMD_SCALABLE in pointSetBoundingRect #27479 ### 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 - [ ] 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/imgproc/src/geometry.cpp | 185 ++++++++++--------------------- 1 file changed, 61 insertions(+), 124 deletions(-) diff --git a/modules/imgproc/src/geometry.cpp b/modules/imgproc/src/geometry.cpp index c7a52b2f46..020a636835 100644 --- a/modules/imgproc/src/geometry.cpp +++ b/modules/imgproc/src/geometry.cpp @@ -710,171 +710,108 @@ static Rect pointSetBoundingRect( const Mat& points ) int depth = points.depth(); CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S)); - int xmin = 0, ymin = 0, xmax = -1, ymax = -1, i; + int xmin = 0, ymin = 0, xmax = -1, ymax = -1, i = 0; bool is_float = depth == CV_32F; if( npoints == 0 ) return Rect(); -#if CV_SIMD // TODO: enable for CV_SIMD_SCALABLE, loop tail related. if( !is_float ) { const int32_t* pts = points.ptr(); int64_t firstval = 0; std::memcpy(&firstval, pts, sizeof(pts[0]) * 2); + xmin = xmax = pts[0]; + ymin = ymax = pts[1]; +#if CV_SIMD || CV_SIMD_SCALABLE v_int32 minval, maxval; minval = maxval = v_reinterpret_as_s32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y - for( i = 1; i <= npoints - VTraits::vlanes()/2; i+= VTraits::vlanes()/2 ) + const int nlanes = VTraits::vlanes()/2; + for (; i < npoints; i += nlanes) { + if (i > npoints - nlanes) + { + if (i == 0) + break; + i = npoints - nlanes; + } v_int32 ptXY2 = vx_load(pts + 2 * i); minval = v_min(ptXY2, minval); maxval = v_max(ptXY2, maxval); } - minval = v_min(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval)))); - maxval = v_max(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval)))); - if( i <= npoints - VTraits::vlanes()/4 ) + constexpr int max_nlanes = VTraits::max_nlanes; + int arr_minval[max_nlanes], arr_maxval[max_nlanes]; + vx_store(arr_minval, minval); + vx_store(arr_maxval, maxval); + for (int j = 0; j < nlanes; j++) { - v_int32 ptXY = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(vx_load_low(pts + 2 * i)))); - minval = v_min(ptXY, minval); - maxval = v_max(ptXY, maxval); - i += VTraits::vlanes()/2; + xmin = std::min(xmin, arr_minval[2*j]); + ymin = std::min(ymin, arr_minval[2*j+1]); + xmax = std::max(xmax, arr_maxval[2*j]); + ymax = std::max(ymax, arr_maxval[2*j+1]); } - for(int j = 16; j < VTraits::vlanes(); j*=2) +#endif + for( ; i < npoints; i++ ) { - minval = v_min(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval)))); - maxval = v_max(v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval)))); + int pt_x = pts[2*i]; + int pt_y = pts[2*i+1]; + + xmin = std::min(xmin, pt_x); + xmax = std::max(xmax, pt_x); + ymin = std::min(ymin, pt_y); + ymax = std::max(ymax, pt_y); } - xmin = v_get0(minval); - xmax = v_get0(maxval); - ymin = v_get0(v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval)))); - ymax = v_get0(v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval)))); -#if CV_SIMD_WIDTH > 16 - if( i < npoints ) - { - v_int32x4 minval2, maxval2; - minval2 = maxval2 = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + 2 * i)))); - for( i++; i < npoints; i++ ) - { - v_int32x4 ptXY = v_reinterpret_as_s32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + 2 * i)))); - minval2 = v_min(ptXY, minval2); - maxval2 = v_max(ptXY, maxval2); - } - xmin = min(xmin, v_get0(minval2)); - xmax = max(xmax, v_get0(maxval2)); - ymin = min(ymin, v_get0(v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(minval2))))); - ymax = max(ymax, v_get0(v_reinterpret_as_s32(v_expand_high(v_reinterpret_as_u32(maxval2))))); - } -#endif // CV_SIMD } else { const float* pts = points.ptr(); int64_t firstval = 0; std::memcpy(&firstval, pts, sizeof(pts[0]) * 2); + xmin = xmax = cvFloor(pts[0]); + ymin = ymax = cvFloor(pts[1]); +#if CV_SIMD || CV_SIMD_SCALABLE v_float32 minval, maxval; minval = maxval = v_reinterpret_as_f32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y - for( i = 1; i <= npoints - VTraits::vlanes()/2; i+= VTraits::vlanes()/2 ) + const int nlanes = VTraits::vlanes()/2; + for (; i < npoints; i += nlanes) { + if (i > npoints - nlanes) + { + if (i == 0) + break; + i = npoints - nlanes; + } v_float32 ptXY2 = vx_load(pts + 2 * i); minval = v_min(ptXY2, minval); maxval = v_max(ptXY2, maxval); } - minval = v_min(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval)))); - maxval = v_max(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval)))); - if( i <= npoints - VTraits::vlanes()/4 ) + constexpr int max_nlanes = VTraits::max_nlanes; + float arr_minval[max_nlanes], arr_maxval[max_nlanes]; + vx_store(arr_minval, minval); + vx_store(arr_maxval, maxval); + for (int j = 0; j < nlanes; j++) { - v_float32 ptXY = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(vx_load_low(pts + 2 * i)))); - minval = v_min(ptXY, minval); - maxval = v_max(ptXY, maxval); - i += VTraits::vlanes()/4; - } - for(int j = 16; j < VTraits::vlanes(); j*=2) - { - minval = v_min(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(minval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval)))); - maxval = v_max(v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(maxval))), v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval)))); - } - xmin = cvFloor(v_get0(minval)); - xmax = cvFloor(v_get0(maxval)); - ymin = cvFloor(v_get0(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval))))); - ymax = cvFloor(v_get0(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval))))); -#if CV_SIMD_WIDTH > 16 - if( i < npoints ) - { - v_float32x4 minval2, maxval2; - minval2 = maxval2 = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + 2 * i)))); - for( i++; i < npoints; i++ ) - { - v_float32x4 ptXY = v_reinterpret_as_f32(v_expand_low(v_reinterpret_as_u32(v_load_low(pts + 2 * i)))); - minval2 = v_min(ptXY, minval2); - maxval2 = v_max(ptXY, maxval2); - } - xmin = min(xmin, cvFloor(v_get0(minval2))); - xmax = max(xmax, cvFloor(v_get0(maxval2))); - ymin = min(ymin, cvFloor(v_get0(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(minval2)))))); - ymax = max(ymax, cvFloor(v_get0(v_reinterpret_as_f32(v_expand_high(v_reinterpret_as_u32(maxval2)))))); + int _xmin = cvFloor(arr_minval[2*j]), _ymin = cvFloor(arr_minval[2*j+1]); + int _xmax = cvFloor(arr_maxval[2*j]), _ymax = cvFloor(arr_maxval[2*j+1]); + xmin = std::min(xmin, _xmin); + ymin = std::min(ymin, _ymin); + xmax = std::max(xmax, _xmax); + ymax = std::max(ymax, _ymax); } #endif - } -#else - const Point* pts = points.ptr(); - Point pt = pts[0]; - - if( !is_float ) - { - xmin = xmax = pt.x; - ymin = ymax = pt.y; - - for( i = 1; i < npoints; i++ ) + for( ; i < npoints; i++ ) { - pt = pts[i]; + // because right and bottom sides of the bounding rectangle are not inclusive + // (note +1 in width and height calculation below), cvFloor is used here instead of cvCeil + int pt_x = cvFloor(pts[2*i]); + int pt_y = cvFloor(pts[2*i+1]); - if( xmin > pt.x ) - xmin = pt.x; - - if( xmax < pt.x ) - xmax = pt.x; - - if( ymin > pt.y ) - ymin = pt.y; - - if( ymax < pt.y ) - ymax = pt.y; + xmin = std::min(xmin, pt_x); + xmax = std::max(xmax, pt_x); + ymin = std::min(ymin, pt_y); + ymax = std::max(ymax, pt_y); } } - else - { - Cv32suf v; - // init values - xmin = xmax = CV_TOGGLE_FLT(pt.x); - ymin = ymax = CV_TOGGLE_FLT(pt.y); - - for( i = 1; i < npoints; i++ ) - { - pt = pts[i]; - pt.x = CV_TOGGLE_FLT(pt.x); - pt.y = CV_TOGGLE_FLT(pt.y); - - if( xmin > pt.x ) - xmin = pt.x; - - if( xmax < pt.x ) - xmax = pt.x; - - if( ymin > pt.y ) - ymin = pt.y; - - if( ymax < pt.y ) - ymax = pt.y; - } - - v.i = CV_TOGGLE_FLT(xmin); xmin = cvFloor(v.f); - v.i = CV_TOGGLE_FLT(ymin); ymin = cvFloor(v.f); - // because right and bottom sides of the bounding rectangle are not inclusive - // (note +1 in width and height calculation below), cvFloor is used here instead of cvCeil - v.i = CV_TOGGLE_FLT(xmax); xmax = cvFloor(v.f); - v.i = CV_TOGGLE_FLT(ymax); ymax = cvFloor(v.f); - } -#endif return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); } From 4c1f32e716fe6212718ba82102ceb222bec9150e Mon Sep 17 00:00:00 2001 From: penghuiho Date: Thu, 13 Nov 2025 13:32:08 +0800 Subject: [PATCH 20/32] Fix the issue of obtaining the size of the _src --- modules/imgproc/src/moments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/src/moments.cpp b/modules/imgproc/src/moments.cpp index 786573bbcb..5f910a26fb 100644 --- a/modules/imgproc/src/moments.cpp +++ b/modules/imgproc/src/moments.cpp @@ -401,7 +401,7 @@ static bool ocl_moments( InputArray _src, Moments& m, bool binary) const int TILE_SIZE = 32; const int K = 10; - Size sz = _src.getSz(); + Size sz = _src.size(); int xtiles = divUp(sz.width, TILE_SIZE); int ytiles = divUp(sz.height, TILE_SIZE); int ntiles = xtiles*ytiles; From 5c02d32ccac16885ee537ff3a10095b149a6e810 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Thu, 13 Nov 2025 14:19:46 +0800 Subject: [PATCH 21/32] Merge pull request #28000 from dkurt:d.kurtaev:reset_winograd_impl ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request resolves https://github.com/opencv/opencv/issues/27580 - [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/dnn/src/layers/convolution_layer.cpp | 11 +++-- modules/dnn/test/test_layers.cpp | 43 ++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 13069f53bc..c33eda428e 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -253,6 +253,7 @@ public: Ptr activ; Ptr fastConvImpl; + bool canUseWinograd = false; #ifdef HAVE_OPENCL Ptr > convolutionOp; @@ -448,6 +449,13 @@ public: #ifdef HAVE_OPENCL convolutionOp.release(); #endif + + // Winograd only works when input h and w >= 12. + canUseWinograd = useWinograd && inputs[0].dims == 4 && inputs[0].size[2] >= 12 && inputs[0].size[3] >= 12; + if (fastConvImpl && (fastConvImpl->conv_type == CONV_TYPE_WINOGRAD3X3) ^ canUseWinograd) + { + fastConvImpl.reset(); + } } bool setActivation(const Ptr& layer) CV_OVERRIDE @@ -1292,9 +1300,6 @@ public: int K = outputs[0].size[1]; int C = inputs[0].size[1]; - // Winograd only works when input h and w >= 12. - bool canUseWinograd = useWinograd && conv_dim == CONV_2D && inputs[0].size[2] >= 12 && inputs[0].size[3] >= 12; - CV_Assert(outputs[0].size[1] % ngroups == 0); fastConvImpl = initFastConv(weightsMat, &biasvec[0], ngroups, K, C, kernel_size, strides, dilations, pads_begin, pads_end, conv_dim, diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 48c968fb6a..9da907758c 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -2788,4 +2788,47 @@ INSTANTIATE_TEST_CASE_P(TestLayerFusion, ConvolutionActivationEltwiseFusion, Com TestLayerFusion::dnnBackendsAndTargetsForFusionTests() )); +TEST(ConvolutionWinograd, Accuracy) +{ + Mat weights({2, 1, 3, 3}, CV_32F); + randn(weights, 0, 1); + + // Check convolution can switch between implementations on changed shape. + auto getNet = [&]() { + Net net; + LayerParams lp; + lp.name = "conv"; + lp.type = "Convolution"; + lp.set("kernel_size", 3); + lp.set("num_output", 2); + lp.set("pad", 0); + lp.set("stride", 1); + lp.set("bias_term", false); + + lp.blobs.push_back(weights); + net.addLayerToPrev(lp.name, lp.type, lp); + return net; + }; + + Mat inpSmall({1, 1, 5, 5}, CV_32F); + Mat inpLarge({1, 1, 64, 64}, CV_32F); + randn(inpSmall, 0, 1); + randn(inpLarge, 0, 1); + + Net net1 = getNet(); + Net net2 = getNet(); + net1.setInput(inpSmall); + net2.setInput(inpLarge); + Mat refSmall = net1.forward(); + Mat refLarge = net2.forward(); + + net1.setInput(inpLarge); + net2.setInput(inpSmall); + Mat outLarge = net1.forward(); + Mat outSmall = net2.forward(); + + normAssert(outSmall, refSmall, "Small input after large", 0.0, 0.0); + normAssert(outLarge, refLarge, "Large input after small", 0.0, 0.0); +} + }} // namespace From 45d233f7bf65a1e10e72757c1b3bb3faf5b2db06 Mon Sep 17 00:00:00 2001 From: "Alessandro de Oliveira Faria (A.K.A.CABELO)" Date: Thu, 13 Nov 2025 04:30:35 -0300 Subject: [PATCH 22/32] Merge pull request #28003 from cabelo:oneapi Building OpenCV with oneAPI #28003 ### 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 --- .../linux_gdb_pretty_printer.markdown | 2 +- .../linux_install/linux_install.markdown | 2 +- .../oneapi_install/oneapi_install.markdown | 106 ++++++++++++++++++ .../table_of_content_introduction.markdown | 1 + 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 doc/tutorials/introduction/oneapi_install/oneapi_install.markdown diff --git a/doc/tutorials/introduction/linux_gdb_pretty_printer/linux_gdb_pretty_printer.markdown b/doc/tutorials/introduction/linux_gdb_pretty_printer/linux_gdb_pretty_printer.markdown index b0b8d404a0..0147ae2f58 100644 --- a/doc/tutorials/introduction/linux_gdb_pretty_printer/linux_gdb_pretty_printer.markdown +++ b/doc/tutorials/introduction/linux_gdb_pretty_printer/linux_gdb_pretty_printer.markdown @@ -1,7 +1,7 @@ Using OpenCV with gdb-powered IDEs {#tutorial_linux_gdb_pretty_printer} ===================== -@prev_tutorial{tutorial_linux_install} +@prev_tutorial{tutorial_oneapi_install} @next_tutorial{tutorial_linux_gcc_cmake} | | | diff --git a/doc/tutorials/introduction/linux_install/linux_install.markdown b/doc/tutorials/introduction/linux_install/linux_install.markdown index c9baa57e8d..e1db4c1550 100644 --- a/doc/tutorials/introduction/linux_install/linux_install.markdown +++ b/doc/tutorials/introduction/linux_install/linux_install.markdown @@ -2,7 +2,7 @@ Installation in Linux {#tutorial_linux_install} ===================== @prev_tutorial{tutorial_env_reference} -@next_tutorial{tutorial_linux_gdb_pretty_printer} +@next_tutorial{tutorial_oneapi_install} | | | | -: | :- | diff --git a/doc/tutorials/introduction/oneapi_install/oneapi_install.markdown b/doc/tutorials/introduction/oneapi_install/oneapi_install.markdown new file mode 100644 index 0000000000..414517df91 --- /dev/null +++ b/doc/tutorials/introduction/oneapi_install/oneapi_install.markdown @@ -0,0 +1,106 @@ +Building OpenCV with oneAPI {#tutorial_oneapi_install} +=========================== + + +@prev_tutorial{tutorial_linux_install} +@next_tutorial{tutorial_linux_gcc_cmake} + +| | | +| -: | :- | +| Original author | Alessandro de Oliveira Faria | +| Compatibility | OpenCV >= 4.11.0 | + +@tableofcontents + +# Quick start {#tutorial_oneapi_install_quick_start} + +**oneAPI** is Intel's open initiative (now also maintained by the UXL Foundation) that combines a specification and a set of toolkits for programming CPUs, GPUs, FPGAs and NPUs with a single code base. The core is the SYCL standard (single-source C++ for parallelism), complemented by high-performance libraries — oneTBB (parallelism), oneMKL (linear algebra), oneDNN (neural networks), oneVPL (video), etc. Thus, when you compile with oneAPI's DPC++ (icpx) compiler, the binary gains optimized execution paths that choose, at runtime, the best vector instructions or the available device, without changing the source code. + +## Why compile OpenCV with the oneAPI ecosystem when targeting the CPU: + +* Simple, because by enabling the CMake options -DWITH_SYCL=ON -DWITH_TBB=ON -DWITH_ONEDNN=ON -DWITH_IPP=ON and using the icpx compiler, the OpenCV core starts to directly invoke oneAPI libraries. +* oneDNN replaces the generic kernels of the cv::dnn layer with implementations that exploit AVX2, AVX-512, AMX and VNNI, accelerating convolutions, matmul and network post-processing by up to 3-5× on modern CPUs. +* oneTBB takes over the thread pool, scheduling filters like cv::resize, cv::GaussianBlur or the G-API pipeline across all cores without busy-wait. +* IPP (now distributed via oneAPI Base Toolkit) provides optimized intrinsic routines for elementary operations (SAD, DFT, median blur), which OpenCV calls when it encounters the HAVE_IPP macro. +* All this happens transparently: the source code that uses cv::Mat remains the same, but the linked symbols point to vectorized versions, and the internal dispatcher selects the appropriate vector width at runtime. + + +## CPU Processor Requirements + +Systems based on Intel® 64 architectures below are supported both as host and target platforms. + +* Intel® Core™ processor family or higher +* Intel® Xeon® processor family +* Intel® Xeon® Scalable processor family + + +### Requirements for Accelerators + +* Integrated GEN9 (and higher) GPUs. See source in Intel® Graphics Compiler for OpenCL™ +* FPGA Card: see Intel(R) DPC++ Compiler System Requirements. + +### Disk Space Requirements + +* 3.3 GB of disk space (minimum) on a standard installation. + +@note: During the installation process, the installer may need up to 6 GB of additional temporary disk storage to manage the download and intermediate installation files. + + +### Memory Requirements + +* 8 GB RAM recommended + + +## How To install oneAPI + +Installing oneAPI: To quickly set up the oneAPI ecosystem on openSUSE, simply follow the official guide https://www.intel.com/content/www/us/en/developer/articles/guide/installation-guide-for-oneapi-toolkits.html, which shows you how to enable the distribution’s dedicated repository (zypper ar … oneAPI) and install the metapackages ― for example, intel-basekit (DPC++, TBB, oneDNN, IPP compilers) and, optionally, intel-hpckit or intel-renderkit if you need HPC or graphics tools. The guide also explains post-installation tweaks, such as loading the environment with source /opt/intel/oneapi/setvars.sh , ensuring that the binaries (icpx, dpcpp) and libraries are immediately available in your shell for compiling and running accelerated applications. + +## Download, Github Instruction, Build and Install + +1. Below are the commands to download last version (latest release on the date of publication of this text): + +``` +git clone https://github.com/opencv/opencv.git +``` + +2. and make sure you are using branch 4.*: + +``` +git status +On branch 4.x +``` + +3. Navigate to OpenCV repository and prepare the build folder: + +``` +cd opencv +mkdir build +cd build +``` + +4. Set up Intel oneAPI environment variables. For default installation: + +``` +source /opt/intel/oneapi/setvars.sh +``` + +5. Run CMake * with Intel® oneAPI DPC++/C++ Compiler to configure the project: + +``` + cmake -DCMAKE_C_COMPILER=icx \ + -DCMAKE_CXX_COMPILER=icpx + -DCMAKE_CXX_FLAGS="-march=native -mavx -mfma -msse -msse2" .. + cmake --build . +``` +6. Now Make sure openCV* is compiled with Intel® oneAPI DPC++/C++ Compiler and install: + +``` +readelf -p .comment bin/opencv_annotation +String dump of section '.comment': + [ 0] GCC: (SUSE Linux) 13.3.1 20250313 [revision 4ef1d8c84faeebffeb0cc01ee22e891b41e5c4e0] + [ 56] GCC: (SUSE Linux) 12.3.0 + [ 6f] Intel(R) oneAPI DPC++/C++ Compiler 2025.1.1 (2025.1.1.20250418) +make install +``` + +Have fun... diff --git a/doc/tutorials/introduction/table_of_content_introduction.markdown b/doc/tutorials/introduction/table_of_content_introduction.markdown index 4d7b0eb94e..2f8303ad6e 100644 --- a/doc/tutorials/introduction/table_of_content_introduction.markdown +++ b/doc/tutorials/introduction/table_of_content_introduction.markdown @@ -9,6 +9,7 @@ Introduction to OpenCV {#tutorial_table_of_content_introduction} ##### Linux - @subpage tutorial_linux_install +- @subpage tutorial_oneapi_install - @subpage tutorial_linux_gdb_pretty_printer - @subpage tutorial_linux_gcc_cmake - @subpage tutorial_linux_eclipse From 49fc1dd796e59aa1866a4d806c010e3704e251ef Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Thu, 13 Nov 2025 11:14:39 +0100 Subject: [PATCH 23/32] Fix out of bounds in calibrateAndNormalizePointsPnP ipoints can have 3 or 4 points. --- modules/calib3d/src/p3p.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/calib3d/src/p3p.cpp b/modules/calib3d/src/p3p.cpp index 20449a178f..40ffa2ffcd 100644 --- a/modules/calib3d/src/p3p.cpp +++ b/modules/calib3d/src/p3p.cpp @@ -173,7 +173,7 @@ void p3p::calibrateAndNormalizePointsPnP(const Mat &opoints_, const Mat &ipoints Mat ipoints; convertPoints(ipoints_, ipoints, 2); - for (int i = 0; i < ipoints.rows; i++) { + for (int i = 0; i < 3; i++) { const double k_inv_u = ipoints.at(i, 0); const double k_inv_v = ipoints.at(i, 1); double x_norm = 1.0 / sqrt(k_inv_u*k_inv_u + k_inv_v*k_inv_v + 1); From 990d9d1a4fe0fe5a4e25d08d3119eb72f2a94317 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Thu, 13 Nov 2025 18:56:05 +0300 Subject: [PATCH 24/32] Solve PnP iterative documentation update --- modules/calib3d/include/opencv2/calib3d.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 052a7712be..1d6793db1c 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -1059,7 +1059,8 @@ More information about Perspective-n-Points is described in @ref calib3d_solvePn of the P3P problem, the last one is used to retain the best solution that minimizes the reprojection error). - With @ref SOLVEPNP_ITERATIVE method and `useExtrinsicGuess=true`, the minimum number of points is 3 (3 points are sufficient to compute a pose but there are up to 4 solutions). The initial solution should be close to the - global solution to converge. + global solution to converge. The function returns true if some solution is found. User code is responsible for + solution quality assessment. - With @ref SOLVEPNP_IPPE input points must be >= 4 and object points must be coplanar. - With @ref SOLVEPNP_IPPE_SQUARE this is a special case suitable for marker pose estimation. Number of input points must be 4. Object points must be defined in the following order: @@ -4331,7 +4332,7 @@ optimization. It is the \f$max(width,height)/\pi\f$ or the provided \f$f_x\f$, \ TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, DBL_EPSILON)); /** - @brief Finds an object pose from 3D-2D point correspondences for fisheye camera moodel. + @brief Finds an object pose from 3D-2D point correspondences for fisheye camera model. @param objectPoints Array of object points in the object coordinate space, Nx3 1-channel or 1xN/Nx1 3-channel, where N is the number of points. vector\ can also be passed here. @@ -4347,7 +4348,7 @@ optimization. It is the \f$max(width,height)/\pi\f$ or the provided \f$f_x\f$, \ vectors, respectively, and further optimizes them. @param flags Method for solving a PnP problem: see @ref calib3d_solvePnP_flags @param criteria Termination criteria for internal undistortPoints call. - The function interally undistorts points with @ref undistortPoints and call @ref cv::solvePnP, + The function internally undistorts points with @ref undistortPoints and call @ref cv::solvePnP, thus the input are very similar. More information about Perspective-n-Points is described in @ref calib3d_solvePnP for more information. */ From 91a720144bbf5da08036fdcea698866268227448 Mon Sep 17 00:00:00 2001 From: Benjamin Buch Date: Fri, 14 Nov 2025 15:44:58 +0100 Subject: [PATCH 25/32] add MSVC 19.50 / VS 2026 / VC 18 to config: fix #28013 --- cmake/OpenCVDetectCXXCompiler.cmake | 2 ++ .../OpenCVConfig.root-WIN32.cmake.in | 36 ++++++++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/cmake/OpenCVDetectCXXCompiler.cmake b/cmake/OpenCVDetectCXXCompiler.cmake index a8a6966108..e7fea567ab 100644 --- a/cmake/OpenCVDetectCXXCompiler.cmake +++ b/cmake/OpenCVDetectCXXCompiler.cmake @@ -184,6 +184,8 @@ elseif(MSVC) set(OpenCV_RUNTIME vc16) elseif(MSVC_VERSION MATCHES "^19[34][0-9]$") set(OpenCV_RUNTIME vc17) + elseif(MSVC_VERSION MATCHES "^195[0-9]$") + set(OpenCV_RUNTIME vc18) else() message(WARNING "OpenCV does not recognize MSVC_VERSION \"${MSVC_VERSION}\". Cannot set OpenCV_RUNTIME") endif() diff --git a/cmake/templates/OpenCVConfig.root-WIN32.cmake.in b/cmake/templates/OpenCVConfig.root-WIN32.cmake.in index 62e36272f3..cc9fd8745f 100644 --- a/cmake/templates/OpenCVConfig.root-WIN32.cmake.in +++ b/cmake/templates/OpenCVConfig.root-WIN32.cmake.in @@ -141,15 +141,33 @@ elseif(MSVC) set(OpenCV_RUNTIME vc17) check_one_config(has_VS2022) if(NOT has_VS2022) - set(OpenCV_RUNTIME vc16) - check_one_config(has_VS2019) - if(NOT has_VS2019) - set(OpenCV_RUNTIME vc15) # selecting previous compatible runtime version - check_one_config(has_VS2017) - if(NOT has_VS2017) - set(OpenCV_RUNTIME vc14) # selecting previous compatible runtime version - endif() - endif() + set(OpenCV_RUNTIME vc16) # selecting previous compatible runtime version + check_one_config(has_VS2019) + if(NOT has_VS2019) + set(OpenCV_RUNTIME vc15) # selecting previous compatible runtime version + check_one_config(has_VS2017) + if(NOT has_VS2017) + set(OpenCV_RUNTIME vc14) # selecting previous compatible runtime version + endif() + endif() + endif() + elseif(MSVC_VERSION MATCHES "^195[0-9]$") + set(OpenCV_RUNTIME vc18) + check_one_config(has_VS2026) + if(NOT has_VS2026) + set(OpenCV_RUNTIME vc17) # selecting previous compatible runtime version + check_one_config(has_VS2022) + if(NOT has_VS2022) + set(OpenCV_RUNTIME vc16) # selecting previous compatible runtime version + check_one_config(has_VS2019) + if(NOT has_VS2019) + set(OpenCV_RUNTIME vc15) # selecting previous compatible runtime version + check_one_config(has_VS2017) + if(NOT has_VS2017) + set(OpenCV_RUNTIME vc14) # selecting previous compatible runtime version + endif() + endif() + endif() endif() endif() elseif(MINGW) From 3dce4fa655606a326374bf857e55e2940c27025f Mon Sep 17 00:00:00 2001 From: omaraziz255 <34421528+omaraziz255@users.noreply.github.com> Date: Fri, 14 Nov 2025 17:07:30 +0200 Subject: [PATCH 26/32] Merge pull request #27943 from omaraziz255:docs/py-pip-install docs(py): add pip-based install tutorial #27943 - New tutorial: `doc/tutorials/py_tutorials/py_setup/py_pip_install/py_pip_install.markdown` with anchor `{#tutorial_py_pip_install}`. - Python setup TOC updated to include the new page near the top. - Windows/Ubuntu/Fedora pages: added **Quick start (pip)** callout - Expanded troubleshooting in the pip tutorial (env mismatch, headless GUI notes, wheel mismatches, Raspberry Pi/ARM guidance). This aligns with the issue request to make `pip install opencv-python` the default path for Python newcomers and reduce confusion from legacy content. Fixes #24360 ### 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 --- .../py_pip_install/py_pip_install.markdown | 116 ++++++++++++++++++ .../py_setup_in_fedora.markdown | 2 + .../py_setup_in_ubuntu.markdown | 2 + .../py_setup_in_windows.markdown | 12 +- .../py_table_of_contents_setup.markdown | 5 + 5 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 doc/py_tutorials/py_setup/py_pip_install/py_pip_install.markdown diff --git a/doc/py_tutorials/py_setup/py_pip_install/py_pip_install.markdown b/doc/py_tutorials/py_setup/py_pip_install/py_pip_install.markdown new file mode 100644 index 0000000000..3630b3067d --- /dev/null +++ b/doc/py_tutorials/py_setup/py_pip_install/py_pip_install.markdown @@ -0,0 +1,116 @@ +# Install OpenCV for Python with pip {#tutorial_py_pip_install} + +This quick-start shows the **recommended** way for most users to get OpenCV in Python: install from +**PyPI** with `pip`. It also explains virtual environments, platform notes, and common troubleshooting. +If you need OS‑specific alternatives (system packages or source builds), see the OS pages linked +below, but those are **not required** for typical Python use. + +@note: OpenCV team maintains **PyPI** packages only. Conda distributions and platform specific builds +are community builds and hardware vendor builds and may differ from the official one. + +## Quick start + +```bash +# 1) Create and activate a virtual environment (recommended) +python -m venv .venv +# Windows: +.venv\Scripts\activate +# Linux/macOS: +source .venv/bin/activate + +# 2) Upgrade pip tooling +python -m pip install --upgrade pip setuptools wheel + +# 3) Install OpenCV from PyPI (choose ONE) +pip install opencv-python # main package (most users) +# or +pip install opencv-contrib-python # + extra modules (contrib) +# or +pip install opencv-python-headless # no GUI/backends (servers/CI) +# or +pip install opencv-contrib-python-headless # no GUI/backends with extra modules (servers/CI) +``` + +### Tiny hello‑world + +```python +import cv2 as cv +import numpy as np + +print("OpenCV:", cv.__version__) +img = np.zeros((120, 400, 3), dtype=np.uint8) +cv.putText(img, "OpenCV OK", (10, 80), cv.FONT_HERSHEY_SIMPLEX, 2, (255,255,255), 3) +# If you installed a non-headless build, you can display a window: +# cv.imshow("hello", img); cv.waitKey(0) +# Always safe (headless or not): save to file +cv.imwrite("hello.png", img) +``` + +## Virtual environments and IDEs + +Using a virtual environment keeps project dependencies isolated. Tools that create or activate envs include: + +- `venv` (built-in) and `virtualenv` +- Conda environments +- IDEs (VS Code, PyCharm) that may **auto-create and auto-activate** an env per workspace + +If imports fail inside an IDE, verify the interpreter selected by the IDE matches the environment +where you installed OpenCV. + +## OS notes + +- **Linux:** Your default Python may be `python3`. Use `python3 -m venv .venv` and `python3 -m pip ...`. +If you cannot use a virtual env, `pip --user` installs to your home directory: `python3 -m pip install --user opencv-python`. +- **Windows:** Install Python from [python.org] or via `winget install Python.Python.3`. Make sure +**“Add python to PATH”** is enabled or use the **“Open in terminal”** from your IDE, which selects +the right interpreter automatically. +- **macOS:** Use the system `python3` or a managed one (Homebrew or Python.org). +Always prefer a virtual environment. +- **Raspberry Pi / ARM boards:** Prebuilt wheels may not exist for some Pi OS / Python combinations. +See **Troubleshooting** below. + +## Choosing a PyPI variant + +- `opencv-python`: core OpenCV modules with GUI/backends +- `opencv-contrib-python`: includes **contrib** modules in addition to the core +- `opencv-python-headless`: no GUI/backends (ideal for servers/containers/CI) +- `opencv-contrib-python-headless`: contrib + headless + +Install exactly **one** of these per environment. + +## Troubleshooting + +Please start with opencv-python project [README](https://github.com/opencv/opencv-python/blob/4.x/README.md) + +**Pip is trying to build from source** +Symptoms: very long build step, CMake errors, compiler errors. +Fixes: +- Upgrade build tooling: `python -m pip install --upgrade pip setuptools wheel` +- Ensure your Python version is supported by the chosen package. +- If you are on an uncommon platform or Python build, switch to a supported Python or try a different +variant (headless vs non‑headless). + +**“No matching distribution found” or “Unsupported wheel”** +- Confirm your Python version (e.g., `python -V`). Choose a wheel that supports that version +(manylinux/macOS/Windows wheels on PyPI target specific Python versions). +- Create a fresh virtual environment with a mainstream Python (e.g., 3.10–3.12 for now) and reinstall. + +**Raspberry Pi / ARM** +- Wheels may lag behind new Python/Pi OS releases. Try `opencv-python-headless` first. If +unavailable, consider system packages for camera/GUI pieces, or build from source following +the OS page linked below. + +**Import works in terminal but fails in IDE** +- The IDE is using a different interpreter. Select the **same** environment inside your +IDE’s interpreter settings. + +## What about system packages or building from source? + +For beginners using Python, **PyPI is recommended**. Native distribution packages and full source +builds are better suited to advanced users with platform‑specific needs. You can still find them on +the OS‑specific pages, moved under “Alternatives.” + +## See also + +- @ref tutorial_py_root +- OS pages: @ref tutorial_py_setup_in_windows, @ref tutorial_py_setup_in_ubuntu, @ref tutorial_py_setup_in_fedora diff --git a/doc/py_tutorials/py_setup/py_setup_in_fedora/py_setup_in_fedora.markdown b/doc/py_tutorials/py_setup/py_setup_in_fedora/py_setup_in_fedora.markdown index 68fa5f678a..138b9125d7 100644 --- a/doc/py_tutorials/py_setup/py_setup_in_fedora/py_setup_in_fedora.markdown +++ b/doc/py_tutorials/py_setup/py_setup_in_fedora/py_setup_in_fedora.markdown @@ -1,6 +1,8 @@ Install OpenCV-Python in Fedora {#tutorial_py_setup_in_fedora} =============================== +@note: Please prefer binaries distributed with PyPI, if possible. See @ref tutorial_py_pip_install for details. + Goals ----- diff --git a/doc/py_tutorials/py_setup/py_setup_in_ubuntu/py_setup_in_ubuntu.markdown b/doc/py_tutorials/py_setup/py_setup_in_ubuntu/py_setup_in_ubuntu.markdown index 8b99c5df92..37a6b72ff9 100644 --- a/doc/py_tutorials/py_setup/py_setup_in_ubuntu/py_setup_in_ubuntu.markdown +++ b/doc/py_tutorials/py_setup/py_setup_in_ubuntu/py_setup_in_ubuntu.markdown @@ -1,6 +1,8 @@ Install OpenCV-Python in Ubuntu {#tutorial_py_setup_in_ubuntu} =============================== +@note: Please prefer binaries distributed with PyPI, if possible. See @ref tutorial_py_pip_install for details. + Goals ----- diff --git a/doc/py_tutorials/py_setup/py_setup_in_windows/py_setup_in_windows.markdown b/doc/py_tutorials/py_setup/py_setup_in_windows/py_setup_in_windows.markdown index 198422d8bf..417ab4cd67 100644 --- a/doc/py_tutorials/py_setup/py_setup_in_windows/py_setup_in_windows.markdown +++ b/doc/py_tutorials/py_setup/py_setup_in_windows/py_setup_in_windows.markdown @@ -1,6 +1,8 @@ Install OpenCV-Python in Windows {#tutorial_py_setup_in_windows} ================================ +@note: Please prefer binaries distributed with PyPI, if possible. See @ref tutorial_py_pip_install for details. + Goals ----- @@ -15,13 +17,13 @@ Installing OpenCV from prebuilt binaries -# Below Python packages are to be downloaded and installed to their default locations. - -# Python 3.x (3.4+) or Python 2.7.x from [here](https://www.python.org/downloads/). + -# Python 3.x (3.4+) from [here](https://www.python.org/downloads/). -# Numpy package (for example, using `pip install numpy` command). -# Matplotlib (`pip install matplotlib`) (*Matplotlib is optional, but recommended since we use it a lot in our tutorials*). --# Install all packages into their default locations. Python will be installed to `C:/Python27/` in case of Python 2.7. +-# Install all packages into their default locations. Python will be installed to `C:/Python34/` in case of Python 3.4. -# After installation, open Python IDLE. Enter **import numpy** and make sure Numpy is working fine. @@ -29,11 +31,11 @@ Installing OpenCV from prebuilt binaries [SourceForge site](https://sourceforge.net/projects/opencvlibrary/files/) and double-click to extract it. --# Goto **opencv/build/python/2.7** folder. +-# Goto **opencv/build/python/3.4** folder. --# Copy **cv2.pyd** to **C:/Python27/lib/site-packages**. +-# Copy **cv2.pyd** to **C:/Python34/lib/site-packages**. --# Copy the **opencv_world.dll** file to **C:/Python27/lib/site-packages** +-# Copy the **opencv_world.dll** file to **C:/Python34/lib/site-packages** -# Open Python IDLE and type following codes in Python terminal. @code diff --git a/doc/py_tutorials/py_setup/py_table_of_contents_setup.markdown b/doc/py_tutorials/py_setup/py_table_of_contents_setup.markdown index a5ea46d750..813e899320 100644 --- a/doc/py_tutorials/py_setup/py_table_of_contents_setup.markdown +++ b/doc/py_tutorials/py_setup/py_table_of_contents_setup.markdown @@ -6,6 +6,11 @@ Introduction to OpenCV {#tutorial_py_table_of_contents_setup} Getting Started with OpenCV-Python +- @subpage tutorial_py_pip_install + + Install OpenCV for + Python with pip + - @subpage tutorial_py_setup_in_windows Set Up From f8e5bf07afb394e7ec1c16d804265d4047b6811d Mon Sep 17 00:00:00 2001 From: Kumataro Date: Sun, 16 Nov 2025 08:45:06 +0900 Subject: [PATCH 27/32] Update windows_install.markdown - add set opencv path for vc18 --- .../introduction/windows_install/windows_install.markdown | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/tutorials/introduction/windows_install/windows_install.markdown b/doc/tutorials/introduction/windows_install/windows_install.markdown index d9b5f8d78b..3498f36ce3 100644 --- a/doc/tutorials/introduction/windows_install/windows_install.markdown +++ b/doc/tutorials/introduction/windows_install/windows_install.markdown @@ -379,6 +379,9 @@ our OpenCV library that we use in our projects. Start up a command window and en setx OpenCV_DIR D:\OpenCV\build\x64\vc17 (suggested for Visual Studio 2022 - 64 bit Windows) setx OpenCV_DIR D:\OpenCV\build\x86\vc17 (suggested for Visual Studio 2022 - 32 bit Windows) + + setx OpenCV_DIR D:\OpenCV\build\x64\vc18 (suggested for Visual Studio 2026 - 64 bit Windows) + setx OpenCV_DIR D:\OpenCV\build\x86\vc18 (suggested for Visual Studio 2026 - 32 bit Windows) @endcode Here the directory is where you have your OpenCV binaries (*extracted* or *built*). You can have different platform (e.g. x64 instead of x86) or compiler type, so substitute appropriate value. From f26d7e1bf47e78eaf070b75d8d2ac0dfcb3e901a Mon Sep 17 00:00:00 2001 From: harunresit Date: Sun, 16 Nov 2025 12:45:50 +0100 Subject: [PATCH 28/32] Merge pull request #28014 from harunresit:fix-28002-clahe Added BitShift option to CLAHE #28014 Briefly, this PR is the fix of https://github.com/opencv/opencv/issues/28002 ### 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. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgproc/include/opencv2/imgproc.hpp | 12 +++ modules/imgproc/src/clahe.cpp | 89 +++++++++++++-------- 2 files changed, 69 insertions(+), 32 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index f9ea2929ad..17d7d50b58 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1067,6 +1067,18 @@ public: //!@brief Returns Size defines the number of tiles in row and column. CV_WRAP virtual Size getTilesGridSize() const = 0; + /** @brief Sets bit shift parameter for histogram bins. + + @param bitShift bit shift value (default is 0). + */ + CV_WRAP virtual void setBitShift(int bitShift) = 0; + + /** @brief Returns the bit shift parameter for histogram bins. + + @return current bit shift value. + */ + CV_WRAP virtual int getBitShift() const = 0; + CV_WRAP virtual void collectGarbage() = 0; }; diff --git a/modules/imgproc/src/clahe.cpp b/modules/imgproc/src/clahe.cpp index b787378ee3..ae41c84c79 100644 --- a/modules/imgproc/src/clahe.cpp +++ b/modules/imgproc/src/clahe.cpp @@ -118,12 +118,12 @@ namespace clahe namespace { - template + template class CLAHE_CalcLut_Body : public cv::ParallelLoopBody { public: - CLAHE_CalcLut_Body(const cv::Mat& src, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& clipLimit, const float& lutScale) : - src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), clipLimit_(clipLimit), lutScale_(lutScale) + CLAHE_CalcLut_Body(const cv::Mat& src, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& clipLimit, const float& lutScale, const int& histSize, const int& shift) : + src_(src), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), clipLimit_(clipLimit), lutScale_(lutScale), histSize_(histSize), shift_(shift) { } @@ -137,10 +137,12 @@ namespace int tilesX_; int clipLimit_; float lutScale_; + int histSize_; + int shift_; }; - template - void CLAHE_CalcLut_Body::operator ()(const cv::Range& range) const + template + void CLAHE_CalcLut_Body::operator ()(const cv::Range& range) const { T* tileLut = lut_.ptr(range.start); const size_t lut_step = lut_.step / sizeof(T); @@ -162,9 +164,9 @@ namespace // calc histogram - cv::AutoBuffer _tileHist(histSize); + cv::AutoBuffer _tileHist(histSize_); int* tileHist = _tileHist.data(); - std::fill(tileHist, tileHist + histSize, 0); + std::fill(tileHist, tileHist + histSize_, 0); int height = tileROI.height; const size_t sstep = src_.step / sizeof(T); @@ -174,13 +176,13 @@ namespace for (; x <= tileROI.width - 4; x += 4) { int t0 = ptr[x], t1 = ptr[x+1]; - tileHist[t0 >> shift]++; tileHist[t1 >> shift]++; + tileHist[t0 >> shift_]++; tileHist[t1 >> shift_]++; t0 = ptr[x+2]; t1 = ptr[x+3]; - tileHist[t0 >> shift]++; tileHist[t1 >> shift]++; + tileHist[t0 >> shift_]++; tileHist[t1 >> shift_]++; } for (; x < tileROI.width; ++x) - tileHist[ptr[x] >> shift]++; + tileHist[ptr[x] >> shift_]++; } // clip histogram @@ -189,7 +191,7 @@ namespace { // how many pixels were clipped int clipped = 0; - for (int i = 0; i < histSize; ++i) + for (int i = 0; i < histSize_; ++i) { if (tileHist[i] > clipLimit_) { @@ -199,16 +201,16 @@ namespace } // redistribute clipped pixels - int redistBatch = clipped / histSize; - int residual = clipped - redistBatch * histSize; + int redistBatch = clipped / histSize_; + int residual = clipped - redistBatch * histSize_; - for (int i = 0; i < histSize; ++i) + for (int i = 0; i < histSize_; ++i) tileHist[i] += redistBatch; if (residual != 0) { - int residualStep = MAX(histSize / residual, 1); - for (int i = 0; i < histSize && residual > 0; i += residualStep, residual--) + int residualStep = MAX(histSize_ / residual, 1); + for (int i = 0; i < histSize_ && residual > 0; i += residualStep, residual--) tileHist[i]++; } } @@ -216,7 +218,7 @@ namespace // calc Lut int sum = 0; - for (int i = 0; i < histSize; ++i) + for (int i = 0; i < histSize_; ++i) { sum += tileHist[i]; tileLut[i] = cv::saturate_cast(sum * lutScale_); @@ -224,12 +226,12 @@ namespace } } - template + template class CLAHE_Interpolation_Body : public cv::ParallelLoopBody { public: - CLAHE_Interpolation_Body(const cv::Mat& src, const cv::Mat& dst, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& tilesY) : - src_(src), dst_(dst), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), tilesY_(tilesY) + CLAHE_Interpolation_Body(const cv::Mat& src, const cv::Mat& dst, const cv::Mat& lut, const cv::Size& tileSize, const int& tilesX, const int& tilesY, const int& shift) : + src_(src), dst_(dst), lut_(lut), tileSize_(tileSize), tilesX_(tilesX), tilesY_(tilesY), shift_(shift) { buf.allocate(src.cols << 2); ind1_p = buf.data(); @@ -268,14 +270,15 @@ namespace cv::Size tileSize_; int tilesX_; int tilesY_; + int shift_; cv::AutoBuffer buf; int * ind1_p, * ind2_p; float * xa_p, * xa1_p; }; - template - void CLAHE_Interpolation_Body::operator ()(const cv::Range& range) const + template + void CLAHE_Interpolation_Body::operator ()(const cv::Range& range) const { float inv_th = 1.0f / tileSize_.height; @@ -299,7 +302,7 @@ namespace for (int x = 0; x < src_.cols; ++x) { - int srcVal = srcRow[x] >> shift; + int srcVal = srcRow[x] >> shift_; int ind1 = ind1_p[x] + srcVal; int ind2 = ind2_p[x] + srcVal; @@ -307,7 +310,7 @@ namespace float res = (lutPlane1[ind1] * xa1_p[x] + lutPlane1[ind2] * xa_p[x]) * ya1 + (lutPlane2[ind1] * xa1_p[x] + lutPlane2[ind2] * xa_p[x]) * ya; - dstRow[x] = cv::saturate_cast(res) << shift; + dstRow[x] = cv::saturate_cast(res) << shift_; } } } @@ -315,7 +318,7 @@ namespace class CLAHE_Impl CV_FINAL : public cv::CLAHE { public: - CLAHE_Impl(double clipLimit = 40.0, int tilesX = 8, int tilesY = 8); + CLAHE_Impl(double clipLimit = 40.0, int tilesX = 8, int tilesY = 8, int bitShift = 0); void apply(cv::InputArray src, cv::OutputArray dst) CV_OVERRIDE; @@ -325,12 +328,16 @@ namespace void setTilesGridSize(cv::Size tileGridSize) CV_OVERRIDE; cv::Size getTilesGridSize() const CV_OVERRIDE; + void setBitShift(int bitShift) CV_OVERRIDE; + int getBitShift() const CV_OVERRIDE; + void collectGarbage() CV_OVERRIDE; private: double clipLimit_; int tilesX_; int tilesY_; + int bitShift_; cv::Mat srcExt_; cv::Mat lut_; @@ -341,8 +348,8 @@ namespace #endif }; - CLAHE_Impl::CLAHE_Impl(double clipLimit, int tilesX, int tilesY) : - clipLimit_(clipLimit), tilesX_(tilesX), tilesY_(tilesY) + CLAHE_Impl::CLAHE_Impl(double clipLimit, int tilesX, int tilesY, int bitShift) : + clipLimit_(clipLimit), tilesX_(tilesX), tilesY_(tilesY), bitShift_(bitShift) { } @@ -356,7 +363,7 @@ namespace bool useOpenCL = cv::ocl::isOpenCLActivated() && _src.isUMat() && _src.dims()<=2 && _src.type() == CV_8UC1; #endif - int histSize = _src.type() == CV_8UC1 ? 256 : 65536; + int histSize = _src.type() == CV_8UC1 ? (256 >> bitShift_) : (65536 >> bitShift_); cv::Size tileSize; cv::_InputArray _srcForLut; @@ -411,9 +418,13 @@ namespace cv::Ptr calcLutBody; if (_src.type() == CV_8UC1) - calcLutBody = cv::makePtr >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale); + { + calcLutBody = cv::makePtr >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale, histSize, bitShift_); + } else if (_src.type() == CV_16UC1) - calcLutBody = cv::makePtr >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale); + { + calcLutBody = cv::makePtr >(srcForLut, lut_, tileSize, tilesX_, clipLimit, lutScale, histSize, bitShift_); + } else CV_Error( cv::Error::StsBadArg, "Unsupported type" ); @@ -421,9 +432,13 @@ namespace cv::Ptr interpolationBody; if (_src.type() == CV_8UC1) - interpolationBody = cv::makePtr >(src, dst, lut_, tileSize, tilesX_, tilesY_); + { + interpolationBody = cv::makePtr >(src, dst, lut_, tileSize, tilesX_, tilesY_, bitShift_); + } else if (_src.type() == CV_16UC1) - interpolationBody = cv::makePtr >(src, dst, lut_, tileSize, tilesX_, tilesY_); + { + interpolationBody = cv::makePtr >(src, dst, lut_, tileSize, tilesX_, tilesY_, bitShift_); + } cv::parallel_for_(cv::Range(0, src.rows), *interpolationBody); } @@ -449,6 +464,16 @@ namespace return cv::Size(tilesX_, tilesY_); } + void CLAHE_Impl::setBitShift(int bitShift) + { + bitShift_ = bitShift; + } + + int CLAHE_Impl::getBitShift() const + { + return bitShift_; + } + void CLAHE_Impl::collectGarbage() { srcExt_.release(); From 27d106d5749c0c7251278dcb4e183def6226b888 Mon Sep 17 00:00:00 2001 From: satyam yadav Date: Sat, 15 Nov 2025 21:41:50 +0530 Subject: [PATCH 29/32] Update torch_importer.cpp --- modules/dnn/src/torch/torch_importer.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/dnn/src/torch/torch_importer.cpp b/modules/dnn/src/torch/torch_importer.cpp index 08822102c7..7e387929ce 100644 --- a/modules/dnn/src/torch/torch_importer.cpp +++ b/modules/dnn/src/torch/torch_importer.cpp @@ -782,12 +782,16 @@ struct TorchImporter int dim = scalarParams.get("dim") - 1; // In Lua we start from 1. int pad = scalarParams.get("pad"); - std::vector paddings((dim + 1) * 2, 0); + AutoBuffer paddingsBuf((dim + 1) * 2); + int *paddings = paddingsBuf.data(); + int paddingsSize = (dim + 1) * 2; + for (int i = 0; i < paddingsSize; ++i) paddings[i] = 0; if (pad > 0) - paddings[dim * 2 + 1] = pad; // Pad after (right). + paddings[dim * 2 + 1] = pad; else - paddings[dim * 2] = -pad; // Pad before (left). - layerParams.set("paddings", DictValue::arrayInt(&paddings[0], paddings.size())); + paddings[dim * 2] = -pad; + layerParams.set("paddings", DictValue::arrayInt(paddings, paddingsSize)); + curModule->modules.push_back(newModule); } @@ -935,12 +939,14 @@ struct TorchImporter // Torch's SpatialZeroPadding works with 3- or 4-dimensional input. // So we add parameter input_dims=3 to ignore batch dimension if it will be. - std::vector paddings(6, 0); // CHW + int paddings[6] = {0, 0, 0, 0, 0, 0}; + paddings[2] = padTop; paddings[3] = padBottom; paddings[4] = padLeft; paddings[5] = padRight; - layerParams.set("paddings", DictValue::arrayInt(&paddings[0], paddings.size())); + + layerParams.set("paddings", DictValue::arrayInt(paddings, 6)); layerParams.set("input_dims", 3); if (nnName == "SpatialReflectionPadding") From 7a0b9f35b5aca74e876f275dbabdb04572232124 Mon Sep 17 00:00:00 2001 From: harunresit Date: Sun, 16 Nov 2025 21:20:33 +0100 Subject: [PATCH 30/32] Initial commit --- modules/dnn/src/torch/torch_importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/dnn/src/torch/torch_importer.cpp b/modules/dnn/src/torch/torch_importer.cpp index 08822102c7..e39e59126c 100644 --- a/modules/dnn/src/torch/torch_importer.cpp +++ b/modules/dnn/src/torch/torch_importer.cpp @@ -598,7 +598,7 @@ struct TorchImporter readTorchTable(scalarParams, tensorParams); CV_Assert(tensorParams.count("weight")); - Mat weightBlob = tensorParams["weight"].second; + const Mat& weightBlob = tensorParams["weight"].second; layerParams.blobs.push_back(weightBlob); bool bias = tensorParams.count("bias") != 0; From 8fec01d73ec79d569ce822894389d3ff4859a3f4 Mon Sep 17 00:00:00 2001 From: Anshu Date: Mon, 17 Nov 2025 13:22:11 +0530 Subject: [PATCH 31/32] Merge pull request #28017 from 0AnshuAditya0:fix-audio-buffer-calculation-27969 Optimize audio buffer duration calculation in MSMF capture. Fixes #27969 #28017 ### Description Cache the repeated calculation of `(bit_per_sample/8)*nChannels` in a local variable to avoid redundant computations in `grabFrame()` and `configureAudioFrame()` functions. ### Changes - Added `bytesPerSample` local variable in both functions - Replaced 6 repeated calculations with the cached variable - Improves performance in frequently called audio processing code Fixes #27969 --- modules/videoio/src/cap_msmf.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/videoio/src/cap_msmf.cpp b/modules/videoio/src/cap_msmf.cpp index 5c2121498d..f5c1b65663 100644 --- a/modules/videoio/src/cap_msmf.cpp +++ b/modules/videoio/src/cap_msmf.cpp @@ -1569,6 +1569,7 @@ bool CvCapture_MSMF::configureAudioFrame() { if (!audioSamples.empty() || !bufferAudioData.empty() && aEOS) { + const int bytesPerSample = (captureAudioFormat.bit_per_sample/8) * captureAudioFormat.nChannels; _ComPtr buf = NULL; std::vector audioDataInUse; BYTE* ptr = NULL; @@ -1601,20 +1602,19 @@ bool CvCapture_MSMF::configureAudioFrame() } audioSamples.clear(); - audioSamplePos += chunkLengthOfBytes/((captureAudioFormat.bit_per_sample/8)*captureAudioFormat.nChannels); - chunkLengthOfBytes = (videoStream != -1) ? (LONGLONG)((requiredAudioTime*captureAudioFormat.nSamplesPerSec*captureAudioFormat.nChannels*(captureAudioFormat.bit_per_sample)/8)/1e7) : cursize; - if ((videoStream != -1) && (chunkLengthOfBytes % ((int)(captureAudioFormat.bit_per_sample)/8* (int)captureAudioFormat.nChannels) != 0)) + audioSamplePos += chunkLengthOfBytes/bytesPerSample; + chunkLengthOfBytes = (videoStream != -1) ? (LONGLONG)((requiredAudioTime*captureAudioFormat.nSamplesPerSec*bytesPerSample)/1e7) : cursize; + if ((videoStream != -1) && (chunkLengthOfBytes % bytesPerSample != 0)) { if ( (double)audioSamplePos/captureAudioFormat.nSamplesPerSec + audioStartOffset * 1e-7 - usedVideoSampleTime * 1e-7 >= 0 ) chunkLengthOfBytes -= numberOfAdditionalAudioBytes; - numberOfAdditionalAudioBytes = ((int)(captureAudioFormat.bit_per_sample)/8* (int)captureAudioFormat.nChannels) - - chunkLengthOfBytes % ((int)(captureAudioFormat.bit_per_sample)/8* (int)captureAudioFormat.nChannels); + numberOfAdditionalAudioBytes = bytesPerSample - chunkLengthOfBytes % bytesPerSample; chunkLengthOfBytes += numberOfAdditionalAudioBytes; } if (lastFrame && !syncLastFrame || aEOS && !vEOS) { chunkLengthOfBytes = bufferAudioData.size(); - audioSamplePos += chunkLengthOfBytes/((captureAudioFormat.bit_per_sample/8)*captureAudioFormat.nChannels); + audioSamplePos += chunkLengthOfBytes/bytesPerSample; } CV_Check((double)chunkLengthOfBytes, chunkLengthOfBytes >= INT_MIN || chunkLengthOfBytes <= INT_MAX, "MSMF: The chunkLengthOfBytes is out of the allowed range"); copy(bufferAudioData.begin(), bufferAudioData.begin() + (int)chunkLengthOfBytes, std::back_inserter(audioDataInUse)); @@ -1825,7 +1825,8 @@ bool CvCapture_MSMF::grabFrame() if (audioStream != -1) { - bufferedAudioDuration = (double)(bufferAudioData.size()/((captureAudioFormat.bit_per_sample/8)*captureAudioFormat.nChannels))/captureAudioFormat.nSamplesPerSec; + const int bytesPerSample = (captureAudioFormat.bit_per_sample/8) * captureAudioFormat.nChannels; + bufferedAudioDuration = (double)(bufferAudioData.size()/bytesPerSample)/captureAudioFormat.nSamplesPerSec; audioFrame.release(); if (!aEOS) returnFlag &= grabAudioFrame(); From 6f745464885e9e660fe3dd81b6a7217ea2bcaeb8 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Mon, 17 Nov 2025 16:58:20 +0900 Subject: [PATCH 32/32] Merge pull request #27318 from Kumataro:fix27298 core: add copyAt() for ROI operation #27318 Close https://github.com/opencv/opencv/issues/27320 Close https://github.com/opencv/opencv/issues/27298 ### 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/core/include/opencv2/core/mat.hpp | 29 ++++++++++ modules/core/src/copy.cpp | 22 ++++++++ modules/core/test/test_mat.cpp | 65 +++++++++++++++++++++++ modules/imgcodecs/src/grfmt_gif.cpp | 2 +- 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 4e86be7196..e8cb786e3f 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -1237,8 +1237,13 @@ public: When the operation mask is specified, if the Mat::create call shown above reallocates the matrix, the newly allocated matrix is initialized with all zeros before copying the data. + + If (re)allocation of destination memory is not necessary (e.g. updating ROI), use copyAt() . + @param m Destination matrix. If it does not have a proper size or type before the operation, it is reallocated. + + @sa copyAt */ void copyTo( OutputArray m ) const; @@ -1250,6 +1255,30 @@ public: */ void copyTo( OutputArray m, InputArray mask ) const; + /** @brief Overwrites the existing matrix + + This method writes existing matrix data, just like copyTo(). + But if it does not have a proper size or type before the operation, an exception is thrown. + This function is helpful to update ROI in an existing matrix. + + If (re)allocation of destination memory is necessary, use copyTo() . + + @param m Destination matrix. + If it does not have a proper size or type before the operation, an exception is thrown. + + @sa copyTo + + */ + void copyAt( OutputArray m ) const; + + /** @overload + @param m Destination matrix. + If it does not have a proper size or type before the operation, an exception is thrown. + @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix + elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels. + */ + void copyAt( OutputArray m, InputArray mask ) const; + /** @brief Converts an array to another data type with optional scaling. The method converts source pixel values to the target data type. saturate_cast\<\> is applied at diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 43f477aeae..271858ad5b 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -615,6 +615,28 @@ void Mat::copyTo( OutputArray _dst, InputArray _mask ) const copymask(ptrs[0], 0, ptrs[2], 0, ptrs[1], 0, sz, &esz); } +/* dst = src */ +void Mat::copyAt( OutputArray _dst ) const +{ + CV_INSTRUMENT_REGION(); + + Mat dst = _dst.getMat(); + CV_CheckTrue( !dst.empty(), "dst must not be empty" ); + CV_CheckTypeEQ(type(), dst.type(), "Make the type of dst the same as src"); + CV_CheckEQ(size(), dst.size(), "Make the size of dst the same as src"); + copyTo(_dst); +} +void Mat::copyAt( OutputArray _dst, InputArray _mask ) const +{ + CV_INSTRUMENT_REGION(); + + Mat dst = _dst.getMat(); + CV_CheckTrue( !dst.empty(), "dst must not be empty" ); + CV_CheckTypeEQ(type(), dst.type(), "Make the type of dst the same as src"); + CV_CheckEQ(size(), dst.size(), "Make the size of dst the same as src"); + copyTo(_dst, _mask); +} + static bool can_apply_memset(const Mat &mat, const Scalar &s, int &fill_value) { diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 9ba47755b4..20f3ce6809 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -2713,4 +2713,69 @@ TEST(Mat, Recreate1DMatWithSameMeta) EXPECT_NO_THROW(m.create(dims, depth)); } +// see https://github.com/opencv/opencv/issues/27298 +TEST(Mat, copyAt_regression27298) +{ + cv::Mat src(40/*height*/, 30/*width*/, CV_8UC1, Scalar(255)); + // Normal + { + cv::Mat dst(100, 100, CV_8UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 30/*width*/, 40/*height*/)); + void* roiData = roi.data; + EXPECT_NO_THROW(src.copyTo(roi)); + EXPECT_EQ(roi.data, roiData); + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + { + cv::Mat dst(100, 100, CV_8UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 30/*width*/, 40/*height*/)); + void* roiData = roi.data; + EXPECT_NO_THROW(src.copyAt(roi)); + EXPECT_EQ(roi.data, roiData); + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + + // Empty + { + cv::Mat roi; // empty + EXPECT_NO_THROW(src.copyTo(roi)); + EXPECT_NE(roi.data, nullptr); // Allocated + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + { + cv::Mat roi; // empty + EXPECT_ANY_THROW(src.copyAt(roi)); + } + + // Different Type + { + cv::Mat dst(100, 100, CV_16UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 30/*width*/, 40/*height*/)); + void* roiData = roi.data; + EXPECT_NO_THROW(src.copyTo(roi)); + EXPECT_NE(roi.data, roiData); // Reallocated + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + { + cv::Mat dst(100, 100, CV_16UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 30/*width*/, 40/*height*/)); + EXPECT_ANY_THROW(src.copyAt(roi)); + } + + // Different Size + { + cv::Mat dst(100, 100, CV_8UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 40/*width*/, 30/*height*/)); + void* roiData = roi.data; + EXPECT_NO_THROW(src.copyTo(roi)); + EXPECT_NE(roi.data, roiData); // Reallocated + EXPECT_EQ(countNonZero(roi), roi.size().width * roi.size().height) << roi; + } + { + cv::Mat dst(100, 100, CV_8UC1, Scalar(0)); + cv::Mat roi(dst, cv::Rect(0, 0, 40/*width*/, 30/*height*/)); + EXPECT_ANY_THROW(src.copyAt(roi)); + } +} + }} // namespace diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index f9c5744466..13f467157e 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -212,7 +212,7 @@ bool GifDecoder::readData(Mat &img) { if(!restore.empty()) { Mat roi = Mat(lastImage, cv::Rect(left,top,width,height)); - restore.copyTo(roi); + restore.copyAt(roi); } return hasRead;