annotate VectorConversion.cpp @ 41:55fcd0e3e513

Fix handling of 2D NumPy arrays
author Chris Cannam
date Wed, 26 Nov 2014 18:08:53 +0000
parents fa3f80d4e340
children 36cc53aad853
rev   line source
Chris@26 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@26 2
Chris@26 3 /*
Chris@28 4 VampyHost
Chris@26 5
Chris@28 6 Use Vamp audio analysis plugins in Python
Chris@26 7
Chris@28 8 Gyorgy Fazekas and Chris Cannam
Chris@28 9 Centre for Digital Music, Queen Mary, University of London
Chris@28 10 Copyright 2008-2014 Queen Mary, University of London
Chris@26 11
Chris@28 12 Permission is hereby granted, free of charge, to any person
Chris@28 13 obtaining a copy of this software and associated documentation
Chris@28 14 files (the "Software"), to deal in the Software without
Chris@28 15 restriction, including without limitation the rights to use, copy,
Chris@28 16 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@28 17 of the Software, and to permit persons to whom the Software is
Chris@28 18 furnished to do so, subject to the following conditions:
Chris@26 19
Chris@28 20 The above copyright notice and this permission notice shall be
Chris@28 21 included in all copies or substantial portions of the Software.
Chris@26 22
Chris@28 23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@28 24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@28 25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@28 26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
Chris@28 27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@28 28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@28 29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@26 30
Chris@28 31 Except as contained in this notice, the names of the Centre for
Chris@28 32 Digital Music; Queen Mary, University of London; and the authors
Chris@28 33 shall not be used in advertising or otherwise to promote the sale,
Chris@28 34 use or other dealings in this Software without prior written
Chris@28 35 authorization.
Chris@26 36 */
Chris@26 37
Chris@26 38 #include <Python.h>
Chris@26 39
Chris@29 40 #include "VectorConversion.h"
Chris@26 41
Chris@26 42 #include <math.h>
Chris@26 43 #include <float.h>
Chris@26 44
Chris@28 45 using namespace std;
Chris@26 46
Chris@26 47 /* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS
Chris@28 48 (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */
Chris@26 49
Chris@29 50 VectorConversion::VectorConversion() :
Chris@28 51 m_error(false),
Chris@28 52 error(m_error) // const public reference for easy access
Chris@26 53 {
Chris@26 54 }
Chris@26 55
Chris@29 56 VectorConversion::~VectorConversion()
Chris@26 57 {
Chris@26 58 }
Chris@26 59
Chris@26 60 /// floating point numbers (TODO: check numpy.float128)
Chris@26 61 float
Chris@29 62 VectorConversion::PyValue_To_Float(PyObject* pyValue) const
Chris@26 63 {
Chris@28 64 // convert float
Chris@28 65 if (pyValue && PyFloat_Check(pyValue))
Chris@28 66 //TODO: check for limits here (same on most systems)
Chris@28 67 return (float) PyFloat_AS_DOUBLE(pyValue);
Chris@26 68
Chris@28 69 if (pyValue == NULL)
Chris@28 70 {
Chris@28 71 setValueError("Error while converting object " + PyValue_Get_TypeName(pyValue) + " to float. ");
Chris@28 72 return 0.0;
Chris@28 73 }
Chris@26 74
Chris@28 75 setValueError("Conversion error: object" + PyValue_Get_TypeName(pyValue) +" is not float.");
Chris@28 76 return 0.0;
Chris@26 77 }
Chris@26 78
Chris@28 79 vector<float>
Chris@29 80 VectorConversion::PyValue_To_FloatVector (PyObject *pyValue) const
Chris@26 81 {
Chris@28 82 /// numpy array
Chris@28 83 if (PyArray_CheckExact(pyValue))
Chris@28 84 return PyArray_To_FloatVector(pyValue);
Chris@26 85
Chris@28 86 /// python list of floats (backward compatible)
Chris@28 87 if (PyList_Check(pyValue)) {
Chris@28 88 return PyList_To_FloatVector(pyValue);
Chris@28 89 }
Chris@26 90
Chris@28 91 string msg = "Value is not list or array of floats";
Chris@28 92 setValueError(msg);
Chris@26 93 #ifdef _DEBUG
Chris@29 94 cerr << "VectorConversion::PyValue_To_FloatVector failed. " << msg << endl;
Chris@26 95 #endif
Chris@28 96 return vector<float>();
Chris@26 97 }
Chris@26 98
Chris@28 99 vector<float>
Chris@29 100 VectorConversion::PyList_To_FloatVector (PyObject *inputList) const
Chris@26 101 {
Chris@28 102 vector<float> v;
Chris@26 103
Chris@28 104 if (!PyList_Check(inputList)) {
Chris@28 105 setValueError("Value is not a list");
Chris@28 106 return v;
Chris@28 107 }
Chris@26 108
Chris@28 109 PyObject **pyObjectArray = PySequence_Fast_ITEMS(inputList);
Chris@28 110 int n = PyList_GET_SIZE(inputList);
Chris@26 111
Chris@28 112 for (int i = 0; i < n; ++i) {
Chris@28 113 v.push_back(PyValue_To_Float(pyObjectArray[i]));
Chris@28 114 }
Chris@28 115
Chris@28 116 return v;
Chris@26 117 }
Chris@26 118
Chris@28 119 vector<float>
Chris@29 120 VectorConversion::PyArray_To_FloatVector (PyObject *pyValue) const
Chris@26 121 {
Chris@28 122 vector<float> v;
Chris@26 123
Chris@28 124 if (!PyArray_Check(pyValue)) {
Chris@28 125 setValueError("Value is not an array");
Chris@28 126 return v;
Chris@28 127 }
Chris@28 128
Chris@28 129 PyArrayObject* pyArray = (PyArrayObject*) pyValue;
Chris@28 130 PyArray_Descr* descr = PyArray_DESCR(pyArray);
Chris@28 131
Chris@28 132 if (PyArray_DATA(pyArray) == 0 || descr == 0) {
Chris@28 133 string msg = "NumPy array with NULL data or descriptor pointer encountered.";
Chris@28 134 setValueError(msg);
Chris@28 135 return v;
Chris@28 136 }
Chris@28 137
Chris@28 138 if (PyArray_NDIM(pyArray) != 1) {
Chris@40 139 string msg = "NumPy array must be a one-dimensional vector.";
Chris@28 140 setValueError(msg);
Chris@28 141 return v;
Chris@28 142 }
Chris@28 143
Chris@28 144 /// check strides (useful if array is not continuous)
Chris@28 145 size_t strides = *((size_t*) PyArray_STRIDES(pyArray));
Chris@28 146
Chris@28 147 /// convert the array
Chris@28 148 switch (descr->type_num) {
Chris@28 149
Chris@28 150 case NPY_FLOAT : // dtype='float32'
Chris@28 151 return PyArray_Convert<float,float>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
Chris@28 152 case NPY_DOUBLE : // dtype='float64'
Chris@28 153 return PyArray_Convert<float,double>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
Chris@28 154 case NPY_INT : // dtype='int'
Chris@28 155 return PyArray_Convert<float,int>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
Chris@28 156 case NPY_LONG : // dtype='long'
Chris@28 157 return PyArray_Convert<float,long>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
Chris@28 158 default :
Chris@28 159 string msg = "Unsupported value type in NumPy array object.";
Chris@28 160 setValueError(msg);
Chris@26 161 #ifdef _DEBUG
Chris@29 162 cerr << "VectorConversion::PyArray_To_FloatVector failed. Error: " << msg << endl;
Chris@26 163 #endif
Chris@28 164 return v;
Chris@28 165 }
Chris@26 166 }
Chris@26 167
Chris@40 168 vector<vector<float> >
Chris@40 169 VectorConversion::Py2DArray_To_FloatVector (PyObject *pyValue) const
Chris@40 170 {
Chris@40 171 vector<vector<float> > v;
Chris@40 172
Chris@40 173 if (!PyArray_Check(pyValue)) {
Chris@40 174 setValueError("Value is not an array");
Chris@40 175 return v;
Chris@40 176 }
Chris@40 177
Chris@40 178 PyArrayObject* pyArray = (PyArrayObject*) pyValue;
Chris@40 179 PyArray_Descr* descr = PyArray_DESCR(pyArray);
Chris@40 180
Chris@40 181 if (PyArray_DATA(pyArray) == 0 || descr == 0) {
Chris@40 182 string msg = "NumPy array with NULL data or descriptor pointer encountered.";
Chris@40 183 setValueError(msg);
Chris@40 184 return v;
Chris@40 185 }
Chris@40 186
Chris@40 187 if (PyArray_NDIM(pyArray) != 2) {
Chris@40 188 string msg = "NumPy array must be a two-dimensional matrix.";
Chris@40 189 setValueError(msg);
Chris@40 190 return v;
Chris@40 191 }
Chris@40 192
Chris@40 193 /// check strides (useful if array is not continuous)
Chris@41 194 size_t *strideptr = (size_t*) PyArray_STRIDES(pyArray);
Chris@40 195
Chris@40 196 /// convert the array
Chris@40 197 for (int i = 0; i < PyArray_DIMS(pyArray)[0]; ++i) {
Chris@40 198
Chris@40 199 vector<float> vv;
Chris@40 200
Chris@40 201 switch (descr->type_num) {
Chris@40 202
Chris@40 203 case NPY_FLOAT : // dtype='float32'
Chris@41 204 vv = PyArray_Convert<float,float>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
Chris@40 205 break;
Chris@40 206 case NPY_DOUBLE : // dtype='float64'
Chris@41 207 vv = PyArray_Convert<float,double>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
Chris@40 208 break;
Chris@40 209 case NPY_INT : // dtype='int'
Chris@41 210 vv = PyArray_Convert<float,int>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
Chris@40 211 break;
Chris@40 212 case NPY_LONG : // dtype='long'
Chris@41 213 vv = PyArray_Convert<float,long>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
Chris@40 214 break;
Chris@40 215 default :
Chris@40 216 string msg = "Unsupported value type in NumPy array object.";
Chris@40 217 cerr << "VectorConversion::PyArray_To_FloatVector failed (value type = " << descr->type_num << "). Error: " << msg << endl;
Chris@40 218 setValueError(msg);
Chris@40 219 return v;
Chris@40 220 }
Chris@40 221
Chris@40 222 v.push_back(vv);
Chris@40 223 }
Chris@40 224
Chris@40 225 return v;
Chris@40 226 }
Chris@40 227
Chris@26 228 PyObject *
Chris@29 229 VectorConversion::PyArray_From_FloatVector(const vector<float> &v) const
Chris@26 230 {
Chris@28 231 npy_intp ndims[1];
Chris@28 232 ndims[0] = (int)v.size();
Chris@28 233 PyObject *arr = PyArray_SimpleNew(1, ndims, NPY_FLOAT);
Chris@28 234 float *data = (float *)PyArray_DATA((PyArrayObject *)arr);
Chris@28 235 for (int i = 0; i < ndims[0]; ++i) {
Chris@28 236 data[i] = v[i];
Chris@28 237 }
Chris@28 238 return arr;
Chris@26 239 }
Chris@26 240
Chris@26 241 PyObject *
Chris@29 242 VectorConversion::PyValue_From_StringVector(const vector<string> &v) const
Chris@26 243 {
Chris@28 244 PyObject *pyList = PyList_New(v.size());
Chris@28 245 for (size_t i = 0; i < v.size(); ++i) {
Chris@28 246 PyObject *pyStr = PyString_FromString(v[i].c_str());
Chris@28 247 PyList_SET_ITEM(pyList, i, pyStr);
Chris@28 248 }
Chris@28 249 return pyList;
Chris@26 250 }
Chris@26 251
Chris@26 252
Chris@28 253 /* Error handling */
Chris@26 254
Chris@26 255 void
Chris@29 256 VectorConversion::setValueError (string message) const
Chris@26 257 {
Chris@28 258 m_error = true;
Chris@28 259 m_errorQueue.push(ValueError(message));
Chris@26 260 }
Chris@26 261
Chris@26 262 /// return a reference to the last error or creates a new one.
Chris@26 263 ValueError&
Chris@29 264 VectorConversion::lastError() const
Chris@26 265 {
Chris@28 266 m_error = false;
Chris@28 267 if (!m_errorQueue.empty()) return m_errorQueue.back();
Chris@28 268 else {
Chris@28 269 m_errorQueue.push(ValueError("Type conversion error."));
Chris@28 270 return m_errorQueue.back();
Chris@28 271 }
Chris@26 272 }
Chris@26 273
Chris@26 274 /// helper function to iterate over the error message queue:
Chris@26 275 /// pops the oldest item
Chris@26 276 ValueError
Chris@29 277 VectorConversion::getError() const
Chris@26 278 {
Chris@28 279 if (!m_errorQueue.empty()) {
Chris@28 280 ValueError e = m_errorQueue.front();
Chris@28 281 m_errorQueue.pop();
Chris@28 282 if (m_errorQueue.empty()) m_error = false;
Chris@28 283 return e;
Chris@28 284 }
Chris@28 285 else {
Chris@28 286 m_error = false;
Chris@28 287 return ValueError();
Chris@28 288 }
Chris@26 289 }
Chris@26 290
Chris@28 291 /* Utilities */
Chris@26 292
Chris@26 293 /// get the type name of an object
Chris@28 294 string
Chris@29 295 VectorConversion::PyValue_Get_TypeName(PyObject* pyValue) const
Chris@26 296 {
Chris@28 297 PyObject *pyType = PyObject_Type(pyValue);
Chris@28 298 if (!pyType)
Chris@28 299 {
Chris@28 300 cerr << "Warning: Object type name could not be found." << endl;
Chris@28 301 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
Chris@28 302 return string ("< unknown type >");
Chris@28 303 }
Chris@28 304 PyObject *pyString = PyObject_Str(pyType);
Chris@28 305 if (!pyString)
Chris@28 306 {
Chris@28 307 cerr << "Warning: Object type name could not be found." << endl;
Chris@28 308 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
Chris@28 309 Py_CLEAR(pyType);
Chris@28 310 return string ("< unknown type >");
Chris@28 311 }
Chris@28 312 char *cstr = PyString_AS_STRING(pyString);
Chris@28 313 if (!cstr)
Chris@28 314 {
Chris@28 315 cerr << "Warning: Object type name could not be found." << endl;
Chris@28 316 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
Chris@28 317 Py_DECREF(pyType);
Chris@28 318 Py_CLEAR(pyString);
Chris@28 319 return string("< unknown type >");
Chris@28 320 }
Chris@28 321 Py_DECREF(pyType);
Chris@28 322 Py_DECREF(pyString);
Chris@28 323 return string(cstr);
Chris@26 324 }