annotate src/Matcher.cpp @ 89:7785bb8a1be6 refactors

Fix diagonal handling: there was an implicit fixed diagonal weight of 2 in comparison of diagmin+distance to othermin directly
author Chris Cannam
date Thu, 27 Nov 2014 12:59:23 +0000
parents fca28ed15c6e
children 35e50f28720f
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
Chris@87 76 bool
Chris@87 77 Matcher::isInRange(int i, int j)
Chris@87 78 {
Chris@87 79 if (m_firstPM) {
Chris@87 80 return ((i >= 0) &&
Chris@87 81 (i < int(m_first.size())) &&
Chris@87 82 (j >= m_first[i]) &&
Chris@87 83 (j < int(m_first[i] + m_bestPathCost[i].size())));
Chris@87 84 } else {
Chris@87 85 return m_otherMatcher->isInRange(j, i);
Chris@87 86 }
Chris@87 87 }
Chris@87 88
Chris@87 89 bool
Chris@87 90 Matcher::isAvailable(int i, int j)
Chris@87 91 {
Chris@87 92 if (m_firstPM) {
Chris@87 93 if (isInRange(i, j)) {
Chris@87 94 return (m_bestPathCost[i][j - m_first[i]] >= 0);
Chris@87 95 } else {
Chris@87 96 return false;
Chris@87 97 }
Chris@87 98 } else {
Chris@87 99 return m_otherMatcher->isAvailable(j, i);
Chris@87 100 }
Chris@87 101 }
Chris@87 102
Chris@87 103 pair<int, int>
Chris@87 104 Matcher::getColRange(int i)
Chris@87 105 {
Chris@87 106 if (i < 0 || i >= int(m_first.size())) {
Chris@87 107 cerr << "ERROR: Matcher::getColRange(" << i << "): Index out of range"
Chris@87 108 << endl;
Chris@87 109 throw "Index out of range";
Chris@87 110 } else {
Chris@87 111 return pair<int, int>(m_first[i], m_last[i]);
Chris@87 112 }
Chris@87 113 }
Chris@87 114
Chris@87 115 pair<int, int>
Chris@87 116 Matcher::getRowRange(int i)
Chris@87 117 {
Chris@87 118 return m_otherMatcher->getColRange(i);
Chris@87 119 }
Chris@87 120
Chris@87 121 float
Chris@87 122 Matcher::getDistance(int i, int j)
Chris@87 123 {
Chris@87 124 if (m_firstPM) {
Chris@87 125 if (!isInRange(i, j)) {
Chris@87 126 cerr << "ERROR: Matcher::getDistance(" << i << ", " << j << "): "
Chris@87 127 << "Location is not in range" << endl;
Chris@87 128 throw "Distance not available";
Chris@87 129 }
Chris@87 130 float dist = m_distance[i][j - m_first[i]];
Chris@87 131 if (dist < 0) {
Chris@87 132 cerr << "ERROR: Matcher::getDistance(" << i << ", " << j << "): "
Chris@87 133 << "Location is in range, but distance ("
Chris@87 134 << dist << ") is invalid or has not been set" << endl;
Chris@87 135 throw "Distance not available";
Chris@87 136 }
Chris@87 137 return dist;
Chris@87 138 } else {
Chris@87 139 return m_otherMatcher->getDistance(j, i);
Chris@87 140 }
Chris@87 141 }
Chris@87 142
Chris@87 143 void
Chris@87 144 Matcher::setDistance(int i, int j, float distance)
Chris@87 145 {
Chris@87 146 if (m_firstPM) {
Chris@87 147 if (!isInRange(i, j)) {
Chris@87 148 cerr << "ERROR: Matcher::setDistance(" << i << ", " << j << ", "
Chris@87 149 << distance << "): Location is out of range" << endl;
Chris@87 150 throw "Indices out of range";
Chris@87 151 }
Chris@87 152 m_distance[i][j - m_first[i]] = distance;
Chris@87 153 } else {
Chris@87 154 m_otherMatcher->setDistance(j, i, distance);
Chris@87 155 }
Chris@87 156 }
Chris@87 157
Chris@87 158 double
Chris@87 159 Matcher::getPathCost(int i, int j)
Chris@87 160 {
Chris@87 161 if (m_firstPM) {
Chris@87 162 if (!isAvailable(i, j)) {
Chris@87 163 if (!isInRange(i, j)) {
Chris@87 164 cerr << "ERROR: Matcher::getPathCost(" << i << ", " << j << "): "
Chris@87 165 << "Location is not in range" << endl;
Chris@87 166 } else {
Chris@87 167 cerr << "ERROR: Matcher::getPathCost(" << i << ", " << j << "): "
Chris@87 168 << "Location is in range, but pathCost ("
Chris@87 169 << m_bestPathCost[i][j - m_first[i]]
Chris@87 170 << ") is invalid or has not been set" << endl;
Chris@87 171 }
Chris@87 172 throw "Path cost not available";
Chris@87 173 }
Chris@87 174 return m_bestPathCost[i][j - m_first[i]];
Chris@87 175 } else {
Chris@87 176 return m_otherMatcher->getPathCost(j, i);
Chris@87 177 }
Chris@87 178 }
Chris@87 179
Chris@87 180 void
Chris@87 181 Matcher::setPathCost(int i, int j, Advance dir, double pathCost)
Chris@87 182 {
Chris@87 183 if (m_firstPM) {
Chris@87 184 if (!isInRange(i, j)) {
Chris@87 185 cerr << "ERROR: Matcher::setPathCost(" << i << ", " << j << ", "
Chris@87 186 << dir << ", " << pathCost
Chris@87 187 << "): Location is out of range" << endl;
Chris@87 188 throw "Indices out of range";
Chris@87 189 }
Chris@87 190 m_advance[i][j - m_first[i]] = dir;
Chris@87 191 m_bestPathCost[i][j - m_first[i]] = pathCost;
Chris@87 192 } else {
Chris@87 193 if (dir == AdvanceThis) {
Chris@87 194 dir = AdvanceOther;
Chris@87 195 } else if (dir == AdvanceOther) {
Chris@87 196 dir = AdvanceThis;
Chris@87 197 }
Chris@87 198 m_otherMatcher->setPathCost(j, i, dir, pathCost);
Chris@87 199 }
Chris@87 200
Chris@87 201 }
Chris@87 202
cannam@0 203 void
Chris@41 204 Matcher::size()
cannam@0 205 {
Chris@43 206 int distSize = (m_params.maxRunCount + 1) * m_blockSize;
Chris@71 207 m_bestPathCost.resize(m_distXSize, vector<double>(distSize, -1));
Chris@71 208 m_distance.resize(m_distXSize, vector<float>(distSize, -1));
Chris@45 209 m_advance.resize(m_distXSize, vector<Advance>(distSize, AdvanceNone));
Chris@43 210 m_first.resize(m_distXSize, 0);
Chris@43 211 m_last.resize(m_distXSize, 0);
Chris@38 212 }
cannam@0 213
Chris@23 214 void
Chris@72 215 Matcher::consumeFeatureVector(vector<double> feature)
Chris@23 216 {
Chris@43 217 if (!m_initialised) init();
Chris@43 218 int frameIndex = m_frameCount % m_blockSize;
Chris@43 219 m_frames[frameIndex] = feature;
Chris@23 220 calcAdvance();
Chris@21 221 }
Chris@21 222
Chris@21 223 void
Chris@21 224 Matcher::calcAdvance()
Chris@21 225 {
Chris@43 226 int frameIndex = m_frameCount % m_blockSize;
Chris@21 227
Chris@43 228 if (m_frameCount >= m_distXSize) {
Chris@43 229 m_distXSize *= 2;
Chris@41 230 size();
cannam@0 231 }
cannam@0 232
Chris@43 233 if (m_firstPM && (m_frameCount >= m_blockSize)) {
cannam@0 234
Chris@43 235 int len = m_last[m_frameCount - m_blockSize] -
Chris@43 236 m_first[m_frameCount - m_blockSize];
cannam@0 237
Chris@43 238 // We need to copy distance[m_frameCount-m_blockSize] to
Chris@43 239 // distance[m_frameCount], and then truncate
Chris@43 240 // distance[m_frameCount-m_blockSize] to its first len elements.
cannam@0 241 // Same for bestPathCost.
cannam@0 242
Chris@69 243 vector<float> dOld = m_distance[m_frameCount - m_blockSize];
Chris@71 244 vector<float> dNew(len, -1.f);
cannam@0 245
Chris@69 246 vector<double> bpcOld = m_bestPathCost[m_frameCount - m_blockSize];
Chris@71 247 vector<double> bpcNew(len, -1.0);
Chris@69 248
Chris@69 249 vector<Advance> adOld = m_advance[m_frameCount - m_blockSize];
Chris@69 250 vector<Advance> adNew(len, AdvanceNone);
Chris@69 251
Chris@69 252 for (int i = 0; i < len; ++i) {
Chris@69 253 dNew[i] = dOld[i];
Chris@69 254 bpcNew[i] = bpcOld[i];
Chris@69 255 adNew[i] = adOld[i];
Chris@69 256 }
Chris@45 257
Chris@69 258 m_distance[m_frameCount] = dOld;
Chris@69 259 m_distance[m_frameCount - m_blockSize] = dNew;
Chris@69 260
Chris@69 261 m_bestPathCost[m_frameCount] = bpcOld;
Chris@69 262 m_bestPathCost[m_frameCount - m_blockSize] = bpcNew;
Chris@69 263
Chris@69 264 m_advance[m_frameCount] = adOld;
Chris@69 265 m_advance[m_frameCount - m_blockSize] = adNew;
cannam@0 266 }
cannam@0 267
Chris@43 268 int stop = m_otherMatcher->m_frameCount;
Chris@43 269 int index = stop - m_blockSize;
Chris@86 270 if (index < 0) index = 0;
Chris@86 271
Chris@43 272 m_first[m_frameCount] = index;
Chris@43 273 m_last[m_frameCount] = stop;
cannam@0 274
cannam@0 275 for ( ; index < stop; index++) {
Chris@26 276
Chris@86 277 float distance = (float) m_metric.calcDistance
Chris@43 278 (m_frames[frameIndex],
Chris@45 279 m_otherMatcher->m_frames[index % m_blockSize]);
Chris@26 280
Chris@89 281 float diagDistance = distance * m_params.diagonalWeight;
Chris@89 282
Chris@86 283 if ((m_frameCount == 0) && (index == 0)) { // first element
Chris@86 284
Chris@86 285 updateValue(0, 0, AdvanceNone,
Chris@86 286 0,
Chris@86 287 distance);
Chris@86 288
Chris@86 289 } else if (m_frameCount == 0) { // first row
Chris@86 290
Chris@71 291 updateValue(0, index, AdvanceOther,
Chris@86 292 getPathCost(0, index-1),
Chris@86 293 distance);
Chris@86 294
Chris@86 295 } else if (index == 0) { // first column
Chris@86 296
Chris@71 297 updateValue(m_frameCount, index, AdvanceThis,
Chris@86 298 getPathCost(m_frameCount - 1, 0),
Chris@86 299 distance);
Chris@86 300
Chris@86 301 } else if (index == m_otherMatcher->m_frameCount - m_blockSize) {
Chris@86 302
cannam@0 303 // missing value(s) due to cutoff
cannam@0 304 // - no previous value in current row (resp. column)
cannam@0 305 // - no diagonal value if prev. dir. == curr. dirn
Chris@86 306
Chris@72 307 double min2 = getPathCost(m_frameCount - 1, index);
Chris@86 308
Chris@86 309 cerr << "NOTE: missing value at i = " << m_frameCount << ", j = "
Chris@86 310 << index << " (first = " << m_firstPM << ")" << endl;
Chris@86 311
Chris@43 312 // if ((m_firstPM && (first[m_frameCount - 1] == index)) ||
Chris@43 313 // (!m_firstPM && (m_last[index-1] < m_frameCount)))
Chris@86 314 if (m_first[m_frameCount - 1] == index) {
Chris@86 315
Chris@86 316 updateValue(m_frameCount, index, AdvanceThis,
Chris@86 317 min2, distance);
Chris@86 318
Chris@86 319 } else {
Chris@86 320
Chris@72 321 double min1 = getPathCost(m_frameCount - 1, index - 1);
Chris@89 322 if (min1 + diagDistance <= min2 + distance) {
Chris@86 323 updateValue(m_frameCount, index, AdvanceBoth,
Chris@86 324 min1, distance);
Chris@86 325 } else {
Chris@86 326 updateValue(m_frameCount, index, AdvanceThis,
Chris@86 327 min2, distance);
Chris@86 328 }
cannam@0 329 }
Chris@86 330
cannam@0 331 } else {
Chris@86 332
Chris@86 333 double min1 = getPathCost(m_frameCount, index - 1);
Chris@72 334 double min2 = getPathCost(m_frameCount - 1, index);
Chris@86 335 double min3 = getPathCost(m_frameCount - 1, index - 1);
Chris@87 336
Chris@87 337 if (m_frameCount == 15 && index == 2) {
Chris@87 338 cerr << "i = "<< m_frameCount << ", j = " << index << ", "
Chris@87 339 << " min1 = " << min1 << ", "
Chris@87 340 << " min2 = " << min2 << ", "
Chris@87 341 << " min3 = " << min3 << endl;
Chris@87 342 }
Chris@89 343
Chris@89 344 double cost1 = min1 + distance;
Chris@89 345 double cost2 = min2 + distance;
Chris@89 346 double cost3 = min3 + diagDistance;
Chris@86 347
Chris@89 348 if (cost1 <= cost2) {
Chris@89 349 if (cost3 <= cost1) {
Chris@86 350 updateValue(m_frameCount, index, AdvanceBoth,
Chris@86 351 min3, distance);
Chris@86 352 } else {
Chris@86 353 updateValue(m_frameCount, index, AdvanceOther,
Chris@86 354 min1, distance);
Chris@86 355 }
cannam@0 356 } else {
Chris@89 357 if (cost3 <= cost2) {
Chris@86 358 updateValue(m_frameCount, index, AdvanceBoth,
Chris@86 359 min3, distance);
Chris@86 360 } else {
Chris@86 361 updateValue(m_frameCount, index, AdvanceThis,
Chris@86 362 min2, distance);
Chris@86 363 }
cannam@0 364 }
cannam@0 365 }
Chris@86 366
Chris@43 367 m_otherMatcher->m_last[index]++;
cannam@0 368 } // loop for row (resp. column)
cannam@0 369
Chris@43 370 m_frameCount++;
Chris@43 371 m_runCount++;
cannam@0 372
Chris@43 373 m_otherMatcher->m_runCount = 0;
Chris@21 374 }
cannam@0 375
cannam@0 376 void
Chris@71 377 Matcher::updateValue(int i, int j, Advance dir, double value, float dMN)
cannam@0 378 {
Chris@83 379 float weighted = dMN;
Chris@83 380 if (dir == AdvanceBoth) {
Chris@83 381 weighted *= m_params.diagonalWeight;
Chris@83 382 }
Chris@83 383
Chris@43 384 if (m_firstPM) {
Chris@45 385
Chris@72 386 m_distance[i][j - m_first[i]] = dMN;
Chris@84 387
Chris@84 388 if (i == 3 && j == 56) {
Chris@84 389 cerr << "i = "<< i << ", j = " << j << ", dir = "
Chris@84 390 << advanceToString(dir)
Chris@84 391 << ", dMN = " << dMN << ", diagonalWeight = "
Chris@84 392 << m_params.diagonalWeight << ", weighted = "
Chris@84 393 << weighted << ", result = " << value + weighted << endl;
Chris@84 394 }
Chris@83 395
Chris@83 396 setPathCost(i, j, dir, value + weighted);
Chris@45 397
cannam@0 398 } else {
Chris@45 399
Chris@86 400 if (dir == AdvanceThis) dir = AdvanceOther;
Chris@86 401 else if (dir == AdvanceOther) dir = AdvanceThis;
Chris@86 402
Chris@87 403 if (i == 15 && j == 2) {
Chris@84 404 cerr << "i = "<< i << ", j = " << j << ", dir = "
Chris@84 405 << advanceToString(dir)
Chris@84 406 << ", dMN = " << dMN << ", diagonalWeight = "
Chris@84 407 << m_params.diagonalWeight << ", weighted = "
Chris@87 408 << weighted << ", value = " << value << ", result = " << value + weighted << endl;
Chris@84 409 }
Chris@84 410
Chris@43 411 int idx = i - m_otherMatcher->m_first[j];
Chris@45 412
Chris@69 413 if (idx == (int)m_otherMatcher->m_distance[j].size()) {
cannam@0 414 // This should never happen, but if we allow arbitrary
cannam@0 415 // pauses in either direction, and arbitrary lengths at
cannam@0 416 // end, it is better than a segmentation fault.
Chris@72 417 cerr << "Emergency resize: " << idx << " -> " << idx * 2 << endl;
Chris@71 418 m_otherMatcher->m_bestPathCost[j].resize(idx * 2, -1);
Chris@71 419 m_otherMatcher->m_distance[j].resize(idx * 2, -1);
Chris@46 420 m_otherMatcher->m_advance[j].resize(idx * 2, AdvanceNone);
cannam@0 421 }
Chris@45 422
Chris@45 423 m_otherMatcher->m_distance[j][idx] = dMN;
Chris@83 424 m_otherMatcher->setPathCost(j, i, dir, value + weighted);
cannam@0 425 }
Chris@71 426 }
cannam@0 427
Chris@72 428 Matcher::Advance
Chris@72 429 Matcher::getAdvance(int i, int j)
Chris@72 430 {
Chris@72 431 if (m_firstPM) {
Chris@72 432 if (!isInRange(i, j)) {
Chris@72 433 cerr << "ERROR: Matcher::getAdvance(" << i << ", " << j << "): "
Chris@72 434 << "Location is not in range" << endl;
Chris@72 435 throw "Advance not available";
Chris@72 436 }
Chris@72 437 return m_advance[i][j - m_first[i]];
Chris@72 438 } else {
Chris@72 439 return m_otherMatcher->getAdvance(j, i);
Chris@72 440 }
Chris@72 441 }