annotate src/Finder.cpp @ 95:bc3b60d65a5f refactors

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