tomwalters@0
|
1 // Copyright 2008-2010, Thomas Walters
|
tomwalters@0
|
2 //
|
tomwalters@0
|
3 // AIM-C: A C++ implementation of the Auditory Image Model
|
tomwalters@0
|
4 // http://www.acousticscale.org/AIMC
|
tomwalters@0
|
5 //
|
tomwalters@0
|
6 // This program is free software: you can redistribute it and/or modify
|
tomwalters@0
|
7 // it under the terms of the GNU General Public License as published by
|
tomwalters@0
|
8 // the Free Software Foundation, either version 3 of the License, or
|
tomwalters@0
|
9 // (at your option) any later version.
|
tomwalters@0
|
10 //
|
tomwalters@0
|
11 // This program is distributed in the hope that it will be useful,
|
tomwalters@0
|
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
tomwalters@0
|
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
tomwalters@0
|
14 // GNU General Public License for more details.
|
tomwalters@0
|
15 //
|
tomwalters@0
|
16 // You should have received a copy of the GNU General Public License
|
tomwalters@0
|
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
|
tomwalters@0
|
18
|
tomwalters@0
|
19 /*! \file
|
tomwalters@0
|
20 * \brief Gaussian features - based on MATLAB code by Christian Feldbauer
|
tomwalters@0
|
21 */
|
tomwalters@0
|
22
|
tomwalters@0
|
23 /*!
|
tomwalters@1
|
24 * \author Thomas Walters <tom@acousticscale.org>
|
tomwalters@0
|
25 * \date created 2008/06/23
|
tomwalters@0
|
26 * \version \$Id: ModuleGaussians.cc 2 2010-02-02 12:59:50Z tcw $
|
tomwalters@0
|
27 */
|
tomwalters@0
|
28
|
tomwalters@0
|
29 #include <math.h>
|
tomwalters@0
|
30
|
tomwalters@0
|
31 #include "Modules/Features/ModuleGaussians.h"
|
tomwalters@0
|
32 #include "Support/Common.h"
|
tomwalters@0
|
33
|
tomwalters@0
|
34 namespace aimc {
|
tomwalters@0
|
35 ModuleGaussians::ModuleGaussians(Parameters *pParam)
|
tomwalters@0
|
36 : Module(pParam) {
|
tomwalters@0
|
37 // Set module metadata
|
tomwalters@0
|
38 module_description_ = "Gaussian Fitting to SSI profile";
|
tomwalters@1
|
39 module_identifier_ = "gaussians";
|
tomwalters@0
|
40 module_type_ = "features";
|
tomwalters@0
|
41 module_version_ = "$Id: ModuleGaussians.cc 2 2010-02-02 12:59:50Z tcw $";
|
tomwalters@0
|
42
|
tomwalters@1
|
43 m_iParamNComp = parameters_->DefaultInt("features.gaussians.ncomp", 4);
|
tomwalters@1
|
44 m_fParamVar = parameters_->DefaultFloat("features.gaussians.var", 115.0);
|
tomwalters@1
|
45 m_fParamPosteriorExp =
|
tomwalters@1
|
46 parameters_->DefaultFloat("features.gaussians.posterior_exp", 6.0);
|
tomwalters@1
|
47 m_iParamMaxIt = parameters_->DefaultInt("features.gaussians.maxit", 250);
|
tomwalters@0
|
48
|
tomwalters@1
|
49 // The parameters system doesn't support tiny numbers well, to define this
|
tomwalters@1
|
50 // variable as a string, then convert it to a float afterwards
|
tomwalters@1
|
51 parameters_->DefaultString("features.gaussians.priors_converged", "1e-7");
|
tomwalters@0
|
52 m_fParamPriorsConverged =
|
tomwalters@1
|
53 parameters_->GetFloat("features.gaussians.priors_converged");
|
tomwalters@0
|
54 }
|
tomwalters@0
|
55
|
tomwalters@0
|
56 ModuleGaussians::~ModuleGaussians() {
|
tomwalters@0
|
57 }
|
tomwalters@0
|
58
|
tomwalters@0
|
59 bool ModuleGaussians::InitializeInternal(const SignalBank &input) {
|
tomwalters@0
|
60 m_pA.resize(m_iParamNComp, 0.0f);
|
tomwalters@0
|
61 m_pMu.resize(m_iParamNComp, 0.0f);
|
tomwalters@0
|
62
|
tomwalters@0
|
63 // Assuming the number of channels is greater than twice the number of
|
tomwalters@0
|
64 // Gaussian components, this is ok
|
tomwalters@0
|
65 if (input.channel_count() >= 2 * m_iParamNComp) {
|
tomwalters@1
|
66 output_.Initialize(m_iParamNComp, 1, input.sample_rate());
|
tomwalters@0
|
67 } else {
|
tomwalters@0
|
68 LOG_ERROR(_T("Too few channels in filterbank to produce sensible "
|
tomwalters@0
|
69 "Gaussian features. Either increase the number of filterbank"
|
tomwalters@0
|
70 " channels, or decrease the number of Gaussian components"));
|
tomwalters@0
|
71 return false;
|
tomwalters@0
|
72 }
|
tomwalters@0
|
73
|
tomwalters@0
|
74 m_iNumChannels = input.channel_count();
|
tomwalters@0
|
75 m_pSpectralProfile.resize(m_iNumChannels, 0.0f);
|
tomwalters@0
|
76
|
tomwalters@0
|
77 return true;
|
tomwalters@0
|
78 }
|
tomwalters@0
|
79
|
tomwalters@0
|
80 void ModuleGaussians::Reset() {
|
tomwalters@0
|
81 m_pSpectralProfile.clear();
|
tomwalters@0
|
82 m_pSpectralProfile.resize(m_iNumChannels, 0.0f);
|
tomwalters@0
|
83 }
|
tomwalters@0
|
84
|
tomwalters@0
|
85 void ModuleGaussians::Process(const SignalBank &input) {
|
tomwalters@1
|
86 if (!initialized_) {
|
tomwalters@1
|
87 LOG_ERROR(_T("Module ModuleGaussians not initialized."));
|
tomwalters@1
|
88 return;
|
tomwalters@1
|
89 }
|
tomwalters@0
|
90 // Calculate spectral profile
|
tomwalters@0
|
91 for (int iChannel = 0;
|
tomwalters@0
|
92 iChannel < input.channel_count();
|
tomwalters@0
|
93 ++iChannel) {
|
tomwalters@0
|
94 m_pSpectralProfile[iChannel] = 0.0f;
|
tomwalters@0
|
95 for (int iSample = 0;
|
tomwalters@0
|
96 iSample < input.buffer_length();
|
tomwalters@0
|
97 ++iSample) {
|
tomwalters@0
|
98 m_pSpectralProfile[iChannel] += input[iChannel][iSample];
|
tomwalters@0
|
99 }
|
tomwalters@2
|
100 m_pSpectralProfile[iChannel] /= static_cast<double>(input.buffer_length());
|
tomwalters@1
|
101 }
|
tomwalters@1
|
102
|
tomwalters@2
|
103 double spectral_profile_sum = 0.0f;
|
tomwalters@1
|
104 for (int i = 0; i < input.channel_count(); ++i) {
|
tomwalters@1
|
105 spectral_profile_sum += m_pSpectralProfile[i];
|
tomwalters@1
|
106 }
|
tomwalters@1
|
107
|
tomwalters@1
|
108 double logsum = log(spectral_profile_sum);
|
tomwalters@1
|
109 if (!isinf(logsum)) {
|
tomwalters@1
|
110 output_.set_sample(m_iParamNComp - 1, 0, logsum);
|
tomwalters@1
|
111 } else {
|
tomwalters@1
|
112 output_.set_sample(m_iParamNComp - 1, 0, -1000.0);
|
tomwalters@0
|
113 }
|
tomwalters@0
|
114
|
tomwalters@0
|
115 for (int iChannel = 0;
|
tomwalters@0
|
116 iChannel < input.channel_count();
|
tomwalters@0
|
117 ++iChannel) {
|
tomwalters@0
|
118 m_pSpectralProfile[iChannel] = pow(m_pSpectralProfile[iChannel], 0.8);
|
tomwalters@0
|
119 }
|
tomwalters@0
|
120
|
tomwalters@0
|
121 RubberGMMCore(2, true);
|
tomwalters@0
|
122
|
tomwalters@2
|
123 double fMean1 = m_pMu[0];
|
tomwalters@2
|
124 double fMean2 = m_pMu[1];
|
tomwalters@2
|
125 //LOG_INFO(_T("Orig. mean 0 = %f"), m_pMu[0]);
|
tomwalters@2
|
126 //LOG_INFO(_T("Orig. mean 1 = %f"), m_pMu[1]);
|
tomwalters@2
|
127 //LOG_INFO(_T("Orig. prob 0 = %f"), m_pA[0]);
|
tomwalters@2
|
128 //LOG_INFO(_T("Orig. prob 1 = %f"), m_pA[1]);
|
tomwalters@0
|
129
|
tomwalters@2
|
130 double fA1 = 0.05 * m_pA[0];
|
tomwalters@2
|
131 double fA2 = 1.0 - 0.25 * m_pA[1];
|
tomwalters@0
|
132
|
tomwalters@2
|
133 //LOG_INFO(_T("fA1 = %f"), fA1);
|
tomwalters@2
|
134 //LOG_INFO(_T("fA2 = %f"), fA2);
|
tomwalters@2
|
135
|
tomwalters@2
|
136 double fGradient = (fMean2 - fMean1) / (fA2 - fA1);
|
tomwalters@2
|
137 double fIntercept = fMean2 - fGradient * fA2;
|
tomwalters@2
|
138
|
tomwalters@2
|
139 //LOG_INFO(_T("fGradient = %f"), fGradient);
|
tomwalters@2
|
140 //LOG_INFO(_T("fIntercept = %f"), fIntercept);
|
tomwalters@0
|
141
|
tomwalters@0
|
142 for (int i = 0; i < m_iParamNComp; ++i) {
|
tomwalters@2
|
143 m_pMu[i] = ((double)i / ((double)m_iParamNComp - 1.0f))
|
tomwalters@2
|
144 * fGradient + fIntercept;
|
tomwalters@2
|
145 //LOG_INFO(_T("mean %d = %f"), i, m_pMu[i]);
|
tomwalters@0
|
146 }
|
tomwalters@0
|
147
|
tomwalters@0
|
148 for (int i = 0; i < m_iParamNComp; ++i) {
|
tomwalters@2
|
149 m_pA[i] = 1.0f / (double)m_iParamNComp;
|
tomwalters@0
|
150 }
|
tomwalters@0
|
151
|
tomwalters@0
|
152 RubberGMMCore(m_iParamNComp, false);
|
tomwalters@0
|
153
|
tomwalters@0
|
154 for (int i = 0; i < m_iParamNComp - 1; ++i) {
|
tomwalters@0
|
155 if (!isnan(m_pA[i])) {
|
tomwalters@0
|
156 output_.set_sample(i, 0, m_pA[i]);
|
tomwalters@0
|
157 } else {
|
tomwalters@0
|
158 output_.set_sample(i, 0, 0.0f);
|
tomwalters@0
|
159 }
|
tomwalters@0
|
160 }
|
tomwalters@1
|
161
|
tomwalters@0
|
162 PushOutput();
|
tomwalters@0
|
163 }
|
tomwalters@0
|
164
|
tomwalters@0
|
165 bool ModuleGaussians::RubberGMMCore(int iNComponents, bool bDoInit) {
|
tomwalters@0
|
166 int iSizeX = m_iNumChannels;
|
tomwalters@0
|
167
|
tomwalters@0
|
168 // Normalise the spectral profile
|
tomwalters@2
|
169 double fSpectralProfileTotal = 0.0f;
|
tomwalters@0
|
170 for (int iCount = 0; iCount < iSizeX; iCount++) {
|
tomwalters@0
|
171 fSpectralProfileTotal += m_pSpectralProfile[iCount];
|
tomwalters@0
|
172 }
|
tomwalters@0
|
173 for (int iCount = 0; iCount < iSizeX; iCount++) {
|
tomwalters@0
|
174 m_pSpectralProfile[iCount] /= fSpectralProfileTotal;
|
tomwalters@0
|
175 }
|
tomwalters@0
|
176
|
tomwalters@0
|
177 if (bDoInit) {
|
tomwalters@0
|
178 // Uniformly spaced components
|
tomwalters@2
|
179 double dd = (iSizeX - 1.0f) / iNComponents;
|
tomwalters@0
|
180 for (int i = 0; i < iNComponents; i++) {
|
tomwalters@0
|
181 m_pMu[i] = dd / 2.0f + (i * dd);
|
tomwalters@0
|
182 m_pA[i] = 1.0f / iNComponents;
|
tomwalters@0
|
183 }
|
tomwalters@0
|
184 }
|
tomwalters@0
|
185
|
tomwalters@2
|
186 vector<double> pA_old;
|
tomwalters@0
|
187 pA_old.resize(iNComponents);
|
tomwalters@2
|
188 vector<double> pP_mod_X;
|
tomwalters@0
|
189 pP_mod_X.resize(iSizeX);
|
tomwalters@2
|
190 vector<double> pP_comp;
|
tomwalters@0
|
191 pP_comp.resize(iSizeX * iNComponents);
|
tomwalters@0
|
192
|
tomwalters@0
|
193 for (int iIteration = 0; iIteration < m_iParamMaxIt; iIteration++) {
|
tomwalters@0
|
194 // (re)calculate posteriors (component probability given observation)
|
tomwalters@0
|
195 // denominator: the model density at all observation points X
|
tomwalters@0
|
196 for (int i = 0; i < iSizeX; ++i) {
|
tomwalters@0
|
197 pP_mod_X[i] = 0.0f;
|
tomwalters@0
|
198 }
|
tomwalters@0
|
199
|
tomwalters@0
|
200 for (int i = 0; i < iNComponents; i++) {
|
tomwalters@0
|
201 for (int iCount = 0; iCount < iSizeX; iCount++) {
|
tomwalters@0
|
202 pP_mod_X[iCount] += 1.0f / sqrt(2.0f * M_PI * m_fParamVar)
|
tomwalters@2
|
203 * exp((-0.5f) * pow(((double)(iCount + 1)-m_pMu[i]), 2)
|
tomwalters@0
|
204 / m_fParamVar) * m_pA[i];
|
tomwalters@0
|
205 }
|
tomwalters@0
|
206 }
|
tomwalters@0
|
207
|
tomwalters@0
|
208 for (int i = 0; i < iSizeX * iNComponents; ++i) {
|
tomwalters@0
|
209 pP_comp[i] = 0.0f;
|
tomwalters@0
|
210 }
|
tomwalters@0
|
211
|
tomwalters@0
|
212 for (int i = 0; i < iNComponents; i++) {
|
tomwalters@0
|
213 for (int iCount = 0; iCount < iSizeX; iCount++) {
|
tomwalters@0
|
214 pP_comp[iCount + i * iSizeX] =
|
tomwalters@0
|
215 1.0f / sqrt(2.0f * M_PI * m_fParamVar)
|
tomwalters@2
|
216 * exp((-0.5f) * pow(((double)(iCount + 1) - m_pMu[i]), 2) / m_fParamVar);
|
tomwalters@0
|
217 pP_comp[iCount + i * iSizeX] =
|
tomwalters@0
|
218 pP_comp[iCount + i * iSizeX] * m_pA[i] / pP_mod_X[iCount];
|
tomwalters@0
|
219 }
|
tomwalters@0
|
220 }
|
tomwalters@0
|
221
|
tomwalters@0
|
222 for (int iCount = 0; iCount < iSizeX; ++iCount) {
|
tomwalters@2
|
223 double fSum = 0.0f;
|
tomwalters@0
|
224 for (int i = 0; i < iNComponents; ++i) {
|
tomwalters@0
|
225 pP_comp[iCount+i*iSizeX] = pow(pP_comp[iCount + i * iSizeX],
|
tomwalters@0
|
226 m_fParamPosteriorExp); // expansion
|
tomwalters@0
|
227 fSum += pP_comp[iCount+i*iSizeX];
|
tomwalters@0
|
228 }
|
tomwalters@0
|
229 for (int i = 0; i < iNComponents; ++i)
|
tomwalters@0
|
230 pP_comp[iCount+i*iSizeX] = pP_comp[iCount + i * iSizeX] / fSum;
|
tomwalters@0
|
231 // renormalisation
|
tomwalters@0
|
232 }
|
tomwalters@0
|
233
|
tomwalters@0
|
234 for (int i = 0; i < iNComponents; ++i) {
|
tomwalters@0
|
235 pA_old[i] = m_pA[i];
|
tomwalters@0
|
236 m_pA[i] = 0.0f;
|
tomwalters@0
|
237 for (int iCount = 0; iCount < iSizeX; ++iCount) {
|
tomwalters@0
|
238 m_pA[i] += pP_comp[iCount + i * iSizeX] * m_pSpectralProfile[iCount];
|
tomwalters@0
|
239 }
|
tomwalters@0
|
240 }
|
tomwalters@0
|
241
|
tomwalters@0
|
242 // finish when already converged
|
tomwalters@2
|
243 double fPrdist = 0.0f;
|
tomwalters@0
|
244 for (int i = 0; i < iNComponents; ++i) {
|
tomwalters@0
|
245 fPrdist += pow((m_pA[i] - pA_old[i]), 2);
|
tomwalters@0
|
246 }
|
tomwalters@0
|
247 fPrdist /= iNComponents;
|
tomwalters@0
|
248
|
tomwalters@0
|
249 if (fPrdist < m_fParamPriorsConverged) {
|
tomwalters@2
|
250 //LOG_INFO("Converged!");
|
tomwalters@0
|
251 break;
|
tomwalters@0
|
252 }
|
tomwalters@2
|
253 //LOG_INFO("Didn't converge!");
|
tomwalters@2
|
254
|
tomwalters@0
|
255
|
tomwalters@0
|
256 // update means (positions)
|
tomwalters@0
|
257 for (int i = 0 ; i < iNComponents; ++i) {
|
tomwalters@2
|
258 double mu_old = m_pMu[i];
|
tomwalters@0
|
259 if (m_pA[i] > 0.0f) {
|
tomwalters@0
|
260 m_pMu[i] = 0.0f;
|
tomwalters@0
|
261 for (int iCount = 0; iCount < iSizeX; ++iCount) {
|
tomwalters@0
|
262 m_pMu[i] += m_pSpectralProfile[iCount]
|
tomwalters@2
|
263 * pP_comp[iCount + i * iSizeX] * (double)(iCount + 1);
|
tomwalters@0
|
264 }
|
tomwalters@0
|
265 m_pMu[i] /= m_pA[i];
|
tomwalters@0
|
266 if (isnan(m_pMu[i])) {
|
tomwalters@0
|
267 m_pMu[i] = mu_old;
|
tomwalters@0
|
268 }
|
tomwalters@0
|
269 }
|
tomwalters@0
|
270 }
|
tomwalters@0
|
271 } // loop over iterations
|
tomwalters@0
|
272
|
tomwalters@0
|
273 // Ensure they are sorted, using a really simple bubblesort
|
tomwalters@0
|
274 bool bSorted = false;
|
tomwalters@0
|
275 while (!bSorted) {
|
tomwalters@0
|
276 bSorted = true;
|
tomwalters@0
|
277 for (int i = 0; i < iNComponents - 1; ++i) {
|
tomwalters@0
|
278 if (m_pMu[i] > m_pMu[i + 1]) {
|
tomwalters@2
|
279 double fTemp = m_pMu[i];
|
tomwalters@0
|
280 m_pMu[i] = m_pMu[i + 1];
|
tomwalters@0
|
281 m_pMu[i + 1] = fTemp;
|
tomwalters@0
|
282 fTemp = m_pA[i];
|
tomwalters@0
|
283 m_pA[i] = m_pA[i + 1];
|
tomwalters@0
|
284 m_pA[i + 1] = fTemp;
|
tomwalters@0
|
285 bSorted = false;
|
tomwalters@0
|
286 }
|
tomwalters@0
|
287 }
|
tomwalters@0
|
288 }
|
tomwalters@0
|
289 return true;
|
tomwalters@0
|
290 }
|
tomwalters@0
|
291 } //namespace aimc
|
tomwalters@0
|
292
|