View Javadoc

1   package org.catacomb.movie.gif;
2   
3   
4   
5   /* NeuQuant Neural-Net Quantization Algorithm
6    * ------------------------------------------
7    *
8    * Copyright (c) 1994 Anthony Dekker
9    *
10   * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994.
11   * See "Kohonen neural networks for optimal colour quantization"
12   * in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367.
13   * for a discussion of the algorithm.
14   *
15   * Any party obtaining a copy of these files from the author, directly or
16   * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
17   * world-wide, paid up, royalty-free, nonexclusive right and license to deal
18   * in this software and documentation files (the "Software"), including without
19   * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
20   * and/or sell copies of the Software, and to permit persons who receive
21   * copies from any such party to do so, with the only requirement being
22   * that this copyright notice remain intact.
23   */
24  
25  // Ported to Java 12/00 K Weiner
26  
27  public class NeuQuant {
28  
29      protected static final int netsize = 256; /* number of colours used */
30  
31      /* four primes near 500 - assume no image has a length so large */
32      /* that it is divisible by all four primes */
33      protected static final int prime1 = 499;
34      protected static final int prime2 = 491;
35      protected static final int prime3 = 487;
36      protected static final int prime4 = 503;
37  
38      protected static final int minpicturebytes = (3 * prime4);
39      /* minimum size for input image */
40  
41      /* Program Skeleton
42         ----------------
43         [select samplefac in range 1..30]
44         [read image from input file]
45         pic = (unsigned char*) malloc(3*width*height);
46         initnet(pic,3*width*height,samplefac);
47         learn();
48         unbiasnet();
49         [write output image header, using writecolourmap(f)]
50         inxbuild();
51         write output image using inxsearch(b,g,r)      */
52  
53      /* Network Definitions
54         ------------------- */
55  
56      protected static final int maxnetpos = (netsize - 1);
57      protected static final int netbiasshift = 4; /* bias for colour values */
58      protected static final int ncycles = 100; /* no. of learning cycles */
59  
60      /* defs for freq and bias */
61      protected static final int intbiasshift = 16; /* bias for fractions */
62      protected static final int intbias = (1 << intbiasshift);
63      protected static final int gammashift = 10; /* gamma = 1024 */
64      protected static final int gamma = (1 << gammashift);
65      protected static final int betashift = 10;
66      protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */
67      protected static final int betagamma =
68          (intbias << (gammashift - betashift));
69  
70      /* defs for decreasing radius factor */
71      protected static final int initrad = (netsize >> 3); /* for 256 cols, radius starts */
72      protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */
73      protected static final int radiusbias = (1 << radiusbiasshift);
74      protected static final int initradius = (initrad * radiusbias); /* and decreases by a */
75      protected static final int radiusdec = 30; /* factor of 1/30 each cycle */
76  
77      /* defs for decreasing alpha factor */
78      protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */
79      protected static final int initalpha = (1 << alphabiasshift);
80  
81      protected int alphadec; /* biased by 10 bits */
82  
83      /* radbias and alpharadbias used for radpower calculation */
84      protected static final int radbiasshift = 8;
85      protected static final int radbias = (1 << radbiasshift);
86      protected static final int alpharadbshift = (alphabiasshift + radbiasshift);
87      protected static final int alpharadbias = (1 << alpharadbshift);
88  
89      /* Types and Global Variables
90      -------------------------- */
91  
92      protected byte[] thepicture; /* the input image itself */
93      protected int lengthcount; /* lengthcount = H*W*3 */
94  
95      protected int samplefac; /* sampling factor 1..30 */
96  
97      //   typedef int pixel[4];                /* BGRc */
98      protected int[][] network; /* the network itself - [netsize][4] */
99  
100     protected int[] netindex = new int[256];
101     /* for network lookup - really 256 */
102 
103     protected int[] bias = new int[netsize];
104     /* bias and freq arrays for learning */
105     protected int[] freq = new int[netsize];
106     protected int[] radpower = new int[initrad];
107     /* radpower for precomputation */
108 
109     /* Initialise network in range (0,0,0) to (255,255,255) and set parameters
110        ----------------------------------------------------------------------- */
111     public NeuQuant(byte[] thepic, int len, int sample) {
112 
113         int i;
114         int[] p;
115 
116         thepicture = thepic;
117         lengthcount = len;
118         samplefac = sample;
119 
120         network = new int[netsize][];
121         for (i = 0; i < netsize; i++) {
122             network[i] = new int[4];
123             p = network[i];
124             p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize;
125             freq[i] = intbias / netsize; /* 1/netsize */
126             bias[i] = 0;
127         }
128     }
129 
130     public byte[] colorMap() {
131         byte[] map = new byte[3 * netsize];
132         int[] index = new int[netsize];
133         for (int i = 0; i < netsize; i++) {
134             index[network[i][3]] = i;
135         }
136         int k = 0;
137         for (int i = 0; i < netsize; i++) {
138             int j = index[i];
139             map[k++] = (byte)(network[j][0]);
140             map[k++] = (byte)(network[j][1]);
141             map[k++] = (byte)(network[j][2]);
142         }
143         return map;
144     }
145 
146     /* Insertion sort of network and building of netindex[0..255] (to do after unbias)
147        ------------------------------------------------------------------------------- */
148     public void inxbuild() {
149 
150         int i, j, smallpos, smallval;
151         int[] p;
152         int[] q;
153         int previouscol, startpos;
154 
155         previouscol = 0;
156         startpos = 0;
157         for (i = 0; i < netsize; i++) {
158             p = network[i];
159             smallpos = i;
160             smallval = p[1]; /* index on g */
161             /* find smallest in i..netsize-1 */
162             for (j = i + 1; j < netsize; j++) {
163                 q = network[j];
164                 if (q[1] < smallval) { /* index on g */
165                     smallpos = j;
166                     smallval = q[1]; /* index on g */
167                 }
168             }
169             q = network[smallpos];
170             /* swap p (i) and q (smallpos) entries */
171             if (i != smallpos) {
172                 j = q[0];
173                 q[0] = p[0];
174                 p[0] = j;
175                 j = q[1];
176                 q[1] = p[1];
177                 p[1] = j;
178                 j = q[2];
179                 q[2] = p[2];
180                 p[2] = j;
181                 j = q[3];
182                 q[3] = p[3];
183                 p[3] = j;
184             }
185             /* smallval entry is now in position i */
186             if (smallval != previouscol) {
187                 netindex[previouscol] = (startpos + i) >> 1;
188                 for (j = previouscol + 1; j < smallval; j++) {
189                     netindex[j] = i;
190                 }
191                 previouscol = smallval;
192                 startpos = i;
193             }
194         }
195         netindex[previouscol] = (startpos + maxnetpos) >> 1;
196         for (j = previouscol + 1; j < 256; j++) {
197             netindex[j] = maxnetpos; /* really 256 */
198         }
199     }
200 
201     /* Main Learning Loop
202        ------------------ */
203     public void learn() {
204 
205         int i, j, b, g, r;
206         int radius, rad, alpha, step, delta, samplepixels;
207         byte[] p;
208         int pix, lim;
209 
210         if (lengthcount < minpicturebytes) {
211             samplefac = 1;
212         }
213         alphadec = 30 + ((samplefac - 1) / 3);
214         p = thepicture;
215         pix = 0;
216         lim = lengthcount;
217         samplepixels = lengthcount / (3 * samplefac);
218         delta = samplepixels / ncycles;
219         alpha = initalpha;
220         radius = initradius;
221 
222         rad = radius >> radiusbiasshift;
223         if (rad <= 1) {
224             rad = 0;
225         }
226         for (i = 0; i < rad; i++) {
227             radpower[i] =
228                 alpha * (((rad * rad - i * i) * radbias) / (rad * rad));
229         }
230 
231         //fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad);
232 
233         if (lengthcount < minpicturebytes) {
234             step = 3;
235         } else if ((lengthcount % prime1) != 0) {
236             step = 3 * prime1;
237         } else {
238             if ((lengthcount % prime2) != 0) {
239                 step = 3 * prime2;
240             } else {
241                 if ((lengthcount % prime3) != 0) {
242                     step = 3 * prime3;
243                 } else {
244                     step = 3 * prime4;
245                 }
246             }
247         }
248 
249         i = 0;
250         while (i < samplepixels) {
251             b = (p[pix + 0] & 0xff) << netbiasshift;
252             g = (p[pix + 1] & 0xff) << netbiasshift;
253             r = (p[pix + 2] & 0xff) << netbiasshift;
254             j = contest(b, g, r);
255 
256             altersingle(alpha, j, b, g, r);
257             if (rad != 0) {
258                 alterneigh(rad, j, b, g, r); /* alter neighbours */
259             }
260             pix += step;
261             if (pix >= lim) {
262                 pix -= lengthcount;
263             }
264             i++;
265             if (delta == 0) {
266                 delta = 1;
267             }
268             if (i % delta == 0) {
269                 alpha -= alpha / alphadec;
270                 radius -= radius / radiusdec;
271                 rad = radius >> radiusbiasshift;
272                 if (rad <= 1) {
273                     rad = 0;
274                 }
275                 for (j = 0; j < rad; j++) {
276                     radpower[j] =
277                         alpha * (((rad * rad - j * j) * radbias) / (rad * rad));
278                 }
279             }
280         }
281         //fprintf(stderr,"finished 1D learning: final alpha=%f !\n",((float)alpha)/initalpha);
282     }
283 
284     /* Search for BGR values 0..255 (after net is unbiased) and return colour index
285        ---------------------------------------------------------------------------- */
286     public int map(int b, int g, int r) {
287 
288         int i, j, dist, a, bestd;
289         int[] p;
290         int best;
291 
292         bestd = 1000; /* biggest possible dist is 256*3 */
293         best = -1;
294         i = netindex[g]; /* index on g */
295         j = i - 1; /* start at netindex[g] and work outwards */
296 
297         while ((i < netsize) || (j >= 0)) {
298             if (i < netsize) {
299                 p = network[i];
300                 dist = p[1] - g; /* inx key */
301                 if (dist >= bestd) {
302                     i = netsize; /* stop iter */
303                 } else {
304                     i++;
305                     if (dist < 0) {
306                         dist = -dist;
307                     }
308                     a = p[0] - b;
309                     if (a < 0) {
310                         a = -a;
311                     }
312                     dist += a;
313                     if (dist < bestd) {
314                         a = p[2] - r;
315                         if (a < 0) {
316                             a = -a;
317                         }
318                         dist += a;
319                         if (dist < bestd) {
320                             bestd = dist;
321                             best = p[3];
322                         }
323                     }
324                 }
325             }
326             if (j >= 0) {
327                 p = network[j];
328                 dist = g - p[1]; /* inx key - reverse dif */
329                 if (dist >= bestd) {
330                     j = -1; /* stop iter */
331                 } else {
332                     j--;
333                     if (dist < 0) {
334                         dist = -dist;
335                     }
336                     a = p[0] - b;
337                     if (a < 0) {
338                         a = -a;
339                     }
340                     dist += a;
341                     if (dist < bestd) {
342                         a = p[2] - r;
343                         if (a < 0) {
344                             a = -a;
345                         }
346                         dist += a;
347                         if (dist < bestd) {
348                             bestd = dist;
349                             best = p[3];
350                         }
351                     }
352                 }
353             }
354         }
355         return (best);
356     }
357     public byte[] process() {
358         learn();
359         unbiasnet();
360         inxbuild();
361         return colorMap();
362     }
363 
364     /* Unbias network to give byte values 0..255 and record position i to prepare for sort
365        ----------------------------------------------------------------------------------- */
366     public void unbiasnet() {
367 
368         int i;
369 
370         for (i = 0; i < netsize; i++) {
371             network[i][0] >>= netbiasshift;
372             network[i][1] >>= netbiasshift;
373             network[i][2] >>= netbiasshift;
374             network[i][3] = i; /* record colour no */
375         }
376     }
377 
378     /* Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|]
379        --------------------------------------------------------------------------------- */
380     protected void alterneigh(int rad, int i, int b, int g, int r) {
381 
382         int j, k, lo, hi, a, m;
383         int[] p;
384 
385         lo = i - rad;
386         if (lo < -1) {
387             lo = -1;
388         }
389         hi = i + rad;
390         if (hi > netsize) {
391             hi = netsize;
392         }
393 
394         j = i + 1;
395         k = i - 1;
396         m = 1;
397         while ((j < hi) || (k > lo)) {
398             a = radpower[m++];
399             if (j < hi) {
400                 p = network[j++];
401                 try {
402                     p[0] -= (a * (p[0] - b)) / alpharadbias;
403                     p[1] -= (a * (p[1] - g)) / alpharadbias;
404                     p[2] -= (a * (p[2] - r)) / alpharadbias;
405                 } catch (Exception e) {
406                 } // prevents 1.3 miscompilation
407             }
408             if (k > lo) {
409                 p = network[k--];
410                 try {
411                     p[0] -= (a * (p[0] - b)) / alpharadbias;
412                     p[1] -= (a * (p[1] - g)) / alpharadbias;
413                     p[2] -= (a * (p[2] - r)) / alpharadbias;
414                 } catch (Exception e) {
415                 }
416             }
417         }
418     }
419 
420     /* Move neuron i towards biased (b,g,r) by factor alpha
421        ---------------------------------------------------- */
422     protected void altersingle(int alpha, int i, int b, int g, int r) {
423 
424         /* alter hit neuron */
425         int[] n = network[i];
426         n[0] -= (alpha * (n[0] - b)) / initalpha;
427         n[1] -= (alpha * (n[1] - g)) / initalpha;
428         n[2] -= (alpha * (n[2] - r)) / initalpha;
429     }
430 
431     /* Search for biased BGR values
432        ---------------------------- */
433     protected int contest(int b, int g, int r) {
434 
435         /* finds closest neuron (min dist) and updates freq */
436         /* finds best neuron (min dist-bias) and returns position */
437         /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */
438         /* bias[i] = gamma*((1/netsize)-freq[i]) */
439 
440         int i, dist, a, biasdist, betafreq;
441         int bestpos, bestbiaspos, bestd, bestbiasd;
442         int[] n;
443 
444         bestd = ~(1 << 31);
445         bestbiasd = bestd;
446         bestpos = -1;
447         bestbiaspos = bestpos;
448 
449         for (i = 0; i < netsize; i++) {
450             n = network[i];
451             dist = n[0] - b;
452             if (dist < 0) {
453                 dist = -dist;
454             }
455 
456             a = n[1] - g;
457             if (a < 0) {
458                 a = -a;
459             }
460 
461             dist += a;
462             a = n[2] - r;
463             if (a < 0) {
464                 a = -a;
465             }
466             dist += a;
467             if (dist < bestd) {
468                 bestd = dist;
469                 bestpos = i;
470             }
471             biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));
472             if (biasdist < bestbiasd) {
473                 bestbiasd = biasdist;
474                 bestbiaspos = i;
475             }
476             betafreq = (freq[i] >> betashift);
477             freq[i] -= betafreq;
478             bias[i] += (betafreq << gammashift);
479         }
480         freq[bestpos] += beta;
481         bias[bestpos] -= betagamma;
482         return (bestbiaspos);
483     }
484 }