annotate src/Finder.cpp @ 146:214b72d55796 noise

Update unit tests
author Chris Cannam
date Fri, 16 Jan 2015 16:48:55 +0000
parents cfba9aec7569
children d6f22887283e 3673e2dae6a7 cf1282d1f940
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 "Finder.h"
cannam@0 18
Chris@30 19 #include "Path.h"
Chris@30 20
Chris@30 21 #include <algorithm>
Chris@92 22 #include <iomanip>
Chris@30 23
Chris@72 24 using namespace std;
cannam@0 25
Chris@140 26 //#define DEBUG_FINDER 1
Chris@140 27 //#define PERFORM_ERROR_CHECKS 1
Chris@140 28
Chris@72 29 Finder::Finder(Matcher *pm)
cannam@0 30 {
Chris@72 31 m_m = pm;
Chris@72 32 m_duration1 = -1;
Chris@72 33 m_duration2 = -1;
cannam@0 34 } // constructor
cannam@0 35
cannam@0 36 Finder::~Finder()
cannam@0 37 {
cannam@0 38 }
cannam@0 39
Chris@60 40 void
Chris@60 41 Finder::setDurations(int d1, int d2)
Chris@60 42 {
Chris@140 43 #ifdef DEBUG_FINDER
Chris@140 44 cerr << "*** setDurations: " << d1 << ", " << d2 << endl;
Chris@140 45 #endif
Chris@72 46 m_duration1 = d1;
Chris@72 47 m_duration2 = d2;
Chris@60 48 }
Chris@60 49
Chris@45 50 Matcher::Advance
cannam@0 51 Finder::getExpandDirection(int row, int col)
cannam@0 52 {
Chris@72 53 double min = m_m->getPathCost(row, col);
Chris@72 54
Chris@72 55 int bestRow = row;
Chris@72 56 int bestCol = col;
Chris@72 57
Chris@72 58 pair<int, int> rowRange = m_m->getRowRange(col);
Chris@72 59 if (rowRange.second > row+1) {
Chris@72 60 rowRange.second = row+1; // don't cheat by looking at future :)
Chris@72 61 }
Chris@72 62 for (int index = rowRange.first; index < rowRange.second; index++) {
Chris@135 63 double tmp = m_m->getNormalisedPathCost(index, col);
cannam@0 64 if (tmp < min) {
cannam@0 65 min = tmp;
cannam@0 66 bestRow = index;
cannam@0 67 }
cannam@0 68 }
Chris@72 69
Chris@72 70 pair<int, int> colRange = m_m->getColRange(row);
Chris@72 71 if (colRange.second > col+1) {
Chris@72 72 colRange.second = col+1; // don't cheat by looking at future :)
Chris@72 73 }
Chris@72 74 for (int index = colRange.first; index < colRange.second; index++) {
Chris@135 75 double tmp = m_m->getNormalisedPathCost(row, index);
cannam@0 76 if (tmp < min) {
cannam@0 77 min = tmp;
cannam@0 78 bestCol = index;
cannam@0 79 bestRow = row;
cannam@0 80 }
cannam@0 81 }
Chris@72 82
Chris@135 83 // cerr << "at [" << row << "," << col << "] (cost " << m_m->getPathCost(row, col) << ") blocksize = " << m_m->getBlockSize() << " best is [" << bestRow << "," << bestCol << "] (cost " << min << ")" << endl;
Chris@135 84
Chris@45 85 if (bestRow == row) {
Chris@45 86 if (bestCol == col) {
Chris@45 87 return Matcher::AdvanceBoth;
Chris@45 88 } else {
Chris@45 89 return Matcher::AdvanceThis;
Chris@45 90 }
Chris@45 91 } else if (bestCol == col) {
Chris@45 92 return Matcher::AdvanceOther;
Chris@45 93 } else {
Chris@46 94 return Matcher::AdvanceNone;
Chris@45 95 }
Chris@73 96 }
cannam@0 97
cannam@0 98 void
cannam@0 99 Finder::recalculatePathCostMatrix(int r1, int c1, int r2, int c2)
cannam@0 100 {
Chris@72 101 int prevRowStart = 0, prevRowStop = 0;
Chris@72 102
Chris@83 103 float diagonalWeight = m_m->getDiagonalWeight();
Chris@83 104
Chris@72 105 for (int r = r1; r <= r2; r++) {
Chris@72 106
Chris@72 107 pair<int, int> colRange = m_m->getColRange(r);
Chris@72 108
Chris@72 109 int rowStart = max(c1, colRange.first);
Chris@72 110 int rowStop = min(c2 + 1, colRange.second);
Chris@72 111
Chris@72 112 for (int c = rowStart; c < rowStop; c++) {
Chris@72 113
Chris@72 114 float newCost = m_m->getDistance(r, c);
Chris@72 115 Matcher::Advance dir = Matcher::AdvanceNone;
Chris@72 116
Chris@72 117 if (r > r1) { // not first row
Chris@72 118 double min = -1;
Chris@72 119 if ((c > prevRowStart) && (c <= prevRowStop)) {
Chris@72 120 // diagonal from (r-1,c-1)
Chris@83 121 min = m_m->getPathCost(r-1, c-1) + newCost * diagonalWeight;
Chris@72 122 dir = Matcher::AdvanceBoth;
Chris@72 123 }
Chris@72 124 if ((c >= prevRowStart) && (c < prevRowStop)) {
Chris@72 125 // vertical from (r-1,c)
Chris@72 126 double cost = m_m->getPathCost(r-1, c) + newCost;
Chris@72 127 if ((min < 0) || (cost < min)) {
Chris@72 128 min = cost;
Chris@72 129 dir = Matcher::AdvanceThis;
Chris@72 130 }
Chris@72 131 }
Chris@72 132 if (c > rowStart) {
Chris@72 133 // horizontal from (r,c-1)
Chris@72 134 double cost = m_m->getPathCost(r, c-1) + newCost;
Chris@72 135 if ((min < 0) || (cost < min)) {
Chris@72 136 min = cost;
Chris@72 137 dir = Matcher::AdvanceOther;
Chris@72 138 }
Chris@72 139 }
Chris@72 140
Chris@72 141 m_m->setPathCost(r, c, dir, min);
Chris@72 142
Chris@72 143 } else if (c > rowStart) { // first row
Chris@72 144 // horizontal from (r,c-1)
Chris@72 145 m_m->setPathCost(r, c, Matcher::AdvanceOther,
Chris@72 146 m_m->getPathCost(r, c-1) + newCost);
Chris@72 147 }
Chris@72 148 }
Chris@72 149
Chris@72 150 prevRowStart = rowStart;
Chris@72 151 prevRowStop = rowStop;
cannam@0 152 }
Chris@72 153 }
Chris@30 154
Chris@82 155 #ifdef PERFORM_ERROR_CHECKS
Chris@81 156 Finder::ErrorPosition
Chris@81 157 Finder::checkPathCostMatrix()
Chris@81 158 {
Chris@81 159 ErrorPosition err;
Chris@81 160
Chris@81 161 int r1 = 0;
Chris@81 162 int c1 = 0;
Chris@81 163 int r2 = m_m->getFrameCount() - 1;
Chris@81 164 int c2 = m_m->getOtherFrameCount() - 1;
Chris@81 165
Chris@81 166 if (r2 < r1 || c2 < c1) {
Chris@81 167 return err;
Chris@81 168 }
Chris@81 169
Chris@81 170 int prevRowStart = 0, prevRowStop = 0;
Chris@81 171
Chris@83 172 float diagonalWeight = m_m->getDiagonalWeight();
Chris@83 173
Chris@81 174 for (int r = r1; r <= r2; r++) {
Chris@81 175
Chris@81 176 pair<int, int> colRange = m_m->getColRange(r);
Chris@81 177
Chris@81 178 int rowStart = max(c1, colRange.first);
Chris@81 179 int rowStop = min(c2 + 1, colRange.second);
Chris@81 180
Chris@81 181 for (int c = rowStart; c < rowStop; c++) {
Chris@81 182
Chris@81 183 float newCost = m_m->getDistance(r, c);
Chris@81 184 double updateTo = -1.0;
Chris@81 185 Matcher::Advance dir = Matcher::AdvanceNone;
Chris@81 186
Chris@95 187 if (r > r1) { // not first row
Chris@81 188 double min = -1;
Chris@81 189 if ((c > prevRowStart) && (c <= prevRowStop)) {
Chris@81 190 // diagonal from (r-1,c-1)
Chris@83 191 min = m_m->getPathCost(r-1, c-1) + newCost * diagonalWeight;
Chris@81 192 err.prevCost = m_m->getPathCost(r-1, c-1);
Chris@83 193 err.distance = newCost * diagonalWeight;
Chris@81 194 dir = Matcher::AdvanceBoth;
Chris@81 195 }
Chris@81 196 if ((c >= prevRowStart) && (c < prevRowStop)) {
Chris@81 197 // vertical from (r-1,c)
Chris@81 198 double cost = m_m->getPathCost(r-1, c) + newCost;
Chris@81 199 if ((min < 0) || (cost < min)) {
Chris@81 200 min = cost;
Chris@81 201 err.prevCost = m_m->getPathCost(r-1, c);
Chris@81 202 err.distance = newCost;
Chris@81 203 dir = Matcher::AdvanceThis;
Chris@81 204 }
Chris@81 205 }
Chris@81 206 if (c > rowStart) {
Chris@81 207 // horizontal from (r,c-1)
Chris@81 208 double cost = m_m->getPathCost(r, c-1) + newCost;
Chris@81 209 if ((min < 0) || (cost < min)) {
Chris@81 210 min = cost;
Chris@81 211 err.prevCost = m_m->getPathCost(r, c-1);
Chris@81 212 err.distance = newCost;
Chris@81 213 dir = Matcher::AdvanceOther;
Chris@81 214 }
Chris@81 215 }
Chris@81 216
Chris@81 217 updateTo = min;
Chris@81 218
Chris@82 219 } else { // first row
Chris@82 220
Chris@82 221 if (c > rowStart) {
Chris@82 222 // horizontal from (r,c-1)
Chris@83 223 updateTo = m_m->getPathCost(r, c-1) + newCost;
Chris@83 224 err.prevCost = m_m->getPathCost(r, c-1);
Chris@83 225 err.distance = newCost;
Chris@82 226 dir = Matcher::AdvanceOther;
Chris@82 227 }
Chris@81 228 }
Chris@81 229
Chris@82 230 if (dir != Matcher::AdvanceNone) {
Chris@86 231 if (m_m->getAdvance(r, c) != dir) {
Chris@86 232 err.type = ErrorPosition::WrongAdvance;
Chris@86 233 err.r = r;
Chris@86 234 err.c = c;
Chris@86 235 err.costWas = m_m->getPathCost(r, c);
Chris@86 236 err.costShouldBe = updateTo;
Chris@86 237 err.advanceWas = m_m->getAdvance(r, c);
Chris@86 238 err.advanceShouldBe = dir;
Chris@86 239 return err;
Chris@86 240 }
Chris@84 241 if (m_m->getPathCost(r, c) != updateTo) {
Chris@84 242 err.type = ErrorPosition::WrongCost;
Chris@84 243 err.r = r;
Chris@84 244 err.c = c;
Chris@84 245 err.costWas = m_m->getPathCost(r, c);
Chris@84 246 err.costShouldBe = updateTo;
Chris@84 247 err.advanceWas = m_m->getAdvance(r, c);
Chris@84 248 err.advanceShouldBe = dir;
Chris@82 249 return err;
Chris@82 250 }
Chris@82 251 } else {
Chris@82 252 // AdvanceNone should occur only at r = r1, c = c1
Chris@82 253 if (r != r1 || c != c1) {
Chris@82 254 err.type = ErrorPosition::NoAdvance;
Chris@82 255 err.r = r;
Chris@82 256 err.c = c;
Chris@82 257 err.costWas = m_m->getPathCost(r, c);
Chris@82 258 err.costShouldBe = updateTo;
Chris@84 259 err.advanceWas = m_m->getAdvance(r, c);
Chris@84 260 err.advanceShouldBe = dir;
Chris@82 261 return err;
Chris@82 262 }
Chris@81 263 }
Chris@81 264 }
Chris@81 265
Chris@81 266 prevRowStart = rowStart;
Chris@81 267 prevRowStop = rowStop;
Chris@81 268 }
Chris@81 269
Chris@81 270 return err;
Chris@82 271 }
Chris@81 272
Chris@92 273 void
Chris@92 274 Finder::checkAndReport()
Chris@30 275 {
Chris@92 276 cerr << "Finder: Checking path-cost matrix..." << endl;
Chris@82 277 ErrorPosition err = checkPathCostMatrix();
Chris@92 278 if (err.type == ErrorPosition::NoError) {
Chris@92 279 cerr << "No errors found" << endl;
Chris@92 280 } else {
Chris@82 281 cerr << "\nWARNING: Checking path-cost matrix returned mismatch:" << endl;
Chris@92 282 cerr << "Type: " << err.type << ": ";
Chris@92 283 switch (err.type) {
Chris@92 284 case ErrorPosition::NoError: break;
Chris@92 285 case ErrorPosition::WrongCost: cerr << "WrongCost"; break;
Chris@92 286 case ErrorPosition::WrongAdvance: cerr << "WrongAdvance"; break;
Chris@92 287 case ErrorPosition::NoAdvance: cerr << "NoAdvance"; break;
Chris@92 288 }
Chris@92 289 cerr << endl;
Chris@84 290 cerr << "At row " << err.r << ", column " << err.c
Chris@84 291 << "\nShould be advancing "
Chris@84 292 << Matcher::advanceToString(err.advanceShouldBe)
Chris@84 293 << ", advance in matrix is "
Chris@84 294 << Matcher::advanceToString(err.advanceWas)
Chris@83 295 << "\nPrev cost " << err.prevCost
Chris@82 296 << " plus distance " << err.distance << " gives "
Chris@84 297 << err.costShouldBe << ", matrix contains " << err.costWas
Chris@83 298 << endl;
Chris@83 299 cerr << "Note: diagonal weight = " << m_m->getDiagonalWeight() << endl;
Chris@83 300 cerr << endl;
Chris@92 301
Chris@95 302 int w(4);
Chris@95 303 int ww(15);
Chris@92 304
Chris@92 305 cerr << "Distance matrix leading up to this point:" << endl;
Chris@95 306 cerr << setprecision(12) << setw(w) << "";
Chris@92 307 for (int i = -4; i <= 0; ++i) {
Chris@95 308 cerr << setw(ww) << i;
Chris@92 309 }
Chris@92 310 cerr << endl;
Chris@92 311 for (int j = -4; j <= 0; ++j) {
Chris@92 312 cerr << setw(w) << j;
Chris@92 313 for (int i = -4; i <= 0; ++i) {
Chris@95 314 cerr << setw(ww) << m_m->getDistance(err.r + j, err.c + i);
Chris@92 315 }
Chris@92 316 cerr << endl;
Chris@92 317 }
Chris@92 318 cerr << endl;
Chris@92 319
Chris@92 320 cerr << "Cost matrix leading up to this point:" << endl;
Chris@92 321 cerr << setw(w) << "";
Chris@92 322 for (int i = -4; i <= 0; ++i) {
Chris@95 323 cerr << setw(ww) << i;
Chris@92 324 }
Chris@92 325 cerr << endl;
Chris@92 326 for (int j = -4; j <= 0; ++j) {
Chris@92 327 cerr << setw(w) << j;
Chris@92 328 for (int i = -4; i <= 0; ++i) {
Chris@95 329 cerr << setw(ww) << m_m->getPathCost(err.r + j, err.c + i);
Chris@92 330 }
Chris@92 331 cerr << endl;
Chris@92 332 }
Chris@92 333 cerr << endl;
Chris@82 334 }
Chris@92 335 }
Chris@92 336 #endif
Chris@92 337
Chris@92 338 int
Chris@92 339 Finder::retrievePath(bool smooth, vector<int> &pathx, vector<int> &pathy)
Chris@92 340 {
Chris@92 341 pathx.clear();
Chris@92 342 pathy.clear();
Chris@92 343
Chris@92 344 #ifdef PERFORM_ERROR_CHECKS
Chris@92 345 checkAndReport();
Chris@82 346 #endif
Chris@82 347
Chris@72 348 int ex = m_m->getOtherFrameCount() - 1;
Chris@72 349 int ey = m_m->getFrameCount() - 1;
Chris@69 350
Chris@69 351 if (ex < 0 || ey < 0) {
Chris@69 352 return 0;
Chris@69 353 }
Chris@66 354
Chris@66 355 int x = ex;
Chris@66 356 int y = ey;
Chris@30 357
Chris@140 358 #ifdef DEBUG_FINDER
Chris@140 359 cerr << "*** retrievePath: smooth = " << smooth << endl;
Chris@140 360 cerr << "*** retrievePath: before: x = " << x << ", y = " << y << endl;
Chris@140 361 #endif
Chris@140 362
Chris@72 363 if (m_duration2 > 0 && m_duration2 < m_m->getOtherFrameCount()) {
Chris@72 364 x = m_duration2 - 1;
Chris@60 365 }
Chris@72 366 if (m_duration1 > 0 && m_duration1 < m_m->getFrameCount()) {
Chris@72 367 y = m_duration1 - 1;
Chris@60 368 }
Chris@60 369
Chris@72 370 if (!m_m->isAvailable(y, x)) {
Chris@66 371 // Path did not pass through the expected end point --
Chris@66 372 // probably means the pieces are substantially different in
Chris@66 373 // the later bits. Reset the expected end point to the end of
Chris@66 374 // both files including any trailing silence.
Chris@66 375 cerr << "NOTE: Path did not pass through expected end point, inputs are probably significantly different" << endl;
Chris@66 376 x = ex;
Chris@66 377 y = ey;
Chris@66 378 }
Chris@66 379
Chris@55 380 recalculatePathCostMatrix(0, 0, y, x);
Chris@55 381
Chris@140 382 #ifdef DEBUG_FINDER
Chris@140 383 cerr << "*** retrievePath: start: x = " << x << ", y = " << y << endl;
Chris@140 384 #endif
Chris@66 385
Chris@72 386 while (m_m->isAvailable(y, x) && (x > 0 || y > 0)) {
Chris@30 387
Chris@33 388 // cerr << "x = " << x << ", y = " << y;
Chris@33 389
Chris@30 390 pathx.push_back(x);
Chris@30 391 pathy.push_back(y);
Chris@30 392
Chris@72 393 switch (m_m->getAdvance(y, x)) {
Chris@45 394 case Matcher::AdvanceThis:
Chris@70 395 // cerr << ", going down (dist = " << getDistance() << ")" << endl;
Chris@33 396 y--;
Chris@33 397 break;
Chris@45 398 case Matcher::AdvanceOther:
Chris@70 399 // cerr << ", going left (dist = " << getDistance() << ")" << endl;
Chris@33 400 x--;
Chris@33 401 break;
Chris@45 402 case Matcher::AdvanceBoth:
Chris@70 403 // cerr << ", going diag (dist = " << getDistance() << ")" << endl;
Chris@33 404 x--;
Chris@33 405 y--;
Chris@33 406 break;
Chris@45 407 case Matcher::AdvanceNone: // this would indicate a bug, but we wouldn't want to hang
Chris@69 408 cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl;
Chris@33 409 if (x > y) {
Chris@33 410 x--;
Chris@33 411 } else {
Chris@33 412 y--;
Chris@33 413 }
Chris@33 414 break;
Chris@30 415 }
Chris@30 416 }
Chris@30 417
Chris@72 418 if (x > 0 || y > 0) {
Chris@72 419 cerr << "WARNING: Ran out of available path at (" << y << "," << x
Chris@72 420 << ")!" << endl;
Chris@72 421 }
Chris@72 422
Chris@72 423 reverse(pathx.begin(), pathx.end());
Chris@72 424 reverse(pathy.begin(), pathy.end());
Chris@30 425
Chris@31 426 if (smooth) {
Chris@31 427 int smoothedLen = Path().smooth(pathx, pathy, pathx.size());
Chris@31 428 return smoothedLen;
Chris@31 429 } else {
Chris@31 430 return pathx.size();
Chris@31 431 }
Chris@30 432 }
Chris@30 433
Chris@30 434