/* Copyright (c) 1994 Regents of the University of California */ #ifndef lint static char SCCSid[] = "$SunId$ LBL"; #endif /* * Neural-Net quantization algorithm based on work of Anthony Dekker */ #include "standard.h" #include "color.h" #include "random.h" #ifdef COMPAT_MODE #define neu_init new_histo #define neu_pixel cnt_pixel #define neu_colrs cnt_colrs #define neu_clrtab new_clrtab #define neu_map_pixel map_pixel #define neu_map_colrs map_colrs #define neu_dith_colrs dith_colrs #endif /* our color table (global) */ extern BYTE clrtab[256][3]; static int clrtabsiz; #ifndef DEFSMPFAC #ifdef SPEED #define DEFSMPFAC (240/SPEED+3) #else #define DEFSMPFAC 30 #endif #endif int samplefac = DEFSMPFAC; /* sampling factor */ /* Samples array starts off holding spacing between adjacent * samples, and ends up holding actual BGR sample values. */ static BYTE *thesamples; static int nsamples; static BYTE *cursamp; static long skipcount; #define MAXSKIP (1<<24-1) #define nskip(sp) ((long)(sp)[0]<<16|(long)(sp)[1]<<8|(long)(sp)[2]) #define setskip(sp,n) ((sp)[0]=(n)>>16,(sp)[1]=((n)>>8)&255,(sp)[2]=(n)&255) neu_init(npixels) /* initialize our sample array */ long npixels; { register int nsleft; register long sv; double rval, cumprob; long npleft; nsamples = npixels/samplefac; if (nsamples < 600) return(-1); thesamples = (BYTE *)malloc(nsamples*3); if (thesamples == NULL) return(-1); cursamp = thesamples; npleft = npixels; nsleft = nsamples; while (nsleft) { rval = frandom(); /* random distance to next sample */ sv = 0; cumprob = 0.; while ((cumprob += (1.-cumprob)*nsleft/(npleft-sv)) < rval) sv++; if (nsleft == nsamples) skipcount = sv; else { setskip(cursamp, sv); cursamp += 3; } npleft -= sv+1; nsleft--; } setskip(cursamp, npleft); /* tag on end to skip the rest */ cursamp = thesamples; return(0); } neu_pixel(col) /* add pixel to our samples */ register BYTE col[]; { if (!skipcount--) { skipcount = nskip(cursamp); cursamp[0] = col[BLU]; cursamp[1] = col[GRN]; cursamp[2] = col[RED]; cursamp += 3; } } neu_colrs(cs, n) /* add a scanline to our samples */ register COLR *cs; register int n; { while (n > skipcount) { cs += skipcount; n -= skipcount+1; skipcount = nskip(cursamp); cursamp[0] = cs[0][BLU]; cursamp[1] = cs[0][GRN]; cursamp[2] = cs[0][RED]; cs++; cursamp += 3; } skipcount -= n; } neu_clrtab(ncolors) /* make new color table using ncolors */ int ncolors; { clrtabsiz = ncolors; if (clrtabsiz > 256) clrtabsiz = 256; initnet(); learn(); unbiasnet(); cpyclrtab(); inxbuild(); /* we're done with our samples */ free((char *)thesamples); /* reset dithering function */ neu_dith_colrs((BYTE *)NULL, (COLR *)NULL, 0); /* return new color table size */ return(clrtabsiz); } int neu_map_pixel(col) /* get pixel for color */ register BYTE col[]; { return(inxsearch(col[BLU],col[GRN],col[RED])); } neu_map_colrs(bs, cs, n) /* convert a scanline to color index values */ register BYTE *bs; register COLR *cs; register int n; { while (n-- > 0) { *bs++ = inxsearch(cs[0][BLU],cs[0][GRN],cs[0][RED]); cs++; } } neu_dith_colrs(bs, cs, n) /* convert scanline to dithered index values */ register BYTE *bs; register COLR *cs; int n; { static short (*cerr)[3] = NULL; static int N = 0; int err[3], errp[3]; register int x, i; if (n != N) { /* get error propogation array */ if (N) { free((char *)cerr); cerr = NULL; } if (n) cerr = (short (*)[3])malloc(3*n*sizeof(short)); if (cerr == NULL) { N = 0; map_colrs(bs, cs, n); return; } N = n; bzero((char *)cerr, 3*N*sizeof(short)); } err[0] = err[1] = err[2] = 0; for (x = 0; x < n; x++) { for (i = 0; i < 3; i++) { /* dither value */ errp[i] = err[i]; err[i] += cerr[x][i]; #ifdef MAXERR if (err[i] > MAXERR) err[i] = MAXERR; else if (err[i] < -MAXERR) err[i] = -MAXERR; #endif err[i] += cs[x][i]; if (err[i] < 0) err[i] = 0; else if (err[i] > 255) err[i] = 255; } bs[x] = inxsearch(err[BLU],err[GRN],err[RED]); for (i = 0; i < 3; i++) { /* propagate error */ err[i] -= clrtab[bs[x]][i]; err[i] /= 3; cerr[x][i] = err[i] + errp[i]; } } } /* The following was adapted and modified from the original (GW) */ /* cheater definitions (GW) */ #define thepicture thesamples #define lengthcount (nsamples*3) #define samplefac 1 /*----------------------------------------------------------------------*/ /* */ /* NeuQuant */ /* -------- */ /* */ /* Copyright: Anthony Dekker, November 1994 */ /* */ /* This program performs colour quantization of graphics images (SUN */ /* raster files). It uses a Kohonen Neural Network. It produces */ /* better results than existing methods and runs faster, using minimal */ /* space (8kB plus the image itself). The algorithm is described in */ /* the paper "Kohonen Neural Networks for Optimal Colour Quantization" */ /* to appear in the journal "Network: Computation in Neural Systems". */ /* It is a significant improvement of an earlier algorithm. */ /* */ /* This program is distributed free for academic use or for evaluation */ /* by commercial organizations. */ /* */ /* Usage: NeuQuant -n inputfile > outputfile */ /* */ /* where n is a sampling factor for neural learning. */ /* */ /* Program performance compared with other methods is as follows: */ /* */ /* Algorithm | Av. CPU Time | Quantization Error */ /* ------------------------------------------------------------- */ /* NeuQuant -3 | 314 | 5.55 */ /* NeuQuant -10 | 119 | 5.97 */ /* NeuQuant -30 | 65 | 6.53 */ /* Oct-Trees | 141 | 8.96 */ /* Median Cut (XV -best) | 420 | 9.28 */ /* Median Cut (XV -slow) | 72 | 12.15 */ /* */ /* Author's address: Dept of ISCS, National University of Singapore */ /* Kent Ridge, Singapore 0511 */ /* Email: tdekker@iscs.nus.sg */ /*----------------------------------------------------------------------*/ #define bool int #define false 0 #define true 1 /* network defs */ #define netsize clrtabsiz /* number of colours - can change this */ #define maxnetpos (netsize-1) #define netbiasshift 4 /* bias for colour values */ #define ncycles 100 /* no. of learning cycles */ /* defs for freq and bias */ #define intbiasshift 16 /* bias for fractions */ #define intbias (((int) 1)<>betashift) /* beta = 1/1024 */ #define betagamma (intbias<<(gammashift-betashift)) /* defs for decreasing radius factor */ #define initrad (256>>3) /* for 256 cols, radius starts */ #define radiusbiasshift 6 /* at 32.0 biased by 6 bits */ #define radiusbias (((int) 1)<>1; for (j=previouscol+1; j>1; for (j=previouscol+1; j<256; j++) netindex[j] = maxnetpos; /* really 256 */ } int inxsearch(b,g,r) /* accepts real BGR values after net is unbiased */ register int b,g,r; { register int i,j,dist,a,bestd; register int *p; int best; bestd = 1000; /* biggest possible dist is 256*3 */ best = -1; i = netindex[g]; /* index on g */ j = i-1; /* start at netindex[g] and work outwards */ while ((i=0)) { if (i= bestd) i = netsize; /* stop iter */ else { i++; if (dist<0) dist = -dist; a = p[0] - b; if (a<0) a = -a; dist += a; if (dist=0) { p = network[j]; dist = g - p[1]; /* inx key - reverse dif */ if (dist >= bestd) j = -1; /* stop iter */ else { j--; if (dist<0) dist = -dist; a = p[0] - b; if (a<0) a = -a; dist += a; if (dist>(intbiasshift-netbiasshift)); if (biasdist> betashift); *f++ -= betafreq; *p++ += (betafreq<netsize) hi=netsize; j = i+1; k = i-1; q = radpower; while ((jlo)) { a = (*(++q)); if (jlo) { p = network[k]; *p -= (a*(*p - b)) / alpharadbias; p++; *p -= (a*(*p - g)) / alpharadbias; p++; *p -= (a*(*p - r)) / alpharadbias; k--; } } } learn() { register int i,j,b,g,r; int radius,rad,alpha,step,delta,samplepixels; register unsigned char *p; unsigned char *lim; alphadec = 30 + ((samplefac-1)/3); p = thepicture; lim = thepicture + lengthcount; samplepixels = lengthcount/(3*samplefac); delta = samplepixels/ncycles; alpha = initalpha; radius = initradius; rad = radius >> radiusbiasshift; if (rad <= 1) rad = 0; for (i=0; i= lim) p -= lengthcount; i++; if (i%delta == 0) { alpha -= alpha / alphadec; radius -= radius / radiusdec; rad = radius >> radiusbiasshift; if (rad <= 1) rad = 0; for (j=0; j>= netbiasshift; network[i][3] = i; /* record colour no */ } } /* Don't do this until the network has been unbiased (GW) */ static cpyclrtab() { register int i,j,k; for (j=0; j