annotate src/Finder.cpp @ 135:42381e437fcd refactors

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