1 /********************************************************************
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. *
8 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
9 * by the XIPHOPHORUS Company http://www.xiph.org/ *
11 ********************************************************************
13 function: train a VQ codebook
14 last mod: $Id: vqgen.c,v 1.41 2002/10/11 07:44:28 xiphmont Exp $
16 ********************************************************************/
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. */
23 /* There are so many optimizations to explore in *both* stages that
24 considering the undertaking is almost withering. For now, we brute
35 /* Codebook generation happens in two steps:
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.
43 2) Generate the codebook and auxiliary data from the trained data set
46 /* Codebook training ****************************************************
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. */
56 /* internal helpers *****************************************************/
57 #define vN(data,i) (data+v->elements*i)
59 /* default metric; squared 'distance' from desired value. */
60 float _dist(vqgen *v,float *a, float *b){
65 float val=(a[i]-b[i]);
71 float *_weight_null(vqgen *v,float *a){
75 /* *must* be beefed up. */
76 void _vqgen_seed(vqgen *v){
78 for(i=0;i<v->entries;i++)
79 memcpy(_now(v,i),_point(v,i),sizeof(float)*v->elements);
83 int directdsort(const void *a, const void *b){
84 float av=*((float *)a);
85 float bv=*((float *)b);
86 return (av<bv)-(av>bv);
89 void vqgen_cellmetric(vqgen *v){
91 float min=-1.f,max=-1.f,mean=0.f,acc=0.f;
96 float spacings[v->entries];
99 sprintf(buff,"cellspace%d.m",v->it);
100 cells=fopen(buff,"w");
103 /* minimum, maximum, cell spacing */
104 for(j=0;j<v->entries;j++){
107 for(k=0;k<v->entries;k++){
109 float this=_dist(v,_now(v,j),_now(v,k));
111 if(v->assigned[k] && (localmin==-1 || this<localmin))
121 if(k<v->entries)continue;
123 if(v->assigned[j]==0){
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;
134 spacings[count++]=localmin;
138 fprintf(stderr,"cell diameter: %.03g::%.03g::%.03g (%ld unused/%ld dup)\n",
139 min,mean/acc,max,unused,dup);
142 qsort(spacings,count,sizeof(float),directdsort);
144 fprintf(cells,"%g\n",spacings[i]);
150 /* External calls *******************************************************/
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.
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. */
165 void vqgen_quantize(vqgen *v,quant_meta *q){
171 float maxquant=((1<<q->quant)-1);
175 mindel=maxdel=_now(v,0)[0];
177 for(j=0;j<v->entries;j++){
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];
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 */
191 delta=(maxdel-mindel)/((1<<q->quant)-1.5f);
193 q->min=_float32_pack(mindel);
194 q->delta=_float32_pack(delta);
196 mindel=_float32_unpack(q->min);
197 delta=_float32_unpack(q->delta);
199 for(j=0;j<v->entries;j++){
201 for(k=0;k<v->elements;k++){
202 float val=_now(v,j)[k];
203 float now=rint((val-last-mindel)/delta);
207 /* be paranoid; this should be impossible */
208 fprintf(stderr,"fault; quantized value<0\n");
213 /* be paranoid; this should be impossible */
214 fprintf(stderr,"fault; quantized value>max\n");
217 if(q->sequencep)last=(now*delta)+mindel+last;
222 /* much easier :-). Unlike in the codebook, we don't un-log log
223 scales; we just make sure they're properly offset. */
224 void vqgen_unquantize(vqgen *v,quant_meta *q){
226 float mindel=_float32_unpack(q->min);
227 float delta=_float32_unpack(q->delta);
229 for(j=0;j<v->entries;j++){
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;
240 void 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));
245 v->centroid=centroid;
246 v->elements=elements;
250 v->pointlist=_ogg_malloc(v->allocated*(v->elements+v->aux)*sizeof(float));
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));
258 v->metric_func=metric;
260 v->metric_func=_dist;
262 v->weight_func=weight;
264 v->weight_func=_weight_null;
266 v->asciipoints=tmpfile();
270 void vqgen_addpoint(vqgen *v, float *p,float *a){
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]);
277 if(v->points>=v->allocated){
279 v->pointlist=_ogg_realloc(v->pointlist,v->allocated*(v->elements+v->aux)*
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);
286 /* quantize to the density mesh if it's selected */
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;
294 if(!(v->points&0xff))spinnit("loading... ",v->points);
297 /* yes, not threadsafe. These utils aren't */
299 static int sortsize=0;
300 static int meshcomp(const void *a,const void *b){
301 if(((sortit++)&0xfff)==0)spinnit("sorting mesh...",sortit);
302 return(memcmp(a,b,sortsize));
305 void vqgen_sortmesh(vqgen *v){
310 /* sort to make uniqueness detection trivial */
311 sortsize=(v->elements+v->aux)*sizeof(float);
312 qsort(v->pointlist,v->points,sortsize,meshcomp);
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);
321 spinnit("eliminating density... ",v->points-i);
325 fprintf(stderr,"\r%ld training points remining out of %ld"
326 " after density mesh (%ld%%)\n",march,v->points,march*100/v->points);
333 float vqgen_iterate(vqgen *v,int biasp){
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");
361 fprintf(stderr,"generation requires at least two entries\n");
365 if(!v->sorted)vqgen_sortmesh(v);
366 if(!v->seeded)_vqgen_seed(v);
368 fdesired=(float)v->points/v->entries;
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));
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);
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];
388 if(!(i&0xff))spinnit("biasing... ",v->points+v->points+v->entries-i);
390 if(firstmetric>secondmetric){
391 float temp=firstmetric;
392 firstmetric=secondmetric;
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;
407 secondmetric=thismetric;
414 for(j=0;j<v->entries;j++){
416 float thismetric,localmetric;
417 float *nearbiasptr=nearbias+desired2*j;
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 */
424 /* use the secondary entry as the threshhold */
425 thismetric=secondmetric-localmetric;
427 /* use the primary entry as the threshhold */
428 thismetric=firstmetric-localmetric;
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) */
435 /* a cute two-stage delayed sorting hack */
437 nearbiasptr[k]=thismetric;
440 spinnit("biasing... ",v->points+v->points+v->entries-i);
441 qsort(nearbiasptr,desired,sizeof(float),directdsort);
444 }else if(thismetric>nearbiasptr[desired-1]){
445 nearbiasptr[k]=thismetric;
448 spinnit("biasing... ",v->points+v->points+v->entries-i);
449 qsort(nearbiasptr,desired2,sizeof(float),directdsort);
457 /* inflate/deflate */
459 for(i=0;i<v->entries;i++){
460 float *nearbiasptr=nearbias+desired2*i;
462 spinnit("biasing... ",v->points+v->entries-i);
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);
468 v->bias[i]=nearbiasptr[desired-1];
472 memset(v->bias,0,v->entries*sizeof(float));
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];
481 if(!(i&0xff))spinnit("centering... ",v->points-i);
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;
494 fprintf(cells,"%g %g\n%g %g\n\n",
495 _now(v,j)[0],_now(v,j)[1],
499 firstmetric-=v->bias[j];
500 meterror+=firstmetric;
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;
509 for(k=0;k<v->elements;k++)
511 v->max[j]=firstmetric;
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];
520 if(firstmetric>v->max[firstentry])v->max[j]=firstmetric;
522 for(k=0;k<v->elements;k++){
524 vN(new2,j)[k]=ppt[k];
526 v->max[firstentry]=firstmetric;
531 /* assign midpoints */
533 for(j=0;j<v->entries;j++){
535 fprintf(assig,"%ld\n",v->assigned[j]);
536 fprintf(bias,"%g\n",v->bias[j]);
538 asserror+=fabs(v->assigned[j]-fdesired);
541 for(k=0;k<v->elements;k++)
542 _now(v,j)[k]=vN(new,j)[k]/v->assigned[j];
544 for(k=0;k<v->elements;k++)
545 _now(v,j)[k]=(vN(new,j)[k]+vN(new2,j)[k])/2.f;
550 asserror/=(v->entries*fdesired);
552 fprintf(stderr,"Pass #%d... ",v->it);
553 fprintf(stderr,": dist %g(%g) metric error=%g \n",
554 asserror,fdesired,meterror/v->points);