From f59c654bfcb2c33037516b4b891450c567da0970 Mon Sep 17 00:00:00 2001
From: 4ekmah
Date: Fri, 29 May 2026 11:18:20 +0300
Subject: [PATCH] Merge pull request #29148 from 4ekmah:eccms_fixpython
Fixing python wrapper around ECCParameters to include itersPerLevel #29148
### 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
---
modules/python/test/test_eccms.py | 47 +++++++++++++++++++
.../video/include/opencv2/video/tracking.hpp | 6 +--
modules/video/src/eccms.cpp | 3 ++
3 files changed, 53 insertions(+), 3 deletions(-)
create mode 100644 modules/python/test/test_eccms.py
diff --git a/modules/python/test/test_eccms.py b/modules/python/test/test_eccms.py
new file mode 100644
index 0000000000..6a3726f7cd
--- /dev/null
+++ b/modules/python/test/test_eccms.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+'''
+ECC multiscale alignment test
+'''
+
+# Python 2/3 compatibility
+from __future__ import print_function
+
+import numpy as np
+import cv2 as cv
+import inspect
+import math
+
+from tests_common import NewOpenCVTests
+
+class eccms_test(NewOpenCVTests):
+ def test_eccms(self):
+ expected_res = np.array([
+ [1.0225, 0.0606, -28.6452],
+ [-0.0475, 1.0314, 11.819],
+ [8.21e-06, -3.65e-07, 1.0]
+ ], dtype=np.float32)
+
+ largeGray0 = self.get_sample('cv/shared/halmosh0.jpg', cv.IMREAD_GRAYSCALE)
+ largeGray1 = self.get_sample('cv/shared/halmosh2.jpg', cv.IMREAD_GRAYSCALE)
+ roiMask0 = self.get_sample('cv/shared/halmosh0mask.png', cv.IMREAD_GRAYSCALE)
+ roiMask1 = self.get_sample('cv/shared/halmosh2mask.png', cv.IMREAD_GRAYSCALE)
+
+ if largeGray0 is None or largeGray1 is None or roiMask0 is None or roiMask1 is None:
+ self.assertEqual(0, 1, 'Missing test data')
+
+ found = np.eye(3, 3, dtype=np.float32)
+ n_iters = 23
+ termination_eps = 1e-6
+ params = cv.ECCParameters()
+ params.criteria = (cv.TERM_CRITERIA_COUNT + cv.TERM_CRITERIA_EPS, n_iters, termination_eps)
+ params.motionType = cv.MOTION_HOMOGRAPHY
+ params.nlevels = 5
+ params.itersPerLevel = [5, 10, 300, 300, 1000]
+
+ _, found = cv.findTransformECCMultiScale(largeGray0,largeGray1, found, params, roiMask0, roiMask1)
+
+ self.assertLess(cv.norm(found - expected_res, cv.NORM_L1), 0.1)
+
+if __name__ == '__main__':
+ NewOpenCVTests.bootstrap()
diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp
index f62660494c..790f7442cf 100644
--- a/modules/video/include/opencv2/video/tracking.hpp
+++ b/modules/video/include/opencv2/video/tracking.hpp
@@ -444,10 +444,10 @@ Affects accuracy, especially when motionType == MOTION_TRANSLATION. (DEFAULT: IN
*/
struct CV_EXPORTS_W_SIMPLE ECCParameters
{
- CV_WRAP ECCParameters() {}
+ CV_WRAP ECCParameters();
CV_PROP_RW int motionType = MOTION_AFFINE;
- CV_PROP_RW cv::TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 50, 1e-6);
- CV_PROP_RW std::vector itersPerLevel = std::vector();
+ CV_PROP_RW cv::TermCriteria criteria;
+ CV_PROP_RW std::vector itersPerLevel;
CV_PROP_RW int gaussFiltSize = 5;
CV_PROP_RW int nlevels = 4;
CV_PROP_RW int interpolation = INTER_LINEAR;
diff --git a/modules/video/src/eccms.cpp b/modules/video/src/eccms.cpp
index 963763d5d3..97557bde89 100644
--- a/modules/video/src/eccms.cpp
+++ b/modules/video/src/eccms.cpp
@@ -9,6 +9,9 @@
\****************************************************************************************/
namespace cv {
+ECCParameters::ECCParameters() : criteria(TermCriteria::COUNT + TermCriteria::EPS, 50, 1e-6)
+{}
+
typedef std::vector MatPyramid;
template struct MotionTraits {};