]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/morphology.c
(no commit message)
[imagemagick] / MagickCore / morphology.c
index c1795f134b2a673b6a459afc5f729fdc446e986e..927a7222528d6b5969b0d44b9ee988f81cd40b30 100644 (file)
@@ -17,7 +17,7 @@
 %                               January 2010                                  %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
@@ -52,6 +52,7 @@
 #include "MagickCore/studio.h"
 #include "MagickCore/artifact.h"
 #include "MagickCore/cache-view.h"
+#include "MagickCore/channel.h"
 #include "MagickCore/color-private.h"
 #include "MagickCore/enhance.h"
 #include "MagickCore/exception.h"
 #include "MagickCore/list.h"
 #include "MagickCore/magick.h"
 #include "MagickCore/memory_.h"
+#include "MagickCore/memory-private.h"
 #include "MagickCore/monitor-private.h"
 #include "MagickCore/morphology.h"
 #include "MagickCore/morphology-private.h"
 #include "MagickCore/option.h"
 #include "MagickCore/pixel-accessor.h"
+#include "MagickCore/pixel-private.h"
 #include "MagickCore/prepress.h"
 #include "MagickCore/quantize.h"
+#include "MagickCore/resource_.h"
 #include "MagickCore/registry.h"
 #include "MagickCore/semaphore.h"
 #include "MagickCore/splay-tree.h"
 #include "MagickCore/statistic.h"
 #include "MagickCore/string_.h"
 #include "MagickCore/string-private.h"
+#include "MagickCore/thread-private.h"
 #include "MagickCore/token.h"
 #include "MagickCore/utility.h"
 #include "MagickCore/utility-private.h"
 \f
-
-/*
-** The following test is for special floating point numbers of value NaN (not
-** a number), that may be used within a Kernel Definition.  NaN's are defined
-** as part of the IEEE standard for floating point number representation.
-**
-** These are used as a Kernel value to mean that this kernel position is not
-** part of the kernel neighbourhood for convolution or morphology processing,
-** and thus should be ignored.  This allows the use of 'shaped' kernels.
-**
-** The special properity that two NaN's are never equal, even if they are from
-** the same variable allow you to test if a value is special NaN value.
-**
-** This macro  IsNaN() is thus is only true if the value given is NaN.
-*/
-#define IsNan(a)   ((a)!=(a))
-
 /*
   Other global definitions used by module.
 */
@@ -112,6 +100,21 @@ static inline double MagickMax(const double x,const double y)
 #define Minimize(assign,value) assign=MagickMin(assign,value)
 #define Maximize(assign,value) assign=MagickMax(assign,value)
 
+/* Integer Factorial Function - for a Binomial kernel */
+#if 1
+static inline size_t fact(size_t n)
+{
+  size_t f,l;
+  for(f=1, l=2; l <= n; f=f*l, l++);
+  return(f);
+}
+#elif 1 /* glibc floating point alternatives */
+#define fact(n) ((size_t)tgamma((double)n+1))
+#else
+#define fact(n) ((size_t)lgamma((double)n+1))
+#endif
+
+
 /* Currently these are only internal to this module */
 static void
   CalcKernelMetaData(KernelInfo *),
@@ -124,7 +127,7 @@ static void
 static inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
 {
   while (kernel->next != (KernelInfo *) NULL)
-    kernel = kernel->next;
+    kernel=kernel->next;
   return(kernel);
 }
 
@@ -260,7 +263,9 @@ static KernelInfo *ParseKernelArray(const char *kernel_string)
   /* clear flags - for Expanding kernel lists thorugh rotations */
    flags = NoValue;
 
-  /* Has a ':' in argument - New user kernel specification */
+  /* Has a ':' in argument - New user kernel specification
+     FUTURE: this split on ':' could be done by StringToken()
+   */
   p = strchr(kernel_string, ':');
   if ( p != (char *) NULL && p < end)
     {
@@ -284,9 +289,9 @@ static KernelInfo *ParseKernelArray(const char *kernel_string)
       if ( args.xi  < 0.0 || args.psi < 0.0 )
         return(DestroyKernelInfo(kernel));
       kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
-                                               : (ssize_t) (kernel->width-1)/2;
+                                        : (ssize_t) (kernel->width-1)/2;
       kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
-                                               : (ssize_t) (kernel->height-1)/2;
+                                        : (ssize_t) (kernel->height-1)/2;
       if ( kernel->x >= (ssize_t) kernel->width ||
            kernel->y >= (ssize_t) kernel->height )
         return(DestroyKernelInfo(kernel));
@@ -314,12 +319,12 @@ static KernelInfo *ParseKernelArray(const char *kernel_string)
     }
 
   /* Read in the kernel values from rest of input string argument */
-  kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-    kernel->height*sizeof(*kernel->values));
+  kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
+    kernel->width,kernel->height*sizeof(*kernel->values)));
   if (kernel->values == (MagickRealType *) NULL)
     return(DestroyKernelInfo(kernel));
-  kernel->minimum = +MagickHuge;
-  kernel->maximum = -MagickHuge;
+  kernel->minimum=MagickMaximumValue;
+  kernel->maximum=(-MagickMaximumValue);
   kernel->negative_range = kernel->positive_range = 0.0;
   for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
   {
@@ -328,10 +333,10 @@ static KernelInfo *ParseKernelArray(const char *kernel_string)
       GetMagickToken(p,&p,token);
     if (    LocaleCompare("nan",token) == 0
         || LocaleCompare("-",token) == 0 ) {
-      kernel->values[i] = nan; /* do not include this value in kernel */
+      kernel->values[i] = nan; /* this value is not part of neighbourhood */
     }
     else {
-      kernel->values[i]=StringToDouble(token,(char **) NULL);
+      kernel->values[i] = StringToDouble(token,(char **) NULL);
       ( kernel->values[i] < 0)
           ?  ( kernel->negative_range += kernel->values[i] )
           :  ( kernel->positive_range += kernel->values[i] );
@@ -360,7 +365,7 @@ static KernelInfo *ParseKernelArray(const char *kernel_string)
 #endif
 
   /* check that we recieved at least one real (non-nan) value! */
-  if ( kernel->minimum == MagickHuge )
+  if (kernel->minimum == MagickMaximumValue)
     return(DestroyKernelInfo(kernel));
 
   if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
@@ -487,7 +492,6 @@ static KernelInfo *ParseKernelName(const char *kernel_string)
 
 MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
 {
-
   KernelInfo
     *kernel,
     *new_kernel;
@@ -498,48 +502,42 @@ MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
   const char
     *p;
 
-  size_t
-    kernel_number;
-
   if (kernel_string == (const char *) NULL)
     return(ParseKernelArray(kernel_string));
-  p = kernel_string;
-  kernel = NULL;
-  kernel_number = 0;
-
-  while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
+  p=kernel_string;
+  kernel=NULL;
 
+  while (GetMagickToken(p,NULL,token), *token != '\0')
+  {
     /* ignore extra or multiple ';' kernel separators */
-    if ( *token != ';' ) {
-
-      /* tokens starting with alpha is a Named kernel */
-      if (isalpha((int) *token) != 0)
-        new_kernel = ParseKernelName(p);
-      else /* otherwise a user defined kernel array */
-        new_kernel = ParseKernelArray(p);
-
-      /* Error handling -- this is not proper error handling! */
-      if ( new_kernel == (KernelInfo *) NULL ) {
-        (void) FormatLocaleFile(stderr, "Failed to parse kernel number #%.20g\n",
-          (double) kernel_number);
-        if ( kernel != (KernelInfo *) NULL )
-          kernel=DestroyKernelInfo(kernel);
-        return((KernelInfo *) NULL);
-      }
+    if (*token != ';')
+      {
+        /* tokens starting with alpha is a Named kernel */
+        if (isalpha((int) ((unsigned char) *token)) != 0)
+          new_kernel=ParseKernelName(p);
+        else /* otherwise a user defined kernel array */
+          new_kernel=ParseKernelArray(p);
+
+        /* Error handling -- this is not proper error handling! */
+        if (new_kernel == (KernelInfo *) NULL)
+          {
+            if (kernel != (KernelInfo *) NULL)
+              kernel=DestroyKernelInfo(kernel);
+            return((KernelInfo *) NULL);
+          }
 
-      /* initialise or append the kernel list */
-      if ( kernel == (KernelInfo *) NULL )
-        kernel = new_kernel;
-      else
-        LastKernelInfo(kernel)->next = new_kernel;
-    }
+        /* initialise or append the kernel list */
+        if (kernel == (KernelInfo *) NULL)
+          kernel=new_kernel;
+        else
+          LastKernelInfo(kernel)->next=new_kernel;
+      }
 
     /* look for the next kernel in list */
-    p = strchr(p, ';');
-    if ( p == (char *) NULL )
+    p=strchr(p,';');
+    if (p == (char *) NULL)
       break;
     p++;
-
   }
   return(kernel);
 }
@@ -629,6 +627,10 @@ MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
 %       Note that the first argument is the width of the kernel and not the
 %       radius of the kernel.
 %
+%    Binomial:[{radius}]
+%       Generate a discrete kernel using a 2 dimentional Pascel's Triangle
+%       of values. Used for special forma of image filters.
+%
 %    # Still to be implemented...
 %    #
 %    # Filter2D
@@ -991,6 +993,7 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
     case LoGKernel:
     case BlurKernel:
     case CometKernel:
+    case BinomialKernel:
     case DiamondKernel:
     case SquareKernel:
     case RectangleKernel:
@@ -1028,8 +1031,8 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
       {
         kernel->height = kernel->width = (size_t) 1;
         kernel->x = kernel->y = (ssize_t) 0;
-        kernel->values=(MagickRealType *) AcquireAlignedMemory(1,
-          sizeof(*kernel->values));
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(1,sizeof(*kernel->values)));
         if (kernel->values == (MagickRealType *) NULL)
           return(DestroyKernelInfo(kernel));
         kernel->maximum = kernel->values[0] = args->rho;
@@ -1052,8 +1055,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
         kernel->height = kernel->width;
         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
-        kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-          kernel->height*sizeof(*kernel->values));
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(kernel->width,kernel->height*
+          sizeof(*kernel->values)));
         if (kernel->values == (MagickRealType *) NULL)
           return(DestroyKernelInfo(kernel));
 
@@ -1075,7 +1079,7 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
               }
             else /* limiting case - a unity (normalized Dirac) kernel */
               { (void) ResetMagickMemory(kernel->values,0, (size_t)
-                            kernel->width*kernel->height*sizeof(double));
+                  kernel->width*kernel->height*sizeof(*kernel->values));
                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
               }
           }
@@ -1107,7 +1111,7 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
               }
             else /* special case - generate a unity kernel */
               { (void) ResetMagickMemory(kernel->values,0, (size_t)
-                            kernel->width*kernel->height*sizeof(double));
+                  kernel->width*kernel->height*sizeof(*kernel->values));
                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
               }
           }
@@ -1143,8 +1147,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
         kernel->x = (ssize_t) (kernel->width-1)/2;
         kernel->y = 0;
         kernel->negative_range = kernel->positive_range = 0.0;
-        kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-          kernel->height*sizeof(*kernel->values));
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(kernel->width,kernel->height*
+          sizeof(*kernel->values)));
         if (kernel->values == (MagickRealType *) NULL)
           return(DestroyKernelInfo(kernel));
 
@@ -1166,7 +1171,7 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
         /* initialize */
         v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
         (void) ResetMagickMemory(kernel->values,0, (size_t)
-                     kernel->width*kernel->height*sizeof(double));
+          kernel->width*kernel->height*sizeof(*kernel->values));
         /* Calculate a Positive 1D Gaussian */
         if ( sigma > MagickEpsilon )
           { sigma *= KernelRank;               /* simplify loop expressions */
@@ -1180,7 +1185,8 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
         else /* special case - generate a unity kernel */
           kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
 #else
-        /* Direct calculation without curve averaging */
+        /* Direct calculation without curve averaging
+           This is equivelent to a KernelRank of 1 */
 
         /* Calculate a Positive Gaussian */
         if ( sigma > MagickEpsilon )
@@ -1191,16 +1197,17 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
           }
         else /* special case - generate a unity kernel */
           { (void) ResetMagickMemory(kernel->values,0, (size_t)
-                         kernel->width*kernel->height*sizeof(double));
+              kernel->width*kernel->height*sizeof(*kernel->values));
             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
           }
 #endif
         /* Note the above kernel may have been 'clipped' by a user defined
         ** radius, producing a smaller (darker) kernel.  Also for very small
-        ** sigma's (> 0.1) the central value becomes larger than one, and thus
-        ** producing a very bright kernel.
+        ** sigma's (> 0.1) the central value becomes larger than one, as a
+        ** result of not generating a actual 'discrete' kernel, and thus
+        ** producing a very bright 'impulse'.
         **
-        ** Normalization will still be needed.
+        ** Becuase of these two factors Normalization is required!
         */
 
         /* Normalize the 1D Gaussian Kernel
@@ -1227,8 +1234,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
         kernel->x = kernel->y = 0;
         kernel->height = 1;
         kernel->negative_range = kernel->positive_range = 0.0;
-        kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-          kernel->height*sizeof(*kernel->values));
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(kernel->width,kernel->height*
+          sizeof(*kernel->values)));
         if (kernel->values == (MagickRealType *) NULL)
           return(DestroyKernelInfo(kernel));
 
@@ -1248,7 +1256,7 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
 #define KernelRank 3
             v = (ssize_t) kernel->width*KernelRank; /* start/end points */
             (void) ResetMagickMemory(kernel->values,0, (size_t)
-                          kernel->width*sizeof(double));
+              kernel->width*sizeof(*kernel->values));
             sigma *= KernelRank;            /* simplify the loop expression */
             A = 1.0/(2.0*sigma*sigma);
             /* B = 1.0/(MagickSQ2PI*sigma); */
