about summary refs log tree commit diff
path: root/analyzer/ppmhist.c
blob: 4c4d3c55a3ef69cc810990673bae392388ede1c2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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;
}