]> granicus.if.org Git - imagemagick/blob - magick/morphology.c
(no commit message)
[imagemagick] / magick / morphology.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %    M   M    OOO    RRRR   PPPP   H   H   OOO   L       OOO    GGGG  Y   Y   %
7 %    MM MM   O   O   R   R  P   P  H   H  O   O  L      O   O  G       Y Y    %
8 %    M M M   O   O   RRRR   PPPP   HHHHH  O   O  L      O   O  G GGG    Y     %
9 %    M   M   O   O   R R    P      H   H  O   O  L      O   O  G   G    Y     %
10 %    M   M    OOO    R  R   P      H   H   OOO   LLLLL   OOO    GGG     Y     %
11 %                                                                             %
12 %                                                                             %
13 %                        MagickCore Morphology Methods                        %
14 %                                                                             %
15 %                              Software Design                                %
16 %                              Anthony Thyssen                                %
17 %                               January 2010                                  %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % Morpology is the the application of various kernels, of any size and even
37 % shape, to a image in various ways (typically binary, but not always).
38 %
39 % Convolution (weighted sum or average) is just one specific type of
40 % morphology. Just one that is very common for image bluring and sharpening
41 % effects.  Not only 2D Gaussian blurring, but also 2-pass 1D Blurring.
42 %
43 % This module provides not only a general morphology function, and the ability
44 % to apply more advanced or iterative morphologies, but also functions for the
45 % generation of many different types of kernel arrays from user supplied
46 % arguments. Prehaps even the generation of a kernel from a small image.
47 */
48 \f
49 /*
50   Include declarations.
51 */
52 #include "magick/studio.h"
53 #include "magick/artifact.h"
54 #include "magick/cache-view.h"
55 #include "magick/color-private.h"
56 #include "magick/enhance.h"
57 #include "magick/exception.h"
58 #include "magick/exception-private.h"
59 #include "magick/gem.h"
60 #include "magick/hashmap.h"
61 #include "magick/image.h"
62 #include "magick/image-private.h"
63 #include "magick/list.h"
64 #include "magick/magick.h"
65 #include "magick/memory_.h"
66 #include "magick/monitor-private.h"
67 #include "magick/morphology.h"
68 #include "magick/morphology-private.h"
69 #include "magick/option.h"
70 #include "magick/pixel-private.h"
71 #include "magick/prepress.h"
72 #include "magick/quantize.h"
73 #include "magick/registry.h"
74 #include "magick/semaphore.h"
75 #include "magick/splay-tree.h"
76 #include "magick/statistic.h"
77 #include "magick/string_.h"
78 #include "magick/string-private.h"
79 #include "magick/token.h"
80 \f
81
82 /*
83 ** The following test is for special floating point numbers of value NaN (not
84 ** a number), that may be used within a Kernel Definition.  NaN's are defined
85 ** as part of the IEEE standard for floating point number representation.
86 **
87 ** These are used as a Kernel value to mean that this kernel position is not
88 ** part of the kernel neighbourhood for convolution or morphology processing,
89 ** and thus should be ignored.  This allows the use of 'shaped' kernels.
90 **
91 ** The special properity that two NaN's are never equal, even if they are from
92 ** the same variable allow you to test if a value is special NaN value.
93 **
94 ** This macro  IsNaN() is thus is only true if the value given is NaN.
95 */
96 #define IsNan(a)   ((a)!=(a))
97
98 /*
99   Other global definitions used by module.
100 */
101 static inline double MagickMin(const double x,const double y)
102 {
103   return( x < y ? x : y);
104 }
105 static inline double MagickMax(const double x,const double y)
106 {
107   return( x > y ? x : y);
108 }
109 #define Minimize(assign,value) assign=MagickMin(assign,value)
110 #define Maximize(assign,value) assign=MagickMax(assign,value)
111
112 /* Currently these are only internal to this module */
113 static void
114   CalcKernelMetaData(KernelInfo *),
115   ExpandMirrorKernelInfo(KernelInfo *),
116   ExpandRotateKernelInfo(KernelInfo *, const double),
117   RotateKernelInfo(KernelInfo *, double);
118 \f
119
120 /* Quick function to find last kernel in a kernel list */
121 static inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
122 {
123   while (kernel->next != (KernelInfo *) NULL)
124     kernel = kernel->next;
125   return(kernel);
126 }
127
128
129 /*
130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131 %                                                                             %
132 %                                                                             %
133 %                                                                             %
134 %     A c q u i r e K e r n e l I n f o                                       %
135 %                                                                             %
136 %                                                                             %
137 %                                                                             %
138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139 %
140 %  AcquireKernelInfo() takes the given string (generally supplied by the
141 %  user) and converts it into a Morphology/Convolution Kernel.  This allows
142 %  users to specify a kernel from a number of pre-defined kernels, or to fully
143 %  specify their own kernel for a specific Convolution or Morphology
144 %  Operation.
145 %
146 %  The kernel so generated can be any rectangular array of floating point
147 %  values (doubles) with the 'control point' or 'pixel being affected'
148 %  anywhere within that array of values.
149 %
150 %  Previously IM was restricted to a square of odd size using the exact
151 %  center as origin, this is no ssize_ter the case, and any rectangular kernel
152 %  with any value being declared the origin. This in turn allows the use of
153 %  highly asymmetrical kernels.
154 %
155 %  The floating point values in the kernel can also include a special value
156 %  known as 'nan' or 'not a number' to indicate that this value is not part
157 %  of the kernel array. This allows you to shaped the kernel within its
158 %  rectangular area. That is 'nan' values provide a 'mask' for the kernel
159 %  shape.  However at least one non-nan value must be provided for correct
160 %  working of a kernel.
161 %
162 %  The returned kernel should be freed using the DestroyKernelInfo() when you
163 %  are finished with it.  Do not free this memory yourself.
164 %
165 %  Input kernel defintion strings can consist of any of three types.
166 %
167 %    "name:args[[@><]"
168 %         Select from one of the built in kernels, using the name and
169 %         geometry arguments supplied.  See AcquireKernelBuiltIn()
170 %
171 %    "WxH[+X+Y][@><]:num, num, num ..."
172 %         a kernel of size W by H, with W*H floating point numbers following.
173 %         the 'center' can be optionally be defined at +X+Y (such that +0+0
174 %         is top left corner). If not defined the pixel in the center, for
175 %         odd sizes, or to the immediate top or left of center for even sizes
176 %         is automatically selected.
177 %
178 %    "num, num, num, num, ..."
179 %         list of floating point numbers defining an 'old style' odd sized
180 %         square kernel.  At least 9 values should be provided for a 3x3
181 %         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
182 %         Values can be space or comma separated.  This is not recommended.
183 %
184 %  You can define a 'list of kernels' which can be used by some morphology
185 %  operators A list is defined as a semi-colon seperated list kernels.
186 %
187 %     " kernel ; kernel ; kernel ; "
188 %
189 %  Any extra ';' characters, at start, end or between kernel defintions are
190 %  simply ignored.
191 %
192 %  The special flags will expand a single kernel, into a list of rotated
193 %  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
194 %  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
195 %  The '<' also exands using 90-degree rotates, but giving a 180-degree
196 %  reflected kernel before the +/- 90-degree rotations, which can be important
197 %  for Thinning operations.
198 %
199 %  Note that 'name' kernels will start with an alphabetic character while the
200 %  new kernel specification has a ':' character in its specification string.
201 %  If neither is the case, it is assumed an old style of a simple list of
202 %  numbers generating a odd-sized square kernel has been given.
203 %
204 %  The format of the AcquireKernal method is:
205 %
206 %      KernelInfo *AcquireKernelInfo(const char *kernel_string)
207 %
208 %  A description of each parameter follows:
209 %
210 %    o kernel_string: the Morphology/Convolution kernel wanted.
211 %
212 */
213
214 /* This was separated so that it could be used as a separate
215 ** array input handling function, such as for -color-matrix
216 */
217 static KernelInfo *ParseKernelArray(const char *kernel_string)
218 {
219   KernelInfo
220     *kernel;
221
222   char
223     token[MaxTextExtent];
224
225   const char
226     *p,
227     *end;
228
229   register ssize_t
230     i;
231
232   double
233     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
234
235   MagickStatusType
236     flags;
237
238   GeometryInfo
239     args;
240
241   kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
242   if (kernel == (KernelInfo *)NULL)
243     return(kernel);
244   (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
245   kernel->minimum = kernel->maximum = kernel->angle = 0.0;
246   kernel->negative_range = kernel->positive_range = 0.0;
247   kernel->type = UserDefinedKernel;
248   kernel->next = (KernelInfo *) NULL;
249   kernel->signature = MagickSignature;
250
251   /* find end of this specific kernel definition string */
252   end = strchr(kernel_string, ';');
253   if ( end == (char *) NULL )
254     end = strchr(kernel_string, '\0');
255
256   /* clear flags - for Expanding kernal lists thorugh rotations */
257    flags = NoValue;
258
259   /* Has a ':' in argument - New user kernel specification */
260   p = strchr(kernel_string, ':');
261   if ( p != (char *) NULL && p < end)
262     {
263       /* ParseGeometry() needs the geometry separated! -- Arrgghh */
264       memcpy(token, kernel_string, (size_t) (p-kernel_string));
265       token[p-kernel_string] = '\0';
266       SetGeometryInfo(&args);
267       flags = ParseGeometry(token, &args);
268
269       /* Size handling and checks of geometry settings */
270       if ( (flags & WidthValue) == 0 ) /* if no width then */
271         args.rho = args.sigma;         /* then  width = height */
272       if ( args.rho < 1.0 )            /* if width too small */
273          args.rho = 1.0;               /* then  width = 1 */
274       if ( args.sigma < 1.0 )          /* if height too small */
275         args.sigma = args.rho;         /* then  height = width */
276       kernel->width = (size_t)args.rho;
277       kernel->height = (size_t)args.sigma;
278
279       /* Offset Handling and Checks */
280       if ( args.xi  < 0.0 || args.psi < 0.0 )
281         return(DestroyKernelInfo(kernel));
282       kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
283                                                : (ssize_t) (kernel->width-1)/2;
284       kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
285                                                : (ssize_t) (kernel->height-1)/2;
286       if ( kernel->x >= (ssize_t) kernel->width ||
287            kernel->y >= (ssize_t) kernel->height )
288         return(DestroyKernelInfo(kernel));
289
290       p++; /* advance beyond the ':' */
291     }
292   else
293     { /* ELSE - Old old specification, forming odd-square kernel */
294       /* count up number of values given */
295       p=(const char *) kernel_string;
296       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
297         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
298       for (i=0; p < end; i++)
299       {
300         GetMagickToken(p,&p,token);
301         if (*token == ',')
302           GetMagickToken(p,&p,token);
303       }
304       /* set the size of the kernel - old sized square */
305       kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
306       kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
307       p=(const char *) kernel_string;
308       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
309         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
310     }
311
312   /* Read in the kernel values from rest of input string argument */
313   kernel->values=(double *) AcquireQuantumMemory(kernel->width,
314                         kernel->height*sizeof(double));
315   if (kernel->values == (double *) NULL)
316     return(DestroyKernelInfo(kernel));
317
318   kernel->minimum = +MagickHuge;
319   kernel->maximum = -MagickHuge;
320   kernel->negative_range = kernel->positive_range = 0.0;
321
322   for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
323   {
324     GetMagickToken(p,&p,token);
325     if (*token == ',')
326       GetMagickToken(p,&p,token);
327     if (    LocaleCompare("nan",token) == 0
328         || LocaleCompare("-",token) == 0 ) {
329       kernel->values[i] = nan; /* do not include this value in kernel */
330     }
331     else {
332       kernel->values[i] = StringToDouble(token);
333       ( kernel->values[i] < 0)
334           ?  ( kernel->negative_range += kernel->values[i] )
335           :  ( kernel->positive_range += kernel->values[i] );
336       Minimize(kernel->minimum, kernel->values[i]);
337       Maximize(kernel->maximum, kernel->values[i]);
338     }
339   }
340
341   /* sanity check -- no more values in kernel definition */
342   GetMagickToken(p,&p,token);
343   if ( *token != '\0' && *token != ';' && *token != '\'' )
344     return(DestroyKernelInfo(kernel));
345
346 #if 0
347   /* this was the old method of handling a incomplete kernel */
348   if ( i < (ssize_t) (kernel->width*kernel->height) ) {
349     Minimize(kernel->minimum, kernel->values[i]);
350     Maximize(kernel->maximum, kernel->values[i]);
351     for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
352       kernel->values[i]=0.0;
353   }
354 #else
355   /* Number of values for kernel was not enough - Report Error */
356   if ( i < (ssize_t) (kernel->width*kernel->height) )
357     return(DestroyKernelInfo(kernel));
358 #endif
359
360   /* check that we recieved at least one real (non-nan) value! */
361   if ( kernel->minimum == MagickHuge )
362     return(DestroyKernelInfo(kernel));
363
364   if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
365     ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
366   else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
367     ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
368   else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
369     ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
370
371   return(kernel);
372 }
373
374 static KernelInfo *ParseKernelName(const char *kernel_string)
375 {
376   KernelInfo
377     *kernel;
378
379   char
380     token[MaxTextExtent];
381
382   ssize_t
383     type;
384
385   const char
386     *p,
387     *end;
388
389   MagickStatusType
390     flags;
391
392   GeometryInfo
393     args;
394
395   /* Parse special 'named' kernel */
396   GetMagickToken(kernel_string,&p,token);
397   type=ParseMagickOption(MagickKernelOptions,MagickFalse,token);
398   if ( type < 0 || type == UserDefinedKernel )
399     return((KernelInfo *)NULL);  /* not a valid named kernel */
400
401   while (((isspace((int) ((unsigned char) *p)) != 0) ||
402           (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
403     p++;
404
405   end = strchr(p, ';'); /* end of this kernel defintion */
406   if ( end == (char *) NULL )
407     end = strchr(p, '\0');
408
409   /* ParseGeometry() needs the geometry separated! -- Arrgghh */
410   memcpy(token, p, (size_t) (end-p));
411   token[end-p] = '\0';
412   SetGeometryInfo(&args);
413   flags = ParseGeometry(token, &args);
414
415 #if 0
416   /* For Debugging Geometry Input */
417   fprintf(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
418        flags, args.rho, args.sigma, args.xi, args.psi );
419 #endif
420
421   /* special handling of missing values in input string */
422   switch( type ) {
423     case RectangleKernel:
424       if ( (flags & WidthValue) == 0 ) /* if no width then */
425         args.rho = args.sigma;         /* then  width = height */
426       if ( args.rho < 1.0 )            /* if width too small */
427           args.rho = 3;                 /* then  width = 3 */
428       if ( args.sigma < 1.0 )          /* if height too small */
429         args.sigma = args.rho;         /* then  height = width */
430       if ( (flags & XValue) == 0 )     /* center offset if not defined */
431         args.xi = (double)(((ssize_t)args.rho-1)/2);
432       if ( (flags & YValue) == 0 )
433         args.psi = (double)(((ssize_t)args.sigma-1)/2);
434       break;
435     case SquareKernel:
436     case DiamondKernel:
437     case DiskKernel:
438     case PlusKernel:
439     case CrossKernel:
440       /* If no scale given (a 0 scale is valid! - set it to 1.0 */
441       if ( (flags & HeightValue) == 0 )
442         args.sigma = 1.0;
443       break;
444     case RingKernel:
445       if ( (flags & XValue) == 0 )
446         args.xi = 1.0;
447       break;
448     case ChebyshevKernel:
449     case ManhattanKernel:
450     case EuclideanKernel:
451       if ( (flags & HeightValue) == 0 )           /* no distance scale */
452         args.sigma = 100.0;                       /* default distance scaling */
453       else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
454         args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
455       else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
456         args.sigma *= QuantumRange/100.0;         /* percentage of color range */
457       break;
458     default:
459       break;
460   }
461
462   kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
463
464   /* global expand to rotated kernel list - only for single kernels */
465   if ( kernel->next == (KernelInfo *) NULL ) {
466     if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
467       ExpandRotateKernelInfo(kernel, 45.0);
468     else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
469       ExpandRotateKernelInfo(kernel, 90.0);
470     else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
471       ExpandMirrorKernelInfo(kernel);
472   }
473
474   return(kernel);
475 }
476
477 MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
478 {
479
480   KernelInfo
481     *kernel,
482     *new_kernel;
483
484   char
485     token[MaxTextExtent];
486
487   const char
488     *p;
489
490   size_t
491     kernel_number;
492
493   p = kernel_string;
494   kernel = NULL;
495   kernel_number = 0;
496
497   while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
498
499     /* ignore extra or multiple ';' kernel seperators */
500     if ( *token != ';' ) {
501
502       /* tokens starting with alpha is a Named kernel */
503       if (isalpha((int) *token) != 0)
504         new_kernel = ParseKernelName(p);
505       else /* otherwise a user defined kernel array */
506         new_kernel = ParseKernelArray(p);
507
508       /* Error handling -- this is not proper error handling! */
509       if ( new_kernel == (KernelInfo *) NULL ) {
510         fprintf(stderr, "Failed to parse kernel number #%.20g\n",(double)
511           kernel_number);
512         if ( kernel != (KernelInfo *) NULL )
513           kernel=DestroyKernelInfo(kernel);
514         return((KernelInfo *) NULL);
515       }
516
517       /* initialise or append the kernel list */
518       if ( kernel == (KernelInfo *) NULL )
519         kernel = new_kernel;
520       else
521         LastKernelInfo(kernel)->next = new_kernel;
522     }
523
524     /* look for the next kernel in list */
525     p = strchr(p, ';');
526     if ( p == (char *) NULL )
527       break;
528     p++;
529
530   }
531   return(kernel);
532 }
533
534 \f
535 /*
536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
537 %                                                                             %
538 %                                                                             %
539 %                                                                             %
540 %     A c q u i r e K e r n e l B u i l t I n                                 %
541 %                                                                             %
542 %                                                                             %
543 %                                                                             %
544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
545 %
546 %  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
547 %  kernels used for special purposes such as gaussian blurring, skeleton
548 %  pruning, and edge distance determination.
549 %
550 %  They take a KernelType, and a set of geometry style arguments, which were
551 %  typically decoded from a user supplied string, or from a more complex
552 %  Morphology Method that was requested.
553 %
554 %  The format of the AcquireKernalBuiltIn method is:
555 %
556 %      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
557 %           const GeometryInfo args)
558 %
559 %  A description of each parameter follows:
560 %
561 %    o type: the pre-defined type of kernel wanted
562 %
563 %    o args: arguments defining or modifying the kernel
564 %
565 %  Convolution Kernels
566 %
567 %    Unity
568 %       the No-Op kernel, also requivelent to  Gaussian of sigma zero.
569 %       Basically a 3x3 kernel of a 1 surrounded by zeros.
570 %
571 %    Gaussian:{radius},{sigma}
572 %       Generate a two-dimentional gaussian kernel, as used by -gaussian.
573 %       The sigma for the curve is required.  The resulting kernel is
574 %       normalized,
575 %
576 %       If 'sigma' is zero, you get a single pixel on a field of zeros.
577 %
578 %       NOTE: that the 'radius' is optional, but if provided can limit (clip)
579 %       the final size of the resulting kernel to a square 2*radius+1 in size.
580 %       The radius should be at least 2 times that of the sigma value, or
581 %       sever clipping and aliasing may result.  If not given or set to 0 the
582 %       radius will be determined so as to produce the best minimal error
583 %       result, which is usally much larger than is normally needed.
584 %
585 %    LoG:{radius},{sigma}
586 %        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
587 %        The supposed ideal edge detection, zero-summing kernel.
588 %
589 %        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
590 %        approx 1.6 (according to wikipedia).
591 %
592 %    DoG:{radius},{sigma1},{sigma2}
593 %        "Difference of Gaussians" Kernel.
594 %        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
595 %        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
596 %        The result is a zero-summing kernel.
597 %
598 %    Blur:{radius},{sigma}[,{angle}]
599 %       Generates a 1 dimensional or linear gaussian blur, at the angle given
600 %       (current restricted to orthogonal angles).  If a 'radius' is given the
601 %       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
602 %       by a 90 degree angle.
603 %
604 %       If 'sigma' is zero, you get a single pixel on a field of zeros.
605 %
606 %       Note that two convolutions with two "Blur" kernels perpendicular to
607 %       each other, is equivelent to a far larger "Gaussian" kernel with the
608 %       same sigma value, However it is much faster to apply. This is how the
609 %       "-blur" operator actually works.
610 %
611 %    Comet:{width},{sigma},{angle}
612 %       Blur in one direction only, much like how a bright object leaves
613 %       a comet like trail.  The Kernel is actually half a gaussian curve,
614 %       Adding two such blurs in opposite directions produces a Blur Kernel.
615 %       Angle can be rotated in multiples of 90 degrees.
616 %
617 %       Note that the first argument is the width of the kernel and not the
618 %       radius of the kernel.
619 %
620 %    # Still to be implemented...
621 %    #
622 %    # Filter2D
623 %    # Filter1D
624 %    #    Set kernel values using a resize filter, and given scale (sigma)
625 %    #    Cylindrical or Linear.   Is this posible with an image?
626 %    #
627 %
628 %  Named Constant Convolution Kernels
629 %
630 %  All these are unscaled, zero-summing kernels by default. As such for
631 %  non-HDRI version of ImageMagick some form of normalization, user scaling,
632 %  and biasing the results is recommended, to prevent the resulting image
633 %  being 'clipped'.
634 %
635 %  The 3x3 kernels (most of these) can be circularly rotated in multiples of
636 %  45 degrees to generate the 8 angled varients of each of the kernels.
637 %
638 %    Laplacian:{type}
639 %      Discrete Lapacian Kernels, (without normalization)
640 %        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
641 %        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
642 %        Type 2 :  3x3 with center:4 edge:1 corner:-2
643 %        Type 3 :  3x3 with center:4 edge:-2 corner:1
644 %        Type 5 :  5x5 laplacian
645 %        Type 7 :  7x7 laplacian
646 %        Type 15 : 5x5 LoG (sigma approx 1.4)
647 %        Type 19 : 9x9 LoG (sigma approx 1.4)
648 %
649 %    Sobel:{angle}
650 %      Sobel 'Edge' convolution kernel (3x3)
651 %           -1, 0, 1
652 %           -2, 0,-2
653 %           -1, 0, 1
654 %
655 %    Roberts:{angle}
656 %      Roberts convolution kernel (3x3)
657 %            0, 0, 0
658 %           -1, 1, 0
659 %            0, 0, 0
660 %    Prewitt:{angle}
661 %      Prewitt Edge convolution kernel (3x3)
662 %           -1, 0, 1
663 %           -1, 0, 1
664 %           -1, 0, 1
665 %    Compass:{angle}
666 %      Prewitt's "Compass" convolution kernel (3x3)
667 %           -1, 1, 1
668 %           -1,-2, 1
669 %           -1, 1, 1
670 %    Kirsch:{angle}
671 %      Kirsch's "Compass" convolution kernel (3x3)
672 %           -3,-3, 5
673 %           -3, 0, 5
674 %           -3,-3, 5
675 %
676 %    FreiChen:{type},{angle}
677 %      Frei-Chen Edge Detector is based on a kernel that is similar to
678 %      the Sobel Kernel, but is designed to be isotropic. That is it takes
679 %      into account the distance of the diagonal in the kernel.
680 %
681 %        Type 0: |   1,     0,   -1     |
682 %                | sqrt(2), 0, -sqrt(2) |
683 %                |   1,     0,   -1     |
684 %
685 %      However this kernel is als at the heart of the FreiChen Edge Detection
686 %      Process which uses a set of 9 specially weighted kernel.  These 9
687 %      kernels not be normalized, but directly applied to the image. The
688 %      results is then added together, to produce the intensity of an edge in
689 %      a specific direction.  The square root of the pixel value can then be
690 %      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
691 %      from each other, both the direction and the strength of the edge can be
692 %      determined.
693 %
694 %        Type 1: |   1,     0,   -1     |
695 %                | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
696 %                |   1,     0,   -1     |
697 %
698 %        Type 2: | 1, sqrt(2), 1 |
699 %                | 0,   0,     0 | / 2*sqrt(2)
700 %                | 1, sqrt(2), 1 |
701 %
702 %        Type 3: | sqrt(2), -1,    0     |
703 %                |  -1,      0,    1     | / 2*sqrt(2)
704 %                |   0,      1, -sqrt(2) |
705 %
706 %        Type 4: |    0,     1, -sqrt(2) |
707 %                |   -1,     0,     1    | / 2*sqrt(2)
708 %                | sqrt(2), -1,     0    |
709 %
710 %        Type 5: | 0, -1, 0 |
711 %                | 1,  0, 1 | / 2
712 %                | 0, -1, 0 |
713 %
714 %        Type 6: |  1, 0, -1 |
715 %                |  0, 0,  0 | / 2
716 %                | -1, 0,  1 |
717 %
718 %        Type 7: |  1, -2,  1 |
719 %                | -2,  4, -2 | / 6
720 %                | -1, -2,  1 |
721 %
722 %        Type 8: | -2, 1, -2 |
723 %                |  1, 4,  1 | / 6
724 %                | -2, 1, -2 |
725 %
726 %        Type 9: | 1, 1, 1 |
727 %                | 1, 1, 1 | / 3
728 %                | 1, 1, 1 |
729 %
730 %      The first 4 are for edge detection, the next 4 are for line detection
731 %      and the last is to add a average component to the results.
732 %
733 %      Using a special type of '-1' will return all 9 pre-weighted kernels
734 %      as a multi-kernel list, so that you can use them directly (without
735 %      normalization) with the special "-set option:morphology:compose Plus"
736 %      setting to apply the full FreiChen Edge Detection Technique.
737 %
738 %      If 'type' is large it will be taken to be an actual rotation angle for
739 %      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
740 %      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
741 %
742 %      WARNING: The above was layed out as per
743 %          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
744 %      But rotated 90 degrees so direction is from left rather than the top.
745 %      I have yet to find any secondary confirmation of the above. The only
746 %      other source found was actual source code at
747 %          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
748 %      Neigher paper defineds the kernels in a way that looks locical or
749 %      correct when taken as a whole.
750 %
751 %  Boolean Kernels
752 %
753 %    Diamond:[{radius}[,{scale}]]
754 %       Generate a diamond shaped kernel with given radius to the points.
755 %       Kernel size will again be radius*2+1 square and defaults to radius 1,
756 %       generating a 3x3 kernel that is slightly larger than a square.
757 %
758 %    Square:[{radius}[,{scale}]]
759 %       Generate a square shaped kernel of size radius*2+1, and defaulting
760 %       to a 3x3 (radius 1).
761 %
762 %       Note that using a larger radius for the "Square" or the "Diamond" is
763 %       also equivelent to iterating the basic morphological method that many
764 %       times. However iterating with the smaller radius is actually faster
765 %       than using a larger kernel radius.
766 %
767 %    Rectangle:{geometry}
768 %       Simply generate a rectangle of 1's with the size given. You can also
769 %       specify the location of the 'control point', otherwise the closest
770 %       pixel to the center of the rectangle is selected.
771 %
772 %       Properly centered and odd sized rectangles work the best.
773 %
774 %    Disk:[{radius}[,{scale}]]
775 %       Generate a binary disk of the radius given, radius may be a float.
776 %       Kernel size will be ceil(radius)*2+1 square.
777 %       NOTE: Here are some disk shapes of specific interest
778 %          "Disk:1"    => "diamond" or "cross:1"
779 %          "Disk:1.5"  => "square"
780 %          "Disk:2"    => "diamond:2"
781 %          "Disk:2.5"  => a general disk shape of radius 2
782 %          "Disk:2.9"  => "square:2"
783 %          "Disk:3.5"  => default - octagonal/disk shape of radius 3
784 %          "Disk:4.2"  => roughly octagonal shape of radius 4
785 %          "Disk:4.3"  => a general disk shape of radius 4
786 %       After this all the kernel shape becomes more and more circular.
787 %
788 %       Because a "disk" is more circular when using a larger radius, using a
789 %       larger radius is preferred over iterating the morphological operation.
790 %
791 %  Symbol Dilation Kernels
792 %
793 %    These kernel is not a good general morphological kernel, but is used
794 %    more for highlighting and marking any single pixels in an image using,
795 %    a "Dilate" method as appropriate.
796 %
797 %    For the same reasons iterating these kernels does not produce the
798 %    same result as using a larger radius for the symbol.
799 %
800 %    Plus:[{radius}[,{scale}]]
801 %    Cross:[{radius}[,{scale}]]
802 %       Generate a kernel in the shape of a 'plus' or a 'cross' with
803 %       a each arm the length of the given radius (default 2).
804 %
805 %       NOTE: "plus:1" is equivelent to a "Diamond" kernel.
806 %
807 %    Ring:{radius1},{radius2}[,{scale}]
808 %       A ring of the values given that falls between the two radii.
809 %       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
810 %       This is the 'edge' pixels of the default "Disk" kernel,
811 %       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
812 %
813 %  Hit and Miss Kernels
814 %
815 %    Peak:radius1,radius2
816 %       Find any peak larger than the pixels the fall between the two radii.
817 %       The default ring of pixels is as per "Ring".
818 %    Edges
819 %       Find edges of a binary shape
820 %    Corners
821 %       Find corners of a binary shape
822 %    Ridges
823 %       Find single pixel ridges or thin lines
824 %    Ridges2
825 %       Find 2 pixel thick ridges or lines
826 %    Ridges3
827 %       Find 2 pixel thick diagonal ridges (experimental)
828 %    LineEnds
829 %       Find end points of lines (for pruning a skeletion)
830 %    LineJunctions
831 %       Find three line junctions (within a skeletion)
832 %    ConvexHull
833 %       Octagonal thicken kernel, to generate convex hulls of 45 degrees
834 %    Skeleton
835 %       Thinning kernel, which leaves behind a skeletion of a shape
836 %
837 %  Distance Measuring Kernels
838 %
839 %    Different types of distance measuring methods, which are used with the
840 %    a 'Distance' morphology method for generating a gradient based on
841 %    distance from an edge of a binary shape, though there is a technique
842 %    for handling a anti-aliased shape.
843 %
844 %    See the 'Distance' Morphological Method, for information of how it is
845 %    applied.
846 %
847 %    Chebyshev:[{radius}][x{scale}[%!]]
848 %       Chebyshev Distance (also known as Tchebychev Distance) is a value of
849 %       one to any neighbour, orthogonal or diagonal. One why of thinking of
850 %       it is the number of squares a 'King' or 'Queen' in chess needs to
851 %       traverse reach any other position on a chess board.  It results in a
852 %       'square' like distance function, but one where diagonals are closer
853 %       than expected.
854 %
855 %    Manhattan:[{radius}][x{scale}[%!]]
856 %       Manhattan Distance (also known as Rectilinear Distance, or the Taxi
857 %       Cab metric), is the distance needed when you can only travel in
858 %       orthogonal (horizontal or vertical) only.  It is the distance a 'Rook'
859 %       in chess would travel. It results in a diamond like distances, where
860 %       diagonals are further than expected.
861 %
862 %    Euclidean:[{radius}][x{scale}[%!]]
863 %       Euclidean Distance is the 'direct' or 'as the crow flys distance.
864 %       However by default the kernel size only has a radius of 1, which
865 %       limits the distance to 'Knight' like moves, with only orthogonal and
866 %       diagonal measurements being correct.  As such for the default kernel
867 %       you will get octagonal like distance function, which is reasonally
868 %       accurate.
869 %
870 %       However if you use a larger radius such as "Euclidean:4" you will
871 %       get a much smoother distance gradient from the edge of the shape.
872 %       Of course a larger kernel is slower to use, and generally not needed.
873 %
874 %       To allow the use of fractional distances that you get with diagonals
875 %       the actual distance is scaled by a fixed value which the user can
876 %       provide.  This is not actually nessary for either ""Chebyshev" or
877 %       "Manhattan" distance kernels, but is done for all three distance
878 %       kernels.  If no scale is provided it is set to a value of 100,
879 %       allowing for a maximum distance measurement of 655 pixels using a Q16
880 %       version of IM, from any edge.  However for small images this can
881 %       result in quite a dark gradient.
882 %
883 */
884
885 MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
886    const GeometryInfo *args)
887 {
888   KernelInfo
889     *kernel;
890
891   register ssize_t
892     i;
893
894   register ssize_t
895     u,
896     v;
897
898   double
899     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
900
901   /* Generate a new empty kernel if needed */
902   kernel=(KernelInfo *) NULL;
903   switch(type) {
904     case UndefinedKernel:    /* These should not call this function */
905     case UserDefinedKernel:
906     case TestKernel:
907       break;
908     case UnityKernel:      /* Named Descrete Convolution Kernels */
909     case LaplacianKernel:
910     case SobelKernel:
911     case RobertsKernel:
912     case PrewittKernel:
913     case CompassKernel:
914     case KirschKernel:
915     case FreiChenKernel:
916     case CornersKernel:    /* Hit and Miss kernels */
917     case LineEndsKernel:
918     case LineJunctionsKernel:
919     case EdgesKernel:
920     case RidgesKernel:
921     case Ridges2Kernel:
922     case ConvexHullKernel:
923     case SkeletonKernel:
924     case MatKernel:
925       /* A pre-generated kernel is not needed */
926       break;
927 #if 0 /* set to 1 to do a compile-time check that we haven't missed anything */
928     case GaussianKernel:
929     case DoGKernel:
930     case LoGKernel:
931     case BlurKernel:
932     case CometKernel:
933     case DiamondKernel:
934     case SquareKernel:
935     case RectangleKernel:
936     case DiskKernel:
937     case PlusKernel:
938     case CrossKernel:
939     case RingKernel:
940     case PeaksKernel:
941     case ChebyshevKernel:
942     case ManhattanKernel:
943     case EuclideanKernel:
944 #else
945     default:
946 #endif
947       /* Generate the base Kernel Structure */
948       kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
949       if (kernel == (KernelInfo *) NULL)
950         return(kernel);
951       (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
952       kernel->minimum = kernel->maximum = kernel->angle = 0.0;
953       kernel->negative_range = kernel->positive_range = 0.0;
954       kernel->type = type;
955       kernel->next = (KernelInfo *) NULL;
956       kernel->signature = MagickSignature;
957       break;
958   }
959
960   switch(type) {
961     /* Convolution Kernels */
962     case GaussianKernel:
963     case DoGKernel:
964     case LoGKernel:
965       { double
966           sigma = fabs(args->sigma),
967           sigma2 = fabs(args->xi),
968           A, B, R;
969
970         if ( args->rho >= 1.0 )
971           kernel->width = (size_t)args->rho*2+1;
972         else if ( (type != DoGKernel) || (sigma >= sigma2) )
973           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
974         else
975           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
976         kernel->height = kernel->width;
977         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
978         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
979                               kernel->height*sizeof(double));
980         if (kernel->values == (double *) NULL)
981           return(DestroyKernelInfo(kernel));
982
983         /* WARNING: The following generates a 'sampled gaussian' kernel.
984          * What we really want is a 'discrete gaussian' kernel.
985          *
986          * How to do this is currently not known, but appears to be
987          * basied on the Error Function 'erf()' (intergral of a gaussian)
988          */
989
990         if ( type == GaussianKernel || type == DoGKernel )
991           { /* Calculate a Gaussian,  OR positive half of a DoG */
992             if ( sigma > MagickEpsilon )
993               { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
994                 B = 1.0/(Magick2PI*sigma*sigma);
995                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
996                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
997                       kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
998               }
999             else /* limiting case - a unity (normalized Dirac) kernel */
1000               { (void) ResetMagickMemory(kernel->values,0, (size_t)
1001                             kernel->width*kernel->height*sizeof(double));
1002                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1003               }
1004           }
1005
1006         if ( type == DoGKernel )
1007           { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
1008             if ( sigma2 > MagickEpsilon )
1009               { sigma = sigma2;                /* simplify loop expressions */
1010                 A = 1.0/(2.0*sigma*sigma);
1011                 B = 1.0/(Magick2PI*sigma*sigma);
1012                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1013                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1014                     kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1015               }
1016             else /* limiting case - a unity (normalized Dirac) kernel */
1017               kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1018           }
1019
1020         if ( type == LoGKernel )
1021           { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
1022             if ( sigma > MagickEpsilon )
1023               { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
1024                 B = 1.0/(MagickPI*sigma*sigma*sigma*sigma);
1025                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1026                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1027                     { R = ((double)(u*u+v*v))*A;
1028                       kernel->values[i] = (1-R)*exp(-R)*B;
1029                     }
1030               }
1031             else /* special case - generate a unity kernel */
1032               { (void) ResetMagickMemory(kernel->values,0, (size_t)
1033                             kernel->width*kernel->height*sizeof(double));
1034                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1035               }
1036           }
1037
1038         /* Note the above kernels may have been 'clipped' by a user defined
1039         ** radius, producing a smaller (darker) kernel.  Also for very small
1040         ** sigma's (> 0.1) the central value becomes larger than one, and thus
1041         ** producing a very bright kernel.
1042         **
1043         ** Normalization will still be needed.
1044         */
1045
1046         /* Normalize the 2D Gaussian Kernel
1047         **
1048         ** NB: a CorrelateNormalize performs a normal Normalize if
1049         ** there are no negative values.
1050         */
1051         CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1052         ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1053
1054         break;
1055       }
1056     case BlurKernel:
1057       { double
1058           sigma = fabs(args->sigma),
1059           alpha, beta;
1060
1061         if ( args->rho >= 1.0 )
1062           kernel->width = (size_t)args->rho*2+1;
1063         else
1064           kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1065         kernel->height = 1;
1066         kernel->x = (ssize_t) (kernel->width-1)/2;
1067         kernel->y = 0;
1068         kernel->negative_range = kernel->positive_range = 0.0;
1069         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1070                               kernel->height*sizeof(double));
1071         if (kernel->values == (double *) NULL)
1072           return(DestroyKernelInfo(kernel));
1073
1074 #if 1
1075 #define KernelRank 3
1076         /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
1077         ** It generates a gaussian 3 times the width, and compresses it into
1078         ** the expected range.  This produces a closer normalization of the
1079         ** resulting kernel, especially for very low sigma values.
1080         ** As such while wierd it is prefered.
1081         **
1082         ** I am told this method originally came from Photoshop.
1083         **
1084         ** A properly normalized curve is generated (apart from edge clipping)
1085         ** even though we later normalize the result (for edge clipping)
1086         ** to allow the correct generation of a "Difference of Blurs".
1087         */
1088
1089         /* initialize */
1090         v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
1091         (void) ResetMagickMemory(kernel->values,0, (size_t)
1092                      kernel->width*kernel->height*sizeof(double));
1093         /* Calculate a Positive 1D Gaussian */
1094         if ( sigma > MagickEpsilon )
1095           { sigma *= KernelRank;               /* simplify loop expressions */
1096             alpha = 1.0/(2.0*sigma*sigma);
1097             beta= 1.0/(MagickSQ2PI*sigma );
1098             for ( u=-v; u <= v; u++) {
1099               kernel->values[(u+v)/KernelRank] +=
1100                               exp(-((double)(u*u))*alpha)*beta;
1101             }
1102           }
1103         else /* special case - generate a unity kernel */
1104           kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1105 #else
1106         /* Direct calculation without curve averaging */
1107
1108         /* Calculate a Positive Gaussian */
1109         if ( sigma > MagickEpsilon )
1110           { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
1111             beta = 1.0/(MagickSQ2PI*sigma);
1112             for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1113               kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1114           }
1115         else /* special case - generate a unity kernel */
1116           { (void) ResetMagickMemory(kernel->values,0, (size_t)
1117                          kernel->width*kernel->height*sizeof(double));
1118             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1119           }
1120 #endif
1121         /* Note the above kernel may have been 'clipped' by a user defined
1122         ** radius, producing a smaller (darker) kernel.  Also for very small
1123         ** sigma's (> 0.1) the central value becomes larger than one, and thus
1124         ** producing a very bright kernel.
1125         **
1126         ** Normalization will still be needed.
1127         */
1128
1129         /* Normalize the 1D Gaussian Kernel
1130         **
1131         ** NB: a CorrelateNormalize performs a normal Normalize if
1132         ** there are no negative values.
1133         */
1134         CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1135         ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1136
1137         /* rotate the 1D kernel by given angle */
1138         RotateKernelInfo(kernel, args->xi );
1139         break;
1140       }
1141     case CometKernel:
1142       { double
1143           sigma = fabs(args->sigma),
1144           A;
1145
1146         if ( args->rho < 1.0 )
1147           kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1148         else
1149           kernel->width = (size_t)args->rho;
1150         kernel->x = kernel->y = 0;
1151         kernel->height = 1;
1152         kernel->negative_range = kernel->positive_range = 0.0;
1153         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1154                               kernel->height*sizeof(double));
1155         if (kernel->values == (double *) NULL)
1156           return(DestroyKernelInfo(kernel));
1157
1158         /* A comet blur is half a 1D gaussian curve, so that the object is
1159         ** blurred in one direction only.  This may not be quite the right
1160         ** curve to use so may change in the future. The function must be
1161         ** normalised after generation, which also resolves any clipping.
1162         **
1163         ** As we are normalizing and not subtracting gaussians,
1164         ** there is no need for a divisor in the gaussian formula
1165         **
1166         ** It is less comples
1167         */
1168         if ( sigma > MagickEpsilon )
1169           {
1170 #if 1
1171 #define KernelRank 3
1172             v = (ssize_t) kernel->width*KernelRank; /* start/end points */
1173             (void) ResetMagickMemory(kernel->values,0, (size_t)
1174                           kernel->width*sizeof(double));
1175             sigma *= KernelRank;            /* simplify the loop expression */
1176             A = 1.0/(2.0*sigma*sigma);
1177             /* B = 1.0/(MagickSQ2PI*sigma); */
1178             for ( u=0; u < v; u++) {
1179               kernel->values[u/KernelRank] +=
1180                   exp(-((double)(u*u))*A);
1181               /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1182             }
1183             for (i=0; i < (ssize_t) kernel->width; i++)
1184               kernel->positive_range += kernel->values[i];
1185 #else
1186             A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
1187             /* B = 1.0/(MagickSQ2PI*sigma); */
1188             for ( i=0; i < (ssize_t) kernel->width; i++)
1189               kernel->positive_range +=
1190                 kernel->values[i] =
1191                   exp(-((double)(i*i))*A);
1192                 /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1193 #endif
1194           }
1195         else /* special case - generate a unity kernel */
1196           { (void) ResetMagickMemory(kernel->values,0, (size_t)
1197                          kernel->width*kernel->height*sizeof(double));
1198             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1199             kernel->positive_range = 1.0;
1200           }
1201
1202         kernel->minimum = 0.0;
1203         kernel->maximum = kernel->values[0];
1204         kernel->negative_range = 0.0;
1205
1206         ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
1207         RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
1208         break;
1209       }
1210
1211     /* Convolution Kernels - Well Known Constants */
1212     case LaplacianKernel:
1213       { switch ( (int) args->rho ) {
1214           case 0:
1215           default: /* laplacian square filter -- default */
1216             kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
1217             break;
1218           case 1:  /* laplacian diamond filter */
1219             kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
1220             break;
1221           case 2:
1222             kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1223             break;
1224           case 3:
1225             kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
1226             break;
1227           case 5:   /* a 5x5 laplacian */
1228             kernel=ParseKernelArray(
1229               "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");
1230             break;
1231           case 7:   /* a 7x7 laplacian */
1232             kernel=ParseKernelArray(
1233               "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" );
1234             break;
1235           case 15:  /* a 5x5 LoG (sigma approx 1.4) */
1236             kernel=ParseKernelArray(
1237               "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");
1238             break;
1239           case 19:  /* a 9x9 LoG (sigma approx 1.4) */
1240             /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
1241             kernel=ParseKernelArray(
1242               "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");
1243             break;
1244         }
1245         if (kernel == (KernelInfo *) NULL)
1246           return(kernel);
1247         kernel->type = type;
1248         break;
1249       }
1250     case SobelKernel:
1251       {
1252         kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1253         if (kernel == (KernelInfo *) NULL)
1254           return(kernel);
1255         kernel->type = type;
1256         RotateKernelInfo(kernel, args->rho); /* Rotate by angle */
1257         break;
1258       }
1259     case RobertsKernel:
1260       {
1261         kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
1262         if (kernel == (KernelInfo *) NULL)
1263           return(kernel);
1264         kernel->type = type;
1265         RotateKernelInfo(kernel, args->rho);
1266         break;
1267       }
1268     case PrewittKernel:
1269       {
1270         kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
1271         if (kernel == (KernelInfo *) NULL)
1272           return(kernel);
1273         kernel->type = type;
1274         RotateKernelInfo(kernel, args->rho);
1275         break;
1276       }
1277     case CompassKernel:
1278       {
1279         kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
1280         if (kernel == (KernelInfo *) NULL)
1281           return(kernel);
1282         kernel->type = type;
1283         RotateKernelInfo(kernel, args->rho);
1284         break;
1285       }
1286     case KirschKernel:
1287       {
1288         kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
1289         if (kernel == (KernelInfo *) NULL)
1290           return(kernel);
1291         kernel->type = type;
1292         RotateKernelInfo(kernel, args->rho);
1293         break;
1294       }
1295     case FreiChenKernel:
1296       /* Direction is set to be left to right positive */
1297       /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
1298       /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
1299       { switch ( (int) args->rho ) {
1300           default:
1301           case 0:
1302             kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1303             if (kernel == (KernelInfo *) NULL)
1304               return(kernel);
1305             kernel->type = type;
1306             kernel->values[3] = +MagickSQ2;
1307             kernel->values[5] = -MagickSQ2;
1308             CalcKernelMetaData(kernel);     /* recalculate meta-data */
1309             break;
1310           case 1:
1311             kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1312             if (kernel == (KernelInfo *) NULL)
1313               return(kernel);
1314             kernel->type = type;
1315             kernel->values[3] = +MagickSQ2;
1316             kernel->values[5] = -MagickSQ2;
1317             CalcKernelMetaData(kernel);     /* recalculate meta-data */
1318             ScaleKernelInfo(kernel, 1.0/2.0*MagickSQ2, NoValue);
1319             break;
1320           case 2:
1321             kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
1322             if (kernel == (KernelInfo *) NULL)
1323               return(kernel);
1324             kernel->type = type;
1325             kernel->values[1] = +MagickSQ2;
1326             kernel->values[7] = +MagickSQ2;
1327             CalcKernelMetaData(kernel);
1328             ScaleKernelInfo(kernel, 1.0/2.0*MagickSQ2, NoValue);
1329             break;
1330           case 3:
1331             kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
1332             if (kernel == (KernelInfo *) NULL)
1333               return(kernel);
1334             kernel->type = type;
1335             kernel->values[0] = +MagickSQ2;
1336             kernel->values[8] = -MagickSQ2;
1337             CalcKernelMetaData(kernel);
1338             ScaleKernelInfo(kernel, 1.0/2.0*MagickSQ2, NoValue);
1339             break;
1340           case 4:
1341             kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
1342             if (kernel == (KernelInfo *) NULL)
1343               return(kernel);
1344             kernel->type = type;
1345             kernel->values[2] = -MagickSQ2;
1346             kernel->values[6] = +MagickSQ2;
1347             CalcKernelMetaData(kernel);
1348             ScaleKernelInfo(kernel, 1.0/2.0*MagickSQ2, NoValue);
1349             break;
1350           case 5:
1351             kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
1352             if (kernel == (KernelInfo *) NULL)
1353               return(kernel);
1354             kernel->type = type;
1355             ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1356             break;
1357           case 6:
1358             kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
1359             if (kernel == (KernelInfo *) NULL)
1360               return(kernel);
1361             kernel->type = type;
1362             ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1363             break;
1364           case 7:
1365             kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
1366             if (kernel == (KernelInfo *) NULL)
1367               return(kernel);
1368             kernel->type = type;
1369             ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1370             break;
1371           case 8:
1372             kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1373             if (kernel == (KernelInfo *) NULL)
1374               return(kernel);
1375             kernel->type = type;
1376             ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1377             break;
1378           case 9:
1379             kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
1380             if (kernel == (KernelInfo *) NULL)
1381               return(kernel);
1382             kernel->type = type;
1383             ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1384             break;
1385           case -1:
1386             kernel=AcquireKernelInfo("FreiChen:1;FreiChen:2;FreiChen:3;FreiChen:4;FreiChen:5;FreiChen:6;FreiChen:7;FreiChen:8;FreiChen:9");
1387             if (kernel == (KernelInfo *) NULL)
1388               return(kernel);
1389             break;
1390         }
1391         if ( fabs(args->sigma) > MagickEpsilon )
1392           /* Rotate by correctly supplied 'angle' */
1393           RotateKernelInfo(kernel, args->sigma);
1394         else if ( args->rho > 30.0 || args->rho < -30.0 )
1395           /* Rotate by out of bounds 'type' */
1396           RotateKernelInfo(kernel, args->rho);
1397         break;
1398       }
1399
1400     /* Boolean Kernels */
1401     case DiamondKernel:
1402       {
1403         if (args->rho < 1.0)
1404           kernel->width = kernel->height = 3;  /* default radius = 1 */
1405         else
1406           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1407         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1408
1409         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1410                               kernel->height*sizeof(double));
1411         if (kernel->values == (double *) NULL)
1412           return(DestroyKernelInfo(kernel));
1413
1414         /* set all kernel values within diamond area to scale given */
1415         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1416           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1417             if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1418               kernel->positive_range += kernel->values[i] = args->sigma;
1419             else
1420               kernel->values[i] = nan;
1421         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1422         break;
1423       }
1424     case SquareKernel:
1425     case RectangleKernel:
1426       { double
1427           scale;
1428         if ( type == SquareKernel )
1429           {
1430             if (args->rho < 1.0)
1431               kernel->width = kernel->height = 3;  /* default radius = 1 */
1432             else
1433               kernel->width = kernel->height = (size_t) (2*args->rho+1);
1434             kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1435             scale = args->sigma;
1436           }
1437         else {
1438             /* NOTE: user defaults set in "AcquireKernelInfo()" */
1439             if ( args->rho < 1.0 || args->sigma < 1.0 )
1440               return(DestroyKernelInfo(kernel));    /* invalid args given */
1441             kernel->width = (size_t)args->rho;
1442             kernel->height = (size_t)args->sigma;
1443             if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
1444                  args->psi < 0.0 || args->psi > (double)kernel->height )
1445               return(DestroyKernelInfo(kernel));    /* invalid args given */
1446             kernel->x = (ssize_t) args->xi;
1447             kernel->y = (ssize_t) args->psi;
1448             scale = 1.0;
1449           }
1450         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1451                               kernel->height*sizeof(double));
1452         if (kernel->values == (double *) NULL)
1453           return(DestroyKernelInfo(kernel));
1454
1455         /* set all kernel values to scale given */
1456         u=(ssize_t) (kernel->width*kernel->height);
1457         for ( i=0; i < u; i++)
1458             kernel->values[i] = scale;
1459         kernel->minimum = kernel->maximum = scale;   /* a flat shape */
1460         kernel->positive_range = scale*u;
1461         break;
1462       }
1463     case DiskKernel:
1464       {
1465         ssize_t
1466          limit = (ssize_t)(args->rho*args->rho);
1467
1468         if (args->rho < 0.4)           /* default radius approx 3.5 */
1469           kernel->width = kernel->height = 7L, limit = 10L;
1470         else
1471            kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
1472         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1473
1474         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1475                               kernel->height*sizeof(double));
1476         if (kernel->values == (double *) NULL)
1477           return(DestroyKernelInfo(kernel));
1478
1479         /* set all kernel values within disk area to scale given */
1480         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1481           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1482             if ((u*u+v*v) <= limit)
1483               kernel->positive_range += kernel->values[i] = args->sigma;
1484             else
1485               kernel->values[i] = nan;
1486         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1487         break;
1488       }
1489     case PlusKernel:
1490       {
1491         if (args->rho < 1.0)
1492           kernel->width = kernel->height = 5;  /* default radius 2 */
1493         else
1494            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1495         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1496
1497         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1498                               kernel->height*sizeof(double));
1499         if (kernel->values == (double *) NULL)
1500           return(DestroyKernelInfo(kernel));
1501
1502         /* set all kernel values along axises to given scale */
1503         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1504           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1505             kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
1506         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1507         kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1508         break;
1509       }
1510     case CrossKernel:
1511       {
1512         if (args->rho < 1.0)
1513           kernel->width = kernel->height = 5;  /* default radius 2 */
1514         else
1515            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1516         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1517
1518         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1519                               kernel->height*sizeof(double));
1520         if (kernel->values == (double *) NULL)
1521           return(DestroyKernelInfo(kernel));
1522
1523         /* set all kernel values along axises to given scale */
1524         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1525           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1526             kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
1527         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1528         kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1529         break;
1530       }
1531     /* HitAndMiss Kernels */
1532     case RingKernel:
1533     case PeaksKernel:
1534       {
1535         ssize_t
1536           limit1,
1537           limit2,
1538           scale;
1539
1540         if (args->rho < args->sigma)
1541           {
1542             kernel->width = ((size_t)args->sigma)*2+1;
1543             limit1 = (ssize_t)(args->rho*args->rho);
1544             limit2 = (ssize_t)(args->sigma*args->sigma);
1545           }
1546         else
1547           {
1548             kernel->width = ((size_t)args->rho)*2+1;
1549             limit1 = (ssize_t)(args->sigma*args->sigma);
1550             limit2 = (ssize_t)(args->rho*args->rho);
1551           }
1552         if ( limit2 <= 0 )
1553           kernel->width = 7L, limit1 = 7L, limit2 = 11L;
1554
1555         kernel->height = kernel->width;
1556         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1557         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1558                               kernel->height*sizeof(double));
1559         if (kernel->values == (double *) NULL)
1560           return(DestroyKernelInfo(kernel));
1561
1562         /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
1563         scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
1564         for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
1565           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1566             { ssize_t radius=u*u+v*v;
1567               if (limit1 < radius && radius <= limit2)
1568                 kernel->positive_range += kernel->values[i] = (double) scale;
1569               else
1570                 kernel->values[i] = nan;
1571             }
1572         kernel->minimum = kernel->minimum = (double) scale;
1573         if ( type == PeaksKernel ) {
1574           /* set the central point in the middle */
1575           kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1576           kernel->positive_range = 1.0;
1577           kernel->maximum = 1.0;
1578         }
1579         break;
1580       }
1581     case EdgesKernel:
1582       {
1583         kernel=ParseKernelArray("3: 0,0,0  -,1,-  1,1,1");
1584         if (kernel == (KernelInfo *) NULL)
1585           return(kernel);
1586         kernel->type = type;
1587         ExpandMirrorKernelInfo(kernel); /* mirror expansion of other kernels */
1588         break;
1589       }
1590     case CornersKernel:
1591       {
1592         kernel=ParseKernelArray("3: 0,0,-  0,1,1  -,1,-");
1593         if (kernel == (KernelInfo *) NULL)
1594           return(kernel);
1595         kernel->type = type;
1596         ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
1597         break;
1598       }
1599     case RidgesKernel:
1600       {
1601         kernel=ParseKernelArray("3x1:0,1,0");
1602         if (kernel == (KernelInfo *) NULL)
1603           return(kernel);
1604         kernel->type = type;
1605         ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
1606         break;
1607       }
1608     case Ridges2Kernel:
1609       {
1610         KernelInfo
1611           *new_kernel;
1612         kernel=ParseKernelArray("4x1:0,1,1,0");
1613         if (kernel == (KernelInfo *) NULL)
1614           return(kernel);
1615         kernel->type = type;
1616         ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
1617 #if 0
1618         /* 2 pixel diagonaly thick - 4 rotates - not needed? */
1619         new_kernel=ParseKernelArray("4x4>:0,-,-,- -,1,-,- -,-,1,- -,-,-,0'");
1620         if (new_kernel == (KernelInfo *) NULL)
1621           return(DestroyKernelInfo(kernel));
1622         new_kernel->type = type;
1623         ExpandRotateKernelInfo(new_kernel, 90.0);  /* 4 rotated kernels */
1624         LastKernelInfo(kernel)->next = new_kernel;
1625 #endif
1626         /* kernels to find a stepped 'thick' line, 4 rotates * mirror */
1627         /* Unfortunatally we can not yet rotate a non-square kernel */
1628         /* But then we can't flip a non-symetrical kernel either */
1629         new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
1630         if (new_kernel == (KernelInfo *) NULL)
1631           return(DestroyKernelInfo(kernel));
1632         new_kernel->type = type;
1633         LastKernelInfo(kernel)->next = new_kernel;
1634         new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
1635         if (new_kernel == (KernelInfo *) NULL)
1636           return(DestroyKernelInfo(kernel));
1637         new_kernel->type = type;
1638         LastKernelInfo(kernel)->next = new_kernel;
1639         new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
1640         if (new_kernel == (KernelInfo *) NULL)
1641           return(DestroyKernelInfo(kernel));
1642         new_kernel->type = type;
1643         LastKernelInfo(kernel)->next = new_kernel;
1644         new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
1645         if (new_kernel == (KernelInfo *) NULL)
1646           return(DestroyKernelInfo(kernel));
1647         new_kernel->type = type;
1648         LastKernelInfo(kernel)->next = new_kernel;
1649         new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
1650         if (new_kernel == (KernelInfo *) NULL)
1651           return(DestroyKernelInfo(kernel));
1652         new_kernel->type = type;
1653         LastKernelInfo(kernel)->next = new_kernel;
1654         new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
1655         if (new_kernel == (KernelInfo *) NULL)
1656           return(DestroyKernelInfo(kernel));
1657         new_kernel->type = type;
1658         LastKernelInfo(kernel)->next = new_kernel;
1659         new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
1660         if (new_kernel == (KernelInfo *) NULL)
1661           return(DestroyKernelInfo(kernel));
1662         new_kernel->type = type;
1663         LastKernelInfo(kernel)->next = new_kernel;
1664         new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
1665         if (new_kernel == (KernelInfo *) NULL)
1666           return(DestroyKernelInfo(kernel));
1667         new_kernel->type = type;
1668         LastKernelInfo(kernel)->next = new_kernel;
1669         break;
1670       }
1671     case LineEndsKernel:
1672       {
1673         KernelInfo
1674           *new_kernel;
1675         kernel=ParseKernelArray("3: 0,0,0  0,1,0  -,1,-");
1676         if (kernel == (KernelInfo *) NULL)
1677           return(kernel);
1678         kernel->type = type;
1679         ExpandRotateKernelInfo(kernel, 90.0);
1680         /* append second set of 4 kernels */
1681         new_kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
1682         if (new_kernel == (KernelInfo *) NULL)
1683           return(DestroyKernelInfo(kernel));
1684         new_kernel->type = type;
1685         ExpandRotateKernelInfo(new_kernel, 90.0);
1686         LastKernelInfo(kernel)->next = new_kernel;
1687         break;
1688       }
1689     case LineJunctionsKernel:
1690       {
1691         KernelInfo
1692           *new_kernel;
1693         /* first set of 4 kernels */
1694         kernel=ParseKernelArray("3: -,1,-  -,1,-  1,-,1");
1695         if (kernel == (KernelInfo *) NULL)
1696           return(kernel);
1697         kernel->type = type;
1698         ExpandRotateKernelInfo(kernel, 45.0);
1699         /* append second set of 4 kernels */
1700         new_kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
1701         if (new_kernel == (KernelInfo *) NULL)
1702           return(DestroyKernelInfo(kernel));
1703         new_kernel->type = type;
1704         ExpandRotateKernelInfo(new_kernel, 90.0);
1705         LastKernelInfo(kernel)->next = new_kernel;
1706         break;
1707       }
1708     case ConvexHullKernel:
1709       {
1710         KernelInfo
1711           *new_kernel;
1712         /* first set of 8 kernels */
1713         kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
1714         if (kernel == (KernelInfo *) NULL)
1715           return(kernel);
1716         kernel->type = type;
1717         ExpandRotateKernelInfo(kernel, 45.0);
1718         /* append the mirror versions too */
1719         new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
1720         if (new_kernel == (KernelInfo *) NULL)
1721           return(DestroyKernelInfo(kernel));
1722         new_kernel->type = type;
1723         ExpandRotateKernelInfo(new_kernel, 45.0);
1724         LastKernelInfo(kernel)->next = new_kernel;
1725         break;
1726       }
1727     case SkeletonKernel:
1728       { /* what is the best form for skeletonization by thinning? */
1729 #if 0
1730         /* Use a edge/corner pruning method to generate a skeleton.
1731         ** This actually works, but tends to generate slightly thick
1732         ** diagonals.  Later thinning of those diagonals results in
1733         ** asymetrically thining.
1734         */
1735         kernel=ParseKernelArray("3: 0,0,0  -,1,-  1,1,1");
1736         if (kernel == (KernelInfo *) NULL)
1737           return(kernel);
1738         kernel->type = type;
1739         ExpandRotateKernelInfo(kernel, 45);
1740         break;
1741       }
1742 #endif
1743 #if 1
1744         /* This is like simple 'Edge' thinning, but with a extra two
1745         ** kernels (3 x 4 rotates => 12) to finish off the pruning
1746         ** of the diagonal lines.
1747         */
1748         KernelInfo
1749           *new_kernel;
1750         kernel=ParseKernelArray("3: 0,0,0  -,1,-  1,1,1");
1751         if (kernel == (KernelInfo *) NULL)
1752           return(kernel);
1753         kernel->type = type;
1754         new_kernel=ParseKernelArray("3: 0,0,0  0,1,1  1,1,-");
1755         if (new_kernel == (KernelInfo *) NULL)
1756           return(DestroyKernelInfo(kernel));
1757         new_kernel->type = type;
1758         LastKernelInfo(kernel)->next = new_kernel;
1759         new_kernel=ParseKernelArray("3: 0,0,0  1,1,0  -,1,1");
1760         if (new_kernel == (KernelInfo *) NULL)
1761           return(DestroyKernelInfo(kernel));
1762         new_kernel->type = type;
1763         LastKernelInfo(kernel)->next = new_kernel;
1764         ExpandMirrorKernelInfo(kernel);
1765         break;
1766 #endif
1767       }
1768     case MatKernel: /* experimental - MAT from a Distance Gradient */
1769       {
1770         KernelInfo
1771           *new_kernel;
1772         /* Ridge Kernel but without the diagonal */
1773         kernel=ParseKernelArray("3x1: 0,1,0");
1774         if (kernel == (KernelInfo *) NULL)
1775           return(kernel);
1776         kernel->type = RidgesKernel;
1777         ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
1778         /* Plus the 2 pixel ridges kernel - no diagonal */
1779         new_kernel=AcquireKernelBuiltIn(Ridges2Kernel,args);
1780         if (new_kernel == (KernelInfo *) NULL)
1781           return(kernel);
1782         LastKernelInfo(kernel)->next = new_kernel;
1783         break;
1784       }
1785     /* Distance Measuring Kernels */
1786     case ChebyshevKernel:
1787       {
1788         if (args->rho < 1.0)
1789           kernel->width = kernel->height = 3;  /* default radius = 1 */
1790         else
1791           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1792         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1793
1794         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1795                               kernel->height*sizeof(double));
1796         if (kernel->values == (double *) NULL)
1797           return(DestroyKernelInfo(kernel));
1798
1799         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1800           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1801             kernel->positive_range += ( kernel->values[i] =
1802                  args->sigma*((labs((long) u)>labs((long) v)) ? labs((long) u) : labs((long) v)) );
1803         kernel->maximum = kernel->values[0];
1804         break;
1805       }
1806     case ManhattanKernel:
1807       {
1808         if (args->rho < 1.0)
1809           kernel->width = kernel->height = 3;  /* default radius = 1 */
1810         else
1811            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1812         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1813
1814         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1815                               kernel->height*sizeof(double));
1816         if (kernel->values == (double *) NULL)
1817           return(DestroyKernelInfo(kernel));
1818
1819         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1820           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1821             kernel->positive_range += ( kernel->values[i] =
1822                  args->sigma*(labs((long) u)+labs((long) v)) );
1823         kernel->maximum = kernel->values[0];
1824         break;
1825       }
1826     case EuclideanKernel:
1827       {
1828         if (args->rho < 1.0)
1829           kernel->width = kernel->height = 3;  /* default radius = 1 */
1830         else
1831            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1832         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1833
1834         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1835                               kernel->height*sizeof(double));
1836         if (kernel->values == (double *) NULL)
1837           return(DestroyKernelInfo(kernel));
1838
1839         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1840           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1841             kernel->positive_range += ( kernel->values[i] =
1842                  args->sigma*sqrt((double)(u*u+v*v)) );
1843         kernel->maximum = kernel->values[0];
1844         break;
1845       }
1846     case UnityKernel:
1847     default:
1848       {
1849         /* Unity or No-Op Kernel - 3x3 with 1 in center */
1850         kernel=ParseKernelArray("3:0,0,0,0,1,0,0,0,0");
1851         if (kernel == (KernelInfo *) NULL)
1852           return(kernel);
1853         kernel->type = ( type == UnityKernel ) ? UnityKernel : UndefinedKernel;
1854         break;
1855       }
1856       break;
1857   }
1858
1859   return(kernel);
1860 }
1861 \f
1862 /*
1863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1864 %                                                                             %
1865 %                                                                             %
1866 %                                                                             %
1867 %     C l o n e K e r n e l I n f o                                           %
1868 %                                                                             %
1869 %                                                                             %
1870 %                                                                             %
1871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1872 %
1873 %  CloneKernelInfo() creates a new clone of the given Kernel List so that its
1874 %  can be modified without effecting the original.  The cloned kernel should
1875 %  be destroyed using DestoryKernelInfo() when no ssize_ter needed.
1876 %
1877 %  The format of the CloneKernelInfo method is:
1878 %
1879 %      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
1880 %
1881 %  A description of each parameter follows:
1882 %
1883 %    o kernel: the Morphology/Convolution kernel to be cloned
1884 %
1885 */
1886 MagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
1887 {
1888   register ssize_t
1889     i;
1890
1891   KernelInfo
1892     *new_kernel;
1893
1894   assert(kernel != (KernelInfo *) NULL);
1895   new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1896   if (new_kernel == (KernelInfo *) NULL)
1897     return(new_kernel);
1898   *new_kernel=(*kernel); /* copy values in structure */
1899
1900   /* replace the values with a copy of the values */
1901   new_kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1902     kernel->height*sizeof(double));
1903   if (new_kernel->values == (double *) NULL)
1904     return(DestroyKernelInfo(new_kernel));
1905   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
1906     new_kernel->values[i]=kernel->values[i];
1907
1908   /* Also clone the next kernel in the kernel list */
1909   if ( kernel->next != (KernelInfo *) NULL ) {
1910     new_kernel->next = CloneKernelInfo(kernel->next);
1911     if ( new_kernel->next == (KernelInfo *) NULL )
1912       return(DestroyKernelInfo(new_kernel));
1913   }
1914
1915   return(new_kernel);
1916 }
1917 \f
1918 /*
1919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1920 %                                                                             %
1921 %                                                                             %
1922 %                                                                             %
1923 %     D e s t r o y K e r n e l I n f o                                       %
1924 %                                                                             %
1925 %                                                                             %
1926 %                                                                             %
1927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1928 %
1929 %  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
1930 %  kernel.
1931 %
1932 %  The format of the DestroyKernelInfo method is:
1933 %
1934 %      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
1935 %
1936 %  A description of each parameter follows:
1937 %
1938 %    o kernel: the Morphology/Convolution kernel to be destroyed
1939 %
1940 */
1941 MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
1942 {
1943   assert(kernel != (KernelInfo *) NULL);
1944
1945   if ( kernel->next != (KernelInfo *) NULL )
1946     kernel->next = DestroyKernelInfo(kernel->next);
1947
1948   kernel->values = (double *)RelinquishMagickMemory(kernel->values);
1949   kernel = (KernelInfo *) RelinquishMagickMemory(kernel);
1950   return(kernel);
1951 }
1952 \f
1953 /*
1954 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1955 %                                                                             %
1956 %                                                                             %
1957 %                                                                             %
1958 %     E x p a n d M i r r o r K e r n e l I n f o                             %
1959 %                                                                             %
1960 %                                                                             %
1961 %                                                                             %
1962 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1963 %
1964 %  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
1965 %  sequence of 90-degree rotated kernels but providing a reflected 180
1966 %  rotatation, before the -/+ 90-degree rotations.
1967 %
1968 %  This special rotation order produces a better, more symetrical thinning of
1969 %  objects.
1970 %
1971 %  The format of the ExpandMirrorKernelInfo method is:
1972 %
1973 %      void ExpandMirrorKernelInfo(KernelInfo *kernel)
1974 %
1975 %  A description of each parameter follows:
1976 %
1977 %    o kernel: the Morphology/Convolution kernel
1978 %
1979 % This function is only internel to this module, as it is not finalized,
1980 % especially with regard to non-orthogonal angles, and rotation of larger
1981 % 2D kernels.
1982 */
1983
1984 #if 0
1985 static void FlopKernelInfo(KernelInfo *kernel)
1986     { /* Do a Flop by reversing each row. */
1987       size_t
1988         y;
1989       register ssize_t
1990         x,r;
1991       register double
1992         *k,t;
1993
1994       for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
1995         for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
1996           t=k[x],  k[x]=k[r],  k[r]=t;
1997
1998       kernel->x = kernel->width - kernel->x - 1;
1999       angle = fmod(angle+180.0, 360.0);
2000     }
2001 #endif
2002
2003 static void ExpandMirrorKernelInfo(KernelInfo *kernel)
2004 {
2005   KernelInfo
2006     *clone,
2007     *last;
2008
2009   last = kernel;
2010
2011   clone = CloneKernelInfo(last);
2012   RotateKernelInfo(clone, 180);   /* flip */
2013   LastKernelInfo(last)->next = clone;
2014   last = clone;
2015
2016   clone = CloneKernelInfo(last);
2017   RotateKernelInfo(clone, 90);   /* transpose */
2018   LastKernelInfo(last)->next = clone;
2019   last = clone;
2020
2021   clone = CloneKernelInfo(last);
2022   RotateKernelInfo(clone, 180);  /* flop */
2023   LastKernelInfo(last)->next = clone;
2024
2025   return;
2026 }
2027 \f
2028 /*
2029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2030 %                                                                             %
2031 %                                                                             %
2032 %                                                                             %
2033 %     E x p a n d R o t a t e K e r n e l I n f o                             %
2034 %                                                                             %
2035 %                                                                             %
2036 %                                                                             %
2037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2038 %
2039 %  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2040 %  incrementally by the angle given, until the first kernel repeats.
2041 %
2042 %  WARNING: 45 degree rotations only works for 3x3 kernels.
2043 %  While 90 degree roatations only works for linear and square kernels
2044 %
2045 %  The format of the ExpandRotateKernelInfo method is:
2046 %
2047 %      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
2048 %
2049 %  A description of each parameter follows:
2050 %
2051 %    o kernel: the Morphology/Convolution kernel
2052 %
2053 %    o angle: angle to rotate in degrees
2054 %
2055 % This function is only internel to this module, as it is not finalized,
2056 % especially with regard to non-orthogonal angles, and rotation of larger
2057 % 2D kernels.
2058 */
2059
2060 /* Internal Routine - Return true if two kernels are the same */
2061 static MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
2062      const KernelInfo *kernel2)
2063 {
2064   register size_t
2065     i;
2066
2067   /* check size and origin location */
2068   if (    kernel1->width != kernel2->width
2069        || kernel1->height != kernel2->height
2070        || kernel1->x != kernel2->x
2071        || kernel1->y != kernel2->y )
2072     return MagickFalse;
2073
2074   /* check actual kernel values */
2075   for (i=0; i < (kernel1->width*kernel1->height); i++) {
2076     /* Test for Nan equivelence */
2077     if ( IsNan(kernel1->values[i]) && !IsNan(kernel2->values[i]) )
2078       return MagickFalse;
2079     if ( IsNan(kernel2->values[i]) && !IsNan(kernel1->values[i]) )
2080       return MagickFalse;
2081     /* Test actual values are equivelent */
2082     if ( fabs(kernel1->values[i] - kernel2->values[i]) > MagickEpsilon )
2083       return MagickFalse;
2084   }
2085
2086   return MagickTrue;
2087 }
2088
2089 static void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
2090 {
2091   KernelInfo
2092     *clone,
2093     *last;
2094
2095   last = kernel;
2096   while(1) {
2097     clone = CloneKernelInfo(last);
2098     RotateKernelInfo(clone, angle);
2099     if ( SameKernelInfo(kernel, clone) == MagickTrue )
2100       break;
2101     LastKernelInfo(last)->next = clone;
2102     last = clone;
2103   }
2104   clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
2105   return;
2106 }
2107 \f
2108 /*
2109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2110 %                                                                             %
2111 %                                                                             %
2112 %                                                                             %
2113 +     C a l c M e t a K e r n a l I n f o                                     %
2114 %                                                                             %
2115 %                                                                             %
2116 %                                                                             %
2117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2118 %
2119 %  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
2120 %  using the kernel values.  This should only ne used if it is not posible to
2121 %  calculate that meta-data in some easier way.
2122 %
2123 %  It is important that the meta-data is correct before ScaleKernelInfo() is
2124 %  used to perform kernel normalization.
2125 %
2126 %  The format of the CalcKernelMetaData method is:
2127 %
2128 %      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
2129 %
2130 %  A description of each parameter follows:
2131 %
2132 %    o kernel: the Morphology/Convolution kernel to modify
2133 %
2134 %  WARNING: Minimum and Maximum values are assumed to include zero, even if
2135 %  zero is not part of the kernel (as in Gaussian Derived kernels). This
2136 %  however is not true for flat-shaped morphological kernels.
2137 %
2138 %  WARNING: Only the specific kernel pointed to is modified, not a list of
2139 %  multiple kernels.
2140 %
2141 % This is an internal function and not expected to be useful outside this
2142 % module.  This could change however.
2143 */
2144 static void CalcKernelMetaData(KernelInfo *kernel)
2145 {
2146   register size_t
2147     i;
2148
2149   kernel->minimum = kernel->maximum = 0.0;
2150   kernel->negative_range = kernel->positive_range = 0.0;
2151   for (i=0; i < (kernel->width*kernel->height); i++)
2152     {
2153       if ( fabs(kernel->values[i]) < MagickEpsilon )
2154         kernel->values[i] = 0.0;
2155       ( kernel->values[i] < 0)
2156           ?  ( kernel->negative_range += kernel->values[i] )
2157           :  ( kernel->positive_range += kernel->values[i] );
2158       Minimize(kernel->minimum, kernel->values[i]);
2159       Maximize(kernel->maximum, kernel->values[i]);
2160     }
2161
2162   return;
2163 }
2164 \f
2165 /*
2166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2167 %                                                                             %
2168 %                                                                             %
2169 %                                                                             %
2170 %     M o r p h o l o g y A p p l y                                           %
2171 %                                                                             %
2172 %                                                                             %
2173 %                                                                             %
2174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2175 %
2176 %  MorphologyApply() applies a morphological method, multiple times using
2177 %  a list of multiple kernels.
2178 %
2179 %  It is basically equivelent to as MorphologyImageChannel() (see below) but
2180 %  without any user controls.  This allows internel programs to use this
2181 %  function, to actually perform a specific task without posible interference
2182 %  by any API user supplied settings.
2183 %
2184 %  It is MorphologyImageChannel() task to extract any such user controls, and
2185 %  pass them to this function for processing.
2186 %
2187 %  More specifically kernels are not normalized/scaled/blended by the
2188 %  'convolve:scale' Image Artifact (setting), nor is the convolve bias
2189 %  (-bias setting or image->bias) loooked at, but must be supplied from the
2190 %  function arguments.
2191 %
2192 %  The format of the MorphologyApply method is:
2193 %
2194 %      Image *MorphologyApply(const Image *image,MorphologyMethod method,
2195 %        const ssize_t iterations,const KernelInfo *kernel,
2196 %        const CompositeMethod compose, const double bias,
2197 %        ExceptionInfo *exception)
2198 %
2199 %  A description of each parameter follows:
2200 %
2201 %    o image: the image.
2202 %
2203 %    o method: the morphology method to be applied.
2204 %
2205 %    o iterations: apply the operation this many times (or no change).
2206 %                  A value of -1 means loop until no change found.
2207 %                  How this is applied may depend on the morphology method.
2208 %                  Typically this is a value of 1.
2209 %
2210 %    o channel: the channel type.
2211 %
2212 %    o kernel: An array of double representing the morphology kernel.
2213 %              Warning: kernel may be normalized for the Convolve method.
2214 %
2215 %    o compose: How to handle or merge multi-kernel results.
2216 %               If 'Undefined' use default of the Morphology method.
2217 %               If 'No' force image to be re-iterated by each kernel.
2218 %               Otherwise merge the results using the mathematical compose
2219 %               method given.
2220 %
2221 %    o bias: Convolution Output Bias.
2222 %
2223 %    o exception: return any errors or warnings in this structure.
2224 %
2225 */
2226
2227
2228 /* Apply a Morphology Primative to an image using the given kernel.
2229 ** Two pre-created images must be provided, no image is created.
2230 ** Returning the number of pixels that changed.
2231 */
2232 static size_t MorphologyPrimitive(const Image *image, Image
2233      *result_image, const MorphologyMethod method, const ChannelType channel,
2234      const KernelInfo *kernel,const double bias,ExceptionInfo *exception)
2235 {
2236 #define MorphologyTag  "Morphology/Image"
2237
2238   CacheView
2239     *p_view,
2240     *q_view;
2241
2242   ssize_t
2243     y, offx, offy,
2244     changed;
2245
2246   MagickBooleanType
2247     status;
2248
2249   MagickOffsetType
2250     progress;
2251
2252   assert(image != (Image *) NULL);
2253   assert(image->signature == MagickSignature);
2254   assert(result_image != (Image *) NULL);
2255   assert(result_image->signature == MagickSignature);
2256   assert(kernel != (KernelInfo *) NULL);
2257   assert(kernel->signature == MagickSignature);
2258   assert(exception != (ExceptionInfo *) NULL);
2259   assert(exception->signature == MagickSignature);
2260
2261   status=MagickTrue;
2262   changed=0;
2263   progress=0;
2264
2265   p_view=AcquireCacheView(image);
2266   q_view=AcquireCacheView(result_image);
2267
2268   /* Some methods (including convolve) needs use a reflected kernel.
2269    * Adjust 'origin' offsets to loop though kernel as a reflection.
2270    */
2271   offx = kernel->x;
2272   offy = kernel->y;
2273   switch(method) {
2274     case ConvolveMorphology:
2275     case DilateMorphology:
2276     case DilateIntensityMorphology:
2277     case DistanceMorphology:
2278       /* kernel needs to used with reflection about origin */
2279       offx = (ssize_t) kernel->width-offx-1;
2280       offy = (ssize_t) kernel->height-offy-1;
2281       break;
2282     case ErodeMorphology:
2283     case ErodeIntensityMorphology:
2284     case HitAndMissMorphology:
2285     case ThinningMorphology:
2286     case ThickenMorphology:
2287       /* kernel is user as is, without reflection */
2288       break;
2289     default:
2290       assert("Not a Primitive Morphology Method" != (char *) NULL);
2291       break;
2292   }
2293
2294 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2295   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2296 #endif
2297   for (y=0; y < (ssize_t) image->rows; y++)
2298   {
2299     MagickBooleanType
2300       sync;
2301
2302     register const PixelPacket
2303       *restrict p;
2304
2305     register const IndexPacket
2306       *restrict p_indexes;
2307
2308     register PixelPacket
2309       *restrict q;
2310
2311     register IndexPacket
2312       *restrict q_indexes;
2313
2314     register ssize_t
2315       x;
2316
2317     size_t
2318       r;
2319
2320     if (status == MagickFalse)
2321       continue;
2322     p=GetCacheViewVirtualPixels(p_view, -offx,  y-offy,
2323          image->columns+kernel->width,  kernel->height,  exception);
2324     q=GetCacheViewAuthenticPixels(q_view,0,y,result_image->columns,1,
2325          exception);
2326     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2327       {
2328         status=MagickFalse;
2329         continue;
2330       }
2331     p_indexes=GetCacheViewVirtualIndexQueue(p_view);
2332     q_indexes=GetCacheViewAuthenticIndexQueue(q_view);
2333     r = (image->columns+kernel->width)*offy+offx; /* constant */
2334
2335     for (x=0; x < (ssize_t) image->columns; x++)
2336     {
2337        ssize_t
2338         v;
2339
2340       register ssize_t
2341         u;
2342
2343       register const double
2344         *restrict k;
2345
2346       register const PixelPacket
2347         *restrict k_pixels;
2348
2349       register const IndexPacket
2350         *restrict k_indexes;
2351
2352       MagickPixelPacket
2353         result,
2354         min,
2355         max;
2356
2357       /* Copy input to ouput image for unused channels
2358        * This removes need for 'cloning' a new image every iteration
2359        */
2360       *q = p[r];
2361       if (image->colorspace == CMYKColorspace)
2362         q_indexes[x] = p_indexes[r];
2363
2364       /* Defaults */
2365       min.red     =
2366       min.green   =
2367       min.blue    =
2368       min.opacity =
2369       min.index   = (MagickRealType) QuantumRange;
2370       max.red     =
2371       max.green   =
2372       max.blue    =
2373       max.opacity =
2374       max.index   = (MagickRealType) 0;
2375       /* default result is the original pixel value */
2376       result.red     = (MagickRealType) p[r].red;
2377       result.green   = (MagickRealType) p[r].green;
2378       result.blue    = (MagickRealType) p[r].blue;
2379       result.opacity = QuantumRange - (MagickRealType) p[r].opacity;
2380       result.index   = 0.0;
2381       if ( image->colorspace == CMYKColorspace)
2382          result.index   = (MagickRealType) p_indexes[r];
2383
2384       switch (method) {
2385         case ConvolveMorphology:
2386           /* Set the user defined bias of the weighted average output */
2387           result.red     =
2388           result.green   =
2389           result.blue    =
2390           result.opacity =
2391           result.index   = bias;
2392           break;
2393         case DilateIntensityMorphology:
2394         case ErodeIntensityMorphology:
2395           /* use a boolean flag indicating when first match found */
2396           result.red = 0.0;  /* result is not used otherwise */
2397           break;
2398         default:
2399           break;
2400       }
2401
2402       switch ( method ) {
2403         case ConvolveMorphology:
2404             /* Weighted Average of pixels using reflected kernel
2405             **
2406             ** NOTE for correct working of this operation for asymetrical
2407             ** kernels, the kernel needs to be applied in its reflected form.
2408             ** That is its values needs to be reversed.
2409             **
2410             ** Correlation is actually the same as this but without reflecting
2411             ** the kernel, and thus 'lower-level' that Convolution.  However
2412             ** as Convolution is the more common method used, and it does not
2413             ** really cost us much in terms of processing to use a reflected
2414             ** kernel, so it is Convolution that is implemented.
2415             **
2416             ** Correlation will have its kernel reflected before calling
2417             ** this function to do a Convolve.
2418             **
2419             ** For more details of Correlation vs Convolution see
2420             **   http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
2421             */
2422             if (((channel & SyncChannels) != 0 ) &&
2423                       (image->matte == MagickTrue))
2424               { /* Channel has a 'Sync' Flag, and Alpha Channel enabled.
2425                 ** Weight the color channels with Alpha Channel so that
2426                 ** transparent pixels are not part of the results.
2427                 */
2428                 MagickRealType
2429                   alpha,  /* color channel weighting : kernel*alpha  */
2430                   gamma;  /* divisor, sum of weighting values */
2431
2432                 gamma=0.0;
2433                 k = &kernel->values[ kernel->width*kernel->height-1 ];
2434                 k_pixels = p;
2435                 k_indexes = p_indexes;
2436                 for (v=0; v < (ssize_t) kernel->height; v++) {
2437                   for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2438                     if ( IsNan(*k) ) continue;
2439                     alpha=(*k)*(QuantumScale*(QuantumRange-
2440                                           k_pixels[u].opacity));
2441                     gamma += alpha;
2442                     result.red     += alpha*k_pixels[u].red;
2443                     result.green   += alpha*k_pixels[u].green;
2444                     result.blue    += alpha*k_pixels[u].blue;
2445                     result.opacity += (*k)*(QuantumRange-k_pixels[u].opacity);
2446                     if ( image->colorspace == CMYKColorspace)
2447                       result.index   += alpha*k_indexes[u];
2448                   }
2449                   k_pixels += image->columns+kernel->width;
2450                   k_indexes += image->columns+kernel->width;
2451                 }
2452                 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2453                 result.red *= gamma;
2454                 result.green *= gamma;
2455                 result.blue *= gamma;
2456                 result.opacity *= gamma;
2457                 result.index *= gamma;
2458               }
2459             else
2460               {
2461                 /* No 'Sync' flag, or no Alpha involved.
2462                 ** Convolution is simple individual channel weigthed sum.
2463                 */
2464                 k = &kernel->values[ kernel->width*kernel->height-1 ];
2465                 k_pixels = p;
2466                 k_indexes = p_indexes;
2467                 for (v=0; v < (ssize_t) kernel->height; v++) {
2468                   for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2469                     if ( IsNan(*k) ) continue;
2470                     result.red     += (*k)*k_pixels[u].red;
2471                     result.green   += (*k)*k_pixels[u].green;
2472                     result.blue    += (*k)*k_pixels[u].blue;
2473                     result.opacity += (*k)*(QuantumRange-k_pixels[u].opacity);
2474                     if ( image->colorspace == CMYKColorspace)
2475                       result.index   += (*k)*k_indexes[u];
2476                   }
2477                   k_pixels += image->columns+kernel->width;
2478                   k_indexes += image->columns+kernel->width;
2479                 }
2480               }
2481             break;
2482
2483         case ErodeMorphology:
2484             /* Minimum Value within kernel neighbourhood
2485             **
2486             ** NOTE that the kernel is not reflected for this operation!
2487             **
2488             ** NOTE: in normal Greyscale Morphology, the kernel value should
2489             ** be added to the real value, this is currently not done, due to
2490             ** the nature of the boolean kernels being used.
2491             */
2492             k = kernel->values;
2493             k_pixels = p;
2494             k_indexes = p_indexes;
2495             for (v=0; v < (ssize_t) kernel->height; v++) {
2496               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
2497                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
2498                 Minimize(min.red,     (double) k_pixels[u].red);
2499                 Minimize(min.green,   (double) k_pixels[u].green);
2500                 Minimize(min.blue,    (double) k_pixels[u].blue);
2501                 Minimize(min.opacity,
2502                             QuantumRange-(double) k_pixels[u].opacity);
2503                 if ( image->colorspace == CMYKColorspace)
2504                   Minimize(min.index,   (double) k_indexes[u]);
2505               }
2506               k_pixels += image->columns+kernel->width;
2507               k_indexes += image->columns+kernel->width;
2508             }
2509             break;
2510
2511
2512         case DilateMorphology:
2513             /* Maximum Value within kernel neighbourhood
2514             **
2515             ** NOTE for correct working of this operation for asymetrical
2516             ** kernels, the kernel needs to be applied in its reflected form.
2517             ** That is its values needs to be reversed.
2518             **
2519             ** NOTE: in normal Greyscale Morphology, the kernel value should
2520             ** be added to the real value, this is currently not done, due to
2521             ** the nature of the boolean kernels being used.
2522             **
2523             */
2524             k = &kernel->values[ kernel->width*kernel->height-1 ];
2525             k_pixels = p;
2526             k_indexes = p_indexes;
2527             for (v=0; v < (ssize_t) kernel->height; v++) {
2528               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2529                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
2530                 Maximize(max.red,     (double) k_pixels[u].red);
2531                 Maximize(max.green,   (double) k_pixels[u].green);
2532                 Maximize(max.blue,    (double) k_pixels[u].blue);
2533                 Maximize(max.opacity,
2534                             QuantumRange-(double) k_pixels[u].opacity);
2535                 if ( image->colorspace == CMYKColorspace)
2536                   Maximize(max.index,   (double) k_indexes[u]);
2537               }
2538               k_pixels += image->columns+kernel->width;
2539               k_indexes += image->columns+kernel->width;
2540             }
2541             break;
2542
2543         case HitAndMissMorphology:
2544         case ThinningMorphology:
2545         case ThickenMorphology:
2546             /* Minimum of Foreground Pixel minus Maxumum of Background Pixels
2547             **
2548             ** NOTE that the kernel is not reflected for this operation,
2549             ** and consists of both foreground and background pixel
2550             ** neighbourhoods, 0.0 for background, and 1.0 for foreground
2551             ** with either Nan or 0.5 values for don't care.
2552             **
2553             ** Note that this can produce negative results, though really
2554             ** only a positive match has any real value.
2555             */
2556             k = kernel->values;
2557             k_pixels = p;
2558             k_indexes = p_indexes;
2559             for (v=0; v < (ssize_t) kernel->height; v++) {
2560               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
2561                 if ( IsNan(*k) ) continue;
2562                 if ( (*k) > 0.7 )
2563                 { /* minimim of foreground pixels */
2564                   Minimize(min.red,     (double) k_pixels[u].red);
2565                   Minimize(min.green,   (double) k_pixels[u].green);
2566                   Minimize(min.blue,    (double) k_pixels[u].blue);
2567                   Minimize(min.opacity,
2568                               QuantumRange-(double) k_pixels[u].opacity);
2569                   if ( image->colorspace == CMYKColorspace)
2570                     Minimize(min.index,   (double) k_indexes[u]);
2571                 }
2572                 else if ( (*k) < 0.3 )
2573                 { /* maximum of background pixels */
2574                   Maximize(max.red,     (double) k_pixels[u].red);
2575                   Maximize(max.green,   (double) k_pixels[u].green);
2576                   Maximize(max.blue,    (double) k_pixels[u].blue);
2577                   Maximize(max.opacity,
2578                               QuantumRange-(double) k_pixels[u].opacity);
2579                   if ( image->colorspace == CMYKColorspace)
2580                     Maximize(max.index,   (double) k_indexes[u]);
2581                 }
2582               }
2583               k_pixels += image->columns+kernel->width;
2584               k_indexes += image->columns+kernel->width;
2585             }
2586             /* Pattern Match  only if min fg larger than min bg pixels */
2587             min.red     -= max.red;     Maximize( min.red,     0.0 );
2588             min.green   -= max.green;   Maximize( min.green,   0.0 );
2589             min.blue    -= max.blue;    Maximize( min.blue,    0.0 );
2590             min.opacity -= max.opacity; Maximize( min.opacity, 0.0 );
2591             min.index   -= max.index;   Maximize( min.index,   0.0 );
2592             break;
2593
2594         case ErodeIntensityMorphology:
2595             /* Select Pixel with Minimum Intensity within kernel neighbourhood
2596             **
2597             ** WARNING: the intensity test fails for CMYK and does not
2598             ** take into account the moderating effect of teh alpha channel
2599             ** on the intensity.
2600             **
2601             ** NOTE that the kernel is not reflected for this operation!
2602             */
2603             k = kernel->values;
2604             k_pixels = p;
2605             k_indexes = p_indexes;
2606             for (v=0; v < (ssize_t) kernel->height; v++) {
2607               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
2608                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
2609                 if ( result.red == 0.0 ||
2610                      PixelIntensity(&(k_pixels[u])) < PixelIntensity(q) ) {
2611                   /* copy the whole pixel - no channel selection */
2612                   *q = k_pixels[u];
2613                   if ( result.red > 0.0 ) changed++;
2614                   result.red = 1.0;
2615                 }
2616               }
2617               k_pixels += image->columns+kernel->width;
2618               k_indexes += image->columns+kernel->width;
2619             }
2620             break;
2621
2622         case DilateIntensityMorphology:
2623             /* Select Pixel with Maximum Intensity within kernel neighbourhood
2624             **
2625             ** WARNING: the intensity test fails for CMYK and does not
2626             ** take into account the moderating effect of the alpha channel
2627             ** on the intensity (yet).
2628             **
2629             ** NOTE for correct working of this operation for asymetrical
2630             ** kernels, the kernel needs to be applied in its reflected form.
2631             ** That is its values needs to be reversed.
2632             */
2633             k = &kernel->values[ kernel->width*kernel->height-1 ];
2634             k_pixels = p;
2635             k_indexes = p_indexes;
2636             for (v=0; v < (ssize_t) kernel->height; v++) {
2637               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2638                 if ( IsNan(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
2639                 if ( result.red == 0.0 ||
2640                      PixelIntensity(&(k_pixels[u])) > PixelIntensity(q) ) {
2641                   /* copy the whole pixel - no channel selection */
2642                   *q = k_pixels[u];
2643                   if ( result.red > 0.0 ) changed++;
2644                   result.red = 1.0;
2645                 }
2646               }
2647               k_pixels += image->columns+kernel->width;
2648               k_indexes += image->columns+kernel->width;
2649             }
2650             break;
2651
2652
2653         case DistanceMorphology:
2654             /* Add kernel Value and select the minimum value found.
2655             ** The result is a iterative distance from edge of image shape.
2656             **
2657             ** All Distance Kernels are symetrical, but that may not always
2658             ** be the case. For example how about a distance from left edges?
2659             ** To work correctly with asymetrical kernels the reflected kernel
2660             ** needs to be applied.
2661             **
2662             ** Actually this is really a GreyErode with a negative kernel!
2663             **
2664             */
2665             k = &kernel->values[ kernel->width*kernel->height-1 ];
2666             k_pixels = p;
2667             k_indexes = p_indexes;
2668             for (v=0; v < (ssize_t) kernel->height; v++) {
2669               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2670                 if ( IsNan(*k) ) continue;
2671                 Minimize(result.red,     (*k)+k_pixels[u].red);
2672                 Minimize(result.green,   (*k)+k_pixels[u].green);
2673                 Minimize(result.blue,    (*k)+k_pixels[u].blue);
2674                 Minimize(result.opacity, (*k)+QuantumRange-k_pixels[u].opacity);
2675                 if ( image->colorspace == CMYKColorspace)
2676                   Minimize(result.index,   (*k)+k_indexes[u]);
2677               }
2678               k_pixels += image->columns+kernel->width;
2679               k_indexes += image->columns+kernel->width;
2680             }
2681             break;
2682
2683         case UndefinedMorphology:
2684         default:
2685             break; /* Do nothing */
2686       }
2687       /* Final mathematics of results (combine with original image?)
2688       **
2689       ** NOTE: Difference Morphology operators Edge* and *Hat could also
2690       ** be done here but works better with iteration as a image difference
2691       ** in the controling function (below).  Thicken and Thinning however
2692       ** should be done here so thay can be iterated correctly.
2693       */
2694       switch ( method ) {
2695         case HitAndMissMorphology:
2696         case ErodeMorphology:
2697           result = min;    /* minimum of neighbourhood */
2698           break;
2699         case DilateMorphology:
2700           result = max;    /* maximum of neighbourhood */
2701           break;
2702         case ThinningMorphology:
2703           /* subtract pattern match from original */
2704           result.red     -= min.red;
2705           result.green   -= min.green;
2706           result.blue    -= min.blue;
2707           result.opacity -= min.opacity;
2708           result.index   -= min.index;
2709           break;
2710         case ThickenMorphology:
2711           /* Union with original image (maximize) - or should this be + */
2712           Maximize( result.red,     min.red );
2713           Maximize( result.green,   min.green );
2714           Maximize( result.blue,    min.blue );
2715           Maximize( result.opacity, min.opacity );
2716           Maximize( result.index,   min.index );
2717           break;
2718         default:
2719           /* result directly calculated or assigned */
2720           break;
2721       }
2722       /* Assign the resulting pixel values - Clamping Result */
2723       switch ( method ) {
2724         case UndefinedMorphology:
2725         case DilateIntensityMorphology:
2726         case ErodeIntensityMorphology:
2727           break;  /* full pixel was directly assigned - not a channel method */
2728         default:
2729           if ((channel & RedChannel) != 0)
2730             q->red = ClampToQuantum(result.red);
2731           if ((channel & GreenChannel) != 0)
2732             q->green = ClampToQuantum(result.green);
2733           if ((channel & BlueChannel) != 0)
2734             q->blue = ClampToQuantum(result.blue);
2735           if ((channel & OpacityChannel) != 0
2736               && image->matte == MagickTrue )
2737             q->opacity = ClampToQuantum(QuantumRange-result.opacity);
2738           if ((channel & IndexChannel) != 0
2739               && image->colorspace == CMYKColorspace)
2740             q_indexes[x] = ClampToQuantum(result.index);
2741           break;
2742       }
2743       /* Count up changed pixels */
2744       if (   ( p[r].red != q->red )
2745           || ( p[r].green != q->green )
2746           || ( p[r].blue != q->blue )
2747           || ( p[r].opacity != q->opacity )
2748           || ( image->colorspace == CMYKColorspace &&
2749                   p_indexes[r] != q_indexes[x] ) )
2750         changed++;  /* The pixel had some value changed! */
2751       p++;
2752       q++;
2753     } /* x */
2754     sync=SyncCacheViewAuthenticPixels(q_view,exception);
2755     if (sync == MagickFalse)
2756       status=MagickFalse;
2757     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2758       {
2759         MagickBooleanType
2760           proceed;
2761
2762 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2763   #pragma omp critical (MagickCore_MorphologyImage)
2764 #endif
2765         proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
2766         if (proceed == MagickFalse)
2767           status=MagickFalse;
2768       }
2769   } /* y */
2770   result_image->type=image->type;
2771   q_view=DestroyCacheView(q_view);
2772   p_view=DestroyCacheView(p_view);
2773   return(status ? (size_t) changed : 0);
2774 }
2775
2776
2777 MagickExport Image *MorphologyApply(const Image *image, const ChannelType
2778      channel,const MorphologyMethod method, const ssize_t iterations,
2779      const KernelInfo *kernel, const CompositeOperator compose,
2780      const double bias, ExceptionInfo *exception)
2781 {
2782   Image
2783     *curr_image,    /* Image we are working with or iterating */
2784     *work_image,    /* secondary image for primative iteration */
2785     *save_image,    /* saved image - for 'edge' method only */
2786     *rslt_image;    /* resultant image - after multi-kernel handling */
2787
2788   KernelInfo
2789     *reflected_kernel, /* A reflected copy of the kernel (if needed) */
2790     *norm_kernel,      /* the current normal un-reflected kernel */
2791     *rflt_kernel,      /* the current reflected kernel (if needed) */
2792     *this_kernel;      /* the kernel being applied */
2793
2794   MorphologyMethod
2795     primative;      /* the current morphology primative being applied */
2796
2797   CompositeOperator
2798     rslt_compose;   /* multi-kernel compose method for results to use */
2799
2800   MagickBooleanType
2801     verbose;        /* verbose output of results */
2802
2803   size_t
2804     method_loop,    /* Loop 1: number of compound method iterations */
2805     method_limit,   /*         maximum number of compound method iterations */
2806     kernel_number,  /* Loop 2: the kernel number being applied */
2807     stage_loop,     /* Loop 3: primative loop for compound morphology */
2808     stage_limit,    /*         how many primatives in this compound */
2809     kernel_loop,    /* Loop 4: iterate the kernel (basic morphology) */
2810     kernel_limit,   /*         number of times to iterate kernel */
2811     count,          /* total count of primative steps applied */
2812     changed,        /* number pixels changed by last primative operation */
2813     kernel_changed, /* total count of changed using iterated kernel */
2814     method_changed; /* total count of changed over method iteration */
2815
2816   char
2817     v_info[80];
2818
2819   assert(image != (Image *) NULL);
2820   assert(image->signature == MagickSignature);
2821   assert(kernel != (KernelInfo *) NULL);
2822   assert(kernel->signature == MagickSignature);
2823   assert(exception != (ExceptionInfo *) NULL);
2824   assert(exception->signature == MagickSignature);
2825
2826   count = 0;      /* number of low-level morphology primatives performed */
2827   if ( iterations == 0 )
2828     return((Image *)NULL);   /* null operation - nothing to do! */
2829
2830   kernel_limit = (size_t) iterations;
2831   if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
2832      kernel_limit = image->columns > image->rows ? image->columns : image->rows;
2833
2834   verbose = ( GetImageArtifact(image,"verbose") != (const char *) NULL ) ?
2835     MagickTrue : MagickFalse;
2836
2837   /* initialise for cleanup */
2838   curr_image = (Image *) image;
2839   work_image = save_image = rslt_image = (Image *) NULL;
2840   reflected_kernel = (KernelInfo *) NULL;
2841
2842   /* Initialize specific methods
2843    * + which loop should use the given iteratations
2844    * + how many primatives make up the compound morphology
2845    * + multi-kernel compose method to use (by default)
2846    */
2847   method_limit = 1;       /* just do method once, unless otherwise set */
2848   stage_limit = 1;        /* assume method is not a compount */
2849   rslt_compose = compose; /* and we are composing multi-kernels as given */
2850   switch( method ) {
2851     case SmoothMorphology:  /* 4 primative compound morphology */
2852       stage_limit = 4;
2853       break;
2854     case OpenMorphology:    /* 2 primative compound morphology */
2855     case OpenIntensityMorphology:
2856     case TopHatMorphology:
2857     case CloseMorphology:
2858     case CloseIntensityMorphology:
2859     case BottomHatMorphology:
2860     case EdgeMorphology:
2861       stage_limit = 2;
2862       break;
2863     case HitAndMissMorphology:
2864       kernel_limit = 1;          /* no method or kernel iteration */
2865       rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
2866       break;
2867     case ThinningMorphology:
2868     case ThickenMorphology:
2869       method_limit = kernel_limit;  /* iterate method with each kernel */
2870       kernel_limit = 1;             /* do not do kernel iteration  */
2871     case DistanceMorphology:
2872       rslt_compose = NoCompositeOp; /* Re-iterate with multiple kernels */
2873       break;
2874     default:
2875       break;
2876   }
2877
2878   /* Handle user (caller) specified multi-kernel composition method */
2879   if ( compose != UndefinedCompositeOp )
2880     rslt_compose = compose;  /* override default composition for method */
2881   if ( rslt_compose == UndefinedCompositeOp )
2882     rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
2883
2884   /* Some methods require a reflected kernel to use with primatives.
2885    * Create the reflected kernel for those methods. */
2886   switch ( method ) {
2887     case CorrelateMorphology:
2888     case CloseMorphology:
2889     case CloseIntensityMorphology:
2890     case BottomHatMorphology:
2891     case SmoothMorphology:
2892       reflected_kernel = CloneKernelInfo(kernel);
2893       if (reflected_kernel == (KernelInfo *) NULL)
2894         goto error_cleanup;
2895       RotateKernelInfo(reflected_kernel,180);
2896       break;
2897     default:
2898       break;
2899   }
2900
2901   /* Loop 1:  iterate the compound method */
2902   method_loop = 0;
2903   method_changed = 1;
2904   while ( method_loop < method_limit && method_changed > 0 ) {
2905     method_loop++;
2906     method_changed = 0;
2907
2908     /* Loop 2:  iterate over each kernel in a multi-kernel list */
2909     norm_kernel = (KernelInfo *) kernel;
2910     this_kernel = (KernelInfo *) kernel;
2911     rflt_kernel = reflected_kernel;
2912
2913     kernel_number = 0;
2914     while ( norm_kernel != NULL ) {
2915
2916       /* Loop 3: Compound Morphology Staging - Select Primative to apply */
2917       stage_loop = 0;          /* the compound morphology stage number */
2918       while ( stage_loop < stage_limit ) {
2919         stage_loop++;   /* The stage of the compound morphology */
2920
2921         /* Select primative morphology for this stage of compound method */
2922         this_kernel = norm_kernel; /* default use unreflected kernel */
2923         primative = method;        /* Assume method is a primative */
2924         switch( method ) {
2925           case ErodeMorphology:      /* just erode */
2926           case EdgeInMorphology:     /* erode and image difference */
2927             primative = ErodeMorphology;
2928             break;
2929           case DilateMorphology:     /* just dilate */
2930           case EdgeOutMorphology:    /* dilate and image difference */
2931             primative = DilateMorphology;
2932             break;
2933           case OpenMorphology:       /* erode then dialate */
2934           case TopHatMorphology:     /* open and image difference */
2935             primative = ErodeMorphology;
2936             if ( stage_loop == 2 )
2937               primative = DilateMorphology;
2938             break;
2939           case OpenIntensityMorphology:
2940             primative = ErodeIntensityMorphology;
2941             if ( stage_loop == 2 )
2942               primative = DilateIntensityMorphology;
2943             break;
2944           case CloseMorphology:      /* dilate, then erode */
2945           case BottomHatMorphology:  /* close and image difference */
2946             this_kernel = rflt_kernel; /* use the reflected kernel */
2947             primative = DilateMorphology;
2948             if ( stage_loop == 2 )
2949               primative = ErodeMorphology;
2950             break;
2951           case CloseIntensityMorphology:
2952             this_kernel = rflt_kernel; /* use the reflected kernel */
2953             primative = DilateIntensityMorphology;
2954             if ( stage_loop == 2 )
2955               primative = ErodeIntensityMorphology;
2956             break;
2957           case SmoothMorphology:         /* open, close */
2958             switch ( stage_loop ) {
2959               case 1: /* start an open method, which starts with Erode */
2960                 primative = ErodeMorphology;
2961                 break;
2962               case 2:  /* now Dilate the Erode */
2963                 primative = DilateMorphology;
2964                 break;
2965               case 3:  /* Reflect kernel a close */
2966                 this_kernel = rflt_kernel; /* use the reflected kernel */
2967                 primative = DilateMorphology;
2968                 break;
2969               case 4:  /* Finish the Close */
2970                 this_kernel = rflt_kernel; /* use the reflected kernel */
2971                 primative = ErodeMorphology;
2972                 break;
2973             }
2974             break;
2975           case EdgeMorphology:        /* dilate and erode difference */
2976             primative = DilateMorphology;
2977             if ( stage_loop == 2 ) {
2978               save_image = curr_image;      /* save the image difference */
2979               curr_image = (Image *) image;
2980               primative = ErodeMorphology;
2981             }
2982             break;
2983           case CorrelateMorphology:
2984             /* A Correlation is a Convolution with a reflected kernel.
2985             ** However a Convolution is a weighted sum using a reflected
2986             ** kernel.  It may seem stange to convert a Correlation into a
2987             ** Convolution as the Correlation is the simplier method, but
2988             ** Convolution is much more commonly used, and it makes sense to
2989             ** implement it directly so as to avoid the need to duplicate the
2990             ** kernel when it is not required (which is typically the
2991             ** default).
2992             */
2993             this_kernel = rflt_kernel; /* use the reflected kernel */
2994             primative = ConvolveMorphology;
2995             break;
2996           default:
2997             break;
2998         }
2999         assert( this_kernel != (KernelInfo *) NULL );
3000
3001         /* Extra information for debugging compound operations */
3002         if ( verbose == MagickTrue ) {
3003           if ( stage_limit > 1 )
3004             (void) FormatMagickString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
3005              MagickOptionToMnemonic(MagickMorphologyOptions,method),(double)
3006              method_loop,(double) stage_loop);
3007           else if ( primative != method )
3008             (void) FormatMagickString(v_info, MaxTextExtent, "%s:%.20g -> ",
3009               MagickOptionToMnemonic(MagickMorphologyOptions, method),(double)
3010               method_loop);
3011           else
3012             v_info[0] = '\0';
3013         }
3014
3015         /* Loop 4: Iterate the kernel with primative */
3016         kernel_loop = 0;
3017         kernel_changed = 0;
3018         changed = 1;
3019         while ( kernel_loop < kernel_limit && changed > 0 ) {
3020           kernel_loop++;     /* the iteration of this kernel */
3021
3022           /* Create a destination image, if not yet defined */
3023           if ( work_image == (Image *) NULL )
3024             {
3025               work_image=CloneImage(image,0,0,MagickTrue,exception);
3026               if (work_image == (Image *) NULL)
3027                 goto error_cleanup;
3028               if (SetImageStorageClass(work_image,DirectClass) == MagickFalse)
3029                 {
3030                   InheritException(exception,&work_image->exception);
3031                   goto error_cleanup;
3032                 }
3033             }
3034
3035           /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
3036           count++;
3037           changed = MorphologyPrimitive(curr_image, work_image, primative,
3038                         channel, this_kernel, bias, exception);
3039           kernel_changed += changed;
3040           method_changed += changed;
3041
3042           if ( verbose == MagickTrue ) {
3043             if ( kernel_loop > 1 )
3044               fprintf(stderr, "\n"); /* add end-of-line from previous */
3045             (void) fprintf(stderr, "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
3046               v_info,MagickOptionToMnemonic(MagickMorphologyOptions,
3047               primative),(this_kernel == rflt_kernel ) ? "*" : "",
3048               (double) (method_loop+kernel_loop-1),(double) kernel_number,
3049               (double) count,(double) changed);
3050           }
3051           /* prepare next loop */
3052           { Image *tmp = work_image;   /* swap images for iteration */
3053             work_image = curr_image;
3054             curr_image = tmp;
3055           }
3056           if ( work_image == image )
3057             work_image = (Image *) NULL; /* replace input 'image' */
3058
3059         } /* End Loop 4: Iterate the kernel with primative */
3060
3061         if ( verbose == MagickTrue && kernel_changed != changed )
3062           fprintf(stderr, "   Total %.20g",(double) kernel_changed);
3063         if ( verbose == MagickTrue && stage_loop < stage_limit )
3064           fprintf(stderr, "\n"); /* add end-of-line before looping */
3065
3066 #if 0
3067     fprintf(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
3068     fprintf(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
3069     fprintf(stderr, "      work =0x%lx\n", (unsigned long)work_image);
3070     fprintf(stderr, "      save =0x%lx\n", (unsigned long)save_image);
3071     fprintf(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
3072 #endif
3073
3074       } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
3075
3076       /*  Final Post-processing for some Compound Methods
3077       **
3078       ** The removal of any 'Sync' channel flag in the Image Compositon
3079       ** below ensures the methematical compose method is applied in a
3080       ** purely mathematical way, and only to the selected channels.
3081       ** Turn off SVG composition 'alpha blending'.
3082       */
3083       switch( method ) {
3084         case EdgeOutMorphology:
3085         case EdgeInMorphology:
3086         case TopHatMorphology:
3087         case BottomHatMorphology:
3088           if ( verbose == MagickTrue )
3089             fprintf(stderr, "\n%s: Difference with original image",
3090                  MagickOptionToMnemonic(MagickMorphologyOptions, method) );
3091           (void) CompositeImageChannel(curr_image,
3092                   (ChannelType) (channel & ~SyncChannels),
3093                   DifferenceCompositeOp, image, 0, 0);
3094           break;
3095         case EdgeMorphology:
3096           if ( verbose == MagickTrue )
3097             fprintf(stderr, "\n%s: Difference of Dilate and Erode",
3098                  MagickOptionToMnemonic(MagickMorphologyOptions, method) );
3099           (void) CompositeImageChannel(curr_image,
3100                   (ChannelType) (channel & ~SyncChannels),
3101                   DifferenceCompositeOp, save_image, 0, 0);
3102           save_image = DestroyImage(save_image); /* finished with save image */
3103           break;
3104         default:
3105           break;
3106       }
3107
3108       /* multi-kernel handling:  re-iterate, or compose results */
3109       if ( kernel->next == (KernelInfo *) NULL )
3110         rslt_image = curr_image;   /* just return the resulting image */
3111       else if ( rslt_compose == NoCompositeOp )
3112         { if ( verbose == MagickTrue ) {
3113             if ( this_kernel->next != (KernelInfo *) NULL )
3114               fprintf(stderr, " (re-iterate)");
3115             else
3116               fprintf(stderr, " (done)");
3117           }
3118           rslt_image = curr_image; /* return result, and re-iterate */
3119         }
3120       else if ( rslt_image == (Image *) NULL)
3121         { if ( verbose == MagickTrue )
3122             fprintf(stderr, " (save for compose)");
3123           rslt_image = curr_image;
3124           curr_image = (Image *) image;  /* continue with original image */
3125         }
3126       else
3127         { /* add the new 'current' result to the composition
3128           **
3129           ** The removal of any 'Sync' channel flag in the Image Compositon
3130           ** below ensures the methematical compose method is applied in a
3131           ** purely mathematical way, and only to the selected channels.
3132           ** Turn off SVG composition 'alpha blending'.
3133           */
3134           if ( verbose == MagickTrue )
3135             fprintf(stderr, " (compose \"%s\")",
3136                  MagickOptionToMnemonic(MagickComposeOptions, rslt_compose) );
3137           (void) CompositeImageChannel(rslt_image,
3138                (ChannelType) (channel & ~SyncChannels), rslt_compose,
3139                curr_image, 0, 0);
3140           curr_image = (Image *) image;  /* continue with original image */
3141         }
3142       if ( verbose == MagickTrue )
3143         fprintf(stderr, "\n");
3144
3145       /* loop to the next kernel in a multi-kernel list */
3146       norm_kernel = norm_kernel->next;
3147       if ( rflt_kernel != (KernelInfo *) NULL )
3148         rflt_kernel = rflt_kernel->next;
3149       kernel_number++;
3150     } /* End Loop 2: Loop over each kernel */
3151
3152   } /* End Loop 1: compound method interation */
3153
3154   goto exit_cleanup;
3155
3156   /* Yes goto's are bad, but it makes cleanup lot more efficient */
3157 error_cleanup:
3158   if ( curr_image != (Image *) NULL &&
3159        curr_image != rslt_image &&
3160        curr_image != image )
3161     curr_image = DestroyImage(curr_image);
3162   if ( rslt_image != (Image *) NULL )
3163     rslt_image = DestroyImage(rslt_image);
3164 exit_cleanup:
3165   if ( curr_image != (Image *) NULL &&
3166        curr_image != rslt_image &&
3167        curr_image != image )
3168     curr_image = DestroyImage(curr_image);
3169   if ( work_image != (Image *) NULL )
3170     work_image = DestroyImage(work_image);
3171   if ( save_image != (Image *) NULL )
3172     save_image = DestroyImage(save_image);
3173   if ( reflected_kernel != (KernelInfo *) NULL )
3174     reflected_kernel = DestroyKernelInfo(reflected_kernel);
3175   return(rslt_image);
3176 }
3177 \f
3178 /*
3179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3180 %                                                                             %
3181 %                                                                             %
3182 %                                                                             %
3183 %     M o r p h o l o g y I m a g e C h a n n e l                             %
3184 %                                                                             %
3185 %                                                                             %
3186 %                                                                             %
3187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3188 %
3189 %  MorphologyImageChannel() applies a user supplied kernel to the image
3190 %  according to the given mophology method.
3191 %
3192 %  This function applies any and all user defined settings before calling
3193 %  the above internal function MorphologyApply().
3194 %
3195 %  User defined settings include...
3196 %    * Output Bias for Convolution and correlation   ("-bias")
3197 %    * Kernel Scale/normalize settings     ("-set 'option:convolve:scale'")
3198 %      This can also includes the addition of a scaled unity kernel.
3199 %    * Show Kernel being applied           ("-set option:showkernel 1")
3200 %
3201 %  The format of the MorphologyImage method is:
3202 %
3203 %      Image *MorphologyImage(const Image *image,MorphologyMethod method,
3204 %        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
3205 %
3206 %      Image *MorphologyImageChannel(const Image *image, const ChannelType
3207 %        channel,MorphologyMethod method,const ssize_t iterations,
3208 %        KernelInfo *kernel,ExceptionInfo *exception)
3209 %
3210 %  A description of each parameter follows:
3211 %
3212 %    o image: the image.
3213 %
3214 %    o method: the morphology method to be applied.
3215 %
3216 %    o iterations: apply the operation this many times (or no change).
3217 %                  A value of -1 means loop until no change found.
3218 %                  How this is applied may depend on the morphology method.
3219 %                  Typically this is a value of 1.
3220 %
3221 %    o channel: the channel type.
3222 %
3223 %    o kernel: An array of double representing the morphology kernel.
3224 %              Warning: kernel may be normalized for the Convolve method.
3225 %
3226 %    o exception: return any errors or warnings in this structure.
3227 %
3228 */
3229
3230 MagickExport Image *MorphologyImageChannel(const Image *image,
3231   const ChannelType channel,const MorphologyMethod method,
3232   const ssize_t iterations,const KernelInfo *kernel,ExceptionInfo *exception)
3233 {
3234   const char
3235     *artifact;
3236
3237   KernelInfo
3238     *curr_kernel;
3239
3240   CompositeOperator
3241     compose;
3242
3243   Image
3244     *morphology_image;
3245
3246
3247   /* Apply Convolve/Correlate Normalization and Scaling Factors.
3248    * This is done BEFORE the ShowKernelInfo() function is called so that
3249    * users can see the results of the 'option:convolve:scale' option.
3250    */
3251   curr_kernel = (KernelInfo *) kernel;
3252   if ( method == ConvolveMorphology ||  method == CorrelateMorphology )
3253     {
3254       artifact = GetImageArtifact(image,"convolve:scale");
3255       if ( artifact != (const char *)NULL ) {
3256         if ( curr_kernel == kernel )
3257           curr_kernel = CloneKernelInfo(kernel);
3258         if (curr_kernel == (KernelInfo *) NULL) {
3259           curr_kernel=DestroyKernelInfo(curr_kernel);
3260           return((Image *) NULL);
3261         }
3262         ScaleGeometryKernelInfo(curr_kernel, artifact);
3263       }
3264     }
3265
3266   /* display the (normalized) kernel via stderr */
3267   artifact = GetImageArtifact(image,"showkernel");
3268   if ( artifact == (const char *) NULL)
3269     artifact = GetImageArtifact(image,"convolve:showkernel");
3270   if ( artifact == (const char *) NULL)
3271     artifact = GetImageArtifact(image,"morphology:showkernel");
3272   if ( artifact != (const char *) NULL)
3273     ShowKernelInfo(curr_kernel);
3274
3275   /* override the default handling of multi-kernel morphology results
3276    * if 'Undefined' use the default method
3277    * if 'None' (default for 'Convolve') re-iterate previous result
3278    * otherwise merge resulting images using compose method given
3279    */
3280   compose = UndefinedCompositeOp;  /* use default for method */
3281   artifact = GetImageArtifact(image,"morphology:compose");
3282   if ( artifact != (const char *) NULL)
3283     compose = (CompositeOperator) ParseMagickOption(
3284                              MagickComposeOptions,MagickFalse,artifact);
3285
3286   /* Apply the Morphology */
3287   morphology_image = MorphologyApply(image, channel, method, iterations,
3288                          curr_kernel, compose, image->bias, exception);
3289
3290   /* Cleanup and Exit */
3291   if ( curr_kernel != kernel )
3292     curr_kernel=DestroyKernelInfo(curr_kernel);
3293   return(morphology_image);
3294 }
3295
3296 MagickExport Image *MorphologyImage(const Image *image, const MorphologyMethod
3297   method, const ssize_t iterations,const KernelInfo *kernel, ExceptionInfo
3298   *exception)
3299 {
3300   Image
3301     *morphology_image;
3302
3303   morphology_image=MorphologyImageChannel(image,DefaultChannels,method,
3304     iterations,kernel,exception);
3305   return(morphology_image);
3306 }
3307 \f
3308 /*
3309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3310 %                                                                             %
3311 %                                                                             %
3312 %                                                                             %
3313 +     R o t a t e K e r n e l I n f o                                         %
3314 %                                                                             %
3315 %                                                                             %
3316 %                                                                             %
3317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3318 %
3319 %  RotateKernelInfo() rotates the kernel by the angle given.
3320 %
3321 %  Currently it is restricted to 90 degree angles, of either 1D kernels
3322 %  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
3323 %  It will ignore usless rotations for specific 'named' built-in kernels.
3324 %
3325 %  The format of the RotateKernelInfo method is:
3326 %
3327 %      void RotateKernelInfo(KernelInfo *kernel, double angle)
3328 %
3329 %  A description of each parameter follows:
3330 %
3331 %    o kernel: the Morphology/Convolution kernel
3332 %
3333 %    o angle: angle to rotate in degrees
3334 %
3335 % This function is currently internal to this module only, but can be exported
3336 % to other modules if needed.
3337 */
3338 static void RotateKernelInfo(KernelInfo *kernel, double angle)
3339 {
3340   /* angle the lower kernels first */
3341   if ( kernel->next != (KernelInfo *) NULL)
3342     RotateKernelInfo(kernel->next, angle);
3343
3344   /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
3345   **
3346   ** TODO: expand beyond simple 90 degree rotates, flips and flops
3347   */
3348
3349   /* Modulus the angle */
3350   angle = fmod(angle, 360.0);
3351   if ( angle < 0 )
3352     angle += 360.0;
3353
3354   if ( 337.5 < angle || angle <= 22.5 )
3355     return;   /* Near zero angle - no change! - At least not at this time */
3356
3357   /* Handle special cases */
3358   switch (kernel->type) {
3359     /* These built-in kernels are cylindrical kernels, rotating is useless */
3360     case GaussianKernel:
3361     case DoGKernel:
3362     case LoGKernel:
3363     case DiskKernel:
3364     case PeaksKernel:
3365     case LaplacianKernel:
3366     case ChebyshevKernel:
3367     case ManhattanKernel:
3368     case EuclideanKernel:
3369       return;
3370
3371     /* These may be rotatable at non-90 angles in the future */
3372     /* but simply rotating them in multiples of 90 degrees is useless */
3373     case SquareKernel:
3374     case DiamondKernel:
3375     case PlusKernel:
3376     case CrossKernel:
3377       return;
3378
3379     /* These only allows a +/-90 degree rotation (by transpose) */
3380     /* A 180 degree rotation is useless */
3381     case BlurKernel:
3382     case RectangleKernel:
3383       if ( 135.0 < angle && angle <= 225.0 )
3384         return;
3385       if ( 225.0 < angle && angle <= 315.0 )
3386         angle -= 180;
3387       break;
3388
3389     default:
3390       break;
3391   }
3392   /* Attempt rotations by 45 degrees */
3393   if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
3394     {
3395       if ( kernel->width == 3 && kernel->height == 3 )
3396         { /* Rotate a 3x3 square by 45 degree angle */
3397           MagickRealType t  = kernel->values[0];
3398           kernel->values[0] = kernel->values[3];
3399           kernel->values[3] = kernel->values[6];
3400           kernel->values[6] = kernel->values[7];
3401           kernel->values[7] = kernel->values[8];
3402           kernel->values[8] = kernel->values[5];
3403           kernel->values[5] = kernel->values[2];
3404           kernel->values[2] = kernel->values[1];
3405           kernel->values[1] = t;
3406           /* rotate non-centered origin */
3407           if ( kernel->x != 1 || kernel->y != 1 ) {
3408             ssize_t x,y;
3409             x = (ssize_t) kernel->x-1;
3410             y = (ssize_t) kernel->y-1;
3411                  if ( x == y  ) x = 0;
3412             else if ( x == 0  ) x = -y;
3413             else if ( x == -y ) y = 0;
3414             else if ( y == 0  ) y = x;
3415             kernel->x = (ssize_t) x+1;
3416             kernel->y = (ssize_t) y+1;
3417           }
3418           angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
3419           kernel->angle = fmod(kernel->angle+45.0, 360.0);
3420         }
3421       else
3422         perror("Unable to rotate non-3x3 kernel by 45 degrees");
3423     }
3424   if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
3425     {
3426       if ( kernel->width == 1 || kernel->height == 1 )
3427         { /* Do a transpose of a 1 dimentional kernel,
3428           ** which results in a fast 90 degree rotation of some type.
3429           */
3430           ssize_t
3431             t;
3432           t = (ssize_t) kernel->width;
3433           kernel->width = kernel->height;
3434           kernel->height = (size_t) t;
3435           t = kernel->x;
3436           kernel->x = kernel->y;
3437           kernel->y = t;
3438           if ( kernel->width == 1 ) {
3439             angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
3440             kernel->angle = fmod(kernel->angle+90.0, 360.0);
3441           } else {
3442             angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
3443             kernel->angle = fmod(kernel->angle+270.0, 360.0);
3444           }
3445         }
3446       else if ( kernel->width == kernel->height )
3447         { /* Rotate a square array of values by 90 degrees */
3448           { register size_t
3449               i,j,x,y;
3450             register MagickRealType
3451               *k,t;
3452             k=kernel->values;
3453             for( i=0, x=kernel->width-1;  i<=x;   i++, x--)
3454               for( j=0, y=kernel->height-1;  j<y;   j++, y--)
3455                 { t                    = k[i+j*kernel->width];
3456                   k[i+j*kernel->width] = k[j+x*kernel->width];
3457                   k[j+x*kernel->width] = k[x+y*kernel->width];
3458                   k[x+y*kernel->width] = k[y+i*kernel->width];
3459                   k[y+i*kernel->width] = t;
3460                 }
3461           }
3462           /* rotate the origin - relative to center of array */
3463           { register ssize_t x,y;
3464             x = (ssize_t) (kernel->x*2-kernel->width+1);
3465             y = (ssize_t) (kernel->y*2-kernel->height+1);
3466             kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
3467             kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
3468           }
3469           angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
3470           kernel->angle = fmod(kernel->angle+90.0, 360.0);
3471         }
3472       else
3473         perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
3474     }
3475   if ( 135.0 < angle && angle <= 225.0 )
3476     {
3477       /* For a 180 degree rotation - also know as a reflection
3478        * This is actually a very very common operation!
3479        * Basically all that is needed is a reversal of the kernel data!
3480        * And a reflection of the origon
3481        */
3482       size_t
3483         i,j;
3484       register double
3485         *k,t;
3486
3487       k=kernel->values;
3488       for ( i=0, j=kernel->width*kernel->height-1;  i<j;  i++, j--)
3489         t=k[i],  k[i]=k[j],  k[j]=t;
3490
3491       kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
3492       kernel->y = (ssize_t) kernel->height - kernel->y - 1;
3493       angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
3494       kernel->angle = fmod(kernel->angle+180.0, 360.0);
3495     }
3496   /* At this point angle should at least between -45 (315) and +45 degrees
3497    * In the future some form of non-orthogonal angled rotates could be
3498    * performed here, posibily with a linear kernel restriction.
3499    */
3500
3501   return;
3502 }
3503 \f
3504 /*
3505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3506 %                                                                             %
3507 %                                                                             %
3508 %                                                                             %
3509 %     S c a l e G e o m e t r y K e r n e l I n f o                           %
3510 %                                                                             %
3511 %                                                                             %
3512 %                                                                             %
3513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3514 %
3515 %  ScaleGeometryKernelInfo() takes a geometry argument string, typically
3516 %  provided as a  "-set option:convolve:scale {geometry}" user setting,
3517 %  and modifies the kernel according to the parsed arguments of that setting.
3518 %
3519 %  The first argument (and any normalization flags) are passed to
3520 %  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
3521 %  is then passed to UnityAddKernelInfo() to add a scled unity kernel
3522 %  into the scaled/normalized kernel.
3523 %
3524 %  The format of the ScaleKernelInfo method is:
3525 %
3526 %      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
3527 %               const MagickStatusType normalize_flags )
3528 %
3529 %  A description of each parameter follows:
3530 %
3531 %    o kernel: the Morphology/Convolution kernel to modify
3532 %
3533 %    o geometry:
3534 %             The geometry string to parse, typically from the user provided
3535 %             "-set option:convolve:scale {geometry}" setting.
3536 %
3537 */
3538 MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
3539      const char *geometry)
3540 {
3541   GeometryFlags
3542     flags;
3543   GeometryInfo
3544     args;
3545
3546   SetGeometryInfo(&args);
3547   flags = (GeometryFlags) ParseGeometry(geometry, &args);
3548
3549 #if 0
3550   /* For Debugging Geometry Input */
3551   fprintf(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
3552        flags, args.rho, args.sigma, args.xi, args.psi );
3553 #endif
3554
3555   if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
3556     args.rho *= 0.01,  args.sigma *= 0.01;
3557
3558   if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
3559     args.rho = 1.0;
3560   if ( (flags & SigmaValue) == 0 )
3561     args.sigma = 0.0;
3562
3563   /* Scale/Normalize the input kernel */
3564   ScaleKernelInfo(kernel, args.rho, flags);
3565
3566   /* Add Unity Kernel, for blending with original */
3567   if ( (flags & SigmaValue) != 0 )
3568     UnityAddKernelInfo(kernel, args.sigma);
3569
3570   return;
3571 }
3572 /*
3573 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3574 %                                                                             %
3575 %                                                                             %
3576 %                                                                             %
3577 %     S c a l e K e r n e l I n f o                                           %
3578 %                                                                             %
3579 %                                                                             %
3580 %                                                                             %
3581 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3582 %
3583 %  ScaleKernelInfo() scales the given kernel list by the given amount, with or
3584 %  without normalization of the sum of the kernel values (as per given flags).
3585 %
3586 %  By default (no flags given) the values within the kernel is scaled
3587 %  directly using given scaling factor without change.
3588 %
3589 %  If either of the two 'normalize_flags' are given the kernel will first be
3590 %  normalized and then further scaled by the scaling factor value given.
3591 %
3592 %  Kernel normalization ('normalize_flags' given) is designed to ensure that
3593 %  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
3594 %  morphology methods will fall into -1.0 to +1.0 range.  Note that for
3595 %  non-HDRI versions of IM this may cause images to have any negative results
3596 %  clipped, unless some 'bias' is used.
3597 %
3598 %  More specifically.  Kernels which only contain positive values (such as a
3599 %  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
3600 %  ensuring a 0.0 to +1.0 output range for non-HDRI images.
3601 %
3602 %  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
3603 %  the kernel will be scaled by the absolute of the sum of kernel values, so
3604 %  that it will generally fall within the +/- 1.0 range.
3605 %
3606 %  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
3607 %  will be scaled by just the sum of the postive values, so that its output
3608 %  range will again fall into the  +/- 1.0 range.
3609 %
3610 %  For special kernels designed for locating shapes using 'Correlate', (often
3611 %  only containing +1 and -1 values, representing foreground/brackground
3612 %  matching) a special normalization method is provided to scale the positive
3613 %  values seperatally to those of the negative values, so the kernel will be
3614 %  forced to become a zero-sum kernel better suited to such searches.
3615 %
3616 %  WARNING: Correct normalization of the kernel assumes that the '*_range'
3617 %  attributes within the kernel structure have been correctly set during the
3618 %  kernels creation.
3619 %
3620 %  NOTE: The values used for 'normalize_flags' have been selected specifically
3621 %  to match the use of geometry options, so that '!' means NormalizeValue, '^'
3622 %  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
3623 %
3624 %  The format of the ScaleKernelInfo method is:
3625 %
3626 %      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
3627 %               const MagickStatusType normalize_flags )
3628 %
3629 %  A description of each parameter follows:
3630 %
3631 %    o kernel: the Morphology/Convolution kernel
3632 %
3633 %    o scaling_factor:
3634 %             multiply all values (after normalization) by this factor if not
3635 %             zero.  If the kernel is normalized regardless of any flags.
3636 %
3637 %    o normalize_flags:
3638 %             GeometryFlags defining normalization method to use.
3639 %             specifically: NormalizeValue, CorrelateNormalizeValue,
3640 %                           and/or PercentValue
3641 %
3642 */
3643 MagickExport void ScaleKernelInfo(KernelInfo *kernel,
3644   const double scaling_factor,const GeometryFlags normalize_flags)
3645 {
3646   register ssize_t
3647     i;
3648
3649   register double
3650     pos_scale,
3651     neg_scale;
3652
3653   /* do the other kernels in a multi-kernel list first */
3654   if ( kernel->next != (KernelInfo *) NULL)
3655     ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
3656
3657   /* Normalization of Kernel */
3658   pos_scale = 1.0;
3659   if ( (normalize_flags&NormalizeValue) != 0 ) {
3660     if ( fabs(kernel->positive_range + kernel->negative_range) > MagickEpsilon )
3661       /* non-zero-summing kernel (generally positive) */
3662       pos_scale = fabs(kernel->positive_range + kernel->negative_range);
3663     else
3664       /* zero-summing kernel */
3665       pos_scale = kernel->positive_range;
3666   }
3667   /* Force kernel into a normalized zero-summing kernel */
3668   if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
3669     pos_scale = ( fabs(kernel->positive_range) > MagickEpsilon )
3670                  ? kernel->positive_range : 1.0;
3671     neg_scale = ( fabs(kernel->negative_range) > MagickEpsilon )
3672                  ? -kernel->negative_range : 1.0;
3673   }
3674   else
3675     neg_scale = pos_scale;
3676
3677   /* finialize scaling_factor for positive and negative components */
3678   pos_scale = scaling_factor/pos_scale;
3679   neg_scale = scaling_factor/neg_scale;
3680
3681   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
3682     if ( ! IsNan(kernel->values[i]) )
3683       kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
3684
3685   /* convolution output range */
3686   kernel->positive_range *= pos_scale;
3687   kernel->negative_range *= neg_scale;
3688   /* maximum and minimum values in kernel */
3689   kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
3690   kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
3691
3692   /* swap kernel settings if user's scaling factor is negative */
3693   if ( scaling_factor < MagickEpsilon ) {
3694     double t;
3695     t = kernel->positive_range;
3696     kernel->positive_range = kernel->negative_range;
3697     kernel->negative_range = t;
3698     t = kernel->maximum;
3699     kernel->maximum = kernel->minimum;
3700     kernel->minimum = 1;
3701   }
3702
3703   return;
3704 }
3705 \f
3706 /*
3707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3708 %                                                                             %
3709 %                                                                             %
3710 %                                                                             %
3711 %     S h o w K e r n e l I n f o                                             %
3712 %                                                                             %
3713 %                                                                             %
3714 %                                                                             %
3715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3716 %
3717 %  ShowKernelInfo() outputs the details of the given kernel defination to
3718 %  standard error, generally due to a users 'showkernel' option request.
3719 %
3720 %  The format of the ShowKernel method is:
3721 %
3722 %      void ShowKernelInfo(KernelInfo *kernel)
3723 %
3724 %  A description of each parameter follows:
3725 %
3726 %    o kernel: the Morphology/Convolution kernel
3727 %
3728 */
3729 MagickExport void ShowKernelInfo(KernelInfo *kernel)
3730 {
3731   KernelInfo
3732     *k;
3733
3734   size_t
3735     c, i, u, v;
3736
3737   for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
3738
3739     fprintf(stderr, "Kernel");
3740     if ( kernel->next != (KernelInfo *) NULL )
3741       fprintf(stderr, " #%lu", (unsigned long) c );
3742     fprintf(stderr, " \"%s",
3743           MagickOptionToMnemonic(MagickKernelOptions, k->type) );
3744     if ( fabs(k->angle) > MagickEpsilon )
3745       fprintf(stderr, "@%lg", k->angle);
3746     fprintf(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long) k->width,
3747       (unsigned long) k->height,(long) k->x,(long) k->y);
3748     fprintf(stderr,
3749           " with values from %.*lg to %.*lg\n",
3750           GetMagickPrecision(), k->minimum,
3751           GetMagickPrecision(), k->maximum);
3752     fprintf(stderr, "Forming a output range from %.*lg to %.*lg",
3753           GetMagickPrecision(), k->negative_range,
3754           GetMagickPrecision(), k->positive_range);
3755     if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
3756       fprintf(stderr, " (Zero-Summing)\n");
3757     else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
3758       fprintf(stderr, " (Normalized)\n");
3759     else
3760       fprintf(stderr, " (Sum %.*lg)\n",
3761           GetMagickPrecision(), k->positive_range+k->negative_range);
3762     for (i=v=0; v < k->height; v++) {
3763       fprintf(stderr, "%2lu:", (unsigned long) v );
3764       for (u=0; u < k->width; u++, i++)
3765         if ( IsNan(k->values[i]) )
3766           fprintf(stderr," %*s", GetMagickPrecision()+3, "nan");
3767         else
3768           fprintf(stderr," %*.*lg", GetMagickPrecision()+3,
3769               GetMagickPrecision(), k->values[i]);
3770       fprintf(stderr,"\n");
3771     }
3772   }
3773 }
3774 \f
3775 /*
3776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3777 %                                                                             %
3778 %                                                                             %
3779 %                                                                             %
3780 %     U n i t y A d d K e r n a l I n f o                                     %
3781 %                                                                             %
3782 %                                                                             %
3783 %                                                                             %
3784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3785 %
3786 %  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
3787 %  to the given pre-scaled and normalized Kernel.  This in effect adds that
3788 %  amount of the original image into the resulting convolution kernel.  This
3789 %  value is usually provided by the user as a percentage value in the
3790 %  'convolve:scale' setting.
3791 %
3792 %  The resulting effect is to convert the defined kernels into blended
3793 %  soft-blurs, unsharp kernels or into sharpening kernels.
3794 %
3795 %  The format of the UnityAdditionKernelInfo method is:
3796 %
3797 %      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
3798 %
3799 %  A description of each parameter follows:
3800 %
3801 %    o kernel: the Morphology/Convolution kernel
3802 %
3803 %    o scale:
3804 %             scaling factor for the unity kernel to be added to
3805 %             the given kernel.
3806 %
3807 */
3808 MagickExport void UnityAddKernelInfo(KernelInfo *kernel,
3809   const double scale)
3810 {
3811   /* do the other kernels in a multi-kernel list first */
3812   if ( kernel->next != (KernelInfo *) NULL)
3813     UnityAddKernelInfo(kernel->next, scale);
3814
3815   /* Add the scaled unity kernel to the existing kernel */
3816   kernel->values[kernel->x+kernel->y*kernel->width] += scale;
3817   CalcKernelMetaData(kernel);  /* recalculate the meta-data */
3818
3819   return;
3820 }
3821 \f
3822 /*
3823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3824 %                                                                             %
3825 %                                                                             %
3826 %                                                                             %
3827 %     Z e r o K e r n e l N a n s                                             %
3828 %                                                                             %
3829 %                                                                             %
3830 %                                                                             %
3831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3832 %
3833 %  ZeroKernelNans() replaces any special 'nan' value that may be present in
3834 %  the kernel with a zero value.  This is typically done when the kernel will
3835 %  be used in special hardware (GPU) convolution processors, to simply
3836 %  matters.
3837 %
3838 %  The format of the ZeroKernelNans method is:
3839 %
3840 %      void ZeroKernelNans (KernelInfo *kernel)
3841 %
3842 %  A description of each parameter follows:
3843 %
3844 %    o kernel: the Morphology/Convolution kernel
3845 %
3846 */
3847 MagickExport void ZeroKernelNans(KernelInfo *kernel)
3848 {
3849   register size_t
3850     i;
3851
3852   /* do the other kernels in a multi-kernel list first */
3853   if ( kernel->next != (KernelInfo *) NULL)
3854     ZeroKernelNans(kernel->next);
3855
3856   for (i=0; i < (kernel->width*kernel->height); i++)
3857     if ( IsNan(kernel->values[i]) )
3858       kernel->values[i] = 0.0;
3859
3860   return;
3861 }