/** * Copyright 2007 DFKI GmbH. * All Rights Reserved. Use is subject to license terms. * * This file is part of MARY TTS. * * MARY TTS is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package marytts.signalproc.analysis.distance; /** * A general purpose class for computing various distance measures Examples include Euclidean, Mahalanobis, distance to GMMs etc. * * @author Oytun Türk **/ public class DistanceComputer { public static final int ABSOLUTE_VALUE_DISTANCE = 1; public static final int EUCLIDEAN_DISTANCE = 2; public static final int NORMALIZED_EUCLIDEAN_DISTANCE = 3; public static final int MAHALANOBIS_DISTANCE = 4; // Computes absolute value distance // dist = sum(|x(i)-y(i)|) public static double getAbsoluteValueDistance(double[] x, double[] y) { assert x.length == y.length; double dist = 0.0; for (int i = 0; i < x.length; i++) dist += Math.abs(x[i] - y[i]); return dist; } // Computes the Euclidean distance metric // dist = sqrt( sum( (x[i]-y[i])^2 ) ) public static double getEuclideanDistance(double[] x, double[] y) { assert x.length == y.length; double dist = 0.0; for (int i = 0; i < x.length; i++) dist += (x[i] - y[i]) * (x[i] - y[i]); dist = Math.sqrt(dist); return dist; } // Computes the normalized Euclidean distance metric // dist = sqrt( sum( (x[i]-y[i])^2 / var[i] ) ) public static double getNormalizedEuclideanDistance(double[] x, double[] y, double[] variances) { assert x.length == y.length; assert x.length == variances.length; double dist = 0.0; for (int i = 0; i < x.length; i++) dist += (x[i] - y[i]) * (x[i] - y[i]) / variances[i]; dist = Math.sqrt(dist); return dist; } /* * Inverse LSF harmonic distance which is used to find closest LSF matches for a given source LSF vector in the source * codebook: * * R. Laroia, N. Phamdo, and N. Farvardin, “Robust and efficient quantization of speech lsp parameters using structured vector * quantizers,” in Proc. of the IEEE ICASSP 1991, pp. 641–644. * * Note that the function requires the lsfWeights array to be created outside of this function for efficiency purposes as this * function is called many times during transformation. The length of the array should be equal to the length of the lsf * vectors */ public static double getLsfInverseHarmonicDistance(double[] lsfs1, double[] lsfs2, double freqRange) { assert lsfs1.length == lsfs2.length; double[] lsfWeights = getLsfWeights(lsfs1, freqRange); double dist = 0.0; for (int i = 0; i < lsfs1.length; i++) dist += lsfWeights[i] * Math.abs(lsfs1[i] - lsfs2[i]); return dist; } // A symmetric version of the inverse harmonic based lsf distance // The weights are averaged in a weighted manner using alpha prior to distance computation // alpha should be in the range [0.0,1.0]. // If it is 0.0 lsfWeights1 get all the weighting, so the function is identical to getLsfInverseHarmonicDistance. // lsfWeights should be provided outside of the function and their length should match the public static double getLsfInverseHarmonicDistanceSymmetric(double[] lsfs1, double[] lsfs2, double alpha, double freqRange) { assert lsfs1.length == lsfs2.length; double[] lsfWeights1 = getLsfWeights(lsfs1, freqRange); double[] lsfWeights2 = getLsfWeights(lsfs2, freqRange); double dist = 0.0; double oneMinusAlpha = 1.0 - alpha; double absVal; for (int i = 0; i < lsfs1.length; i++) { absVal = Math.abs(lsfs1[i] - lsfs2[i]); dist += oneMinusAlpha * lsfWeights1[i] * absVal + alpha * lsfWeights2[i] * absVal; } return dist; } // Fills in the lsfWeights array with weights estimated for lsfs // Note that the function requires the lsfWeights array to be created outside of this function // for efficiency purposes as this function is called many times during transformation public static double[] getLsfWeights(double[] lsfs, double freqRange) { int i; double[] lsfWeights = new double[lsfs.length]; lsfWeights[0] = 1.0 / Math.abs(lsfs[1] - lsfs[0]); for (i = 1; i < lsfWeights.length - 1; i++) lsfWeights[i] = 1.0 / Math.min(Math.abs(lsfs[i] - lsfs[i - 1]), Math.abs(lsfs[i + 1] - lsfs[i])); lsfWeights[lsfWeights.length - 1] = 1.0 / Math.abs(lsfs[lsfWeights.length - 1] - lsfs[lsfWeights.length - 2]); /* * //Method 1 int ind = MathUtils.getMaxIndex(lsfWeights); double centerFreq; if (ind==0) centerFreq = * 0.5*(lsfs[ind+1]+lsfs[ind])-0.5*freqRange; else { if (ind<lsfs.length-1) { if (Math.abs(lsfs[ind]-lsfs[ind-1]) < * Math.abs(lsfs[ind+1]-lsfs[ind])) centerFreq = 0.5*(lsfs[ind]+lsfs[ind-1])-0.5*freqRange; else centerFreq = * 0.5*(lsfs[ind+1]+lsfs[ind])-0.5*freqRange; } else centerFreq = 0.5*(lsfs[ind]+lsfs[ind-1])-0.5*freqRange; } // */ // Method 2 double tempSum = 0.0; double centerFreq = 0.0; for (i = 0; i < lsfs.length; i++) { centerFreq += lsfs[i] * lsfWeights[i]; tempSum += lsfWeights[i]; } centerFreq /= tempSum; // double lowerFreq = Math.max(0.0, centerFreq - 0.5 * freqRange); double upperFreq = lowerFreq + freqRange; for (i = 0; i < lsfs.length; i++) { if (lsfs[i] < lowerFreq || lsfs[i] > upperFreq) lsfWeights[i] = 0.0; } return lsfWeights; } // Note that this requires an inverse covariance matrix // If you have a diagonal matrix only, then the Mahalanobis distance reduces to // Normalized Eucledian distance // dist = sqrt( sum( (x-meanX)' * (covarianceMatrix^-1) * (x-meanX) ) ) public static double getMahalanobisDistance(double[] x, double[] mean, double[][] inverseCovarianceMatrix) { assert x.length == mean.length; assert x.length == inverseCovarianceMatrix.length; int i, j; double dist = 0.0; double tmpDist; for (i = 0; i < x.length; i++) { tmpDist = 0.0; for (j = 0; j < x.length; j++) tmpDist += (x[j] - mean[j]) * inverseCovarianceMatrix[j][i]; dist += tmpDist * (x[i] - mean[i]); } dist = Math.sqrt(dist); return dist; } }