diff --git a/bob/learn/misc/cpp/IVectorMachine.cpp b/bob/learn/misc/cpp/IVectorMachine.cpp index b717c70de0ccad8f4f6163b5b46e5b05b27612ff..933cfe9870f9f249925969724eaff53ddfc9ad01 100644 --- a/bob/learn/misc/cpp/IVectorMachine.cpp +++ b/bob/learn/misc/cpp/IVectorMachine.cpp @@ -18,7 +18,7 @@ bob::learn::misc::IVectorMachine::IVectorMachine() bob::learn::misc::IVectorMachine::IVectorMachine(const boost::shared_ptr<bob::learn::misc::GMMMachine> ubm, const size_t rt, const double variance_threshold): m_ubm(ubm), m_rt(rt), - m_T(getDimCD(),rt), m_sigma(getDimCD()), + m_T(getSupervectorLength(),rt), m_sigma(getSupervectorLength()), m_variance_threshold(variance_threshold) { resizePrecompute(); @@ -204,7 +204,7 @@ void bob::learn::misc::IVectorMachine::resizeTmp() void bob::learn::misc::IVectorMachine::forward(const bob::learn::misc::GMMStats& gs, blitz::Array<double,1>& ivector) const { - bob::core::array::assertSameDimensionLength(ivector.extent(0), (int)m_rt); + bob::core::array::assertSameDimensionLength(ivector.extent(0), (int)m_rt); forward_(gs, ivector); } @@ -214,7 +214,7 @@ void bob::learn::misc::IVectorMachine::computeIdTtSigmaInvT( // Computes \f$(Id + \sum_{c=1}^{C} N_{i,j,c} T^{T} \Sigma_{c}^{-1} T)\f$ blitz::Range rall = blitz::Range::all(); bob::math::eye(output); - for (int c=0; c<(int)getDimC(); ++c) + for (int c=0; c<(int)getNGaussians(); ++c) output += gs.n(c) * m_cache_Tct_sigmacInv_Tc(c, rall, rall); } @@ -224,7 +224,7 @@ void bob::learn::misc::IVectorMachine::computeTtSigmaInvFnorm( // Computes \f$T^{T} \Sigma^{-1} \sum_{c=1}^{C} (F_c - N_c ubmmean_{c})\f$ blitz::Range rall = blitz::Range::all(); output = 0; - for (int c=0; c<(int)getDimC(); ++c) + for (int c=0; c<(int)getNGaussians(); ++c) { m_tmp_d = gs.sumPx(c,rall) - gs.n(c) * m_ubm->getGaussian(c)->getMean(); blitz::Array<double,2> Tct_sigmacInv = m_cache_Tct_sigmacInv(c, rall, rall); diff --git a/bob/learn/misc/gmm_machine.cpp b/bob/learn/misc/gmm_machine.cpp index 356a800e0a19a596e411ca5a7e5d9973ecb659a7..9bea2a88943616ed71568945b8d797c7828f99c2 100644 --- a/bob/learn/misc/gmm_machine.cpp +++ b/bob/learn/misc/gmm_machine.cpp @@ -48,13 +48,11 @@ static int PyBobLearnMiscGMMMachine_init_number(PyBobLearnMiscGMMMachineObject* if(n_gaussians < 0){ PyErr_Format(PyExc_TypeError, "gaussians argument must be greater than or equal to zero"); - GMMMachine_doc.print_usage(); return -1; } if(n_inputs < 0){ PyErr_Format(PyExc_TypeError, "input argument must be greater than or equal to zero"); - GMMMachine_doc.print_usage(); return -1; } diff --git a/bob/learn/misc/include/bob.learn.misc/IVectorMachine.h b/bob/learn/misc/include/bob.learn.misc/IVectorMachine.h index efff4b7b8ed160a91ece4e091e55dcd0af4597f7..88dd5c1f6f26e683edd76610a9fdc2434e504665 100644 --- a/bob/learn/misc/include/bob.learn.misc/IVectorMachine.h +++ b/bob/learn/misc/include/bob.learn.misc/IVectorMachine.h @@ -9,7 +9,6 @@ #define BOB_LEARN_MISC_IVECTOR_MACHINE_H #include <blitz/array.h> -#include <bob.learn.misc/Machine.h> #include <bob.learn.misc/GMMMachine.h> #include <bob.learn.misc/GMMStats.h> #include <bob.io.base/HDF5File.h> @@ -24,7 +23,7 @@ namespace bob { namespace learn { namespace misc { * N. Dehak, P. Kenny, R. Dehak, P. Dumouchel, P. Ouellet, * IEEE Trans. on Audio, Speech and Language Processing */ -class IVectorMachine: public bob::learn::misc::Machine<bob::learn::misc::GMMStats, blitz::Array<double,1> > +class IVectorMachine { public: /** @@ -124,7 +123,7 @@ class IVectorMachine: public bob::learn::misc::Machine<bob::learn::misc::GMMStat * @warning An exception is thrown if no Universal Background Model has * been set yet. */ - const size_t getDimC() const + const size_t getNGaussians() const { return m_ubm->getNGaussians(); } /** @@ -132,7 +131,7 @@ class IVectorMachine: public bob::learn::misc::Machine<bob::learn::misc::GMMStat * @warning An exception is thrown if no Universal Background Model has * been set yet. */ - const size_t getDimD() const + const size_t getNInputs() const { return m_ubm->getNInputs(); } /** @@ -141,7 +140,7 @@ class IVectorMachine: public bob::learn::misc::Machine<bob::learn::misc::GMMStat * @warning An exception is thrown if no Universal Background Model has * been set yet. */ - const size_t getDimCD() const + const size_t getSupervectorLength() const { return m_ubm->getNGaussians()*m_ubm->getNInputs(); } /** @@ -229,7 +228,7 @@ class IVectorMachine: public bob::learn::misc::Machine<bob::learn::misc::GMMStat */ void forward_(const bob::learn::misc::GMMStats& input, blitz::Array<double,1>& output) const; - protected: + private: /** * @brief Apply the variance flooring thresholds. * This method is called when using setVarianceThresholds() diff --git a/bob/learn/misc/isv_base.cpp b/bob/learn/misc/isv_base.cpp index ca169377dc62fc6d353e9057f26197686fac63c3..023a6d36b1375de442d32a11bfc4361dc21b137a 100644 --- a/bob/learn/misc/isv_base.cpp +++ b/bob/learn/misc/isv_base.cpp @@ -82,6 +82,11 @@ static int PyBobLearnMiscISVBase_init_ubm(PyBobLearnMiscISVBaseObject* self, PyO return -1; } + if(ru < 0){ + PyErr_Format(PyExc_TypeError, "ru argument must be greater than or equal to one"); + return -1; + } + self->cxx.reset(new bob::learn::misc::ISVBase(ubm->cxx, ru)); return 0; } diff --git a/bob/learn/misc/ivector_machine.cpp b/bob/learn/misc/ivector_machine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eaae8264c71b2373c041e147340d29470c8fe210 --- /dev/null +++ b/bob/learn/misc/ivector_machine.cpp @@ -0,0 +1,522 @@ +/** + * @date Wed Jan 28 17:46:15 2015 +0200 + * @author Tiago de Freitas Pereira <tiago.pereira@idiap.ch> + * + * @brief Python API for bob::learn::em + * + * Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland + */ + +#include "main.h" + +/******************************************************************/ +/************ Constructor Section *********************************/ +/******************************************************************/ + +static auto IVectorMachine_doc = bob::extension::ClassDoc( + BOB_EXT_MODULE_PREFIX ".IVectorMachine", + "An IVectorMachine consists of a Total Variability subspace \f$T\f$ and allows the extraction of IVector" + "References: [Dehak2010]", + "" +).add_constructor( + bob::extension::FunctionDoc( + "__init__", + "Constructor. Builds a new IVectorMachine", + "", + true + ) + .add_prototype("ubm, rt, variance_threshold","") + .add_prototype("other","") + .add_prototype("hdf5","") + + .add_parameter("ubm", ":py:class:`bob.learn.misc.GMMMachine`", "The Universal Background Model.") + .add_parameter("rt", "int", "Size of the Total Variability matrix (CD x rt).") + .add_parameter("variance_threshold", "double", "Variance flooring threshold for the Sigma (diagonal) matrix") + + .add_parameter("other", ":py:class:`bob.learn.misc.IVectorMachine`", "A IVectorMachine object to be copied.") + .add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading") + +); + + +static int PyBobLearnMiscIVectorMachine_init_copy(PyBobLearnMiscIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { + + char** kwlist = IVectorMachine_doc.kwlist(1); + PyBobLearnMiscIVectorMachineObject* o; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnMiscIVectorMachine_Type, &o)){ + IVectorMachine_doc.print_usage(); + return -1; + } + + self->cxx.reset(new bob::learn::misc::IVectorMachine(*o->cxx)); + return 0; +} + + +static int PyBobLearnMiscIVectorMachine_init_hdf5(PyBobLearnMiscIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { + + char** kwlist = IVectorMachine_doc.kwlist(2); + + PyBobIoHDF5FileObject* config = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, &PyBobIoHDF5File_Converter, &config)){ + IVectorMachine_doc.print_usage(); + return -1; + } + + self->cxx.reset(new bob::learn::misc::IVectorMachine(*(config->f))); + + return 0; +} + + +static int PyBobLearnMiscIVectorMachine_init_ubm(PyBobLearnMiscIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { + + char** kwlist = IVectorMachine_doc.kwlist(0); + + PyBobLearnMiscGMMMachineObject* gmm_machine; + int rt = 1; + double variance_threshold = 1e-10; + + //Here we have to select which keyword argument to read + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!i|d", kwlist, &PyBobLearnMiscGMMMachine_Type, &gmm_machine, &rt, &variance_threshold)){ + IVectorMachine_doc.print_usage(); + return -1; + } + + if(rt < 1){ + PyErr_Format(PyExc_TypeError, "rt argument must be greater than or equal to one"); + return -1; + } + + if(variance_threshold <= 0){ + PyErr_Format(PyExc_TypeError, "variance_threshold argument must be greater than zero"); + return -1; + } + + self->cxx.reset(new bob::learn::misc::IVectorMachine(gmm_machine->cxx, rt, variance_threshold)); + return 0; +} + + +static int PyBobLearnMiscIVectorMachine_init(PyBobLearnMiscIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + // get the number of command line arguments + int nargs = (args?PyTuple_Size(args):0) + (kwargs?PyDict_Size(kwargs):0); + + if(nargs == 1){ + //Reading the input argument + PyObject* arg = 0; + if (PyTuple_Size(args)) + arg = PyTuple_GET_ITEM(args, 0); + else { + PyObject* tmp = PyDict_Values(kwargs); + auto tmp_ = make_safe(tmp); + arg = PyList_GET_ITEM(tmp, 0); + } + + // If the constructor input is Gaussian object + if (PyBobLearnMiscIVectorMachine_Check(arg)) + return PyBobLearnMiscIVectorMachine_init_copy(self, args, kwargs); + // If the constructor input is a HDF5 + else + return PyBobLearnMiscIVectorMachine_init_hdf5(self, args, kwargs); + } + else if ((nargs == 2) || (nargs == 3)) + PyBobLearnMiscIVectorMachine_init_ubm(self, args, kwargs); + else{ + PyErr_Format(PyExc_RuntimeError, "number of arguments mismatch - %s requires 1,2 or 3 argument, but you provided %d (see help)", Py_TYPE(self)->tp_name, nargs); + IVectorMachine_doc.print_usage(); + return -1; + } + + BOB_CATCH_MEMBER("cannot create IVectorMachine", 0) + return 0; +} + +static void PyBobLearnMiscIVectorMachine_delete(PyBobLearnMiscIVectorMachineObject* self) { + self->cxx.reset(); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject* PyBobLearnMiscIVectorMachine_RichCompare(PyBobLearnMiscIVectorMachineObject* self, PyObject* other, int op) { + BOB_TRY + + if (!PyBobLearnMiscIVectorMachine_Check(other)) { + PyErr_Format(PyExc_TypeError, "cannot compare `%s' with `%s'", Py_TYPE(self)->tp_name, Py_TYPE(other)->tp_name); + return 0; + } + auto other_ = reinterpret_cast<PyBobLearnMiscIVectorMachineObject*>(other); + switch (op) { + case Py_EQ: + if (*self->cxx==*other_->cxx) Py_RETURN_TRUE; else Py_RETURN_FALSE; + case Py_NE: + if (*self->cxx==*other_->cxx) Py_RETURN_FALSE; else Py_RETURN_TRUE; + default: + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + BOB_CATCH_MEMBER("cannot compare IVectorMachine objects", 0) +} + +int PyBobLearnMiscIVectorMachine_Check(PyObject* o) { + return PyObject_IsInstance(o, reinterpret_cast<PyObject*>(&PyBobLearnMiscIVectorMachine_Type)); +} + + +/******************************************************************/ +/************ Variables Section ***********************************/ +/******************************************************************/ + +/***** shape *****/ +static auto shape = bob::extension::VariableDoc( + "shape", + "(int,int, int)", + "A tuple that represents the number of gaussians, dimensionality of each Gaussian, dimensionality of the rT (total variability matrix) ``(#Gaussians, #Inputs, #rT)``.", + "" +); +PyObject* PyBobLearnMiscIVectorMachine_getShape(PyBobLearnMiscIVectorMachineObject* self, void*) { + BOB_TRY + return Py_BuildValue("(i,i,i)", self->cxx->getNGaussians(), self->cxx->getNInputs(), self->cxx->getDimRt()); + BOB_CATCH_MEMBER("shape could not be read", 0) +} + +/***** supervector_length *****/ +static auto supervector_length = bob::extension::VariableDoc( + "supervector_length", + "int", + + "Returns the supervector length." + "NGaussians x NInputs: Number of Gaussian components by the feature dimensionality", + + "@warning An exception is thrown if no Universal Background Model has been set yet." +); +PyObject* PyBobLearnMiscIVectorMachine_getSupervectorLength(PyBobLearnMiscIVectorMachineObject* self, void*) { + BOB_TRY + return Py_BuildValue("i", self->cxx->getSupervectorLength()); + BOB_CATCH_MEMBER("supervector_length could not be read", 0) +} + + +/***** T *****/ +static auto T = bob::extension::VariableDoc( + "t", + "array_like <float, 2D>", + "Returns the Total Variability matrix", + "" +); +PyObject* PyBobLearnMiscIVectorMachine_getT(PyBobLearnMiscIVectorMachineObject* self, void*){ + BOB_TRY + return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getT()); + BOB_CATCH_MEMBER("`t` could not be read", 0) +} +int PyBobLearnMiscIVectorMachine_setT(PyBobLearnMiscIVectorMachineObject* self, PyObject* value, void*){ + BOB_TRY + PyBlitzArrayObject* o; + if (!PyBlitzArray_Converter(value, &o)){ + PyErr_Format(PyExc_RuntimeError, "%s %s expects a 2D array of floats", Py_TYPE(self)->tp_name, T.name()); + return -1; + } + auto o_ = make_safe(o); + auto b = PyBlitzArrayCxx_AsBlitz<double,2>(o, "t"); + if (!b) return -1; + self->cxx->setT(*b); + return 0; + BOB_CATCH_MEMBER("`t` vector could not be set", -1) +} + + +/***** sigma *****/ +static auto sigma = bob::extension::VariableDoc( + "sigma", + "array_like <float, 1D>", + "The residual matrix of the model sigma", + "" +); +PyObject* PyBobLearnMiscIVectorMachine_getSigma(PyBobLearnMiscIVectorMachineObject* self, void*){ + BOB_TRY + return PyBlitzArrayCxx_AsConstNumpy(self->cxx->getSigma()); + BOB_CATCH_MEMBER("`sigma` could not be read", 0) +} +int PyBobLearnMiscIVectorMachine_setSigma(PyBobLearnMiscIVectorMachineObject* self, PyObject* value, void*){ + BOB_TRY + PyBlitzArrayObject* o; + if (!PyBlitzArray_Converter(value, &o)){ + PyErr_Format(PyExc_RuntimeError, "%s %s expects a 1D array of floats", Py_TYPE(self)->tp_name, sigma.name()); + return -1; + } + auto o_ = make_safe(o); + auto b = PyBlitzArrayCxx_AsBlitz<double,1>(o, "sigma"); + if (!b) return -1; + self->cxx->setSigma(*b); + return 0; + BOB_CATCH_MEMBER("`sigma` vector could not be set", -1) +} + + +/***** variance_threshold *****/ +static auto variance_threshold = bob::extension::VariableDoc( + "variance_threshold", + "double", + "Threshold for the variance contained in sigma", + "" +); +PyObject* PyBobLearnMiscIVectorMachine_getVarianceThreshold(PyBobLearnMiscIVectorMachineObject* self, void*) { + BOB_TRY + return Py_BuildValue("d", self->cxx->getVarianceThreshold()); + BOB_CATCH_MEMBER("variance_threshold could not be read", 0) +} +int PyBobLearnMiscIVectorMachine_setVarianceThreshold(PyBobLearnMiscIVectorMachineObject* self, PyObject* value, void*){ + BOB_TRY + + if (!PyNumber_Check(value)){ + PyErr_Format(PyExc_RuntimeError, "%s %s expects an double", Py_TYPE(self)->tp_name, variance_threshold.name()); + return -1; + } + + if (PyFloat_AS_DOUBLE(value) < 0){ + PyErr_Format(PyExc_TypeError, "variance_threshold must be greater than or equal to zero"); + return -1; + } + + self->cxx->setVarianceThreshold(PyFloat_AS_DOUBLE(value)); + BOB_CATCH_MEMBER("variance_threshold could not be set", -1) + return 0; +} + + + +static PyGetSetDef PyBobLearnMiscIVectorMachine_getseters[] = { + { + shape.name(), + (getter)PyBobLearnMiscIVectorMachine_getShape, + 0, + shape.doc(), + 0 + }, + + { + supervector_length.name(), + (getter)PyBobLearnMiscIVectorMachine_getSupervectorLength, + 0, + supervector_length.doc(), + 0 + }, + + { + T.name(), + (getter)PyBobLearnMiscIVectorMachine_getT, + (setter)PyBobLearnMiscIVectorMachine_setT, + T.doc(), + 0 + }, + + { + variance_threshold.name(), + (getter)PyBobLearnMiscIVectorMachine_getVarianceThreshold, + (setter)PyBobLearnMiscIVectorMachine_setVarianceThreshold, + variance_threshold.doc(), + 0 + }, + + { + sigma.name(), + (getter)PyBobLearnMiscIVectorMachine_getSigma, + (setter)PyBobLearnMiscIVectorMachine_setSigma, + sigma.doc(), + 0 + }, + + + {0} // Sentinel +}; + + +/******************************************************************/ +/************ Functions Section ***********************************/ +/******************************************************************/ + + +/*** save ***/ +static auto save = bob::extension::FunctionDoc( + "save", + "Save the configuration of the IVectorMachine to a given HDF5 file" +) +.add_prototype("hdf5") +.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for writing"); +static PyObject* PyBobLearnMiscIVectorMachine_Save(PyBobLearnMiscIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { + + BOB_TRY + + // get list of arguments + char** kwlist = save.kwlist(0); + PyBobIoHDF5FileObject* hdf5; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; + + auto hdf5_ = make_safe(hdf5); + self->cxx->save(*hdf5->f); + + BOB_CATCH_MEMBER("cannot save the data", 0) + Py_RETURN_NONE; +} + +/*** load ***/ +static auto load = bob::extension::FunctionDoc( + "load", + "Load the configuration of the IVectorMachine to a given HDF5 file" +) +.add_prototype("hdf5") +.add_parameter("hdf5", ":py:class:`bob.io.base.HDF5File`", "An HDF5 file open for reading"); +static PyObject* PyBobLearnMiscIVectorMachine_Load(PyBobLearnMiscIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + char** kwlist = load.kwlist(0); + PyBobIoHDF5FileObject* hdf5; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&", kwlist, PyBobIoHDF5File_Converter, &hdf5)) return 0; + + auto hdf5_ = make_safe(hdf5); + self->cxx->load(*hdf5->f); + + BOB_CATCH_MEMBER("cannot load the data", 0) + Py_RETURN_NONE; +} + + +/*** is_similar_to ***/ +static auto is_similar_to = bob::extension::FunctionDoc( + "is_similar_to", + + "Compares this IVectorMachine with the ``other`` one to be approximately the same.", + "The optional values ``r_epsilon`` and ``a_epsilon`` refer to the " + "relative and absolute precision for the ``weights``, ``biases`` " + "and any other values internal to this machine." +) +.add_prototype("other, [r_epsilon], [a_epsilon]","output") +.add_parameter("other", ":py:class:`bob.learn.misc.IVectorMachine`", "A IVectorMachine object to be compared.") +.add_parameter("r_epsilon", "float", "Relative precision.") +.add_parameter("a_epsilon", "float", "Absolute precision.") +.add_return("output","bool","True if it is similar, otherwise false."); +static PyObject* PyBobLearnMiscIVectorMachine_IsSimilarTo(PyBobLearnMiscIVectorMachineObject* self, PyObject* args, PyObject* kwds) { + + /* Parses input arguments in a single shot */ + char** kwlist = is_similar_to.kwlist(0); + + //PyObject* other = 0; + PyBobLearnMiscIVectorMachineObject* other = 0; + double r_epsilon = 1.e-5; + double a_epsilon = 1.e-8; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|dd", kwlist, + &PyBobLearnMiscIVectorMachine_Type, &other, + &r_epsilon, &a_epsilon)){ + + is_similar_to.print_usage(); + return 0; + } + + if (self->cxx->is_similar_to(*other->cxx, r_epsilon, a_epsilon)) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + + + +/*** forward ***/ +static auto forward = bob::extension::FunctionDoc( + "forward", + "Execute the machine", + "", + true +) +.add_prototype("stats") +.add_parameter("stats", ":py:class:`bob.learn.misc.GMMStats`", "Statistics as input"); +static PyObject* PyBobLearnMiscIVectorMachine_Forward(PyBobLearnMiscIVectorMachineObject* self, PyObject* args, PyObject* kwargs) { + BOB_TRY + + char** kwlist = forward.kwlist(0); + + PyBobLearnMiscGMMStatsObject* stats = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &PyBobLearnMiscGMMStats_Type, &stats)) + Py_RETURN_NONE; + + blitz::Array<double,1> ivector(self->cxx->getDimRt()); + self->cxx->forward(*stats->cxx, ivector); + + return PyBlitzArrayCxx_AsConstNumpy(ivector); + + BOB_CATCH_MEMBER("cannot forward", 0) + +} + + +static PyMethodDef PyBobLearnMiscIVectorMachine_methods[] = { + { + save.name(), + (PyCFunction)PyBobLearnMiscIVectorMachine_Save, + METH_VARARGS|METH_KEYWORDS, + save.doc() + }, + { + load.name(), + (PyCFunction)PyBobLearnMiscIVectorMachine_Load, + METH_VARARGS|METH_KEYWORDS, + load.doc() + }, + { + is_similar_to.name(), + (PyCFunction)PyBobLearnMiscIVectorMachine_IsSimilarTo, + METH_VARARGS|METH_KEYWORDS, + is_similar_to.doc() + }, + +/* + { + forward.name(), + (PyCFunction)PyBobLearnMiscIVectorMachine_Forward, + METH_VARARGS|METH_KEYWORDS, + forward.doc() + },*/ + + + {0} /* Sentinel */ +}; + + +/******************************************************************/ +/************ Module Section **************************************/ +/******************************************************************/ + +// Define the JFA type struct; will be initialized later +PyTypeObject PyBobLearnMiscIVectorMachine_Type = { + PyVarObject_HEAD_INIT(0,0) + 0 +}; + +bool init_BobLearnMiscIVectorMachine(PyObject* module) +{ + // initialize the type struct + PyBobLearnMiscIVectorMachine_Type.tp_name = IVectorMachine_doc.name(); + PyBobLearnMiscIVectorMachine_Type.tp_basicsize = sizeof(PyBobLearnMiscIVectorMachineObject); + PyBobLearnMiscIVectorMachine_Type.tp_flags = Py_TPFLAGS_DEFAULT; + PyBobLearnMiscIVectorMachine_Type.tp_doc = IVectorMachine_doc.doc(); + + // set the functions + PyBobLearnMiscIVectorMachine_Type.tp_new = PyType_GenericNew; + PyBobLearnMiscIVectorMachine_Type.tp_init = reinterpret_cast<initproc>(PyBobLearnMiscIVectorMachine_init); + PyBobLearnMiscIVectorMachine_Type.tp_dealloc = reinterpret_cast<destructor>(PyBobLearnMiscIVectorMachine_delete); + PyBobLearnMiscIVectorMachine_Type.tp_richcompare = reinterpret_cast<richcmpfunc>(PyBobLearnMiscIVectorMachine_RichCompare); + PyBobLearnMiscIVectorMachine_Type.tp_methods = PyBobLearnMiscIVectorMachine_methods; + PyBobLearnMiscIVectorMachine_Type.tp_getset = PyBobLearnMiscIVectorMachine_getseters; + PyBobLearnMiscIVectorMachine_Type.tp_call = reinterpret_cast<ternaryfunc>(PyBobLearnMiscIVectorMachine_Forward); + + + // check that everything is fine + if (PyType_Ready(&PyBobLearnMiscIVectorMachine_Type) < 0) return false; + + // add the type to the module + Py_INCREF(&PyBobLearnMiscIVectorMachine_Type); + return PyModule_AddObject(module, "IVectorMachine", (PyObject*)&PyBobLearnMiscIVectorMachine_Type) >= 0; +} + diff --git a/bob/learn/misc/jfa_base.cpp b/bob/learn/misc/jfa_base.cpp index d995114e65cee760d96697e5645903243a644e05..5793919e134042ed6e2840e90847e74e4bd1eb3e 100644 --- a/bob/learn/misc/jfa_base.cpp +++ b/bob/learn/misc/jfa_base.cpp @@ -84,6 +84,16 @@ static int PyBobLearnMiscJFABase_init_ubm(PyBobLearnMiscJFABaseObject* self, PyO return -1; } + if(ru < 0){ + PyErr_Format(PyExc_TypeError, "ru argument must be greater than or equal to one"); + return -1; + } + + if(rv < 0){ + PyErr_Format(PyExc_TypeError, "rv argument must be greater than or equal to one"); + return -1; + } + self->cxx.reset(new bob::learn::misc::JFABase(ubm->cxx, ru, rv)); return 0; } diff --git a/bob/learn/misc/main.cpp b/bob/learn/misc/main.cpp index f57fdfe3f96beb72d0f3eaeb9870793f3c5ac4f2..316a9c17a743f2a780c21a6c85ad586710097f7e 100644 --- a/bob/learn/misc/main.cpp +++ b/bob/learn/misc/main.cpp @@ -52,7 +52,8 @@ static PyObject* create_module (void) { if (!init_BobLearnMiscISVBase(module)) return 0; if (!init_BobLearnMiscJFAMachine(module)) return 0; - if (!init_BobLearnMiscISVMachine(module)) return 0; + if (!init_BobLearnMiscISVMachine(module)) return 0; + if (!init_BobLearnMiscIVectorMachine(module)) return 0; static void* PyBobLearnMisc_API[PyBobLearnMisc_API_pointers]; diff --git a/bob/learn/misc/main.h b/bob/learn/misc/main.h index aa36669e70d44455100f17211043fa698fad586a..50b5de618822780333b9013a2c41b40d86ddeb83 100644 --- a/bob/learn/misc/main.h +++ b/bob/learn/misc/main.h @@ -32,6 +32,7 @@ #include <bob.learn.misc/JFAMachine.h> #include <bob.learn.misc/ISVMachine.h> +#include <bob.learn.misc/IVectorMachine.h> #if PY_VERSION_HEX >= 0x03000000 @@ -204,5 +205,16 @@ bool init_BobLearnMiscISVMachine(PyObject* module); int PyBobLearnMiscISVMachine_Check(PyObject* o); +// IVectorMachine +typedef struct { + PyObject_HEAD + boost::shared_ptr<bob::learn::misc::IVectorMachine> cxx; +} PyBobLearnMiscIVectorMachineObject; + +extern PyTypeObject PyBobLearnMiscIVectorMachine_Type; +bool init_BobLearnMiscIVectorMachine(PyObject* module); +int PyBobLearnMiscIVectorMachine_Check(PyObject* o); + + #endif // BOB_LEARN_EM_MAIN_H diff --git a/bob/learn/misc/test_ivector.py b/bob/learn/misc/test_ivector.py index f16638ae36449f9fc9a9558afb10b012e560fe94..fb9c8b23e9afedc8013d89ce254fcea5ec02db3e 100644 --- a/bob/learn/misc/test_ivector.py +++ b/bob/learn/misc/test_ivector.py @@ -32,14 +32,15 @@ class IVectorMachinePy(): def resize(self): if self.m_ubm: - dim_cd = self.m_ubm.dim_c * self.m_ubm.dim_d + dim_cd = self.m_ubm.shape[0] * self.m_ubm.shape[1] self.m_t = numpy.random.randn(dim_cd, self.m_dim_t) self.m_sigma = numpy.random.randn(dim_cd) def precompute(self): if self.m_ubm and self.m_t is not None and self.m_sigma is not None: - dim_c = self.m_ubm.dim_c - dim_d = self.m_ubm.dim_d + #dim_c = self.m_ubm.dim_c + #dim_d = self.m_ubm.dim_d + dim_c,dim_d = self.m_ubm.shape self.m_cache_TtSigmaInv = {} self.m_cache_TtSigmaInvT = {} for c in range(dim_c): @@ -78,13 +79,14 @@ class IVectorMachinePy(): def _get_TtSigmaInv_Fnorm(self, N, F): # Initialization - dim_c = self.m_ubm.dim_c - dim_d = self.m_ubm.dim_d + #dim_c = self.m_ubm.dim_c + #dim_d = self.m_ubm.dim_d + dim_c,dim_d = self.m_ubm.shape mean_supervector = self.m_ubm.mean_supervector TtSigmaInv_Fnorm = numpy.zeros(shape=(self.m_dim_t,), dtype=numpy.float64) # Loop over each Gaussian component - dim_c = self.m_ubm.dim_c + dim_c = self.m_ubm.shape[0] for c in range(dim_c): start = c*dim_d end = (c+1)*dim_d @@ -94,8 +96,9 @@ class IVectorMachinePy(): def _get_I_TtSigmaInvNT(self, N): # Initialization - dim_c = self.m_ubm.dim_c - dim_d = self.m_ubm.dim_d + #dim_c = self.m_ubm.dim_c + #dim_d = self.m_ubm.dim_d + dim_c, dim_d = self.m_ubm.shape TtSigmaInvNT = numpy.eye(self.m_dim_t, dtype=numpy.float64) for c in range(dim_c): @@ -152,5 +155,5 @@ def test_machine(): mc.sigma = sigma wij_ref = numpy.array([-0.04213415, 0.21463343]) # Reference from original Chris implementation - wij = mc.forward(gs) + wij = mc(gs) assert numpy.allclose(wij_ref, wij, 1e-5) diff --git a/doc/index.rst b/doc/index.rst index 04bf7cd0bdb654f06e62096b97f38f52a99c6cde..058e2263595cf5b3ba034fc2170c945100bec97c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -30,6 +30,7 @@ References .. [Reynolds2000] *Reynolds, Douglas A., Thomas F. Quatieri, and Robert B. Dunn*. **Speaker Verification Using Adapted Gaussian Mixture Models**, Digital signal processing 10.1 (2000): 19-41. .. [Vogt2008] *R. Vogt, S. Sridharan*. **'Explicit Modelling of Session Variability for Speaker Verification'**, Computer Speech & Language, 2008, vol. 22, no. 1, pp. 17-38 .. [McCool2013] *C. McCool, R. Wallace, M. McLaren, L. El Shafey, S. Marcel*. **'Session Variability Modelling for Face Authentication'**, IET Biometrics, 2013 +.. [Dehak2010] *N. Dehak, P. Kenny, R. Dehak, P. Dumouchel, P. Ouellet*, **'Front End Factor Analysis for Speaker Verification'**, IEEE Transactions on Audio, Speech and Language Processing, 2010, vol. 19, issue 4, pp. 788-798 Indices and tables diff --git a/setup.py b/setup.py index 187d11ccf38b0d4026ab960b0075de5d0d3f0664..7e139cc4f246253bdc0e18a83d793ae627039df6 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ setup( "bob/learn/misc/cpp/Gaussian.cpp", "bob/learn/misc/cpp/GMMMachine.cpp", "bob/learn/misc/cpp/GMMStats.cpp", - #"bob/learn/misc/cpp/IVectorMachine.cpp", + "bob/learn/misc/cpp/IVectorMachine.cpp", "bob/learn/misc/cpp/KMeansMachine.cpp", "bob/learn/misc/cpp/LinearScoring.cpp", #"bob/learn/misc/cpp/PLDAMachine.cpp", @@ -117,6 +117,8 @@ setup( "bob/learn/misc/isv_base.cpp", "bob/learn/misc/jfa_machine.cpp", "bob/learn/misc/isv_machine.cpp", + + "bob/learn/misc/ivector_machine.cpp", "bob/learn/misc/main.cpp", ],