1 |
greg |
1.1 |
#ifndef lint |
2 |
greg |
2.5 |
static const char RCSid[] = "$Id: cut.c,v 2.4 2004/03/28 20:33:13 schorsch Exp $"; |
3 |
greg |
1.1 |
#endif |
4 |
greg |
1.2 |
/* |
5 |
greg |
1.1 |
CUT - Median bisection (k-d tree) algorithm for color image quantization |
6 |
|
|
|
7 |
|
|
flaw: doesn't always create as many colors as requested |
8 |
|
|
because the binary subdivision is always done in a balanced tree and when a box |
9 |
|
|
contains only one color it's unsplittable |
10 |
|
|
|
11 |
|
|
Paul Heckbert |
12 |
|
|
*/ |
13 |
|
|
|
14 |
|
|
#include "ciq.h" |
15 |
|
|
|
16 |
|
|
struct index {short *o; int freq;}; |
17 |
|
|
/* offset and pixel count for a box */ |
18 |
|
|
|
19 |
|
|
struct pail {struct plum *first; int freak;}; |
20 |
|
|
/* each pail is a bucket containing plums */ |
21 |
|
|
|
22 |
|
|
struct plum {struct plum *next; int code;}; |
23 |
|
|
/* a color in a pail's linked list */ |
24 |
|
|
|
25 |
|
|
static short off[len]; |
26 |
|
|
/* color codes: offsets of colors in hist array */ |
27 |
|
|
|
28 |
schorsch |
2.4 |
static void splitbox(struct index *ii, struct index *io); |
29 |
greg |
1.1 |
|
30 |
schorsch |
2.4 |
|
31 |
|
|
int |
32 |
|
|
makecm( /* subdivide colorspace to generate a colormap*/ |
33 |
|
|
int nw, |
34 |
|
|
int *na /* number of colors wanted, actual */ |
35 |
|
|
) |
36 |
greg |
1.1 |
{ |
37 |
|
|
int i,m,n,freq,weight,sr,sg,sb; |
38 |
|
|
short *o; |
39 |
|
|
struct index index[257]; /* pointers into off */ |
40 |
|
|
/* index[i].o is pointer to code for first color in box i */ |
41 |
|
|
|
42 |
|
|
for (o=off, *na=i=0; i<len; i++) |
43 |
|
|
if (hist[i]) { |
44 |
|
|
(*na)++; |
45 |
|
|
*o++ = i; /* initialize offsets */ |
46 |
|
|
} |
47 |
|
|
index[0].o = off; /* start with one big box which contains all *na colors */ |
48 |
|
|
index[0].freq = xmax*ymax; /* and (sum of hist[i]) pixels */ |
49 |
|
|
index[1].o = off+*na; |
50 |
|
|
|
51 |
|
|
/* breadth-first binary subdivision: try to make nw boxes */ |
52 |
|
|
for (m=1; m<nw; m<<=1) { |
53 |
|
|
/*printf("%d ",m);*/ |
54 |
|
|
index[m<<1].o = index[m].o; |
55 |
|
|
for (i=m-1; i>=0; --i) splitbox(index+i,index+(i<<1)); |
56 |
|
|
} |
57 |
|
|
for (n=i=0; i<m && n<nw; i++) if (index[i+1].o>index[i].o) { |
58 |
|
|
sr = sg = sb = (freq = index[i].freq)>>1; |
59 |
|
|
for (o=index[i].o; o<index[i+1].o; o++) { |
60 |
|
|
weight = hist[*o]; |
61 |
|
|
/* find weighted average of colors in box i */ |
62 |
|
|
sr += red(*o)*weight; |
63 |
|
|
sg += gre(*o)*weight; |
64 |
|
|
sb += blu(*o)*weight; |
65 |
|
|
} |
66 |
|
|
color[0][n] = sr/freq; |
67 |
|
|
color[1][n] = sg/freq; |
68 |
|
|
color[2][n] = sb/freq; |
69 |
|
|
/* printf("color[%d]=(%3d,%3d,%3d) freq=%d\n", |
70 |
|
|
n,color[0][n],color[1][n],color[2][n],freq); */ |
71 |
|
|
n++; |
72 |
|
|
} |
73 |
|
|
/*printf("[%d empty boxes] from %d to %d colors\n",nw-n,*na,n);*/ |
74 |
|
|
return n; |
75 |
|
|
} |
76 |
|
|
|
77 |
schorsch |
2.4 |
static void |
78 |
|
|
splitbox( /* split a box in two: half of the pixels from */ |
79 |
|
|
struct index *ii, /* box ii will go into io, the other half into io+1 */ |
80 |
|
|
struct index *io |
81 |
|
|
) |
82 |
greg |
1.1 |
{ |
83 |
|
|
register short *o,*o1,*o2; |
84 |
|
|
register int shift,count,k,freq,r,g,b,r1,g1,b1,r2,g2,b2; |
85 |
|
|
struct pail pail[32],*p,*q; /* buckets for sorting colors */ |
86 |
|
|
struct plum plum[len],*pl=plum,*u; /* colors */ |
87 |
|
|
|
88 |
|
|
o1 = ii[0].o; |
89 |
|
|
o2 = ii[1].o; |
90 |
|
|
freq = ii[0].freq; /* number of pixels with color in this box */ |
91 |
|
|
r1 = g1 = b1 = 256; r2 = g2 = b2 = -1; |
92 |
|
|
for (o=o1; o<o2; o++) { |
93 |
|
|
r = red(*o); |
94 |
|
|
g = gre(*o); |
95 |
|
|
b = blu(*o); |
96 |
|
|
if (r<r1) r1 = r; if (r>r2) r2 = r; /* find min&max r, g, and b */ |
97 |
|
|
if (g<g1) g1 = g; if (g>g2) g2 = g; |
98 |
|
|
if (b<b1) b1 = b; if (b>b2) b2 = b; |
99 |
|
|
} |
100 |
|
|
|
101 |
|
|
/* adaptive partitioning: find dominant dimension */ |
102 |
|
|
shift = r2-r1>g2-g1?r2-r1>=b2-b1?10:0:g2-g1>=b2-b1?5:0; |
103 |
|
|
/* printf("splitbox %3d-%3d %3dx%3dx%3d %c ", |
104 |
|
|
o1-off,o2-off,r2-r1,g2-g1,b2-b1,shift==10?'R':shift==5?'G':'B'); */ |
105 |
|
|
|
106 |
|
|
for (p=pail, k=0; k<32; k++, p++) { /* start with empty pails */ |
107 |
|
|
p->first = 0; |
108 |
|
|
p->freak = 0; |
109 |
|
|
} |
110 |
|
|
for (o=o1; o<o2; o++) { |
111 |
|
|
p = pail+(*o>>shift&31); /* find appropriate pail */ |
112 |
|
|
pl->code = *o; |
113 |
|
|
pl->next = p->first; |
114 |
|
|
p->first = pl++; /* add new plum to pail */ |
115 |
|
|
p->freak += hist[*o]; |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
/* find median along dominant dimension */ |
119 |
|
|
for (count=freq>>1, p=pail; count>0; p++) count -= p->freak; |
120 |
|
|
|
121 |
|
|
if (p>pail && count+(p-1)->freak<-count) /* back up one? */ |
122 |
|
|
count += (--p)->freak; |
123 |
|
|
|
124 |
|
|
io[1].o = o1; /* in case of empty box */ |
125 |
|
|
for (o=o1, q=pail; o<o2;) { /* dump the pails to reorder colors in box */ |
126 |
|
|
for (u=q->first; u; u=u->next) *o++ = u->code; |
127 |
|
|
if (++q==p) io[1].o = o; /* place partition at offset o */ |
128 |
|
|
} |
129 |
|
|
io[0].o = o1; |
130 |
|
|
io[0].freq = (freq>>1)-count; |
131 |
schorsch |
2.3 |
io[1].freq = ((freq+1)>>1)+count; |
132 |
greg |
1.1 |
/* printf(" at %3d %d=%d+%d\n",io[1].o-off,freq,io[0].freq,io[1].freq); */ |
133 |
|
|
} |