MagickCore  6.7.5
morphology.c
Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %    M   M    OOO    RRRR   PPPP   H   H   OOO   L       OOO    GGGG  Y   Y   %
00007 %    MM MM   O   O   R   R  P   P  H   H  O   O  L      O   O  G       Y Y    %
00008 %    M M M   O   O   RRRR   PPPP   HHHHH  O   O  L      O   O  G GGG    Y     %
00009 %    M   M   O   O   R R    P      H   H  O   O  L      O   O  G   G    Y     %
00010 %    M   M    OOO    R  R   P      H   H   OOO   LLLLL   OOO    GGG     Y     %
00011 %                                                                             %
00012 %                                                                             %
00013 %                        MagickCore Morphology Methods                        %
00014 %                                                                             %
00015 %                              Software Design                                %
00016 %                              Anthony Thyssen                                %
00017 %                               January 2010                                  %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 % Morpology is the the application of various kernels, of any size and even
00037 % shape, to a image in various ways (typically binary, but not always).
00038 %
00039 % Convolution (weighted sum or average) is just one specific type of
00040 % morphology. Just one that is very common for image bluring and sharpening
00041 % effects.  Not only 2D Gaussian blurring, but also 2-pass 1D Blurring.
00042 %
00043 % This module provides not only a general morphology function, and the ability
00044 % to apply more advanced or iterative morphologies, but also functions for the
00045 % generation of many different types of kernel arrays from user supplied
00046 % arguments. Prehaps even the generation of a kernel from a small image.
00047 */
00048 
00049 /*
00050   Include declarations.
00051 */
00052 #include "MagickCore/studio.h"
00053 #include "MagickCore/artifact.h"
00054 #include "MagickCore/cache-view.h"
00055 #include "MagickCore/color-private.h"
00056 #include "MagickCore/enhance.h"
00057 #include "MagickCore/exception.h"
00058 #include "MagickCore/exception-private.h"
00059 #include "MagickCore/gem.h"
00060 #include "MagickCore/gem-private.h"
00061 #include "MagickCore/hashmap.h"
00062 #include "MagickCore/image.h"
00063 #include "MagickCore/image-private.h"
00064 #include "MagickCore/list.h"
00065 #include "MagickCore/magick.h"
00066 #include "MagickCore/memory_.h"
00067 #include "MagickCore/monitor-private.h"
00068 #include "MagickCore/morphology.h"
00069 #include "MagickCore/morphology-private.h"
00070 #include "MagickCore/option.h"
00071 #include "MagickCore/pixel-accessor.h"
00072 #include "MagickCore/prepress.h"
00073 #include "MagickCore/quantize.h"
00074 #include "MagickCore/registry.h"
00075 #include "MagickCore/semaphore.h"
00076 #include "MagickCore/splay-tree.h"
00077 #include "MagickCore/statistic.h"
00078 #include "MagickCore/string_.h"
00079 #include "MagickCore/string-private.h"
00080 #include "MagickCore/token.h"
00081 #include "MagickCore/utility.h"
00082 #include "MagickCore/utility-private.h"
00083 
00084 
00085 /*
00086 ** The following test is for special floating point numbers of value NaN (not
00087 ** a number), that may be used within a Kernel Definition.  NaN's are defined
00088 ** as part of the IEEE standard for floating point number representation.
00089 **
00090 ** These are used as a Kernel value to mean that this kernel position is not
00091 ** part of the kernel neighbourhood for convolution or morphology processing,
00092 ** and thus should be ignored.  This allows the use of 'shaped' kernels.
00093 **
00094 ** The special properity that two NaN's are never equal, even if they are from
00095 ** the same variable allow you to test if a value is special NaN value.
00096 **
00097 ** This macro  IsNaN() is thus is only true if the value given is NaN.
00098 */
00099 #define IsNan(a)   ((a)!=(a))
00100 
00101 /*
00102   Other global definitions used by module.
00103 */
00104 static inline double MagickMin(const double x,const double y)
00105 {
00106   return( x < y ? x : y);
00107 }
00108 static inline double MagickMax(const double x,const double y)
00109 {
00110   return( x > y ? x : y);
00111 }
00112 #define Minimize(assign,value) assign=MagickMin(assign,value)
00113 #define Maximize(assign,value) assign=MagickMax(assign,value)
00114 
00115 /* Currently these are only internal to this module */
00116 static void
00117   CalcKernelMetaData(KernelInfo *),
00118   ExpandMirrorKernelInfo(KernelInfo *),
00119   ExpandRotateKernelInfo(KernelInfo *, const double),
00120   RotateKernelInfo(KernelInfo *, double);
00121 
00122 
00123 /* Quick function to find last kernel in a kernel list */
00124 static inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
00125 {
00126   while (kernel->next != (KernelInfo *) NULL)
00127     kernel = kernel->next;
00128   return(kernel);
00129 }
00130 
00131 /*
00132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00133 %                                                                             %
00134 %                                                                             %
00135 %                                                                             %
00136 %     A c q u i r e K e r n e l I n f o                                       %
00137 %                                                                             %
00138 %                                                                             %
00139 %                                                                             %
00140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00141 %
00142 %  AcquireKernelInfo() takes the given string (generally supplied by the
00143 %  user) and converts it into a Morphology/Convolution Kernel.  This allows
00144 %  users to specify a kernel from a number of pre-defined kernels, or to fully
00145 %  specify their own kernel for a specific Convolution or Morphology
00146 %  Operation.
00147 %
00148 %  The kernel so generated can be any rectangular array of floating point
00149 %  values (doubles) with the 'control point' or 'pixel being affected'
00150 %  anywhere within that array of values.
00151 %
00152 %  Previously IM was restricted to a square of odd size using the exact
00153 %  center as origin, this is no longer the case, and any rectangular kernel
00154 %  with any value being declared the origin. This in turn allows the use of
00155 %  highly asymmetrical kernels.
00156 %
00157 %  The floating point values in the kernel can also include a special value
00158 %  known as 'nan' or 'not a number' to indicate that this value is not part
00159 %  of the kernel array. This allows you to shaped the kernel within its
00160 %  rectangular area. That is 'nan' values provide a 'mask' for the kernel
00161 %  shape.  However at least one non-nan value must be provided for correct
00162 %  working of a kernel.
00163 %
00164 %  The returned kernel should be freed using the DestroyKernelInfo() when you
00165 %  are finished with it.  Do not free this memory yourself.
00166 %
00167 %  Input kernel defintion strings can consist of any of three types.
00168 %
00169 %    "name:args[[@><]"
00170 %         Select from one of the built in kernels, using the name and
00171 %         geometry arguments supplied.  See AcquireKernelBuiltIn()
00172 %
00173 %    "WxH[+X+Y][@><]:num, num, num ..."
00174 %         a kernel of size W by H, with W*H floating point numbers following.
00175 %         the 'center' can be optionally be defined at +X+Y (such that +0+0
00176 %         is top left corner). If not defined the pixel in the center, for
00177 %         odd sizes, or to the immediate top or left of center for even sizes
00178 %         is automatically selected.
00179 %
00180 %    "num, num, num, num, ..."
00181 %         list of floating point numbers defining an 'old style' odd sized
00182 %         square kernel.  At least 9 values should be provided for a 3x3
00183 %         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
00184 %         Values can be space or comma separated.  This is not recommended.
00185 %
00186 %  You can define a 'list of kernels' which can be used by some morphology
00187 %  operators A list is defined as a semi-colon separated list kernels.
00188 %
00189 %     " kernel ; kernel ; kernel ; "
00190 %
00191 %  Any extra ';' characters, at start, end or between kernel defintions are
00192 %  simply ignored.
00193 %
00194 %  The special flags will expand a single kernel, into a list of rotated
00195 %  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
00196 %  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
00197 %  The '<' also exands using 90-degree rotates, but giving a 180-degree
00198 %  reflected kernel before the +/- 90-degree rotations, which can be important
00199 %  for Thinning operations.
00200 %
00201 %  Note that 'name' kernels will start with an alphabetic character while the
00202 %  new kernel specification has a ':' character in its specification string.
00203 %  If neither is the case, it is assumed an old style of a simple list of
00204 %  numbers generating a odd-sized square kernel has been given.
00205 %
00206 %  The format of the AcquireKernal method is:
00207 %
00208 %      KernelInfo *AcquireKernelInfo(const char *kernel_string)
00209 %
00210 %  A description of each parameter follows:
00211 %
00212 %    o kernel_string: the Morphology/Convolution kernel wanted.
00213 %
00214 */
00215 
00216 /* This was separated so that it could be used as a separate
00217 ** array input handling function, such as for -color-matrix
00218 */
00219 static KernelInfo *ParseKernelArray(const char *kernel_string)
00220 {
00221   KernelInfo
00222     *kernel;
00223 
00224   char
00225     token[MaxTextExtent];
00226 
00227   const char
00228     *p,
00229     *end;
00230 
00231   register ssize_t
00232     i;
00233 
00234   double
00235     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
00236 
00237   MagickStatusType
00238     flags;
00239 
00240   GeometryInfo
00241     args;
00242 
00243   kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
00244   if (kernel == (KernelInfo *)NULL)
00245     return(kernel);
00246   (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
00247   kernel->minimum = kernel->maximum = kernel->angle = 0.0;
00248   kernel->negative_range = kernel->positive_range = 0.0;
00249   kernel->type = UserDefinedKernel;
00250   kernel->next = (KernelInfo *) NULL;
00251   kernel->signature = MagickSignature;
00252   if (kernel_string == (const char *) NULL)
00253     return(kernel);
00254 
00255   /* find end of this specific kernel definition string */
00256   end = strchr(kernel_string, ';');
00257   if ( end == (char *) NULL )
00258     end = strchr(kernel_string, '\0');
00259 
00260   /* clear flags - for Expanding kernel lists thorugh rotations */
00261    flags = NoValue;
00262 
00263   /* Has a ':' in argument - New user kernel specification */
00264   p = strchr(kernel_string, ':');
00265   if ( p != (char *) NULL && p < end)
00266     {
00267       /* ParseGeometry() needs the geometry separated! -- Arrgghh */
00268       memcpy(token, kernel_string, (size_t) (p-kernel_string));
00269       token[p-kernel_string] = '\0';
00270       SetGeometryInfo(&args);
00271       flags = ParseGeometry(token, &args);
00272 
00273       /* Size handling and checks of geometry settings */
00274       if ( (flags & WidthValue) == 0 ) /* if no width then */
00275         args.rho = args.sigma;         /* then  width = height */
00276       if ( args.rho < 1.0 )            /* if width too small */
00277          args.rho = 1.0;               /* then  width = 1 */
00278       if ( args.sigma < 1.0 )          /* if height too small */
00279         args.sigma = args.rho;         /* then  height = width */
00280       kernel->width = (size_t)args.rho;
00281       kernel->height = (size_t)args.sigma;
00282 
00283       /* Offset Handling and Checks */
00284       if ( args.xi  < 0.0 || args.psi < 0.0 )
00285         return(DestroyKernelInfo(kernel));
00286       kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
00287                                                : (ssize_t) (kernel->width-1)/2;
00288       kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
00289                                                : (ssize_t) (kernel->height-1)/2;
00290       if ( kernel->x >= (ssize_t) kernel->width ||
00291            kernel->y >= (ssize_t) kernel->height )
00292         return(DestroyKernelInfo(kernel));
00293 
00294       p++; /* advance beyond the ':' */
00295     }
00296   else
00297     { /* ELSE - Old old specification, forming odd-square kernel */
00298       /* count up number of values given */
00299       p=(const char *) kernel_string;
00300       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
00301         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
00302       for (i=0; p < end; i++)
00303       {
00304         GetMagickToken(p,&p,token);
00305         if (*token == ',')
00306           GetMagickToken(p,&p,token);
00307       }
00308       /* set the size of the kernel - old sized square */
00309       kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
00310       kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
00311       p=(const char *) kernel_string;
00312       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
00313         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
00314     }
00315 
00316   /* Read in the kernel values from rest of input string argument */
00317   kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
00318     kernel->height*sizeof(*kernel->values));
00319   if (kernel->values == (MagickRealType *) NULL)
00320     return(DestroyKernelInfo(kernel));
00321   kernel->minimum = +MagickHuge;
00322   kernel->maximum = -MagickHuge;
00323   kernel->negative_range = kernel->positive_range = 0.0;
00324   for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
00325   {
00326     GetMagickToken(p,&p,token);
00327     if (*token == ',')
00328       GetMagickToken(p,&p,token);
00329     if (    LocaleCompare("nan",token) == 0
00330         || LocaleCompare("-",token) == 0 ) {
00331       kernel->values[i] = nan; /* do not include this value in kernel */
00332     }
00333     else {
00334       kernel->values[i]=StringToDouble(token,(char **) NULL);
00335       ( kernel->values[i] < 0)
00336           ?  ( kernel->negative_range += kernel->values[i] )
00337           :  ( kernel->positive_range += kernel->values[i] );
00338       Minimize(kernel->minimum, kernel->values[i]);
00339       Maximize(kernel->maximum, kernel->values[i]);
00340     }
00341   }
00342 
00343   /* sanity check -- no more values in kernel definition */
00344   GetMagickToken(p,&p,token);
00345   if ( *token != '\0' && *token != ';' && *token != '\'' )
00346     return(DestroyKernelInfo(kernel));
00347 
00348 #if 0
00349   /* this was the old method of handling a incomplete kernel */
00350   if ( i < (ssize_t) (kernel->width*kernel->height) ) {
00351     Minimize(kernel->minimum, kernel->values[i]);
00352     Maximize(kernel->maximum, kernel->values[i]);
00353     for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
00354       kernel->values[i]=0.0;
00355   }
00356 #else
00357   /* Number of values for kernel was not enough - Report Error */
00358   if ( i < (ssize_t) (kernel->width*kernel->height) )
00359     return(DestroyKernelInfo(kernel));
00360 #endif
00361 
00362   /* check that we recieved at least one real (non-nan) value! */
00363   if ( kernel->minimum == MagickHuge )
00364     return(DestroyKernelInfo(kernel));
00365 
00366   if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
00367     ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
00368   else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
00369     ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
00370   else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
00371     ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
00372 
00373   return(kernel);
00374 }
00375 
00376 static KernelInfo *ParseKernelName(const char *kernel_string)
00377 {
00378   char
00379     token[MaxTextExtent];
00380 
00381   const char
00382     *p,
00383     *end;
00384 
00385   GeometryInfo
00386     args;
00387 
00388   KernelInfo
00389     *kernel;
00390 
00391   MagickStatusType
00392     flags;
00393 
00394   ssize_t
00395     type;
00396 
00397   /* Parse special 'named' kernel */
00398   GetMagickToken(kernel_string,&p,token);
00399   type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
00400   if ( type < 0 || type == UserDefinedKernel )
00401     return((KernelInfo *)NULL);  /* not a valid named kernel */
00402 
00403   while (((isspace((int) ((unsigned char) *p)) != 0) ||
00404           (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
00405     p++;
00406 
00407   end = strchr(p, ';'); /* end of this kernel defintion */
00408   if ( end == (char *) NULL )
00409     end = strchr(p, '\0');
00410 
00411   /* ParseGeometry() needs the geometry separated! -- Arrgghh */
00412   memcpy(token, p, (size_t) (end-p));
00413   token[end-p] = '\0';
00414   SetGeometryInfo(&args);
00415   flags = ParseGeometry(token, &args);
00416 
00417 #if 0
00418   /* For Debugging Geometry Input */
00419   (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
00420     flags, args.rho, args.sigma, args.xi, args.psi );
00421 #endif
00422 
00423   /* special handling of missing values in input string */
00424   switch( type ) {
00425     /* Shape Kernel Defaults */
00426     case UnityKernel:
00427       if ( (flags & WidthValue) == 0 )
00428         args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
00429       break;
00430     case SquareKernel:
00431     case DiamondKernel:
00432     case OctagonKernel:
00433     case DiskKernel:
00434     case PlusKernel:
00435     case CrossKernel:
00436       if ( (flags & HeightValue) == 0 )
00437         args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
00438       break;
00439     case RingKernel:
00440       if ( (flags & XValue) == 0 )
00441         args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
00442       break;
00443     case RectangleKernel:    /* Rectangle - set size defaults */
00444       if ( (flags & WidthValue) == 0 ) /* if no width then */
00445         args.rho = args.sigma;         /* then  width = height */
00446       if ( args.rho < 1.0 )            /* if width too small */
00447           args.rho = 3;                /* then  width = 3 */
00448       if ( args.sigma < 1.0 )          /* if height too small */
00449         args.sigma = args.rho;         /* then  height = width */
00450       if ( (flags & XValue) == 0 )     /* center offset if not defined */
00451         args.xi = (double)(((ssize_t)args.rho-1)/2);
00452       if ( (flags & YValue) == 0 )
00453         args.psi = (double)(((ssize_t)args.sigma-1)/2);
00454       break;
00455     /* Distance Kernel Defaults */
00456     case ChebyshevKernel:
00457     case ManhattanKernel:
00458     case OctagonalKernel:
00459     case EuclideanKernel:
00460       if ( (flags & HeightValue) == 0 )           /* no distance scale */
00461         args.sigma = 100.0;                       /* default distance scaling */
00462       else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
00463         args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
00464       else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
00465         args.sigma *= QuantumRange/100.0;         /* percentage of color range */
00466       break;
00467     default:
00468       break;
00469   }
00470 
00471   kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
00472   if ( kernel == (KernelInfo *) NULL )
00473     return(kernel);
00474 
00475   /* global expand to rotated kernel list - only for single kernels */
00476   if ( kernel->next == (KernelInfo *) NULL ) {
00477     if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
00478       ExpandRotateKernelInfo(kernel, 45.0);
00479     else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
00480       ExpandRotateKernelInfo(kernel, 90.0);
00481     else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
00482       ExpandMirrorKernelInfo(kernel);
00483   }
00484 
00485   return(kernel);
00486 }
00487 
00488 MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
00489 {
00490 
00491   KernelInfo
00492     *kernel,
00493     *new_kernel;
00494 
00495   char
00496     token[MaxTextExtent];
00497 
00498   const char
00499     *p;
00500 
00501   size_t
00502     kernel_number;
00503 
00504   if (kernel_string == (const char *) NULL)
00505     return(ParseKernelArray(kernel_string));
00506   p = kernel_string;
00507   kernel = NULL;
00508   kernel_number = 0;
00509 
00510   while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
00511 
00512     /* ignore extra or multiple ';' kernel separators */
00513     if ( *token != ';' ) {
00514 
00515       /* tokens starting with alpha is a Named kernel */
00516       if (isalpha((int) *token) != 0)
00517         new_kernel = ParseKernelName(p);
00518       else /* otherwise a user defined kernel array */
00519         new_kernel = ParseKernelArray(p);
00520 
00521       /* Error handling -- this is not proper error handling! */
00522       if ( new_kernel == (KernelInfo *) NULL ) {
00523         (void) FormatLocaleFile(stderr, "Failed to parse kernel number #%.20g\n",
00524           (double) kernel_number);
00525         if ( kernel != (KernelInfo *) NULL )
00526           kernel=DestroyKernelInfo(kernel);
00527         return((KernelInfo *) NULL);
00528       }
00529 
00530       /* initialise or append the kernel list */
00531       if ( kernel == (KernelInfo *) NULL )
00532         kernel = new_kernel;
00533       else
00534         LastKernelInfo(kernel)->next = new_kernel;
00535     }
00536 
00537     /* look for the next kernel in list */
00538     p = strchr(p, ';');
00539     if ( p == (char *) NULL )
00540       break;
00541     p++;
00542 
00543   }
00544   return(kernel);
00545 }
00546 
00547 
00548 /*
00549 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00550 %                                                                             %
00551 %                                                                             %
00552 %                                                                             %
00553 %     A c q u i r e K e r n e l B u i l t I n                                 %
00554 %                                                                             %
00555 %                                                                             %
00556 %                                                                             %
00557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00558 %
00559 %  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
00560 %  kernels used for special purposes such as gaussian blurring, skeleton
00561 %  pruning, and edge distance determination.
00562 %
00563 %  They take a KernelType, and a set of geometry style arguments, which were
00564 %  typically decoded from a user supplied string, or from a more complex
00565 %  Morphology Method that was requested.
00566 %
00567 %  The format of the AcquireKernalBuiltIn method is:
00568 %
00569 %      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
00570 %           const GeometryInfo args)
00571 %
00572 %  A description of each parameter follows:
00573 %
00574 %    o type: the pre-defined type of kernel wanted
00575 %
00576 %    o args: arguments defining or modifying the kernel
00577 %
00578 %  Convolution Kernels
00579 %
00580 %    Unity
00581 %       The a No-Op or Scaling single element kernel.
00582 %
00583 %    Gaussian:{radius},{sigma}
00584 %       Generate a two-dimensional gaussian kernel, as used by -gaussian.
00585 %       The sigma for the curve is required.  The resulting kernel is
00586 %       normalized,
00587 %
00588 %       If 'sigma' is zero, you get a single pixel on a field of zeros.
00589 %
00590 %       NOTE: that the 'radius' is optional, but if provided can limit (clip)
00591 %       the final size of the resulting kernel to a square 2*radius+1 in size.
00592 %       The radius should be at least 2 times that of the sigma value, or
00593 %       sever clipping and aliasing may result.  If not given or set to 0 the
00594 %       radius will be determined so as to produce the best minimal error
00595 %       result, which is usally much larger than is normally needed.
00596 %
00597 %    LoG:{radius},{sigma}
00598 %        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
00599 %        The supposed ideal edge detection, zero-summing kernel.
00600 %
00601 %        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
00602 %        approx 1.6 (according to wikipedia).
00603 %
00604 %    DoG:{radius},{sigma1},{sigma2}
00605 %        "Difference of Gaussians" Kernel.
00606 %        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
00607 %        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
00608 %        The result is a zero-summing kernel.
00609 %
00610 %    Blur:{radius},{sigma}[,{angle}]
00611 %       Generates a 1 dimensional or linear gaussian blur, at the angle given
00612 %       (current restricted to orthogonal angles).  If a 'radius' is given the
00613 %       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
00614 %       by a 90 degree angle.
00615 %
00616 %       If 'sigma' is zero, you get a single pixel on a field of zeros.
00617 %
00618 %       Note that two convolutions with two "Blur" kernels perpendicular to
00619 %       each other, is equivalent to a far larger "Gaussian" kernel with the
00620 %       same sigma value, However it is much faster to apply. This is how the
00621 %       "-blur" operator actually works.
00622 %
00623 %    Comet:{width},{sigma},{angle}
00624 %       Blur in one direction only, much like how a bright object leaves
00625 %       a comet like trail.  The Kernel is actually half a gaussian curve,
00626 %       Adding two such blurs in opposite directions produces a Blur Kernel.
00627 %       Angle can be rotated in multiples of 90 degrees.
00628 %
00629 %       Note that the first argument is the width of the kernel and not the
00630 %       radius of the kernel.
00631 %
00632 %    # Still to be implemented...
00633 %    #
00634 %    # Filter2D
00635 %    # Filter1D
00636 %    #    Set kernel values using a resize filter, and given scale (sigma)
00637 %    #    Cylindrical or Linear.   Is this possible with an image?
00638 %    #
00639 %
00640 %  Named Constant Convolution Kernels
00641 %
00642 %  All these are unscaled, zero-summing kernels by default. As such for
00643 %  non-HDRI version of ImageMagick some form of normalization, user scaling,
00644 %  and biasing the results is recommended, to prevent the resulting image
00645 %  being 'clipped'.
00646 %
00647 %  The 3x3 kernels (most of these) can be circularly rotated in multiples of
00648 %  45 degrees to generate the 8 angled varients of each of the kernels.
00649 %
00650 %    Laplacian:{type}
00651 %      Discrete Lapacian Kernels, (without normalization)
00652 %        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
00653 %        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
00654 %        Type 2 :  3x3 with center:4 edge:1 corner:-2
00655 %        Type 3 :  3x3 with center:4 edge:-2 corner:1
00656 %        Type 5 :  5x5 laplacian
00657 %        Type 7 :  7x7 laplacian
00658 %        Type 15 : 5x5 LoG (sigma approx 1.4)
00659 %        Type 19 : 9x9 LoG (sigma approx 1.4)
00660 %
00661 %    Sobel:{angle}
00662 %      Sobel 'Edge' convolution kernel (3x3)
00663 %          | -1, 0, 1 |
00664 %          | -2, 0,-2 |
00665 %          | -1, 0, 1 |
00666 %
00667 %    Roberts:{angle}
00668 %      Roberts convolution kernel (3x3)
00669 %          |  0, 0, 0 |
00670 %          | -1, 1, 0 |
00671 %          |  0, 0, 0 |
00672 %
00673 %    Prewitt:{angle}
00674 %      Prewitt Edge convolution kernel (3x3)
00675 %          | -1, 0, 1 |
00676 %          | -1, 0, 1 |
00677 %          | -1, 0, 1 |
00678 %
00679 %    Compass:{angle}
00680 %      Prewitt's "Compass" convolution kernel (3x3)
00681 %          | -1, 1, 1 |
00682 %          | -1,-2, 1 |
00683 %          | -1, 1, 1 |
00684 %
00685 %    Kirsch:{angle}
00686 %      Kirsch's "Compass" convolution kernel (3x3)
00687 %          | -3,-3, 5 |
00688 %          | -3, 0, 5 |
00689 %          | -3,-3, 5 |
00690 %
00691 %    FreiChen:{angle}
00692 %      Frei-Chen Edge Detector is based on a kernel that is similar to
00693 %      the Sobel Kernel, but is designed to be isotropic. That is it takes
00694 %      into account the distance of the diagonal in the kernel.
00695 %
00696 %          |   1,     0,   -1     |
00697 %          | sqrt(2), 0, -sqrt(2) |
00698 %          |   1,     0,   -1     |
00699 %
00700 %    FreiChen:{type},{angle}
00701 %
00702 %      Frei-Chen Pre-weighted kernels...
00703 %
00704 %        Type 0:  default un-nomalized version shown above.
00705 %
00706 %        Type 1: Orthogonal Kernel (same as type 11 below)
00707 %          |   1,     0,   -1     |
00708 %          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
00709 %          |   1,     0,   -1     |
00710 %
00711 %        Type 2: Diagonal form of Kernel...
00712 %          |   1,     sqrt(2),    0     |
00713 %          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
00714 %          |   0,    -sqrt(2)    -1     |
00715 %
00716 %      However this kernel is als at the heart of the FreiChen Edge Detection
00717 %      Process which uses a set of 9 specially weighted kernel.  These 9
00718 %      kernels not be normalized, but directly applied to the image. The
00719 %      results is then added together, to produce the intensity of an edge in
00720 %      a specific direction.  The square root of the pixel value can then be
00721 %      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
00722 %      from each other, both the direction and the strength of the edge can be
00723 %      determined.
00724 %
00725 %        Type 10: All 9 of the following pre-weighted kernels...
00726 %
00727 %        Type 11: |   1,     0,   -1     |
00728 %                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
00729 %                 |   1,     0,   -1     |
00730 %
00731 %        Type 12: | 1, sqrt(2), 1 |
00732 %                 | 0,   0,     0 | / 2*sqrt(2)
00733 %                 | 1, sqrt(2), 1 |
00734 %
00735 %        Type 13: | sqrt(2), -1,    0     |
00736 %                 |  -1,      0,    1     | / 2*sqrt(2)
00737 %                 |   0,      1, -sqrt(2) |
00738 %
00739 %        Type 14: |    0,     1, -sqrt(2) |
00740 %                 |   -1,     0,     1    | / 2*sqrt(2)
00741 %                 | sqrt(2), -1,     0    |
00742 %
00743 %        Type 15: | 0, -1, 0 |
00744 %                 | 1,  0, 1 | / 2
00745 %                 | 0, -1, 0 |
00746 %
00747 %        Type 16: |  1, 0, -1 |
00748 %                 |  0, 0,  0 | / 2
00749 %                 | -1, 0,  1 |
00750 %
00751 %        Type 17: |  1, -2,  1 |
00752 %                 | -2,  4, -2 | / 6
00753 %                 | -1, -2,  1 |
00754 %
00755 %        Type 18: | -2, 1, -2 |
00756 %                 |  1, 4,  1 | / 6
00757 %                 | -2, 1, -2 |
00758 %
00759 %        Type 19: | 1, 1, 1 |
00760 %                 | 1, 1, 1 | / 3
00761 %                 | 1, 1, 1 |
00762 %
00763 %      The first 4 are for edge detection, the next 4 are for line detection
00764 %      and the last is to add a average component to the results.
00765 %
00766 %      Using a special type of '-1' will return all 9 pre-weighted kernels
00767 %      as a multi-kernel list, so that you can use them directly (without
00768 %      normalization) with the special "-set option:morphology:compose Plus"
00769 %      setting to apply the full FreiChen Edge Detection Technique.
00770 %
00771 %      If 'type' is large it will be taken to be an actual rotation angle for
00772 %      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
00773 %      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
00774 %
00775 %      WARNING: The above was layed out as per
00776 %          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
00777 %      But rotated 90 degrees so direction is from left rather than the top.
00778 %      I have yet to find any secondary confirmation of the above. The only
00779 %      other source found was actual source code at
00780 %          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
00781 %      Neigher paper defineds the kernels in a way that looks locical or
00782 %      correct when taken as a whole.
00783 %
00784 %  Boolean Kernels
00785 %
00786 %    Diamond:[{radius}[,{scale}]]
00787 %       Generate a diamond shaped kernel with given radius to the points.
00788 %       Kernel size will again be radius*2+1 square and defaults to radius 1,
00789 %       generating a 3x3 kernel that is slightly larger than a square.
00790 %
00791 %    Square:[{radius}[,{scale}]]
00792 %       Generate a square shaped kernel of size radius*2+1, and defaulting
00793 %       to a 3x3 (radius 1).
00794 %
00795 %    Octagon:[{radius}[,{scale}]]
00796 %       Generate octagonal shaped kernel of given radius and constant scale.
00797 %       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
00798 %       in "Diamond" kernel.
00799 %
00800 %    Disk:[{radius}[,{scale}]]
00801 %       Generate a binary disk, thresholded at the radius given, the radius
00802 %       may be a float-point value. Final Kernel size is floor(radius)*2+1
00803 %       square. A radius of 5.3 is the default.
00804 %
00805 %       NOTE: That a low radii Disk kernels produce the same results as
00806 %       many of the previously defined kernels, but differ greatly at larger
00807 %       radii.  Here is a table of equivalences...
00808 %          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
00809 %          "Disk:1.5"  => "Square"
00810 %          "Disk:2"    => "Diamond:2"
00811 %          "Disk:2.5"  => "Octagon"
00812 %          "Disk:2.9"  => "Square:2"
00813 %          "Disk:3.5"  => "Octagon:3"
00814 %          "Disk:4.5"  => "Octagon:4"
00815 %          "Disk:5.4"  => "Octagon:5"
00816 %          "Disk:6.4"  => "Octagon:6"
00817 %       All other Disk shapes are unique to this kernel, but because a "Disk"
00818 %       is more circular when using a larger radius, using a larger radius is
00819 %       preferred over iterating the morphological operation.
00820 %
00821 %    Rectangle:{geometry}
00822 %       Simply generate a rectangle of 1's with the size given. You can also
00823 %       specify the location of the 'control point', otherwise the closest
00824 %       pixel to the center of the rectangle is selected.
00825 %
00826 %       Properly centered and odd sized rectangles work the best.
00827 %
00828 %  Symbol Dilation Kernels
00829 %
00830 %    These kernel is not a good general morphological kernel, but is used
00831 %    more for highlighting and marking any single pixels in an image using,
00832 %    a "Dilate" method as appropriate.
00833 %
00834 %    For the same reasons iterating these kernels does not produce the
00835 %    same result as using a larger radius for the symbol.
00836 %
00837 %    Plus:[{radius}[,{scale}]]
00838 %    Cross:[{radius}[,{scale}]]
00839 %       Generate a kernel in the shape of a 'plus' or a 'cross' with
00840 %       a each arm the length of the given radius (default 2).
00841 %
00842 %       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
00843 %
00844 %    Ring:{radius1},{radius2}[,{scale}]
00845 %       A ring of the values given that falls between the two radii.
00846 %       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
00847 %       This is the 'edge' pixels of the default "Disk" kernel,
00848 %       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
00849 %
00850 %  Hit and Miss Kernels
00851 %
00852 %    Peak:radius1,radius2
00853 %       Find any peak larger than the pixels the fall between the two radii.
00854 %       The default ring of pixels is as per "Ring".
00855 %    Edges
00856 %       Find flat orthogonal edges of a binary shape
00857 %    Corners
00858 %       Find 90 degree corners of a binary shape
00859 %    Diagonals:type
00860 %       A special kernel to thin the 'outside' of diagonals
00861 %    LineEnds:type
00862 %       Find end points of lines (for pruning a skeletion)
00863 %       Two types of lines ends (default to both) can be searched for
00864 %         Type 0: All line ends
00865 %         Type 1: single kernel for 4-conneected line ends
00866 %         Type 2: single kernel for simple line ends
00867 %    LineJunctions
00868 %       Find three line junctions (within a skeletion)
00869 %         Type 0: all line junctions
00870 %         Type 1: Y Junction kernel
00871 %         Type 2: Diagonal T Junction kernel
00872 %         Type 3: Orthogonal T Junction kernel
00873 %         Type 4: Diagonal X Junction kernel
00874 %         Type 5: Orthogonal + Junction kernel
00875 %    Ridges:type
00876 %       Find single pixel ridges or thin lines
00877 %         Type 1: Fine single pixel thick lines and ridges
00878 %         Type 2: Find two pixel thick lines and ridges
00879 %    ConvexHull
00880 %       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
00881 %    Skeleton:type
00882 %       Traditional skeleton generating kernels.
00883 %         Type 1: Tradional Skeleton kernel (4 connected skeleton)
00884 %         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
00885 %         Type 3: Thinning skeleton based on a ressearch paper by
00886 %                 Dan S. Bloomberg (Default Type)
00887 %    ThinSE:type
00888 %       A huge variety of Thinning Kernels designed to preserve conectivity.
00889 %       many other kernel sets use these kernels as source definitions.
00890 %       Type numbers are 41-49, 81-89, 481, and 482 which are based on
00891 %       the super and sub notations used in the source research paper.
00892 %
00893 %  Distance Measuring Kernels
00894 %
00895 %    Different types of distance measuring methods, which are used with the
00896 %    a 'Distance' morphology method for generating a gradient based on
00897 %    distance from an edge of a binary shape, though there is a technique
00898 %    for handling a anti-aliased shape.
00899 %
00900 %    See the 'Distance' Morphological Method, for information of how it is
00901 %    applied.
00902 %
00903 %    Chebyshev:[{radius}][x{scale}[%!]]
00904 %       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
00905 %       is a value of one to any neighbour, orthogonal or diagonal. One why
00906 %       of thinking of it is the number of squares a 'King' or 'Queen' in
00907 %       chess needs to traverse reach any other position on a chess board.
00908 %       It results in a 'square' like distance function, but one where
00909 %       diagonals are given a value that is closer than expected.
00910 %
00911 %    Manhattan:[{radius}][x{scale}[%!]]
00912 %       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
00913 %       Cab distance metric), it is the distance needed when you can only
00914 %       travel in horizontal or vertical directions only.  It is the
00915 %       distance a 'Rook' in chess would have to travel, and results in a
00916 %       diamond like distances, where diagonals are further than expected.
00917 %
00918 %    Octagonal:[{radius}][x{scale}[%!]]
00919 %       An interleving of Manhatten and Chebyshev metrics producing an
00920 %       increasing octagonally shaped distance.  Distances matches those of
00921 %       the "Octagon" shaped kernel of the same radius.  The minimum radius
00922 %       and default is 2, producing a 5x5 kernel.
00923 %
00924 %    Euclidean:[{radius}][x{scale}[%!]]
00925 %       Euclidean distance is the 'direct' or 'as the crow flys' distance.
00926 %       However by default the kernel size only has a radius of 1, which
00927 %       limits the distance to 'Knight' like moves, with only orthogonal and
00928 %       diagonal measurements being correct.  As such for the default kernel
00929 %       you will get octagonal like distance function.
00930 %
00931 %       However using a larger radius such as "Euclidean:4" you will get a
00932 %       much smoother distance gradient from the edge of the shape. Especially
00933 %       if the image is pre-processed to include any anti-aliasing pixels.
00934 %       Of course a larger kernel is slower to use, and not always needed.
00935 %
00936 %    The first three Distance Measuring Kernels will only generate distances
00937 %    of exact multiples of {scale} in binary images. As such you can use a
00938 %    scale of 1 without loosing any information.  However you also need some
00939 %    scaling when handling non-binary anti-aliased shapes.
00940 %
00941 %    The "Euclidean" Distance Kernel however does generate a non-integer
00942 %    fractional results, and as such scaling is vital even for binary shapes.
00943 %
00944 */
00945 
00946 MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
00947    const GeometryInfo *args)
00948 {
00949   KernelInfo
00950     *kernel;
00951 
00952   register ssize_t
00953     i;
00954 
00955   register ssize_t
00956     u,
00957     v;
00958 
00959   double
00960     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
00961 
00962   /* Generate a new empty kernel if needed */
00963   kernel=(KernelInfo *) NULL;
00964   switch(type) {
00965     case UndefinedKernel:    /* These should not call this function */
00966     case UserDefinedKernel:
00967       assert("Should not call this function" != (char *)NULL);
00968       break;
00969     case LaplacianKernel:   /* Named Descrete Convolution Kernels */
00970     case SobelKernel:       /* these are defined using other kernels */
00971     case RobertsKernel:
00972     case PrewittKernel:
00973     case CompassKernel:
00974     case KirschKernel:
00975     case FreiChenKernel:
00976     case EdgesKernel:       /* Hit and Miss kernels */
00977     case CornersKernel:
00978     case DiagonalsKernel:
00979     case LineEndsKernel:
00980     case LineJunctionsKernel:
00981     case RidgesKernel:
00982     case ConvexHullKernel:
00983     case SkeletonKernel:
00984     case ThinSEKernel:
00985       break;               /* A pre-generated kernel is not needed */
00986 #if 0
00987     /* set to 1 to do a compile-time check that we haven't missed anything */
00988     case UnityKernel:
00989     case GaussianKernel:
00990     case DoGKernel:
00991     case LoGKernel:
00992     case BlurKernel:
00993     case CometKernel:
00994     case DiamondKernel:
00995     case SquareKernel:
00996     case RectangleKernel:
00997     case OctagonKernel:
00998     case DiskKernel:
00999     case PlusKernel:
01000     case CrossKernel:
01001     case RingKernel:
01002     case PeaksKernel:
01003     case ChebyshevKernel:
01004     case ManhattanKernel:
01005     case OctangonalKernel:
01006     case EuclideanKernel:
01007 #else
01008     default:
01009 #endif
01010       /* Generate the base Kernel Structure */
01011       kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
01012       if (kernel == (KernelInfo *) NULL)
01013         return(kernel);
01014       (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
01015       kernel->minimum = kernel->maximum = kernel->angle = 0.0;
01016       kernel->negative_range = kernel->positive_range = 0.0;
01017       kernel->type = type;
01018       kernel->next = (KernelInfo *) NULL;
01019       kernel->signature = MagickSignature;
01020       break;
01021   }
01022 
01023   switch(type) {
01024     /*
01025       Convolution Kernels
01026     */
01027     case UnityKernel:
01028       {
01029         kernel->height = kernel->width = (size_t) 1;
01030         kernel->x = kernel->y = (ssize_t) 0;
01031         kernel->values=(MagickRealType *) AcquireAlignedMemory(1,
01032           sizeof(*kernel->values));
01033         if (kernel->values == (MagickRealType *) NULL)
01034           return(DestroyKernelInfo(kernel));
01035         kernel->maximum = kernel->values[0] = args->rho;
01036         break;
01037       }
01038       break;
01039     case GaussianKernel:
01040     case DoGKernel:
01041     case LoGKernel:
01042       { double
01043           sigma = fabs(args->sigma),
01044           sigma2 = fabs(args->xi),
01045           A, B, R;
01046 
01047         if ( args->rho >= 1.0 )
01048           kernel->width = (size_t)args->rho*2+1;
01049         else if ( (type != DoGKernel) || (sigma >= sigma2) )
01050           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
01051         else
01052           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
01053         kernel->height = kernel->width;
01054         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
01055         kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01056           kernel->height*sizeof(*kernel->values));
01057         if (kernel->values == (MagickRealType *) NULL)
01058           return(DestroyKernelInfo(kernel));
01059 
01060         /* WARNING: The following generates a 'sampled gaussian' kernel.
01061          * What we really want is a 'discrete gaussian' kernel.
01062          *
01063          * How to do this is I don't know, but appears to be basied on the
01064          * Error Function 'erf()' (intergral of a gaussian)
01065          */
01066 
01067         if ( type == GaussianKernel || type == DoGKernel )
01068           { /* Calculate a Gaussian,  OR positive half of a DoG */
01069             if ( sigma > MagickEpsilon )
01070               { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
01071                 B = (double) (1.0/(Magick2PI*sigma*sigma));
01072                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
01073                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01074                       kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
01075               }
01076             else /* limiting case - a unity (normalized Dirac) kernel */
01077               { (void) ResetMagickMemory(kernel->values,0, (size_t)
01078                             kernel->width*kernel->height*sizeof(double));
01079                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
01080               }
01081           }
01082 
01083         if ( type == DoGKernel )
01084           { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
01085             if ( sigma2 > MagickEpsilon )
01086               { sigma = sigma2;                /* simplify loop expressions */
01087                 A = 1.0/(2.0*sigma*sigma);
01088                 B = (double) (1.0/(Magick2PI*sigma*sigma));
01089                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
01090                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01091                     kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
01092               }
01093             else /* limiting case - a unity (normalized Dirac) kernel */
01094               kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
01095           }
01096 
01097         if ( type == LoGKernel )
01098           { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
01099             if ( sigma > MagickEpsilon )
01100               { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
01101                 B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
01102                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
01103                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01104                     { R = ((double)(u*u+v*v))*A;
01105                       kernel->values[i] = (1-R)*exp(-R)*B;
01106                     }
01107               }
01108             else /* special case - generate a unity kernel */
01109               { (void) ResetMagickMemory(kernel->values,0, (size_t)
01110                             kernel->width*kernel->height*sizeof(double));
01111                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
01112               }
01113           }
01114 
01115         /* Note the above kernels may have been 'clipped' by a user defined
01116         ** radius, producing a smaller (darker) kernel.  Also for very small
01117         ** sigma's (> 0.1) the central value becomes larger than one, and thus
01118         ** producing a very bright kernel.
01119         **
01120         ** Normalization will still be needed.
01121         */
01122 
01123         /* Normalize the 2D Gaussian Kernel
01124         **
01125         ** NB: a CorrelateNormalize performs a normal Normalize if
01126         ** there are no negative values.
01127         */
01128         CalcKernelMetaData(kernel);  /* the other kernel meta-data */
01129         ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
01130 
01131         break;
01132       }
01133     case BlurKernel:
01134       { double
01135           sigma = fabs(args->sigma),
01136           alpha, beta;
01137 
01138         if ( args->rho >= 1.0 )
01139           kernel->width = (size_t)args->rho*2+1;
01140         else
01141           kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
01142         kernel->height = 1;
01143         kernel->x = (ssize_t) (kernel->width-1)/2;
01144         kernel->y = 0;
01145         kernel->negative_range = kernel->positive_range = 0.0;
01146         kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01147           kernel->height*sizeof(*kernel->values));
01148         if (kernel->values == (MagickRealType *) NULL)
01149           return(DestroyKernelInfo(kernel));
01150 
01151 #if 1
01152 #define KernelRank 3
01153         /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
01154         ** It generates a gaussian 3 times the width, and compresses it into
01155         ** the expected range.  This produces a closer normalization of the
01156         ** resulting kernel, especially for very low sigma values.
01157         ** As such while wierd it is prefered.
01158         **
01159         ** I am told this method originally came from Photoshop.
01160         **
01161         ** A properly normalized curve is generated (apart from edge clipping)
01162         ** even though we later normalize the result (for edge clipping)
01163         ** to allow the correct generation of a "Difference of Blurs".
01164         */
01165 
01166         /* initialize */
01167         v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
01168         (void) ResetMagickMemory(kernel->values,0, (size_t)
01169                      kernel->width*kernel->height*sizeof(double));
01170         /* Calculate a Positive 1D Gaussian */
01171         if ( sigma > MagickEpsilon )
01172           { sigma *= KernelRank;               /* simplify loop expressions */
01173             alpha = 1.0/(2.0*sigma*sigma);
01174             beta= (double) (1.0/(MagickSQ2PI*sigma ));
01175             for ( u=-v; u <= v; u++) {
01176               kernel->values[(u+v)/KernelRank] +=
01177                               exp(-((double)(u*u))*alpha)*beta;
01178             }
01179           }
01180         else /* special case - generate a unity kernel */
01181           kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
01182 #else
01183         /* Direct calculation without curve averaging */
01184 
01185         /* Calculate a Positive Gaussian */
01186         if ( sigma > MagickEpsilon )
01187           { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
01188             beta = 1.0/(MagickSQ2PI*sigma);
01189             for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01190               kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
01191           }
01192         else /* special case - generate a unity kernel */
01193           { (void) ResetMagickMemory(kernel->values,0, (size_t)
01194                          kernel->width*kernel->height*sizeof(double));
01195             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
01196           }
01197 #endif
01198         /* Note the above kernel may have been 'clipped' by a user defined
01199         ** radius, producing a smaller (darker) kernel.  Also for very small
01200         ** sigma's (> 0.1) the central value becomes larger than one, and thus
01201         ** producing a very bright kernel.
01202         **
01203         ** Normalization will still be needed.
01204         */
01205 
01206         /* Normalize the 1D Gaussian Kernel
01207         **
01208         ** NB: a CorrelateNormalize performs a normal Normalize if
01209         ** there are no negative values.
01210         */
01211         CalcKernelMetaData(kernel);  /* the other kernel meta-data */
01212         ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
01213 
01214         /* rotate the 1D kernel by given angle */
01215         RotateKernelInfo(kernel, args->xi );
01216         break;
01217       }
01218     case CometKernel:
01219       { double
01220           sigma = fabs(args->sigma),
01221           A;
01222 
01223         if ( args->rho < 1.0 )
01224           kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
01225         else
01226           kernel->width = (size_t)args->rho;
01227         kernel->x = kernel->y = 0;
01228         kernel->height = 1;
01229         kernel->negative_range = kernel->positive_range = 0.0;
01230         kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01231           kernel->height*sizeof(*kernel->values));
01232         if (kernel->values == (MagickRealType *) NULL)
01233           return(DestroyKernelInfo(kernel));
01234 
01235         /* A comet blur is half a 1D gaussian curve, so that the object is
01236         ** blurred in one direction only.  This may not be quite the right
01237         ** curve to use so may change in the future. The function must be
01238         ** normalised after generation, which also resolves any clipping.
01239         **
01240         ** As we are normalizing and not subtracting gaussians,
01241         ** there is no need for a divisor in the gaussian formula
01242         **
01243         ** It is less comples
01244         */
01245         if ( sigma > MagickEpsilon )
01246           {
01247 #if 1
01248 #define KernelRank 3
01249             v = (ssize_t) kernel->width*KernelRank; /* start/end points */
01250             (void) ResetMagickMemory(kernel->values,0, (size_t)
01251                           kernel->width*sizeof(double));
01252             sigma *= KernelRank;            /* simplify the loop expression */
01253             A = 1.0/(2.0*sigma*sigma);
01254             /* B = 1.0/(MagickSQ2PI*sigma); */
01255             for ( u=0; u < v; u++) {
01256               kernel->values[u/KernelRank] +=
01257                   exp(-((double)(u*u))*A);
01258               /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
01259             }
01260             for (i=0; i < (ssize_t) kernel->width; i++)
01261               kernel->positive_range += kernel->values[i];
01262 #else
01263             A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
01264             /* B = 1.0/(MagickSQ2PI*sigma); */
01265             for ( i=0; i < (ssize_t) kernel->width; i++)
01266               kernel->positive_range +=
01267                 kernel->values[i] =
01268                   exp(-((double)(i*i))*A);
01269                 /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
01270 #endif
01271           }
01272         else /* special case - generate a unity kernel */
01273           { (void) ResetMagickMemory(kernel->values,0, (size_t)
01274                          kernel->width*kernel->height*sizeof(double));
01275             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
01276             kernel->positive_range = 1.0;
01277           }
01278 
01279         kernel->minimum = 0.0;
01280         kernel->maximum = kernel->values[0];
01281         kernel->negative_range = 0.0;
01282 
01283         ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
01284         RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
01285         break;
01286       }
01287 
01288     /*
01289       Convolution Kernels - Well Known Named Constant Kernels
01290     */
01291     case LaplacianKernel:
01292       { switch ( (int) args->rho ) {
01293           case 0:
01294           default: /* laplacian square filter -- default */
01295             kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
01296             break;
01297           case 1:  /* laplacian diamond filter */
01298             kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
01299             break;
01300           case 2:
01301             kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
01302             break;
01303           case 3:
01304             kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
01305             break;
01306           case 5:   /* a 5x5 laplacian */
01307             kernel=ParseKernelArray(
01308               "5: -4,-1,0,-1,-4  -1,2,3,2,-1  0,3,4,3,0  -1,2,3,2,-1  -4,-1,0,-1,-4");
01309             break;
01310           case 7:   /* a 7x7 laplacian */
01311             kernel=ParseKernelArray(
01312               "7:-10,-5,-2,-1,-2,-5,-10 -5,0,3,4,3,0,-5 -2,3,6,7,6,3,-2 -1,4,7,8,7,4,-1 -2,3,6,7,6,3,-2 -5,0,3,4,3,0,-5 -10,-5,-2,-1,-2,-5,-10" );
01313             break;
01314           case 15:  /* a 5x5 LoG (sigma approx 1.4) */
01315             kernel=ParseKernelArray(
01316               "5: 0,0,-1,0,0  0,-1,-2,-1,0  -1,-2,16,-2,-1  0,-1,-2,-1,0  0,0,-1,0,0");
01317             break;
01318           case 19:  /* a 9x9 LoG (sigma approx 1.4) */
01319             /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
01320             kernel=ParseKernelArray(
01321               "9: 0,-1,-1,-2,-2,-2,-1,-1,0  -1,-2,-4,-5,-5,-5,-4,-2,-1  -1,-4,-5,-3,-0,-3,-5,-4,-1  -2,-5,-3,12,24,12,-3,-5,-2  -2,-5,-0,24,40,24,-0,-5,-2  -2,-5,-3,12,24,12,-3,-5,-2  -1,-4,-5,-3,-0,-3,-5,-4,-1  -1,-2,-4,-5,-5,-5,-4,-2,-1  0,-1,-1,-2,-2,-2,-1,-1,0");
01322             break;
01323         }
01324         if (kernel == (KernelInfo *) NULL)
01325           return(kernel);
01326         kernel->type = type;
01327         break;
01328       }
01329     case SobelKernel:
01330       { /* Simple Sobel Kernel */
01331         kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
01332         if (kernel == (KernelInfo *) NULL)
01333           return(kernel);
01334         kernel->type = type;
01335         RotateKernelInfo(kernel, args->rho);
01336         break;
01337       }
01338     case RobertsKernel:
01339       {
01340         kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
01341         if (kernel == (KernelInfo *) NULL)
01342           return(kernel);
01343         kernel->type = type;
01344         RotateKernelInfo(kernel, args->rho);
01345         break;
01346       }
01347     case PrewittKernel:
01348       {
01349         kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
01350         if (kernel == (KernelInfo *) NULL)
01351           return(kernel);
01352         kernel->type = type;
01353         RotateKernelInfo(kernel, args->rho);
01354         break;
01355       }
01356     case CompassKernel:
01357       {
01358         kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
01359         if (kernel == (KernelInfo *) NULL)
01360           return(kernel);
01361         kernel->type = type;
01362         RotateKernelInfo(kernel, args->rho);
01363         break;
01364       }
01365     case KirschKernel:
01366       {
01367         kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
01368         if (kernel == (KernelInfo *) NULL)
01369           return(kernel);
01370         kernel->type = type;
01371         RotateKernelInfo(kernel, args->rho);
01372         break;
01373       }
01374     case FreiChenKernel:
01375       /* Direction is set to be left to right positive */
01376       /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
01377       /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
01378       { switch ( (int) args->rho ) {
01379           default:
01380           case 0:
01381             kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
01382             if (kernel == (KernelInfo *) NULL)
01383               return(kernel);
01384             kernel->type = type;
01385             kernel->values[3] = +MagickSQ2;
01386             kernel->values[5] = -MagickSQ2;
01387             CalcKernelMetaData(kernel);     /* recalculate meta-data */
01388             break;
01389           case 2:
01390             kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
01391             if (kernel == (KernelInfo *) NULL)
01392               return(kernel);
01393             kernel->type = type;
01394             kernel->values[1] = kernel->values[3] = +MagickSQ2;
01395             kernel->values[5] = kernel->values[7] = -MagickSQ2;
01396             CalcKernelMetaData(kernel);     /* recalculate meta-data */
01397             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
01398             break;
01399           case 10:
01400             kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
01401             if (kernel == (KernelInfo *) NULL)
01402               return(kernel);
01403             break;
01404           case 1:
01405           case 11:
01406             kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
01407             if (kernel == (KernelInfo *) NULL)
01408               return(kernel);
01409             kernel->type = type;
01410             kernel->values[3] = +MagickSQ2;
01411             kernel->values[5] = -MagickSQ2;
01412             CalcKernelMetaData(kernel);     /* recalculate meta-data */
01413             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
01414             break;
01415           case 12:
01416             kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
01417             if (kernel == (KernelInfo *) NULL)
01418               return(kernel);
01419             kernel->type = type;
01420             kernel->values[1] = +MagickSQ2;
01421             kernel->values[7] = +MagickSQ2;
01422             CalcKernelMetaData(kernel);
01423             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
01424             break;
01425           case 13:
01426             kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
01427             if (kernel == (KernelInfo *) NULL)
01428               return(kernel);
01429             kernel->type = type;
01430             kernel->values[0] = +MagickSQ2;
01431             kernel->values[8] = -MagickSQ2;
01432             CalcKernelMetaData(kernel);
01433             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
01434             break;
01435           case 14:
01436             kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
01437             if (kernel == (KernelInfo *) NULL)
01438               return(kernel);
01439             kernel->type = type;
01440             kernel->values[2] = -MagickSQ2;
01441             kernel->values[6] = +MagickSQ2;
01442             CalcKernelMetaData(kernel);
01443             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
01444             break;
01445           case 15:
01446             kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
01447             if (kernel == (KernelInfo *) NULL)
01448               return(kernel);
01449             kernel->type = type;
01450             ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
01451             break;
01452           case 16:
01453             kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
01454             if (kernel == (KernelInfo *) NULL)
01455               return(kernel);
01456             kernel->type = type;
01457             ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
01458             break;
01459           case 17:
01460             kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
01461             if (kernel == (KernelInfo *) NULL)
01462               return(kernel);
01463             kernel->type = type;
01464             ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
01465             break;
01466           case 18:
01467             kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
01468             if (kernel == (KernelInfo *) NULL)
01469               return(kernel);
01470             kernel->type = type;
01471             ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
01472             break;
01473           case 19:
01474             kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
01475             if (kernel == (KernelInfo *) NULL)
01476               return(kernel);
01477             kernel->type = type;
01478             ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
01479             break;
01480         }
01481         if ( fabs(args->sigma) > MagickEpsilon )
01482           /* Rotate by correctly supplied 'angle' */
01483           RotateKernelInfo(kernel, args->sigma);
01484         else if ( args->rho > 30.0 || args->rho < -30.0 )
01485           /* Rotate by out of bounds 'type' */
01486           RotateKernelInfo(kernel, args->rho);
01487         break;
01488       }
01489 
01490     /*
01491       Boolean or Shaped Kernels
01492     */
01493     case DiamondKernel:
01494       {
01495         if (args->rho < 1.0)
01496           kernel->width = kernel->height = 3;  /* default radius = 1 */
01497         else
01498           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
01499         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
01500 
01501         kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01502           kernel->height*sizeof(*kernel->values));
01503         if (kernel->values == (MagickRealType *) NULL)
01504           return(DestroyKernelInfo(kernel));
01505 
01506         /* set all kernel values within diamond area to scale given */
01507         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
01508           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01509             if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
01510               kernel->positive_range += kernel->values[i] = args->sigma;
01511             else
01512               kernel->values[i] = nan;
01513         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
01514         break;
01515       }
01516     case SquareKernel:
01517     case RectangleKernel:
01518       { double
01519           scale;
01520         if ( type == SquareKernel )
01521           {
01522             if (args->rho < 1.0)
01523               kernel->width = kernel->height = 3;  /* default radius = 1 */
01524             else
01525               kernel->width = kernel->height = (size_t) (2*args->rho+1);
01526             kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
01527             scale = args->sigma;
01528           }
01529         else {
01530             /* NOTE: user defaults set in "AcquireKernelInfo()" */
01531             if ( args->rho < 1.0 || args->sigma < 1.0 )
01532               return(DestroyKernelInfo(kernel));    /* invalid args given */
01533             kernel->width = (size_t)args->rho;
01534             kernel->height = (size_t)args->sigma;
01535             if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
01536                  args->psi < 0.0 || args->psi > (double)kernel->height )
01537               return(DestroyKernelInfo(kernel));    /* invalid args given */
01538             kernel->x = (ssize_t) args->xi;
01539             kernel->y = (ssize_t) args->psi;
01540             scale = 1.0;
01541           }
01542         kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01543           kernel->height*sizeof(*kernel->values));
01544         if (kernel->values == (MagickRealType *) NULL)
01545           return(DestroyKernelInfo(kernel));
01546 
01547         /* set all kernel values to scale given */
01548         u=(ssize_t) (kernel->width*kernel->height);
01549         for ( i=0; i < u; i++)
01550             kernel->values[i] = scale;
01551         kernel->minimum = kernel->maximum = scale;   /* a flat shape */
01552         kernel->positive_range = scale*u;
01553         break;
01554       }
01555       case OctagonKernel:
01556         {
01557           if (args->rho < 1.0)
01558             kernel->width = kernel->height = 5;  /* default radius = 2 */
01559           else
01560             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
01561           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
01562 
01563           kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01564             kernel->height*sizeof(*kernel->values));
01565           if (kernel->values == (MagickRealType *) NULL)
01566             return(DestroyKernelInfo(kernel));
01567 
01568           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
01569             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01570               if ( (labs((long) u)+labs((long) v)) <=
01571                         ((long)kernel->x + (long)(kernel->x/2)) )
01572                 kernel->positive_range += kernel->values[i] = args->sigma;
01573               else
01574                 kernel->values[i] = nan;
01575           kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
01576           break;
01577         }
01578       case DiskKernel:
01579         {
01580           ssize_t
01581             limit = (ssize_t)(args->rho*args->rho);
01582 
01583           if (args->rho < 0.4)           /* default radius approx 4.3 */
01584             kernel->width = kernel->height = 9L, limit = 18L;
01585           else
01586             kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
01587           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
01588 
01589           kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01590             kernel->height*sizeof(*kernel->values));
01591           if (kernel->values == (MagickRealType *) NULL)
01592             return(DestroyKernelInfo(kernel));
01593 
01594           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
01595             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01596               if ((u*u+v*v) <= limit)
01597                 kernel->positive_range += kernel->values[i] = args->sigma;
01598               else
01599                 kernel->values[i] = nan;
01600           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
01601           break;
01602         }
01603       case PlusKernel:
01604         {
01605           if (args->rho < 1.0)
01606             kernel->width = kernel->height = 5;  /* default radius 2 */
01607           else
01608             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
01609           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
01610 
01611           kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01612             kernel->height*sizeof(*kernel->values));
01613           if (kernel->values == (MagickRealType *) NULL)
01614             return(DestroyKernelInfo(kernel));
01615 
01616           /* set all kernel values along axises to given scale */
01617           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
01618             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01619               kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
01620           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
01621           kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
01622           break;
01623         }
01624       case CrossKernel:
01625         {
01626           if (args->rho < 1.0)
01627             kernel->width = kernel->height = 5;  /* default radius 2 */
01628           else
01629             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
01630           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
01631 
01632           kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01633             kernel->height*sizeof(*kernel->values));
01634           if (kernel->values == (MagickRealType *) NULL)
01635             return(DestroyKernelInfo(kernel));
01636 
01637           /* set all kernel values along axises to given scale */
01638           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
01639             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01640               kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
01641           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
01642           kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
01643           break;
01644         }
01645       /*
01646         HitAndMiss Kernels
01647       */
01648       case RingKernel:
01649       case PeaksKernel:
01650         {
01651           ssize_t
01652             limit1,
01653             limit2,
01654             scale;
01655 
01656           if (args->rho < args->sigma)
01657             {
01658               kernel->width = ((size_t)args->sigma)*2+1;
01659               limit1 = (ssize_t)(args->rho*args->rho);
01660               limit2 = (ssize_t)(args->sigma*args->sigma);
01661             }
01662           else
01663             {
01664               kernel->width = ((size_t)args->rho)*2+1;
01665               limit1 = (ssize_t)(args->sigma*args->sigma);
01666               limit2 = (ssize_t)(args->rho*args->rho);
01667             }
01668           if ( limit2 <= 0 )
01669             kernel->width = 7L, limit1 = 7L, limit2 = 11L;
01670 
01671           kernel->height = kernel->width;
01672           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
01673           kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
01674             kernel->height*sizeof(*kernel->values));
01675           if (kernel->values == (MagickRealType *) NULL)
01676             return(DestroyKernelInfo(kernel));
01677 
01678           /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
01679           scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
01680           for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
01681             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
01682               { ssize_t radius=u*u+v*v;
01683                 if (limit1 < radius && radius <= limit2)
01684                   kernel->positive_range += kernel->values[i] = (double) scale;
01685                 else
01686                   kernel->values[i] = nan;
01687               }
01688           kernel->minimum = kernel->maximum = (double) scale;
01689           if ( type == PeaksKernel ) {
01690             /* set the central point in the middle */
01691             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
01692             kernel->positive_range = 1.0;
01693             kernel->maximum = 1.0;
01694           }
01695           break;
01696         }
01697       case EdgesKernel:
01698         {
01699           kernel=AcquireKernelInfo("ThinSE:482");
01700           if (kernel == (KernelInfo *) NULL)
01701             return(kernel);
01702           kernel->type = type;
01703           ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
01704           break;
01705         }
01706       case CornersKernel:
01707         {
01708           kernel=AcquireKernelInfo("ThinSE:87");
01709           if (kernel == (KernelInfo *) NULL)
01710             return(kernel);
01711           kernel->type = type;
01712           ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
01713           break;
01714         }
01715       case DiagonalsKernel:
01716         {
01717           switch ( (int) args->rho ) {
01718             case 0:
01719             default:
01720               { KernelInfo
01721                   *new_kernel;
01722                 kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
01723                 if (kernel == (KernelInfo *) NULL)
01724                   return(kernel);
01725                 kernel->type = type;
01726                 new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
01727                 if (new_kernel == (KernelInfo *) NULL)
01728                   return(DestroyKernelInfo(kernel));
01729                 new_kernel->type = type;
01730                 LastKernelInfo(kernel)->next = new_kernel;
01731                 ExpandMirrorKernelInfo(kernel);
01732                 return(kernel);
01733               }
01734             case 1:
01735               kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
01736               break;
01737             case 2:
01738               kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
01739               break;
01740           }
01741           if (kernel == (KernelInfo *) NULL)
01742             return(kernel);
01743           kernel->type = type;
01744           RotateKernelInfo(kernel, args->sigma);
01745           break;
01746         }
01747       case LineEndsKernel:
01748         { /* Kernels for finding the end of thin lines */
01749           switch ( (int) args->rho ) {
01750             case 0:
01751             default:
01752               /* set of kernels to find all end of lines */
01753               return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
01754             case 1:
01755               /* kernel for 4-connected line ends - no rotation */
01756               kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
01757               break;
01758           case 2:
01759               /* kernel to add for 8-connected lines - no rotation */
01760               kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
01761               break;
01762           case 3:
01763               /* kernel to add for orthogonal line ends - does not find corners */
01764               kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
01765               break;
01766           case 4:
01767               /* traditional line end - fails on last T end */
01768               kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
01769               break;
01770           }
01771           if (kernel == (KernelInfo *) NULL)
01772             return(kernel);
01773           kernel->type = type;
01774           RotateKernelInfo(kernel, args->sigma);
01775           break;
01776         }
01777       case LineJunctionsKernel:
01778         { /* kernels for finding the junctions of multiple lines */
01779           switch ( (int) args->rho ) {
01780             case 0:
01781             default:
01782               /* set of kernels to find all line junctions */
01783               return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
01784             case 1:
01785               /* Y Junction */
01786               kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
01787               break;
01788             case 2:
01789               /* Diagonal T Junctions */
01790               kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
01791               break;
01792             case 3:
01793               /* Orthogonal T Junctions */
01794               kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
01795               break;
01796             case 4:
01797               /* Diagonal X Junctions */
01798               kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
01799               break;
01800             case 5:
01801               /* Orthogonal X Junctions - minimal diamond kernel */
01802               kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
01803               break;
01804           }
01805           if (kernel == (KernelInfo *) NULL)
01806             return(kernel);
01807           kernel->type = type;
01808           RotateKernelInfo(kernel, args->sigma);
01809           break;
01810         }
01811       case RidgesKernel:
01812         { /* Ridges - Ridge finding kernels */
01813           KernelInfo
01814             *new_kernel;
01815           switch ( (int) args->rho ) {
01816             case 1:
01817             default:
01818               kernel=ParseKernelArray("3x1:0,1,0");
01819               if (kernel == (KernelInfo *) NULL)
01820                 return(kernel);
01821               kernel->type = type;
01822               ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
01823               break;
01824             case 2:
01825               kernel=ParseKernelArray("4x1:0,1,1,0");
01826               if (kernel == (KernelInfo *) NULL)
01827                 return(kernel);
01828               kernel->type = type;
01829               ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
01830 
01831               /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
01832               /* Unfortunatally we can not yet rotate a non-square kernel */
01833               /* But then we can't flip a non-symetrical kernel either */
01834               new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
01835               if (new_kernel == (KernelInfo *) NULL)
01836                 return(DestroyKernelInfo(kernel));
01837               new_kernel->type = type;
01838               LastKernelInfo(kernel)->next = new_kernel;
01839               new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
01840               if (new_kernel == (KernelInfo *) NULL)
01841                 return(DestroyKernelInfo(kernel));
01842               new_kernel->type = type;
01843               LastKernelInfo(kernel)->next = new_kernel;
01844               new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
01845               if (new_kernel == (KernelInfo *) NULL)
01846                 return(DestroyKernelInfo(kernel));
01847               new_kernel->type = type;
01848               LastKernelInfo(kernel)->next = new_kernel;
01849               new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
01850               if (new_kernel == (KernelInfo *) NULL)
01851                 return(DestroyKernelInfo(kernel));
01852               new_kernel->type = type;
01853               LastKernelInfo(kernel)->next = new_kernel;
01854               new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
01855               if (new_kernel == (KernelInfo *) NULL)
01856                 return(DestroyKernelInfo(kernel));
01857               new_kernel->type = type;
01858               LastKernelInfo(kernel)->next = new_kernel;
01859               new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
01860               if (new_kernel == (KernelInfo *) NULL)
01861                 return(DestroyKernelInfo(kernel));
01862               new_kernel->type = type;
01863               LastKernelInfo(kernel)->next = new_kernel;
01864               new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
01865               if (new_kernel == (KernelInfo *) NULL)
01866                 return(DestroyKernelInfo(kernel));
01867               new_kernel->type = type;
01868               LastKernelInfo(kernel)->next = new_kernel;
01869               new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
01870               if (new_kernel == (KernelInfo *) NULL)
01871                 return(DestroyKernelInfo(kernel));
01872               new_kernel->type = type;
01873               LastKernelInfo(kernel)->next = new_kernel;
01874               break;
01875           }
01876           break;
01877         }
01878       case ConvexHullKernel:
01879         {
01880           KernelInfo
01881             *new_kernel;
01882           /* first set of 8 kernels */
01883           kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
01884           if (kernel == (KernelInfo *) NULL)
01885             return(kernel);
01886           kernel->type = type;
01887           ExpandRotateKernelInfo(kernel, 90.0);
01888           /* append the mirror versions too - no flip function yet */
01889           new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
01890           if (new_kernel == (KernelInfo *) NULL)
01891             return(DestroyKernelInfo(kernel));
01892           new_kernel->type = type;
01893           ExpandRotateKernelInfo(new_kernel, 90.0);
01894           LastKernelInfo(kernel)->next = new_kernel;
01895           break;
01896         }
01897       case SkeletonKernel:
01898         {
01899           switch ( (int) args->rho ) {
01900             case 1:
01901             default:
01902               /* Traditional Skeleton...
01903               ** A cyclically rotated single kernel
01904               */
01905               kernel=AcquireKernelInfo("ThinSE:482");
01906               if (kernel == (KernelInfo *) NULL)
01907                 return(kernel);
01908               kernel->type = type;
01909               ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
01910               break;
01911             case 2:
01912               /* HIPR Variation of the cyclic skeleton
01913               ** Corners of the traditional method made more forgiving,
01914               ** but the retain the same cyclic order.
01915               */
01916               kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
01917               if (kernel == (KernelInfo *) NULL)
01918                 return(kernel);
01919               if (kernel->next == (KernelInfo *) NULL)
01920                 return(DestroyKernelInfo(kernel));
01921               kernel->type = type;
01922               kernel->next->type = type;
01923               ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
01924               break;
01925             case 3:
01926               /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
01927               ** "Connectivity-Preserving Morphological Image Thransformations"
01928               ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
01929               **   http://www.leptonica.com/papers/conn.pdf
01930               */
01931               kernel=AcquireKernelInfo(
01932                             "ThinSE:41; ThinSE:42; ThinSE:43");
01933               if (kernel == (KernelInfo *) NULL)
01934                 return(kernel);
01935               kernel->type = type;
01936               kernel->next->type = type;
01937               kernel->next->next->type = type;
01938               ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
01939               break;
01940            }
01941           break;
01942         }
01943       case ThinSEKernel:
01944         { /* Special kernels for general thinning, while preserving connections
01945           ** "Connectivity-Preserving Morphological Image Thransformations"
01946           ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
01947           **   http://www.leptonica.com/papers/conn.pdf
01948           ** And
01949           **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
01950           **
01951           ** Note kernels do not specify the origin pixel, allowing them
01952           ** to be used for both thickening and thinning operations.
01953           */
01954           switch ( (int) args->rho ) {
01955             /* SE for 4-connected thinning */
01956             case 41: /* SE_4_1 */
01957               kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
01958               break;
01959             case 42: /* SE_4_2 */
01960               kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
01961               break;
01962             case 43: /* SE_4_3 */
01963               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
01964               break;
01965             case 44: /* SE_4_4 */
01966               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
01967               break;
01968             case 45: /* SE_4_5 */
01969               kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
01970               break;
01971             case 46: /* SE_4_6 */
01972               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
01973               break;
01974             case 47: /* SE_4_7 */
01975               kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
01976               break;
01977             case 48: /* SE_4_8 */
01978               kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
01979               break;
01980             case 49: /* SE_4_9 */
01981               kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
01982               break;
01983             /* SE for 8-connected thinning - negatives of the above */
01984             case 81: /* SE_8_0 */
01985               kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
01986               break;
01987             case 82: /* SE_8_2 */
01988               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
01989               break;
01990             case 83: /* SE_8_3 */
01991               kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
01992               break;
01993             case 84: /* SE_8_4 */
01994               kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
01995               break;
01996             case 85: /* SE_8_5 */
01997               kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
01998               break;
01999             case 86: /* SE_8_6 */
02000               kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
02001               break;
02002             case 87: /* SE_8_7 */
02003               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
02004               break;
02005             case 88: /* SE_8_8 */
02006               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
02007               break;
02008             case 89: /* SE_8_9 */
02009               kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
02010               break;
02011             /* Special combined SE kernels */
02012             case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
02013               kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
02014               break;
02015             case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
02016               kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
02017               break;
02018             case 481: /* SE_48_1 - General Connected Corner Kernel */
02019               kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
02020               break;
02021             default:
02022             case 482: /* SE_48_2 - General Edge Kernel */
02023               kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
02024               break;
02025           }
02026           if (kernel == (KernelInfo *) NULL)
02027             return(kernel);
02028           kernel->type = type;
02029           RotateKernelInfo(kernel, args->sigma);
02030           break;
02031         }
02032       /*
02033         Distance Measuring Kernels
02034       */
02035       case ChebyshevKernel:
02036         {
02037           if (args->rho < 1.0)
02038             kernel->width = kernel->height = 3;  /* default radius = 1 */
02039           else
02040             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
02041           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
02042 
02043           kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
02044             kernel->height*sizeof(*kernel->values));
02045           if (kernel->values == (MagickRealType *) NULL)
02046             return(DestroyKernelInfo(kernel));
02047 
02048           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
02049             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
02050               kernel->positive_range += ( kernel->values[i] =
02051                   args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
02052           kernel->maximum = kernel->values[0];
02053           break;
02054         }
02055       case ManhattanKernel:
02056         {
02057           if (args->rho < 1.0)
02058             kernel->width = kernel->height = 3;  /* default radius = 1 */
02059           else
02060             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
02061           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
02062 
02063           kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
02064             kernel->height*sizeof(*kernel->values));
02065           if (kernel->values == (MagickRealType *) NULL)
02066             return(DestroyKernelInfo(kernel));
02067 
02068           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
02069             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
02070               kernel->positive_range += ( kernel->values[i] =
02071                   args->sigma*(labs((long) u)+labs((long) v)) );
02072           kernel->maximum = kernel->values[0];
02073           break;
02074         }
02075       case OctagonalKernel:
02076       {
02077         if (args->rho < 2.0)
02078           kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
02079         else
02080           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
02081         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
02082 
02083         kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
02084           kernel->height*sizeof(*kernel->values));
02085         if (kernel->values == (MagickRealType *) NULL)
02086           return(DestroyKernelInfo(kernel));
02087 
02088         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
02089           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
02090             {
02091               double
02092                 r1 = MagickMax(fabs((double)u),fabs((double)v)),
02093                 r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
02094               kernel->positive_range += kernel->values[i] =
02095                         args->sigma*MagickMax(r1,r2);
02096             }
02097         kernel->maximum = kernel->values[0];
02098         break;
02099       }
02100     case EuclideanKernel:
02101       {
02102         if (args->rho < 1.0)
02103           kernel->width = kernel->height = 3;  /* default radius = 1 */
02104         else
02105           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
02106         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
02107 
02108         kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
02109           kernel->height*sizeof(*kernel->values));
02110         if (kernel->values == (MagickRealType *) NULL)
02111           return(DestroyKernelInfo(kernel));
02112 
02113         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
02114           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
02115             kernel->positive_range += ( kernel->values[i] =
02116                  args->sigma*sqrt((double)(u*u+v*v)) );
02117         kernel->maximum = kernel->values[0];
02118         break;
02119       }
02120     default:
02121       {
02122         /* No-Op Kernel - Basically just a single pixel on its own */
02123         kernel=ParseKernelArray("1:1");
02124         if (kernel == (KernelInfo *) NULL)
02125           return(kernel);
02126         kernel->type = UndefinedKernel;
02127         break;
02128       }
02129       break;
02130   }
02131   return(kernel);
02132 }
02133 
02134 /*
02135 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02136 %                                                                             %
02137 %                                                                             %
02138 %                                                                             %
02139 %     C l o n e K e r n e l I n f o                                           %
02140 %                                                                             %
02141 %                                                                             %
02142 %                                                                             %
02143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02144 %
02145 %  CloneKernelInfo() creates a new clone of the given Kernel List so that its
02146 %  can be modified without effecting the original.  The cloned kernel should
02147 %  be destroyed using DestroyKernelInfo() when no longer needed.
02148 %
02149 %  The format of the CloneKernelInfo method is:
02150 %
02151 %      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
02152 %
02153 %  A description of each parameter follows:
02154 %
02155 %    o kernel: the Morphology/Convolution kernel to be cloned
02156 %
02157 */
02158 MagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
02159 {
02160   register ssize_t
02161     i;
02162 
02163   KernelInfo
02164     *new_kernel;
02165 
02166   assert(kernel != (KernelInfo *) NULL);
02167   new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
02168   if (new_kernel == (KernelInfo *) NULL)
02169     return(new_kernel);
02170   *new_kernel=(*kernel); /* copy values in structure */
02171 
02172   /* replace the values with a copy of the values */
02173   new_kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
02174     kernel->height*sizeof(*kernel->values));
02175   if (new_kernel->values == (MagickRealType *) NULL)
02176     return(DestroyKernelInfo(new_kernel));
02177   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
02178     new_kernel->values[i]=kernel->values[i];
02179 
02180   /* Also clone the next kernel in the kernel list */
02181   if ( kernel->next != (KernelInfo *) NULL ) {
02182     new_kernel->next = CloneKernelInfo(kernel->next);
02183     if ( new_kernel->next == (KernelInfo *) NULL )
02184       return(DestroyKernelInfo(new_kernel));
02185   }
02186 
02187   return(new_kernel);
02188 }
02189 
02190 /*
02191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02192 %                                                                             %
02193 %                                                                             %
02194 %                                                                             %
02195 %     D e s t r o y K e r n e l I n f o                                       %
02196 %                                                                             %
02197 %                                                                             %
02198 %                                                                             %
02199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02200 %
02201 %  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
02202 %  kernel.
02203 %
02204 %  The format of the DestroyKernelInfo method is:
02205 %
02206 %      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
02207 %
02208 %  A description of each parameter follows:
02209 %
02210 %    o kernel: the Morphology/Convolution kernel to be destroyed
02211 %
02212 */
02213 MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
02214 {
02215   assert(kernel != (KernelInfo *) NULL);
02216   if ( kernel->next != (KernelInfo *) NULL )
02217     kernel->next=DestroyKernelInfo(kernel->next);
02218   kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
02219   kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
02220   return(kernel);
02221 }
02222 
02223 /*
02224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02225 %                                                                             %
02226 %                                                                             %
02227 %                                                                             %
02228 +     E x p a n d M i r r o r K e r n e l I n f o                             %
02229 %                                                                             %
02230 %                                                                             %
02231 %                                                                             %
02232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02233 %
02234 %  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
02235 %  sequence of 90-degree rotated kernels but providing a reflected 180
02236 %  rotatation, before the -/+ 90-degree rotations.
02237 %
02238 %  This special rotation order produces a better, more symetrical thinning of
02239 %  objects.
02240 %
02241 %  The format of the ExpandMirrorKernelInfo method is:
02242 %
02243 %      void ExpandMirrorKernelInfo(KernelInfo *kernel)
02244 %
02245 %  A description of each parameter follows:
02246 %
02247 %    o kernel: the Morphology/Convolution kernel
02248 %
02249 % This function is only internel to this module, as it is not finalized,
02250 % especially with regard to non-orthogonal angles, and rotation of larger
02251 % 2D kernels.
02252 */
02253 
02254 #if 0
02255 static void FlopKernelInfo(KernelInfo *kernel)
02256     { /* Do a Flop by reversing each row. */
02257       size_t
02258         y;
02259       register ssize_t
02260         x,r;
02261       register double
02262         *k,t;
02263 
02264       for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
02265         for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
02266           t=k[x],  k[x]=k[r],  k[r]=t;
02267 
02268       kernel->x = kernel->width - kernel->x - 1;
02269       angle = fmod(angle+180.0, 360.0);
02270     }
02271 #endif
02272 
02273 static void ExpandMirrorKernelInfo(KernelInfo *kernel)
02274 {
02275   KernelInfo
02276     *clone,
02277     *last;
02278 
02279   last = kernel;
02280 
02281   clone = CloneKernelInfo(last);
02282   RotateKernelInfo(clone, 180);   /* flip */
02283   LastKernelInfo(last)->next = clone;
02284   last = clone;
02285 
02286   clone = CloneKernelInfo(last);
02287   RotateKernelInfo(clone, 90);   /* transpose */
02288   LastKernelInfo(last)->next = clone;
02289   last = clone;
02290 
02291   clone = CloneKernelInfo(last);
02292   RotateKernelInfo(clone, 180);  /* flop */
02293   LastKernelInfo(last)->next = clone;
02294 
02295   return;
02296 }
02297 
02298 /*
02299 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02300 %                                                                             %
02301 %                                                                             %
02302 %                                                                             %
02303 +     E x p a n d R o t a t e K e r n e l I n f o                             %
02304 %                                                                             %
02305 %                                                                             %
02306 %                                                                             %
02307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02308 %
02309 %  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
02310 %  incrementally by the angle given, until the kernel repeats.
02311 %
02312 %  WARNING: 45 degree rotations only works for 3x3 kernels.
02313 %  While 90 degree roatations only works for linear and square kernels
02314 %
02315 %  The format of the ExpandRotateKernelInfo method is:
02316 %
02317 %      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
02318 %
02319 %  A description of each parameter follows:
02320 %
02321 %    o kernel: the Morphology/Convolution kernel
02322 %
02323 %    o angle: angle to rotate in degrees
02324 %
02325 % This function is only internel to this module, as it is not finalized,
02326 % especially with regard to non-orthogonal angles, and rotation of larger
02327 % 2D kernels.
02328 */
02329 
02330 /* Internal Routine - Return true if two kernels are the same */
02331 static MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
02332      const KernelInfo *kernel2)
02333 {
02334   register size_t
02335     i;
02336 
02337   /* check size and origin location */
02338   if (    kernel1->width != kernel2->width
02339        || kernel1->height != kernel2->height
02340        || kernel1->x != kernel2->x
02341        || kernel1->y != kernel2->y )
02342     return MagickFalse;
02343 
02344   /* check actual kernel values */
02345   for (i=0; i < (kernel1->width*kernel1->height); i++) {
02346     /* Test for Nan equivalence */
02347     if ( IsNan(kernel1->values[i]) && !IsNan(kernel2->values[i]) )
02348       return MagickFalse;
02349     if ( IsNan(kernel2->values[i]) && !IsNan(kernel1->values[i]) )
02350       return MagickFalse;
02351     /* Test actual values are equivalent */
02352     if ( fabs(kernel1->values[i] - kernel2->values[i]) > MagickEpsilon )
02353       return MagickFalse;
02354   }
02355 
02356   return MagickTrue;
02357 }
02358 
02359 static void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
02360 {
02361   KernelInfo
02362     *clone,
02363     *last;
02364 
02365   last = kernel;
02366   while(1) {
02367     clone = CloneKernelInfo(last);
02368     RotateKernelInfo(clone, angle);
02369     if ( SameKernelInfo(kernel, clone) == MagickTrue )
02370       break;
02371     LastKernelInfo(last)->next = clone;
02372     last = clone;
02373   }
02374   clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
02375   return;
02376 }
02377 
02378 /*
02379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02380 %                                                                             %
02381 %                                                                             %
02382 %                                                                             %
02383 +     C a l c M e t a K e r n a l I n f o                                     %
02384 %                                                                             %
02385 %                                                                             %
02386 %                                                                             %
02387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02388 %
02389 %  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
02390 %  using the kernel values.  This should only ne used if it is not possible to
02391 %  calculate that meta-data in some easier way.
02392 %
02393 %  It is important that the meta-data is correct before ScaleKernelInfo() is
02394 %  used to perform kernel normalization.
02395 %
02396 %  The format of the CalcKernelMetaData method is:
02397 %
02398 %      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
02399 %
02400 %  A description of each parameter follows:
02401 %
02402 %    o kernel: the Morphology/Convolution kernel to modify
02403 %
02404 %  WARNING: Minimum and Maximum values are assumed to include zero, even if
02405 %  zero is not part of the kernel (as in Gaussian Derived kernels). This
02406 %  however is not true for flat-shaped morphological kernels.
02407 %
02408 %  WARNING: Only the specific kernel pointed to is modified, not a list of
02409 %  multiple kernels.
02410 %
02411 % This is an internal function and not expected to be useful outside this
02412 % module.  This could change however.
02413 */
02414 static void CalcKernelMetaData(KernelInfo *kernel)
02415 {
02416   register size_t
02417     i;
02418 
02419   kernel->minimum = kernel->maximum = 0.0;
02420   kernel->negative_range = kernel->positive_range = 0.0;
02421   for (i=0; i < (kernel->width*kernel->height); i++)
02422     {
02423       if ( fabs(kernel->values[i]) < MagickEpsilon )
02424         kernel->values[i] = 0.0;
02425       ( kernel->values[i] < 0)
02426           ?  ( kernel->negative_range += kernel->values[i] )
02427           :  ( kernel->positive_range += kernel->values[i] );
02428       Minimize(kernel->minimum, kernel->values[i]);
02429       Maximize(kernel->maximum, kernel->values[i]);
02430     }
02431 
02432   return;
02433 }
02434 
02435 /*
02436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02437 %                                                                             %
02438 %                                                                             %
02439 %                                                                             %
02440 %     M o r p h o l o g y A p p l y                                           %
02441 %                                                                             %
02442 %                                                                             %
02443 %                                                                             %
02444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02445 %
02446 %  MorphologyApply() applies a morphological method, multiple times using
02447 %  a list of multiple kernels.
02448 %
02449 %  It is basically equivalent to as MorphologyImage() (see below) but
02450 %  without any user controls.  This allows internel programs to use this
02451 %  function, to actually perform a specific task without possible interference
02452 %  by any API user supplied settings.
02453 %
02454 %  It is MorphologyImage() task to extract any such user controls, and
02455 %  pass them to this function for processing.
02456 %
02457 %  More specifically kernels are not normalized/scaled/blended by the
02458 %  'convolve:scale' Image Artifact (setting), nor is the convolve bias
02459 %  (-bias setting or image->bias) loooked at, but must be supplied from the
02460 %  function arguments.
02461 %
02462 %  The format of the MorphologyApply method is:
02463 %
02464 %      Image *MorphologyApply(const Image *image,MorphologyMethod method,
02465 %        const ssize_t iterations,const KernelInfo *kernel,
02466 %        const CompositeMethod compose,const double bias,
02467 %        ExceptionInfo *exception)
02468 %
02469 %  A description of each parameter follows:
02470 %
02471 %    o image: the source image
02472 %
02473 %    o method: the morphology method to be applied.
02474 %
02475 %    o iterations: apply the operation this many times (or no change).
02476 %                  A value of -1 means loop until no change found.
02477 %                  How this is applied may depend on the morphology method.
02478 %                  Typically this is a value of 1.
02479 %
02480 %    o channel: the channel type.
02481 %
02482 %    o kernel: An array of double representing the morphology kernel.
02483 %
02484 %    o compose: How to handle or merge multi-kernel results.
02485 %          If 'UndefinedCompositeOp' use default for the Morphology method.
02486 %          If 'NoCompositeOp' force image to be re-iterated by each kernel.
02487 %          Otherwise merge the results using the compose method given.
02488 %
02489 %    o bias: Convolution Output Bias.
02490 %
02491 %    o exception: return any errors or warnings in this structure.
02492 %
02493 */
02494 
02495 /* Apply a Morphology Primative to an image using the given kernel.
02496 ** Two pre-created images must be provided, and no image is created.
02497 ** It returns the number of pixels that changed between the images
02498 ** for result convergence determination.
02499 */
02500 static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
02501   const MorphologyMethod method,const KernelInfo *kernel,const double bias,
02502   ExceptionInfo *exception)
02503 {
02504 #define MorphologyTag  "Morphology/Image"
02505 
02506   CacheView
02507     *image_view,
02508     *morphology_view;
02509 
02510   ssize_t
02511     y, offx, offy;
02512 
02513   size_t
02514     virt_width,
02515     changed;
02516 
02517   MagickBooleanType
02518     status;
02519 
02520   MagickOffsetType
02521     progress;
02522 
02523   assert(image != (Image *) NULL);
02524   assert(image->signature == MagickSignature);
02525   assert(morphology_image != (Image *) NULL);
02526   assert(morphology_image->signature == MagickSignature);
02527   assert(kernel != (KernelInfo *) NULL);
02528   assert(kernel->signature == MagickSignature);
02529   assert(exception != (ExceptionInfo *) NULL);
02530   assert(exception->signature == MagickSignature);
02531 
02532   status=MagickTrue;
02533   changed=0;
02534   progress=0;
02535 
02536   image_view=AcquireCacheView(image);
02537   morphology_view=AcquireCacheView(morphology_image);
02538   virt_width=image->columns+kernel->width-1;
02539 
02540   /* Some methods (including convolve) needs use a reflected kernel.
02541    * Adjust 'origin' offsets to loop though kernel as a reflection.
02542    */
02543   offx = kernel->x;
02544   offy = kernel->y;
02545   switch(method) {
02546     case ConvolveMorphology:
02547     case DilateMorphology:
02548     case DilateIntensityMorphology:
02549     /*case DistanceMorphology:*/
02550       /* kernel needs to used with reflection about origin */
02551       offx = (ssize_t) kernel->width-offx-1;
02552       offy = (ssize_t) kernel->height-offy-1;
02553       break;
02554     case ErodeMorphology:
02555     case ErodeIntensityMorphology:
02556     case HitAndMissMorphology:
02557     case ThinningMorphology:
02558     case ThickenMorphology:
02559       /* kernel is used as is, without reflection */
02560       break;
02561     default:
02562       assert("Not a Primitive Morphology Method" != (char *) NULL);
02563       break;
02564   }
02565 
02566   if ( method == ConvolveMorphology && kernel->width == 1 )
02567   { /* Special handling (for speed) of vertical (blur) kernels.
02568     ** This performs its handling in columns rather than in rows.
02569     ** This is only done for convolve as it is the only method that
02570     ** generates very large 1-D vertical kernels (such as a 'BlurKernel')
02571     **
02572     ** Timing tests (on single CPU laptop)
02573     ** Using a vertical 1-d Blue with normal row-by-row (below)
02574     **   time convert logo: -morphology Convolve Blur:0x10+90 null:
02575     **      0.807u
02576     ** Using this column method
02577     **   time convert logo: -morphology Convolve Blur:0x10+90 null:
02578     **      0.620u
02579     **
02580     ** Anthony Thyssen, 14 June 2010
02581     */
02582     register ssize_t
02583       x;
02584 
02585 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02586 #pragma omp parallel for schedule(static,4) shared(progress,status)
02587 #endif
02588     for (x=0; x < (ssize_t) image->columns; x++)
02589     {
02590       register const Quantum
02591         *restrict p;
02592 
02593       register Quantum
02594         *restrict q;
02595 
02596       register ssize_t
02597         y;
02598 
02599       ssize_t
02600         r;
02601 
02602       if (status == MagickFalse)
02603         continue;
02604       p=GetCacheViewVirtualPixels(image_view,x,-offy,1,image->rows+
02605         kernel->height-1,exception);
02606       q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
02607         morphology_image->rows,exception);
02608       if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
02609         {
02610           status=MagickFalse;
02611           continue;
02612         }
02613       /* offset to origin in 'p'. while 'q' points to it directly */
02614       r = offy;
02615 
02616       for (y=0; y < (ssize_t) image->rows; y++)
02617       {
02618         PixelInfo
02619           result;
02620 
02621         register ssize_t
02622           v;
02623 
02624         register const MagickRealType
02625           *restrict k;
02626 
02627         register const Quantum
02628           *restrict k_pixels;
02629 
02630         /* Copy input image to the output image for unused channels
02631         * This removes need for 'cloning' a new image every iteration
02632         */
02633         SetPixelRed(morphology_image,GetPixelRed(image,p+r*
02634           GetPixelChannels(image)),q);
02635         SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
02636           GetPixelChannels(image)),q);
02637         SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
02638           GetPixelChannels(image)),q);
02639         if (image->colorspace == CMYKColorspace)
02640           SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
02641             GetPixelChannels(image)),q);
02642 
02643         /* Set the bias of the weighted average output */
02644         result.red     =
02645         result.green   =
02646         result.blue    =
02647         result.alpha =
02648         result.black   = bias;
02649 
02650 
02651         /* Weighted Average of pixels using reflected kernel
02652         **
02653         ** NOTE for correct working of this operation for asymetrical
02654         ** kernels, the kernel needs to be applied in its reflected form.
02655         ** That is its values needs to be reversed.
02656         */
02657         k = &kernel->values[ kernel->height-1 ];
02658         k_pixels = p;
02659         if ( (image->channel_mask != DefaultChannels) || (image->matte == MagickFalse) )
02660           { /* No 'Sync' involved.
02661             ** Convolution is simple greyscale channel operation
02662             */
02663             for (v=0; v < (ssize_t) kernel->height; v++) {
02664               if ( IsNan(*k) ) continue;
02665               result.red     += (*k)*GetPixelRed(image,k_pixels);
02666               result.green   += (*k)*GetPixelGreen(image,k_pixels);
02667               result.blue    += (*k)*GetPixelBlue(image,k_pixels);
02668               if (image->colorspace == CMYKColorspace)
02669                 result.black+=(*k)*GetPixelBlack(image,k_pixels);
02670               result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
02671               k--;
02672               k_pixels+=GetPixelChannels(image);
02673             }
02674             if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
02675               SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
02676             if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
02677               SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
02678             if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
02679               SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
02680             if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
02681                 (image->colorspace == CMYKColorspace))
02682               SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
02683             if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
02684                 (image->matte == MagickTrue))
02685               SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
02686           }
02687         else
02688           { /* Channel 'Sync' Flag, and Alpha Channel enabled.
02689             ** Weight the color channels with Alpha Channel so that
02690             ** transparent pixels are not part of the results.
02691             */
02692             MagickRealType
02693               alpha,  /* alpha weighting of colors : kernel*alpha  */
02694               gamma;  /* divisor, sum of color weighting values */
02695 
02696             gamma=0.0;
02697             for (v=0; v < (ssize_t) kernel->height; v++) {
02698               if ( IsNan(*k) ) continue;
02699               alpha=(*k)*(QuantumScale*GetPixelAlpha(image,k_pixels));
02700               gamma += alpha;
02701               result.red     += alpha*GetPixelRed(image,k_pixels);
02702               result.green   += alpha*GetPixelGreen(image,k_pixels);
02703               result.blue    += alpha*GetPixelBlue(image,k_pixels);
02704               if (image->colorspace == CMYKColorspace)
02705                 result.black += alpha*GetPixelBlack(image,k_pixels);
02706               result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
02707               k--;
02708               k_pixels+=GetPixelChannels(image);
02709             }
02710             /* Sync'ed channels, all channels are modified */
02711             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02712             SetPixelRed(morphology_image,
02713               ClampToQuantum(gamma*result.red),q);
02714             SetPixelGreen(morphology_image,
02715               ClampToQuantum(gamma*result.green),q);
02716             SetPixelBlue(morphology_image,
02717               ClampToQuantum(gamma*result.blue),q);
02718             if (image->colorspace == CMYKColorspace)
02719               SetPixelBlack(morphology_image,
02720                 ClampToQuantum(gamma*result.black),q);
02721             SetPixelAlpha(morphology_image,
02722               ClampToQuantum(result.alpha),q);
02723           }
02724 
02725         /* Count up changed pixels */
02726         if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q))
02727             || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q))
02728             || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q))
02729             || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q))
02730             || ((image->colorspace == CMYKColorspace) &&
02731                 (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
02732           changed++;  /* The pixel was changed in some way! */
02733         p+=GetPixelChannels(image);
02734         q+=GetPixelChannels(morphology_image);
02735       } /* y */
02736       if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
02737         status=MagickFalse;
02738       if (image->progress_monitor != (MagickProgressMonitor) NULL)
02739         {
02740           MagickBooleanType
02741             proceed;
02742 
02743 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02744   #pragma omp critical (MagickCore_MorphologyImage)
02745 #endif
02746           proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
02747           if (proceed == MagickFalse)
02748             status=MagickFalse;
02749         }
02750     } /* x */
02751     morphology_image->type=image->type;
02752     morphology_view=DestroyCacheView(morphology_view);
02753     image_view=DestroyCacheView(image_view);
02754     return(status ? (ssize_t) changed : 0);
02755   }
02756 
02757   /*
02758   ** Normal handling of horizontal or rectangular kernels (row by row)
02759   */
02760 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02761   #pragma omp parallel for schedule(static,4) shared(progress,status)
02762 #endif
02763   for (y=0; y < (ssize_t) image->rows; y++)
02764   {
02765     register const Quantum
02766       *restrict p;
02767 
02768     register Quantum
02769       *restrict q;
02770 
02771     register ssize_t
02772       x;
02773 
02774     size_t
02775       r;
02776 
02777     if (status == MagickFalse)
02778       continue;
02779     p=GetCacheViewVirtualPixels(image_view, -offx, y-offy, virt_width,
02780       kernel->height,  exception);
02781     q=GetCacheViewAuthenticPixels(morphology_view,0,y,
02782       morphology_image->columns,1,exception);
02783     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
02784       {
02785         status=MagickFalse;
02786         continue;
02787       }
02788     /* offset to origin in 'p'. while 'q' points to it directly */
02789     r = virt_width*offy + offx;
02790 
02791     for (x=0; x < (ssize_t) image->columns; x++)
02792     {
02793        ssize_t
02794         v;
02795 
02796       register ssize_t
02797         u;
02798 
02799       register const MagickRealType
02800         *restrict k;
02801 
02802       register const Quantum
02803         *restrict k_pixels;
02804 
02805       PixelInfo
02806         result,
02807         min,
02808         max;
02809 
02810       /* Copy input image to the output image for unused channels
02811        * This removes need for 'cloning' a new image every iteration
02812        */
02813       SetPixelRed(morphology_image,GetPixelRed(image,p+r*
02814         GetPixelChannels(image)),q);
02815       SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
02816         GetPixelChannels(image)),q);
02817       SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
02818         GetPixelChannels(image)),q);
02819       if (image->colorspace == CMYKColorspace)
02820         SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
02821           GetPixelChannels(image)),q);
02822 
02823       /* Defaults */
02824       min.red     =
02825       min.green   =
02826       min.blue    =
02827       min.alpha =
02828       min.black   = (MagickRealType) QuantumRange;
02829       max.red     =
02830       max.green   =
02831       max.blue    =
02832       max.alpha =
02833       max.black   = (MagickRealType) 0;
02834       /* default result is the original pixel value */
02835       result.red     = (MagickRealType) GetPixelRed(image,p+r*GetPixelChannels(image));
02836       result.green   = (MagickRealType) GetPixelGreen(image,p+r*GetPixelChannels(image));
02837       result.blue    = (MagickRealType) GetPixelBlue(image,p+r*GetPixelChannels(image));
02838       result.black   = 0.0;
02839       if (image->colorspace == CMYKColorspace)
02840         result.black = (MagickRealType) GetPixelBlack(image,p+r*GetPixelChannels(image));
02841       result.alpha=(MagickRealType) GetPixelAlpha(image,p+r*GetPixelChannels(image));
02842 
02843       switch (method) {
02844         case ConvolveMorphology:
02845           /* Set the bias of the weighted average output */
02846           result.red     =
02847           result.green   =
02848           result.blue    =
02849           result.alpha =
02850           result.black   = bias;
02851           break;
02852         case DilateIntensityMorphology:
02853         case ErodeIntensityMorphology:
02854           /* use a boolean flag indicating when first match found */
02855           result.red = 0.0;  /* result is not used otherwise */
02856           break;
02857         default:
02858           break;
02859       }
02860 
02861       switch ( method ) {
02862         case ConvolveMorphology:
02863             /* Weighted Average of pixels using reflected kernel
02864             **
02865             ** NOTE for correct working of this operation for asymetrical
02866             ** kernels, the kernel needs to be applied in its reflected form.
02867             ** That is its values needs to be reversed.
02868             **
02869             ** Correlation is actually the same as this but without reflecting
02870             ** the kernel, and thus 'lower-level' that Convolution.  However
02871             ** as Convolution is the more common method used, and it does not
02872             ** really cost us much in terms of processing to use a reflected
02873             ** kernel, so it is Convolution that is implemented.
02874             **
02875             ** Correlation will have its kernel reflected before calling
02876             ** this function to do a Convolve.
02877             **
02878             ** For more details of Correlation vs Convolution see
02879             **   http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
02880             */
02881             k = &kernel->values[ kernel->width*kernel->height-1 ];
02882             k_pixels = p;
02883             if ( (image->channel_mask != DefaultChannels) ||
02884                  (image->matte == MagickFalse) )
02885               { /* No 'Sync' involved.
02886                 ** Convolution is simple greyscale channel operation
02887                 */
02888                 for (v=0; v < (ssize_t) kernel->height; v++) {
02889                   for (u=0; u < (ssize_t) kernel->width; u++, k--) {
02890                     if ( IsNan(*k) ) continue;
02891                     result.red     += (*k)*
02892                       GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
02893                     result.green   += (*k)*
02894                       GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
02895                     result.blue    += (*k)*
02896                       GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
02897                     if (image->colorspace == CMYKColorspace)
02898                       result.black += (*k)*
02899                         GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
02900                     result.alpha += (*k)*
02901                       GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
02902                   }
02903                   k_pixels += virt_width*GetPixelChannels(image);
02904                 }
02905                 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
02906                   SetPixelRed(morphology_image,ClampToQuantum(result.red),
02907                     q);
02908                 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
02909                   SetPixelGreen(morphology_image,ClampToQuantum(result.green),
02910                     q);
02911                 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
02912                   SetPixelBlue(morphology_image,ClampToQuantum(result.blue),
02913                     q);
02914                 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
02915                     (image->colorspace == CMYKColorspace))
02916                   SetPixelBlack(morphology_image,ClampToQuantum(result.black),
02917                     q);
02918                 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
02919                     (image->matte == MagickTrue))
02920                   SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),
02921                     q);
02922               }
02923             else
02924               { /* Channel 'Sync' Flag, and Alpha Channel enabled.
02925                 ** Weight the color channels with Alpha Channel so that
02926                 ** transparent pixels are not part of the results.
02927                 */
02928                 MagickRealType
02929                   alpha,  /* alpha weighting of colors : kernel*alpha  */
02930                   gamma;  /* divisor, sum of color weighting values */
02931 
02932                 gamma=0.0;
02933                 for (v=0; v < (ssize_t) kernel->height; v++) {
02934                   for (u=0; u < (ssize_t) kernel->width; u++, k--) {
02935                     if ( IsNan(*k) ) continue;
02936                     alpha=(*k)*(QuantumScale*GetPixelAlpha(image,k_pixels+u*
02937                       GetPixelChannels(image)));
02938                     gamma += alpha;
02939                     result.red     += alpha*
02940                       GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
02941                     result.green   += alpha*
02942                       GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
02943                     result.blue    += alpha*
02944                       GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
02945                     if (image->colorspace == CMYKColorspace)
02946                       result.black+=alpha*
02947                         GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
02948                     result.alpha += (*k)*
02949                       GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
02950                   }
02951                   k_pixels += virt_width*GetPixelChannels(image);
02952                 }
02953                 /* Sync'ed channels, all channels are modified */
02954                 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02955                 SetPixelRed(morphology_image,
02956                   ClampToQuantum(gamma*result.red),q);
02957                 SetPixelGreen(morphology_image,
02958                   ClampToQuantum(gamma*result.green),q);
02959                 SetPixelBlue(morphology_image,
02960                   ClampToQuantum(gamma*result.blue),q);
02961                 if (image->colorspace == CMYKColorspace)
02962                   SetPixelBlack(morphology_image,
02963                     ClampToQuantum(gamma*result.black),q);
02964                 SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
02965               }
02966             break;
02967 
02968         case ErodeMorphology:
02969             /* Minimum Value within kernel neighbourhood
02970             **
02971             ** NOTE that the kernel is not reflected for this operation!
02972             **
02973             ** NOTE: in normal Greyscale Morphology, the kernel value should
02974             ** be added to the real value, this is currently not done, due to
02975             ** the nature of the boolean kernels being used.
02976             */
02977             k = kernel->values;
02978             k_pixels = p;
02979             for (v=0; v < (ssize_t) kernel->height; v++) {
02980               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
02981                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
02982                 Minimize(min.red,     (double)
02983                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
02984                 Minimize(min.green,   (double) 
02985                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
02986                 Minimize(min.blue,    (double)
02987                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
02988                 if (image->colorspace == CMYKColorspace)
02989                   Minimize(min.black,(double)
02990                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
02991                 Minimize(min.alpha,(double)
02992                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
02993               }
02994               k_pixels += virt_width*GetPixelChannels(image);
02995             }
02996             break;
02997 
02998         case DilateMorphology:
02999             /* Maximum Value within kernel neighbourhood
03000             **
03001             ** NOTE for correct working of this operation for asymetrical
03002             ** kernels, the kernel needs to be applied in its reflected form.
03003             ** That is its values needs to be reversed.
03004             **
03005             ** NOTE: in normal Greyscale Morphology, the kernel value should
03006             ** be added to the real value, this is currently not done, due to
03007             ** the nature of the boolean kernels being used.
03008             **
03009             */
03010             k = &kernel->values[ kernel->width*kernel->height-1 ];
03011             k_pixels = p;
03012             for (v=0; v < (ssize_t) kernel->height; v++) {
03013               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
03014                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
03015                 Maximize(max.red,     (double)
03016                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
03017                 Maximize(max.green,   (double) 
03018                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
03019                 Maximize(max.blue,    (double) 
03020                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
03021                 if (image->colorspace == CMYKColorspace)
03022                   Maximize(max.black,   (double)
03023                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
03024                 Maximize(max.alpha,(double) 
03025                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
03026               }
03027               k_pixels += virt_width*GetPixelChannels(image);
03028             }
03029             break;
03030 
03031         case HitAndMissMorphology:
03032         case ThinningMorphology:
03033         case ThickenMorphology:
03034             /* Minimum of Foreground Pixel minus Maxumum of Background Pixels
03035             **
03036             ** NOTE that the kernel is not reflected for this operation,
03037             ** and consists of both foreground and background pixel
03038             ** neighbourhoods, 0.0 for background, and 1.0 for foreground
03039             ** with either Nan or 0.5 values for don't care.
03040             **
03041             ** Note that this will never produce a meaningless negative
03042             ** result.  Such results can cause Thinning/Thicken to not work
03043             ** correctly when used against a greyscale image.
03044             */
03045             k = kernel->values;
03046             k_pixels = p;
03047             for (v=0; v < (ssize_t) kernel->height; v++) {
03048               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
03049                 if ( IsNan(*k) ) continue;
03050                 if ( (*k) > 0.7 )
03051                 { /* minimim of foreground pixels */
03052                   Minimize(min.red,     (double)
03053                     GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
03054                   Minimize(min.green,   (double)
03055                     GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
03056                   Minimize(min.blue,    (double)
03057                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
03058                   if ( image->colorspace == CMYKColorspace)
03059                     Minimize(min.black,(double)
03060                       GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
03061                   Minimize(min.alpha,(double)
03062                     GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
03063                 }
03064                 else if ( (*k) < 0.3 )
03065                 { /* maximum of background pixels */
03066                   Maximize(max.red,     (double)
03067                     GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
03068                   Maximize(max.green,   (double)
03069                     GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
03070                   Maximize(max.blue,    (double)
03071                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
03072                   if (image->colorspace == CMYKColorspace)
03073                     Maximize(max.black,   (double)
03074                       GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
03075                   Maximize(max.alpha,(double)
03076                     GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
03077                 }
03078               }
03079               k_pixels += virt_width*GetPixelChannels(image);
03080             }
03081             /* Pattern Match if difference is positive */
03082             min.red     -= max.red;     Maximize( min.red,     0.0 );
03083             min.green   -= max.green;   Maximize( min.green,   0.0 );
03084             min.blue    -= max.blue;    Maximize( min.blue,    0.0 );
03085             min.black   -= max.black;   Maximize( min.black,   0.0 );
03086             min.alpha -= max.alpha; Maximize( min.alpha, 0.0 );
03087             break;
03088 
03089         case ErodeIntensityMorphology:
03090             /* Select Pixel with Minimum Intensity within kernel neighbourhood
03091             **
03092             ** WARNING: the intensity test fails for CMYK and does not
03093             ** take into account the moderating effect of the alpha channel
03094             ** on the intensity.
03095             **
03096             ** NOTE that the kernel is not reflected for this operation!
03097             */
03098             k = kernel->values;
03099             k_pixels = p;
03100             for (v=0; v < (ssize_t) kernel->height; v++) {
03101               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
03102                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
03103                 if ( result.red == 0.0 ||
03104                      GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) < GetPixelIntensity(morphology_image,q) ) {
03105                   /* copy the whole pixel - no channel selection */
03106                   SetPixelRed(morphology_image,GetPixelRed(image,
03107                     k_pixels+u*GetPixelChannels(image)),q);
03108                   SetPixelGreen(morphology_image,GetPixelGreen(image,
03109                     k_pixels+u*GetPixelChannels(image)),q);
03110                   SetPixelBlue(morphology_image,GetPixelBlue(image,
03111                     k_pixels+u*GetPixelChannels(image)),q);
03112                   SetPixelAlpha(morphology_image,GetPixelAlpha(image,
03113                     k_pixels+u*GetPixelChannels(image)),q);
03114                   if ( result.red > 0.0 ) changed++;
03115                   result.red = 1.0;
03116                 }
03117               }
03118               k_pixels += virt_width*GetPixelChannels(image);
03119             }
03120             break;
03121 
03122         case DilateIntensityMorphology:
03123             /* Select Pixel with Maximum Intensity within kernel neighbourhood
03124             **
03125             ** WARNING: the intensity test fails for CMYK and does not
03126             ** take into account the moderating effect of the alpha channel
03127             ** on the intensity (yet).
03128             **
03129             ** NOTE for correct working of this operation for asymetrical
03130             ** kernels, the kernel needs to be applied in its reflected form.
03131             ** That is its values needs to be reversed.
03132             */
03133             k = &kernel->values[ kernel->width*kernel->height-1 ];
03134             k_pixels = p;
03135             for (v=0; v < (ssize_t) kernel->height; v++) {
03136               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
03137                 if ( IsNan(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
03138                 if ( result.red == 0.0 ||
03139                      GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) > GetPixelIntensity(morphology_image,q) ) {
03140                   /* copy the whole pixel - no channel selection */
03141                   SetPixelRed(morphology_image,GetPixelRed(image,
03142                     k_pixels+u*GetPixelChannels(image)),q);
03143                   SetPixelGreen(morphology_image,GetPixelGreen(image,
03144                     k_pixels+u*GetPixelChannels(image)),q);
03145                   SetPixelBlue(morphology_image,GetPixelBlue(image,
03146                     k_pixels+u*GetPixelChannels(image)),q);
03147                   SetPixelAlpha(morphology_image,GetPixelAlpha(image,
03148                     k_pixels+u*GetPixelChannels(image)),q);
03149                   if ( result.red > 0.0 ) changed++;
03150                   result.red = 1.0;
03151                 }
03152               }
03153               k_pixels += virt_width*GetPixelChannels(image);
03154             }
03155             break;
03156 #if 0
03157   This code has been obsoleted by the MorphologyPrimitiveDirect() function.
03158   However it is still (almost) correct coding for Grayscale Morphology.
03159   That is...
03160 
03161   GrayErode    is equivalent but with kernel values subtracted from pixels
03162                without the kernel rotation
03163   GreyDilate   is equivalent but using Maximum() instead of Minimum()
03164                using kernel rotation
03165 
03166   It has thus been preserved for future implementation of those methods.
03167 
03168         case DistanceMorphology:
03169             /* Add kernel Value and select the minimum value found.
03170             ** The result is a iterative distance from edge of image shape.
03171             **
03172             ** All Distance Kernels are symetrical, but that may not always
03173             ** be the case. For example how about a distance from left edges?
03174             ** To work correctly with asymetrical kernels the reflected kernel
03175             ** needs to be applied.
03176             */
03177             k = &kernel->values[ kernel->width*kernel->height-1 ];
03178             k_pixels = p;
03179             for (v=0; v < (ssize_t) kernel->height; v++) {
03180               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
03181                 if ( IsNan(*k) ) continue;
03182                 Minimize(result.red,     (*k)+k_pixels[u].red);
03183                 Minimize(result.green,   (*k)+k_pixels[u].green);
03184                 Minimize(result.blue,    (*k)+k_pixels[u].blue);
03185                 Minimize(result.alpha, (*k)+k_pixels[u].alpha);
03186                 if ( image->colorspace == CMYKColorspace)
03187                   Minimize(result.black,(*k)+GetPixelBlack(p_image,k_indexes+u));
03188               }
03189               k_pixels += virt_width*GetPixelChannels(image);
03190             }
03191             break;
03192 #endif
03193         case UndefinedMorphology:
03194         default:
03195             break; /* Do nothing */
03196       }
03197       /* Final mathematics of results (combine with original image?)
03198       **
03199       ** NOTE: Difference Morphology operators Edge* and *Hat could also
03200       ** be done here but works better with iteration as a image difference
03201       ** in the controling function (below).  Thicken and Thinning however
03202       ** should be done here so thay can be iterated correctly.
03203       */
03204       switch ( method ) {
03205         case HitAndMissMorphology:
03206         case ErodeMorphology:
03207           result = min;    /* minimum of neighbourhood */
03208           break;
03209         case DilateMorphology:
03210           result = max;    /* maximum of neighbourhood */
03211           break;
03212         case ThinningMorphology:
03213           /* subtract pattern match from original */
03214           result.red     -= min.red;
03215           result.green   -= min.green;
03216           result.blue    -= min.blue;
03217           result.black   -= min.black;
03218           result.alpha -= min.alpha;
03219           break;
03220         case ThickenMorphology:
03221           /* Add the pattern matchs to the original */
03222           result.red     += min.red;
03223           result.green   += min.green;
03224           result.blue    += min.blue;
03225           result.black   += min.black;
03226           result.alpha += min.alpha;
03227           break;
03228         default:
03229           /* result directly calculated or assigned */
03230           break;
03231       }
03232       /* Assign the resulting pixel values - Clamping Result */
03233       switch ( method ) {
03234         case UndefinedMorphology:
03235         case ConvolveMorphology:
03236         case DilateIntensityMorphology:
03237         case ErodeIntensityMorphology:
03238           break;  /* full pixel was directly assigned - not a channel method */
03239         default:
03240           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
03241             SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
03242           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
03243             SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
03244           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
03245             SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
03246           if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
03247               (image->colorspace == CMYKColorspace))
03248             SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
03249           if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
03250               (image->matte == MagickTrue))
03251             SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
03252           break;
03253       }
03254       /* Count up changed pixels */
03255       if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q)) ||
03256           (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q)) ||
03257           (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q)) ||
03258           (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q)) ||
03259           ((image->colorspace == CMYKColorspace) &&
03260            (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
03261         changed++;  /* The pixel was changed in some way! */
03262       p+=GetPixelChannels(image);
03263       q+=GetPixelChannels(morphology_image);
03264     } /* x */
03265     if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
03266       status=MagickFalse;
03267     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03268       {
03269         MagickBooleanType
03270           proceed;
03271 
03272 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03273   #pragma omp critical (MagickCore_MorphologyImage)
03274 #endif
03275         proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
03276         if (proceed == MagickFalse)
03277           status=MagickFalse;
03278       }
03279   } /* y */
03280   morphology_view=DestroyCacheView(morphology_view);
03281   image_view=DestroyCacheView(image_view);
03282   return(status ? (ssize_t)changed : -1);
03283 }
03284 
03285 /* This is almost identical to the MorphologyPrimative() function above,
03286 ** but will apply the primitive directly to the image in two passes.
03287 **
03288 ** That is after each row is 'Sync'ed' into the image, the next row will
03289 ** make use of those values as part of the calculation of the next row.
03290 ** It then repeats, but going in the oppisite (bottom-up) direction.
03291 **
03292 ** Because of this 'iterative' handling this function can not make use
03293 ** of multi-threaded, parellel processing.
03294 */
03295 static ssize_t MorphologyPrimitiveDirect(Image *image,
03296   const MorphologyMethod method,const KernelInfo *kernel,
03297   ExceptionInfo *exception)
03298 {
03299   CacheView
03300     *auth_view,
03301     *virt_view;
03302 
03303   MagickBooleanType
03304     status;
03305 
03306   MagickOffsetType
03307     progress;
03308 
03309   ssize_t
03310     y, offx, offy;
03311 
03312   size_t
03313     virt_width,
03314     changed;
03315 
03316   status=MagickTrue;
03317   changed=0;
03318   progress=0;
03319 
03320   assert(image != (Image *) NULL);
03321   assert(image->signature == MagickSignature);
03322   assert(kernel != (KernelInfo *) NULL);
03323   assert(kernel->signature == MagickSignature);
03324   assert(exception != (ExceptionInfo *) NULL);
03325   assert(exception->signature == MagickSignature);
03326 
03327   /* Some methods (including convolve) needs use a reflected kernel.
03328    * Adjust 'origin' offsets to loop though kernel as a reflection.
03329    */
03330   offx = kernel->x;
03331   offy = kernel->y;
03332   switch(method) {
03333     case DistanceMorphology:
03334     case VoronoiMorphology:
03335       /* kernel needs to used with reflection about origin */
03336       offx = (ssize_t) kernel->width-offx-1;
03337       offy = (ssize_t) kernel->height-offy-1;
03338       break;
03339 #if 0
03340     case ?????Morphology:
03341       /* kernel is used as is, without reflection */
03342       break;
03343 #endif
03344     default:
03345       assert("Not a PrimativeDirect Morphology Method" != (char *) NULL);
03346       break;
03347   }
03348 
03349   /* DO NOT THREAD THIS CODE! */
03350   /* two views into same image (virtual, and actual) */
03351   virt_view=AcquireCacheView(image);
03352   auth_view=AcquireCacheView(image);
03353   virt_width=image->columns+kernel->width-1;
03354 
03355   for (y=0; y < (ssize_t) image->rows; y++)
03356   {
03357     register const Quantum
03358       *restrict p;
03359 
03360     register Quantum
03361       *restrict q;
03362 
03363     register ssize_t
03364       x;
03365 
03366     ssize_t
03367       r;
03368 
03369     /* NOTE read virtual pixels, and authentic pixels, from the same image!
03370     ** we read using virtual to get virtual pixel handling, but write back
03371     ** into the same image.
03372     **
03373     ** Only top half of kernel is processed as we do a single pass downward
03374     ** through the image iterating the distance function as we go.
03375     */
03376     if (status == MagickFalse)
03377       break;
03378     p=GetCacheViewVirtualPixels(virt_view,-offx,y-offy,virt_width,(size_t)
03379       offy+1,exception);
03380     q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
03381       exception);
03382     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
03383       status=MagickFalse;
03384     if (status == MagickFalse)
03385       break;
03386 
03387     /* offset to origin in 'p'. while 'q' points to it directly */
03388     r = (ssize_t) virt_width*offy + offx;
03389 
03390     for (x=0; x < (ssize_t) image->columns; x++)
03391     {
03392       ssize_t
03393         v;
03394 
03395       register ssize_t
03396         u;
03397 
03398       register const MagickRealType
03399         *restrict k;
03400 
03401       register const Quantum
03402         *restrict k_pixels;
03403 
03404       PixelInfo
03405         result;
03406 
03407       /* Starting Defaults */
03408       GetPixelInfo(image,&result);
03409       GetPixelInfoPixel(image,q,&result);
03410       if ( method != VoronoiMorphology )
03411         result.alpha = QuantumRange - result.alpha;
03412 
03413       switch ( method ) {
03414         case DistanceMorphology:
03415             /* Add kernel Value and select the minimum value found. */
03416             k = &kernel->values[ kernel->width*kernel->height-1 ];
03417             k_pixels = p;
03418             for (v=0; v <= (ssize_t) offy; v++) {
03419               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
03420                 if ( IsNan(*k) ) continue;
03421                 Minimize(result.red,     (*k)+
03422                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
03423                 Minimize(result.green,   (*k)+
03424                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
03425                 Minimize(result.blue,    (*k)+
03426                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
03427                 if (image->colorspace == CMYKColorspace)
03428                   Minimize(result.black,(*k)+
03429                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
03430                 Minimize(result.alpha, (*k)+
03431                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
03432               }
03433               k_pixels += virt_width*GetPixelChannels(image);
03434             }
03435             /* repeat with the just processed pixels of this row */
03436             k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
03437             k_pixels = q-offx*GetPixelChannels(image);
03438               for (u=0; u < (ssize_t) offx; u++, k--) {
03439                 if ( x+u-offx < 0 ) continue;  /* off the edge! */
03440                 if ( IsNan(*k) ) continue;
03441                 Minimize(result.red,     (*k)+
03442                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
03443                 Minimize(result.green,   (*k)+
03444                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
03445                 Minimize(result.blue,    (*k)+
03446                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
03447                 if (image->colorspace == CMYKColorspace)
03448                   Minimize(result.black,(*k)+
03449                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
03450                 Minimize(result.alpha,(*k)+
03451                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
03452               }
03453             break;
03454         case VoronoiMorphology:
03455             /* Apply Distance to 'Matte' channel, coping the closest color.
03456             **
03457             ** This is experimental, and realy the 'alpha' component should
03458             ** be completely separate 'masking' channel.
03459             */
03460             k = &kernel->values[ kernel->width*kernel->height-1 ];
03461             k_pixels = p;
03462             for (v=0; v <= (ssize_t) offy; v++) {
03463               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
03464                 if ( IsNan(*k) ) continue;
03465                 if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
03466                   {
03467                     GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
03468                       &result);
03469                     result.alpha += *k;
03470                   }
03471               }
03472               k_pixels += virt_width*GetPixelChannels(image);
03473             }
03474             /* repeat with the just processed pixels of this row */
03475             k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
03476             k_pixels = q-offx*GetPixelChannels(image);
03477               for (u=0; u < (ssize_t) offx; u++, k--) {
03478                 if ( x+u-offx < 0 ) continue;  /* off the edge! */
03479                 if ( IsNan(*k) ) continue;
03480                 if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
03481                   {
03482                     GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
03483                       &result);
03484                     result.alpha += *k;
03485                   }
03486               }
03487             break;
03488         default:
03489           /* result directly calculated or assigned */
03490           break;
03491       }
03492       /* Assign the resulting pixel values - Clamping Result */
03493       switch ( method ) {
03494         case VoronoiMorphology:
03495           SetPixelInfoPixel(image,&result,q);
03496           break;
03497         default:
03498           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
03499             SetPixelRed(image,ClampToQuantum(result.red),q);
03500           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
03501             SetPixelGreen(image,ClampToQuantum(result.green),q);
03502           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
03503             SetPixelBlue(image,ClampToQuantum(result.blue),q);
03504           if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
03505               (image->colorspace == CMYKColorspace))
03506             SetPixelBlack(image,ClampToQuantum(result.black),q);
03507           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
03508               (image->matte == MagickTrue))
03509             SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
03510           break;
03511       }
03512       /* Count up changed pixels */
03513       if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q)) ||
03514           (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q)) ||
03515           (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q)) ||
03516           (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q)) ||
03517           ((image->colorspace == CMYKColorspace) &&
03518            (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
03519         changed++;  /* The pixel was changed in some way! */
03520 
03521       p+=GetPixelChannels(image); /* increment pixel buffers */
03522       q+=GetPixelChannels(image);
03523     } /* x */
03524 
03525     if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
03526       status=MagickFalse;
03527     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03528       if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
03529                 == MagickFalse )
03530         status=MagickFalse;
03531 
03532   } /* y */
03533 
03534   /* Do the reversed pass through the image */
03535   for (y=(ssize_t)image->rows-1; y >= 0; y--)
03536   {
03537     register const Quantum
03538       *restrict p;
03539 
03540     register Quantum
03541       *restrict q;
03542 
03543     register ssize_t
03544       x;
03545 
03546     ssize_t
03547       r;
03548 
03549     if (status == MagickFalse)
03550       break;
03551     /* NOTE read virtual pixels, and authentic pixels, from the same image!
03552     ** we read using virtual to get virtual pixel handling, but write back
03553     ** into the same image.
03554     **
03555     ** Only the bottom half of the kernel will be processes as we
03556     ** up the image.
03557     */
03558     p=GetCacheViewVirtualPixels(virt_view,-offx,y,virt_width,(size_t)
03559       kernel->y+1,exception);
03560     q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
03561       exception);
03562     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
03563       status=MagickFalse;
03564     if (status == MagickFalse)
03565       break;
03566 
03567     /* adjust positions to end of row */
03568     p += (image->columns-1)*GetPixelChannels(image);
03569     q += (image->columns-1)*GetPixelChannels(image);
03570 
03571     /* offset to origin in 'p'. while 'q' points to it directly */
03572     r = offx;
03573 
03574     for (x=(ssize_t)image->columns-1; x >= 0; x--)
03575     {
03576       ssize_t
03577         v;
03578 
03579       register ssize_t
03580         u;
03581 
03582       register const MagickRealType
03583         *restrict k;
03584 
03585       register const Quantum
03586         *restrict k_pixels;
03587 
03588       PixelInfo
03589         result;
03590 
03591       /* Default - previously modified pixel */
03592       GetPixelInfo(image,&result);
03593       GetPixelInfoPixel(image,q,&result);
03594       if ( method != VoronoiMorphology )
03595         result.alpha = QuantumRange - result.alpha;
03596 
03597       switch ( method ) {
03598         case DistanceMorphology:
03599             /* Add kernel Value and select the minimum value found. */
03600             k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
03601             k_pixels = p;
03602             for (v=offy; v < (ssize_t) kernel->height; v++) {
03603               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
03604                 if ( IsNan(*k) ) continue;
03605                 Minimize(result.red,     (*k)+
03606                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
03607                 Minimize(result.green,   (*k)+
03608                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
03609                 Minimize(result.blue,    (*k)+
03610                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
03611                 if ( image->colorspace == CMYKColorspace)
03612                   Minimize(result.black,(*k)+
03613                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
03614                 Minimize(result.alpha, (*k)+
03615                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
03616               }
03617               k_pixels += virt_width*GetPixelChannels(image);
03618             }
03619             /* repeat with the just processed pixels of this row */
03620             k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
03621             k_pixels = q-offx*GetPixelChannels(image);
03622               for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
03623                 if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
03624                 if ( IsNan(*k) ) continue;
03625                 Minimize(result.red,     (*k)+
03626                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
03627                 Minimize(result.green,   (*k)+
03628                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
03629                 Minimize(result.blue,    (*k)+
03630                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
03631                 if ( image->colorspace == CMYKColorspace)
03632                   Minimize(result.black,   (*k)+
03633                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
03634                 Minimize(result.alpha, (*k)+
03635                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
03636               }
03637             break;
03638         case