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