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@45
|
6 // Licensed under the Apache License, Version 2.0 (the "License");
|
tomwalters@45
|
7 // you may not use this file except in compliance with the License.
|
tomwalters@45
|
8 // You may obtain a copy of the License at
|
tomwalters@0
|
9 //
|
tomwalters@45
|
10 // http://www.apache.org/licenses/LICENSE-2.0
|
tomwalters@0
|
11 //
|
tomwalters@45
|
12 // Unless required by applicable law or agreed to in writing, software
|
tomwalters@45
|
13 // distributed under the License is distributed on an "AS IS" BASIS,
|
tomwalters@45
|
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
tomwalters@45
|
15 // See the License for the specific language governing permissions and
|
tomwalters@45
|
16 // limitations under the License.
|
tomwalters@0
|
17
|
tomwalters@0
|
18 /*! \file
|
tomwalters@0
|
19 * \brief Gaussian features - based on MATLAB code by Christian Feldbauer
|
tomwalters@0
|
20 */
|
tomwalters@0
|
21
|
tomwalters@0
|
22 /*!
|
tomwalters@1
|
23 * \author Thomas Walters <tom@acousticscale.org>
|
tomwalters@0
|
24 * \date created 2008/06/23
|
tomwalters@23
|
25 * \version \$Id$
|
tomwalters@0
|
26 */
|
tomwalters@0
|
27
|
tomwalters@0
|
28 #include <math.h>
|
tomwalters@0
|
29
|
tomwalters@0
|
30 #include "Modules/Features/ModuleGaussians.h"
|
tomwalters@0
|
31 #include "Support/Common.h"
|
tomwalters@0
|
32
|
tomwalters@0
|
33 namespace aimc {
|
tomwalters@6
|
34 ModuleGaussians::ModuleGaussians(Parameters *params) : Module(params) {
|
tomwalters@0
|
35 // Set module metadata
|
tomwalters@0
|
36 module_description_ = "Gaussian Fitting to SSI profile";
|
tomwalters@1
|
37 module_identifier_ = "gaussians";
|
tomwalters@0
|
38 module_type_ = "features";
|
tomwalters@23
|
39 module_version_ = "$Id$";
|
tomwalters@0
|
40
|
tomwalters@84
|
41 m_iParamNComp = parameters_->DefaultInt("gaussians.ncomp", 4);
|
tomwalters@84
|
42 m_fParamVar = parameters_->DefaultFloat("gaussians.var", 115.0);
|
tomwalters@84
|
43 m_fParamPosteriorExp = parameters_->DefaultFloat("gaussians.posterior_exp",
|
tomwalters@84
|
44 6.0);
|
tomwalters@84
|
45 m_iParamMaxIt = parameters_->DefaultInt("gaussians.maxit", 250);
|
tomwalters@0
|
46
|
tomwalters@1
|
47 // The parameters system doesn't support tiny numbers well, to define this
|
tomwalters@1
|
48 // variable as a string, then convert it to a float afterwards
|
tomwalters@84
|
49 parameters_->DefaultString("gaussians.priors_converged", "1e-7");
|
tomwalters@84
|
50 priors_converged_ = parameters_->GetFloat("gaussians.priors_converged");
|
tomwalters@84
|
51 output_positions_ = parameters_->DefaultBool("gaussians.positions", false);
|
tomwalters@0
|
52 }
|
tomwalters@0
|
53
|
tomwalters@0
|
54 ModuleGaussians::~ModuleGaussians() {
|
tomwalters@0
|
55 }
|
tomwalters@0
|
56
|
tomwalters@0
|
57 bool ModuleGaussians::InitializeInternal(const SignalBank &input) {
|
tomwalters@0
|
58 m_pA.resize(m_iParamNComp, 0.0f);
|
tomwalters@0
|
59 m_pMu.resize(m_iParamNComp, 0.0f);
|
tomwalters@0
|
60
|
tomwalters@0
|
61 // Assuming the number of channels is greater than twice the number of
|
tomwalters@0
|
62 // Gaussian components, this is ok
|
tomwalters@84
|
63 output_component_count_ = 1; // Energy component
|
tomwalters@0
|
64 if (input.channel_count() >= 2 * m_iParamNComp) {
|
tomwalters@84
|
65 output_component_count_ += (m_iParamNComp - 1);
|
tomwalters@0
|
66 } else {
|
tomwalters@0
|
67 LOG_ERROR(_T("Too few channels in filterbank to produce sensible "
|
tomwalters@0
|
68 "Gaussian features. Either increase the number of filterbank"
|
tomwalters@0
|
69 " channels, or decrease the number of Gaussian components"));
|
tomwalters@0
|
70 return false;
|
tomwalters@0
|
71 }
|
tomwalters@0
|
72
|
tomwalters@84
|
73 if (output_positions_) {
|
tomwalters@84
|
74 output_component_count_ += m_iParamNComp;
|
tomwalters@84
|
75 }
|
tomwalters@84
|
76
|
tomwalters@84
|
77 output_.Initialize(output_component_count_, 1, input.sample_rate());
|
tomwalters@84
|
78
|
tomwalters@0
|
79 m_iNumChannels = input.channel_count();
|
tomwalters@0
|
80 m_pSpectralProfile.resize(m_iNumChannels, 0.0f);
|
tomwalters@0
|
81
|
tomwalters@0
|
82 return true;
|
tomwalters@0
|
83 }
|
tomwalters@0
|
84
|
tomwalters@3
|
85 void ModuleGaussians::ResetInternal() {
|
tomwalters@0
|
86 m_pSpectralProfile.clear();
|
tomwalters@0
|
87 m_pSpectralProfile.resize(m_iNumChannels, 0.0f);
|
tomwalters@20
|
88 m_pA.clear();
|
tomwalters@20
|
89 m_pA.resize(m_iParamNComp, 0.0f);
|
tomwalters@20
|
90 m_pMu.clear();
|
tomwalters@20
|
91 m_pMu.resize(m_iParamNComp, 0.0f);
|
tomwalters@0
|
92 }
|
tomwalters@0
|
93
|
tomwalters@0
|
94 void ModuleGaussians::Process(const SignalBank &input) {
|
tomwalters@1
|
95 if (!initialized_) {
|
tomwalters@1
|
96 LOG_ERROR(_T("Module ModuleGaussians not initialized."));
|
tomwalters@1
|
97 return;
|
tomwalters@1
|
98 }
|
tom@136
|
99 output_.set_start_time(input.start_time());
|
tomwalters@0
|
100 // Calculate spectral profile
|
tomwalters@84
|
101 for (int ch = 0; ch < input.channel_count(); ++ch) {
|
tomwalters@84
|
102 m_pSpectralProfile[ch] = 0.0f;
|
tomwalters@84
|
103 for (int i = 0; i < input.buffer_length(); ++i) {
|
tomwalters@84
|
104 m_pSpectralProfile[ch] += input[ch][i];
|
tomwalters@0
|
105 }
|
tomwalters@84
|
106 m_pSpectralProfile[ch] /= static_cast<float>(input.buffer_length());
|
tomwalters@1
|
107 }
|
tomwalters@1
|
108
|
tomwalters@8
|
109 float spectral_profile_sum = 0.0f;
|
tomwalters@1
|
110 for (int i = 0; i < input.channel_count(); ++i) {
|
tomwalters@1
|
111 spectral_profile_sum += m_pSpectralProfile[i];
|
tomwalters@1
|
112 }
|
tomwalters@1
|
113
|
tomwalters@84
|
114 // Set the last component of the feature vector to be the log energy
|
tomwalters@8
|
115 float logsum = log(spectral_profile_sum);
|
tomwalters@1
|
116 if (!isinf(logsum)) {
|
tomwalters@84
|
117 output_.set_sample(output_component_count_ - 1, 0, logsum);
|
tomwalters@1
|
118 } else {
|
tomwalters@84
|
119 output_.set_sample(output_component_count_ - 1, 0, -1000.0);
|
tomwalters@0
|
120 }
|
tomwalters@0
|
121
|
tomwalters@84
|
122 for (int ch = 0; ch < input.channel_count(); ++ch) {
|
tomwalters@84
|
123 m_pSpectralProfile[ch] = pow(m_pSpectralProfile[ch], 0.8);
|
tomwalters@0
|
124 }
|
tomwalters@0
|
125
|
tomwalters@0
|
126 RubberGMMCore(2, true);
|
tomwalters@0
|
127
|
tomwalters@84
|
128 float mean1 = m_pMu[0];
|
tomwalters@84
|
129 float mean2 = m_pMu[1];
|
tomwalters@8
|
130 // LOG_INFO(_T("Orig. mean 0 = %f"), m_pMu[0]);
|
tomwalters@8
|
131 // LOG_INFO(_T("Orig. mean 1 = %f"), m_pMu[1]);
|
tomwalters@8
|
132 // LOG_INFO(_T("Orig. prob 0 = %f"), m_pA[0]);
|
tomwalters@8
|
133 // LOG_INFO(_T("Orig. prob 1 = %f"), m_pA[1]);
|
tomwalters@0
|
134
|
tomwalters@84
|
135 float a1 = 0.05 * m_pA[0];
|
tomwalters@84
|
136 float a2 = 1.0 - 0.25 * m_pA[1];
|
tomwalters@0
|
137
|
tomwalters@8
|
138 // LOG_INFO(_T("fA1 = %f"), fA1);
|
tomwalters@8
|
139 // LOG_INFO(_T("fA2 = %f"), fA2);
|
tomwalters@2
|
140
|
tomwalters@84
|
141 float gradient = (mean2 - mean1) / (a2 - a1);
|
tomwalters@84
|
142 float intercept = mean2 - gradient * a2;
|
tomwalters@2
|
143
|
tomwalters@8
|
144 // LOG_INFO(_T("fGradient = %f"), fGradient);
|
tomwalters@8
|
145 // LOG_INFO(_T("fIntercept = %f"), fIntercept);
|
tomwalters@0
|
146
|
tomwalters@0
|
147 for (int i = 0; i < m_iParamNComp; ++i) {
|
tomwalters@8
|
148 m_pMu[i] = (static_cast<float>(i)
|
tomwalters@8
|
149 / (static_cast<float>(m_iParamNComp) - 1.0f))
|
tomwalters@84
|
150 * gradient + intercept;
|
tomwalters@8
|
151 // LOG_INFO(_T("mean %d = %f"), i, m_pMu[i]);
|
tomwalters@0
|
152 }
|
tomwalters@0
|
153
|
tomwalters@0
|
154 for (int i = 0; i < m_iParamNComp; ++i) {
|
tomwalters@8
|
155 m_pA[i] = 1.0f / static_cast<float>(m_iParamNComp);
|
tomwalters@0
|
156 }
|
tomwalters@0
|
157
|
tomwalters@0
|
158 RubberGMMCore(m_iParamNComp, false);
|
tomwalters@0
|
159
|
tomwalters@84
|
160 // Amplitudes first
|
tomwalters@0
|
161 for (int i = 0; i < m_iParamNComp - 1; ++i) {
|
tomwalters@0
|
162 if (!isnan(m_pA[i])) {
|
tomwalters@0
|
163 output_.set_sample(i, 0, m_pA[i]);
|
tomwalters@0
|
164 } else {
|
tomwalters@0
|
165 output_.set_sample(i, 0, 0.0f);
|
tomwalters@0
|
166 }
|
tomwalters@0
|
167 }
|
tomwalters@1
|
168
|
tomwalters@84
|
169 // Then means if required
|
tomwalters@84
|
170 if (output_positions_) {
|
tomwalters@84
|
171 int idx = 0;
|
tomwalters@84
|
172 for (int i = m_iParamNComp - 1; i < 2 * m_iParamNComp - 1; ++i) {
|
tomwalters@84
|
173 if (!isnan(m_pMu[i])) {
|
tomwalters@84
|
174 output_.set_sample(i, 0, m_pMu[idx]);
|
tomwalters@84
|
175 } else {
|
tomwalters@84
|
176 output_.set_sample(i, 0, 0.0f);
|
tomwalters@84
|
177 }
|
tomwalters@84
|
178 ++idx;
|
tomwalters@84
|
179 }
|
tomwalters@84
|
180 }
|
tomwalters@84
|
181
|
tomwalters@0
|
182 PushOutput();
|
tomwalters@0
|
183 }
|
tomwalters@0
|
184
|
tomwalters@0
|
185 bool ModuleGaussians::RubberGMMCore(int iNComponents, bool bDoInit) {
|
tomwalters@0
|
186 int iSizeX = m_iNumChannels;
|
tomwalters@0
|
187
|
tomwalters@0
|
188 // Normalise the spectral profile
|
tomwalters@84
|
189 float SpectralProfileTotal = 0.0f;
|
tomwalters@0
|
190 for (int iCount = 0; iCount < iSizeX; iCount++) {
|
tomwalters@84
|
191 SpectralProfileTotal += m_pSpectralProfile[iCount];
|
tomwalters@0
|
192 }
|
tomwalters@0
|
193 for (int iCount = 0; iCount < iSizeX; iCount++) {
|
tomwalters@84
|
194 m_pSpectralProfile[iCount] /= SpectralProfileTotal;
|
tomwalters@0
|
195 }
|
tomwalters@0
|
196
|
tomwalters@0
|
197 if (bDoInit) {
|
tomwalters@0
|
198 // Uniformly spaced components
|
tomwalters@8
|
199 float dd = (iSizeX - 1.0f) / iNComponents;
|
tomwalters@0
|
200 for (int i = 0; i < iNComponents; i++) {
|
tomwalters@0
|
201 m_pMu[i] = dd / 2.0f + (i * dd);
|
tomwalters@0
|
202 m_pA[i] = 1.0f / iNComponents;
|
tomwalters@0
|
203 }
|
tomwalters@0
|
204 }
|
tomwalters@0
|
205
|
tomwalters@8
|
206 vector<float> pA_old;
|
tomwalters@0
|
207 pA_old.resize(iNComponents);
|
tomwalters@8
|
208 vector<float> pP_mod_X;
|
tomwalters@0
|
209 pP_mod_X.resize(iSizeX);
|
tomwalters@8
|
210 vector<float> pP_comp;
|
tomwalters@0
|
211 pP_comp.resize(iSizeX * iNComponents);
|
tomwalters@0
|
212
|
tomwalters@0
|
213 for (int iIteration = 0; iIteration < m_iParamMaxIt; iIteration++) {
|
tomwalters@0
|
214 // (re)calculate posteriors (component probability given observation)
|
tomwalters@0
|
215 // denominator: the model density at all observation points X
|
tomwalters@0
|
216 for (int i = 0; i < iSizeX; ++i) {
|
tomwalters@0
|
217 pP_mod_X[i] = 0.0f;
|
tomwalters@0
|
218 }
|
tomwalters@0
|
219
|
tomwalters@84
|
220 for (int c = 0; c < iNComponents; c++) {
|
tomwalters@0
|
221 for (int iCount = 0; iCount < iSizeX; iCount++) {
|
tomwalters@0
|
222 pP_mod_X[iCount] += 1.0f / sqrt(2.0f * M_PI * m_fParamVar)
|
tomwalters@8
|
223 * exp((-0.5f)
|
tomwalters@84
|
224 * pow(static_cast<float>(iCount+1) - m_pMu[c], 2)
|
tomwalters@84
|
225 / m_fParamVar) * m_pA[c];
|
tomwalters@0
|
226 }
|
tomwalters@0
|
227 }
|
tomwalters@0
|
228
|
tomwalters@0
|
229 for (int i = 0; i < iSizeX * iNComponents; ++i) {
|
tomwalters@0
|
230 pP_comp[i] = 0.0f;
|
tomwalters@0
|
231 }
|
tomwalters@0
|
232
|
tomwalters@0
|
233 for (int i = 0; i < iNComponents; i++) {
|
tomwalters@0
|
234 for (int iCount = 0; iCount < iSizeX; iCount++) {
|
tomwalters@0
|
235 pP_comp[iCount + i * iSizeX] =
|
tomwalters@0
|
236 1.0f / sqrt(2.0f * M_PI * m_fParamVar)
|
tomwalters@8
|
237 * exp((-0.5f) * pow((static_cast<float>(iCount + 1) - m_pMu[i]), 2)
|
tomwalters@8
|
238 / m_fParamVar);
|
tomwalters@0
|
239 pP_comp[iCount + i * iSizeX] =
|
tomwalters@0
|
240 pP_comp[iCount + i * iSizeX] * m_pA[i] / pP_mod_X[iCount];
|
tomwalters@0
|
241 }
|
tomwalters@0
|
242 }
|
tomwalters@0
|
243
|
tomwalters@0
|
244 for (int iCount = 0; iCount < iSizeX; ++iCount) {
|
tomwalters@8
|
245 float fSum = 0.0f;
|
tomwalters@0
|
246 for (int i = 0; i < iNComponents; ++i) {
|
tomwalters@0
|
247 pP_comp[iCount+i*iSizeX] = pow(pP_comp[iCount + i * iSizeX],
|
tomwalters@8
|
248 m_fParamPosteriorExp); // expansion
|
tomwalters@0
|
249 fSum += pP_comp[iCount+i*iSizeX];
|
tomwalters@0
|
250 }
|
tomwalters@0
|
251 for (int i = 0; i < iNComponents; ++i)
|
tomwalters@0
|
252 pP_comp[iCount+i*iSizeX] = pP_comp[iCount + i * iSizeX] / fSum;
|
tomwalters@0
|
253 // renormalisation
|
tomwalters@0
|
254 }
|
tomwalters@0
|
255
|
tomwalters@0
|
256 for (int i = 0; i < iNComponents; ++i) {
|
tomwalters@0
|
257 pA_old[i] = m_pA[i];
|
tomwalters@0
|
258 m_pA[i] = 0.0f;
|
tomwalters@0
|
259 for (int iCount = 0; iCount < iSizeX; ++iCount) {
|
tomwalters@0
|
260 m_pA[i] += pP_comp[iCount + i * iSizeX] * m_pSpectralProfile[iCount];
|
tomwalters@0
|
261 }
|
tomwalters@0
|
262 }
|
tomwalters@0
|
263
|
tomwalters@0
|
264 // finish when already converged
|
tomwalters@8
|
265 float fPrdist = 0.0f;
|
tomwalters@0
|
266 for (int i = 0; i < iNComponents; ++i) {
|
tomwalters@0
|
267 fPrdist += pow((m_pA[i] - pA_old[i]), 2);
|
tomwalters@0
|
268 }
|
tomwalters@0
|
269 fPrdist /= iNComponents;
|
tomwalters@0
|
270
|
tomwalters@84
|
271 if (fPrdist < priors_converged_) {
|
tomwalters@8
|
272 // LOG_INFO("Converged!");
|
tomwalters@0
|
273 break;
|
tomwalters@0
|
274 }
|
tomwalters@8
|
275 // LOG_INFO("Didn't converge!");
|
tomwalters@2
|
276
|
tomwalters@0
|
277
|
tomwalters@0
|
278 // update means (positions)
|
tomwalters@0
|
279 for (int i = 0 ; i < iNComponents; ++i) {
|
tomwalters@8
|
280 float mu_old = m_pMu[i];
|
tomwalters@0
|
281 if (m_pA[i] > 0.0f) {
|
tomwalters@0
|
282 m_pMu[i] = 0.0f;
|
tomwalters@0
|
283 for (int iCount = 0; iCount < iSizeX; ++iCount) {
|
tomwalters@0
|
284 m_pMu[i] += m_pSpectralProfile[iCount]
|
tomwalters@8
|
285 * pP_comp[iCount + i * iSizeX]
|
tomwalters@8
|
286 * static_cast<float>(iCount + 1);
|
tomwalters@0
|
287 }
|
tomwalters@0
|
288 m_pMu[i] /= m_pA[i];
|
tomwalters@0
|
289 if (isnan(m_pMu[i])) {
|
tomwalters@0
|
290 m_pMu[i] = mu_old;
|
tomwalters@0
|
291 }
|
tomwalters@0
|
292 }
|
tomwalters@0
|
293 }
|
tomwalters@8
|
294 } // loop over iterations
|
tomwalters@0
|
295
|
tomwalters@0
|
296 // Ensure they are sorted, using a really simple bubblesort
|
tomwalters@0
|
297 bool bSorted = false;
|
tomwalters@0
|
298 while (!bSorted) {
|
tomwalters@0
|
299 bSorted = true;
|
tomwalters@0
|
300 for (int i = 0; i < iNComponents - 1; ++i) {
|
tomwalters@0
|
301 if (m_pMu[i] > m_pMu[i + 1]) {
|
tomwalters@8
|
302 float fTemp = m_pMu[i];
|
tomwalters@0
|
303 m_pMu[i] = m_pMu[i + 1];
|
tomwalters@0
|
304 m_pMu[i + 1] = fTemp;
|
tomwalters@0
|
305 fTemp = m_pA[i];
|
tomwalters@0
|
306 m_pA[i] = m_pA[i + 1];
|
tomwalters@0
|
307 m_pA[i + 1] = fTemp;
|
tomwalters@0
|
308 bSorted = false;
|
tomwalters@0
|
309 }
|
tomwalters@0
|
310 }
|
tomwalters@0
|
311 }
|
tomwalters@0
|
312 return true;
|
tomwalters@0
|
313 }
|
tomwalters@8
|
314 } // namespace aimc
|
tomwalters@0
|
315
|