about summary refs log tree commit diff
path: root/editor
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2020-12-28 01:09:14 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2020-12-28 01:09:14 +0000
commitaff215cc29a295b7bdcc604ae1e71c5514113c29 (patch)
tree57d963c2047f0f4ee666035c93857f2a5991d162 /editor
parent903b4c784afe71fa323e5e4ae65e3a7605f51eee (diff)
downloadnetpbm-mirror-aff215cc29a295b7bdcc604ae1e71c5514113c29.tar.gz
netpbm-mirror-aff215cc29a295b7bdcc604ae1e71c5514113c29.tar.xz
netpbm-mirror-aff215cc29a295b7bdcc604ae1e71c5514113c29.zip
Promote Development to Advanced as Release 10.93.00
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@4017 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor')
-rw-r--r--editor/pamaddnoise.c614
-rw-r--r--editor/pamcomp.c436
-rw-r--r--editor/specialty/ppmshift.c129
3 files changed, 617 insertions, 562 deletions
diff --git a/editor/pamaddnoise.c b/editor/pamaddnoise.c
index ccfde0b6..20c68d99 100644
--- a/editor/pamaddnoise.c
+++ b/editor/pamaddnoise.c
@@ -1,8 +1,8 @@
 /*
-** 
-** Add gaussian, multiplicative gaussian, impulse, laplacian or 
-** poisson noise to a portable anymap.
-** 
+**
+** Add gaussian, multiplicative gaussian, impulse, laplacian or
+** poisson noise to a Netpbm image
+**
 ** Version 1.0  November 1995
 **
 ** Copyright (C) 1995 by Mike Burns (burns@cac.psu.edu)
@@ -28,33 +28,194 @@
 
 #define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
+#include <assert.h>
 #include <math.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pm_gamma.h"
 #include "pam.h"
 
-#define RANDOM_MASK 0x7FFF  /* only compare lower 15 bits.  Stupid PCs. */
-
 static double const EPSILON = 1.0e-5;
-static double const arand = 32767.0;      /* 2^15-1 in case stoopid computer */
-
-enum noiseType {
-    GAUSSIAN,
-    IMPULSE,  /* aka salt and pepper noise */
-    LAPLACIAN,
-    MULTIPLICATIVE_GAUSSIAN,
-    POISSON,
-    MAX_NOISE_TYPES
+
+
+
+static double
+rand1() {
+
+    return (double)rand()/RAND_MAX;
+}
+
+
+
+enum NoiseType {
+    NOISETYPE_GAUSSIAN,
+    NOISETYPE_IMPULSE,  /* aka salt and pepper noise */
+    NOISETYPE_LAPLACIAN,
+    NOISETYPE_MULTIPLICATIVE_GAUSSIAN,
+    NOISETYPE_POISSON
+};
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+
+    enum NoiseType noiseType;
+
+    unsigned int seed;
+
+    float lambda;
+    float lsigma;
+    float mgsigma;
+    float sigma1;
+    float sigma2;
+    float tolerance;
 };
 
 
 
