/* * Module: codebook * * Implements the routines for handling equivalance classes * and adding/averaging the codebook. * * The codebook module matches against the average mark example. * We use a 21% XOR template matcher. This works well for lossless * compression, but not very well for lossy compression. * * For lossless compression, the template is close enough to give * the contexts some reliability. For lossy compression, a CSIS * style template matcher should be employed. * * The codebook_configure() routine can be used to change the * default behaviour of the codebook. The default values * are an unlimited number of equivalence classes, and at most * 10 examples in each class. * * Author: * Stuart Inglis (singlis@internz.co.nz) * (c) 1998 * */ #include #include #include #include #include "marklist.h" #include "pbmtools.h" #include "template_match.h" #include "globals.h" #include "codebook.h" int SCALE=256; /* the matching function uses globals.g_match_threshold */ /****************************************************************** * Equivalence class * Routines * * ******************************************************************/ static void equiv_classes_init(equiv_classes *e, int max_examples) { assert(e); if((globals.g_match_mode==MATCH_B_SINGLE)|| (globals.g_match_mode==MATCH_F_SINGLE)|| (globals.g_match_mode==MATCH_HYBRID)){ e->max_examples=SCALE-1; } else { e->max_examples=max_examples; } e->num=0; e->set=NULL; marktype_init(&(e->avg)); e->avg.bitmap=NULL; e->avg_uptodate=1; e->time=0; } static void equiv_classes_free(equiv_classes *e) { int i; assert(e); for(i=0;inum;i++){ marktype_free(&(e->set[i])); } marktype_free(&(e->avg)); free(e->set); } static int equiv_classes_add(equiv_classes *e, marktype *d) { int num,i; assert(e); assert(d); num=e->num; if((e->max_examples<1) || (nummax_examples)){ e->set=(marktype*)realloc(e->set,sizeof(marktype)*(num+1)); assert(e->set); e->set[num]=marktype_copy(*d); e->avg_uptodate=0; e->num++; } else { /* get rid of the oldest example */ marktype_free(&(e->set[0])); for(i=1;iset[i-1]=e->set[i]; e->set[num-1]=marktype_copy(*d); e->avg_uptodate=0; num=num-1; } return num; } /* static void equiv_classes_dump(equiv_classes *e, FILE *fp) { int i; assert(e); assert(fp); for(i=0;inum;i++){ marktype_writeascii(fp,e->set[i]); fprintf(fp,"crc: %d\n",marktype_crc(&(e->set[i]))); } } */ /* static int equiv_classes_match(equiv_classes *e, marktype *mark, int *bestscore) { int i; int score,found=-2; assert(e); assert(mark); assert(bestscore); *bestscore=-1; for(i=0;inum;i++){ if(NOT_SCREENED(*mark,e->set[i])){ score=TEMP_FUNC(mark,&(e->set[i])); if(scorenum;i++){ if(NOT_SCREENED(*mark,e->set[i])){ score=TEMP_FUNC(mark,&(e->set[i])); if(score<=globals.g_match_threshold){ total+=score; matched+=1; found=1; /*break;*/ } } } if(found>=0) *bestscore=(total/matched); return found; } /****************************************************************** * Codebook * Routines * * ******************************************************************/ void codebook_configure(codebook *c, int max_classes, int max_examples) { assert(c); c->max_classes=max_classes; c->max_examples=max_examples; } void codebook_init(codebook *c, int max_classes, int max_examples) { assert(c); c->num_eqv=0; c->eqv=NULL; c->time=0; codebook_configure(c,max_classes, max_examples); } void codebook_updatetime(codebook *c) { c->time++; } void codebook_free(codebook *c) { int i; assert(c); if(c->eqv){ for(i=0;inum_eqv;i++) equiv_classes_free(&(c->eqv[i])); free(c->eqv); } } int codebook_add_equiv(codebook *c) { int num,i; assert(c); num=c->num_eqv; if((c->max_classes<1) || (nummax_classes)){ c->eqv=(equiv_classes*)realloc(c->eqv,sizeof(equiv_classes)*(num+1)); assert(c->eqv); equiv_classes_init(&(c->eqv[num]),c->max_examples); c->num_eqv++; } else { int min,max; int minpos=0; min=max=c->eqv[0].time; /* getting rid of the oldest isn't very good */ /* getting rid of the smallest is much better...*/ /* now we get rid of the marks using LRU */ for(i=0;ieqv[i].timeeqv[i].time; minpos=i; } if(c->eqv[i].time>max) max=c->eqv[i].time; } /* fprintf(stderr,"deleting class %d min=%d,max=%d\n",minpos,min,max);*/ equiv_classes_free(&(c->eqv[minpos])); for(i=minpos+1;ieqv[i-1]=c->eqv[i]; equiv_classes_init(&(c->eqv[num-1]),c->max_examples); num=num-1; } c->eqv[num].time=c->time; return num; } int codebook_add_equiv2(codebook *c) { int num,i; assert(c); num=c->num_eqv; if((c->max_classes<1) || (nummax_classes)){ c->eqv=(equiv_classes*)realloc(c->eqv,sizeof(equiv_classes)*(num+1)); assert(c->eqv); equiv_classes_init(&(c->eqv[num]),c->max_examples); c->num_eqv++; } else { int min,max; int minpos=0; min=max=c->eqv[0].time; /* getting rid of the oldest isn't very good */ /* getting rid of the smallest is much better...*/ /* now we get rid of the marks using LRU */ for(i=0;ieqv[i].timeeqv[i].time; minpos=i; } if(c->eqv[i].time>max) max=c->eqv[i].time; } /* fprintf(stderr,"deleting class %d min=%d,max=%d\n",minpos,min,max);*/ equiv_classes_free(&(c->eqv[minpos])); for(i=minpos+1;ieqv[i-1]=c->eqv[i]; equiv_classes_init(&(c->eqv[num-1]),c->max_examples); num=num-1; } c->eqv[num].time=c->time; return num*SCALE; } marktype * codebook_get_avg_mark(codebook *c, int eq, int updatetimeaccess) { assert(c); assert((eq>=0) && (eqnum_eqv)); if(c->eqv[eq].avg_uptodate==0){ if(c->eqv[eq].avg.bitmap!=NULL) marktype_free(&(c->eqv[eq].avg)); equiv_classes_average_mark(&(c->eqv[eq]),&(c->eqv[eq].avg)); c->eqv[eq].avg_uptodate=1; } if(updatetimeaccess==UPDATETIME){ c->eqv[eq].time=c->time; } return &(c->eqv[eq].avg); } /* the "2" series of functions don't match with respect to the average, but with respect to the best fitting component over all. To keep compatible with the other functions we map the equivalence class along with it's number in the class to an integer using n=class*SCALE + pos; */ marktype * codebook_get_avg_mark2(codebook *c, int eq, int updatetimeaccess) { int i,j; assert(c); i=eq/SCALE; j=eq%SCALE; if(updatetimeaccess==UPDATETIME){ c->eqv[i].time=c->time; } return &(c->eqv[i].set[j]); } int codebook_add_mark(codebook *c,int eq,marktype *mark) { int n; assert(c); assert((eq>=0) && (eqnum_eqv)); assert(mark); n=equiv_classes_add(&(c->eqv[eq]), mark); return n; } int codebook_add_mark2(codebook *c,int eq,marktype *mark) { int n; assert(c); assert(mark); /* find out the class */ eq=eq/SCALE; /*assert(c->eqv[eq].numeqv[eq]), mark); return n; } int codebook_match(codebook *c,marktype *mark) { int i; int found=-1,ret; int score; int bestscore=-1; assert(c); assert(mark); for(i=0;inum_eqv;i++){ /* ret=equiv_classes_match(&(c->eqv[i]),mark, &score);*/ ret=equiv_classes_avg_match(&(c->eqv[i]),mark, &score); if(ret>=0){ if((bestscore<0)||(scorenum_eqv;i++){ avg=codebook_get_avg_mark(c,i, NO_UPDATE); if(NOT_SCREENED(*avg,*mark)){ score=TEMP_FUNC(avg,mark); if(score<=globals.g_match_threshold){ if((bestscore<0)||(scoreeqv[i].time=c->time; /* if we've already found the best possible */ if(bestscore==0) break; if((globals.g_match_mode==MATCH_F_SINGLE)|| (globals.g_match_mode==MATCH_F_AVG)) break; } } } } return found; } int codebook_match_against_average2(codebook *c,marktype *mark) { int i,j; int found=-1; int score,score2; int bestscore=-1; marktype *avg=NULL; assert(c); assert(mark); for(i=0;inum_eqv;i++){ for(j=0;jeqv[i].num;j++){ avg=&(c->eqv[i].set[j]); if(NOT_SCREENED(*avg,*mark)){ score=TEMP_FUNC(avg,mark); if(score<=globals.g_match_threshold){ if((bestscore<0)||(scoreeqv[i].time=c->time; /* if we've already found the best possible */ if(bestscore==0) break; if((globals.g_match_mode==MATCH_F_SINGLE)|| (globals.g_match_mode==MATCH_F_AVG)) break; } } } } } return found; } void codebook_dump(codebook *c, int all) { int i,j; assert(c); for(i=0;inum_eqv;i++){ fprintf(stderr,"********** %d: %d ******************\n",i,c->eqv[i].num); if(all){ for(j=0;jeqv[i].num;j++) marktype_writeascii(stderr,c->eqv[i].set[j]); } } } void codebook_dump_pbm(codebook *c, char *fn) { int i,j; int MM=3000; int gapx=1,gapy=1; int MAXh,h; int MAXw,w; marktype image; marktype *avg=NULL; assert(c); assert(fn); MAXw=0; MAXh=0; for(i=0;inum_eqv;i++){ w=0; h=0; avg=codebook_get_avg_mark(c,i, NO_UPDATE); w+=avg->w+gapx; for(j=0;jeqv[i].num;j++){ if(c->eqv[i].set[j].h>h) h=c->eqv[i].set[j].h; w+=c->eqv[i].set[j].w; w+=gapx; if(w>MAXw) MAXw=w; if((w>=MM) && (jeqv[i].num-1)){ w=0; MAXh+=h; } } MAXh+=h; MAXh+=gapy; } marktype_alloc(&image,MAXw,MAXh); MAXw=0; MAXh=0; for(i=0;inum_eqv;i++){ w=0; h=0; avg=codebook_get_avg_mark(c,i, NO_UPDATE); marktype_placeat(image,*avg,w,MAXh); w+=avg->w+gapx; for(j=0;jeqv[i].num;j++){ marktype_placeat(image,c->eqv[i].set[j],w,MAXh); if(c->eqv[i].set[j].h>h) h=c->eqv[i].set[j].h; w+=c->eqv[i].set[j].w; w+=gapx; if(w>MAXw) MAXw=w; if((w>=MM) && (jeqv[i].num-1)){ w=0; MAXh+=h; } } MAXh+=h; MAXh+=gapy; } marktype_writenamed(fn,image); marktype_free(&image); } void equiv_classes_average_mark(equiv_classes *e, marktype *mark) { int w = 0, h = 0; int t = INT_MAX, b = 0, l = INT_MAX, r = 0, matched = 0; marktype d, *temp=NULL; int **a=NULL; int x, y,i; int set; assert(e); assert(mark); for(i=0;inum;i++){ matched++; temp = &(e->set[i]); w = MAX (w, temp->w); h = MAX (h, temp->h); r = MAX (r, temp->xcen); l = MIN (l, temp->xcen); b = MAX (b, temp->ycen); t = MIN (t, temp->ycen); } w = w + (r - l); h = h + (b - t); if (matched > 1) { int x_off, y_off; CALLOC_2D(a,w,h,int); assert(a); for (x = 0; x < w; x++) for (y = 0; y < h; y++) a[x][y] = 0; for(i=0;inum;i++){ temp=&(e->set[i]); x_off = r - temp->xcen; y_off = b - temp->ycen; for (x = 0; x < temp->w; x++) for (y = 0; y < temp->h; y++) if (pbm_getpixel (temp->bitmap, x, y)){ assert(x+x_off>=0); assert(x+x_off=0); assert(y+y_off gives better CR than >= */ if (a[x][y] > MAX (1, (matched / 2))) { pbm_putpixel (d.bitmap, x, y, 1); set++; } } FREE_2D(a,w); marktype_area (&d); assert(d.set!=0); marktype_adj_bound (&d); d.set = set; d.name = (char *) realloc (d.name, sizeof (char) * (strlen ("?") + 1)); assert(d.name); strcpy (d.name, "?"); marktype_calc_centroid (&d); } else d = marktype_copy (e->set[0]); *mark = d; }