/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/WuQuantizer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /////////////////////////////////////////////////////////////////////// |
2 | | // C Implementation of Wu's Color Quantizer (v. 2) |
3 | | // (see Graphics Gems vol. II, pp. 126-133) |
4 | | // |
5 | | // Author: Xiaolin Wu |
6 | | // Dept. of Computer Science |
7 | | // Univ. of Western Ontario |
8 | | // London, Ontario N6A 5B7 |
9 | | // wu@csd.uwo.ca |
10 | | // |
11 | | // Algorithm: Greedy orthogonal bipartition of RGB space for variance |
12 | | // minimization aided by inclusion-exclusion tricks. |
13 | | // For speed no nearest neighbor search is done. Slightly |
14 | | // better performance can be expected by more sophisticated |
15 | | // but more expensive versions. |
16 | | // |
17 | | // The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of |
18 | | // additional documentation and a cure to a previous bug. |
19 | | // |
20 | | // Free to distribute, comments and suggestions are appreciated. |
21 | | /////////////////////////////////////////////////////////////////////// |
22 | | |
23 | | /////////////////////////////////////////////////////////////////////// |
24 | | // History |
25 | | // ------- |
26 | | // July 2000: C++ Implementation of Wu's Color Quantizer |
27 | | // and adaptation for the FreeImage 2 Library |
28 | | // Author: Hervé Drolon (drolon@infonie.fr) |
29 | | // March 2004: Adaptation for the FreeImage 3 library (port to big endian processors) |
30 | | // Author: Hervé Drolon (drolon@infonie.fr) |
31 | | /////////////////////////////////////////////////////////////////////// |
32 | | |
33 | | #include "Quantizers.h" |
34 | | #include "FreeImage.h" |
35 | | #include "Utilities.h" |
36 | | |
37 | | /////////////////////////////////////////////////////////////////////// |
38 | | |
39 | | // Size of a 3D array : 33 x 33 x 33 |
40 | 0 | #define SIZE_3D 35937 |
41 | | |
42 | | // 3D array indexation |
43 | 0 | #define INDEX(r, g, b) ((r << 10) + (r << 6) + r + (g << 5) + g + b) |
44 | | |
45 | | #define MAXCOLOR 256 |
46 | | |
47 | | // Constructor / Destructor |
48 | | |
49 | 0 | WuQuantizer::WuQuantizer(FIBITMAP *dib) { |
50 | 0 | width = FreeImage_GetWidth(dib); |
51 | 0 | height = FreeImage_GetHeight(dib); |
52 | 0 | pitch = FreeImage_GetPitch(dib); |
53 | 0 | m_dib = dib; |
54 | |
|
55 | 0 | gm2 = NULL; |
56 | 0 | wt = mr = mg = mb = NULL; |
57 | 0 | Qadd = NULL; |
58 | | |
59 | | // Allocate 3D arrays |
60 | 0 | gm2 = (float*)malloc(SIZE_3D * sizeof(float)); |
61 | 0 | wt = (LONG*)malloc(SIZE_3D * sizeof(LONG)); |
62 | 0 | mr = (LONG*)malloc(SIZE_3D * sizeof(LONG)); |
63 | 0 | mg = (LONG*)malloc(SIZE_3D * sizeof(LONG)); |
64 | 0 | mb = (LONG*)malloc(SIZE_3D * sizeof(LONG)); |
65 | | |
66 | | // Allocate Qadd |
67 | 0 | Qadd = (WORD *)malloc(sizeof(WORD) * width * height); |
68 | |
|
69 | 0 | if(!gm2 || !wt || !mr || !mg || !mb || !Qadd) { |
70 | 0 | if(gm2) free(gm2); |
71 | 0 | if(wt) free(wt); |
72 | 0 | if(mr) free(mr); |
73 | 0 | if(mg) free(mg); |
74 | 0 | if(mb) free(mb); |
75 | 0 | if(Qadd) free(Qadd); |
76 | 0 | throw FI_MSG_ERROR_MEMORY; |
77 | 0 | } |
78 | 0 | memset(gm2, 0, SIZE_3D * sizeof(float)); |
79 | 0 | memset(wt, 0, SIZE_3D * sizeof(LONG)); |
80 | 0 | memset(mr, 0, SIZE_3D * sizeof(LONG)); |
81 | 0 | memset(mg, 0, SIZE_3D * sizeof(LONG)); |
82 | 0 | memset(mb, 0, SIZE_3D * sizeof(LONG)); |
83 | 0 | memset(Qadd, 0, sizeof(WORD) * width * height); |
84 | 0 | } |
85 | | |
86 | 0 | WuQuantizer::~WuQuantizer() { |
87 | 0 | if(gm2) free(gm2); |
88 | 0 | if(wt) free(wt); |
89 | 0 | if(mr) free(mr); |
90 | 0 | if(mg) free(mg); |
91 | 0 | if(mb) free(mb); |
92 | 0 | if(Qadd) free(Qadd); |
93 | 0 | } |
94 | | |
95 | | |
96 | | // Histogram is in elements 1..HISTSIZE along each axis, |
97 | | // element 0 is for base or marginal value |
98 | | // NB: these must start out 0! |
99 | | |
100 | | // Build 3-D color histogram of counts, r/g/b, c^2 |
101 | | void |
102 | 0 | WuQuantizer::Hist3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2, int ReserveSize, RGBQUAD *ReservePalette) { |
103 | 0 | int ind = 0; |
104 | 0 | int inr, ing, inb, table[256]; |
105 | 0 | int i; |
106 | 0 | unsigned y, x; |
107 | |
|
108 | 0 | for(i = 0; i < 256; i++) |
109 | 0 | table[i] = i * i; |
110 | |
|
111 | 0 | if (FreeImage_GetBPP(m_dib) == 24) { |
112 | 0 | for(y = 0; y < height; y++) { |
113 | 0 | BYTE *bits = FreeImage_GetScanLine(m_dib, y); |
114 | |
|
115 | 0 | for(x = 0; x < width; x++) { |
116 | 0 | inr = (bits[FI_RGBA_RED] >> 3) + 1; |
117 | 0 | ing = (bits[FI_RGBA_GREEN] >> 3) + 1; |
118 | 0 | inb = (bits[FI_RGBA_BLUE] >> 3) + 1; |
119 | 0 | ind = INDEX(inr, ing, inb); |
120 | 0 | Qadd[y*width + x] = (WORD)ind; |
121 | | // [inr][ing][inb] |
122 | 0 | vwt[ind]++; |
123 | 0 | vmr[ind] += bits[FI_RGBA_RED]; |
124 | 0 | vmg[ind] += bits[FI_RGBA_GREEN]; |
125 | 0 | vmb[ind] += bits[FI_RGBA_BLUE]; |
126 | 0 | m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]); |
127 | 0 | bits += 3; |
128 | 0 | } |
129 | 0 | } |
130 | 0 | } else { |
131 | 0 | for(y = 0; y < height; y++) { |
132 | 0 | BYTE *bits = FreeImage_GetScanLine(m_dib, y); |
133 | |
|
134 | 0 | for(x = 0; x < width; x++) { |
135 | 0 | inr = (bits[FI_RGBA_RED] >> 3) + 1; |
136 | 0 | ing = (bits[FI_RGBA_GREEN] >> 3) + 1; |
137 | 0 | inb = (bits[FI_RGBA_BLUE] >> 3) + 1; |
138 | 0 | ind = INDEX(inr, ing, inb); |
139 | 0 | Qadd[y*width + x] = (WORD)ind; |
140 | | // [inr][ing][inb] |
141 | 0 | vwt[ind]++; |
142 | 0 | vmr[ind] += bits[FI_RGBA_RED]; |
143 | 0 | vmg[ind] += bits[FI_RGBA_GREEN]; |
144 | 0 | vmb[ind] += bits[FI_RGBA_BLUE]; |
145 | 0 | m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]); |
146 | 0 | bits += 4; |
147 | 0 | } |
148 | 0 | } |
149 | 0 | } |
150 | |
|
151 | 0 | if( ReserveSize > 0 ) { |
152 | 0 | int max = 0; |
153 | 0 | for(i = 0; i < SIZE_3D; i++) { |
154 | 0 | if( vwt[i] > max ) max = vwt[i]; |
155 | 0 | } |
156 | 0 | max++; |
157 | 0 | for(i = 0; i < ReserveSize; i++) { |
158 | 0 | inr = (ReservePalette[i].rgbRed >> 3) + 1; |
159 | 0 | ing = (ReservePalette[i].rgbGreen >> 3) + 1; |
160 | 0 | inb = (ReservePalette[i].rgbBlue >> 3) + 1; |
161 | 0 | ind = INDEX(inr, ing, inb); |
162 | 0 | wt[ind] = max; |
163 | 0 | mr[ind] = max * ReservePalette[i].rgbRed; |
164 | 0 | mg[ind] = max * ReservePalette[i].rgbGreen; |
165 | 0 | mb[ind] = max * ReservePalette[i].rgbBlue; |
166 | 0 | gm2[ind] = (float)max * (float)(table[ReservePalette[i].rgbRed] + table[ReservePalette[i].rgbGreen] + table[ReservePalette[i].rgbBlue]); |
167 | 0 | } |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | | |
172 | | // At conclusion of the histogram step, we can interpret |
173 | | // wt[r][g][b] = sum over voxel of P(c) |
174 | | // mr[r][g][b] = sum over voxel of r*P(c) , similarly for mg, mb |
175 | | // m2[r][g][b] = sum over voxel of c^2*P(c) |
176 | | // Actually each of these should be divided by 'ImageSize' to give the usual |
177 | | // interpretation of P() as ranging from 0 to 1, but we needn't do that here. |
178 | | |
179 | | |
180 | | // We now convert histogram into moments so that we can rapidly calculate |
181 | | // the sums of the above quantities over any desired box. |
182 | | |
183 | | // Compute cumulative moments |
184 | | void |
185 | 0 | WuQuantizer::M3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2) { |
186 | 0 | unsigned ind1, ind2; |
187 | 0 | BYTE i, r, g, b; |
188 | 0 | LONG line, line_r, line_g, line_b; |
189 | 0 | LONG area[33], area_r[33], area_g[33], area_b[33]; |
190 | 0 | float line2, area2[33]; |
191 | |
|
192 | 0 | for(r = 1; r <= 32; r++) { |
193 | 0 | for(i = 0; i <= 32; i++) { |
194 | 0 | area2[i] = 0; |
195 | 0 | area[i] = area_r[i] = area_g[i] = area_b[i] = 0; |
196 | 0 | } |
197 | 0 | for(g = 1; g <= 32; g++) { |
198 | 0 | line2 = 0; |
199 | 0 | line = line_r = line_g = line_b = 0; |
200 | 0 | for(b = 1; b <= 32; b++) { |
201 | 0 | ind1 = INDEX(r, g, b); // [r][g][b] |
202 | 0 | line += vwt[ind1]; |
203 | 0 | line_r += vmr[ind1]; |
204 | 0 | line_g += vmg[ind1]; |
205 | 0 | line_b += vmb[ind1]; |
206 | 0 | line2 += m2[ind1]; |
207 | 0 | area[b] += line; |
208 | 0 | area_r[b] += line_r; |
209 | 0 | area_g[b] += line_g; |
210 | 0 | area_b[b] += line_b; |
211 | 0 | area2[b] += line2; |
212 | 0 | ind2 = ind1 - 1089; // [r-1][g][b] |
213 | 0 | vwt[ind1] = vwt[ind2] + area[b]; |
214 | 0 | vmr[ind1] = vmr[ind2] + area_r[b]; |
215 | 0 | vmg[ind1] = vmg[ind2] + area_g[b]; |
216 | 0 | vmb[ind1] = vmb[ind2] + area_b[b]; |
217 | 0 | m2[ind1] = m2[ind2] + area2[b]; |
218 | 0 | } |
219 | 0 | } |
220 | 0 | } |
221 | 0 | } |
222 | | |
223 | | // Compute sum over a box of any given statistic |
224 | | LONG |
225 | 0 | WuQuantizer::Vol( Box *cube, LONG *mmt ) { |
226 | 0 | return( mmt[INDEX(cube->r1, cube->g1, cube->b1)] |
227 | 0 | - mmt[INDEX(cube->r1, cube->g1, cube->b0)] |
228 | 0 | - mmt[INDEX(cube->r1, cube->g0, cube->b1)] |
229 | 0 | + mmt[INDEX(cube->r1, cube->g0, cube->b0)] |
230 | 0 | - mmt[INDEX(cube->r0, cube->g1, cube->b1)] |
231 | 0 | + mmt[INDEX(cube->r0, cube->g1, cube->b0)] |
232 | 0 | + mmt[INDEX(cube->r0, cube->g0, cube->b1)] |
233 | 0 | - mmt[INDEX(cube->r0, cube->g0, cube->b0)] ); |
234 | 0 | } |
235 | | |
236 | | // The next two routines allow a slightly more efficient calculation |
237 | | // of Vol() for a proposed subbox of a given box. The sum of Top() |
238 | | // and Bottom() is the Vol() of a subbox split in the given direction |
239 | | // and with the specified new upper bound. |
240 | | |
241 | | |
242 | | // Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1 |
243 | | // (depending on dir) |
244 | | |
245 | | LONG |
246 | 0 | WuQuantizer::Bottom(Box *cube, BYTE dir, LONG *mmt) { |
247 | 0 | switch(dir) |
248 | 0 | { |
249 | 0 | case FI_RGBA_RED: |
250 | 0 | return( - mmt[INDEX(cube->r0, cube->g1, cube->b1)] |
251 | 0 | + mmt[INDEX(cube->r0, cube->g1, cube->b0)] |
252 | 0 | + mmt[INDEX(cube->r0, cube->g0, cube->b1)] |
253 | 0 | - mmt[INDEX(cube->r0, cube->g0, cube->b0)] ); |
254 | 0 | break; |
255 | 0 | case FI_RGBA_GREEN: |
256 | 0 | return( - mmt[INDEX(cube->r1, cube->g0, cube->b1)] |
257 | 0 | + mmt[INDEX(cube->r1, cube->g0, cube->b0)] |
258 | 0 | + mmt[INDEX(cube->r0, cube->g0, cube->b1)] |
259 | 0 | - mmt[INDEX(cube->r0, cube->g0, cube->b0)] ); |
260 | 0 | break; |
261 | 0 | case FI_RGBA_BLUE: |
262 | 0 | return( - mmt[INDEX(cube->r1, cube->g1, cube->b0)] |
263 | 0 | + mmt[INDEX(cube->r1, cube->g0, cube->b0)] |
264 | 0 | + mmt[INDEX(cube->r0, cube->g1, cube->b0)] |
265 | 0 | - mmt[INDEX(cube->r0, cube->g0, cube->b0)] ); |
266 | 0 | break; |
267 | 0 | } |
268 | | |
269 | 0 | return 0; |
270 | 0 | } |
271 | | |
272 | | |
273 | | // Compute remainder of Vol(cube, mmt), substituting pos for |
274 | | // r1, g1, or b1 (depending on dir) |
275 | | |
276 | | LONG |
277 | 0 | WuQuantizer::Top(Box *cube, BYTE dir, int pos, LONG *mmt) { |
278 | 0 | switch(dir) |
279 | 0 | { |
280 | 0 | case FI_RGBA_RED: |
281 | 0 | return( mmt[INDEX(pos, cube->g1, cube->b1)] |
282 | 0 | -mmt[INDEX(pos, cube->g1, cube->b0)] |
283 | 0 | -mmt[INDEX(pos, cube->g0, cube->b1)] |
284 | 0 | +mmt[INDEX(pos, cube->g0, cube->b0)] ); |
285 | 0 | break; |
286 | 0 | case FI_RGBA_GREEN: |
287 | 0 | return( mmt[INDEX(cube->r1, pos, cube->b1)] |
288 | 0 | -mmt[INDEX(cube->r1, pos, cube->b0)] |
289 | 0 | -mmt[INDEX(cube->r0, pos, cube->b1)] |
290 | 0 | +mmt[INDEX(cube->r0, pos, cube->b0)] ); |
291 | 0 | break; |
292 | 0 | case FI_RGBA_BLUE: |
293 | 0 | return( mmt[INDEX(cube->r1, cube->g1, pos)] |
294 | 0 | -mmt[INDEX(cube->r1, cube->g0, pos)] |
295 | 0 | -mmt[INDEX(cube->r0, cube->g1, pos)] |
296 | 0 | +mmt[INDEX(cube->r0, cube->g0, pos)] ); |
297 | 0 | break; |
298 | 0 | } |
299 | | |
300 | 0 | return 0; |
301 | 0 | } |
302 | | |
303 | | // Compute the weighted variance of a box |
304 | | // NB: as with the raw statistics, this is really the variance * ImageSize |
305 | | |
306 | | float |
307 | 0 | WuQuantizer::Var(Box *cube) { |
308 | 0 | float dr = (float) Vol(cube, mr); |
309 | 0 | float dg = (float) Vol(cube, mg); |
310 | 0 | float db = (float) Vol(cube, mb); |
311 | 0 | float xx = gm2[INDEX(cube->r1, cube->g1, cube->b1)] |
312 | 0 | -gm2[INDEX(cube->r1, cube->g1, cube->b0)] |
313 | 0 | -gm2[INDEX(cube->r1, cube->g0, cube->b1)] |
314 | 0 | +gm2[INDEX(cube->r1, cube->g0, cube->b0)] |
315 | 0 | -gm2[INDEX(cube->r0, cube->g1, cube->b1)] |
316 | 0 | +gm2[INDEX(cube->r0, cube->g1, cube->b0)] |
317 | 0 | +gm2[INDEX(cube->r0, cube->g0, cube->b1)] |
318 | 0 | -gm2[INDEX(cube->r0, cube->g0, cube->b0)]; |
319 | |
|
320 | 0 | return (xx - (dr*dr+dg*dg+db*db)/(float)Vol(cube,wt)); |
321 | 0 | } |
322 | | |
323 | | // We want to minimize the sum of the variances of two subboxes. |
324 | | // The sum(c^2) terms can be ignored since their sum over both subboxes |
325 | | // is the same (the sum for the whole box) no matter where we split. |
326 | | // The remaining terms have a minus sign in the variance formula, |
327 | | // so we drop the minus sign and MAXIMIZE the sum of the two terms. |
328 | | |
329 | | float |
330 | 0 | WuQuantizer::Maximize(Box *cube, BYTE dir, int first, int last , int *cut, LONG whole_r, LONG whole_g, LONG whole_b, LONG whole_w) { |
331 | 0 | LONG half_r, half_g, half_b, half_w; |
332 | 0 | int i; |
333 | 0 | float temp; |
334 | |
|
335 | 0 | LONG base_r = Bottom(cube, dir, mr); |
336 | 0 | LONG base_g = Bottom(cube, dir, mg); |
337 | 0 | LONG base_b = Bottom(cube, dir, mb); |
338 | 0 | LONG base_w = Bottom(cube, dir, wt); |
339 | |
|
340 | 0 | float max = 0.0; |
341 | |
|
342 | 0 | *cut = -1; |
343 | |
|
344 | 0 | for (i = first; i < last; i++) { |
345 | 0 | half_r = base_r + Top(cube, dir, i, mr); |
346 | 0 | half_g = base_g + Top(cube, dir, i, mg); |
347 | 0 | half_b = base_b + Top(cube, dir, i, mb); |
348 | 0 | half_w = base_w + Top(cube, dir, i, wt); |
349 | | |
350 | | // now half_x is sum over lower half of box, if split at i |
351 | |
|
352 | 0 | if (half_w == 0) { // subbox could be empty of pixels! |
353 | 0 | continue; // never split into an empty box |
354 | 0 | } else { |
355 | 0 | temp = ((float)half_r*half_r + (float)half_g*half_g + (float)half_b*half_b)/half_w; |
356 | 0 | } |
357 | | |
358 | 0 | half_r = whole_r - half_r; |
359 | 0 | half_g = whole_g - half_g; |
360 | 0 | half_b = whole_b - half_b; |
361 | 0 | half_w = whole_w - half_w; |
362 | |
|
363 | 0 | if (half_w == 0) { // subbox could be empty of pixels! |
364 | 0 | continue; // never split into an empty box |
365 | 0 | } else { |
366 | 0 | temp += ((float)half_r*half_r + (float)half_g*half_g + (float)half_b*half_b)/half_w; |
367 | 0 | } |
368 | | |
369 | 0 | if (temp > max) { |
370 | 0 | max=temp; |
371 | 0 | *cut=i; |
372 | 0 | } |
373 | 0 | } |
374 | |
|
375 | 0 | return max; |
376 | 0 | } |
377 | | |
378 | | bool |
379 | 0 | WuQuantizer::Cut(Box *set1, Box *set2) { |
380 | 0 | BYTE dir; |
381 | 0 | int cutr, cutg, cutb; |
382 | |
|
383 | 0 | LONG whole_r = Vol(set1, mr); |
384 | 0 | LONG whole_g = Vol(set1, mg); |
385 | 0 | LONG whole_b = Vol(set1, mb); |
386 | 0 | LONG whole_w = Vol(set1, wt); |
387 | |
|
388 | 0 | float maxr = Maximize(set1, FI_RGBA_RED, set1->r0+1, set1->r1, &cutr, whole_r, whole_g, whole_b, whole_w); |
389 | 0 | float maxg = Maximize(set1, FI_RGBA_GREEN, set1->g0+1, set1->g1, &cutg, whole_r, whole_g, whole_b, whole_w); |
390 | 0 | float maxb = Maximize(set1, FI_RGBA_BLUE, set1->b0+1, set1->b1, &cutb, whole_r, whole_g, whole_b, whole_w); |
391 | |
|
392 | 0 | if ((maxr >= maxg) && (maxr >= maxb)) { |
393 | 0 | dir = FI_RGBA_RED; |
394 | |
|
395 | 0 | if (cutr < 0) { |
396 | 0 | return false; // can't split the box |
397 | 0 | } |
398 | 0 | } else if ((maxg >= maxr) && (maxg>=maxb)) { |
399 | 0 | dir = FI_RGBA_GREEN; |
400 | 0 | } else { |
401 | 0 | dir = FI_RGBA_BLUE; |
402 | 0 | } |
403 | | |
404 | 0 | set2->r1 = set1->r1; |
405 | 0 | set2->g1 = set1->g1; |
406 | 0 | set2->b1 = set1->b1; |
407 | |
|
408 | 0 | switch (dir) { |
409 | 0 | case FI_RGBA_RED: |
410 | 0 | set2->r0 = set1->r1 = cutr; |
411 | 0 | set2->g0 = set1->g0; |
412 | 0 | set2->b0 = set1->b0; |
413 | 0 | break; |
414 | | |
415 | 0 | case FI_RGBA_GREEN: |
416 | 0 | set2->g0 = set1->g1 = cutg; |
417 | 0 | set2->r0 = set1->r0; |
418 | 0 | set2->b0 = set1->b0; |
419 | 0 | break; |
420 | | |
421 | 0 | case FI_RGBA_BLUE: |
422 | 0 | set2->b0 = set1->b1 = cutb; |
423 | 0 | set2->r0 = set1->r0; |
424 | 0 | set2->g0 = set1->g0; |
425 | 0 | break; |
426 | 0 | } |
427 | | |
428 | 0 | set1->vol = (set1->r1-set1->r0)*(set1->g1-set1->g0)*(set1->b1-set1->b0); |
429 | 0 | set2->vol = (set2->r1-set2->r0)*(set2->g1-set2->g0)*(set2->b1-set2->b0); |
430 | |
|
431 | 0 | return true; |
432 | 0 | } |
433 | | |
434 | | |
435 | | void |
436 | 0 | WuQuantizer::Mark(Box *cube, int label, BYTE *tag) { |
437 | 0 | for (int r = cube->r0 + 1; r <= cube->r1; r++) { |
438 | 0 | for (int g = cube->g0 + 1; g <= cube->g1; g++) { |
439 | 0 | for (int b = cube->b0 + 1; b <= cube->b1; b++) { |
440 | 0 | tag[INDEX(r, g, b)] = (BYTE)label; |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | 0 | } |
445 | | |
446 | | // Wu Quantization algorithm |
447 | | FIBITMAP * |
448 | 0 | WuQuantizer::Quantize(int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette) { |
449 | 0 | BYTE *tag = NULL; |
450 | |
|
451 | 0 | try { |
452 | 0 | Box cube[MAXCOLOR]; |
453 | 0 | int next; |
454 | 0 | LONG i, weight; |
455 | 0 | int k; |
456 | 0 | float vv[MAXCOLOR], temp; |
457 | | |
458 | | // Compute 3D histogram |
459 | |
|
460 | 0 | Hist3D(wt, mr, mg, mb, gm2, ReserveSize, ReservePalette); |
461 | | |
462 | | // Compute moments |
463 | |
|
464 | 0 | M3D(wt, mr, mg, mb, gm2); |
465 | |
|
466 | 0 | cube[0].r0 = cube[0].g0 = cube[0].b0 = 0; |
467 | 0 | cube[0].r1 = cube[0].g1 = cube[0].b1 = 32; |
468 | 0 | next = 0; |
469 | |
|
470 | 0 | for (i = 1; i < PaletteSize; i++) { |
471 | 0 | if(Cut(&cube[next], &cube[i])) { |
472 | | // volume test ensures we won't try to cut one-cell box |
473 | 0 | vv[next] = (cube[next].vol > 1) ? Var(&cube[next]) : 0; |
474 | 0 | vv[i] = (cube[i].vol > 1) ? Var(&cube[i]) : 0; |
475 | 0 | } else { |
476 | 0 | vv[next] = 0.0; // don't try to split this box again |
477 | 0 | i--; // didn't create box i |
478 | 0 | } |
479 | |
|
480 | 0 | next = 0; temp = vv[0]; |
481 | |
|
482 | 0 | for (k = 1; k <= i; k++) { |
483 | 0 | if (vv[k] > temp) { |
484 | 0 | temp = vv[k]; next = k; |
485 | 0 | } |
486 | 0 | } |
487 | |
|
488 | 0 | if (temp <= 0.0) { |
489 | 0 | PaletteSize = i + 1; |
490 | | |
491 | | // Error: "Only got 'PaletteSize' boxes" |
492 | |
|
493 | 0 | break; |
494 | 0 | } |
495 | 0 | } |
496 | | |
497 | | // Partition done |
498 | | |
499 | | // the space for array gm2 can be freed now |
500 | |
|
501 | 0 | free(gm2); |
502 | |
|
503 | 0 | gm2 = NULL; |
504 | | |
505 | | // Allocate a new dib |
506 | |
|
507 | 0 | FIBITMAP *new_dib = FreeImage_Allocate(width, height, 8); |
508 | |
|
509 | 0 | if (new_dib == NULL) { |
510 | 0 | throw FI_MSG_ERROR_MEMORY; |
511 | 0 | } |
512 | | |
513 | | // create an optimized palette |
514 | | |
515 | 0 | RGBQUAD *new_pal = FreeImage_GetPalette(new_dib); |
516 | |
|
517 | 0 | tag = (BYTE*) malloc(SIZE_3D * sizeof(BYTE)); |
518 | 0 | if (tag == NULL) { |
519 | 0 | throw FI_MSG_ERROR_MEMORY; |
520 | 0 | } |
521 | 0 | memset(tag, 0, SIZE_3D * sizeof(BYTE)); |
522 | |
|
523 | 0 | for (k = 0; k < PaletteSize ; k++) { |
524 | 0 | Mark(&cube[k], k, tag); |
525 | 0 | weight = Vol(&cube[k], wt); |
526 | |
|
527 | 0 | if (weight) { |
528 | 0 | new_pal[k].rgbRed = (BYTE)(((float)Vol(&cube[k], mr) / (float)weight) + 0.5f); |
529 | 0 | new_pal[k].rgbGreen = (BYTE)(((float)Vol(&cube[k], mg) / (float)weight) + 0.5f); |
530 | 0 | new_pal[k].rgbBlue = (BYTE)(((float)Vol(&cube[k], mb) / (float)weight) + 0.5f); |
531 | 0 | } else { |
532 | | // Error: bogus box 'k' |
533 | |
|
534 | 0 | new_pal[k].rgbRed = new_pal[k].rgbGreen = new_pal[k].rgbBlue = 0; |
535 | 0 | } |
536 | 0 | } |
537 | |
|
538 | 0 | int npitch = FreeImage_GetPitch(new_dib); |
539 | |
|
540 | 0 | for (unsigned y = 0; y < height; y++) { |
541 | 0 | BYTE *new_bits = FreeImage_GetBits(new_dib) + (y * npitch); |
542 | |
|
543 | 0 | for (unsigned x = 0; x < width; x++) { |
544 | 0 | new_bits[x] = tag[Qadd[y*width + x]]; |
545 | 0 | } |
546 | 0 | } |
547 | | |
548 | | // output 'new_pal' as color look-up table contents, |
549 | | // 'new_bits' as the quantized image (array of table addresses). |
550 | |
|
551 | 0 | free(tag); |
552 | |
|
553 | 0 | return (FIBITMAP*) new_dib; |
554 | 0 | } catch(...) { |
555 | 0 | free(tag); |
556 | 0 | } |
557 | | |
558 | 0 | return NULL; |
559 | 0 | } |