+static enum NoiseType
+typeFmName(const char * const name) {
+
+    enum NoiseType retval;
+
+    if (false)
+        assert(false);
+    else if (pm_keymatch(name, "gaussian", 1))
+        retval = NOISETYPE_GAUSSIAN;
+    else if (pm_keymatch(name, "impulse", 1))
+        retval = NOISETYPE_IMPULSE;
+    else if (pm_keymatch(name, "laplacian", 1))
+        retval = NOISETYPE_LAPLACIAN;
+    else if (pm_keymatch(name, "multiplicative_gaussian", 1))
+        retval = NOISETYPE_MULTIPLICATIVE_GAUSSIAN;
+    else if (pm_keymatch(name, "poisson", 1))
+        retval = NOISETYPE_POISSON;
+    else
+        pm_error("Unrecognized -type value '%s'.  "
+                 "We recognize 'gaussian', 'impulse', 'laplacian', "
+                 "'multiplicative_gaussian', and 'poisson'", name);
+
+    return retval;
+}
+
+
+
+static void
+parseCommandLine(int argc, const 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;
+        /* Instructions to OptParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int typeSpec, seedSpec, lambdaSpec, lsigmaSpec, mgsigmaSpec,
+        sigma1Spec, sigma2Spec, toleranceSpec;
+
+    const char * type;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "type",            OPT_STRING,   &type,
+            &typeSpec,         0);
+    OPTENT3(0,   "seed",            OPT_UINT,     &cmdlineP->seed,
+            &seedSpec,         0);
+    OPTENT3(0,   "lambda",          OPT_FLOAT,    &cmdlineP->lambda,
+            &lambdaSpec,       0);
+    OPTENT3(0,   "lsigma",          OPT_FLOAT,    &cmdlineP->lsigma,
+            &lsigmaSpec,       0);
+    OPTENT3(0,   "mgsigma",         OPT_FLOAT,    &cmdlineP->mgsigma,
+            &mgsigmaSpec,      0);
+    OPTENT3(0,   "sigma1",          OPT_FLOAT,    &cmdlineP->sigma1,
+            &sigma1Spec,       0);
+    OPTENT3(0,   "sigma2",          OPT_FLOAT,    &cmdlineP->sigma2,
+            &sigma2Spec,       0);
+    OPTENT3(0,   "tolerance",       OPT_FLOAT,    &cmdlineP->tolerance,
+            &toleranceSpec,    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, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!typeSpec)
+        cmdlineP->noiseType = NOISETYPE_GAUSSIAN;
+    else
+        cmdlineP->noiseType = typeFmName(type);
+
+    if (sigma1Spec && cmdlineP->noiseType != NOISETYPE_GAUSSIAN)
+        pm_error("-sigma1 is valid only with -type=gaussian");
+
+    if (sigma2Spec && cmdlineP->noiseType != NOISETYPE_GAUSSIAN)
+        pm_error("-sigma2 is valid only with -type=gaussian");
+
+    if (mgsigmaSpec &&
+        cmdlineP->noiseType != NOISETYPE_MULTIPLICATIVE_GAUSSIAN)
+        pm_error("-mgsigma is valid only with -type=multiplicative_guassian");
+
+    if (toleranceSpec && cmdlineP->noiseType != NOISETYPE_IMPULSE)
+        pm_error("-tolerance is valid only with -type=impulse");
+
+    if (lsigmaSpec && cmdlineP->noiseType != NOISETYPE_LAPLACIAN)
+        pm_error("-lsigma is valid only with -type=laplacian");
+
+    if (lambdaSpec && cmdlineP->noiseType != NOISETYPE_POISSON)
+        pm_error("-lambda is valid only with -type=poisson");
+
+    if (!lambdaSpec)
+        cmdlineP->lambda = 12.0;
+
+    if (!lsigmaSpec)
+        cmdlineP->lsigma = 10.0;
+
+    if (!mgsigmaSpec)
+        cmdlineP->mgsigma = 0.5;
+
+    if (!sigma1Spec)
+        cmdlineP->sigma1 = 4.0;
+
+    if (!sigma2Spec)
+        cmdlineP->sigma2 = 20.0;
+
+    if (!toleranceSpec)
+        cmdlineP->tolerance = 0.10;
+
+    if (!seedSpec)
+        cmdlineP->seed = pm_randseed();
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  File spec is the only argument.",
+                 argc-1);
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
+}
+
+
+
 static void
-gaussian_noise(sample   const maxval,
-               sample   const origSample,
-               sample * const newSampleP,
-               float    const sigma1,
-               float    const sigma2) {
+addGaussianNoise(sample   const maxval,
+                 sample   const origSample,
+                 sample * const newSampleP,
+                 float    const sigma1,
+                 float    const sigma2) {
 /*----------------------------------------------------------------------------
    Add Gaussian noise.
 
@@ -64,14 +225,14 @@ gaussian_noise(sample   const maxval,
     double x1, x2, xn, yn;
     double rawNewSample;
 
-    x1 = (rand() & RANDOM_MASK) / arand; 
+    x1 = rand1();
 
     if (x1 == 0.0)
         x1 = 1.0;
-    x2 = (rand() & RANDOM_MASK) / arand;
+    x2 = rand1();
     xn = sqrt(-2.0 * log(x1)) * cos(2.0 * M_PI * x2);
     yn = sqrt(-2.0 * log(x1)) * sin(2.0 * M_PI * x2);
-    
+
     rawNewSample =
         origSample + (sqrt((double) origSample) * sigma1 * xn) + (sigma2 * yn);
 
@@ -81,19 +242,19 @@ gaussian_noise(sample   const maxval,
 
 
 static void
-impulse_noise(sample   const maxval,
-              sample   const origSample,
-              sample * const newSampleP,
-              float    const tolerance) {
+addImpulseNoise(sample   const maxval,
+                sample   const origSample,
+                sample * const newSampleP,
+                float    const tolerance) {
 /*----------------------------------------------------------------------------
    Add impulse (salt and pepper) noise
 -----------------------------------------------------------------------------*/
 
     double const low_tol  = tolerance / 2.0;
     double const high_tol = 1.0 - (tolerance / 2.0);
-    double const sap = (rand() & RANDOM_MASK) / arand; 
+    double const sap = rand1();
 
-    if (sap < low_tol) 
+    if (sap < low_tol)
         *newSampleP = 0;
     else if ( sap >= high_tol )
         *newSampleP = maxval;
@@ -104,18 +265,18 @@ impulse_noise(sample   const maxval,
 
 
 static void
-laplacian_noise(sample   const maxval,
-                double   const infinity,
-                sample   const origSample,
-                sample * const newSampleP,
-                float    const lsigma) {
+addLaplacianNoise(sample   const maxval,
+                  double   const infinity,
+                  sample   const origSample,
+                  sample * const newSampleP,
+                  float    const lsigma) {
 /*----------------------------------------------------------------------------
    Add Laplacian noise
 
    From Pitas' book.
 -----------------------------------------------------------------------------*/
-    double const u = (rand() & RANDOM_MASK) / arand; 
-                
+    double const u = rand1();
+
     double rawNewSample;
 
     if (u <= 0.5) {
@@ -136,11 +297,11 @@ laplacian_noise(sample   const maxval,
 
 
 static void
-multiplicative_gaussian_noise(sample   const maxval,
-                              double   const infinity,
-                              sample   const origSample,
-                              sample * const newSampleP,
-                              float    const mgsigma) {
+addMultiplicativeGaussianNoise(sample   const maxval,
+                               double   const infinity,
+                               sample   const origSample,
+                               sample * const newSampleP,
+                               float    const mgsigma) {
 /*----------------------------------------------------------------------------
    Add multiplicative Gaussian noise
 
@@ -150,14 +311,14 @@ multiplicative_gaussian_noise(sample   const maxval,
     double rawNewSample;
 
     {
-        double const uniform = (rand() & RANDOM_MASK) / arand; 
+        double const uniform = rand1();
         if (uniform <= EPSILON)
             rayleigh = infinity;
         else
             rayleigh = sqrt(-2.0 * log( uniform));
     }
     {
-        double const uniform = (rand() & RANDOM_MASK) / arand; 
+        double const uniform = rand1();
         gauss = rayleigh * cos(2.0 * M_PI * uniform);
     }
     rawNewSample = origSample + (origSample * mgsigma * gauss);
@@ -166,40 +327,89 @@ multiplicative_gaussian_noise(sample   const maxval,
 }
 
 
+static double
+poissonPmf(double       const lambda,
+           unsigned int const k) {
+/*----------------------------------------------------------------------------
+   This is the probability mass function (PMF) of a discrete random variable
+   with lambda 'lambda'.
+
+   I.e. it gives the probability that a value sampled from a Poisson
+   distribution with lambda 'lambda' has the value 'k'.
+
+   That means it's the probability that in a Poisson stream of events in which
+   the mean number of events in an interval of a certains size is 'lambda' that
+   'k' events happen.
+-----------------------------------------------------------------------------*/
+    double x;
+    unsigned int i;
+
+    /* We're computing the formula
+
+         (pow(lamda, k) * exp(-lambda)) / fact(k).
+
+       Note that k is ordinarily quite small.
+    */
+
+    x = exp(-lambda);
+
+    for (i = 1; i <= k; ++i) {
+        x *= lambda;
+        x /= i;
+    }
+    return x;
+}
+
+
 
 static void
-poisson_noise(sample   const maxval,
-              sample   const origSample,
-              sample * const newSampleP,
-              float    const lambda) {
+addPoissonNoise(struct pam * const pamP,
+                sample       const origSample,
+                sample *     const newSampleP,
+                float        const lambdaOfMaxval) {
 /*----------------------------------------------------------------------------
    Add Poisson noise
 -----------------------------------------------------------------------------*/
-    double const x  = lambda * origSample;
-    double const x1 = exp(-x);
+    samplen const origSamplen = pnm_normalized_sample(pamP, origSample);
+
+    float const origSampleIntensity = pm_ungamma709(origSamplen);
+
+    double const lambda  = origSampleIntensity * lambdaOfMaxval;
+
+    double const u = rand1();
+
+    /* We now apply the inverse CDF (cumulative distribution function) of the
+       Poisson distribution to uniform random variable 'u' to get a Poisson
+       random variable.  Unfortunately, we have no algebraic equation for the
+       inverse of the CDF, but the random variable is discrete, so we can just
+       iterate.
+    */
 
-    double rawNewSample;
-    float rr;
     unsigned int k;
+    double cumProb;
 
-    rr = 1.0;  /* initial value */
-    k = 0;     /* initial value */
-    rr = rr * ((rand() & RANDOM_MASK) / arand);
-    while (rr > x1) {
-        ++k;
-        rr = rr * ((rand() & RANDOM_MASK) / arand);
+    for (k = 0, cumProb = 0.0; k < lambdaOfMaxval; ++k) {
+
+        cumProb += poissonPmf(lambda, k);
+
+        if (cumProb >= u)
+            break;
     }
-    rawNewSample = k / lambda;
 
-    *newSampleP = MIN(MAX((int)rawNewSample, 0), maxval);
+    {
+        samplen const newSamplen = pm_gamma709(k/lambdaOfMaxval);
+
+        *newSampleP = pnm_unnormalized_sample(pamP, newSamplen);
+    }
 }
 
 
 
-int 
-main(int argc, char * argv[]) {
+int
+main(int argc, const char ** argv) {
 
     FILE * ifP;
+    struct CmdlineInfo cmdline;
     struct pam inpam;
     struct pam outpam;
     tuple * tuplerow;
@@ -207,221 +417,13 @@ main(int argc, char * argv[]) {
     unsigned int row;
     double infinity;
 
-    int argn;
-    const char * inputFilename;
-    int noise_type;
-    unsigned int seed;
-    int i;
-    const char * const usage = "[-type noise_type] [-lsigma x] [-mgsigma x] "
-        "[-sigma1 x] [-sigma2 x] [-lambda x] [-seed n] "
-        "[-tolerance ratio] [pgmfile]";
-
-    const char * const noise_name[] = { 
-        "gaussian",
-        "impulse",
-        "laplacian",
-        "multiplicative_gaussian",
-        "poisson"
-    };
-    int const noise_id[] = { 
-        GAUSSIAN,
-        IMPULSE,
-        LAPLACIAN,
-        MULTIPLICATIVE_GAUSSIAN,
-        POISSON
-    };
-    /* minimum number of characters to match noise name for pm_keymatch() */
-    int const noise_compare[] = {
-        1,
-        1,
-        1,
-        1,
-        1
-    };
-
-    /* define default values for configurable options */
-    float lambda = 0.05;        
-    float lsigma = 10.0;
-    float mgsigma = 0.5;
-    float sigma1 = 4.0;
-    float sigma2 = 20.0;
-    float tolerance = 0.10;
-
-    pnm_init(&argc, argv);
-
-    seed = pm_randseed();
-    noise_type = GAUSSIAN;
-
-    argn = 1;
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-    {
-        if ( pm_keymatch( argv[argn], "-lambda", 3 ) )
-        {
-            ++argn;
-            if ( argn >= argc )
-            {
-                pm_message( 
-                    "incorrect number of arguments for -lambda option" );
-                pm_usage( usage );
-            }
-            else if ( argv[argn][0] == '-' )
-            {
-                pm_message( "invalid argument to -lambda option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            lambda = atof( argv[argn] );
-        }
-        else if ( pm_keymatch( argv[argn], "-lsigma", 3 ) )
-        {
-            ++argn;
-            if ( argn >= argc )
-            {
-                pm_message( 
-                    "incorrect number of arguments for -lsigma option" );
-                pm_usage( usage );
-            }
-            else if ( argv[argn][0] == '-' )
-            {
-                pm_message( "invalid argument to -lsigma option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            lsigma = atof( argv[argn] );
-        }
-        else if ( pm_keymatch( argv[argn], "-mgsigma", 2 ) )
-        {
-            ++argn;
-            if ( argn >= argc )
-            {
-                pm_message( 
-                    "incorrect number of arguments for -mgsigma option" );
-                pm_usage( usage );
-            }
-            else if ( argv[argn][0] == '-' )
-            {
-                pm_message( "invalid argument to -mgsigma option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            mgsigma = atof( argv[argn] );
-        }
-        else if ( pm_keymatch( argv[argn], "-seed", 3 ) )
-        {
-            ++argn;
-            if ( argn >= argc )
-            {
-                pm_message( "incorrect number of arguments for -seed option" );
-                pm_usage( usage );
-            }
-            else if ( argv[argn][0] == '-' )
-            {
-                pm_message( "invalid argument to -seed option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            seed = atoi(argv[argn]);
-        }
-        else if ( pm_keymatch( argv[argn], "-sigma1", 7 ) ||
-                  pm_keymatch( argv[argn], "-s1", 3 ) )
-        {
-            ++argn;
-            if ( argn >= argc )
-            {
-                pm_message( 
-                    "incorrect number of arguments for -sigma1 option" );
-                pm_usage( usage );
-            }
-            else if ( argv[argn][0] == '-' )
-            {
-                pm_message( "invalid argument to -sigma1 option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            sigma1 = atof( argv[argn] );
-        }
-        else if ( pm_keymatch( argv[argn], "-sigma2", 7 ) ||
-                  pm_keymatch( argv[argn], "-s2", 3 ) )
-        {
-            ++argn;
-            if ( argn >= argc )
-            {
-                pm_message( 
-                    "incorrect number of arguments for -sigma2 option" );
-                pm_usage( usage );
-            }
-            else if ( argv[argn][0] == '-' )
-            {
-                pm_message( "invalid argument to -sigma2 option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            sigma2 = atof( argv[argn] );
-        }
-        else if ( pm_keymatch( argv[argn], "-tolerance", 3 ) )
-        {
-            ++argn;
-            if ( argn >= argc )
-            {
-                pm_message( 
-                    "incorrect number of arguments for -tolerance option" );
-                pm_usage( usage );
-            }
-            else if ( argv[argn][0] == '-' )
-            {
-                pm_message( "invalid argument to -tolerance option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            tolerance = atof( argv[argn] );
-        }
-        else if ( pm_keymatch( argv[argn], "-type", 3 ) )
-        {
-            ++argn;
-            if ( argn >= argc )
-            {
-                pm_message( "incorrect number of arguments for -type option" );
-                pm_usage( usage );
-            }
-            else if ( argv[argn][0] == '-' )
-            {
-                pm_message( "invalid argument to -type option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            /* search through list of valid noise types and compare */
-            i = 0;
-            while ( ( i < MAX_NOISE_TYPES ) && 
-                    !pm_keymatch( argv[argn], 
-                                  noise_name[i], noise_compare[i] ) )
-                ++i;
-            if ( i >= MAX_NOISE_TYPES )
-            {
-                pm_message( "invalid argument to -type option: %s", 
-                            argv[argn] );
-                pm_usage( usage );
-            }
-            noise_type = noise_id[i];
-        }
-        else
-            pm_usage( usage );
-        ++argn;
-    }
-
-    if ( argn < argc )
-    {
-        inputFilename = argv[argn];
-        argn++;
-    }
-    else
-        inputFilename = "-";
+    pm_proginit(&argc, argv);
 
-    if ( argn != argc )
-        pm_usage( usage );
+    parseCommandLine(argc, argv, &cmdline);
 
-    srand(seed);
+    srand(cmdline.seed);
 
-    ifP = pm_openr(inputFilename);
+    ifP = pm_openr(cmdline.inputFileName);
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
@@ -430,50 +432,51 @@ main(int argc, char * argv[]) {
 
     pnm_writepaminit(&outpam);
 
-    tuplerow = pnm_allocpamrow(&inpam);
+    tuplerow    = pnm_allocpamrow(&inpam);
     newtuplerow = pnm_allocpamrow(&inpam);
+
     infinity = (double) inpam.maxval;
-    
+
     for (row = 0; row < inpam.height; ++row) {
         unsigned int col;
         pnm_readpamrow(&inpam, tuplerow);
         for (col = 0; col < inpam.width; ++col) {
             unsigned int plane;
             for (plane = 0; plane < inpam.depth; ++plane) {
-                switch (noise_type) {
-                case GAUSSIAN:
-                    gaussian_noise(inpam.maxval,
-                                   tuplerow[col][plane],
-                                   &newtuplerow[col][plane],
-                                   sigma1, sigma2);
+                switch (cmdline.noiseType) {
+                case NOISETYPE_GAUSSIAN:
+                    addGaussianNoise(inpam.maxval,
+                                     tuplerow[col][plane],
+                                     &newtuplerow[col][plane],
+                                     cmdline.sigma1, cmdline.sigma2);
                     break;
-                    
-                case IMPULSE:
-                    impulse_noise(inpam.maxval,
-                                  tuplerow[col][plane],
-                                  &newtuplerow[col][plane],
-                                  tolerance);
-                   break;
-                    
-                case LAPLACIAN:
-                    laplacian_noise(inpam.maxval, infinity,
+
+                case NOISETYPE_IMPULSE:
+                    addImpulseNoise(inpam.maxval,
                                     tuplerow[col][plane],
                                     &newtuplerow[col][plane],
-                                    lsigma);
+                                    cmdline.tolerance);
+                   break;
+
+                case NOISETYPE_LAPLACIAN:
+                    addLaplacianNoise(inpam.maxval, infinity,
+                                      tuplerow[col][plane],
+                                      &newtuplerow[col][plane],
+                                      cmdline.lsigma);
                     break;
-                    
-                case MULTIPLICATIVE_GAUSSIAN:
-                    multiplicative_gaussian_noise(inpam.maxval, infinity,
-                                                  tuplerow[col][plane],
-                                                  &newtuplerow[col][plane],
-                                                  mgsigma);
+
+                case NOISETYPE_MULTIPLICATIVE_GAUSSIAN:
+                    addMultiplicativeGaussianNoise(inpam.maxval, infinity,
+                                                   tuplerow[col][plane],
+                                                   &newtuplerow[col][plane],
+                                                   cmdline.mgsigma);
                     break;
-                    
-                case POISSON:
-                    poisson_noise(inpam.maxval,
-                                  tuplerow[col][plane],
-                                  &newtuplerow[col][plane],
-                                  lambda);
+
+                case NOISETYPE_POISSON:
+                    addPoissonNoise(&inpam,
+                                    tuplerow[col][plane],
+                                    &newtuplerow[col][plane],
+                                    cmdline.lambda);
                     break;
 
                 }
@@ -486,3 +489,6 @@ main(int argc, char * argv[]) {
 
     return 0;
 }
+
+
+
diff --git a/editor/pamcomp.c b/editor/pamcomp.c
index 6e1e7f7d..9ccd41c7 100644
--- a/editor/pamcomp.c
+++ b/editor/pamcomp.c
@@ -208,117 +208,49 @@ parseCommandLine(int                        argc,
 
 
 
-static int
-commonFormat(int const formatA,
-             int const formatB) {
-/*----------------------------------------------------------------------------
-   Return a viable format for the result of composing the two formats
-   'formatA' and 'formatB'.
------------------------------------------------------------------------------*/
-    int retval;
-
-    int const typeA = PAM_FORMAT_TYPE(formatA);
-    int const typeB = PAM_FORMAT_TYPE(formatB);
-
-    if (typeA == PAM_TYPE || typeB == PAM_TYPE)
-        retval = PAM_FORMAT;
-    else if (typeA == PPM_TYPE || typeB == PPM_TYPE)
-        retval = PPM_FORMAT;
-    else if (typeA == PGM_TYPE || typeB == PGM_TYPE)
-        retval = PGM_FORMAT;
-    else if (typeA == PBM_TYPE || typeB == PBM_TYPE)
-        retval = PBM_FORMAT;
-    else {
-        /* Results are undefined for this case, so we do a hail Mary. */
-        retval = formatA;
-    }
-    return retval;
-}
-
-
-
-typedef enum { TT_BLACKANDWHITE, TT_GRAYSCALE, TT_RGB } BaseTupletype;
-
-
-
-static BaseTupletype
-commonTupletype(const char * const tupletypeA,
-                const char * const tupletypeB) {
-
-    if (strneq(tupletypeA, "RGB", 3) ||
-        strneq(tupletypeB, "RGB", 3))
-        return TT_RGB;
-    else if (strneq(tupletypeA, "GRAYSCALE", 9) ||
-             strneq(tupletypeB, "GRAYSCALE", 9))
-        return TT_GRAYSCALE;
-    else if (strneq(tupletypeA, "BLACKANDWHITE", 13) ||
-             strneq(tupletypeB, "BLACKANDWHITE", 13))
-        return TT_BLACKANDWHITE;
-    else
-        /* Results are undefined for this case, so we do a hail Mary. */
-        return TT_RGB;
-}
-
-
-
 static void
-determineOutputTupleType(BaseTupletype const baseTupletype,
-                         bool          const underlayHaveOpacity,
-                         char *        const tupleType,
-                         size_t        const size) {
+initAlphaFile(struct CmdlineInfo const cmdline,
+              struct pam *       const overlayPamP,
+              FILE **            const filePP,
+              struct pam *       const pamP) {
 
-    char buffer[80];
+    FILE * fileP;
 
-    switch (baseTupletype) {
-    case TT_BLACKANDWHITE:
-        STRSCPY(buffer, "RGB");
-        break;
-    case TT_GRAYSCALE:
-        STRSCPY(buffer, "GRAYSCALE");
-        break;
-    case TT_RGB:
-        STRSCPY(buffer, "RGB");
-        break;
-    }
+    if (cmdline.alphaFilespec) {
+        fileP = pm_openr(cmdline.alphaFilespec);
+        pamP->comment_p = NULL;
+        pnm_readpaminit(fileP, pamP, PAM_STRUCT_SIZE(opacity_plane));
 
-    if (underlayHaveOpacity)
-        STRSCAT(buffer, "_ALPHA");
+        if (overlayPamP->width != pamP->width ||
+            overlayPamP->height != pamP->height)
+            pm_error("Opacity map and overlay image are not the same size");
+    } else
+        fileP = NULL;
 
-    strncpy(tupleType, buffer, size);
+    *filePP = fileP;
 }
 
 
 
-static void
-determineOutputType(const struct pam * const underlayPamP,
-                    const struct pam * const overlayPamP,
-                    struct pam *       const composedPamP) {
-
-    BaseTupletype const baseTupletype =
-        commonTupletype(underlayPamP->tuple_type, overlayPamP->tuple_type);
-
-    composedPamP->height = underlayPamP->height;
-    composedPamP->width  = underlayPamP->width;
+typedef enum { TT_BLACKANDWHITE, TT_GRAYSCALE, TT_RGB } BaseTupletype;
 
-    composedPamP->format = commonFormat(underlayPamP->format,
-                                        overlayPamP->format);
-    composedPamP->plainformat = FALSE;
 
-    composedPamP->maxval = pm_lcm(underlayPamP->maxval, overlayPamP->maxval,
-                                  1, PNM_OVERALLMAXVAL);
 
-    composedPamP->visual = true;
-    composedPamP->color_depth = (baseTupletype == TT_RGB ? 3 : 1);
-    composedPamP->have_opacity = underlayPamP->have_opacity;
-    composedPamP->opacity_plane = (baseTupletype == TT_RGB ? 3 : 1);
-
-    composedPamP->depth =
-        (baseTupletype == TT_RGB ? 3 : 1) +
-        (underlayPamP->have_opacity ? 1 : 0);
+static void
+validateComputableHeight(int const originTop,
+                         int const overRows) {
 
-    determineOutputTupleType(baseTupletype, underlayPamP->have_opacity,
-                             composedPamP->tuple_type,
-                             sizeof(composedPamP->tuple_type));
+    if (originTop < 0) {
+        if (originTop < -INT_MAX)
+            pm_error("Overlay starts too far above the underlay image to be "
+                     "computable.  Overlay can be at most %d rows above "
+                     "the underlay.", INT_MAX);
+    } else {
+        if (INT_MAX - originTop <= overRows)
+            pm_error("Too many total rows involved to be computable.  "
+                     "You must have a shorter overlay image or compose it "
+                     "higher on the underlay image.");
+    }
 }
 
 
@@ -362,25 +294,6 @@ warnOutOfFrame(int const originLeft,
 
 
 static void
-validateComputableHeight(int const originTop,
-                         int const overRows) {
-
-    if (originTop < 0) {
-        if (originTop < -INT_MAX)
-            pm_error("Overlay starts too far above the underlay image to be "
-                     "computable.  Overlay can be at most %d rows above "
-                     "the underlay.", INT_MAX);
-    } else {
-        if (INT_MAX - originTop <= overRows)
-            pm_error("Too many total rows involved to be computable.  "
-                     "You must have a shorter overlay image or compose it "
-                     "higher on the underlay image.");
-    }
-}
-
-
-
-static void
 computeOverlayPosition(int                const underCols,
                        int                const underRows,
                        int                const overCols,
@@ -483,6 +396,194 @@ computeOverlayPosition(int                const underCols,
 
 
 
+static BaseTupletype
+commonTupletype(const char * const tupletypeA,
+                const char * const tupletypeB) {
+
+    if (strneq(tupletypeA, "RGB", 3) ||
+        strneq(tupletypeB, "RGB", 3))
+        return TT_RGB;
+    else if (strneq(tupletypeA, "GRAYSCALE", 9) ||
+             strneq(tupletypeB, "GRAYSCALE", 9))
+        return TT_GRAYSCALE;
+    else if (strneq(tupletypeA, "BLACKANDWHITE", 13) ||
+             strneq(tupletypeB, "BLACKANDWHITE", 13))
+        return TT_BLACKANDWHITE;
+    else
+        /* Results are undefined for this case, so we do a hail Mary. */
+        return TT_RGB;
+}
+
+
+
+static int
+commonFormat(int const formatA,
+             int const formatB) {
+/*----------------------------------------------------------------------------
+   Return a viable format for the result of composing the two formats
+   'formatA' and 'formatB'.
+-----------------------------------------------------------------------------*/
+    int retval;
+
+    int const typeA = PAM_FORMAT_TYPE(formatA);
+    int const typeB = PAM_FORMAT_TYPE(formatB);
+
+    if (typeA == PAM_TYPE || typeB == PAM_TYPE)
+        retval = PAM_FORMAT;
+    else if (typeA == PPM_TYPE || typeB == PPM_TYPE)
+        retval = PPM_FORMAT;
+    else if (typeA == PGM_TYPE || typeB == PGM_TYPE)
+        retval = PGM_FORMAT;
+    else if (typeA == PBM_TYPE || typeB == PBM_TYPE)
+        retval = PBM_FORMAT;
+    else {
+        /* Results are undefined for this case, so we do a hail Mary. */
+        retval = formatA;
+    }
+    return retval;
+}
+
+
+
+static void
+determineOutputTupleType(BaseTupletype const baseTupletype,
+                         bool          const underlayHaveOpacity,
+                         char *        const tupleType,
+                         size_t        const size) {
+
+    char buffer[80];
+
+    switch (baseTupletype) {
+    case TT_BLACKANDWHITE:
+        STRSCPY(buffer, "RGB");
+        break;
+    case TT_GRAYSCALE:
+        STRSCPY(buffer, "GRAYSCALE");
+        break;
+    case TT_RGB:
+        STRSCPY(buffer, "RGB");
+        break;
+    }
+
+    if (underlayHaveOpacity)
+        STRSCAT(buffer, "_ALPHA");
+
+    strncpy(tupleType, buffer, size);
+}
+
+
+
+static void
+determineOutputType(const struct pam * const underlayPamP,
+                    const struct pam * const overlayPamP,
+                    struct pam *       const composedPamP) {
+
+    BaseTupletype const baseTupletype =
+        commonTupletype(underlayPamP->tuple_type, overlayPamP->tuple_type);
+
+    composedPamP->height = underlayPamP->height;
+    composedPamP->width  = underlayPamP->width;
+
+    composedPamP->format = commonFormat(underlayPamP->format,
+                                        overlayPamP->format);
+    composedPamP->plainformat = FALSE;
+
+    composedPamP->maxval = pm_lcm(underlayPamP->maxval, overlayPamP->maxval,
+                                  1, PNM_OVERALLMAXVAL);
+
+    composedPamP->visual = true;
+    composedPamP->color_depth = (baseTupletype == TT_RGB ? 3 : 1);
+    composedPamP->have_opacity = underlayPamP->have_opacity;
+    composedPamP->opacity_plane = (baseTupletype == TT_RGB ? 3 : 1);
+
+    composedPamP->depth =
+        (baseTupletype == TT_RGB ? 3 : 1) +
+        (underlayPamP->have_opacity ? 1 : 0);
+
+    determineOutputTupleType(baseTupletype, underlayPamP->have_opacity,
+                             composedPamP->tuple_type,
+                             sizeof(composedPamP->tuple_type));
+}
+
+
+
+static void
+determineInputAdaptations(const struct pam * const underlayPamP,
+                          const struct pam * const overlayPamP,
+                          const struct pam * const composedPamP,
+                          struct pam *       const adaptUnderlayPamP,
+                          struct pam *       const adaptOverlayPamP) {
+/*----------------------------------------------------------------------------
+   For easy of computation, this program reads a tuple row from one of the
+   input files, then transforms it something similar to the format of the
+   eventual output tuple row.  E.g. if the input is grayscale and the
+   output color, it converts the depth 1 row read from the file to a depth
+   3 row for use in computations.
+
+   This function determines what the result of that transformation should be.
+   It's not as simple as it sounds because of opacity.  The overlay may have
+   an opacity plane that has to be kept for the computations, while the output
+   has no opacity plane.
+
+   Our output PAMs are meaningless except in the fields that pertain to a
+   row of tuples.  E.g. the file descriptor and image height members are
+   meaningless.
+-----------------------------------------------------------------------------*/
+    /* We make the underlay row identical to the composed (output) row,
+       except for its width.
+    */
+
+    *adaptUnderlayPamP = *composedPamP;
+    adaptUnderlayPamP->width = underlayPamP->width;
+
+    /* Same for the overlay row, except that it retains is original
+       opacity.
+    */
+
+    adaptOverlayPamP->width = overlayPamP->width;
+    adaptOverlayPamP->tuple_type[0] = '\0';  /* a hack; this doesn't matter */
+    adaptOverlayPamP->visual = true;
+    adaptOverlayPamP->color_depth = composedPamP->color_depth;
+    adaptOverlayPamP->have_opacity = overlayPamP->have_opacity;
+    adaptOverlayPamP->opacity_plane = composedPamP->color_depth;
+    adaptOverlayPamP->depth =
+        composedPamP->color_depth +
+        (overlayPamP->have_opacity ? 1 : 0);
+    adaptOverlayPamP->maxval = composedPamP->maxval;
+    adaptOverlayPamP->bytes_per_sample = composedPamP->bytes_per_sample;
+    adaptOverlayPamP->allocation_depth = overlayPamP->allocation_depth;
+}
+
+
+
+static void
+adaptRowFormat(struct pam * const inpamP,
+               struct pam * const outpamP,
+               tuple *      const tuplerow) {
+/*----------------------------------------------------------------------------
+   Convert the row in 'tuplerow', which is in a format described by
+   *inpamP, to the format described by *outpamP.
+
+   'tuplerow' must have enough allocated depth to do this.
+-----------------------------------------------------------------------------*/
+    assert(outpamP->visual);
+    assert(inpamP->visual);
+
+    pnm_scaletuplerow(inpamP, tuplerow, tuplerow, outpamP->maxval);
+
+    if (outpamP->color_depth == 3) {
+        if (outpamP->have_opacity)
+            pnm_makerowrgba(inpamP, tuplerow);
+        else
+            pnm_makerowrgb(inpamP, tuplerow);
+    } else {
+        if (outpamP->have_opacity)
+            pnm_addopacityrow(inpamP, tuplerow);
+    }
+}
+
+
+
 static sample
 composeComponents(sample           const compA,
                   sample           const compB,
@@ -680,34 +781,6 @@ overlayPixel(tuple            const overlayTuple,
 
 
 static void
-adaptRowFormat(struct pam * const inpamP,
-               struct pam * const outpamP,
-               tuple *      const tuplerow) {
-/*----------------------------------------------------------------------------
-   Convert the row in 'tuplerow', which is in a format described by
-   *inpamP, to the format described by *outpamP.
-
-   'tuplerow' must have enough allocated depth to do this.
------------------------------------------------------------------------------*/
-    assert(outpamP->visual);
-    assert(inpamP->visual);
-
-    pnm_scaletuplerow(inpamP, tuplerow, tuplerow, outpamP->maxval);
-
-    if (outpamP->color_depth == 3) {
-        if (outpamP->have_opacity)
-            pnm_makerowrgba(inpamP, tuplerow);
-        else
-            pnm_makerowrgb(inpamP, tuplerow);
-    } else {
-        if (outpamP->have_opacity)
-            pnm_addopacityrow(inpamP, tuplerow);
-    }
-}
-
-
-
-static void
 composeRow(int              const originleft,
            struct pam *     const underlayPamP,
            struct pam *     const overlayPamP,
@@ -751,55 +824,6 @@ composeRow(int              const originleft,
 
 
 static void
-determineInputAdaptations(const struct pam * const underlayPamP,
-                          const struct pam * const overlayPamP,
-                          const struct pam * const composedPamP,
-                          struct pam *       const adaptUnderlayPamP,
-                          struct pam *       const adaptOverlayPamP) {
-/*----------------------------------------------------------------------------
-   For easy of computation, this program reads a tuple row from one of the
-   input files, then transforms it something similar to the format of the
-   eventual output tuple row.  E.g. if the input is grayscale and the
-   output color, it converts the depth 1 row read from the file to a depth
-   3 row for use in computations.
-
-   This function determines what the result of that transformation should be.
-   It's not as simple as it sounds because of opacity.  The overlay may have
-   an opacity plane that has to be kept for the computations, while the output
-   has no opacity plane.
-
-   Our output PAMs are meaningless except in the fields that pertain to a
-   row of tuples.  E.g. the file descriptor and image height members are
-   meaningless.
------------------------------------------------------------------------------*/
-    /* We make the underlay row identical to the composed (output) row,
-       except for its width.
-    */
-
-    *adaptUnderlayPamP = *composedPamP;
-    adaptUnderlayPamP->width = underlayPamP->width;
-
-    /* Same for the overlay row, except that it retains is original
-       opacity.
-    */
-
-    adaptOverlayPamP->width = overlayPamP->width;
-    adaptOverlayPamP->tuple_type[0] = '\0';  /* a hack; this doesn't matter */
-    adaptOverlayPamP->visual = true;
-    adaptOverlayPamP->color_depth = composedPamP->color_depth;
-    adaptOverlayPamP->have_opacity = overlayPamP->have_opacity;
-    adaptOverlayPamP->opacity_plane = composedPamP->color_depth;
-    adaptOverlayPamP->depth =
-        composedPamP->color_depth +
-        (overlayPamP->have_opacity ? 1 : 0);
-    adaptOverlayPamP->maxval = composedPamP->maxval;
-    adaptOverlayPamP->bytes_per_sample = composedPamP->bytes_per_sample;
-    adaptOverlayPamP->allocation_depth = overlayPamP->allocation_depth;
-}
-
-
-
-static void
 composite(int          const originleft,
           int          const origintop,
           struct pam * const underlayPamP,
@@ -899,30 +923,6 @@ composite(int          const originleft,
 
 
 
-static void
-initAlphaFile(struct CmdlineInfo const cmdline,
-              struct pam *       const overlayPamP,
-              FILE **            const filePP,
-              struct pam *       const pamP) {
-
-    FILE * fileP;
-
-    if (cmdline.alphaFilespec) {
-        fileP = pm_openr(cmdline.alphaFilespec);
-        pamP->comment_p = NULL;
-        pnm_readpaminit(fileP, pamP, PAM_STRUCT_SIZE(opacity_plane));
-
-        if (overlayPamP->width != pamP->width ||
-            overlayPamP->height != pamP->height)
-            pm_error("Opacity map and overlay image are not the same size");
-    } else
-        fileP = NULL;
-
-    *filePP = fileP;
-}
-
-
-
 int
 main(int argc, const char *argv[]) {
 
diff --git a/editor/specialty/ppmshift.c b/editor/specialty/ppmshift.c
index a765daa5..cdb0f173 100644
--- a/editor/specialty/ppmshift.c
+++ b/editor/specialty/ppmshift.c
@@ -9,61 +9,111 @@
 /* V1.1    16.11.1993  Rewritten to be NetPBM.programming conforming */
 /*********************************************************************/
 
+#include <stdbool.h>
+
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "ppm.h"
 
-/**************************/
-/* start of main function */
-/**************************/
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+
+    unsigned int shift;
+    unsigned int seed;
+};
+
+
+
+static void
+parseCommandLine(int argc, const 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;
+        /* Instructions to OptParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int seedSpec;
+
+    MALLOCARRAY(option_def, 100);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = true;  /* We have no parms that are negative numbers */
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "seed",            OPT_UINT,     &cmdlineP->seed,
+            &seedSpec,         0);
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!seedSpec)
+        cmdlineP->seed = pm_randseed();
+
+    if (argc-1 < 1)
+        pm_error("You must specify the shift factor as an argument");
+    else {
+        int const arg1 = atoi(argv[1]);
+        if (arg1 < 0)
+            pm_error("shift factor must be 0 or more");
+        cmdlineP->shift = arg1;
+
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = "-";
+        else {
+            cmdlineP->inputFileName = argv[2];
+
+            if (argc-1 > 2)
+                pm_error("Too many arguments (%u).  "
+                         "Shift factor and input file name are the only "
+                         "possible arguments", argc-1);
+        }
+    }
+    free(option_def);
+}
+
+
+
 int
-main(int    argc,
-     char * argv[]) {
+main(int argc, const char ** argv) {
 
     FILE * ifP;
-    unsigned int row;
-    int argn, rows, cols, format;
+    struct CmdlineInfo cmdline;
+    int rows, cols, format;
+    pixval maxval;
     pixel * srcrow;
     pixel * destrow;
-    pixval maxval;
-    int shift, nowshift;
-    int shiftArg;
-
-    const char * const usage = "shift [ppmfile]\n        shift: maximum number of pixels to shift a line by\n";
+    unsigned int row;
+    unsigned int shift;
 
     /* parse in 'default' parameters */
-    ppm_init(&argc, argv);
-
-    argn = 1;
-
-    /* parse in shift number */
-    if (argn == argc)
-        pm_usage(usage);
-    if (sscanf(argv[argn], "%d", &shiftArg) != 1)
-        pm_usage(usage);
-    if (shiftArg < 0)
-        pm_error("shift factor must be 0 or more");
-    ++argn;
-
-    /* parse in filename (if present, stdin otherwise) */
-    if (argn != argc)
-    {
-        ifP = pm_openr(argv[argn]);
-        ++argn;
-    }
-    else
-        ifP = stdin;
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
 
-    if (argn != argc)
-        pm_usage(usage);
+    srand(cmdline.seed);
+
+    ifP = pm_openr(cmdline.inputFileName);
 
     /* read first data from file */
     ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
 
-    if (shiftArg > cols) {
+    if (cmdline.shift > cols) {
         shift = cols;
-        pm_message("shift amount is larger than picture width - reset to %d",
+        pm_message("shift amount is larger than picture width - reset to %u",
                    shift);
     } else
-        shift = shiftArg;
+        shift = cmdline.shift;
 
     srcrow = ppm_allocrow(cols);
 
@@ -71,8 +121,6 @@ main(int    argc,
 
     ppm_writeppminit(stdout, cols, rows, maxval, 0);
 
-    srand(pm_randseed());
-
     /** now do the shifting **/
     /* the range by which a line is shifted lays in the range from */
     /* -shift/2 .. +shift/2 pixels; however, within this range it is */
@@ -80,6 +128,7 @@ main(int    argc,
     for (row = 0; row < rows; ++row) {
         pixel * pP;
         pixel * pP2;
+        unsigned int nowshift;
 
         if (shift != 0)
             nowshift = (rand() % (shift+1)) - ((shift+1) / 2);