annotate src/Matcher.cpp @ 83:10e76188c846 refactors

Expose diagonal weight as a parameter
author Chris Cannam
date Thu, 27 Nov 2014 10:49:11 +0000
parents b9aa663a607b
children de7034e93dd0
rev   line source
cannam@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
cannam@0 2
cannam@0 3 /*
cannam@0 4 Vamp feature extraction plugin using the MATCH audio alignment
cannam@0 5 algorithm.
cannam@0 6
cannam@0 7 Centre for Digital Music, Queen Mary, University of London.
cannam@0 8 This file copyright 2007 Simon Dixon, Chris Cannam and QMUL.
cannam@0 9
cannam@0 10 This program is free software; you can redistribute it and/or
cannam@0 11 modify it under the terms of the GNU General Public License as
cannam@0 12 published by the Free Software Foundation; either version 2 of the
cannam@0 13 License, or (at your option) any later version. See the file
cannam@0 14 COPYING included with this distribution for more information.
cannam@0 15 */
cannam@0 16
cannam@0 17 #include "Matcher.h"
cannam@0 18
cannam@0 19 #include <iostream>
cannam@0 20
cannam@4 21 #include <cstdlib>
Chris@16 22 #include <cassert>
cannam@4 23
Chris@72 24 using namespace std;
Chris@72 25
Chris@10 26 //#define DEBUG_MATCHER 1
Chris@10 27
Chris@43 28 Matcher::Matcher(Parameters parameters, Matcher *p, int m_featureSize_) :
Chris@43 29 m_params(parameters),
Chris@43 30 m_featureSize(m_featureSize_),
Chris@43 31 m_metric(parameters.distanceNorm)
Chris@23 32 {
Chris@23 33 #ifdef DEBUG_MATCHER
Chris@43 34 cerr << "Matcher::Matcher(" << m_params.sampleRate << ", " << p << ", " << m_featureSize << ")" << endl;
Chris@23 35 #endif
Chris@23 36
Chris@43 37 m_otherMatcher = p; // the first matcher will need this to be set later
Chris@43 38 m_firstPM = (!p);
Chris@43 39 m_frameCount = 0;
Chris@43 40 m_runCount = 0;
Chris@43 41 m_blockSize = 0;
cannam@0 42
Chris@43 43 m_blockSize = lrint(m_params.blockTime / m_params.hopTime);
Chris@15 44 #ifdef DEBUG_MATCHER
Chris@43 45 cerr << "Matcher: m_blockSize = " << m_blockSize << endl;
Chris@15 46 #endif
cannam@0 47
Chris@43 48 m_initialised = false;
Chris@23 49 }
cannam@0 50
cannam@0 51 Matcher::~Matcher()
cannam@0 52 {
Chris@10 53 #ifdef DEBUG_MATCHER
Chris@15 54 cerr << "Matcher(" << this << ")::~Matcher()" << endl;
Chris@10 55 #endif
cannam@0 56 }
cannam@0 57
cannam@0 58 void
cannam@0 59 Matcher::init()
cannam@0 60 {
Chris@43 61 if (m_initialised) return;
cannam@0 62
Chris@43 63 m_frames = vector<vector<double> >
Chris@69 64 (m_blockSize, vector<double>(m_featureSize, -1.0));
cannam@0 65
Chris@43 66 m_distXSize = m_blockSize * 2;
Chris@45 67
Chris@41 68 size();
cannam@0 69
Chris@43 70 m_frameCount = 0;
Chris@43 71 m_runCount = 0;
Chris@38 72
Chris@43 73 m_initialised = true;
Chris@16 74 }
Chris@16 75
cannam@0 76 void
Chris@41 77 Matcher::size()
cannam@0 78 {
Chris@43 79 int distSize = (m_params.maxRunCount + 1) * m_blockSize;
Chris@71 80 m_bestPathCost.resize(m_distXSize, vector<double>(distSize, -1));
Chris@71 81 m_distance.resize(m_distXSize, vector<float>(distSize, -1));
Chris@45 82 m_advance.resize(m_distXSize, vector<Advance>(distSize, AdvanceNone));
Chris@43 83 m_first.resize(m_distXSize, 0);
Chris@43 84 m_last.resize(m_distXSize, 0);
Chris@38 85 }
cannam@0 86
Chris@23 87 void
Chris@72 88 Matcher::consumeFeatureVector(vector<double> feature)
Chris@23 89 {
Chris@43 90 if (!m_initialised) init();
Chris@43 91 int frameIndex = m_frameCount % m_blockSize;
Chris@43 92 m_frames[frameIndex] = feature;
Chris@23 93 calcAdvance();
Chris@21 94 }
Chris@21 95
Chris@21 96 void
Chris@21 97 Matcher::calcAdvance()
Chris@21 98 {
Chris@43 99 int frameIndex = m_frameCount % m_blockSize;
Chris@21 100
Chris@43 101 if (m_frameCount >= m_distXSize) {
Chris@43 102 m_distXSize *= 2;
Chris@41 103 size();
cannam@0 104 }
cannam@0 105
Chris@43 106 if (m_firstPM && (m_frameCount >= m_blockSize)) {
cannam@0 107
Chris@43 108 int len = m_last[m_frameCount - m_blockSize] -
Chris@43 109 m_first[m_frameCount - m_blockSize];
cannam@0 110
Chris@43 111 // We need to copy distance[m_frameCount-m_blockSize] to
Chris@43 112 // distance[m_frameCount], and then truncate
Chris@43 113 // distance[m_frameCount-m_blockSize] to its first len elements.
cannam@0 114 // Same for bestPathCost.
cannam@0 115
Chris@69 116 vector<float> dOld = m_distance[m_frameCount - m_blockSize];
Chris@71 117 vector<float> dNew(len, -1.f);
cannam@0 118
Chris@69 119 vector<double> bpcOld = m_bestPathCost[m_frameCount - m_blockSize];
Chris@71 120 vector<double> bpcNew(len, -1.0);
Chris@69 121
Chris@69 122 vector<Advance> adOld = m_advance[m_frameCount - m_blockSize];
Chris@69 123 vector<Advance> adNew(len, AdvanceNone);
Chris@69 124
Chris@69 125 for (int i = 0; i < len; ++i) {
Chris@69 126 dNew[i] = dOld[i];
Chris@69 127 bpcNew[i] = bpcOld[i];
Chris@69 128 adNew[i] = adOld[i];
Chris@69 129 }
Chris@45 130
Chris@69 131 m_distance[m_frameCount] = dOld;
Chris@69 132 m_distance[m_frameCount - m_blockSize] = dNew;
Chris@69 133
Chris@69 134 m_bestPathCost[m_frameCount] = bpcOld;
Chris@69 135 m_bestPathCost[m_frameCount - m_blockSize] = bpcNew;
Chris@69 136
Chris@69 137 m_advance[m_frameCount] = adOld;
Chris@69 138 m_advance[m_frameCount - m_blockSize] = adNew;
cannam@0 139 }
cannam@0 140
Chris@43 141 int stop = m_otherMatcher->m_frameCount;
Chris@43 142 int index = stop - m_blockSize;
cannam@0 143 if (index < 0)
cannam@0 144 index = 0;
Chris@43 145 m_first[m_frameCount] = index;
Chris@43 146 m_last[m_frameCount] = stop;
cannam@0 147
Chris@46 148 float mn= -1;
Chris@46 149 float mx= -1;
cannam@0 150 for ( ; index < stop; index++) {
Chris@26 151
Chris@52 152 float dMN = (float) m_metric.calcDistance
Chris@43 153 (m_frames[frameIndex],
Chris@45 154 m_otherMatcher->m_frames[index % m_blockSize]);
Chris@26 155
cannam@0 156 if (mx<0)
cannam@0 157 mx = mn = dMN;
cannam@0 158 else if (dMN > mx)
cannam@0 159 mx = dMN;
cannam@0 160 else if (dMN < mn)
cannam@0 161 mn = dMN;
Chris@26 162
Chris@43 163 if ((m_frameCount == 0) && (index == 0)) // first element
Chris@71 164 updateValue(0, 0, AdvanceNone, 0, dMN);
Chris@43 165 else if (m_frameCount == 0) // first row
Chris@71 166 updateValue(0, index, AdvanceOther,
Chris@72 167 getPathCost(0, index-1), dMN);
cannam@0 168 else if (index == 0) // first column
Chris@71 169 updateValue(m_frameCount, index, AdvanceThis,
Chris@72 170 getPathCost(m_frameCount - 1, 0), dMN);
Chris@43 171 else if (index == m_otherMatcher->m_frameCount - m_blockSize) {
cannam@0 172 // missing value(s) due to cutoff
cannam@0 173 // - no previous value in current row (resp. column)
cannam@0 174 // - no diagonal value if prev. dir. == curr. dirn
Chris@72 175 double min2 = getPathCost(m_frameCount - 1, index);
Chris@43 176 // if ((m_firstPM && (first[m_frameCount - 1] == index)) ||
Chris@43 177 // (!m_firstPM && (m_last[index-1] < m_frameCount)))
Chris@43 178 if (m_first[m_frameCount - 1] == index)
Chris@71 179 updateValue(m_frameCount, index, AdvanceThis, min2, dMN);
cannam@0 180 else {
Chris@72 181 double min1 = getPathCost(m_frameCount - 1, index - 1);
cannam@0 182 if (min1 + dMN <= min2)
Chris@71 183 updateValue(m_frameCount, index, AdvanceBoth, min1,dMN);
cannam@0 184 else
Chris@71 185 updateValue(m_frameCount, index, AdvanceThis, min2,dMN);
cannam@0 186 }
cannam@0 187 } else {
Chris@72 188 double min1 = getPathCost(m_frameCount, index-1);
Chris@72 189 double min2 = getPathCost(m_frameCount - 1, index);
Chris@72 190 double min3 = getPathCost(m_frameCount - 1, index-1);
cannam@0 191 if (min1 <= min2) {
cannam@0 192 if (min3 + dMN <= min1)
Chris@71 193 updateValue(m_frameCount, index, AdvanceBoth, min3,dMN);
cannam@0 194 else
Chris@71 195 updateValue(m_frameCount, index, AdvanceOther,min1,dMN);
cannam@0 196 } else {
cannam@0 197 if (min3 + dMN <= min2)
Chris@71 198 updateValue(m_frameCount, index, AdvanceBoth, min3,dMN);
cannam@0 199 else
Chris@71 200 updateValue(m_frameCount, index, AdvanceThis, min2,dMN);
cannam@0 201 }
cannam@0 202 }
Chris@43 203 m_otherMatcher->m_last[index]++;
cannam@0 204 } // loop for row (resp. column)
cannam@0 205
Chris@43 206 m_frameCount++;
Chris@43 207 m_runCount++;
cannam@0 208
Chris@43 209 m_otherMatcher->m_runCount = 0;
Chris@21 210 }
cannam@0 211
Chris@71 212 bool
Chris@71 213 Matcher::isInRange(int i, int j)
Chris@71 214 {
Chris@71 215 if (m_firstPM) {
Chris@71 216 return ((i >= 0) &&
Chris@71 217 (i < int(m_first.size())) &&
Chris@71 218 (j >= m_first[i]) &&
Chris@71 219 (j < int(m_first[i] + m_bestPathCost[i].size())));
Chris@71 220 } else {
Chris@71 221 return m_otherMatcher->isInRange(j, i);
Chris@71 222 }
Chris@71 223 }
Chris@71 224
Chris@71 225 bool
Chris@71 226 Matcher::isAvailable(int i, int j)
Chris@71 227 {
Chris@71 228 if (m_firstPM) {
Chris@71 229 if (isInRange(i, j)) {
Chris@71 230 return (m_bestPathCost[i][j - m_first[i]] >= 0);
Chris@71 231 } else {
Chris@71 232 return false;
Chris@71 233 }
Chris@71 234 } else {
Chris@71 235 return m_otherMatcher->isAvailable(j, i);
Chris@71 236 }
Chris@71 237 }
Chris@71 238
Chris@72 239 pair<int, int>
Chris@72 240 Matcher::getColRange(int i)
Chris@72 241 {
Chris@72 242 if (i < 0 || i >= int(m_first.size())) {
Chris@72 243 cerr << "ERROR: Matcher::getColRange(" << i << "): Index out of range"
Chris@72 244 << endl;
Chris@72 245 throw "Index out of range";
Chris@72 246 } else {
Chris@72 247 return pair<int, int>(m_first[i], m_last[i]);
Chris@72 248 }
Chris@72 249 }
Chris@72 250
Chris@72 251 pair<int, int>
Chris@72 252 Matcher::getRowRange(int i)
Chris@72 253 {
Chris@72 254 return m_otherMatcher->getColRange(i);
Chris@72 255 }
Chris@72 256
Chris@72 257 float
Chris@72 258 Matcher::getDistance(int i, int j)
Chris@72 259 {
Chris@72 260 if (m_firstPM) {
Chris@72 261 if (!isInRange(i, j)) {
Chris@72 262 cerr << "ERROR: Matcher::getDistance(" << i << ", " << j << "): "
Chris@72 263 << "Location is not in range" << endl;
Chris@72 264 throw "Distance not available";
Chris@72 265 }
Chris@72 266 float dist = m_distance[i][j - m_first[i]];
Chris@72 267 if (dist < 0) {
Chris@72 268 cerr << "ERROR: Matcher::getDistance(" << i << ", " << j << "): "
Chris@72 269 << "Location is in range, but distance ("
Chris@72 270 << dist << ") is invalid or has not been set" << endl;
Chris@72 271 throw "Distance not available";
Chris@72 272 }
Chris@72 273 return dist;
Chris@72 274 } else {
Chris@72 275 return m_otherMatcher->getDistance(j, i);
Chris@72 276 }
Chris@72 277 }
Chris@72 278
Chris@72 279 void
Chris@72 280 Matcher::setDistance(int i, int j, float distance)
Chris@72 281 {
Chris@72 282 if (m_firstPM) {
Chris@72 283 if (!isInRange(i, j)) {
Chris@72 284 cerr << "ERROR: Matcher::setDistance(" << i << ", " << j << ", "
Chris@72 285 << distance << "): Location is out of range" << endl;
Chris@72 286 throw "Indices out of range";
Chris@72 287 }
Chris@72 288 m_distance[i][j - m_first[i]] = distance;
Chris@72 289 } else {
Chris@72 290 m_otherMatcher->setDistance(j, i, distance);
Chris@72 291 }
Chris@72 292 }
Chris@72 293
Chris@53 294 double
Chris@72 295 Matcher::getPathCost(int i, int j)
cannam@0 296 {
Chris@71 297 if (m_firstPM) {
Chris@71 298 if (!isAvailable(i, j)) {
Chris@71 299 if (!isInRange(i, j)) {
Chris@72 300 cerr << "ERROR: Matcher::getPathCost(" << i << ", " << j << "): "
Chris@71 301 << "Location is not in range" << endl;
Chris@71 302 } else {
Chris@72 303 cerr << "ERROR: Matcher::getPathCost(" << i << ", " << j << "): "
Chris@72 304 << "Location is in range, but pathCost ("
Chris@71 305 << m_bestPathCost[i][j - m_first[i]]
Chris@71 306 << ") is invalid or has not been set" << endl;
Chris@71 307 }
Chris@72 308 throw "Path cost not available";
Chris@71 309 }
Chris@43 310 return m_bestPathCost[i][j - m_first[i]];
Chris@71 311 } else {
Chris@72 312 return m_otherMatcher->getPathCost(j, i);
Chris@71 313 }
Chris@71 314 }
Chris@71 315
Chris@71 316 void
Chris@72 317 Matcher::setPathCost(int i, int j, Advance dir, double pathCost)
Chris@71 318 {
Chris@71 319 if (m_firstPM) {
Chris@71 320 if (!isInRange(i, j)) {
Chris@72 321 cerr << "ERROR: Matcher::setPathCost(" << i << ", " << j << ", "
Chris@72 322 << dir << ", " << pathCost
Chris@72 323 << "): Location is out of range" << endl;
Chris@71 324 throw "Indices out of range";
Chris@71 325 }
Chris@72 326 m_advance[i][j - m_first[i]] = dir;
Chris@72 327 m_bestPathCost[i][j - m_first[i]] = pathCost;
Chris@71 328 } else {
Chris@72 329 if (dir == AdvanceThis) {
Chris@72 330 dir = AdvanceOther;
Chris@72 331 } else if (dir == AdvanceOther) {
Chris@72 332 dir = AdvanceThis;
Chris@72 333 }
Chris@72 334 m_otherMatcher->setPathCost(j, i, dir, pathCost);
Chris@71 335 }
Chris@72 336
Chris@71 337 }
cannam@0 338
cannam@0 339 void
Chris@71 340 Matcher::updateValue(int i, int j, Advance dir, double value, float dMN)
cannam@0 341 {
Chris@83 342 float weighted = dMN;
Chris@83 343 if (dir == AdvanceBoth) {
Chris@83 344 weighted *= m_params.diagonalWeight;
Chris@83 345 }
Chris@83 346
Chris@43 347 if (m_firstPM) {
Chris@45 348
Chris@72 349 m_distance[i][j - m_first[i]] = dMN;
Chris@83 350
Chris@83 351 setPathCost(i, j, dir, value + weighted);
Chris@45 352
cannam@0 353 } else {
Chris@45 354
Chris@43 355 int idx = i - m_otherMatcher->m_first[j];
Chris@45 356
Chris@69 357 if (idx == (int)m_otherMatcher->m_distance[j].size()) {
cannam@0 358 // This should never happen, but if we allow arbitrary
cannam@0 359 // pauses in either direction, and arbitrary lengths at
cannam@0 360 // end, it is better than a segmentation fault.
Chris@72 361 cerr << "Emergency resize: " << idx << " -> " << idx * 2 << endl;
Chris@71 362 m_otherMatcher->m_bestPathCost[j].resize(idx * 2, -1);
Chris@71 363 m_otherMatcher->m_distance[j].resize(idx * 2, -1);
Chris@46 364 m_otherMatcher->m_advance[j].resize(idx * 2, AdvanceNone);
cannam@0 365 }
Chris@45 366
Chris@45 367 m_otherMatcher->m_distance[j][idx] = dMN;
Chris@83 368 m_otherMatcher->setPathCost(j, i, dir, value + weighted);
cannam@0 369 }
Chris@71 370 }
cannam@0 371
Chris@72 372 Matcher::Advance
Chris@72 373 Matcher::getAdvance(int i, int j)
Chris@72 374 {
Chris@72 375 if (m_firstPM) {
Chris@72 376 if (!isInRange(i, j)) {
Chris@72 377 cerr << "ERROR: Matcher::getAdvance(" << i << ", " << j << "): "
Chris@72 378 << "Location is not in range" << endl;
Chris@72 379 throw "Advance not available";
Chris@72 380 }
Chris@72 381 return m_advance[i][j - m_first[i]];
Chris@72 382 } else {
Chris@72 383 return m_otherMatcher->getAdvance(j, i);
Chris@72 384 }
Chris@72 385 }