@@ -1264,14 +1272,13 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             /* B = 1.0/(MagickSQ2PI*sigma); */
             for ( i=0; i < (ssize_t) kernel->width; i++)
               kernel->positive_range +=
-                kernel->values[i] =
-                  exp(-((double)(i*i))*A);
+                kernel->values[i] = exp(-((double)(i*i))*A);
                 /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
 #endif
           }
         else /* special case - generate a unity kernel */
           { (void) ResetMagickMemory(kernel->values,0, (size_t)
-                         kernel->width*kernel->height*sizeof(double));
+              kernel->width*kernel->height*sizeof(*kernel->values));
             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
             kernel->positive_range = 1.0;
           }
@@ -1284,6 +1291,38 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
         RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
         break;
       }
+    case BinomialKernel:
+      {
+        size_t
+          order_f;
+
+        if (args->rho < 1.0)
+          kernel->width = kernel->height = 3;  /* default radius = 1 */
+        else
+          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
+        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
+
+        order_f = fact(kernel->width-1);
+
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(kernel->width,kernel->height*
+          sizeof(*kernel->values)));
+        if (kernel->values == (MagickRealType *) NULL)
+          return(DestroyKernelInfo(kernel));
+
+        /* set all kernel values within diamond area to scale given */
+        for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
+          { size_t
+              alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
+            for ( u=0; u < (ssize_t)kernel->width; u++, i++)
+              kernel->positive_range += kernel->values[i] = (double)
+                (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
+          }
+        kernel->minimum = 1.0;
+        kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
+        kernel->negative_range = 0.0;
+        break;
+      }
 
     /*
       Convolution Kernels - Well Known Named Constant Kernels
@@ -1382,8 +1421,8 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             if (kernel == (KernelInfo *) NULL)
               return(kernel);
             kernel->type = type;
-            kernel->values[3]+=(MagickRealType) MagickSQ2;
-            kernel->values[5]-=(MagickRealType) MagickSQ2;
+            kernel->values[3] = +(MagickRealType) MagickSQ2;
+            kernel->values[5] = -(MagickRealType) MagickSQ2;
             CalcKernelMetaData(kernel);     /* recalculate meta-data */
             break;
           case 2:
@@ -1391,8 +1430,8 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             if (kernel == (KernelInfo *) NULL)
               return(kernel);
             kernel->type = type;
-            kernel->values[1] = kernel->values[3]+=(MagickRealType) MagickSQ2;
-            kernel->values[5] = kernel->values[7]-=(MagickRealType) MagickSQ2;
+            kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
+            kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
             CalcKernelMetaData(kernel);     /* recalculate meta-data */
             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
             break;
@@ -1407,8 +1446,8 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             if (kernel == (KernelInfo *) NULL)
               return(kernel);
             kernel->type = type;
-            kernel->values[3]+=(MagickRealType) MagickSQ2;
-            kernel->values[5]-=(MagickRealType) MagickSQ2;
+            kernel->values[3] = +(MagickRealType) MagickSQ2;
+            kernel->values[5] = -(MagickRealType) MagickSQ2;
             CalcKernelMetaData(kernel);     /* recalculate meta-data */
             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
             break;
@@ -1417,8 +1456,8 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             if (kernel == (KernelInfo *) NULL)
               return(kernel);
             kernel->type = type;
-            kernel->values[1]+=(MagickRealType) MagickSQ2;
-            kernel->values[7]+=(MagickRealType) MagickSQ2;
+            kernel->values[1] = +(MagickRealType) MagickSQ2;
+            kernel->values[7] = +(MagickRealType) MagickSQ2;
             CalcKernelMetaData(kernel);
             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
             break;
@@ -1427,8 +1466,8 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             if (kernel == (KernelInfo *) NULL)
               return(kernel);
             kernel->type = type;
-            kernel->values[0]+=(MagickRealType) MagickSQ2;
-            kernel->values[8]-=(MagickRealType) MagickSQ2;
+            kernel->values[0] = +(MagickRealType) MagickSQ2;
+            kernel->values[8] = -(MagickRealType) MagickSQ2;
             CalcKernelMetaData(kernel);
             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
             break;
@@ -1437,8 +1476,8 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             if (kernel == (KernelInfo *) NULL)
               return(kernel);
             kernel->type = type;
-            kernel->values[2]-=(MagickRealType) MagickSQ2;
-            kernel->values[6]+=(MagickRealType) MagickSQ2;
+            kernel->values[2] = -(MagickRealType) MagickSQ2;
+            kernel->values[6] = +(MagickRealType) MagickSQ2;
             CalcKernelMetaData(kernel);
             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
             break;
@@ -1478,7 +1517,7 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
             break;
         }
-        if ( fabs(args->sigma) > MagickEpsilon )
+        if ( fabs(args->sigma) >= MagickEpsilon )
           /* Rotate by correctly supplied 'angle' */
           RotateKernelInfo(kernel, args->sigma);
         else if ( args->rho > 30.0 || args->rho < -30.0 )
@@ -1498,8 +1537,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-        kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-          kernel->height*sizeof(*kernel->values));
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(kernel->width,kernel->height*
+          sizeof(*kernel->values)));
         if (kernel->values == (MagickRealType *) NULL)
           return(DestroyKernelInfo(kernel));
 
@@ -1539,8 +1579,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             kernel->y = (ssize_t) args->psi;
             scale = 1.0;
           }
-        kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-          kernel->height*sizeof(*kernel->values));
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(kernel->width,kernel->height*
+          sizeof(*kernel->values)));
         if (kernel->values == (MagickRealType *) NULL)
           return(DestroyKernelInfo(kernel));
 
@@ -1560,8 +1601,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-          kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-            kernel->height*sizeof(*kernel->values));
+          kernel->values=(MagickRealType *) MagickAssumeAligned(
+            AcquireAlignedMemory(kernel->width,kernel->height*
+            sizeof(*kernel->values)));
           if (kernel->values == (MagickRealType *) NULL)
             return(DestroyKernelInfo(kernel));
 
@@ -1586,8 +1628,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-          kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-            kernel->height*sizeof(*kernel->values));
+          kernel->values=(MagickRealType *) MagickAssumeAligned(
+            AcquireAlignedMemory(kernel->width,kernel->height*
+            sizeof(*kernel->values)));
           if (kernel->values == (MagickRealType *) NULL)
             return(DestroyKernelInfo(kernel));
 
@@ -1608,8 +1651,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-          kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-            kernel->height*sizeof(*kernel->values));
+          kernel->values=(MagickRealType *) MagickAssumeAligned(
+            AcquireAlignedMemory(kernel->width,kernel->height*
+            sizeof(*kernel->values)));
           if (kernel->values == (MagickRealType *) NULL)
             return(DestroyKernelInfo(kernel));
 
@@ -1629,8 +1673,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-          kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-            kernel->height*sizeof(*kernel->values));
+          kernel->values=(MagickRealType *) MagickAssumeAligned(
+            AcquireAlignedMemory(kernel->width,kernel->height*
+            sizeof(*kernel->values)));
           if (kernel->values == (MagickRealType *) NULL)
             return(DestroyKernelInfo(kernel));
 
@@ -1670,8 +1715,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
 
           kernel->height = kernel->width;
           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
-          kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-            kernel->height*sizeof(*kernel->values));
+          kernel->values=(MagickRealType *) MagickAssumeAligned(
+            AcquireAlignedMemory(kernel->width,kernel->height*
+            sizeof(*kernel->values)));
           if (kernel->values == (MagickRealType *) NULL)
             return(DestroyKernelInfo(kernel));
 
@@ -2040,8 +2086,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-          kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-            kernel->height*sizeof(*kernel->values));
+          kernel->values=(MagickRealType *) MagickAssumeAligned(
+            AcquireAlignedMemory(kernel->width,kernel->height*
+            sizeof(*kernel->values)));
           if (kernel->values == (MagickRealType *) NULL)
             return(DestroyKernelInfo(kernel));
 
@@ -2060,8 +2107,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-          kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-            kernel->height*sizeof(*kernel->values));
+          kernel->values=(MagickRealType *) MagickAssumeAligned(
+            AcquireAlignedMemory(kernel->width,kernel->height*
+            sizeof(*kernel->values)));
           if (kernel->values == (MagickRealType *) NULL)
             return(DestroyKernelInfo(kernel));
 
@@ -2080,8 +2128,9 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-        kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-          kernel->height*sizeof(*kernel->values));
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(kernel->width,kernel->height*
+          sizeof(*kernel->values)));
         if (kernel->values == (MagickRealType *) NULL)
           return(DestroyKernelInfo(kernel));
 
@@ -2105,15 +2154,16 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
 
-        kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-          kernel->height*sizeof(*kernel->values));
+        kernel->values=(MagickRealType *) MagickAssumeAligned(
+          AcquireAlignedMemory(kernel->width,kernel->height*
+          sizeof(*kernel->values)));
         if (kernel->values == (MagickRealType *) NULL)
           return(DestroyKernelInfo(kernel));
 
         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
             kernel->positive_range += ( kernel->values[i] =
-                 args->sigma*sqrt((double)(u*u+v*v)) );
+              args->sigma*sqrt((double)(u*u+v*v)) );
         kernel->maximum = kernel->values[0];
         break;
       }
@@ -2144,7 +2194,7 @@ MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
 %
 %  CloneKernelInfo() creates a new clone of the given Kernel List so that its
 %  can be modified without effecting the original.  The cloned kernel should
-%  be destroyed using DestroyKernelInfo() when no longer needed.
+%  be destroyed using DestoryKernelInfo() when no longer needed.
 %
 %  The format of the CloneKernelInfo method is:
 %
@@ -2170,8 +2220,8 @@ MagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
   *new_kernel=(*kernel); /* copy values in structure */
 
   /* replace the values with a copy of the values */
