tomwalters@268: // Copyright 2008-2010, Thomas Walters
tomwalters@268: //
tomwalters@268: // AIM-C: A C++ implementation of the Auditory Image Model
tomwalters@268: // http://www.acousticscale.org/AIMC
tomwalters@268: //
tomwalters@268: // This program is free software: you can redistribute it and/or modify
tomwalters@268: // it under the terms of the GNU General Public License as published by
tomwalters@268: // the Free Software Foundation, either version 3 of the License, or
tomwalters@268: // (at your option) any later version.
tomwalters@268: //
tomwalters@268: // This program is distributed in the hope that it will be useful,
tomwalters@268: // but WITHOUT ANY WARRANTY; without even the implied warranty of
tomwalters@268: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
tomwalters@268: // GNU General Public License for more details.
tomwalters@268: //
tomwalters@268: // You should have received a copy of the GNU General Public License
tomwalters@268: // along with this program. If not, see .
tomwalters@268:
tomwalters@268: /*! \file
tomwalters@268: * \brief Gaussian features - based on MATLAB code by Christian Feldbauer
tomwalters@268: */
tomwalters@268:
tomwalters@268: /*!
tomwalters@273: * \author Thomas Walters
tomwalters@268: * \date created 2008/06/23
tomwalters@296: * \version \$Id$
tomwalters@268: */
tomwalters@268:
tomwalters@268: #include
tomwalters@268:
tomwalters@268: #include "Modules/Features/ModuleGaussians.h"
tomwalters@268: #include "Support/Common.h"
tomwalters@268:
tomwalters@268: namespace aimc {
tomwalters@278: ModuleGaussians::ModuleGaussians(Parameters *params) : Module(params) {
tomwalters@268: // Set module metadata
tomwalters@268: module_description_ = "Gaussian Fitting to SSI profile";
tomwalters@273: module_identifier_ = "gaussians";
tomwalters@268: module_type_ = "features";
tomwalters@296: module_version_ = "$Id$";
tomwalters@268:
tomwalters@273: m_iParamNComp = parameters_->DefaultInt("features.gaussians.ncomp", 4);
tomwalters@273: m_fParamVar = parameters_->DefaultFloat("features.gaussians.var", 115.0);
tomwalters@273: m_fParamPosteriorExp =
tomwalters@273: parameters_->DefaultFloat("features.gaussians.posterior_exp", 6.0);
tomwalters@273: m_iParamMaxIt = parameters_->DefaultInt("features.gaussians.maxit", 250);
tomwalters@268:
tomwalters@273: // The parameters system doesn't support tiny numbers well, to define this
tomwalters@273: // variable as a string, then convert it to a float afterwards
tomwalters@273: parameters_->DefaultString("features.gaussians.priors_converged", "1e-7");
tomwalters@268: m_fParamPriorsConverged =
tomwalters@273: parameters_->GetFloat("features.gaussians.priors_converged");
tomwalters@268: }
tomwalters@268:
tomwalters@268: ModuleGaussians::~ModuleGaussians() {
tomwalters@268: }
tomwalters@268:
tomwalters@268: bool ModuleGaussians::InitializeInternal(const SignalBank &input) {
tomwalters@268: m_pA.resize(m_iParamNComp, 0.0f);
tomwalters@268: m_pMu.resize(m_iParamNComp, 0.0f);
tomwalters@268:
tomwalters@268: // Assuming the number of channels is greater than twice the number of
tomwalters@268: // Gaussian components, this is ok
tomwalters@268: if (input.channel_count() >= 2 * m_iParamNComp) {
tomwalters@273: output_.Initialize(m_iParamNComp, 1, input.sample_rate());
tomwalters@268: } else {
tomwalters@268: LOG_ERROR(_T("Too few channels in filterbank to produce sensible "
tomwalters@268: "Gaussian features. Either increase the number of filterbank"
tomwalters@268: " channels, or decrease the number of Gaussian components"));
tomwalters@268: return false;
tomwalters@268: }
tomwalters@268:
tomwalters@268: m_iNumChannels = input.channel_count();
tomwalters@268: m_pSpectralProfile.resize(m_iNumChannels, 0.0f);
tomwalters@268:
tomwalters@268: return true;
tomwalters@268: }
tomwalters@268:
tomwalters@275: void ModuleGaussians::ResetInternal() {
tomwalters@268: m_pSpectralProfile.clear();
tomwalters@268: m_pSpectralProfile.resize(m_iNumChannels, 0.0f);
tomwalters@292: m_pA.clear();
tomwalters@292: m_pA.resize(m_iParamNComp, 0.0f);
tomwalters@292: m_pMu.clear();
tomwalters@292: m_pMu.resize(m_iParamNComp, 0.0f);
tomwalters@268: }
tomwalters@268:
tomwalters@268: void ModuleGaussians::Process(const SignalBank &input) {
tomwalters@273: if (!initialized_) {
tomwalters@273: LOG_ERROR(_T("Module ModuleGaussians not initialized."));
tomwalters@273: return;
tomwalters@273: }
tomwalters@268: // Calculate spectral profile
tomwalters@268: for (int iChannel = 0;
tomwalters@268: iChannel < input.channel_count();
tomwalters@268: ++iChannel) {
tomwalters@268: m_pSpectralProfile[iChannel] = 0.0f;
tomwalters@268: for (int iSample = 0;
tomwalters@268: iSample < input.buffer_length();
tomwalters@268: ++iSample) {
tomwalters@268: m_pSpectralProfile[iChannel] += input[iChannel][iSample];
tomwalters@268: }
tomwalters@280: m_pSpectralProfile[iChannel] /= static_cast(input.buffer_length());
tomwalters@273: }
tomwalters@273:
tomwalters@280: float spectral_profile_sum = 0.0f;
tomwalters@273: for (int i = 0; i < input.channel_count(); ++i) {
tomwalters@273: spectral_profile_sum += m_pSpectralProfile[i];
tomwalters@273: }
tomwalters@273:
tomwalters@280: float logsum = log(spectral_profile_sum);
tomwalters@273: if (!isinf(logsum)) {
tomwalters@273: output_.set_sample(m_iParamNComp - 1, 0, logsum);
tomwalters@273: } else {
tomwalters@273: output_.set_sample(m_iParamNComp - 1, 0, -1000.0);
tomwalters@268: }
tomwalters@268:
tomwalters@268: for (int iChannel = 0;
tomwalters@268: iChannel < input.channel_count();
tomwalters@268: ++iChannel) {
tomwalters@268: m_pSpectralProfile[iChannel] = pow(m_pSpectralProfile[iChannel], 0.8);
tomwalters@268: }
tomwalters@268:
tomwalters@268: RubberGMMCore(2, true);
tomwalters@268:
tomwalters@280: float fMean1 = m_pMu[0];
tomwalters@280: float fMean2 = m_pMu[1];
tomwalters@280: // LOG_INFO(_T("Orig. mean 0 = %f"), m_pMu[0]);
tomwalters@280: // LOG_INFO(_T("Orig. mean 1 = %f"), m_pMu[1]);
tomwalters@280: // LOG_INFO(_T("Orig. prob 0 = %f"), m_pA[0]);
tomwalters@280: // LOG_INFO(_T("Orig. prob 1 = %f"), m_pA[1]);
tomwalters@268:
tomwalters@280: float fA1 = 0.05 * m_pA[0];
tomwalters@280: float fA2 = 1.0 - 0.25 * m_pA[1];
tomwalters@268:
tomwalters@280: // LOG_INFO(_T("fA1 = %f"), fA1);
tomwalters@280: // LOG_INFO(_T("fA2 = %f"), fA2);
tomwalters@274:
tomwalters@280: float fGradient = (fMean2 - fMean1) / (fA2 - fA1);
tomwalters@280: float fIntercept = fMean2 - fGradient * fA2;
tomwalters@274:
tomwalters@280: // LOG_INFO(_T("fGradient = %f"), fGradient);
tomwalters@280: // LOG_INFO(_T("fIntercept = %f"), fIntercept);
tomwalters@268:
tomwalters@268: for (int i = 0; i < m_iParamNComp; ++i) {
tomwalters@280: m_pMu[i] = (static_cast(i)
tomwalters@280: / (static_cast(m_iParamNComp) - 1.0f))
tomwalters@280: * fGradient + fIntercept;
tomwalters@280: // LOG_INFO(_T("mean %d = %f"), i, m_pMu[i]);
tomwalters@268: }
tomwalters@268:
tomwalters@268: for (int i = 0; i < m_iParamNComp; ++i) {
tomwalters@280: m_pA[i] = 1.0f / static_cast(m_iParamNComp);
tomwalters@268: }
tomwalters@268:
tomwalters@268: RubberGMMCore(m_iParamNComp, false);
tomwalters@268:
tomwalters@268: for (int i = 0; i < m_iParamNComp - 1; ++i) {
tomwalters@268: if (!isnan(m_pA[i])) {
tomwalters@268: output_.set_sample(i, 0, m_pA[i]);
tomwalters@268: } else {
tomwalters@268: output_.set_sample(i, 0, 0.0f);
tomwalters@268: }
tomwalters@268: }
tomwalters@273:
tomwalters@268: PushOutput();
tomwalters@268: }
tomwalters@268:
tomwalters@268: bool ModuleGaussians::RubberGMMCore(int iNComponents, bool bDoInit) {
tomwalters@268: int iSizeX = m_iNumChannels;
tomwalters@268:
tomwalters@268: // Normalise the spectral profile
tomwalters@280: float fSpectralProfileTotal = 0.0f;
tomwalters@268: for (int iCount = 0; iCount < iSizeX; iCount++) {
tomwalters@268: fSpectralProfileTotal += m_pSpectralProfile[iCount];
tomwalters@268: }
tomwalters@268: for (int iCount = 0; iCount < iSizeX; iCount++) {
tomwalters@268: m_pSpectralProfile[iCount] /= fSpectralProfileTotal;
tomwalters@268: }
tomwalters@268:
tomwalters@268: if (bDoInit) {
tomwalters@268: // Uniformly spaced components
tomwalters@280: float dd = (iSizeX - 1.0f) / iNComponents;
tomwalters@268: for (int i = 0; i < iNComponents; i++) {
tomwalters@268: m_pMu[i] = dd / 2.0f + (i * dd);
tomwalters@268: m_pA[i] = 1.0f / iNComponents;
tomwalters@268: }
tomwalters@268: }
tomwalters@268:
tomwalters@280: vector pA_old;
tomwalters@268: pA_old.resize(iNComponents);
tomwalters@280: vector pP_mod_X;
tomwalters@268: pP_mod_X.resize(iSizeX);
tomwalters@280: vector pP_comp;
tomwalters@268: pP_comp.resize(iSizeX * iNComponents);
tomwalters@268:
tomwalters@268: for (int iIteration = 0; iIteration < m_iParamMaxIt; iIteration++) {
tomwalters@268: // (re)calculate posteriors (component probability given observation)
tomwalters@268: // denominator: the model density at all observation points X
tomwalters@268: for (int i = 0; i < iSizeX; ++i) {
tomwalters@268: pP_mod_X[i] = 0.0f;
tomwalters@268: }
tomwalters@268:
tomwalters@268: for (int i = 0; i < iNComponents; i++) {
tomwalters@268: for (int iCount = 0; iCount < iSizeX; iCount++) {
tomwalters@268: pP_mod_X[iCount] += 1.0f / sqrt(2.0f * M_PI * m_fParamVar)
tomwalters@280: * exp((-0.5f)
tomwalters@280: * pow(static_cast(iCount+1) - m_pMu[i], 2)
tomwalters@280: / m_fParamVar) * m_pA[i];
tomwalters@268: }
tomwalters@268: }
tomwalters@268:
tomwalters@268: for (int i = 0; i < iSizeX * iNComponents; ++i) {
tomwalters@268: pP_comp[i] = 0.0f;
tomwalters@268: }
tomwalters@268:
tomwalters@268: for (int i = 0; i < iNComponents; i++) {
tomwalters@268: for (int iCount = 0; iCount < iSizeX; iCount++) {
tomwalters@268: pP_comp[iCount + i * iSizeX] =
tomwalters@268: 1.0f / sqrt(2.0f * M_PI * m_fParamVar)
tomwalters@280: * exp((-0.5f) * pow((static_cast(iCount + 1) - m_pMu[i]), 2)
tomwalters@280: / m_fParamVar);
tomwalters@268: pP_comp[iCount + i * iSizeX] =
tomwalters@268: pP_comp[iCount + i * iSizeX] * m_pA[i] / pP_mod_X[iCount];
tomwalters@268: }
tomwalters@268: }
tomwalters@268:
tomwalters@268: for (int iCount = 0; iCount < iSizeX; ++iCount) {
tomwalters@280: float fSum = 0.0f;
tomwalters@268: for (int i = 0; i < iNComponents; ++i) {
tomwalters@268: pP_comp[iCount+i*iSizeX] = pow(pP_comp[iCount + i * iSizeX],
tomwalters@280: m_fParamPosteriorExp); // expansion
tomwalters@268: fSum += pP_comp[iCount+i*iSizeX];
tomwalters@268: }
tomwalters@268: for (int i = 0; i < iNComponents; ++i)
tomwalters@268: pP_comp[iCount+i*iSizeX] = pP_comp[iCount + i * iSizeX] / fSum;
tomwalters@268: // renormalisation
tomwalters@268: }
tomwalters@268:
tomwalters@268: for (int i = 0; i < iNComponents; ++i) {
tomwalters@268: pA_old[i] = m_pA[i];
tomwalters@268: m_pA[i] = 0.0f;
tomwalters@268: for (int iCount = 0; iCount < iSizeX; ++iCount) {
tomwalters@268: m_pA[i] += pP_comp[iCount + i * iSizeX] * m_pSpectralProfile[iCount];
tomwalters@268: }
tomwalters@268: }
tomwalters@268:
tomwalters@268: // finish when already converged
tomwalters@280: float fPrdist = 0.0f;
tomwalters@268: for (int i = 0; i < iNComponents; ++i) {
tomwalters@268: fPrdist += pow((m_pA[i] - pA_old[i]), 2);
tomwalters@268: }
tomwalters@268: fPrdist /= iNComponents;
tomwalters@268:
tomwalters@268: if (fPrdist < m_fParamPriorsConverged) {
tomwalters@280: // LOG_INFO("Converged!");
tomwalters@268: break;
tomwalters@268: }
tomwalters@280: // LOG_INFO("Didn't converge!");
tomwalters@274:
tomwalters@268:
tomwalters@268: // update means (positions)
tomwalters@268: for (int i = 0 ; i < iNComponents; ++i) {
tomwalters@280: float mu_old = m_pMu[i];
tomwalters@268: if (m_pA[i] > 0.0f) {
tomwalters@268: m_pMu[i] = 0.0f;
tomwalters@268: for (int iCount = 0; iCount < iSizeX; ++iCount) {
tomwalters@268: m_pMu[i] += m_pSpectralProfile[iCount]
tomwalters@280: * pP_comp[iCount + i * iSizeX]
tomwalters@280: * static_cast(iCount + 1);
tomwalters@268: }
tomwalters@268: m_pMu[i] /= m_pA[i];
tomwalters@268: if (isnan(m_pMu[i])) {
tomwalters@268: m_pMu[i] = mu_old;
tomwalters@268: }
tomwalters@268: }
tomwalters@268: }
tomwalters@280: } // loop over iterations
tomwalters@268:
tomwalters@268: // Ensure they are sorted, using a really simple bubblesort
tomwalters@268: bool bSorted = false;
tomwalters@268: while (!bSorted) {
tomwalters@268: bSorted = true;
tomwalters@268: for (int i = 0; i < iNComponents - 1; ++i) {
tomwalters@268: if (m_pMu[i] > m_pMu[i + 1]) {
tomwalters@280: float fTemp = m_pMu[i];
tomwalters@268: m_pMu[i] = m_pMu[i + 1];
tomwalters@268: m_pMu[i + 1] = fTemp;
tomwalters@268: fTemp = m_pA[i];
tomwalters@268: m_pA[i] = m_pA[i + 1];
tomwalters@268: m_pA[i + 1] = fTemp;
tomwalters@268: bSorted = false;
tomwalters@268: }
tomwalters@268: }
tomwalters@268: }
tomwalters@268: return true;
tomwalters@268: }
tomwalters@280: } // namespace aimc
tomwalters@268: