examples/sfexamples/oggvorbiscodec/src/libvorbis/vq/latticehint.c

00001 /********************************************************************
00002  *                                                                  *
00003  * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
00004  * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
00005  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
00006  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
00007  *                                                                  *
00008  * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
00009  * by the XIPHOPHORUS Company http://www.xiph.org/                  *
00010  *                                                                  *
00011  ********************************************************************
00012 
00013  function: utility main for building thresh/pigeonhole encode hints
00014  last mod: $Id: latticehint.c 7187 2004-07-20 07:24:27Z xiphmont $
00015 
00016  ********************************************************************/
00017 
00018 #include <stdlib.h>
00019 #include <stdio.h>
00020 #include <math.h>
00021 #include <string.h>
00022 #include <errno.h>
00023 #include "../lib/scales.h"
00024 #include "bookutil.h"
00025 #include "vqgen.h"
00026 #include "vqsplit.h"
00027 
00028 /* The purpose of this util is to build encode hints for lattice
00029    codebooks so that brute forcing each codebook entry isn't needed.
00030    Threshhold hints are for books in which each scalar in the vector
00031    is independant (eg, residue) and pigeonhole lookups provide a
00032    minimum error fit for words where the scalars are interdependant
00033    (each affecting the fit of the next in sequence) as in an LSP
00034    sequential book (or can be used along with a sparse threshhold map,
00035    like a splitting tree that need not be trained) 
00036 
00037    If the input book is non-sequential, a threshhold hint is built.
00038    If the input book is sequential, a pigeonholing hist is built.
00039    If the book is sparse, a pigeonholing hint is built, possibly in addition
00040      to the threshhold hint 
00041 
00042    command line:
00043    latticehint book.vqh [threshlist]
00044 
00045    latticehint produces book.vqh on stdout */
00046 
00047 static int longsort(const void *a, const void *b){
00048   return(**((long **)a)-**((long **)b));
00049 }
00050 
00051 static int addtosearch(int entry,long **tempstack,long *tempcount,int add){
00052   long *ptr=tempstack[entry];
00053   long i=tempcount[entry];
00054 
00055   if(ptr){
00056     while(i--)
00057       if(*ptr++==add)return(0);
00058     tempstack[entry]=_ogg_realloc(tempstack[entry],
00059                              (tempcount[entry]+1)*sizeof(long));
00060   }else{
00061     tempstack[entry]=_ogg_malloc(sizeof(long));
00062   }
00063 
00064   tempstack[entry][tempcount[entry]++]=add;
00065   return(1);
00066 }
00067 
00068 static void setvals(int dim,encode_aux_pigeonhole *p,
00069                     long *temptrack,float *tempmin,float *tempmax,
00070                     int seqp){
00071   int i;
00072   float last=0.f;
00073   for(i=0;i<dim;i++){
00074     tempmin[i]=(temptrack[i])*p->del+p->min+last;
00075     tempmax[i]=tempmin[i]+p->del;
00076     if(seqp)last=tempmin[i];
00077   }
00078 }
00079 
00080 /* note that things are currently set up such that input fits that
00081    quantize outside the pigeonmap are dropped and brute-forced.  So we
00082    can ignore the <0 and >=n boundary cases in min/max error */
00083 
00084 static float minerror(int dim,float *a,encode_aux_pigeonhole *p,
00085                        long *temptrack,float *tempmin,float *tempmax){
00086   int i;
00087   float err=0.f;
00088   for(i=0;i<dim;i++){
00089     float eval=0.f;
00090     if(a[i]<tempmin[i]){
00091       eval=tempmin[i]-a[i];
00092     }else if(a[i]>tempmax[i]){
00093       eval=a[i]-tempmax[i];
00094     }
00095     err+=eval*eval;
00096   }
00097   return(err);
00098 }
00099 
00100 static float maxerror(int dim,float *a,encode_aux_pigeonhole *p,
00101                        long *temptrack,float *tempmin,float *tempmax){
00102   int i;
00103   float err=0.f,eval;
00104   for(i=0;i<dim;i++){
00105     if(a[i]<tempmin[i]){
00106       eval=tempmax[i]-a[i];
00107     }else if(a[i]>tempmax[i]){
00108       eval=a[i]-tempmin[i];
00109     }else{
00110       float t1=a[i]-tempmin[i];
00111       eval=tempmax[i]-a[i];
00112       if(t1>eval)eval=t1;
00113     }
00114     err+=eval*eval;
00115   }
00116   return(err);
00117 }
00118 
00119 int main(int argc,char *argv[]){
00120   codebook *b;
00121   static_codebook *c;
00122   int entries=-1,dim=-1;
00123   float min,del;
00124   char *name;
00125   long i,j;
00126   float *suggestions;
00127   int suggcount=0;
00128 
00129   if(argv[1]==NULL){
00130     fprintf(stderr,"Need a lattice book on the command line.\n");
00131     exit(1);
00132   }
00133 
00134   {
00135     char *ptr;
00136     char *filename=strdup(argv[1]);
00137 
00138     b=codebook_load(filename);
00139     c=(static_codebook *)(b->c);
00140     
00141     ptr=strrchr(filename,'.');
00142     if(ptr){
00143       *ptr='\0';
00144       name=strdup(filename);
00145     }else{
00146       name=strdup(filename);
00147     }
00148   }
00149 
00150   if(c->maptype!=1){
00151     fprintf(stderr,"Provided book is not a latticebook.\n");
00152     exit(1);
00153   }
00154 
00155   entries=b->entries;
00156   dim=b->dim;
00157   min=_float32_unpack(c->q_min);
00158   del=_float32_unpack(c->q_delta);
00159 
00160   /* Do we want to gen a threshold hint? */
00161   if(c->q_sequencep==0){
00162     /* yes. Discard any preexisting threshhold hint */
00163     long quantvals=_book_maptype1_quantvals(c);
00164     long **quantsort=alloca(quantvals*sizeof(long *));
00165     encode_aux_threshmatch *t=_ogg_calloc(1,sizeof(encode_aux_threshmatch));
00166     c->thresh_tree=t;
00167 
00168     fprintf(stderr,"Adding threshold hint to %s...\n",name);
00169 
00170     /* partial/complete suggestions */
00171     if(argv[2]){
00172       char *ptr=strdup(argv[2]);
00173       suggestions=alloca(sizeof(float)*quantvals);
00174                          
00175       for(suggcount=0;ptr && suggcount<quantvals;suggcount++){
00176         char *ptr2=strchr(ptr,',');
00177         if(ptr2)*ptr2++='\0';
00178         suggestions[suggcount]=atof(ptr);
00179         ptr=ptr2;
00180       }
00181     }
00182 
00183     /* simplest possible threshold hint only */
00184     t->quantthresh=_ogg_calloc(quantvals-1,sizeof(float));
00185     t->quantmap=_ogg_calloc(quantvals,sizeof(int));
00186     t->threshvals=quantvals;
00187     t->quantvals=quantvals;
00188 
00189     /* the quantvals may not be in order; sort em first */
00190     for(i=0;i<quantvals;i++)quantsort[i]=c->quantlist+i;
00191     qsort(quantsort,quantvals,sizeof(long *),longsort);
00192 
00193     /* ok, gen the map and thresholds */
00194     for(i=0;i<quantvals;i++)t->quantmap[i]=quantsort[i]-c->quantlist;
00195     for(i=0;i<quantvals-1;i++){
00196       float v1=*(quantsort[i])*del+min;
00197       float v2=*(quantsort[i+1])*del+min;
00198       
00199       for(j=0;j<suggcount;j++)
00200         if(v1<suggestions[j] && suggestions[j]<v2){
00201           t->quantthresh[i]=suggestions[j];
00202           break;
00203         }
00204       
00205       if(j==suggcount){
00206         t->quantthresh[i]=(v1+v2)*.5;
00207       }
00208     }
00209   }
00210 
00211   /* Do we want to gen a pigeonhole hint? */
00212 #if 0
00213   for(i=0;i<entries;i++)if(c->lengthlist[i]==0)break;
00214   if(c->q_sequencep || i<entries){
00215     long **tempstack;
00216     long *tempcount;
00217     long *temptrack;
00218     float *tempmin;
00219     float *tempmax;
00220     long totalstack=0;
00221     long pigeons;
00222     long subpigeons;
00223     long quantvals=_book_maptype1_quantvals(c);
00224     int changep=1,factor;
00225 
00226     encode_aux_pigeonhole *p=_ogg_calloc(1,sizeof(encode_aux_pigeonhole));
00227     c->pigeon_tree=p;
00228 
00229     fprintf(stderr,"Adding pigeonhole hint to %s...\n",name);
00230     
00231     /* the idea is that we quantize uniformly, even in a nonuniform
00232        lattice, so that quantization of one scalar has a predictable
00233        result on the next sequential scalar in a greedy matching
00234        algorithm.  We generate a lookup based on the quantization of
00235        the vector (pigeonmap groups quantized entries together) and
00236        list the entries that could possible be the best fit for any
00237        given member of that pigeonhole.  The encode process then has a
00238        much smaller list to brute force */
00239 
00240     /* find our pigeonhole-specific quantization values, fill in the
00241        quant value->pigeonhole map */
00242     factor=3;
00243     p->del=del;
00244     p->min=min;
00245     p->quantvals=quantvals;
00246     {
00247       int max=0;
00248       for(i=0;i<quantvals;i++)if(max<c->quantlist[i])max=c->quantlist[i];
00249       p->mapentries=max;
00250     }
00251     p->pigeonmap=_ogg_malloc(p->mapentries*sizeof(long));
00252     p->quantvals=(quantvals+factor-1)/factor;
00253 
00254     /* pigeonhole roughly on the boundaries of the quantvals; the
00255        exact pigeonhole grouping is an optimization issue, not a
00256        correctness issue */
00257     for(i=0;i<p->mapentries;i++){
00258       float thisval=del*i+min; /* middle of the quant zone */
00259       int quant=0;
00260       float err=fabs(c->quantlist[0]*del+min-thisval);
00261       for(j=1;j<quantvals;j++){
00262         float thiserr=fabs(c->quantlist[j]*del+min-thisval);
00263         if(thiserr<err){
00264           quant=j/factor;
00265           err=thiserr;
00266         }
00267       }
00268       p->pigeonmap[i]=quant;
00269     }
00270     
00271     /* pigeonmap complete.  Now do the grungy business of finding the
00272     entries that could possibly be the best fit for a value appearing
00273     in the pigeonhole. The trick that allows the below to work is the
00274     uniform quantization; even though the scalars may be 'sequential'
00275     (each a delta from the last), the uniform quantization means that
00276     the error variance is *not* dependant.  Given a pigeonhole and an
00277     entry, we can find the minimum and maximum possible errors
00278     (relative to the entry) for any point that could appear in the
00279     pigeonhole */
00280     
00281     /* must iterate over both pigeonholes and entries */
00282     /* temporarily (in order to avoid thinking hard), we grow each
00283        pigeonhole seperately, the build a stack of 'em later */
00284     pigeons=1;
00285     subpigeons=1;
00286     for(i=0;i<dim;i++)subpigeons*=p->mapentries;
00287     for(i=0;i<dim;i++)pigeons*=p->quantvals;
00288     temptrack=_ogg_calloc(dim,sizeof(long));
00289     tempmin=_ogg_calloc(dim,sizeof(float));
00290     tempmax=_ogg_calloc(dim,sizeof(float));
00291     tempstack=_ogg_calloc(pigeons,sizeof(long *));
00292     tempcount=_ogg_calloc(pigeons,sizeof(long));
00293 
00294     while(1){
00295       float errorpost=-1;
00296       char buffer[80];
00297 
00298       /* map our current pigeonhole to a 'big pigeonhole' so we know
00299          what list we're after */
00300       int entry=0;
00301       for(i=dim-1;i>=0;i--)entry=entry*p->quantvals+p->pigeonmap[temptrack[i]];
00302       setvals(dim,p,temptrack,tempmin,tempmax,c->q_sequencep);
00303       sprintf(buffer,"Building pigeonhole search list [%ld]...",totalstack);
00304 
00305 
00306       /* Search all entries to find the one with the minimum possible
00307          maximum error.  Record that error */
00308       for(i=0;i<entries;i++){
00309         if(c->lengthlist[i]>0){
00310           float this=maxerror(dim,b->valuelist+i*dim,p,
00311                                temptrack,tempmin,tempmax);
00312           if(errorpost==-1 || this<errorpost)errorpost=this;
00313           spinnit(buffer,subpigeons);
00314         }
00315       }
00316 
00317       /* Our search list will contain all entries with a minimum
00318          possible error <= our errorpost */
00319       for(i=0;i<entries;i++)
00320         if(c->lengthlist[i]>0){
00321           spinnit(buffer,subpigeons);
00322           if(minerror(dim,b->valuelist+i*dim,p,
00323                       temptrack,tempmin,tempmax)<errorpost)
00324             totalstack+=addtosearch(entry,tempstack,tempcount,i);
00325         }
00326 
00327       for(i=0;i<dim;i++){
00328         temptrack[i]++;
00329         if(temptrack[i]<p->mapentries)break;
00330         temptrack[i]=0;
00331       }
00332       if(i==dim)break;
00333       subpigeons--;
00334     }
00335 
00336     fprintf(stderr,"\r                                                     "
00337             "\rTotal search list size (all entries): %ld\n",totalstack);
00338 
00339     /* pare the index of lists for improbable quantizations (where
00340        improbable is determined by c->lengthlist; we assume that
00341        pigeonholing is in sync with the codeword cells, which it is */
00342     /*for(i=0;i<entries;i++){
00343       float probability= 1.f/(1<<c->lengthlist[i]);
00344       if(c->lengthlist[i]==0 || probability*entries<cutoff){
00345         totalstack-=tempcount[i];
00346         tempcount[i]=0;
00347       }
00348       }*/
00349 
00350     /* pare the list of shortlists; merge contained and similar lists
00351        together */
00352     p->fitmap=_ogg_malloc(pigeons*sizeof(long));
00353     for(i=0;i<pigeons;i++)p->fitmap[i]=-1;
00354     while(changep){
00355       char buffer[80];
00356       changep=0;
00357 
00358       for(i=0;i<pigeons;i++){
00359         if(p->fitmap[i]<0 && tempcount[i]){
00360           for(j=i+1;j<pigeons;j++){
00361             if(p->fitmap[j]<0 && tempcount[j]){
00362               /* is one list a superset, or are they sufficiently similar? */
00363               int amiss=0,bmiss=0,ii,jj;
00364               for(ii=0;ii<tempcount[i];ii++){
00365                 for(jj=0;jj<tempcount[j];jj++)
00366                   if(tempstack[i][ii]==tempstack[j][jj])break;
00367                 if(jj==tempcount[j])amiss++;
00368               }
00369               for(jj=0;jj<tempcount[j];jj++){
00370                 for(ii=0;ii<tempcount[i];ii++)
00371                   if(tempstack[i][ii]==tempstack[j][jj])break;
00372                 if(ii==tempcount[i])bmiss++;
00373               }
00374               if(amiss==0 ||
00375                  bmiss==0 ||
00376                  (amiss*2<tempcount[i] && bmiss*2<tempcount[j] &&
00377                  tempcount[i]+bmiss<entries/30)){
00378 
00379                 /*superset/similar  Add all of one to the other. */
00380                 for(jj=0;jj<tempcount[j];jj++)
00381                   totalstack+=addtosearch(i,tempstack,tempcount,
00382                                           tempstack[j][jj]);
00383                 totalstack-=tempcount[j];
00384                 p->fitmap[j]=i;
00385                 changep=1;
00386               }
00387             }
00388           }
00389           sprintf(buffer,"Consolidating [%ld total, %s]... ",totalstack,
00390                   changep?"reit":"nochange");
00391           spinnit(buffer,pigeons-i);
00392         }
00393       }
00394     }
00395 
00396     /* repack the temp stack in final form */
00397     fprintf(stderr,"\r                                                       "
00398             "\rFinal total list size: %ld\n",totalstack);
00399     
00400 
00401     p->fittotal=totalstack;
00402     p->fitlist=_ogg_malloc((totalstack+1)*sizeof(long));
00403     p->fitlength=_ogg_malloc(pigeons*sizeof(long));
00404     {
00405       long usage=0;
00406       for(i=0;i<pigeons;i++){
00407         if(p->fitmap[i]==-1){
00408           if(tempcount[i])
00409             memcpy(p->fitlist+usage,tempstack[i],tempcount[i]*sizeof(long));
00410           p->fitmap[i]=usage;
00411           p->fitlength[i]=tempcount[i];
00412           usage+=tempcount[i];
00413           if(usage>totalstack){
00414             fprintf(stderr,"Internal error; usage>totalstack\n");
00415             exit(1);
00416           }
00417         }else{
00418           p->fitlength[i]=p->fitlength[p->fitmap[i]];
00419           p->fitmap[i]=p->fitmap[p->fitmap[i]];
00420         }
00421       }
00422     }
00423   }
00424 #endif
00425 
00426   write_codebook(stdout,name,c); 
00427   fprintf(stderr,"\r                                                     "
00428           "\nDone.\n");
00429   exit(0);
00430 }

Generated by  doxygen 1.6.2