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 16037 2009-05-26 21:10:58Z 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. */ 60 float _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 71 float *_weight_null(vqgen *v,float *a){ 72 return a; 73 } 74 75 /* *must* be beefed up. */ 76 void _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 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); 87 } 88 89 void 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 165 void 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. */ 224 void 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 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)); 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 270 void 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 */ 298 static int sortit=0; 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)); 303 } 304 305 void 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 333 float 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 568