examples/SFExamples/oggvorbiscodec94/src/libvorbis/vq/vqgen.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: train a VQ codebook 
00014  last mod: $Id: vqgen.c 7187 2004-07-20 07:24:27Z xiphmont $
00015 
00016  ********************************************************************/
00017 
00018 /* This code is *not* part of libvorbis.  It is used to generate
00019    trained codebooks offline and then spit the results into a
00020    pregenerated codebook that is compiled into libvorbis.  It is an
00021    expensive (but good) algorithm.  Run it on big iron. */
00022 
00023 /* There are so many optimizations to explore in *both* stages that
00024    considering the undertaking is almost withering.  For now, we brute
00025    force it all */
00026 
00027 #include <stdlib.h>
00028 #include <stdio.h>
00029 #include <math.h>
00030 #include <string.h>
00031 
00032 #include "vqgen.h"
00033 #include "bookutil.h"
00034 
00035 /* Codebook generation happens in two steps: 
00036 
00037    1) Train the codebook with data collected from the encoder: We use
00038    one of a few error metrics (which represent the distance between a
00039    given data point and a candidate point in the training set) to
00040    divide the training set up into cells representing roughly equal
00041    probability of occurring. 
00042 
00043    2) Generate the codebook and auxiliary data from the trained data set
00044 */
00045 
00046 /* Codebook training ****************************************************
00047  *
00048  * The basic idea here is that a VQ codebook is like an m-dimensional
00049  * foam with n bubbles.  The bubbles compete for space/volume and are
00050  * 'pressurized' [biased] according to some metric.  The basic alg
00051  * iterates through allowing the bubbles to compete for space until
00052  * they converge (if the damping is dome properly) on a steady-state
00053  * solution. Individual input points, collected from libvorbis, are
00054  * used to train the algorithm monte-carlo style.  */
00055 
00056 /* internal helpers *****************************************************/
00057 #define vN(data,i) (data+v->elements*i)
00058 
00059 /* default metric; squared 'distance' from desired value. */
00060 float _dist(vqgen *v,float *a, float *b){
00061   int i;
00062   int el=v->elements;
00063   float acc=0.f;
00064   for(i=0;i<el;i++){
00065     float val=(a[i]-b[i]);
00066     acc+=val*val;
00067   }
00068   return sqrt(acc);
00069 }
00070 
00071 float *_weight_null(vqgen *v,float *a){
00072   return a;
00073 }
00074 
00075 /* *must* be beefed up. */
00076 void _vqgen_seed(vqgen *v){
00077   long i;
00078   for(i=0;i<v->entries;i++)
00079     memcpy(_now(v,i),_point(v,i),sizeof(float)*v->elements);
00080   v->seeded=1;
00081 }
00082 
00083 int directdsort(const void *a, const void *b){
00084   float av=*((float *)a);
00085   float bv=*((float *)b);
00086   return (av<bv)-(av>bv);
00087 }
00088 
00089 void vqgen_cellmetric(vqgen *v){
00090   int j,k;
00091   float min=-1.f,max=-1.f,mean=0.f,acc=0.f;
00092   long dup=0,unused=0;
00093  #ifdef NOISY
00094   int i;
00095    char buff[80];
00096    float spacings[v->entries];
00097    int count=0;
00098    FILE *cells;
00099    sprintf(buff,"cellspace%d.m",v->it);
00100    cells=fopen(buff,"w");
00101 #endif
00102 
00103   /* minimum, maximum, cell spacing */
00104   for(j=0;j<v->entries;j++){
00105     float localmin=-1.;
00106 
00107     for(k=0;k<v->entries;k++){
00108       if(j!=k){
00109         float this=_dist(v,_now(v,j),_now(v,k));
00110         if(this>0){
00111           if(v->assigned[k] && (localmin==-1 || this<localmin))
00112             localmin=this;
00113         }else{  
00114           if(k<j){
00115             dup++;
00116             break;
00117           }
00118         }
00119       }
00120     }
00121     if(k<v->entries)continue;
00122 
00123     if(v->assigned[j]==0){
00124       unused++;
00125       continue;
00126     }
00127     
00128     localmin=v->max[j]+localmin/2; /* this gives us rough diameter */
00129     if(min==-1 || localmin<min)min=localmin;
00130     if(max==-1 || localmin>max)max=localmin;
00131     mean+=localmin;
00132     acc++;
00133 #ifdef NOISY
00134     spacings[count++]=localmin;
00135 #endif
00136   }
00137 
00138   fprintf(stderr,"cell diameter: %.03g::%.03g::%.03g (%ld unused/%ld dup)\n",
00139           min,mean/acc,max,unused,dup);
00140 
00141 #ifdef NOISY
00142   qsort(spacings,count,sizeof(float),directdsort);
00143   for(i=0;i<count;i++)
00144     fprintf(cells,"%g\n",spacings[i]);
00145   fclose(cells);
00146 #endif      
00147 
00148 }
00149 
00150 /* External calls *******************************************************/
00151 
00152 /* We have two forms of quantization; in the first, each vector
00153    element in the codebook entry is orthogonal.  Residues would use this
00154    quantization for example.
00155 
00156    In the second, we have a sequence of monotonically increasing
00157    values that we wish to quantize as deltas (to save space).  We
00158    still need to quantize so that absolute values are accurate. For
00159    example, LSP quantizes all absolute values, but the book encodes
00160    distance between values because each successive value is larger
00161    than the preceeding value.  Thus the desired quantibits apply to
00162    the encoded (delta) values, not abs positions. This requires minor
00163    additional encode-side trickery. */
00164 
00165 void vqgen_quantize(vqgen *v,quant_meta *q){
00166 
00167   float maxdel;
00168   float mindel;
00169 
00170   float delta;
00171   float maxquant=((1<<q->quant)-1);
00172 
00173   int j,k;
00174 
00175   mindel=maxdel=_now(v,0)[0];
00176   
00177   for(j=0;j<v->entries;j++){
00178     float last=0.f;
00179     for(k=0;k<v->elements;k++){
00180       if(mindel>_now(v,j)[k]-last)mindel=_now(v,j)[k]-last;
00181       if(maxdel<_now(v,j)[k]-last)maxdel=_now(v,j)[k]-last;
00182       if(q->sequencep)last=_now(v,j)[k];
00183     }
00184   }
00185 
00186 
00187   /* first find the basic delta amount from the maximum span to be
00188      encoded.  Loosen the delta slightly to allow for additional error
00189      during sequence quantization */
00190 
00191   delta=(maxdel-mindel)/((1<<q->quant)-1.5f);
00192 
00193   q->min=_float32_pack(mindel);
00194   q->delta=_float32_pack(delta);
00195 
00196   mindel=_float32_unpack(q->min);
00197   delta=_float32_unpack(q->delta);
00198 
00199   for(j=0;j<v->entries;j++){
00200     float last=0;
00201     for(k=0;k<v->elements;k++){
00202       float val=_now(v,j)[k];
00203       float now=rint((val-last-mindel)/delta);
00204       
00205       _now(v,j)[k]=now;
00206       if(now<0){
00207         /* be paranoid; this should be impossible */
00208         fprintf(stderr,"fault; quantized value<0\n");
00209         exit(1);
00210       }
00211 
00212       if(now>maxquant){
00213         /* be paranoid; this should be impossible */
00214         fprintf(stderr,"fault; quantized value>max\n");
00215         exit(1);
00216       }
00217       if(q->sequencep)last=(now*delta)+mindel+last;
00218     }
00219   }
00220 }
00221 
00222 /* much easier :-).  Unlike in the codebook, we don't un-log log
00223    scales; we just make sure they're properly offset. */
00224 void vqgen_unquantize(vqgen *v,quant_meta *q){
00225   long j,k;
00226   float mindel=_float32_unpack(q->min);
00227   float delta=_float32_unpack(q->delta);
00228 
00229   for(j=0;j<v->entries;j++){
00230     float last=0.f;
00231     for(k=0;k<v->elements;k++){
00232       float now=_now(v,j)[k];
00233       now=fabs(now)*delta+last+mindel;
00234       if(q->sequencep)last=now;
00235       _now(v,j)[k]=now;
00236     }
00237   }
00238 }
00239 
00240 void vqgen_init(vqgen *v,int elements,int aux,int entries,float mindist,
00241                 float  (*metric)(vqgen *,float *, float *),
00242                 float *(*weight)(vqgen *,float *),int centroid){
00243   memset(v,0,sizeof(vqgen));
00244 
00245   v->centroid=centroid;
00246   v->elements=elements;
00247   v->aux=aux;
00248   v->mindist=mindist;
00249   v->allocated=32768;
00250   v->pointlist=_ogg_malloc(v->allocated*(v->elements+v->aux)*sizeof(float));
00251 
00252   v->entries=entries;
00253   v->entrylist=_ogg_malloc(v->entries*v->elements*sizeof(float));
00254   v->assigned=_ogg_malloc(v->entries*sizeof(long));
00255   v->bias=_ogg_calloc(v->entries,sizeof(float));
00256   v->max=_ogg_calloc(v->entries,sizeof(float));
00257   if(metric)
00258     v->metric_func=metric;
00259   else
00260     v->metric_func=_dist;
00261   if(weight)
00262     v->weight_func=weight;
00263   else
00264     v->weight_func=_weight_null;
00265 
00266   v->asciipoints=tmpfile();
00267 
00268 }
00269 
00270 void vqgen_addpoint(vqgen *v, float *p,float *a){
00271   int k;
00272   for(k=0;k<v->elements;k++)
00273     fprintf(v->asciipoints,"%.12g\n",p[k]);
00274   for(k=0;k<v->aux;k++)
00275     fprintf(v->asciipoints,"%.12g\n",a[k]);
00276 
00277   if(v->points>=v->allocated){
00278     v->allocated*=2;
00279     v->pointlist=_ogg_realloc(v->pointlist,v->allocated*(v->elements+v->aux)*
00280                          sizeof(float));
00281   }
00282 
00283   memcpy(_point(v,v->points),p,sizeof(float)*v->elements);
00284   if(v->aux)memcpy(_point(v,v->points)+v->elements,a,sizeof(float)*v->aux);
00285  
00286   /* quantize to the density mesh if it's selected */
00287   if(v->mindist>0.f){
00288     /* quantize to the mesh */
00289     for(k=0;k<v->elements+v->aux;k++)
00290       _point(v,v->points)[k]=
00291         rint(_point(v,v->points)[k]/v->mindist)*v->mindist;
00292   }
00293   v->points++;
00294   if(!(v->points&0xff))spinnit("loading... ",v->points);
00295 }
00296 
00297 /* yes, not threadsafe.  These utils aren't */
00298 static int sortit=0;
00299 static int sortsize=0;
00300 static int meshcomp(const void *a,const void *b){
00301   if(((sortit++)&0xfff)==0)spinnit("sorting mesh...",sortit);
00302   return(memcmp(a,b,sortsize));
00303 }
00304 
00305 void vqgen_sortmesh(vqgen *v){
00306   sortit=0;
00307   if(v->mindist>0.f){
00308     long i,march=1;
00309 
00310     /* sort to make uniqueness detection trivial */
00311     sortsize=(v->elements+v->aux)*sizeof(float);
00312     qsort(v->pointlist,v->points,sortsize,meshcomp);
00313 
00314     /* now march through and eliminate dupes */
00315     for(i=1;i<v->points;i++){
00316       if(memcmp(_point(v,i),_point(v,i-1),sortsize)){
00317         /* a new, unique entry.  march it down */
00318         if(i>march)memcpy(_point(v,march),_point(v,i),sortsize);
00319         march++;
00320       }
00321       spinnit("eliminating density... ",v->points-i);
00322     }
00323 
00324     /* we're done */
00325     fprintf(stderr,"\r%ld training points remining out of %ld"
00326             " after density mesh (%ld%%)\n",march,v->points,march*100/v->points);
00327     v->points=march;
00328 
00329   }
00330   v->sorted=1;
00331 }
00332 
00333 float vqgen_iterate(vqgen *v,int biasp){
00334   long   i,j,k;
00335 
00336   float fdesired;
00337   long  desired;
00338   long  desired2;
00339 
00340   float asserror=0.f;
00341   float meterror=0.f;
00342   float *new;
00343   float *new2;
00344   long   *nearcount;
00345   float *nearbias;
00346  #ifdef NOISY
00347    char buff[80];
00348    FILE *assig;
00349    FILE *bias;
00350    FILE *cells;
00351    sprintf(buff,"cells%d.m",v->it);
00352    cells=fopen(buff,"w");
00353    sprintf(buff,"assig%d.m",v->it);
00354    assig=fopen(buff,"w");
00355    sprintf(buff,"bias%d.m",v->it);
00356    bias=fopen(buff,"w");
00357  #endif
00358  
00359 
00360   if(v->entries<2){
00361     fprintf(stderr,"generation requires at least two entries\n");
00362     exit(1);
00363   }
00364 
00365   if(!v->sorted)vqgen_sortmesh(v);
00366   if(!v->seeded)_vqgen_seed(v);
00367 
00368   fdesired=(float)v->points/v->entries;
00369   desired=fdesired;
00370   desired2=desired*2;
00371   new=_ogg_malloc(sizeof(float)*v->entries*v->elements);
00372   new2=_ogg_malloc(sizeof(float)*v->entries*v->elements);
00373   nearcount=_ogg_malloc(v->entries*sizeof(long));
00374   nearbias=_ogg_malloc(v->entries*desired2*sizeof(float));
00375 
00376   /* fill in nearest points for entry biasing */
00377   /*memset(v->bias,0,sizeof(float)*v->entries);*/
00378   memset(nearcount,0,sizeof(long)*v->entries);
00379   memset(v->assigned,0,sizeof(long)*v->entries);
00380   if(biasp){
00381     for(i=0;i<v->points;i++){
00382       float *ppt=v->weight_func(v,_point(v,i));
00383       float firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
00384       float secondmetric=v->metric_func(v,_now(v,1),ppt)+v->bias[1];
00385       long   firstentry=0;
00386       long   secondentry=1;
00387       
00388       if(!(i&0xff))spinnit("biasing... ",v->points+v->points+v->entries-i);
00389       
00390       if(firstmetric>secondmetric){
00391         float temp=firstmetric;
00392         firstmetric=secondmetric;
00393         secondmetric=temp;
00394         firstentry=1;
00395         secondentry=0;
00396       }
00397       
00398       for(j=2;j<v->entries;j++){
00399         float thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
00400         if(thismetric<secondmetric){
00401           if(thismetric<firstmetric){
00402             secondmetric=firstmetric;
00403             secondentry=firstentry;
00404             firstmetric=thismetric;
00405             firstentry=j;
00406           }else{
00407             secondmetric=thismetric;
00408             secondentry=j;
00409           }
00410         }
00411       }
00412       
00413       j=firstentry;
00414       for(j=0;j<v->entries;j++){
00415         
00416         float thismetric,localmetric;
00417         float *nearbiasptr=nearbias+desired2*j;
00418         long k=nearcount[j];
00419         
00420         localmetric=v->metric_func(v,_now(v,j),ppt);
00421         /* 'thismetric' is to be the bias value necessary in the current
00422            arrangement for entry j to capture point i */
00423         if(firstentry==j){
00424           /* use the secondary entry as the threshhold */
00425           thismetric=secondmetric-localmetric;
00426         }else{
00427           /* use the primary entry as the threshhold */
00428           thismetric=firstmetric-localmetric;
00429         }
00430         
00431         /* support the idea of 'minimum distance'... if we want the
00432            cells in a codebook to be roughly some minimum size (as with
00433            the low resolution residue books) */
00434         
00435         /* a cute two-stage delayed sorting hack */
00436         if(k<desired){
00437           nearbiasptr[k]=thismetric;
00438           k++;
00439           if(k==desired){
00440             spinnit("biasing... ",v->points+v->points+v->entries-i);
00441             qsort(nearbiasptr,desired,sizeof(float),directdsort);
00442           }
00443           
00444         }else if(thismetric>nearbiasptr[desired-1]){
00445           nearbiasptr[k]=thismetric;
00446           k++;
00447           if(k==desired2){
00448             spinnit("biasing... ",v->points+v->points+v->entries-i);
00449             qsort(nearbiasptr,desired2,sizeof(float),directdsort);
00450             k=desired;
00451           }
00452         }
00453         nearcount[j]=k;
00454       }
00455     }
00456     
00457     /* inflate/deflate */
00458     
00459     for(i=0;i<v->entries;i++){
00460       float *nearbiasptr=nearbias+desired2*i;
00461       
00462       spinnit("biasing... ",v->points+v->entries-i);
00463       
00464       /* due to the delayed sorting, we likely need to finish it off....*/
00465       if(nearcount[i]>desired)
00466         qsort(nearbiasptr,nearcount[i],sizeof(float),directdsort);
00467 
00468       v->bias[i]=nearbiasptr[desired-1];
00469 
00470     }
00471   }else{ 
00472     memset(v->bias,0,v->entries*sizeof(float));
00473   }
00474 
00475   /* Now assign with new bias and find new midpoints */
00476   for(i=0;i<v->points;i++){
00477     float *ppt=v->weight_func(v,_point(v,i));
00478     float firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
00479     long   firstentry=0;
00480 
00481     if(!(i&0xff))spinnit("centering... ",v->points-i);
00482 
00483     for(j=0;j<v->entries;j++){
00484       float thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
00485       if(thismetric<firstmetric){
00486         firstmetric=thismetric;
00487         firstentry=j;
00488       }
00489     }
00490 
00491     j=firstentry;
00492       
00493 #ifdef NOISY
00494     fprintf(cells,"%g %g\n%g %g\n\n",
00495           _now(v,j)[0],_now(v,j)[1],
00496           ppt[0],ppt[1]);
00497 #endif
00498 
00499     firstmetric-=v->bias[j];
00500     meterror+=firstmetric;
00501 
00502     if(v->centroid==0){
00503       /* set up midpoints for next iter */
00504       if(v->assigned[j]++){
00505         for(k=0;k<v->elements;k++)
00506           vN(new,j)[k]+=ppt[k];
00507         if(firstmetric>v->max[j])v->max[j]=firstmetric;
00508       }else{
00509         for(k=0;k<v->elements;k++)
00510           vN(new,j)[k]=ppt[k];
00511         v->max[j]=firstmetric;
00512       }
00513     }else{
00514       /* centroid */
00515       if(v->assigned[j]++){
00516         for(k=0;k<v->elements;k++){
00517           if(vN(new,j)[k]>ppt[k])vN(new,j)[k]=ppt[k];
00518           if(vN(new2,j)[k]<ppt[k])vN(new2,j)[k]=ppt[k];
00519         }
00520         if(firstmetric>v->max[firstentry])v->max[j]=firstmetric;
00521       }else{
00522         for(k=0;k<v->elements;k++){
00523           vN(new,j)[k]=ppt[k];
00524           vN(new2,j)[k]=ppt[k];
00525         }
00526         v->max[firstentry]=firstmetric;
00527       }
00528     }
00529   }
00530 
00531   /* assign midpoints */
00532 
00533   for(j=0;j<v->entries;j++){
00534 #ifdef NOISY
00535     fprintf(assig,"%ld\n",v->assigned[j]);
00536     fprintf(bias,"%g\n",v->bias[j]);
00537 #endif
00538     asserror+=fabs(v->assigned[j]-fdesired);
00539     if(v->assigned[j]){
00540       if(v->centroid==0){
00541         for(k=0;k<v->elements;k++)
00542           _now(v,j)[k]=vN(new,j)[k]/v->assigned[j];
00543       }else{
00544         for(k=0;k<v->elements;k++)
00545           _now(v,j)[k]=(vN(new,j)[k]+vN(new2,j)[k])/2.f;
00546       }
00547     }
00548   }
00549 
00550   asserror/=(v->entries*fdesired);
00551 
00552   fprintf(stderr,"Pass #%d... ",v->it);
00553   fprintf(stderr,": dist %g(%g) metric error=%g \n",
00554           asserror,fdesired,meterror/v->points);
00555   v->it++;
00556   
00557   free(new);
00558   free(nearcount);
00559   free(nearbias);
00560 #ifdef NOISY
00561   fclose(assig);
00562   fclose(bias);
00563   fclose(cells);
00564 #endif
00565   return(asserror);
00566 }
00567 

Generated by  doxygen 1.6.2