/****************************************************************************** pamsummcol ******************************************************************************* Summarize the columns of a PAM image with various functions. By Bryan Henderson, San Jose CA 2004.02.07. Contributed to the public domain ******************************************************************************/ #include "pm_c_util.h" #include "pam.h" #include "shhopt.h" #include "mallocvar.h" enum function {FN_ADD, FN_MEAN, FN_MIN, FN_MAX}; struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * inputFilespec; /* Filespec of input file */ enum function function; unsigned int verbose; }; static void parseCommandLine(int argc, char ** const argv, struct cmdlineInfo * const 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 = malloc(100*sizeof(optEntry)); /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; unsigned int sumSpec, meanSpec, minSpec, maxSpec; option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "sum", OPT_FLAG, NULL, &sumSpec, 0); OPTENT3(0, "mean", OPT_FLAG, NULL, &meanSpec, 0); OPTENT3(0, "min", OPT_FLAG, NULL, &minSpec, 0); OPTENT3(0, "max", OPT_FLAG, NULL, &maxSpec, 0); OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 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 */ pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (sumSpec + minSpec + maxSpec > 1) pm_error("You may specify at most one of -sum, -min, and -max"); if (sumSpec) { cmdlineP->function = FN_ADD; } else if (meanSpec) { cmdlineP->function = FN_MEAN; } else if (minSpec) { cmdlineP->function = FN_MIN; } else if (maxSpec) { cmdlineP->function = FN_MAX; } else pm_error("You must specify one of -sum, -min, or -max"); if (argc-1 > 1) pm_error("Too many arguments (%d). File spec is the only argument.", argc-1); if (argc-1 < 1) cmdlineP->inputFilespec = "-"; else cmdlineP->inputFilespec = argv[1]; } struct accum { union { unsigned int sum; unsigned int min; unsigned int max; } u; }; static void createAccumulator(enum function const function, unsigned int const cols, unsigned int const planes, struct accum *** const accumulatorP) { struct accum ** accumulator; unsigned int col; MALLOCARRAY_NOFAIL(accumulator, cols); for (col = 0; col < cols; ++col) { unsigned int plane; MALLOCARRAY_NOFAIL(accumulator[col], planes); for (plane = 0; plane < planes; ++plane) { switch(function) { case FN_ADD: accumulator[col][plane].u.sum = 0; break; case FN_MEAN: accumulator[col][plane].u.sum = 0; break; case FN_MIN: accumulator[col][plane].u.min = UINT_MAX; break; case FN_MAX: accumulator[col][plane].u.max = 0; break; } } } *accumulatorP = accumulator; } static void destroyAccumulator(struct accum ** accumulator, unsigned int const cols) { unsigned int col; for (col = 0; col < cols; ++col) free(accumulator[col]); free(accumulator); } static void aggregate(struct pam * const inpamP, tuple * const tupleRow, enum function const function, struct accum ** const accumulator) { unsigned int col; for (col = 0; col < inpamP->width; ++col) { unsigned int plane; for (plane = 0; plane < inpamP->depth; ++plane) { switch(function) { case FN_ADD: case FN_MEAN: if (accumulator[col][plane].u.sum > UINT_MAX - tupleRow[col][plane]) pm_error("Numerical overflow in Column %u", col); accumulator[col][plane].u.sum += tupleRow[col][plane]; break; case FN_MIN: if (tupleRow[col][plane] < accumulator[col][plane].u.min) accumulator[col][plane].u.min = tupleRow[col][plane]; break; case FN_MAX: if (tupleRow[col][plane] > accumulator[col][plane].u.min) accumulator[col][plane].u.min = tupleRow[col][plane]; break; } } } } static void makeSummaryRow(struct accum ** const accumulator, unsigned int const count, struct pam * const pamP, enum function const function, tuple * const tupleRow) { unsigned int col; for (col = 0; col < pamP->width; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) { switch(function) { case FN_ADD: tupleRow[col][plane] = MIN(accumulator[col][plane].u.sum, pamP->maxval); break; case FN_MEAN: tupleRow[col][plane] = ROUNDU((double)accumulator[col][plane].u.sum / count); break; case FN_MIN: tupleRow[col][plane] = accumulator[col][plane].u.min; break; case FN_MAX: tupleRow[col][plane] = accumulator[col][plane].u.max; break; } } } } int main(int argc, char *argv[]) { FILE* ifP; tuple* inputRow; /* Row from input image */ tuple* outputRow; /* Output row */ int row; struct cmdlineInfo cmdline; struct pam inpam; /* Input PAM image */ struct pam outpam; /* Output PAM image */ struct accum ** accumulator; /* malloc'ed two-dimensional array */ pnm_init( &argc, argv ); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); createAccumulator(cmdline.function, inpam.width, inpam.depth, &accumulator); inputRow = pnm_allocpamrow(&inpam); outpam = inpam; /* Initial value -- most fields should be same */ outpam.file = stdout; outpam.height = 1; pnm_writepaminit(&outpam); outputRow = pnm_allocpamrow(&outpam); for (row = 0; row < inpam.height; row++) { pnm_readpamrow(&inpam, inputRow); aggregate(&inpam, inputRow, cmdline.function, accumulator); } makeSummaryRow(accumulator, inpam.height, &outpam, cmdline.function, outputRow); pnm_writepamrow(&outpam, outputRow); pnm_freepamrow(outputRow); pnm_freepamrow(inputRow); destroyAccumulator(accumulator, inpam.width); pm_close(inpam.file); pm_close(outpam.file); return 0; }