annotate vamp-sdk/hostext/PluginSummarisingAdapter.cpp @ 183:c053ababbf7e

* Fixes to continuous time averaging
author cannam
date Fri, 05 Sep 2008 13:52:57 +0000
parents 3fcac0f3afdc
children 26c200c3fc42
rev   line source
cannam@173 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@173 2
cannam@173 3 /*
cannam@173 4 Vamp
cannam@173 5
cannam@173 6 An API for audio analysis and feature extraction plugins.
cannam@173 7
cannam@173 8 Centre for Digital Music, Queen Mary, University of London.
cannam@173 9 Copyright 2006-2008 Chris Cannam and QMUL.
cannam@173 10
cannam@173 11 Permission is hereby granted, free of charge, to any person
cannam@173 12 obtaining a copy of this software and associated documentation
cannam@173 13 files (the "Software"), to deal in the Software without
cannam@173 14 restriction, including without limitation the rights to use, copy,
cannam@173 15 modify, merge, publish, distribute, sublicense, and/or sell copies
cannam@173 16 of the Software, and to permit persons to whom the Software is
cannam@173 17 furnished to do so, subject to the following conditions:
cannam@173 18
cannam@173 19 The above copyright notice and this permission notice shall be
cannam@173 20 included in all copies or substantial portions of the Software.
cannam@173 21
cannam@173 22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
cannam@173 23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
cannam@173 24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
cannam@173 25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
cannam@173 26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
cannam@173 27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
cannam@173 28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cannam@173 29
cannam@173 30 Except as contained in this notice, the names of the Centre for
cannam@173 31 Digital Music; Queen Mary, University of London; and Chris Cannam
cannam@173 32 shall not be used in advertising or otherwise to promote the sale,
cannam@173 33 use or other dealings in this Software without prior written
cannam@173 34 authorization.
cannam@173 35 */
cannam@173 36
cannam@173 37 #include "PluginSummarisingAdapter.h"
cannam@173 38
cannam@174 39 #include <map>
cannam@175 40 #include <cmath>
cannam@174 41
cannam@173 42 namespace Vamp {
cannam@173 43
cannam@173 44 namespace HostExt {
cannam@173 45
cannam@173 46 class PluginSummarisingAdapter::Impl
cannam@173 47 {
cannam@173 48 public:
cannam@173 49 Impl(Plugin *plugin, float inputSampleRate);
cannam@173 50 ~Impl();
cannam@173 51
cannam@173 52 FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
cannam@173 53 FeatureSet getRemainingFeatures();
cannam@173 54
cannam@173 55 void setSummarySegmentBoundaries(const SegmentBoundaries &);
cannam@173 56
cannam@180 57 FeatureList getSummaryForOutput(int output,
cannam@180 58 SummaryType type,
cannam@180 59 AveragingMethod avg);
cannam@180 60
cannam@180 61 FeatureSet getSummaryForAllOutputs(SummaryType type,
cannam@180 62 AveragingMethod avg);
cannam@173 63
cannam@173 64 protected:
cannam@174 65 Plugin *m_plugin;
cannam@181 66 float m_inputSampleRate;
cannam@174 67
cannam@173 68 SegmentBoundaries m_boundaries;
cannam@174 69
cannam@174 70 typedef std::vector<float> ValueList;
cannam@174 71 typedef std::map<int, ValueList> BinValueMap;
cannam@180 72 typedef std::vector<RealTime> DurationList;
cannam@174 73
cannam@174 74 struct OutputAccumulator {
cannam@174 75 int count;
cannam@180 76 BinValueMap values; // bin number -> values ordered by time
cannam@180 77 DurationList durations;
cannam@180 78 OutputAccumulator() : count(0), values(), durations() { }
cannam@174 79 };
cannam@174 80
cannam@174 81 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap;
cannam@180 82 OutputAccumulatorMap m_accumulators; // output number -> accumulator
cannam@180 83
cannam@180 84 typedef std::map<int, RealTime> OutputTimestampMap;
cannam@180 85 OutputTimestampMap m_prevTimestamps; // output number -> timestamp
cannam@183 86 OutputTimestampMap m_prevDurations; // output number -> durations
cannam@174 87
cannam@174 88 struct OutputBinSummary {
cannam@180 89
cannam@180 90 int count;
cannam@180 91
cannam@180 92 // extents
cannam@174 93 float minimum;
cannam@174 94 float maximum;
cannam@180 95 float sum;
cannam@180 96
cannam@180 97 // sample-average results
cannam@174 98 float median;
cannam@174 99 float mode;
cannam@174 100 float variance;
cannam@180 101
cannam@180 102 // continuous-time average results
cannam@180 103 float median_c;
cannam@180 104 float mode_c;
cannam@180 105 float mean_c;
cannam@180 106 float variance_c;
cannam@174 107 };
cannam@174 108
cannam@174 109 typedef std::map<int, OutputBinSummary> OutputSummary;
cannam@174 110 typedef std::map<RealTime, OutputSummary> SummarySegmentMap;
cannam@174 111 typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap;
cannam@174 112
cannam@174 113 OutputSummarySegmentMap m_summaries;
cannam@174 114
cannam@183 115 bool m_reduced;
cannam@174 116 RealTime m_lastTimestamp;
cannam@174 117
cannam@180 118 void accumulate(const FeatureSet &fs, RealTime, bool final);
cannam@180 119 void accumulate(int output, const Feature &f, RealTime, bool final);
cannam@174 120 void reduce();
cannam@173 121 };
cannam@173 122
cannam@173 123 PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
cannam@173 124 PluginWrapper(plugin)
cannam@173 125 {
cannam@173 126 m_impl = new Impl(plugin, m_inputSampleRate);
cannam@173 127 }
cannam@173 128
cannam@173 129 PluginSummarisingAdapter::~PluginSummarisingAdapter()
cannam@173 130 {
cannam@173 131 delete m_impl;
cannam@173 132 }
cannam@173 133
cannam@173 134 Plugin::FeatureSet
cannam@173 135 PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
cannam@173 136 {
cannam@173 137 return m_impl->process(inputBuffers, timestamp);
cannam@173 138 }
cannam@173 139
cannam@174 140 Plugin::FeatureSet
cannam@174 141 PluginSummarisingAdapter::getRemainingFeatures()
cannam@174 142 {
cannam@174 143 return m_impl->getRemainingFeatures();
cannam@174 144 }
cannam@174 145
cannam@175 146 Plugin::FeatureList
cannam@180 147 PluginSummarisingAdapter::getSummaryForOutput(int output,
cannam@180 148 SummaryType type,
cannam@180 149 AveragingMethod avg)
cannam@175 150 {
cannam@180 151 return m_impl->getSummaryForOutput(output, type, avg);
cannam@176 152 }
cannam@176 153
cannam@176 154 Plugin::FeatureSet
cannam@180 155 PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
cannam@180 156 AveragingMethod avg)
cannam@176 157 {
cannam@180 158 return m_impl->getSummaryForAllOutputs(type, avg);
cannam@175 159 }
cannam@173 160
cannam@173 161 PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
cannam@181 162 m_plugin(plugin),
cannam@183 163 m_inputSampleRate(inputSampleRate),
cannam@183 164 m_reduced(false)
cannam@173 165 {
cannam@173 166 }
cannam@173 167
cannam@173 168 PluginSummarisingAdapter::Impl::~Impl()
cannam@173 169 {
cannam@173 170 }
cannam@173 171
cannam@174 172 Plugin::FeatureSet
cannam@174 173 PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp)
cannam@174 174 {
cannam@183 175 if (m_reduced) {
cannam@183 176 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
cannam@183 177 }
cannam@174 178 FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
cannam@180 179 accumulate(fs, timestamp, false);
cannam@174 180 m_lastTimestamp = timestamp;
cannam@174 181 return fs;
cannam@174 182 }
cannam@174 183
cannam@174 184 Plugin::FeatureSet
cannam@174 185 PluginSummarisingAdapter::Impl::getRemainingFeatures()
cannam@174 186 {
cannam@183 187 if (m_reduced) {
cannam@183 188 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
cannam@183 189 }
cannam@174 190 FeatureSet fs = m_plugin->getRemainingFeatures();
cannam@180 191 accumulate(fs, m_lastTimestamp, true);
cannam@174 192 return fs;
cannam@174 193 }
cannam@174 194
cannam@175 195 Plugin::FeatureList
cannam@180 196 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
cannam@180 197 SummaryType type,
cannam@180 198 AveragingMethod avg)
cannam@175 199 {
cannam@183 200 if (!m_reduced) reduce();
cannam@183 201
cannam@180 202 bool continuous = (avg == ContinuousTimeAverage);
cannam@180 203
cannam@175 204 FeatureList fl;
cannam@175 205 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
cannam@175 206 i != m_summaries[output].end(); ++i) {
cannam@177 207
cannam@175 208 Feature f;
cannam@175 209 f.hasTimestamp = true;
cannam@175 210 f.timestamp = i->first;
cannam@175 211 f.hasDuration = false;
cannam@177 212
cannam@175 213 for (OutputSummary::const_iterator j = i->second.begin();
cannam@175 214 j != i->second.end(); ++j) {
cannam@175 215
cannam@175 216 // these will be ordered by bin number, and no bin numbers
cannam@175 217 // will be missing except at the end (because of the way
cannam@175 218 // the accumulators were initially filled in accumulate())
cannam@175 219
cannam@175 220 const OutputBinSummary &summary = j->second;
cannam@175 221 float result = 0.f;
cannam@175 222
cannam@175 223 switch (type) {
cannam@175 224
cannam@175 225 case Minimum:
cannam@175 226 result = summary.minimum;
cannam@175 227 break;
cannam@175 228
cannam@175 229 case Maximum:
cannam@175 230 result = summary.maximum;
cannam@175 231 break;
cannam@175 232
cannam@175 233 case Mean:
cannam@180 234 if (continuous) {
cannam@180 235 result = summary.mean_c;
cannam@180 236 } else if (summary.count) {
cannam@175 237 result = summary.sum / summary.count;
cannam@175 238 }
cannam@175 239 break;
cannam@175 240
cannam@175 241 case Median:
cannam@180 242 if (continuous) result = summary.median_c;
cannam@180 243 else result = summary.median;
cannam@175 244 break;
cannam@175 245
cannam@175 246 case Mode:
cannam@180 247 if (continuous) result = summary.mode_c;
cannam@180 248 else result = summary.mode;
cannam@175 249 break;
cannam@175 250
cannam@175 251 case Sum:
cannam@175 252 result = summary.sum;
cannam@175 253 break;
cannam@175 254
cannam@175 255 case Variance:
cannam@180 256 if (continuous) result = summary.variance_c;
cannam@180 257 else result = summary.variance;
cannam@175 258 break;
cannam@175 259
cannam@175 260 case StandardDeviation:
cannam@180 261 if (continuous) result = sqrtf(summary.variance_c);
cannam@180 262 else result = sqrtf(summary.variance);
cannam@175 263 break;
cannam@175 264
cannam@175 265 case Count:
cannam@175 266 result = summary.count;
cannam@175 267 break;
cannam@180 268
cannam@180 269 default:
cannam@180 270 break;
cannam@175 271 }
cannam@177 272
cannam@177 273 f.values.push_back(result);
cannam@175 274 }
cannam@175 275
cannam@175 276 fl.push_back(f);
cannam@175 277 }
cannam@175 278 return fl;
cannam@175 279 }
cannam@175 280
cannam@176 281 Plugin::FeatureSet
cannam@180 282 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
cannam@180 283 AveragingMethod avg)
cannam@176 284 {
cannam@183 285 if (!m_reduced) reduce();
cannam@183 286
cannam@176 287 FeatureSet fs;
cannam@176 288 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
cannam@176 289 i != m_summaries.end(); ++i) {
cannam@180 290 fs[i->first] = getSummaryForOutput(i->first, type, avg);
cannam@176 291 }
cannam@176 292 return fs;
cannam@176 293 }
cannam@176 294
cannam@174 295 void
cannam@174 296 PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
cannam@180 297 RealTime timestamp,
cannam@180 298 bool final)
cannam@174 299 {
cannam@174 300 for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
cannam@174 301 for (FeatureList::const_iterator j = i->second.begin();
cannam@174 302 j != i->second.end(); ++j) {
cannam@182 303 if (j->hasTimestamp) {
cannam@182 304 accumulate(i->first, *j, j->timestamp, final);
cannam@182 305 } else {
cannam@182 306 //!!! is this correct?
cannam@182 307 accumulate(i->first, *j, timestamp, final);
cannam@182 308 }
cannam@174 309 }
cannam@174 310 }
cannam@174 311 }
cannam@174 312
cannam@174 313 void
cannam@174 314 PluginSummarisingAdapter::Impl::accumulate(int output,
cannam@174 315 const Feature &f,
cannam@180 316 RealTime timestamp,
cannam@180 317 bool final)
cannam@174 318 {
cannam@180 319 //!!! to do: use timestamp to determine which segment we're on
cannam@180 320
cannam@174 321 m_accumulators[output].count++;
cannam@180 322
cannam@182 323 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << std::endl;
cannam@182 324
cannam@182 325 //!!! also, this will not work if we are called repeatedly with
cannam@182 326 //!!! the same timestamp -- no values will be registered until a
cannam@182 327 //!!! new timestamp is seen -- we need a better test for "not
cannam@182 328 //!!! first result" below
cannam@182 329
cannam@183 330 if (m_prevDurations[output] == RealTime::zeroTime) {
cannam@180 331 if (m_prevTimestamps.find(output) != m_prevTimestamps.end()) {
cannam@183 332 m_prevDurations[output] = timestamp - m_prevTimestamps[output];
cannam@180 333 }
cannam@180 334 }
cannam@183 335 if (m_prevDurations[output] != RealTime::zeroTime ||
cannam@180 336 !m_accumulators[output].durations.empty()) {
cannam@180 337 // ... i.e. if not first result. We don't push a duration
cannam@180 338 // when we process the first result; then the duration we push
cannam@180 339 // each time is that for the result before the one we're
cannam@180 340 // processing, and we push an extra one at the end. This
cannam@180 341 // permits handling the case where the feature itself doesn't
cannam@180 342 // have a duration field, and we have to calculate it from the
cannam@180 343 // time to the following feature. The net effect is simply
cannam@180 344 // that values[n] and durations[n] refer to the same result.
cannam@183 345 m_accumulators[output].durations.push_back(m_prevDurations[output]);
cannam@180 346 }
cannam@180 347
cannam@180 348 m_prevTimestamps[output] = timestamp;
cannam@180 349
cannam@174 350 for (int i = 0; i < int(f.values.size()); ++i) {
cannam@174 351 m_accumulators[output].values[i].push_back(f.values[i]);
cannam@174 352 }
cannam@180 353
cannam@180 354 if (final) {
cannam@180 355 RealTime finalDuration;
cannam@180 356 if (f.hasDuration) finalDuration = f.duration;
cannam@180 357 m_accumulators[output].durations.push_back(finalDuration);
cannam@180 358 }
cannam@180 359
cannam@183 360 if (f.hasDuration) m_prevDurations[output] = f.duration;
cannam@183 361 else m_prevDurations[output] = RealTime::zeroTime;
cannam@174 362 }
cannam@174 363
cannam@181 364 struct ValueDurationFloatPair
cannam@181 365 {
cannam@181 366 float value;
cannam@181 367 float duration;
cannam@181 368
cannam@181 369 ValueDurationFloatPair() : value(0), duration(0) { }
cannam@181 370 ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
cannam@181 371 ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
cannam@181 372 value = p.value;
cannam@181 373 duration = p.duration;
cannam@181 374 return *this;
cannam@181 375 }
cannam@181 376 bool operator<(const ValueDurationFloatPair &p) const {
cannam@181 377 return value < p.value;
cannam@181 378 }
cannam@181 379 };
cannam@181 380
cannam@181 381 static double toSec(const RealTime &r)
cannam@181 382 {
cannam@181 383 return r.sec + double(r.nsec) / 1000000000.0;
cannam@181 384 }
cannam@181 385
cannam@174 386 void
cannam@174 387 PluginSummarisingAdapter::Impl::reduce()
cannam@174 388 {
cannam@174 389 RealTime segmentStart = RealTime::zeroTime; //!!!
cannam@174 390
cannam@174 391 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
cannam@174 392 i != m_accumulators.end(); ++i) {
cannam@174 393
cannam@174 394 int output = i->first;
cannam@174 395 OutputAccumulator &accumulator = i->second;
cannam@174 396
cannam@182 397 double totalDuration = 0.0;
cannam@181 398 for (int k = 0; k < accumulator.durations.size(); ++k) {
cannam@181 399 totalDuration += toSec(accumulator.durations[k]);
cannam@180 400 }
cannam@180 401
cannam@174 402 for (BinValueMap::iterator j = accumulator.values.begin();
cannam@174 403 j != accumulator.values.end(); ++j) {
cannam@174 404
cannam@180 405 // work on all values over time for a single bin
cannam@180 406
cannam@174 407 int bin = j->first;
cannam@181 408 const ValueList &values = j->second;
cannam@180 409 const DurationList &durations = accumulator.durations;
cannam@174 410
cannam@174 411 OutputBinSummary summary;
cannam@180 412
cannam@180 413 summary.count = accumulator.count;
cannam@180 414
cannam@174 415 summary.minimum = 0.f;
cannam@174 416 summary.maximum = 0.f;
cannam@180 417
cannam@174 418 summary.median = 0.f;
cannam@174 419 summary.mode = 0.f;
cannam@174 420 summary.sum = 0.f;
cannam@174 421 summary.variance = 0.f;
cannam@180 422
cannam@180 423 summary.median_c = 0.f;
cannam@180 424 summary.mode_c = 0.f;
cannam@180 425 summary.mean_c = 0.f;
cannam@180 426 summary.variance_c = 0.f;
cannam@180 427
cannam@174 428 if (summary.count == 0 || values.empty()) continue;
cannam@174 429
cannam@174 430 int sz = values.size();
cannam@174 431
cannam@180 432 if (sz != durations.size()) {
cannam@180 433 std::cerr << "WARNING: sz " << sz << " != durations.size() "
cannam@180 434 << durations.size() << std::endl;
cannam@181 435 // while (durations.size() < sz) {
cannam@181 436 // durations.push_back(RealTime::zeroTime);
cannam@181 437 // }
cannam@181 438 //!!! then what?
cannam@180 439 }
cannam@180 440
cannam@181 441 std::vector<ValueDurationFloatPair> valvec;
cannam@181 442
cannam@181 443 for (int k = 0; k < sz; ++k) {
cannam@181 444 valvec.push_back(ValueDurationFloatPair(values[k],
cannam@181 445 toSec(durations[k])));
cannam@181 446 }
cannam@181 447
cannam@181 448 std::sort(valvec.begin(), valvec.end());
cannam@181 449
cannam@181 450 summary.minimum = valvec[0].value;
cannam@181 451 summary.maximum = valvec[sz-1].value;
cannam@174 452
cannam@174 453 if (sz % 2 == 1) {
cannam@181 454 summary.median = valvec[sz/2].value;
cannam@174 455 } else {
cannam@181 456 summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
cannam@174 457 }
cannam@181 458
cannam@181 459 double duracc = 0.0;
cannam@181 460 summary.median_c = valvec[sz-1].value;
cannam@174 461
cannam@181 462 for (int k = 0; k < sz; ++k) {
cannam@181 463 duracc += valvec[k].duration;
cannam@181 464 if (duracc > totalDuration/2) {
cannam@181 465 summary.median_c = valvec[k].value;
cannam@181 466 break;
cannam@181 467 }
cannam@181 468 }
cannam@181 469
cannam@174 470 std::map<float, int> distribution;
cannam@174 471
cannam@174 472 for (int k = 0; k < sz; ++k) {
cannam@174 473 summary.sum += values[k];
cannam@180 474 distribution[values[k]] += 1;
cannam@174 475 }
cannam@174 476
cannam@174 477 int md = 0;
cannam@174 478
cannam@174 479 for (std::map<float, int>::iterator di = distribution.begin();
cannam@174 480 di != distribution.end(); ++di) {
cannam@174 481 if (di->second > md) {
cannam@174 482 md = di->second;
cannam@174 483 summary.mode = di->first;
cannam@174 484 }
cannam@174 485 }
cannam@174 486
cannam@174 487 distribution.clear();
cannam@174 488
cannam@180 489 //!!! we want to omit this bit if the features all have
cannam@180 490 //!!! equal duration (and set mode_c equal to mode instead)
cannam@180 491
cannam@181 492 std::map<float, double> distribution_c;
cannam@180 493
cannam@180 494 for (int k = 0; k < sz; ++k) {
cannam@181 495 distribution_c[values[k]] += toSec(durations[k]);
cannam@180 496 }
cannam@180 497
cannam@181 498 double mrd = 0.0;
cannam@180 499
cannam@181 500 for (std::map<float, double>::iterator di = distribution_c.begin();
cannam@180 501 di != distribution_c.end(); ++di) {
cannam@180 502 if (di->second > mrd) {
cannam@180 503 mrd = di->second;
cannam@180 504 summary.mode_c = di->first;
cannam@180 505 }
cannam@180 506 }
cannam@180 507
cannam@180 508 distribution_c.clear();
cannam@180 509
cannam@181 510 if (totalDuration > 0.0) {
cannam@181 511
cannam@181 512 double sum_c = 0.0;
cannam@181 513
cannam@181 514 for (int k = 0; k < sz; ++k) {
cannam@181 515 double value = values[k] * toSec(durations[k]);
cannam@181 516 sum_c += value;
cannam@181 517 }
cannam@182 518
cannam@182 519 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
cannam@182 520 << sum_c / totalDuration << std::endl;
cannam@181 521
cannam@181 522 summary.mean_c = sum_c / totalDuration;
cannam@181 523
cannam@181 524 for (int k = 0; k < sz; ++k) {
cannam@181 525 double value = values[k] * toSec(durations[k]);
cannam@181 526 summary.variance_c +=
cannam@181 527 (value - summary.mean_c) * (value - summary.mean_c);
cannam@181 528 }
cannam@181 529
cannam@181 530 summary.variance_c /= summary.count;
cannam@181 531 }
cannam@181 532
cannam@181 533 //!!! still to handle: median_c
cannam@180 534
cannam@174 535 float mean = summary.sum / summary.count;
cannam@174 536
cannam@182 537 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
cannam@182 538 << summary.sum / summary.count << std::endl;
cannam@182 539
cannam@174 540 for (int k = 0; k < sz; ++k) {
cannam@174 541 summary.variance += (values[k] - mean) * (values[k] - mean);
cannam@174 542 }
cannam@174 543 summary.variance /= summary.count;
cannam@174 544
cannam@174 545 m_summaries[output][segmentStart][bin] = summary;
cannam@174 546 }
cannam@174 547 }
cannam@175 548
cannam@175 549 m_accumulators.clear();
cannam@183 550 m_reduced = true;
cannam@174 551 }
cannam@174 552
cannam@174 553
cannam@174 554 }
cannam@174 555
cannam@174 556 }
cannam@174 557