@ -493,6 +493,33 @@ bool parseSequence(PyObject* obj, RefWrapper<T> (&value)[N], const ArgInfo& info
}
} // 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 < char > vector_char ;
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 ( ) ;
}
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 < >
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) ;
}
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 < >
PyObject * pyopencv_from ( const std : : pair < int , double > & src )
{
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 < >
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 ) ;
}
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 )
{
PyGILState_STATE gstate ;