1 /******************************************************************************
2                                pamsummcol
3 *******************************************************************************
4   Summarize the columns of a PAM image with various functions.
5 
6   By Bryan Henderson, San Jose CA 2004.02.07.
7 
8   Contributed to the public domain
9 
10 
11 ******************************************************************************/
12 
13 #include "pm_c_util.h"
14 #include "pam.h"
15 #include "shhopt.h"
16 #include "mallocvar.h"
17 
18 enum function {FN_ADD, FN_MEAN, FN_MIN, FN_MAX};
19 
20 struct cmdlineInfo {
21     /* All the information the user supplied in the command line,
22        in a form easy for the program to use.
23     */
24     const char *inputFilespec;  /* Filespec of input file */
25     enum function function;
26     unsigned int verbose;
27 };
28 
29 
30 static void
parseCommandLine(int argc,char ** const argv,struct cmdlineInfo * const cmdlineP)31 parseCommandLine(int argc, char ** const argv,
32                  struct cmdlineInfo * const cmdlineP) {
33 /*----------------------------------------------------------------------------
34    Note that the file spec array we return is stored in the storage that
35    was passed to us as the argv array.
36 -----------------------------------------------------------------------------*/
37     optEntry *option_def = malloc(100*sizeof(optEntry));
38         /* Instructions to OptParseOptions2 on how to parse our options.
39          */
40     optStruct3 opt;
41 
42     unsigned int option_def_index;
43 
44     unsigned int sumSpec, meanSpec, minSpec, maxSpec;
45 
46     option_def_index = 0;   /* incremented by OPTENTRY */
47     OPTENT3(0,   "sum",      OPT_FLAG,  NULL, &sumSpec,           0);
48     OPTENT3(0,   "mean",     OPT_FLAG,  NULL, &meanSpec,          0);
49     OPTENT3(0,   "min",      OPT_FLAG,  NULL, &minSpec,           0);
50     OPTENT3(0,   "max",      OPT_FLAG,  NULL, &maxSpec,           0);
51     OPTENT3(0,   "verbose",  OPT_FLAG,  NULL, &cmdlineP->verbose, 0);
52 
53     opt.opt_table = option_def;
54     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
55     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
56 
57     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
58         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
59 
60     if (sumSpec + minSpec + maxSpec > 1)
61         pm_error("You may specify at most one of -sum, -min, and -max");
62 
63     if (sumSpec) {
64         cmdlineP->function = FN_ADD;
65     } else if (meanSpec) {
66         cmdlineP->function = FN_MEAN;
67     } else if (minSpec) {
68         cmdlineP->function = FN_MIN;
69     } else if (maxSpec) {
70         cmdlineP->function = FN_MAX;
71     } else
72         pm_error("You must specify one of -sum, -min, or -max");
73 
74     if (argc-1 > 1)
75         pm_error("Too many arguments (%d).  File spec is the only argument.",
76                  argc-1);
77 
78     if (argc-1 < 1)
79         cmdlineP->inputFilespec = "-";
80     else
81         cmdlineP->inputFilespec = argv[1];
82 
83 }
84 
85 
86 struct accum {
87     union {
88         unsigned int sum;
89         unsigned int min;
90         unsigned int max;
91     } u;
92 };
93 
94 
95 
96 static void
createAccumulator(enum function const function,unsigned int const cols,unsigned int const planes,struct accum *** const accumulatorP)97 createAccumulator(enum function    const function,
98                   unsigned int     const cols,
99                   unsigned int     const planes,
100                   struct accum *** const accumulatorP) {
101 
102     struct accum ** accumulator;
103     unsigned int col;
104 
105     MALLOCARRAY_NOFAIL(accumulator, cols);
106 
107     for (col = 0; col < cols; ++col) {
108         unsigned int plane;
109 
110         MALLOCARRAY_NOFAIL(accumulator[col], planes);
111 
112         for (plane = 0; plane < planes; ++plane) {
113             switch(function) {
114             case FN_ADD:  accumulator[col][plane].u.sum = 0;        break;
115             case FN_MEAN: accumulator[col][plane].u.sum = 0;        break;
116             case FN_MIN:  accumulator[col][plane].u.min = UINT_MAX; break;
117             case FN_MAX:  accumulator[col][plane].u.max = 0;        break;
118             }
119         }
120     }
121     *accumulatorP = accumulator;
122 }
123 
124 
125 
126 static void
destroyAccumulator(struct accum ** accumulator,unsigned int const cols)127 destroyAccumulator(struct accum **    accumulator,
128                    unsigned int const cols) {
129 
130     unsigned int col;
131     for (col = 0; col < cols; ++col)
132         free(accumulator[col]);
133 
134     free(accumulator);
135 }
136 
137 
138 
139 static void
aggregate(struct pam * const inpamP,tuple * const tupleRow,enum function const function,struct accum ** const accumulator)140 aggregate(struct pam *    const inpamP,
141           tuple *         const tupleRow,
142           enum function   const function,
143           struct accum ** const accumulator) {
144 
145     unsigned int col;
146 
147     for (col = 0; col < inpamP->width; ++col) {
148         unsigned int plane;
149         for (plane = 0; plane < inpamP->depth; ++plane) {
150             switch(function) {
151             case FN_ADD:
152             case FN_MEAN:
153                 if (accumulator[col][plane].u.sum >
154                     UINT_MAX - tupleRow[col][plane])
155                     pm_error("Numerical overflow in Column %u", col);
156                 accumulator[col][plane].u.sum += tupleRow[col][plane];
157             break;
158             case FN_MIN:
159                 if (tupleRow[col][plane] < accumulator[col][plane].u.min)
160                     accumulator[col][plane].u.min = tupleRow[col][plane];
161                 break;
162             case FN_MAX:
163                 if (tupleRow[col][plane] > accumulator[col][plane].u.min)
164                     accumulator[col][plane].u.min = tupleRow[col][plane];
165                 break;
166             }
167         }
168     }
169 }
170 
171 
172 
173 static void
makeSummaryRow(struct accum ** const accumulator,unsigned int const count,struct pam * const pamP,enum function const function,tuple * const tupleRow)174 makeSummaryRow(struct accum ** const accumulator,
175                unsigned int  const   count,
176                struct pam *  const   pamP,
177                enum function const   function,
178                tuple *       const   tupleRow) {
179 
180     unsigned int col;
181 
182     for (col = 0; col < pamP->width; ++col) {
183         unsigned int plane;
184         for (plane = 0; plane < pamP->depth; ++plane) {
185             switch(function) {
186             case FN_ADD:
187                 tupleRow[col][plane] =
188                     MIN(accumulator[col][plane].u.sum, pamP->maxval);
189                 break;
190             case FN_MEAN:
191                 tupleRow[col][plane] =
192                     ROUNDU((double)accumulator[col][plane].u.sum / count);
193                 break;
194             case FN_MIN:
195                 tupleRow[col][plane] =
196                     accumulator[col][plane].u.min;
197                 break;
198             case FN_MAX:
199                 tupleRow[col][plane] =
200                     accumulator[col][plane].u.max;
201                 break;
202             }
203         }
204     }
205 }
206 
207 
208 
209 int
main(int argc,char * argv[])210 main(int argc, char *argv[]) {
211 
212     FILE* ifP;
213     tuple* inputRow;   /* Row from input image */
214     tuple* outputRow;  /* Output row */
215     int row;
216     struct cmdlineInfo cmdline;
217     struct pam inpam;   /* Input PAM image */
218     struct pam outpam;  /* Output PAM image */
219     struct accum ** accumulator;  /* malloc'ed two-dimensional array */
220 
221     pnm_init( &argc, argv );
222 
223     parseCommandLine(argc, argv, &cmdline);
224 
225     ifP = pm_openr(cmdline.inputFilespec);
226 
227     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
228 
229     createAccumulator(cmdline.function, inpam.width, inpam.depth,
230                       &accumulator);
231 
232     inputRow = pnm_allocpamrow(&inpam);
233 
234     outpam = inpam;    /* Initial value -- most fields should be same */
235     outpam.file = stdout;
236     outpam.height = 1;
237 
238     pnm_writepaminit(&outpam);
239 
240     outputRow = pnm_allocpamrow(&outpam);
241 
242     for (row = 0; row < inpam.height; row++) {
243         pnm_readpamrow(&inpam, inputRow);
244 
245         aggregate(&inpam, inputRow, cmdline.function, accumulator);
246     }
247     makeSummaryRow(accumulator, inpam.height, &outpam, cmdline.function,
248                    outputRow);
249     pnm_writepamrow(&outpam, outputRow);
250 
251     pnm_freepamrow(outputRow);
252     pnm_freepamrow(inputRow);
253     destroyAccumulator(accumulator, inpam.width);
254     pm_close(inpam.file);
255     pm_close(outpam.file);
256 
257     return 0;
258 }
259