From 973b367da65abcddcc77e1a69ad1b50e907a7ef0 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 15 Nov 2019 18:37:56 +0300 Subject: [PATCH 1/9] python: emit bindings conversion failures on OPENCV_PYTHON_DEBUG=1 --- modules/python/src2/cv2.cpp | 47 ++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 88683c146c..b1943fdb03 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -28,6 +28,9 @@ #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include +#include "opencv2/core/utils/configuration.private.hpp" +#include "opencv2/core/utils/logger.hpp" + #include "pyopencv_generated_include.h" #include "opencv2/core/types_c.h" #include "opencv2/opencv_modules.hpp" @@ -49,6 +52,22 @@ PyObject* pyopencv_from(const T& src) { return PyOpenCV_Converter::from(src); static PyObject* opencv_error = NULL; +static bool isPythonBindingsDebugEnabled() +{ + static bool param_debug = cv::utils::getConfigurationParameterBool("OPENCV_PYTHON_DEBUG", false); + return param_debug; +} + +static void emit_failmsg(PyObject * exc, const char *msg) +{ + static bool param_debug = isPythonBindingsDebugEnabled(); + if (param_debug) + { + CV_LOG_WARNING(NULL, "Bindings conversion failed: " << msg); + } + PyErr_SetString(exc, msg); +} + static int failmsg(const char *fmt, ...) { char str[1000]; @@ -58,7 +77,20 @@ static int failmsg(const char *fmt, ...) vsnprintf(str, sizeof(str), fmt, ap); va_end(ap); - PyErr_SetString(PyExc_TypeError, str); + emit_failmsg(PyExc_TypeError, str); + return 0; +} + +static PyObject* failmsgp(const char *fmt, ...) +{ + char str[1000]; + + va_list ap; + va_start(ap, fmt); + vsnprintf(str, sizeof(str), fmt, ap); + va_end(ap); + + emit_failmsg(PyExc_TypeError, str); return 0; } @@ -153,19 +185,6 @@ typedef std::vector > vector_vector_Point3f; typedef std::vector > vector_vector_DMatch; typedef std::vector > vector_vector_KeyPoint; -static PyObject* failmsgp(const char *fmt, ...) -{ - char str[1000]; - - va_list ap; - va_start(ap, fmt); - vsnprintf(str, sizeof(str), fmt, ap); - va_end(ap); - - PyErr_SetString(PyExc_TypeError, str); - return 0; -} - class NumpyAllocator : public MatAllocator { public: From 7ec91aefc1a35d2c60caf29735153f61f349aa82 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 15 Nov 2019 17:29:51 +0300 Subject: [PATCH 2/9] python: force using of ArgInfo --- modules/dnn/misc/python/pyopencv_dnn.hpp | 10 +- modules/flann/misc/python/pyopencv_flann.hpp | 12 +- modules/ml/misc/python/pyopencv_ml.hpp | 8 +- modules/python/src2/cv2.cpp | 237 +++++++----------- modules/python/src2/gen2.py | 18 +- modules/python/src2/pycompat.hpp | 12 +- .../videoio/misc/python/pyopencv_videoio.hpp | 6 +- 7 files changed, 121 insertions(+), 182 deletions(-) diff --git a/modules/dnn/misc/python/pyopencv_dnn.hpp b/modules/dnn/misc/python/pyopencv_dnn.hpp index b1cc7c72d3..34aeacb08d 100644 --- a/modules/dnn/misc/python/pyopencv_dnn.hpp +++ b/modules/dnn/misc/python/pyopencv_dnn.hpp @@ -4,9 +4,9 @@ typedef std::vector vector_MatShape; typedef std::vector > vector_vector_MatShape; template<> -bool pyopencv_to(PyObject *o, dnn::DictValue &dv, const char *name) +bool pyopencv_to(PyObject *o, dnn::DictValue &dv, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if (!o || o == Py_None) return true; //Current state will be used else if (PyLong_Check(o)) @@ -36,12 +36,6 @@ bool pyopencv_to(PyObject *o, dnn::DictValue &dv, const char *name) return false; } -template<> -bool pyopencv_to(PyObject *o, std::vector &blobs, const char *name) //required for Layer::blobs RW -{ - return pyopencvVecConverter::to(o, blobs, ArgInfo(name, false)); -} - template PyObject* pyopencv_from(const dnn::DictValue &dv) { diff --git a/modules/flann/misc/python/pyopencv_flann.hpp b/modules/flann/misc/python/pyopencv_flann.hpp index 2f56afb398..3d97edbb59 100644 --- a/modules/flann/misc/python/pyopencv_flann.hpp +++ b/modules/flann/misc/python/pyopencv_flann.hpp @@ -15,9 +15,9 @@ PyObject* pyopencv_from(const cvflann_flann_distance_t& value) } template<> -bool pyopencv_to(PyObject *o, cv::flann::IndexParams& p, const char *name) +bool pyopencv_to(PyObject *o, cv::flann::IndexParams& p, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); bool ok = true; PyObject* key = NULL; PyObject* item = NULL; @@ -71,16 +71,16 @@ bool pyopencv_to(PyObject *o, cv::flann::IndexParams& p, const char *name) } template<> -bool pyopencv_to(PyObject* obj, cv::flann::SearchParams & value, const char * name) +bool pyopencv_to(PyObject* obj, cv::flann::SearchParams & value, const ArgInfo& info) { - return pyopencv_to(obj, value, name); + return pyopencv_to(obj, value, info); } template<> -bool pyopencv_to(PyObject *o, cvflann::flann_distance_t& dist, const char *name) +bool pyopencv_to(PyObject *o, cvflann::flann_distance_t& dist, const ArgInfo& info) { int d = (int)dist; - bool ok = pyopencv_to(o, d, name); + bool ok = pyopencv_to(o, d, info); dist = (cvflann::flann_distance_t)d; return ok; } diff --git a/modules/ml/misc/python/pyopencv_ml.hpp b/modules/ml/misc/python/pyopencv_ml.hpp index e5b072816e..564eba5fac 100644 --- a/modules/ml/misc/python/pyopencv_ml.hpp +++ b/modules/ml/misc/python/pyopencv_ml.hpp @@ -1,16 +1,16 @@ template<> -bool pyopencv_to(PyObject *obj, CvTermCriteria& dst, const char *name) +bool pyopencv_to(PyObject *obj, CvTermCriteria& dst, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj) return true; return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.max_iter, &dst.epsilon) > 0; } template<> -bool pyopencv_to(PyObject* obj, CvSlice& r, const char* name) +bool pyopencv_to(PyObject* obj, CvSlice& r, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; if(PyObject_Size(obj) == 0) diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 88683c146c..f394b22050 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -34,15 +34,31 @@ #include "pycompat.hpp" #include +class ArgInfo +{ +public: + const char * name; + bool outputarg; + // more fields may be added if necessary + + ArgInfo(const char * name_, bool outputarg_) + : name(name_) + , outputarg(outputarg_) {} + +private: + ArgInfo(const ArgInfo&); // = delete + ArgInfo& operator=(const ArgInfo&); // = delete +}; + template // TEnable is used for SFINAE checks struct PyOpenCV_Converter { - //static inline bool to(PyObject* obj, T& p, const char* name); + //static inline bool to(PyObject* obj, T& p, const ArgInfo& info); //static inline PyObject* from(const T& src); }; template static -bool pyopencv_to(PyObject* obj, T& p, const char* name = "") { return PyOpenCV_Converter::to(obj, p, name); } +bool pyopencv_to(PyObject* obj, T& p, const ArgInfo& info) { return PyOpenCV_Converter::to(obj, p, info); } template static PyObject* pyopencv_from(const T& src) { return PyOpenCV_Converter::from(src); } @@ -62,20 +78,6 @@ static int failmsg(const char *fmt, ...) return 0; } -struct ArgInfo -{ - const char * name; - bool outputarg; - // more fields may be added if necessary - - ArgInfo(const char * name_, bool outputarg_) - : name(name_) - , outputarg(outputarg_) {} - - // to match with older pyopencv_to function signature - operator const char *() const { return name; } -}; - class PyAllowThreads { public: @@ -243,7 +245,7 @@ NumpyAllocator g_numpyAllocator; enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 }; // special case, when the converter needs full ArgInfo structure -static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo info) +static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo& info) { bool allowND = true; if(!o || o == Py_None) @@ -422,14 +424,8 @@ static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo info) return true; } -template<> -bool pyopencv_to(PyObject* o, Mat& m, const char* name) -{ - return pyopencv_to(o, m, ArgInfo(name, 0)); -} - template -bool pyopencv_to(PyObject* o, Matx<_Tp, m, n>& mx, const ArgInfo info) +bool pyopencv_to(PyObject* o, Matx<_Tp, m, n>& mx, const ArgInfo& info) { Mat tmp; if (!pyopencv_to(o, tmp, info)) { @@ -440,10 +436,10 @@ bool pyopencv_to(PyObject* o, Matx<_Tp, m, n>& mx, const ArgInfo info) return true; } -template -bool pyopencv_to(PyObject* o, Matx<_Tp, m, n>& mx, const char* name) +template +bool pyopencv_to(PyObject* o, Vec<_Tp, cn>& vec, const ArgInfo& info) { - return pyopencv_to(o, mx, ArgInfo(name, 0)); + return pyopencv_to(o, (Matx<_Tp, cn, 1>&)vec, info); } template<> @@ -478,19 +474,19 @@ struct PyOpenCV_Converter< cv::Ptr > Py_RETURN_NONE; return pyopencv_from(*p); } - static bool to(PyObject *o, Ptr& p, const char *name) + static bool to(PyObject *o, Ptr& p, const ArgInfo& info) { if (!o || o == Py_None) return true; p = makePtr(); - return pyopencv_to(o, *p, name); + return pyopencv_to(o, *p, info); } }; template<> -bool pyopencv_to(PyObject* obj, void*& ptr, const char* name) +bool pyopencv_to(PyObject* obj, void*& ptr, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if (!obj || obj == Py_None) return true; @@ -512,7 +508,7 @@ struct SafeSeqItem ~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) return true; @@ -543,12 +539,6 @@ static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo info) return true; } -template<> -bool pyopencv_to(PyObject *o, Scalar& s, const char *name) -{ - return pyopencv_to(o, s, ArgInfo(name, 0)); -} - template<> PyObject* pyopencv_from(const Scalar& src) { @@ -562,9 +552,9 @@ PyObject* pyopencv_from(const bool& value) } template<> -bool pyopencv_to(PyObject* obj, bool& value, const char* name) +bool pyopencv_to(PyObject* obj, bool& value, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; int _val = PyObject_IsTrue(obj); @@ -581,9 +571,9 @@ PyObject* pyopencv_from(const size_t& value) } template<> -bool pyopencv_to(PyObject* obj, size_t& value, const char* name) +bool pyopencv_to(PyObject* obj, size_t& value, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; value = (int)PyLong_AsUnsignedLong(obj); @@ -597,9 +587,9 @@ PyObject* pyopencv_from(const int& value) } template<> -bool pyopencv_to(PyObject* obj, int& value, const char* name) +bool pyopencv_to(PyObject* obj, int& value, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; if(PyInt_Check(obj)) @@ -618,9 +608,9 @@ PyObject* pyopencv_from(const uchar& value) } template<> -bool pyopencv_to(PyObject* obj, uchar& value, const char* name) +bool pyopencv_to(PyObject* obj, uchar& value, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; int ivalue = (int)PyInt_AsLong(obj); @@ -635,9 +625,9 @@ PyObject* pyopencv_from(const double& value) } template<> -bool pyopencv_to(PyObject* obj, double& value, const char* name) +bool pyopencv_to(PyObject* obj, double& value, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; if(!!PyInt_CheckExact(obj)) @@ -654,9 +644,9 @@ PyObject* pyopencv_from(const float& value) } template<> -bool pyopencv_to(PyObject* obj, float& value, const char* name) +bool pyopencv_to(PyObject* obj, float& value, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; if(!!PyInt_CheckExact(obj)) @@ -679,9 +669,9 @@ PyObject* pyopencv_from(const String& value) } template<> -bool pyopencv_to(PyObject* obj, String &value, const char* name) +bool pyopencv_to(PyObject* obj, String &value, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; std::string str; @@ -694,9 +684,9 @@ bool pyopencv_to(PyObject* obj, String &value, const char* name) } template<> -bool pyopencv_to(PyObject* obj, Size& sz, const char* name) +bool pyopencv_to(PyObject* obj, Size& sz, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; return PyArg_ParseTuple(obj, "ii", &sz.width, &sz.height) > 0; @@ -709,9 +699,9 @@ PyObject* pyopencv_from(const Size& sz) } template<> -bool pyopencv_to(PyObject* obj, Size_& sz, const char* name) +bool pyopencv_to(PyObject* obj, Size_& sz, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; return PyArg_ParseTuple(obj, "ff", &sz.width, &sz.height) > 0; @@ -724,9 +714,9 @@ PyObject* pyopencv_from(const Size_& sz) } template<> -bool pyopencv_to(PyObject* obj, Rect& r, const char* name) +bool pyopencv_to(PyObject* obj, Rect& r, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; return PyArg_ParseTuple(obj, "iiii", &r.x, &r.y, &r.width, &r.height) > 0; @@ -739,9 +729,9 @@ PyObject* pyopencv_from(const Rect& r) } template<> -bool pyopencv_to(PyObject* obj, Rect2d& r, const char* name) +bool pyopencv_to(PyObject* obj, Rect2d& r, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; return PyArg_ParseTuple(obj, "dddd", &r.x, &r.y, &r.width, &r.height) > 0; @@ -754,16 +744,16 @@ PyObject* pyopencv_from(const Rect2d& r) } template<> -bool pyopencv_to(PyObject* obj, Range& r, const char* name) +bool pyopencv_to(PyObject* obj, Range& r, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); 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", name); + failmsg("Range value for argument '%s' is longer than 2", info.name); return false; } { @@ -772,7 +762,7 @@ bool pyopencv_to(PyObject* obj, Range& r, const char* name) if (PyInt_Check(item)) { r.start = (int)PyInt_AsLong(item); } else { - failmsg("Range.start value for argument '%s' is not integer", name); + failmsg("Range.start value for argument '%s' is not integer", info.name); break; } } @@ -782,7 +772,7 @@ bool pyopencv_to(PyObject* obj, Range& r, const char* name) if (PyInt_Check(item)) { r.end = (int)PyInt_AsLong(item); } else { - failmsg("Range.end value for argument '%s' is not integer", name); + failmsg("Range.end value for argument '%s' is not integer", info.name); break; } } @@ -803,9 +793,9 @@ PyObject* pyopencv_from(const Range& r) } template<> -bool pyopencv_to(PyObject* obj, Point& p, const char* name) +bool pyopencv_to(PyObject* obj, Point& p, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; if(PyComplex_Check(obj)) @@ -818,9 +808,9 @@ bool pyopencv_to(PyObject* obj, Point& p, const char* name) } template<> -bool pyopencv_to(PyObject* obj, Point2f& p, const char* name) +bool pyopencv_to(PyObject* obj, Point2f& p, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; if (PyComplex_Check(obj)) @@ -833,9 +823,9 @@ bool pyopencv_to(PyObject* obj, Point2f& p, const char* name) } template<> -bool pyopencv_to(PyObject* obj, Point2d& p, const char* name) +bool pyopencv_to(PyObject* obj, Point2d& p, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; if(PyComplex_Check(obj)) @@ -848,18 +838,18 @@ bool pyopencv_to(PyObject* obj, Point2d& p, const char* name) } template<> -bool pyopencv_to(PyObject* obj, Point3f& p, const char* name) +bool pyopencv_to(PyObject* obj, Point3f& p, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; return PyArg_ParseTuple(obj, "fff", &p.x, &p.y, &p.z) > 0; } template<> -bool pyopencv_to(PyObject* obj, Point3d& p, const char* name) +bool pyopencv_to(PyObject* obj, Point3d& p, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj || obj == Py_None) return true; return PyArg_ParseTuple(obj, "ddd", &p.x, &p.y, &p.z) > 0; @@ -883,122 +873,77 @@ PyObject* pyopencv_from(const Point3f& p) return Py_BuildValue("(ddd)", p.x, p.y, p.z); } -static bool pyopencv_to(PyObject* obj, Vec4d& v, ArgInfo info) +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; } -template<> -bool pyopencv_to(PyObject* obj, Vec4d& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} -static bool pyopencv_to(PyObject* obj, Vec4f& v, ArgInfo 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; } -template<> -bool pyopencv_to(PyObject* obj, Vec4f& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} -static bool pyopencv_to(PyObject* obj, Vec4i& v, ArgInfo 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; } -template<> -bool pyopencv_to(PyObject* obj, Vec4i& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} -static bool pyopencv_to(PyObject* obj, Vec3d& v, ArgInfo 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; } -template<> -bool pyopencv_to(PyObject* obj, Vec3d& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} -static bool pyopencv_to(PyObject* obj, Vec3f& v, ArgInfo 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; } -template<> -bool pyopencv_to(PyObject* obj, Vec3f& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} -static bool pyopencv_to(PyObject* obj, Vec3i& v, ArgInfo 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; } -template<> -bool pyopencv_to(PyObject* obj, Vec3i& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} -static bool pyopencv_to(PyObject* obj, Vec2d& v, ArgInfo 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; } -template<> -bool pyopencv_to(PyObject* obj, Vec2d& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} -static bool pyopencv_to(PyObject* obj, Vec2f& v, ArgInfo 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; } -template<> -bool pyopencv_to(PyObject* obj, Vec2f& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} -static bool pyopencv_to(PyObject* obj, Vec2i& v, ArgInfo 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; } -template<> -bool pyopencv_to(PyObject* obj, Vec2i& v, const char* name) -{ - return pyopencv_to(obj, v, ArgInfo(name, 0)); -} template<> PyObject* pyopencv_from(const Vec4d& v) @@ -1101,7 +1046,7 @@ template struct pyopencvVecConverter } return true; } - static bool to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info) { if(!obj || obj == Py_None) return true; @@ -1181,7 +1126,7 @@ template struct pyopencvVecConverter }; template -bool pyopencv_to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo info) +bool pyopencv_to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info) { return pyopencvVecConverter<_Tp>::to(obj, value, info); } @@ -1192,7 +1137,7 @@ PyObject* pyopencv_from(const std::vector<_Tp>& value) return pyopencvVecConverter<_Tp>::from(value); } -template static inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector<_Tp>& value, const ArgInfo info) +template static inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info) { if(!obj || obj == Py_None) return true; @@ -1236,7 +1181,7 @@ PyObject* pyopencv_from(const std::pair& src) template struct pyopencvVecConverter > { - static bool to(PyObject* obj, std::vector >& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector >& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -1249,7 +1194,7 @@ template struct pyopencvVecConverter struct pyopencvVecConverter > { - static bool to(PyObject* obj, std::vector >& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector >& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -1262,7 +1207,7 @@ template struct pyopencvVecConverter > template<> struct pyopencvVecConverter { - static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -1275,7 +1220,7 @@ template<> struct pyopencvVecConverter template<> struct pyopencvVecConverter { - static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -1288,7 +1233,7 @@ template<> struct pyopencvVecConverter template<> struct pyopencvVecConverter { - static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -1301,7 +1246,7 @@ template<> struct pyopencvVecConverter template<> struct pyopencvVecConverter { - static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -1314,7 +1259,7 @@ template<> struct pyopencvVecConverter template<> struct pyopencvVecConverter { - static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -1327,7 +1272,7 @@ template<> struct pyopencvVecConverter template<> struct pyopencvVecConverter { - static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -1338,9 +1283,9 @@ template<> struct pyopencvVecConverter }; template<> -bool pyopencv_to(PyObject *obj, TermCriteria& dst, const char *name) +bool pyopencv_to(PyObject *obj, TermCriteria& dst, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj) return true; return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.maxCount, &dst.epsilon) > 0; @@ -1353,9 +1298,9 @@ PyObject* pyopencv_from(const TermCriteria& src) } template<> -bool pyopencv_to(PyObject *obj, RotatedRect& dst, const char *name) +bool pyopencv_to(PyObject *obj, RotatedRect& dst, const ArgInfo& info) { - CV_UNUSED(name); + CV_UNUSED(info); if(!obj) return true; return PyArg_ParseTuple(obj, "(ff)(ff)f", &dst.center.x, &dst.center.y, &dst.size.width, &dst.size.height, &dst.angle) > 0; @@ -1595,7 +1540,7 @@ static PyObject *pycvCreateButton(PyObject*, PyObject *args, PyObject *kw) /////////////////////////////////////////////////////////////////////////////////////// -static int convert_to_char(PyObject *o, char *dst, const char *name = "no_name") +static int convert_to_char(PyObject *o, char *dst, const ArgInfo& info) { std::string str; if (getUnicodeString(o, str)) @@ -1604,7 +1549,7 @@ static int convert_to_char(PyObject *o, char *dst, const char *name = "no_name") return 1; } (*dst) = 0; - return failmsg("Expected single character string for argument '%s'", name); + return failmsg("Expected single character string for argument '%s'", info.name); } #ifdef __GNUC__ diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 69d03a66fd..cd1b8f677c 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -45,7 +45,7 @@ gen_template_func_body = Template("""$code_decl gen_template_mappable = Template(""" { ${mappable} _src; - if (pyopencv_to(src, _src, name)) + if (pyopencv_to(src, _src, info)) { return cv_mappable_to(_src, dst); } @@ -62,7 +62,7 @@ struct PyOpenCV_Converter< ${cname} > { return pyopencv_${name}_Instance(r); } - static bool to(PyObject* src, ${cname}& dst, const char* name) + static bool to(PyObject* src, ${cname}& dst, const ArgInfo& info) { if(!src || src == Py_None) return true; @@ -73,7 +73,7 @@ struct PyOpenCV_Converter< ${cname} > return true; } ${mappable_code} - failmsg("Expected ${cname} for argument '%s'", name); + failmsg("Expected ${cname} for argument '%s'", info.name); return false; } }; @@ -81,7 +81,7 @@ struct PyOpenCV_Converter< ${cname} > """) gen_template_map_type_cvt = Template(""" -template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name); +template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const ArgInfo& info); """) @@ -89,7 +89,7 @@ gen_template_set_prop_from_map = Template(""" if( PyMapping_HasKeyString(src, (char*)"$propname") ) { tmp = PyMapping_GetItemString(src, (char*)"$propname"); - ok = tmp && pyopencv_to(tmp, dst.$propname); + ok = tmp && pyopencv_to(tmp, dst.$propname, ArgInfo("$propname", false)); Py_DECREF(tmp); if(!ok) return false; }""") @@ -143,7 +143,7 @@ static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute"); return -1; } - return pyopencv_to(value, p->v${access}${member}) ? 0 : -1; + return pyopencv_to(value, p->v${access}${member}, ArgInfo("value", false)) ? 0 : -1; } """) @@ -161,7 +161,7 @@ static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value failmsgp("Incorrect type of object (must be '${name}' or its derivative)"); return -1; } - return pyopencv_to(value, _self_${access}${member}) ? 0 : -1; + return pyopencv_to(value, _self_${access}${member}, ArgInfo("value", false)) ? 0 : -1; } """) @@ -238,10 +238,10 @@ class ClassInfo(object): def gen_map_code(self, codegen): all_classes = codegen.classes - code = "static bool pyopencv_to(PyObject* src, %s& dst, const char* name)\n{\n PyObject* tmp;\n bool ok;\n" % (self.cname) + code = "static bool pyopencv_to(PyObject* src, %s& dst, const ArgInfo& info)\n{\n PyObject* tmp;\n bool ok;\n" % (self.cname) code += "".join([gen_template_set_prop_from_map.substitute(propname=p.name,proptype=p.tp) for p in self.props]) if self.base: - code += "\n return pyopencv_to(src, (%s&)dst, name);\n}\n" % all_classes[self.base].cname + code += "\n return pyopencv_to(src, (%s&)dst, info);\n}\n" % all_classes[self.base].cname else: code += "\n return true;\n}\n" return code diff --git a/modules/python/src2/pycompat.hpp b/modules/python/src2/pycompat.hpp index 8ba0500863..054117d625 100644 --- a/modules/python/src2/pycompat.hpp +++ b/modules/python/src2/pycompat.hpp @@ -101,13 +101,13 @@ static inline bool getUnicodeString(PyObject * obj, std::string &str) #define CV_PY_TO_CLASS(TYPE) \ template<> \ -bool pyopencv_to(PyObject* dst, TYPE& src, const char* name) \ +bool pyopencv_to(PyObject* dst, TYPE& src, const ArgInfo& info) \ { \ if (!dst || dst == Py_None) \ return true; \ Ptr ptr; \ \ - if (!pyopencv_to(dst, ptr, name)) return false; \ + if (!pyopencv_to(dst, ptr, info)) return false; \ src = *ptr; \ return true; \ } @@ -124,13 +124,13 @@ PyObject* pyopencv_from(const TYPE& src) #define CV_PY_TO_CLASS_PTR(TYPE) \ template<> \ -bool pyopencv_to(PyObject* dst, TYPE*& src, const char* name) \ +bool pyopencv_to(PyObject* dst, TYPE*& src, const ArgInfo& info) \ { \ if (!dst || dst == Py_None) \ return true; \ Ptr ptr; \ \ - if (!pyopencv_to(dst, ptr, name)) return false; \ + if (!pyopencv_to(dst, ptr, info)) return false; \ src = ptr; \ return true; \ } @@ -143,13 +143,13 @@ static PyObject* pyopencv_from(TYPE*& src) #define CV_PY_TO_ENUM(TYPE) \ template<> \ -bool pyopencv_to(PyObject* dst, TYPE& src, const char* name) \ +bool pyopencv_to(PyObject* dst, TYPE& src, const ArgInfo& info) \ { \ if (!dst || dst == Py_None) \ return true; \ int underlying = 0; \ \ - if (!pyopencv_to(dst, underlying, name)) return false; \ + if (!pyopencv_to(dst, underlying, info)) return false; \ src = static_cast(underlying); \ return true; \ } diff --git a/modules/videoio/misc/python/pyopencv_videoio.hpp b/modules/videoio/misc/python/pyopencv_videoio.hpp index 8ea62ebd73..5fa2f9e221 100644 --- a/modules/videoio/misc/python/pyopencv_videoio.hpp +++ b/modules/videoio/misc/python/pyopencv_videoio.hpp @@ -3,7 +3,7 @@ typedef std::vector vector_VideoCaptureAPIs; template<> struct pyopencvVecConverter { - static bool to(PyObject* obj, std::vector& value, const ArgInfo info) + static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) { return pyopencv_to_generic_vec(obj, value, info); } @@ -15,9 +15,9 @@ template<> struct pyopencvVecConverter }; template<> -bool pyopencv_to(PyObject *o, std::vector& apis, const char *name) +bool pyopencv_to(PyObject *o, std::vector& apis, const ArgInfo& info) { - return pyopencvVecConverter::to(o, apis, ArgInfo(name, false)); + return pyopencvVecConverter::to(o, apis, info); } #endif // HAVE_OPENCV_VIDEOIO From 2185bce4b711ce61f9929d3c50a37c974c94bbed Mon Sep 17 00:00:00 2001 From: clunietp Date: Mon, 18 Nov 2019 07:41:34 -0500 Subject: [PATCH 3/9] Fix 13577 --- modules/core/test/test_umat.cpp | 12 ++++++++++++ modules/imgproc/src/resize.cpp | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/modules/core/test/test_umat.cpp b/modules/core/test/test_umat.cpp index a4f32db712..d25916c18a 100644 --- a/modules/core/test/test_umat.cpp +++ b/modules/core/test/test_umat.cpp @@ -1383,4 +1383,16 @@ TEST(UMat, testTempObjects_Mat_issue_8693) EXPECT_EQ(0, cvtest::norm(srcUMat.getMat(ACCESS_READ), srcMat, NORM_INF)); } +TEST(UMat, resize_Mat_issue_13577) +{ + // save the current state + bool useOCL = cv::ocl::useOpenCL(); + + cv::ocl::setUseOpenCL(false); + UMat foo(10, 10, CV_32FC1); + cv::resize(foo, foo, cv::Size(), .5, .5); + + cv::ocl::setUseOpenCL(useOCL); // restore state +} + } } // namespace opencv_test::ocl diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index ab0d3fe89f..b0283e5ca4 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -3738,6 +3738,11 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat() && _src.cols() > 10 && _src.rows() > 10, ocl_resize(_src, _dst, dsize, inv_scale_x, inv_scale_y, interpolation)) + // Fake reference to source. Resolves issue 13577 in case of src == dst. + UMat srcUMat; + if (_src.isUMat()) + srcUMat = _src.getUMat(); + Mat src = _src.getMat(); _dst.create(dsize, src.type()); Mat dst = _dst.getMat(); From aff8c9bd2899ca39ab769a892ebeeed5972edf7b Mon Sep 17 00:00:00 2001 From: cudawarped Date: Mon, 18 Nov 2019 14:07:06 +0000 Subject: [PATCH 4/9] videoio: retrieve encoded frames through FFmpeg backend - backport 15290 - add extra test case --- modules/videoio/include/opencv2/videoio.hpp | 4 +- modules/videoio/src/cap_ffmpeg_api.hpp | 4 +- modules/videoio/src/cap_ffmpeg_impl.hpp | 198 +++++++++++++++++++- modules/videoio/test/test_ffmpeg.cpp | 87 +++++++++ 4 files changed, 287 insertions(+), 6 deletions(-) diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index 4228eab759..0a450042a7 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -136,7 +136,8 @@ enum VideoCaptureProperties { CAP_PROP_FPS =5, //!< Frame rate. CAP_PROP_FOURCC =6, //!< 4-character code of codec. see VideoWriter::fourcc . CAP_PROP_FRAME_COUNT =7, //!< Number of frames in the video file. - CAP_PROP_FORMAT =8, //!< Format of the %Mat objects returned by VideoCapture::retrieve(). + CAP_PROP_FORMAT =8, //!< Format of the %Mat objects (see Mat::type()) returned by VideoCapture::retrieve(). + //!< Set value -1 to fetch undecoded RAW video streams (as Mat 8UC1). CAP_PROP_MODE =9, //!< Backend-specific value indicating the current capture mode. CAP_PROP_BRIGHTNESS =10, //!< Brightness of the image (only for those cameras that support). CAP_PROP_CONTRAST =11, //!< Contrast of the image (only for cameras). @@ -173,6 +174,7 @@ enum VideoCaptureProperties { CAP_PROP_CHANNEL =43, //!< Video input or Channel Number (only for those cameras that support) CAP_PROP_AUTO_WB =44, //!< enable/ disable auto white-balance CAP_PROP_WB_TEMPERATURE=45, //!< white-balance color temperature + CAP_PROP_CODEC_PIXEL_FORMAT =46, //!< (read-only) codec's pixel format. 4-character code - see VideoWriter::fourcc . Subset of [AV_PIX_FMT_*](https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/raw.c) or -1 if unknown #ifndef CV_DOXYGEN CV__CAP_PROP_LATEST #endif diff --git a/modules/videoio/src/cap_ffmpeg_api.hpp b/modules/videoio/src/cap_ffmpeg_api.hpp index 216328e535..2366ee13a3 100644 --- a/modules/videoio/src/cap_ffmpeg_api.hpp +++ b/modules/videoio/src/cap_ffmpeg_api.hpp @@ -24,8 +24,10 @@ enum CV_FFMPEG_CAP_PROP_FPS=5, CV_FFMPEG_CAP_PROP_FOURCC=6, CV_FFMPEG_CAP_PROP_FRAME_COUNT=7, + CV_FFMPEG_CAP_PROP_FORMAT=8, CV_FFMPEG_CAP_PROP_SAR_NUM=40, - CV_FFMPEG_CAP_PROP_SAR_DEN=41 + CV_FFMPEG_CAP_PROP_SAR_DEN=41, + CV_FFMPEG_CAP_PROP_CODEC_PIXEL_FORMAT=46 }; typedef struct CvCapture_FFMPEG CvCapture_FFMPEG; diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index a1cb97fcc9..aa972b2b1c 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -528,6 +528,17 @@ struct CvCapture_FFMPEG #if USE_AV_INTERRUPT_CALLBACK AVInterruptCallbackMetadata interrupt_metadata; #endif + + bool setRaw(); + bool processRawPacket(); + bool rawMode; + bool rawModeInitialized; + AVPacket packet_filtered; +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100) + AVBSFContext* bsfc; + #else + AVBitStreamFilterContext* bsfc; +#endif }; void CvCapture_FFMPEG::init() @@ -552,6 +563,12 @@ void CvCapture_FFMPEG::init() #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) dict = NULL; #endif + + rawMode = false; + rawModeInitialized = false; + memset(&packet_filtered, 0, sizeof(packet_filtered)); + av_init_packet(&packet_filtered); + bsfc = NULL; } @@ -620,6 +637,21 @@ void CvCapture_FFMPEG::close() av_dict_free(&dict); #endif + if (packet_filtered.data) + { + _opencv_ffmpeg_av_packet_unref(&packet_filtered); + packet_filtered.data = NULL; + } + + if (bsfc) + { +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100) + av_bsf_free(&bsfc); +#else + av_bitstream_filter_close(bsfc); +#endif + } + init(); } @@ -996,6 +1028,124 @@ exit_func: return valid; } +bool CvCapture_FFMPEG::setRaw() +{ + if (!rawMode) + { + if (frame_number != 0) + { + CV_WARN("Incorrect usage: do not grab frames before .set(CAP_PROP_FORMAT, -1)"); + } + // binary stream filter creation is moved into processRawPacket() + rawMode = true; + } + return true; +} + +bool CvCapture_FFMPEG::processRawPacket() +{ + if (packet.data == NULL) // EOF + return false; + if (!rawModeInitialized) + { + rawModeInitialized = true; +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100) + AVCodecID eVideoCodec = ic->streams[video_stream]->codecpar->codec_id; +#elif LIBAVFORMAT_BUILD > 4628 + AVCodecID eVideoCodec = video_st->codec->codec_id; +#else + AVCodecID eVideoCodec = video_st->codec.codec_id; +#endif + const char* filterName = NULL; + if (eVideoCodec == CV_CODEC(CODEC_ID_H264) +#if LIBAVCODEC_VERSION_MICRO >= 100 \ + && LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(57, 24, 102) // FFmpeg 3.0 + || eVideoCodec == CV_CODEC(CODEC_ID_H265) +#elif LIBAVCODEC_VERSION_MICRO < 100 \ + && LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(55, 34, 1) // libav v10+ + || eVideoCodec == CV_CODEC(CODEC_ID_HEVC) +#endif + ) + { + // check start code prefixed mode (as defined in the Annex B H.264 / H.265 specification) + if (packet.size >= 5 + && !(packet.data[0] == 0 && packet.data[1] == 0 && packet.data[2] == 0 && packet.data[3] == 1) + && !(packet.data[0] == 0 && packet.data[1] == 0 && packet.data[2] == 1) + ) + { + filterName = eVideoCodec == CV_CODEC(CODEC_ID_H264) ? "h264_mp4toannexb" : "hevc_mp4toannexb"; + } + } + if (filterName) + { +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100) + const AVBitStreamFilter * bsf = av_bsf_get_by_name(filterName); + if (!bsf) + { + CV_WARN(cv::format("Bitstream filter is not available: %s", filterName).c_str()); + return false; + } + int err = av_bsf_alloc(bsf, &bsfc); + if (err < 0) + { + CV_WARN("Error allocating context for bitstream buffer"); + return false; + } + avcodec_parameters_copy(bsfc->par_in, ic->streams[video_stream]->codecpar); + err = av_bsf_init(bsfc); + if (err < 0) + { + CV_WARN("Error initializing bitstream buffer"); + return false; + } +#else + bsfc = av_bitstream_filter_init(filterName); + if (!bsfc) + { + CV_WARN(cv::format("Bitstream filter is not available: %s", filterName).c_str()); + return false; + } +#endif + } + } + if (bsfc) + { + if (packet_filtered.data) + { + _opencv_ffmpeg_av_packet_unref(&packet_filtered); + } + +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100) + int err = av_bsf_send_packet(bsfc, &packet); + if (err < 0) + { + CV_WARN("Packet submission for filtering failed"); + return false; + } + err = av_bsf_receive_packet(bsfc, &packet_filtered); + if (err < 0) + { + CV_WARN("Filtered packet retrieve failed"); + return false; + } +#else +#if LIBAVFORMAT_BUILD > 4628 + AVCodecContext* ctx = ic->streams[video_stream]->codec; +#else + AVCodecContext* ctx = &ic->streams[video_stream]->codec; +#endif + int err = av_bitstream_filter_filter(bsfc, ctx, NULL, &packet_filtered.data, + &packet_filtered.size, packet.data, packet.size, packet_filtered.flags & AV_PKT_FLAG_KEY); + if (err < 0) + { + CV_WARN("Packet filtering failed"); + return false; + } +#endif + return packet_filtered.data != NULL; + } + return packet.data != NULL; +} bool CvCapture_FFMPEG::grabFrame() { @@ -1047,6 +1197,12 @@ bool CvCapture_FFMPEG::grabFrame() continue; } + if (rawMode) + { + valid = processRawPacket(); + break; + } + // Decode video frame #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) avcodec_decode_video2(video_st->codec, picture, &got_picture, &packet); @@ -1067,7 +1223,6 @@ bool CvCapture_FFMPEG::grabFrame() if( picture_pts == AV_NOPTS_VALUE_ ) picture_pts = picture->pkt_pts != AV_NOPTS_VALUE_ && picture->pkt_pts != 0 ? picture->pkt_pts : picture->pkt_dts; - frame_number++; valid = true; } else @@ -1078,7 +1233,10 @@ bool CvCapture_FFMPEG::grabFrame() } } - if( valid && first_frame_number < 0 ) + if (valid) + frame_number++; + + if (!rawMode && valid && first_frame_number < 0) first_frame_number = dts_to_frame_number(picture_pts); #if USE_AV_INTERRUPT_CALLBACK @@ -1086,14 +1244,28 @@ bool CvCapture_FFMPEG::grabFrame() interrupt_metadata.timeout_after_ms = 0; #endif - // return if we have a new picture or not + // return if we have a new frame or not return valid; } bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn) { - if( !video_st || !picture->data[0] ) + if (!video_st) + return false; + + if (rawMode) + { + AVPacket& p = bsfc ? packet_filtered : packet; + *data = p.data; + *step = p.size; + *width = p.size; + *height = 1; + *cn = 1; + return p.data != NULL; + } + + if (!picture->data[0]) return false; if( img_convert_ctx == NULL || @@ -1216,6 +1388,20 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const return _opencv_ffmpeg_get_sample_aspect_ratio(ic->streams[video_stream]).num; case CV_FFMPEG_CAP_PROP_SAR_DEN: return _opencv_ffmpeg_get_sample_aspect_ratio(ic->streams[video_stream]).den; + case CV_FFMPEG_CAP_PROP_CODEC_PIXEL_FORMAT: + { +#if LIBAVFORMAT_BUILD > 4628 + AVPixelFormat pix_fmt = video_st->codec->pix_fmt; +#else + AVPixelFormat pix_fmt = video_st->codec.pix_fmt; +#endif + unsigned int fourcc_tag = avcodec_pix_fmt_to_codec_tag(pix_fmt); + return (fourcc_tag == 0) ? (double)-1 : (double)fourcc_tag; + } + case CV_FFMPEG_CAP_PROP_FORMAT: + if (rawMode) + return -1; + break; default: break; } @@ -1385,6 +1571,10 @@ bool CvCapture_FFMPEG::setProperty( int property_id, double value ) picture_pts=(int64_t)value; } break; + case CV_FFMPEG_CAP_PROP_FORMAT: + if (value == -1) + return setRaw(); + return false; default: return false; } diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 7e508e7782..6519c57751 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -218,6 +218,93 @@ TEST(Videoio_Video, ffmpeg_image) { CV_FFmpegReadImageTest test; test.safe_run() #if defined(HAVE_FFMPEG) +typedef tuple videoio_container_params_t; +typedef testing::TestWithParam< videoio_container_params_t > videoio_container; + +TEST_P(videoio_container, read) +{ + const VideoCaptureAPIs api = get<0>(GetParam()); + + //if (!videoio_registry::hasBackend(api)) + // throw SkipTestException("Backend was not found"); + + const string path = get<1>(GetParam()); + const string ext = get<2>(GetParam()); + const string ext_raw = get<3>(GetParam()); + const string codec = get<4>(GetParam()); + const string pixelFormat = get<5>(GetParam()); + const string fileName = path + "." + ext; + const string fileNameOut = tempfile(cv::format("test_container_stream.%s", ext_raw.c_str()).c_str()); + + // Write encoded video read using VideoContainer to tmp file + size_t totalBytes = 0; + { + VideoCapture container(findDataFile(fileName), api); + if (!container.isOpened()) + throw SkipTestException("Video stream is not supported"); + if (!container.set(CAP_PROP_FORMAT, -1)) // turn off video decoder (extract stream) + throw SkipTestException("Fetching of RAW video streams is not supported"); + ASSERT_EQ(-1.f, container.get(CAP_PROP_FORMAT)); // check + EXPECT_EQ(codec, fourccToString((int)container.get(CAP_PROP_FOURCC))); + EXPECT_EQ(pixelFormat, fourccToString((int)container.get(CAP_PROP_CODEC_PIXEL_FORMAT))); + + std::ofstream file(fileNameOut.c_str(), ios::out | ios::trunc | std::ios::binary); + Mat raw_data; + while (true) + { + container >> raw_data; + size_t size = raw_data.total(); + if (raw_data.empty()) + break; + ASSERT_EQ(CV_8UC1, raw_data.type()); + ASSERT_LE(raw_data.dims, 2); + ASSERT_EQ(raw_data.rows, 1); + ASSERT_EQ((size_t)raw_data.cols, raw_data.total()); + ASSERT_TRUE(raw_data.isContinuous()); + totalBytes += size; + file.write(reinterpret_cast(raw_data.data), size); + ASSERT_FALSE(file.fail()); + } + ASSERT_GE(totalBytes, (size_t)65536) << "Encoded stream is too small"; + } + + std::cout << "Checking extracted video stream: " << fileNameOut << " (size: " << totalBytes << " bytes)" << std::endl; + + // Check decoded frames read from original media are equal to frames decoded from tmp file + { + VideoCapture capReference(findDataFile(fileName), api); + ASSERT_TRUE(capReference.isOpened()); + VideoCapture capActual(fileNameOut.c_str(), api); + ASSERT_TRUE(capActual.isOpened()); + Mat reference, actual; + int nframes = 0, n_err = 0; + while (capReference.read(reference) && n_err < 3) + { + nframes++; + ASSERT_TRUE(capActual.read(actual)) << nframes; + EXPECT_EQ(0, cvtest::norm(actual, reference, NORM_INF)) << "frame=" << nframes << " err=" << ++n_err; + } + ASSERT_GT(nframes, 0); + } + + ASSERT_EQ(0, remove(fileNameOut.c_str())); +} + +const videoio_container_params_t videoio_container_params[] = +{ + videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h264", "h264", "h264", "I420"), + videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h265", "h265", "hevc", "I420"), + videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "mjpg.avi", "mjpg", "MJPG", "I420"), + //videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h264.mkv", "mkv.h264", "h264", "I420"), + //videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h265.mkv", "mkv.h265", "hevc", "I420"), + //videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h264.mp4", "mp4.avc1", "avc1", "I420"), + //videoio_container_params_t(CAP_FFMPEG, "video/big_buck_bunny", "h265.mp4", "mp4.hev1", "hev1", "I420"), +}; + +INSTANTIATE_TEST_CASE_P(/**/, videoio_container, testing::ValuesIn(videoio_container_params)); + +//========================================================================== + //////////////////////////////// Parallel VideoWriters and VideoCaptures //////////////////////////////////// class CreateVideoWriterInvoker : From 3f431a16a38be35e52b4887a3591d23030d34fc5 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 18 Nov 2019 18:56:57 +0000 Subject: [PATCH 5/9] videoio: fix ffmpeg wrapper build --- modules/videoio/src/cap_ffmpeg_impl.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index aa972b2b1c..fdc6b31b19 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -1082,7 +1082,11 @@ bool CvCapture_FFMPEG::processRawPacket() const AVBitStreamFilter * bsf = av_bsf_get_by_name(filterName); if (!bsf) { +#ifdef __OPENCV_BUILD CV_WARN(cv::format("Bitstream filter is not available: %s", filterName).c_str()); +#else + CV_WARN("Bitstream filter is not available"); +#endif return false; } int err = av_bsf_alloc(bsf, &bsfc); @@ -1102,7 +1106,11 @@ bool CvCapture_FFMPEG::processRawPacket() bsfc = av_bitstream_filter_init(filterName); if (!bsfc) { +#ifdef __OPENCV_BUILD CV_WARN(cv::format("Bitstream filter is not available: %s", filterName).c_str()); +#else + CV_WARN("Bitstream filter is not available"); +#endif return false; } #endif From d00eb451bf17a7082277da27620cbc23438e2994 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 18 Nov 2019 19:13:45 +0000 Subject: [PATCH 6/9] ffmpeg/3.4: update FFmpeg wrapper --- 3rdparty/ffmpeg/ffmpeg.cmake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/3rdparty/ffmpeg/ffmpeg.cmake b/3rdparty/ffmpeg/ffmpeg.cmake index ce73758696..81513655de 100644 --- a/3rdparty/ffmpeg/ffmpeg.cmake +++ b/3rdparty/ffmpeg/ffmpeg.cmake @@ -1,8 +1,8 @@ -# Binaries branch name: ffmpeg/3.4_20190905 -# Binaries were created for OpenCV: fafada28ebc0f2e5423a7d8ece425574ef01ff60 -ocv_update(FFMPEG_BINARIES_COMMIT "bf1730f4c4ba1996bed1fe268b52e4e942151cd6") -ocv_update(FFMPEG_FILE_HASH_BIN32 "6899624f88cb4cbb1604edc5e12b18ab") -ocv_update(FFMPEG_FILE_HASH_BIN64 "4729c052cb0c53ab56e9a0eed91559df") +# Binaries branch name: ffmpeg/3.4_20191118 +# Binaries were created for OpenCV: 3f431a16a38be35e52b4887a3591d23030d34fc5 +ocv_update(FFMPEG_BINARIES_COMMIT "548e3c997a80d65f710b9048f1d33371e3a203ac") +ocv_update(FFMPEG_FILE_HASH_BIN32 "168635a192fc2d2ae7d7d40150f0b349") +ocv_update(FFMPEG_FILE_HASH_BIN64 "cbc25f8f2954475b127c7558558c18e6") ocv_update(FFMPEG_FILE_HASH_CMAKE "3b90f67f4b429e77d3da36698cef700c") function(download_win_ffmpeg script_var) From 09d54c9f52c995486f0ee19ce57c37288cc80a78 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 19 Nov 2019 06:55:14 +0000 Subject: [PATCH 7/9] dnn: update InferenceEngine extension handling --- modules/dnn/src/op_inf_engine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index fd937657d1..64d4a19e69 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -609,7 +609,7 @@ void InfEngineBackendNet::initPlugin(InferenceEngine::CNNNetwork& net) { candidates.push_back(param_pluginPath); } - +#if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2019R3) if (device_name == "CPU" || device_name == "FPGA") { std::string suffixes[] = {"_avx2", "_sse4", ""}; @@ -632,6 +632,7 @@ void InfEngineBackendNet::initPlugin(InferenceEngine::CNNNetwork& net) #endif // _WIN32 } } +#endif bool found = false; for (size_t i = 0; i != candidates.size(); ++i) { From ec8db970f982e65850d77627dcf599a0b1ce8abd Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 19 Nov 2019 07:40:12 +0000 Subject: [PATCH 8/9] js(test): update features2d test --- modules/js/test/test_features2d.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/js/test/test_features2d.js b/modules/js/test/test_features2d.js index c8ca17ec3a..45e3d4d715 100644 --- a/modules/js/test/test_features2d.js +++ b/modules/js/test/test_features2d.js @@ -59,7 +59,7 @@ QUnit.test('Detectors', function(assert) { let akaze = new cv.AKAZE(); akaze.detect(image, kp); - assert.equal(kp.size(), 52, 'AKAZE'); + assert.equal(kp.size(), 53, 'AKAZE'); }); QUnit.test('BFMatcher', function(assert) { From e07a4880123ac98759f9d96ce1dedc567722602a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 19 Nov 2019 21:08:45 +0300 Subject: [PATCH 9/9] Merge pull request #15925 from alalek:core_test_simd_cpp_emulation core(test): extending tests with SIMD C++ emulation code (intrin_cpp.hpp) * core(test): test SIMD CPP emulation code (intrin_cpp.hpp) * core(simd): eliminate build warnings from intrin_cpp.hpp --- .../core/include/opencv2/core/hal/intrin.hpp | 14 ++++++----- .../include/opencv2/core/hal/intrin_cpp.hpp | 9 ++++++- modules/core/test/test_intrin.cpp | 17 +++++++++++++ modules/core/test/test_intrin_emulator.cpp | 24 +++++++++++++++++++ 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 modules/core/test/test_intrin_emulator.cpp diff --git a/modules/core/include/opencv2/core/hal/intrin.hpp b/modules/core/include/opencv2/core/hal/intrin.hpp index 3bdbf05f2f..427a8420a0 100644 --- a/modules/core/include/opencv2/core/hal/intrin.hpp +++ b/modules/core/include/opencv2/core/hal/intrin.hpp @@ -143,6 +143,7 @@ CV_INTRIN_DEF_TYPE_TRAITS(double, int64, uint64, double, void, void, double, 2); #ifndef CV_DOXYGEN +#ifndef CV_CPU_OPTIMIZATION_HAL_NAMESPACE #ifdef CV_CPU_DISPATCH_MODE #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE __CV_CAT(hal_, CV_CPU_DISPATCH_MODE) #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace __CV_CAT(hal_, CV_CPU_DISPATCH_MODE) { @@ -152,6 +153,7 @@ CV_INTRIN_DEF_TYPE_TRAITS(double, int64, uint64, double, void, void, double, 2); #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace hal_baseline { #define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } #endif +#endif // CV_CPU_OPTIMIZATION_HAL_NAMESPACE CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END @@ -168,29 +170,29 @@ using namespace CV_CPU_OPTIMIZATION_HAL_NAMESPACE; # undef CV_MSA #endif -#if CV_SSE2 || CV_NEON || CV_VSX || CV_MSA || CV_WASM_SIMD +#if (CV_SSE2 || CV_NEON || CV_VSX || CV_MSA || CV_WASM_SIMD) && !defined(CV_FORCE_SIMD128_CPP) #define CV__SIMD_FORWARD 128 #include "opencv2/core/hal/intrin_forward.hpp" #endif -#if CV_SSE2 +#if CV_SSE2 && !defined(CV_FORCE_SIMD128_CPP) #include "opencv2/core/hal/intrin_sse_em.hpp" #include "opencv2/core/hal/intrin_sse.hpp" -#elif CV_NEON +#elif CV_NEON && !defined(CV_FORCE_SIMD128_CPP) #include "opencv2/core/hal/intrin_neon.hpp" -#elif CV_VSX +#elif CV_VSX && !defined(CV_FORCE_SIMD128_CPP) #include "opencv2/core/hal/intrin_vsx.hpp" -#elif CV_MSA +#elif CV_MSA && !defined(CV_FORCE_SIMD128_CPP) #include "opencv2/core/hal/intrin_msa.hpp" -#elif CV_WASM_SIMD +#elif CV_WASM_SIMD && !defined(CV_FORCE_SIMD128_CPP) #include "opencv2/core/hal/intrin_wasm.hpp" #else diff --git a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp index 61ebd4b982..ed5396e49e 100644 --- a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp @@ -365,6 +365,13 @@ template struct v_reg return c; } + v_reg& operator=(const v_reg<_Tp, n> & r) + { + for( int i = 0; i < n; i++ ) + s[i] = r.s[i]; + return *this; + } + _Tp s[n]; //! @endcond }; @@ -623,7 +630,7 @@ template inline v_reg::abs_type, n> v_popcount(const v_reg<_Tp, n>& a) { v_reg::abs_type, n> b = v_reg::abs_type, n>::zero(); - for( int i = 0; i < n*sizeof(_Tp); i++ ) + for (int i = 0; i < n*(int)sizeof(_Tp); i++) b.s[i/sizeof(_Tp)] += popCountTable[v_reinterpret_as_u8(a).s[i]]; return b; } diff --git a/modules/core/test/test_intrin.cpp b/modules/core/test/test_intrin.cpp index 9bc4981cda..321fa64264 100644 --- a/modules/core/test/test_intrin.cpp +++ b/modules/core/test/test_intrin.cpp @@ -4,6 +4,19 @@ #include "test_precomp.hpp" #include "test_intrin128.simd.hpp" + +// see "test_intrin_emulator.cpp" +// see "opencv2/core/private/cv_cpu_include_simd_declarations.hpp" +#define CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY +#undef CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN +#undef CV_CPU_OPTIMIZATION_NAMESPACE_END +#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace opt_EMULATOR_CPP { +#define CV_CPU_OPTIMIZATION_NAMESPACE_END } +#include "test_intrin128.simd.hpp" +#undef CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN +#undef CV_CPU_OPTIMIZATION_NAMESPACE_END +#undef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + #include "test_intrin128.simd_declarations.hpp" #undef CV_CPU_DISPATCH_MODES_ALL @@ -22,6 +35,8 @@ namespace opencv_test { namespace hal { +#define CV_CPU_CALL_CPP_EMULATOR_(fn, args) return (opt_EMULATOR_CPP::fn args) + #define CV_CPU_CALL_BASELINE_(fn, args) CV_CPU_CALL_BASELINE(fn, args) #define DISPATCH_SIMD128(fn, cpu_opt) do { \ @@ -53,6 +68,8 @@ TEST(hal_intrin ## simd_size, float64x2_ ## cpu_opt) { DISPATCH_SIMD ## simd_siz namespace intrin128 { +DEFINE_SIMD_TESTS(128, CPP_EMULATOR) + DEFINE_SIMD_TESTS(128, BASELINE) #if defined CV_CPU_DISPATCH_COMPILE_SSE2 || defined CV_CPU_BASELINE_COMPILE_SSE2 diff --git a/modules/core/test/test_intrin_emulator.cpp b/modules/core/test/test_intrin_emulator.cpp new file mode 100644 index 0000000000..0ae3c02b86 --- /dev/null +++ b/modules/core/test/test_intrin_emulator.cpp @@ -0,0 +1,24 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +#include "test_precomp.hpp" + +// see "opencv2/core/hal/intrin.hpp" +#define CV_CPU_OPTIMIZATION_HAL_NAMESPACE hal_EMULATOR_CPP +#define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN namespace hal_EMULATOR_CPP { +#define CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END } + +// see "opencv2/core/private/cv_cpu_include_simd_declarations.hpp" +//#define CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY +#define CV_FORCE_SIMD128_CPP +#undef CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN +#undef CV_CPU_OPTIMIZATION_NAMESPACE_END +#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace opt_EMULATOR_CPP { +#define CV_CPU_OPTIMIZATION_NAMESPACE_END } +#include "test_intrin128.simd.hpp" +#undef CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN +#undef CV_CPU_OPTIMIZATION_NAMESPACE_END +#undef CV_CPU_DISPATCH_MODE +#undef CV_FORCE_SIMD128_CPP + +// tests implementation is in test_intrin_utils.hpp