}
}
+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
typedef std::vector<uchar> vector_uchar;
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)
{
if(!o || o == Py_None)
template<>
bool pyopencv_to(PyObject* obj, Size& sz, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- return true;
- return PyArg_ParseTuple(obj, "ii", &sz.width, &sz.height) > 0;
+ RefWrapper<int> values[] = {RefWrapper<int>(sz.width),
+ RefWrapper<int>(sz.height)};
+ return parseSequence(obj, values, info);
}
template<>
template<>
bool pyopencv_to(PyObject* obj, Size_<float>& sz, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- return true;
- return PyArg_ParseTuple(obj, "ff", &sz.width, &sz.height) > 0;
+ RefWrapper<float> values[] = {RefWrapper<float>(sz.width),
+ RefWrapper<float>(sz.height)};
+ return parseSequence(obj, values, info);
}
template<>
}
template<>
+bool pyopencv_to(PyObject* obj, Rect& r, const ArgInfo& info)
+{
+ RefWrapper<int> values[] = {RefWrapper<int>(r.x), RefWrapper<int>(r.y),
+ RefWrapper<int>(r.width),
+ RefWrapper<int>(r.height)};
+ return parseSequence(obj, values, info);
+}
+
+template<>
PyObject* pyopencv_from(const Rect& r)
{
return Py_BuildValue("(iiii)", r.x, r.y, r.width, r.height);
template<>
bool pyopencv_to(PyObject* obj, Rect2d& r, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- return true;
- return PyArg_ParseTuple(obj, "dddd", &r.x, &r.y, &r.width, &r.height) > 0;
+ RefWrapper<double> values[] = {
+ RefWrapper<double>(r.x), RefWrapper<double>(r.y),
+ RefWrapper<double>(r.width), RefWrapper<double>(r.height)};
+ return parseSequence(obj, values, info);
}
template<>
template<>
bool pyopencv_to(PyObject* obj, Range& r, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- return true;
- while (PySequence_Check(obj))
+ if (!obj || obj == Py_None)
{
- 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;
}
- if(PyObject_Size(obj) == 0)
+ if (PyObject_Size(obj) == 0)
{
r = Range::all();
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<>
bool pyopencv_to(PyObject* obj, Point& p, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- 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;
+ RefWrapper<int> values[] = {RefWrapper<int>(p.x), RefWrapper<int>(p.y)};
+ return parseSequence(obj, values, info);
}
-template<>
+template <>
bool pyopencv_to(PyObject* obj, Point2f& p, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- return true;
- 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;
+ RefWrapper<float> values[] = {RefWrapper<float>(p.x),
+ RefWrapper<float>(p.y)};
+ return parseSequence(obj, values, info);
}
template<>
bool pyopencv_to(PyObject* obj, Point2d& p, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- return true;
- 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;
+ RefWrapper<double> values[] = {RefWrapper<double>(p.x),
+ RefWrapper<double>(p.y)};
+ return parseSequence(obj, values, info);
}
template<>
bool pyopencv_to(PyObject* obj, Point3f& p, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- return true;
- return PyArg_ParseTuple(obj, "fff", &p.x, &p.y, &p.z) > 0;
+ RefWrapper<float> values[] = {RefWrapper<float>(p.x),
+ RefWrapper<float>(p.y),
+ RefWrapper<float>(p.z)};
+ return parseSequence(obj, values, info);
}
template<>
bool pyopencv_to(PyObject* obj, Point3d& p, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
- return true;
- return PyArg_ParseTuple(obj, "ddd", &p.x, &p.y, &p.z) > 0;
+ RefWrapper<double> values[] = {RefWrapper<double>(p.x),
+ RefWrapper<double>(p.y),
+ RefWrapper<double>(p.z)};
+ return parseSequence(obj, values, info);
}
template<>
static bool pyopencv_to(PyObject* obj, Vec4d& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "dddd", &v[0], &v[1], &v[2], &v[3]) > 0;
+ RefWrapper<double> values[] = {RefWrapper<double>(v[0]), RefWrapper<double>(v[1]),
+ RefWrapper<double>(v[2]), RefWrapper<double>(v[3])};
+ return parseSequence(obj, values, info);
}
static bool pyopencv_to(PyObject* obj, Vec4f& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "ffff", &v[0], &v[1], &v[2], &v[3]) > 0;
+ RefWrapper<float> values[] = {RefWrapper<float>(v[0]), RefWrapper<float>(v[1]),
+ RefWrapper<float>(v[2]), RefWrapper<float>(v[3])};
+ return parseSequence(obj, values, info);
}
static bool pyopencv_to(PyObject* obj, Vec4i& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "iiii", &v[0], &v[1], &v[2], &v[3]) > 0;
+ RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1]),
+ RefWrapper<int>(v[2]), RefWrapper<int>(v[3])};
+ return parseSequence(obj, values, info);
}
static bool pyopencv_to(PyObject* obj, Vec3d& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "ddd", &v[0], &v[1], &v[2]) > 0;
+ RefWrapper<double> values[] = {RefWrapper<double>(v[0]),
+ RefWrapper<double>(v[1]),
+ RefWrapper<double>(v[2])};
+ return parseSequence(obj, values, info);
}
static bool pyopencv_to(PyObject* obj, Vec3f& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "fff", &v[0], &v[1], &v[2]) > 0;
+ RefWrapper<float> values[] = {RefWrapper<float>(v[0]),
+ RefWrapper<float>(v[1]),
+ RefWrapper<float>(v[2])};
+ return parseSequence(obj, values, info);
}
static bool pyopencv_to(PyObject* obj, Vec3i& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "iii", &v[0], &v[1], &v[2]) > 0;
+ RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1]),
+ RefWrapper<int>(v[2])};
+ return parseSequence(obj, values, info);
}
static bool pyopencv_to(PyObject* obj, Vec2d& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "dd", &v[0], &v[1]) > 0;
+ RefWrapper<double> values[] = {RefWrapper<double>(v[0]),
+ RefWrapper<double>(v[1])};
+ return parseSequence(obj, values, info);
}
static bool pyopencv_to(PyObject* obj, Vec2f& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "ff", &v[0], &v[1]) > 0;
+ RefWrapper<float> values[] = {RefWrapper<float>(v[0]),
+ RefWrapper<float>(v[1])};
+ return parseSequence(obj, values, info);
}
static bool pyopencv_to(PyObject* obj, Vec2i& v, ArgInfo& info)
{
- CV_UNUSED(info);
- if (!obj)
- return true;
- return PyArg_ParseTuple(obj, "ii", &v[0], &v[1]) > 0;
+ RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1])};
+ return parseSequence(obj, values, info);
}
template<>
};
template<>
-bool pyopencv_to(PyObject* obj, Rect& r, const ArgInfo& info)
+bool pyopencv_to(PyObject* obj, TermCriteria& dst, const ArgInfo& info)
{
- CV_UNUSED(info);
- if(!obj || obj == Py_None)
+ if (!obj || obj == Py_None)
+ {
return true;
-
- if (PyTuple_Check(obj))
- return PyArg_ParseTuple(obj, "iiii", &r.x, &r.y, &r.width, &r.height) > 0;
- else
+ }
+ 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;
+ }
{
- std::vector<int> value(4);
- if (!pyopencvVecConverter<int>::to(obj, value, info))
+ 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;
}
- if (value.size() != 4)
+ }
+ {
+ 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))
{
- failmsg("Expected 4 values for '%s', got %d", info.name, (int)value.size());
return false;
}
- r = Rect(value[0], value[1], value[2], value[3]);
- return true;
}
-
-}
-
-template<>
-bool pyopencv_to(PyObject *obj, TermCriteria& dst, const ArgInfo& info)
-{
- CV_UNUSED(info);
- if(!obj)
- return true;
- return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.maxCount, &dst.epsilon) > 0;
+ {
+ 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<>
-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)
+ if (!obj || obj == Py_None)
+ {
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<>
import ctypes
from functools import partial
+from collections import namedtuple
import numpy as np
import cv2 as cv
with self.assertRaises((TypeError), msg=get_no_exception_msg(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):