annotate src/FullDTW.cpp @ 246:aac9ad4064ea subsequence tip

Fix incorrect handling of silent tail in the non-subsequence MATCH phase; some debug output changes
author Chris Cannam
date Fri, 24 Jul 2020 14:29:55 +0100
parents e4715a35f7b0
children
rev   line source
Chris@235 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@235 2
Chris@235 3 /*
Chris@235 4 Vamp feature extraction plugin using the MATCH audio alignment
Chris@235 5 algorithm.
Chris@235 6
Chris@235 7 Centre for Digital Music, Queen Mary, University of London.
Chris@236 8 Copyright (c) 2007-2020 Simon Dixon, Chris Cannam, and Queen Mary
Chris@235 9 University of London, Copyright (c) 2014-2015 Tido GmbH.
Chris@235 10
Chris@235 11 This program is free software; you can redistribute it and/or
Chris@235 12 modify it under the terms of the GNU General Public License as
Chris@235 13 published by the Free Software Foundation; either version 2 of the
Chris@235 14 License, or (at your option) any later version. See the file
Chris@235 15 COPYING included with this distribution for more information.
Chris@235 16 */
Chris@235 17
Chris@235 18 #include "FullDTW.h"
Chris@235 19
Chris@235 20 #include <stdexcept>
Chris@238 21 #include <algorithm>
Chris@242 22 #include <iostream>
Chris@242 23 #include <cmath>
Chris@242 24
Chris@242 25 //#define DEBUG_DTW 1
Chris@235 26
Chris@235 27 FullDTW::FullDTW(Parameters params, DistanceMetric::Parameters dparams) :
Chris@235 28 m_params(params),
Chris@235 29 m_metric(dparams)
Chris@235 30 {
Chris@235 31 }
Chris@235 32
Chris@235 33 pathcost_t
Chris@235 34 FullDTW::choose(CostOption x, CostOption y, CostOption d) {
Chris@235 35 if (x.present && y.present) {
Chris@235 36 if (!d.present) {
Chris@235 37 throw std::logic_error("if x & y both exist, so must diagonal");
Chris@235 38 }
Chris@235 39 return std::min(std::min(x.cost, y.cost), d.cost);
Chris@235 40 } else if (x.present) {
Chris@235 41 return x.cost;
Chris@235 42 } else if (y.present) {
Chris@235 43 return y.cost;
Chris@235 44 } else {
Chris@235 45 return 0.0;
Chris@235 46 }
Chris@235 47 }
Chris@235 48
Chris@235 49 pathcostmat_t
Chris@235 50 FullDTW::costSequences(const featureseq_t &s1, const featureseq_t &s2) {
Chris@235 51
Chris@235 52 pathcostmat_t costs(s1.size(), pathcostvec_t(s2.size(), 0));
Chris@235 53
Chris@242 54 #ifdef DEBUG_DTW
Chris@242 55 std::cerr << "Distance matrix:" << std::endl;
Chris@242 56 #endif
Chris@242 57
Chris@235 58 for (size_t j = 0; j < s1.size(); ++j) {
Chris@235 59 for (size_t i = 0; i < s2.size(); ++i) {
Chris@235 60 distance_t dist = m_metric.calcDistance(s1[j], s2[i]);
Chris@242 61 #ifdef DEBUG_DTW
Chris@244 62 std::cerr << distance_print_t(dist) << " ";
Chris@242 63 #endif
Chris@242 64
Chris@235 65 if (i == 0 && m_params.subsequence) {
Chris@235 66 costs[j][i] = dist;
Chris@235 67 } else {
Chris@235 68 costs[j][i] = choose
Chris@235 69 (
Chris@235 70 { j > 0,
Chris@242 71 j > 0 ?
Chris@242 72 (dist + costs[j-1][i])
Chris@242 73 : 0
Chris@235 74 },
Chris@235 75 { i > 0,
Chris@242 76 i > 0 ?
Chris@242 77 (dist + costs[j][i-1])
Chris@242 78 : 0
Chris@235 79 },
Chris@242 80 { (j > 0 && i > 0),
Chris@242 81 (j > 0 && i > 0) ?
Chris@242 82 pathcost_t(round(m_params.diagonalWeight * dist)
Chris@242 83 + costs[j-1][i-1])
Chris@242 84 : 0
Chris@235 85 });
Chris@235 86 }
Chris@235 87 }
Chris@242 88 #ifdef DEBUG_DTW
Chris@242 89 std::cerr << "\n";
Chris@242 90 #endif
Chris@235 91 }
Chris@235 92
Chris@235 93 return costs;
Chris@235 94 }
Chris@235 95
Chris@235 96 std::vector<size_t>
Chris@235 97 FullDTW::align(const featureseq_t &s1, const featureseq_t &s2) {
Chris@235 98
Chris@235 99 // Return the index into s1 for each element in s2
Chris@235 100
Chris@235 101 std::vector<size_t> alignment(s2.size(), 0);
Chris@235 102
Chris@235 103 if (s1.empty() || s2.empty()) {
Chris@235 104 return alignment;
Chris@235 105 }
Chris@235 106
Chris@235 107 auto costs = costSequences(s1, s2);
Chris@235 108
Chris@235 109 #ifdef DEBUG_DTW
Chris@242 110 std::cerr << "Path cost matrix:" << std::endl;
Chris@242 111 for (auto v: costs) {
Chris@235 112 for (auto x: v) {
Chris@242 113 std::cerr << x << " ";
Chris@235 114 }
Chris@242 115 std::cerr << "\n";
Chris@235 116 }
Chris@235 117 #endif
Chris@235 118
Chris@235 119 size_t j = s1.size() - 1;
Chris@235 120 size_t i = s2.size() - 1;
Chris@235 121
Chris@235 122 if (m_params.subsequence) {
Chris@235 123 pathcost_t min = 0.0;
Chris@235 124 size_t minidx = 0;
Chris@235 125 for (size_t j = 0; j < s1.size(); ++j) {
Chris@235 126 if (j == 0 || costs[j][i] < min) {
Chris@235 127 min = costs[j][i];
Chris@235 128 minidx = j;
Chris@235 129 }
Chris@235 130 }
Chris@235 131 j = minidx;
Chris@235 132 #ifdef DEBUG_DTW
Chris@242 133 std::cerr << "Lowest cost at end of subsequence = " << min
Chris@242 134 << " at index " << j << ", tracking back from there"
Chris@242 135 << std::endl;
Chris@235 136 #endif
Chris@235 137 }
Chris@242 138
Chris@242 139 while (i > 0 || j > 0) {
Chris@235 140
Chris@235 141 alignment[i] = j;
Chris@242 142
Chris@242 143 if (i == 0) {
Chris@242 144 if (m_params.subsequence) {
Chris@242 145 break;
Chris@242 146 } else {
Chris@242 147 --j;
Chris@242 148 continue;
Chris@242 149 }
Chris@242 150 }
Chris@242 151
Chris@242 152 if (j == 0) {
Chris@242 153 --i;
Chris@242 154 continue;
Chris@242 155 }
Chris@242 156
Chris@235 157 pathcost_t a = costs[j-1][i];
Chris@235 158 pathcost_t b = costs[j][i-1];
Chris@235 159 pathcost_t both = costs[j-1][i-1];
Chris@235 160
Chris@235 161 if (a < b) {
Chris@235 162 --j;
Chris@235 163 if (both <= a) {
Chris@235 164 --i;
Chris@235 165 }
Chris@235 166 } else {
Chris@235 167 --i;
Chris@235 168 if (both <= b) {
Chris@235 169 --j;
Chris@235 170 }
Chris@235 171 }
Chris@235 172 }
Chris@235 173
Chris@235 174 if (m_params.subsequence) {
Chris@235 175 alignment[0] = j;
Chris@235 176 }
Chris@242 177
Chris@242 178 #ifdef DEBUG_DTW
Chris@246 179 std::cerr << "Costed path:" << std::endl;
Chris@242 180 pathcost_t prevcost = 0;
Chris@242 181 int indent = 0;
Chris@242 182 size_t prevj = 0;
Chris@242 183 for (size_t i = 0; i < alignment.size(); ++i) {
Chris@242 184 size_t j = alignment[i];
Chris@242 185 pathcost_t cost = costs[j][i];
Chris@242 186 if (prevcost > 1000) indent += 5;
Chris@242 187 else if (prevcost > 100) indent += 4;
Chris@242 188 else if (prevcost > 10) indent += 3;
Chris@242 189 else if (prevcost > 0) indent += 2;
Chris@242 190 if (j > prevj) {
Chris@242 191 while (j > prevj) {
Chris@242 192 std::cerr << "\n";
Chris@242 193 ++prevj;
Chris@242 194 }
Chris@242 195 for (int k = 0; k < indent; ++k) std::cerr << " ";
Chris@242 196 } else {
Chris@242 197 std::cerr << " ";
Chris@242 198 }
Chris@242 199 std::cerr << cost;
Chris@242 200 prevcost = cost;
Chris@242 201 if (prevcost == 0) prevcost = 1;
Chris@242 202 }
Chris@242 203 std::cerr << "\n";
Chris@246 204
Chris@246 205 std::cerr << "Alignment:" << std::endl;
Chris@246 206 for (size_t i = 0; i < alignment.size(); ++i) {
Chris@246 207 std::cerr << i << " -> " << alignment[i] << "\n";
Chris@246 208 }
Chris@242 209 #endif
Chris@242 210
Chris@235 211 return alignment;
Chris@235 212 }