Merge remote-tracking branch 'upstream/3.4' into merge-3.4
[platform/upstream/opencv.git] / modules / python / src2 / cv2.cpp
index 1515240..1e7bfd6 100644 (file)
@@ -407,6 +407,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
 
 typedef std::vector<uchar> vector_uchar;
@@ -781,13 +838,6 @@ static PyObject* pyopencv_from(void*& 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)
 {
     if(!o || o == Py_None)
@@ -1138,10 +1188,9 @@ bool pyopencv_to(PyObject* obj, String &value, const ArgInfo& info)
 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<>
@@ -1153,10 +1202,9 @@ PyObject* pyopencv_from(const Size& sz)
 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<>
@@ -1166,6 +1214,15 @@ PyObject* pyopencv_from(const Size_<float>& sz)
 }
 
 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);
@@ -1174,10 +1231,10 @@ PyObject* pyopencv_from(const Rect& r)
 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<>
@@ -1189,44 +1246,17 @@ PyObject* pyopencv_from(const Rect2d& r)
 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<>
@@ -1238,64 +1268,42 @@ PyObject* pyopencv_from(const Range& r)
 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<>
@@ -1318,74 +1326,66 @@ PyObject* pyopencv_from(const Point3f& p)
 
 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<>
@@ -1766,39 +1766,54 @@ template<> struct pyopencvVecConverter<RotatedRect>
 };
 
 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<>
@@ -1808,12 +1823,54 @@ PyObject* pyopencv_from(const TermCriteria& src)
 }
 
 template<>
-bool pyopencv_to(PyObject *obj, RotatedRect& dst, const ArgInfo& info)
+bool pyopencv_to(PyObjectobj, 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<>