latticehint.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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 XIPHOPHORUS Company http://www.xiph.org/ *
  10. ********************************************************************
  11. function: utility main for building thresh/pigeonhole encode hints
  12. last mod: $Id: latticehint.c,v 1.9.2.1 2001/07/08 08:48:09 xiphmont Exp $
  13. ********************************************************************/
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <math.h>
  17. #include <string.h>
  18. #include <errno.h>
  19. #include "../lib/scales.h"
  20. #include "bookutil.h"
  21. #include "vqgen.h"
  22. #include "vqsplit.h"
  23. /* The purpose of this util is to build encode hints for lattice
  24. codebooks so that brute forcing each codebook entry isn't needed.
  25. Threshhold hints are for books in which each scalar in the vector
  26. is independant (eg, residue) and pigeonhole lookups provide a
  27. minimum error fit for words where the scalars are interdependant
  28. (each affecting the fit of the next in sequence) as in an LSP
  29. sequential book (or can be used along with a sparse threshhold map,
  30. like a splitting tree that need not be trained)
  31. If the input book is non-sequential, a threshhold hint is built.
  32. If the input book is sequential, a pigeonholing hist is built.
  33. If the book is sparse, a pigeonholing hint is built, possibly in addition
  34. to the threshhold hint
  35. command line:
  36. latticehint book.vqh [threshlist]
  37. latticehint produces book.vqh on stdout */
  38. static int longsort(const void *a, const void *b){
  39. return(**((long **)a)-**((long **)b));
  40. }
  41. static int addtosearch(int entry,long **tempstack,long *tempcount,int add){
  42. long *ptr=tempstack[entry];
  43. long i=tempcount[entry];
  44. if(ptr){
  45. while(i--)
  46. if(*ptr++==add)return(0);
  47. tempstack[entry]=_ogg_realloc(tempstack[entry],
  48. (tempcount[entry]+1)*sizeof(long));
  49. }else{
  50. tempstack[entry]=_ogg_malloc(sizeof(long));
  51. }
  52. tempstack[entry][tempcount[entry]++]=add;
  53. return(1);
  54. }
  55. static void setvals(int dim,encode_aux_pigeonhole *p,
  56. long *temptrack,float *tempmin,float *tempmax,
  57. int seqp){
  58. int i;
  59. float last=0.f;
  60. for(i=0;i<dim;i++){
  61. tempmin[i]=(temptrack[i])*p->del+p->min+last;
  62. tempmax[i]=tempmin[i]+p->del;
  63. if(seqp)last=tempmin[i];
  64. }
  65. }
  66. /* note that things are currently set up such that input fits that
  67. quantize outside the pigeonmap are dropped and brute-forced. So we
  68. can ignore the <0 and >=n boundary cases in min/max error */
  69. static float minerror(int dim,float *a,encode_aux_pigeonhole *p,
  70. long *temptrack,float *tempmin,float *tempmax){
  71. int i;
  72. float err=0.f;
  73. for(i=0;i<dim;i++){
  74. float eval=0.f;
  75. if(a[i]<tempmin[i]){
  76. eval=tempmin[i]-a[i];
  77. }else if(a[i]>tempmax[i]){
  78. eval=a[i]-tempmax[i];
  79. }
  80. err+=eval*eval;
  81. }
  82. return(err);
  83. }
  84. static float maxerror(int dim,float *a,encode_aux_pigeonhole *p,
  85. long *temptrack,float *tempmin,float *tempmax){
  86. int i;
  87. float err=0.f,eval;
  88. for(i=0;i<dim;i++){
  89. if(a[i]<tempmin[i]){
  90. eval=tempmax[i]-a[i];
  91. }else if(a[i]>tempmax[i]){
  92. eval=a[i]-tempmin[i];
  93. }else{
  94. float t1=a[i]-tempmin[i];
  95. eval=tempmax[i]-a[i];
  96. if(t1>eval)eval=t1;
  97. }
  98. err+=eval*eval;
  99. }
  100. return(err);
  101. }
  102. int main(int argc,char *argv[]){
  103. codebook *b;
  104. static_codebook *c;
  105. int entries=-1,dim=-1;
  106. float min,del;
  107. char *name;
  108. long i,j;
  109. float *suggestions;
  110. int suggcount=0;
  111. if(argv[1]==NULL){
  112. fprintf(stderr,"Need a lattice book on the command line.\n");
  113. exit(1);
  114. }
  115. {
  116. char *ptr;
  117. char *filename=strdup(argv[1]);
  118. b=codebook_load(filename);
  119. c=(static_codebook *)(b->c);
  120. ptr=strrchr(filename,'.');
  121. if(ptr){
  122. *ptr='\0';
  123. name=strdup(filename);
  124. }else{
  125. name=strdup(filename);
  126. }
  127. }
  128. if(c->maptype!=1){
  129. fprintf(stderr,"Provided book is not a latticebook.\n");
  130. exit(1);
  131. }
  132. entries=b->entries;
  133. dim=b->dim;
  134. min=_float32_unpack(c->q_min);
  135. del=_float32_unpack(c->q_delta);
  136. /* Do we want to gen a threshold hint? */
  137. if(c->q_sequencep==0){
  138. /* yes. Discard any preexisting threshhold hint */
  139. long quantvals=_book_maptype1_quantvals(c);
  140. long **quantsort=alloca(quantvals*sizeof(long *));
  141. encode_aux_threshmatch *t=_ogg_calloc(1,sizeof(encode_aux_threshmatch));
  142. c->thresh_tree=t;
  143. fprintf(stderr,"Adding threshold hint to %s...\n",name);
  144. /* partial/complete suggestions */
  145. if(argv[2]){
  146. char *ptr=strdup(argv[2]);
  147. suggestions=alloca(sizeof(float)*quantvals);
  148. for(suggcount=0;ptr && suggcount<quantvals;suggcount++){
  149. char *ptr2=strchr(ptr,',');
  150. if(ptr2)*ptr2++='\0';
  151. suggestions[suggcount]=atof(ptr);
  152. ptr=ptr2;
  153. }
  154. }
  155. /* simplest possible threshold hint only */
  156. t->quantthresh=_ogg_calloc(quantvals-1,sizeof(float));
  157. t->quantmap=_ogg_calloc(quantvals,sizeof(int));
  158. t->threshvals=quantvals;
  159. t->quantvals=quantvals;
  160. /* the quantvals may not be in order; sort em first */
  161. for(i=0;i<quantvals;i++)quantsort[i]=c->quantlist+i;
  162. qsort(quantsort,quantvals,sizeof(long *),longsort);
  163. /* ok, gen the map and thresholds */
  164. for(i=0;i<quantvals;i++)t->quantmap[i]=quantsort[i]-c->quantlist;
  165. for(i=0;i<quantvals-1;i++){
  166. float v1=*(quantsort[i])*del+min;
  167. float v2=*(quantsort[i+1])*del+min;
  168. for(j=0;j<suggcount;j++)
  169. if(v1<suggestions[j] && suggestions[j]<v2){
  170. t->quantthresh[i]=suggestions[j];
  171. break;
  172. }
  173. if(j==suggcount){
  174. t->quantthresh[i]=(v1+v2)*.5;
  175. }
  176. }
  177. }
  178. /* Do we want to gen a pigeonhole hint? */
  179. for(i=0;i<entries;i++)if(c->lengthlist[i]==0)break;
  180. if(c->q_sequencep || i<entries){
  181. long **tempstack;
  182. long *tempcount;
  183. long *temptrack;
  184. float *tempmin;
  185. float *tempmax;
  186. long totalstack=0;
  187. long pigeons;
  188. long subpigeons;
  189. long quantvals=_book_maptype1_quantvals(c);
  190. int changep=1,factor;
  191. encode_aux_pigeonhole *p=_ogg_calloc(1,sizeof(encode_aux_pigeonhole));
  192. c->pigeon_tree=p;
  193. fprintf(stderr,"Adding pigeonhole hint to %s...\n",name);
  194. /* the idea is that we quantize uniformly, even in a nonuniform
  195. lattice, so that quantization of one scalar has a predictable
  196. result on the next sequential scalar in a greedy matching
  197. algorithm. We generate a lookup based on the quantization of
  198. the vector (pigeonmap groups quantized entries together) and
  199. list the entries that could possible be the best fit for any
  200. given member of that pigeonhole. The encode process then has a
  201. much smaller list to brute force */
  202. /* find our pigeonhole-specific quantization values, fill in the
  203. quant value->pigeonhole map */
  204. factor=3;
  205. p->del=del;
  206. p->min=min;
  207. p->quantvals=quantvals;
  208. {
  209. int max=0;
  210. for(i=0;i<quantvals;i++)if(max<c->quantlist[i])max=c->quantlist[i];
  211. p->mapentries=max;
  212. }
  213. p->pigeonmap=_ogg_malloc(p->mapentries*sizeof(long));
  214. p->quantvals=(quantvals+factor-1)/factor;
  215. /* pigeonhole roughly on the boundaries of the quantvals; the
  216. exact pigeonhole grouping is an optimization issue, not a
  217. correctness issue */
  218. for(i=0;i<p->mapentries;i++){
  219. float thisval=del*i+min; /* middle of the quant zone */
  220. int quant=0;
  221. float err=fabs(c->quantlist[0]*del+min-thisval);
  222. for(j=1;j<quantvals;j++){
  223. float thiserr=fabs(c->quantlist[j]*del+min-thisval);
  224. if(thiserr<err){
  225. quant=j/factor;
  226. err=thiserr;
  227. }
  228. }
  229. p->pigeonmap[i]=quant;
  230. }
  231. /* pigeonmap complete. Now do the grungy business of finding the
  232. entries that could possibly be the best fit for a value appearing
  233. in the pigeonhole. The trick that allows the below to work is the
  234. uniform quantization; even though the scalars may be 'sequential'
  235. (each a delta from the last), the uniform quantization means that
  236. the error variance is *not* dependant. Given a pigeonhole and an
  237. entry, we can find the minimum and maximum possible errors
  238. (relative to the entry) for any point that could appear in the
  239. pigeonhole */
  240. /* must iterate over both pigeonholes and entries */
  241. /* temporarily (in order to avoid thinking hard), we grow each
  242. pigeonhole seperately, the build a stack of 'em later */
  243. pigeons=1;
  244. subpigeons=1;
  245. for(i=0;i<dim;i++)subpigeons*=p->mapentries;
  246. for(i=0;i<dim;i++)pigeons*=p->quantvals;
  247. temptrack=_ogg_calloc(dim,sizeof(long));
  248. tempmin=_ogg_calloc(dim,sizeof(float));
  249. tempmax=_ogg_calloc(dim,sizeof(float));
  250. tempstack=_ogg_calloc(pigeons,sizeof(long *));
  251. tempcount=_ogg_calloc(pigeons,sizeof(long));
  252. while(1){
  253. float errorpost=-1;
  254. char buffer[80];
  255. /* map our current pigeonhole to a 'big pigeonhole' so we know
  256. what list we're after */
  257. int entry=0;
  258. for(i=dim-1;i>=0;i--)entry=entry*p->quantvals+p->pigeonmap[temptrack[i]];
  259. setvals(dim,p,temptrack,tempmin,tempmax,c->q_sequencep);
  260. sprintf(buffer,"Building pigeonhole search list [%ld]...",totalstack);
  261. /* Search all entries to find the one with the minimum possible
  262. maximum error. Record that error */
  263. for(i=0;i<entries;i++){
  264. if(c->lengthlist[i]>0){
  265. float this=maxerror(dim,b->valuelist+i*dim,p,
  266. temptrack,tempmin,tempmax);
  267. if(errorpost==-1 || this<errorpost)errorpost=this;
  268. spinnit(buffer,subpigeons);
  269. }
  270. }
  271. /* Our search list will contain all entries with a minimum
  272. possible error <= our errorpost */
  273. for(i=0;i<entries;i++)
  274. if(c->lengthlist[i]>0){
  275. spinnit(buffer,subpigeons);
  276. if(minerror(dim,b->valuelist+i*dim,p,
  277. temptrack,tempmin,tempmax)<errorpost)
  278. totalstack+=addtosearch(entry,tempstack,tempcount,i);
  279. }
  280. for(i=0;i<dim;i++){
  281. temptrack[i]++;
  282. if(temptrack[i]<p->mapentries)break;
  283. temptrack[i]=0;
  284. }
  285. if(i==dim)break;
  286. subpigeons--;
  287. }
  288. fprintf(stderr,"\r "
  289. "\rTotal search list size (all entries): %ld\n",totalstack);
  290. /* pare the index of lists for improbable quantizations (where
  291. improbable is determined by c->lengthlist; we assume that
  292. pigeonholing is in sync with the codeword cells, which it is */
  293. /*for(i=0;i<entries;i++){
  294. float probability= 1.f/(1<<c->lengthlist[i]);
  295. if(c->lengthlist[i]==0 || probability*entries<cutoff){
  296. totalstack-=tempcount[i];
  297. tempcount[i]=0;
  298. }
  299. }*/
  300. /* pare the list of shortlists; merge contained and similar lists
  301. together */
  302. p->fitmap=_ogg_malloc(pigeons*sizeof(long));
  303. for(i=0;i<pigeons;i++)p->fitmap[i]=-1;
  304. while(changep){
  305. char buffer[80];
  306. changep=0;
  307. for(i=0;i<pigeons;i++){
  308. if(p->fitmap[i]<0 && tempcount[i]){
  309. for(j=i+1;j<pigeons;j++){
  310. if(p->fitmap[j]<0 && tempcount[j]){
  311. /* is one list a superset, or are they sufficiently similar? */
  312. int amiss=0,bmiss=0,ii,jj;
  313. for(ii=0;ii<tempcount[i];ii++){
  314. for(jj=0;jj<tempcount[j];jj++)
  315. if(tempstack[i][ii]==tempstack[j][jj])break;
  316. if(jj==tempcount[j])amiss++;
  317. }
  318. for(jj=0;jj<tempcount[j];jj++){
  319. for(ii=0;ii<tempcount[i];ii++)
  320. if(tempstack[i][ii]==tempstack[j][jj])break;
  321. if(ii==tempcount[i])bmiss++;
  322. }
  323. if(amiss==0 ||
  324. bmiss==0 ||
  325. (amiss*2<tempcount[i] && bmiss*2<tempcount[j] &&
  326. tempcount[i]+bmiss<entries/30)){
  327. /*superset/similar Add all of one to the other. */
  328. for(jj=0;jj<tempcount[j];jj++)
  329. totalstack+=addtosearch(i,tempstack,tempcount,
  330. tempstack[j][jj]);
  331. totalstack-=tempcount[j];
  332. p->fitmap[j]=i;
  333. changep=1;
  334. }
  335. }
  336. }
  337. sprintf(buffer,"Consolidating [%ld total, %s]... ",totalstack,
  338. changep?"reit":"nochange");
  339. spinnit(buffer,pigeons-i);
  340. }
  341. }
  342. }
  343. /* repack the temp stack in final form */
  344. fprintf(stderr,"\r "
  345. "\rFinal total list size: %ld\n",totalstack);
  346. p->fittotal=totalstack;
  347. p->fitlist=_ogg_malloc((totalstack+1)*sizeof(long));
  348. p->fitlength=_ogg_malloc(pigeons*sizeof(long));
  349. {
  350. long usage=0;
  351. for(i=0;i<pigeons;i++){
  352. if(p->fitmap[i]==-1){
  353. if(tempcount[i])
  354. memcpy(p->fitlist+usage,tempstack[i],tempcount[i]*sizeof(long));
  355. p->fitmap[i]=usage;
  356. p->fitlength[i]=tempcount[i];
  357. usage+=tempcount[i];
  358. if(usage>totalstack){
  359. fprintf(stderr,"Internal error; usage>totalstack\n");
  360. exit(1);
  361. }
  362. }else{
  363. p->fitlength[i]=p->fitlength[p->fitmap[i]];
  364. p->fitmap[i]=p->fitmap[p->fitmap[i]];
  365. }
  366. }
  367. }
  368. }
  369. write_codebook(stdout,name,c);
  370. fprintf(stderr,"\r "
  371. "\nDone.\n");
  372. exit(0);
  373. }