annotate src/Finder.cpp @ 86:f07b9b7f1ab6 refactors

Previous commit was a mistake: the ahead-of-time business is in Finder::getExpandDirection. In fact we were failing to swap advance directions in forward path when writing to the "other" finder. This does not actually affect the backward path calculation, but it does mean we can restore the sanity check.
author Chris Cannam
date Thu, 27 Nov 2014 12:08:16 +0000
parents a540b26db228
children 1f5291760ed9
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@30 22
Chris@72 23 using namespace std;
cannam@0 24
Chris@72 25 Finder::Finder(Matcher *pm)
cannam@0 26 {
Chris@72 27 m_m = pm;
Chris@72 28 m_duration1 = -1;
Chris@72 29 m_duration2 = -1;
cannam@0 30 } // constructor
cannam@0 31
cannam@0 32 Finder::~Finder()
cannam@0 33 {
cannam@0 34 }
cannam@0 35
Chris@60 36 void
Chris@60 37 Finder::setDurations(int d1, int d2)
Chris@60 38 {
Chris@72 39 m_duration1 = d1;
Chris@72 40 m_duration2 = d2;
Chris@60 41 }
Chris@60 42
Chris@45 43 Matcher::Advance
cannam@0 44 Finder::getExpandDirection(int row, int col)
cannam@0 45 {
Chris@72 46 double min = m_m->getPathCost(row, col);
Chris@72 47
Chris@72 48 int bestRow = row;
Chris@72 49 int bestCol = col;
Chris@72 50
Chris@72 51 pair<int, int> rowRange = m_m->getRowRange(col);
Chris@72 52 if (rowRange.second > row+1) {
Chris@72 53 rowRange.second = row+1; // don't cheat by looking at future :)
Chris@72 54 }
Chris@72 55 for (int index = rowRange.first; index < rowRange.second; index++) {
Chris@72 56 double tmp = m_m->getPathCost(index, col);
cannam@0 57 if (tmp < min) {
cannam@0 58 min = tmp;
cannam@0 59 bestRow = index;
cannam@0 60 }
cannam@0 61 }
Chris@72 62
Chris@72 63 pair<int, int> colRange = m_m->getColRange(row);
Chris@72 64 if (colRange.second > col+1) {
Chris@72 65 colRange.second = col+1; // don't cheat by looking at future :)
Chris@72 66 }
Chris@72 67 for (int index = colRange.first; index < colRange.second; index++) {
Chris@72 68 double tmp = m_m->getPathCost(row, index);
cannam@0 69 if (tmp < min) {
cannam@0 70 min = tmp;
cannam@0 71 bestCol = index;
cannam@0 72 bestRow = row;
cannam@0 73 }
cannam@0 74 }
Chris@72 75
Chris@45 76 if (bestRow == row) {
Chris@45 77 if (bestCol == col) {
Chris@45 78 return Matcher::AdvanceBoth;
Chris@45 79 } else {
Chris@45 80 return Matcher::AdvanceThis;
Chris@45 81 }
Chris@45 82 } else if (bestCol == col) {
Chris@45 83 return Matcher::AdvanceOther;
Chris@45 84 } else {
Chris@46 85 return Matcher::AdvanceNone;
Chris@45 86 }
cannam@0 87
Chris@73 88 }
cannam@0 89
cannam@0 90 void
cannam@0 91 Finder::recalculatePathCostMatrix(int r1, int c1, int r2, int c2)
cannam@0 92 {
Chris@72 93 int prevRowStart = 0, prevRowStop = 0;
Chris@72 94
Chris@83 95 float diagonalWeight = m_m->getDiagonalWeight();
Chris@83 96
Chris@72 97 for (int r = r1; r <= r2; r++) {
Chris@72 98
Chris@72 99 pair<int, int> colRange = m_m->getColRange(r);
Chris@72 100
Chris@72 101 int rowStart = max(c1, colRange.first);
Chris@72 102 int rowStop = min(c2 + 1, colRange.second);
Chris@72 103
Chris@72 104 for (int c = rowStart; c < rowStop; c++) {
Chris@72 105
Chris@72 106 float newCost = m_m->getDistance(r, c);
Chris@72 107 Matcher::Advance dir = Matcher::AdvanceNone;
Chris@72 108
Chris@72 109 if (r > r1) { // not first row
Chris@72 110 double min = -1;
Chris@72 111 if ((c > prevRowStart) && (c <= prevRowStop)) {
Chris@72 112 // diagonal from (r-1,c-1)
Chris@83 113 min = m_m->getPathCost(r-1, c-1) + newCost * diagonalWeight;
Chris@72 114 dir = Matcher::AdvanceBoth;
Chris@72 115 }
Chris@72 116 if ((c >= prevRowStart) && (c < prevRowStop)) {
Chris@72 117 // vertical from (r-1,c)
Chris@72 118 double cost = m_m->getPathCost(r-1, c) + newCost;
Chris@72 119 if ((min < 0) || (cost < min)) {
Chris@72 120 min = cost;
Chris@72 121 dir = Matcher::AdvanceThis;
Chris@72 122 }
Chris@72 123 }
Chris@72 124 if (c > rowStart) {
Chris@72 125 // horizontal from (r,c-1)
Chris@72 126 double cost = m_m->getPathCost(r, c-1) + newCost;
Chris@72 127 if ((min < 0) || (cost < min)) {
Chris@72 128 min = cost;
Chris@72 129 dir = Matcher::AdvanceOther;
Chris@72 130 }
Chris@72 131 }
Chris@72 132
Chris@72 133 m_m->setPathCost(r, c, dir, min);
Chris@72 134
Chris@72 135 } else if (c > rowStart) { // first row
Chris@72 136 // horizontal from (r,c-1)
Chris@72 137 m_m->setPathCost(r, c, Matcher::AdvanceOther,
Chris@72 138 m_m->getPathCost(r, c-1) + newCost);
Chris@72 139 }
Chris@72 140 }
Chris@72 141
Chris@72 142 prevRowStart = rowStart;
Chris@72 143 prevRowStop = rowStop;
cannam@0 144 }
Chris@72 145 }
Chris@30 146
Chris@82 147 #ifdef PERFORM_ERROR_CHECKS
Chris@81 148 Finder::ErrorPosition
Chris@81 149 Finder::checkPathCostMatrix()
Chris@81 150 {
Chris@82 151 cerr << "Finder: Checking path-cost matrix..." << endl;
Chris@82 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@81 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 cerr << "WrongAdvance found" << endl;
Chris@86 227 err.type = ErrorPosition::WrongAdvance;
Chris@86 228 err.r = r;
Chris@86 229 err.c = c;
Chris@86 230 err.costWas = m_m->getPathCost(r, c);
Chris@86 231 err.costShouldBe = updateTo;
Chris@86 232 err.advanceWas = m_m->getAdvance(r, c);
Chris@86 233 err.advanceShouldBe = dir;
Chris@86 234 return err;
Chris@86 235 }
Chris@84 236 if (m_m->getPathCost(r, c) != updateTo) {
Chris@84 237 cerr << "WrongCost found" << endl;
Chris@84 238 err.type = ErrorPosition::WrongCost;
Chris@84 239 err.r = r;
Chris@84 240 err.c = c;
Chris@84 241 err.costWas = m_m->getPathCost(r, c);
Chris@84 242 err.costShouldBe = updateTo;
Chris@84 243 err.advanceWas = m_m->getAdvance(r, c);
Chris@84 244 err.advanceShouldBe = dir;
Chris@82 245 return err;
Chris@82 246 }
Chris@82 247 } else {
Chris@82 248 // AdvanceNone should occur only at r = r1, c = c1
Chris@82 249 if (r != r1 || c != c1) {
Chris@82 250 cerr << "AdvanceNone error found" << endl;
Chris@82 251 err.type = ErrorPosition::NoAdvance;
Chris@82 252 err.r = r;
Chris@82 253 err.c = c;
Chris@82 254 err.costWas = m_m->getPathCost(r, c);
Chris@82 255 err.costShouldBe = updateTo;
Chris@84 256 err.advanceWas = m_m->getAdvance(r, c);
Chris@84 257 err.advanceShouldBe = dir;
Chris@82 258 return err;
Chris@82 259 }
Chris@81 260 }
Chris@81 261 }
Chris@81 262
Chris@81 263 prevRowStart = rowStart;
Chris@81 264 prevRowStop = rowStop;
Chris@81 265 }
Chris@81 266
Chris@82 267 cerr << "No errors found" << endl;
Chris@81 268 return err;
Chris@82 269 }
Chris@82 270 #endif
Chris@81 271
Chris@30 272 int
Chris@31 273 Finder::retrievePath(bool smooth, vector<int> &pathx, vector<int> &pathy)
Chris@30 274 {
Chris@69 275 pathx.clear();
Chris@69 276 pathy.clear();
Chris@69 277
Chris@82 278 #ifdef PERFORM_ERROR_CHECKS
Chris@82 279 ErrorPosition err = checkPathCostMatrix();
Chris@82 280 if (err.type != ErrorPosition::NoError) {
Chris@82 281 cerr << "\nWARNING: Checking path-cost matrix returned mismatch:" << endl;
Chris@82 282 cerr << "Type: " << err.type << 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@82 294 }
Chris@82 295 #endif
Chris@82 296
Chris@72 297 int ex = m_m->getOtherFrameCount() - 1;
Chris@72 298 int ey = m_m->getFrameCount() - 1;
Chris@69 299
Chris@69 300 if (ex < 0 || ey < 0) {
Chris@69 301 return 0;
Chris@69 302 }
Chris@66 303
Chris@66 304 int x = ex;
Chris@66 305 int y = ey;
Chris@30 306
Chris@72 307 if (m_duration2 > 0 && m_duration2 < m_m->getOtherFrameCount()) {
Chris@72 308 x = m_duration2 - 1;
Chris@60 309 }
Chris@72 310 if (m_duration1 > 0 && m_duration1 < m_m->getFrameCount()) {
Chris@72 311 y = m_duration1 - 1;
Chris@60 312 }
Chris@79 313
Chris@79 314 // cerr << "before: x = " << x << ", y = " << y << endl;
Chris@60 315
Chris@72 316 if (!m_m->isAvailable(y, x)) {
Chris@66 317 // Path did not pass through the expected end point --
Chris@66 318 // probably means the pieces are substantially different in
Chris@66 319 // the later bits. Reset the expected end point to the end of
Chris@66 320 // both files including any trailing silence.
Chris@66 321 cerr << "NOTE: Path did not pass through expected end point, inputs are probably significantly different" << endl;
Chris@66 322 x = ex;
Chris@66 323 y = ey;
Chris@66 324 }
Chris@66 325
Chris@55 326 recalculatePathCostMatrix(0, 0, y, x);
Chris@55 327
Chris@66 328 // cerr << "start: x = " << x << ", y = " << y << endl;
Chris@66 329
Chris@72 330 while (m_m->isAvailable(y, x) && (x > 0 || y > 0)) {
Chris@30 331
Chris@33 332 // cerr << "x = " << x << ", y = " << y;
Chris@33 333
Chris@30 334 pathx.push_back(x);
Chris@30 335 pathy.push_back(y);
Chris@30 336
Chris@72 337 switch (m_m->getAdvance(y, x)) {
Chris@45 338 case Matcher::AdvanceThis:
Chris@70 339 // cerr << ", going down (dist = " << getDistance() << ")" << endl;
Chris@33 340 y--;
Chris@33 341 break;
Chris@45 342 case Matcher::AdvanceOther:
Chris@70 343 // cerr << ", going left (dist = " << getDistance() << ")" << endl;
Chris@33 344 x--;
Chris@33 345 break;
Chris@45 346 case Matcher::AdvanceBoth:
Chris@70 347 // cerr << ", going diag (dist = " << getDistance() << ")" << endl;
Chris@33 348 x--;
Chris@33 349 y--;
Chris@33 350 break;
Chris@45 351 case Matcher::AdvanceNone: // this would indicate a bug, but we wouldn't want to hang
Chris@69 352 cerr << "WARNING: Neither matcher advanced in path backtrack at (" << x << "," << y << ")" << endl;
Chris@33 353 if (x > y) {
Chris@33 354 x--;
Chris@33 355 } else {
Chris@33 356 y--;
Chris@33 357 }
Chris@33 358 break;
Chris@30 359 }
Chris@30 360 }
Chris@30 361
Chris@72 362 if (x > 0 || y > 0) {
Chris@72 363 cerr << "WARNING: Ran out of available path at (" << y << "," << x
Chris@72 364 << ")!" << endl;
Chris@72 365 }
Chris@72 366
Chris@72 367 reverse(pathx.begin(), pathx.end());
Chris@72 368 reverse(pathy.begin(), pathy.end());
Chris@30 369
Chris@31 370 if (smooth) {
Chris@31 371 int smoothedLen = Path().smooth(pathx, pathy, pathx.size());
Chris@31 372 return smoothedLen;
Chris@31 373 } else {
Chris@31 374 return pathx.size();
Chris@31 375 }
Chris@30 376 }
Chris@30 377
Chris@30 378