Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/libvorbis-1.2.0/vq/vqgen.c @ 28

Last change on this file since 28 was 16, checked in by landauf, 17 years ago

added libvorbis

File size: 15.0 KB
Line 
1/********************************************************************
2 *                                                                  *
3 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
7 *                                                                  *
8 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
9 * by the Xiph.Org Foundation http://www.xiph.org/                  *
10 *                                                                  *
11 ********************************************************************
12
13 function: train a VQ codebook
14 last mod: $Id: vqgen.c 13293 2007-07-24 00:09:47Z xiphmont $
15
16 ********************************************************************/
17
18/* This code is *not* part of libvorbis.  It is used to generate
19   trained codebooks offline and then spit the results into a
20   pregenerated codebook that is compiled into libvorbis.  It is an
21   expensive (but good) algorithm.  Run it on big iron. */
22
23/* There are so many optimizations to explore in *both* stages that
24   considering the undertaking is almost withering.  For now, we brute
25   force it all */
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <math.h>
30#include <string.h>
31
32#include "vqgen.h"
33#include "bookutil.h"
34
35/* Codebook generation happens in two steps:
36
37   1) Train the codebook with data collected from the encoder: We use
38   one of a few error metrics (which represent the distance between a
39   given data point and a candidate point in the training set) to
40   divide the training set up into cells representing roughly equal
41   probability of occurring.
42
43   2) Generate the codebook and auxiliary data from the trained data set
44*/
45
46/* Codebook training ****************************************************
47 *
48 * The basic idea here is that a VQ codebook is like an m-dimensional
49 * foam with n bubbles.  The bubbles compete for space/volume and are
50 * 'pressurized' [biased] according to some metric.  The basic alg
51 * iterates through allowing the bubbles to compete for space until
52 * they converge (if the damping is dome properly) on a steady-state
53 * solution. Individual input points, collected from libvorbis, are
54 * used to train the algorithm monte-carlo style.  */
55
56/* internal helpers *****************************************************/
57#define vN(data,i) (data+v->elements*i)
58
59/* default metric; squared 'distance' from desired value. */
60float _dist(vqgen *v,float *a, float *b){
61  int i;
62  int el=v->elements;
63  float acc=0.f;
64  for(i=0;i<el;i++){
65    float val=(a[i]-b[i]);
66    acc+=val*val;
67  }
68  return sqrt(acc);
69}
70
71float *_weight_null(vqgen *v,float *a){
72  return a;
73}
74
75/* *must* be beefed up. */
76void _vqgen_seed(vqgen *v){
77  long i;
78  for(i=0;i<v->entries;i++)
79    memcpy(_now(v,i),_point(v,i),sizeof(float)*v->elements);
80  v->seeded=1;
81}
82
83int directdsort(const void *a, const void *b){
84  float av=*((float *)a);
85  float bv=*((float *)b);
86  return (av<bv)-(av>bv);
87}
88
89void vqgen_cellmetric(vqgen *v){
90  int j,k;
91  float min=-1.f,max=-1.f,mean=0.f,acc=0.f;
92  long dup=0,unused=0;
93 #ifdef NOISY
94  int i;
95   char buff[80];
96   float spacings[v->entries];
97   int count=0;
98   FILE *cells;
99   sprintf(buff,"cellspace%d.m",v->it);
100   cells=fopen(buff,"w");
101#endif
102
103  /* minimum, maximum, cell spacing */
104  for(j=0;j<v->entries;j++){
105    float localmin=-1.;
106
107    for(k=0;k<v->entries;k++){
108      if(j!=k){
109        float this=_dist(v,_now(v,j),_now(v,k));
110        if(this>0){
111          if(v->assigned[k] && (localmin==-1 || this<localmin))
112            localmin=this;
113        }else{ 
114          if(k<j){
115            dup++;
116            break;
117          }
118        }
119      }
120    }
121    if(k<v->entries)continue;
122
123    if(v->assigned[j]==0){
124      unused++;
125      continue;
126    }
127   
128    localmin=v->max[j]+localmin/2; /* this gives us rough diameter */
129    if(min==-1 || localmin<min)min=localmin;
130    if(max==-1 || localmin>max)max=localmin;
131    mean+=localmin;
132    acc++;
133#ifdef NOISY
134    spacings[count++]=localmin;
135#endif
136  }
137
138  fprintf(stderr,"cell diameter: %.03g::%.03g::%.03g (%ld unused/%ld dup)\n",
139          min,mean/acc,max,unused,dup);
140
141#ifdef NOISY
142  qsort(spacings,count,sizeof(float),directdsort);
143  for(i=0;i<count;i++)
144    fprintf(cells,"%g\n",spacings[i]);
145  fclose(cells);
146#endif     
147
148}
149
150/* External calls *******************************************************/
151
152/* We have two forms of quantization; in the first, each vector
153   element in the codebook entry is orthogonal.  Residues would use this
154   quantization for example.
155
156   In the second, we have a sequence of monotonically increasing
157   values that we wish to quantize as deltas (to save space).  We
158   still need to quantize so that absolute values are accurate. For
159   example, LSP quantizes all absolute values, but the book encodes
160   distance between values because each successive value is larger
161   than the preceeding value.  Thus the desired quantibits apply to
162   the encoded (delta) values, not abs positions. This requires minor
163   additional encode-side trickery. */
164
165void vqgen_quantize(vqgen *v,quant_meta *q){
166
167  float maxdel;
168  float mindel;
169
170  float delta;
171  float maxquant=((1<<q->quant)-1);
172
173  int j,k;
174
175  mindel=maxdel=_now(v,0)[0];
176 
177  for(j=0;j<v->entries;j++){
178    float last=0.f;
179    for(k=0;k<v->elements;k++){
180      if(mindel>_now(v,j)[k]-last)mindel=_now(v,j)[k]-last;
181      if(maxdel<_now(v,j)[k]-last)maxdel=_now(v,j)[k]-last;
182      if(q->sequencep)last=_now(v,j)[k];
183    }
184  }
185
186
187  /* first find the basic delta amount from the maximum span to be
188     encoded.  Loosen the delta slightly to allow for additional error
189     during sequence quantization */
190
191  delta=(maxdel-mindel)/((1<<q->quant)-1.5f);
192
193  q->min=_float32_pack(mindel);
194  q->delta=_float32_pack(delta);
195
196  mindel=_float32_unpack(q->min);
197  delta=_float32_unpack(q->delta);
198
199  for(j=0;j<v->entries;j++){
200    float last=0;
201    for(k=0;k<v->elements;k++){
202      float val=_now(v,j)[k];
203      float now=rint((val-last-mindel)/delta);
204     
205      _now(v,j)[k]=now;
206      if(now<0){
207        /* be paranoid; this should be impossible */
208        fprintf(stderr,"fault; quantized value<0\n");
209        exit(1);
210      }
211
212      if(now>maxquant){
213        /* be paranoid; this should be impossible */
214        fprintf(stderr,"fault; quantized value>max\n");
215        exit(1);
216      }
217      if(q->sequencep)last=(now*delta)+mindel+last;
218    }
219  }
220}
221
222/* much easier :-).  Unlike in the codebook, we don't un-log log
223   scales; we just make sure they're properly offset. */
224void vqgen_unquantize(vqgen *v,quant_meta *q){
225  long j,k;
226  float mindel=_float32_unpack(q->min);
227  float delta=_float32_unpack(q->delta);
228
229  for(j=0;j<v->entries;j++){
230    float last=0.f;
231    for(k=0;k<v->elements;k++){
232      float now=_now(v,j)[k];
233      now=fabs(now)*delta+last+mindel;
234      if(q->sequencep)last=now;
235      _now(v,j)[k]=now;
236    }
237  }
238}
239
240void vqgen_init(vqgen *v,int elements,int aux,int entries,float mindist,
241                float  (*metric)(vqgen *,float *, float *),
242                float *(*weight)(vqgen *,float *),int centroid){
243  memset(v,0,sizeof(vqgen));
244
245  v->centroid=centroid;
246  v->elements=elements;
247  v->aux=aux;
248  v->mindist=mindist;
249  v->allocated=32768;
250  v->pointlist=_ogg_malloc(v->allocated*(v->elements+v->aux)*sizeof(float));
251
252  v->entries=entries;
253  v->entrylist=_ogg_malloc(v->entries*v->elements*sizeof(float));
254  v->assigned=_ogg_malloc(v->entries*sizeof(long));
255  v->bias=_ogg_calloc(v->entries,sizeof(float));
256  v->max=_ogg_calloc(v->entries,sizeof(float));
257  if(metric)
258    v->metric_func=metric;
259  else
260    v->metric_func=_dist;
261  if(weight)
262    v->weight_func=weight;
263  else
264    v->weight_func=_weight_null;
265
266  v->asciipoints=tmpfile();
267
268}
269
270void vqgen_addpoint(vqgen *v, float *p,float *a){
271  int k;
272  for(k=0;k<v->elements;k++)
273    fprintf(v->asciipoints,"%.12g\n",p[k]);
274  for(k=0;k<v->aux;k++)
275    fprintf(v->asciipoints,"%.12g\n",a[k]);
276
277  if(v->points>=v->allocated){
278    v->allocated*=2;
279    v->pointlist=_ogg_realloc(v->pointlist,v->allocated*(v->elements+v->aux)*
280                         sizeof(float));
281  }
282
283  memcpy(_point(v,v->points),p,sizeof(float)*v->elements);
284  if(v->aux)memcpy(_point(v,v->points)+v->elements,a,sizeof(float)*v->aux);
285 
286  /* quantize to the density mesh if it's selected */
287  if(v->mindist>0.f){
288    /* quantize to the mesh */
289    for(k=0;k<v->elements+v->aux;k++)
290      _point(v,v->points)[k]=
291        rint(_point(v,v->points)[k]/v->mindist)*v->mindist;
292  }
293  v->points++;
294  if(!(v->points&0xff))spinnit("loading... ",v->points);
295}
296
297/* yes, not threadsafe.  These utils aren't */
298static int sortit=0;
299static int sortsize=0;
300static int meshcomp(const void *a,const void *b){
301  if(((sortit++)&0xfff)==0)spinnit("sorting mesh...",sortit);
302  return(memcmp(a,b,sortsize));
303}
304
305void vqgen_sortmesh(vqgen *v){
306  sortit=0;
307  if(v->mindist>0.f){
308    long i,march=1;
309
310    /* sort to make uniqueness detection trivial */
311    sortsize=(v->elements+v->aux)*sizeof(float);
312    qsort(v->pointlist,v->points,sortsize,meshcomp);
313
314    /* now march through and eliminate dupes */
315    for(i=1;i<v->points;i++){
316      if(memcmp(_point(v,i),_point(v,i-1),sortsize)){
317        /* a new, unique entry.  march it down */
318        if(i>march)memcpy(_point(v,march),_point(v,i),sortsize);
319        march++;
320      }
321      spinnit("eliminating density... ",v->points-i);
322    }
323
324    /* we're done */
325    fprintf(stderr,"\r%ld training points remining out of %ld"
326            " after density mesh (%ld%%)\n",march,v->points,march*100/v->points);
327    v->points=march;
328
329  }
330  v->sorted=1;
331}
332
333float vqgen_iterate(vqgen *v,int biasp){
334  long   i,j,k;
335
336  float fdesired;
337  long  desired;
338  long  desired2;
339
340  float asserror=0.f;
341  float meterror=0.f;
342  float *new;
343  float *new2;
344  long   *nearcount;
345  float *nearbias;
346 #ifdef NOISY
347   char buff[80];
348   FILE *assig;
349   FILE *bias;
350   FILE *cells;
351   sprintf(buff,"cells%d.m",v->it);
352   cells=fopen(buff,"w");
353   sprintf(buff,"assig%d.m",v->it);
354   assig=fopen(buff,"w");
355   sprintf(buff,"bias%d.m",v->it);
356   bias=fopen(buff,"w");
357 #endif
358 
359
360  if(v->entries<2){
361    fprintf(stderr,"generation requires at least two entries\n");
362    exit(1);
363  }
364
365  if(!v->sorted)vqgen_sortmesh(v);
366  if(!v->seeded)_vqgen_seed(v);
367
368  fdesired=(float)v->points/v->entries;
369  desired=fdesired;
370  desired2=desired*2;
371  new=_ogg_malloc(sizeof(float)*v->entries*v->elements);
372  new2=_ogg_malloc(sizeof(float)*v->entries*v->elements);
373  nearcount=_ogg_malloc(v->entries*sizeof(long));
374  nearbias=_ogg_malloc(v->entries*desired2*sizeof(float));
375
376  /* fill in nearest points for entry biasing */
377  /*memset(v->bias,0,sizeof(float)*v->entries);*/
378  memset(nearcount,0,sizeof(long)*v->entries);
379  memset(v->assigned,0,sizeof(long)*v->entries);
380  if(biasp){
381    for(i=0;i<v->points;i++){
382      float *ppt=v->weight_func(v,_point(v,i));
383      float firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
384      float secondmetric=v->metric_func(v,_now(v,1),ppt)+v->bias[1];
385      long   firstentry=0;
386      long   secondentry=1;
387     
388      if(!(i&0xff))spinnit("biasing... ",v->points+v->points+v->entries-i);
389     
390      if(firstmetric>secondmetric){
391        float temp=firstmetric;
392        firstmetric=secondmetric;
393        secondmetric=temp;
394        firstentry=1;
395        secondentry=0;
396      }
397     
398      for(j=2;j<v->entries;j++){
399        float thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
400        if(thismetric<secondmetric){
401          if(thismetric<firstmetric){
402            secondmetric=firstmetric;
403            secondentry=firstentry;
404            firstmetric=thismetric;
405            firstentry=j;
406          }else{
407            secondmetric=thismetric;
408            secondentry=j;
409          }
410        }
411      }
412     
413      j=firstentry;
414      for(j=0;j<v->entries;j++){
415       
416        float thismetric,localmetric;
417        float *nearbiasptr=nearbias+desired2*j;
418        long k=nearcount[j];
419       
420        localmetric=v->metric_func(v,_now(v,j),ppt);
421        /* 'thismetric' is to be the bias value necessary in the current
422           arrangement for entry j to capture point i */
423        if(firstentry==j){
424          /* use the secondary entry as the threshhold */
425          thismetric=secondmetric-localmetric;
426        }else{
427          /* use the primary entry as the threshhold */
428          thismetric=firstmetric-localmetric;
429        }
430       
431        /* support the idea of 'minimum distance'... if we want the
432           cells in a codebook to be roughly some minimum size (as with
433           the low resolution residue books) */
434       
435        /* a cute two-stage delayed sorting hack */
436        if(k<desired){
437          nearbiasptr[k]=thismetric;
438          k++;
439          if(k==desired){
440            spinnit("biasing... ",v->points+v->points+v->entries-i);
441            qsort(nearbiasptr,desired,sizeof(float),directdsort);
442          }
443         
444        }else if(thismetric>nearbiasptr[desired-1]){
445          nearbiasptr[k]=thismetric;
446          k++;
447          if(k==desired2){
448            spinnit("biasing... ",v->points+v->points+v->entries-i);
449            qsort(nearbiasptr,desired2,sizeof(float),directdsort);
450            k=desired;
451          }
452        }
453        nearcount[j]=k;
454      }
455    }
456   
457    /* inflate/deflate */
458   
459    for(i=0;i<v->entries;i++){
460      float *nearbiasptr=nearbias+desired2*i;
461     
462      spinnit("biasing... ",v->points+v->entries-i);
463     
464      /* due to the delayed sorting, we likely need to finish it off....*/
465      if(nearcount[i]>desired)
466        qsort(nearbiasptr,nearcount[i],sizeof(float),directdsort);
467
468      v->bias[i]=nearbiasptr[desired-1];
469
470    }
471  }else{ 
472    memset(v->bias,0,v->entries*sizeof(float));
473  }
474
475  /* Now assign with new bias and find new midpoints */
476  for(i=0;i<v->points;i++){
477    float *ppt=v->weight_func(v,_point(v,i));
478    float firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0];
479    long   firstentry=0;
480
481    if(!(i&0xff))spinnit("centering... ",v->points-i);
482
483    for(j=0;j<v->entries;j++){
484      float thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j];
485      if(thismetric<firstmetric){
486        firstmetric=thismetric;
487        firstentry=j;
488      }
489    }
490
491    j=firstentry;
492     
493#ifdef NOISY
494    fprintf(cells,"%g %g\n%g %g\n\n",
495          _now(v,j)[0],_now(v,j)[1],
496          ppt[0],ppt[1]);
497#endif
498
499    firstmetric-=v->bias[j];
500    meterror+=firstmetric;
501
502    if(v->centroid==0){
503      /* set up midpoints for next iter */
504      if(v->assigned[j]++){
505        for(k=0;k<v->elements;k++)
506          vN(new,j)[k]+=ppt[k];
507        if(firstmetric>v->max[j])v->max[j]=firstmetric;
508      }else{
509        for(k=0;k<v->elements;k++)
510          vN(new,j)[k]=ppt[k];
511        v->max[j]=firstmetric;
512      }
513    }else{
514      /* centroid */
515      if(v->assigned[j]++){
516        for(k=0;k<v->elements;k++){
517          if(vN(new,j)[k]>ppt[k])vN(new,j)[k]=ppt[k];
518          if(vN(new2,j)[k]<ppt[k])vN(new2,j)[k]=ppt[k];
519        }
520        if(firstmetric>v->max[firstentry])v->max[j]=firstmetric;
521      }else{
522        for(k=0;k<v->elements;k++){
523          vN(new,j)[k]=ppt[k];
524          vN(new2,j)[k]=ppt[k];
525        }
526        v->max[firstentry]=firstmetric;
527      }
528    }
529  }
530
531  /* assign midpoints */
532
533  for(j=0;j<v->entries;j++){
534#ifdef NOISY
535    fprintf(assig,"%ld\n",v->assigned[j]);
536    fprintf(bias,"%g\n",v->bias[j]);
537#endif
538    asserror+=fabs(v->assigned[j]-fdesired);
539    if(v->assigned[j]){
540      if(v->centroid==0){
541        for(k=0;k<v->elements;k++)
542          _now(v,j)[k]=vN(new,j)[k]/v->assigned[j];
543      }else{
544        for(k=0;k<v->elements;k++)
545          _now(v,j)[k]=(vN(new,j)[k]+vN(new2,j)[k])/2.f;
546      }
547    }
548  }
549
550  asserror/=(v->entries*fdesired);
551
552  fprintf(stderr,"Pass #%d... ",v->it);
553  fprintf(stderr,": dist %g(%g) metric error=%g \n",
554          asserror,fdesired,meterror/v->points);
555  v->it++;
556 
557  free(new);
558  free(nearcount);
559  free(nearbias);
560#ifdef NOISY
561  fclose(assig);
562  fclose(bias);
563  fclose(cells);
564#endif
565  return(asserror);
566}
567
Note: See TracBrowser for help on using the repository browser.