about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/HISTORY10
-rw-r--r--editor/pamaddnoise.c130
2 files changed, 86 insertions, 54 deletions
diff --git a/doc/HISTORY b/doc/HISTORY
index 206b6ff3..09616dee 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -6,6 +6,11 @@ CHANGE HISTORY
 
 not yet  BJH  Release 11.04.00
 
+              pamaddnoise: add -salt.
+
+              pamaddnoise: reject options that aren't meaningful for the type
+              of noise specified rather than just ignore them.
+
               ppmtosixel: Add -7bit, so it works on more terminals, including
               xterms.  Thanks Scott Pakin.
 
@@ -33,6 +38,9 @@ not yet  BJH  Release 11.04.00
               ppmfade: fix incorrect block mode fade.  Always broken
               (ppmfade was new in Netpbm 8.4 (April 2000)).
 
+              pamaddnoise: fix very incorrect noise added for all types.
+              Introduced in Netpbm 10.94 (March 2021).
+
               pgmtexture: Fix buffer overflow with maxval > 255.  Always
               broken.  Maxvals > 255 were possible starting in Netpbm 9.0
               (April 2000).
@@ -380,7 +388,7 @@ not yet  BJH  Release 11.04.00
               ppmshift: Add -seed .
 
               pamaddnoise: Fix incorrect output for -type poisson.  Always
