annotate vamp-sdk/hostext/PluginSummarisingAdapter.cpp @ 182:3fcac0f3afdc

* More fixes to continuous time averaging
author cannam
date Thu, 04 Sep 2008 16:15:01 +0000
parents cd16cbf80c87
children c053ababbf7e
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@174 86
cannam@174 87 struct OutputBinSummary {
cannam@180 88
cannam@180 89 int count;
cannam@180 90
cannam@180 91 // extents
cannam@174 92 float minimum;
cannam@174 93 float maximum;
cannam@180 94 float sum;
cannam@180 95
cannam@180 96 // sample-average results
cannam@174 97 float median;
cannam@174 98 float mode;
cannam@174 99 float variance;
cannam@180 100
cannam@180 101 // continuous-time average results
cannam@180 102 float median_c;
cannam@180 103 float mode_c;
cannam@180 104 float mean_c;
cannam@180 105 float variance_c;
cannam@174 106 };
cannam@174 107
cannam@174 108 typedef std::map<int, OutputBinSummary> OutputSummary;
cannam@174 109 typedef std::map<RealTime, OutputSummary> SummarySegmentMap;
cannam@174 110 typedef std::map<int, SummarySegmentMap> OutputSummarySegmentMap;
cannam@174 111
cannam@174 112 OutputSummarySegmentMap m_summaries;
cannam@174 113
cannam@174 114 RealTime m_lastTimestamp;
cannam@180 115 RealTime m_prevDuration;
cannam@174 116
cannam@180 117 void accumulate(const FeatureSet &fs, RealTime, bool final);
cannam@180 118 void accumulate(int output, const Feature &f, RealTime, bool final);
cannam@174 119 void reduce();
cannam@173 120 };
cannam@173 121
cannam@173 122 PluginSummarisingAdapter::PluginSummarisingAdapter(Plugin *plugin) :
cannam@173 123 PluginWrapper(plugin)
cannam@173 124 {
cannam@173 125 m_impl = new Impl(plugin, m_inputSampleRate);
cannam@173 126 }
cannam@173 127
cannam@173 128 PluginSummarisingAdapter::~PluginSummarisingAdapter()
cannam@173 129 {
cannam@173 130 delete m_impl;
cannam@173 131 }
cannam@173 132
cannam@173 133 Plugin::FeatureSet
cannam@173 134 PluginSummarisingAdapter::process(const float *const *inputBuffers, RealTime timestamp)
cannam@173 135 {
cannam@173 136 return m_impl->process(inputBuffers, timestamp);
cannam@173 137 }
cannam@173 138
cannam@174 139 Plugin::FeatureSet
cannam@174 140 PluginSummarisingAdapter::getRemainingFeatures()
cannam@174 141 {
cannam@174 142 return m_impl->getRemainingFeatures();
cannam@174 143 }
cannam@174 144
cannam@175 145 Plugin::FeatureList
cannam@180 146 PluginSummarisingAdapter::getSummaryForOutput(int output,
cannam@180 147 SummaryType type,
cannam@180 148 AveragingMethod avg)
cannam@175 149 {
cannam@180 150 return m_impl->getSummaryForOutput(output, type, avg);
cannam@176 151 }
cannam@176 152
cannam@176 153 Plugin::FeatureSet
cannam@180 154 PluginSummarisingAdapter::getSummaryForAllOutputs(SummaryType type,
cannam@180 155 AveragingMethod avg)
cannam@176 156 {
cannam@180 157 return m_impl->getSummaryForAllOutputs(type, avg);
cannam@175 158 }
cannam@173 159
cannam@173 160 PluginSummarisingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
cannam@181 161 m_plugin(plugin),
cannam@181 162 m_inputSampleRate(inputSampleRate)
cannam@173 163 {
cannam@173 164 }
cannam@173 165
cannam@173 166 PluginSummarisingAdapter::Impl::~Impl()
cannam@173 167 {
cannam@173 168 }
cannam@173 169
cannam@174 170 Plugin::FeatureSet
cannam@174 171 PluginSummarisingAdapter::Impl::process(const float *const *inputBuffers, RealTime timestamp)
cannam@174 172 {
cannam@174 173 FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
cannam@180 174 accumulate(fs, timestamp, false);
cannam@174 175 m_lastTimestamp = timestamp;
cannam@174 176 return fs;
cannam@174 177 }
cannam@174 178
cannam@174 179 Plugin::FeatureSet
cannam@174 180 PluginSummarisingAdapter::Impl::getRemainingFeatures()
cannam@174 181 {
cannam@174 182 FeatureSet fs = m_plugin->getRemainingFeatures();
cannam@180 183 accumulate(fs, m_lastTimestamp, true);
cannam@174 184 reduce();
cannam@174 185 return fs;
cannam@174 186 }
cannam@174 187
cannam@175 188 Plugin::FeatureList
cannam@180 189 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
cannam@180 190 SummaryType type,
cannam@180 191 AveragingMethod avg)
cannam@175 192 {
cannam@180 193 bool continuous = (avg == ContinuousTimeAverage);
cannam@180 194
cannam@175 195 //!!! need to ensure that this is only called after processing is
cannam@175 196 //!!! complete (at the moment processing is "completed" in the
cannam@175 197 //!!! call to getRemainingFeatures, but we don't want to require
cannam@175 198 //!!! the host to call getRemainingFeatures at all unless it
cannam@175 199 //!!! actually wants the raw features too -- calling getSummary
cannam@175 200 //!!! should be enough -- we do need to ensure that all data has
cannam@175 201 //!!! been processed though!)
cannam@175 202 FeatureList fl;
cannam@175 203 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
cannam@175 204 i != m_summaries[output].end(); ++i) {
cannam@177 205
cannam@175 206 Feature f;
cannam@175 207 f.hasTimestamp = true;
cannam@175 208 f.timestamp = i->first;
cannam@175 209 f.hasDuration = false;
cannam@177 210
cannam@175 211 for (OutputSummary::const_iterator j = i->second.begin();
cannam@175 212 j != i->second.end(); ++j) {
cannam@175 213
cannam@175 214 // these will be ordered by bin number, and no bin numbers
cannam@175 215 // will be missing except at the end (because of the way
cannam@175 216 // the accumulators were initially filled in accumulate())
cannam@175 217
cannam@175 218 const OutputBinSummary &summary = j->second;
cannam@175 219 float result = 0.f;
cannam@175 220
cannam@175 221 switch (type) {
cannam@175 222
cannam@175 223 case Minimum:
cannam@175 224 result = summary.minimum;
cannam@175 225 break;
cannam@175 226
cannam@175 227 case Maximum:
cannam@175 228 result = summary.maximum;
cannam@175 229 break;
cannam@175 230
cannam@175 231 case Mean:
cannam@180 232 if (continuous) {
cannam@180 233 result = summary.mean_c;
cannam@180 234 } else if (summary.count) {
cannam@175 235 result = summary.sum / summary.count;
cannam@175 236 }
cannam@175 237 break;
cannam@175 238
cannam@175 239 case Median:
cannam@180 240 if (continuous) result = summary.median_c;
cannam@180 241 else result = summary.median;
cannam@175 242 break;
cannam@175 243
cannam@175 244 case Mode:
cannam@180 245 if (continuous) result = summary.mode_c;
cannam@180 246 else result = summary.mode;
cannam@175 247 break;
cannam@175 248
cannam@175 249 case Sum:
cannam@175 250 result = summary.sum;
cannam@175 251 break;
cannam@175 252
cannam@175 253 case Variance:
cannam@180 254 if (continuous) result = summary.variance_c;
cannam@180 255 else result = summary.variance;
cannam@175 256 break;
cannam@175 257
cannam@175 258 case StandardDeviation:
cannam@180 259 if (continuous) result = sqrtf(summary.variance_c);
cannam@180 260 else result = sqrtf(summary.variance);
cannam@175 261 break;
cannam@175 262
cannam@175 263 case Count:
cannam@175 264 result = summary.count;
cannam@175 265 break;
cannam@180 266
cannam@180 267 default:
cannam@180 268 break;
cannam@175 269 }
cannam@177 270
cannam@177 271 f.values.push_back(result);
cannam@175 272 }
cannam@175 273
cannam@175 274 fl.push_back(f);
cannam@175 275 }
cannam@175 276 return fl;
cannam@175 277 }
cannam@175 278
cannam@176 279 Plugin::FeatureSet
cannam@180 280 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
cannam@180 281 AveragingMethod avg)
cannam@176 282 {
cannam@176 283 FeatureSet fs;
cannam@176 284 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
cannam@176 285 i != m_summaries.end(); ++i) {
cannam@180 286 fs[i->first] = getSummaryForOutput(i->first, type, avg);
cannam@176 287 }
cannam@176 288 return fs;
cannam@176 289 }
cannam@176 290
cannam@174 291 void
cannam@174 292 PluginSummarisingAdapter::Impl::accumulate(const FeatureSet &fs,
cannam@180 293 RealTime timestamp,
cannam@180 294 bool final)
cannam@174 295 {
cannam@174 296 for (FeatureSet::const_iterator i = fs.begin(); i != fs.end(); ++i) {
cannam@174 297 for (FeatureList::const_iterator j = i->second.begin();
cannam@174 298 j != i->second.end(); ++j) {
cannam@182 299 if (j->hasTimestamp) {
cannam@182 300 accumulate(i->first, *j, j->timestamp, final);
cannam@182 301 } else {
cannam@182 302 //!!! is this correct?
cannam@182 303 accumulate(i->first, *j, timestamp, final);
cannam@182 304 }
cannam@174 305 }
cannam@174 306 }
cannam@174 307 }
cannam@174 308
cannam@174 309 void
cannam@174 310 PluginSummarisingAdapter::Impl::accumulate(int output,
cannam@174 311 const Feature &f,
cannam@180 312 RealTime timestamp,
cannam@180 313 bool final)
cannam@174 314 {
cannam@180 315 //!!! to do: use timestamp to determine which segment we're on
cannam@180 316
cannam@174 317 m_accumulators[output].count++;
cannam@180 318
cannam@182 319 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << std::endl;
cannam@182 320
cannam@182 321 //!!! m_prevDuration needs to be per output
cannam@182 322
cannam@182 323 //!!! also, this will not work if we are called repeatedly with
cannam@182 324 //!!! the same timestamp -- no values will be registered until a
cannam@182 325 //!!! new timestamp is seen -- we need a better test for "not
cannam@182 326 //!!! first result" below
cannam@182 327
cannam@180 328 if (m_prevDuration == RealTime::zeroTime) {
cannam@180 329 if (m_prevTimestamps.find(output) != m_prevTimestamps.end()) {
cannam@180 330 m_prevDuration = timestamp - m_prevTimestamps[output];
cannam@180 331 }
cannam@180 332 }
cannam@180 333 if (m_prevDuration != RealTime::zeroTime ||
cannam@180 334 !m_accumulators[output].durations.empty()) {
cannam@180 335 // ... i.e. if not first result. We don't push a duration
cannam@180 336 // when we process the first result; then the duration we push
cannam@180 337 // each time is that for the result before the one we're
cannam@180 338 // processing, and we push an extra one at the end. This
cannam@180 339 // permits handling the case where the feature itself doesn't
cannam@180 340 // have a duration field, and we have to calculate it from the
cannam@180 341 // time to the following feature. The net effect is simply
cannam@180 342 // that values[n] and durations[n] refer to the same result.
cannam@180 343 m_accumulators[output].durations.push_back(m_prevDuration);
cannam@180 344 }
cannam@180 345
cannam@180 346 m_prevTimestamps[output] = timestamp;
cannam@180 347
cannam@174 348 for (int i = 0; i < int(f.values.size()); ++i) {
cannam@174 349 m_accumulators[output].values[i].push_back(f.values[i]);
cannam@174 350 }
cannam@180 351
cannam@180 352 if (final) {
cannam@180 353 RealTime finalDuration;
cannam@180 354 if (f.hasDuration) finalDuration = f.duration;
cannam@180 355 m_accumulators[output].durations.push_back(finalDuration);
cannam@180 356 }
cannam@180 357
cannam@180 358 if (f.hasDuration) m_prevDuration = f.duration;
cannam@180 359 else m_prevDuration = RealTime::zeroTime;
cannam@174 360 }
cannam@174 361
cannam@181 362 struct ValueDurationFloatPair
cannam@181 363 {
cannam@181 364 float value;
cannam@181 365 float duration;
cannam@181 366
cannam@181 367 ValueDurationFloatPair() : value(0), duration(0) { }
cannam@181 368 ValueDurationFloatPair(float v, float d) : value(v), duration(d) { }
cannam@181 369 ValueDurationFloatPair &operator=(const ValueDurationFloatPair &p) {
cannam@181 370 value = p.value;
cannam@181 371 duration = p.duration;
cannam@181 372 return *this;
cannam@181 373 }
cannam@181 374 bool operator<(const ValueDurationFloatPair &p) const {
cannam@181 375 return value < p.value;
cannam@181 376 }
cannam@181 377 };
cannam@181 378
cannam@181 379 static double toSec(const RealTime &r)
cannam@181 380 {
cannam@181 381 return r.sec + double(r.nsec) / 1000000000.0;
cannam@181 382 }
cannam@181 383
cannam@174 384 void
cannam@174 385 PluginSummarisingAdapter::Impl::reduce()
cannam@174 386 {
cannam@174 387 RealTime segmentStart = RealTime::zeroTime; //!!!
cannam@174 388
cannam@174 389 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
cannam@174 390 i != m_accumulators.end(); ++i) {
cannam@174 391
cannam@174 392 int output = i->first;
cannam@174 393 OutputAccumulator &accumulator = i->second;
cannam@174 394
cannam@182 395 double totalDuration = 0.0;
cannam@181 396 for (int k = 0; k < accumulator.durations.size(); ++k) {
cannam@181 397 totalDuration += toSec(accumulator.durations[k]);
cannam@180 398 }
cannam@180 399
cannam@174 400 for (BinValueMap::iterator j = accumulator.values.begin();
cannam@174 401 j != accumulator.values.end(); ++j) {
cannam@174 402
cannam@180 403 // work on all values over time for a single bin
cannam@180 404
cannam@174 405 int bin = j->first;
cannam@181 406 const ValueList &values = j->second;
cannam@180 407 const DurationList &durations = accumulator.durations;
cannam@174 408
cannam@174 409 OutputBinSummary summary;
cannam@180 410
cannam@180 411 summary.count = accumulator.count;
cannam@180 412
cannam@174 413 summary.minimum = 0.f;
cannam@174 414 summary.maximum = 0.f;
cannam@180 415
cannam@174 416 summary.median = 0.f;
cannam@174 417 summary.mode = 0.f;
cannam@174 418 summary.sum = 0.f;
cannam@174 419 summary.variance = 0.f;
cannam@180 420
cannam@180 421 summary.median_c = 0.f;
cannam@180 422 summary.mode_c = 0.f;
cannam@180 423 summary.mean_c = 0.f;
cannam@180 424 summary.variance_c = 0.f;
cannam@180 425
cannam@174 426 if (summary.count == 0 || values.empty()) continue;
cannam@174 427
cannam@174 428 int sz = values.size();
cannam@174 429
cannam@180 430 if (sz != durations.size()) {
cannam@180 431 std::cerr << "WARNING: sz " << sz << " != durations.size() "
cannam@180 432 << durations.size() << std::endl;
cannam@181 433 // while (durations.size() < sz) {
cannam@181 434 // durations.push_back(RealTime::zeroTime);
cannam@181 435 // }
cannam@181 436 //!!! then what?
cannam@180 437 }
cannam@180 438
cannam@181 439 std::vector<ValueDurationFloatPair> valvec;
cannam@181 440
cannam@181 441 for (int k = 0; k < sz; ++k) {
cannam@181 442 valvec.push_back(ValueDurationFloatPair(values[k],
cannam@181 443 toSec(durations[k])));
cannam@181 444 }
cannam@181 445
cannam@181 446 std::sort(valvec.begin(), valvec.end());
cannam@181 447
cannam@181 448 summary.minimum = valvec[0].value;
cannam@181 449 summary.maximum = valvec[sz-1].value;
cannam@174 450
cannam@174 451 if (sz % 2 == 1) {
cannam@181 452 summary.median = valvec[sz/2].value;
cannam@174 453 } else {
cannam@181 454 summary.median = (valvec[sz/2].value + valvec[sz/2 + 1].value) / 2;
cannam@174 455 }
cannam@181 456
cannam@181 457 double duracc = 0.0;
cannam@181 458 summary.median_c = valvec[sz-1].value;
cannam@174 459
cannam@181 460 for (int k = 0; k < sz; ++k) {
cannam@181 461 duracc += valvec[k].duration;
cannam@181 462 if (duracc > totalDuration/2) {
cannam@181 463 summary.median_c = valvec[k].value;
cannam@181 464 break;
cannam@181 465 }
cannam@181 466 }
cannam@181 467
cannam@174 468 std::map<float, int> distribution;
cannam@174 469
cannam@174 470 for (int k = 0; k < sz; ++k) {
cannam@174 471 summary.sum += values[k];
cannam@180 472 distribution[values[k]] += 1;
cannam@174 473 }
cannam@174 474
cannam@174 475 int md = 0;
cannam@174 476
cannam@174 477 for (std::map<float, int>::iterator di = distribution.begin();
cannam@174 478 di != distribution.end(); ++di) {
cannam@174 479 if (di->second > md) {
cannam@174 480 md = di->second;
cannam@174 481 summary.mode = di->first;
cannam@174 482 }
cannam@174 483 }
cannam@174 484
cannam@174 485 distribution.clear();
cannam@174 486
cannam@180 487 //!!! we want to omit this bit if the features all have
cannam@180 488 //!!! equal duration (and set mode_c equal to mode instead)
cannam@180 489
cannam@181 490 std::map<float, double> distribution_c;
cannam@180 491
cannam@180 492 for (int k = 0; k < sz; ++k) {
cannam@181 493 distribution_c[values[k]] += toSec(durations[k]);
cannam@180 494 }
cannam@180 495
cannam@181 496 double mrd = 0.0;
cannam@180 497
cannam@181 498 for (std::map<float, double>::iterator di = distribution_c.begin();
cannam@180 499 di != distribution_c.end(); ++di) {
cannam@180 500 if (di->second > mrd) {
cannam@180 501 mrd = di->second;
cannam@180 502 summary.mode_c = di->first;
cannam@180 503 }
cannam@180 504 }
cannam@180 505
cannam@180 506 distribution_c.clear();
cannam@180 507
cannam@181 508 if (totalDuration > 0.0) {
cannam@181 509
cannam@181 510 double sum_c = 0.0;
cannam@181 511
cannam@181 512 for (int k = 0; k < sz; ++k) {
cannam@181 513 double value = values[k] * toSec(durations[k]);
cannam@181 514 sum_c += value;
cannam@181 515 }
cannam@182 516
cannam@182 517 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
cannam@182 518 << sum_c / totalDuration << std::endl;
cannam@181 519
cannam@181 520 summary.mean_c = sum_c / totalDuration;
cannam@181 521
cannam@181 522 for (int k = 0; k < sz; ++k) {
cannam@181 523 double value = values[k] * toSec(durations[k]);
cannam@181 524 summary.variance_c +=
cannam@181 525 (value - summary.mean_c) * (value - summary.mean_c);
cannam@181 526 }
cannam@181 527
cannam@181 528 summary.variance_c /= summary.count;
cannam@181 529 }
cannam@181 530
cannam@181 531 //!!! still to handle: median_c
cannam@180 532
cannam@174 533 float mean = summary.sum / summary.count;
cannam@174 534
cannam@182 535 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
cannam@182 536 << summary.sum / summary.count << std::endl;
cannam@182 537
cannam@174 538 for (int k = 0; k < sz; ++k) {
cannam@174 539 summary.variance += (values[k] - mean) * (values[k] - mean);
cannam@174 540 }
cannam@174 541 summary.variance /= summary.count;
cannam@174 542
cannam@174 543 m_summaries[output][segmentStart][bin] = summary;
cannam@174 544 }
cannam@174 545 }
cannam@175 546
cannam@175 547 m_accumulators.clear();
cannam@174 548 }
cannam@174 549
cannam@174 550
cannam@174 551 }
cannam@174 552
cannam@174 553 }
cannam@174 554