annotate Peaks.h @ 4:c06cf6f7cb04

Bring in Peaks code from BeatRoot
author Chris Cannam
date Mon, 19 Sep 2011 15:48:26 +0100
parents
children 887c629502a9
rev   line source
Chris@4 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@4 2
Chris@4 3 /*
Chris@4 4 Vamp feature extraction plugin for the BeatRoot beat tracker.
Chris@4 5
Chris@4 6 Centre for Digital Music, Queen Mary, University of London.
Chris@4 7 This file copyright 2011 Simon Dixon, Chris Cannam and QMUL.
Chris@4 8
Chris@4 9 This program is free software; you can redistribute it and/or
Chris@4 10 modify it under the terms of the GNU General Public License as
Chris@4 11 published by the Free Software Foundation; either version 2 of the
Chris@4 12 License, or (at your option) any later version. See the file
Chris@4 13 COPYING included with this distribution for more information.
Chris@4 14 */
Chris@4 15
Chris@4 16 #ifndef _BEATROOT_PEAKS_H_
Chris@4 17 #define _BEATROOT_PEAKS_H_
Chris@4 18
Chris@4 19 #include <vector>
Chris@4 20 #include <cmath>
Chris@4 21
Chris@4 22 using std::vector;
Chris@4 23
Chris@4 24 class Peaks
Chris@4 25 {
Chris@4 26 protected:
Chris@4 27 static int pre;
Chris@4 28 static int post;
Chris@4 29
Chris@4 30 public:
Chris@4 31 /** General peak picking method for finding n local maxima in an array
Chris@4 32 * @param data input data
Chris@4 33 * @param peaks list of peak indexes
Chris@4 34 * @param width minimum distance between peaks
Chris@4 35 */
Chris@4 36 static int findPeaks(const vector<double> &data, vector<int> peaks, int width) {
Chris@4 37 int peakCount = 0;
Chris@4 38 int maxp = 0;
Chris@4 39 int mid = 0;
Chris@4 40 int end = data.size();
Chris@4 41 while (mid < end) {
Chris@4 42 int i = mid - width;
Chris@4 43 if (i < 0)
Chris@4 44 i = 0;
Chris@4 45 int stop = mid + width + 1;
Chris@4 46 if (stop > data.size())
Chris@4 47 stop = data.size();
Chris@4 48 maxp = i;
Chris@4 49 for (i++; i < stop; i++)
Chris@4 50 if (data[i] > data[maxp])
Chris@4 51 maxp = i;
Chris@4 52 if (maxp == mid) {
Chris@4 53 int j;
Chris@4 54 for (j = peakCount; j > 0; j--) {
Chris@4 55 if (data[maxp] <= data[peaks[j-1]])
Chris@4 56 break;
Chris@4 57 else if (j < peaks.size())
Chris@4 58 peaks[j] = peaks[j-1];
Chris@4 59 }
Chris@4 60 if (j != peaks.size())
Chris@4 61 peaks[j] = maxp;
Chris@4 62 if (peakCount != peaks.size())
Chris@4 63 peakCount++;
Chris@4 64 }
Chris@4 65 mid++;
Chris@4 66 }
Chris@4 67 return peakCount;
Chris@4 68 } // findPeaks()
Chris@4 69
Chris@4 70 /** General peak picking method for finding local maxima in an array
Chris@4 71 * @param data input data
Chris@4 72 * @param width minimum distance between peaks
Chris@4 73 * @param threshold minimum value of peaks
Chris@4 74 * @return list of peak indexes
Chris@4 75 */
Chris@4 76 static vector<int> findPeaks(const vector<double> &data, int width,
Chris@4 77 double threshold) {
Chris@4 78 return findPeaks(data, width, threshold, 0, false);
Chris@4 79 } // findPeaks()
Chris@4 80
Chris@4 81 /** General peak picking method for finding local maxima in an array
Chris@4 82 * @param data input data
Chris@4 83 * @param width minimum distance between peaks
Chris@4 84 * @param threshold minimum value of peaks
Chris@4 85 * @param decayRate how quickly previous peaks are forgotten
Chris@4 86 * @param isRelative minimum value of peaks is relative to local average
Chris@4 87 * @return list of peak indexes
Chris@4 88 */
Chris@4 89 static vector<int> findPeaks(const vector<double> &data, int width,
Chris@4 90 double threshold, double decayRate, bool isRelative) {
Chris@4 91 vector<int> peaks;
Chris@4 92 int maxp = 0;
Chris@4 93 int mid = 0;
Chris@4 94 int end = data.size();
Chris@4 95 double av = data[0];
Chris@4 96 while (mid < end) {
Chris@4 97 av = decayRate * av + (1 - decayRate) * data[mid];
Chris@4 98 if (av < data[mid])
Chris@4 99 av = data[mid];
Chris@4 100 int i = mid - width;
Chris@4 101 if (i < 0)
Chris@4 102 i = 0;
Chris@4 103 int stop = mid + width + 1;
Chris@4 104 if (stop > data.size())
Chris@4 105 stop = data.size();
Chris@4 106 maxp = i;
Chris@4 107 for (i++; i < stop; i++)
Chris@4 108 if (data[i] > data[maxp])
Chris@4 109 maxp = i;
Chris@4 110 if (maxp == mid) {
Chris@4 111 if (overThreshold(data, maxp, width, threshold, isRelative,av)){
Chris@4 112 peaks.push_back(maxp);
Chris@4 113 }
Chris@4 114 }
Chris@4 115 mid++;
Chris@4 116 }
Chris@4 117 return peaks;
Chris@4 118 } // findPeaks()
Chris@4 119
Chris@4 120 static double expDecayWithHold(double av, double decayRate,
Chris@4 121 const vector<double> &data, int start, int stop) {
Chris@4 122 while (start < stop) {
Chris@4 123 av = decayRate * av + (1 - decayRate) * data[start];
Chris@4 124 if (av < data[start])
Chris@4 125 av = data[start];
Chris@4 126 start++;
Chris@4 127 }
Chris@4 128 return av;
Chris@4 129 } // expDecayWithHold()
Chris@4 130
Chris@4 131 static bool overThreshold(const vector<double> &data, int index, int width,
Chris@4 132 double threshold, bool isRelative,
Chris@4 133 double av) {
Chris@4 134 if (data[index] < av)
Chris@4 135 return false;
Chris@4 136 if (isRelative) {
Chris@4 137 int iStart = index - pre * width;
Chris@4 138 if (iStart < 0)
Chris@4 139 iStart = 0;
Chris@4 140 int iStop = index + post * width;
Chris@4 141 if (iStop > data.size())
Chris@4 142 iStop = data.size();
Chris@4 143 double sum = 0;
Chris@4 144 int count = iStop - iStart;
Chris@4 145 while (iStart < iStop)
Chris@4 146 sum += data[iStart++];
Chris@4 147 return (data[index] > sum / count + threshold);
Chris@4 148 } else
Chris@4 149 return (data[index] > threshold);
Chris@4 150 } // overThreshold()
Chris@4 151
Chris@4 152 static void normalise(vector<double> &data) {
Chris@4 153 double sx = 0;
Chris@4 154 double sxx = 0;
Chris@4 155 for (int i = 0; i < data.size(); i++) {
Chris@4 156 sx += data[i];
Chris@4 157 sxx += data[i] * data[i];
Chris@4 158 }
Chris@4 159 double mean = sx / data.size();
Chris@4 160 double sd = sqrt((sxx - sx * mean) / data.size());
Chris@4 161 if (sd == 0)
Chris@4 162 sd = 1; // all data[i] == mean -> 0; avoids div by 0
Chris@4 163 for (int i = 0; i < data.size(); i++) {
Chris@4 164 data[i] = (data[i] - mean) / sd;
Chris@4 165 }
Chris@4 166 } // normalise()
Chris@4 167
Chris@4 168 /** Uses an n-point linear regression to estimate the slope of data.
Chris@4 169 * @param data input data
Chris@4 170 * @param hop spacing of data points
Chris@4 171 * @param n length of linear regression
Chris@4 172 * @param slope output data
Chris@4 173 */
Chris@4 174 static void getSlope(const vector<double> &data, double hop, int n,
Chris@4 175 vector<double> &slope) {
Chris@4 176 int i = 0, j = 0;
Chris@4 177 double t;
Chris@4 178 double sx = 0, sxx = 0, sy = 0, sxy = 0;
Chris@4 179 for ( ; i < n; i++) {
Chris@4 180 t = i * hop;
Chris@4 181 sx += t;
Chris@4 182 sxx += t * t;
Chris@4 183 sy += data[i];
Chris@4 184 sxy += t * data[i];
Chris@4 185 }
Chris@4 186 double delta = n * sxx - sx * sx;
Chris@4 187 for ( ; j < n / 2; j++)
Chris@4 188 slope[j] = (n * sxy - sx * sy) / delta;
Chris@4 189 for ( ; j < data.size() - (n + 1) / 2; j++, i++) {
Chris@4 190 slope[j] = (n * sxy - sx * sy) / delta;
Chris@4 191 sy += data[i] - data[i - n];
Chris@4 192 sxy += hop * (n * data[i] - sy);
Chris@4 193 }
Chris@4 194 for ( ; j < data.size(); j++)
Chris@4 195 slope[j] = (n * sxy - sx * sy) / delta;
Chris@4 196 } // getSlope()
Chris@4 197
Chris@4 198 static double min(const vector<double> &arr) { return arr[imin(arr)]; }
Chris@4 199
Chris@4 200 static double max(const vector<double> &arr) { return arr[imax(arr)]; }
Chris@4 201
Chris@4 202 static int imin(const vector<double> &arr) {
Chris@4 203 int i = 0;
Chris@4 204 for (int j = 1; j < arr.size(); j++)
Chris@4 205 if (arr[j] < arr[i])
Chris@4 206 i = j;
Chris@4 207 return i;
Chris@4 208 } // imin()
Chris@4 209
Chris@4 210 static int imax(const vector<double> &arr) {
Chris@4 211 int i = 0;
Chris@4 212 for (int j = 1; j < arr.size(); j++)
Chris@4 213 if (arr[j] > arr[i])
Chris@4 214 i = j;
Chris@4 215 return i;
Chris@4 216 } // imax()
Chris@4 217
Chris@4 218 }; // class Peaks
Chris@4 219
Chris@4 220 #endif