#include #include #include "neuquant.h" #include "\lib\h\hybrid.h" #include int DITHER = 5; char infile[256]; char outfile[256]; char fname[256]; char palette[768]; int samplefac = 10; int inwidth=0,inheight=0; int outwidth=0,outheight=0; int view = 0; int writefile = 1; char mypal[768]; char *buffer; int vesa; int vesabuf = 0; int orig8bit = 0; char *irqstack; void show_smaller(char *s, int width, int height, int ww, int wh) { int px,py,xs,ys,i; px = width; py = height; xs = (ww-px)/2; ys = (wh-py)/2; for (i=0; iwidth-ww) x= width-ww; if (x<0) x = 0; if (y>height-wh) y = height-wh; if (y<0) y = 0; px = (width-x); if (px>ww) px = ww; py = (height-y); if (py>wh) py = wh; if (px>width) px = width; if (py>height) py = height; for (i=0; i=200) vesa = 1; for (i=0; i<768; i++) mypal[i]=p[i]/4; if (vesa) { if (VESA_set_mode(640,480,8)) vesa = 0; else { ww = 640; wh = 480; buffer = (char *)linAddr; } } if (!vesa) { VGA_set_mode (0x13); ww=320; wh=200; buffer = (char*)VGA_screen; } VGA_set_palette(mypal); if (width>ww || height>wh) show_larger (s,width,height,ww,wh); else show_smaller (s,width,height,ww,wh); VGA_set_mode(0x3); } int save_tga (char *tgafile, char *buffer, char *palette, int width, int height) { FILE *of; TGA TGA_dest; char tgapal[768]; int i; of = fopen(tgafile,"wb"); if (!of) return -1; TGA_dest.idsize = 0; // no id TGA_dest.cmap_type = 1; // color map included TGA_dest.imagetype = 1; // uncompressed, truecolor TGA_dest.cmap_first = 0; // no first color TGA_dest.cmap_size = 256; // no palette TGA_dest.cmap_bits = 24; // 24 bits per color TGA_dest.xorigin = 0; // image origin TGA_dest.yorigin = 0; TGA_dest.width = width; // image size TGA_dest.height = height; TGA_dest.bitdepth = 8; // byte per pixel TGA_dest.descriptor = 0x20; // start top left for(i=0;i<256;i++) { tgapal[i*3+0] = palette[i*3+2]; tgapal[i*3+1] = palette[i*3+1]; tgapal[i*3+2] = palette[i*3+0]; } fwrite(&TGA_dest, 1, sizeof(TGA), of); fwrite (tgapal,1,768,of); fwrite (buffer,1,width*height,of); fclose (of); return 0; } void show_info() { printf ("The program quantizes hi- and truecolour images to optimal 8-bit palette.\n"); printf ("The algorithm uses Kohonen neural networks for creating the palette.\n"); printf ("Error-correction dithering is applied in the quantization phase.\n"); printf ("The program can scale the output image to any size. If the size specified"); printf ("is larger than the input image, bilinear filtering is used. If the output\n"); printf ("image is smaller, area sampling is applied.\n"); printf ("\n"); printf ("The image quality and speed of the learning phase can be tuned with\n"); printf ("the -s parameter. The amount of random diffuse dithering can be tuned\n"); printf ("with the -d parameter. When quantizing images which use a long 'range'\n"); printf ("of colours (such as a smooth transition from black to white with 64\n"); printf ("grays), the random dithering can improve image quality considerably.\n"); printf ("\n"); printf ("The program takes both compressed and uncompressed targa files as input\n"); printf ("but currently only outputs uncompressed ones. If the view (-v) option is\n"); printf ("selected, the quantized image is displayed on the screen. If UniVBE is loaded\n"); printf ("into memory or VGA card supports VESA 2.0, the screen mode used is 640x480,\n"); printf ("otherwise it is 320x200. If output image size is larger than the display size,\n"); printf ("use the cursor keys to pan the image. If input image is 8-bit and no scaling is\n"); printf ("requested and the -v paramete is used, whole quantization process is skipped\n"); printf ("and picture is just displayed.\n"); printf ("\n"); printf ("You can exit the program at any time by pressing .\n"); } void myerror2() { KBD_free(); MEM_shutdown(); exit(1); } void myerror() { printf ("\n"); printf ("TGA image quantization program using Kohonen neural network\n"); printf ("author wili@hybrid.org 1996, based on code by anthony dekker\n\n"); printf ("Syntax: %s [] [options]\n",fname); printf ("Options can be the following:\n\n"); printf ("-h for more information about the program\n"); printf ("-v view 8-bit image\n"); printf ("-x output image width (default is same as input)\n"); printf ("-y output image height\n"); printf ("-s sample factor (1=best/slowest,10=default,1000=fastest)\n"); printf ("-d random dither value (1=none,7=default)\n"); printf ("\nfor example: %s truecol.tga 8bit.tga -x320 -y200 -s5 -d10\n",fname); myerror2(); } void check_abort() { if (!KBD_buffer[KEY_ESC]) return; printf ("\n detected. Aborting.\n"); myerror2(); } void load_8bit (char *dest, TGA *tga) { char *tmp,*p; char pal[768]; int i,s; s = tga->width*tga->height; tmp = MEM_allocate(s); if (!tmp) { printf ("ERROR: out of memory\n"); myerror2(); } TGA_rle_uncompress (tmp,tga,1); TGA_get_palette(tga,pal); for (i=0; i255) return 255; else return a; } int errorr,errorg,errorb; void quantize_pixel (char *dest, char *source) { int r,g,b; char *p; r = clamp((int)source[0]-errorr); g = clamp((int)source[1]-errorg); b = clamp((int)source[2]-errorb); *dest = inxsearch (r,g,b); p = palette+*dest*3; errorr=((int)p[0]-source[0])*0.25+rand()%DITHER-DITHER/2; errorg=((int)p[1]-source[1])*0.25+rand()%DITHER-DITHER/2; errorb=((int)p[2]-source[2])*0.25+rand()%DITHER-DITHER/2; } char *load_picture (char *name, int *inwidth, int *inheight, int *inlen8, int *inlen) { char *pic; TGA *tga; int i,t; char pallie[768]; tga = FILE_allocate(name); if (!tga) { printf ("ERROR: Cannot open file <%s>\n",name); myerror2(); } if (tga->bitdepth!=24 && tga->bitdepth!=32 && tga->bitdepth!=8) { printf("ERROR: This program operates only on 8/24/32 bit TGA images\n"); myerror(); } *inwidth = tga->width; *inheight = tga->height; *inlen8 = (*inwidth)*(*inheight); *inlen = 3*(*inlen8); pic = MEM_allocate(*inlen); if (!pic) { printf ("ERROR: Not enough memory to open file\n"); myerror2(); } printf ("loaded input image <%s> (%d x %d %d bits)\n",name,*inwidth,*inheight,tga->bitdepth); if (tga->bitdepth==8) { if (view && !outwidth && !outheight) { TGA_rle_uncompress (pic,tga,1); TGA_get_palette (tga,pallie); view_image(pic,pallie,*inwidth,*inheight); myerror2(); } else load_8bit (pic,tga); } else TGA_rle_uncompress (pic,tga,3); // swap picture order for (i=0; i<*inlen8; i++) { t = pic[i*3]; pic[i*3] = pic[i*3+2]; pic[i*3+2] = t; } MEM_free(tga); return pic; } void quantize_image (char *dest, char *source, int pixels) { char *p; int percadd; int perc,i; p = source; percadd = (pixels/70); perc = percadd; for (i=0; iperc) { check_abort(); printf("%cQuantizing image (%d%%)",13,(i*100)/pixels); perc = i+percadd; fflush(stdout); } quantize_pixel (dest+i,p); } } int FTOI(float foo); #pragma aux FTOI = \ "push eax"\ "fistp dword ptr [esp]" \ "pop eax" \ parm [8087] value [eax]; int scalex; int scaley; // bilinear interpolation int get_point (char *src, int ix, int iy, int channel) { if (ix>=scalex || ix<0) return 0; if (iy>=scaley || iy<0) return 0; return src[(ix+iy*scalex)*3+channel]; } float get_pixel (char *src, int channel, float x, float y) { float fx,fy,f0,f1; int ix,iy; char *s; int p[4]; x*=scalex; // scale to input image size y*=scaley; if (x<0) x = 0; if (y<0) y = 0; fx = 1.0-(x-floor(x)); // get fractional part for x fy = 1.0-(y-floor(y)); // get fractional part for y ix = x; // convert to integer iy = y; s = src+(ix+iy*scalex)*3+channel; p[0] = get_point (src,ix,iy,channel); p[1] = get_point (src,ix+1,iy,channel); p[2] = get_point (src,ix,iy+1,channel); p[3] = get_point (src,ix+1,iy+1,channel); f0 = p[0]*fy+p[2]*(1.0-fy); f1 = p[1]*fy+p[3]*(1.0-fy); return (fx*f0 + (1.0-fx)*f1); // bi-linear interpolation } char *scale_image (char *source, int inwidth, int inheight, int outwidth, int outheight) { char *d,*d2; int l; int x,y,i; float fx,fy,fxadd,fyadd; float sx,sy,s2,gx,gy,v; l = outwidth*outheight*3; d2 = d = MEM_allocate(l); // just to make sure.. if (!d) { printf ("ERROR: Not enough memory to scale image\n"); myerror2(); } scalex = inwidth; scaley = inheight; sx = (float)(inwidth-1)/outwidth/2.0; sy = (float)(inheight-1)/outheight/2.0; s2 = 0; for (gy = -sy; gy <= sy; gy+=1.0) for (gx = -sx; gx <= sx; gx+=1.0) s2+=1.0; s2 = 1.0/s2; if (inwidth==outwidth && inheight==outheight) // 1:1 aspect ratio scale { MEM_copy (d2,source,l); return d2; } fxadd = 1.0/(outwidth-1); fyadd = 1.0/(outheight-1); for (fy = 0, y=0; y1000) samplefac = 1000; continue; } if(!strncmp(argv[i],"-x",2)) { outwidth = atoi(argv[i]+2); if (outwidth<=0) outwidth = 1; else if (outwidth>4096) outwidth = 4096; continue; } if(!strncmp(argv[i],"-y",2)) { outheight = atoi(argv[i]+2); if (outheight<=0) outheight = 1; else if (outheight>4096) outheight = 4096; continue; } if(!strncmp(argv[i],"-d",2)) { DITHER = atoi(argv[i]+2); if (DITHER<=0) DITHER = 1; else if (DITHER>255) DITHER=255; continue; } if (!strncmp(argv[i],"-",1)) { printf ("ERROR: unknown option <%s>\n",argv[i]); myerror(); } if (fc==0) { strcpy (infile,argv[i]); fc++; continue; } else if (fc==1) { strcpy (outfile,argv[i]); fc++; continue; } else { printf ("ERROR: unknown option <%s>\n",argv[i]); myerror(); continue; } } if (fc==0) { printf ("ERROR: input file not specified\n"); myerror(); } else if (fc==1) writefile = 0; } void clear_line() { printf ("%c ",13); } //----------------------------------------------------------------------------- void main(int argc, char *argv[]) { TGA *tga; char *pic,*p,*finalpic; char *outpic; int inlen,outlen,inlen8,outlen8; MEM_init(100); strncpy (fname,argv[0],79); // name of executable irqstack = MEM_allocate(16384); IRQ_init(irqstack); KBD_hook(); MEM_clear_allocs = 0; // no need to clear on allocate parse_command_line (argc, argv); if (!writefile && !view) { printf ("No output file specified and -v flag not set. Quitting.\n"); exit(1); } strlwr(infile); if (writefile) { strlwr(outfile); if (strcmp(outfile+strlen(outfile)-4,".tga")) strcat(outfile,".tga"); } if (strcmp(infile+strlen(infile)-4,".tga")) strcat(infile,".tga"); pic = load_picture (infile, &inwidth, &inheight, &inlen8, &inlen); if (!outwidth) outwidth = inwidth; // use source file size as default if (!outheight) outheight = inheight; finalpic = scale_image (pic,inwidth,inheight,outwidth,outheight); if (!finalpic) { printf ("ERROR: Not enough memory for temporary scale image\n"); myerror2(); } MEM_free (pic); outlen8 = outwidth*outheight; outlen = outlen8*3; clear_line(); outpic = MEM_allocate (outlen8); if (!outpic) { printf ("ERROR: Not enough memory for 8-bit image\n"); myerror2(); } initnet (finalpic,outlen,samplefac); learn (); unbiasnet (); copy_colourmap (palette); inxbuild (); quantize_image (outpic, finalpic, outlen8); if (writefile) { printf ("%cSaving output image <%s> (%d x %d 8 bits) (%d kB)\n",13,outfile,outwidth, outheight, outlen8/1024); if (save_tga (outfile, outpic, palette, outwidth, outheight)==-1) { printf ("ERROR: cannot write output file\n"); myerror2(); } } else printf ("%cNo output file was specified. Saving skipped.\n",13); if (view) view_image(outpic,palette,outwidth,outheight); myerror2(); }