diff options
Diffstat (limited to 'analyzer/ppmhist.c')
-rw-r--r-- | analyzer/ppmhist.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/analyzer/ppmhist.c b/analyzer/ppmhist.c new file mode 100644 index 00000000..4c4d3c55 --- /dev/null +++ b/analyzer/ppmhist.c @@ -0,0 +1,290 @@ +/* ppmhist.c - read a PPM image and compute a color histogram +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** 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. +*/ + +#include <assert.h> + +#include "ppm.h" +#include "shhopt.h" +#include "nstring.h" + +enum sort {SORT_BY_FREQUENCY, SORT_BY_RGB}; + +enum colorFmt {FMT_DECIMAL, FMT_HEX, FMT_FLOAT, FMT_PPMPLAIN}; + +struct cmdline_info { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input_filespec; /* Filespecs of input files */ + unsigned int noheader; /* -noheader option */ + enum colorFmt colorFmt; + unsigned int colorname; /* -colorname option */ + enum sort sort; /* -sort option */ +}; + + + +static void +parse_command_line(int argc, char ** argv, + struct cmdline_info * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optStruct3 opt; /* set by OPTENT3 */ + optEntry *option_def = malloc(100*sizeof(optEntry)); + unsigned int option_def_index; + + unsigned int hexcolorOpt, floatOpt, mapOpt, nomapOpt; + const char * sort_type; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "map", OPT_FLAG, NULL, &mapOpt, 0); + OPTENT3(0, "nomap", OPT_FLAG, NULL, &nomapOpt, 0); + OPTENT3(0, "noheader", OPT_FLAG, NULL, &cmdlineP->noheader, 0); + OPTENT3(0, "hexcolor", OPT_FLAG, NULL, &hexcolorOpt, 0); + OPTENT3(0, "float", OPT_FLAG, NULL, &floatOpt, 0); + OPTENT3(0, "colorname", OPT_FLAG, NULL, &cmdlineP->colorname, 0); + OPTENT3(0, "sort", OPT_STRING, &sort_type, NULL, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + /* Set defaults */ + sort_type = "frequency"; + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 == 0) + cmdlineP->input_filespec = "-"; + else if (argc-1 != 1) + pm_error("Program takes zero or one argument (filename). You " + "specified %d", argc-1); + else + cmdlineP->input_filespec = argv[1]; + + if (hexcolorOpt + floatOpt + mapOpt > 1) + pm_error("You can specify only one of -hexcolor, -float, and -map"); + if (hexcolorOpt) + cmdlineP->colorFmt = FMT_HEX; + else if (floatOpt) + cmdlineP->colorFmt = FMT_FLOAT; + else if (mapOpt) + cmdlineP->colorFmt = FMT_PPMPLAIN; + else + cmdlineP->colorFmt = FMT_DECIMAL; + + if (strcmp(sort_type, "frequency") == 0) + cmdlineP->sort = SORT_BY_FREQUENCY; + else if (strcmp(sort_type, "rgb") == 0) + cmdlineP->sort = SORT_BY_RGB; + else + pm_error("Invalid -sort value: '%s'. The valid values are " + "'frequency' and 'rgb'.", sort_type); +} + + + +static int +countcompare(const void *ch1, const void *ch2) { + return ((colorhist_vector)ch2)->value - ((colorhist_vector)ch1)->value; +} + + +static int +rgbcompare(const void * arg1, const void * arg2) { + + colorhist_vector const ch1 = (colorhist_vector) arg1; + colorhist_vector const ch2 = (colorhist_vector) arg2; + + int retval; + + retval = (PPM_GETR(ch1->color) - PPM_GETR(ch2->color)); + if (retval == 0) { + retval = (PPM_GETG(ch1->color) - PPM_GETG(ch2->color)); + if (retval == 0) + retval = (PPM_GETB(ch1->color) - PPM_GETB(ch2->color)); + } + return retval; +} + + + +static const char * +colornameLabel(pixel const color, + pixval const maxval, + unsigned int const nDictColor, + pixel const dictColors[], + const char * const dictColornames[]) { +/*---------------------------------------------------------------------------- + Return the name of the color 'color' or the closest color in the + dictionary to it. If the name returned is not the exact color, + prefix it with "*". Otherwise, prefix it with " ". + + 'nDictColor', dictColors[], and dictColorNames[] are the color + dictionary. + + Return the name in static storage within this subroutine. +-----------------------------------------------------------------------------*/ + static char retval[32]; + int colorIndex; + + pixel color255; + /* The color, normalized to a maxval of 255: the maxval of a color + dictionary. + */ + + PPM_DEPTH(color255, color, maxval, 255); + + colorIndex = ppm_findclosestcolor(dictColors, nDictColor, &color); + + assert(colorIndex >= 0 && colorIndex < nDictColor); + + if (PPM_EQUAL(dictColors[colorIndex], color)) + STRSCPY(retval, " "); + else + STRSCPY(retval, "*"); + + STRSCAT(retval, dictColornames[colorIndex]); + + return retval; +} + + + +static void +printColors(colorhist_vector const chv, + int const nColors, + pixval const maxval, + enum colorFmt const colorFmt, + unsigned int const nKnown, + pixel const knownColors[], + const char * const colornames[]) { + + int i; + + for (i = 0; i < nColors; i++) { + pixval const r = PPM_GETR(chv[i].color); + pixval const g = PPM_GETG(chv[i].color); + pixval const b = PPM_GETB(chv[i].color); + double const lum = PPM_LUMIN(chv[i].color); + unsigned int const intLum = lum + 0.5; + double const floatLum = lum / maxval; + unsigned int const count = chv[i].value; + + const char * colornameValue; + + if (colornames) + colornameValue = colornameLabel(chv[i].color, maxval, + nKnown, knownColors, colornames); + else + colornameValue = ""; + + switch(colorFmt) { + case FMT_FLOAT: + printf(" %1.3f %1.3f %1.3f\t%1.3f\t%7d %s\n", + (double)r / maxval, + (double)g / maxval, + (double)b / maxval, + floatLum, count, colornameValue); + break; + case FMT_HEX: + printf(" %04x %04x %04x\t%5d\t%7d %s\n", + r, g, b, intLum, count, colornameValue); + break; + case FMT_DECIMAL: + printf(" %5d %5d %5d\t%5d\t%7d %s\n", + r, g, b, intLum, count, colornameValue); + break; + case FMT_PPMPLAIN: + printf(" %5d %5d %5d#\t%5d\t%7d %s\n", + r, g, b, intLum, count, colornameValue); + break; + } + } +} + + + +int +main(int argc, char *argv[] ) { + struct cmdline_info cmdline; + FILE* ifP; + colorhist_vector chv; + int rows, cols; + pixval maxval; + int format; + int nColors; + int (*compare_function)(const void *, const void *); + /* The compare function to be used with qsort() to sort the + histogram for output + */ + unsigned int nDictColor; + const char ** dictColornames; + pixel * dictColors; + + ppm_init( &argc, argv ); + + parse_command_line(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.input_filespec); + + ppm_readppminit(ifP, &cols, &rows, &maxval, &format); + + chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, 0, &nColors); + + pm_close(ifP); + + switch (cmdline.sort) { + case SORT_BY_FREQUENCY: + compare_function = countcompare; break; + case SORT_BY_RGB: + compare_function = rgbcompare; break; + } + + qsort((char*) chv, nColors, sizeof(struct colorhist_item), + compare_function); + + /* And print the histogram. */ + if (cmdline.colorFmt == FMT_PPMPLAIN) + printf("P3\n# color map\n%d 1\n%d\n", nColors, maxval); + + if (!cmdline.noheader) { + const char commentDelim = cmdline.colorFmt == FMT_PPMPLAIN ? '#' : ' '; + printf("%c r g b \t lum \t count %s\n", + commentDelim, cmdline.colorname ? "name" : ""); + printf("%c----- ----- ----- \t-----\t------- %s\n", + commentDelim, cmdline.colorname ? "----" : ""); + } + if (cmdline.colorname) { + bool mustOpenTrue = TRUE; + ppm_readcolordict(NULL, mustOpenTrue, + &nDictColor, &dictColornames, &dictColors, NULL); + } else { + dictColors = NULL; + dictColornames = NULL; + } + + printColors(chv, nColors, maxval, + cmdline.colorFmt, nDictColor, dictColors, dictColornames); + + if (dictColors) + free(dictColors); + if (dictColornames) + free(dictColornames); + + ppm_freecolorhist(chv); + + return 0; +} |