-  new_kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
-    kernel->height*sizeof(*kernel->values));
+  new_kernel->values=(MagickRealType *) MagickAssumeAligned(
+    AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
   if (new_kernel->values == (MagickRealType *) NULL)
     return(DestroyKernelInfo(new_kernel));
   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
@@ -2213,7 +2263,7 @@ MagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
 MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
 {
   assert(kernel != (KernelInfo *) NULL);
-  if ( kernel->next != (KernelInfo *) NULL )
+  if (kernel->next != (KernelInfo *) NULL)
     kernel->next=DestroyKernelInfo(kernel->next);
   kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
   kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
@@ -2344,12 +2394,12 @@ static MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
   /* check actual kernel values */
   for (i=0; i < (kernel1->width*kernel1->height); i++) {
     /* Test for Nan equivalence */
-    if ( IsNan(kernel1->values[i]) && !IsNan(kernel2->values[i]) )
+    if ( IfNaN(kernel1->values[i]) && !IfNaN(kernel2->values[i]) )
       return MagickFalse;
-    if ( IsNan(kernel2->values[i]) && !IsNan(kernel1->values[i]) )
+    if ( IfNaN(kernel2->values[i]) && !IfNaN(kernel1->values[i]) )
       return MagickFalse;
     /* Test actual values are equivalent */
-    if ( fabs(kernel1->values[i] - kernel2->values[i]) > MagickEpsilon )
+    if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
       return MagickFalse;
   }
 
@@ -2363,10 +2413,12 @@ static void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
     *last;
 
   last = kernel;
+DisableMSCWarning(4127)
   while(1) {
+RestoreMSCWarning
     clone = CloneKernelInfo(last);
     RotateKernelInfo(clone, angle);
-    if ( SameKernelInfo(kernel, clone) == MagickTrue )
+    if ( SameKernelInfo(kernel, clone) != MagickFalse )
       break;
     LastKernelInfo(last)->next = clone;
     last = clone;
@@ -2444,26 +2496,28 @@ static void CalcKernelMetaData(KernelInfo *kernel)
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
 %  MorphologyApply() applies a morphological method, multiple times using
-%  a list of multiple kernels.
+%  a list of multiple kernels.  This is the method that should be called by
+%  other 'operators' that internally use morphology operations as part of
+%  their processing.
 %
-%  It is basically equivalent to as MorphologyImage() (see below) but
-%  without any user controls.  This allows internel programs to use this
-%  function, to actually perform a specific task without possible interference
-%  by any API user supplied settings.
+%  It is basically equivalent to as MorphologyImage() (see below) but without
+%  any user controls.  This allows internel programs to use this method to
+%  perform a specific task without possible interference by any API user
+%  supplied settings.
 %
 %  It is MorphologyImage() task to extract any such user controls, and
 %  pass them to this function for processing.
 %
-%  More specifically kernels are not normalized/scaled/blended by the
-%  'convolve:scale' Image Artifact (setting), nor is the convolve bias
-%  (-bias setting or image->bias) loooked at, but must be supplied from the
-%  function arguments.
+%  More specifically all given kernels should already be scaled, normalised,
+%  and blended appropriatally before being parred to this routine. The
+%  appropriate bias, and compose (typically 'UndefinedComposeOp') given.
 %
 %  The format of the MorphologyApply method is:
 %
 %      Image *MorphologyApply(const Image *image,MorphologyMethod method,
 %        const ssize_t iterations,const KernelInfo *kernel,
-%        const CompositeMethod compose,ExceptionInfo *exception)
+%        const CompositeMethod compose,const double bias,
+%        ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -2485,17 +2539,13 @@ static void CalcKernelMetaData(KernelInfo *kernel)
 %          If 'NoCompositeOp' force image to be re-iterated by each kernel.
 %          Otherwise merge the results using the compose method given.
 %
+%    o bias: Convolution Output Bias.
+%
 %    o exception: return any errors or warnings in this structure.
 %
 */
-
-/* Apply a Morphology Primative to an image using the given kernel.
-** Two pre-created images must be provided, and no image is created.
-** It returns the number of pixels that changed between the images
-** for result convergence determination.
-*/
 static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
-  const MorphologyMethod method,const KernelInfo *kernel,
+  const MorphologyMethod method,const KernelInfo *kernel,const double bias,
   ExceptionInfo *exception)
 {
 #define MorphologyTag  "Morphology/Image"
@@ -2504,12 +2554,19 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
     *image_view,
     *morphology_view;
 
+  OffsetInfo
+    offset;
+
+  register ssize_t
+    i;
+
   ssize_t
-    y, offx, offy;
+    y;
 
   size_t
-    virt_width,
-    changed;
+    *changes,
+    changed,
+    width;
 
   MagickBooleanType
     status;
@@ -2525,240 +2582,234 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
   assert(kernel->signature == MagickSignature);
   assert(exception != (ExceptionInfo *) NULL);
   assert(exception->signature == MagickSignature);
-
   status=MagickTrue;
-  changed=0;
   progress=0;
-
-  image_view=AcquireCacheView(image);
-  morphology_view=AcquireCacheView(morphology_image);
-  virt_width=image->columns+kernel->width-1;
-
-  /* Some methods (including convolve) needs use a reflected kernel.
-   * Adjust 'origin' offsets to loop though kernel as a reflection.
-   */
-  offx = kernel->x;
-  offy = kernel->y;
-  switch(method) {
+  image_view=AcquireVirtualCacheView(image,exception);
+  morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
+  width=image->columns+kernel->width-1;
+  offset.x=0.0;
+  offset.y=0.0;
+  switch (method)
+  {
     case ConvolveMorphology:
     case DilateMorphology:
     case DilateIntensityMorphology:
     case IterativeDistanceMorphology:
-      /* kernel needs to used with reflection about origin */
-      offx = (ssize_t) kernel->width-offx-1;
-      offy = (ssize_t) kernel->height-offy-1;
+    {
+      /*
+        Kernel needs to used with reflection about origin.
+      */
+      offset.x=(ssize_t) kernel->width-kernel->x-1;
+      offset.y=(ssize_t) kernel->height-kernel->y-1;
       break;
+    }
     case ErodeMorphology:
     case ErodeIntensityMorphology:
     case HitAndMissMorphology:
     case ThinningMorphology:
     case ThickenMorphology:
-      /* kernel is used as is, without reflection */
+    {
+      offset.x=kernel->x;
+      offset.y=kernel->y;
       break;
+    }
     default:
+    {
       assert("Not a Primitive Morphology Method" != (char *) NULL);
       break;
+    }
   }
-
-  if ( method == ConvolveMorphology && kernel->width == 1 )
-  { /* Special handling (for speed) of vertical (blur) kernels.
-    ** This performs its handling in columns rather than in rows.
-    ** This is only done for convolve as it is the only method that
-    ** generates very large 1-D vertical kernels (such as a 'BlurKernel')
-    **
-    ** Timing tests (on single CPU laptop)
-    ** Using a vertical 1-d Blue with normal row-by-row (below)
-    **   time convert logo: -morphology Convolve Blur:0x10+90 null:
-    **      0.807u
-    ** Using this column method
-    **   time convert logo: -morphology Convolve Blur:0x10+90 null:
-    **      0.620u
-    **
-    ** Anthony Thyssen, 14 June 2010
-    */
-    register ssize_t
-      x;
-
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-#pragma omp parallel for schedule(static,4) shared(progress,status)
-#endif
-    for (x=0; x < (ssize_t) image->columns; x++)
+  changed=0;
+  changes=(size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
+    sizeof(*changes));
+  if (changes == (size_t *) NULL)
+    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+  for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
+    changes[i]=0;
+  if ((method == ConvolveMorphology) && (kernel->width == 1))
     {
-      register const Quantum
-        *restrict p;
-
-      register Quantum
-        *restrict q;
+      const int
+        id = GetOpenMPThreadId();
 
       register ssize_t
-        y;
-
-      ssize_t
-        r;
-
-      if (status == MagickFalse)
-        continue;
-      p=GetCacheViewVirtualPixels(image_view,x,-offy,1,image->rows+
-        kernel->height-1,exception);
-      q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
-        morphology_image->rows,exception);
-      if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
-        {
-          status=MagickFalse;
-          continue;
-        }
-      /* offset to origin in 'p'. while 'q' points to it directly */
-      r = offy;
+        x;
 
-      for (y=0; y < (ssize_t) image->rows; y++)
+      /*
+        Special handling (for speed) of vertical (blur) kernels.  This performs
+        its handling in columns rather than in rows.  This is only done
+        for convolve as it is the only method that generates very large 1-D
+        vertical kernels (such as a 'BlurKernel')
+     */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+     #pragma omp parallel for schedule(static,4) shared(progress,status) \
+       magick_threads(image,morphology_image,image->columns,1)
+#endif
+      for (x=0; x < (ssize_t) image->columns; x++)
       {
-        PixelInfo
-          result;
+        register const Quantum
+          *restrict p;
 
-        register ssize_t
-          v;
+        register Quantum
+          *restrict q;
 
-        register const MagickRealType
-          *restrict k;
+        register ssize_t
+          y;
 
-        register const Quantum
-          *restrict k_pixels;
+        ssize_t
+          center;
 
-        /* Copy input image to the output image for unused channels
-        * This removes need for 'cloning' a new image every iteration
-        */
-        SetPixelRed(morphology_image,GetPixelRed(image,p+r*
-          GetPixelChannels(image)),q);
-        SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
-          GetPixelChannels(image)),q);
-        SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
-          GetPixelChannels(image)),q);
-        if (image->colorspace == CMYKColorspace)
-          SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
-            GetPixelChannels(image)),q);
-
-        /* Set the bias of the weighted average output */
-        result.red     =
-        result.green   =
-        result.blue    =
-        result.alpha =
-        result.black   = 0.0;
-
-
-        /* Weighted Average of pixels using reflected kernel
-        **
-        ** NOTE for correct working of this operation for asymetrical
-        ** kernels, the kernel needs to be applied in its reflected form.
-        ** That is its values needs to be reversed.
-        */
-        k = &kernel->values[ kernel->height-1 ];
-        k_pixels = p;
-        if ( (image->channel_mask != DefaultChannels) || (image->matte == MagickFalse) )
-          { /* No 'Sync' involved.
-            ** Convolution is simple greyscale channel operation
-            */
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              if ( IsNan(*k) ) continue;
-              result.red     += (*k)*GetPixelRed(image,k_pixels);
-              result.green   += (*k)*GetPixelGreen(image,k_pixels);
-              result.blue    += (*k)*GetPixelBlue(image,k_pixels);
-              if (image->colorspace == CMYKColorspace)
-                result.black+=(*k)*GetPixelBlack(image,k_pixels);
-              result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
-              k--;
-              k_pixels+=GetPixelChannels(image);
-            }
-            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-              SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
-            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-              SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
-            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-              SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
-            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-                (image->colorspace == CMYKColorspace))
-              SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
-            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
-                (image->matte == MagickTrue))
-              SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
+        if (status == MagickFalse)
+          continue;
+        p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
+          kernel->height-1,exception);
+        q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
+          morphology_image->rows,exception);
+        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
+          {
+            status=MagickFalse;
+            continue;
           }
-        else
-          { /* Channel 'Sync' Flag, and Alpha Channel enabled.
-            ** Weight the color channels with Alpha Channel so that
-            ** transparent pixels are not part of the results.
-            */
-            MagickRealType
-              alpha,  /* alpha weighting of colors : kernel*alpha  */
-              gamma;  /* divisor, sum of color weighting values */
+        center=(ssize_t) GetPixelChannels(image)*offset.y;
+        for (y=0; y < (ssize_t) image->rows; y++)
+        {
+          register ssize_t
+            i;
 
+          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+          {
+            double
+              alpha,
+              gamma,
+              pixel;
+
+            PixelChannel
+              channel;
+
+            PixelTrait
+              morphology_traits,
+              traits;
+
+            register const MagickRealType
+              *restrict k;
+
+            register const Quantum
+              *restrict pixels;
+
+            register ssize_t
+              u;
+
+            size_t
+              count;
+
+            ssize_t
+              v;
+
+            channel=GetPixelChannelChannel(image,i);
+            traits=GetPixelChannelTraits(image,channel);
+            morphology_traits=GetPixelChannelTraits(morphology_image,channel);
+            if ((traits == UndefinedPixelTrait) ||
+                (morphology_traits == UndefinedPixelTrait))
+              continue;
+            if (((morphology_traits & CopyPixelTrait) != 0) ||
+                (GetPixelReadMask(image,p+center) == 0))
+              {
+                SetPixelChannel(morphology_image,channel,p[center+i],q);
+                continue;
+              }
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            pixels=p;
+            pixel=bias;
+            count=0;
+            if ((morphology_traits & BlendPixelTrait) == 0)
+              {
+                /*
+                  No alpha blending.
+                */
+                for (v=0; v < (ssize_t) kernel->height; v++)
+                {
+                  for (u=0; u < (ssize_t) kernel->width; u++)
+                  {
+                    if (IfNaN(*k) == MagickFalse)
+                      {
+                        pixel+=(*k)*pixels[i];
+                        count++;
+                      }
+                    k--;
+                    pixels+=GetPixelChannels(image);
+                  }
+                }
+                if (fabs(pixel-p[center+i]) > MagickEpsilon)
+                  changes[id]++;
+                gamma=(double) kernel->height*kernel->width/count;
+                SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
+                  pixel),q);
+                continue;
+              }
+            /*
+              Alpha blending.
+            */
             gamma=0.0;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              if ( IsNan(*k) ) continue;
-              alpha=(*k)*(QuantumScale*GetPixelAlpha(image,k_pixels));
-              gamma += alpha;
-              result.red     += alpha*GetPixelRed(image,k_pixels);
-              result.green   += alpha*GetPixelGreen(image,k_pixels);
-              result.blue    += alpha*GetPixelBlue(image,k_pixels);
-              if (image->colorspace == CMYKColorspace)
-                result.black += alpha*GetPixelBlack(image,k_pixels);
-              result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
-              k--;
-              k_pixels+=GetPixelChannels(image);
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IfNaN(*k) == MagickFalse)
+                  {
+                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
+                    pixel+=(*k)*alpha*pixels[i];
+                    gamma+=(*k)*alpha;
+                    count++;
+                  }
+                k--;
+                pixels+=GetPixelChannels(image);
+              }
             }
