Package org.vocvark.Aggregators

Source Code of org.vocvark.Aggregators.MultipleFeatureHistogram

/**
* Multiple Feature Histogram
*
* created by Daniel McEnnis for the 2006 ISMIR jAUdio release
*
*/
package org.vocvark.Aggregators;

import org.vocvark.DataTypes.AggregatorDefinition;
import org.vocvark.DataTypes.FeatureDefinition;

/**
* <h2>Multiple Fetaure Histogram</h2>
* <p>This specific algorithm correlates different feature dimensions provided, binning them
* into equal bins relative to each feature's range. The resulting feature set, similar to the
* area method of moments, captures cross-feature information.  However, this feature uses an extremely
* large feature set that grows exponentially with the number of features added.  On the positive side, the order
* of the features is irrelevant to its performance.
* </p>
* <h2>Histogram Binning History</h2>
* <p>First used in 2000, it was not formally published until McEnnis and Fujinaga 2005.</p>
*
* @author Daniel McEnnis
*/
public class MultipleFeatureHistogram extends AggregatorBaseImpl {

    String[] base = null;

    int[] indecis = null;

    int binsPerDimension;

    /**
     * Constructs a new aggregator.  This aggregator is not valid until it has a feature list set as a parameter.
     */
    public MultipleFeatureHistogram() {
        metadata = new AggregatorDefinition("Multiple Feature Histogram", "a histogram of categories of input", false,
                new String[] {"Number of bins for 1st dimension"});
    }

    /**
     * Constructs a fully functional aggregator.
     */
    public MultipleFeatureHistogram(String[] fe, int bins) {
        base = fe;
        String name = "Histogram:";
        String description = "Histogram of concurrent changes in";
        int dimensions = 0;
        for (int i = 0; i < fe.length; ++i) {
            name += " " + fe[i];
            description += " " + fe[i];
        }
        definition = new FeatureDefinition(name, description, true, dimensions);
        binsPerDimension = bins;
        metadata = new AggregatorDefinition("Multiple Feature Histogram",
                "a histogram of categories of input", false,
                new String[]{"Number of bins for 1st dimension"});
    }

    /* (non-Javadoc)
      * @see jAudioFeatureExtractor.Aggregators.Aggregator#getParameters()
      */
    @Override
    public String[] getParameters() {
        return new String[]{Integer.toString(binsPerDimension)};
    }

    @Override
    public void setParameters(String[] features, String[] params)
            throws Exception {
        if (features == null) {
            throw new Exception(
                    "MultipleFeatureHistogram requires a list of features to aggregate");
        }
        if (params.length == 1) {
            try {
                binsPerDimension = Integer.parseInt(params[0]);
                base = features;
                String name = "Histogram:";
                String description = "Histogram of concurrent changes in";
                int dimensions = 0;
                for (int i = 0; i < features.length; ++i) {
                    name += " " + features[i];
                    description += " " + features[i];
                    // dimensions +=
                    // features[i].getFeatureDefinition().dimensions;
                }
                // dimensions = (int) Math.pow(binsPerDimension, dimensions);
                definition = new FeatureDefinition(name, description, true,
                        dimensions);

            } catch (NumberFormatException e) {
                throw new Exception(
                        "Parameters to MultipleFeatureHistogram must be an integer");
            }
        } else {
            throw new Exception(
                    "MultipleFeatureHistogram takes exactly one argument of type integer");
        }
    }

    @Override
    public void aggregate(double[][][] values) throws Exception {

        // flatten features/dimensions into a single array
        int[][] featureList = super.collapseFeatures(values, indecis);

        // now we know how precisely how many dimensions have values, adjust the
        // FeatureDefinition accordingly
        definition.dimensions = (int) Math.pow(binsPerDimension,
                featureList.length);

        if (definition.dimensions > 1048576) {
            throw new Exception("Number of dimensions for " + definition.name + " exceeds 1048576 - " + definition.dimensions);
        }

        // calculate earliest window that has values for all features to be
        // included
        int offset = super.calculateOffset(values, indecis);

        // transform the arrays of values into arrays of bins
        // The dimensions are [dimension][windows]
        Integer[][] bins = new Integer[featureList.length][];
        for (int i = 0; i < featureList.length; ++i) {
            bins[i] = assignToBins(values, featureList[i][0], featureList[i][1]);
        }

        // combine these bins into a single histogram
        result = combineBins(bins, offset);

    }

    @Override
    public Object clone() {
        MultipleFeatureHistogram ret = new MultipleFeatureHistogram();
        if (base != null) {
            try {
                ret.setParameters(base, new String[]{Integer
                        .toString(binsPerDimension)});
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        return ret;
    }

    @Override
    public FeatureDefinition getFeatureDefinition() {
        return definition;
    }

    @Override
    public String[] getFeaturesToApply() {

        return base;
    }

    @Override
    public void init(int[] featureIndecis) throws Exception {
        indecis = featureIndecis;
    }

    /**
     * Takes a particular dimension of a particular feature and divides the
     * distance between the largest and smallest value into 4 linear bins.
     * values less than min+25% are bin 0, values less than min+50% are bin 1,
     * etc. Null entries become null entries in the resulting Integer array.
     *
     * @param values    array holding output of all features
     * @param feature   which feature in the values array to extract
     * @param dimension which dimension of this feature to extract
     * @return
     */
    Integer[] assignToBins(double values[][][], int feature, int dimension) {
        Integer[] ret = new Integer[values.length];

        // first calculate the bin values
        double[] bin = new double[binsPerDimension - 1];
        double max = Double.MIN_VALUE;
        double min = Double.MAX_VALUE;
        for (int i = 0; i < values.length; ++i) {
            if ((values[i][feature] != null)
                    && (values[i][feature][dimension] > max)) {
                max = values[i][feature][dimension];
            }
            if ((values[i][feature] != null)
                    && (values[i][feature][dimension] < min)) {
                min = values[i][feature][dimension];
            }
        }
        double separator = (max - min) / ((double) binsPerDimension);
        bin[0] = min + separator;
        for (int j = 1; j < bin.length; ++j) {
            bin[j] = bin[j - 1] + separator;
        }

        // now assign a bin to every value in the dataset
        for (int i = 0; i < values.length; ++i) {
            if (values[i][feature] == null) {
                ret[i] = null;
            } else {
                ret[i] = null;
                for (int j = 0; j < bin.length; ++j) {
                    if (values[i][feature][dimension] < bin[j]) {
                        ret[i] = new Integer(j);
                        break;
                    }
                }
                if (ret[i] == null) {
                    ret[i] = new Integer(binsPerDimension - 1);
                }
            }
        }

        return ret;
    }

    double[] combineBins(Integer[][] bins, int offset) {
        int numDimensions = (int) Math.pow(binsPerDimension, bins.length);
        double[] ret = new double[numDimensions];

        // for every bin combiniation, increment the histogram
        for (int i = offset; i < bins[0].length; ++i) {
            int index = 0;
            int factor = 1;
            for (int j = 0; j < bins.length; ++j) {
                index += (bins[j][i].intValue()) * factor;
                factor *= binsPerDimension;
            }
            ret[index] += 1.0;
        }

        // Normalize the histogram
        for (int i = 0; i < ret.length; ++i) {
            ret[i] /= (bins[0].length - offset);
        }
        return ret;
    }

}
TOP

Related Classes of org.vocvark.Aggregators.MultipleFeatureHistogram

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.