annotate src/Finder.cpp @ 147:3673e2dae6a7 structure

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