-            /* Sync'ed channels, all channels are modified */
-            gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
-            SetPixelRed(morphology_image,
-              ClampToQuantum(gamma*result.red),q);
-            SetPixelGreen(morphology_image,
-              ClampToQuantum(gamma*result.green),q);
-            SetPixelBlue(morphology_image,
-              ClampToQuantum(gamma*result.blue),q);
-            if (image->colorspace == CMYKColorspace)
-              SetPixelBlack(morphology_image,
-                ClampToQuantum(gamma*result.black),q);
-            SetPixelAlpha(morphology_image,
-              ClampToQuantum(result.alpha),q);
+            if (fabs(pixel-p[center+i]) > MagickEpsilon)
+              changes[id]++;
+            gamma=PerceptibleReciprocal(gamma);
+            gamma*=(double) kernel->height*kernel->width/count;
+            SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
+              pixel),q);
           }
-
-        /* Count up changed pixels */
-        if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q))
-            || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q))
-            || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q))
-            || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q))
-            || ((image->colorspace == CMYKColorspace) &&
-                (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
-          changed++;  /* The pixel was changed in some way! */
-        p+=GetPixelChannels(image);
-        q+=GetPixelChannels(morphology_image);
-      } /* y */
-      if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
-        status=MagickFalse;
-      if (image->progress_monitor != (MagickProgressMonitor) NULL)
-        {
-          MagickBooleanType
-            proceed;
+          p+=GetPixelChannels(image);
+          q+=GetPixelChannels(morphology_image);
+        }
+        if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
+          status=MagickFalse;
+        if (image->progress_monitor != (MagickProgressMonitor) NULL)
+          {
+            MagickBooleanType
+              proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_MorphologyImage)
+            #pragma omp critical (MagickCore_MorphologyPrimitive)
 #endif
-          proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
-          if (proceed == MagickFalse)
-            status=MagickFalse;
-        }
-    } /* x */
-    morphology_image->type=image->type;
-    morphology_view=DestroyCacheView(morphology_view);
-    image_view=DestroyCacheView(image_view);
-    return(status ? (ssize_t) changed : 0);
-  }
-
+            proceed=SetImageProgress(image,MorphologyTag,progress++,
+              image->rows);
+            if (proceed == MagickFalse)
+              status=MagickFalse;
+          }
+      }
+      morphology_image->type=image->type;
+      morphology_view=DestroyCacheView(morphology_view);
+      image_view=DestroyCacheView(image_view);
+      for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
+        changed+=changes[i];
+      changes=(size_t *) RelinquishMagickMemory(changes);
+      return(status ? (ssize_t) changed : 0);
+    }
   /*
-  ** Normal handling of horizontal or rectangular kernels (row by row)
+    Normal handling of horizontal or rectangular kernels (row by row).
   */
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(static,4) shared(progress,status)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,morphology_image,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
+    const int
+      id = GetOpenMPThreadId();
+
     register const Quantum
       *restrict p;
 
@@ -2768,506 +2819,385 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
     register ssize_t
       x;
 
-    size_t
-      r;
+    ssize_t
+      center;
 
     if (status == MagickFalse)
       continue;
-    p=GetCacheViewVirtualPixels(image_view, -offx, y-offy, virt_width,
-      kernel->height,  exception);
-    q=GetCacheViewAuthenticPixels(morphology_view,0,y,
-      morphology_image->columns,1,exception);
+    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
+      kernel->height,exception);
+    q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
+      1,exception);
     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
       {
         status=MagickFalse;
         continue;
       }
-    /* offset to origin in 'p'. while 'q' points to it directly */
-    r = virt_width*offy + offx;
-
+    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
+      GetPixelChannels(image)*offset.x);
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-       ssize_t
-        v;
-
       register ssize_t
-        u;
+        i;
 
-      register const MagickRealType
-        *restrict k;
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        double
+          alpha,
+          gamma,
+          maximum,
+          minimum,
+          pixel;
 
-      register const Quantum
-        *restrict k_pixels;
+        PixelChannel
+          channel;
 
-      PixelInfo
-        result,
-        min,
-        max;
+        PixelTrait
+          morphology_traits,
+          traits;
 
-      /* Copy input image to the output image for unused channels
-       * This removes need for 'cloning' a new image every iteration
-       */
-      SetPixelRed(morphology_image,GetPixelRed(image,p+r*
-        GetPixelChannels(image)),q);
-      SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
-        GetPixelChannels(image)),q);
-      SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
-        GetPixelChannels(image)),q);
-      if (image->colorspace == CMYKColorspace)
-        SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
-          GetPixelChannels(image)),q);
-
-      /* Defaults */
-      min.red     =
-      min.green   =
-      min.blue    =
-      min.alpha =
-      min.black   = (MagickRealType) QuantumRange;
-      max.red     =
-      max.green   =
-      max.blue    =
-      max.alpha =
-      max.black   = (MagickRealType) 0;
-      /* default result is the original pixel value */
-      result.red     = (MagickRealType) GetPixelRed(image,p+r*GetPixelChannels(image));
-      result.green   = (MagickRealType) GetPixelGreen(image,p+r*GetPixelChannels(image));
-      result.blue    = (MagickRealType) GetPixelBlue(image,p+r*GetPixelChannels(image));
-      result.black   = 0.0;
-      if (image->colorspace == CMYKColorspace)
-        result.black = (MagickRealType) GetPixelBlack(image,p+r*GetPixelChannels(image));
-      result.alpha=(MagickRealType) GetPixelAlpha(image,p+r*GetPixelChannels(image));
-
-      switch (method) {
-        case ConvolveMorphology:
-          /* Set the bias of the weighted average output */
-          result.red     =
-          result.green   =
-          result.blue    =
-          result.alpha =
-          result.black   = 0.0;
-          break;
-        case DilateIntensityMorphology:
-        case ErodeIntensityMorphology:
-          /* use a boolean flag indicating when first match found */
-          result.red = 0.0;  /* result is not used otherwise */
-          break;
-        default:
-          break;
-      }
+        register const MagickRealType
+          *restrict k;
+
+        register const Quantum
+          *restrict pixels;
+
+        register ssize_t
+          u;
+
+        size_t
+          count;
+
+        ssize_t
+          v;
+
+        channel=GetPixelChannelChannel(image,i);
+        traits=GetPixelChannelTraits(image,channel);
+        morphology_traits=GetPixelChannelTraits(morphology_image,channel);
+        if ((traits == UndefinedPixelTrait) ||
+            (morphology_traits == UndefinedPixelTrait))
+          continue;
+        if (((morphology_traits & CopyPixelTrait) != 0) ||
+            (GetPixelReadMask(image,p+center) == 0))
+          {
+            SetPixelChannel(morphology_image,channel,p[center+i],q);
+            continue;
+          }
+        pixels=p;
+        maximum=0.0;
+        minimum=(double) QuantumRange;
+        count=kernel->width*kernel->height;
+        switch (method)
+        {
+          case ConvolveMorphology: pixel=bias; break;
+          case HitAndMissMorphology: pixel=(double) QuantumRange; break;
+          case ThinningMorphology: pixel=(double) QuantumRange; break;
+          case ThickenMorphology: pixel=(double) QuantumRange; break;
+          case ErodeMorphology: pixel=(double) QuantumRange; break;
+          case DilateMorphology: pixel=0.0; break;
+          case ErodeIntensityMorphology:
+          case DilateIntensityMorphology:
+          case IterativeDistanceMorphology:
+          {
+            pixel=(double) p[center+i];
+            break;
+          }
+          default: pixel=0; break;
+        }
+        gamma=1.0;
+        switch (method)
+        {
+          case ConvolveMorphology:
+          {
+            /*
+               Weighted Average of pixels using reflected kernel
+
+               For correct working of this operation for asymetrical
+               kernels, the kernel needs to be applied in its reflected form.
+               That is its values needs to be reversed.
 
-      switch ( method ) {
-        case ConvolveMorphology:
-            /* Weighted Average of pixels using reflected kernel
-            **
-            ** NOTE for correct working of this operation for asymetrical
-            ** kernels, the kernel needs to be applied in its reflected form.
-            ** That is its values needs to be reversed.
-            **
-            ** Correlation is actually the same as this but without reflecting
-            ** the kernel, and thus 'lower-level' that Convolution.  However
-            ** as Convolution is the more common method used, and it does not
-            ** really cost us much in terms of processing to use a reflected
-            ** kernel, so it is Convolution that is implemented.
-            **
-            ** Correlation will have its kernel reflected before calling
-            ** this function to do a Convolve.
-            **
-            ** For more details of Correlation vs Convolution see
-            **   http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
+               Correlation is actually the same as this but without reflecting
+               the kernel, and thus 'lower-level' that Convolution.  However
+               as Convolution is the more common method used, and it does not
+               really cost us much in terms of processing to use a reflected
+               kernel, so it is Convolution that is implemented.
+
+               Correlation will have its kernel reflected before calling
+               this function to do a Convolve.
+
+               For more details of Correlation vs Convolution see
+                 http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
             */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            if ( (image->channel_mask != DefaultChannels) ||
-                 (image->matte == MagickFalse) )
-              { /* No 'Sync' involved.
-                ** Convolution is simple greyscale channel operation
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            count=0;
+            if ((morphology_traits & BlendPixelTrait) == 0)
+              {
+                /*
+                  No alpha blending.
                 */
-                for (v=0; v < (ssize_t) kernel->height; v++) {
-                  for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                    if ( IsNan(*k) ) continue;
-                    result.red     += (*k)*
-                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
-                    result.green   += (*k)*
-                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
-                    result.blue    += (*k)*
-                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
-                    if (image->colorspace == CMYKColorspace)
-                      result.black += (*k)*
-                        GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
-                    result.alpha += (*k)*
-                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
+                for (v=0; v < (ssize_t) kernel->height; v++)
+                {
+                  for (u=0; u < (ssize_t) kernel->width; u++)
+                  {
+                    if (IfNaN(*k) == MagickFalse)
+                      {
+                        pixel+=(*k)*pixels[i];
+                        count++;
+                      }
+                    k--;
+                    pixels+=GetPixelChannels(image);
                   }
-                  k_pixels += virt_width*GetPixelChannels(image);
+                  pixels+=(image->columns-1)*GetPixelChannels(image);
                 }
-                if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-                  SetPixelRed(morphology_image,ClampToQuantum(result.red),
-                    q);
-                if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-                  SetPixelGreen(morphology_image,ClampToQuantum(result.green),
-                    q);
-                if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-                  SetPixelBlue(morphology_image,ClampToQuantum(result.blue),
-                    q);
-                if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-                    (image->colorspace == CMYKColorspace))
-                  SetPixelBlack(morphology_image,ClampToQuantum(result.black),
-                    q);
-                if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
-                    (image->matte == MagickTrue))
-                  SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),
-                    q);
+                break;
               }
-            else
-              { /* Channel 'Sync' Flag, and Alpha Channel enabled.
-                ** Weight the color channels with Alpha Channel so that
-                ** transparent pixels are not part of the results.
-                */
-                MagickRealType
-                  alpha,  /* alpha weighting of colors : kernel*alpha  */
-                  gamma;  /* divisor, sum of color weighting values */
-
-                gamma=0.0;
-                for (v=0; v < (ssize_t) kernel->height; v++) {
-                  for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                    if ( IsNan(*k) ) continue;
-                    alpha=(*k)*(QuantumScale*GetPixelAlpha(image,k_pixels+u*
-                      GetPixelChannels(image)));
-                    gamma += alpha;
-                    result.red     += alpha*
-                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
-                    result.green   += alpha*
-                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
-                    result.blue    += alpha*
-                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
-                    if (image->colorspace == CMYKColorspace)
-                      result.black+=alpha*
-                        GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
-                    result.alpha += (*k)*
-                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
+            /*
+              Alpha blending.
+            */
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IfNaN(*k) == MagickFalse)
+                  {
+                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
+                    pixel+=(*k)*alpha*pixels[i];
+                    gamma+=(*k)*alpha;
+                    count++;
                   }
-                  k_pixels += virt_width*GetPixelChannels(image);
-                }
-                /* Sync'ed channels, all channels are modified */
-                gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
-                SetPixelRed(morphology_image,
-                  ClampToQuantum(gamma*result.red),q);
-                SetPixelGreen(morphology_image,
-                  ClampToQuantum(gamma*result.green),q);
-                SetPixelBlue(morphology_image,
-                  ClampToQuantum(gamma*result.blue),q);
-                if (image->colorspace == CMYKColorspace)
-                  SetPixelBlack(morphology_image,
-                    ClampToQuantum(gamma*result.black),q);
-                SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
+                k--;
+                pixels+=GetPixelChannels(image);
               }
+              pixels+=(image->columns-1)*GetPixelChannels(image);
+            }
             break;
