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/$$