home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Photo CD Demo 1
/
Demo.bin
/
gems
/
gemsii
/
quantize.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-09-22
|
9KB
|
362 lines
/**********************************************************************
C Implementation of Wu's Color Quantizer
(see Graphics Gems vol. II, pp. 126-133)
Author: Xiaolin Wu
Dept. of Computer Science
Univ. of Western Ontario
London, Ontario N6A 5B7
wu@csd.uwo.ca
Algorithm: Greedy orthogonal bipartition of RGB space for variance
minimization aided by inclusion-exclusion tricks.
For speed no nearest neighbor search is done. Slightly
better performance can be expected by more sophisticated
but more expensive versions.
Free to distribute, comments and suggestions are appreciated.
**********************************************************************/
#include<stdio.h>
#define MAXCOLOR 256
#define RED 2
#define GREEN 1
#define BLUE 0
struct box {
int r0;
int r1;
int g0;
int g1;
int b0;
int b1;
int vol;
};
float m2[33][33][33];
long int wt[33][33][33], mr[33][33][33], mg[33][33][33], mb[33][33][33];
unsigned char *Ir, *Ig, *Ib;
int size; /*image size*/
int K; /*color look-up table size*/
unsigned short int *Qadd;
Hist3d(vwt, vmr, vmg, vmb, m2) /* build 3-D color density */
long int *vwt, *vmr, *vmg, *vmb;
float *m2;
{
register int ind, r, g, b;
int inr, ing, inb, table[256];
register long int i;
for(i=0; i<256; ++i) table[i]=i*i;
Qadd = (unsigned short int *)malloc(sizeof(short int)*size);
if (Qadd==NULL) {printf("Not enough space\n"); exit(1);}
for(i=0; i<size; ++i){
r = Ir[i]; g = Ig[i]; b = Ib[i];
inr=(r>>3)+1;
ing=(g>>3)+1;
inb=(b>>3)+1;
Qadd[i]=ind=(inr<<10)+(inr<<6)+inr+(ing<<5)+ing+inb;
/*[inr][ing][inb]*/
++vwt[ind];
vmr[ind] += r;
vmg[ind] += g;
vmb[ind] += b;
m2[ind] += (float)(table[r]+table[g]+table[b]);
}
}
M3d(vwt, vmr, vmg, vmb, m2) /* compute cumulative moments. */
long int *vwt, *vmr, *vmg, *vmb;
float *m2;
{
register unsigned short int ind1, ind2;
register unsigned char i, r, g, b;
long int line, line_r, line_g, line_b,
area[33], area_r[33], area_g[33], area_b[33];
float line2, area2[33];
for(r=1; r<=32; ++r){
for(i=0; i<=32; ++i)
area2[i]=area[i]=area_r[i]=area_g[i]=area_b[i]=0;
for(g=1; g<=32; ++g){
line2 = line = line_r = line_g = line_b = 0;
for(b=1; b<=32; ++b){
ind1 = (r<<10) + (r<<6) + r + (g<<5) + g + b; /* [r][g][b] */
line += vwt[ind1];
line_r += vmr[ind1];
line_g += vmg[ind1];
line_b += vmb[ind1];
line2 += m2[ind1];
area[b] += line;
area_r[b] += line_r;
area_g[b] += line_g;
area_b[b] += line_b;
area2[b] += line2;
ind2 = ind1 - 1089; /* [r-1][g][b] */
vwt[ind1] = vwt[ind2] + area[b];
vmr[ind1] = vmr[ind2] + area_r[b];
vmg[ind1] = vmg[ind2] + area_g[b];
vmb[ind1] = vmb[ind2] + area_b[b];
m2[ind1] = m2[ind2] + area2[b];
}
}
}
}
long int Vol(cube, mmt)
struct box *cube;
long int mmt[33][33][33];
{
return( mmt[cube->r1][cube->g1][cube->b1]
-mmt[cube->r1][cube->g1][cube->b0]
-mmt[cube->r1][cube->g0][cube->b1]
+mmt[cube->r1][cube->g0][cube->b0]
-mmt[cube->r0][cube->g1][cube->b1]
+mmt[cube->r0][cube->g1][cube->b0]
+mmt[cube->r0][cube->g0][cube->b1]
-mmt[cube->r0][cube->g0][cube->b0] );
}
long int Bottom(cube, dir, mmt)
struct box *cube;
unsigned char dir;
long int mmt[33][33][33];
{
switch(dir){
case RED:
return( -mmt[cube->r0][cube->g1][cube->b1]
+mmt[cube->r0][cube->g1][cube->b0]
+mmt[cube->r0][cube->g0][cube->b1]
-mmt[cube->r0][cube->g0][cube->b0] );
break;
case GREEN:
return( -mmt[cube->r1][cube->g0][cube->b1]
+mmt[cube->r1][cube->g0][cube->b0]
+mmt[cube->r0][cube->g0][cube->b1]
-mmt[cube->r0][cube->g0][cube->b0] );
break;
case BLUE:
return( -mmt[cube->r1][cube->g1][cube->b0]
+mmt[cube->r1][cube->g0][cube->b0]
+mmt[cube->r0][cube->g1][cube->b0]
-mmt[cube->r0][cube->g0][cube->b0] );
break;
}
}
long int Top(cube, dir, pos, mmt)
struct box *cube;
unsigned char dir;
int pos;
long int mmt[33][33][33];
{
switch(dir){
case RED:
return( mmt[pos][cube->g1][cube->b1]
-mmt[pos][cube->g1][cube->b0]
-mmt[pos][cube->g0][cube->b1]
+mmt[pos][cube->g0][cube->b0] );
break;
case GREEN:
return( mmt[cube->r1][pos][cube->b1]
-mmt[cube->r1][pos][cube->b0]
-mmt[cube->r0][pos][cube->b1]
+mmt[cube->r0][pos][cube->b0] );
break;
case BLUE:
return( mmt[cube->r1][cube->g1][pos]
-mmt[cube->r1][cube->g0][pos]
-mmt[cube->r0][cube->g1][pos]
+mmt[cube->r0][cube->g0][pos] );
break;
}
}
float Var(cube)
struct box *cube;
{
float dr, dg, db, xx;
dr = Vol(cube, mr);
dg = Vol(cube, mg);
db = Vol(cube, mb);
xx = m2[cube->r1][cube->g1][cube->b1]
-m2[cube->r1][cube->g1][cube->b0]
-m2[cube->r1][cube->g0][cube->b1]
+m2[cube->r1][cube->g0][cube->b0]
-m2[cube->r0][cube->g1][cube->b1]
+m2[cube->r0][cube->g1][cube->b0]
+m2[cube->r0][cube->g0][cube->b1]
-m2[cube->r0][cube->g0][cube->b0];
return( xx - (dr*dr+dg*dg+db*db)/(float)Vol(cube,wt) );
}
float Maximize(cube, dir, first, last, cut,
whole_r, whole_g, whole_b, whole_w)
struct box *cube;
unsigned char dir;
int first, last, *cut;
long int whole_r, whole_g, whole_b, whole_w;
{
register long int half_r, half_g, half_b, half_w;
long int base_r, base_g, base_b, base_w;
register int i;
register float temp, max;
base_r = Bottom(cube, dir, mr);
base_g = Bottom(cube, dir, mg);
base_b = Bottom(cube, dir, mb);
base_w = Bottom(cube, dir, wt);
max = 0.0;
for(i=first; i<last; ++i){
half_r = base_r + Top(cube, dir, i, mr);
half_g = base_g + Top(cube, dir, i, mg);
half_b = base_b + Top(cube, dir, i, mb);
half_w = base_w + Top(cube, dir, i, wt);
temp = ((float)half_r*half_r + (float)half_g*half_g +
(float)half_b*half_b)/half_w;
half_r = whole_r - half_r;
half_g = whole_g - half_g;
half_b = whole_b - half_b;
half_w = whole_w - half_w;
temp = ((float)half_r*half_r + (float)half_g*half_g +
(float)half_b*half_b)/half_w + temp;
if (temp > max) {max=temp; *cut=i;}
}
return(max);
}
Cut(set1, set2)
struct box *set1, *set2;
{
unsigned char dir;
int cutr, cutg, cutb;
float maxr, maxg, maxb;
long int whole_r, whole_g, whole_b, whole_w;
whole_r = Vol(set1, mr);
whole_g = Vol(set1, mg);
whole_b = Vol(set1, mb);
whole_w = Vol(set1, wt);
maxr = Maximize(set1, RED, set1->r0+1, set1->r1, &cutr,
whole_r, whole_g, whole_b, whole_w);
maxg = Maximize(set1, GREEN, set1->g0+1, set1->g1, &cutg,
whole_r, whole_g, whole_b, whole_w);
maxb = Maximize(set1, BLUE, set1->b0+1, set1->b1, &cutb,
whole_r, whole_g, whole_b, whole_w);
if( (maxr>=maxg)&&(maxr>=maxb) )
dir = RED;
else
if( (maxg>=maxr)&&(maxg>=maxb) )
dir = GREEN;
else
dir = BLUE;
set2->r1 = set1->r1;
set2->g1 = set1->g1;
set2->b1 = set1->b1;
switch (dir){
case RED:
set2->r0 = set1->r1 = cutr;
set2->g0 = set1->g0;
set2->b0 = set1->b0;
break;
case GREEN:
set2->g0 = set1->g1 = cutg;
set2->r0 = set1->r0;
set2->b0 = set1->b0;
break;
case BLUE:
set2->b0 = set1->b1 = cutb;
set2->r0 = set1->r0;
set2->g0 = set1->g0;
break;
}
set1->vol=(set1->r1-set1->r0)*(set1->g1-set1->g0)*(set1->b1-set1->b0);
set2->vol=(set2->r1-set2->r0)*(set2->g1-set2->g0)*(set2->b1-set2->b0);
}
Mark(cube, label, tag)
struct box *cube;
int label;
unsigned char *tag;
{
register int r, g, b;
for(r=cube->r0+1; r<=cube->r1; ++r)
for(g=cube->g0+1; g<=cube->g1; ++g)
for(b=cube->b0+1; b<=cube->b1; ++b)
tag[(r<<10) + (r<<6) + r + (g<<5) + g + b] = label;
}
main()
{
struct box cube[MAXCOLOR];
unsigned char *tag;
unsigned char lut_r[MAXCOLOR], lut_g[MAXCOLOR], lut_b[MAXCOLOR];
int next;
register long int i, weight;
register int k;
float vv[MAXCOLOR], temp;
/* input R,G,B components into Ir, Ig, Ib;
set size to width*height */
printf("no. of colors:\n");
scanf("%d", &K);
Hist3d(wt, mr, mg, mb, m2); printf("Histogram done\n");
free(Ig); free(Ib); free(Ir);
M3d(wt, mr, mg, mb, m2); printf("Moments done\n");
cube[0].r0 = cube[0].g0 = cube[0].b0 = 0;
cube[0].r1 = cube[0].g1 = cube[0].b1 = 32;
next = 0;
for(i=1; i<K; ++i){
Cut(&cube[next], &cube[i]);
vv[next] = (cube[next].vol>2) ? Var(&cube[next]) : 0.0;
vv[i] = (cube[i].vol>2) ? Var(&cube[i]) : 0.0;
next = 0; temp = vv[0];
for(k=1; k<=i; ++k)
if (vv[k] > temp) {
temp = vv[k]; next = k;
}
}
printf("Partition done\n");
/* the space for array m2 can be freed now */
tag = (unsigned char *)malloc(33*33*33);
if (tag==NULL) {printf("Not enough space\n"); exit(1);}
for(k=0; k<K; ++k){
Mark(&cube[k], k, tag);
weight = Vol(&cube[k], wt);
lut_r[k] = Vol(&cube[k], mr) / weight;
lut_g[k] = Vol(&cube[k], mg) / weight;
lut_b[k] = Vol(&cube[k], mb) / weight;
}
for(i=0; i<size; ++i) Qadd[i] = tag[Qadd[i]];
/* output lut_r, lut_g, lut_b as color look-up table contents,
Qadd as the quantized image (array of table addresses). */
}