/****************************************************************************** pamfunc ******************************************************************************* Apply one of various functions to each sample in a PAM image By Bryan Henderson, San Jose CA 2002.06.16. Contributed to the public domain ENHANCEMENT IDEAS: 1) speed up by doing integer arithmetic instead of floating point for multiply/divide where possible. Especially when multiplying by an integer. 2) For multiply/divide, give option of simply changing the maxval and leaving the raster alone. 3) Add a -map option that specifies another PAM image to define an arbitrary mapping function. The map PAM would be have depth one and height one and width equal to the maxval of the input image. The value of Column C in the map PAM would be the value in the output where the input contains the value C. ******************************************************************************/ #include "pam.h" #include "shhopt.h" enum function {FN_MULTIPLY, FN_DIVIDE, FN_ADD, FN_SUBTRACT, FN_MIN, FN_MAX}; /* Note that when the user specifies a minimum, that means he's requesting a "max" function. */ 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; union { float multiplier; float divisor; int adder; int subtractor; unsigned int max; unsigned int min; } u; 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 multiplierSpec, divisorSpec, adderSpec, subtractorSpec; unsigned int maxSpec, minSpec; option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "multiplier", OPT_FLOAT, &cmdlineP->u.multiplier, &multiplierSpec, 0); OPTENT3(0, "divisor", OPT_FLOAT, &cmdlineP->u.divisor, &divisorSpec, 0); OPTENT3(0, "adder", OPT_INT, &cmdlineP->u.adder, &adderSpec, 0); OPTENT3(0, "subtractor", OPT_INT, &cmdlineP->u.subtractor, &subtractorSpec, 0); OPTENT3(0, "min", OPT_UINT, &cmdlineP->u.min, &minSpec, 0); OPTENT3(0, "max", OPT_UINT, &cmdlineP->u.max, &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 */ optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (multiplierSpec + divisorSpec + adderSpec + subtractorSpec + minSpec + maxSpec > 1) pm_error("You may specify at most one of -multiplier, -divisor," "-adder, -subtractor, -min, and -max"); if (multiplierSpec) { cmdlineP->function = FN_MULTIPLY; if (cmdlineP->u.multiplier < 0) pm_error("Multiplier must be nonnegative. You specified %f", cmdlineP->u.multiplier); } else if (divisorSpec) { cmdlineP->function = FN_DIVIDE; if (cmdlineP->u.divisor < 0) pm_error("Divisor must be nonnegative. You specified %f", cmdlineP->u.divisor); } else if (adderSpec) { cmdlineP->function = FN_ADD; } else if (subtractorSpec) { cmdlineP->function = FN_SUBTRACT; } else if (minSpec) { cmdlineP->function = FN_MAX; } else if (maxSpec) { cmdlineP->function = FN_MIN; } else pm_error("You must specify one of -multiplier, -divisor, " "-adder, -subtractor, -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]; } static void applyFunction(struct cmdlineInfo const cmdline, struct pam const inpam, struct pam const outpam, tuple * const inputRow, tuple * const outputRow) { int col; for (col = 0; col < inpam.width; ++col) { int plane; for (plane = 0; plane < inpam.depth; ++plane) { sample const inSample = inputRow[col][plane]; sample outSample; /* Could be > maxval */ switch (cmdline.function) { case FN_MULTIPLY: outSample = ROUNDU(inSample * cmdline.u.multiplier); break; case FN_DIVIDE: outSample = ROUNDU(inSample / cmdline.u.divisor); break; case FN_ADD: outSample = MAX(0, inSample + cmdline.u.adder); break; case FN_SUBTRACT: outSample = MAX(0, inSample - cmdline.u.subtractor); break; case FN_MAX: outSample = MAX(inSample, cmdline.u.min); break; case FN_MIN: outSample = MIN(inSample, cmdline.u.max); break; } outputRow[col][plane] = MIN(outpam.maxval, outSample); } } } int main(int argc, char *argv[]) { FILE* ifP; tuple* inputRow; /* Row from input image */ tuple* outputRow; /* Row of output image */ int row; struct cmdlineInfo cmdline; struct pam inpam; /* Input PAM image */ struct pam outpam; /* Output PAM image */ pnm_init( &argc, argv ); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFilespec); pnm_readpaminit(ifP, &inpam, sizeof(inpam)); inputRow = pnm_allocpamrow(&inpam); outpam = inpam; /* Initial value -- most fields should be same */ outpam.file = stdout; pnm_writepaminit(&outpam); outputRow = pnm_allocpamrow(&outpam); for (row = 0; row < inpam.height; row++) { pnm_readpamrow(&inpam, inputRow); applyFunction(cmdline, inpam, outpam, inputRow, outputRow); pnm_writepamrow(&outpam, outputRow); } pnm_freepamrow(outputRow); pnm_freepamrow(inputRow); pm_close(inpam.file); pm_close(outpam.file); exit(0); }