#ifndef lint static const char RCSid[] = "$Id: neuclrtab.c,v 2.14 2011/05/20 02:06:39 greg Exp $"; #endif /* * Neural-Net quantization algorithm based on work of Anthony Dekker */ #include "copyright.h" #include #include "standard.h" #include "color.h" #include "random.h" #include "clrtab.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 uby8 clrtab[256][3]; static int clrtabsiz; #ifndef DEFSMPFAC #define DEFSMPFAC 3 #endif int samplefac = DEFSMPFAC; /* sampling factor */ /* Samples array starts off holding spacing between adjacent * samples, and ends up holding actual BGR sample values. */ static uby8 *thesamples; static int nsamples; static uby8 *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) static void initnet(void); static void inxbuild(void); static int inxsearch(int b, int g, int r); static int contest(int b, int g, int r); static void altersingle(int alpha, int i, int b, int g, int r); static void alterneigh(int rad, int i, int b, int g, int r); static void learn(void); static void unbiasnet(void); static void cpyclrtab(void); extern int neu_init( /* 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 = (uby8 *)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); } extern void neu_pixel( /* add pixel to our samples */ register uby8 col[] ) { if (!skipcount--) { skipcount = nskip(cursamp); cursamp[0] = col[BLU]; cursamp[1] = col[GRN]; cursamp[2] = col[RED]; cursamp += 3; } } extern void neu_colrs( /* 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; } extern int neu_clrtab( /* 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((void *)thesamples); /* reset dithering function */ neu_dith_colrs((uby8 *)NULL, (COLR *)NULL, 0); /* return new color table size */ return(clrtabsiz); } extern int neu_map_pixel( /* get pixel for color */ register uby8 col[] ) { return(inxsearch(col[BLU],col[GRN],col[RED])); } extern void neu_map_colrs( /* convert a scanline to color index values */ register uby8 *bs, register COLR *cs, register int n ) { while (n-- > 0) { *bs++ = inxsearch(cs[0][BLU],cs[0][GRN],cs[0][RED]); cs++; } } extern void neu_dith_colrs( /* convert scanline to dithered index values */ register uby8 *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((void *)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; memset((char *)cerr, '\0', 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 Neural-Net Quantization Algorithm Interface * ---------------------------------------------------- * * Copyright (c) 1994 Anthony Dekker * * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. * See "Kohonen neural networks for optimal colour quantization" * in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367. * for a discussion of the algorithm. * See also http://members.ozemail.com.au/~dekker/NEUQUANT.HTML * * Any party obtaining a copy of these files from the author, directly or * indirectly, is granted, free of charge, a full and unrestricted irrevocable, * world-wide, paid up, royalty-free, nonexclusive right and license to deal * in this software and documentation files (the "Software"), including without * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons who receive * copies from any such party to do so, with the only requirement being * that this copyright notice remain intact. */ #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 */ } static int inxsearch( /* accepts real BGR values after net is unbiased */ register int b, register int g, register int 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--; } } } static void learn(void) { 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 void cpyclrtab(void) { register int i,j,k; for (j=0; j