www.pudn.com > jseg.rar > xv24to8.c
/* * The Conv24to8 procedure takes a pointer to a 24-bit image (loaded * previously). The image will be a w * h * 3 byte array of * bytes. The image will be arranged with 3 bytes per pixel (in order * R, G, and B), pixel 0 at the top left corner. (As normal.) * The procedure also takes a maximum number of colors to use (numcols) * and pointers to three 256-long arrays of bytes (to hold the returned * colormap) * * Note that Conv24to8() does NOT free the pic24 image under any circumstances * * The Conv24to8 procedure will set up the following: it will allocate, make * & return 'pic8', a 'w' by 'h' (passed in values) 8-bit picture. * it will load up the rmap, gmap and bmap colormap arrays. it will NOT * calculate numcols, since the cmap sort procedure has to be called anyway * * Conv24to8 returns 'pic8' if successful, 'NULL' on failure (presumably on a * malloc()) * * The 'slow' code is based on Heckbert's Median Cut algorithm. * */ #include "xv.h" #define MAX_CMAP_SIZE 256 #define COLOR_DEPTH 8 #define MAX_COLOR 256 #define B_DEPTH 5 /* # bits/pixel to use */ #define B_LEN (1<next; if (freeboxes) freeboxes->prev = NULL; ptr->next = usedboxes; usedboxes = ptr; if (ptr->next) ptr->next->prev = ptr; get_histogram(ptr); /**** STEP 3: continually subdivide boxes until no more free boxes remain */ while (freeboxes) { ptr = largest_box(); if (ptr) splitbox(ptr); else break; } /**** STEP 4: assign colors to all boxes ****/ for (i=0, ptr=usedboxes; i next) { assign_color(ptr, &rmap[i], &gmap[i], &bmap[i]); } /* We're done with the boxes now */ num_colors = i; free(box_list); box_list = freeboxes = usedboxes = NULL; /**** STEP 5: scan histogram and map all values to closest color */ /* 5a: create cell list as described in Heckbert[2] */ ColorCells = (CCELL **) calloc(C_LEN*C_LEN*C_LEN, sizeof(CCELL *)); /* 5b: create mapping from truncated pixel space to color table entries */ map_colortable(); /**** STEP 6: scan image, match input values to table entries */ i = quant_fsdither(); /* free everything that can be freed */ for (j=0 ; j < C_LEN*C_LEN*C_LEN ; j++) { if (ColorCells[j] != NULL) free (ColorCells[j]); } free(ColorCells); if (i) { free(pic8); pic8 = NULL; } /* free 'pic' on failure */ return pic8; } /****************************/ static void get_histogram(box) CBOX *box; /****************************/ { int i,j,r,g,b,*ptr; byte *p; box->rmin = box->gmin = box->bmin = 999; box->rmax = box->gmax = box->bmax = -1; box->total = WIDE * HIGH; /* zero out histogram */ ptr = &histogram[0][0][0]; for (i=B_LEN*B_LEN*B_LEN; i>0; i-- ) *ptr++ = 0; /* calculate histogram */ p = pic24; for (i=0; i > (COLOR_DEPTH-B_DEPTH); g = (*p++) >> (COLOR_DEPTH-B_DEPTH); b = (*p++) >> (COLOR_DEPTH-B_DEPTH); if (r < box->rmin) box->rmin = r; if (r > box->rmax) box->rmax = r; if (g < box->gmin) box->gmin = g; if (g > box->gmax) box->gmax = g; if (b < box->bmin) box->bmin = b; if (b > box->bmax) box->bmax = b; histogram[r][g][b]++; } } /******************************/ static CBOX *largest_box() /******************************/ { CBOX *tmp, *ptr; int size = -1; tmp = usedboxes; ptr = NULL; while (tmp) { if ( (tmp->rmax > tmp->rmin || tmp->gmax > tmp->gmin || tmp->bmax > tmp->bmin) && tmp->total > size ) { ptr = tmp; size = tmp->total; } tmp = tmp->next; } return(ptr); } /******************************/ static void splitbox(ptr) CBOX *ptr; /******************************/ { int hist2[B_LEN], first, last, i, rdel, gdel, bdel; CBOX *new; int *iptr, *histp, ir, ig, ib; int rmin,rmax,gmin,gmax,bmin,bmax; enum {RED,GREEN,BLUE} which; /* * see which axis is the largest, do a histogram along that * axis. Split at median point. Contract both new boxes to * fit points and return */ first = last = 0; /* shut RT hcc compiler up */ rmin = ptr->rmin; rmax = ptr->rmax; gmin = ptr->gmin; gmax = ptr->gmax; bmin = ptr->bmin; bmax = ptr->bmax; rdel = rmax - rmin; gdel = gmax - gmin; bdel = bmax - bmin; if (rdel>=gdel && rdel>=bdel) which = RED; else if (gdel>=bdel) which = GREEN; else which = BLUE; /* get histogram along longest axis */ switch (which) { case RED: histp = &hist2[rmin]; for (ir=rmin; ir<=rmax; ir++) { *histp = 0; for (ig=gmin; ig<=gmax; ig++) { iptr = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) { *histp += *iptr; ++iptr; } } ++histp; } first = rmin; last = rmax; break; case GREEN: histp = &hist2[gmin]; for (ig=gmin; ig<=gmax; ig++) { *histp = 0; for (ir=rmin; ir<=rmax; ir++) { iptr = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) { *histp += *iptr; ++iptr; } } ++histp; } first = gmin; last = gmax; break; case BLUE: histp = &hist2[bmin]; for (ib=bmin; ib<=bmax; ib++) { *histp = 0; for (ir=rmin; ir<=rmax; ir++) { iptr = &histogram[ir][gmin][ib]; for (ig=gmin; ig<=gmax; ig++) { *histp += *iptr; iptr += B_LEN; } } ++histp; } first = bmin; last = bmax; break; } /* find median point */ { int sum, sum2; histp = &hist2[first]; sum2 = ptr->total/2; histp = &hist2[first]; sum = 0; for (i=first; i<=last && (sum += *histp++) next; if (freeboxes) freeboxes->prev = NULL; if (usedboxes) usedboxes->prev = new; new->next = usedboxes; usedboxes = new; { int sum1,sum2,j; histp = &hist2[first]; sum1 = 0; for (j = first; j < i; ++j) sum1 += *histp++; sum2 = 0; for (j = i; j <= last; ++j) sum2 += *histp++; new->total = sum1; ptr->total = sum2; } new->rmin = rmin; new->rmax = rmax; new->gmin = gmin; new->gmax = gmax; new->bmin = bmin; new->bmax = bmax; switch (which) { case RED: new->rmax = i-1; ptr->rmin = i; break; case GREEN: new->gmax = i-1; ptr->gmin = i; break; case BLUE: new->bmax = i-1; ptr->bmin = i; break; } shrinkbox(new); shrinkbox(ptr); } /****************************/ static void shrinkbox(box) CBOX *box; /****************************/ { int *histp,ir,ig,ib; int rmin,rmax,gmin,gmax,bmin,bmax; rmin = box->rmin; rmax = box->rmax; gmin = box->gmin; gmax = box->gmax; bmin = box->bmin; bmax = box->bmax; if (rmax>rmin) { for (ir=rmin; ir<=rmax; ir++) for (ig=gmin; ig<=gmax; ig++) { histp = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) if (*histp++ != 0) { box->rmin = rmin = ir; goto have_rmin; } } have_rmin: if (rmax>rmin) for (ir=rmax; ir>=rmin; --ir) for (ig=gmin; ig<=gmax; ig++) { histp = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) if (*histp++ != 0) { box->rmax = rmax = ir; goto have_rmax; } } } have_rmax: if (gmax>gmin) { for (ig=gmin; ig<=gmax; ig++) for (ir=rmin; ir<=rmax; ir++) { histp = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) if (*histp++ != 0) { box->gmin = gmin = ig; goto have_gmin; } } have_gmin: if (gmax>gmin) for (ig=gmax; ig>=gmin; --ig) for (ir=rmin; ir<=rmax; ir++) { histp = &histogram[ir][ig][bmin]; for (ib=bmin; ib<=bmax; ib++) if (*histp++ != 0) { box->gmax = gmax = ig; goto have_gmax; } } } have_gmax: if (bmax>bmin) { for (ib=bmin; ib<=bmax; ib++) for (ir=rmin; ir<=rmax; ir++) { histp = &histogram[ir][gmin][ib]; for (ig=gmin; ig<=gmax; ig++) { if (*histp != 0) { box->bmin = bmin = ib; goto have_bmin; } histp += B_LEN; } } have_bmin: if (bmax>bmin) for (ib=bmax; ib>=bmin; --ib) for (ir=rmin; ir<=rmax; ir++) { histp = &histogram[ir][gmin][ib]; for (ig=gmin; ig<=gmax; ig++) { if (*histp != 0) { bmax = ib; goto have_bmax; } histp += B_LEN; } } } have_bmax: return; } /*******************************/ static void assign_color(ptr,rp,gp,bp) CBOX *ptr; byte *rp,*gp,*bp; /*******************************/ { int r,g,b; r = ((ptr->rmin + ptr->rmax) << (COLOR_DEPTH - B_DEPTH)) / 2; g = ((ptr->gmin + ptr->gmax) << (COLOR_DEPTH - B_DEPTH)) / 2; b = ((ptr->bmin + ptr->bmax) << (COLOR_DEPTH - B_DEPTH)) / 2; *rp = (byte) r; *gp = (byte) g; *bp = (byte) b; } /*******************************/ static CCELL *create_colorcell(r1,g1,b1) int r1,g1,b1; /*******************************/ { register int i; register CCELL *ptr; register byte *rp,*gp,*bp; int ir,ig,ib; long dist, mindist, tmp; ir = r1 >> (COLOR_DEPTH-C_DEPTH); ig = g1 >> (COLOR_DEPTH-C_DEPTH); ib = b1 >> (COLOR_DEPTH-C_DEPTH); r1 &= ~1 << (COLOR_DEPTH-C_DEPTH); g1 &= ~1 << (COLOR_DEPTH-C_DEPTH); b1 &= ~1 << (COLOR_DEPTH-C_DEPTH); ptr = (CCELL *) malloc(sizeof(CCELL)); *(ColorCells + ir*C_LEN*C_LEN + ig*C_LEN + ib) = ptr; ptr->num_ents = 0; /* step 1: find all colors inside this cell, while we're at it, find distance of centermost point to furthest corner */ mindist = 2000000000; rp=rmap; gp=gmap; bp=bmap; for (i=0; i >(COLOR_DEPTH-C_DEPTH) == ir && *gp>>(COLOR_DEPTH-C_DEPTH) == ig && *bp>>(COLOR_DEPTH-C_DEPTH) == ib) { ptr->entries[ptr->num_ents][0] = i; ptr->entries[ptr->num_ents][1] = 0; ++ptr->num_ents; tmp = *rp - r1; if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp; dist = (tmp*tmp * R2FACT); tmp = *gp - g1; if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp; dist += (tmp*tmp * G2FACT); tmp = *bp - b1; if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp; dist += (tmp*tmp * B2FACT); if (dist < mindist) mindist = dist; } /* step 3: find all points within that distance to box */ rp=rmap; gp=gmap; bp=bmap; for (i=0; i > (COLOR_DEPTH-C_DEPTH) != ir || *gp >> (COLOR_DEPTH-C_DEPTH) != ig || *bp >> (COLOR_DEPTH-C_DEPTH) != ib) { dist = 0; if ((tmp = r1 - *rp)>0 || (tmp = *rp - (r1 + MAX_COLOR/C_LEN-1)) > 0 ) dist += (tmp*tmp * R2FACT); if( (tmp = g1 - *gp)>0 || (tmp = *gp - (g1 + MAX_COLOR/C_LEN-1)) > 0 ) dist += (tmp*tmp * G2FACT); if( (tmp = b1 - *bp)>0 || (tmp = *bp - (b1 + MAX_COLOR/C_LEN-1)) > 0 ) dist += (tmp*tmp * B2FACT); if( dist < mindist ) { ptr->entries[ptr->num_ents][0] = i; ptr->entries[ptr->num_ents][1] = dist; ++ptr->num_ents; } } /* sort color cells by distance, use cheap exchange sort */ { int n, next_n; n = ptr->num_ents - 1; while (n>0) { next_n = 0; for (i=0; i entries[i][1] > ptr->entries[i+1][1]) { tmp = ptr->entries[i][0]; ptr->entries[i][0] = ptr->entries[i+1][0]; ptr->entries[i+1][0] = tmp; tmp = ptr->entries[i][1]; ptr->entries[i][1] = ptr->entries[i+1][1]; ptr->entries[i+1][1] = tmp; next_n = i; } } n = next_n; } } return (ptr); } /***************************/ static void map_colortable() /***************************/ { int ir,ig,ib, *histp; CCELL *cell; histp = &histogram[0][0][0]; for (ir=0; ir >(B_DEPTH-C_DEPTH)) << C_DEPTH*2) + ((ig>>(B_DEPTH-C_DEPTH)) << C_DEPTH) + (ib>>(B_DEPTH-C_DEPTH)) ) ); if (cell==NULL) cell = create_colorcell(ir<<(COLOR_DEPTH-B_DEPTH), ig<<(COLOR_DEPTH-B_DEPTH), ib<<(COLOR_DEPTH-B_DEPTH)); dist = 2000000000; for (i=0; i num_ents && dist>cell->entries[i][1]; i++) { j = cell->entries[i][0]; d2 = rmap[j] - (ir << (COLOR_DEPTH-B_DEPTH)); d2 = (d2 * d2 * R2FACT); tmp = gmap[j] - (ig << (COLOR_DEPTH-B_DEPTH)); d2 += (tmp*tmp * G2FACT); tmp = bmap[j] - (ib << (COLOR_DEPTH-B_DEPTH)); d2 += (tmp*tmp * B2FACT); if( d2 < dist ) { dist = d2; *histp = j; } } } histp++; } } /*****************************/ static int quant_fsdither() /*****************************/ { register int *thisptr, *nextptr; int *thisline, *nextline, *tmpptr; int r1, g1, b1, r2, g2, b2; int i, j, imax, jmax, oval; byte *inptr, *outptr; int lastline, lastpixel; imax = HIGH - 1; jmax = WIDE - 1; thisline = (int *) malloc(WIDE * 3 * sizeof(int)); nextline = (int *) malloc(WIDE * 3 * sizeof(int)); if (thisline == NULL || nextline == NULL) { fprintf(stderr,"unable to allocate stuff for the 'dither' routine\n"); return 1; } inptr = (byte *) pic24; outptr = (byte *) pic8; /* get first line of picture */ for (j=WIDE * 3, tmpptr=nextline; j; j--) *tmpptr++ = (int) *inptr++; for (i=0; i >= (COLOR_DEPTH-B_DEPTH); g2 >>= (COLOR_DEPTH-B_DEPTH); b2 >>= (COLOR_DEPTH-B_DEPTH); if ( (oval=histogram[r2][g2][b2]) == -1) { int ci, cj; long dist, d2, tmp; CCELL *cell; cell = *( ColorCells + ( ((r2>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2) + ((g2>>(B_DEPTH-C_DEPTH)) << C_DEPTH ) + (b2>>(B_DEPTH-C_DEPTH)) ) ); if (cell==NULL) cell = create_colorcell(r1,g1,b1); dist = 2000000000; for (ci=0; ci num_ents && dist>cell->entries[ci][1]; ci++) { cj = cell->entries[ci][0]; d2 = (rmap[cj] >> (COLOR_DEPTH-B_DEPTH)) - r2; d2 = (d2*d2 * R2FACT); tmp = (gmap[cj] >> (COLOR_DEPTH-B_DEPTH)) - g2; d2 += (tmp*tmp * G2FACT); tmp = (bmap[cj] >> (COLOR_DEPTH-B_DEPTH)) - b2; d2 += (tmp*tmp * B2FACT); if (d2 >R_SHIFT) | ((g1&GMASK)>>G_SHIFT) | ((b1&BMASK)>>B_SHIFT)); *pp = val; /* compute color errors */ r1 -= rmap[val]; g1 -= gmap[val]; b1 -= bmap[val]; /* Add fractions of errors to adjacent pixels */ r1 += 256; /* make positive for table indexing */ g1 += 256; b1 += 256; if (j!=jmax) { /* adjust RIGHT pixel */ thisptr[0] += tbl7[r1]; thisptr[1] += tbl7[g1]; thisptr[2] += tbl7[b1]; } if (i!=imax) { /* do BOTTOM pixel */ nextptr[0] += tbl5[r1]; nextptr[1] += tbl5[g1]; nextptr[2] += tbl5[b1]; if (j>0) { /* do BOTTOM LEFT pixel */ nextptr[-3] += tbl3[r1]; nextptr[-2] += tbl3[g1]; nextptr[-1] += tbl3[b1]; } if (j!=jmax) { /* do BOTTOM RIGHT pixel */ nextptr[3] += tbl1[r1]; nextptr[4] += tbl1[g1]; nextptr[5] += tbl1[b1]; } nextptr += 3; } } } return 0; } /****************************/ static int QuickCheck(pic24,w,h,maxcol) byte *pic24; int w,h,maxcol; { /* scans picture until it finds more than 'maxcol' different colors. If it finds more than 'maxcol' colors, it returns '0'. If it DOESN'T, it does the 24-to-8 conversion by simply sticking the colors it found into a colormap, and changing instances of a color in pic24 into colormap indicies (in pic8) */ unsigned long colors[256],col; int i, nc, low, high, mid; byte *p, *pix; if (maxcol>256) maxcol = 256; /* put the first color in the table by hand */ nc = 0; mid = 0; for (i=w*h,p=pic24; i; i--) { col = (((u_long) *p++) << 16); col += (((u_long) *p++) << 8); col += *p++; /* binary search the 'colors' array to see if it's in there */ low = 0; high = nc-1; while (low <= high) { mid = (low+high)/2; if (col < colors[mid]) high = mid - 1; else if (col > colors[mid]) low = mid + 1; else break; } if (high < low) { /* didn't find color in list, add it. */ if (nc>=maxcol) return 0; xvbcopy((char *) &colors[low], (char *) &colors[low+1], (nc - low) * sizeof(u_long)); colors[low] = col; nc++; } } /* run through the data a second time, this time mapping pixel values in pic24 into colormap offsets into 'colors' */ for (i=w*h,p=pic24, pix=pic8; i; i--,pix++) { col = (((u_long) *p++) << 16); col += (((u_long) *p++) << 8); col += *p++; /* binary search the 'colors' array. It *IS* in there */ low = 0; high = nc-1; while (low <= high) { mid = (low+high)/2; if (col < colors[mid]) high = mid - 1; else if (col > colors[mid]) low = mid + 1; else break; } if (high < low) { fprintf(stderr,"QuickCheck: impossible situation!\n"); exit(1); } *pix = mid; } /* and load up the 'desired colormap' */ for (i=0; i >16; gmap[i] = (colors[i]>>8) & 0xff; bmap[i] = colors[i] & 0xff; } return 1; } /***************************************************************/ /* The following code based on code from the 'pbmplus' package */ /* written by Jef Poskanzer */ /***************************************************************/ /* ppmquant.c - quantize the colors in a pixmap down to a specified number ** ** Copyright (C) 1989, 1991 by Jef Poskanzer. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. */ #undef max #define max(a,b) ((a) > (b) ? (a) : (b)) #undef min #define min(a,b) ((a) < (b) ? (a) : (b)) #undef abs #define abs(a) ((a) >= 0 ? (a) : -(a)) #undef odd #define odd(n) ((n) & 1) typedef unsigned char pixval; #define PPM_MAXMAXVAL 255 typedef struct { pixval r, g, b; } pixel; #define PPM_GETR(p) ((p).r) #define PPM_GETG(p) ((p).g) #define PPM_GETB(p) ((p).b) #define PPM_ASSIGN(p,red,grn,blu) \ { (p).r = (red); (p).g = (grn); (p).b = (blu); } #define PPM_EQUAL(p,q) ( (p).r == (q).r && (p).g == (q).g && (p).b == (q).b ) /* Color scaling macro -- to make writing ppmtowhatever easier. */ #define PPM_DEPTH(newp,p,oldmaxval,newmaxval) \ PPM_ASSIGN( (newp), \ (int) PPM_GETR(p) * (newmaxval) / (oldmaxval), \ (int) PPM_GETG(p) * (newmaxval) / (oldmaxval), \ (int) PPM_GETB(p) * (newmaxval) / (oldmaxval) ) /* Luminance macro. */ /* * #define PPM_LUMIN(p) \ * ( 0.299 * PPM_GETR(p) + 0.587 * PPM_GETG(p) + 0.114 * PPM_GETB(p) ) */ /* Luminance macro, using only integer ops. Returns an int (*256) JHB */ #define PPM_LUMIN(p) \ ( 77 * PPM_GETR(p) + 150 * PPM_GETG(p) + 29 * PPM_GETB(p) ) /* Color histogram stuff. */ typedef struct colorhist_item* colorhist_vector; struct colorhist_item { pixel color; int value; }; typedef struct colorhist_list_item* colorhist_list; struct colorhist_list_item { struct colorhist_item ch; colorhist_list next; }; typedef colorhist_list* colorhash_table; typedef struct box* box_vector; struct box { int index; int colors; int sum; }; #define MAXCOLORS 32767 #define CLUSTER_MAXVAL 63 #define LARGE_LUM #define REP_AVERAGE_PIXELS #define FS_SCALE 1024 #define HASH_SIZE 6553 #define ppm_hashpixel(p) ((((int) PPM_GETR(p) * 33023 + \ (int) PPM_GETG(p) * 30013 + \ (int) PPM_GETB(p) * 27011) & 0x7fffffff) \ % HASH_SIZE) /*** function defs ***/ #ifdef __STDC__ static colorhist_vector mediancut(colorhist_vector, int, int, int, int); static int redcompare (colorhist_vector, colorhist_vector); static int greencompare(colorhist_vector, colorhist_vector); static int bluecompare (colorhist_vector, colorhist_vector); static int sumcompare (box_vector, box_vector); static colorhist_vector ppm_computecolorhist(pixel **, int,int,int,int *); static colorhash_table ppm_computecolorhash(pixel **, int,int,int,int *); static colorhist_vector ppm_colorhashtocolorhist(colorhash_table, int); static colorhash_table ppm_alloccolorhash(void); static void ppm_freecolorhist(colorhist_vector); static void ppm_freecolorhash(colorhash_table); #else static colorhist_vector mediancut(); static int redcompare(), greencompare(), bluecompare(); static int sumcompare(); static colorhist_vector ppm_computecolorhist(), ppm_colorhashtocolorhist(); static colorhash_table ppm_computecolorhash(), ppm_alloccolorhash(); static void ppm_freecolorhist(), ppm_freecolorhash(); #endif /****************************************************************************/ static int ppmquant(pic24, cols, rows, newcolors) byte *pic24; int cols, rows, newcolors; { pixel** pixels; register pixel* pP; int row; register int col, limitcol; pixval maxval, newmaxval; int colors; register int index; colorhist_vector chv, colormap; colorhash_table cht; int i; unsigned char *picptr; static char *fn = "ppmquant()"; index = 0; maxval = 255; /* * reformat 24-bit pic24 image (3 bytes per pixel) into 2-dimensional * array of pixel structures */ if (DEBUG) fprintf(stderr,"%s: remapping to ppm-style internal fmt\n", fn); pixels = (pixel **) malloc(rows * sizeof(pixel *)); if (!pixels) FatalError("couldn't allocate 'pixels' array"); for (row=0; row r = *pic24++; pP->g = *pic24++; pP->b = *pic24++; } } if (DEBUG) fprintf(stderr,"%s: done format remapping\n", fn); /* * attempt to make a histogram of the colors, unclustered. * If at first we don't succeed, lower maxval to increase color * coherence and try again. This will eventually terminate, with * maxval at worst 15, since 32^3 is approximately MAXCOLORS. */ for ( ; ; ) { if (DEBUG) fprintf(stderr, "%s: making histogram\n", fn); chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors); if (chv != (colorhist_vector) 0) break; if (DEBUG) fprintf(stderr, "%s: too many colors!\n", fn); newmaxval = maxval / 2; if (DEBUG) fprintf(stderr, "%s: rescaling colors (maxval=%d) %s\n", fn, newmaxval, "to improve clustering"); for (row=0; row next) if (PPM_EQUAL(chl->ch.color, *pP)) {index = chl->ch.value; break;} if (!chl /*index = -1*/) {/* No; search colormap for closest match. */ register int i, r1, g1, b1, r2, g2, b2; register long dist, newdist; r1 = PPM_GETR( *pP ); g1 = PPM_GETG( *pP ); b1 = PPM_GETB( *pP ); dist = 2000000000; for (i=0; i ch.color = *pP; chl->ch.value = index; chl->next = cht[hash]; cht[hash] = chl; } *picptr++ = index; ++col; ++pP; } while (col != limitcol); } /* rescale the colormap and load the XV colormap */ for (i=0; i maxr) maxr = v; v = PPM_GETG( chv[indx + i].color ); if (v < ming) ming = v; if (v > maxg) maxg = v; v = PPM_GETB( chv[indx + i].color ); if (v < minb) minb = v; if (v > maxb) maxb = v; } /* ** Find the largest dimension, and sort by that component. I have ** included two methods for determining the "largest" dimension; ** first by simply comparing the range in RGB space, and second ** by transforming into luminosities before the comparison. You ** can switch which method is used by switching the commenting on ** the LARGE_ defines at the beginning of this source file. */ { /* LARGE_LUM version */ pixel p; int rl, gl, bl; PPM_ASSIGN(p, maxr - minr, 0, 0); rl = PPM_LUMIN(p); PPM_ASSIGN(p, 0, maxg - ming, 0); gl = PPM_LUMIN(p); PPM_ASSIGN(p, 0, 0, maxb - minb); bl = PPM_LUMIN(p); if (rl >= gl && rl >= bl) qsort( (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item), redcompare ); else if (gl >= bl) qsort( (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item), greencompare ); else qsort( (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item), bluecompare ); } /* ** Now find the median based on the counts, so that about half the ** pixels (not colors, pixels) are in each subdivision. */ lowersum = chv[indx].value; halfsum = sm / 2; for (i=1; i = halfsum) break; lowersum += chv[indx + i].value; } /* ** Split the box, and sort to bring the biggest boxes to the top. */ bv[bi].colors = i; bv[bi].sum = lowersum; bv[boxes].index = indx + i; bv[boxes].colors = clrs - i; bv[boxes].sum = sm - lowersum; ++boxes; qsort( (char*) bv, boxes, sizeof(struct box), sumcompare ); } /* while (boxes ... */ /* ** Ok, we've got enough boxes. Now choose a representative color for ** each box. There are a number of possible ways to make this choice. ** One would be to choose the center of the box; this ignores any structure ** within the boxes. Another method would be to average all the colors in ** the box - this is the method specified in Heckbert's paper. A third ** method is to average all the pixels in the box. You can switch which ** method is used by switching the commenting on the REP_ defines at ** the beginning of this source file. */ for (bi=0; bi maxval) r = maxval; /* avoid math errors */ g = g / sum; if (g>maxval) g = maxval; b = b / sum; if (b>maxval) b = maxval; PPM_ASSIGN( colormap[bi].color, r, g, b ); } free(bv); return colormap; } /**********************************/ static int redcompare(ch1, ch2) colorhist_vector ch1, ch2; { return (int) PPM_GETR( ch1->color ) - (int) PPM_GETR( ch2->color ); } /**********************************/ static int greencompare(ch1, ch2) colorhist_vector ch1, ch2; { return (int) PPM_GETG( ch1->color ) - (int) PPM_GETG( ch2->color ); } /**********************************/ static int bluecompare( ch1, ch2 ) colorhist_vector ch1, ch2; { return (int) PPM_GETB( ch1->color ) - (int) PPM_GETB( ch2->color ); } /**********************************/ static int sumcompare( b1, b2 ) box_vector b1, b2; { return b2->sum - b1->sum; } /****************************************************************************/ static colorhist_vector ppm_computecolorhist(pixels, cols, rows, maxcolors, colorsP) pixel** pixels; int cols, rows, maxcolors; int* colorsP; { colorhash_table cht; colorhist_vector chv; cht = ppm_computecolorhash(pixels, cols, rows, maxcolors, colorsP); if (!cht) return (colorhist_vector) 0; chv = ppm_colorhashtocolorhist(cht, maxcolors); ppm_freecolorhash(cht); return chv; } /****************************************************************************/ static colorhash_table ppm_computecolorhash(pixels, cols, rows, maxcolors, colorsP ) pixel** pixels; int cols, rows, maxcolors; int* colorsP; { colorhash_table cht; register pixel* pP; colorhist_list chl; int col, row, hash; cht = ppm_alloccolorhash( ); *colorsP = 0; /* Go through the entire image, building a hash table of colors. */ for (row=0; row next) if (PPM_EQUAL(chl->ch.color, *pP)) break; if (chl != (colorhist_list) 0) ++(chl->ch.value); else { if ((*colorsP)++ > maxcolors) { ppm_freecolorhash(cht); return (colorhash_table) 0; } chl = (colorhist_list) malloc(sizeof(struct colorhist_list_item)); if (!chl) FatalError("ran out of memory computing hash table"); chl->ch.color = *pP; chl->ch.value = 1; chl->next = cht[hash]; cht[hash] = chl; } } return cht; } /****************************************************************************/ static colorhash_table ppm_alloccolorhash() { colorhash_table cht; int i; cht = (colorhash_table) malloc( HASH_SIZE * sizeof(colorhist_list) ); if (!cht) FatalError("ran out of memory allocating hash table"); for (i=0; i next) { /* Add the new entry. */ chv[j] = chl->ch; ++j; } return chv; } /****************************************************************************/ static void ppm_freecolorhist( chv ) colorhist_vector chv; { free( (char*) chv ); } /****************************************************************************/ static void ppm_freecolorhash( cht ) colorhash_table cht; { int i; colorhist_list chl, chlnext; for (i=0; i next; free( (char*) chl ); } free( (char*) cht ); }