+          }
+          case ErodeMorphology:
+          {
+            /*
+              Minimum value within kernel neighbourhood.
 
-        case ErodeMorphology:
-            /* Minimum Value within kernel neighbourhood
-            **
-            ** NOTE that the kernel is not reflected for this operation!
-            **
-            ** NOTE: in normal Greyscale Morphology, the kernel value should
-            ** be added to the real value, this is currently not done, due to
-            ** the nature of the boolean kernels being used.
+              The kernel is not reflected for this operation.  In normal
+              Greyscale Morphology, the kernel value should be added
+              to the real value, this is currently not done, due to the
+              nature of the boolean kernels being used.
             */
-            k = kernel->values;
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
-                if ( IsNan(*k) || (*k) < 0.5 ) continue;
-                Minimize(min.red,     (double)
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(min.green,   (double)
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(min.blue,    (double)
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(min.alpha,   (double)
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                if (image->colorspace == CMYKColorspace)
-                  Minimize(min.black,  (double)
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
+            k=kernel->values;
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
+                  {
+                    if ((double) pixels[i] < pixel)
+                      pixel=(double) pixels[i];
+                  }
+                k++;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=(image->columns-1)*GetPixelChannels(image);
             }
             break;
+          }
+          case DilateMorphology:
+          {
+            /*
+               Maximum value within kernel neighbourhood.
+
+               For correct working of this operation for asymetrical kernels,
+               the kernel needs to be applied in its reflected form.  That is
+               its values needs to be reversed.
 
-        case DilateMorphology:
-            /* Maximum Value within kernel neighbourhood
-            **
-            ** NOTE for correct working of this operation for asymetrical
-            ** kernels, the kernel needs to be applied in its reflected form.
-            ** That is its values needs to be reversed.
-            **
-            ** NOTE: in normal Greyscale Morphology, the kernel value should
-            ** be added to the real value, this is currently not done, due to
-            ** the nature of the boolean kernels being used.
-            **
+               In normal Greyscale Morphology, the kernel value should be
+               added to the real value, this is currently not done, due to the
+               nature of the boolean kernels being used.
             */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNan(*k) || (*k) < 0.5 ) continue;
-                Maximize(max.red,     (double)
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Maximize(max.green,   (double)
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Maximize(max.blue,    (double)
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                Maximize(max.alpha,   (double)
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                if (image->colorspace == CMYKColorspace)
-                  Maximize(max.black,   (double)
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
+            count=0;
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if ((IfNaN(*k) == MagickFalse) && (*k > 0.5))
+                  {
+                    if ((double) pixels[i] > pixel)
+                      pixel=(double) pixels[i];
+                    count++;
+                  }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=(image->columns-1)*GetPixelChannels(image);
             }
             break;
+          }
+          case HitAndMissMorphology:
+          case ThinningMorphology:
+          case ThickenMorphology:
+          {
+            /*
+               Minimum of foreground pixel minus maxumum of background pixels.
 
-        case HitAndMissMorphology:
-        case ThinningMorphology:
-        case ThickenMorphology:
-            /* Minimum of Foreground Pixel minus Maxumum of Background Pixels
-            **
-            ** NOTE that the kernel is not reflected for this operation,
-            ** and consists of both foreground and background pixel
-            ** neighbourhoods, 0.0 for background, and 1.0 for foreground
-            ** with either Nan or 0.5 values for don't care.
-            **
-            ** Note that this will never produce a meaningless negative
-            ** result.  Such results can cause Thinning/Thicken to not work
-            ** correctly when used against a greyscale image.
+               The kernel is not reflected for this operation, and consists
+               of both foreground and background pixel neighbourhoods, 0.0 for
+               background, and 1.0 for foreground with either Nan or 0.5 values
+               for don't care.
+
+               This never produces a meaningless negative result.  Such results
+               cause Thinning/Thicken to not work correctly when used against a
+               greyscale image.
             */
-            k = kernel->values;
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
-                if ( IsNan(*k) ) continue;
-                if ( (*k) > 0.7 )
-                { /* minimim of foreground pixels */
-                  Minimize(min.red,     (double)
-                    GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                  Minimize(min.green,   (double)
-                    GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                  Minimize(min.blue,    (double)
-                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                  Minimize(min.alpha,(double)
-                    GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                  if ( image->colorspace == CMYKColorspace)
-                    Minimize(min.black,(double)
-                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                }
-                else if ( (*k) < 0.3 )
-                { /* maximum of background pixels */
-                  Maximize(max.red,     (double)
-                    GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                  Maximize(max.green,   (double)
-                    GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                  Maximize(max.blue,    (double)
-                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                  Maximize(max.alpha,(double)
-                    GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                  if (image->colorspace == CMYKColorspace)
-                    Maximize(max.black,   (double)
-                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                }
+            count=0;
+            k=kernel->values;
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IfNaN(*k) == MagickFalse)
+                  {
+                    if (*k > 0.7)
+                      {
+                        if ((double) pixels[i] < pixel)
+                          pixel=(double) pixels[i];
+                      }
+                    else
+                      if (*k < 0.3)
+                        {
+                          if ((double) pixels[i] > maximum)
+                            maximum=(double) pixels[i];
+                        }
+                    count++;
+                  }
+                k++;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=(image->columns-1)*GetPixelChannels(image);
             }
-            /* Pattern Match if difference is positive */
-            min.red     -= max.red;     Maximize( min.red,     0.0 );
-            min.green   -= max.green;   Maximize( min.green,   0.0 );
-            min.blue    -= max.blue;    Maximize( min.blue,    0.0 );
-            min.black   -= max.black;   Maximize( min.black,   0.0 );
-            min.alpha -= max.alpha; Maximize( min.alpha, 0.0 );
+            pixel-=maximum;
+            if (pixel < 0.0)
+              pixel=0.0;
+            if (method ==  ThinningMorphology)
+              pixel=(double) p[center+i]-pixel;
+            else
+              if (method ==  ThickenMorphology)
+                pixel+=(double) p[center+i]+pixel;
             break;
+          }
+          case ErodeIntensityMorphology:
+          {
+            /*
+              Select pixel with minimum intensity within kernel neighbourhood.
 
-        case ErodeIntensityMorphology:
-            /* Select Pixel with Minimum Intensity within kernel neighbourhood
-            **
-            ** WARNING: the intensity test fails for CMYK and does not
-            ** take into account the moderating effect of the alpha channel
-            ** on the intensity.
-            **
-            ** NOTE that the kernel is not reflected for this operation!
+              The kernel is not reflected for this operation.
             */
-            k = kernel->values;
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
-                if ( IsNan(*k) || (*k) < 0.5 ) continue;
-                if ( result.red == 0.0 ||
-                     GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) < GetPixelIntensity(morphology_image,q) ) {
-                  /* copy the whole pixel - no channel selection */
-                  SetPixelRed(morphology_image,GetPixelRed(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelGreen(morphology_image,GetPixelGreen(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelBlue(morphology_image,GetPixelBlue(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelAlpha(morphology_image,GetPixelAlpha(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  if ( result.red > 0.0 ) changed++;
-                  result.red = 1.0;
-                }
+            count=0;
+            k=kernel->values;
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
+                  {
+                    if (GetPixelIntensity(image,pixels) < minimum)
+                      {
+                        pixel=(double) pixels[i];
+                        minimum=GetPixelIntensity(image,pixels);
+                      }
+                    count++;
+                  }
+                k++;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=(image->columns-1)*GetPixelChannels(image);
             }
             break;
+          }
+          case DilateIntensityMorphology:
+          {
+            /*
+              Select pixel with maximum intensity within kernel neighbourhood.
 
-        case DilateIntensityMorphology:
-            /* Select Pixel with Maximum Intensity within kernel neighbourhood
-            **
-            ** WARNING: the intensity test fails for CMYK and does not
-            ** take into account the moderating effect of the alpha channel
-            ** on the intensity (yet).
-            **
-            ** NOTE for correct working of this operation for asymetrical
-            ** kernels, the kernel needs to be applied in its reflected form.
-            ** That is its values needs to be reversed.
+              The kernel is not reflected for this operation.
             */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNan(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
-                if ( result.red == 0.0 ||
-                     GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) > GetPixelIntensity(morphology_image,q) ) {
-                  /* copy the whole pixel - no channel selection */
-                  SetPixelRed(morphology_image,GetPixelRed(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelGreen(morphology_image,GetPixelGreen(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelBlue(morphology_image,GetPixelBlue(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelAlpha(morphology_image,GetPixelAlpha(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  if ( result.red > 0.0 ) changed++;
-                  result.red = 1.0;
-                }
+            count=0;
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
+                  {
+                    if (GetPixelIntensity(image,pixels) > maximum)
+                      {
+                        pixel=(double) pixels[i];
+                        maximum=GetPixelIntensity(image,pixels);
+                      }
+                    count++;
+                  }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=(image->columns-1)*GetPixelChannels(image);
             }
             break;
+          }
+          case IterativeDistanceMorphology:
+          {
+            /*
+               Compute th iterative distance from black edge of a white image
+               shape.  Essentually white values are decreased to the smallest
+               'distance from edge' it can find.
+
+               It works by adding kernel values to the neighbourhood, and and
+               select the minimum value found. The kernel is rotated before
+               use, so kernel distances match resulting distances, when a user
+               provided asymmetric kernel is applied.
 
-        case IterativeDistanceMorphology:
-            /* Work out an iterative distance from black edge of a white image
-            ** shape.  Essentually white values are decreased to the smallest
-            ** 'distance from edge' it can find.
-            **
-            ** It works by adding kernel values to the neighbourhood, and and
-            ** select the minimum value found. The kernel is rotated before
-            ** use, so kernel distances match resulting distances, when a user
-            ** provided asymmetric kernel is applied.
-            **
-            **
-            ** This code is almost identical to True GrayScale Morphology But
-            ** not quite.
-            **
-            ** GreyDilate  Kernel values added, maximum value found Kernel is
-            ** rotated before use.
-            **
-            ** GrayErode:  Kernel values subtracted and minimum value found No
-            ** kernel rotation used.
-            **
-            ** Note the the Iterative Distance method is essentially a
-            ** GrayErode, but with negative kernel values, and kernel
-            ** rotation applied.
+               This code is nearly identical to True GrayScale Morphology but
+               not quite.
+
+               GreyDilate Kernel values added, maximum value found Kernel is
+               rotated before use.
+
+               GrayErode:  Kernel values subtracted and minimum value found No
+               kernel rotation used.
+
+               Note the the Iterative Distance method is essentially a
+               GrayErode, but with negative kernel values, and kernel rotation
+               applied.
             */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNan(*k) ) continue;
-                Minimize(result.red,     (*k)+(double)
-                     GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+(double)
-                     GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+(double)
-                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha,   (*k)+(double)
-                     GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                if ( image->colorspace == CMYKColorspace)
-                  Maximize(result.black, (*k)+(double)
-                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
+            count=0;
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IfNaN(*k) == MagickFalse)
+                  {
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
+                    count++;
+                  }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=(image->columns-1)*GetPixelChannels(image);
             }
             break;
-
-        case UndefinedMorphology:
-        default:
-            break; /* Do nothing */
-      }
-      /* Final mathematics of results (combine with original image?)
-      **
-      ** NOTE: Difference Morphology operators Edge* and *Hat could also
-      ** be done here but works better with iteration as a image difference
-      ** in the controling function (below).  Thicken and Thinning however
-      ** should be done here so thay can be iterated correctly.
-      */
-      switch ( method ) {
-        case HitAndMissMorphology:
-        case ErodeMorphology:
-          result = min;    /* minimum of neighbourhood */
-          break;
-        case DilateMorphology:
-          result = max;    /* maximum of neighbourhood */
-          break;
-        case ThinningMorphology:
-          /* subtract pattern match from original */
-          result.red     -= min.red;
-          result.green   -= min.green;
-          result.blue    -= min.blue;
-          result.black   -= min.black;
-          result.alpha   -= min.alpha;
-          break;
-        case ThickenMorphology:
-          /* Add the pattern matchs to the original */
-          result.red     += min.red;
-          result.green   += min.green;
-          result.blue    += min.blue;
-          result.black   += min.black;
-          result.alpha   += min.alpha;
-          break;
-        default:
-          /* result directly calculated or assigned */
-          break;
-      }
-      /* Assign the resulting pixel values - Clamping Result */
-      switch ( method ) {
-        case UndefinedMorphology:
-        case ConvolveMorphology:
-        case DilateIntensityMorphology:
-        case ErodeIntensityMorphology:
-          break;  /* full pixel was directly assigned - not a channel method */
-        default:
-          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
-          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
-          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
-          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-              (image->colorspace == CMYKColorspace))
-            SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
-          if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
-              (image->matte == MagickTrue))
-            SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
-          break;
+          }
+          case UndefinedMorphology:
+          default:
+            break;
+        }
+        if (fabs(pixel-p[center+i]) > MagickEpsilon)
+          changes[id]++;
+        gamma=PerceptibleReciprocal(gamma);
+        gamma*=(double) kernel->height*kernel->width/count;
+        SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*pixel),q);
       }
-      /* Count up changed pixels */
-      if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q)) ||
-          (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q)) ||
-          (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q)) ||
-          (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q)) ||
-          ((image->colorspace == CMYKColorspace) &&
-           (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
-        changed++;  /* The pixel was changed in some way! */
       p+=GetPixelChannels(image);
       q+=GetPixelChannels(morphology_image);
-    } /* x */
+    }
     if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
       status=MagickFalse;
     if (image->progress_monitor != (MagickProgressMonitor) NULL)
@@ -3276,37 +3206,41 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_MorphologyImage)
+        #pragma omp critical (MagickCore_MorphologyPrimitive)
 #endif
         proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
         if (proceed == MagickFalse)
           status=MagickFalse;
       }
-  } /* y */
+  }
   morphology_view=DestroyCacheView(morphology_view);
   image_view=DestroyCacheView(image_view);
-  return(status ? (ssize_t)changed : -1);
+  for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
+    changed+=changes[i];
+  changes=(size_t *) RelinquishMagickMemory(changes);
+  return(status ? (ssize_t) changed : -1);
 }
 
-/* This is almost identical to the MorphologyPrimative() function above,
-** but will apply the primitive directly to the actual image using two
-** passes, once in each direction, with the results of the previous (and
-** current) row being re-used.
-**
-** That is after each row is 'Sync'ed' into the image, the next row will
-** make use of those values as part of the calculation of the next row.
-** It then repeats, but going in the oppisite (bottom-up) direction.
-**
-** Because of this 're-use of results' this function can not make use
-** of multi-threaded, parellel processing.
+/*
+  This is almost identical to the MorphologyPrimative() function above, but
+  applies the primitive directly to the actual image using two passes, once in
+  each direction, with the results of the previous (and current) row being
+  re-used.
+
+  That is after each row is 'Sync'ed' into the image, the next row makes use of
+  those values as part of the calculation of the next row.  It repeats, but
+  going in the oppisite (bottom-up) direction.
+
+  Because of this 're-use of results' this function can not make use of multi-
+  threaded, parellel processing.
 */
 static ssize_t MorphologyPrimitiveDirect(Image *image,
   const MorphologyMethod method,const KernelInfo *kernel,
   ExceptionInfo *exception)
 {
   CacheView
-    *auth_view,
-    *virt_view;
+    *morphology_view,
+    *image_view;
 
   MagickBooleanType
     status;
@@ -3314,16 +3248,15 @@ static ssize_t MorphologyPrimitiveDirect(Image *image,
   MagickOffsetType
     progress;
 
-  ssize_t
-    y, offx, offy;
+  OffsetInfo
+    offset;
 
   size_t
-    virt_width,
+    width,
     changed;
 
-  status=MagickTrue;
-  changed=0;
-  progress=0;
+  ssize_t
+    y;
 
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
@@ -3331,35 +3264,34 @@ static ssize_t MorphologyPrimitiveDirect(Image *image,
   assert(kernel->signature == MagickSignature);
   assert(exception != (ExceptionInfo *) NULL);
   assert(exception->signature == MagickSignature);
-
-  /* Some methods (including convolve) needs use a reflected kernel.
-   * Adjust 'origin' offsets to loop though kernel as a reflection.
-   */
-  offx = kernel->x;
-  offy = kernel->y;
-  switch(method) {
+  status=MagickTrue;
+  changed=0;
+  progress=0;
+  switch(method)
+  {
     case DistanceMorphology:
     case VoronoiMorphology:
-      /* kernel needs to used with reflection about origin */
-      offx = (ssize_t) kernel->width-offx-1;
-      offy = (ssize_t) kernel->height-offy-1;
-      break;
-#if 0
-    case ?????Morphology:
-      /* kernel is used as is, without reflection */
+    {
+      /*
+        Kernel reflected about origin.
+      */
+      offset.x=(ssize_t) kernel->width-kernel->x-1;
+      offset.y=(ssize_t) kernel->height-kernel->y-1;
       break;
-#endif
+    }
     default:
-      assert("Not a PrimativeDirect Morphology Method" != (char *) NULL);
+    {
+      offset.x=kernel->x;
+      offset.y=kernel->y;
       break;
+    }
   }
-
-  /* DO NOT THREAD THIS CODE! */
-  /* two views into same image (virtual, and actual) */
-  virt_view=AcquireCacheView(image);
-  auth_view=AcquireCacheView(image);
-  virt_width=image->columns+kernel->width-1;
-
+  /*
+    Two views into same image, do not thread.
+  */
+  image_view=AcquireVirtualCacheView(image,exception);
+  morphology_view=AcquireAuthenticCacheView(image,exception);
+  width=image->columns+kernel->width-1;
   for (y=0; y < (ssize_t) image->rows; y++)
   {
     register const Quantum
@@ -3372,176 +3304,156 @@ static ssize_t MorphologyPrimitiveDirect(Image *image,
       x;
 
     ssize_t
-      r;
-
-    /* NOTE read virtual pixels, and authentic pixels, from the same image!
-    ** we read using virtual to get virtual pixel handling, but write back
-    ** into the same image.
-    **
-    ** Only top half of kernel is processed as we do a single pass downward
-    ** through the image iterating the distance function as we go.
+      center;
+
+    /*
+      Read virtual pixels, and authentic pixels, from the same image!  We read
+      using virtual to get virtual pixel handling, but write back into the same
+      image.
+
+      Only top half of kernel is processed as we do a single pass downward
+      through the image iterating the distance function as we go.
     */
     if (status == MagickFalse)
-      break;
-    p=GetCacheViewVirtualPixels(virt_view,-offx,y-offy,virt_width,(size_t)
-      offy+1,exception);
-    q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
+      continue;
+    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(size_t)
+      offset.y+1,exception);
+    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
       exception);
     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
-      status=MagickFalse;
-    if (status == MagickFalse)
-      break;
-
-    /* offset to origin in 'p'. while 'q' points to it directly */
-    r = (ssize_t) virt_width*offy + offx;
-
+      {
+        status=MagickFalse;
+        continue;
+      }
+    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
+      GetPixelChannels(image)*offset.x);
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      ssize_t
-        v;
-
       register ssize_t
-        u;
-
-      register const MagickRealType
-        *restrict k;
-
-      register const Quantum
-        *restrict k_pixels;
-
-      PixelInfo
-        result;
-
-      /* Starting Defaults */
-      GetPixelInfo(image,&result);
-      GetPixelInfoPixel(image,q,&result);
-      if ( method != VoronoiMorphology )
-        result.alpha = QuantumRange - result.alpha;
-
-      switch ( method ) {
-        case DistanceMorphology:
-            /* Add kernel Value and select the minimum value found. */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v <= (ssize_t) offy; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNan(*k) ) continue;
-                Minimize(result.red,     (*k)+
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                if (image->colorspace == CMYKColorspace)
-                  Minimize(result.black,(*k)+
-                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha, (*k)+
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-              }
-              k_pixels += virt_width*GetPixelChannels(image);
-            }
-            /* repeat with the just processed pixels of this row */
-            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
-            k_pixels = q-offx*GetPixelChannels(image);
-              for (u=0; u < (ssize_t) offx; u++, k--) {
-                if ( x+u-offx < 0 ) continue;  /* off the edge! */
-                if ( IsNan(*k) ) continue;
-                Minimize(result.red,     (*k)+
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                if (image->colorspace == CMYKColorspace)
-                  Minimize(result.black,(*k)+
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha,(*k)+
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-              }
-            break;
-        case VoronoiMorphology:
-            /* Apply Distance to 'Matte' channel, coping the closest color.
-            **
-            ** This is experimental, and realy the 'alpha' component should
-            ** be completely separate 'masking' channel so that alpha can
-            ** also be used as part of the results.
-            */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v <= (ssize_t) offy; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNan(*k) ) continue;
-                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
+        i;
+
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        double
+          pixel;
+
+        PixelTrait
+          traits;
+
+        register const MagickRealType
+          *restrict k;
+
+        register const Quantum
+          *restrict pixels;
+
+        register ssize_t
+          u;
+
+        ssize_t
+          v;
+
+        traits=GetPixelChannelTraits(image,(PixelChannel) i);
+        if (traits == UndefinedPixelTrait)
+          continue;
+        if (((traits & CopyPixelTrait) != 0) ||
+            (GetPixelReadMask(image,p+center) == 0))
+          continue;
+        pixels=p;
+        pixel=(double) QuantumRange;
+        switch (method)
+        {
+          case DistanceMorphology:
+          {
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v <= offset.y; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IfNaN(*k) == MagickFalse)
                   {
-                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
-                      &result);
-                    result.alpha += *k;
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
                   }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=(image->columns-1)*GetPixelChannels(image);
             }
-            /* repeat with the just processed pixels of this row */
-            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
-            k_pixels = q-offx*GetPixelChannels(image);
-              for (u=0; u < (ssize_t) offx; u++, k--) {
-                if ( x+u-offx < 0 ) continue;  /* off the edge! */
-                if ( IsNan(*k) ) continue;
-                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            pixels=q-offset.x*GetPixelChannels(image);
+            for (u=0; u < offset.x; u++)
+            {
+              if ((IfNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
+                {
+                  if ((pixels[i]+(*k)) < pixel)
+                    pixel=(double) pixels[i]+(*k);
+                }
+              k--;
+              pixels+=GetPixelChannels(image);
+            }
+            break;
+          }
+          case VoronoiMorphology:
+          {
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v < offset.y; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IfNaN(*k) == MagickFalse)
                   {
-                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
-                      &result);
-                    result.alpha += *k;
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
                   }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
+              pixels+=(image->columns-1)*GetPixelChannels(image);
+            }
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            pixels=q-offset.x*GetPixelChannels(image);
+            for (u=0; u < offset.x; u++)
+            {
+              if ((IfNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
+                {
+                  if ((pixels[i]+(*k)) < pixel)
+                    pixel=(double) pixels[i]+(*k);
+                }
+              k--;
+              pixels+=GetPixelChannels(image);
+            }
             break;
-        default:
-          /* result directly calculated or assigned */
-          break;
-      }
-      /* Assign the resulting pixel values - Clamping Result */
-      switch ( method ) {
-        case VoronoiMorphology:
-          SetPixelInfoPixel(image,&result,q);
-          break;
-        default:
-          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelRed(image,ClampToQuantum(result.red),q);
-          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelGreen(image,ClampToQuantum(result.green),q);
-          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelBlue(image,ClampToQuantum(result.blue),q);
-          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-              (image->colorspace == CMYKColorspace))
-            SetPixelBlack(image,ClampToQuantum(result.black),q);
-          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
-              (image->matte == MagickTrue))
-            SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
-          break;
+          }
+          default:
+            break;
+        }
+        if (fabs(pixel-q[i]) > MagickEpsilon)
+          changed++;
+        q[i]=ClampToQuantum(pixel);
       }
-      /* Count up changed pixels */
-      if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q)) ||
-          (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q)) ||
-          (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q)) ||
-          (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q)) ||
-          ((image->colorspace == CMYKColorspace) &&
-           (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
-        changed++;  /* The pixel was changed in some way! */
-
-      p+=GetPixelChannels(image); /* increment pixel buffers */
+      p+=GetPixelChannels(image);
       q+=GetPixelChannels(image);
-    } /* x */
-
-    if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
+    }
+    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
       status=MagickFalse;
     if (image->progress_monitor != (MagickProgressMonitor) NULL)
-      if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
-                == MagickFalse )
-        status=MagickFalse;
-
-  } /* y */
+      {
+        MagickBooleanType
+          proceed;
 
-  /* Do the reversed pass through the image */
-  for (y=(ssize_t)image->rows-1; y >= 0; y--)
+        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
+        if (proceed == MagickFalse)
+          status=MagickFalse;
+      }
+  }
+  morphology_view=DestroyCacheView(morphology_view);
+  image_view=DestroyCacheView(image_view);
+  /*
+    Do the reverse pass through the image.
+  */
+  image_view=AcquireVirtualCacheView(image,exception);
+  morphology_view=AcquireAuthenticCacheView(image,exception);
+  for (y=(ssize_t) image->rows-1; y >= 0; y--)
   {
     register const Quantum
       *restrict p;
@@ -3553,192 +3465,167 @@ static ssize_t MorphologyPrimitiveDirect(Image *image,
       x;
 
     ssize_t
-      r;
+      center;
 
-    if (status == MagickFalse)
-      break;
-    /* NOTE read virtual pixels, and authentic pixels, from the same image!
-    ** we read using virtual to get virtual pixel handling, but write back
-    ** into the same image.
-    **
-    ** Only the bottom half of the kernel will be processes as we
-    ** up the image.
+    /*
+       Read virtual pixels, and authentic pixels, from the same image.  We
+       read using virtual to get virtual pixel handling, but write back
+       into the same image.
+
+       Only the bottom half of the kernel is processed as we up the image.
     */
-    p=GetCacheViewVirtualPixels(virt_view,-offx,y,virt_width,(size_t)
+    if (status == MagickFalse)
+      continue;
+    p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(size_t)
       kernel->y+1,exception);
-    q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
+    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
       exception);
     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
-      status=MagickFalse;
-    if (status == MagickFalse)
-      break;
+      {
+        status=MagickFalse;
+        continue;
+      }
+    p+=(image->columns-1)*GetPixelChannels(image);
+    q+=(image->columns-1)*GetPixelChannels(image);
+    center=(ssize_t) (offset.x*GetPixelChannels(image));
+    for (x=(ssize_t) image->columns-1; x >= 0; x--)
+    {
+      register ssize_t
+        i;
 
-    /* adjust positions to end of row */
-    p += (image->columns-1)*GetPixelChannels(image);
-    q += (image->columns-1)*GetPixelChannels(image);
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        double
+          pixel;
 
-    /* offset to origin in 'p'. while 'q' points to it directly */
-    r = offx;
+        PixelTrait
+          traits;
 
-    for (x=(ssize_t)image->columns-1; x >= 0; x--)
-    {
-      ssize_t
-        v;
+        register const MagickRealType
+          *restrict k;
 
-      register ssize_t
-        u;
-
-      register const MagickRealType
-        *restrict k;
-
-      register const Quantum
-        *restrict k_pixels;
-
-      PixelInfo
-        result;
-
-      /* Default - previously modified pixel */
-      GetPixelInfo(image,&result);
-      GetPixelInfoPixel(image,q,&result);
-      if ( method != VoronoiMorphology )
-        result.alpha = QuantumRange - result.alpha;
-
-      switch ( method ) {
-        case DistanceMorphology:
-            /* Add kernel Value and select the minimum value found. */
-            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
-            k_pixels = p;
-            for (v=offy; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNan(*k) ) continue;
-                Minimize(result.red,     (*k)+
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                if ( image->colorspace == CMYKColorspace)
-                  Minimize(result.black,(*k)+
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha, (*k)+
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-              }
-              k_pixels += virt_width*GetPixelChannels(image);
-            }
-            /* repeat with the just processed pixels of this row */
-            k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
-            k_pixels = q-offx*GetPixelChannels(image);
-              for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
-                if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
-                if ( IsNan(*k) ) continue;
-                Minimize(result.red,     (*k)+
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                if ( image->colorspace == CMYKColorspace)
-                  Minimize(result.black,   (*k)+
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha, (*k)+
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-              }
-            break;
-        case VoronoiMorphology:
-            /* Apply Distance to 'Matte' channel, coping the closest color.
-            **
-            ** This is experimental, and realy the 'alpha' component should
-            ** be completely separate 'masking' channel.
-            */
-            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
-            k_pixels = p;
-            for (v=offy; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNan(*k) ) continue;
-                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
+        register const Quantum
+          *restrict pixels;
+
+        register ssize_t
+          u;
+
+        ssize_t
+          v;
+
+        traits=GetPixelChannelTraits(image,(PixelChannel) i);
+        if (traits == UndefinedPixelTrait)
+          continue;
+        if (((traits & CopyPixelTrait) != 0) ||
+            (GetPixelReadMask(image,p+center) == 0))
+          continue;
+        pixels=p;
+        pixel=(double) QuantumRange;
+        switch (method)
+        {
+          case DistanceMorphology:
+          {
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            for (v=offset.y; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IfNaN(*k) == MagickFalse)
                   {
-                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
-                      &result);
-                    result.alpha += *k;
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
                   }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=(image->columns-1)*GetPixelChannels(image);
+            }
+            k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
+            pixels=q-offset.x*GetPixelChannels(image);
+            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
+            {
+              if ((IfNaN(*k) == MagickFalse) &&
+                  ((x+u-offset.x) < (ssize_t) image->columns))
+                {
+                  if ((pixels[i]+(*k)) < pixel)
+                    pixel=(double) pixels[i]+(*k);
+                }
+              k--;
+              pixels+=GetPixelChannels(image);
             }
-            /* repeat with the just processed pixels of this row */
-            k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
-            k_pixels = q-offx*GetPixelChannels(image);
-              for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
-                if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
-                if ( IsNan(*k) ) continue;
-                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
+            break;
+          }
+          case VoronoiMorphology:
+          {
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            for (v=offset.y; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IfNaN(*k) == MagickFalse)
                   {
-                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
-                      &result);
-                    result.alpha += *k;
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
                   }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
+              pixels+=(image->columns-1)*GetPixelChannels(image);
+            }
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            pixels=q-offset.x*GetPixelChannels(image);
+            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
+            {
+              if ((IfNaN(*k) == MagickFalse) &&
+                  ((x+u-offset.x) < (ssize_t) image->columns))
+                {
+                  if ((pixels[i]+(*k)) < pixel)
+                    pixel=(double) pixels[i]+(*k);
+                }
+              k--;
+              pixels+=GetPixelChannels(image);
+            }
             break;
-        default:
-          /* result directly calculated or assigned */
-          break;
-      }
-      /* Assign the resulting pixel values - Clamping Result */
-      switch ( method ) {
-        case VoronoiMorphology:
-          SetPixelInfoPixel(image,&result,q);
-          break;
-        default:
-          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelRed(image,ClampToQuantum(result.red),q);
-          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelGreen(image,ClampToQuantum(result.green),q);
-          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelBlue(image,ClampToQuantum(result.blue),q);
-          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-              (image->colorspace == CMYKColorspace))
-            SetPixelBlack(image,ClampToQuantum(result.black),q);
-          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
-              (image->matte == MagickTrue))
-            SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
-          break;
+          }
+          default:
+            break;
+        }
+        if (fabs(pixel-q[i]) > MagickEpsilon)
+          changed++;
+        q[i]=ClampToQuantum(pixel);
       }
-      /* Count up changed pixels */
-      if (   (GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q))
-          || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q))
-          || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q))
-          || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q))
-          || ((image->colorspace == CMYKColorspace) &&
-              (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
-        changed++;  /* The pixel was changed in some way! */
-
-      p-=GetPixelChannels(image); /* go backward through pixel buffers */
+      p-=GetPixelChannels(image);
       q-=GetPixelChannels(image);
-    } /* x */
-    if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
+    }
+    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
       status=MagickFalse;
     if (image->progress_monitor != (MagickProgressMonitor) NULL)
