Package com.meapsoft.composers

Source Code of com.meapsoft.composers.VQComposer

/*
*  Copyright 2006-2007 Columbia University.
*
*  This file is part of MEAPsoft.
*
*  MEAPsoft is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License version 2 as
*  published by the Free Software Foundation.
*
*  MEAPsoft 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
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with MEAPsoft; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
*  02110-1301 USA
*
*  See the file "COPYING" for the text of the license.
*/

package com.meapsoft.composers;

import gnu.getopt.Getopt;

import java.io.IOException;
import java.util.Iterator;
import java.util.Vector;
import java.util.Arrays;

import com.meapsoft.ChunkDist;
import com.meapsoft.EuclideanDist;
import com.meapsoft.DSP;
import com.meapsoft.EDLChunk;
import com.meapsoft.EDLFile;
import com.meapsoft.FeatChunk;
import com.meapsoft.FeatFile;
import com.meapsoft.ParserException;

/**
* Program that learns a vector quantizer from a FeatFile and
* uses it to quantize the chunks in another FeatFile.
*
* All about VQ: http://www.data-compression.com/vq.html
*
* @author Ron Weiss (ronw@ee.columbia.edu)
*/
public class VQComposer extends Composer
{

  public static String description = "VQComposer trains a vector quantizer on the chunks in the input file.  It then uses it to quantize the chunks in another file.  For best results use the beat segmenter so each chunk has roughly the same length.";
 
  protected String outFileName = "vq.edl";

    // file to train the VQ
  protected FeatFile trainFile;

    // file to quantize.  If this is null, the composer will output an
    // EDL containing the cluster template chunks.
  protected FeatFile featsToQuantize = null;

    // number of codewords to use
    protected int cbSize = 32;
    protected Vector templateChunks = new Vector(cbSize);

    // how many beats should we put in each state?
    protected int beatsPerCodeword = 4;

    // only supports euclidean distance for now
    protected ChunkDist dist = new EuclideanDist();

    protected int[] featdim = null;
    protected boolean debug = false;

  public VQComposer(FeatFile trainFN, EDLFile outFN)
  {
    if (trainFN == null || outFN == null)
      return;
     
    trainFile = trainFN;
    outFile = outFN;

    if(outFile == null)
      outFile = new EDLFile("");
  }

  public void printUsageAndExit()
  {
    System.out.println("Usage: VQComposer [-options] features.feat \n\n" +
         "  where options include:\n" +
         "    -o output_file   the file to write the output to (defaults to "+outFileName+")\n" +
               "    -g               debug mode\n" +
               "    -f file.feat     feature file to quantize (uses training features file by default)\n" +
               "    -b nbeats        number of beats each codeword should contain (defaults to "+beatsPerCodeword+")\n" +
               "    -q codebook_size number of templates to use in the VQ codebook (defaults to 8).");
        printCommandLineOptions('i');
        printCommandLineOptions('d');
        printCommandLineOptions('c');
    System.out.println();
    System.exit(0);
  }

  public VQComposer(String[] args)
  {
    if(args.length == 0)
      printUsageAndExit();

    Vector features = new Vector();

    // Parse arguments
    String argString = "o:c:q:i:gd:f:b:";
        featdim = parseFeatDim(args, argString);
        dist = parseChunkDist(args, argString, featdim);
        parseCommands(args, argString);

    Getopt opt = new Getopt("VQComposer", args, argString);
    opt.setOpterr(false);
       
    int c = -1;
    while ((c =opt.getopt()) != -1)
    {
      switch(c)
      {
      case 'o':
        outFileName = opt.getOptarg();
        break;
      case 'g':
        debug = true;
        break;
      case 'q':
        cbSize = Integer.parseInt(opt.getOptarg());
                break;
      case 'b':
        beatsPerCodeword = Integer.parseInt(opt.getOptarg());
        break;
      case 'f':
        featsToQuantize = new FeatFile(opt.getOptarg());
        break;
            case 'c'// already handled above
                break;
            case 'd'// already handled above
                break;
            case 'i'// already handled above
                break;
      case '?':
        printUsageAndExit();
        break;
      default:
        System.out.print("getopt() returned " + c + "\n");
      }
    }
       
    // parse arguments
    int ind = opt.getOptind();
    if(ind > args.length)
      printUsageAndExit();
       
    trainFile = new FeatFile(args[args.length-1]);
        if(featsToQuantize == null)
            featsToQuantize = trainFile;
    outFile = new EDLFile(outFileName);

    System.out.println("Composing " + outFileName +
               " from " +  args[args.length-1] + ".");
  }

  public void setup() throws IOException, ParserException
  {
    super.setup();

    if(!trainFile.haveReadFile)
      trainFile.readFile();

    if(trainFile.chunks.size() == 0)
      throw new ParserException(trainFile.filename, "No chunks found");

        trainFile = (FeatFile)trainFile.clone();
        trainFile.normalizeFeatures();
        trainFile.applyFeatureWeights();

        // To change the number of beats per state all we have to do
        // is modify the chunks in trainFile by joining every
        // beatsPerCodeword chunk into one a superchunk.
        Vector newChunks = new Vector();
        for(int x = 0; x < trainFile.chunks.size()-beatsPerCodeword+1;
            x += beatsPerCodeword)
        {
            FeatChunk newChunk =
                (FeatChunk)((FeatChunk)trainFile.chunks.get(x)).clone();

            double length = 0;
            for(int y = 1; y < beatsPerCodeword; y++)
            {
                FeatChunk f = (FeatChunk)trainFile.chunks.get(x+y);
               
                newChunk.addFeature(f.getFeatures());
                newChunk.length += f.length;
            }
           
            newChunks.add(newChunk);
        }

        trainFile.chunks = newChunks;

        progress.setMaximum(trainFile.chunks.size());

    if(featsToQuantize != null)
        {
            if(!featsToQuantize.haveReadFile)
                featsToQuantize.readFile();


            // What if features don't match
            if(!featsToQuantize.isCompatibleWith(trainFile))
                throw new ParserException(trainFile.filename,
                                      "Features do not match those in "
                                          + featsToQuantize.filename);
           
            featsToQuantize = (FeatFile)featsToQuantize.clone();
            featsToQuantize.normalizeFeatures();
            featsToQuantize.applyFeatureWeights();

            // To change the number of beats per state all we have to do
            // is modify the chunks in featFile by joining every
            // beatsPerCodeword chunk into one a superchunk.
            newChunks = new Vector();
            for(int x = 0;
                x < featsToQuantize.chunks.size()-beatsPerCodeword+1;
                x += beatsPerCodeword)
            {
                FeatChunk newChunk =
                    (FeatChunk)((FeatChunk)featsToQuantize.chunks.get(x)).clone();
               
                double length = 0;
                for(int y = 1; y < beatsPerCodeword; y++)
                {
                    FeatChunk f = (FeatChunk)featsToQuantize.chunks.get(x+y);
                   
                    newChunk.addFeature(f.getFeatures());
                    newChunk.length += f.length;
                }
               
                newChunks.add(newChunk);
            }
           
            featsToQuantize.chunks = newChunks;

            progress.setMaximum(featsToQuantize.chunks.size());
        }
  }


    /**
     * Use the LBG splitting algorithm to learn a VQ codebook
     */
    protected void learnCodebook(FeatFile trainFile)
    {
        double[][] features = trainFile.getFeatures();
        int ndat = features.length;
        int ndim = features[0].length;

        progress.setMaximum(progress.getMaximum() + (int)(Math.log(cbSize)/Math.log(2)));

        // initial codebook:
        templateChunks = new Vector(cbSize);
       
        // create a placeholder template chunk
        FeatChunk template0 = new FeatChunk("templateChunk0", 0, 0);
        template0.setFeatures(DSP.mean(DSP.transpose(features)));
        templateChunks.add(template0);

        // distortions of between each codeword and each chunk
        double[][] distortion = new double[cbSize][ndat];
        for(int x = 0; x < distortion.length; x++)
            Arrays.fill(distortion[x], Double.MAX_VALUE);
        // indicies into cbMeans for each chunk
        int[] idx = new int[ndat];

        // how much should the means be nudged when splitting
        double delta = 1e-3;

        // start from one codeword and go from there
        for(int nValidCW = 2; nValidCW <= cbSize;
            nValidCW = Math.min(2*nValidCW, cbSize))
        {
            if(debug)
                System.out.println("Splitting into "+nValidCW+" codewords.");

            // split codewords
            for(int c = 0; c < nValidCW; c += 2)
            {
                FeatChunk ch = (FeatChunk)templateChunks.get(c);
                ch.setFeatures(DSP.minus(ch.getFeatures(), delta));
                templateChunks.set(c, ch);

                FeatChunk newch = new FeatChunk("templateChunk"+c, 0, 0);
                newch.setFeatures(DSP.plus(ch.getFeatures(), delta));
                templateChunks.add(c+1, newch);
            }

            double currTotalDist = 0;
            double prevTotalDist = Double.MAX_VALUE;
            do
            {
                prevTotalDist = currTotalDist;
                currTotalDist = 0;
                for(int c = 0; c < nValidCW; c++)
                {
                    FeatChunk cw = (FeatChunk)templateChunks.get(c);
                    for(int n = 0; n < ndat; n++)
                    {
                        FeatChunk ch = (FeatChunk)trainFile.chunks.get(n);
                        distortion[c][n] = dist.distance(cw, ch);
                        currTotalDist += distortion[c][n];
                    }
                }

                // quantize
                for(int n = 0; n < ndat; n++)
                    idx[n] = DSP.argmin(DSP.getColumn(distortion, n));

                // update means
                double[] newCW = new double[ndim];
                for(int c = 0; c < nValidCW; c++)
                {
                    FeatChunk ch = (FeatChunk)templateChunks.get(c);
                    Arrays.fill(newCW, 0);
                    int nmatch = 0;
                    for(int n = 0; n < ndat; n++)
                    {
                        if(idx[n] == c)
                        {
                            nmatch++;
                          
                            for(int i = 0; i < ndim; i++)
                                newCW[i] += features[n][i];
                        }
                    }

                    if(nmatch != 0)
                        ch.setFeatures(DSP.rdivide(newCW, nmatch));
                }
                if(debug)
                    System.out.println("  distortion = "
                                       + Math.abs(currTotalDist-prevTotalDist));
            } while(Math.abs(currTotalDist - prevTotalDist) > 0.0);

            progress.setValue(progress.getValue()+1);

            // make sure we exit the loop once we're done splitting
            if(nValidCW == cbSize)
                break;
        }

        // use the chunk closest to cbMeans as the template for each
        // codeword
        templateChunks = new Vector(cbSize);
        for(int c = 0; c < cbSize; c++)
        {
            int n = DSP.argmin(distortion[c]);
            templateChunks.add(c, trainFile.chunks.get(n));
        }
    }

    protected int quantizeChunk(FeatChunk f)
    {
        double minDist = Double.MAX_VALUE;
        int match = -1;
        for(int c = 0; c < templateChunks.size(); c++)
        {
            FeatChunk chunk = (FeatChunk)templateChunks.get(c);

            double d = dist.distance(chunk, f);
           
            if(d < minDist)
            {
                minDist = d;
                match = c;
            }
        }

        return match;
    }

  public EDLFile compose()
  {
        // learn vq codebook from trainFile
        learnCodebook(trainFile);

        if(featsToQuantize == null)
        {
            double currTime = 0.0;
            Iterator i = templateChunks.iterator();
            while(i.hasNext())
            {
                FeatChunk currChunk = (FeatChunk)i.next();

                EDLChunk nc = new EDLChunk(
                    (FeatChunk)templateChunks.get(quantizeChunk(currChunk)),
                    currTime);
                outFile.chunks.add(nc);

                currTime += nc.length;
                progress.setValue(progress.getValue()+1);
            }

        }
        else  // quantize featsToQuantize
        {
            Iterator i = featsToQuantize.chunks.iterator();
            while(i.hasNext())
            {
                FeatChunk currChunk = (FeatChunk)i.next();
               
                EDLChunk nc = new EDLChunk(
                    (FeatChunk)templateChunks.get(quantizeChunk(currChunk)),
                    currChunk.startTime);
               
                outFile.chunks.add(nc);
                progress.setValue(progress.getValue()+1);
            }
        }

        return outFile;
    }

    public void setCodebookSize(int cb)
    {
        cbSize = cb;

        if(cbSize == 0)
            cbSize = 1;
    }

    public void setBeatsPerCodeword(int nbeats)
    {
        if(nbeats > 0)
            beatsPerCodeword = nbeats;
    }

    public void setFeatsToQuantize(FeatFile featFile)
    {
        featsToQuantize = featFile;
    }
 

  public static void main(String[] args)
  {
    VQComposer m = new VQComposer(args);
    long startTime = System.currentTimeMillis();
    m.run();
    System.out.println("Done. Took " +
               ((System.currentTimeMillis() - startTime)/1000.0)
               + "s");
    System.exit(0);
  }
}
TOP

Related Classes of com.meapsoft.composers.VQComposer

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.