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 Xiph.Org Foundation http://www.xiph.org/ *
11 ********************************************************************
13 function: build a VQ codebook and the encoding decision 'tree'
14 last mod: $Id: vqsplit.c 16037 2009-05-26 21:10:58Z xiphmont $
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
37 /* Codebook generation happens in two steps:
39 1) Train the codebook with data collected from the encoder: We use
40 one of a few error metrics (which represent the distance between a
41 given data point and a candidate point in the training set) to
42 divide the training set up into cells representing roughly equal
43 probability of occurring.
45 2) Generate the codebook and auxiliary data from the trained data set
48 /* Building a codebook from trained set **********************************
50 The codebook in raw form is technically finished once it's trained.
51 However, we want to finalize the representative codebook values for
52 each entry and generate auxiliary information to optimize encoding.
53 We generate the auxiliary coding tree using collected data,
54 probably the same data as in the original training */
56 /* At each recursion, the data set is split in half. Cells with data
57 points on side A go into set A, same with set B. The sets may
58 overlap. If the cell overlaps the deviding line only very slightly
59 (provided parameter), we may choose to ignore the overlap in order
60 to pare the tree down */
63 int iascsort(const void *a
,const void *b
){
64 long av
=isortvals
[*((long *)a
)];
65 long bv
=isortvals
[*((long *)b
)];
69 static float _Ndist(int el
,float *a
, float *b
){
73 float val
=(a
[i
]-b
[i
]);
79 #define _Npoint(i) (pointlist+dim*(i))
80 #define _Nnow(i) (entrylist+dim*(i))
83 /* goes through the split, but just counts it and returns a metric*/
84 int vqsp_count(float *entrylist
,float *pointlist
,int dim
,
85 long *membership
,long *reventry
,
86 long *entryindex
,long entries
,
87 long *pointindex
,long points
,int splitp
,
88 long *entryA
,long *entryB
,
89 long besti
,long bestj
,
90 long *entriesA
,long *entriesB
,long *entriesC
){
95 long *temppointsA
=NULL
;
96 long *temppointsB
=NULL
;
99 temppointsA
=_ogg_malloc(points
*sizeof(long));
100 temppointsB
=_ogg_malloc(points
*sizeof(long));
103 memset(entryA
,0,sizeof(long)*entries
);
104 memset(entryB
,0,sizeof(long)*entries
);
106 /* Do the points belonging to this cell occur on sideA, sideB or
109 for(i
=0;i
<points
;i
++){
110 float *ppt
=_Npoint(pointindex
[i
]);
111 long firstentry
=membership
[pointindex
[i
]];
113 if(firstentry
==besti
){
114 entryA
[reventry
[firstentry
]]=1;
115 if(splitp
)temppointsA
[pointsA
++]=pointindex
[i
];
118 if(firstentry
==bestj
){
119 entryB
[reventry
[firstentry
]]=1;
120 if(splitp
)temppointsB
[pointsB
++]=pointindex
[i
];
124 float distA
=_Ndist(dim
,ppt
,_Nnow(besti
));
125 float distB
=_Ndist(dim
,ppt
,_Nnow(bestj
));
127 entryA
[reventry
[firstentry
]]=1;
128 if(splitp
)temppointsA
[pointsA
++]=pointindex
[i
];
130 entryB
[reventry
[firstentry
]]=1;
131 if(splitp
)temppointsB
[pointsB
++]=pointindex
[i
];
136 /* The entry splitting isn't total, so that storage has to be
137 allocated for recursion. Reuse the entryA/entryB vectors */
138 /* keep the entries in ascending order (relative to the original
139 list); we rely on that stability when ordering p/q choice */
140 for(j
=0;j
<entries
;j
++){
141 if(entryA
[j
] && entryB
[j
])C
++;
142 if(entryA
[j
])entryA
[A
++]=entryindex
[j
];
143 if(entryB
[j
])entryB
[B
++]=entryindex
[j
];
149 memcpy(pointindex
,temppointsA
,sizeof(long)*pointsA
);
150 memcpy(pointindex
+pointsA
,temppointsB
,sizeof(long)*pointsB
);
157 int lp_split(float *pointlist
,long totalpoints
,
159 long *entryindex
,long entries
,
160 long *pointindex
,long points
,
161 long *membership
,long *reventry
,
162 long depth
, long *pointsofar
){
164 encode_aux_nearestmatch
*t
=b
->c
->nearest_tree
;
166 /* The encoder, regardless of book, will be using a straight
167 euclidian distance-to-point metric to determine closest point.
168 Thus we split the cells using the same (we've already trained the
169 codebook set spacing and distribution using special metrics and
170 even a midpoint division won't disturb the basic properties) */
173 float *entrylist
=b
->valuelist
;
175 long *entryA
=_ogg_calloc(entries
,sizeof(long));
176 long *entryB
=_ogg_calloc(entries
,sizeof(long));
187 sprintf(spinbuf
,"splitting [%ld left]... ",totalpoints
-*pointsofar
);
189 /* one reverse index needed */
190 for(i
=0;i
<b
->entries
;i
++)reventry
[i
]=-1;
191 for(i
=0;i
<entries
;i
++)reventry
[entryindex
[i
]]=i
;
193 /* We need to find the dividing hyperplane. find the median of each
194 axis as the centerpoint and the normal facing farthest point */
196 /* more than one way to do this part. For small sets, we can brute
199 if(entries
<8 || (float)points
*entries
*entries
<16.f
*1024*1024){
200 /* try every pair possibility */
203 for(i
=0;i
<entries
-1;i
++){
204 for(j
=i
+1;j
<entries
;j
++){
205 spinnit(spinbuf
,entries
-i
);
206 vqsp_count(b
->valuelist
,pointlist
,dim
,
211 entryindex
[i
],entryindex
[j
],
212 &entriesA
,&entriesB
,&entriesC
);
213 this=(entriesA
-entriesC
)*(entriesB
-entriesC
);
215 /* when choosing best, we also want some form of stability to
216 make sure more branches are pared later; secondary
217 weighting isn;t needed as the entry lists are in ascending
218 order, and we always try p/q in the same sequence */
231 float *p
=alloca(dim
*sizeof(float));
232 float *q
=alloca(dim
*sizeof(float));
235 /* try COG/normal and furthest pairs */
237 /* eventually, we want to select the closest entry and figure n/c
238 from p/q (because storing n/c is too large */
240 spinnit(spinbuf
,entries
);
243 for(j
=0;j
<entries
;j
++)
244 p
[k
]+=b
->valuelist
[entryindex
[j
]*dim
+k
];
249 /* we go through the entries one by one, looking for the entry on
250 the other side closest to the point of reflection through the
253 for(i
=0;i
<entries
;i
++){
254 float *ppi
=_Nnow(entryindex
[i
]);
258 spinnit(spinbuf
,entries
-i
);
263 for(j
=0;j
<entries
;j
++){
265 float this=_Ndist(dim
,q
,_Nnow(entryindex
[j
]));
266 if(ref_j
==-1 || this<=ref_best
){ /* <=, not <; very important */
273 vqsp_count(b
->valuelist
,pointlist
,dim
,
279 &entriesA
,&entriesB
,&entriesC
);
280 this=(entriesA
-entriesC
)*(entriesB
-entriesC
);
282 /* when choosing best, we also want some form of stability to
283 make sure more branches are pared later; secondary
284 weighting isn;t needed as the entry lists are in ascending
285 order, and we always try p/q in the same sequence */
304 /* find cells enclosing points */
305 /* count A/B points */
307 pointsA
=vqsp_count(b
->valuelist
,pointlist
,dim
,
313 &entriesA
,&entriesB
,&entriesC
);
315 /* fprintf(stderr,"split: total=%ld depth=%ld set A=%ld:%ld:%ld=B\n",
316 entries,depth,entriesA-entriesC,entriesC,entriesB-entriesC);*/
318 long thisaux
=t
->aux
++;
319 if(t
->aux
>=t
->alloc
){
321 t
->ptr0
=_ogg_realloc(t
->ptr0
,sizeof(long)*t
->alloc
);
322 t
->ptr1
=_ogg_realloc(t
->ptr1
,sizeof(long)*t
->alloc
);
323 t
->p
=_ogg_realloc(t
->p
,sizeof(long)*t
->alloc
);
324 t
->q
=_ogg_realloc(t
->q
,sizeof(long)*t
->alloc
);
332 t
->ptr0
[thisaux
]=entryA
[0];
333 *pointsofar
+=pointsA
;
335 t
->ptr0
[thisaux
]= -t
->aux
;
336 ret
=lp_split(pointlist
,totalpoints
,b
,entryA
,entriesA
,pointindex
,pointsA
,
337 membership
,reventry
,depth
+1,pointsofar
);
341 t
->ptr1
[thisaux
]=entryB
[0];
342 *pointsofar
+=points
-pointsA
;
344 t
->ptr1
[thisaux
]= -t
->aux
;
345 ret
+=lp_split(pointlist
,totalpoints
,b
,entryB
,entriesB
,pointindex
+pointsA
,
346 points
-pointsA
,membership
,reventry
,
355 static int _node_eq(encode_aux_nearestmatch
*v
, long a
, long b
){
356 long Aptr0
=v
->ptr0
[a
];
357 long Aptr1
=v
->ptr1
[a
];
358 long Bptr0
=v
->ptr0
[b
];
359 long Bptr1
=v
->ptr1
[b
];
361 /* the possibility of choosing the same p and q, but switched, can;t
362 happen because we always look for the best p/q in the same search
363 order and the search is stable */
365 if(Aptr0
==Bptr0
&& Aptr1
==Bptr1
)
371 void vqsp_book(vqgen
*v
, codebook
*b
, long *quantlist
){
373 static_codebook
*c
=(static_codebook
*)b
->c
;
374 encode_aux_nearestmatch
*t
;
376 memset(b
,0,sizeof(codebook
));
377 memset(c
,0,sizeof(static_codebook
));
379 t
=c
->nearest_tree
=_ogg_calloc(1,sizeof(encode_aux_nearestmatch
));
382 /* make sure there are no duplicate entries and that every
385 for(i
=0;i
<v
->entries
;){
386 /* duplicate? if so, eliminate */
388 if(_Ndist(v
->elements
,_now(v
,i
),_now(v
,j
))==0.f
){
389 fprintf(stderr
,"found a duplicate entry! removing...\n");
391 memcpy(_now(v
,i
),_now(v
,v
->entries
),sizeof(float)*v
->elements
);
392 memcpy(quantlist
+i
*v
->elements
,quantlist
+v
->entries
*v
->elements
,
393 sizeof(long)*v
->elements
);
401 v
->assigned
=_ogg_calloc(v
->entries
,sizeof(long));
402 for(i
=0;i
<v
->points
;i
++){
403 float *ppt
=_point(v
,i
);
404 float firstmetric
=_Ndist(v
->elements
,_now(v
,0),ppt
);
407 if(!(i
&0xff))spinnit("checking... ",v
->points
-i
);
409 for(j
=0;j
<v
->entries
;j
++){
410 float thismetric
=_Ndist(v
->elements
,_now(v
,j
),ppt
);
411 if(thismetric
<firstmetric
){
412 firstmetric
=thismetric
;
417 v
->assigned
[firstentry
]++;
420 for(j
=0;j
<v
->entries
;){
421 if(v
->assigned
[j
]==0){
422 fprintf(stderr
,"found an unused entry! removing...\n");
424 memcpy(_now(v
,j
),_now(v
,v
->entries
),sizeof(float)*v
->elements
);
425 v
->assigned
[j
]=v
->assigned
[v
->elements
];
426 memcpy(quantlist
+j
*v
->elements
,quantlist
+v
->entries
*v
->elements
,
427 sizeof(long)*v
->elements
);
434 fprintf(stderr
,"Building a book with %ld unique entries...\n",v
->entries
);
437 long *entryindex
=_ogg_malloc(v
->entries
*sizeof(long *));
438 long *pointindex
=_ogg_malloc(v
->points
*sizeof(long));
439 long *membership
=_ogg_malloc(v
->points
*sizeof(long));
440 long *reventry
=_ogg_malloc(v
->entries
*sizeof(long));
443 for(i
=0;i
<v
->entries
;i
++)entryindex
[i
]=i
;
444 for(i
=0;i
<v
->points
;i
++)pointindex
[i
]=i
;
447 t
->ptr0
=_ogg_malloc(sizeof(long)*t
->alloc
);
448 t
->ptr1
=_ogg_malloc(sizeof(long)*t
->alloc
);
449 t
->p
=_ogg_malloc(sizeof(long)*t
->alloc
);
450 t
->q
=_ogg_malloc(sizeof(long)*t
->alloc
);
453 c
->entries
=v
->entries
;
454 c
->lengthlist
=_ogg_calloc(c
->entries
,sizeof(long));
455 b
->valuelist
=v
->entrylist
; /* temporary; replaced later */
457 b
->entries
=c
->entries
;
459 for(i
=0;i
<v
->points
;i
++)membership
[i
]=-1;
460 for(i
=0;i
<v
->points
;i
++){
461 float *ppt
=_point(v
,i
);
463 float firstmetric
=_Ndist(v
->elements
,_now(v
,0),ppt
);
465 if(!(i
&0xff))spinnit("assigning... ",v
->points
-i
);
467 for(j
=1;j
<v
->entries
;j
++){
468 if(v
->assigned
[j
]!=-1){
469 float thismetric
=_Ndist(v
->elements
,_now(v
,j
),ppt
);
470 if(thismetric
<=firstmetric
){
471 firstmetric
=thismetric
;
477 membership
[i
]=firstentry
;
480 fprintf(stderr
,"Leaves added: %d \n",
481 lp_split(v
->pointlist
,v
->points
,
482 b
,entryindex
,v
->entries
,
483 pointindex
,v
->points
,
491 fprintf(stderr
,"Paring/rerouting redundant branches... ");
493 /* The tree is likely big and redundant. Pare and reroute branches */
500 /* span the tree node by node; list unique decision nodes and
501 short circuit redundant branches */
506 /* check list of unique decisions */
508 if(_node_eq(t
,i
,j
))break;
511 /* a redundant entry; find all higher nodes referencing it and
512 short circuit them to the previously noted unique entry */
514 for(k
=0;k
<t
->aux
;k
++){
515 if(t
->ptr0
[k
]==-i
)t
->ptr0
[k
]=-j
;
516 if(t
->ptr1
[k
]==-i
)t
->ptr1
[k
]=-j
;
519 /* Now, we need to fill in the hole from this redundant
520 entry in the listing. Insert the last entry in the list.
521 Fix the forward pointers to that last entry */
523 t
->ptr0
[i
]=t
->ptr0
[t
->aux
];
524 t
->ptr1
[i
]=t
->ptr1
[t
->aux
];
525 t
->p
[i
]=t
->p
[t
->aux
];
526 t
->q
[i
]=t
->q
[t
->aux
];
527 for(k
=0;k
<t
->aux
;k
++){
528 if(t
->ptr0
[k
]==-t
->aux
)t
->ptr0
[k
]=-i
;
529 if(t
->ptr1
[k
]==-t
->aux
)t
->ptr1
[k
]=-i
;
537 fprintf(stderr
,"\rParing/rerouting redundant branches... "
538 "%ld remaining ",t
->aux
);
540 fprintf(stderr
,"\n");
544 /* run all training points through the decision tree to get a final
547 long *probability
=_ogg_malloc(c
->entries
*sizeof(long));
548 for(i
=0;i
<c
->entries
;i
++)probability
[i
]=1; /* trivial guard */
551 /* sigh. A necessary hack */
552 for(i
=0;i
<t
->aux
;i
++)t
->p
[i
]*=c
->dim
;
553 for(i
=0;i
<t
->aux
;i
++)t
->q
[i
]*=c
->dim
;
555 for(i
=0;i
<v
->points
;i
++){
556 /* we use the linear matcher regardless becuase the trainer
557 doesn't convert log to linear */
558 int ret
=_best(b
,v
->pointlist
+i
*v
->elements
,1);
560 if(!(i
&0xff))spinnit("counting hits... ",v
->points
-i
);
562 for(i
=0;i
<t
->aux
;i
++)t
->p
[i
]/=c
->dim
;
563 for(i
=0;i
<t
->aux
;i
++)t
->q
[i
]/=c
->dim
;
565 build_tree_from_lengths(c
->entries
,probability
,c
->lengthlist
);
570 /* Sort the entries by codeword length, short to long (eases
571 assignment and packing to do it now) */
573 long *wordlen
=c
->lengthlist
;
574 long *index
=_ogg_malloc(c
->entries
*sizeof(long));
575 long *revindex
=_ogg_malloc(c
->entries
*sizeof(long));
577 for(i
=0;i
<c
->entries
;i
++)index
[i
]=i
;
578 isortvals
=c
->lengthlist
;
579 qsort(index
,c
->entries
,sizeof(long),iascsort
);
581 /* rearrange storage; ptr0/1 first as it needs a reverse index */
582 /* n and c stay unchanged */
583 for(i
=0;i
<c
->entries
;i
++)revindex
[index
[i
]]=i
;
584 for(i
=0;i
<t
->aux
;i
++){
585 if(!(i
&0x3f))spinnit("sorting... ",t
->aux
-i
);
587 if(t
->ptr0
[i
]>=0)t
->ptr0
[i
]=revindex
[t
->ptr0
[i
]];
588 if(t
->ptr1
[i
]>=0)t
->ptr1
[i
]=revindex
[t
->ptr1
[i
]];
589 t
->p
[i
]=revindex
[t
->p
[i
]];
590 t
->q
[i
]=revindex
[t
->q
[i
]];
594 /* map lengthlist and vallist with index */
595 c
->lengthlist
=_ogg_calloc(c
->entries
,sizeof(long));
596 b
->valuelist
=_ogg_malloc(sizeof(float)*c
->entries
*c
->dim
);
597 c
->quantlist
=_ogg_malloc(sizeof(long)*c
->entries
*c
->dim
);
598 for(i
=0;i
<c
->entries
;i
++){
600 for(k
=0;k
<c
->dim
;k
++){
601 b
->valuelist
[i
*c
->dim
+k
]=v
->entrylist
[e
*c
->dim
+k
];
602 c
->quantlist
[i
*c
->dim
+k
]=quantlist
[e
*c
->dim
+k
];
604 c
->lengthlist
[i
]=wordlen
[e
];
610 fprintf(stderr
,"Done. \n\n");