-      if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
-                == MagickFalse )
-        status=MagickFalse;
-
-  } /* y */
+      {
+        MagickBooleanType
+          proceed;
 
-  auth_view=DestroyCacheView(auth_view);
-  virt_view=DestroyCacheView(virt_view);
+        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
+        if (proceed == MagickFalse)
+          status=MagickFalse;
+      }
+  }
+  morphology_view=DestroyCacheView(morphology_view);
+  image_view=DestroyCacheView(image_view);
   return(status ? (ssize_t) changed : -1);
 }
 
-/* Apply a Morphology by calling one of the above low level primitive
-** application functions.  This function handles any iteration loops,
-** composition or re-iteration of results, and compound morphology methods
-** that is based on multiple low-level (staged) morphology methods.
-**
-** Basically this provides the complex grue between the requested morphology
-** method and raw low-level implementation (above).
+/*
+  Apply a Morphology by calling one of the above low level primitive
+  application functions.  This function handles any iteration loops,
+  composition or re-iteration of results, and compound morphology methods that
+  is based on multiple low-level (staged) morphology methods.
+
+  Basically this provides the complex glue between the requested morphology
+  method and raw low-level implementation (above).
 */
 MagickPrivate Image *MorphologyApply(const Image *image,
   const MorphologyMethod method, const ssize_t iterations,
-  const KernelInfo *kernel, const CompositeOperator compose,
+  const KernelInfo *kernel, const CompositeOperator compose,const double bias,
   ExceptionInfo *exception)
 {
   CompositeOperator
@@ -3782,7 +3669,7 @@ MagickPrivate Image *MorphologyApply(const Image *image,
     changed;        /* number pixels changed by last primitive operation */
 
   char
-    v_info[80];
+    v_info[MaxTextExtent];
 
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
@@ -3799,7 +3686,7 @@ MagickPrivate Image *MorphologyApply(const Image *image,
   if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
      kernel_limit = image->columns>image->rows ? image->columns : image->rows;
 
-  verbose = IsMagickTrue(GetImageArtifact(image,"verbose"));
+  verbose = IsStringTrue(GetImageArtifact(image,"debug"));
 
   /* initialise for cleanup */
   curr_image = (Image *) image;
@@ -3849,7 +3736,7 @@ MagickPrivate Image *MorphologyApply(const Image *image,
   /* Apply special methods with special requirments
   ** For example, single run only, or post-processing requirements
   */
-  if ( special == MagickTrue )
+  if ( special != MagickFalse )
     {
       rslt_image=CloneImage(image,0,0,MagickTrue,exception);
       if (rslt_image == (Image *) NULL)
@@ -3857,10 +3744,9 @@ MagickPrivate Image *MorphologyApply(const Image *image,
       if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
         goto error_cleanup;
 
-      changed = MorphologyPrimitiveDirect(rslt_image, method,
-         kernel, exception);
+      changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
 
-      if ( verbose == MagickTrue )
+      if ( IfMagickTrue(verbose) )
         (void) (void) FormatLocaleFile(stderr,
           "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
           CommandOptionToMnemonic(MagickMorphologyOptions, method),
@@ -3870,11 +3756,11 @@ MagickPrivate Image *MorphologyApply(const Image *image,
         goto error_cleanup;
 
       if ( method == VoronoiMorphology ) {
-        /* Preserve the alpha channel of input image - but turned off */
+        /* Preserve the alpha channel of input image - but turned it off */
         (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
           exception);
-        (void) CompositeImage(rslt_image, CopyAlphaCompositeOp, image, 0, 0,
-          exception);
+        (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
+          MagickTrue,0,0,exception);
         (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
           exception);
       }
@@ -4008,7 +3894,7 @@ MagickPrivate Image *MorphologyApply(const Image *image,
         assert( this_kernel != (KernelInfo *) NULL );
 
         /* Extra information for debugging compound operations */
-        if ( verbose == MagickTrue ) {
+        if ( IfMagickTrue(verbose) ) {
           if ( stage_limit > 1 )
             (void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
              CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
@@ -4036,15 +3922,14 @@ MagickPrivate Image *MorphologyApply(const Image *image,
                 goto error_cleanup;
               if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
                 goto error_cleanup;
-              /* work_image->type=image->type; ??? */
             }
 
           /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
           count++;
           changed = MorphologyPrimitive(curr_image, work_image, primitive,
-             this_kernel, exception);
+                       this_kernel, bias, exception);
 
-          if ( verbose == MagickTrue ) {
+          if ( IfMagickTrue(verbose) ) {
             if ( kernel_loop > 1 )
               (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
             (void) (void) FormatLocaleFile(stderr,
@@ -4069,9 +3954,9 @@ MagickPrivate Image *MorphologyApply(const Image *image,
 
         } /* End Loop 4: Iterate the kernel with primitive */
 
-        if ( verbose == MagickTrue && kernel_changed != (size_t)changed )
+        if ( IfMagickTrue(verbose) && kernel_changed != (size_t)changed )
           (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
-        if ( verbose == MagickTrue && stage_loop < stage_limit )
+        if ( IfMagickTrue(verbose) && stage_loop < stage_limit )
           (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
 
 #if 0
@@ -4096,20 +3981,20 @@ MagickPrivate Image *MorphologyApply(const Image *image,
         case EdgeInMorphology:
         case TopHatMorphology:
         case BottomHatMorphology:
-          if ( verbose == MagickTrue )
+          if ( IfMagickTrue(verbose) )
             (void) FormatLocaleFile(stderr,
               "\n%s: Difference with original image",CommandOptionToMnemonic(
               MagickMorphologyOptions, method) );
-          (void) CompositeImage(curr_image,DifferenceCompositeOp,image,0,0,
-            exception);
+          (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
+            MagickTrue,0,0,exception);
           break;
         case EdgeMorphology:
-          if ( verbose == MagickTrue )
+          if ( IfMagickTrue(verbose) )
             (void) FormatLocaleFile(stderr,
               "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
               MagickMorphologyOptions, method) );
-          (void) CompositeImage(curr_image,DifferenceCompositeOp,save_image,0,
-            0,exception);
+          (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
+            MagickTrue,0,0,exception);
           save_image = DestroyImage(save_image); /* finished with save image */
           break;
         default:
@@ -4120,7 +4005,7 @@ MagickPrivate Image *MorphologyApply(const Image *image,
       if ( kernel->next == (KernelInfo *) NULL )
         rslt_image = curr_image;   /* just return the resulting image */
       else if ( rslt_compose == NoCompositeOp )
-        { if ( verbose == MagickTrue ) {
+        { if ( IfMagickTrue(verbose) ) {
             if ( this_kernel->next != (KernelInfo *) NULL )
               (void) FormatLocaleFile(stderr, " (re-iterate)");
             else
@@ -4129,7 +4014,7 @@ MagickPrivate Image *MorphologyApply(const Image *image,
           rslt_image = curr_image; /* return result, and re-iterate */
         }
       else if ( rslt_image == (Image *) NULL)
-        { if ( verbose == MagickTrue )
+        { if ( IfMagickTrue(verbose) )
             (void) FormatLocaleFile(stderr, " (save for compose)");
           rslt_image = curr_image;
           curr_image = (Image *) image;  /* continue with original image */
@@ -4142,15 +4027,15 @@ MagickPrivate Image *MorphologyApply(const Image *image,
           ** purely mathematical way, and only to the selected channels.
           ** IE: Turn off SVG composition 'alpha blending'.
           */
-          if ( verbose == MagickTrue )
+          if ( IfMagickTrue(verbose) )
             (void) FormatLocaleFile(stderr, " (compose \"%s\")",
-                 CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
-          (void) CompositeImage(rslt_image, rslt_compose, curr_image, 0, 0,
-            exception);
+              CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
+          (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
+            0,0,exception);
           curr_image = DestroyImage(curr_image);
           curr_image = (Image *) image;  /* continue with original image */
         }
-      if ( verbose == MagickTrue )
+      if ( IfMagickTrue(verbose) )
         (void) FormatLocaleFile(stderr, "\n");
 
       /* loop to the next kernel in a multi-kernel list */
@@ -4196,27 +4081,27 @@ exit_cleanup:
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  MorphologyImage() applies a user supplied kernel to the image
-%  according to the given mophology method.
+%  MorphologyImage() applies a user supplied kernel to the image according to
+%  the given mophology method.
 %
 %  This function applies any and all user defined settings before calling
 %  the above internal function MorphologyApply().
 %
 %  User defined settings include...
-%    * Output Bias for Convolution and correlation   ("-bias")
-%    * Kernel Scale/normalize settings     ("-set 'option:convolve:scale'")
+%    * Output Bias for Convolution and correlation ("-define convolve:bias=??")
+%    * Kernel Scale/normalize settings             ("-define convolve:scale=??")
 %      This can also includes the addition of a scaled unity kernel.
-%    * Show Kernel being applied           ("-set option:showkernel 1")
+%    * Show Kernel being applied                   ("-define showkernel=1")
+%
+%  Other operators that do not want user supplied options interfering,
+%  especially "convolve:bias" and "showkernel" should use MorphologyApply()
+%  directly.
 %
 %  The format of the MorphologyImage method is:
 %
 %      Image *MorphologyImage(const Image *image,MorphologyMethod method,
 %        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
 %
-%      Image *MorphologyImage(const Image *image, const ChannelType
-%        channel,MorphologyMethod method,const ssize_t iterations,
-%        KernelInfo *kernel,ExceptionInfo *exception)
-%
 %  A description of each parameter follows:
 %
 %    o image: the image.
@@ -4247,32 +4132,53 @@ MagickExport Image *MorphologyImage(const Image *image,
   Image
     *morphology_image;
 
+  double
+    bias;
+
+  curr_kernel = (KernelInfo *) kernel;
+  bias=0.0;
+  compose = UndefinedCompositeOp;  /* use default for method */
 
   /* Apply Convolve/Correlate Normalization and Scaling Factors.
    * This is done BEFORE the ShowKernelInfo() function is called so that
    * users can see the results of the 'option:convolve:scale' option.
    */
-  curr_kernel = (KernelInfo *) kernel;
-  if ( method == ConvolveMorphology ||  method == CorrelateMorphology )
-    {
+  if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
       const char
         *artifact;
+
+      /* Get the bias value as it will be needed */
+      artifact = GetImageArtifact(image,"convolve:bias");
+      if ( artifact != (const char *) NULL) {
+        if (IfMagickFalse(IsGeometry(artifact)))
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionWarning,"InvalidSetting","'%s' '%s'",
+               "convolve:bias",artifact);
+        else
+          bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
+      }
+
+      /* Scale kernel according to user wishes */
       artifact = GetImageArtifact(image,"convolve:scale");
       if ( artifact != (const char *)NULL ) {
-        if ( curr_kernel == kernel )
-          curr_kernel = CloneKernelInfo(kernel);
-        if (curr_kernel == (KernelInfo *) NULL) {
-          curr_kernel=DestroyKernelInfo(curr_kernel);
-          return((Image *) NULL);
+        if (IfMagickFalse(IsGeometry(artifact)))
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionWarning,"InvalidSetting","'%s' '%s'",
+               "convolve:scale",artifact);
+        else {
+          if ( curr_kernel == kernel )
+            curr_kernel = CloneKernelInfo(kernel);
+          if (curr_kernel == (KernelInfo *) NULL)
+            return((Image *) NULL);
+          ScaleGeometryKernelInfo(curr_kernel, artifact);
         }
-        ScaleGeometryKernelInfo(curr_kernel, artifact);
       }
     }
 
   /* display the (normalized) kernel via stderr */
-  if ( IsMagickTrue(GetImageArtifact(image,"showkernel"))
-    || IsMagickTrue(GetImageArtifact(image,"convolve:showkernel"))
-    || IsMagickTrue(GetImageArtifact(image,"morphology:showkernel")) )
+  if ( IfStringTrue(GetImageArtifact(image,"showkernel"))
+    || IfStringTrue(GetImageArtifact(image,"convolve:showkernel"))
+    || IfStringTrue(GetImageArtifact(image,"morphology:showkernel")) )
     ShowKernelInfo(curr_kernel);
 
   /* Override the default handling of multi-kernel morphology results
@@ -4283,15 +4189,24 @@ MagickExport Image *MorphologyImage(const Image *image,
    */
   { const char
       *artifact;
+    ssize_t
+      parse;
+
     artifact = GetImageArtifact(image,"morphology:compose");
-    compose = UndefinedCompositeOp;  /* use default for method */
-    if ( artifact != (const char *) NULL)
-      compose=(CompositeOperator) ParseCommandOption(MagickComposeOptions,
+    if ( artifact != (const char *) NULL) {
+      parse=ParseCommandOption(MagickComposeOptions,
         MagickFalse,artifact);
+      if ( parse < 0 )
+        (void) ThrowMagickException(exception,GetMagickModule(),
+             OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
+             "morphology:compose",artifact);
+      else
+        compose=(CompositeOperator)parse;
+    }
   }
   /* Apply the Morphology */
-  morphology_image=MorphologyApply(image,method,iterations,curr_kernel,compose,
-    exception);
+  morphology_image = MorphologyApply(image,method,iterations,
+    curr_kernel,compose,bias,exception);
 
   /* Cleanup and Exit */
   if ( curr_kernel != kernel )
@@ -4387,7 +4302,7 @@ static void RotateKernelInfo(KernelInfo *kernel, double angle)
     {
       if ( kernel->width == 3 && kernel->height == 3 )
         { /* Rotate a 3x3 square by 45 degree angle */
-          MagickRealType t  = kernel->values[0];
+          double t  = kernel->values[0];
           kernel->values[0] = kernel->values[3];
           kernel->values[3] = kernel->values[6];
           kernel->values[6] = kernel->values[7];
@@ -4438,13 +4353,15 @@ static void RotateKernelInfo(KernelInfo *kernel, double angle)
         }
       else if ( kernel->width == kernel->height )
         { /* Rotate a square array of values by 90 degrees */
-          { register size_t
+          { register ssize_t
               i,j,x,y;
+
             register MagickRealType
               *k,t;
+
             k=kernel->values;
-            for( i=0, x=kernel->width-1;  i<=x;   i++, x--)
-              for( j=0, y=kernel->height-1;  j<y;   j++, y--)
+            for( i=0, x=(ssize_t) kernel->width-1;  i<=x;   i++, x--)
+              for( j=0, y=(ssize_t) kernel->height-1;  j<y;   j++, y--)
                 { t                    = k[i+j*kernel->width];
                   k[i+j*kernel->width] = k[j+x*kernel->width];
                   k[j+x*kernel->width] = k[x+y*kernel->width];
@@ -4472,7 +4389,7 @@ static void RotateKernelInfo(KernelInfo *kernel, double angle)
        * Basically all that is needed is a reversal of the kernel data!
        * And a reflection of the origon
        */
-      double
+      MagickRealType
         t;
 
       register MagickRealType
@@ -4535,15 +4452,16 @@ static void RotateKernelInfo(KernelInfo *kernel, double angle)
 %
 */
 MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
-     const char *geometry)
+  const char *geometry)
 {
-  GeometryFlags
+  MagickStatusType
     flags;
+
   GeometryInfo
     args;
 
   SetGeometryInfo(&args);
-  flags = (GeometryFlags) ParseGeometry(geometry, &args);
+  flags = ParseGeometry(geometry, &args);
 
 #if 0
   /* For Debugging Geometry Input */
@@ -4560,7 +4478,7 @@ MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
     args.sigma = 0.0;
 
   /* Scale/Normalize the input kernel */
-  ScaleKernelInfo(kernel, args.rho, flags);
+  ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
 
   /* Add Unity Kernel, for blending with original */
   if ( (flags & SigmaValue) != 0 )
@@ -4642,13 +4560,13 @@ MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
 MagickExport void ScaleKernelInfo(KernelInfo *kernel,
   const double scaling_factor,const GeometryFlags normalize_flags)
 {
-  register ssize_t
-    i;
-
   register double
     pos_scale,
     neg_scale;
 
+  register ssize_t
+    i;
+
   /* do the other kernels in a multi-kernel list first */
   if ( kernel->next != (KernelInfo *) NULL)
     ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
@@ -4656,7 +4574,7 @@ MagickExport void ScaleKernelInfo(KernelInfo *kernel,
   /* Normalization of Kernel */
   pos_scale = 1.0;
   if ( (normalize_flags&NormalizeValue) != 0 ) {
-    if ( fabs(kernel->positive_range + kernel->negative_range) > MagickEpsilon )
+    if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
       /* non-zero-summing kernel (generally positive) */
       pos_scale = fabs(kernel->positive_range + kernel->negative_range);
     else
@@ -4665,9 +4583,9 @@ MagickExport void ScaleKernelInfo(KernelInfo *kernel,
   }
   /* Force kernel into a normalized zero-summing kernel */
   if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
-    pos_scale = ( fabs(kernel->positive_range) > MagickEpsilon )
+    pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
                  ? kernel->positive_range : 1.0;
-    neg_scale = ( fabs(kernel->negative_range) > MagickEpsilon )
+    neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
                  ? -kernel->negative_range : 1.0;
   }
   else
@@ -4678,7 +4596,7 @@ MagickExport void ScaleKernelInfo(KernelInfo *kernel,
   neg_scale = scaling_factor/neg_scale;
 
   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
-    if ( ! IsNan(kernel->values[i]) )
+    if ( ! IfNaN(kernel->values[i]) )
       kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
 
   /* convolution output range */
@@ -4740,7 +4658,7 @@ MagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
       (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
     (void) FormatLocaleFile(stderr, " \"%s",
           CommandOptionToMnemonic(MagickKernelOptions, k->type) );
-    if ( fabs(k->angle) > MagickEpsilon )
+    if ( fabs(k->angle) >= MagickEpsilon )
       (void) FormatLocaleFile(stderr, "@%lg", k->angle);
     (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
       k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
@@ -4761,11 +4679,11 @@ MagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
     for (i=v=0; v < k->height; v++) {
       (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
       for (u=0; u < k->width; u++, i++)
-        if ( IsNan(k->values[i]) )
+        if ( IfNaN(k->values[i]) )
           (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
         else
           (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
-              GetMagickPrecision(), k->values[i]);
+              GetMagickPrecision(), (double) k->values[i]);
       (void) FormatLocaleFile(stderr,"\n");
     }
   }
@@ -4853,7 +4771,7 @@ MagickPrivate void ZeroKernelNans(KernelInfo *kernel)
     ZeroKernelNans(kernel->next);
 
   for (i=0; i < (kernel->width*kernel->height); i++)
-    if ( IsNan(kernel->values[i]) )
+    if ( IfNaN(kernel->values[i]) )
       kernel->values[i] = 0.0;
 
   return;