bert@let.rug.nl (Bert Bos) (11/22/90)
I needed a utility to convert numbers to colors, but I couldn't find it among the PBM programs. Therefore I decided to make one myself. pgmcolor reads a PGM file and outputs a PPM file, using the gray values as indices into a palette which is itself a PPM file. pgmcolor can be used to make false-color images. I use it to assign colors to level sets of Mandelbrot and Julia fractals. It is also possible to convert a gray image from a scanner to colors, if there where not too maany colors in the original. A Makefile and a manual are included. You need to have the pbmplus package (specifically the libraries). ----- CUT HERE ----------------------------------------------------------- # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by Bert Bos <bert@gufal03> on Tue Nov 20 19:41:08 1990 # # This archive contains: # README pgmcolor.1 pgmcolor.c Makefile # LANG=""; export LANG PATH=/bin:/usr/bin:$PATH; export PATH echo x - README cat >README <<'@EOF' pgmcolor converts a portable gray map to a portable pixmap, using a palette (which is itself a PPM file). pgmcolor can be used to make false-color images. I use it to assign colors to level sets of Mandelbrot and Julia fractals. It is also possible to convert a gray image from a scanner to colors (but choosing convincing colors is not a sinecure). A Makefile and a manual are included. You need to have the pbmplus package to compile this. The program is called with: pgmcolor [-v] [-a] palettefile [pgmfile] Copyright by Bert Bos <bert@let.rug.nl>. @EOF chmod 644 README echo x - pgmcolor.1 cat >pgmcolor.1 <<'@EOF' .TH pgmcolor 1 "20 November 1990" .SH NAME pgmcolor - create false color image from a portable graymap .SH SYNOPSIS pgmcolor [-v] [-a] palette [pgmfile] .SH DESCRIPTION Reads a portable graymap as input and a portable pixmap as palette. The different gray values are replaced by pixel values from the palette, from darkest to lightest. Normally, the darkest gray is replaced by the first color in the palette file, the next gray by the next color, etc. If there are more pixel values than actual gray levels, the last pixels are discarded. If there are insufficient pixel values, they are applied cyclicly. The -a (absolute) option forces the first pixel to be assigned to gray level 0, the next to gray level 1, etc., regardless of the actual existence of that gray level in the input. The -v (verbose) flag reports the number of gray levels and the number of pixel values in the input and the palette, respectively. An example palette might look like this: .nf P3 # Palette for 16 yellowish colors 16 1 15 0 0 0 1 1 0 3 2 0 5 3 0 7 4 0 9 5 0 11 6 0 13 7 0 15 8 1 15 9 3 15 10 5 15 11 7 15 12 9 15 13 11 15 14 13 15 15 15 .fi You can use pgmhist(1) to see how many gray levels a PGM file has. .SH "SEE ALSO" pgmhist(1), pgm(5), ppm(5) .SH AUTHOR Copyright (C) 1990 by Bert Bos <bert@let.rug.nl>. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. This software is provided "as is" without express or implied warranty. @EOF chmod 644 pgmcolor.1 echo x - pgmcolor.c cat >pgmcolor.c <<'@EOF' /* pgmcolor -- Make a false color image from a portable gray map Copyright: Bert Bos <bert@let.rug.nl> Date: 19 Nov 1990 Usage: pgmcolor [-v] [-a] palette [pgmfile] The palette file is a PPM file. It should contain a number of RGB values. Pgmcolor assigns the first pixel's color to the darkest gray and the next pixels color to the next darkest gray. If there are more grays than pixels in the palette file, the colors will be applied cyclicly If there are more RGB values than gray values, the superfluous RGB colors will be ignored. The -v option (verbose) will display some statistics on this. If the -a option is specified, the colors in the palette will be assigned to graylevels 0, 1, 2, etc. regardless of the existence of this levels in the PGM file. */ #include <stdio.h> #include <pgm.h> #include <ppm.h> #define TRUE 1 #define FALSE 0 #define NULL 0 #define Bool int static Bool verbose = FALSE; static Bool absolute = FALSE; static char *paletteName = (char *) NULL; static char *infileName = (char *) NULL; static char *progName = (char *) NULL; /* usage -- Print usage message and exit */ void usage() { fprintf(stderr, "Usage: %s [-v] [-a] palette [pgm-file]\n", progName); exit(1); } /* arguments -- interpret command line arguments */ void arguments(argc, argv) int argc; char **argv; { char **p, *arg; int i = 0; p = argv; while (i < argc) { /* Loop over arguments */ arg = *p; if (*arg == '-') { /* It's an option */ arg++; /* Skip '-' */ while (*arg) { /* Loop over all letters in option */ switch (*arg) { case 'v': /* -v verbose */ verbose = TRUE; break; case 'a': /* -a absolute */ absolute = TRUE; break; default: usage(); /* Unknown option */ } arg++; /* Next letter */ } } else if (! progName) { /* It's the program's own name */ progName = arg; } else if (! paletteName) { /* It's the palette's name */ paletteName = arg; } else if (! infileName) { /* It's the input file's name */ infileName = arg; } else /* Too many arguments */ usage(); p++; /* Next argument */ i++; } if (! paletteName) /* Too few arguments */ usage(); } /* doAbsolute -- replace each gray level by corresponding pixel value */ void doAbsolute(infile, palette) FILE *infile, *palette; { pixel **colors; /* Holds palette */ int colsP, rowsP; /* For palette file */ pixval maxvalP; /* For palette file */ int colsG, rowsG, formatG; /* For PGM file */ gray maxvalG; /* For PGM file */ gray *grayrow; /* Holds successive rows of pgm file */ pixel *newrow; /* Holds successive rows of output */ int row, col; /* Temporary variables */ /* Read colors and header of pgm input file, then write output header */ colors = ppm_readppm(palette, &colsP, &rowsP, &maxvalP); fclose(palette); if (! colors) { fprintf(stderr, "%s: out of memory\n", progName); exit(-1); } pgm_readpgminit(infile, &colsG, &rowsG, &maxvalG, &formatG); ppm_writeppminit(stdout, colsG, rowsG, maxvalP); if (verbose) { fprintf(stderr, "%s is a %dx%d PGM file with %d gray levels.\n", infileName?infileName:"standard input", colsG, rowsG, maxvalG); fprintf(stderr, "%s defines %d colors", paletteName, colsP * rowsP); if (maxvalG + 1 < colsP * rowsP) fprintf(stderr, ", last %d will be ignored.\n", colsP*rowsP-maxvalG-1); else if (maxvalG + 1 > colsP * rowsP) fprintf(stderr, ", colors will be used cyclicly\n"); else fprintf(stderr, " exactly\n"); } /* Loop over rows of PGM file */ newrow = ppm_allocrow(colsG); for (row = 0; row < rowsG; row++) { pgm_readpgmrow(infile, grayrow, colsG, maxvalG, formatG); for (col = 0; col < colsG; col++) newrow[col] = (*colors)[(int) grayrow[col] % (colsP * rowsP)]; ppm_writeppmrow(stdout, newrow, colsG, maxvalP); } /* Close input */ fclose(infile); } /* doRelative -- replace only existing graylevels with pixel values */ void doRelative(infile, palette) FILE *infile, *palette; { pixel **colors, *cmap; /* Hold palette & color map */ int colsP, rowsP; /* For palette file */ pixval maxvalP; /* For palette file */ gray **graymap; /* Holds PGM file */ int colsG, rowsG; /* For PGM file */ gray maxvalG; /* For PGM file */ pixel *newrow; /* Holds successive rows of output */ int *hist, count; /* Histogram */ int row, col, i, j; /* Temporary variables */ /* Read colors and gray map */ colors = ppm_readppm(palette, &colsP, &rowsP, &maxvalP); fclose(palette); graymap = pgm_readpgm(infile, &colsG, &rowsG, &maxvalG); fclose(infile); if (! colors || ! graymap) { fprintf(stderr, "%s: out of memory\n", progName); exit(-1); } /* Build histogram of PGM file */ hist = (int *) malloc((maxvalG + 1)*sizeof(int)); cmap = ppm_allocrow(maxvalG); newrow = ppm_allocrow(colsG); if (! hist || ! cmap || ! newrow) { fprintf(stderr, "%s: out of memory\n", progName); exit(-1); } for (i = 0; i <= maxvalG; i++) hist[i] = 0; for (row = 0; row < rowsG; row++) for (col = 0; col < colsG; col++) hist[(int) graymap[row][col]]++; /* Now put a pixel value in cmap next to each nonzero hist[i] */ count = 0; for (i = 0; i <= maxvalG; i++) if (hist[i]) { cmap[i] = (*colors)[count % (colsP * rowsP)]; count++; } if (verbose) { if (infileName) fprintf(stderr, "%s is a %dx%d PGM file with %d gray levels out of %d\n", infileName, colsG, rowsG, count, maxvalG); else fprintf(stderr, "standard input is a %dx%d PGM file with %d gray levels out of %d\n", colsG, rowsG, count, maxvalG); fprintf(stderr, "%s defines %d colors", paletteName, colsP * rowsP); if (count < colsP * rowsP) fprintf(stderr, "; last %d will be ignored.\n", colsP * rowsP - count); else if (count > colsP * rowsP) fprintf(stderr, "; colors will be used cyclicly\n"); else fprintf(stderr, " exactly\n"); } /* Write out a PPM file of the same size as the PGM file */ ppm_writeppminit(stdout, colsG, rowsG, maxvalP); for (row = 0; row < rowsG; row++) { for (col = 0; col <colsG; col++) newrow[col] = cmap[(int) graymap[row][col]]; ppm_writeppmrow(stdout, newrow, colsG, maxvalP); } } /* main -- main routine of pgmcolor */ int main(argc, argv) { FILE *infile, *palette; /* get command line arguments */ arguments(argc, argv); /* Read palette and PGM input file */ if (!(palette = fopen(paletteName, "r"))) { fprintf(stderr, "%s: cannot open %s\n", progName, paletteName); return 1; } if (! infileName) infile = stdin; else if (!(infile = fopen(infileName, "r"))) { fprintf(stderr, "%s: cannot open %s\n", progName, infileName); return 1; } /* Now call the appropriate routine */ if (absolute) doAbsolute(infile, palette); else doRelative(infile, palette); /* Exit */ return 0; } @EOF chmod 644 pgmcolor.c echo x - Makefile cat >Makefile <<'@EOF' # Makefile for pgmcolor # Copyright Bert Bos, 1990 # pgmcolor needs the three pbm libraries: libpbm, libpgm and libppm # The following three macros comtain the directories where they can be found: PBMDIR=../pbm PGMDIR=../pgm PPMDIR=../ppm # The files pgm.h and ppm.h are also needed, if they are not in the same # directories as their respective libraries, the following macro needs to # be changed: INC=-I $(PPMDIR) -I $(PGMDIR) -I $(PBMDIR) # Choose your compiler and compiler flags: CC=gcc CFLAGS=-O $(INC) LDFLAGS=-L $(PPMDIR) -L $(PGMDIR) -L $(PBMDIR) -lppm -lpgm -lpbm # The single rule to make pgmcolor: pgmcolor: @EOF chmod 644 Makefile exit 0 ----- CUT HERE ----------------------------------------------------------- -- "Always remember, however, that there's Bert Bos (bert@let.rug.nl) usually a simpler and better way to do Alfa-informatica something than the first way that pops RijksUniversiteit Groningen into your head." (D.E. Knuth, TeXbook) Groningen, The Netherlands