diff options
Diffstat (limited to 'analyzer/pnmhistmap.c')
-rw-r--r-- | analyzer/pnmhistmap.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/analyzer/pnmhistmap.c b/analyzer/pnmhistmap.c new file mode 100644 index 00000000..0b1eeb1f --- /dev/null +++ b/analyzer/pnmhistmap.c @@ -0,0 +1,483 @@ +/* pnmhistmap.c - + * Draw a histogram for a PGM or PPM file + * + * Options: -verbose: the usual + * -max N: force scaling value to N + * -black: ignore all-black count + * -white: ignore all-white count + * + * - PGM histogram is a PBM file, PPM histogram is a PPM file + * - No conditional code - assumes all three: PBM, PGM, PPM + * + * Copyright (C) 1993 by Wilson H. Bent, Jr (whb@usc.edu) + * + * 2004-12-11 john h. dubois iii (john@armory.com) + * - Added options: + * -dots, -nmax, -red, -green, -blue, -width, -height, -lval, -rval + * - Deal properly with maxvals other than 256 + */ + +#include <string.h> + +#include "pnm.h" +#include "shhopt.h" +#include "mallocvar.h" + +static double const epsilon = .00001; + +#define SCALE_H(value) (hscale_unity ? (value) : (int)((value) * hscale)) + +enum wantedColor {WANT_RED=0, WANT_GRN=1, WANT_BLU=2}; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* Filespecs of input files */ + unsigned int black; + unsigned int white; + unsigned int dots; + bool colorWanted[3]; + /* subscript is enum wantedColor */ + unsigned int verbose; + unsigned int nmaxSpec; + float nmax; + unsigned int lval; + unsigned int rval; + unsigned int widthSpec; + unsigned int width; + unsigned int height; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + unsigned int lvalSpec, rvalSpec, heightSpec; + unsigned int redSpec, greenSpec, blueSpec; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "black", OPT_FLAG, NULL, &cmdlineP->black, 0); + OPTENT3(0, "white", OPT_FLAG, NULL, &cmdlineP->white, 0); + OPTENT3(0, "dots", OPT_FLAG, NULL, &cmdlineP->dots, 0); + OPTENT3(0, "red", OPT_FLAG, NULL, &redSpec, 0); + OPTENT3(0, "green", OPT_FLAG, NULL, &greenSpec, 0); + OPTENT3(0, "blue", OPT_FLAG, NULL, &blueSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + OPTENT3(0, "nmax", OPT_FLOAT, &cmdlineP->nmax, + &cmdlineP->nmaxSpec, 0); + OPTENT3(0, "lval", OPT_UINT, &cmdlineP->lval, + &lvalSpec, 0); + OPTENT3(0, "rval", OPT_UINT, &cmdlineP->rval, + &rvalSpec, 0); + OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, + &cmdlineP->widthSpec, 0); + OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, + &heightSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!lvalSpec) + cmdlineP->lval = 0; + if (!rvalSpec) + cmdlineP->rval = PNM_OVERALLMAXVAL; + + if (!redSpec && !greenSpec && !blueSpec) { + cmdlineP->colorWanted[WANT_RED] = TRUE; + cmdlineP->colorWanted[WANT_GRN] = TRUE; + cmdlineP->colorWanted[WANT_BLU] = TRUE; + } else { + cmdlineP->colorWanted[WANT_RED] = redSpec; + cmdlineP->colorWanted[WANT_GRN] = greenSpec; + cmdlineP->colorWanted[WANT_BLU] = blueSpec; + } + + if (!heightSpec) + cmdlineP->height = 200; + + if (argc-1 == 0) + cmdlineP->inputFilespec = "-"; + else if (argc-1 != 1) + pm_error("Program takes zero or one argument (filename). You " + "specified %d", argc-1); + else + cmdlineP->inputFilespec = argv[1]; +} + + + +static unsigned int +maxSlotCount(const unsigned int * const hist, + unsigned int const hist_width, + bool const no_white, + bool const no_black) { +/*---------------------------------------------------------------------------- + Return the maximum count among all the slots in hist[], not counting + the first and last as suggested by 'no_white' and 'no_black'. +-----------------------------------------------------------------------------*/ + unsigned int hmax; + unsigned int i; + + unsigned int const start = (no_black ? 1 : 0); + unsigned int const finish = (no_white ? hist_width - 1 : hist_width); + for (hmax = 0, i = start; i < finish; ++i) + if (hmax < hist[i]) + hmax = hist[i]; + + return hmax; +} + + + +static void +clipHistogram(unsigned int * const hist, + unsigned int const hist_width, + unsigned int const hmax) { + + unsigned int i; + + for (i = 0; i < hist_width; ++i) + hist[i] = MIN(hmax, hist[i]); +} + + + +static void +pgm_hist(FILE * const fp, + int const cols, + int const rows, + xelval const maxval, + int const format, + bool const dots, + bool const no_white, + bool const no_black, + bool const verbose, + xelval const startval, + xelval const endval, + unsigned int const hist_width, + unsigned int const hist_height, + bool const clipSpec, + unsigned int const clipCount, + double const hscale) { + + bool const hscale_unity = hscale - 1 < epsilon; + + gray * grayrow; + bit ** bits; + int i, j; + unsigned int * ghist; + double vscale; + unsigned int hmax; + + if ((ghist = calloc(hist_width, sizeof(int))) == NULL) + pm_error ("Not enough memory for histogram array (%d bytes)", + hist_width * sizeof(int)); + if ((bits = pbm_allocarray (hist_width, hist_height)) == NULL) + pm_error ("no space for output array (%d bits)", + hist_width * hist_height); + memset (ghist, 0, sizeof (ghist)); + + /* read the pixel values into the histogram arrays */ + grayrow = pgm_allocrow (cols); + /*XX error-check! */ + if (verbose) pm_message ("making histogram..."); + for (i = rows; i > 0; --i) { + pgm_readpgmrow (fp, grayrow, cols, maxval, format); + for (j = cols-1; j >= 0; --j) { + int value; + + if ((value = grayrow[j]) >= startval && value <= endval) + ghist[SCALE_H(value-startval)]++; + } + } + pgm_freerow (grayrow); + fclose (fp); + + /* find the highest-valued slot and set the vertical scale value */ + if (verbose) + pm_message ("finding max. slot height..."); + if (clipSpec) + hmax = clipCount; + else + hmax = maxSlotCount(ghist, hist_width, no_white, no_black); + + if (verbose) + pm_message ("Done: height = %u", hmax); + + clipHistogram(ghist, hist_width, hmax); + + vscale = (double) hist_height / hmax; + + for (i = 0; i < hist_width; ++i) { + int mark = hist_height - (int)(vscale * ghist[i]); + for (j = 0; j < mark; ++j) + bits[j][i] = PBM_BLACK; + if (j < hist_height) + bits[j++][i] = PBM_WHITE; + for ( ; j < hist_height; ++j) + bits[j][i] = dots ? PBM_BLACK : PBM_WHITE; + } + + pbm_writepbm (stdout, bits, hist_width, hist_height, 0); +} + + + +static unsigned int +maxSlotCountAll(unsigned int * const hist[3], + unsigned int const hist_width, + bool const no_white, + bool const no_black) { +/*---------------------------------------------------------------------------- + Return the maximum count among all the slots in hist[x] not + counting the first and last as suggested by 'no_white' and + 'no_black'. hist[x] may be NULL to indicate none. +-----------------------------------------------------------------------------*/ + unsigned int hmax; + unsigned int color; + + hmax = 0; + + for (color = 0; color < 3; ++color) + if (hist[color]) + hmax = MAX(hmax, + maxSlotCount(hist[color], + hist_width, no_white, no_black)); + + return hmax; +} + + + +static void +createHist(bool const colorWanted[3], + unsigned int const hist_width, + unsigned int * (* const histP)[3]) { +/*---------------------------------------------------------------------------- + Allocate the histogram arrays and set each slot count to zero. +-----------------------------------------------------------------------------*/ + unsigned int color; + + for (color = 0; color < 3; ++color) + if (colorWanted[color]) { + unsigned int * hist; + unsigned int i; + MALLOCARRAY(hist, hist_width); + if (hist == NULL) + pm_error ("Not enough memory for histogram arrays (%u bytes)", + hist_width * sizeof(int) * 3); + + for (i = 0; i < hist_width; ++i) + hist[i] = 0; + (*histP)[color] = hist; + } else + (*histP)[color] = NULL; +} + + + +static void +clipHistogramAll(unsigned int * const hist[3], + unsigned int const hist_width, + unsigned int const hmax) { + + unsigned int color; + + for (color = 0; color < 3; ++color) + if (hist[color]) + clipHistogram(hist[color], hist_width, hmax); +} + + + +static void +ppm_hist(FILE * const fp, + int const cols, + int const rows, + xelval const maxval, + int const format, + bool const dots, + bool const no_white, + bool const no_black, + bool const colorWanted[3], + bool const verbose, + xelval const startval, + xelval const endval, + unsigned int const hist_width, + unsigned int const hist_height, + bool const clipSpec, + unsigned int const clipCount, + double const hscale) { + + bool const hscale_unity = hscale - 1 < epsilon; + + pixel *pixrow; + pixel **pixels; + int i, j; + unsigned int * hist[3]; /* Subscript is enum wantedColor */ + double vscale; + unsigned int hmax; + + createHist(colorWanted, hist_width, &hist); + + if ((pixels = ppm_allocarray (hist_width, hist_height)) == NULL) + pm_error ("no space for output array (%d pixels)", + hist_width * hist_height); + for (i = 0; i < hist_height; ++i) + memset (pixels[i], 0, hist_width * sizeof (pixel)); + + /* read the pixel values into the histogram arrays */ + pixrow = ppm_allocrow (cols); + /*XX error-check! */ + if (verbose) pm_message ("making histogram..."); + for (i = rows; i > 0; --i) { + ppm_readppmrow (fp, pixrow, cols, maxval, format); + for (j = cols-1; j >= 0; --j) { + int value; + + if (colorWanted[WANT_RED] && + (value = PPM_GETR(pixrow[j])) >= startval && + value <= endval) + hist[WANT_RED][SCALE_H(value-startval)]++; + if (colorWanted[WANT_GRN] && + (value = PPM_GETG(pixrow[j])) >= startval && + value <= endval) + hist[WANT_GRN][SCALE_H(value-startval)]++; + if (colorWanted[WANT_BLU] && + (value = PPM_GETB(pixrow[j])) >= startval && + value <= endval) + hist[WANT_BLU][SCALE_H(value-startval)]++; + } + } + ppm_freerow (pixrow); + fclose (fp); + + /* find the highest-valued slot and set the vertical scale value */ + if (verbose) + pm_message ("finding max. slot height..."); + if (clipSpec) + hmax = clipCount; + else + hmax = maxSlotCountAll(hist, hist_width, no_white, no_black); + + clipHistogramAll(hist, hist_width, hmax); + + vscale = (double) hist_height / hmax; + if (verbose) + pm_message("Done: height = %d, vertical scale factor = %g", + hmax, vscale); + + for (i = 0; i < hist_width; ++i) { + if (hist[WANT_RED]) { + unsigned int j; + bool plotted; + plotted = FALSE; + for (j = hist_height - (int)(vscale * hist[WANT_RED][i]); + j < hist_height && !plotted; + ++j) { + PPM_PUTR(pixels[j][i], maxval); + plotted = dots; + } + } + if (hist[WANT_GRN]) { + unsigned int j; + bool plotted; + plotted = FALSE; + for (j = hist_height - (int)(vscale * hist[WANT_GRN][i]); + j < hist_height && !plotted; + ++j) { + PPM_PUTG(pixels[j][i], maxval); + plotted = dots; + } + } + if (hist[WANT_BLU]) { + unsigned int j; + bool plotted; + plotted = FALSE; + for (j = hist_height - (int)(vscale * hist[WANT_BLU][i]); + j < hist_height && !plotted; + ++j) { + PPM_PUTB(pixels[j][i], maxval); + plotted = dots; + } + } + } + ppm_writeppm (stdout, pixels, hist_width, hist_height, maxval, 0); +} + + + +int +main (int argc, char ** argv) { + + struct cmdlineInfo cmdline; + FILE* ifP; + int cols, rows; + xelval maxval; + int format; + unsigned int hist_width; + unsigned int range; + double hscale; + int hmax; + + pnm_init (&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + pnm_readpnminit(ifP, &cols, &rows, &maxval, &format); + + range = MIN(maxval, cmdline.rval) - cmdline.lval + 1; + + if (cmdline.widthSpec) + hist_width = cmdline.width; + else + hist_width = range; + + hscale = (float)hist_width / range; + if (hscale - 1.0 < epsilon && cmdline.verbose) + pm_message("Horizontal scale factor: %g (maxval = %u)", + hscale, maxval); + + if (cmdline.nmaxSpec) + hmax = cols * rows / hist_width * cmdline.nmax; + + switch (PNM_FORMAT_TYPE(format)) { + case PPM_TYPE: + ppm_hist(ifP, cols, rows, maxval, format, + cmdline.dots, cmdline.white, cmdline.black, + cmdline.colorWanted, + cmdline.verbose, cmdline.lval, cmdline.rval, + hist_width, cmdline.height, cmdline.nmaxSpec, hmax, hscale); + break; + case PGM_TYPE: + pgm_hist(ifP, cols, rows, maxval, format, + cmdline.dots, cmdline.white, cmdline.black, + cmdline.verbose, cmdline.lval, cmdline.rval, + hist_width, cmdline.height, cmdline.nmaxSpec, hmax, hscale); + break; + case PBM_TYPE: + pm_error("Cannot do a histogram of a a PBM file"); + break; + } + return 0; +} |