annotate src/Finder.cpp @ 84:de7034e93dd0 refactors

More error checking (check advance as well as cost)
author Chris Cannam
date Thu, 27 Nov 2014 10:53:00 +0000
parents 10e76188c846
children a540b26db228
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@84 225 if (m_m->getAdvance(r, c) != dir) {
Chris@84 226 cerr << "WrongAdvance found" << endl;
Chris@84 227 err.type = ErrorPosition::WrongAdvance;
Chris@82 228 err.r = r;
Chris@82 229 err.c = c;
Chris@82 230 err.costWas = m_m->getPathCost(r, c);
Chris@82 231 err.costShouldBe = updateTo;
Chris@84 232 err.advanceWas = m_m->getAdvance(r, c);
Chris@84 233 err.advanceShouldBe = dir;
Chris@84 234 return err;
Chris@84 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