mirror of
https://github.com/opencv/opencv.git
synced 2025-08-02 03:06:29 +08:00
Merge pull request #20618 from VadimLevin:dev/vlevin/fix-vector-conversion
This commit is contained in:
commit
6625810d2a
@ -122,6 +122,53 @@ String testReservedKeywordConversion(int positional_argument, int lambda = 2, in
|
|||||||
return format("arg=%d, lambda=%d, from=%d", positional_argument, lambda, from);
|
return format("arg=%d, lambda=%d, from=%d", positional_argument, lambda, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CV_EXPORTS_W String dumpVectorOfInt(const std::vector<int>& vec);
|
||||||
|
|
||||||
|
CV_EXPORTS_W String dumpVectorOfDouble(const std::vector<double>& vec);
|
||||||
|
|
||||||
|
CV_EXPORTS_W String dumpVectorOfRect(const std::vector<Rect>& vec);
|
||||||
|
|
||||||
|
CV_WRAP static inline
|
||||||
|
void generateVectorOfRect(size_t len, CV_OUT std::vector<Rect>& vec)
|
||||||
|
{
|
||||||
|
vec.resize(len);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
RNG rng(12345);
|
||||||
|
Mat tmp(static_cast<int>(len), 1, CV_32SC4);
|
||||||
|
rng.fill(tmp, RNG::UNIFORM, 10, 20);
|
||||||
|
tmp.copyTo(vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CV_WRAP static inline
|
||||||
|
void generateVectorOfInt(size_t len, CV_OUT std::vector<int>& vec)
|
||||||
|
{
|
||||||
|
vec.resize(len);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
RNG rng(554433);
|
||||||
|
Mat tmp(static_cast<int>(len), 1, CV_32SC1);
|
||||||
|
rng.fill(tmp, RNG::UNIFORM, -10, 10);
|
||||||
|
tmp.copyTo(vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CV_WRAP static inline
|
||||||
|
void generateVectorOfMat(size_t len, int rows, int cols, int dtype, CV_OUT std::vector<Mat>& vec)
|
||||||
|
{
|
||||||
|
vec.resize(len);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
RNG rng(65431);
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
vec[i].create(rows, cols, dtype);
|
||||||
|
rng.fill(vec[i], RNG::UNIFORM, 0, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CV_WRAP static inline
|
CV_WRAP static inline
|
||||||
void testRaiseGeneralException()
|
void testRaiseGeneralException()
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "precomp.hpp"
|
#include "precomp.hpp"
|
||||||
#include "opencv2/core/bindings_utils.hpp"
|
#include "opencv2/core/bindings_utils.hpp"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
namespace cv { namespace utils {
|
namespace cv { namespace utils {
|
||||||
|
|
||||||
@ -208,4 +209,50 @@ CV_EXPORTS_W String dumpInputOutputArrayOfArrays(InputOutputArrayOfArrays argume
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline std::ostream& operator<<(std::ostream& os, const cv::Rect& rect)
|
||||||
|
{
|
||||||
|
return os << "[x=" << rect.x << ", y=" << rect.y << ", w=" << rect.width << ", h=" << rect.height << ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class Formatter>
|
||||||
|
static inline String dumpVector(const std::vector<T>& vec, Formatter format)
|
||||||
|
{
|
||||||
|
std::ostringstream oss("[", std::ios::ate);
|
||||||
|
if (!vec.empty())
|
||||||
|
{
|
||||||
|
oss << format << vec[0];
|
||||||
|
for (std::size_t i = 1; i < vec.size(); ++i)
|
||||||
|
{
|
||||||
|
oss << ", " << format << vec[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oss << "]";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::ostream& noFormat(std::ostream& os)
|
||||||
|
{
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::ostream& floatFormat(std::ostream& os)
|
||||||
|
{
|
||||||
|
return os << std::fixed << std::setprecision(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
String dumpVectorOfInt(const std::vector<int>& vec)
|
||||||
|
{
|
||||||
|
return dumpVector(vec, &noFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
String dumpVectorOfDouble(const std::vector<double>& vec)
|
||||||
|
{
|
||||||
|
return dumpVector(vec, &floatFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
String dumpVectorOfRect(const std::vector<Rect>& vec)
|
||||||
|
{
|
||||||
|
return dumpVector(vec, &noFormat);
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
@ -493,6 +493,33 @@ bool parseSequence(PyObject* obj, RefWrapper<T> (&value)[N], const ArgInfo& info
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace traits {
|
||||||
|
template <bool Value>
|
||||||
|
struct BooleanConstant
|
||||||
|
{
|
||||||
|
static const bool value = Value;
|
||||||
|
typedef BooleanConstant<Value> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef BooleanConstant<true> TrueType;
|
||||||
|
typedef BooleanConstant<false> FalseType;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct VoidType {
|
||||||
|
typedef void type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class DType = void>
|
||||||
|
struct IsRepresentableAsMatDataType : FalseType
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct IsRepresentableAsMatDataType<T, typename VoidType<typename DataType<T>::channel_type>::type> : TrueType
|
||||||
|
{
|
||||||
|
};
|
||||||
|
} // namespace traits
|
||||||
|
|
||||||
typedef std::vector<uchar> vector_uchar;
|
typedef std::vector<uchar> vector_uchar;
|
||||||
typedef std::vector<char> vector_char;
|
typedef std::vector<char> vector_char;
|
||||||
typedef std::vector<int> vector_int;
|
typedef std::vector<int> vector_int;
|
||||||
@ -1042,6 +1069,30 @@ bool pyopencv_to(PyObject* obj, uchar& value, const ArgInfo& info)
|
|||||||
return ivalue != -1 || !PyErr_Occurred();
|
return ivalue != -1 || !PyErr_Occurred();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool pyopencv_to(PyObject* obj, char& value, const ArgInfo& info)
|
||||||
|
{
|
||||||
|
if (!obj || obj == Py_None)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isBool(obj))
|
||||||
|
{
|
||||||
|
failmsg("Argument '%s' must be an integer, not bool", info.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (PyArray_IsIntegerScalar(obj))
|
||||||
|
{
|
||||||
|
value = saturate_cast<char>(PyArray_PyIntAsInt(obj));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
failmsg("Argument '%s' is required to be an integer", info.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !CV_HAS_CONVERSION_ERROR(value);
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
PyObject* pyopencv_from(const double& value)
|
PyObject* pyopencv_from(const double& value)
|
||||||
{
|
{
|
||||||
@ -1454,277 +1505,12 @@ PyObject* pyopencv_from(const Point3d& p)
|
|||||||
return Py_BuildValue("(ddd)", p.x, p.y, p.z);
|
return Py_BuildValue("(ddd)", p.x, p.y, p.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename _Tp> struct pyopencvVecConverter
|
|
||||||
{
|
|
||||||
typedef typename DataType<_Tp>::channel_type _Cp;
|
|
||||||
static inline bool copyOneItem(PyObject *obj, size_t start, int channels, _Cp * data)
|
|
||||||
{
|
|
||||||
for(size_t j = 0; (int)j < channels; j++ )
|
|
||||||
{
|
|
||||||
SafeSeqItem sub_item_wrap(obj, start + j);
|
|
||||||
PyObject* item_ij = sub_item_wrap.item;
|
|
||||||
if( PyInt_Check(item_ij))
|
|
||||||
{
|
|
||||||
int v = (int)PyInt_AsLong(item_ij);
|
|
||||||
if( v == -1 && PyErr_Occurred() )
|
|
||||||
return false;
|
|
||||||
data[j] = saturate_cast<_Cp>(v);
|
|
||||||
}
|
|
||||||
else if( PyLong_Check(item_ij))
|
|
||||||
{
|
|
||||||
int v = (int)PyLong_AsLong(item_ij);
|
|
||||||
if( v == -1 && PyErr_Occurred() )
|
|
||||||
return false;
|
|
||||||
data[j] = saturate_cast<_Cp>(v);
|
|
||||||
}
|
|
||||||
else if( PyFloat_Check(item_ij))
|
|
||||||
{
|
|
||||||
double v = PyFloat_AsDouble(item_ij);
|
|
||||||
if( PyErr_Occurred() )
|
|
||||||
return false;
|
|
||||||
data[j] = saturate_cast<_Cp>(v);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static bool to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
if(!obj || obj == Py_None)
|
|
||||||
return true;
|
|
||||||
if (PyArray_Check(obj))
|
|
||||||
{
|
|
||||||
Mat m;
|
|
||||||
pyopencv_to(obj, m, info);
|
|
||||||
m.copyTo(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (PySequence_Check(obj))
|
|
||||||
{
|
|
||||||
const int type = traits::Type<_Tp>::value;
|
|
||||||
const int depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type);
|
|
||||||
size_t i, n = PySequence_Size(obj);
|
|
||||||
value.resize(n);
|
|
||||||
for (i = 0; i < n; i++ )
|
|
||||||
{
|
|
||||||
SafeSeqItem item_wrap(obj, i);
|
|
||||||
PyObject* item = item_wrap.item;
|
|
||||||
_Cp* data = (_Cp*)&value[i];
|
|
||||||
|
|
||||||
if( channels == 2 && PyComplex_Check(item) )
|
|
||||||
{
|
|
||||||
data[0] = saturate_cast<_Cp>(PyComplex_RealAsDouble(item));
|
|
||||||
data[1] = saturate_cast<_Cp>(PyComplex_ImagAsDouble(item));
|
|
||||||
}
|
|
||||||
else if( channels > 1 )
|
|
||||||
{
|
|
||||||
if( PyArray_Check(item))
|
|
||||||
{
|
|
||||||
Mat src;
|
|
||||||
pyopencv_to(item, src, info);
|
|
||||||
if( src.dims != 2 || src.channels() != 1 ||
|
|
||||||
((src.cols != 1 || src.rows != channels) &&
|
|
||||||
(src.cols != channels || src.rows != 1)))
|
|
||||||
break;
|
|
||||||
Mat dst(src.rows, src.cols, depth, data);
|
|
||||||
src.convertTo(dst, type);
|
|
||||||
if( dst.data != (uchar*)data )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (PySequence_Check(item))
|
|
||||||
{
|
|
||||||
if (!copyOneItem(item, 0, channels, data))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (channels == 1)
|
|
||||||
{
|
|
||||||
if (!copyOneItem(obj, i, channels, data))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i == n;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject* from(const std::vector<_Tp>& value)
|
|
||||||
{
|
|
||||||
if(value.empty())
|
|
||||||
return PyTuple_New(0);
|
|
||||||
int type = traits::Type<_Tp>::value;
|
|
||||||
int depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type);
|
|
||||||
Mat src((int)value.size(), channels, depth, (uchar*)&value[0]);
|
|
||||||
return pyopencv_from(src);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename _Tp>
|
|
||||||
bool pyopencv_to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencvVecConverter<_Tp>::to(obj, value, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Tp>
|
|
||||||
PyObject* pyopencv_from(const std::vector<_Tp>& value)
|
|
||||||
{
|
|
||||||
return pyopencvVecConverter<_Tp>::from(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Tp> static inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
if(!obj || obj == Py_None)
|
|
||||||
return true;
|
|
||||||
if (!PySequence_Check(obj))
|
|
||||||
return false;
|
|
||||||
size_t n = PySequence_Size(obj);
|
|
||||||
value.resize(n);
|
|
||||||
for(size_t i = 0; i < n; i++ )
|
|
||||||
{
|
|
||||||
SafeSeqItem item_wrap(obj, i);
|
|
||||||
if(!pyopencv_to(item_wrap.item, value[i], info))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename _Tp> static inline PyObject* pyopencv_from_generic_vec(const std::vector<_Tp>& value)
|
|
||||||
{
|
|
||||||
int i, n = (int)value.size();
|
|
||||||
PyObject* seq = PyList_New(n);
|
|
||||||
for( i = 0; i < n; i++ )
|
|
||||||
{
|
|
||||||
PyObject* item = pyopencv_from(value[i]);
|
|
||||||
if(!item)
|
|
||||||
break;
|
|
||||||
PyList_SetItem(seq, i, item);
|
|
||||||
}
|
|
||||||
if( i < n )
|
|
||||||
{
|
|
||||||
Py_DECREF(seq);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return seq;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
PyObject* pyopencv_from(const std::pair<int, double>& src)
|
PyObject* pyopencv_from(const std::pair<int, double>& src)
|
||||||
{
|
{
|
||||||
return Py_BuildValue("(id)", src.first, src.second);
|
return Py_BuildValue("(id)", src.first, src.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename _Tp, typename _Tr> struct pyopencvVecConverter<std::pair<_Tp, _Tr> >
|
|
||||||
{
|
|
||||||
static bool to(PyObject* obj, std::vector<std::pair<_Tp, _Tr> >& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencv_to_generic_vec(obj, value, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject* from(const std::vector<std::pair<_Tp, _Tr> >& value)
|
|
||||||
{
|
|
||||||
return pyopencv_from_generic_vec(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename _Tp> struct pyopencvVecConverter<std::vector<_Tp> >
|
|
||||||
{
|
|
||||||
static bool to(PyObject* obj, std::vector<std::vector<_Tp> >& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencv_to_generic_vec(obj, value, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject* from(const std::vector<std::vector<_Tp> >& value)
|
|
||||||
{
|
|
||||||
return pyopencv_from_generic_vec(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct pyopencvVecConverter<Mat>
|
|
||||||
{
|
|
||||||
static bool to(PyObject* obj, std::vector<Mat>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencv_to_generic_vec(obj, value, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject* from(const std::vector<Mat>& value)
|
|
||||||
{
|
|
||||||
return pyopencv_from_generic_vec(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct pyopencvVecConverter<UMat>
|
|
||||||
{
|
|
||||||
static bool to(PyObject* obj, std::vector<UMat>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencv_to_generic_vec(obj, value, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject* from(const std::vector<UMat>& value)
|
|
||||||
{
|
|
||||||
return pyopencv_from_generic_vec(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct pyopencvVecConverter<KeyPoint>
|
|
||||||
{
|
|
||||||
static bool to(PyObject* obj, std::vector<KeyPoint>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencv_to_generic_vec(obj, value, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject* from(const std::vector<KeyPoint>& value)
|
|
||||||
{
|
|
||||||
return pyopencv_from_generic_vec(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct pyopencvVecConverter<DMatch>
|
|
||||||
{
|
|
||||||
static bool to(PyObject* obj, std::vector<DMatch>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencv_to_generic_vec(obj, value, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject* from(const std::vector<DMatch>& value)
|
|
||||||
{
|
|
||||||
return pyopencv_from_generic_vec(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct pyopencvVecConverter<String>
|
|
||||||
{
|
|
||||||
static bool to(PyObject* obj, std::vector<String>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencv_to_generic_vec(obj, value, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject* from(const std::vector<String>& value)
|
|
||||||
{
|
|
||||||
return pyopencv_from_generic_vec(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct pyopencvVecConverter<RotatedRect>
|
|
||||||
{
|
|
||||||
static bool to(PyObject* obj, std::vector<RotatedRect>& value, const ArgInfo& info)
|
|
||||||
{
|
|
||||||
return pyopencv_to_generic_vec(obj, value, info);
|
|
||||||
}
|
|
||||||
static PyObject* from(const std::vector<RotatedRect>& value)
|
|
||||||
{
|
|
||||||
return pyopencv_from_generic_vec(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
bool pyopencv_to(PyObject* obj, TermCriteria& dst, const ArgInfo& info)
|
bool pyopencv_to(PyObject* obj, TermCriteria& dst, const ArgInfo& info)
|
||||||
{
|
{
|
||||||
@ -1852,6 +1638,183 @@ PyObject* pyopencv_from(const Moments& m)
|
|||||||
"nu30", m.nu30, "nu21", m.nu21, "nu12", m.nu12, "nu03", m.nu03);
|
"nu30", m.nu30, "nu21", m.nu21, "nu12", m.nu12, "nu03", m.nu03);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Tp>
|
||||||
|
struct pyopencvVecConverter;
|
||||||
|
|
||||||
|
template <typename Tp>
|
||||||
|
bool pyopencv_to(PyObject* obj, std::vector<Tp>& value, const ArgInfo& info)
|
||||||
|
{
|
||||||
|
if (!obj || obj == Py_None)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return pyopencvVecConverter<Tp>::to(obj, value, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Tp>
|
||||||
|
PyObject* pyopencv_from(const std::vector<Tp>& value)
|
||||||
|
{
|
||||||
|
return pyopencvVecConverter<Tp>::from(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Tp>
|
||||||
|
static bool pyopencv_to_generic_vec(PyObject* obj, std::vector<Tp>& value, 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 size_t n = static_cast<size_t>(PySequence_Size(obj));
|
||||||
|
value.resize(n);
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
SafeSeqItem item_wrap(obj, i);
|
||||||
|
if (!pyopencv_to(item_wrap.item, value[i], info))
|
||||||
|
{
|
||||||
|
failmsg("Can't parse '%s'. Sequence item with index %lu has a wrong type", info.name, i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Tp>
|
||||||
|
static PyObject* pyopencv_from_generic_vec(const std::vector<Tp>& value)
|
||||||
|
{
|
||||||
|
Py_ssize_t n = static_cast<Py_ssize_t>(value.size());
|
||||||
|
PySafeObject seq(PyTuple_New(n));
|
||||||
|
for (Py_ssize_t i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
PyObject* item = pyopencv_from(value[i]);
|
||||||
|
// If item can't be assigned - PyTuple_SetItem raises exception and returns -1.
|
||||||
|
if (!item || PyTuple_SetItem(seq, i, item) == -1)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return seq.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Tp>
|
||||||
|
struct pyopencvVecConverter
|
||||||
|
{
|
||||||
|
typedef typename std::vector<Tp>::iterator VecIt;
|
||||||
|
|
||||||
|
static bool to(PyObject* obj, std::vector<Tp>& value, const ArgInfo& info)
|
||||||
|
{
|
||||||
|
if (!PyArray_Check(obj))
|
||||||
|
{
|
||||||
|
return pyopencv_to_generic_vec(obj, value, info);
|
||||||
|
}
|
||||||
|
// If user passed an array it is possible to make faster conversions in several cases
|
||||||
|
PyArrayObject* array_obj = reinterpret_cast<PyArrayObject*>(obj);
|
||||||
|
const NPY_TYPES target_type = asNumpyType<Tp>();
|
||||||
|
const NPY_TYPES source_type = static_cast<NPY_TYPES>(PyArray_TYPE(array_obj));
|
||||||
|
if (target_type == NPY_OBJECT)
|
||||||
|
{
|
||||||
|
// Non-planar arrays representing objects (e.g. array of N Rect is an array of shape Nx4) have NPY_OBJECT
|
||||||
|
// as their target type.
|
||||||
|
return pyopencv_to_generic_vec(obj, value, info);
|
||||||
|
}
|
||||||
|
if (PyArray_NDIM(array_obj) > 1)
|
||||||
|
{
|
||||||
|
failmsg("Can't parse %dD array as '%s' vector argument", PyArray_NDIM(array_obj), info.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (target_type != source_type)
|
||||||
|
{
|
||||||
|
// Source type requires conversion
|
||||||
|
// Allowed conversions for target type is handled in the corresponding pyopencv_to function
|
||||||
|
return pyopencv_to_generic_vec(obj, value, info);
|
||||||
|
}
|
||||||
|
// For all other cases, all array data can be directly copied to std::vector data
|
||||||
|
// Simple `memcpy` is not possible because NumPy array can reference a slice of the bigger array:
|
||||||
|
// ```
|
||||||
|
// arr = np.ones((8, 4, 5), dtype=np.int32)
|
||||||
|
// convertible_to_vector_of_int = arr[:, 0, 1]
|
||||||
|
// ```
|
||||||
|
value.resize(static_cast<size_t>(PyArray_SIZE(array_obj)));
|
||||||
|
const npy_intp item_step = PyArray_STRIDE(array_obj, 0) / PyArray_ITEMSIZE(array_obj);
|
||||||
|
const Tp* data_ptr = static_cast<Tp*>(PyArray_DATA(array_obj));
|
||||||
|
for (VecIt it = value.begin(); it != value.end(); ++it, data_ptr += item_step) {
|
||||||
|
*it = *data_ptr;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* from(const std::vector<Tp>& value)
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
return PyTuple_New(0);
|
||||||
|
}
|
||||||
|
return from(value, ::traits::IsRepresentableAsMatDataType<Tp>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static PyObject* from(const std::vector<Tp>& value, ::traits::FalseType)
|
||||||
|
{
|
||||||
|
// Underlying type is not representable as Mat Data Type
|
||||||
|
return pyopencv_from_generic_vec(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject* from(const std::vector<Tp>& value, ::traits::TrueType)
|
||||||
|
{
|
||||||
|
// Underlying type is representable as Mat Data Type, so faster return type is available
|
||||||
|
typedef DataType<Tp> DType;
|
||||||
|
typedef typename DType::channel_type UnderlyingArrayType;
|
||||||
|
|
||||||
|
// If Mat is always exposed as NumPy array this code path can be reduced to the following snipped:
|
||||||
|
// Mat src(value);
|
||||||
|
// PyObject* array = pyopencv_from(src);
|
||||||
|
// return PyArray_Squeeze(reinterpret_cast<PyArrayObject*>(array));
|
||||||
|
// This puts unnecessary restrictions on Mat object those might be avoided without losing the performance.
|
||||||
|
// Moreover, this version is a bit faster, because it doesn't create temporary objects with reference counting.
|
||||||
|
|
||||||
|
const NPY_TYPES target_type = asNumpyType<UnderlyingArrayType>();
|
||||||
|
const int cols = DType::channels;
|
||||||
|
PyObject* array;
|
||||||
|
if (cols == 1)
|
||||||
|
{
|
||||||
|
npy_intp dims = static_cast<npy_intp>(value.size());
|
||||||
|
array = PyArray_SimpleNew(1, &dims, target_type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
npy_intp dims[2] = {static_cast<npy_intp>(value.size()), cols};
|
||||||
|
array = PyArray_SimpleNew(2, dims, target_type);
|
||||||
|
}
|
||||||
|
if(!array)
|
||||||
|
{
|
||||||
|
// NumPy arrays with shape (N, 1) and (N) are not equal, so correct error message should distinguish
|
||||||
|
// them too.
|
||||||
|
String shape;
|
||||||
|
if (cols > 1)
|
||||||
|
{
|
||||||
|
shape = cv::format("(%d x %d)", static_cast<int>(value.size()), cols);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shape = cv::format("(%d)", static_cast<int>(value.size()));
|
||||||
|
}
|
||||||
|
CV_Error_(Error::StsError, ("Can't allocate NumPy array for vector with dtype=%d shape=%s",
|
||||||
|
static_cast<int>(target_type), static_cast<int>(value.size()), shape.c_str()));
|
||||||
|
}
|
||||||
|
// Fill the array
|
||||||
|
PyArrayObject* array_obj = reinterpret_cast<PyArrayObject*>(array);
|
||||||
|
UnderlyingArrayType* array_data = static_cast<UnderlyingArrayType*>(PyArray_DATA(array_obj));
|
||||||
|
// if Tp is representable as Mat DataType, so the following cast is pretty safe...
|
||||||
|
const UnderlyingArrayType* value_data = reinterpret_cast<const UnderlyingArrayType*>(value.data());
|
||||||
|
memcpy(array_data, value_data, sizeof(UnderlyingArrayType) * value.size() * static_cast<size_t>(cols));
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static int OnError(int status, const char *func_name, const char *err_msg, const char *file_name, int line, void *userdata)
|
static int OnError(int status, const char *func_name, const char *err_msg, const char *file_name, int line, void *userdata)
|
||||||
{
|
{
|
||||||
PyGILState_STATE gstate;
|
PyGILState_STATE gstate;
|
||||||
|
@ -20,8 +20,13 @@ class Hackathon244Tests(NewOpenCVTests):
|
|||||||
flag, ajpg = cv.imencode("img_q90.jpg", a, [cv.IMWRITE_JPEG_QUALITY, 90])
|
flag, ajpg = cv.imencode("img_q90.jpg", a, [cv.IMWRITE_JPEG_QUALITY, 90])
|
||||||
self.assertEqual(flag, True)
|
self.assertEqual(flag, True)
|
||||||
self.assertEqual(ajpg.dtype, np.uint8)
|
self.assertEqual(ajpg.dtype, np.uint8)
|
||||||
self.assertGreater(ajpg.shape[0], 1)
|
self.assertTrue(isinstance(ajpg, np.ndarray), "imencode returned buffer of wrong type: {}".format(type(ajpg)))
|
||||||
self.assertEqual(ajpg.shape[1], 1)
|
self.assertEqual(len(ajpg.shape), 1, "imencode returned buffer with wrong shape: {}".format(ajpg.shape))
|
||||||
|
self.assertGreaterEqual(len(ajpg), 1, "imencode length of the returned buffer should be at least 1")
|
||||||
|
self.assertLessEqual(
|
||||||
|
len(ajpg), a.size,
|
||||||
|
"imencode length of the returned buffer shouldn't exceed number of elements in original image"
|
||||||
|
)
|
||||||
|
|
||||||
def test_projectPoints(self):
|
def test_projectPoints(self):
|
||||||
objpt = np.float64([[1,2,3]])
|
objpt = np.float64([[1,2,3]])
|
||||||
|
@ -480,6 +480,109 @@ class Arguments(NewOpenCVTests):
|
|||||||
cv.utils.testReservedKeywordConversion(20, lambda_=-4, from_=12), format_str.format(20, -4, 12)
|
cv.utils.testReservedKeywordConversion(20, lambda_=-4, from_=12), format_str.format(20, -4, 12)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_parse_vector_int_convertible(self):
|
||||||
|
np.random.seed(123098765)
|
||||||
|
try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfInt)
|
||||||
|
arr = np.random.randint(-20, 20, 40).astype(np.int32).reshape(10, 2, 2)
|
||||||
|
int_min, int_max = get_limits(ctypes.c_int)
|
||||||
|
for convertible in ((int_min, 1, 2, 3, int_max), [40, 50], tuple(),
|
||||||
|
np.array([int_min, -10, 24, int_max], dtype=np.int32),
|
||||||
|
np.array([10, 230, 12], dtype=np.uint8), arr[:, 0, 1],):
|
||||||
|
expected = "[" + ", ".join(map(str, convertible)) + "]"
|
||||||
|
actual = try_to_convert(convertible)
|
||||||
|
self.assertEqual(expected, actual,
|
||||||
|
msg=get_conversion_error_msg(convertible, expected, actual))
|
||||||
|
|
||||||
|
def test_parse_vector_int_not_convertible(self):
|
||||||
|
np.random.seed(123098765)
|
||||||
|
arr = np.random.randint(-20, 20, 40).astype(np.float).reshape(10, 2, 2)
|
||||||
|
int_min, int_max = get_limits(ctypes.c_int)
|
||||||
|
test_dict = {1: 2, 3: 10, 10: 20}
|
||||||
|
for not_convertible in ((int_min, 1, 2.5, 3, int_max), [True, 50], 'test', test_dict,
|
||||||
|
reversed([1, 2, 3]),
|
||||||
|
np.array([int_min, -10, 24, [1, 2]], dtype=np.object),
|
||||||
|
np.array([[1, 2], [3, 4]]), arr[:, 0, 1],):
|
||||||
|
with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)):
|
||||||
|
_ = cv.utils.dumpVectorOfInt(not_convertible)
|
||||||
|
|
||||||
|
def test_parse_vector_double_convertible(self):
|
||||||
|
np.random.seed(1230965)
|
||||||
|
try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfDouble)
|
||||||
|
arr = np.random.randint(-20, 20, 40).astype(np.int32).reshape(10, 2, 2)
|
||||||
|
for convertible in ((1, 2.12, 3.5), [40, 50], tuple(),
|
||||||
|
np.array([-10, 24], dtype=np.int32),
|
||||||
|
np.array([-12.5, 1.4], dtype=np.double),
|
||||||
|
np.array([10, 230, 12], dtype=np.float), arr[:, 0, 1], ):
|
||||||
|
expected = "[" + ", ".join(map(lambda v: "{:.2f}".format(v), convertible)) + "]"
|
||||||
|
actual = try_to_convert(convertible)
|
||||||
|
self.assertEqual(expected, actual,
|
||||||
|
msg=get_conversion_error_msg(convertible, expected, actual))
|
||||||
|
|
||||||
|
def test_parse_vector_double_not_convertible(self):
|
||||||
|
test_dict = {1: 2, 3: 10, 10: 20}
|
||||||
|
for not_convertible in (('t', 'e', 's', 't'), [True, 50.55], 'test', test_dict,
|
||||||
|
np.array([-10.1, 24.5, [1, 2]], dtype=np.object),
|
||||||
|
np.array([[1, 2], [3, 4]]),):
|
||||||
|
with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)):
|
||||||
|
_ = cv.utils.dumpVectorOfDouble(not_convertible)
|
||||||
|
|
||||||
|
def test_parse_vector_rect_convertible(self):
|
||||||
|
np.random.seed(1238765)
|
||||||
|
try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfRect)
|
||||||
|
arr_of_rect_int32 = np.random.randint(5, 20, 4 * 3).astype(np.int32).reshape(3, 4)
|
||||||
|
arr_of_rect_cast = np.random.randint(10, 40, 4 * 5).astype(np.uint8).reshape(5, 4)
|
||||||
|
for convertible in (((1, 2, 3, 4), (10, -20, 30, 10)), arr_of_rect_int32, arr_of_rect_cast,
|
||||||
|
arr_of_rect_int32.astype(np.int8), [[5, 3, 1, 4]],
|
||||||
|
((np.int8(4), np.uint8(10), np.int(32), np.int16(55)),)):
|
||||||
|
expected = "[" + ", ".join(map(lambda v: "[x={}, y={}, w={}, h={}]".format(*v), convertible)) + "]"
|
||||||
|
actual = try_to_convert(convertible)
|
||||||
|
self.assertEqual(expected, actual,
|
||||||
|
msg=get_conversion_error_msg(convertible, expected, actual))
|
||||||
|
|
||||||
|
def test_parse_vector_rect_not_convertible(self):
|
||||||
|
np.random.seed(1238765)
|
||||||
|
arr = np.random.randint(5, 20, 4 * 3).astype(np.float).reshape(3, 4)
|
||||||
|
for not_convertible in (((1, 2, 3, 4), (10.5, -20, 30.1, 10)), arr,
|
||||||
|
[[5, 3, 1, 4], []],
|
||||||
|
((np.float(4), np.uint8(10), np.int(32), np.int16(55)),)):
|
||||||
|
with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)):
|
||||||
|
_ = cv.utils.dumpVectorOfRect(not_convertible)
|
||||||
|
|
||||||
|
def test_vector_general_return(self):
|
||||||
|
expected_number_of_mats = 5
|
||||||
|
expected_shape = (10, 10, 3)
|
||||||
|
expected_type = np.uint8
|
||||||
|
mats = cv.utils.generateVectorOfMat(5, 10, 10, cv.CV_8UC3)
|
||||||
|
self.assertTrue(isinstance(mats, tuple),
|
||||||
|
"Vector of Mats objects should be returned as tuple. Got: {}".format(type(mats)))
|
||||||
|
self.assertEqual(len(mats), expected_number_of_mats, "Returned array has wrong length")
|
||||||
|
for mat in mats:
|
||||||
|
self.assertEqual(mat.shape, expected_shape, "Returned Mat has wrong shape")
|
||||||
|
self.assertEqual(mat.dtype, expected_type, "Returned Mat has wrong elements type")
|
||||||
|
empty_mats = cv.utils.generateVectorOfMat(0, 10, 10, cv.CV_32FC1)
|
||||||
|
self.assertTrue(isinstance(empty_mats, tuple),
|
||||||
|
"Empty vector should be returned as empty tuple. Got: {}".format(type(mats)))
|
||||||
|
self.assertEqual(len(empty_mats), 0, "Vector of size 0 should be returned as tuple of length 0")
|
||||||
|
|
||||||
|
def test_vector_fast_return(self):
|
||||||
|
expected_shape = (5, 4)
|
||||||
|
rects = cv.utils.generateVectorOfRect(expected_shape[0])
|
||||||
|
self.assertTrue(isinstance(rects, np.ndarray),
|
||||||
|
"Vector of rectangles should be returned as numpy array. Got: {}".format(type(rects)))
|
||||||
|
self.assertEqual(rects.dtype, np.int32, "Vector of rectangles has wrong elements type")
|
||||||
|
self.assertEqual(rects.shape, expected_shape, "Vector of rectangles has wrong shape")
|
||||||
|
empty_rects = cv.utils.generateVectorOfRect(0)
|
||||||
|
self.assertTrue(isinstance(empty_rects, tuple),
|
||||||
|
"Empty vector should be returned as empty tuple. Got: {}".format(type(empty_rects)))
|
||||||
|
self.assertEqual(len(empty_rects), 0, "Vector of size 0 should be returned as tuple of length 0")
|
||||||
|
|
||||||
|
expected_shape = (10,)
|
||||||
|
ints = cv.utils.generateVectorOfInt(expected_shape[0])
|
||||||
|
self.assertTrue(isinstance(ints, np.ndarray),
|
||||||
|
"Vector of integers should be returned as numpy array. Got: {}".format(type(ints)))
|
||||||
|
self.assertEqual(ints.dtype, np.int32, "Vector of integers has wrong elements type")
|
||||||
|
self.assertEqual(ints.shape, expected_shape, "Vector of integers has wrong shape.")
|
||||||
|
|
||||||
|
|
||||||
class SamplesFindFile(NewOpenCVTests):
|
class SamplesFindFile(NewOpenCVTests):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user