1 package org.freehep.graphicsio.gif;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 public class NeuQuant {
25
26 public static final int ncycles = 100;
27
28 public static final int netsize = 255;
29 public static final int specials = 3;
30 public static final int bgColour = specials-1;
31 public static final int cutnetsize = netsize - specials;
32 public static final int maxnetpos = netsize-1;
33
34 public static final int initrad = netsize/8;
35 public static final int radiusbiasshift = 6;
36 public static final int radiusbias = 1 << radiusbiasshift;
37 public static final int initBiasRadius = initrad*radiusbias;
38 public static final int radiusdec = 30;
39
40 public static final int alphabiasshift = 10;
41 public static final int initalpha = 1<<alphabiasshift;
42
43 public static final double gamma = 1024.0;
44 public static final double beta = 1.0/1024.0;
45 public static final double betagamma = beta * gamma;
46
47 private double [] [] network = new double [netsize] [3];
48 protected int [] [] colormap = new int [netsize] [4];
49
50 private int [] netindex = new int [256];
51
52 private double [] bias = new double [netsize];
53 private double [] freq = new double [netsize];
54
55
56
57
58 public static final int prime1 = 499;
59 public static final int prime2 = 491;
60 public static final int prime3 = 487;
61 public static final int prime4 = 503;
62 public static final int maxprime= prime4;
63
64 protected int [][] pixels = null;
65 private int samplefac = 0;
66
67
68 public NeuQuant (int sample, int[][] pixels) {
69 if (sample < 1) throw new RuntimeException ("Sample must be 1..30");
70 if (sample > 30) throw new RuntimeException ("Sample must be 1..30");
71 samplefac = sample;
72 setPixels (pixels);
73 setUpArrays ();
74 }
75
76 public int getColorCount () {
77 return netsize;
78 }
79
80 public int[] getColorMap() {
81
82 int[] c = new int[netsize+1];
83 c[0] = 0x00000000;
84 for (int i=0; i<netsize; i++) {
85 c[i+1] = (colormap[i][0] ) |
86 (colormap[i][1] << 8) |
87 (colormap[i][2] << 16) |
88 (0xFF << 24);
89 }
90 return c;
91 }
92
93 protected void setUpArrays () {
94 network [0] [0] = 0.0;
95 network [0] [1] = 0.0;
96 network [0] [2] = 0.0;
97
98 network [1] [0] = 1.0;
99 network [1] [1] = 1.0;
100 network [1] [2] = 1.0;
101
102
103
104 for (int i=0; i<specials; i++) {
105 freq[i] = 1.0 / netsize;
106 bias[i] = 0.0;
107 }
108
109 for (int i=specials; i<netsize; i++) {
110 double [] p = network [i];
111 p[0] = (256.0 * (i-specials)) / cutnetsize;
112 p[1] = (256.0 * (i-specials)) / cutnetsize;
113 p[2] = (256.0 * (i-specials)) / cutnetsize;
114
115 freq[i] = 1.0 / netsize;
116 bias[i] = 0.0;
117 }
118 }
119
120 private void setPixels (int[][] pixels) {
121 if (pixels.length*pixels[0].length < maxprime) throw new RuntimeException ("Image is too small");
122 this.pixels = pixels;
123 }
124
125 public void init () {
126 learn ();
127 fix ();
128 inxbuild ();
129 }
130
131 private void altersingle(double alpha, int i, double b, double g, double r) {
132
133 double [] n = network[i];
134 n[0] -= (alpha*(n[0] - b));
135 n[1] -= (alpha*(n[1] - g));
136 n[2] -= (alpha*(n[2] - r));
137 }
138
139 private void alterneigh(double alpha, int rad, int i, double b, double g, double r) {
140
141 int lo = i-rad; if (lo<specials-1) lo=specials-1;
142 int hi = i+rad; if (hi>netsize) hi=netsize;
143
144 int j = i+1;
145 int k = i-1;
146 int q = 0;
147 while ((j<hi) || (k>lo)) {
148 double a = (alpha * (rad*rad - q*q)) / (rad*rad);
149 q ++;
150 if (j<hi) {
151 double [] p = network[j];
152 p[0] -= (a*(p[0] - b));
153 p[1] -= (a*(p[1] - g));
154 p[2] -= (a*(p[2] - r));
155 j++;
156 }
157 if (k>lo) {
158 double [] p = network[k];
159 p[0] -= (a*(p[0] - b));
160 p[1] -= (a*(p[1] - g));
161 p[2] -= (a*(p[2] - r));
162 k--;
163 }
164 }
165 }
166
167 private int contest (double b, double g, double r) {
168
169
170
171
172
173 double bestd = Float.MAX_VALUE;
174 double bestbiasd = bestd;
175 int bestpos = -1;
176 int bestbiaspos = bestpos;
177
178 for (int i=specials; i<netsize; i++) {
179 double [] n = network[i];
180 double dist = n[0] - b; if (dist<0) dist = -dist;
181 double a = n[1] - g; if (a<0) a = -a;
182 dist += a;
183 a = n[2] - r; if (a<0) a = -a;
184 dist += a;
185 if (dist<bestd) {bestd=dist; bestpos=i;}
186 double biasdist = dist - bias [i];
187 if (biasdist<bestbiasd) {bestbiasd=biasdist; bestbiaspos=i;}
188 freq [i] -= beta * freq [i];
189 bias [i] += betagamma * freq [i];
190 }
191 freq[bestpos] += beta;
192 bias[bestpos] -= betagamma;
193 return bestbiaspos;
194 }
195
196 private int specialFind (double b, double g, double r) {
197 for (int i=0; i<specials; i++) {
198 double [] n = network[i];
199 if (n[0] == b && n[1] == g && n[2] == r) return i;
200 }
201 return -1;
202 }
203
204 private void learn() {
205 int biasRadius = initBiasRadius;
206 int alphadec = 30 + ((samplefac-1)/3);
207 int lengthcount = pixels.length*pixels[0].length;
208 int samplepixels = lengthcount / samplefac;
209 int delta = samplepixels / ncycles;
210 int alpha = initalpha;
211
212 int i = 0;
213 int rad = biasRadius >> radiusbiasshift;
214 if (rad <= 1) rad = 0;
215
216
217
218 int step = 0;
219 int pos = 0;
220
221 if ((lengthcount%prime1) != 0) step = prime1;
222 else {
223 if ((lengthcount%prime2) !=0) step = prime2;
224 else {
225 if ((lengthcount%prime3) !=0) step = prime3;
226 else step = prime4;
227 }
228 }
229
230 i = 0;
231 while (i < samplepixels) {
232 int p = pixels [pos / pixels[0].length][pos % pixels[0].length];
233 int red = (p >> 16) & 0xff;
234 int green = (p >> 8) & 0xff;
235 int blue = (p ) & 0xff;
236
237 double b = blue;
238 double g = green;
239 double r = red;
240
241 if (i == 0) {
242 network [bgColour] [0] = b;
243 network [bgColour] [1] = g;
244 network [bgColour] [2] = r;
245 }
246
247 int j = specialFind (b, g, r);
248 j = j < 0 ? contest (b, g, r) : j;
249
250 if (j >= specials) {
251 double a = (1.0 * alpha) / initalpha;
252 altersingle (a, j, b, g, r);
253 if (rad > 0) alterneigh (a, rad, j, b, g, r);
254 }
255
256 pos += step;
257 while (pos >= lengthcount) pos -= lengthcount;
258
259 i++;
260 if (i%delta == 0) {
261 alpha -= alpha / alphadec;
262 biasRadius -= biasRadius / radiusdec;
263 rad = biasRadius >> radiusbiasshift;
264 if (rad <= 1) rad = 0;
265 }
266 }
267
268 }
269
270 private void fix() {
271 for (int i=0; i<netsize; i++) {
272 for (int j=0; j<3; j++) {
273 int x = (int) (0.5 + network[i][j]);
274 if (x < 0) x = 0;
275 if (x > 255) x = 255;
276 colormap[i][j] = x;
277 }
278 colormap[i][3] = i;
279 }
280 }
281
282 private void inxbuild() {
283
284
285 int previouscol = 0;
286 int startpos = 0;
287
288 for (int i=0; i<netsize; i++) {
289 int[] p = colormap[i];
290 int[] q = null;
291 int smallpos = i;
292 int smallval = p[1];
293
294 for (int j=i+1; j<netsize; j++) {
295 q = colormap[j];
296 if (q[1] < smallval) {
297 smallpos = j;
298 smallval = q[1];
299 }
300 }
301 q = colormap[smallpos];
302
303 if (i != smallpos) {
304 int j = q[0]; q[0] = p[0]; p[0] = j;
305 j = q[1]; q[1] = p[1]; p[1] = j;
306 j = q[2]; q[2] = p[2]; p[2] = j;
307 j = q[3]; q[3] = p[3]; p[3] = j;
308 }
309
310 if (smallval != previouscol) {
311 netindex[previouscol] = (startpos+i)>>1;
312 for (int j=previouscol+1; j<smallval; j++) netindex[j] = i;
313 previouscol = smallval;
314 startpos = i;
315 }
316 }
317 netindex[previouscol] = (startpos+maxnetpos)>>1;
318 for (int j=previouscol+1; j<256; j++) netindex[j] = maxnetpos;
319 }
320
321 public int convert (int pixel) {
322 int alfa = (pixel >> 24) & 0xff;
323 int r = (pixel >> 16) & 0xff;
324 int g = (pixel >> 8) & 0xff;
325 int b = (pixel ) & 0xff;
326 int i = inxsearch(b, g, r);
327 int bb = colormap[i][0];
328 int gg = colormap[i][1];
329 int rr = colormap[i][2];
330 return (alfa << 24) | (rr << 16) | (gg << 8) | (bb);
331 }
332
333 public int lookup (int pixel) {
334 int r = (pixel >> 16) & 0xff;
335 int g = (pixel >> 8) & 0xff;
336 int b = (pixel ) & 0xff;
337 int i = inxsearch(b, g, r);
338
339 return i+1;
340 }
341
342 private int not_used_slow_inxsearch(int b, int g, int r) {
343
344 int bestd = 1000;
345 int best = -1;
346 for (int i = 0; i<netsize; i++) {
347 int [] p = colormap[i];
348 int dist = p[1] - g;
349 if (dist<0) dist = -dist;
350 int a = p[0] - b; if (a<0) a = -a;
351 dist += a;
352 a = p[2] - r; if (a<0) a = -a;
353 dist += a;
354 if (dist<bestd) {bestd=dist; best=i;}
355 }
356 return best;
357 }
358
359 protected int inxsearch(int b, int g, int r) {
360
361 int bestd = 1000;
362 int best = -1;
363 int i = netindex[g];
364 int j = i-1;
365
366 while ((i<netsize) || (j>=0)) {
367 if (i<netsize) {
368 int [] p = colormap[i];
369 int dist = p[1] - g;
370 if (dist >= bestd) i = netsize;
371 else {
372 if (dist<0) dist = -dist;
373 int a = p[0] - b; if (a<0) a = -a;
374 dist += a;
375 if (dist<bestd) {
376 a = p[2] - r; if (a<0) a = -a;
377 dist += a;
378 if (dist<bestd) {bestd=dist; best=i;}
379 }
380 i++;
381 }
382 }
383 if (j>=0) {
384 int [] p = colormap[j];
385 int dist = g - p[1];
386 if (dist >= bestd) j = -1;
387 else {
388 if (dist<0) dist = -dist;
389 int a = p[0] - b; if (a<0) a = -a;
390 dist += a;
391 if (dist<bestd) {
392 a = p[2] - r; if (a<0) a = -a;
393 dist += a;
394 if (dist<bestd) {bestd=dist; best=j;}
395 }
396 j--;
397 }
398 }
399 }
400
401 return best;
402 }
403 }