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.Arrays;
import java.util.Iterator;
import java.util.Vector;

import com.meapsoft.ChunkDist;
import com.meapsoft.DSP;
import com.meapsoft.EDLChunk;
import com.meapsoft.EDLFile;
import com.meapsoft.EuclideanDist;
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 oldDesc = "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;

  //@mike: empty constructor just to sniff this guy
  //please don't call this constructor, it will not work
  public VQComposer()
  {
    initNameAndDescription("Vector Quantizer", new String(oldDesc));
  }
 
  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, null);
    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, null);
        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.