about summary refs log tree commit diff
path: root/analyzer/pnmhistmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'analyzer/pnmhistmap.c')
-rw-r--r--analyzer/pnmhistmap.c483
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;
+}