Merge pull request #19344 from VadimLevin:dev/vlevin/generic-sequence-conversion

pull/19446/head
Alexander Alekhin 4 years ago
commit 62b60b11bb
  1. 35
      modules/core/include/opencv2/core/bindings_utils.hpp
  2. 349
      modules/python/src2/cv2.cpp
  3. 79
      modules/python/test/test_misc.py

@ -78,6 +78,41 @@ String testOverloadResolution(const Rect& rect)
rect.width, rect.height); rect.width, rect.height);
} }
CV_WRAP static inline
String dumpRect(const Rect& argument)
{
return format("rect: (x=%d, y=%d, w=%d, h=%d)", argument.x, argument.y,
argument.width, argument.height);
}
CV_WRAP static inline
String dumpTermCriteria(const TermCriteria& argument)
{
return format("term_criteria: (type=%d, max_count=%d, epsilon=%lf",
argument.type, argument.maxCount, argument.epsilon);
}
CV_WRAP static inline
String dumpRotatedRect(const RotatedRect& argument)
{
return format("rotated_rect: (c_x=%f, c_y=%f, w=%f, h=%f, a=%f)",
argument.center.x, argument.center.y, argument.size.width,
argument.size.height, argument.angle);
}
CV_WRAP static inline
String dumpRange(const Range& argument)
{
if (argument == Range::all())
{
return "range: all";
}
else
{
return format("range: (s=%d, e=%d)", argument.start, argument.end);
}
}
CV_WRAP static inline CV_WRAP static inline
AsyncArray testAsyncArray(InputArray argument) AsyncArray testAsyncArray(InputArray argument)
{ {

@ -404,6 +404,63 @@ void pyPopulateArgumentConversionErrors()
} }
} }
struct SafeSeqItem
{
PyObject * item;
SafeSeqItem(PyObject *obj, size_t idx) { item = PySequence_GetItem(obj, idx); }
~SafeSeqItem() { Py_XDECREF(item); }
private:
SafeSeqItem(const SafeSeqItem&); // = delete
SafeSeqItem& operator=(const SafeSeqItem&); // = delete
};
template <class T>
class RefWrapper
{
public:
RefWrapper(T& item) : item_(item) {}
T& get() CV_NOEXCEPT { return item_; }
private:
T& item_;
};
// In order to support this conversion on 3.x branch - use custom reference_wrapper
// and C-style array instead of std::array<T, N>
template <class T, std::size_t N>
bool parseSequence(PyObject* obj, RefWrapper<T> (&value)[N], const ArgInfo& info)
{
if (!obj || obj == Py_None)
{
return true;
}
if (!PySequence_Check(obj))
{
failmsg("Can't parse '%s'. Input argument doesn't provide sequence "
"protocol", info.name);
return false;
}
const std::size_t sequenceSize = PySequence_Size(obj);
if (sequenceSize != N)
{
failmsg("Can't parse '%s'. Expected sequence length %lu, got %lu",
info.name, N, sequenceSize);
return false;
}
for (std::size_t i = 0; i < N; ++i)
{
SafeSeqItem seqItem(obj, i);
if (!pyopencv_to(seqItem.item, value[i].get(), info))
{
failmsg("Can't parse '%s'. Sequence item with index %lu has a "
"wrong type", info.name, i);
return false;
}
}
return true;
}
} // namespace } // namespace
typedef std::vector<uchar> vector_uchar; typedef std::vector<uchar> vector_uchar;
@ -777,13 +834,6 @@ static PyObject* pyopencv_from(void*& ptr)
return PyLong_FromVoidPtr(ptr); return PyLong_FromVoidPtr(ptr);
} }
struct SafeSeqItem
{
PyObject * item;
SafeSeqItem(PyObject *obj, size_t idx) { item = PySequence_GetItem(obj, idx); }
~SafeSeqItem() { Py_XDECREF(item); }
};
static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo& info) static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo& info)
{ {
if(!o || o == Py_None) if(!o || o == Py_None)
@ -1108,10 +1158,9 @@ bool pyopencv_to(PyObject* obj, String &value, const ArgInfo& info)
template<> template<>
bool pyopencv_to(PyObject* obj, Size& sz, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Size& sz, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(sz.width),
if(!obj || obj == Py_None) RefWrapper<int>(sz.height)};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "ii", &sz.width, &sz.height) > 0;
} }
template<> template<>
@ -1123,10 +1172,9 @@ PyObject* pyopencv_from(const Size& sz)
template<> template<>
bool pyopencv_to(PyObject* obj, Size_<float>& sz, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Size_<float>& sz, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(sz.width),
if(!obj || obj == Py_None) RefWrapper<float>(sz.height)};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "ff", &sz.width, &sz.height) > 0;
} }
template<> template<>
@ -1138,10 +1186,10 @@ PyObject* pyopencv_from(const Size_<float>& sz)
template<> template<>
bool pyopencv_to(PyObject* obj, Rect& r, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Rect& r, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(r.x), RefWrapper<int>(r.y),
if(!obj || obj == Py_None) RefWrapper<int>(r.width),
return true; RefWrapper<int>(r.height)};
return PyArg_ParseTuple(obj, "iiii", &r.x, &r.y, &r.width, &r.height) > 0; return parseSequence(obj, values, info);
} }
template<> template<>
@ -1153,10 +1201,10 @@ PyObject* pyopencv_from(const Rect& r)
template<> template<>
bool pyopencv_to(PyObject* obj, Rect2d& r, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Rect2d& r, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {
if(!obj || obj == Py_None) RefWrapper<double>(r.x), RefWrapper<double>(r.y),
return true; RefWrapper<double>(r.width), RefWrapper<double>(r.height)};
return PyArg_ParseTuple(obj, "dddd", &r.x, &r.y, &r.width, &r.height) > 0; return parseSequence(obj, values, info);
} }
template<> template<>
@ -1168,44 +1216,17 @@ PyObject* pyopencv_from(const Rect2d& r)
template<> template<>
bool pyopencv_to(PyObject* obj, Range& r, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Range& r, const ArgInfo& info)
{ {
CV_UNUSED(info); if (!obj || obj == Py_None)
if(!obj || obj == Py_None)
return true;
while (PySequence_Check(obj))
{ {
if (2 != PySequence_Size(obj))
{
failmsg("Range value for argument '%s' is longer than 2", info.name);
return false;
}
{
SafeSeqItem item_wrap(obj, 0);
PyObject *item = item_wrap.item;
if (PyInt_Check(item)) {
r.start = (int)PyInt_AsLong(item);
} else {
failmsg("Range.start value for argument '%s' is not integer", info.name);
break;
}
}
{
SafeSeqItem item_wrap(obj, 1);
PyObject *item = item_wrap.item;
if (PyInt_Check(item)) {
r.end = (int)PyInt_AsLong(item);
} else {
failmsg("Range.end value for argument '%s' is not integer", info.name);
break;
}
}
return true; return true;
} }
if(PyObject_Size(obj) == 0) if (PyObject_Size(obj) == 0)
{ {
r = Range::all(); r = Range::all();
return true; return true;
} }
return PyArg_ParseTuple(obj, "ii", &r.start, &r.end) > 0; RefWrapper<int> values[] = {RefWrapper<int>(r.start), RefWrapper<int>(r.end)};
return parseSequence(obj, values, info);
} }
template<> template<>
@ -1217,64 +1238,42 @@ PyObject* pyopencv_from(const Range& r)
template<> template<>
bool pyopencv_to(PyObject* obj, Point& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(p.x), RefWrapper<int>(p.y)};
if(!obj || obj == Py_None) return parseSequence(obj, values, info);
return true;
if(PyComplex_Check(obj))
{
p.x = saturate_cast<int>(PyComplex_RealAsDouble(obj));
p.y = saturate_cast<int>(PyComplex_ImagAsDouble(obj));
return true;
}
return PyArg_ParseTuple(obj, "ii", &p.x, &p.y) > 0;
} }
template<> template <>
bool pyopencv_to(PyObject* obj, Point2f& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point2f& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(p.x),
if(!obj || obj == Py_None) RefWrapper<float>(p.y)};
return true; return parseSequence(obj, values, info);
if (PyComplex_Check(obj))
{
p.x = saturate_cast<float>(PyComplex_RealAsDouble(obj));
p.y = saturate_cast<float>(PyComplex_ImagAsDouble(obj));
return true;
}
return PyArg_ParseTuple(obj, "ff", &p.x, &p.y) > 0;
} }
template<> template<>
bool pyopencv_to(PyObject* obj, Point2d& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point2d& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(p.x),
if(!obj || obj == Py_None) RefWrapper<double>(p.y)};
return true; return parseSequence(obj, values, info);
if(PyComplex_Check(obj))
{
p.x = PyComplex_RealAsDouble(obj);
p.y = PyComplex_ImagAsDouble(obj);
return true;
}
return PyArg_ParseTuple(obj, "dd", &p.x, &p.y) > 0;
} }
template<> template<>
bool pyopencv_to(PyObject* obj, Point3f& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point3f& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(p.x),
if(!obj || obj == Py_None) RefWrapper<float>(p.y),
return true; RefWrapper<float>(p.z)};
return PyArg_ParseTuple(obj, "fff", &p.x, &p.y, &p.z) > 0; return parseSequence(obj, values, info);
} }
template<> template<>
bool pyopencv_to(PyObject* obj, Point3d& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point3d& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(p.x),
if(!obj || obj == Py_None) RefWrapper<double>(p.y),
return true; RefWrapper<double>(p.z)};
return PyArg_ParseTuple(obj, "ddd", &p.x, &p.y, &p.z) > 0; return parseSequence(obj, values, info);
} }
template<> template<>
@ -1297,74 +1296,66 @@ PyObject* pyopencv_from(const Point3f& p)
static bool pyopencv_to(PyObject* obj, Vec4d& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec4d& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(v[0]), RefWrapper<double>(v[1]),
if (!obj) RefWrapper<double>(v[2]), RefWrapper<double>(v[3])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "dddd", &v[0], &v[1], &v[2], &v[3]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec4f& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec4f& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(v[0]), RefWrapper<float>(v[1]),
if (!obj) RefWrapper<float>(v[2]), RefWrapper<float>(v[3])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "ffff", &v[0], &v[1], &v[2], &v[3]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec4i& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec4i& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1]),
if (!obj) RefWrapper<int>(v[2]), RefWrapper<int>(v[3])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "iiii", &v[0], &v[1], &v[2], &v[3]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec3d& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec3d& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(v[0]),
if (!obj) RefWrapper<double>(v[1]),
return true; RefWrapper<double>(v[2])};
return PyArg_ParseTuple(obj, "ddd", &v[0], &v[1], &v[2]) > 0; return parseSequence(obj, values, info);
} }
static bool pyopencv_to(PyObject* obj, Vec3f& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec3f& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(v[0]),
if (!obj) RefWrapper<float>(v[1]),
return true; RefWrapper<float>(v[2])};
return PyArg_ParseTuple(obj, "fff", &v[0], &v[1], &v[2]) > 0; return parseSequence(obj, values, info);
} }
static bool pyopencv_to(PyObject* obj, Vec3i& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec3i& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1]),
if (!obj) RefWrapper<int>(v[2])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "iii", &v[0], &v[1], &v[2]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec2d& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec2d& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(v[0]),
if (!obj) RefWrapper<double>(v[1])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "dd", &v[0], &v[1]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec2f& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec2f& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(v[0]),
if (!obj) RefWrapper<float>(v[1])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "ff", &v[0], &v[1]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec2i& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec2i& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1])};
if (!obj) return parseSequence(obj, values, info);
return true;
return PyArg_ParseTuple(obj, "ii", &v[0], &v[1]) > 0;
} }
template<> template<>
@ -1705,12 +1696,54 @@ template<> struct pyopencvVecConverter<RotatedRect>
}; };
template<> template<>
bool pyopencv_to(PyObject *obj, TermCriteria& dst, const ArgInfo& info) bool pyopencv_to(PyObject* obj, TermCriteria& dst, const ArgInfo& info)
{ {
CV_UNUSED(info); if (!obj || obj == Py_None)
if(!obj) {
return true; return true;
return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.maxCount, &dst.epsilon) > 0; }
if (!PySequence_Check(obj))
{
failmsg("Can't parse '%s' as TermCriteria."
"Input argument doesn't provide sequence protocol",
info.name);
return false;
}
const std::size_t sequenceSize = PySequence_Size(obj);
if (sequenceSize != 3) {
failmsg("Can't parse '%s' as TermCriteria. Expected sequence length 3, "
"got %lu",
info.name, sequenceSize);
return false;
}
{
const String typeItemName = format("'%s' criteria type", info.name);
const ArgInfo typeItemInfo(typeItemName.c_str(), false);
SafeSeqItem typeItem(obj, 0);
if (!pyopencv_to(typeItem.item, dst.type, typeItemInfo))
{
return false;
}
}
{
const String maxCountItemName = format("'%s' max count", info.name);
const ArgInfo maxCountItemInfo(maxCountItemName.c_str(), false);
SafeSeqItem maxCountItem(obj, 1);
if (!pyopencv_to(maxCountItem.item, dst.maxCount, maxCountItemInfo))
{
return false;
}
}
{
const String epsilonItemName = format("'%s' epsilon", info.name);
const ArgInfo epsilonItemInfo(epsilonItemName.c_str(), false);
SafeSeqItem epsilonItem(obj, 2);
if (!pyopencv_to(epsilonItem.item, dst.epsilon, epsilonItemInfo))
{
return false;
}
}
return true;
} }
template<> template<>
@ -1720,12 +1753,54 @@ PyObject* pyopencv_from(const TermCriteria& src)
} }
template<> template<>
bool pyopencv_to(PyObject *obj, RotatedRect& dst, const ArgInfo& info) bool pyopencv_to(PyObject* obj, RotatedRect& dst, const ArgInfo& info)
{ {
CV_UNUSED(info); if (!obj || obj == Py_None)
if(!obj) {
return true; return true;
return PyArg_ParseTuple(obj, "(ff)(ff)f", &dst.center.x, &dst.center.y, &dst.size.width, &dst.size.height, &dst.angle) > 0; }
if (!PySequence_Check(obj))
{
failmsg("Can't parse '%s' as RotatedRect."
"Input argument doesn't provide sequence protocol",
info.name);
return false;
}
const std::size_t sequenceSize = PySequence_Size(obj);
if (sequenceSize != 3)
{
failmsg("Can't parse '%s' as RotatedRect. Expected sequence length 3, got %lu",
info.name, sequenceSize);
return false;
}
{
const String centerItemName = format("'%s' center point", info.name);
const ArgInfo centerItemInfo(centerItemName.c_str(), false);
SafeSeqItem centerItem(obj, 0);
if (!pyopencv_to(centerItem.item, dst.center, centerItemInfo))
{
return false;
}
}
{
const String sizeItemName = format("'%s' size", info.name);
const ArgInfo sizeItemInfo(sizeItemName.c_str(), false);
SafeSeqItem sizeItem(obj, 1);
if (!pyopencv_to(sizeItem.item, dst.size, sizeItemInfo))
{
return false;
}
}
{
const String angleItemName = format("'%s' angle", info.name);
const ArgInfo angleItemInfo(angleItemName.c_str(), false);
SafeSeqItem angleItem(obj, 2);
if (!pyopencv_to(angleItem.item, dst.angle, angleItemInfo))
{
return false;
}
}
return true;
} }
template<> template<>

