Mercurial > hg > match-vamp
changeset 26:9f60d097f0b2
Pull out DistanceMetric into its own class
author | Chris Cannam |
---|---|
date | Fri, 31 Oct 2014 11:31:08 +0000 |
parents | e6a143e4e143 |
children | 2744c870d8c5 |
files | DistanceMetric.cpp DistanceMetric.h Makefile.inc MatchVampPlugin.cpp Matcher.cpp Matcher.h test/regressiontest.sh |
diffstat | 7 files changed, 173 insertions(+), 80 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DistanceMetric.cpp Fri Oct 31 11:31:08 2014 +0000 @@ -0,0 +1,67 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp feature extraction plugin using the MATCH audio alignment + algorithm. + + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 Simon Dixon, Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "DistanceMetric.h" + +#include <cassert> +#include <cmath> + +using std::vector; + +double +DistanceMetric::calcDistance(const vector<double> &f1, + const vector<double> &f2) +{ + double d = 0; + double sum = 0; + + int featureSize = f1.size(); + assert(int(f2.size()) == featureSize); + + for (int i = 0; i < featureSize; i++) { + d += fabs(f1[i] - f2[i]); + sum += f1[i] + f2[i]; + } + + if (sum == 0) + return 0; + if (m_norm == NormaliseDistanceToSum) + return d / sum; // 0 <= d/sum <= 2 + if (m_norm != NormaliseDistanceToLogSum) + return d; + + // note if this were to be restored, it would have to use + // totalEnergies vector instead of f1[freqMapSize] which used to + // store the total energy: + // double weight = (5 + Math.log(f1[freqMapSize] + f2[freqMapSize]))/10.0; + + double weight = (8 + log(sum)) / 10.0; + + if (weight < 0) weight = 0; + else if (weight > 1) weight = 1; + + return d / sum * weight; +} + +int +DistanceMetric::calcDistanceScaled(const vector<double> &f1, + const vector<double> &f2, + double scale) +{ + double distance = calcDistance(f1, f2); + return int(distance * scale); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DistanceMetric.h Fri Oct 31 11:31:08 2014 +0000 @@ -0,0 +1,73 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp feature extraction plugin using the MATCH audio alignment + algorithm. + + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 Simon Dixon, Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef DISTANCE_METRIC_H +#define DISTANCE_METRIC_H + +#include <vector> + +class DistanceMetric +{ +public: + enum DistanceNormalisation { + + /** Do not normalise distance metrics */ + NoDistanceNormalisation, + + /** Normalise distance metric for pairs of frames by the sum + * of the two frames. */ + NormaliseDistanceToSum, + + /** Normalise distance metric for pairs of frames by the log + * of the sum of the frames. */ + NormaliseDistanceToLogSum, + }; + + DistanceMetric(DistanceNormalisation norm) : m_norm(norm) { } + + /** 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. + * + * @param f1 one of the vectors involved in the distance calculation + * @param f2 one of the vectors involved in the distance calculation + * @return the distance + */ + double calcDistance(const std::vector<double> &f1, + const std::vector<double> &f2); + + /** 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. + * + * @param f1 one of the vectors involved in the distance calculation + * @param f2 one of the vectors involved in the distance calculation + * @param scale the scaling factor to place the result in integer range + * @return the distance, scaled by scale and truncated to an integer + */ + int calcDistanceScaled(const std::vector<double> &f1, + const std::vector<double> &f2, + double scale); + +private: + DistanceNormalisation m_norm; +}; + +#endif
--- a/Makefile.inc Fri Oct 17 12:57:14 2014 +0100 +++ b/Makefile.inc Fri Oct 31 11:31:08 2014 +0000 @@ -4,8 +4,8 @@ CXX ?= g++ CC ?= gcc -HEADERS := Finder.h Matcher.h MatchFeeder.h MatchFeatureFeeder.h MatchVampPlugin.h Path.h -SOURCES := Finder.cpp Matcher.cpp MatchFeeder.cpp MatchFeatureFeeder.cpp MatchVampPlugin.cpp Path.cpp +HEADERS := DistanceMetric.h Finder.h Matcher.h MatchFeeder.h MatchFeatureFeeder.h MatchVampPlugin.h Path.h +SOURCES := DistanceMetric.cpp Finder.cpp Matcher.cpp MatchFeeder.cpp MatchFeatureFeeder.cpp MatchVampPlugin.cpp Path.cpp OBJECTS := $(SOURCES:.cpp=.o) @@ -23,10 +23,17 @@ # DO NOT DELETE -Finder.o: Finder.h Matcher.h -Matcher.o: Matcher.h -MatchFeeder.o: MatchFeeder.h Matcher.h Finder.h -MatchVampPlugin.o: MatchVampPlugin.h Matcher.h MatchFeeder.h Finder.h Path.h +DistanceMetric.o: DistanceMetric.h +Finder.o: Finder.h Matcher.h DistanceMetric.h +Matcher.o: Matcher.h DistanceMetric.h +MatchFeeder.o: MatchFeeder.h Matcher.h DistanceMetric.h Finder.h +MatchFeatureFeeder.o: MatchFeatureFeeder.h Matcher.h DistanceMetric.h +MatchFeatureFeeder.o: Finder.h +MatchVampPlugin.o: MatchVampPlugin.h Matcher.h DistanceMetric.h MatchFeeder.h +MatchVampPlugin.o: Finder.h Path.h Path.o: Path.h -Finder.o: Matcher.h -MatchFeeder.o: Matcher.h Finder.h +Finder.o: Matcher.h DistanceMetric.h +Matcher.o: DistanceMetric.h +MatchFeeder.o: Matcher.h DistanceMetric.h Finder.h +MatchFeatureFeeder.o: Matcher.h DistanceMetric.h Finder.h +MatchVampPlugin.o: Matcher.h DistanceMetric.h
--- a/MatchVampPlugin.cpp Fri Oct 17 12:57:14 2014 +0100 +++ b/MatchVampPlugin.cpp Fri Oct 31 11:31:08 2014 +0000 @@ -255,7 +255,7 @@ } else if (name == "framenorm") { m_params.frameNorm = (Matcher::FrameNormalisation)(int(value + 0.1)); } else if (name == "distnorm") { - m_params.distanceNorm = (Matcher::DistanceNormalisation)(int(value + 0.1)); + m_params.distanceNorm = (DistanceMetric::DistanceNormalisation)(int(value + 0.1)); } else if (name == "usespecdiff") { m_params.useSpectralDifference = (value > 0.5); } else if (name == "usechroma") {
--- a/Matcher.cpp Fri Oct 17 12:57:14 2014 +0100 +++ b/Matcher.cpp Fri Oct 31 11:31:08 2014 +0000 @@ -26,7 +26,8 @@ //#define DEBUG_MATCHER 1 Matcher::Matcher(Parameters parameters, Matcher *p) : - params(parameters) + params(parameters), + metric(parameters.distanceNorm) { #ifdef DEBUG_MATCHER cerr << "Matcher::Matcher(" << params.sampleRate << ", " << p << ")" << endl; @@ -58,7 +59,8 @@ Matcher::Matcher(Parameters parameters, Matcher *p, int featureSize) : params(parameters), - externalFeatureSize(featureSize) + externalFeatureSize(featureSize), + metric(parameters.distanceNorm) { #ifdef DEBUG_MATCHER cerr << "Matcher::Matcher(" << params.sampleRate << ", " << p << ", " << featureSize << ")" << endl; @@ -392,8 +394,12 @@ int mn= -1; int mx= -1; for ( ; index < stop; index++) { - int dMN = calcDistance(frames[frameIndex], - otherMatcher->frames[index % blockSize]); + + int dMN = metric.calcDistanceScaled + (frames[frameIndex], + otherMatcher->frames[index % blockSize], + scale); + if (mx<0) mx = mn = dMN; else if (dMN > mx) @@ -404,6 +410,7 @@ overflow = true; dMN = 255; } + if ((frameCount == 0) && (index == 0)) // first element setValue(0, 0, 0, 0, dMN); else if (frameCount == 0) // first row @@ -461,44 +468,6 @@ } int -Matcher::calcDistance(const vector<double> &f1, const vector<double> &f2) -{ - double d = 0; - double sum = 0; - for (int i = 0; i < featureSize; i++) { - d += fabs(f1[i] - f2[i]); - sum += f1[i] + f2[i]; - } - // System.err.print(" " + Format.d(d,3)); - if (sum == 0) - return 0; - if (params.distanceNorm == NormaliseDistanceToSum) - return (int)(scale * d / sum); // 0 <= d/sum <= 2 - if (params.distanceNorm != NormaliseDistanceToLogSum) - return (int)(scale * d); - - // note if this were to be restored, it would have to use - // totalEnergies vector instead of f1[freqMapSize] which used to - // store the total energy: - // double weight = (5 + Math.log(f1[freqMapSize] + f2[freqMapSize]))/10.0; - - double weight = (8 + log(sum)) / 10.0; - // if (weight < mins) { - // mins = weight; - // System.err.println(Format.d(mins,3) + " " + Format.d(maxs)); - // } - // if (weight > maxs) { - // maxs = weight; - // System.err.println(Format.d(mins,3) + " " + Format.d(maxs)); - // } - if (weight < 0) - weight = 0; - else if (weight > 1) - weight = 1; - return (int)(scale * d / sum * weight); -} // calcDistance() - -int Matcher::getValue(int i, int j, bool firstAttempt) { if (firstPM)
--- a/Matcher.h Fri Oct 17 12:57:14 2014 +0100 +++ b/Matcher.h Fri Oct 31 11:31:08 2014 +0000 @@ -27,6 +27,7 @@ #define ADVANCE_BOTH 3 #define MASK 0xfc +#include "DistanceMetric.h" using std::vector; using std::string; @@ -55,26 +56,12 @@ NormaliseFrameToLTAverage, }; - enum DistanceNormalisation { - - /** Do not normalise distance metrics */ - NoDistanceNormalisation, - - /** Normalise distance metric for pairs of audio frames by - * the sum of the two frames. */ - NormaliseDistanceToSum, - - /** Normalise distance metric for pairs of audio frames by - * the log of the sum of the frames. */ - NormaliseDistanceToLogSum, - }; - struct Parameters { Parameters(float rate_, double hopTime_, int fftSize_) : sampleRate(rate_), frameNorm(NormaliseFrameToSum1), - distanceNorm(NormaliseDistanceToLogSum), + distanceNorm(DistanceMetric::NormaliseDistanceToLogSum), useSpectralDifference(true), useChromaFrequencyMap(false), hopTime(hopTime_), @@ -92,7 +79,7 @@ FrameNormalisation frameNorm; /** Type of distance metric normalisation */ - DistanceNormalisation distanceNorm; + DistanceMetric::DistanceNormalisation distanceNorm; /** Flag indicating whether or not the half-wave rectified * spectral difference should be used in calculating the @@ -362,18 +349,6 @@ */ void consumeFeatureVector(std::vector<double> feature); - /** 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. - * - * @param f1 one of the vectors involved in the distance calculation - * @param f2 one of the vectors involved in the distance calculation - * @return the distance, scaled and truncated to an integer - */ - int calcDistance(const vector<double> &f1, const vector<double> &f2); - /** Retrieves values from the minimum cost matrix. * * @param i the frame number of this Matcher @@ -396,6 +371,8 @@ vector<double> processFrameFromFreqData(double *, double *); void calcAdvance(); + DistanceMetric metric; + friend class MatchFeeder; friend class MatchFeatureFeeder; friend class Finder;
--- a/test/regressiontest.sh Fri Oct 17 12:57:14 2014 +0100 +++ b/test/regressiontest.sh Fri Oct 31 11:31:08 2014 +0000 @@ -12,6 +12,6 @@ ~/Music/cc-kids-abrsm-dataset/Kids/Allegro\ in\ G.mp3 \ -w csv --csv-stdout 2>/dev/null | sed 's/^[^,]*,//' > /tmp/$$ || exit 1 -diff -u /tmp/$$ `dirname $0`/expected.csv && echo Passed +sdiff -w 78 /tmp/$$ `dirname $0`/expected.csv && echo Passed rm /tmp/$$