# HG changeset patch # User Chris Cannam # Date 1422538188 0 # Node ID d6cdbd814c8ced504c3e5a183368781fe139c688 # Parent 2b61e0cb684732c5cc2bf37c4bf5d2777f63f405# Parent d6c1556fadd0189df1fed2355242c68273196358 Merge from refactors branch diff -r 2b61e0cb6847 -r d6cdbd814c8c src/DistanceMetric.cpp --- a/src/DistanceMetric.cpp Fri Jan 23 17:33:37 2015 +0000 +++ b/src/DistanceMetric.cpp Thu Jan 29 13:29:48 2015 +0000 @@ -39,13 +39,44 @@ { double d = 0; double sum = 0; + double eps = 1e-16; int featureSize = f1.size(); assert(int(f2.size()) == featureSize); - for (int i = 0; i < featureSize; i++) { - d += fabs(f1[i] - f2[i]); - sum += fabs(f1[i]) + fabs(f2[i]); + if (m_params.metric == Cosine) { + + double num = 0, denom1 = 0, denom2 = 0; + + for (int i = 0; i < featureSize; ++i) { + num += f1[i] * f2[i]; + denom1 += f1[i] * f1[i]; + denom2 += f2[i] * f2[i]; + } + + d = 1.0 - (num / (eps + sqrt(denom1 * denom2))); + + if (m_params.noise == AddNoise) { + d += 1e-2; + } + if (d > 1.0) d = 1.0; + + return d; // normalisation param ignored + + } + + if (m_params.metric == Manhattan) { + for (int i = 0; i < featureSize; i++) { + d += fabs(f1[i] - f2[i]); + sum += fabs(f1[i]) + fabs(f2[i]); + } + } else { + // Euclidean + for (int i = 0; i < featureSize; i++) { + d += (f1[i] - f2[i]) * (f1[i] - f2[i]); + sum += fabs(f1[i]) + fabs(f2[i]); + } + d = sqrt(d); } double noise = 1e-3 * featureSize; diff -r 2b61e0cb6847 -r d6cdbd814c8c src/DistanceMetric.h --- a/src/DistanceMetric.h Fri Jan 23 17:33:37 2015 +0000 +++ b/src/DistanceMetric.h Thu Jan 29 13:29:48 2015 +0000 @@ -22,6 +22,23 @@ class DistanceMetric { public: + enum Metric { + + /** Calculate the Manhattan distance between feature + * vectors. If the vectors contain energy, as the default + * MATCH feature does, this could be considered as a squared + * Euclidean distance metric. */ + Manhattan, + + /** Calculate the Euclidean distance between feature vectors. */ + Euclidean, + + /** Calculate the cosine distance between feature vectors. The + * normalisation setting will be ignored as the result is + * already magnitude-independent. */ + Cosine, + }; + enum DistanceNormalisation { /** Do not normalise distance metrics */ @@ -35,7 +52,7 @@ * of the sum of the frames. */ NormaliseDistanceToLogSum, }; - + enum NoiseAddition { /** Don't add noise. */ @@ -49,22 +66,22 @@ struct Parameters { Parameters() : + metric(Manhattan), norm(NormaliseDistanceToLogSum), noise(AddNoise) {} - /** Normalisation for distance metrics. */ + Metric metric; DistanceNormalisation norm; NoiseAddition noise; }; DistanceMetric(Parameters params); - /** Calculates the Manhattan distance between two vectors, with an - * optional normalisation by the combined values in the - * vectors. Since the vectors contain energy, this could be - * considered as a squared Euclidean distance metric. Note that - * normalisation assumes the values are all non-negative. + /** Calculates the distance in some metric between two vectors, + * with an optional normalisation by the combined values in the + * vectors. Note that normalisation assumes the values are all + * non-negative. * * @param f1 one of the vectors involved in the distance calculation * @param f2 one of the vectors involved in the distance calculation diff -r 2b61e0cb6847 -r d6cdbd814c8c src/MatchVampPlugin.cpp --- a/src/MatchVampPlugin.cpp Fri Jan 23 17:33:37 2015 +0000 +++ b/src/MatchVampPlugin.cpp Thu Jan 29 13:29:48 2015 +0000 @@ -215,6 +215,21 @@ desc.isQuantized = false; list.push_back(desc); + desc.identifier = "metric"; + desc.name = "Distance metric"; + desc.description = "Metric for distance calculations."; + desc.minValue = 0; + desc.maxValue = 2; + desc.defaultValue = (int)m_defaultDParams.metric; + desc.isQuantized = true; + desc.quantizeStep = 1; + desc.valueNames.clear(); + desc.valueNames.push_back("Manhattan"); + desc.valueNames.push_back("Euclidean"); + desc.valueNames.push_back("Cosine"); + list.push_back(desc); + desc.valueNames.clear(); + desc.identifier = "noise"; desc.name = "Mix in Noise"; desc.description = "Whether to mix in a small constant white noise term when calculating feature distance. This can improve alignment against sources containing cleanly synthesised audio."; @@ -293,6 +308,8 @@ return m_smooth ? 1.0 : 0.0; } else if (name == "silencethreshold") { return (float)m_fcParams.silenceThreshold; + } else if (name == "metric") { + return (int)m_dParams.metric; } else if (name == "noise") { return m_dParams.noise; } @@ -323,6 +340,8 @@ m_smooth = (value > 0.5); } else if (name == "silencethreshold") { m_fcParams.silenceThreshold = value; + } else if (name == "metric") { + m_dParams.metric = (DistanceMetric::Metric)(int(value + 0.1)); } else if (name == "noise") { m_dParams.noise = (DistanceMetric::NoiseAddition)(int(value + 0.1)); }