@ -3,6 +3,7 @@ from __future__ import print_function
import ctypes import ctypes
from functools import partial from functools import partial
from collections import namedtuple
import numpy as np import numpy as np
import cv2 as cv import cv2 as cv
@ -378,6 +379,84 @@ class Arguments(NewOpenCVTests):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)): with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpString(not_convertible) _ = cv.utils.dumpString(not_convertible)
def test_parse_to_rect_convertible(self):
Rect = namedtuple('Rect', ('x', 'y', 'w', 'h'))
try_to_convert = partial(self._try_to_convert, cv.utils.dumpRect)
for convertible in ((1, 2, 4, 5), [5, 3, 10, 20], np.array([10, 20, 23, 10]),
Rect(10, 30, 40, 55), tuple(np.array([40, 20, 24, 20])),
list(np.array([20, 40, 30, 35]))):
expected = 'rect: (x={}, y={}, w={}, h={})'.format(*convertible)
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_rect_not_convertible(self):
for not_convertible in (np.empty(shape=(4, 1)), (), [], np.array([]), (12, ),
[3, 4, 5, 10, 123], {1: 2, 3:4, 5:10, 6:30},
'1234', np.array([1, 2, 3, 4], dtype=np.float32),
np.array([[1, 2], [3, 4], [5, 6], [6, 8]]), (1, 2, 5, 1.5)):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpRect(not_convertible)
def test_parse_to_rotated_rect_convertible(self):
RotatedRect = namedtuple('RotatedRect', ('center', 'size', 'angle'))
try_to_convert = partial(self._try_to_convert, cv.utils.dumpRotatedRect)
for convertible in (((2.5, 2.5), (10., 20.), 12.5), [[1.5, 10.5], (12.5, 51.5), 10],
RotatedRect((10, 40), np.array([10.5, 20.5]), 5),
np.array([[10, 6], [50, 50], 5.5], dtype=object)):
center, size, angle = convertible
expected = 'rotated_rect: (c_x={:.6f}, c_y={:.6f}, w={:.6f},' \
' h={:.6f}, a={:.6f})'.format(center[0], center[1],
size[0], size[1], angle)
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_rotated_rect_not_convertible(self):
for not_convertible in ([], (), np.array([]), (123, (45, 34), 1), {1: 2, 3: 4}, 123,
np.array([[123, 123, 14], [1, 3], 56], dtype=object), '123'):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpRotatedRect(not_convertible)
def test_parse_to_term_criteria_convertible(self):
TermCriteria = namedtuple('TermCriteria', ('type', 'max_count', 'epsilon'))
try_to_convert = partial(self._try_to_convert, cv.utils.dumpTermCriteria)
for convertible in ((1, 10, 1e-3), [2, 30, 1e-1], np.array([10, 20, 0.5], dtype=object),
TermCriteria(0, 5, 0.1)):
expected = 'term_criteria: (type={}, max_count={}, epsilon={:.6f}'.format(*convertible)
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_term_criteria_not_convertible(self):
for not_convertible in ([], (), np.array([]), [1, 4], (10,), (1.5, 34, 0.1),
{1: 5, 3: 5, 10: 10}, '145'):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpTermCriteria(not_convertible)
def test_parse_to_range_convertible_to_all(self):
try_to_convert = partial(self._try_to_convert, cv.utils.dumpRange)
for convertible in ((), [], np.array([])):
expected = 'range: all'
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_range_convertible(self):
Range = namedtuple('Range', ('start', 'end'))
try_to_convert = partial(self._try_to_convert, cv.utils.dumpRange)
for convertible in ((10, 20), [-1, 3], np.array([10, 24]), Range(-4, 6)):
expected = 'range: (s={}, e={})'.format(*convertible)
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_range_not_convertible(self):
for not_convertible in ((1, ), [40, ], np.array([1, 4, 6]), {'a': 1, 'b': 40},
(1.5, 13.5), [3, 6.7], np.array([6.3, 2.1]), '14, 4'):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpRange(not_convertible)
class SamplesFindFile(NewOpenCVTests): class SamplesFindFile(NewOpenCVTests):

Loading…
Cancel
Save