add train.c, command line util for training/retraining codebooks.
[platform/upstream/libvorbis.git] / vq / train.c
1 /********************************************************************
2  *                                                                  *
3  * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE.  *
4  * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
5  * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
6  * PLEASE READ THESE TERMS DISTRIBUTING.                            *
7  *                                                                  *
8  * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-1999             *
9  * by 1999 Monty <monty@xiph.org> and The XIPHOPHORUS Company       *
10  * http://www.xiph.org/                                             *
11  *                                                                  *
12  ********************************************************************
13
14  function: utility main for training codebooks
15  author: Monty <xiphmont@mit.edu>
16  modifications by: Monty
17  last modification date: Dec 10 1999
18
19  ********************************************************************/
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <math.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <signal.h>
27 #include "vqgen.h"
28
29 /* A metric for LSP codes */
30                             /* candidate,actual */
31 static double _dist_and_pos(vqgen *v,double *b, double *a){
32   int i;
33   int el=v->elements;
34   double acc=0.;
35   double lastb=0.;
36   for(i=0;i<el;i++){
37     double actualdist=(a[i]-lastb);
38     double testdist=(b[i]-lastb);
39     if(actualdist>0 && testdist>0){
40       double val;
41       if(actualdist>testdist)
42         val=actualdist/testdist-1.;
43       else
44         val=testdist/actualdist-1.;
45       acc+=val;
46     }else{
47       acc+=999999.;
48     }
49     lastb=b[i];
50   }
51   return acc;
52 }
53
54 static void *set_metric(int m){
55   switch(m){
56   case 0:
57     return(NULL);
58   case 1:
59     return(_dist_and_pos);
60   default:
61     fprintf(stderr,"Invalid metric number\n");
62     exit(0);
63   }
64 }
65
66 static int rline(FILE *in,FILE *out,char *line,int max,int pass){
67   while(fgets(line,160,in)){
68     if(line[0]=='#'){
69       if(pass)fprintf(out,"%s",line);
70     }else{
71       return(1);
72     }
73   }
74   return(0);
75 }
76
77 /* command line:
78    trainvq [vq=file | [entries=n] [dim=n]] met=n in=file,firstcol 
79            [in=file,firstcol]
80 */
81
82 int exiting=0;
83 void setexit(int dummy){
84   fprintf(stderr,"\nexiting... please wait to finish this iteration\n");
85   exiting=1;
86 }
87
88 int main(int argc,char *argv[]){
89   vqgen v;
90   int entries=-1,dim=-1;
91   FILE *out=NULL;
92   int met=0;
93   double (*metric)(vqgen *,double *, double *)=NULL;
94   char line[1024];
95   long i,j,k;
96
97   double desired=.05;
98   int iter=1000;
99
100   int init=0;
101   while(*argv){
102
103     /* continue training an existing book */
104     if(!strncmp(*argv,"vq=",3)){
105       FILE *in=NULL;
106       char filename[80],*ptr;
107       if(sscanf(*argv,"vq=%70s",filename)!=1){
108         fprintf(stderr,"Syntax error in argument '%s'\n",*argv);
109         exit(1);
110       }
111
112       in=fopen(filename,"r");
113       ptr=strrchr(filename,'-');
114       if(ptr){
115         int num;
116         ptr++;
117         num=atoi(ptr);
118         sprintf(ptr,"%d.vqi",num+1);
119       }else
120         strcat(filename,"-0.vqi");
121       
122       out=fopen(filename,"w");
123       if(out==NULL){
124         fprintf(stderr,"Unable to open %s for writing\n",filename);
125         exit(1);
126       }
127       fprintf(out,"# OggVorbis VQ codebook trainer, intermediate file\n");
128
129       if(in){
130         /* we wish to suck in a preexisting book and continue to train it */
131         double a;
132             
133         rline(in,out,line,160,1);
134         if(sscanf(line,"%d %d %d",&entries,&dim,&met)!=3){
135           fprintf(stderr,"Syntax error reading book file\n");
136           exit(1);
137         }
138
139         metric=set_metric(met);
140         vqgen_init(&v,dim,entries,metric,0.);
141         init=1;
142
143         /* entries, bias, points */
144         i=0;
145         for(j=0;j<entries;j++){
146           for(k=0;k<dim;k++){
147             rline(in,out,line,160,0);
148             sscanf(line,"%lf",&a);
149             v.entrylist[i++]=a;
150           }
151         }
152
153         i=0;
154         for(j=0;j<entries;j++){
155           rline(in,out,line,160,0);
156           sscanf(line,"%lf",&a);
157           v.bias[i++]=a;
158         }
159
160         {
161           double b[80];
162           i=0;
163           v.entries=0; /* hack to avoid reseeding */
164           while(1){
165             for(k=0;k<dim && k<80;k++){
166               rline(in,out,line,160,0);
167               sscanf(line,"%lf",b+k);
168             }
169             if(feof(in))break;
170             vqgen_addpoint(&v,b);
171           }
172           v.entries=entries;
173         }
174
175         fclose(in);
176       }
177     }
178
179     /* set parameters if we're not loading a pre book */
180     if(!strncmp(*argv,"entries=",8)){
181       sscanf(*argv,"entries=%d",&entries);
182     }
183     if(!strncmp(*argv,"desired=",8)){
184       sscanf(*argv,"desired=%lf",&desired);
185     }
186     if(!strncmp(*argv,"dim=",4)){
187       sscanf(*argv,"dim=%d",&dim);
188     }
189
190     /* which error metric (0==euclidian distance default) */
191     if(!strncmp(*argv,"met=",4)){
192       sscanf(*argv,"met=%d",&met);
193       metric=set_metric(met);
194     }
195
196     if(!strncmp(*argv,"in=",3)){
197       int start;
198       char file[80];
199       FILE *in;
200
201       if(sscanf(*argv,"in=%79[^,],%d",file,&start)!=2)goto syner;
202       if(!out){
203         fprintf(stderr,"vq= must preceed in= arguments\n");
204         exit(1);
205       }
206       if(!init){
207         if(dim==-1 || entries==-1){
208           fprintf(stderr,"Must specify dimensionality and entries before"
209                   " first input file\n");
210           exit(1);
211         }
212         vqgen_init(&v,dim,entries,metric,0.);
213         init=1;
214       }
215
216       in=fopen(file,"r");
217       if(in==NULL){
218         fprintf(stderr,"Could not open input file %s\n",file);
219         exit(1);
220       }
221       fprintf(out,"# training file entry: %s\n",file);
222
223       while(rline(in,out,line,1024,1)){
224         double b[16];
225         int n=sscanf(line,"%lf %lf %lf %lf %lf %lf %lf %lf "
226                      "%lf %lf %lf %lf %lf %lf %lf %lf",
227                      b,b+1,b+2,b+3,b+4,b+5,b+6,b+7,b+8,b+9,b+10,b+11,b+12,b+13,
228                      b+14,b+15);
229         if(start+dim>n){
230           fprintf(stderr,"ran out of columns reading %s\n",file);
231           exit(1);
232         }
233         vqgen_addpoint(&v,b+start);
234       }
235
236       fclose(in);
237     }
238     argv++;
239   }
240
241   /* train the book */
242   signal(SIGTERM,setexit);
243   signal(SIGINT,setexit);
244
245   for(i=0;i<iter && !exiting;i++){
246     if(vqgen_iterate(&v)<desired)break;
247   }
248
249   /* save the book */
250
251   fprintf(out,"%d %d %d\n",entries,dim,met);
252
253   i=0;
254   for(j=0;j<entries;j++)
255     for(k=0;k<dim;k++)
256       fprintf(out,"%f\n",v.entrylist[i++]);
257   
258   fprintf(out,"# biases---\n");
259   i=0;
260   for(j=0;j<entries;j++)
261     fprintf(out,"%f\n",v.bias[i++]);
262
263   fprintf(out,"# points---\n");
264   i=0;
265   for(j=0;j<v.points;j++)
266     for(k=0;k<dim && k<80;k++)
267       fprintf(out,"%f\n",v.pointlist[i++]);
268
269   fclose(out);
270   exit(0);
271
272   syner:
273     fprintf(stderr,"Syntax error in argument '%s'\n",*argv);
274     exit(1);
275 }