/Users/lyon/j4p/src/ip/gif/neuquantAnimation/NeuQuant.java

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