comparison vamp-sdk/hostext/PluginSummarisingAdapter.cpp @ 185:701505ac170c

* Reorganise accumulators to facilitate segmentation
author cannam
date Thu, 11 Sep 2008 13:46:59 +0000
parents 26c200c3fc42
children 8311695c13f9
comparison
equal deleted inserted replaced
184:26c200c3fc42 185:701505ac170c
65 protected: 65 protected:
66 Plugin *m_plugin; 66 Plugin *m_plugin;
67 float m_inputSampleRate; 67 float m_inputSampleRate;
68 68
69 SegmentBoundaries m_boundaries; 69 SegmentBoundaries m_boundaries;
70 70
71 typedef std::vector<float> ValueList; 71 typedef std::vector<float> ValueList;
72 typedef std::map<int, ValueList> BinValueMap; 72
73 typedef std::vector<RealTime> DurationList; 73 struct Result { // smaller than Feature
74 74 RealTime time;
75 RealTime duration;
76 ValueList values; // bin number -> value
77 };
78
79 typedef std::vector<Result> ResultList;
80
75 struct OutputAccumulator { 81 struct OutputAccumulator {
76 int count; 82 int bins;
77 BinValueMap values; // bin number -> values ordered by time 83 ResultList results;
78 DurationList durations; 84 OutputAccumulator() : bins(0) { }
79 OutputAccumulator() : count(0), values(), durations() { }
80 }; 85 };
81 86
82 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap; 87 typedef std::map<int, OutputAccumulator> OutputAccumulatorMap;
83 OutputAccumulatorMap m_accumulators; // output number -> accumulator 88 OutputAccumulatorMap m_accumulators; // output number -> accumulator
89
90 typedef std::map<RealTime, OutputAccumulator> SegmentAccumulatorMap;
91 typedef std::map<int, SegmentAccumulatorMap> OutputSegmentAccumulatorMap;
92 OutputSegmentAccumulatorMap m_segmentedAccumulators;
84 93
85 typedef std::map<int, RealTime> OutputTimestampMap; 94 typedef std::map<int, RealTime> OutputTimestampMap;
86 OutputTimestampMap m_prevTimestamps; // output number -> timestamp 95 OutputTimestampMap m_prevTimestamps; // output number -> timestamp
87 OutputTimestampMap m_prevDurations; // output number -> durations 96 OutputTimestampMap m_prevDurations; // output number -> durations
88 97
117 RealTime m_lastTimestamp; 126 RealTime m_lastTimestamp;
118 127
119 void accumulate(const FeatureSet &fs, RealTime, bool final); 128 void accumulate(const FeatureSet &fs, RealTime, bool final);
120 void accumulate(int output, const Feature &f, RealTime, bool final); 129 void accumulate(int output, const Feature &f, RealTime, bool final);
121 void accumulateFinalDurations(); 130 void accumulateFinalDurations();
131 void segment();
122 void reduce(); 132 void reduce();
123 }; 133 };
124 134
125 static RealTime INVALID_DURATION(INT_MIN, INT_MIN); 135 static RealTime INVALID_DURATION(INT_MIN, INT_MIN);
126 136
179 if (m_reduced) { 189 if (m_reduced) {
180 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl; 190 std::cerr << "WARNING: Cannot call PluginSummarisingAdapter::process() or getRemainingFeatures() after one of the getSummary methods" << std::endl;
181 } 191 }
182 FeatureSet fs = m_plugin->process(inputBuffers, timestamp); 192 FeatureSet fs = m_plugin->process(inputBuffers, timestamp);
183 accumulate(fs, timestamp, false); 193 accumulate(fs, timestamp, false);
194 //!!! should really be "timestamp plus step size"
184 m_lastTimestamp = timestamp; 195 m_lastTimestamp = timestamp;
185 return fs; 196 return fs;
186 } 197 }
187 198
188 Plugin::FeatureSet 199 Plugin::FeatureSet
199 Plugin::FeatureList 210 Plugin::FeatureList
200 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output, 211 PluginSummarisingAdapter::Impl::getSummaryForOutput(int output,
201 SummaryType type, 212 SummaryType type,
202 AveragingMethod avg) 213 AveragingMethod avg)
203 { 214 {
204 if (!m_reduced) reduce(); 215 if (!m_reduced) {
216 segment();
217 reduce();
218 m_reduced = true;
219 }
205 220
206 bool continuous = (avg == ContinuousTimeAverage); 221 bool continuous = (avg == ContinuousTimeAverage);
207 222
208 FeatureList fl; 223 FeatureList fl;
209 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin(); 224 for (SummarySegmentMap::const_iterator i = m_summaries[output].begin();
284 299
285 Plugin::FeatureSet 300 Plugin::FeatureSet
286 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type, 301 PluginSummarisingAdapter::Impl::getSummaryForAllOutputs(SummaryType type,
287 AveragingMethod avg) 302 AveragingMethod avg)
288 { 303 {
289 if (!m_reduced) reduce(); 304 if (!m_reduced) {
305 segment();
306 reduce();
307 m_reduced = true;
308 }
290 309
291 FeatureSet fs; 310 FeatureSet fs;
292 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin(); 311 for (OutputSummarySegmentMap::const_iterator i = m_summaries.begin();
293 i != m_summaries.end(); ++i) { 312 i != m_summaries.end(); ++i) {
294 fs[i->first] = getSummaryForOutput(i->first, type, avg); 313 fs[i->first] = getSummaryForOutput(i->first, type, avg);
319 const Feature &f, 338 const Feature &f,
320 RealTime timestamp, 339 RealTime timestamp,
321 bool final) 340 bool final)
322 { 341 {
323 //!!! to do: use timestamp to determine which segment we're on 342 //!!! to do: use timestamp to determine which segment we're on
324 343
325 m_accumulators[output].count++; 344 //!!! What should happen if a feature's duration spans a segment
345 // boundary? I think we probably want to chop it, and pretend that it
346 // appears in both -- don't we? do we? A very long feature (e.g. key,
347 // if the whole audio is in a single key) might span many or all
348 // segments, and we want that to be reflected in the results (e.g. it
349 // is the modal key in all of those segments, not just the first).
350 // That is actually quite complicated to do!
351
352 //!!! This affects how we record things. If features spanning a
353 // boundary should be chopped, then we need to have per-segment
354 // accumulators (and the feature value goes into both -- perhaps we
355 // need a separate phase to split the accumulator up into segments).
356 // If features spanning a boundary should be counted only in the first
357 // segment, with their full duration, then we should store them in a
358 // single accumulator and distribute into segments only on reduce.
326 359
327 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl; 360 std::cerr << "output " << output << ": timestamp " << timestamp << ", prev timestamp " << m_prevTimestamps[output] << ", final " << final << std::endl;
328 361
329 // At each process step, accumulate() is called once for each 362 // At each process step, accumulate() is called once for each
330 // feature on each output within that process's returned feature 363 // feature on each output within that process's returned feature
371 } 404 }
372 405
373 std::cerr << "output " << output << ": "; 406 std::cerr << "output " << output << ": ";
374 407
375 std::cerr << "Pushing previous duration as " << prevDuration << std::endl; 408 std::cerr << "Pushing previous duration as " << prevDuration << std::endl;
376 m_accumulators[output].durations.push_back(prevDuration); 409
410 m_accumulators[output].results
411 [m_accumulators[output].results.size() - 1]
412 .duration = prevDuration;
377 } 413 }
378 414
379 if (f.hasDuration) m_prevDurations[output] = f.duration; 415 if (f.hasDuration) m_prevDurations[output] = f.duration;
380 else m_prevDurations[output] = INVALID_DURATION; 416 else m_prevDurations[output] = INVALID_DURATION;
381 417
382 m_prevTimestamps[output] = timestamp; 418 m_prevTimestamps[output] = timestamp;
419
420 //!!! should really be "timestamp plus duration" or "timestamp plus output resolution"
383 if (timestamp > m_lastTimestamp) m_lastTimestamp = timestamp; 421 if (timestamp > m_lastTimestamp) m_lastTimestamp = timestamp;
384 422
423 Result result;
424 result.time = timestamp;
425 result.duration = INVALID_DURATION;
426
427 if (f.values.size() > m_accumulators[output].bins) {
428 m_accumulators[output].bins = f.values.size();
429 }
430
385 for (int i = 0; i < int(f.values.size()); ++i) { 431 for (int i = 0; i < int(f.values.size()); ++i) {
386 m_accumulators[output].values[i].push_back(f.values[i]); 432 result.values.push_back(f.values[i]);
387 } 433 }
434
435 m_accumulators[output].results.push_back(result);
388 } 436 }
389 437
390 void 438 void
391 PluginSummarisingAdapter::Impl::accumulateFinalDurations() 439 PluginSummarisingAdapter::Impl::accumulateFinalDurations()
392 { 440 {
393 for (OutputTimestampMap::iterator i = m_prevTimestamps.begin(); 441 for (OutputTimestampMap::iterator i = m_prevTimestamps.begin();
394 i != m_prevTimestamps.end(); ++i) { 442 i != m_prevTimestamps.end(); ++i) {
395 443
396 int output = i->first; 444 int output = i->first;
445
446 int acount = m_accumulators[output].results.size();
447
448 if (acount == 0) continue;
449
397 RealTime prevTimestamp = i->second; 450 RealTime prevTimestamp = i->second;
398 451
399 std::cerr << "output " << output << ": "; 452 std::cerr << "output " << output << ": ";
400 453
401 if (m_prevDurations.find(output) != m_prevDurations.end() && 454 if (m_prevDurations.find(output) != m_prevDurations.end() &&
402 m_prevDurations[output] != INVALID_DURATION) { 455 m_prevDurations[output] != INVALID_DURATION) {
403 456
404 std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl; 457 std::cerr << "Pushing final duration from feature as " << m_prevDurations[output] << std::endl;
405 458
406 m_accumulators[output].durations.push_back(m_prevDurations[output]); 459 m_accumulators[output].results[acount - 1].duration =
460 m_prevDurations[output];
407 461
408 } else { 462 } else {
409 463
410 std::cerr << "Pushing final duration from diff as " << m_lastTimestamp << " - " << m_prevTimestamps[output] << std::endl; 464 std::cerr << "Pushing final duration from diff as " << m_lastTimestamp << " - " << m_prevTimestamps[output] << std::endl;
411 465
412 m_accumulators[output].durations.push_back 466 m_accumulators[output].results[acount - 1].duration =
413 (m_lastTimestamp - m_prevTimestamps[output]); 467 m_lastTimestamp - m_prevTimestamps[output];
414 } 468 }
415 } 469 }
470 }
471
472 void
473 PluginSummarisingAdapter::Impl::segment()
474 {
475 /*
476 SegmentBoundaries::iterator boundaryitr = m_boundaries.begin();
477 RealTime segmentStart = RealTime::zeroTime;
478
479 for (OutputAccumulatorMap::iterator i = m_accumulators.begin();
480 i != m_accumulators.end(); ++i) {
481
482 int output = i->first;
483 OutputAccumulator &source = i->second;
484 RealTime accumulatedTime = RealTime::zeroTime;
485
486 for (int n = 0; n < source.durations.size(); ++n) {
487 */
488
489
490 /*
491 if (boundaryitr == m_boundaries.end()) {
492 m_segmentedAccumulators[output][segmentStart] = source;
493 source.clear();
494 continue;
495 }
496 */
497
498
499
500
416 } 501 }
417 502
418 struct ValueDurationFloatPair 503 struct ValueDurationFloatPair
419 { 504 {
420 float value; 505 float value;
448 i != m_accumulators.end(); ++i) { 533 i != m_accumulators.end(); ++i) {
449 534
450 int output = i->first; 535 int output = i->first;
451 OutputAccumulator &accumulator = i->second; 536 OutputAccumulator &accumulator = i->second;
452 537
538 int sz = accumulator.results.size();
539
453 double totalDuration = 0.0; 540 double totalDuration = 0.0;
454 for (int k = 0; k < accumulator.durations.size(); ++k) { 541 //!!! is this right?
455 totalDuration += toSec(accumulator.durations[k]); 542 if (sz > 0) {
543 totalDuration = toSec(accumulator.results[sz-1].time +
544 accumulator.results[sz-1].duration);
456 } 545 }
457 546
458 for (BinValueMap::iterator j = accumulator.values.begin(); 547 for (int bin = 0; bin < accumulator.bins; ++bin) {
459 j != accumulator.values.end(); ++j) {
460 548
461 // work on all values over time for a single bin 549 // work on all values over time for a single bin
462 550
463 int bin = j->first;
464 const ValueList &values = j->second;
465 const DurationList &durations = accumulator.durations;
466
467 OutputBinSummary summary; 551 OutputBinSummary summary;
468 552
469 summary.count = accumulator.count; 553 summary.count = sz;
470 554
471 summary.minimum = 0.f; 555 summary.minimum = 0.f;
472 summary.maximum = 0.f; 556 summary.maximum = 0.f;
473 557
474 summary.median = 0.f; 558 summary.median = 0.f;
479 summary.median_c = 0.f; 563 summary.median_c = 0.f;
480 summary.mode_c = 0.f; 564 summary.mode_c = 0.f;
481 summary.mean_c = 0.f; 565 summary.mean_c = 0.f;
482 summary.variance_c = 0.f; 566 summary.variance_c = 0.f;
483 567
484 if (summary.count == 0 || values.empty()) continue; 568 if (sz == 0) continue;
485
486 int sz = values.size();
487
488 if (sz != durations.size()) {
489 std::cerr << "WARNING: sz " << sz << " != durations.size() "
490 << durations.size() << std::endl;
491 // while (durations.size() < sz) {
492 // durations.push_back(RealTime::zeroTime);
493 // }
494 //!!! then what?
495 }
496 569
497 std::vector<ValueDurationFloatPair> valvec; 570 std::vector<ValueDurationFloatPair> valvec;
498 571
499 for (int k = 0; k < sz; ++k) { 572 for (int k = 0; k < sz; ++k) {
500 valvec.push_back(ValueDurationFloatPair(values[k], 573 while (accumulator.results[k].values.size() <
501 toSec(durations[k]))); 574 accumulator.bins) {
575 accumulator.results[k].values.push_back(0.f);
576 }
577 }
578
579 for (int k = 0; k < sz; ++k) {
580 float value = accumulator.results[k].values[bin];
581 valvec.push_back(ValueDurationFloatPair
582 (value,
583 toSec(accumulator.results[k].duration)));
502 } 584 }
503 585
504 std::sort(valvec.begin(), valvec.end()); 586 std::sort(valvec.begin(), valvec.end());
505 587
506 summary.minimum = valvec[0].value; 588 summary.minimum = valvec[0].value;
520 if (duracc > totalDuration/2) { 602 if (duracc > totalDuration/2) {
521 summary.median_c = valvec[k].value; 603 summary.median_c = valvec[k].value;
522 break; 604 break;
523 } 605 }
524 } 606 }
607
608 std::cerr << "median_c = " << summary.median_c << std::endl;
609 std::cerr << "median = " << summary.median << std::endl;
525 610
526 std::map<float, int> distribution; 611 std::map<float, int> distribution;
527 612
528 for (int k = 0; k < sz; ++k) { 613 for (int k = 0; k < sz; ++k) {
529 summary.sum += values[k]; 614 summary.sum += accumulator.results[k].values[bin];
530 distribution[values[k]] += 1; 615 distribution[accumulator.results[k].values[bin]] += 1;
531 } 616 }
532 617
533 int md = 0; 618 int md = 0;
534 619
535 for (std::map<float, int>::iterator di = distribution.begin(); 620 for (std::map<float, int>::iterator di = distribution.begin();
540 } 625 }
541 } 626 }
542 627
543 distribution.clear(); 628 distribution.clear();
544 629
545 //!!! we want to omit this bit if the features all have
546 //!!! equal duration (and set mode_c equal to mode instead)
547
548 std::map<float, double> distribution_c; 630 std::map<float, double> distribution_c;
549 631
550 for (int k = 0; k < sz; ++k) { 632 for (int k = 0; k < sz; ++k) {
551 distribution_c[values[k]] += toSec(durations[k]); 633 distribution_c[accumulator.results[k].values[bin]]
634 += toSec(accumulator.results[k].duration);
552 } 635 }
553 636
554 double mrd = 0.0; 637 double mrd = 0.0;
555 638
556 for (std::map<float, double>::iterator di = distribution_c.begin(); 639 for (std::map<float, double>::iterator di = distribution_c.begin();
566 if (totalDuration > 0.0) { 649 if (totalDuration > 0.0) {
567 650
568 double sum_c = 0.0; 651 double sum_c = 0.0;
569 652
570 for (int k = 0; k < sz; ++k) { 653 for (int k = 0; k < sz; ++k) {
571 double value = values[k] * toSec(durations[k]); 654 double value = accumulator.results[k].values[bin]
655 * toSec(accumulator.results[k].duration);
572 sum_c += value; 656 sum_c += value;
573 } 657 }
574 658
575 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = " 659 std::cerr << "mean_c = " << sum_c << " / " << totalDuration << " = "
576 << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl; 660 << sum_c / totalDuration << " (sz = " << sz << ")" << std::endl;
577 661
578 summary.mean_c = sum_c / totalDuration; 662 summary.mean_c = sum_c / totalDuration;
579 663
580 for (int k = 0; k < sz; ++k) { 664 for (int k = 0; k < sz; ++k) {
581 double value = values[k] * toSec(durations[k]); 665 double value = accumulator.results[k].values[bin]
666 * toSec(accumulator.results[k].duration);
582 summary.variance_c += 667 summary.variance_c +=
583 (value - summary.mean_c) * (value - summary.mean_c); 668 (value - summary.mean_c) * (value - summary.mean_c);
584 } 669 }
585 670
586 summary.variance_c /= summary.count; 671 summary.variance_c /= summary.count;
587 } 672 }
588 673
589 //!!! still to handle: median_c
590
591 float mean = summary.sum / summary.count; 674 float mean = summary.sum / summary.count;
592 675
593 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = " 676 std::cerr << "mean = " << summary.sum << " / " << summary.count << " = "
594 << summary.sum / summary.count << std::endl; 677 << summary.sum / summary.count << std::endl;
595 678
596 for (int k = 0; k < sz; ++k) { 679 for (int k = 0; k < sz; ++k) {
597 summary.variance += (values[k] - mean) * (values[k] - mean); 680 float value = accumulator.results[k].values[bin];
681 summary.variance += (value - mean) * (value - mean);
598 } 682 }
599 summary.variance /= summary.count; 683 summary.variance /= summary.count;
600 684
601 m_summaries[output][segmentStart][bin] = summary; 685 m_summaries[output][segmentStart][bin] = summary;
602 } 686 }
603 } 687 }
604 688
605 m_accumulators.clear(); 689 m_accumulators.clear();
606 m_reduced = true; 690 }
607 } 691
608 692
609 693 }
610 } 694
611 695 }
612 } 696
613