diff --git a/xbob/io/file.cpp b/xbob/io/file.cpp index f551810c724937eb9a1419ffdff2cd3693d375ab..fc38194e21f3aa31922fe1c420cacb60013090aa 100644 --- a/xbob/io/file.cpp +++ b/xbob/io/file.cpp @@ -243,15 +243,40 @@ static PyObject* PyBobIoFile_GetSlice (PyBobIoFileObject* self, Py_ssize_t start PyObject* retval = PyList_New(length); if (!retval) return 0; + const bob::core::array::typeinfo& info = self->f->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 + Py_ssize_t counter = 0; for (auto i = start; (start<=stop)?i<stop:i>stop; i+=step) { - PyObject* item = PyBobIoFile_GetIndex(self, i); + PyObject* item = PyArray_SimpleNew(info.nd, shape, type_num); if (!item) { Py_DECREF(retval); return 0; } + try { + bobskin skin(item, info.dtype); + self->f->read(skin, i); + } + catch (std::exception& e) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading object #%" PY_FORMAT_SIZE_T "d from file `%s': %s", i, self->f->filename().c_str(), e.what()); + Py_DECREF(retval); + Py_DECREF(item); + return 0; + } + catch (...) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading object #%" PY_FORMAT_SIZE_T "d from file `%s'", i, self->f->filename().c_str()); + Py_DECREF(retval); + Py_DECREF(item); + return 0; + } + PyList_SET_ITEM(retval, counter++, item); } diff --git a/xbob/io/test/test_file.py b/xbob/io/test/test_file.py index 6046492e2f66772040da66a5a3927b3b127477c3..77971923059b73a6560bbec44e050efd710bde62 100644 --- a/xbob/io/test/test_file.py +++ b/xbob/io/test/test_file.py @@ -24,59 +24,79 @@ def test_peek(): assert peek_all(f) == (numpy.uint16, (3,3), (3,1)) def test_indexing(): - - f = File(testutils.datafile('matlab_2d.hdf5', __name__), 'r') + + fname = testutils.datafile('matlab_2d.hdf5', __name__) + f = File(fname, 'r') nose.tools.eq_(len(f), 512) - objs = f[:] + objs = load(fname) nose.tools.eq_(len(f), len(objs)) - obj0 = f[0] - obj1 = f[1] # simple indexing - assert numpy.allclose(objs[0], obj0) - assert numpy.allclose(objs[1], obj1) - assert numpy.allclose(f[len(f)-1], f[-1]) - assert numpy.allclose(f[len(f)-2], f[-2]) + assert numpy.allclose(f[0], objs[0]) + assert numpy.allclose(f[1], objs[1]) + assert numpy.allclose(f[-1], objs[-1]) + assert numpy.allclose(f[-2], objs[-2]) + +def test_slicing_0(): + + fname = testutils.datafile('matlab_2d.hdf5', __name__) + f = File(fname, 'r') + + objs = f[:] + for i, k in enumerate(load(fname)): + assert numpy.allclose(k, objs[i]) + +def test_slicing_1(): + + fname = testutils.datafile('matlab_2d.hdf5', __name__) + f = File(fname, 'r') # get slice s1 = f[3:10:2] nose.tools.eq_(len(s1), 4) - assert numpy.allclose(s1[0], objs[3]) - assert numpy.allclose(s1[1], objs[5]) - assert numpy.allclose(s1[2], objs[7]) - assert numpy.allclose(s1[3], objs[9]) + assert numpy.allclose(s1[0], f[3]) + assert numpy.allclose(s1[1], f[5]) + assert numpy.allclose(s1[2], f[7]) + assert numpy.allclose(s1[3], f[9]) + +def test_slicing_2(): + + fname = testutils.datafile('matlab_2d.hdf5', __name__) + f = File(fname, 'r') # get negative slicing - s2 = f[-10:-2:3] - nose.tools.eq_(len(s2), 3) - assert numpy.allclose(s2[0], f[len(f)-10]) - assert numpy.allclose(s2[1], f[len(f)-7]) - assert numpy.allclose(s2[2], f[len(f)-4]) + s = f[-10:-2:3] + nose.tools.eq_(len(s), 3) + assert numpy.allclose(s[0], f[len(f)-10]) + assert numpy.allclose(s[1], f[len(f)-7]) + assert numpy.allclose(s[2], f[len(f)-4]) + +def test_slicing_3(): + + fname = testutils.datafile('matlab_2d.hdf5', __name__) + f = File(fname, 'r') # get negative stepping slice - s3 = f[20:10:-3] - nose.tools.eq_(len(s3), 4) - assert numpy.allclose(s3[0], f[20]) - assert numpy.allclose(s3[1], f[17]) - assert numpy.allclose(s3[2], f[14]) - assert numpy.allclose(s3[3], f[11]) - - # get negative indexing and positive stepping slice - s4 = f[-20:-10:3] - nose.tools.eq_(len(s4), 4) - assert numpy.allclose(s4[0], f[len(f)-20]) - assert numpy.allclose(s4[1], f[len(f)-17]) - assert numpy.allclose(s4[2], f[len(f)-14]) - assert numpy.allclose(s4[3], f[len(f)-11]) + s = f[20:10:-3] + nose.tools.eq_(len(s), 4) + assert numpy.allclose(s[0], f[20]) + assert numpy.allclose(s[1], f[17]) + assert numpy.allclose(s[2], f[14]) + assert numpy.allclose(s[3], f[11]) + +def test_slicing_4(): + + fname = testutils.datafile('matlab_2d.hdf5', __name__) + f = File(fname, 'r') # get all negative slice - s5 = f[-10:-20:-3] - nose.tools.eq_(len(s5), 4) - assert numpy.allclose(s5[0], f[len(f)-10]) - assert numpy.allclose(s5[1], f[len(f)-13]) - assert numpy.allclose(s5[2], f[len(f)-16]) - assert numpy.allclose(s5[3], f[len(f)-19]) + s = f[-10:-20:-3] + nose.tools.eq_(len(s), 4) + assert numpy.allclose(s[0], f[len(f)-10]) + assert numpy.allclose(s[1], f[len(f)-13]) + assert numpy.allclose(s[2], f[len(f)-16]) + assert numpy.allclose(s[3], f[len(f)-19]) @nose.tools.raises(TypeError) def test_indexing_type_check(): diff --git a/xbob/io/test/test_video.py b/xbob/io/test/test_video.py index 323b946ce7a64df4673de7af107534c123925e5e..f13a3cc2ac5288d8687045bdf7d12096a3bfd048 100644 --- a/xbob/io/test/test_video.py +++ b/xbob/io/test/test_video.py @@ -100,11 +100,94 @@ def test_video_reader_str(): assert repr(iv) assert str(iv) +@testutils.ffmpeg_found() +def test_indexing(): + + from .. import VideoReader + f = VideoReader(INPUT_VIDEO) + + nose.tools.eq_(len(f), 375) + + objs = f[:10] + nose.tools.eq_(len(objs), 10) + obj0 = f[0] + obj1 = f[1] + + # simple indexing + assert numpy.allclose(objs[0], obj0) + assert numpy.allclose(objs[1], obj1) + assert numpy.allclose(f[len(f)-1], f[-1]) + assert numpy.allclose(f[len(f)-2], f[-2]) + +@testutils.ffmpeg_found() +def test_slicing_0(): + + from .. import load, VideoReader + f = VideoReader(INPUT_VIDEO) + + objs = f[:] + for i, k in enumerate(load(INPUT_VIDEO)): + assert numpy.allclose(k, objs[i]) + +@testutils.ffmpeg_found() +def test_slicing_1(): + + from .. import VideoReader + f = VideoReader(INPUT_VIDEO) + + s = f[3:10:2] + nose.tools.eq_(len(s), 4) + assert numpy.allclose(s[0], f[3]) + assert numpy.allclose(s[1], f[5]) + assert numpy.allclose(s[2], f[7]) + assert numpy.allclose(s[3], f[9]) + +@testutils.ffmpeg_found() +def test_slicing_2(): + + from .. import VideoReader + f = VideoReader(INPUT_VIDEO) + + s = f[-10:-2:3] + nose.tools.eq_(len(s), 3) + assert numpy.allclose(s[0], f[len(f)-10]) + assert numpy.allclose(s[1], f[len(f)-7]) + assert numpy.allclose(s[2], f[len(f)-4]) + +@testutils.ffmpeg_found() +def test_slicing_3(): + + from .. import VideoReader + f = VideoReader(INPUT_VIDEO) + objs = f.load() + + # get negative stepping slice + s = f[20:10:-3] + nose.tools.eq_(len(s), 4) + assert numpy.allclose(s[0], f[20]) + assert numpy.allclose(s[1], f[17]) + assert numpy.allclose(s[2], f[14]) + assert numpy.allclose(s[3], f[11]) + +@testutils.ffmpeg_found() +def test_slicing_4(): + + from .. import VideoReader + f = VideoReader(INPUT_VIDEO) + objs = f[:21] + + # get all negative slice + s = f[-10:-20:-3] + nose.tools.eq_(len(s), 4) + assert numpy.allclose(s[0], f[len(f)-10]) + assert numpy.allclose(s[1], f[len(f)-13]) + assert numpy.allclose(s[2], f[len(f)-16]) + assert numpy.allclose(s[3], f[len(f)-19]) + + @testutils.ffmpeg_found() def test_can_use_array_interface(): - # This shows you can use the array interface to read an entire video - # sequence in a single shot from .. import load, VideoReader array = load(INPUT_VIDEO) iv = VideoReader(INPUT_VIDEO) diff --git a/xbob/io/videoreader.cpp b/xbob/io/videoreader.cpp index fdb2f4462e16662e6a560140c761936adbbc5b5d..8856aadbffc4ce0829dbadedfb8acf13eab91abe 100644 --- a/xbob/io/videoreader.cpp +++ b/xbob/io/videoreader.cpp @@ -129,8 +129,8 @@ PyDoc_STRVAR(s_width_str, "width"); PyDoc_STRVAR(s_width_doc, "[int] The width of each frame in the video (a multiple of 2)"); -PyObject* PyBobIoVideoReader_Len(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("n", self->v->numberOfFrames()); +Py_ssize_t PyBobIoVideoReader_Len(PyBobIoVideoReaderObject* self) { + return self->v->numberOfFrames(); } PyDoc_STRVAR(s_number_of_frames_str, "number_of_frames"); @@ -412,7 +412,9 @@ static PyMethodDef PyBobIoVideoReader_Methods[] = { {0} /* Sentinel */ }; -static PyObject* PyBobIoVideoReader_GetItem (PyBobIoVideoReaderObject* self, Py_ssize_t i) { +static PyObject* PyBobIoVideoReader_GetIndex (PyBobIoVideoReaderObject* self, Py_ssize_t i) { + + if (i < 0) i += self->v->numberOfFrames(); ///< adjust for negative indexing if (i < 0 || i >= self->v->numberOfFrames()) { PyErr_Format(PyExc_IndexError, "video frame index out of range - `%s' only contains %" PY_FORMAT_SIZE_T "d frame(s)", self->v->filename().c_str(), self->v->numberOfFrames()); @@ -451,17 +453,10 @@ static PyObject* PyBobIoVideoReader_GetItem (PyBobIoVideoReaderObject* self, Py_ } -static PyObject* PyBobIoVideoReader_GetSlice (PyBobIoVideoReaderObject* self, Py_ssize_t lo, Py_ssize_t hi) { - - if (lo < 0 || lo >= self->v->numberOfFrames()) { - PyErr_Format(PyExc_IndexError, "video frame index out of range - `%s' only contains %" PY_FORMAT_SIZE_T "d frame(s)", self->v->filename().c_str(), self->v->numberOfFrames()); - return 0; - } +static PyObject* PyBobIoVideoReader_GetSlice (PyBobIoVideoReaderObject* self, Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step, Py_ssize_t length) { - if (hi < 0 || hi >= self->v->numberOfFrames()) { - PyErr_Format(PyExc_IndexError, "video frame index out of range - `%s' only contains %" PY_FORMAT_SIZE_T "d frame(s)", self->v->filename().c_str(), self->v->numberOfFrames()); - return 0; - } + PyObject* retval = PyList_New(length); + if (!retval) return 0; const bob::core::array::typeinfo& info = self->v->frame_type(); @@ -471,55 +466,73 @@ static PyObject* PyBobIoVideoReader_GetSlice (PyBobIoVideoReaderObject* self, Py int type_num = PyBobIo_AsTypenum(info.dtype); if (type_num == NPY_NOTYPE) return 0; ///< failure - PyObject* retval = PyList_New(0); - if (!retval) return 0; + Py_ssize_t counter = 0; + Py_ssize_t lo, hi, st; + auto it = self->v->begin(); - PyObject* frame = 0; + if (start <= stop) lo = start, hi = stop, st = step, it += lo; + else lo = stop, hi = start, st = -step, it += lo + (hi-lo)%st; - try { - auto it = self->v->begin(); - it += lo; - for (size_t i=lo; it.parent() && i<hi; ++i) { - Check_Interrupt(); - frame = PyArray_SimpleNew(info.nd, shape, type_num); - if (!frame) { - Py_DECREF(retval); - return 0; - } - bobskin skin(frame, info.dtype); + for (auto i=lo; i<hi; i+=st) { + + PyObject* item = PyArray_SimpleNew(info.nd, shape, type_num); + if (!item) { + Py_DECREF(retval); + return 0; + } + + try { + bobskin skin(item, info.dtype); it.read(skin); - int ok = PyList_Append(retval, frame); - Py_DECREF(frame); - frame = 0; - if (ok != 0) { - Py_DECREF(retval); - return 0; - } + it += (st-1); } - } - catch (std::exception& e) { - if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading slice [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d[ from file `%s': %s", lo, hi, self->v->filename().c_str(), e.what()); - Py_XDECREF(frame); - Py_DECREF(retval); - return 0; - } - catch (...) { - if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught std::exception while reading slice [%" PY_FORMAT_SIZE_T "d, %" PY_FORMAT_SIZE_T "d[ from file `%s'", lo, hi, self->v->filename().c_str()); - Py_XDECREF(frame); - Py_DECREF(retval); - return 0; + 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", i, self->v->filename().c_str(), e.what()); + Py_DECREF(retval); + Py_DECREF(item); + return 0; + } + catch (...) { + if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading frame #%" PY_FORMAT_SIZE_T "d from file `%s'", i, self->v->filename().c_str()); + Py_DECREF(retval); + Py_DECREF(item); + return 0; + } + + PyList_SET_ITEM(retval, counter++, item); + } + if (st == -step) PyList_Reverse(retval); + return retval; } -static PySequenceMethods PyBobIoVideoReader_Sequence = { - (lenfunc)PyBobIoVideoReader_Len, - 0, /* concat */ - 0, /* repeat */ - (ssizeargfunc)PyBobIoVideoReader_GetItem, - (ssizessizeargfunc)PyBobIoVideoReader_GetSlice +static PyObject* PyBobIoVideoReader_GetItem (PyBobIoVideoReaderObject* self, PyObject* item) { + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) return 0; + return PyBobIoVideoReader_GetIndex(self, i); + } + if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + if (PySlice_GetIndicesEx((PySliceObject*)item, self->v->numberOfFrames(), + &start, &stop, &step, &slicelength) < 0) return 0; + if (slicelength <= 0) return PyList_New(0); + return PyBobIoVideoReader_GetSlice(self, start, stop, step, slicelength); + } + else { + PyErr_Format(PyExc_TypeError, "VideoReader indices must be integers, not %.200s", + item->ob_type->tp_name); + return 0; + } +} + +static PyMappingMethods PyBobIoVideoReader_Mapping = { + (lenfunc)PyBobIoVideoReader_Len, //mp_lenght + (binaryfunc)PyBobIoVideoReader_GetItem, //mp_subscript + 0 /* (objobjargproc)PyBobIoVideoReader_SetItem //mp_ass_subscript */ }; PyTypeObject PyBobIoVideoReader_Type = { @@ -535,8 +548,8 @@ PyTypeObject PyBobIoVideoReader_Type = { 0, /*tp_compare*/ (reprfunc)PyBobIoVideoReader_Repr, /*tp_repr*/ 0, /*tp_as_number*/ - &PyBobIoVideoReader_Sequence, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ + 0, /*tp_as_sequence*/ + &PyBobIoVideoReader_Mapping, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ (reprfunc)PyBobIoVideoReader_Print, /*tp_str*/