From ea24782fc205a6417b9b89ff91013f4e1da94e0b Mon Sep 17 00:00:00 2001 From: Andre Anjos <andre.dos.anjos@gmail.com> Date: Mon, 11 Nov 2013 18:48:49 +0100 Subject: [PATCH] Implement VideoReader iterator --- xbob/io/file.cpp | 76 ++++++++++++++++++++++- xbob/io/include/xbob.io/api.h | 39 ++++++++++-- xbob/io/main.cpp | 16 +++++ xbob/io/test/test_file.py | 11 ++++ xbob/io/test/test_video.py | 11 ++++ xbob/io/videoreader.cpp | 113 +++++++++++++++++++++++++++++++--- 6 files changed, 250 insertions(+), 16 deletions(-) diff --git a/xbob/io/file.cpp b/xbob/io/file.cpp index fc38194..9d4ec03 100644 --- a/xbob/io/file.cpp +++ b/xbob/io/file.cpp @@ -14,7 +14,7 @@ #include <stdexcept> #include <bobskin.h> -#define FILETYPE_NAME file +#define FILETYPE_NAME File PyDoc_STRVAR(s_file_str, BOOST_PP_STRINGIZE(XBOB_IO_MODULE_PREFIX) "." BOOST_PP_STRINGIZE(FILETYPE_NAME)); /* How to create a new PyBobIoFileObject */ @@ -299,7 +299,7 @@ static PyObject* PyBobIoFile_GetItem (PyBobIoFileObject* self, PyObject* item) { return PyBobIoFile_GetSlice(self, start, stop, step, slicelength); } else { - PyErr_Format(PyExc_TypeError, "File indices must be integers, not %.200s", + PyErr_Format(PyExc_TypeError, "File indices must be integers, not %s", item->ob_type->tp_name); return 0; } @@ -592,6 +592,76 @@ static PyMethodDef PyBobIoFile_Methods[] = { {0} /* Sentinel */ }; +/********************************** + * Definition of Iterator to File * + **********************************/ + +#define FILEITERTYPE_NAME File.iter +PyDoc_STRVAR(s_fileiterator_str, BOOST_PP_STRINGIZE(XBOB_IO_MODULE_PREFIX) "." BOOST_PP_STRINGIZE(FILEITERTYPE_NAME)); + +/* How to create a new PyBobIoFileIteratorObject */ +static PyObject* PyBobIoFileIterator_New(PyTypeObject* type, PyObject*, PyObject*) { + + /* Allocates the python object itself */ + PyBobIoFileIteratorObject* self = (PyBobIoFileIteratorObject*)type->tp_alloc(type, 0); + + return reinterpret_cast<PyObject*>(self); +} + +static PyObject* PyBobIoFileIterator_Iter (PyBobIoFileIteratorObject* self) { + Py_INCREF(self); + return reinterpret_cast<PyObject*>(self); +} + +static PyObject* PyBobIoFileIterator_Next (PyBobIoFileIteratorObject* self) { + if (self->curpos >= self->pyfile->f->size()) { + Py_XDECREF((PyObject*)self->pyfile); + self->pyfile = 0; + return 0; + } + return PyBobIoFile_GetIndex(self->pyfile, self->curpos++); +} + +PyTypeObject PyBobIoFileIterator_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + s_fileiterator_str, /* tp_name */ + sizeof(PyBobIoFileIteratorObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)PyBobIoFileIterator_Iter, /* tp_iter */ + (iternextfunc)PyBobIoFileIterator_Next /* tp_iternext */ +}; + +static PyObject* PyBobIoFile_Iter (PyBobIoFileObject* self) { + PyBobIoFileIteratorObject* retval = (PyBobIoFileIteratorObject*)PyBobIoFileIterator_New(&PyBobIoFileIterator_Type, 0, 0); + if (!retval) return 0; + Py_INCREF(self); + retval->pyfile = self; + retval->curpos = 0; + return reinterpret_cast<PyObject*>(retval); +} + PyTypeObject PyBobIoFile_Type = { PyObject_HEAD_INIT(0) 0, /*ob_size*/ @@ -619,7 +689,7 @@ PyTypeObject PyBobIoFile_Type = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)PyBobIoFile_Iter, /* tp_iter */ 0, /* tp_iternext */ PyBobIoFile_Methods, /* tp_methods */ 0, /* tp_members */ diff --git a/xbob/io/include/xbob.io/api.h b/xbob/io/include/xbob.io/api.h index f29a090..6df961f 100644 --- a/xbob/io/include/xbob.io/api.h +++ b/xbob/io/include/xbob.io/api.h @@ -50,15 +50,27 @@ typedef struct { #define PyBobIoFile_Type_NUM 1 #define PyBobIoFile_Type_TYPE PyTypeObject +typedef struct { + PyObject_HEAD + + /* Type-specific fields go here. */ + PyBobIoFileObject* pyfile; + Py_ssize_t curpos; + +} PyBobIoFileIteratorObject; + +#define PyBobIoFileIterator_Type_NUM 2 +#define PyBobIoFileIterator_Type_TYPE PyTypeObject + /************************ * I/O generic bindings * ************************/ -#define PyBobIo_AsTypenum_NUM 2 +#define PyBobIo_AsTypenum_NUM 3 #define PyBobIo_AsTypenum_RET int #define PyBobIo_AsTypenum_PROTO (bob::core::array::ElementType et) -#define PyBobIo_TypeInfoAsTuple_NUM 3 +#define PyBobIo_TypeInfoAsTuple_NUM 4 #define PyBobIo_TypeInfoAsTuple_RET PyObject* #define PyBobIo_TypeInfoAsTuple_PROTO (const bob::core::array::typeinfo& ti) @@ -76,16 +88,28 @@ typedef struct { } PyBobIoVideoReaderObject; -#define PyBobIoVideoReader_Type_NUM 4 +#define PyBobIoVideoReader_Type_NUM 5 #define PyBobIoVideoReader_Type_TYPE PyTypeObject +typedef struct { + PyObject_HEAD + + /* Type-specific fields go here. */ + PyBobIoVideoReaderObject* pyreader; + boost::shared_ptr<bob::io::VideoReader::const_iterator> iter; + +} PyBobIoVideoReaderIteratorObject; + +#define PyBobIoVideoReaderIterator_Type_NUM 5 +#define PyBobIoVideoReaderIterator_Type_TYPE PyTypeObject + #endif /* WITH_FFMPEG */ /* Total number of C API pointers */ #if WITH_FFMPEG -# define PyXbobIo_API_pointers 5 -#else # define PyXbobIo_API_pointers 6 +#else +# define PyXbobIo_API_pointers 7 #endif /* WITH_FFMPEG */ #ifdef XBOB_IO_MODULE @@ -103,6 +127,7 @@ typedef struct { *****************************/ extern PyBobIoFile_Type_TYPE PyBobIoFile_Type; + extern PyBobIoFileIterator_Type_TYPE PyBobIoFileIterator_Type; /************************ * I/O generic bindings * @@ -118,6 +143,7 @@ typedef struct { ******************/ extern PyBobIoVideoReader_Type_TYPE PyBobIoVideoReader_Type; + extern PyBobIoVideoReaderIterator_Type_TYPE PyBobIoVideoReaderIterator_Type; #endif /* WITH_FFMPEG */ #else @@ -159,6 +185,7 @@ typedef struct { *****************************/ # define PyBobIoFile_Type (*(PyBobIoFile_Type_TYPE *)PyXbobIo_API[PyBobIoFile_Type_NUM]) +# define PyBobIoFileIterator_Type (*(PyBobIoFileIterator_Type_TYPE *)PyXbobIo_API[PyBobIoFileIterator_Type_NUM]) /************************ * I/O generic bindings * @@ -174,6 +201,8 @@ typedef struct { ******************/ # define PyBobIoVideoReader_Type (*(PyBobIoVideoReader_Type_TYPE *)PyXbobIo_API[PyBobIoVideoReader_Type_NUM]) + +# define PyBobIoVideoReaderIterator_Type (*(PyBobIoVideoReaderIterator_Type_TYPE *)PyXbobIo_API[PyBobIoVideoReaderIterator_Type_NUM]) #endif /* WITH_FFMPEG */ /** diff --git a/xbob/io/main.cpp b/xbob/io/main.cpp index c24476a..33cbc81 100644 --- a/xbob/io/main.cpp +++ b/xbob/io/main.cpp @@ -29,9 +29,15 @@ PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_MODULE_NAME) (void) { PyBobIoFile_Type.tp_new = PyType_GenericNew; if (PyType_Ready(&PyBobIoFile_Type) < 0) return; + PyBobIoFileIterator_Type.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyBobIoFileIterator_Type) < 0) return; + #if WITH_FFMPEG PyBobIoVideoReader_Type.tp_new = PyType_GenericNew; if (PyType_Ready(&PyBobIoVideoReader_Type) < 0) return; + + PyBobIoVideoReaderIterator_Type.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyBobIoVideoReaderIterator_Type) < 0) return; #endif /* WITH_FFMPEG */ PyObject* m = Py_InitModule3(BOOST_PP_STRINGIZE(XBOB_IO_MODULE_NAME), @@ -45,9 +51,15 @@ PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_MODULE_NAME) (void) { Py_INCREF(&PyBobIoFile_Type); PyModule_AddObject(m, "File", (PyObject *)&PyBobIoFile_Type); + Py_INCREF(&PyBobIoFileIterator_Type); + PyModule_AddObject(m, "File.iter", (PyObject *)&PyBobIoFileIterator_Type); + #if WITH_FFMPEG Py_INCREF(&PyBobIoVideoReader_Type); PyModule_AddObject(m, "VideoReader", (PyObject *)&PyBobIoVideoReader_Type); + + Py_INCREF(&PyBobIoVideoReaderIterator_Type); + PyModule_AddObject(m, "VideoReader.iter", (PyObject *)&PyBobIoVideoReaderIterator_Type); #endif /* WITH_FFMPEG */ static void* PyXbobIo_API[PyXbobIo_API_pointers]; @@ -66,6 +78,8 @@ PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_MODULE_NAME) (void) { PyXbobIo_API[PyBobIoFile_Type_NUM] = (void *)&PyBobIoFile_Type; + PyXbobIo_API[PyBobIoFileIterator_Type_NUM] = (void *)&PyBobIoFileIterator_Type; + /************************ * I/O generic bindings * ************************/ @@ -80,6 +94,8 @@ PyMODINIT_FUNC ENTRY_FUNCTION(XBOB_IO_MODULE_NAME) (void) { ******************/ PyXbobIo_API[PyBobIoVideoReader_Type_NUM] = (void *)&PyBobIoVideoReader_Type; + + PyXbobIo_API[PyBobIoVideoReaderIterator_Type_NUM] = (void *)&PyBobIoVideoReaderIterator_Type; #endif /* WITH_FFMPEG */ /* imports the NumPy C-API */ diff --git a/xbob/io/test/test_file.py b/xbob/io/test/test_file.py index 7797192..018572a 100644 --- a/xbob/io/test/test_file.py +++ b/xbob/io/test/test_file.py @@ -23,6 +23,17 @@ def test_peek(): assert peek(f) == (numpy.uint16, (3,), (1,)) assert peek_all(f) == (numpy.uint16, (3,3), (3,1)) +def test_iteration(): + + fname = testutils.datafile('matlab_2d.hdf5', __name__) + f = File(fname, 'r') + nose.tools.eq_(len(f), 512) + + objs = load(fname) + + for l, i in zip(objs, f): + assert numpy.allclose(l, i) + def test_indexing(): fname = testutils.datafile('matlab_2d.hdf5', __name__) diff --git a/xbob/io/test/test_video.py b/xbob/io/test/test_video.py index f13a3cc..b53afc3 100644 --- a/xbob/io/test/test_video.py +++ b/xbob/io/test/test_video.py @@ -100,6 +100,17 @@ def test_video_reader_str(): assert repr(iv) assert str(iv) +@testutils.ffmpeg_found() +def test_iteration(): + + from .. import load, VideoReader + f = VideoReader(INPUT_VIDEO) + objs = load(INPUT_VIDEO) + + nose.tools.eq_(len(f), len(objs)) + for l, i in zip(objs, f): + assert numpy.allclose(l, i) + @testutils.ffmpeg_found() def test_indexing(): diff --git a/xbob/io/videoreader.cpp b/xbob/io/videoreader.cpp index 8856aad..c20f5a7 100644 --- a/xbob/io/videoreader.cpp +++ b/xbob/io/videoreader.cpp @@ -535,6 +535,110 @@ static PyMappingMethods PyBobIoVideoReader_Mapping = { 0 /* (objobjargproc)PyBobIoVideoReader_SetItem //mp_ass_subscript */ }; +/***************************************** + * Definition of Iterator to VideoReader * + *****************************************/ + +#define VIDEOITERTYPE_NAME VideoReader.iter +PyDoc_STRVAR(s_videoreaderiterator_str, BOOST_PP_STRINGIZE(XBOB_IO_MODULE_PREFIX) "." BOOST_PP_STRINGIZE(VIDEOITERTYPE_NAME)); + +static PyObject* PyBobIoVideoReaderIterator_New(PyTypeObject* type, PyObject*, PyObject*) { + + /* Allocates the python object itself */ + PyBobIoVideoReaderIteratorObject* self = (PyBobIoVideoReaderIteratorObject*)type->tp_alloc(type, 0); + + self->iter.reset(); + + return reinterpret_cast<PyObject*>(self); +} + +static PyObject* PyBobIoVideoReaderIterator_Iter (PyBobIoVideoReaderIteratorObject* self) { + Py_INCREF(self); + return reinterpret_cast<PyObject*>(self); +} + +static PyObject* PyBobIoVideoReaderIterator_Next (PyBobIoVideoReaderIteratorObject* self) { + + if (*self->iter == self->pyreader->v->end()) { + self->iter->reset(); + self->iter.reset(); + Py_XDECREF((PyObject*)self->pyreader); + return 0; + } + + const bob::core::array::typeinfo& info = self->pyreader->v->frame_type(); + + npy_intp shape[NPY_MAXDIMS]; + for (int k=0; k<info.nd; ++k) shape[k] = info.shape[k]; + + int type_num = PyBobIo_AsTypenum(info.dtype); + if (type_num == NPY_NOTYPE) return 0; ///< failure + + PyObject* retval = PyArray_SimpleNew(info.nd, shape, type_num); + if (!retval) return 0; + + try { + bobskin skin(retval, info.dtype); + self->iter->read(skin); + } + catch (std::exception& e) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading frame %" PY_FORMAT_SIZE_T "d from file `%s': %s", self->iter->cur(), self->pyreader->v->filename().c_str(), e.what()); + Py_DECREF(retval); + return 0; + } + catch (...) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading frame #%" PY_FORMAT_SIZE_T "d from file `%s'", self->iter->cur(), self->pyreader->v->filename().c_str()); + Py_DECREF(retval); + return 0; + } + + return retval; + +} + +PyTypeObject PyBobIoVideoReaderIterator_Type = { + PyObject_HEAD_INIT(0) + 0, /* ob_size */ + s_videoreaderiterator_str, /* tp_name */ + sizeof(PyBobIoVideoReaderIteratorObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)PyBobIoVideoReaderIterator_Iter, /* tp_iter */ + (iternextfunc)PyBobIoVideoReaderIterator_Next /* tp_iternext */ +}; + +static PyObject* PyBobIoVideoReader_Iter (PyBobIoVideoReaderObject* self) { + + /* Allocates the python object itself */ + PyBobIoVideoReaderIteratorObject* retval = (PyBobIoVideoReaderIteratorObject*)PyBobIoVideoReaderIterator_New(&PyBobIoVideoReaderIterator_Type, 0, 0); + if (!retval) return 0; + + Py_INCREF(self); + retval->pyreader = self; + retval->iter.reset(new bob::io::VideoReader::const_iterator(self->v->begin())); + return reinterpret_cast<PyObject*>(retval); +} + PyTypeObject PyBobIoVideoReader_Type = { PyObject_HEAD_INIT(0) 0, /*ob_size*/ @@ -562,7 +666,7 @@ PyTypeObject PyBobIoVideoReader_Type = { 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ + (getiterfunc)PyBobIoVideoReader_Iter, /* tp_iter */ 0, /* tp_iternext */ PyBobIoVideoReader_Methods, /* tp_methods */ 0, /* tp_members */ @@ -577,11 +681,4 @@ PyTypeObject PyBobIoVideoReader_Type = { PyBobIoVideoReader_New, /* tp_new */ }; -/** - .def("__iter__", &bob::io::VideoReader::begin, with_custodian_and_ward_postcall<0,1>()) - - .def("__getitem__", &videoreader_getslice) - -**/ - #endif /* WITH_FFMPEG */ -- GitLab