Artifact [c0e7352341]

Artifact c0e735234181d2fe7a3a745d45f22e459fcaded9:


/*
 * 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 <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>

#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;i<e->num;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) || (num<e->max_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;i<num;i++)
      e->set[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;i<e->num;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;i<e->num;i++){
    if(NOT_SCREENED(*mark,e->set[i])){
      score=TEMP_FUNC(mark,&(e->set[i]));
      if(score<XORTHRESHOLD){
	if((*bestscore<0) || (score<*bestscore)){
	  *bestscore=score;
	  found=i;
	}
      }
    }
  }
  return found;
}
*/


static int 
equiv_classes_avg_match(equiv_classes *e, marktype *mark, int *bestscore)
{
  int i;
  int score,found=-2,score2;
  int total=0,matched=0;

  assert(e);
  assert(mark);
  assert(bestscore);

  *bestscore=-1;
  for(i=0;i<e->num;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;i<c->num_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) || (num<c->max_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;i<num;i++){
      if(c->eqv[i].time<min){
	min=c->eqv[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;i<num;i++)
      c->eqv[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) || (num<c->max_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;i<num;i++){
      if(c->eqv[i].time<min){
	min=c->eqv[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;i<num;i++)
      c->eqv[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) && (eq<c->num_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) && (eq<c->num_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].num<SCALE);*/
  
  n=equiv_classes_add(&(c->eqv[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;i<c->num_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)||(score<bestscore)){
	bestscore=score;
	found=i;
      }
    }
  }
  return found;
}

int 
codebook_match_against_average(codebook *c,marktype *mark)
{
  int i;
  int found=-1;
  int score,score2;
  int bestscore=-1;
  marktype *avg=NULL;
  
  assert(c);
  assert(mark);

  for(i=0;i<c->num_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)||(score<bestscore)){
	  bestscore=score;
	  found=i;
	  
	  c->eqv[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;i<c->num_eqv;i++){
    for(j=0;j<c->eqv[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)||(score<bestscore)){
	    bestscore=score;
	    found=i*SCALE + j;

	    c->eqv[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;i<c->num_eqv;i++){
    fprintf(stderr,"********** %d: %d ******************\n",i,c->eqv[i].num);
    if(all){
      for(j=0;j<c->eqv[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;i<c->num_eqv;i++){
    w=0;
    h=0;

    avg=codebook_get_avg_mark(c,i, NO_UPDATE);
    w+=avg->w+gapx;
    
    for(j=0;j<c->eqv[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) && (j<c->eqv[i].num-1)){
	w=0;
	MAXh+=h;
      }
    }
    MAXh+=h;
    MAXh+=gapy;
  }

  marktype_alloc(&image,MAXw,MAXh);
  MAXw=0;
  MAXh=0;
  for(i=0;i<c->num_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;j<c->eqv[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) && (j<c->eqv[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;i<e->num;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;i<e->num;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<w);
	    assert(y+y_off>=0);
	    assert(y+y_off<h);
	    a[x + x_off][y + y_off]++;
	  }
    }
    
    set = 0;
    d.w = w;
    d.h = h;
    marktype_alloc (&d, w, h);
    for (x = 0; x < w; x++)
      for (y = 0; y < h; y++){
	/* > 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;
}