]> granicus.if.org Git - imagemagick/blob - MagickCore/morphology.c
(no commit message)
[imagemagick] / MagickCore / 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-2012 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 "MagickCore/studio.h"
53 #include "MagickCore/artifact.h"
54 #include "MagickCore/cache-view.h"
55 #include "MagickCore/color-private.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/gem-private.h"
61 #include "MagickCore/hashmap.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/magick.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/monitor-private.h"
68 #include "MagickCore/morphology.h"
69 #include "MagickCore/morphology-private.h"
70 #include "MagickCore/option.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/prepress.h"
73 #include "MagickCore/quantize.h"
74 #include "MagickCore/resource_.h"
75 #include "MagickCore/registry.h"
76 #include "MagickCore/semaphore.h"
77 #include "MagickCore/splay-tree.h"
78 #include "MagickCore/statistic.h"
79 #include "MagickCore/string_.h"
80 #include "MagickCore/string-private.h"
81 #include "MagickCore/thread-private.h"
82 #include "MagickCore/token.h"
83 #include "MagickCore/utility.h"
84 #include "MagickCore/utility-private.h"
85 \f
86
87 /*
88 ** The following test is for special floating point numbers of value NaN (not
89 ** a number), that may be used within a Kernel Definition.  NaN's are defined
90 ** as part of the IEEE standard for floating point number representation.
91 **
92 ** These are used as a Kernel value to mean that this kernel position is not
93 ** part of the kernel neighbourhood for convolution or morphology processing,
94 ** and thus should be ignored.  This allows the use of 'shaped' kernels.
95 **
96 ** The special property that two NaN's are never equal, even if they are from
97 ** the same variable allow you to test if a value is special NaN value.
98 **
99 ** This macro  IsNaN() is thus is only true if the value given is NaN.
100 */
101 #define IsNan(a)   ((a)!=(a))
102
103 /*
104   Other global definitions used by module.
105 */
106 static inline double MagickMin(const double x,const double y)
107 {
108   return( x < y ? x : y);
109 }
110 static inline double MagickMax(const double x,const double y)
111 {
112   return( x > y ? x : y);
113 }
114 #define Minimize(assign,value) assign=MagickMin(assign,value)
115 #define Maximize(assign,value) assign=MagickMax(assign,value)
116
117 /* Integer Factorial Function - for a Binomial kernel */
118 #if 1
119 static inline size_t fact(size_t n)
120 {
121   size_t f,l;
122   for(f=1, l=2; l <= n; f=f*l, l++);
123   return(f);
124 }
125 #elif 1 /* glibc floating point alternatives */
126 #define fact(n) ((size_t)tgamma((double)n+1))
127 #else
128 #define fact(n) ((size_t)lgamma((double)n+1))
129 #endif
130
131
132 /* Currently these are only internal to this module */
133 static void
134   CalcKernelMetaData(KernelInfo *),
135   ExpandMirrorKernelInfo(KernelInfo *),
136   ExpandRotateKernelInfo(KernelInfo *, const double),
137   RotateKernelInfo(KernelInfo *, double);
138 \f
139
140 /* Quick function to find last kernel in a kernel list */
141 static inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
142 {
143   while (kernel->next != (KernelInfo *) NULL)
144     kernel = kernel->next;
145   return(kernel);
146 }
147
148 /*
149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150 %                                                                             %
151 %                                                                             %
152 %                                                                             %
153 %     A c q u i r e K e r n e l I n f o                                       %
154 %                                                                             %
155 %                                                                             %
156 %                                                                             %
157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158 %
159 %  AcquireKernelInfo() takes the given string (generally supplied by the
160 %  user) and converts it into a Morphology/Convolution Kernel.  This allows
161 %  users to specify a kernel from a number of pre-defined kernels, or to fully
162 %  specify their own kernel for a specific Convolution or Morphology
163 %  Operation.
164 %
165 %  The kernel so generated can be any rectangular array of floating point
166 %  values (doubles) with the 'control point' or 'pixel being affected'
167 %  anywhere within that array of values.
168 %
169 %  Previously IM was restricted to a square of odd size using the exact
170 %  center as origin, this is no longer the case, and any rectangular kernel
171 %  with any value being declared the origin. This in turn allows the use of
172 %  highly asymmetrical kernels.
173 %
174 %  The floating point values in the kernel can also include a special value
175 %  known as 'nan' or 'not a number' to indicate that this value is not part
176 %  of the kernel array. This allows you to shaped the kernel within its
177 %  rectangular area. That is 'nan' values provide a 'mask' for the kernel
178 %  shape.  However at least one non-nan value must be provided for correct
179 %  working of a kernel.
180 %
181 %  The returned kernel should be freed using the DestroyKernelInfo() when you
182 %  are finished with it.  Do not free this memory yourself.
183 %
184 %  Input kernel defintion strings can consist of any of three types.
185 %
186 %    "name:args[[@><]"
187 %         Select from one of the built in kernels, using the name and
188 %         geometry arguments supplied.  See AcquireKernelBuiltIn()
189 %
190 %    "WxH[+X+Y][@><]:num, num, num ..."
191 %         a kernel of size W by H, with W*H floating point numbers following.
192 %         the 'center' can be optionally be defined at +X+Y (such that +0+0
193 %         is top left corner). If not defined the pixel in the center, for
194 %         odd sizes, or to the immediate top or left of center for even sizes
195 %         is automatically selected.
196 %
197 %    "num, num, num, num, ..."
198 %         list of floating point numbers defining an 'old style' odd sized
199 %         square kernel.  At least 9 values should be provided for a 3x3
200 %         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
201 %         Values can be space or comma separated.  This is not recommended.
202 %
203 %  You can define a 'list of kernels' which can be used by some morphology
204 %  operators A list is defined as a semi-colon separated list kernels.
205 %
206 %     " kernel ; kernel ; kernel ; "
207 %
208 %  Any extra ';' characters, at start, end or between kernel defintions are
209 %  simply ignored.
210 %
211 %  The special flags will expand a single kernel, into a list of rotated
212 %  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
213 %  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
214 %  The '<' also exands using 90-degree rotates, but giving a 180-degree
215 %  reflected kernel before the +/- 90-degree rotations, which can be important
216 %  for Thinning operations.
217 %
218 %  Note that 'name' kernels will start with an alphabetic character while the
219 %  new kernel specification has a ':' character in its specification string.
220 %  If neither is the case, it is assumed an old style of a simple list of
221 %  numbers generating a odd-sized square kernel has been given.
222 %
223 %  The format of the AcquireKernal method is:
224 %
225 %      KernelInfo *AcquireKernelInfo(const char *kernel_string)
226 %
227 %  A description of each parameter follows:
228 %
229 %    o kernel_string: the Morphology/Convolution kernel wanted.
230 %
231 */
232
233 /* This was separated so that it could be used as a separate
234 ** array input handling function, such as for -color-matrix
235 */
236 static KernelInfo *ParseKernelArray(const char *kernel_string)
237 {
238   KernelInfo
239     *kernel;
240
241   char
242     token[MaxTextExtent];
243
244   const char
245     *p,
246     *end;
247
248   register ssize_t
249     i;
250
251   double
252     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
253
254   MagickStatusType
255     flags;
256
257   GeometryInfo
258     args;
259
260   kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
261   if (kernel == (KernelInfo *)NULL)
262     return(kernel);
263   (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
264   kernel->minimum = kernel->maximum = kernel->angle = 0.0;
265   kernel->negative_range = kernel->positive_range = 0.0;
266   kernel->type = UserDefinedKernel;
267   kernel->next = (KernelInfo *) NULL;
268   kernel->signature = MagickSignature;
269   if (kernel_string == (const char *) NULL)
270     return(kernel);
271
272   /* find end of this specific kernel definition string */
273   end = strchr(kernel_string, ';');
274   if ( end == (char *) NULL )
275     end = strchr(kernel_string, '\0');
276
277   /* clear flags - for Expanding kernel lists thorugh rotations */
278    flags = NoValue;
279
280   /* Has a ':' in argument - New user kernel specification
281      FUTURE: this split on ':' could be done by StringToken()
282    */
283   p = strchr(kernel_string, ':');
284   if ( p != (char *) NULL && p < end)
285     {
286       /* ParseGeometry() needs the geometry separated! -- Arrgghh */
287       memcpy(token, kernel_string, (size_t) (p-kernel_string));
288       token[p-kernel_string] = '\0';
289       SetGeometryInfo(&args);
290       flags = ParseGeometry(token, &args);
291
292       /* Size handling and checks of geometry settings */
293       if ( (flags & WidthValue) == 0 ) /* if no width then */
294         args.rho = args.sigma;         /* then  width = height */
295       if ( args.rho < 1.0 )            /* if width too small */
296          args.rho = 1.0;               /* then  width = 1 */
297       if ( args.sigma < 1.0 )          /* if height too small */
298         args.sigma = args.rho;         /* then  height = width */
299       kernel->width = (size_t)args.rho;
300       kernel->height = (size_t)args.sigma;
301
302       /* Offset Handling and Checks */
303       if ( args.xi  < 0.0 || args.psi < 0.0 )
304         return(DestroyKernelInfo(kernel));
305       kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
306                                         : (ssize_t) (kernel->width-1)/2;
307       kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
308                                         : (ssize_t) (kernel->height-1)/2;
309       if ( kernel->x >= (ssize_t) kernel->width ||
310            kernel->y >= (ssize_t) kernel->height )
311         return(DestroyKernelInfo(kernel));
312
313       p++; /* advance beyond the ':' */
314     }
315   else
316     { /* ELSE - Old old specification, forming odd-square kernel */
317       /* count up number of values given */
318       p=(const char *) kernel_string;
319       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
320         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
321       for (i=0; p < end; i++)
322       {
323         GetMagickToken(p,&p,token);
324         if (*token == ',')
325           GetMagickToken(p,&p,token);
326       }
327       /* set the size of the kernel - old sized square */
328       kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
329       kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
330       p=(const char *) kernel_string;
331       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
332         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
333     }
334
335   /* Read in the kernel values from rest of input string argument */
336   kernel->values=(double *) AcquireAlignedMemory(kernel->width,
337     kernel->height*sizeof(*kernel->values));
338   if (kernel->values == (double *) NULL)
339     return(DestroyKernelInfo(kernel));
340   kernel->minimum = +MagickHuge;
341   kernel->maximum = -MagickHuge;
342   kernel->negative_range = kernel->positive_range = 0.0;
343   for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
344   {
345     GetMagickToken(p,&p,token);
346     if (*token == ',')
347       GetMagickToken(p,&p,token);
348     if (    LocaleCompare("nan",token) == 0
349         || LocaleCompare("-",token) == 0 ) {
350       kernel->values[i] = nan; /* this value is not part of neighbourhood */
351     }
352     else {
353       kernel->values[i] = StringToDouble(token,(char **) NULL);
354       ( kernel->values[i] < 0)
355           ?  ( kernel->negative_range += kernel->values[i] )
356           :  ( kernel->positive_range += kernel->values[i] );
357       Minimize(kernel->minimum, kernel->values[i]);
358       Maximize(kernel->maximum, kernel->values[i]);
359     }
360   }
361
362   /* sanity check -- no more values in kernel definition */
363   GetMagickToken(p,&p,token);
364   if ( *token != '\0' && *token != ';' && *token != '\'' )
365     return(DestroyKernelInfo(kernel));
366
367 #if 0
368   /* this was the old method of handling a incomplete kernel */
369   if ( i < (ssize_t) (kernel->width*kernel->height) ) {
370     Minimize(kernel->minimum, kernel->values[i]);
371     Maximize(kernel->maximum, kernel->values[i]);
372     for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
373       kernel->values[i]=0.0;
374   }
375 #else
376   /* Number of values for kernel was not enough - Report Error */
377   if ( i < (ssize_t) (kernel->width*kernel->height) )
378     return(DestroyKernelInfo(kernel));
379 #endif
380
381   /* check that we recieved at least one real (non-nan) value! */
382   if ( kernel->minimum == MagickHuge )
383     return(DestroyKernelInfo(kernel));
384
385   if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
386     ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
387   else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
388     ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
389   else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
390     ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
391
392   return(kernel);
393 }
394
395 static KernelInfo *ParseKernelName(const char *kernel_string)
396 {
397   char
398     token[MaxTextExtent];
399
400   const char
401     *p,
402     *end;
403
404   GeometryInfo
405     args;
406
407   KernelInfo
408     *kernel;
409
410   MagickStatusType
411     flags;
412
413   ssize_t
414     type;
415
416   /* Parse special 'named' kernel */
417   GetMagickToken(kernel_string,&p,token);
418   type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
419   if ( type < 0 || type == UserDefinedKernel )
420     return((KernelInfo *)NULL);  /* not a valid named kernel */
421
422   while (((isspace((int) ((unsigned char) *p)) != 0) ||
423           (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
424     p++;
425
426   end = strchr(p, ';'); /* end of this kernel defintion */
427   if ( end == (char *) NULL )
428     end = strchr(p, '\0');
429
430   /* ParseGeometry() needs the geometry separated! -- Arrgghh */
431   memcpy(token, p, (size_t) (end-p));
432   token[end-p] = '\0';
433   SetGeometryInfo(&args);
434   flags = ParseGeometry(token, &args);
435
436 #if 0
437   /* For Debugging Geometry Input */
438   (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
439     flags, args.rho, args.sigma, args.xi, args.psi );
440 #endif
441
442   /* special handling of missing values in input string */
443   switch( type ) {
444     /* Shape Kernel Defaults */
445     case UnityKernel:
446       if ( (flags & WidthValue) == 0 )
447         args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
448       break;
449     case SquareKernel:
450     case DiamondKernel:
451     case OctagonKernel:
452     case DiskKernel:
453     case PlusKernel:
454     case CrossKernel:
455       if ( (flags & HeightValue) == 0 )
456         args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
457       break;
458     case RingKernel:
459       if ( (flags & XValue) == 0 )
460         args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
461       break;
462     case RectangleKernel:    /* Rectangle - set size defaults */
463       if ( (flags & WidthValue) == 0 ) /* if no width then */
464         args.rho = args.sigma;         /* then  width = height */
465       if ( args.rho < 1.0 )            /* if width too small */
466           args.rho = 3;                /* then  width = 3 */
467       if ( args.sigma < 1.0 )          /* if height too small */
468         args.sigma = args.rho;         /* then  height = width */
469       if ( (flags & XValue) == 0 )     /* center offset if not defined */
470         args.xi = (double)(((ssize_t)args.rho-1)/2);
471       if ( (flags & YValue) == 0 )
472         args.psi = (double)(((ssize_t)args.sigma-1)/2);
473       break;
474     /* Distance Kernel Defaults */
475     case ChebyshevKernel:
476     case ManhattanKernel:
477     case OctagonalKernel:
478     case EuclideanKernel:
479       if ( (flags & HeightValue) == 0 )           /* no distance scale */
480         args.sigma = 100.0;                       /* default distance scaling */
481       else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
482         args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
483       else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
484         args.sigma *= QuantumRange/100.0;         /* percentage of color range */
485       break;
486     default:
487       break;
488   }
489
490   kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
491   if ( kernel == (KernelInfo *) NULL )
492     return(kernel);
493
494   /* global expand to rotated kernel list - only for single kernels */
495   if ( kernel->next == (KernelInfo *) NULL ) {
496     if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
497       ExpandRotateKernelInfo(kernel, 45.0);
498     else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
499       ExpandRotateKernelInfo(kernel, 90.0);
500     else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
501       ExpandMirrorKernelInfo(kernel);
502   }
503
504   return(kernel);
505 }
506
507 MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
508 {
509
510   KernelInfo
511     *kernel,
512     *new_kernel;
513
514   char
515     token[MaxTextExtent];
516
517   const char
518     *p;
519
520   size_t
521     kernel_number;
522
523   if (kernel_string == (const char *) NULL)
524     return(ParseKernelArray(kernel_string));
525   p = kernel_string;
526   kernel = NULL;
527   kernel_number = 0;
528
529   while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
530
531     /* ignore extra or multiple ';' kernel separators */
532     if ( *token != ';' ) {
533
534       /* tokens starting with alpha is a Named kernel */
535       if (isalpha((int) *token) != 0)
536         new_kernel = ParseKernelName(p);
537       else /* otherwise a user defined kernel array */
538         new_kernel = ParseKernelArray(p);
539
540       /* Error handling -- this is not proper error handling! */
541       if ( new_kernel == (KernelInfo *) NULL ) {
542         (void) FormatLocaleFile(stderr, "Failed to parse kernel number #%.20g\n",
543           (double) kernel_number);
544         if ( kernel != (KernelInfo *) NULL )
545           kernel=DestroyKernelInfo(kernel);
546         return((KernelInfo *) NULL);
547       }
548
549       /* initialise or append the kernel list */
550       if ( kernel == (KernelInfo *) NULL )
551         kernel = new_kernel;
552       else
553         LastKernelInfo(kernel)->next = new_kernel;
554     }
555
556     /* look for the next kernel in list */
557     p = strchr(p, ';');
558     if ( p == (char *) NULL )
559       break;
560     p++;
561
562   }
563   return(kernel);
564 }
565
566 \f
567 /*
568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
569 %                                                                             %
570 %                                                                             %
571 %                                                                             %
572 %     A c q u i r e K e r n e l B u i l t I n                                 %
573 %                                                                             %
574 %                                                                             %
575 %                                                                             %
576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577 %
578 %  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
579 %  kernels used for special purposes such as gaussian blurring, skeleton
580 %  pruning, and edge distance determination.
581 %
582 %  They take a KernelType, and a set of geometry style arguments, which were
583 %  typically decoded from a user supplied string, or from a more complex
584 %  Morphology Method that was requested.
585 %
586 %  The format of the AcquireKernalBuiltIn method is:
587 %
588 %      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
589 %           const GeometryInfo args)
590 %
591 %  A description of each parameter follows:
592 %
593 %    o type: the pre-defined type of kernel wanted
594 %
595 %    o args: arguments defining or modifying the kernel
596 %
597 %  Convolution Kernels
598 %
599 %    Unity
600 %       The a No-Op or Scaling single element kernel.
601 %
602 %    Gaussian:{radius},{sigma}
603 %       Generate a two-dimensional gaussian kernel, as used by -gaussian.
604 %       The sigma for the curve is required.  The resulting kernel is
605 %       normalized,
606 %
607 %       If 'sigma' is zero, you get a single pixel on a field of zeros.
608 %
609 %       NOTE: that the 'radius' is optional, but if provided can limit (clip)
610 %       the final size of the resulting kernel to a square 2*radius+1 in size.
611 %       The radius should be at least 2 times that of the sigma value, or
612 %       sever clipping and aliasing may result.  If not given or set to 0 the
613 %       radius will be determined so as to produce the best minimal error
614 %       result, which is usally much larger than is normally needed.
615 %
616 %    LoG:{radius},{sigma}
617 %        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
618 %        The supposed ideal edge detection, zero-summing kernel.
619 %
620 %        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
621 %        approx 1.6 (according to wikipedia).
622 %
623 %    DoG:{radius},{sigma1},{sigma2}
624 %        "Difference of Gaussians" Kernel.
625 %        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
626 %        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
627 %        The result is a zero-summing kernel.
628 %
629 %    Blur:{radius},{sigma}[,{angle}]
630 %       Generates a 1 dimensional or linear gaussian blur, at the angle given
631 %       (current restricted to orthogonal angles).  If a 'radius' is given the
632 %       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
633 %       by a 90 degree angle.
634 %
635 %       If 'sigma' is zero, you get a single pixel on a field of zeros.
636 %
637 %       Note that two convolutions with two "Blur" kernels perpendicular to
638 %       each other, is equivalent to a far larger "Gaussian" kernel with the
639 %       same sigma value, However it is much faster to apply. This is how the
640 %       "-blur" operator actually works.
641 %
642 %    Comet:{width},{sigma},{angle}
643 %       Blur in one direction only, much like how a bright object leaves
644 %       a comet like trail.  The Kernel is actually half a gaussian curve,
645 %       Adding two such blurs in opposite directions produces a Blur Kernel.
646 %       Angle can be rotated in multiples of 90 degrees.
647 %
648 %       Note that the first argument is the width of the kernel and not the
649 %       radius of the kernel.
650 %
651 %    Binomial:[{radius}]
652 %       Generate a discrete kernel using a 2 dimentional Pascel's Triangle
653 %       of values.
654 %
655 %    # Still to be implemented...
656 %    #
657 %    # Filter2D
658 %    # Filter1D
659 %    #    Set kernel values using a resize filter, and given scale (sigma)
660 %    #    Cylindrical or Linear.   Is this possible with an image?
661 %    #
662 %
663 %  Named Constant Convolution Kernels
664 %
665 %  All these are unscaled, zero-summing kernels by default. As such for
666 %  non-HDRI version of ImageMagick some form of normalization, user scaling,
667 %  and biasing the results is recommended, to prevent the resulting image
668 %  being 'clipped'.
669 %
670 %  The 3x3 kernels (most of these) can be circularly rotated in multiples of
671 %  45 degrees to generate the 8 angled varients of each of the kernels.
672 %
673 %    Laplacian:{type}
674 %      Discrete Lapacian Kernels, (without normalization)
675 %        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
676 %        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
677 %        Type 2 :  3x3 with center:4 edge:1 corner:-2
678 %        Type 3 :  3x3 with center:4 edge:-2 corner:1
679 %        Type 5 :  5x5 laplacian
680 %        Type 7 :  7x7 laplacian
681 %        Type 15 : 5x5 LoG (sigma approx 1.4)
682 %        Type 19 : 9x9 LoG (sigma approx 1.4)
683 %
684 %    Sobel:{angle}
685 %      Sobel 'Edge' convolution kernel (3x3)
686 %          | -1, 0, 1 |
687 %          | -2, 0,-2 |
688 %          | -1, 0, 1 |
689 %
690 %    Roberts:{angle}
691 %      Roberts convolution kernel (3x3)
692 %          |  0, 0, 0 |
693 %          | -1, 1, 0 |
694 %          |  0, 0, 0 |
695 %
696 %    Prewitt:{angle}
697 %      Prewitt Edge convolution kernel (3x3)
698 %          | -1, 0, 1 |
699 %          | -1, 0, 1 |
700 %          | -1, 0, 1 |
701 %
702 %    Compass:{angle}
703 %      Prewitt's "Compass" convolution kernel (3x3)
704 %          | -1, 1, 1 |
705 %          | -1,-2, 1 |
706 %          | -1, 1, 1 |
707 %
708 %    Kirsch:{angle}
709 %      Kirsch's "Compass" convolution kernel (3x3)
710 %          | -3,-3, 5 |
711 %          | -3, 0, 5 |
712 %          | -3,-3, 5 |
713 %
714 %    FreiChen:{angle}
715 %      Frei-Chen Edge Detector is based on a kernel that is similar to
716 %      the Sobel Kernel, but is designed to be isotropic. That is it takes
717 %      into account the distance of the diagonal in the kernel.
718 %
719 %          |   1,     0,   -1     |
720 %          | sqrt(2), 0, -sqrt(2) |
721 %          |   1,     0,   -1     |
722 %
723 %    FreiChen:{type},{angle}
724 %
725 %      Frei-Chen Pre-weighted kernels...
726 %
727 %        Type 0:  default un-nomalized version shown above.
728 %
729 %        Type 1: Orthogonal Kernel (same as type 11 below)
730 %          |   1,     0,   -1     |
731 %          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
732 %          |   1,     0,   -1     |
733 %
734 %        Type 2: Diagonal form of Kernel...
735 %          |   1,     sqrt(2),    0     |
736 %          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
737 %          |   0,    -sqrt(2)    -1     |
738 %
739 %      However this kernel is als at the heart of the FreiChen Edge Detection
740 %      Process which uses a set of 9 specially weighted kernel.  These 9
741 %      kernels not be normalized, but directly applied to the image. The
742 %      results is then added together, to produce the intensity of an edge in
743 %      a specific direction.  The square root of the pixel value can then be
744 %      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
745 %      from each other, both the direction and the strength of the edge can be
746 %      determined.
747 %
748 %        Type 10: All 9 of the following pre-weighted kernels...
749 %
750 %        Type 11: |   1,     0,   -1     |
751 %                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
752 %                 |   1,     0,   -1     |
753 %
754 %        Type 12: | 1, sqrt(2), 1 |
755 %                 | 0,   0,     0 | / 2*sqrt(2)
756 %                 | 1, sqrt(2), 1 |
757 %
758 %        Type 13: | sqrt(2), -1,    0     |
759 %                 |  -1,      0,    1     | / 2*sqrt(2)
760 %                 |   0,      1, -sqrt(2) |
761 %
762 %        Type 14: |    0,     1, -sqrt(2) |
763 %                 |   -1,     0,     1    | / 2*sqrt(2)
764 %                 | sqrt(2), -1,     0    |
765 %
766 %        Type 15: | 0, -1, 0 |
767 %                 | 1,  0, 1 | / 2
768 %                 | 0, -1, 0 |
769 %
770 %        Type 16: |  1, 0, -1 |
771 %                 |  0, 0,  0 | / 2
772 %                 | -1, 0,  1 |
773 %
774 %        Type 17: |  1, -2,  1 |
775 %                 | -2,  4, -2 | / 6
776 %                 | -1, -2,  1 |
777 %
778 %        Type 18: | -2, 1, -2 |
779 %                 |  1, 4,  1 | / 6
780 %                 | -2, 1, -2 |
781 %
782 %        Type 19: | 1, 1, 1 |
783 %                 | 1, 1, 1 | / 3
784 %                 | 1, 1, 1 |
785 %
786 %      The first 4 are for edge detection, the next 4 are for line detection
787 %      and the last is to add a average component to the results.
788 %
789 %      Using a special type of '-1' will return all 9 pre-weighted kernels
790 %      as a multi-kernel list, so that you can use them directly (without
791 %      normalization) with the special "-set option:morphology:compose Plus"
792 %      setting to apply the full FreiChen Edge Detection Technique.
793 %
794 %      If 'type' is large it will be taken to be an actual rotation angle for
795 %      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
796 %      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
797 %
798 %      WARNING: The above was layed out as per
799 %          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
800 %      But rotated 90 degrees so direction is from left rather than the top.
801 %      I have yet to find any secondary confirmation of the above. The only
802 %      other source found was actual source code at
803 %          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
804 %      Neigher paper defineds the kernels in a way that looks locical or
805 %      correct when taken as a whole.
806 %
807 %  Boolean Kernels
808 %
809 %    Diamond:[{radius}[,{scale}]]
810 %       Generate a diamond shaped kernel with given radius to the points.
811 %       Kernel size will again be radius*2+1 square and defaults to radius 1,
812 %       generating a 3x3 kernel that is slightly larger than a square.
813 %
814 %    Square:[{radius}[,{scale}]]
815 %       Generate a square shaped kernel of size radius*2+1, and defaulting
816 %       to a 3x3 (radius 1).
817 %
818 %    Octagon:[{radius}[,{scale}]]
819 %       Generate octagonal shaped kernel of given radius and constant scale.
820 %       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
821 %       in "Diamond" kernel.
822 %
823 %    Disk:[{radius}[,{scale}]]
824 %       Generate a binary disk, thresholded at the radius given, the radius
825 %       may be a float-point value. Final Kernel size is floor(radius)*2+1
826 %       square. A radius of 5.3 is the default.
827 %
828 %       NOTE: That a low radii Disk kernels produce the same results as
829 %       many of the previously defined kernels, but differ greatly at larger
830 %       radii.  Here is a table of equivalences...
831 %          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
832 %          "Disk:1.5"  => "Square"
833 %          "Disk:2"    => "Diamond:2"
834 %          "Disk:2.5"  => "Octagon"
835 %          "Disk:2.9"  => "Square:2"
836 %          "Disk:3.5"  => "Octagon:3"
837 %          "Disk:4.5"  => "Octagon:4"
838 %          "Disk:5.4"  => "Octagon:5"
839 %          "Disk:6.4"  => "Octagon:6"
840 %       All other Disk shapes are unique to this kernel, but because a "Disk"
841 %       is more circular when using a larger radius, using a larger radius is
842 %       preferred over iterating the morphological operation.
843 %
844 %    Rectangle:{geometry}
845 %       Simply generate a rectangle of 1's with the size given. You can also
846 %       specify the location of the 'control point', otherwise the closest
847 %       pixel to the center of the rectangle is selected.
848 %
849 %       Properly centered and odd sized rectangles work the best.
850 %
851 %  Symbol Dilation Kernels
852 %
853 %    These kernel is not a good general morphological kernel, but is used
854 %    more for highlighting and marking any single pixels in an image using,
855 %    a "Dilate" method as appropriate.
856 %
857 %    For the same reasons iterating these kernels does not produce the
858 %    same result as using a larger radius for the symbol.
859 %
860 %    Plus:[{radius}[,{scale}]]
861 %    Cross:[{radius}[,{scale}]]
862 %       Generate a kernel in the shape of a 'plus' or a 'cross' with
863 %       a each arm the length of the given radius (default 2).
864 %
865 %       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
866 %
867 %    Ring:{radius1},{radius2}[,{scale}]
868 %       A ring of the values given that falls between the two radii.
869 %       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
870 %       This is the 'edge' pixels of the default "Disk" kernel,
871 %       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
872 %
873 %  Hit and Miss Kernels
874 %
875 %    Peak:radius1,radius2
876 %       Find any peak larger than the pixels the fall between the two radii.
877 %       The default ring of pixels is as per "Ring".
878 %    Edges
879 %       Find flat orthogonal edges of a binary shape
880 %    Corners
881 %       Find 90 degree corners of a binary shape
882 %    Diagonals:type
883 %       A special kernel to thin the 'outside' of diagonals
884 %    LineEnds:type
885 %       Find end points of lines (for pruning a skeletion)
886 %       Two types of lines ends (default to both) can be searched for
887 %         Type 0: All line ends
888 %         Type 1: single kernel for 4-conneected line ends
889 %         Type 2: single kernel for simple line ends
890 %    LineJunctions
891 %       Find three line junctions (within a skeletion)
892 %         Type 0: all line junctions
893 %         Type 1: Y Junction kernel
894 %         Type 2: Diagonal T Junction kernel
895 %         Type 3: Orthogonal T Junction kernel
896 %         Type 4: Diagonal X Junction kernel
897 %         Type 5: Orthogonal + Junction kernel
898 %    Ridges:type
899 %       Find single pixel ridges or thin lines
900 %         Type 1: Fine single pixel thick lines and ridges
901 %         Type 2: Find two pixel thick lines and ridges
902 %    ConvexHull
903 %       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
904 %    Skeleton:type
905 %       Traditional skeleton generating kernels.
906 %         Type 1: Tradional Skeleton kernel (4 connected skeleton)
907 %         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
908 %         Type 3: Thinning skeleton based on a ressearch paper by
909 %                 Dan S. Bloomberg (Default Type)
910 %    ThinSE:type
911 %       A huge variety of Thinning Kernels designed to preserve conectivity.
912 %       many other kernel sets use these kernels as source definitions.
913 %       Type numbers are 41-49, 81-89, 481, and 482 which are based on
914 %       the super and sub notations used in the source research paper.
915 %
916 %  Distance Measuring Kernels
917 %
918 %    Different types of distance measuring methods, which are used with the
919 %    a 'Distance' morphology method for generating a gradient based on
920 %    distance from an edge of a binary shape, though there is a technique
921 %    for handling a anti-aliased shape.
922 %
923 %    See the 'Distance' Morphological Method, for information of how it is
924 %    applied.
925 %
926 %    Chebyshev:[{radius}][x{scale}[%!]]
927 %       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
928 %       is a value of one to any neighbour, orthogonal or diagonal. One why
929 %       of thinking of it is the number of squares a 'King' or 'Queen' in
930 %       chess needs to traverse reach any other position on a chess board.
931 %       It results in a 'square' like distance function, but one where
932 %       diagonals are given a value that is closer than expected.
933 %
934 %    Manhattan:[{radius}][x{scale}[%!]]
935 %       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
936 %       Cab distance metric), it is the distance needed when you can only
937 %       travel in horizontal or vertical directions only.  It is the
938 %       distance a 'Rook' in chess would have to travel, and results in a
939 %       diamond like distances, where diagonals are further than expected.
940 %
941 %    Octagonal:[{radius}][x{scale}[%!]]
942 %       An interleving of Manhatten and Chebyshev metrics producing an
943 %       increasing octagonally shaped distance.  Distances matches those of
944 %       the "Octagon" shaped kernel of the same radius.  The minimum radius
945 %       and default is 2, producing a 5x5 kernel.
946 %
947 %    Euclidean:[{radius}][x{scale}[%!]]
948 %       Euclidean distance is the 'direct' or 'as the crow flys' distance.
949 %       However by default the kernel size only has a radius of 1, which
950 %       limits the distance to 'Knight' like moves, with only orthogonal and
951 %       diagonal measurements being correct.  As such for the default kernel
952 %       you will get octagonal like distance function.
953 %
954 %       However using a larger radius such as "Euclidean:4" you will get a
955 %       much smoother distance gradient from the edge of the shape. Especially
956 %       if the image is pre-processed to include any anti-aliasing pixels.
957 %       Of course a larger kernel is slower to use, and not always needed.
958 %
959 %    The first three Distance Measuring Kernels will only generate distances
960 %    of exact multiples of {scale} in binary images. As such you can use a
961 %    scale of 1 without loosing any information.  However you also need some
962 %    scaling when handling non-binary anti-aliased shapes.
963 %
964 %    The "Euclidean" Distance Kernel however does generate a non-integer
965 %    fractional results, and as such scaling is vital even for binary shapes.
966 %
967 */
968
969 MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
970    const GeometryInfo *args)
971 {
972   KernelInfo
973     *kernel;
974
975   register ssize_t
976     i;
977
978   register ssize_t
979     u,
980     v;
981
982   double
983     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
984
985   /* Generate a new empty kernel if needed */
986   kernel=(KernelInfo *) NULL;
987   switch(type) {
988     case UndefinedKernel:    /* These should not call this function */
989     case UserDefinedKernel:
990       assert("Should not call this function" != (char *)NULL);
991       break;
992     case LaplacianKernel:   /* Named Descrete Convolution Kernels */
993     case SobelKernel:       /* these are defined using other kernels */
994     case RobertsKernel:
995     case PrewittKernel:
996     case CompassKernel:
997     case KirschKernel:
998     case FreiChenKernel:
999     case EdgesKernel:       /* Hit and Miss kernels */
1000     case CornersKernel:
1001     case DiagonalsKernel:
1002     case LineEndsKernel:
1003     case LineJunctionsKernel:
1004     case RidgesKernel:
1005     case ConvexHullKernel:
1006     case SkeletonKernel:
1007     case ThinSEKernel:
1008       break;               /* A pre-generated kernel is not needed */
1009 #if 0
1010     /* set to 1 to do a compile-time check that we haven't missed anything */
1011     case UnityKernel:
1012     case GaussianKernel:
1013     case DoGKernel:
1014     case LoGKernel:
1015     case BlurKernel:
1016     case CometKernel:
1017     case BinomialKernel:
1018     case DiamondKernel:
1019     case SquareKernel:
1020     case RectangleKernel:
1021     case OctagonKernel:
1022     case DiskKernel:
1023     case PlusKernel:
1024     case CrossKernel:
1025     case RingKernel:
1026     case PeaksKernel:
1027     case ChebyshevKernel:
1028     case ManhattanKernel:
1029     case OctangonalKernel:
1030     case EuclideanKernel:
1031 #else
1032     default:
1033 #endif
1034       /* Generate the base Kernel Structure */
1035       kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1036       if (kernel == (KernelInfo *) NULL)
1037         return(kernel);
1038       (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
1039       kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1040       kernel->negative_range = kernel->positive_range = 0.0;
1041       kernel->type = type;
1042       kernel->next = (KernelInfo *) NULL;
1043       kernel->signature = MagickSignature;
1044       break;
1045   }
1046
1047   switch(type) {
1048     /*
1049       Convolution Kernels
1050     */
1051     case UnityKernel:
1052       {
1053         kernel->height = kernel->width = (size_t) 1;
1054         kernel->x = kernel->y = (ssize_t) 0;
1055         kernel->values=(double *) AcquireAlignedMemory(1,
1056           sizeof(*kernel->values));
1057         if (kernel->values == (double *) NULL)
1058           return(DestroyKernelInfo(kernel));
1059         kernel->maximum = kernel->values[0] = args->rho;
1060         break;
1061       }
1062       break;
1063     case GaussianKernel:
1064     case DoGKernel:
1065     case LoGKernel:
1066       { double
1067           sigma = fabs(args->sigma),
1068           sigma2 = fabs(args->xi),
1069           A, B, R;
1070
1071         if ( args->rho >= 1.0 )
1072           kernel->width = (size_t)args->rho*2+1;
1073         else if ( (type != DoGKernel) || (sigma >= sigma2) )
1074           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1075         else
1076           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1077         kernel->height = kernel->width;
1078         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1079         kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1080           kernel->height*sizeof(*kernel->values));
1081         if (kernel->values == (double *) NULL)
1082           return(DestroyKernelInfo(kernel));
1083
1084         /* WARNING: The following generates a 'sampled gaussian' kernel.
1085          * What we really want is a 'discrete gaussian' kernel.
1086          *
1087          * How to do this is I don't know, but appears to be basied on the
1088          * Error Function 'erf()' (intergral of a gaussian)
1089          */
1090
1091         if ( type == GaussianKernel || type == DoGKernel )
1092           { /* Calculate a Gaussian,  OR positive half of a DoG */
1093             if ( sigma > MagickEpsilon )
1094               { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
1095                 B = (double) (1.0/(Magick2PI*sigma*sigma));
1096                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1097                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1098                       kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
1099               }
1100             else /* limiting case - a unity (normalized Dirac) kernel */
1101               { (void) ResetMagickMemory(kernel->values,0, (size_t)
1102                             kernel->width*kernel->height*sizeof(double));
1103                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1104               }
1105           }
1106
1107         if ( type == DoGKernel )
1108           { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
1109             if ( sigma2 > MagickEpsilon )
1110               { sigma = sigma2;                /* simplify loop expressions */
1111                 A = 1.0/(2.0*sigma*sigma);
1112                 B = (double) (1.0/(Magick2PI*sigma*sigma));
1113                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1114                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1115                     kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1116               }
1117             else /* limiting case - a unity (normalized Dirac) kernel */
1118               kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1119           }
1120
1121         if ( type == LoGKernel )
1122           { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
1123             if ( sigma > MagickEpsilon )
1124               { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
1125                 B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1126                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1127                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1128                     { R = ((double)(u*u+v*v))*A;
1129                       kernel->values[i] = (1-R)*exp(-R)*B;
1130                     }
1131               }
1132             else /* special case - generate a unity kernel */
1133               { (void) ResetMagickMemory(kernel->values,0, (size_t)
1134                             kernel->width*kernel->height*sizeof(double));
1135                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1136               }
1137           }
1138
1139         /* Note the above kernels may have been 'clipped' by a user defined
1140         ** radius, producing a smaller (darker) kernel.  Also for very small
1141         ** sigma's (> 0.1) the central value becomes larger than one, and thus
1142         ** producing a very bright kernel.
1143         **
1144         ** Normalization will still be needed.
1145         */
1146
1147         /* Normalize the 2D Gaussian Kernel
1148         **
1149         ** NB: a CorrelateNormalize performs a normal Normalize if
1150         ** there are no negative values.
1151         */
1152         CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1153         ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1154
1155         break;
1156       }
1157     case BlurKernel:
1158       { double
1159           sigma = fabs(args->sigma),
1160           alpha, beta;
1161
1162         if ( args->rho >= 1.0 )
1163           kernel->width = (size_t)args->rho*2+1;
1164         else
1165           kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1166         kernel->height = 1;
1167         kernel->x = (ssize_t) (kernel->width-1)/2;
1168         kernel->y = 0;
1169         kernel->negative_range = kernel->positive_range = 0.0;
1170         kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1171           kernel->height*sizeof(*kernel->values));
1172         if (kernel->values == (double *) NULL)
1173           return(DestroyKernelInfo(kernel));
1174
1175 #if 1
1176 #define KernelRank 3
1177         /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
1178         ** It generates a gaussian 3 times the width, and compresses it into
1179         ** the expected range.  This produces a closer normalization of the
1180         ** resulting kernel, especially for very low sigma values.
1181         ** As such while wierd it is prefered.
1182         **
1183         ** I am told this method originally came from Photoshop.
1184         **
1185         ** A properly normalized curve is generated (apart from edge clipping)
1186         ** even though we later normalize the result (for edge clipping)
1187         ** to allow the correct generation of a "Difference of Blurs".
1188         */
1189
1190         /* initialize */
1191         v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
1192         (void) ResetMagickMemory(kernel->values,0, (size_t)
1193                      kernel->width*kernel->height*sizeof(double));
1194         /* Calculate a Positive 1D Gaussian */
1195         if ( sigma > MagickEpsilon )
1196           { sigma *= KernelRank;               /* simplify loop expressions */
1197             alpha = 1.0/(2.0*sigma*sigma);
1198             beta= (double) (1.0/(MagickSQ2PI*sigma ));
1199             for ( u=-v; u <= v; u++) {
1200               kernel->values[(u+v)/KernelRank] +=
1201                               exp(-((double)(u*u))*alpha)*beta;
1202             }
1203           }
1204         else /* special case - generate a unity kernel */
1205           kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1206 #else
1207         /* Direct calculation without curve averaging
1208            This is equivelent to a KernelRank of 1 */
1209
1210         /* Calculate a Positive Gaussian */
1211         if ( sigma > MagickEpsilon )
1212           { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
1213             beta = 1.0/(MagickSQ2PI*sigma);
1214             for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1215               kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1216           }
1217         else /* special case - generate a unity kernel */
1218           { (void) ResetMagickMemory(kernel->values,0, (size_t)
1219                          kernel->width*kernel->height*sizeof(double));
1220             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1221           }
1222 #endif
1223         /* Note the above kernel may have been 'clipped' by a user defined
1224         ** radius, producing a smaller (darker) kernel.  Also for very small
1225         ** sigma's (> 0.1) the central value becomes larger than one, as a
1226         ** result of not generating a actual 'discrete' kernel, and thus
1227         ** producing a very bright 'impulse'.
1228         **
1229         ** Becuase of these two factors Normalization is required!
1230         */
1231
1232         /* Normalize the 1D Gaussian Kernel
1233         **
1234         ** NB: a CorrelateNormalize performs a normal Normalize if
1235         ** there are no negative values.
1236         */
1237         CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1238         ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1239
1240         /* rotate the 1D kernel by given angle */
1241         RotateKernelInfo(kernel, args->xi );
1242         break;
1243       }
1244     case CometKernel:
1245       { double
1246           sigma = fabs(args->sigma),
1247           A;
1248
1249         if ( args->rho < 1.0 )
1250           kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1251         else
1252           kernel->width = (size_t)args->rho;
1253         kernel->x = kernel->y = 0;
1254         kernel->height = 1;
1255         kernel->negative_range = kernel->positive_range = 0.0;
1256         kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1257           kernel->height*sizeof(*kernel->values));
1258         if (kernel->values == (double *) NULL)
1259           return(DestroyKernelInfo(kernel));
1260
1261         /* A comet blur is half a 1D gaussian curve, so that the object is
1262         ** blurred in one direction only.  This may not be quite the right
1263         ** curve to use so may change in the future. The function must be
1264         ** normalised after generation, which also resolves any clipping.
1265         **
1266         ** As we are normalizing and not subtracting gaussians,
1267         ** there is no need for a divisor in the gaussian formula
1268         **
1269         ** It is less comples
1270         */
1271         if ( sigma > MagickEpsilon )
1272           {
1273 #if 1
1274 #define KernelRank 3
1275             v = (ssize_t) kernel->width*KernelRank; /* start/end points */
1276             (void) ResetMagickMemory(kernel->values,0, (size_t)
1277                           kernel->width*sizeof(double));
1278             sigma *= KernelRank;            /* simplify the loop expression */
1279             A = 1.0/(2.0*sigma*sigma);
1280             /* B = 1.0/(MagickSQ2PI*sigma); */
1281             for ( u=0; u < v; u++) {
1282               kernel->values[u/KernelRank] +=
1283                   exp(-((double)(u*u))*A);
1284               /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1285             }
1286             for (i=0; i < (ssize_t) kernel->width; i++)
1287               kernel->positive_range += kernel->values[i];
1288 #else
1289             A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
1290             /* B = 1.0/(MagickSQ2PI*sigma); */
1291             for ( i=0; i < (ssize_t) kernel->width; i++)
1292               kernel->positive_range +=
1293                 kernel->values[i] = exp(-((double)(i*i))*A);
1294                 /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1295 #endif
1296           }
1297         else /* special case - generate a unity kernel */
1298           { (void) ResetMagickMemory(kernel->values,0, (size_t)
1299                          kernel->width*kernel->height*sizeof(double));
1300             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1301             kernel->positive_range = 1.0;
1302           }
1303
1304         kernel->minimum = 0.0;
1305         kernel->maximum = kernel->values[0];
1306         kernel->negative_range = 0.0;
1307
1308         ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
1309         RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
1310         break;
1311       }
1312     case BinomialKernel:
1313       {
1314         size_t
1315           order_f;
1316
1317         if (args->rho < 1.0)
1318           kernel->width = kernel->height = 3;  /* default radius = 1 */
1319         else
1320           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1321         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1322
1323         order_f = fact(kernel->width-1);
1324
1325         kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1326           kernel->height*sizeof(*kernel->values));
1327         if (kernel->values == (double *) NULL)
1328           return(DestroyKernelInfo(kernel));
1329
1330         /* set all kernel values within diamond area to scale given */
1331         for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
1332           { size_t
1333               alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
1334             for ( u=0; u < (ssize_t)kernel->width; u++, i++)
1335               kernel->positive_range += kernel->values[i] = (double)
1336                 (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
1337           }
1338         kernel->minimum = 1.0;
1339         kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
1340         kernel->negative_range = 0.0;
1341         break;
1342       }
1343
1344     /*
1345       Convolution Kernels - Well Known Named Constant Kernels
1346     */
1347     case LaplacianKernel:
1348       { switch ( (int) args->rho ) {
1349           case 0:
1350           default: /* laplacian square filter -- default */
1351             kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
1352             break;
1353           case 1:  /* laplacian diamond filter */
1354             kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
1355             break;
1356           case 2:
1357             kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1358             break;
1359           case 3:
1360             kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
1361             break;
1362           case 5:   /* a 5x5 laplacian */
1363             kernel=ParseKernelArray(
1364               "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");
1365             break;
1366           case 7:   /* a 7x7 laplacian */
1367             kernel=ParseKernelArray(
1368               "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" );
1369             break;
1370           case 15:  /* a 5x5 LoG (sigma approx 1.4) */
1371             kernel=ParseKernelArray(
1372               "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");
1373             break;
1374           case 19:  /* a 9x9 LoG (sigma approx 1.4) */
1375             /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
1376             kernel=ParseKernelArray(
1377               "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");
1378             break;
1379         }
1380         if (kernel == (KernelInfo *) NULL)
1381           return(kernel);
1382         kernel->type = type;
1383         break;
1384       }
1385     case SobelKernel:
1386       { /* Simple Sobel Kernel */
1387         kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1388         if (kernel == (KernelInfo *) NULL)
1389           return(kernel);
1390         kernel->type = type;
1391         RotateKernelInfo(kernel, args->rho);
1392         break;
1393       }
1394     case RobertsKernel:
1395       {
1396         kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
1397         if (kernel == (KernelInfo *) NULL)
1398           return(kernel);
1399         kernel->type = type;
1400         RotateKernelInfo(kernel, args->rho);
1401         break;
1402       }
1403     case PrewittKernel:
1404       {
1405         kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
1406         if (kernel == (KernelInfo *) NULL)
1407           return(kernel);
1408         kernel->type = type;
1409         RotateKernelInfo(kernel, args->rho);
1410         break;
1411       }
1412     case CompassKernel:
1413       {
1414         kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
1415         if (kernel == (KernelInfo *) NULL)
1416           return(kernel);
1417         kernel->type = type;
1418         RotateKernelInfo(kernel, args->rho);
1419         break;
1420       }
1421     case KirschKernel:
1422       {
1423         kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
1424         if (kernel == (KernelInfo *) NULL)
1425           return(kernel);
1426         kernel->type = type;
1427         RotateKernelInfo(kernel, args->rho);
1428         break;
1429       }
1430     case FreiChenKernel:
1431       /* Direction is set to be left to right positive */
1432       /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
1433       /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
1434       { switch ( (int) args->rho ) {
1435           default:
1436           case 0:
1437             kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1438             if (kernel == (KernelInfo *) NULL)
1439               return(kernel);
1440             kernel->type = type;
1441             kernel->values[3] = +(double) MagickSQ2;
1442             kernel->values[5] = -(double) MagickSQ2;
1443             CalcKernelMetaData(kernel);     /* recalculate meta-data */
1444             break;
1445           case 2:
1446             kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
1447             if (kernel == (KernelInfo *) NULL)
1448               return(kernel);
1449             kernel->type = type;
1450             kernel->values[1] = kernel->values[3]= +(double) MagickSQ2;
1451             kernel->values[5] = kernel->values[7]= -(double) MagickSQ2;
1452             CalcKernelMetaData(kernel);     /* recalculate meta-data */
1453             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1454             break;
1455           case 10:
1456             kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
1457             if (kernel == (KernelInfo *) NULL)
1458               return(kernel);
1459             break;
1460           case 1:
1461           case 11:
1462             kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1463             if (kernel == (KernelInfo *) NULL)
1464               return(kernel);
1465             kernel->type = type;
1466             kernel->values[3] = +(double) MagickSQ2;
1467             kernel->values[5] = -(double) MagickSQ2;
1468             CalcKernelMetaData(kernel);     /* recalculate meta-data */
1469             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1470             break;
1471           case 12:
1472             kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
1473             if (kernel == (KernelInfo *) NULL)
1474               return(kernel);
1475             kernel->type = type;
1476             kernel->values[1] = +(double) MagickSQ2;
1477             kernel->values[7] = +(double) MagickSQ2;
1478             CalcKernelMetaData(kernel);
1479             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1480             break;
1481           case 13:
1482             kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
1483             if (kernel == (KernelInfo *) NULL)
1484               return(kernel);
1485             kernel->type = type;
1486             kernel->values[0] = +(double) MagickSQ2;
1487             kernel->values[8] = -(double) MagickSQ2;
1488             CalcKernelMetaData(kernel);
1489             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1490             break;
1491           case 14:
1492             kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
1493             if (kernel == (KernelInfo *) NULL)
1494               return(kernel);
1495             kernel->type = type;
1496             kernel->values[2] = -(double) MagickSQ2;
1497             kernel->values[6] = +(double) MagickSQ2;
1498             CalcKernelMetaData(kernel);
1499             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1500             break;
1501           case 15:
1502             kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
1503             if (kernel == (KernelInfo *) NULL)
1504               return(kernel);
1505             kernel->type = type;
1506             ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1507             break;
1508           case 16:
1509             kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
1510             if (kernel == (KernelInfo *) NULL)
1511               return(kernel);
1512             kernel->type = type;
1513             ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1514             break;
1515           case 17:
1516             kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
1517             if (kernel == (KernelInfo *) NULL)
1518               return(kernel);
1519             kernel->type = type;
1520             ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1521             break;
1522           case 18:
1523             kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1524             if (kernel == (KernelInfo *) NULL)
1525               return(kernel);
1526             kernel->type = type;
1527             ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1528             break;
1529           case 19:
1530             kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
1531             if (kernel == (KernelInfo *) NULL)
1532               return(kernel);
1533             kernel->type = type;
1534             ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1535             break;
1536         }
1537         if ( fabs(args->sigma) >= MagickEpsilon )
1538           /* Rotate by correctly supplied 'angle' */
1539           RotateKernelInfo(kernel, args->sigma);
1540         else if ( args->rho > 30.0 || args->rho < -30.0 )
1541           /* Rotate by out of bounds 'type' */
1542           RotateKernelInfo(kernel, args->rho);
1543         break;
1544       }
1545
1546     /*
1547       Boolean or Shaped Kernels
1548     */
1549     case DiamondKernel:
1550       {
1551         if (args->rho < 1.0)
1552           kernel->width = kernel->height = 3;  /* default radius = 1 */
1553         else
1554           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1555         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1556
1557         kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1558           kernel->height*sizeof(*kernel->values));
1559         if (kernel->values == (double *) NULL)
1560           return(DestroyKernelInfo(kernel));
1561
1562         /* set all kernel values within diamond area to scale given */
1563         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1564           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1565             if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1566               kernel->positive_range += kernel->values[i] = args->sigma;
1567             else
1568               kernel->values[i] = nan;
1569         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1570         break;
1571       }
1572     case SquareKernel:
1573     case RectangleKernel:
1574       { double
1575           scale;
1576         if ( type == SquareKernel )
1577           {
1578             if (args->rho < 1.0)
1579               kernel->width = kernel->height = 3;  /* default radius = 1 */
1580             else
1581               kernel->width = kernel->height = (size_t) (2*args->rho+1);
1582             kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1583             scale = args->sigma;
1584           }
1585         else {
1586             /* NOTE: user defaults set in "AcquireKernelInfo()" */
1587             if ( args->rho < 1.0 || args->sigma < 1.0 )
1588               return(DestroyKernelInfo(kernel));    /* invalid args given */
1589             kernel->width = (size_t)args->rho;
1590             kernel->height = (size_t)args->sigma;
1591             if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
1592                  args->psi < 0.0 || args->psi > (double)kernel->height )
1593               return(DestroyKernelInfo(kernel));    /* invalid args given */
1594             kernel->x = (ssize_t) args->xi;
1595             kernel->y = (ssize_t) args->psi;
1596             scale = 1.0;
1597           }
1598         kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1599           kernel->height*sizeof(*kernel->values));
1600         if (kernel->values == (double *) NULL)
1601           return(DestroyKernelInfo(kernel));
1602
1603         /* set all kernel values to scale given */
1604         u=(ssize_t) (kernel->width*kernel->height);
1605         for ( i=0; i < u; i++)
1606             kernel->values[i] = scale;
1607         kernel->minimum = kernel->maximum = scale;   /* a flat shape */
1608         kernel->positive_range = scale*u;
1609         break;
1610       }
1611       case OctagonKernel:
1612         {
1613           if (args->rho < 1.0)
1614             kernel->width = kernel->height = 5;  /* default radius = 2 */
1615           else
1616             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1617           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1618
1619           kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1620             kernel->height*sizeof(*kernel->values));
1621           if (kernel->values == (double *) NULL)
1622             return(DestroyKernelInfo(kernel));
1623
1624           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1625             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1626               if ( (labs((long) u)+labs((long) v)) <=
1627                         ((long)kernel->x + (long)(kernel->x/2)) )
1628                 kernel->positive_range += kernel->values[i] = args->sigma;
1629               else
1630                 kernel->values[i] = nan;
1631           kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
1632           break;
1633         }
1634       case DiskKernel:
1635         {
1636           ssize_t
1637             limit = (ssize_t)(args->rho*args->rho);
1638
1639           if (args->rho < 0.4)           /* default radius approx 4.3 */
1640             kernel->width = kernel->height = 9L, limit = 18L;
1641           else
1642             kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
1643           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1644
1645           kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1646             kernel->height*sizeof(*kernel->values));
1647           if (kernel->values == (double *) NULL)
1648             return(DestroyKernelInfo(kernel));
1649
1650           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1651             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1652               if ((u*u+v*v) <= limit)
1653                 kernel->positive_range += kernel->values[i] = args->sigma;
1654               else
1655                 kernel->values[i] = nan;
1656           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1657           break;
1658         }
1659       case PlusKernel:
1660         {
1661           if (args->rho < 1.0)
1662             kernel->width = kernel->height = 5;  /* default radius 2 */
1663           else
1664             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1665           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1666
1667           kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1668             kernel->height*sizeof(*kernel->values));
1669           if (kernel->values == (double *) NULL)
1670             return(DestroyKernelInfo(kernel));
1671
1672           /* set all kernel values along axises to given scale */
1673           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1674             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1675               kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
1676           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1677           kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1678           break;
1679         }
1680       case CrossKernel:
1681         {
1682           if (args->rho < 1.0)
1683             kernel->width = kernel->height = 5;  /* default radius 2 */
1684           else
1685             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1686           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1687
1688           kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1689             kernel->height*sizeof(*kernel->values));
1690           if (kernel->values == (double *) NULL)
1691             return(DestroyKernelInfo(kernel));
1692
1693           /* set all kernel values along axises to given scale */
1694           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1695             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1696               kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
1697           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1698           kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1699           break;
1700         }
1701       /*
1702         HitAndMiss Kernels
1703       */
1704       case RingKernel:
1705       case PeaksKernel:
1706         {
1707           ssize_t
1708             limit1,
1709             limit2,
1710             scale;
1711
1712           if (args->rho < args->sigma)
1713             {
1714               kernel->width = ((size_t)args->sigma)*2+1;
1715               limit1 = (ssize_t)(args->rho*args->rho);
1716               limit2 = (ssize_t)(args->sigma*args->sigma);
1717             }
1718           else
1719             {
1720               kernel->width = ((size_t)args->rho)*2+1;
1721               limit1 = (ssize_t)(args->sigma*args->sigma);
1722               limit2 = (ssize_t)(args->rho*args->rho);
1723             }
1724           if ( limit2 <= 0 )
1725             kernel->width = 7L, limit1 = 7L, limit2 = 11L;
1726
1727           kernel->height = kernel->width;
1728           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1729           kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1730             kernel->height*sizeof(*kernel->values));
1731           if (kernel->values == (double *) NULL)
1732             return(DestroyKernelInfo(kernel));
1733
1734           /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
1735           scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
1736           for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
1737             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1738               { ssize_t radius=u*u+v*v;
1739                 if (limit1 < radius && radius <= limit2)
1740                   kernel->positive_range += kernel->values[i] = (double) scale;
1741                 else
1742                   kernel->values[i] = nan;
1743               }
1744           kernel->minimum = kernel->maximum = (double) scale;
1745           if ( type == PeaksKernel ) {
1746             /* set the central point in the middle */
1747             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1748             kernel->positive_range = 1.0;
1749             kernel->maximum = 1.0;
1750           }
1751           break;
1752         }
1753       case EdgesKernel:
1754         {
1755           kernel=AcquireKernelInfo("ThinSE:482");
1756           if (kernel == (KernelInfo *) NULL)
1757             return(kernel);
1758           kernel->type = type;
1759           ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
1760           break;
1761         }
1762       case CornersKernel:
1763         {
1764           kernel=AcquireKernelInfo("ThinSE:87");
1765           if (kernel == (KernelInfo *) NULL)
1766             return(kernel);
1767           kernel->type = type;
1768           ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
1769           break;
1770         }
1771       case DiagonalsKernel:
1772         {
1773           switch ( (int) args->rho ) {
1774             case 0:
1775             default:
1776               { KernelInfo
1777                   *new_kernel;
1778                 kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
1779                 if (kernel == (KernelInfo *) NULL)
1780                   return(kernel);
1781                 kernel->type = type;
1782                 new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
1783                 if (new_kernel == (KernelInfo *) NULL)
1784                   return(DestroyKernelInfo(kernel));
1785                 new_kernel->type = type;
1786                 LastKernelInfo(kernel)->next = new_kernel;
1787                 ExpandMirrorKernelInfo(kernel);
1788                 return(kernel);
1789               }
1790             case 1:
1791               kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
1792               break;
1793             case 2:
1794               kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
1795               break;
1796           }
1797           if (kernel == (KernelInfo *) NULL)
1798             return(kernel);
1799           kernel->type = type;
1800           RotateKernelInfo(kernel, args->sigma);
1801           break;
1802         }
1803       case LineEndsKernel:
1804         { /* Kernels for finding the end of thin lines */
1805           switch ( (int) args->rho ) {
1806             case 0:
1807             default:
1808               /* set of kernels to find all end of lines */
1809               return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
1810             case 1:
1811               /* kernel for 4-connected line ends - no rotation */
1812               kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
1813               break;
1814           case 2:
1815               /* kernel to add for 8-connected lines - no rotation */
1816               kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
1817               break;
1818           case 3:
1819               /* kernel to add for orthogonal line ends - does not find corners */
1820               kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
1821               break;
1822           case 4:
1823               /* traditional line end - fails on last T end */
1824               kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
1825               break;
1826           }
1827           if (kernel == (KernelInfo *) NULL)
1828             return(kernel);
1829           kernel->type = type;
1830           RotateKernelInfo(kernel, args->sigma);
1831           break;
1832         }
1833       case LineJunctionsKernel:
1834         { /* kernels for finding the junctions of multiple lines */
1835           switch ( (int) args->rho ) {
1836             case 0:
1837             default:
1838               /* set of kernels to find all line junctions */
1839               return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
1840             case 1:
1841               /* Y Junction */
1842               kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
1843               break;
1844             case 2:
1845               /* Diagonal T Junctions */
1846               kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
1847               break;
1848             case 3:
1849               /* Orthogonal T Junctions */
1850               kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
1851               break;
1852             case 4:
1853               /* Diagonal X Junctions */
1854               kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
1855               break;
1856             case 5:
1857               /* Orthogonal X Junctions - minimal diamond kernel */
1858               kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
1859               break;
1860           }
1861           if (kernel == (KernelInfo *) NULL)
1862             return(kernel);
1863           kernel->type = type;
1864           RotateKernelInfo(kernel, args->sigma);
1865           break;
1866         }
1867       case RidgesKernel:
1868         { /* Ridges - Ridge finding kernels */
1869           KernelInfo
1870             *new_kernel;
1871           switch ( (int) args->rho ) {
1872             case 1:
1873             default:
1874               kernel=ParseKernelArray("3x1:0,1,0");
1875               if (kernel == (KernelInfo *) NULL)
1876                 return(kernel);
1877               kernel->type = type;
1878               ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
1879               break;
1880             case 2:
1881               kernel=ParseKernelArray("4x1:0,1,1,0");
1882               if (kernel == (KernelInfo *) NULL)
1883                 return(kernel);
1884               kernel->type = type;
1885               ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
1886
1887               /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
1888               /* Unfortunatally we can not yet rotate a non-square kernel */
1889               /* But then we can't flip a non-symetrical kernel either */
1890               new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
1891               if (new_kernel == (KernelInfo *) NULL)
1892                 return(DestroyKernelInfo(kernel));
1893               new_kernel->type = type;
1894               LastKernelInfo(kernel)->next = new_kernel;
1895               new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
1896               if (new_kernel == (KernelInfo *) NULL)
1897                 return(DestroyKernelInfo(kernel));
1898               new_kernel->type = type;
1899               LastKernelInfo(kernel)->next = new_kernel;
1900               new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
1901               if (new_kernel == (KernelInfo *) NULL)
1902                 return(DestroyKernelInfo(kernel));
1903               new_kernel->type = type;
1904               LastKernelInfo(kernel)->next = new_kernel;
1905               new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
1906               if (new_kernel == (KernelInfo *) NULL)
1907                 return(DestroyKernelInfo(kernel));
1908               new_kernel->type = type;
1909               LastKernelInfo(kernel)->next = new_kernel;
1910               new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
1911               if (new_kernel == (KernelInfo *) NULL)
1912                 return(DestroyKernelInfo(kernel));
1913               new_kernel->type = type;
1914               LastKernelInfo(kernel)->next = new_kernel;
1915               new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
1916               if (new_kernel == (KernelInfo *) NULL)
1917                 return(DestroyKernelInfo(kernel));
1918               new_kernel->type = type;
1919               LastKernelInfo(kernel)->next = new_kernel;
1920               new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
1921               if (new_kernel == (KernelInfo *) NULL)
1922                 return(DestroyKernelInfo(kernel));
1923               new_kernel->type = type;
1924               LastKernelInfo(kernel)->next = new_kernel;
1925               new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
1926               if (new_kernel == (KernelInfo *) NULL)
1927                 return(DestroyKernelInfo(kernel));
1928               new_kernel->type = type;
1929               LastKernelInfo(kernel)->next = new_kernel;
1930               break;
1931           }
1932           break;
1933         }
1934       case ConvexHullKernel:
1935         {
1936           KernelInfo
1937             *new_kernel;
1938           /* first set of 8 kernels */
1939           kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
1940           if (kernel == (KernelInfo *) NULL)
1941             return(kernel);
1942           kernel->type = type;
1943           ExpandRotateKernelInfo(kernel, 90.0);
1944           /* append the mirror versions too - no flip function yet */
1945           new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
1946           if (new_kernel == (KernelInfo *) NULL)
1947             return(DestroyKernelInfo(kernel));
1948           new_kernel->type = type;
1949           ExpandRotateKernelInfo(new_kernel, 90.0);
1950           LastKernelInfo(kernel)->next = new_kernel;
1951           break;
1952         }
1953       case SkeletonKernel:
1954         {
1955           switch ( (int) args->rho ) {
1956             case 1:
1957             default:
1958               /* Traditional Skeleton...
1959               ** A cyclically rotated single kernel
1960               */
1961               kernel=AcquireKernelInfo("ThinSE:482");
1962               if (kernel == (KernelInfo *) NULL)
1963                 return(kernel);
1964               kernel->type = type;
1965               ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
1966               break;
1967             case 2:
1968               /* HIPR Variation of the cyclic skeleton
1969               ** Corners of the traditional method made more forgiving,
1970               ** but the retain the same cyclic order.
1971               */
1972               kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
1973               if (kernel == (KernelInfo *) NULL)
1974                 return(kernel);
1975               if (kernel->next == (KernelInfo *) NULL)
1976                 return(DestroyKernelInfo(kernel));
1977               kernel->type = type;
1978               kernel->next->type = type;
1979               ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
1980               break;
1981             case 3:
1982               /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
1983               ** "Connectivity-Preserving Morphological Image Thransformations"
1984               ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
1985               **   http://www.leptonica.com/papers/conn.pdf
1986               */
1987               kernel=AcquireKernelInfo(
1988                             "ThinSE:41; ThinSE:42; ThinSE:43");
1989               if (kernel == (KernelInfo *) NULL)
1990                 return(kernel);
1991               kernel->type = type;
1992               kernel->next->type = type;
1993               kernel->next->next->type = type;
1994               ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
1995               break;
1996            }
1997           break;
1998         }
1999       case ThinSEKernel:
2000         { /* Special kernels for general thinning, while preserving connections
2001           ** "Connectivity-Preserving Morphological Image Thransformations"
2002           ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
2003           **   http://www.leptonica.com/papers/conn.pdf
2004           ** And
2005           **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
2006           **
2007           ** Note kernels do not specify the origin pixel, allowing them
2008           ** to be used for both thickening and thinning operations.
2009           */
2010           switch ( (int) args->rho ) {
2011             /* SE for 4-connected thinning */
2012             case 41: /* SE_4_1 */
2013               kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
2014               break;
2015             case 42: /* SE_4_2 */
2016               kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
2017               break;
2018             case 43: /* SE_4_3 */
2019               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
2020               break;
2021             case 44: /* SE_4_4 */
2022               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
2023               break;
2024             case 45: /* SE_4_5 */
2025               kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
2026               break;
2027             case 46: /* SE_4_6 */
2028               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
2029               break;
2030             case 47: /* SE_4_7 */
2031               kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
2032               break;
2033             case 48: /* SE_4_8 */
2034               kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
2035               break;
2036             case 49: /* SE_4_9 */
2037               kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
2038               break;
2039             /* SE for 8-connected thinning - negatives of the above */
2040             case 81: /* SE_8_0 */
2041               kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
2042               break;
2043             case 82: /* SE_8_2 */
2044               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
2045               break;
2046             case 83: /* SE_8_3 */
2047               kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
2048               break;
2049             case 84: /* SE_8_4 */
2050               kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
2051               break;
2052             case 85: /* SE_8_5 */
2053               kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
2054               break;
2055             case 86: /* SE_8_6 */
2056               kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
2057               break;
2058             case 87: /* SE_8_7 */
2059               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
2060               break;
2061             case 88: /* SE_8_8 */
2062               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
2063               break;
2064             case 89: /* SE_8_9 */
2065               kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
2066               break;
2067             /* Special combined SE kernels */
2068             case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
2069               kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
2070               break;
2071             case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
2072               kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
2073               break;
2074             case 481: /* SE_48_1 - General Connected Corner Kernel */
2075               kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
2076               break;
2077             default:
2078             case 482: /* SE_48_2 - General Edge Kernel */
2079               kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
2080               break;
2081           }
2082           if (kernel == (KernelInfo *) NULL)
2083             return(kernel);
2084           kernel->type = type;
2085           RotateKernelInfo(kernel, args->sigma);
2086           break;
2087         }
2088       /*
2089         Distance Measuring Kernels
2090       */
2091       case ChebyshevKernel:
2092         {
2093           if (args->rho < 1.0)
2094             kernel->width = kernel->height = 3;  /* default radius = 1 */
2095           else
2096             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2097           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2098
2099           kernel->values=(double *) AcquireAlignedMemory(kernel->width,
2100             kernel->height*sizeof(*kernel->values));
2101           if (kernel->values == (double *) NULL)
2102             return(DestroyKernelInfo(kernel));
2103
2104           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2105             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2106               kernel->positive_range += ( kernel->values[i] =
2107                   args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
2108           kernel->maximum = kernel->values[0];
2109           break;
2110         }
2111       case ManhattanKernel:
2112         {
2113           if (args->rho < 1.0)
2114             kernel->width = kernel->height = 3;  /* default radius = 1 */
2115           else
2116             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2117           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2118
2119           kernel->values=(double *) AcquireAlignedMemory(kernel->width,
2120             kernel->height*sizeof(*kernel->values));
2121           if (kernel->values == (double *) NULL)
2122             return(DestroyKernelInfo(kernel));
2123
2124           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2125             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2126               kernel->positive_range += ( kernel->values[i] =
2127                   args->sigma*(labs((long) u)+labs((long) v)) );
2128           kernel->maximum = kernel->values[0];
2129           break;
2130         }
2131       case OctagonalKernel:
2132       {
2133         if (args->rho < 2.0)
2134           kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
2135         else
2136           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2137         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2138
2139         kernel->values=(double *) AcquireAlignedMemory(kernel->width,
2140           kernel->height*sizeof(*kernel->values));
2141         if (kernel->values == (double *) NULL)
2142           return(DestroyKernelInfo(kernel));
2143
2144         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2145           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2146             {
2147               double
2148                 r1 = MagickMax(fabs((double)u),fabs((double)v)),
2149                 r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
2150               kernel->positive_range += kernel->values[i] =
2151                         args->sigma*MagickMax(r1,r2);
2152             }
2153         kernel->maximum = kernel->values[0];
2154         break;
2155       }
2156     case EuclideanKernel:
2157       {
2158         if (args->rho < 1.0)
2159           kernel->width = kernel->height = 3;  /* default radius = 1 */
2160         else
2161           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2162         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2163
2164         kernel->values=(double *) AcquireAlignedMemory(kernel->width,
2165           kernel->height*sizeof(*kernel->values));
2166         if (kernel->values == (double *) NULL)
2167           return(DestroyKernelInfo(kernel));
2168
2169         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2170           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2171             kernel->positive_range += ( kernel->values[i] =
2172               args->sigma*sqrt((double)(u*u+v*v)) );
2173         kernel->maximum = kernel->values[0];
2174         break;
2175       }
2176     default:
2177       {
2178         /* No-Op Kernel - Basically just a single pixel on its own */
2179         kernel=ParseKernelArray("1:1");
2180         if (kernel == (KernelInfo *) NULL)
2181           return(kernel);
2182         kernel->type = UndefinedKernel;
2183         break;
2184       }
2185       break;
2186   }
2187   return(kernel);
2188 }
2189 \f
2190 /*
2191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2192 %                                                                             %
2193 %                                                                             %
2194 %                                                                             %
2195 %     C l o n e K e r n e l I n f o                                           %
2196 %                                                                             %
2197 %                                                                             %
2198 %                                                                             %
2199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2200 %
2201 %  CloneKernelInfo() creates a new clone of the given Kernel List so that its
2202 %  can be modified without effecting the original.  The cloned kernel should
2203 %  be destroyed using DestoryKernelInfo() when no longer needed.
2204 %
2205 %  The format of the CloneKernelInfo method is:
2206 %
2207 %      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
2208 %
2209 %  A description of each parameter follows:
2210 %
2211 %    o kernel: the Morphology/Convolution kernel to be cloned
2212 %
2213 */
2214 MagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
2215 {
2216   register ssize_t
2217     i;
2218
2219   KernelInfo
2220     *new_kernel;
2221
2222   assert(kernel != (KernelInfo *) NULL);
2223   new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
2224   if (new_kernel == (KernelInfo *) NULL)
2225     return(new_kernel);
2226   *new_kernel=(*kernel); /* copy values in structure */
2227
2228   /* replace the values with a copy of the values */
2229   new_kernel->values=(double *) AcquireAlignedMemory(kernel->width,
2230     kernel->height*sizeof(*kernel->values));
2231   if (new_kernel->values == (double *) NULL)
2232     return(DestroyKernelInfo(new_kernel));
2233   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
2234     new_kernel->values[i]=kernel->values[i];
2235
2236   /* Also clone the next kernel in the kernel list */
2237   if ( kernel->next != (KernelInfo *) NULL ) {
2238     new_kernel->next = CloneKernelInfo(kernel->next);
2239     if ( new_kernel->next == (KernelInfo *) NULL )
2240       return(DestroyKernelInfo(new_kernel));
2241   }
2242
2243   return(new_kernel);
2244 }
2245 \f
2246 /*
2247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2248 %                                                                             %
2249 %                                                                             %
2250 %                                                                             %
2251 %     D e s t r o y K e r n e l I n f o                                       %
2252 %                                                                             %
2253 %                                                                             %
2254 %                                                                             %
2255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2256 %
2257 %  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
2258 %  kernel.
2259 %
2260 %  The format of the DestroyKernelInfo method is:
2261 %
2262 %      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2263 %
2264 %  A description of each parameter follows:
2265 %
2266 %    o kernel: the Morphology/Convolution kernel to be destroyed
2267 %
2268 */
2269 MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2270 {
2271   assert(kernel != (KernelInfo *) NULL);
2272   if ( kernel->next != (KernelInfo *) NULL )
2273     kernel->next=DestroyKernelInfo(kernel->next);
2274   kernel->values=(double *) RelinquishAlignedMemory(kernel->values);
2275   kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
2276   return(kernel);
2277 }
2278 \f
2279 /*
2280 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2281 %                                                                             %
2282 %                                                                             %
2283 %                                                                             %
2284 +     E x p a n d M i r r o r K e r n e l I n f o                             %
2285 %                                                                             %
2286 %                                                                             %
2287 %                                                                             %
2288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2289 %
2290 %  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
2291 %  sequence of 90-degree rotated kernels but providing a reflected 180
2292 %  rotatation, before the -/+ 90-degree rotations.
2293 %
2294 %  This special rotation order produces a better, more symetrical thinning of
2295 %  objects.
2296 %
2297 %  The format of the ExpandMirrorKernelInfo method is:
2298 %
2299 %      void ExpandMirrorKernelInfo(KernelInfo *kernel)
2300 %
2301 %  A description of each parameter follows:
2302 %
2303 %    o kernel: the Morphology/Convolution kernel
2304 %
2305 % This function is only internel to this module, as it is not finalized,
2306 % especially with regard to non-orthogonal angles, and rotation of larger
2307 % 2D kernels.
2308 */
2309
2310 #if 0
2311 static void FlopKernelInfo(KernelInfo *kernel)
2312     { /* Do a Flop by reversing each row. */
2313       size_t
2314         y;
2315       register ssize_t
2316         x,r;
2317       register double
2318         *k,t;
2319
2320       for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2321         for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2322           t=k[x],  k[x]=k[r],  k[r]=t;
2323
2324       kernel->x = kernel->width - kernel->x - 1;
2325       angle = fmod(angle+180.0, 360.0);
2326     }
2327 #endif
2328
2329 static void ExpandMirrorKernelInfo(KernelInfo *kernel)
2330 {
2331   KernelInfo
2332     *clone,
2333     *last;
2334
2335   last = kernel;
2336
2337   clone = CloneKernelInfo(last);
2338   RotateKernelInfo(clone, 180);   /* flip */
2339   LastKernelInfo(last)->next = clone;
2340   last = clone;
2341
2342   clone = CloneKernelInfo(last);
2343   RotateKernelInfo(clone, 90);   /* transpose */
2344   LastKernelInfo(last)->next = clone;
2345   last = clone;
2346
2347   clone = CloneKernelInfo(last);
2348   RotateKernelInfo(clone, 180);  /* flop */
2349   LastKernelInfo(last)->next = clone;
2350
2351   return;
2352 }
2353 \f
2354 /*
2355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2356 %                                                                             %
2357 %                                                                             %
2358 %                                                                             %
2359 +     E x p a n d R o t a t e K e r n e l I n f o                             %
2360 %                                                                             %
2361 %                                                                             %
2362 %                                                                             %
2363 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2364 %
2365 %  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2366 %  incrementally by the angle given, until the kernel repeats.
2367 %
2368 %  WARNING: 45 degree rotations only works for 3x3 kernels.
2369 %  While 90 degree roatations only works for linear and square kernels
2370 %
2371 %  The format of the ExpandRotateKernelInfo method is:
2372 %
2373 %      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
2374 %
2375 %  A description of each parameter follows:
2376 %
2377 %    o kernel: the Morphology/Convolution kernel
2378 %
2379 %    o angle: angle to rotate in degrees
2380 %
2381 % This function is only internel to this module, as it is not finalized,
2382 % especially with regard to non-orthogonal angles, and rotation of larger
2383 % 2D kernels.
2384 */
2385
2386 /* Internal Routine - Return true if two kernels are the same */
2387 static MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
2388      const KernelInfo *kernel2)
2389 {
2390   register size_t
2391     i;
2392
2393   /* check size and origin location */
2394   if (    kernel1->width != kernel2->width
2395        || kernel1->height != kernel2->height
2396        || kernel1->x != kernel2->x
2397        || kernel1->y != kernel2->y )
2398     return MagickFalse;
2399
2400   /* check actual kernel values */
2401   for (i=0; i < (kernel1->width*kernel1->height); i++) {
2402     /* Test for Nan equivalence */
2403     if ( IsNan(kernel1->values[i]) && !IsNan(kernel2->values[i]) )
2404       return MagickFalse;
2405     if ( IsNan(kernel2->values[i]) && !IsNan(kernel1->values[i]) )
2406       return MagickFalse;
2407     /* Test actual values are equivalent */
2408     if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
2409       return MagickFalse;
2410   }
2411
2412   return MagickTrue;
2413 }
2414
2415 static void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
2416 {
2417   KernelInfo
2418     *clone,
2419     *last;
2420
2421   last = kernel;
2422   while(1) {
2423     clone = CloneKernelInfo(last);
2424     RotateKernelInfo(clone, angle);
2425     if ( SameKernelInfo(kernel, clone) == MagickTrue )
2426       break;
2427     LastKernelInfo(last)->next = clone;
2428     last = clone;
2429   }
2430   clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
2431   return;
2432 }
2433 \f
2434 /*
2435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2436 %                                                                             %
2437 %                                                                             %
2438 %                                                                             %
2439 +     C a l c M e t a K e r n a l I n f o                                     %
2440 %                                                                             %
2441 %                                                                             %
2442 %                                                                             %
2443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2444 %
2445 %  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
2446 %  using the kernel values.  This should only ne used if it is not possible to
2447 %  calculate that meta-data in some easier way.
2448 %
2449 %  It is important that the meta-data is correct before ScaleKernelInfo() is
2450 %  used to perform kernel normalization.
2451 %
2452 %  The format of the CalcKernelMetaData method is:
2453 %
2454 %      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
2455 %
2456 %  A description of each parameter follows:
2457 %
2458 %    o kernel: the Morphology/Convolution kernel to modify
2459 %
2460 %  WARNING: Minimum and Maximum values are assumed to include zero, even if
2461 %  zero is not part of the kernel (as in Gaussian Derived kernels). This
2462 %  however is not true for flat-shaped morphological kernels.
2463 %
2464 %  WARNING: Only the specific kernel pointed to is modified, not a list of
2465 %  multiple kernels.
2466 %
2467 % This is an internal function and not expected to be useful outside this
2468 % module.  This could change however.
2469 */
2470 static void CalcKernelMetaData(KernelInfo *kernel)
2471 {
2472   register size_t
2473     i;
2474
2475   kernel->minimum = kernel->maximum = 0.0;
2476   kernel->negative_range = kernel->positive_range = 0.0;
2477   for (i=0; i < (kernel->width*kernel->height); i++)
2478     {
2479       if ( fabs(kernel->values[i]) < MagickEpsilon )
2480         kernel->values[i] = 0.0;
2481       ( kernel->values[i] < 0)
2482           ?  ( kernel->negative_range += kernel->values[i] )
2483           :  ( kernel->positive_range += kernel->values[i] );
2484       Minimize(kernel->minimum, kernel->values[i]);
2485       Maximize(kernel->maximum, kernel->values[i]);
2486     }
2487
2488   return;
2489 }
2490 \f
2491 /*
2492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2493 %                                                                             %
2494 %                                                                             %
2495 %                                                                             %
2496 %     M o r p h o l o g y A p p l y                                           %
2497 %                                                                             %
2498 %                                                                             %
2499 %                                                                             %
2500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2501 %
2502 %  MorphologyApply() applies a morphological method, multiple times using
2503 %  a list of multiple kernels.  This is the method that should be called by
2504 %  other 'operators' that internally use morphology operations as part of
2505 %  their processing.
2506 %
2507 %  It is basically equivalent to as MorphologyImage() (see below) but
2508 %  without any user controls.  This allows internel programs to use this
2509 %  function, to actually perform a specific task without possible interference
2510 %  by any API user supplied settings.
2511 %
2512 %  It is MorphologyImage() task to extract any such user controls, and
2513 %  pass them to this function for processing.
2514 %
2515 %  More specifically all given kernels should already be scaled, normalised,
2516 %  and blended appropriatally before being parred to this routine. The
2517 %  appropriate bias, and compose (typically 'UndefinedComposeOp') given.
2518 %
2519 %  The format of the MorphologyApply method is:
2520 %
2521 %      Image *MorphologyApply(const Image *image,MorphologyMethod method,
2522 %        const ssize_t iterations,const KernelInfo *kernel,
2523 %        const CompositeMethod compose,const double bias,
2524 %        ExceptionInfo *exception)
2525 %
2526 %  A description of each parameter follows:
2527 %
2528 %    o image: the source image
2529 %
2530 %    o method: the morphology method to be applied.
2531 %
2532 %    o iterations: apply the operation this many times (or no change).
2533 %                  A value of -1 means loop until no change found.
2534 %                  How this is applied may depend on the morphology method.
2535 %                  Typically this is a value of 1.
2536 %
2537 %    o channel: the channel type.
2538 %
2539 %    o kernel: An array of double representing the morphology kernel.
2540 %
2541 %    o compose: How to handle or merge multi-kernel results.
2542 %          If 'UndefinedCompositeOp' use default for the Morphology method.
2543 %          If 'NoCompositeOp' force image to be re-iterated by each kernel.
2544 %          Otherwise merge the results using the compose method given.
2545 %
2546 %    o bias: Convolution Output Bias.
2547 %
2548 %    o exception: return any errors or warnings in this structure.
2549 %
2550 */
2551
2552 /* Apply a Morphology Primative to an image using the given kernel.
2553 ** Two pre-created images must be provided, and no image is created.
2554 ** It returns the number of pixels that changed between the images
2555 ** for result convergence determination.
2556 */
2557 static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
2558   const MorphologyMethod method,const KernelInfo *kernel,const double bias,
2559   ExceptionInfo *exception)
2560 {
2561 #define MorphologyTag  "Morphology/Image"
2562
2563   CacheView
2564     *image_view,
2565     *morphology_view;
2566
2567   ssize_t
2568     y, offx, offy;
2569
2570   size_t
2571     virt_width,
2572     changed;
2573
2574   MagickBooleanType
2575     status;
2576
2577   MagickOffsetType
2578     progress;
2579
2580   assert(image != (Image *) NULL);
2581   assert(image->signature == MagickSignature);
2582   assert(morphology_image != (Image *) NULL);
2583   assert(morphology_image->signature == MagickSignature);
2584   assert(kernel != (KernelInfo *) NULL);
2585   assert(kernel->signature == MagickSignature);
2586   assert(exception != (ExceptionInfo *) NULL);
2587   assert(exception->signature == MagickSignature);
2588
2589   status=MagickTrue;
2590   changed=0;
2591   progress=0;
2592
2593   image_view=AcquireVirtualCacheView(image,exception);
2594   morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
2595   virt_width=image->columns+kernel->width-1;
2596
2597   /* Some methods (including convolve) needs use a reflected kernel.
2598    * Adjust 'origin' offsets to loop though kernel as a reflection.
2599    */
2600   offx = kernel->x;
2601   offy = kernel->y;
2602   switch(method) {
2603     case ConvolveMorphology:
2604     case DilateMorphology:
2605     case DilateIntensityMorphology:
2606     case IterativeDistanceMorphology:
2607       /* kernel needs to used with reflection about origin */
2608       offx = (ssize_t) kernel->width-offx-1;
2609       offy = (ssize_t) kernel->height-offy-1;
2610       break;
2611     case ErodeMorphology:
2612     case ErodeIntensityMorphology:
2613     case HitAndMissMorphology:
2614     case ThinningMorphology:
2615     case ThickenMorphology:
2616       /* kernel is used as is, without reflection */
2617       break;
2618     default:
2619       assert("Not a Primitive Morphology Method" != (char *) NULL);
2620       break;
2621   }
2622
2623   if ( method == ConvolveMorphology && kernel->width == 1 )
2624   { /* Special handling (for speed) of vertical (blur) kernels.
2625     ** This performs its handling in columns rather than in rows.
2626     ** This is only done for convolve as it is the only method that
2627     ** generates very large 1-D vertical kernels (such as a 'BlurKernel')
2628     **
2629     ** Timing tests (on single CPU laptop)
2630     ** Using a vertical 1-d Blue with normal row-by-row (below)
2631     **   time convert logo: -morphology Convolve Blur:0x10+90 null:
2632     **      0.807u
2633     ** Using this column method
2634     **   time convert logo: -morphology Convolve Blur:0x10+90 null:
2635     **      0.620u
2636     **
2637     ** Anthony Thyssen, 14 June 2010
2638     */
2639     register ssize_t
2640       x;
2641
2642 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2643     #pragma omp parallel for schedule(static,4) shared(progress,status) \
2644       dynamic_number_threads(image,image->columns,image->rows,1)
2645 #endif
2646     for (x=0; x < (ssize_t) image->columns; x++)
2647     {
2648       register const Quantum
2649         *restrict p;
2650
2651       register Quantum
2652         *restrict q;
2653
2654       register ssize_t
2655         y;
2656
2657       ssize_t
2658         r;
2659
2660       if (status == MagickFalse)
2661         continue;
2662       p=GetCacheViewVirtualPixels(image_view,x,-offy,1,image->rows+
2663         kernel->height-1,exception);
2664       q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2665         morphology_image->rows,exception);
2666       if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2667         {
2668           status=MagickFalse;
2669           continue;
2670         }
2671       /* offset to origin in 'p'. while 'q' points to it directly */
2672       r = offy;
2673
2674       for (y=0; y < (ssize_t) image->rows; y++)
2675       {
2676         PixelInfo
2677           result;
2678
2679         register ssize_t
2680           v;
2681
2682         register const double
2683           *restrict k;
2684
2685         register const Quantum
2686           *restrict k_pixels;
2687
2688         /* Copy input image to the output image for unused channels
2689         * This removes need for 'cloning' a new image every iteration
2690         */
2691         SetPixelRed(morphology_image,GetPixelRed(image,p+r*
2692           GetPixelChannels(image)),q);
2693         SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
2694           GetPixelChannels(image)),q);
2695         SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
2696           GetPixelChannels(image)),q);
2697         if (image->colorspace == CMYKColorspace)
2698           SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
2699             GetPixelChannels(image)),q);
2700
2701         /* Set the bias of the weighted average output */
2702         result.red   =
2703         result.green =
2704         result.blue  =
2705         result.alpha =
2706         result.black = bias;
2707
2708
2709         /* Weighted Average of pixels using reflected kernel
2710         **
2711         ** NOTE for correct working of this operation for asymetrical
2712         ** kernels, the kernel needs to be applied in its reflected form.
2713         ** That is its values needs to be reversed.
2714         */
2715         k = &kernel->values[ kernel->height-1 ];
2716         k_pixels = p;
2717         if ( (image->channel_mask != DefaultChannels) ||
2718              (image->matte == MagickFalse) )
2719           { /* No 'Sync' involved.
2720             ** Convolution is just a simple greyscale channel operation
2721             */
2722             for (v=0; v < (ssize_t) kernel->height; v++) {
2723               if ( IsNan(*k) ) continue;
2724               result.red     += (*k)*GetPixelRed(image,k_pixels);
2725               result.green   += (*k)*GetPixelGreen(image,k_pixels);
2726               result.blue    += (*k)*GetPixelBlue(image,k_pixels);
2727               if (image->colorspace == CMYKColorspace)
2728                 result.black+=(*k)*GetPixelBlack(image,k_pixels);
2729               result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
2730               k--;
2731               k_pixels+=GetPixelChannels(image);
2732             }
2733             if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2734               SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
2735             if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2736               SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
2737             if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2738               SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
2739             if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2740                 (image->colorspace == CMYKColorspace))
2741               SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
2742             if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2743                 (image->matte == MagickTrue))
2744               SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
2745           }
2746         else
2747           { /* Channel 'Sync' Flag, and Alpha Channel enabled.
2748             ** Weight the color channels with Alpha Channel so that
2749             ** transparent pixels are not part of the results.
2750             */
2751             double
2752               alpha,  /* alpha weighting for colors : alpha  */
2753               gamma;  /* divisor, sum of color alpha weighting */
2754             size_t
2755               count;  /* alpha valus collected, number kernel values */
2756
2757             count=0;
2758             gamma=0.0;
2759             for (v=0; v < (ssize_t) kernel->height; v++) {
2760               if ( IsNan(*k) ) continue;
2761               alpha=QuantumScale*GetPixelAlpha(image,k_pixels);
2762               gamma += alpha; /* normalize alpha weights only */
2763               count++;        /* number of alpha values collected */
2764               alpha*=(*k);    /* include kernel weighting now */
2765               result.red     += alpha*GetPixelRed(image,k_pixels);
2766               result.green   += alpha*GetPixelGreen(image,k_pixels);
2767               result.blue    += alpha*GetPixelBlue(image,k_pixels);
2768               if (image->colorspace == CMYKColorspace)
2769                 result.black += alpha*GetPixelBlack(image,k_pixels);
2770               result.alpha   += (*k)*GetPixelAlpha(image,k_pixels);
2771               k--;
2772               k_pixels+=GetPixelChannels(image);
2773             }
2774             /* Sync'ed channels, all channels are modified */
2775             gamma=(double)count/(fabs((double) gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2776             SetPixelRed(morphology_image,ClampToQuantum(gamma*result.red),q);
2777             SetPixelGreen(morphology_image,ClampToQuantum(gamma*result.green),q);
2778             SetPixelBlue(morphology_image,ClampToQuantum(gamma*result.blue),q);
2779             if (image->colorspace == CMYKColorspace)
2780               SetPixelBlack(morphology_image,ClampToQuantum(gamma*result.black),q);
2781             SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
2782           }
2783
2784         /* Count up changed pixels */
2785         if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q))
2786             || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q))
2787             || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q))
2788             || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q))
2789             || ((image->colorspace == CMYKColorspace) &&
2790                 (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
2791           changed++;  /* The pixel was changed in some way! */
2792         p+=GetPixelChannels(image);
2793         q+=GetPixelChannels(morphology_image);
2794       } /* y */
2795       if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
2796         status=MagickFalse;
2797       if (image->progress_monitor != (MagickProgressMonitor) NULL)
2798         {
2799           MagickBooleanType
2800             proceed;
2801
2802 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2803           #pragma omp critical (MagickCore_MorphologyImage)
2804 #endif
2805           proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
2806           if (proceed == MagickFalse)
2807             status=MagickFalse;
2808         }
2809     } /* x */
2810     morphology_image->type=image->type;
2811     morphology_view=DestroyCacheView(morphology_view);
2812     image_view=DestroyCacheView(image_view);
2813     return(status ? (ssize_t) changed : 0);
2814   }
2815
2816   /*
2817   ** Normal handling of horizontal or rectangular kernels (row by row)
2818   */
2819 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2820   #pragma omp parallel for schedule(static,4) shared(progress,status) \
2821     dynamic_number_threads(image,image->columns,image->rows,1)
2822 #endif
2823   for (y=0; y < (ssize_t) image->rows; y++)
2824   {
2825     register const Quantum
2826       *restrict p;
2827
2828     register Quantum
2829       *restrict q;
2830
2831     register ssize_t
2832       x;
2833
2834     size_t
2835       r;
2836
2837     if (status == MagickFalse)
2838       continue;
2839     p=GetCacheViewVirtualPixels(image_view, -offx, y-offy, virt_width,
2840       kernel->height,  exception);
2841     q=GetCacheViewAuthenticPixels(morphology_view,0,y,
2842       morphology_image->columns,1,exception);
2843     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2844       {
2845         status=MagickFalse;
2846         continue;
2847       }
2848     /* offset to origin in 'p'. while 'q' points to it directly */
2849     r = virt_width*offy + offx;
2850
2851     for (x=0; x < (ssize_t) image->columns; x++)
2852     {
2853        ssize_t
2854         v;
2855
2856       register ssize_t
2857         u;
2858
2859       register const double
2860         *restrict k;
2861
2862       register const Quantum
2863         *restrict k_pixels;
2864
2865       PixelInfo
2866         result,
2867         min,
2868         max;
2869
2870       /* Copy input image to the output image for unused channels
2871        * This removes need for 'cloning' a new image every iteration
2872        */
2873       SetPixelRed(morphology_image,GetPixelRed(image,p+r*
2874         GetPixelChannels(image)),q);
2875       SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
2876         GetPixelChannels(image)),q);
2877       SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
2878         GetPixelChannels(image)),q);
2879       if (image->colorspace == CMYKColorspace)
2880         SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
2881           GetPixelChannels(image)),q);
2882
2883       /* Defaults */
2884       min.red     =
2885       min.green   =
2886       min.blue    =
2887       min.alpha =
2888       min.black   = (double) QuantumRange;
2889       max.red     =
2890       max.green   =
2891       max.blue    =
2892       max.alpha =
2893       max.black   = (double) 0;
2894       /* default result is the original pixel value */
2895       result.red     = (double) GetPixelRed(image,p+r*GetPixelChannels(image));
2896       result.green   = (double) GetPixelGreen(image,p+r*GetPixelChannels(image));
2897       result.blue    = (double) GetPixelBlue(image,p+r*GetPixelChannels(image));
2898       result.black   = 0.0;
2899       if (image->colorspace == CMYKColorspace)
2900         result.black = (double) GetPixelBlack(image,p+r*GetPixelChannels(image));
2901       result.alpha=(double) GetPixelAlpha(image,p+r*GetPixelChannels(image));
2902
2903       switch (method) {
2904         case ConvolveMorphology:
2905           /* Set the bias of the weighted average output */
2906           result.red     =
2907           result.green   =
2908           result.blue    =
2909           result.alpha =
2910           result.black   = bias;
2911           break;
2912         case DilateIntensityMorphology:
2913         case ErodeIntensityMorphology:
2914           /* use a boolean flag indicating when first match found */
2915           result.red = 0.0;  /* result is not used otherwise */
2916           break;
2917         default:
2918           break;
2919       }
2920
2921       switch ( method ) {
2922         case ConvolveMorphology:
2923             /* Weighted Average of pixels using reflected kernel
2924             **
2925             ** NOTE for correct working of this operation for asymetrical
2926             ** kernels, the kernel needs to be applied in its reflected form.
2927             ** That is its values needs to be reversed.
2928             **
2929             ** Correlation is actually the same as this but without reflecting
2930             ** the kernel, and thus 'lower-level' that Convolution.  However
2931             ** as Convolution is the more common method used, and it does not
2932             ** really cost us much in terms of processing to use a reflected
2933             ** kernel, so it is Convolution that is implemented.
2934             **
2935             ** Correlation will have its kernel reflected before calling
2936             ** this function to do a Convolve.
2937             **
2938             ** For more details of Correlation vs Convolution see
2939             **   http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
2940             */
2941             k = &kernel->values[ kernel->width*kernel->height-1 ];
2942             k_pixels = p;
2943             if ( (image->channel_mask != DefaultChannels) ||
2944                  (image->matte == MagickFalse) )
2945               { /* No 'Sync' involved.
2946                 ** Convolution is simple greyscale channel operation
2947                 */
2948                 for (v=0; v < (ssize_t) kernel->height; v++) {
2949                   for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2950                     if ( IsNan(*k) ) continue;
2951                     result.red     += (*k)*
2952                       GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
2953                     result.green   += (*k)*
2954                       GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
2955                     result.blue    += (*k)*
2956                       GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
2957                     if (image->colorspace == CMYKColorspace)
2958                       result.black += (*k)*
2959                         GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
2960                     result.alpha += (*k)*
2961                       GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
2962                   }
2963                   k_pixels += virt_width*GetPixelChannels(image);
2964                 }
2965                 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2966                   SetPixelRed(morphology_image,ClampToQuantum(result.red),
2967                     q);
2968                 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2969                   SetPixelGreen(morphology_image,ClampToQuantum(result.green),
2970                     q);
2971                 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2972                   SetPixelBlue(morphology_image,ClampToQuantum(result.blue),
2973                     q);
2974                 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2975                     (image->colorspace == CMYKColorspace))
2976                   SetPixelBlack(morphology_image,ClampToQuantum(result.black),
2977                     q);
2978                 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2979                     (image->matte == MagickTrue))
2980                   SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),
2981                     q);
2982               }
2983             else
2984               { /* Channel 'Sync' Flag, and Alpha Channel enabled.
2985                 ** Weight the color channels with Alpha Channel so that
2986                 ** transparent pixels are not part of the results.
2987                 */
2988                 double
2989                   alpha,  /* alpha weighting for colors : alpha  */
2990                   gamma;  /* divisor, sum of color alpha weighting */
2991                 size_t
2992                   count;  /* alpha valus collected, number kernel values */
2993
2994                 count=0;
2995                 gamma=0.0;
2996                 for (v=0; v < (ssize_t) kernel->height; v++) {
2997                   for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2998                     if ( IsNan(*k) ) continue;
2999                     alpha=QuantumScale*GetPixelAlpha(image,
3000                                 k_pixels+u*GetPixelChannels(image));
3001                     gamma += alpha;    /* normalize alpha weights only */
3002                     count++;           /* number of alpha values collected */
3003                     alpha=alpha*(*k);  /* include kernel weighting now */
3004                     result.red     += alpha*
3005                       GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
3006                     result.green   += alpha*
3007                       GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
3008                     result.blue    += alpha*
3009                       GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
3010                     if (image->colorspace == CMYKColorspace)
3011                       result.black += alpha*
3012                         GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
3013                     result.alpha   += (*k)*
3014                       GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
3015                   }
3016                   k_pixels += virt_width*GetPixelChannels(image);
3017                 }
3018                 /* Sync'ed channels, all channels are modified */
3019                 gamma=(double)count/(fabs((double) gamma) < MagickEpsilon ? MagickEpsilon : gamma);
3020                 SetPixelRed(morphology_image,
3021                   ClampToQuantum(gamma*result.red),q);
3022                 SetPixelGreen(morphology_image,
3023                   ClampToQuantum(gamma*result.green),q);
3024                 SetPixelBlue(morphology_image,
3025                   ClampToQuantum(gamma*result.blue),q);
3026                 if (image->colorspace == CMYKColorspace)
3027                   SetPixelBlack(morphology_image,
3028                     ClampToQuantum(gamma*result.black),q);
3029                 SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
3030               }
3031             break;
3032
3033         case ErodeMorphology:
3034             /* Minimum Value within kernel neighbourhood
3035             **
3036             ** NOTE that the kernel is not reflected for this operation!
3037             **
3038             ** NOTE: in normal Greyscale Morphology, the kernel value should
3039             ** be added to the real value, this is currently not done, due to
3040             ** the nature of the boolean kernels being used.
3041             */
3042             k = kernel->values;
3043             k_pixels = p;
3044             for (v=0; v < (ssize_t) kernel->height; v++) {
3045               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3046                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
3047                 Minimize(min.red,     (double)
3048                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3049                 Minimize(min.green,   (double)
3050                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3051                 Minimize(min.blue,    (double)
3052                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3053                 Minimize(min.alpha,   (double)
3054                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3055                 if (image->colorspace == CMYKColorspace)
3056                   Minimize(min.black,  (double)
3057                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3058               }
3059               k_pixels += virt_width*GetPixelChannels(image);
3060             }
3061             break;
3062
3063         case DilateMorphology:
3064             /* Maximum Value within kernel neighbourhood
3065             **
3066             ** NOTE for correct working of this operation for asymetrical
3067             ** kernels, the kernel needs to be applied in its reflected form.
3068             ** That is its values needs to be reversed.
3069             **
3070             ** NOTE: in normal Greyscale Morphology, the kernel value should
3071             ** be added to the real value, this is currently not done, due to
3072             ** the nature of the boolean kernels being used.
3073             **
3074             */
3075             k = &kernel->values[ kernel->width*kernel->height-1 ];
3076             k_pixels = p;
3077             for (v=0; v < (ssize_t) kernel->height; v++) {
3078               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3079                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
3080                 Maximize(max.red,     (double)
3081                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3082                 Maximize(max.green,   (double)
3083                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3084                 Maximize(max.blue,    (double)
3085                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3086                 Maximize(max.alpha,   (double)
3087                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3088                 if (image->colorspace == CMYKColorspace)
3089                   Maximize(max.black,   (double)
3090                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3091               }
3092               k_pixels += virt_width*GetPixelChannels(image);
3093             }
3094             break;
3095
3096         case HitAndMissMorphology:
3097         case ThinningMorphology:
3098         case ThickenMorphology:
3099             /* Minimum of Foreground Pixel minus Maxumum of Background Pixels
3100             **
3101             ** NOTE that the kernel is not reflected for this operation,
3102             ** and consists of both foreground and background pixel
3103             ** neighbourhoods, 0.0 for background, and 1.0 for foreground
3104             ** with either Nan or 0.5 values for don't care.
3105             **
3106             ** Note that this will never produce a meaningless negative
3107             ** result.  Such results can cause Thinning/Thicken to not work
3108             ** correctly when used against a greyscale image.
3109             */
3110             k = kernel->values;
3111             k_pixels = p;
3112             for (v=0; v < (ssize_t) kernel->height; v++) {
3113               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3114                 if ( IsNan(*k) ) continue;
3115                 if ( (*k) > 0.7 )
3116                 { /* minimim of foreground pixels */
3117                   Minimize(min.red,     (double)
3118                     GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3119                   Minimize(min.green,   (double)
3120                     GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3121                   Minimize(min.blue,    (double)
3122                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3123                   Minimize(min.alpha,(double)
3124                     GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3125                   if ( image->colorspace == CMYKColorspace)
3126                     Minimize(min.black,(double)
3127                       GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3128                 }
3129                 else if ( (*k) < 0.3 )
3130                 { /* maximum of background pixels */
3131                   Maximize(max.red,     (double)
3132                     GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3133                   Maximize(max.green,   (double)
3134                     GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3135                   Maximize(max.blue,    (double)
3136                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3137                   Maximize(max.alpha,(double)
3138                     GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3139                   if (image->colorspace == CMYKColorspace)
3140                     Maximize(max.black,   (double)
3141                       GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3142                 }
3143               }
3144               k_pixels += virt_width*GetPixelChannels(image);
3145             }
3146             /* Pattern Match if difference is positive */
3147             min.red     -= max.red;     Maximize( min.red,     0.0 );
3148             min.green   -= max.green;   Maximize( min.green,   0.0 );
3149             min.blue    -= max.blue;    Maximize( min.blue,    0.0 );
3150             min.black   -= max.black;   Maximize( min.black,   0.0 );
3151             min.alpha -= max.alpha; Maximize( min.alpha, 0.0 );
3152             break;
3153
3154         case ErodeIntensityMorphology:
3155             /* Select Pixel with Minimum Intensity within kernel neighbourhood
3156             **
3157             ** WARNING: the intensity test fails for CMYK and does not
3158             ** take into account the moderating effect of the alpha channel
3159             ** on the intensity.
3160             **
3161             ** NOTE that the kernel is not reflected for this operation!
3162             */
3163             k = kernel->values;
3164             k_pixels = p;
3165             for (v=0; v < (ssize_t) kernel->height; v++) {
3166               for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3167                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
3168                 if ( result.red == 0.0 ||
3169                      GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) < GetPixelIntensity(morphology_image,q) ) {
3170                   /* copy the whole pixel - no channel selection */
3171                   SetPixelRed(morphology_image,GetPixelRed(image,
3172                     k_pixels+u*GetPixelChannels(image)),q);
3173                   SetPixelGreen(morphology_image,GetPixelGreen(image,
3174                     k_pixels+u*GetPixelChannels(image)),q);
3175                   SetPixelBlue(morphology_image,GetPixelBlue(image,
3176                     k_pixels+u*GetPixelChannels(image)),q);
3177                   SetPixelAlpha(morphology_image,GetPixelAlpha(image,
3178                     k_pixels+u*GetPixelChannels(image)),q);
3179                   if ( result.red > 0.0 ) changed++;
3180                   result.red = 1.0;
3181                 }
3182               }
3183               k_pixels += virt_width*GetPixelChannels(image);
3184             }
3185             break;
3186
3187         case DilateIntensityMorphology:
3188             /* Select Pixel with Maximum Intensity within kernel neighbourhood
3189             **
3190             ** WARNING: the intensity test fails for CMYK and does not
3191             ** take into account the moderating effect of the alpha channel
3192             ** on the intensity (yet).
3193             **
3194             ** NOTE for correct working of this operation for asymetrical
3195             ** kernels, the kernel needs to be applied in its reflected form.
3196             ** That is its values needs to be reversed.
3197             */
3198             k = &kernel->values[ kernel->width*kernel->height-1 ];
3199             k_pixels = p;
3200             for (v=0; v < (ssize_t) kernel->height; v++) {
3201               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3202                 if ( IsNan(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
3203                 if ( result.red == 0.0 ||
3204                      GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) > GetPixelIntensity(morphology_image,q) ) {
3205                   /* copy the whole pixel - no channel selection */
3206                   SetPixelRed(morphology_image,GetPixelRed(image,
3207                     k_pixels+u*GetPixelChannels(image)),q);
3208                   SetPixelGreen(morphology_image,GetPixelGreen(image,
3209                     k_pixels+u*GetPixelChannels(image)),q);
3210                   SetPixelBlue(morphology_image,GetPixelBlue(image,
3211                     k_pixels+u*GetPixelChannels(image)),q);
3212                   SetPixelAlpha(morphology_image,GetPixelAlpha(image,
3213                     k_pixels+u*GetPixelChannels(image)),q);
3214                   if ( result.red > 0.0 ) changed++;
3215                   result.red = 1.0;
3216                 }
3217               }
3218               k_pixels += virt_width*GetPixelChannels(image);
3219             }
3220             break;
3221
3222         case IterativeDistanceMorphology:
3223             /* Work out an iterative distance from black edge of a white image
3224             ** shape.  Essentually white values are decreased to the smallest
3225             ** 'distance from edge' it can find.
3226             **
3227             ** It works by adding kernel values to the neighbourhood, and and
3228             ** select the minimum value found. The kernel is rotated before
3229             ** use, so kernel distances match resulting distances, when a user
3230             ** provided asymmetric kernel is applied.
3231             **
3232             **
3233             ** This code is almost identical to True GrayScale Morphology But
3234             ** not quite.
3235             **
3236             ** GreyDilate  Kernel values added, maximum value found Kernel is
3237             ** rotated before use.
3238             **
3239             ** GrayErode:  Kernel values subtracted and minimum value found No
3240             ** kernel rotation used.
3241             **
3242             ** Note the the Iterative Distance method is essentially a
3243             ** GrayErode, but with negative kernel values, and kernel
3244             ** rotation applied.
3245             */
3246             k = &kernel->values[ kernel->width*kernel->height-1 ];
3247             k_pixels = p;
3248             for (v=0; v < (ssize_t) kernel->height; v++) {
3249               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3250                 if ( IsNan(*k) ) continue;
3251                 Minimize(result.red,     (*k)+(double)
3252                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3253                 Minimize(result.green,   (*k)+(double)
3254                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3255                 Minimize(result.blue,    (*k)+(double)
3256                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3257                 Minimize(result.alpha,   (*k)+(double)
3258                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3259                 if ( image->colorspace == CMYKColorspace)
3260                   Maximize(result.black, (*k)+(double)
3261                       GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3262               }
3263               k_pixels += virt_width*GetPixelChannels(image);
3264             }
3265             break;
3266
3267         case UndefinedMorphology:
3268         default:
3269             break; /* Do nothing */
3270       }
3271       /* Final mathematics of results (combine with original image?)
3272       **
3273       ** NOTE: Difference Morphology operators Edge* and *Hat could also
3274       ** be done here but works better with iteration as a image difference
3275       ** in the controling function (below).  Thicken and Thinning however
3276       ** should be done here so thay can be iterated correctly.
3277       */
3278       switch ( method ) {
3279         case HitAndMissMorphology:
3280         case ErodeMorphology:
3281           result = min;    /* minimum of neighbourhood */
3282           break;
3283         case DilateMorphology:
3284           result = max;    /* maximum of neighbourhood */
3285           break;
3286         case ThinningMorphology:
3287           /* subtract pattern match from original */
3288           result.red     -= min.red;
3289           result.green   -= min.green;
3290           result.blue    -= min.blue;
3291           result.black   -= min.black;
3292           result.alpha   -= min.alpha;
3293           break;
3294         case ThickenMorphology:
3295           /* Add the pattern matchs to the original */
3296           result.red     += min.red;
3297           result.green   += min.green;
3298           result.blue    += min.blue;
3299           result.black   += min.black;
3300           result.alpha   += min.alpha;
3301           break;
3302         default:
3303           /* result directly calculated or assigned */
3304           break;
3305       }
3306       /* Assign the resulting pixel values - Clamping Result */
3307       switch ( method ) {
3308         case UndefinedMorphology:
3309         case ConvolveMorphology:
3310         case DilateIntensityMorphology:
3311         case ErodeIntensityMorphology:
3312           break;  /* full pixel was directly assigned - not a channel method */
3313         default:
3314           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3315             SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
3316           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3317             SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
3318           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3319             SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
3320           if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3321               (image->colorspace == CMYKColorspace))
3322             SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
3323           if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
3324               (image->matte == MagickTrue))
3325             SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
3326           break;
3327       }
3328       /* Count up changed pixels */
3329       if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q)) ||
3330           (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q)) ||
3331           (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q)) ||
3332           (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q)) ||
3333           ((image->colorspace == CMYKColorspace) &&
3334            (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
3335         changed++;  /* The pixel was changed in some way! */
3336       p+=GetPixelChannels(image);
3337       q+=GetPixelChannels(morphology_image);
3338     } /* x */
3339     if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3340       status=MagickFalse;
3341     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3342       {
3343         MagickBooleanType
3344           proceed;
3345
3346 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3347         #pragma omp critical (MagickCore_MorphologyImage)
3348 #endif
3349         proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
3350         if (proceed == MagickFalse)
3351           status=MagickFalse;
3352       }
3353   } /* y */
3354   morphology_view=DestroyCacheView(morphology_view);
3355   image_view=DestroyCacheView(image_view);
3356   return(status ? (ssize_t)changed : -1);
3357 }
3358
3359 /* This is almost identical to the MorphologyPrimative() function above,
3360 ** but will apply the primitive directly to the actual image using two
3361 ** passes, once in each direction, with the results of the previous (and
3362 ** current) row being re-used.
3363 **
3364 ** That is after each row is 'Sync'ed' into the image, the next row will
3365 ** make use of those values as part of the calculation of the next row.
3366 ** It then repeats, but going in the oppisite (bottom-up) direction.
3367 **
3368 ** Because of this 're-use of results' this function can not make use
3369 ** of multi-threaded, parellel processing.
3370 */
3371 static ssize_t MorphologyPrimitiveDirect(Image *image,
3372   const MorphologyMethod method,const KernelInfo *kernel,
3373   ExceptionInfo *exception)
3374 {
3375   CacheView
3376     *auth_view,
3377     *virt_view;
3378
3379   MagickBooleanType
3380     status;
3381
3382   MagickOffsetType
3383     progress;
3384
3385   ssize_t
3386     y, offx, offy;
3387
3388   size_t
3389     virt_width,
3390     changed;
3391
3392   status=MagickTrue;
3393   changed=0;
3394   progress=0;
3395
3396   assert(image != (Image *) NULL);
3397   assert(image->signature == MagickSignature);
3398   assert(kernel != (KernelInfo *) NULL);
3399   assert(kernel->signature == MagickSignature);
3400   assert(exception != (ExceptionInfo *) NULL);
3401   assert(exception->signature == MagickSignature);
3402
3403   /* Some methods (including convolve) needs use a reflected kernel.
3404    * Adjust 'origin' offsets to loop though kernel as a reflection.
3405    */
3406   offx = kernel->x;
3407   offy = kernel->y;
3408   switch(method) {
3409     case DistanceMorphology:
3410     case VoronoiMorphology:
3411       /* kernel needs to used with reflection about origin */
3412       offx = (ssize_t) kernel->width-offx-1;
3413       offy = (ssize_t) kernel->height-offy-1;
3414       break;
3415 #if 0
3416     case ?????Morphology:
3417       /* kernel is used as is, without reflection */
3418       break;
3419 #endif
3420     default:
3421       assert("Not a PrimativeDirect Morphology Method" != (char *) NULL);
3422       break;
3423   }
3424
3425   /* DO NOT THREAD THIS CODE! */
3426   /* two views into same image (virtual, and actual) */
3427   virt_view=AcquireVirtualCacheView(image,exception);
3428   auth_view=AcquireAuthenticCacheView(image,exception);
3429   virt_width=image->columns+kernel->width-1;
3430
3431   for (y=0; y < (ssize_t) image->rows; y++)
3432   {
3433     register const Quantum
3434       *restrict p;
3435
3436     register Quantum
3437       *restrict q;
3438
3439     register ssize_t
3440       x;
3441
3442     ssize_t
3443       r;
3444
3445     /* NOTE read virtual pixels, and authentic pixels, from the same image!
3446     ** we read using virtual to get virtual pixel handling, but write back
3447     ** into the same image.
3448     **
3449     ** Only top half of kernel is processed as we do a single pass downward
3450     ** through the image iterating the distance function as we go.
3451     */
3452     if (status == MagickFalse)
3453       break;
3454     p=GetCacheViewVirtualPixels(virt_view,-offx,y-offy,virt_width,(size_t)
3455       offy+1,exception);
3456     q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
3457       exception);
3458     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3459       status=MagickFalse;
3460     if (status == MagickFalse)
3461       break;
3462
3463     /* offset to origin in 'p'. while 'q' points to it directly */
3464     r = (ssize_t) virt_width*offy + offx;
3465
3466     for (x=0; x < (ssize_t) image->columns; x++)
3467     {
3468       ssize_t
3469         v;
3470
3471       register ssize_t
3472         u;
3473
3474       register const double
3475         *restrict k;
3476
3477       register const Quantum
3478         *restrict k_pixels;
3479
3480       PixelInfo
3481         result;
3482
3483       /* Starting Defaults */
3484       GetPixelInfo(image,&result);
3485       GetPixelInfoPixel(image,q,&result);
3486       if ( method != VoronoiMorphology )
3487         result.alpha = QuantumRange - result.alpha;
3488
3489       switch ( method ) {
3490         case DistanceMorphology:
3491             /* Add kernel Value and select the minimum value found. */
3492             k = &kernel->values[ kernel->width*kernel->height-1 ];
3493             k_pixels = p;
3494             for (v=0; v <= (ssize_t) offy; v++) {
3495               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3496                 if ( IsNan(*k) ) continue;
3497                 Minimize(result.red,     (*k)+
3498                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3499                 Minimize(result.green,   (*k)+
3500                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3501                 Minimize(result.blue,    (*k)+
3502                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3503                 if (image->colorspace == CMYKColorspace)
3504                   Minimize(result.black,(*k)+
3505                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3506                 Minimize(result.alpha, (*k)+
3507                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3508               }
3509               k_pixels += virt_width*GetPixelChannels(image);
3510             }
3511             /* repeat with the just processed pixels of this row */
3512             k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3513             k_pixels = q-offx*GetPixelChannels(image);
3514               for (u=0; u < (ssize_t) offx; u++, k--) {
3515                 if ( x+u-offx < 0 ) continue;  /* off the edge! */
3516                 if ( IsNan(*k) ) continue;
3517                 Minimize(result.red,     (*k)+
3518                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3519                 Minimize(result.green,   (*k)+
3520                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3521                 Minimize(result.blue,    (*k)+
3522                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3523                 if (image->colorspace == CMYKColorspace)
3524                   Minimize(result.black,(*k)+
3525                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3526                 Minimize(result.alpha,(*k)+
3527                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3528               }
3529             break;
3530         case VoronoiMorphology:
3531             /* Apply Distance to 'Matte' channel, while coping the color
3532             ** values of the closest pixel.
3533             **
3534             ** This is experimental, and realy the 'alpha' component should
3535             ** be completely separate 'masking' channel so that alpha can
3536             ** also be used as part of the results.
3537             */
3538             k = &kernel->values[ kernel->width*kernel->height-1 ];
3539             k_pixels = p;
3540             for (v=0; v <= (ssize_t) offy; v++) {
3541               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3542                 if ( IsNan(*k) ) continue;
3543                 if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3544                   {
3545                     GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
3546                       &result);
3547                     result.alpha += *k;
3548                   }
3549               }
3550               k_pixels += virt_width*GetPixelChannels(image);
3551             }
3552             /* repeat with the just processed pixels of this row */
3553             k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3554             k_pixels = q-offx*GetPixelChannels(image);
3555               for (u=0; u < (ssize_t) offx; u++, k--) {
3556                 if ( x+u-offx < 0 ) continue;  /* off the edge! */
3557                 if ( IsNan(*k) ) continue;
3558                 if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3559                   {
3560                     GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
3561                       &result);
3562                     result.alpha += *k;
3563                   }
3564               }
3565             break;
3566         default:
3567           /* result directly calculated or assigned */
3568           break;
3569       }
3570       /* Assign the resulting pixel values - Clamping Result */
3571       switch ( method ) {
3572         case VoronoiMorphology:
3573           SetPixelInfoPixel(image,&result,q);
3574           break;
3575         default:
3576           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3577             SetPixelRed(image,ClampToQuantum(result.red),q);
3578           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3579             SetPixelGreen(image,ClampToQuantum(result.green),q);
3580           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3581             SetPixelBlue(image,ClampToQuantum(result.blue),q);
3582           if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3583               (image->colorspace == CMYKColorspace))
3584             SetPixelBlack(image,ClampToQuantum(result.black),q);
3585           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
3586               (image->matte == MagickTrue))
3587             SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
3588           break;
3589       }
3590       /* Count up changed pixels */
3591       if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q)) ||
3592           (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q)) ||
3593           (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q)) ||
3594           (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q)) ||
3595           ((image->colorspace == CMYKColorspace) &&
3596            (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
3597         changed++;  /* The pixel was changed in some way! */
3598
3599       p+=GetPixelChannels(image); /* increment pixel buffers */
3600       q+=GetPixelChannels(image);
3601     } /* x */
3602
3603     if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3604       status=MagickFalse;
3605     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3606       if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
3607                 == MagickFalse )
3608         status=MagickFalse;
3609
3610   } /* y */
3611
3612   /* Do the reversed pass through the image */
3613   for (y=(ssize_t)image->rows-1; y >= 0; y--)
3614   {
3615     register const Quantum
3616       *restrict p;
3617
3618     register Quantum
3619       *restrict q;
3620
3621     register ssize_t
3622       x;
3623
3624     ssize_t
3625       r;
3626
3627     if (status == MagickFalse)
3628       break;
3629     /* NOTE read virtual pixels, and authentic pixels, from the same image!
3630     ** we read using virtual to get virtual pixel handling, but write back
3631     ** into the same image.
3632     **
3633     ** Only the bottom half of the kernel will be processes as we
3634     ** up the image.
3635     */
3636     p=GetCacheViewVirtualPixels(virt_view,-offx,y,virt_width,(size_t)
3637       kernel->y+1,exception);
3638     q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
3639       exception);
3640     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3641       status=MagickFalse;
3642     if (status == MagickFalse)
3643       break;
3644
3645     /* adjust positions to end of row */
3646     p += (image->columns-1)*GetPixelChannels(image);
3647     q += (image->columns-1)*GetPixelChannels(image);
3648
3649     /* offset to origin in 'p'. while 'q' points to it directly */
3650     r = offx;
3651
3652     for (x=(ssize_t)image->columns-1; x >= 0; x--)
3653     {
3654       ssize_t
3655         v;
3656
3657       register ssize_t
3658         u;
3659
3660       register const double
3661         *restrict k;
3662
3663       register const Quantum
3664         *restrict k_pixels;
3665
3666       PixelInfo
3667         result;
3668
3669       /* Default - previously modified pixel */
3670       GetPixelInfo(image,&result);
3671       GetPixelInfoPixel(image,q,&result);
3672       if ( method != VoronoiMorphology )
3673         result.alpha = QuantumRange - result.alpha;
3674
3675       switch ( method ) {
3676         case DistanceMorphology:
3677             /* Add kernel Value and select the minimum value found. */
3678             k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3679             k_pixels = p;
3680             for (v=offy; v < (ssize_t) kernel->height; v++) {
3681               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3682                 if ( IsNan(*k) ) continue;
3683                 Minimize(result.red,     (*k)+
3684                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3685                 Minimize(result.green,   (*k)+
3686                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3687                 Minimize(result.blue,    (*k)+
3688                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3689                 if ( image->colorspace == CMYKColorspace)
3690                   Minimize(result.black,(*k)+
3691                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3692                 Minimize(result.alpha, (*k)+
3693                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3694               }
3695               k_pixels += virt_width*GetPixelChannels(image);
3696             }
3697             /* repeat with the just processed pixels of this row */
3698             k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3699             k_pixels = q-offx*GetPixelChannels(image);
3700               for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3701                 if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
3702                 if ( IsNan(*k) ) continue;
3703                 Minimize(result.red,     (*k)+
3704                   GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3705                 Minimize(result.green,   (*k)+
3706                   GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3707                 Minimize(result.blue,    (*k)+
3708                   GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3709                 if ( image->colorspace == CMYKColorspace)
3710                   Minimize(result.black,   (*k)+
3711                     GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3712                 Minimize(result.alpha, (*k)+
3713                   GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3714               }
3715             break;
3716         case VoronoiMorphology:
3717             /* Apply Distance to 'Matte' channel, coping the closest color.
3718             **
3719             ** This is experimental, and realy the 'alpha' component should
3720             ** be completely separate 'masking' channel.
3721             */
3722             k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3723             k_pixels = p;
3724             for (v=offy; v < (ssize_t) kernel->height; v++) {
3725               for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3726                 if ( IsNan(*k) ) continue;
3727                 if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3728                   {
3729                     GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
3730                       &result);
3731                     result.alpha += *k;
3732                   }
3733               }
3734               k_pixels += virt_width*GetPixelChannels(image);
3735             }
3736             /* repeat with the just processed pixels of this row */
3737             k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3738             k_pixels = q-offx*GetPixelChannels(image);
3739               for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3740                 if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
3741                 if ( IsNan(*k) ) continue;
3742                 if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3743                   {
3744                     GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
3745                       &result);
3746                     result.alpha += *k;
3747                   }
3748               }
3749             break;
3750         default:
3751           /* result directly calculated or assigned */
3752           break;
3753       }
3754       /* Assign the resulting pixel values - Clamping Result */
3755       switch ( method ) {
3756         case VoronoiMorphology:
3757           SetPixelInfoPixel(image,&result,q);
3758           break;
3759         default:
3760           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3761             SetPixelRed(image,ClampToQuantum(result.red),q);
3762           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3763             SetPixelGreen(image,ClampToQuantum(result.green),q);
3764           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3765             SetPixelBlue(image,ClampToQuantum(result.blue),q);
3766           if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
3767               (image->colorspace == CMYKColorspace))
3768             SetPixelBlack(image,ClampToQuantum(result.black),q);
3769           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
3770               (image->matte == MagickTrue))
3771             SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
3772           break;
3773       }
3774       /* Count up changed pixels */
3775       if (   (GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q))
3776           || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q))
3777           || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q))
3778           || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q))
3779           || ((image->colorspace == CMYKColorspace) &&
3780               (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
3781         changed++;  /* The pixel was changed in some way! */
3782
3783       p-=GetPixelChannels(image); /* go backward through pixel buffers */
3784       q-=GetPixelChannels(image);
3785     } /* x */
3786     if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3787       status=MagickFalse;
3788     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3789       if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
3790                 == MagickFalse )
3791         status=MagickFalse;
3792
3793   } /* y */
3794
3795   auth_view=DestroyCacheView(auth_view);
3796   virt_view=DestroyCacheView(virt_view);
3797   return(status ? (ssize_t) changed : -1);
3798 }
3799
3800 /* Apply a Morphology by calling one of the above low level primitive
3801 ** application functions.  This function handles any iteration loops,
3802 ** composition or re-iteration of results, and compound morphology methods
3803 ** that is based on multiple low-level (staged) morphology methods.
3804 **
3805 ** Basically this provides the complex glue between the requested morphology
3806 ** method and raw low-level implementation (above).
3807 */
3808 MagickPrivate Image *MorphologyApply(const Image *image,
3809   const MorphologyMethod method, const ssize_t iterations,
3810   const KernelInfo *kernel, const CompositeOperator compose,const double bias,
3811   ExceptionInfo *exception)
3812 {
3813   CompositeOperator
3814     curr_compose;
3815
3816   Image
3817     *curr_image,    /* Image we are working with or iterating */
3818     *work_image,    /* secondary image for primitive iteration */
3819     *save_image,    /* saved image - for 'edge' method only */
3820     *rslt_image;    /* resultant image - after multi-kernel handling */
3821
3822   KernelInfo
3823     *reflected_kernel, /* A reflected copy of the kernel (if needed) */
3824     *norm_kernel,      /* the current normal un-reflected kernel */
3825     *rflt_kernel,      /* the current reflected kernel (if needed) */
3826     *this_kernel;      /* the kernel being applied */
3827
3828   MorphologyMethod
3829     primitive;      /* the current morphology primitive being applied */
3830
3831   CompositeOperator
3832     rslt_compose;   /* multi-kernel compose method for results to use */
3833
3834   MagickBooleanType
3835     special,        /* do we use a direct modify function? */
3836     verbose;        /* verbose output of results */
3837
3838   size_t
3839     method_loop,    /* Loop 1: number of compound method iterations (norm 1) */
3840     method_limit,   /*         maximum number of compound method iterations */
3841     kernel_number,  /* Loop 2: the kernel number being applied */
3842     stage_loop,     /* Loop 3: primitive loop for compound morphology */
3843     stage_limit,    /*         how many primitives are in this compound */
3844     kernel_loop,    /* Loop 4: iterate the kernel over image */
3845     kernel_limit,   /*         number of times to iterate kernel */
3846     count,          /* total count of primitive steps applied */
3847     kernel_changed, /* total count of changed using iterated kernel */
3848     method_changed; /* total count of changed over method iteration */
3849
3850   ssize_t
3851     changed;        /* number pixels changed by last primitive operation */
3852
3853   char
3854     v_info[80];
3855
3856   assert(image != (Image *) NULL);
3857   assert(image->signature == MagickSignature);
3858   assert(kernel != (KernelInfo *) NULL);
3859   assert(kernel->signature == MagickSignature);
3860   assert(exception != (ExceptionInfo *) NULL);
3861   assert(exception->signature == MagickSignature);
3862
3863   count = 0;      /* number of low-level morphology primitives performed */
3864   if ( iterations == 0 )
3865     return((Image *)NULL);   /* null operation - nothing to do! */
3866
3867   kernel_limit = (size_t) iterations;
3868   if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
3869      kernel_limit = image->columns>image->rows ? image->columns : image->rows;
3870
3871   verbose = IsStringTrue(GetImageArtifact(image,"verbose"));
3872
3873   /* initialise for cleanup */
3874   curr_image = (Image *) image;
3875   curr_compose = image->compose;
3876   (void) curr_compose;
3877   work_image = save_image = rslt_image = (Image *) NULL;
3878   reflected_kernel = (KernelInfo *) NULL;
3879
3880   /* Initialize specific methods
3881    * + which loop should use the given iteratations
3882    * + how many primitives make up the compound morphology
3883    * + multi-kernel compose method to use (by default)
3884    */
3885   method_limit = 1;       /* just do method once, unless otherwise set */
3886   stage_limit = 1;        /* assume method is not a compound */
3887   special = MagickFalse;   /* assume it is NOT a direct modify primitive */
3888   rslt_compose = compose; /* and we are composing multi-kernels as given */
3889   switch( method ) {
3890     case SmoothMorphology:  /* 4 primitive compound morphology */
3891       stage_limit = 4;
3892       break;
3893     case OpenMorphology:    /* 2 primitive compound morphology */
3894     case OpenIntensityMorphology:
3895     case TopHatMorphology:
3896     case CloseMorphology:
3897     case CloseIntensityMorphology:
3898     case BottomHatMorphology:
3899     case EdgeMorphology:
3900       stage_limit = 2;
3901       break;
3902     case HitAndMissMorphology:
3903       rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
3904       /* FALL THUR */
3905     case ThinningMorphology:
3906     case ThickenMorphology:
3907       method_limit = kernel_limit;  /* iterate the whole method */
3908       kernel_limit = 1;             /* do not do kernel iteration  */
3909       break;
3910     case DistanceMorphology:
3911     case VoronoiMorphology:
3912       special = MagickTrue;         /* use special direct primative */
3913       break;
3914     default:
3915       break;
3916   }
3917
3918   /* Apply special methods with special requirments
3919   ** For example, single run only, or post-processing requirements
3920   */
3921   if ( special == MagickTrue )
3922     {
3923       rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3924       if (rslt_image == (Image *) NULL)
3925         goto error_cleanup;
3926       if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3927         goto error_cleanup;
3928
3929       changed = MorphologyPrimitiveDirect(rslt_image, method,
3930          kernel, exception);
3931
3932       if ( IfMagickTrue(verbose) )
3933         (void) (void) FormatLocaleFile(stderr,
3934           "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
3935           CommandOptionToMnemonic(MagickMorphologyOptions, method),
3936           1.0,0.0,1.0, (double) changed);
3937
3938       if ( changed < 0 )
3939         goto error_cleanup;
3940
3941       if ( method == VoronoiMorphology ) {
3942         /* Preserve the alpha channel of input image - but turned off */
3943         (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3944           exception);
3945         (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
3946           MagickTrue,0,0,exception);
3947         (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3948           exception);
3949       }
3950       goto exit_cleanup;
3951     }
3952
3953   /* Handle user (caller) specified multi-kernel composition method */
3954   if ( compose != UndefinedCompositeOp )
3955     rslt_compose = compose;  /* override default composition for method */
3956   if ( rslt_compose == UndefinedCompositeOp )
3957     rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
3958
3959   /* Some methods require a reflected kernel to use with primitives.
3960    * Create the reflected kernel for those methods. */
3961   switch ( method ) {
3962     case CorrelateMorphology:
3963     case CloseMorphology:
3964     case CloseIntensityMorphology:
3965     case BottomHatMorphology:
3966     case SmoothMorphology:
3967       reflected_kernel = CloneKernelInfo(kernel);
3968       if (reflected_kernel == (KernelInfo *) NULL)
3969         goto error_cleanup;
3970       RotateKernelInfo(reflected_kernel,180);
3971       break;
3972     default:
3973       break;
3974   }
3975
3976   /* Loops around more primitive morpholgy methods
3977   **  erose, dilate, open, close, smooth, edge, etc...
3978   */
3979   /* Loop 1:  iterate the compound method */
3980   method_loop = 0;
3981   method_changed = 1;
3982   while ( method_loop < method_limit && method_changed > 0 ) {
3983     method_loop++;
3984     method_changed = 0;
3985
3986     /* Loop 2:  iterate over each kernel in a multi-kernel list */
3987     norm_kernel = (KernelInfo *) kernel;
3988     this_kernel = (KernelInfo *) kernel;
3989     rflt_kernel = reflected_kernel;
3990
3991     kernel_number = 0;
3992     while ( norm_kernel != NULL ) {
3993
3994       /* Loop 3: Compound Morphology Staging - Select Primative to apply */
3995       stage_loop = 0;          /* the compound morphology stage number */
3996       while ( stage_loop < stage_limit ) {
3997         stage_loop++;   /* The stage of the compound morphology */
3998
3999         /* Select primitive morphology for this stage of compound method */
4000         this_kernel = norm_kernel; /* default use unreflected kernel */
4001         primitive = method;        /* Assume method is a primitive */
4002         switch( method ) {
4003           case ErodeMorphology:      /* just erode */
4004           case EdgeInMorphology:     /* erode and image difference */
4005             primitive = ErodeMorphology;
4006             break;
4007           case DilateMorphology:     /* just dilate */
4008           case EdgeOutMorphology:    /* dilate and image difference */
4009             primitive = DilateMorphology;
4010             break;
4011           case OpenMorphology:       /* erode then dialate */
4012           case TopHatMorphology:     /* open and image difference */
4013             primitive = ErodeMorphology;
4014             if ( stage_loop == 2 )
4015               primitive = DilateMorphology;
4016             break;
4017           case OpenIntensityMorphology:
4018             primitive = ErodeIntensityMorphology;
4019             if ( stage_loop == 2 )
4020               primitive = DilateIntensityMorphology;
4021             break;
4022           case CloseMorphology:      /* dilate, then erode */
4023           case BottomHatMorphology:  /* close and image difference */
4024             this_kernel = rflt_kernel; /* use the reflected kernel */
4025             primitive = DilateMorphology;
4026             if ( stage_loop == 2 )
4027               primitive = ErodeMorphology;
4028             break;
4029           case CloseIntensityMorphology:
4030             this_kernel = rflt_kernel; /* use the reflected kernel */
4031             primitive = DilateIntensityMorphology;
4032             if ( stage_loop == 2 )
4033               primitive = ErodeIntensityMorphology;
4034             break;
4035           case SmoothMorphology:         /* open, close */
4036             switch ( stage_loop ) {
4037               case 1: /* start an open method, which starts with Erode */
4038                 primitive = ErodeMorphology;
4039                 break;
4040               case 2:  /* now Dilate the Erode */
4041                 primitive = DilateMorphology;
4042                 break;
4043               case 3:  /* Reflect kernel a close */
4044                 this_kernel = rflt_kernel; /* use the reflected kernel */
4045                 primitive = DilateMorphology;
4046                 break;
4047               case 4:  /* Finish the Close */
4048                 this_kernel = rflt_kernel; /* use the reflected kernel */
4049                 primitive = ErodeMorphology;
4050                 break;
4051             }
4052             break;
4053           case EdgeMorphology:        /* dilate and erode difference */
4054             primitive = DilateMorphology;
4055             if ( stage_loop == 2 ) {
4056               save_image = curr_image;      /* save the image difference */
4057               curr_image = (Image *) image;
4058               primitive = ErodeMorphology;
4059             }
4060             break;
4061           case CorrelateMorphology:
4062             /* A Correlation is a Convolution with a reflected kernel.
4063             ** However a Convolution is a weighted sum using a reflected
4064             ** kernel.  It may seem stange to convert a Correlation into a
4065             ** Convolution as the Correlation is the simplier method, but
4066             ** Convolution is much more commonly used, and it makes sense to
4067             ** implement it directly so as to avoid the need to duplicate the
4068             ** kernel when it is not required (which is typically the
4069             ** default).
4070             */
4071             this_kernel = rflt_kernel; /* use the reflected kernel */
4072             primitive = ConvolveMorphology;
4073             break;
4074           default:
4075             break;
4076         }
4077         assert( this_kernel != (KernelInfo *) NULL );
4078
4079         /* Extra information for debugging compound operations */
4080         if ( IfMagickTrue(verbose) ) {
4081           if ( stage_limit > 1 )
4082             (void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
4083              CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
4084              method_loop,(double) stage_loop);
4085           else if ( primitive != method )
4086             (void) FormatLocaleString(v_info, MaxTextExtent, "%s:%.20g -> ",
4087               CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
4088               method_loop);
4089           else
4090             v_info[0] = '\0';
4091         }
4092
4093         /* Loop 4: Iterate the kernel with primitive */
4094         kernel_loop = 0;
4095         kernel_changed = 0;
4096         changed = 1;
4097         while ( kernel_loop < kernel_limit && changed > 0 ) {
4098           kernel_loop++;     /* the iteration of this kernel */
4099
4100           /* Create a clone as the destination image, if not yet defined */
4101           if ( work_image == (Image *) NULL )
4102             {
4103               work_image=CloneImage(image,0,0,MagickTrue,exception);
4104               if (work_image == (Image *) NULL)
4105                 goto error_cleanup;
4106               if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
4107                 goto error_cleanup;
4108               /* work_image->type=image->type; ??? */
4109             }
4110
4111           /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
4112           count++;
4113           changed = MorphologyPrimitive(curr_image, work_image, primitive,
4114                        this_kernel, bias, exception);
4115
4116           if ( IfMagickTrue(verbose) ) {
4117             if ( kernel_loop > 1 )
4118               (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
4119             (void) (void) FormatLocaleFile(stderr,
4120               "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
4121               v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
4122               primitive),(this_kernel == rflt_kernel ) ? "*" : "",
4123               (double) (method_loop+kernel_loop-1),(double) kernel_number,
4124               (double) count,(double) changed);
4125           }
4126           if ( changed < 0 )
4127             goto error_cleanup;
4128           kernel_changed += changed;
4129           method_changed += changed;
4130
4131           /* prepare next loop */
4132           { Image *tmp = work_image;   /* swap images for iteration */
4133             work_image = curr_image;
4134             curr_image = tmp;
4135           }
4136           if ( work_image == image )
4137             work_image = (Image *) NULL; /* replace input 'image' */
4138
4139         } /* End Loop 4: Iterate the kernel with primitive */
4140
4141         if ( IfMagickTrue(verbose) && kernel_changed != (size_t)changed )
4142           (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
4143         if ( IfMagickTrue(verbose) && stage_loop < stage_limit )
4144           (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
4145
4146 #if 0
4147     (void) FormatLocaleFile(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
4148     (void) FormatLocaleFile(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
4149     (void) FormatLocaleFile(stderr, "      work =0x%lx\n", (unsigned long)work_image);
4150     (void) FormatLocaleFile(stderr, "      save =0x%lx\n", (unsigned long)save_image);
4151     (void) FormatLocaleFile(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
4152 #endif
4153
4154       } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
4155
4156       /*  Final Post-processing for some Compound Methods
4157       **
4158       ** The removal of any 'Sync' channel flag in the Image Compositon
4159       ** below ensures the methematical compose method is applied in a
4160       ** purely mathematical way, and only to the selected channels.
4161       ** Turn off SVG composition 'alpha blending'.
4162       */
4163       switch( method ) {
4164         case EdgeOutMorphology:
4165         case EdgeInMorphology:
4166         case TopHatMorphology:
4167         case BottomHatMorphology:
4168           if ( IfMagickTrue(verbose) )
4169             (void) FormatLocaleFile(stderr,
4170               "\n%s: Difference with original image",CommandOptionToMnemonic(
4171               MagickMorphologyOptions, method) );
4172           (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
4173             MagickTrue,0,0,exception);
4174           break;
4175         case EdgeMorphology:
4176           if ( IfMagickTrue(verbose) )
4177             (void) FormatLocaleFile(stderr,
4178               "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
4179               MagickMorphologyOptions, method) );
4180           (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
4181             MagickTrue,0,0,exception);
4182           save_image = DestroyImage(save_image); /* finished with save image */
4183           break;
4184         default:
4185           break;
4186       }
4187
4188       /* multi-kernel handling:  re-iterate, or compose results */
4189       if ( kernel->next == (KernelInfo *) NULL )
4190         rslt_image = curr_image;   /* just return the resulting image */
4191       else if ( rslt_compose == NoCompositeOp )
4192         { if ( IfMagickTrue(verbose) ) {
4193             if ( this_kernel->next != (KernelInfo *) NULL )
4194               (void) FormatLocaleFile(stderr, " (re-iterate)");
4195             else
4196               (void) FormatLocaleFile(stderr, " (done)");
4197           }
4198           rslt_image = curr_image; /* return result, and re-iterate */
4199         }
4200       else if ( rslt_image == (Image *) NULL)
4201         { if ( IfMagickTrue(verbose) )
4202             (void) FormatLocaleFile(stderr, " (save for compose)");
4203           rslt_image = curr_image;
4204           curr_image = (Image *) image;  /* continue with original image */
4205         }
4206       else
4207         { /* Add the new 'current' result to the composition
4208           **
4209           ** The removal of any 'Sync' channel flag in the Image Compositon
4210           ** below ensures the methematical compose method is applied in a
4211           ** purely mathematical way, and only to the selected channels.
4212           ** IE: Turn off SVG composition 'alpha blending'.
4213           */
4214           if ( IfMagickTrue(verbose) )
4215             (void) FormatLocaleFile(stderr, " (compose \"%s\")",
4216               CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
4217           (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
4218             0,0,exception);
4219           curr_image = DestroyImage(curr_image);
4220           curr_image = (Image *) image;  /* continue with original image */
4221         }
4222       if ( IfMagickTrue(verbose) )
4223         (void) FormatLocaleFile(stderr, "\n");
4224
4225       /* loop to the next kernel in a multi-kernel list */
4226       norm_kernel = norm_kernel->next;
4227       if ( rflt_kernel != (KernelInfo *) NULL )
4228         rflt_kernel = rflt_kernel->next;
4229       kernel_number++;
4230     } /* End Loop 2: Loop over each kernel */
4231
4232   } /* End Loop 1: compound method interation */
4233
4234   goto exit_cleanup;
4235
4236   /* Yes goto's are bad, but it makes cleanup lot more efficient */
4237 error_cleanup:
4238   if ( curr_image == rslt_image )
4239     curr_image = (Image *) NULL;
4240   if ( rslt_image != (Image *) NULL )
4241     rslt_image = DestroyImage(rslt_image);
4242 exit_cleanup:
4243   if ( curr_image == rslt_image || curr_image == image )
4244     curr_image = (Image *) NULL;
4245   if ( curr_image != (Image *) NULL )
4246     curr_image = DestroyImage(curr_image);
4247   if ( work_image != (Image *) NULL )
4248     work_image = DestroyImage(work_image);
4249   if ( save_image != (Image *) NULL )
4250     save_image = DestroyImage(save_image);
4251   if ( reflected_kernel != (KernelInfo *) NULL )
4252     reflected_kernel = DestroyKernelInfo(reflected_kernel);
4253   return(rslt_image);
4254 }
4255
4256 \f
4257 /*
4258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4259 %                                                                             %
4260 %                                                                             %
4261 %                                                                             %
4262 %     M o r p h o l o g y I m a g e                                           %
4263 %                                                                             %
4264 %                                                                             %
4265 %                                                                             %
4266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4267 %
4268 %  MorphologyImage() applies a user supplied kernel to the image
4269 %  according to the given mophology method.
4270 %
4271 %  This function applies any and all user defined settings before calling
4272 %  the above internal function MorphologyApply().
4273 %
4274 %  User defined settings include...
4275 %    * Output Bias for Convolution and correlation ("-define convolve:bias=??")
4276 %    * Kernel Scale/normalize settings             ("-define convolve:scale=??")
4277 %      This can also includes the addition of a scaled unity kernel.
4278 %    * Show Kernel being applied                   ("-define showkernel=1")
4279 %
4280 %  Other operators that do not want user supplied options interfering,
4281 %  especially "convolve:bias" and "showkernel" should use MorphologyApply()
4282 %  directly.
4283 %
4284 %  The format of the MorphologyImage method is:
4285 %
4286 %      Image *MorphologyImage(const Image *image,MorphologyMethod method,
4287 %        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
4288 %
4289 %  A description of each parameter follows:
4290 %
4291 %    o image: the image.
4292 %
4293 %    o method: the morphology method to be applied.
4294 %
4295 %    o iterations: apply the operation this many times (or no change).
4296 %                  A value of -1 means loop until no change found.
4297 %                  How this is applied may depend on the morphology method.
4298 %                  Typically this is a value of 1.
4299 %
4300 %    o kernel: An array of double representing the morphology kernel.
4301 %              Warning: kernel may be normalized for the Convolve method.
4302 %
4303 %    o exception: return any errors or warnings in this structure.
4304 %
4305 */
4306 MagickExport Image *MorphologyImage(const Image *image,
4307   const MorphologyMethod method,const ssize_t iterations,
4308   const KernelInfo *kernel,ExceptionInfo *exception)
4309 {
4310   KernelInfo
4311     *curr_kernel;
4312
4313   CompositeOperator
4314     compose;
4315
4316   Image
4317     *morphology_image;
4318
4319   double
4320     bias;
4321
4322   curr_kernel = (KernelInfo *) kernel;
4323   bias=0.0;
4324   compose = UndefinedCompositeOp;  /* use default for method */
4325
4326   /* Apply Convolve/Correlate Normalization and Scaling Factors.
4327    * This is done BEFORE the ShowKernelInfo() function is called so that
4328    * users can see the results of the 'option:convolve:scale' option.
4329    */
4330   if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
4331       const char
4332         *artifact;
4333
4334       /* Get the bias value as it will be needed */
4335       artifact = GetImageArtifact(image,"convolve:bias");
4336       if ( artifact != (const char *) NULL) {
4337         if (IfMagickFalse(IsGeometry(artifact)))
4338           (void) ThrowMagickException(exception,GetMagickModule(),
4339                OptionWarning,"InvalidSetting","'%s' '%s'",
4340                "convolve:bias",artifact);
4341         else
4342           bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
4343       }
4344
4345       /* Scale kernel according to user wishes */
4346       artifact = GetImageArtifact(image,"convolve:scale");
4347       if ( artifact != (const char *)NULL ) {
4348         if (IfMagickFalse(IsGeometry(artifact)))
4349           (void) ThrowMagickException(exception,GetMagickModule(),
4350                OptionWarning,"InvalidSetting","'%s' '%s'",
4351                "convolve:scale",artifact);
4352         else {
4353           if ( curr_kernel == kernel )
4354             curr_kernel = CloneKernelInfo(kernel);
4355           if (curr_kernel == (KernelInfo *) NULL)
4356             return((Image *) NULL);
4357           ScaleGeometryKernelInfo(curr_kernel, artifact);
4358         }
4359       }
4360     }
4361
4362   /* display the (normalized) kernel via stderr */
4363   if ( IfStringTrue(GetImageArtifact(image,"showkernel"))
4364     || IfStringTrue(GetImageArtifact(image,"convolve:showkernel"))
4365     || IfStringTrue(GetImageArtifact(image,"morphology:showkernel")) )
4366     ShowKernelInfo(curr_kernel);
4367
4368   /* Override the default handling of multi-kernel morphology results
4369    * If 'Undefined' use the default method
4370    * If 'None' (default for 'Convolve') re-iterate previous result
4371    * Otherwise merge resulting images using compose method given.
4372    * Default for 'HitAndMiss' is 'Lighten'.
4373    */
4374   { const char
4375       *artifact;
4376     ssize_t
4377       parse;
4378
4379     artifact = GetImageArtifact(image,"morphology:compose");
4380     if ( artifact != (const char *) NULL) {
4381       parse=ParseCommandOption(MagickComposeOptions,
4382         MagickFalse,artifact);
4383       if ( parse < 0 )
4384         (void) ThrowMagickException(exception,GetMagickModule(),
4385              OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
4386              "morphology:compose",artifact);
4387       else
4388         compose=(CompositeOperator)parse;
4389     }
4390   }
4391   /* Apply the Morphology */
4392   morphology_image = MorphologyApply(image,method,iterations,
4393     curr_kernel,compose,bias,exception);
4394
4395   /* Cleanup and Exit */
4396   if ( curr_kernel != kernel )
4397     curr_kernel=DestroyKernelInfo(curr_kernel);
4398   return(morphology_image);
4399 }
4400 \f
4401 /*
4402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4403 %                                                                             %
4404 %                                                                             %
4405 %                                                                             %
4406 +     R o t a t e K e r n e l I n f o                                         %
4407 %                                                                             %
4408 %                                                                             %
4409 %                                                                             %
4410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4411 %
4412 %  RotateKernelInfo() rotates the kernel by the angle given.
4413 %
4414 %  Currently it is restricted to 90 degree angles, of either 1D kernels
4415 %  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
4416 %  It will ignore usless rotations for specific 'named' built-in kernels.
4417 %
4418 %  The format of the RotateKernelInfo method is:
4419 %
4420 %      void RotateKernelInfo(KernelInfo *kernel, double angle)
4421 %
4422 %  A description of each parameter follows:
4423 %
4424 %    o kernel: the Morphology/Convolution kernel
4425 %
4426 %    o angle: angle to rotate in degrees
4427 %
4428 % This function is currently internal to this module only, but can be exported
4429 % to other modules if needed.
4430 */
4431 static void RotateKernelInfo(KernelInfo *kernel, double angle)
4432 {
4433   /* angle the lower kernels first */
4434   if ( kernel->next != (KernelInfo *) NULL)
4435     RotateKernelInfo(kernel->next, angle);
4436
4437   /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
4438   **
4439   ** TODO: expand beyond simple 90 degree rotates, flips and flops
4440   */
4441
4442   /* Modulus the angle */
4443   angle = fmod(angle, 360.0);
4444   if ( angle < 0 )
4445     angle += 360.0;
4446
4447   if ( 337.5 < angle || angle <= 22.5 )
4448     return;   /* Near zero angle - no change! - At least not at this time */
4449
4450   /* Handle special cases */
4451   switch (kernel->type) {
4452     /* These built-in kernels are cylindrical kernels, rotating is useless */
4453     case GaussianKernel:
4454     case DoGKernel:
4455     case LoGKernel:
4456     case DiskKernel:
4457     case PeaksKernel:
4458     case LaplacianKernel:
4459     case ChebyshevKernel:
4460     case ManhattanKernel:
4461     case EuclideanKernel:
4462       return;
4463
4464     /* These may be rotatable at non-90 angles in the future */
4465     /* but simply rotating them in multiples of 90 degrees is useless */
4466     case SquareKernel:
4467     case DiamondKernel:
4468     case PlusKernel:
4469     case CrossKernel:
4470       return;
4471
4472     /* These only allows a +/-90 degree rotation (by transpose) */
4473     /* A 180 degree rotation is useless */
4474     case BlurKernel:
4475       if ( 135.0 < angle && angle <= 225.0 )
4476         return;
4477       if ( 225.0 < angle && angle <= 315.0 )
4478         angle -= 180;
4479       break;
4480
4481     default:
4482       break;
4483   }
4484   /* Attempt rotations by 45 degrees  -- 3x3 kernels only */
4485   if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
4486     {
4487       if ( kernel->width == 3 && kernel->height == 3 )
4488         { /* Rotate a 3x3 square by 45 degree angle */
4489           double t  = kernel->values[0];
4490           kernel->values[0] = kernel->values[3];
4491           kernel->values[3] = kernel->values[6];
4492           kernel->values[6] = kernel->values[7];
4493           kernel->values[7] = kernel->values[8];
4494           kernel->values[8] = kernel->values[5];
4495           kernel->values[5] = kernel->values[2];
4496           kernel->values[2] = kernel->values[1];
4497           kernel->values[1] = t;
4498           /* rotate non-centered origin */
4499           if ( kernel->x != 1 || kernel->y != 1 ) {
4500             ssize_t x,y;
4501             x = (ssize_t) kernel->x-1;
4502             y = (ssize_t) kernel->y-1;
4503                  if ( x == y  ) x = 0;
4504             else if ( x == 0  ) x = -y;
4505             else if ( x == -y ) y = 0;
4506             else if ( y == 0  ) y = x;
4507             kernel->x = (ssize_t) x+1;
4508             kernel->y = (ssize_t) y+1;
4509           }
4510           angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
4511           kernel->angle = fmod(kernel->angle+45.0, 360.0);
4512         }
4513       else
4514         perror("Unable to rotate non-3x3 kernel by 45 degrees");
4515     }
4516   if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
4517     {
4518       if ( kernel->width == 1 || kernel->height == 1 )
4519         { /* Do a transpose of a 1 dimensional kernel,
4520           ** which results in a fast 90 degree rotation of some type.
4521           */
4522           ssize_t
4523             t;
4524           t = (ssize_t) kernel->width;
4525           kernel->width = kernel->height;
4526           kernel->height = (size_t) t;
4527           t = kernel->x;
4528           kernel->x = kernel->y;
4529           kernel->y = t;
4530           if ( kernel->width == 1 ) {
4531             angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
4532             kernel->angle = fmod(kernel->angle+90.0, 360.0);
4533           } else {
4534             angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
4535             kernel->angle = fmod(kernel->angle+270.0, 360.0);
4536           }
4537         }
4538       else if ( kernel->width == kernel->height )
4539         { /* Rotate a square array of values by 90 degrees */
4540           { register size_t
4541               i,j,x,y;
4542             register double
4543               *k,t;
4544             k=kernel->values;
4545             for( i=0, x=kernel->width-1;  i<=x;   i++, x--)
4546               for( j=0, y=kernel->height-1;  j<y;   j++, y--)
4547                 { t                    = k[i+j*kernel->width];
4548                   k[i+j*kernel->width] = k[j+x*kernel->width];
4549                   k[j+x*kernel->width] = k[x+y*kernel->width];
4550                   k[x+y*kernel->width] = k[y+i*kernel->width];
4551                   k[y+i*kernel->width] = t;
4552                 }
4553           }
4554           /* rotate the origin - relative to center of array */
4555           { register ssize_t x,y;
4556             x = (ssize_t) (kernel->x*2-kernel->width+1);
4557             y = (ssize_t) (kernel->y*2-kernel->height+1);
4558             kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4559             kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
4560           }
4561           angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
4562           kernel->angle = fmod(kernel->angle+90.0, 360.0);
4563         }
4564       else
4565         perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
4566     }
4567   if ( 135.0 < angle && angle <= 225.0 )
4568     {
4569       /* For a 180 degree rotation - also know as a reflection
4570        * This is actually a very very common operation!
4571        * Basically all that is needed is a reversal of the kernel data!
4572        * And a reflection of the origon
4573        */
4574       double
4575         t;
4576
4577       register double
4578         *k;
4579
4580       ssize_t
4581         i,
4582         j;
4583
4584       k=kernel->values;
4585       j=(ssize_t) (kernel->width*kernel->height-1);
4586       for (i=0;  i < j;  i++, j--)
4587         t=k[i],  k[i]=k[j],  k[j]=t;
4588
4589       kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
4590       kernel->y = (ssize_t) kernel->height - kernel->y - 1;
4591       angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
4592       kernel->angle = fmod(kernel->angle+180.0, 360.0);
4593     }
4594   /* At this point angle should at least between -45 (315) and +45 degrees
4595    * In the future some form of non-orthogonal angled rotates could be
4596    * performed here, posibily with a linear kernel restriction.
4597    */
4598
4599   return;
4600 }
4601 \f
4602 /*
4603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4604 %                                                                             %
4605 %                                                                             %
4606 %                                                                             %
4607 %     S c a l e G e o m e t r y K e r n e l I n f o                           %
4608 %                                                                             %
4609 %                                                                             %
4610 %                                                                             %
4611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4612 %
4613 %  ScaleGeometryKernelInfo() takes a geometry argument string, typically
4614 %  provided as a  "-set option:convolve:scale {geometry}" user setting,
4615 %  and modifies the kernel according to the parsed arguments of that setting.
4616 %
4617 %  The first argument (and any normalization flags) are passed to
4618 %  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
4619 %  is then passed to UnityAddKernelInfo() to add a scled unity kernel
4620 %  into the scaled/normalized kernel.
4621 %
4622 %  The format of the ScaleGeometryKernelInfo method is:
4623 %
4624 %      void ScaleGeometryKernelInfo(KernelInfo *kernel,
4625 %        const double scaling_factor,const MagickStatusType normalize_flags)
4626 %
4627 %  A description of each parameter follows:
4628 %
4629 %    o kernel: the Morphology/Convolution kernel to modify
4630 %
4631 %    o geometry:
4632 %             The geometry string to parse, typically from the user provided
4633 %             "-set option:convolve:scale {geometry}" setting.
4634 %
4635 */
4636 MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
4637      const char *geometry)
4638 {
4639   //GeometryFlags
4640   MagickStatusType
4641     flags;
4642
4643   GeometryInfo
4644     args;
4645
4646   SetGeometryInfo(&args);
4647   flags = ParseGeometry(geometry, &args);
4648
4649 #if 0
4650   /* For Debugging Geometry Input */
4651   (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4652        flags, args.rho, args.sigma, args.xi, args.psi );
4653 #endif
4654
4655   if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
4656     args.rho *= 0.01,  args.sigma *= 0.01;
4657
4658   if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
4659     args.rho = 1.0;
4660   if ( (flags & SigmaValue) == 0 )
4661     args.sigma = 0.0;
4662
4663   /* Scale/Normalize the input kernel */
4664   ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
4665
4666   /* Add Unity Kernel, for blending with original */
4667   if ( (flags & SigmaValue) != 0 )
4668     UnityAddKernelInfo(kernel, args.sigma);
4669
4670   return;
4671 }
4672 /*
4673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4674 %                                                                             %
4675 %                                                                             %
4676 %                                                                             %
4677 %     S c a l e K e r n e l I n f o                                           %
4678 %                                                                             %
4679 %                                                                             %
4680 %                                                                             %
4681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4682 %
4683 %  ScaleKernelInfo() scales the given kernel list by the given amount, with or
4684 %  without normalization of the sum of the kernel values (as per given flags).
4685 %
4686 %  By default (no flags given) the values within the kernel is scaled
4687 %  directly using given scaling factor without change.
4688 %
4689 %  If either of the two 'normalize_flags' are given the kernel will first be
4690 %  normalized and then further scaled by the scaling factor value given.
4691 %
4692 %  Kernel normalization ('normalize_flags' given) is designed to ensure that
4693 %  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
4694 %  morphology methods will fall into -1.0 to +1.0 range.  Note that for
4695 %  non-HDRI versions of IM this may cause images to have any negative results
4696 %  clipped, unless some 'bias' is used.
4697 %
4698 %  More specifically.  Kernels which only contain positive values (such as a
4699 %  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
4700 %  ensuring a 0.0 to +1.0 output range for non-HDRI images.
4701 %
4702 %  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
4703 %  the kernel will be scaled by the absolute of the sum of kernel values, so
4704 %  that it will generally fall within the +/- 1.0 range.
4705 %
4706 %  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
4707 %  will be scaled by just the sum of the postive values, so that its output
4708 %  range will again fall into the  +/- 1.0 range.
4709 %
4710 %  For special kernels designed for locating shapes using 'Correlate', (often
4711 %  only containing +1 and -1 values, representing foreground/brackground
4712 %  matching) a special normalization method is provided to scale the positive
4713 %  values separately to those of the negative values, so the kernel will be
4714 %  forced to become a zero-sum kernel better suited to such searches.
4715 %
4716 %  WARNING: Correct normalization of the kernel assumes that the '*_range'
4717 %  attributes within the kernel structure have been correctly set during the
4718 %  kernels creation.
4719 %
4720 %  NOTE: The values used for 'normalize_flags' have been selected specifically
4721 %  to match the use of geometry options, so that '!' means NormalizeValue, '^'
4722 %  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
4723 %
4724 %  The format of the ScaleKernelInfo method is:
4725 %
4726 %      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
4727 %               const MagickStatusType normalize_flags )
4728 %
4729 %  A description of each parameter follows:
4730 %
4731 %    o kernel: the Morphology/Convolution kernel
4732 %
4733 %    o scaling_factor:
4734 %             multiply all values (after normalization) by this factor if not
4735 %             zero.  If the kernel is normalized regardless of any flags.
4736 %
4737 %    o normalize_flags:
4738 %             GeometryFlags defining normalization method to use.
4739 %             specifically: NormalizeValue, CorrelateNormalizeValue,
4740 %                           and/or PercentValue
4741 %
4742 */
4743 MagickExport void ScaleKernelInfo(KernelInfo *kernel,
4744   const double scaling_factor,const GeometryFlags normalize_flags)
4745 {
4746   register ssize_t
4747     i;
4748
4749   register double
4750     pos_scale,
4751     neg_scale;
4752
4753   /* do the other kernels in a multi-kernel list first */
4754   if ( kernel->next != (KernelInfo *) NULL)
4755     ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
4756
4757   /* Normalization of Kernel */
4758   pos_scale = 1.0;
4759   if ( (normalize_flags&NormalizeValue) != 0 ) {
4760     if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4761       /* non-zero-summing kernel (generally positive) */
4762       pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4763     else
4764       /* zero-summing kernel */
4765       pos_scale = kernel->positive_range;
4766   }
4767   /* Force kernel into a normalized zero-summing kernel */
4768   if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4769     pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4770                  ? kernel->positive_range : 1.0;
4771     neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4772                  ? -kernel->negative_range : 1.0;
4773   }
4774   else
4775     neg_scale = pos_scale;
4776
4777   /* finialize scaling_factor for positive and negative components */
4778   pos_scale = scaling_factor/pos_scale;
4779   neg_scale = scaling_factor/neg_scale;
4780
4781   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4782     if ( ! IsNan(kernel->values[i]) )
4783       kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4784
4785   /* convolution output range */
4786   kernel->positive_range *= pos_scale;
4787   kernel->negative_range *= neg_scale;
4788   /* maximum and minimum values in kernel */
4789   kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4790   kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4791
4792   /* swap kernel settings if user's scaling factor is negative */
4793   if ( scaling_factor < MagickEpsilon ) {
4794     double t;
4795     t = kernel->positive_range;
4796     kernel->positive_range = kernel->negative_range;
4797     kernel->negative_range = t;
4798     t = kernel->maximum;
4799     kernel->maximum = kernel->minimum;
4800     kernel->minimum = 1;
4801   }
4802
4803   return;
4804 }
4805 \f
4806 /*
4807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4808 %                                                                             %
4809 %                                                                             %
4810 %                                                                             %
4811 %     S h o w K e r n e l I n f o                                             %
4812 %                                                                             %
4813 %                                                                             %
4814 %                                                                             %
4815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4816 %
4817 %  ShowKernelInfo() outputs the details of the given kernel defination to
4818 %  standard error, generally due to a users 'showkernel' option request.
4819 %
4820 %  The format of the ShowKernel method is:
4821 %
4822 %      void ShowKernelInfo(const KernelInfo *kernel)
4823 %
4824 %  A description of each parameter follows:
4825 %
4826 %    o kernel: the Morphology/Convolution kernel
4827 %
4828 */
4829 MagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
4830 {
4831   const KernelInfo
4832     *k;
4833
4834   size_t
4835     c, i, u, v;
4836
4837   for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
4838
4839     (void) FormatLocaleFile(stderr, "Kernel");
4840     if ( kernel->next != (KernelInfo *) NULL )
4841       (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
4842     (void) FormatLocaleFile(stderr, " \"%s",
4843           CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4844     if ( fabs(k->angle) >= MagickEpsilon )
4845       (void) FormatLocaleFile(stderr, "@%lg", k->angle);
4846     (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
4847       k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
4848     (void) FormatLocaleFile(stderr,
4849           " with values from %.*lg to %.*lg\n",
4850           GetMagickPrecision(), k->minimum,
4851           GetMagickPrecision(), k->maximum);
4852     (void) FormatLocaleFile(stderr, "Forming a output range from %.*lg to %.*lg",
4853           GetMagickPrecision(), k->negative_range,
4854           GetMagickPrecision(), k->positive_range);
4855     if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
4856       (void) FormatLocaleFile(stderr, " (Zero-Summing)\n");
4857     else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
4858       (void) FormatLocaleFile(stderr, " (Normalized)\n");
4859     else
4860       (void) FormatLocaleFile(stderr, " (Sum %.*lg)\n",
4861           GetMagickPrecision(), k->positive_range+k->negative_range);
4862     for (i=v=0; v < k->height; v++) {
4863       (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
4864       for (u=0; u < k->width; u++, i++)
4865         if ( IsNan(k->values[i]) )
4866           (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
4867         else
4868           (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
4869               GetMagickPrecision(), k->values[i]);
4870       (void) FormatLocaleFile(stderr,"\n");
4871     }
4872   }
4873 }
4874 \f
4875 /*
4876 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4877 %                                                                             %
4878 %                                                                             %
4879 %                                                                             %
4880 %     U n i t y A d d K e r n a l I n f o                                     %
4881 %                                                                             %
4882 %                                                                             %
4883 %                                                                             %
4884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4885 %
4886 %  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
4887 %  to the given pre-scaled and normalized Kernel.  This in effect adds that
4888 %  amount of the original image into the resulting convolution kernel.  This
4889 %  value is usually provided by the user as a percentage value in the
4890 %  'convolve:scale' setting.
4891 %
4892 %  The resulting effect is to convert the defined kernels into blended
4893 %  soft-blurs, unsharp kernels or into sharpening kernels.
4894 %
4895 %  The format of the UnityAdditionKernelInfo method is:
4896 %
4897 %      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
4898 %
4899 %  A description of each parameter follows:
4900 %
4901 %    o kernel: the Morphology/Convolution kernel
4902 %
4903 %    o scale:
4904 %             scaling factor for the unity kernel to be added to
4905 %             the given kernel.
4906 %
4907 */
4908 MagickExport void UnityAddKernelInfo(KernelInfo *kernel,
4909   const double scale)
4910 {
4911   /* do the other kernels in a multi-kernel list first */
4912   if ( kernel->next != (KernelInfo *) NULL)
4913     UnityAddKernelInfo(kernel->next, scale);
4914
4915   /* Add the scaled unity kernel to the existing kernel */
4916   kernel->values[kernel->x+kernel->y*kernel->width] += scale;
4917   CalcKernelMetaData(kernel);  /* recalculate the meta-data */
4918
4919   return;
4920 }
4921 \f
4922 /*
4923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4924 %                                                                             %
4925 %                                                                             %
4926 %                                                                             %
4927 %     Z e r o K e r n e l N a n s                                             %
4928 %                                                                             %
4929 %                                                                             %
4930 %                                                                             %
4931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4932 %
4933 %  ZeroKernelNans() replaces any special 'nan' value that may be present in
4934 %  the kernel with a zero value.  This is typically done when the kernel will
4935 %  be used in special hardware (GPU) convolution processors, to simply
4936 %  matters.
4937 %
4938 %  The format of the ZeroKernelNans method is:
4939 %
4940 %      void ZeroKernelNans (KernelInfo *kernel)
4941 %
4942 %  A description of each parameter follows:
4943 %
4944 %    o kernel: the Morphology/Convolution kernel
4945 %
4946 */
4947 MagickPrivate void ZeroKernelNans(KernelInfo *kernel)
4948 {
4949   register size_t
4950     i;
4951
4952   /* do the other kernels in a multi-kernel list first */
4953   if ( kernel->next != (KernelInfo *) NULL)
4954     ZeroKernelNans(kernel->next);
4955
4956   for (i=0; i < (kernel->width*kernel->height); i++)
4957     if ( IsNan(kernel->values[i]) )
4958       kernel->values[i] = 0.0;
4959
4960   return;
4961 }