-              broken.  (pamaddnoise's precursor pnmaddnoies was new to Netpbm
+              broken.  (pamaddnoise's precursor pnmaddnoise was new to Netpbm
               in Netpbm 10.29 (August 2005)).
 
               pamaddnoise: fix bug: garbage output with -type impulse.  Always
diff --git a/editor/pamaddnoise.c b/editor/pamaddnoise.c
index 9ca80394..b662aa96 100644
--- a/editor/pamaddnoise.c
+++ b/editor/pamaddnoise.c
@@ -39,16 +39,14 @@
 #include "pam.h"
 
 static double const EPSILON = 1.0e-5;
-static double const SALT_RATIO = 0.5;
-
-
-
-static double
-rand1(struct pm_randSt * const randStP) {
-
-    return (double)pm_rand(randStP)/RAND_MAX;
-}
 
+static double const SIGMA1_DEFAULT  = 4.0;
+static double const SIGMA2_DEFAULT  = 20.0;
+static double const MGSIGMA_DEFAULT = 0.5;
+static double const LSIGMA_DEFAULT  = 10.0;
+static double const TOLERANCE_DEFAULT  = 0.10;
+static double const SALT_RATIO_DEFAULT = 0.5;
+static double const LAMBDA_DEFAULT  = 12.0;
 
 
 enum NoiseType {
@@ -78,6 +76,7 @@ struct CmdlineInfo {
     float sigma1;
     float sigma2;
     float tolerance;
+    float saltRatio;
 };
 
 
@@ -123,7 +122,7 @@ parseCommandLine(int argc, const char ** const argv,
     unsigned int option_def_index;
 
     unsigned int typeSpec, lambdaSpec, lsigmaSpec, mgsigmaSpec,
-        sigma1Spec, sigma2Spec, toleranceSpec;
+      sigma1Spec, sigma2Spec, toleranceSpec, saltRatioSpec;
 
     const char * type;
 
@@ -146,6 +145,8 @@ parseCommandLine(int argc, const char ** const argv,
             &sigma2Spec,         0);
     OPTENT3(0,   "tolerance",       OPT_FLOAT,    &cmdlineP->tolerance,
             &toleranceSpec,      0);
+    OPTENT3(0,   "salt",            OPT_FLOAT,    &cmdlineP->saltRatio,
+            &saltRatioSpec,      0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -159,42 +160,82 @@ parseCommandLine(int argc, const char ** const argv,
     else
         cmdlineP->noiseType = typeFmName(type);
 
-    if (sigma1Spec && cmdlineP->noiseType != NOISETYPE_GAUSSIAN)
-        pm_error("-sigma1 is valid only with -type=gaussian");
+    if (sigma1Spec) {
+        if (cmdlineP->noiseType != NOISETYPE_GAUSSIAN)
+            pm_error("-sigma1 is valid only with -type=gaussian");
+        else if (cmdlineP->sigma1 < 0)
+            pm_error("-sigma1 value must be non-negative.  You specified %f",
+                     cmdlineP->sigma1);
+    }
 
-    if (sigma2Spec && cmdlineP->noiseType != NOISETYPE_GAUSSIAN)
-        pm_error("-sigma2 is valid only with -type=gaussian");
+    if (sigma2Spec) {
+        if (cmdlineP->noiseType != NOISETYPE_GAUSSIAN)
+            pm_error("-sigma2 is valid only with -type=gaussian");
+        else if (cmdlineP->sigma2 < 0)
+            pm_error("-sigma2 value must be non-negative.  You specified %f",
+                     cmdlineP->sigma2);
+    }
 
-    if (mgsigmaSpec &&
-        cmdlineP->noiseType != NOISETYPE_MULTIPLICATIVE_GAUSSIAN)
-        pm_error("-mgsigma is valid only with -type=multiplicative_guassian");
+    if (mgsigmaSpec) {
+        if (cmdlineP->noiseType != NOISETYPE_MULTIPLICATIVE_GAUSSIAN)
+            pm_error("-mgsigma is valid only with -type=multiplicative_guassian");
+        else if (cmdlineP->mgsigma < 0)
+            pm_error("-mgsigma value must be non-negative.  You specified %f",
+                     cmdlineP->mgsigma);
+    }
+
+    if (toleranceSpec) {
+        if (cmdlineP->noiseType != NOISETYPE_IMPULSE)
+            pm_error("-tolerance is valid only with -type=impulse");
+        else if (cmdlineP->tolerance < 0 || cmdlineP->tolerance > 1.0)
+            pm_error("-tolerance value must be between 0.0 and 1.0.  "
+                     "You specified %f",  cmdlineP->tolerance);
+    }
 
-    if (toleranceSpec && cmdlineP->noiseType != NOISETYPE_IMPULSE)
-        pm_error("-tolerance is valid only with -type=impulse");
+    if (saltRatioSpec) {
+        if (cmdlineP->noiseType != NOISETYPE_IMPULSE)
+            pm_error("-salt is valid only with -type=impulse");
+        else if (cmdlineP->saltRatio < 0 || cmdlineP->saltRatio > 1.0)
+            pm_error("-salt value must be between 0.0 and 1.0.  "
+                     "You specified %f",  cmdlineP->saltRatio);
+    }
 
-    if (lsigmaSpec && cmdlineP->noiseType != NOISETYPE_LAPLACIAN)
+    if (lsigmaSpec) {
+        if (cmdlineP->noiseType != NOISETYPE_LAPLACIAN)
         pm_error("-lsigma is valid only with -type=laplacian");
+        else if (cmdlineP->lsigma <= 0)
+            pm_error("-lsigma value must be positive.  You specified %f",
+                     cmdlineP->lsigma);
+    }
 
-    if (lambdaSpec && cmdlineP->noiseType != NOISETYPE_POISSON)
+    if (lambdaSpec) {
+        if (cmdlineP->noiseType != NOISETYPE_POISSON)
         pm_error("-lambda is valid only with -type=poisson");
+        else if (cmdlineP->lambda <= 0)
+            pm_error("-lambda value must be positive.  You specified %f",
+                     cmdlineP->lambda);
+    }
 
     if (!lambdaSpec)
-        cmdlineP->lambda = 12.0;
+        cmdlineP->lambda = LAMBDA_DEFAULT;
 
     if (!lsigmaSpec)
-        cmdlineP->lsigma = 10.0;
+        cmdlineP->lsigma = LSIGMA_DEFAULT;
 
     if (!mgsigmaSpec)
-        cmdlineP->mgsigma = 0.5;
+        cmdlineP->mgsigma = MGSIGMA_DEFAULT;
 
     if (!sigma1Spec)
-        cmdlineP->sigma1 = 4.0;
+        cmdlineP->sigma1 = SIGMA1_DEFAULT;
 
     if (!sigma2Spec)
-        cmdlineP->sigma2 = 20.0;
+        cmdlineP->sigma2 = SIGMA2_DEFAULT;
 
     if (!toleranceSpec)
-        cmdlineP->tolerance = 0.10;
+        cmdlineP->tolerance = TOLERANCE_DEFAULT;
+
+    if (!saltRatioSpec)
+        cmdlineP->saltRatio = SALT_RATIO_DEFAULT;
 
     if (!cmdlineP->seedSpec)
         cmdlineP->seed = pm_randseed();
@@ -226,19 +267,13 @@ addGaussianNoise(sample             const maxval,
    Based on Kasturi/Algorithms of the ACM
 -----------------------------------------------------------------------------*/
 
-    double x1, x2, xn, yn;
+    double grnd1, grnd2; /* Gaussian random numbers.  mean=0 sigma=1 */
     double rawNewSample;
 
-    x1 = rand1(randStP);
-
-    if (x1 == 0.0)
-        x1 = 1.0;
-    x2 = rand1(randStP);
-    xn = sqrt(-2.0 * log(x1)) * cos(2.0 * M_PI * x2);
-    yn = sqrt(-2.0 * log(x1)) * sin(2.0 * M_PI * x2);
+    pm_gaussrand2(randStP, &grnd1, &grnd2);
 
     rawNewSample =
-        origSample + (sqrt((double) origSample) * sigma1 * xn) + (sigma2 * yn);
+      origSample + (sqrt((double) origSample) * sigma1 * grnd1) + (sigma2 * grnd2);
 
     *newSampleP = MAX(MIN((int)rawNewSample, maxval), 0);
 }
@@ -259,7 +294,7 @@ addImpulseNoise(sample             const maxval,
     double const pepperRatio = 1.0 - saltRatio;
     double const loTolerance = tolerance * pepperRatio;
     double const hiTolerance = 1.0 - tolerance * saltRatio;
-    double const sap         = rand1(randStP);
+    double const sap         = pm_drand(randStP);
 
     *newSampleP =
         sap < loTolerance ? 0 :
@@ -281,7 +316,7 @@ addLaplacianNoise(sample             const maxval,
 
    From Pitas' book.
 -----------------------------------------------------------------------------*/
-    double const u = rand1(randStP);
+    double const u = pm_drand(randStP);
 
     double rawNewSample;
 
@@ -314,21 +349,10 @@ addMultiplicativeGaussianNoise(sample             const maxval,
 
    From Pitas' book.
 -----------------------------------------------------------------------------*/
-    double rayleigh, gauss;
+
     double rawNewSample;
 
-    {
-        double const uniform = rand1(randStP);
-        if (uniform <= EPSILON)
-            rayleigh = infinity;
-        else
-            rayleigh = sqrt(-2.0 * log( uniform));
-    }
-    {
-        double const uniform = rand1(randStP);
-        gauss = rayleigh * cos(2.0 * M_PI * uniform);
-    }
-    rawNewSample = origSample + (origSample * mgsigma * gauss);
+    rawNewSample = origSample + (origSample * mgsigma * pm_gaussrand(randStP));
 
     *newSampleP = MIN(MAX((int)rawNewSample, 0), maxval);
 }
@@ -384,7 +408,7 @@ addPoissonNoise(struct pam *       const pamP,
 
     double const lambda  = origSampleIntensity * lambdaOfMaxval;
 
-    double const u = rand1(randStP);
+    double const u = pm_drand(randStP);
 
     /* We now apply the inverse CDF (cumulative distribution function) of the
        Poisson distribution to uniform random variable 'u' to get a Poisson
@@ -466,7 +490,7 @@ main(int argc, const char ** argv) {
                     addImpulseNoise(inpam.maxval,
                                     tuplerow[col][plane],
                                     &newtuplerow[col][plane],
-                                    cmdline.tolerance, SALT_RATIO,
+                                    cmdline.tolerance, cmdline.saltRatio,
                                     &randSt);
                    break;