]> granicus.if.org Git - imagemagick/blob - magick/morphology.c
multi-kernel parsing - ignore extra ;'s around kernel defintions
[imagemagick] / magick / morphology.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %    M   M    OOO    RRRR   PPPP   H   H   OOO   L       OOO    GGGG  Y   Y   %
7 %    MM MM   O   O   R   R  P   P  H   H  O   O  L      O   O  G       Y Y    %
8 %    M M M   O   O   RRRR   PPPP   HHHHH  O   O  L      O   O  G GGG    Y     %
9 %    M   M   O   O   R R    P      H   H  O   O  L      O   O  G   G    Y     %
10 %    M   M    OOO    R  R   P      H   H   OOO   LLLLL   OOO    GGG     Y     %
11 %                                                                             %
12 %                                                                             %
13 %                        MagickCore Morphology Methods                        %
14 %                                                                             %
15 %                              Software Design                                %
16 %                              Anthony Thyssen                                %
17 %                               January 2010                                  %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % Morpology is the the application of various kernels, of any size and even
37 % shape, to a image in various ways (typically binary, but not always).
38 %
39 % Convolution (weighted sum or average) is just one specific type of
40 % morphology. Just one that is very common for image bluring and sharpening
41 % effects.  Not only 2D Gaussian blurring, but also 2-pass 1D Blurring.
42 %
43 % This module provides not only a general morphology function, and the ability
44 % to apply more advanced or iterative morphologies, but also functions for the
45 % generation of many different types of kernel arrays from user supplied
46 % arguments. Prehaps even the generation of a kernel from a small image.
47 */
48 \f
49 /*
50   Include declarations.
51 */
52 #include "magick/studio.h"
53 #include "magick/artifact.h"
54 #include "magick/cache-view.h"
55 #include "magick/color-private.h"
56 #include "magick/enhance.h"
57 #include "magick/exception.h"
58 #include "magick/exception-private.h"
59 #include "magick/gem.h"
60 #include "magick/hashmap.h"
61 #include "magick/image.h"
62 #include "magick/image-private.h"
63 #include "magick/list.h"
64 #include "magick/magick.h"
65 #include "magick/memory_.h"
66 #include "magick/monitor-private.h"
67 #include "magick/morphology.h"
68 #include "magick/option.h"
69 #include "magick/pixel-private.h"
70 #include "magick/prepress.h"
71 #include "magick/quantize.h"
72 #include "magick/registry.h"
73 #include "magick/semaphore.h"
74 #include "magick/splay-tree.h"
75 #include "magick/statistic.h"
76 #include "magick/string_.h"
77 #include "magick/string-private.h"
78 #include "magick/token.h"
79 \f
80 /*
81   The following test is for special floating point numbers of value NaN (not
82   a number), that may be used within a Kernel Definition.  NaN's are defined
83   as part of the IEEE standard for floating point number representation.
84
85   These are used a Kernel value of NaN means that that kernel position is not
86   part of the normal convolution or morphology process, and thus allowing the
87   use of 'shaped' kernels.
88
89   Special properities two NaN's are never equal, even if they are from the
90   same variable That is the IsNaN() macro is only true if the value is NaN.
91 */
92 #define IsNan(a)   ((a)!=(a))
93
94 /*
95   Other global definitions used by module.
96 */
97 static inline double MagickMin(const double x,const double y)
98 {
99   return( x < y ? x : y);
100 }
101 static inline double MagickMax(const double x,const double y)
102 {
103   return( x > y ? x : y);
104 }
105 #define Minimize(assign,value) assign=MagickMin(assign,value)
106 #define Maximize(assign,value) assign=MagickMax(assign,value)
107
108 /* Currently these are only internal to this module */
109 static void
110   RotateKernelInfo(KernelInfo *, double);
111 \f
112 /*
113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 %                                                                             %
115 %                                                                             %
116 %                                                                             %
117 %     A c q u i r e K e r n e l I n f o                                       %
118 %                                                                             %
119 %                                                                             %
120 %                                                                             %
121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 %
123 %  AcquireKernelInfo() takes the given string (generally supplied by the
124 %  user) and converts it into a Morphology/Convolution Kernel.  This allows
125 %  users to specify a kernel from a number of pre-defined kernels, or to fully
126 %  specify their own kernel for a specific Convolution or Morphology
127 %  Operation.
128 %
129 %  The kernel so generated can be any rectangular array of floating point
130 %  values (doubles) with the 'control point' or 'pixel being affected'
131 %  anywhere within that array of values.
132 %
133 %  Previously IM was restricted to a square of odd size using the exact
134 %  center as origin, this is no longer the case, and any rectangular kernel
135 %  with any value being declared the origin. This in turn allows the use of
136 %  highly asymmetrical kernels.
137 %
138 %  The floating point values in the kernel can also include a special value
139 %  known as 'nan' or 'not a number' to indicate that this value is not part
140 %  of the kernel array. This allows you to shaped the kernel within its
141 %  rectangular area. That is 'nan' values provide a 'mask' for the kernel
142 %  shape.  However at least one non-nan value must be provided for correct
143 %  working of a kernel.
144 %
145 %  The returned kernel should be freed using the DestroyKernelInfo() when you
146 %  are finished with it.  Do not free this memory yourself.
147 %
148 %  Input kernel defintion strings can consist of any of three types.
149 %
150 %    "name:args"
151 %         Select from one of the built in kernels, using the name and
152 %         geometry arguments supplied.  See AcquireKernelBuiltIn()
153 %
154 %    "WxH[+X+Y]:num, num, num ..."
155 %         a kernel of size W by H, with W*H floating point numbers following.
156 %         the 'center' can be optionally be defined at +X+Y (such that +0+0
157 %         is top left corner). If not defined the pixel in the center, for
158 %         odd sizes, or to the immediate top or left of center for even sizes
159 %         is automatically selected.
160 %
161 %    "num, num, num, num, ..."
162 %         list of floating point numbers defining an 'old style' odd sized
163 %         square kernel.  At least 9 values should be provided for a 3x3
164 %         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
165 %         Values can be space or comma separated.  This is not recommended.
166 %
167 %  Note that 'name' kernels will start with an alphabetic character while the
168 %  new kernel specification has a ':' character in its specification string.
169 %  If neither is the case, it is assumed an old style of a simple list of
170 %  numbers generating a odd-sized square kernel has been given.
171 %
172 %  You can define a 'list of kernels' which can be used by some morphology
173 %  operators A list is defined as a semi-colon seperated list kernels.
174 %
175 %     " kernel ; kernel ; kernel ; "
176 %
177 %  Extra ';' characters are simply ignored.
178 %
179 %  The format of the AcquireKernal method is:
180 %
181 %      KernelInfo *AcquireKernelInfo(const char *kernel_string)
182 %
183 %  A description of each parameter follows:
184 %
185 %    o kernel_string: the Morphology/Convolution kernel wanted.
186 %
187 */
188
189 /* This was separated so that it could be used as a separate
190 ** array input handling function, such as for -color-matrix
191 */
192 static KernelInfo *ParseKernelArray(const char *kernel_string)
193 {
194   KernelInfo
195     *kernel;
196
197   char
198     token[MaxTextExtent];
199
200   const char
201     *p,
202     *end;
203
204   register long
205     i;
206
207   double
208     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
209
210   kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
211   if (kernel == (KernelInfo *)NULL)
212     return(kernel);
213   (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
214   kernel->minimum = kernel->maximum = 0.0;
215   kernel->negative_range = kernel->positive_range = 0.0;
216   kernel->type = UserDefinedKernel;
217   kernel->next = (KernelInfo *) NULL;
218   kernel->signature = MagickSignature;
219
220   /* find end of this specific kernel definition string */
221   end = strchr(kernel_string, ';');
222   if ( end == (char *) NULL )
223     end = strchr(kernel_string, '\0');
224
225   /* Has a ':' in argument - New user kernel specification */
226   p = strchr(kernel_string, ':');
227   if ( p != (char *) NULL && p < end)
228     {
229       MagickStatusType
230         flags;
231
232       GeometryInfo
233         args;
234
235       /* ParseGeometry() needs the geometry separated! -- Arrgghh */
236       memcpy(token, kernel_string, (size_t) (p-kernel_string));
237       token[p-kernel_string] = '\0';
238       SetGeometryInfo(&args);
239       flags = ParseGeometry(token, &args);
240
241       /* Size handling and checks of geometry settings */
242       if ( (flags & WidthValue) == 0 ) /* if no width then */
243         args.rho = args.sigma;         /* then  width = height */
244       if ( args.rho < 1.0 )            /* if width too small */
245          args.rho = 1.0;               /* then  width = 1 */
246       if ( args.sigma < 1.0 )          /* if height too small */
247         args.sigma = args.rho;         /* then  height = width */
248       kernel->width = (unsigned long)args.rho;
249       kernel->height = (unsigned long)args.sigma;
250
251       /* Offset Handling and Checks */
252       if ( args.xi  < 0.0 || args.psi < 0.0 )
253         return(DestroyKernelInfo(kernel));
254       kernel->x = ((flags & XValue)!=0) ? (long)args.xi
255                                                : (long) (kernel->width-1)/2;
256       kernel->y = ((flags & YValue)!=0) ? (long)args.psi
257                                                : (long) (kernel->height-1)/2;
258       if ( kernel->x >= (long) kernel->width ||
259            kernel->y >= (long) kernel->height )
260         return(DestroyKernelInfo(kernel));
261
262       p++; /* advance beyond the ':' */
263     }
264   else
265     { /* ELSE - Old old specification, forming odd-square kernel */
266       /* count up number of values given */
267       p=(const char *) kernel_string;
268       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
269         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
270       for (i=0; p < end; i++)
271       {
272         GetMagickToken(p,&p,token);
273         if (*token == ',')
274           GetMagickToken(p,&p,token);
275       }
276       /* set the size of the kernel - old sized square */
277       kernel->width = kernel->height= (unsigned long) sqrt((double) i+1.0);
278       kernel->x = kernel->y = (long) (kernel->width-1)/2;
279       p=(const char *) kernel_string;
280       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
281         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
282     }
283
284   /* Read in the kernel values from rest of input string argument */
285   kernel->values=(double *) AcquireQuantumMemory(kernel->width,
286                         kernel->height*sizeof(double));
287   if (kernel->values == (double *) NULL)
288     return(DestroyKernelInfo(kernel));
289
290   kernel->minimum = +MagickHuge;
291   kernel->maximum = -MagickHuge;
292   kernel->negative_range = kernel->positive_range = 0.0;
293
294   for (i=0; (i < (long) (kernel->width*kernel->height)) && (p < end); i++)
295   {
296     GetMagickToken(p,&p,token);
297     if (*token == ',')
298       GetMagickToken(p,&p,token);
299     if (    LocaleCompare("nan",token) == 0
300         || LocaleCompare("-",token) == 0 ) {
301       kernel->values[i] = nan; /* do not include this value in kernel */
302     }
303     else {
304       kernel->values[i] = StringToDouble(token);
305       ( kernel->values[i] < 0)
306           ?  ( kernel->negative_range += kernel->values[i] )
307           :  ( kernel->positive_range += kernel->values[i] );
308       Minimize(kernel->minimum, kernel->values[i]);
309       Maximize(kernel->maximum, kernel->values[i]);
310     }
311   }
312
313   /* sanity check -- no more values in kernel definition */
314   GetMagickToken(p,&p,token);
315   if ( *token != '\0' && *token != ';' && *token != '\'' )
316     return(DestroyKernelInfo(kernel));
317
318 #if 0
319   /* this was the old method of handling a incomplete kernel */
320   if ( i < (long) (kernel->width*kernel->height) ) {
321     Minimize(kernel->minimum, kernel->values[i]);
322     Maximize(kernel->maximum, kernel->values[i]);
323     for ( ; i < (long) (kernel->width*kernel->height); i++)
324       kernel->values[i]=0.0;
325   }
326 #else
327   /* Number of values for kernel was not enough - Report Error */
328   if ( i < (long) (kernel->width*kernel->height) )
329     return(DestroyKernelInfo(kernel));
330 #endif
331
332   /* check that we recieved at least one real (non-nan) value! */
333   if ( kernel->minimum == MagickHuge )
334     return(DestroyKernelInfo(kernel));
335
336   return(kernel);
337 }
338
339 static KernelInfo *ParseNamedKernel(const char *kernel_string)
340 {
341   char
342     token[MaxTextExtent];
343
344   long
345     type;
346
347   const char
348     *p,
349     *end;
350
351   MagickStatusType
352     flags;
353
354   GeometryInfo
355     args;
356
357   /* Parse special 'named' kernel */
358   GetMagickToken(kernel_string,&p,token);
359   type=ParseMagickOption(MagickKernelOptions,MagickFalse,token);
360   if ( type < 0 || type == UserDefinedKernel )
361     return((KernelInfo *)NULL);  /* not a valid named kernel */
362
363   while (((isspace((int) ((unsigned char) *p)) != 0) ||
364           (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
365     p++;
366
367   end = strchr(p, ';'); /* end of this kernel defintion */
368   if ( end == (char *) NULL )
369     end = strchr(p, '\0');
370
371   /* ParseGeometry() needs the geometry separated! -- Arrgghh */
372   memcpy(token, p, (size_t) (end-p));
373   token[end-p] = '\0';
374   SetGeometryInfo(&args);
375   flags = ParseGeometry(token, &args);
376
377   /* special handling of missing values in input string */
378   switch( type ) {
379     case RectangleKernel:
380       if ( (flags & WidthValue) == 0 ) /* if no width then */
381         args.rho = args.sigma;         /* then  width = height */
382       if ( args.rho < 1.0 )            /* if width too small */
383           args.rho = 3;                 /* then  width = 3 */
384       if ( args.sigma < 1.0 )          /* if height too small */
385         args.sigma = args.rho;         /* then  height = width */
386       if ( (flags & XValue) == 0 )     /* center offset if not defined */
387         args.xi = (double)(((long)args.rho-1)/2);
388       if ( (flags & YValue) == 0 )
389         args.psi = (double)(((long)args.sigma-1)/2);
390       break;
391     case SquareKernel:
392     case DiamondKernel:
393     case DiskKernel:
394     case PlusKernel:
395       /* If no scale given (a 0 scale is valid! - set it to 1.0 */
396       if ( (flags & HeightValue) == 0 )
397         args.sigma = 1.0;
398       break;
399     case ChebyshevKernel:
400     case ManhattenKernel:
401     case EuclideanKernel:
402       if ( (flags & HeightValue) == 0 )
403         args.sigma = 100.0;                    /* default distance scaling */
404       else if ( (flags & AspectValue ) != 0 )  /* '!' flag */
405         args.sigma = QuantumRange/args.sigma;  /* maximum pixel distance */
406       else if ( (flags & PercentValue ) != 0 ) /* '%' flag */
407         args.sigma *= QuantumRange/100.0;      /* percentage of color range */
408       break;
409     default:
410       break;
411   }
412
413   return(AcquireKernelBuiltIn((KernelInfoType)type, &args));
414 }
415
416 MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
417 {
418
419   KernelInfo
420     *kernel,
421     *new_kernel,
422     *last_kernel;
423
424   char
425     token[MaxTextExtent];
426
427   const char
428     *p;
429
430   p = kernel_string;
431   kernel = last_kernel = NULL;
432
433   while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
434
435     /* ignore multiple ';' following each other */
436     if ( *token != ';' ) {
437
438       /* tokens starting with alpha is a Named kernel */
439       if (isalpha((int) *token) == 0)
440         new_kernel = ParseKernelArray(p);
441       else /* otherwise a user defined kernel array */
442         new_kernel = ParseNamedKernel(p);
443
444       if ( last_kernel == (KernelInfo *) NULL ) {
445         /* first kernel: initialize the kernel list */
446         if ( new_kernel == (KernelInfo *) NULL )
447           return((KernelInfo *) NULL);
448         kernel = last_kernel = new_kernel;
449       }
450       else {
451         /* append kernel to end of the list */
452         if ( new_kernel == (KernelInfo *) NULL )
453           return(DestroyKernelInfo(kernel));
454         last_kernel->next = new_kernel;
455         last_kernel = new_kernel;
456       }
457     }
458
459     /* look for the next kernel in list */
460     p = strchr(p, ';');
461     if ( p == (char *) NULL )
462       break;
463     p++;
464
465   }
466   return(kernel);
467 }
468
469 \f
470 /*
471 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472 %                                                                             %
473 %                                                                             %
474 %                                                                             %
475 %     A c q u i r e K e r n e l B u i l t I n                                 %
476 %                                                                             %
477 %                                                                             %
478 %                                                                             %
479 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480 %
481 %  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
482 %  kernels used for special purposes such as gaussian blurring, skeleton
483 %  pruning, and edge distance determination.
484 %
485 %  They take a KernelType, and a set of geometry style arguments, which were
486 %  typically decoded from a user supplied string, or from a more complex
487 %  Morphology Method that was requested.
488 %
489 %  The format of the AcquireKernalBuiltIn method is:
490 %
491 %      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
492 %           const GeometryInfo args)
493 %
494 %  A description of each parameter follows:
495 %
496 %    o type: the pre-defined type of kernel wanted
497 %
498 %    o args: arguments defining or modifying the kernel
499 %
500 %  Convolution Kernels
501 %
502 %    Gaussian  "{radius},{sigma}"
503 %       Generate a two-dimentional gaussian kernel, as used by -gaussian
504 %       A sigma is required, (with the 'x'), due to historical reasons.
505 %
506 %       NOTE: that the 'radius' is optional, but if provided can limit (clip)
507 %       the final size of the resulting kernel to a square 2*radius+1 in size.
508 %       The radius should be at least 2 times that of the sigma value, or
509 %       sever clipping and aliasing may result.  If not given or set to 0 the
510 %       radius will be determined so as to produce the best minimal error
511 %       result, which is usally much larger than is normally needed.
512 %
513 %    Blur  "{radius},{sigma},{angle}"
514 %       As per Gaussian, but generates a 1 dimensional or linear gaussian
515 %       blur, at the angle given (current restricted to orthogonal angles).
516 %       If a 'radius' is given the kernel is clipped to a width of 2*radius+1.
517 %
518 %       NOTE that two such blurs perpendicular to each other is equivelent to
519 %       -blur and the previous gaussian, but is often 10 or more times faster.
520 %
521 %    Comet  "{width},{sigma},{angle}"
522 %       Blur in one direction only, mush like how a bright object leaves
523 %       a comet like trail.  The Kernel is actually half a gaussian curve,
524 %       Adding two such blurs in oppiste directions produces a Linear Blur.
525 %
526 %       NOTE: that the first argument is the width of the kernel and not the
527 %       radius of the kernel.
528 %
529 %    # Still to be implemented...
530 %    #
531 %    # Sharpen "{radius},{sigma}
532 %    #    Negated Gaussian (center zeroed and re-normalized),
533 %    #    with a 2 unit positive peak.   -- Check On line documentation
534 %    #
535 %    # Laplacian  "{radius},{sigma}"
536 %    #    Laplacian (a mexican hat like) Function
537 %    #
538 %    # LOG  "{radius},{sigma1},{sigma2}
539 %    #    Laplacian of Gaussian
540 %    #
541 %    # DOG  "{radius},{sigma1},{sigma2}
542 %    #    Difference of two Gaussians
543 %    #
544 %    # Filter2D
545 %    # Filter1D
546 %    #    Set kernel values using a resize filter, and given scale (sigma)
547 %    #    Cylindrical or Linear.   Is this posible with an image?
548 %    #
549 %
550 %  Boolean Kernels
551 %
552 %    Rectangle "{geometry}"
553 %       Simply generate a rectangle of 1's with the size given. You can also
554 %       specify the location of the 'control point', otherwise the closest
555 %       pixel to the center of the rectangle is selected.
556 %
557 %       Properly centered and odd sized rectangles work the best.
558 %
559 %    Diamond  "[{radius}[,{scale}]]"
560 %       Generate a diamond shaped kernel with given radius to the points.
561 %       Kernel size will again be radius*2+1 square and defaults to radius 1,
562 %       generating a 3x3 kernel that is slightly larger than a square.
563 %
564 %    Square  "[{radius}[,{scale}]]"
565 %       Generate a square shaped kernel of size radius*2+1, and defaulting
566 %       to a 3x3 (radius 1).
567 %
568 %       Note that using a larger radius for the "Square" or the "Diamond"
569 %       is also equivelent to iterating the basic morphological method
570 %       that many times. However However iterating with the smaller radius 1
571 %       default is actually faster than using a larger kernel radius.
572 %
573 %    Disk   "[{radius}[,{scale}]]
574 %       Generate a binary disk of the radius given, radius may be a float.
575 %       Kernel size will be ceil(radius)*2+1 square.
576 %       NOTE: Here are some disk shapes of specific interest
577 %          "disk:1"    => "diamond" or "cross:1"
578 %          "disk:1.5"  => "square"
579 %          "disk:2"    => "diamond:2"
580 %          "disk:2.5"  => a general disk shape of radius 2
581 %          "disk:2.9"  => "square:2"
582 %          "disk:3.5"  => default - octagonal/disk shape of radius 3
583 %          "disk:4.2"  => roughly octagonal shape of radius 4
584 %          "disk:4.3"  => a general disk shape of radius 4
585 %       After this all the kernel shape becomes more and more circular.
586 %
587 %       Because a "disk" is more circular when using a larger radius, using a
588 %       larger radius is preferred over iterating the morphological operation.
589 %
590 %    Plus  "[{radius}[,{scale}]]"
591 %       Generate a kernel in the shape of a 'plus' sign. The length of each
592 %       arm is also the radius, which defaults to 2.
593 %
594 %       This kernel is not a good general morphological kernel, but is used
595 %       more for highlighting and marking any single pixels in an image using,
596 %       a "Dilate" or "Erode" method as appropriate.
597 %
598 %       NOTE: "plus:1" is equivelent to a "Diamond" kernel.
599 %
600 %       Note that unlike other kernels iterating a plus does not produce the
601 %       same result as using a larger radius for the cross.
602 %
603 %  Distance Measuring Kernels
604 %
605 %    Chebyshev "[{radius}][x{scale}[%!]]"
606 %    Manhatten "[{radius}][x{scale}[%!]]"
607 %    Euclidean "[{radius}][x{scale}[%!]]"
608 %
609 %       Different types of distance measuring methods, which are used with the
610 %       a 'Distance' morphology method for generating a gradient based on
611 %       distance from an edge of a binary shape, though there is a technique
612 %       for handling a anti-aliased shape.
613 %
614 %       Chebyshev Distance (also known as Tchebychev Distance) is a value of
615 %       one to any neighbour, orthogonal or diagonal. One why of thinking of
616 %       it is the number of squares a 'King' or 'Queen' in chess needs to
617 %       traverse reach any other position on a chess board.  It results in a
618 %       'square' like distance function, but one where diagonals are closer
619 %       than expected.
620 %
621 %       Manhatten Distance (also known as Rectilinear Distance, or the Taxi
622 %       Cab metric), is the distance needed when you can only travel in
623 %       orthogonal (horizontal or vertical) only.  It is the distance a 'Rook'
624 %       in chess would travel. It results in a diamond like distances, where
625 %       diagonals are further than expected.
626 %
627 %       Euclidean Distance is the 'direct' or 'as the crow flys distance.
628 %       However by default the kernel size only has a radius of 1, which
629 %       limits the distance to 'Knight' like moves, with only orthogonal and
630 %       diagonal measurements being correct.  As such for the default kernel
631 %       you will get octagonal like distance function, which is reasonally
632 %       accurate.
633 %
634 %       However if you use a larger radius such as "Euclidean:4" you will
635 %       get a much smoother distance gradient from the edge of the shape.
636 %       Of course a larger kernel is slower to use, and generally not needed.
637 %
638 %       To allow the use of fractional distances that you get with diagonals
639 %       the actual distance is scaled by a fixed value which the user can
640 %       provide.  This is not actually nessary for either ""Chebyshev" or
641 %       "Manhatten" distance kernels, but is done for all three distance
642 %       kernels.  If no scale is provided it is set to a value of 100,
643 %       allowing for a maximum distance measurement of 655 pixels using a Q16
644 %       version of IM, from any edge.  However for small images this can
645 %       result in quite a dark gradient.
646 %
647 %       See the 'Distance' Morphological Method, for information of how it is
648 %       applied.
649 %
650 %  # Hit-n-Miss Kernel-Lists -- Still to be implemented
651 %  #
652 %  # specifically for   Pruning,  Thinning,  Thickening
653 %  #
654 */
655
656 MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
657    const GeometryInfo *args)
658 {
659   KernelInfo
660     *kernel;
661
662   register long
663     i;
664
665   register long
666     u,
667     v;
668
669   double
670     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
671
672   kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
673   if (kernel == (KernelInfo *) NULL)
674     return(kernel);
675   (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
676   kernel->minimum = kernel->maximum = 0.0;
677   kernel->negative_range = kernel->positive_range = 0.0;
678   kernel->type = type;
679   kernel->next = (KernelInfo *) NULL;
680   kernel->signature = MagickSignature;
681
682   switch(type) {
683     /* Convolution Kernels */
684     case GaussianKernel:
685       { double
686           sigma = fabs(args->sigma);
687
688         sigma = (sigma <= MagickEpsilon) ? 1.0 : sigma;
689
690         kernel->width = kernel->height =
691                             GetOptimalKernelWidth2D(args->rho,sigma);
692         kernel->x = kernel->y = (long) (kernel->width-1)/2;
693         kernel->negative_range = kernel->positive_range = 0.0;
694         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
695                               kernel->height*sizeof(double));
696         if (kernel->values == (double *) NULL)
697           return(DestroyKernelInfo(kernel));
698
699         sigma = 2.0*sigma*sigma; /* simplify the expression */
700         for ( i=0, v=-kernel->y; v <= (long)kernel->y; v++)
701           for ( u=-kernel->x; u <= (long)kernel->x; u++, i++)
702             kernel->positive_range += (
703               kernel->values[i] =
704                  exp(-((double)(u*u+v*v))/sigma)
705                        /*  / (MagickPI*sigma)  */ );
706         kernel->minimum = 0;
707         kernel->maximum = kernel->values[
708                          kernel->y*kernel->width+kernel->x ];
709
710         ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
711
712         break;
713       }
714     case BlurKernel:
715       { double
716           sigma = fabs(args->sigma);
717
718         sigma = (sigma <= MagickEpsilon) ? 1.0 : sigma;
719
720         kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
721         kernel->x = (long) (kernel->width-1)/2;
722         kernel->height = 1;
723         kernel->y = 0;
724         kernel->negative_range = kernel->positive_range = 0.0;
725         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
726                               kernel->height*sizeof(double));
727         if (kernel->values == (double *) NULL)
728           return(DestroyKernelInfo(kernel));
729
730 #if 1
731 #define KernelRank 3
732         /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
733         ** It generates a gaussian 3 times the width, and compresses it into
734         ** the expected range.  This produces a closer normalization of the
735         ** resulting kernel, especially for very low sigma values.
736         ** As such while wierd it is prefered.
737         **
738         ** I am told this method originally came from Photoshop.
739         */
740         sigma *= KernelRank;                /* simplify expanded curve */
741         v = (long) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
742         (void) ResetMagickMemory(kernel->values,0, (size_t)
743                        kernel->width*sizeof(double));
744         for ( u=-v; u <= v; u++) {
745           kernel->values[(u+v)/KernelRank] +=
746                 exp(-((double)(u*u))/(2.0*sigma*sigma))
747                        /*   / (MagickSQ2PI*sigma/KernelRank)  */ ;
748         }
749         for (i=0; i < (long) kernel->width; i++)
750           kernel->positive_range += kernel->values[i];
751 #else
752         for ( i=0, u=-kernel->x; i < kernel->width; i++, u++)
753           kernel->positive_range += (
754               kernel->values[i] =
755                 exp(-((double)(u*u))/(2.0*sigma*sigma))
756                        /*  / (MagickSQ2PI*sigma)  */ );
757 #endif
758         kernel->minimum = 0;
759         kernel->maximum = kernel->values[ kernel->x ];
760         /* Note that neither methods above generate a normalized kernel,
761         ** though it gets close. The kernel may be 'clipped' by a user defined
762         ** radius, producing a smaller (darker) kernel.  Also for very small
763         ** sigma's (> 0.1) the central value becomes larger than one, and thus
764         ** producing a very bright kernel.
765         */
766
767         /* Normalize the 1D Gaussian Kernel
768         **
769         ** Because of this the divisor in the above kernel generator is
770         ** not needed, so is not done above.
771         */
772         ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
773
774         /* rotate the kernel by given angle */
775         RotateKernelInfo(kernel, args->xi);
776         break;
777       }
778     case CometKernel:
779       { double
780           sigma = fabs(args->sigma);
781
782         sigma = (sigma <= MagickEpsilon) ? 1.0 : sigma;
783
784         if ( args->rho < 1.0 )
785           kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
786         else
787           kernel->width = (unsigned long)args->rho;
788         kernel->x = kernel->y = 0;
789         kernel->height = 1;
790         kernel->negative_range = kernel->positive_range = 0.0;
791         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
792                               kernel->height*sizeof(double));
793         if (kernel->values == (double *) NULL)
794           return(DestroyKernelInfo(kernel));
795
796         /* A comet blur is half a gaussian curve, so that the object is
797         ** blurred in one direction only.  This may not be quite the right
798         ** curve so may change in the future. The function must be normalised.
799         */
800 #if 1
801 #define KernelRank 3
802         sigma *= KernelRank;                /* simplify expanded curve */
803         v = (long) kernel->width*KernelRank; /* start/end points to fit range */
804         (void) ResetMagickMemory(kernel->values,0, (size_t)
805                        kernel->width*sizeof(double));
806         for ( u=0; u < v; u++) {
807           kernel->values[u/KernelRank] +=
808                exp(-((double)(u*u))/(2.0*sigma*sigma))
809                        /*   / (MagickSQ2PI*sigma/KernelRank)  */ ;
810         }
811         for (i=0; i < (long) kernel->width; i++)
812           kernel->positive_range += kernel->values[i];
813 #else
814         for ( i=0; i < (long) kernel->width; i++)
815           kernel->positive_range += (
816             kernel->values[i] =
817                exp(-((double)(i*i))/(2.0*sigma*sigma))
818                        /*  / (MagickSQ2PI*sigma)  */ );
819 #endif
820         kernel->minimum = 0;
821         kernel->maximum = kernel->values[0];
822
823         ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
824         RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
825         break;
826       }
827     /* Boolean Kernels */
828     case RectangleKernel:
829     case SquareKernel:
830       {
831         double scale;
832         if ( type == SquareKernel )
833           {
834             if (args->rho < 1.0)
835               kernel->width = kernel->height = 3;  /* default radius = 1 */
836             else
837               kernel->width = kernel->height = (unsigned long) (2*args->rho+1);
838             kernel->x = kernel->y = (long) (kernel->width-1)/2;
839             scale = args->sigma;
840           }
841         else {
842             /* NOTE: user defaults set in "AcquireKernelInfo()" */
843             if ( args->rho < 1.0 || args->sigma < 1.0 )
844               return(DestroyKernelInfo(kernel));    /* invalid args given */
845             kernel->width = (unsigned long)args->rho;
846             kernel->height = (unsigned long)args->sigma;
847             if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
848                  args->psi < 0.0 || args->psi > (double)kernel->height )
849               return(DestroyKernelInfo(kernel));    /* invalid args given */
850             kernel->x = (long) args->xi;
851             kernel->y = (long) args->psi;
852             scale = 1.0;
853           }
854         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
855                               kernel->height*sizeof(double));
856         if (kernel->values == (double *) NULL)
857           return(DestroyKernelInfo(kernel));
858
859         /* set all kernel values to 1.0 */
860         u=(long) kernel->width*kernel->height;
861         for ( i=0; i < u; i++)
862             kernel->values[i] = scale;
863         kernel->minimum = kernel->maximum = scale;   /* a flat shape */
864         kernel->positive_range = scale*u;
865         break;
866       }
867     case DiamondKernel:
868       {
869         if (args->rho < 1.0)
870           kernel->width = kernel->height = 3;  /* default radius = 1 */
871         else
872           kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
873         kernel->x = kernel->y = (long) (kernel->width-1)/2;
874
875         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
876                               kernel->height*sizeof(double));
877         if (kernel->values == (double *) NULL)
878           return(DestroyKernelInfo(kernel));
879
880         /* set all kernel values within diamond area to scale given */
881         for ( i=0, v=-kernel->y; v <= (long)kernel->y; v++)
882           for ( u=-kernel->x; u <= (long)kernel->x; u++, i++)
883             if ((labs(u)+labs(v)) <= (long)kernel->x)
884               kernel->positive_range += kernel->values[i] = args->sigma;
885             else
886               kernel->values[i] = nan;
887         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
888         break;
889       }
890     case DiskKernel:
891       {
892         long
893           limit;
894
895         limit = (long)(args->rho*args->rho);
896         if (args->rho < 0.1)             /* default radius approx 3.5 */
897           kernel->width = kernel->height = 7L, limit = 10L;
898         else
899            kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
900         kernel->x = kernel->y = (long) (kernel->width-1)/2;
901
902         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
903                               kernel->height*sizeof(double));
904         if (kernel->values == (double *) NULL)
905           return(DestroyKernelInfo(kernel));
906
907         /* set all kernel values within disk area to 1.0 */
908         for ( i=0, v= -kernel->y; v <= (long)kernel->y; v++)
909           for ( u=-kernel->x; u <= (long)kernel->x; u++, i++)
910             if ((u*u+v*v) <= limit)
911               kernel->positive_range += kernel->values[i] = args->sigma;
912             else
913               kernel->values[i] = nan;
914         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
915         break;
916       }
917     case PlusKernel:
918       {
919         if (args->rho < 1.0)
920           kernel->width = kernel->height = 5;  /* default radius 2 */
921         else
922            kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
923         kernel->x = kernel->y = (long) (kernel->width-1)/2;
924
925         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
926                               kernel->height*sizeof(double));
927         if (kernel->values == (double *) NULL)
928           return(DestroyKernelInfo(kernel));
929
930         /* set all kernel values along axises to 1.0 */
931         for ( i=0, v=-kernel->y; v <= (long)kernel->y; v++)
932           for ( u=-kernel->x; u <= (long)kernel->x; u++, i++)
933             kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
934         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
935         kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
936         break;
937       }
938     /* Distance Measuring Kernels */
939     case ChebyshevKernel:
940       {
941         if (args->rho < 1.0)
942           kernel->width = kernel->height = 3;  /* default radius = 1 */
943         else
944           kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
945         kernel->x = kernel->y = (long) (kernel->width-1)/2;
946
947         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
948                               kernel->height*sizeof(double));
949         if (kernel->values == (double *) NULL)
950           return(DestroyKernelInfo(kernel));
951
952         for ( i=0, v=-kernel->y; v <= (long)kernel->y; v++)
953           for ( u=-kernel->x; u <= (long)kernel->x; u++, i++)
954             kernel->positive_range += ( kernel->values[i] =
955                  args->sigma*((labs(u)>labs(v)) ? labs(u) : labs(v)) );
956         kernel->maximum = kernel->values[0];
957         break;
958       }
959     case ManhattenKernel:
960       {
961         if (args->rho < 1.0)
962           kernel->width = kernel->height = 3;  /* default radius = 1 */
963         else
964            kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
965         kernel->x = kernel->y = (long) (kernel->width-1)/2;
966
967         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
968                               kernel->height*sizeof(double));
969         if (kernel->values == (double *) NULL)
970           return(DestroyKernelInfo(kernel));
971
972         for ( i=0, v=-kernel->y; v <= (long)kernel->y; v++)
973           for ( u=-kernel->x; u <= (long)kernel->x; u++, i++)
974             kernel->positive_range += ( kernel->values[i] =
975                  args->sigma*(labs(u)+labs(v)) );
976         kernel->maximum = kernel->values[0];
977         break;
978       }
979     case EuclideanKernel:
980       {
981         if (args->rho < 1.0)
982           kernel->width = kernel->height = 3;  /* default radius = 1 */
983         else
984            kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
985         kernel->x = kernel->y = (long) (kernel->width-1)/2;
986
987         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
988                               kernel->height*sizeof(double));
989         if (kernel->values == (double *) NULL)
990           return(DestroyKernelInfo(kernel));
991
992         for ( i=0, v=-kernel->y; v <= (long)kernel->y; v++)
993           for ( u=-kernel->x; u <= (long)kernel->x; u++, i++)
994             kernel->positive_range += ( kernel->values[i] =
995                  args->sigma*sqrt((double)(u*u+v*v)) );
996         kernel->maximum = kernel->values[0];
997         break;
998       }
999     /* Undefined Kernels */
1000     case LaplacianKernel:
1001     case LOGKernel:
1002     case DOGKernel:
1003       perror("Kernel Type has not been defined yet");
1004       /* FALL THRU */
1005     default:
1006       /* Generate a No-Op minimal kernel - 1x1 pixel */
1007       kernel->values=(double *)AcquireQuantumMemory((size_t)1,sizeof(double));
1008       if (kernel->values == (double *) NULL)
1009         return(DestroyKernelInfo(kernel));
1010       kernel->width = kernel->height = 1;
1011       kernel->x = kernel->x = 0;
1012       kernel->type = UndefinedKernel;
1013       kernel->maximum =
1014         kernel->positive_range =
1015           kernel->values[0] = 1.0;  /* a flat single-point no-op kernel! */
1016       break;
1017   }
1018
1019   return(kernel);
1020 }
1021 \f
1022 /*
1023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1024 %                                                                             %
1025 %                                                                             %
1026 %                                                                             %
1027 %     C l o n e K e r n e l I n f o                                           %
1028 %                                                                             %
1029 %                                                                             %
1030 %                                                                             %
1031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1032 %
1033 %  CloneKernelInfo() creates a new clone of the given Kernel List so that its
1034 %  can be modified without effecting the original.  The cloned kernel should
1035 %  be destroyed using DestoryKernelInfo() when no longer needed.
1036 %
1037 %  The format of the CloneKernelInfo method is:
1038 %
1039 %      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
1040 %
1041 %  A description of each parameter follows:
1042 %
1043 %    o kernel: the Morphology/Convolution kernel to be cloned
1044 %
1045 */
1046 MagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
1047 {
1048   register long
1049     i;
1050
1051   KernelInfo
1052     *new_kernel;
1053
1054   assert(kernel != (KernelInfo *) NULL);
1055   new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1056   if (new_kernel == (KernelInfo *) NULL)
1057     return(new_kernel);
1058   *new_kernel=(*kernel); /* copy values in structure */
1059
1060   /* replace the values with a copy of the values */
1061   new_kernel->values=(double *) AcquireQuantumMemory(kernel->width,
1062     kernel->height*sizeof(double));
1063   if (new_kernel->values == (double *) NULL)
1064     return(DestroyKernelInfo(new_kernel));
1065   for (i=0; i < (long) (kernel->width*kernel->height); i++)
1066     new_kernel->values[i]=kernel->values[i];
1067
1068   /* Also clone the next kernel in the kernel list */
1069   if ( kernel->next != (KernelInfo *) NULL ) {
1070     new_kernel->next = CloneKernelInfo(kernel->next);
1071     if ( new_kernel->next == (KernelInfo *) NULL )
1072       return(DestroyKernelInfo(new_kernel));
1073   }
1074
1075   return(new_kernel);
1076 }
1077 \f
1078 /*
1079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1080 %                                                                             %
1081 %                                                                             %
1082 %                                                                             %
1083 %     D e s t r o y K e r n e l I n f o                                       %
1084 %                                                                             %
1085 %                                                                             %
1086 %                                                                             %
1087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1088 %
1089 %  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
1090 %  kernel.
1091 %
1092 %  The format of the DestroyKernelInfo method is:
1093 %
1094 %      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
1095 %
1096 %  A description of each parameter follows:
1097 %
1098 %    o kernel: the Morphology/Convolution kernel to be destroyed
1099 %
1100 */
1101
1102 MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
1103 {
1104   assert(kernel != (KernelInfo *) NULL);
1105
1106   if ( kernel->next != (KernelInfo *) NULL )
1107     kernel->next = DestroyKernelInfo(kernel->next);
1108
1109   kernel->values = (double *)RelinquishMagickMemory(kernel->values);
1110   kernel = (KernelInfo *) RelinquishMagickMemory(kernel);
1111   return(kernel);
1112 }
1113 \f
1114 /*
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116 %                                                                             %
1117 %                                                                             %
1118 %                                                                             %
1119 %     M o r p h o l o g y I m a g e C h a n n e l                             %
1120 %                                                                             %
1121 %                                                                             %
1122 %                                                                             %
1123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124 %
1125 %  MorphologyImageChannel() applies a user supplied kernel to the image
1126 %  according to the given mophology method.
1127 %
1128 %  The given kernel is assumed to have been pre-scaled appropriatally, usally
1129 %  by the kernel generator.
1130 %
1131 %  The format of the MorphologyImage method is:
1132 %
1133 %      Image *MorphologyImage(const Image *image,MorphologyMethod method,
1134 %        const long iterations,KernelInfo *kernel,ExceptionInfo *exception)
1135 %      Image *MorphologyImageChannel(const Image *image, const ChannelType
1136 %        channel,MorphologyMethod method,const long iterations,
1137 %        KernelInfo *kernel,ExceptionInfo *exception)
1138 %
1139 %  A description of each parameter follows:
1140 %
1141 %    o image: the image.
1142 %
1143 %    o method: the morphology method to be applied.
1144 %
1145 %    o iterations: apply the operation this many times (or no change).
1146 %                  A value of -1 means loop until no change found.
1147 %                  How this is applied may depend on the morphology method.
1148 %                  Typically this is a value of 1.
1149 %
1150 %    o channel: the channel type.
1151 %
1152 %    o kernel: An array of double representing the morphology kernel.
1153 %              Warning: kernel may be normalized for the Convolve method.
1154 %
1155 %    o exception: return any errors or warnings in this structure.
1156 %
1157 %
1158 % TODO: bias and auto-scale handling of the kernel for convolution
1159 %     The given kernel is assumed to have been pre-scaled appropriatally, usally
1160 %     by the kernel generator.
1161 %
1162 */
1163
1164
1165 /* Internal function
1166  * Apply the low-level Morphology Method Primatives using the given Kernel
1167  * Returning the number of pixels that changed.
1168  * Two pre-created images must be provided, no image is created.
1169  */
1170 static unsigned long MorphologyPrimative(const Image *image, Image
1171      *result_image, const MorphologyMethod method, const ChannelType channel,
1172      const KernelInfo *kernel, ExceptionInfo *exception)
1173 {
1174 #define MorphologyTag  "Morphology/Image"
1175
1176   long
1177     progress,
1178     y, offx, offy,
1179     changed;
1180
1181   MagickBooleanType
1182     status;
1183
1184   MagickPixelPacket
1185     bias;
1186
1187   CacheView
1188     *p_view,
1189     *q_view;
1190
1191   /* Only the most basic morphology is actually performed by this routine */
1192
1193   /*
1194     Apply Basic Morphology to Image.
1195   */
1196   status=MagickTrue;
1197   changed=0;
1198   progress=0;
1199
1200   GetMagickPixelPacket(image,&bias);
1201   SetMagickPixelPacketBias(image,&bias);
1202   /* Future: handle auto-bias from user, based on kernel input */
1203
1204   p_view=AcquireCacheView(image);
1205   q_view=AcquireCacheView(result_image);
1206
1207   /* Some methods (including convolve) needs use a reflected kernel.
1208    * Adjust 'origin' offsets for this reflected kernel.
1209    */
1210   offx = kernel->x;
1211   offy = kernel->y;
1212   switch(method) {
1213     case ConvolveMorphology:
1214     case DilateMorphology:
1215     case DilateIntensityMorphology:
1216     case DistanceMorphology:
1217       /* kernel needs to used with reflection about origin */
1218       offx = (long) kernel->width-offx-1;
1219       offy = (long) kernel->height-offy-1;
1220       break;
1221     case ErodeMorphology:
1222     case ErodeIntensityMorphology:
1223     case HitAndMissMorphology:
1224     case ThinningMorphology:
1225     case ThickenMorphology:
1226       /* kernel is user as is, without reflection */
1227       break;
1228     default:
1229       perror("Not a low level Morphology Method");
1230       break;
1231   }
1232
1233 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1234   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1235 #endif
1236   for (y=0; y < (long) image->rows; y++)
1237   {
1238     MagickBooleanType
1239       sync;
1240
1241     register const PixelPacket
1242       *restrict p;
1243
1244     register const IndexPacket
1245       *restrict p_indexes;
1246
1247     register PixelPacket
1248       *restrict q;
1249
1250     register IndexPacket
1251       *restrict q_indexes;
1252
1253     register long
1254       x;
1255
1256     unsigned long
1257       r;
1258
1259     if (status == MagickFalse)
1260       continue;
1261     p=GetCacheViewVirtualPixels(p_view, -offx,  y-offy,
1262          image->columns+kernel->width,  kernel->height,  exception);
1263     q=GetCacheViewAuthenticPixels(q_view,0,y,result_image->columns,1,
1264          exception);
1265     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1266       {
1267         status=MagickFalse;
1268         continue;
1269       }
1270     p_indexes=GetCacheViewVirtualIndexQueue(p_view);
1271     q_indexes=GetCacheViewAuthenticIndexQueue(q_view);
1272     r = (image->columns+kernel->width)*offy+offx; /* constant */
1273
1274     for (x=0; x < (long) image->columns; x++)
1275     {
1276        long
1277         v;
1278
1279       register long
1280         u;
1281
1282       register const double
1283         *restrict k;
1284
1285       register const PixelPacket
1286         *restrict k_pixels;
1287
1288       register const IndexPacket
1289         *restrict k_indexes;
1290
1291       MagickPixelPacket
1292         result,
1293         min,
1294         max;
1295
1296       /* Copy input to ouput image for unused channels
1297        * This removes need for 'cloning' a new image every iteration
1298        */
1299       *q = p[r];
1300       if (image->colorspace == CMYKColorspace)
1301         q_indexes[x] = p_indexes[r];
1302
1303       /* Defaults */
1304       min.red     =
1305       min.green   =
1306       min.blue    =
1307       min.opacity =
1308       min.index   = (MagickRealType) QuantumRange;
1309       max.red     =
1310       max.green   =
1311       max.blue    =
1312       max.opacity =
1313       max.index   = (MagickRealType) 0;
1314       /* original pixel value */
1315       result.red     = (MagickRealType) p[r].red;
1316       result.green   = (MagickRealType) p[r].green;
1317       result.blue    = (MagickRealType) p[r].blue;
1318       result.opacity = QuantumRange - (MagickRealType) p[r].opacity;
1319       if ( image->colorspace == CMYKColorspace)
1320          result.index   = (MagickRealType) p_indexes[r];
1321
1322       switch (method) {
1323         case ConvolveMorphology:
1324           /* Set the user defined bias of the weighted average output
1325           **
1326           ** FUTURE: provide some way for internal functions to disable
1327           ** user provided bias and scaling effects.
1328           */
1329           result=bias;
1330           break;
1331         case DilateIntensityMorphology:
1332         case ErodeIntensityMorphology:
1333           result.red = 0.0;  /* flag indicating when first match found */
1334           break;
1335         default:
1336           break;
1337       }
1338
1339       switch ( method ) {
1340         case ConvolveMorphology:
1341             /* Weighted Average of pixels using reflected kernel
1342             **
1343             ** NOTE for correct working of this operation for asymetrical
1344             ** kernels, the kernel needs to be applied in its reflected form.
1345             ** That is its values needs to be reversed.
1346             **
1347             ** Correlation is actually the same as this but without reflecting
1348             ** the kernel, and thus 'lower-level' that Convolution.  However
1349             ** as Convolution is the more common method used, and it does not
1350             ** really cost us much in terms of processing to use a reflected
1351             ** kernel, so it is Convolution that is implemented.
1352             **
1353             ** Correlation will have its kernel reflected before calling
1354             ** this function to do a Convolve.
1355             **
1356             ** For more details of Correlation vs Convolution see
1357             **   http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
1358             */
1359             if (((channel & SyncChannels) != 0 ) &&
1360                       (image->matte == MagickTrue))
1361               { /* Channel has a 'Sync' Flag, and Alpha Channel enabled.
1362                 ** Weight the color channels with Alpha Channel so that
1363                 ** transparent pixels are not part of the results.
1364                 */
1365                 MagickRealType
1366                   alpha,  /* color channel weighting : kernel*alpha  */
1367                   gamma;  /* divisor, sum of weighting values */
1368
1369                 gamma=0.0;
1370                 k = &kernel->values[ kernel->width*kernel->height-1 ];
1371                 k_pixels = p;
1372                 k_indexes = p_indexes;
1373                 for (v=0; v < (long) kernel->height; v++) {
1374                   for (u=0; u < (long) kernel->width; u++, k--) {
1375                     if ( IsNan(*k) ) continue;
1376                     alpha=(*k)*(QuantumScale*(QuantumRange-
1377                                           k_pixels[u].opacity));
1378                     gamma += alpha;
1379                     result.red     += alpha*k_pixels[u].red;
1380                     result.green   += alpha*k_pixels[u].green;
1381                     result.blue    += alpha*k_pixels[u].blue;
1382                     result.opacity += (*k)*(QuantumRange-k_pixels[u].opacity);
1383                     if ( image->colorspace == CMYKColorspace)
1384                       result.index   += alpha*k_indexes[u];
1385                   }
1386                   k_pixels += image->columns+kernel->width;
1387                   k_indexes += image->columns+kernel->width;
1388                 }
1389                 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1390                 result.red *= gamma;
1391                 result.green *= gamma;
1392                 result.blue *= gamma;
1393                 result.opacity *= gamma;
1394                 result.index *= gamma;
1395               }
1396             else
1397               {
1398                 /* No 'Sync' flag, or no Alpha involved.
1399                 ** Convolution is simple individual channel weigthed sum.
1400                 */
1401                 k = &kernel->values[ kernel->width*kernel->height-1 ];
1402                 k_pixels = p;
1403                 k_indexes = p_indexes;
1404                 for (v=0; v < (long) kernel->height; v++) {
1405                   for (u=0; u < (long) kernel->width; u++, k--) {
1406                     if ( IsNan(*k) ) continue;
1407                     result.red     += (*k)*k_pixels[u].red;
1408                     result.green   += (*k)*k_pixels[u].green;
1409                     result.blue    += (*k)*k_pixels[u].blue;
1410                     result.opacity += (*k)*(QuantumRange-k_pixels[u].opacity);
1411                     if ( image->colorspace == CMYKColorspace)
1412                       result.index   += (*k)*k_indexes[u];
1413                   }
1414                   k_pixels += image->columns+kernel->width;
1415                   k_indexes += image->columns+kernel->width;
1416                 }
1417               }
1418             break;
1419
1420         case ErodeMorphology:
1421             /* Minimum Value within kernel neighbourhood
1422             **
1423             ** NOTE that the kernel is not reflected for this operation!
1424             **
1425             ** NOTE: in normal Greyscale Morphology, the kernel value should
1426             ** be added to the real value, this is currently not done, due to
1427             ** the nature of the boolean kernels being used.
1428             */
1429             k = kernel->values;
1430             k_pixels = p;
1431             k_indexes = p_indexes;
1432             for (v=0; v < (long) kernel->height; v++) {
1433               for (u=0; u < (long) kernel->width; u++, k++) {
1434                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
1435                 Minimize(min.red,     (double) k_pixels[u].red);
1436                 Minimize(min.green,   (double) k_pixels[u].green);
1437                 Minimize(min.blue,    (double) k_pixels[u].blue);
1438                 Minimize(min.opacity,
1439                             QuantumRange-(double) k_pixels[u].opacity);
1440                 if ( image->colorspace == CMYKColorspace)
1441                   Minimize(min.index,   (double) k_indexes[u]);
1442               }
1443               k_pixels += image->columns+kernel->width;
1444               k_indexes += image->columns+kernel->width;
1445             }
1446             break;
1447
1448
1449         case DilateMorphology:
1450             /* Maximum Value within kernel neighbourhood
1451             **
1452             ** NOTE for correct working of this operation for asymetrical
1453             ** kernels, the kernel needs to be applied in its reflected form.
1454             ** That is its values needs to be reversed.
1455             **
1456             ** NOTE: in normal Greyscale Morphology, the kernel value should
1457             ** be added to the real value, this is currently not done, due to
1458             ** the nature of the boolean kernels being used.
1459             **
1460             */
1461             k = &kernel->values[ kernel->width*kernel->height-1 ];
1462             k_pixels = p;
1463             k_indexes = p_indexes;
1464             for (v=0; v < (long) kernel->height; v++) {
1465               for (u=0; u < (long) kernel->width; u++, k--) {
1466                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
1467                 Maximize(max.red,     (double) k_pixels[u].red);
1468                 Maximize(max.green,   (double) k_pixels[u].green);
1469                 Maximize(max.blue,    (double) k_pixels[u].blue);
1470                 Maximize(max.opacity,
1471                             QuantumRange-(double) k_pixels[u].opacity);
1472                 if ( image->colorspace == CMYKColorspace)
1473                   Maximize(max.index,   (double) k_indexes[u]);
1474               }
1475               k_pixels += image->columns+kernel->width;
1476               k_indexes += image->columns+kernel->width;
1477             }
1478             break;
1479
1480         case HitAndMissMorphology:
1481         case ThinningMorphology:
1482         case ThickenMorphology:
1483             /* Minimum of Foreground Pixel minus Maxumum of Background Pixels
1484             **
1485             ** NOTE that the kernel is not reflected for this operation,
1486             ** and consists of both foreground and background pixel
1487             ** neighbourhoods, 0.0 for background, and 1.0 for foreground
1488             ** with either Nan or 0.5 values for don't care.
1489             **
1490             ** Note that this can produce negative results, though really
1491             ** only a positive match has any real value.
1492             */
1493             k = kernel->values;
1494             k_pixels = p;
1495             k_indexes = p_indexes;
1496             for (v=0; v < (long) kernel->height; v++) {
1497               for (u=0; u < (long) kernel->width; u++, k++) {
1498                 if ( IsNan(*k) ) continue;
1499                 if ( (*k) > 0.7 )
1500                 { /* minimim of foreground pixels */
1501                   Minimize(min.red,     (double) k_pixels[u].red);
1502                   Minimize(min.green,   (double) k_pixels[u].green);
1503                   Minimize(min.blue,    (double) k_pixels[u].blue);
1504                   Minimize(min.opacity,
1505                               QuantumRange-(double) k_pixels[u].opacity);
1506                   if ( image->colorspace == CMYKColorspace)
1507                     Minimize(min.index,   (double) k_indexes[u]);
1508                 }
1509                 else if ( (*k) < 0.3 )
1510                 { /* maximum of background pixels */
1511                   Maximize(max.red,     (double) k_pixels[u].red);
1512                   Maximize(max.green,   (double) k_pixels[u].green);
1513                   Maximize(max.blue,    (double) k_pixels[u].blue);
1514                   Maximize(max.opacity,
1515                               QuantumRange-(double) k_pixels[u].opacity);
1516                   if ( image->colorspace == CMYKColorspace)
1517                     Maximize(max.index,   (double) k_indexes[u]);
1518                 }
1519               }
1520               k_pixels += image->columns+kernel->width;
1521               k_indexes += image->columns+kernel->width;
1522             }
1523             /* Pattern Match  only if min fg larger than min bg pixels */
1524             min.red     -= max.red;     Maximize( min.red,     0.0 );
1525             min.green   -= max.green;   Maximize( min.green,   0.0 );
1526             min.blue    -= max.blue;    Maximize( min.blue,    0.0 );
1527             min.opacity -= max.opacity; Maximize( min.opacity, 0.0 );
1528             min.index   -= max.index;   Maximize( min.index,   0.0 );
1529             break;
1530
1531         case ErodeIntensityMorphology:
1532             /* Select Pixel with Minimum Intensity within kernel neighbourhood
1533             **
1534             ** WARNING: the intensity test fails for CMYK and does not
1535             ** take into account the moderating effect of teh alpha channel
1536             ** on the intensity.
1537             **
1538             ** NOTE that the kernel is not reflected for this operation!
1539             */
1540             k = kernel->values;
1541             k_pixels = p;
1542             k_indexes = p_indexes;
1543             for (v=0; v < (long) kernel->height; v++) {
1544               for (u=0; u < (long) kernel->width; u++, k++) {
1545                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
1546                 if ( result.red == 0.0 ||
1547                      PixelIntensity(&(k_pixels[u])) < PixelIntensity(q) ) {
1548                   /* copy the whole pixel - no channel selection */
1549                   *q = k_pixels[u];
1550                   if ( result.red > 0.0 ) changed++;
1551                   result.red = 1.0;
1552                 }
1553               }
1554               k_pixels += image->columns+kernel->width;
1555               k_indexes += image->columns+kernel->width;
1556             }
1557             break;
1558
1559         case DilateIntensityMorphology:
1560             /* Select Pixel with Maximum Intensity within kernel neighbourhood
1561             **
1562             ** WARNING: the intensity test fails for CMYK and does not
1563             ** take into account the moderating effect of teh alpha channel
1564             ** on the intensity.
1565             **
1566             ** NOTE for correct working of this operation for asymetrical
1567             ** kernels, the kernel needs to be applied in its reflected form.
1568             ** That is its values needs to be reversed.
1569             */
1570             k = &kernel->values[ kernel->width*kernel->height-1 ];
1571             k_pixels = p;
1572             k_indexes = p_indexes;
1573             for (v=0; v < (long) kernel->height; v++) {
1574               for (u=0; u < (long) kernel->width; u++, k--) {
1575                 if ( IsNan(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
1576                 if ( result.red == 0.0 ||
1577                      PixelIntensity(&(k_pixels[u])) > PixelIntensity(q) ) {
1578                   /* copy the whole pixel - no channel selection */
1579                   *q = k_pixels[u];
1580                   if ( result.red > 0.0 ) changed++;
1581                   result.red = 1.0;
1582                 }
1583               }
1584               k_pixels += image->columns+kernel->width;
1585               k_indexes += image->columns+kernel->width;
1586             }
1587             break;
1588
1589
1590         case DistanceMorphology:
1591             /* Add kernel Value and select the minimum value found.
1592             ** The result is a iterative distance from edge of image shape.
1593             **
1594             ** All Distance Kernels are symetrical, but that may not always
1595             ** be the case. For example how about a distance from left edges?
1596             ** To work correctly with asymetrical kernels the reflected kernel
1597             ** needs to be applied.
1598             **
1599             ** Actually this is really a GreyErode with a negative kernel!
1600             **
1601             */
1602             k = &kernel->values[ kernel->width*kernel->height-1 ];
1603             k_pixels = p;
1604             k_indexes = p_indexes;
1605             for (v=0; v < (long) kernel->height; v++) {
1606               for (u=0; u < (long) kernel->width; u++, k--) {
1607                 if ( IsNan(*k) ) continue;
1608                 Minimize(result.red,     (*k)+k_pixels[u].red);
1609                 Minimize(result.green,   (*k)+k_pixels[u].green);
1610                 Minimize(result.blue,    (*k)+k_pixels[u].blue);
1611                 Minimize(result.opacity, (*k)+QuantumRange-k_pixels[u].opacity);
1612                 if ( image->colorspace == CMYKColorspace)
1613                   Minimize(result.index,   (*k)+k_indexes[u]);
1614               }
1615               k_pixels += image->columns+kernel->width;
1616               k_indexes += image->columns+kernel->width;
1617             }
1618             break;
1619
1620         case UndefinedMorphology:
1621         default:
1622             break; /* Do nothing */
1623       }
1624       /* Final mathematics of results (combine with original image?)
1625       **
1626       ** NOTE: Difference Morphology operators Edge* and *Hat could also
1627       ** be done here but works better with iteration as a image difference
1628       ** in the controling function (below).  Thicken and Thinning however
1629       ** should be done here so thay can be iterated correctly.
1630       */
1631       switch ( method ) {
1632         case HitAndMissMorphology:
1633         case ErodeMorphology:
1634           result = min;    /* minimum of neighbourhood */
1635           break;
1636         case DilateMorphology:
1637           result = max;    /* maximum of neighbourhood */
1638           break;
1639         case ThinningMorphology:
1640           /* subtract pattern match from original */
1641           result.red     -= min.red;
1642           result.green   -= min.green;
1643           result.blue    -= min.blue;
1644           result.opacity -= min.opacity;
1645           result.index   -= min.index;
1646           break;
1647         case ThickenMorphology:
1648           /* Union with original image (maximize) - or should this be + */
1649           Maximize( result.red,     min.red );
1650           Maximize( result.green,   min.green );
1651           Maximize( result.blue,    min.blue );
1652           Maximize( result.opacity, min.opacity );
1653           Maximize( result.index,   min.index );
1654           break;
1655         default:
1656           /* result directly calculated or assigned */
1657           break;
1658       }
1659       /* Assign the resulting pixel values - Clamping Result */
1660       switch ( method ) {
1661         case UndefinedMorphology:
1662         case DilateIntensityMorphology:
1663         case ErodeIntensityMorphology:
1664           break;  /* full pixel was directly assigned - not a channel method */
1665         default:
1666           if ((channel & RedChannel) != 0)
1667             q->red = ClampToQuantum(result.red);
1668           if ((channel & GreenChannel) != 0)
1669             q->green = ClampToQuantum(result.green);
1670           if ((channel & BlueChannel) != 0)
1671             q->blue = ClampToQuantum(result.blue);
1672           if ((channel & OpacityChannel) != 0
1673               && image->matte == MagickTrue )
1674             q->opacity = ClampToQuantum(QuantumRange-result.opacity);
1675           if ((channel & IndexChannel) != 0
1676               && image->colorspace == CMYKColorspace)
1677             q_indexes[x] = ClampToQuantum(result.index);
1678           break;
1679       }
1680       /* Count up changed pixels */
1681       if (   ( p[r].red != q->red )
1682           || ( p[r].green != q->green )
1683           || ( p[r].blue != q->blue )
1684           || ( p[r].opacity != q->opacity )
1685           || ( image->colorspace == CMYKColorspace &&
1686                   p_indexes[r] != q_indexes[x] ) )
1687         changed++;  /* The pixel had some value changed! */
1688       p++;
1689       q++;
1690     } /* x */
1691     sync=SyncCacheViewAuthenticPixels(q_view,exception);
1692     if (sync == MagickFalse)
1693       status=MagickFalse;
1694     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1695       {
1696         MagickBooleanType
1697           proceed;
1698
1699 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1700   #pragma omp critical (MagickCore_MorphologyImage)
1701 #endif
1702         proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
1703         if (proceed == MagickFalse)
1704           status=MagickFalse;
1705       }
1706   } /* y */
1707   result_image->type=image->type;
1708   q_view=DestroyCacheView(q_view);
1709   p_view=DestroyCacheView(p_view);
1710   return(status ? (unsigned long) changed : 0);
1711 }
1712
1713
1714 MagickExport Image *MorphologyImage(const Image *image, const MorphologyMethod
1715   method, const long iterations,const KernelInfo *kernel, ExceptionInfo
1716   *exception)
1717 {
1718   Image
1719     *morphology_image;
1720
1721   morphology_image=MorphologyImageChannel(image,DefaultChannels,method,
1722     iterations,kernel,exception);
1723   return(morphology_image);
1724 }
1725
1726
1727 MagickExport Image *MorphologyImageChannel(const Image *image,
1728   const ChannelType channel,const MorphologyMethod method,
1729   const long iterations,const KernelInfo *kernel,ExceptionInfo *exception)
1730 {
1731   Image
1732     *new_image,
1733     *old_image,
1734     *grad_image;
1735
1736   KernelInfo
1737     *curr_kernel,
1738     *this_kernel;
1739
1740   MorphologyMethod
1741     curr_method;
1742
1743   unsigned long
1744     count,
1745     limit,
1746     changed,
1747     total_changed,
1748     kernel_number;
1749
1750   assert(image != (Image *) NULL);
1751   assert(image->signature == MagickSignature);
1752   assert(kernel != (KernelInfo *) NULL);
1753   assert(kernel->signature == MagickSignature);
1754   assert(exception != (ExceptionInfo *) NULL);
1755   assert(exception->signature == MagickSignature);
1756
1757   if ( iterations == 0 )
1758     return((Image *)NULL); /* null operation - nothing to do! */
1759
1760   /* kernel must be valid at this point
1761    * (except maybe for posible future morphology methods like "Prune"
1762    */
1763   assert(kernel != (KernelInfo *)NULL);
1764
1765   new_image  = (Image *) NULL;          /* for cleanup */
1766   old_image  = (Image *) NULL;
1767   grad_image = (Image *) NULL;
1768   curr_kernel = (KernelInfo *) NULL;
1769   count = 0;                           /* interation count */
1770   changed = 1;                         /* was last run succesfull */
1771   curr_kernel = (KernelInfo *)kernel;  /* allow kernel and method */
1772   curr_method = method;                /* to be changed as nessary */
1773
1774   limit = (unsigned long) iterations;
1775   if ( iterations < 0 )
1776     limit = image->columns > image->rows ? image->columns : image->rows;
1777
1778   /* Third-level morphology methods */
1779   switch( curr_method ) {
1780     case EdgeMorphology:
1781       grad_image = MorphologyImageChannel(image, channel,
1782             DilateMorphology, iterations, curr_kernel, exception);
1783       if ( grad_image == (Image *) NULL )
1784         goto error_cleanup;
1785       /* FALL-THRU */
1786     case EdgeInMorphology:
1787       curr_method = ErodeMorphology;
1788       break;
1789     case EdgeOutMorphology:
1790       curr_method = DilateMorphology;
1791       break;
1792     case TopHatMorphology:
1793       curr_method = OpenMorphology;
1794       break;
1795     case BottomHatMorphology:
1796       curr_method = CloseMorphology;
1797       break;
1798     default:
1799       break; /* not a third-level method */
1800   }
1801
1802   /* Second-level morphology methods */
1803   switch( curr_method ) {
1804     case OpenMorphology:
1805       /* Open is a Erode then a Dilate without reflection */
1806       new_image = MorphologyImageChannel(image, channel,
1807             ErodeMorphology, iterations, curr_kernel, exception);
1808       if (new_image == (Image *) NULL)
1809         goto error_cleanup;
1810       curr_method = DilateMorphology;
1811       break;
1812     case OpenIntensityMorphology:
1813       new_image = MorphologyImageChannel(image, channel,
1814             ErodeIntensityMorphology, iterations, curr_kernel, exception);
1815       if (new_image == (Image *) NULL)
1816         goto error_cleanup;
1817       curr_method = DilateIntensityMorphology;
1818       break;
1819
1820     case CloseMorphology:
1821       /* Close is a Dilate then Erode using reflected kernel */
1822       /* A reflected kernel is needed for a Close */
1823       if ( curr_kernel == kernel )
1824         curr_kernel = CloneKernelInfo(kernel);
1825       if (curr_kernel == (KernelInfo *) NULL)
1826         goto error_cleanup;
1827       RotateKernelInfo(curr_kernel,180);
1828       new_image = MorphologyImageChannel(image, channel,
1829             DilateMorphology, iterations, curr_kernel, exception);
1830       if (new_image == (Image *) NULL)
1831         goto error_cleanup;
1832       curr_method = ErodeMorphology;
1833       break;
1834     case CloseIntensityMorphology:
1835       /* A reflected kernel is needed for a Close */
1836       if ( curr_kernel == kernel )
1837         curr_kernel = CloneKernelInfo(kernel);
1838       if (curr_kernel == (KernelInfo *) NULL)
1839         goto error_cleanup;
1840       RotateKernelInfo(curr_kernel,180);
1841       new_image = MorphologyImageChannel(image, channel,
1842             DilateIntensityMorphology, iterations, curr_kernel, exception);
1843       if (new_image == (Image *) NULL)
1844         goto error_cleanup;
1845       curr_method = ErodeIntensityMorphology;
1846       break;
1847
1848     case CorrelateMorphology:
1849       /* A Correlation is actually a Convolution with a reflected kernel.
1850       ** However a Convolution is a weighted sum with a reflected kernel.
1851       ** It may seem stange to convert a Correlation into a Convolution
1852       ** as the Correleation is the simplier method, but Convolution is
1853       ** much more commonly used, and it makes sense to implement it directly
1854       ** so as to avoid the need to duplicate the kernel when it is not
1855       ** required (which is typically the default).
1856       */
1857       if ( curr_kernel == kernel )
1858         curr_kernel = CloneKernelInfo(kernel);
1859       if (curr_kernel == (KernelInfo *) NULL)
1860         goto error_cleanup;
1861       RotateKernelInfo(curr_kernel,180);
1862       curr_method = ConvolveMorphology;
1863       /* FALL-THRU into Correlate (weigthed sum without reflection) */
1864
1865     case ConvolveMorphology:
1866       { /* Scale or Normalize kernel, according to user wishes
1867         ** before using it for the Convolve/Correlate method.
1868         **
1869         ** FUTURE: provide some way for internal functions to disable
1870         ** user bias and scaling effects.
1871         */
1872         const char
1873           *artifact = GetImageArtifact(image,"convolve:scale");
1874         if ( artifact != (char *)NULL ) {
1875           GeometryFlags
1876             flags;
1877           GeometryInfo
1878             args;
1879
1880           if ( curr_kernel == kernel )
1881             curr_kernel = CloneKernelInfo(kernel);
1882           if (curr_kernel == (KernelInfo *) NULL)
1883             goto error_cleanup;
1884           args.rho = 1.0;
1885           flags = (GeometryFlags) ParseGeometry(artifact, &args);
1886           ScaleKernelInfo(curr_kernel, args.rho, flags);
1887         }
1888       }
1889       /* FALL-THRU to do the first, and typically the only iteration */
1890
1891     default:
1892       /* Do a single iteration using the Low-Level Morphology method!
1893       ** This ensures a "new_image" has been generated, but allows us to skip
1894       ** the creation of 'old_image' if no more iterations are needed.
1895       **
1896       ** The "curr_method" should also be set to a low-level method that is
1897       ** understood by the MorphologyPrimative() internal function.
1898       */
1899       new_image=CloneImage(image,0,0,MagickTrue,exception);
1900       if (new_image == (Image *) NULL)
1901         goto error_cleanup;
1902       if (SetImageStorageClass(new_image,DirectClass) == MagickFalse)
1903         {
1904           InheritException(exception,&new_image->exception);
1905           goto exit_cleanup;
1906         }
1907       count++;  /* iteration count */
1908       changed = MorphologyPrimative(image,new_image,curr_method,channel,
1909            curr_kernel, exception);
1910       if ( GetImageArtifact(image,"verbose") != (const char *) NULL )
1911         fprintf(stderr, "Morphology %s:%lu.%lu => Changed %lu\n",
1912               MagickOptionToMnemonic(MagickMorphologyOptions, curr_method),
1913               count, 0L, changed);
1914       break;
1915   }
1916   /* At this point
1917    *   + "curr_method" should be set to a low-level morphology method
1918    *   + "count=1" if the first iteration of the first kernel has been done.
1919    *   + "new_image" is defined and contains the current resulting image
1920    */
1921
1922   /* The Hit-And-Miss Morphology is not 'iterated' against the resulting
1923   ** image from the previous morphology step.  It must always be applied
1924   ** to the original image, with the results collected into a union (maximimum
1925   ** or lighten) of all the results.  Multiple kernels may be applied but
1926   ** an iteration of the morphology does nothing, so is ignored.
1927   **
1928   ** The first kernel is guranteed to have been done, so lets continue the
1929   ** process, with the other kernels in the kernel list.
1930   */
1931   if ( method == HitAndMissMorphology )
1932   {
1933     if ( curr_kernel->next != (KernelInfo *) NULL ) {
1934       /* create a second working image */
1935       old_image = CloneImage(image,0,0,MagickTrue,exception);
1936       if (old_image == (Image *) NULL)
1937         goto error_cleanup;
1938       if (SetImageStorageClass(old_image,DirectClass) == MagickFalse)
1939         {
1940           InheritException(exception,&old_image->exception);
1941           goto exit_cleanup;
1942         }
1943
1944       /* loop through rest of the kernels */
1945       this_kernel=curr_kernel->next;
1946       kernel_number=1;
1947       while( this_kernel != (KernelInfo *) NULL )
1948         {
1949           changed = MorphologyPrimative(image,old_image,curr_method,channel,
1950               this_kernel,exception);
1951           (void) CompositeImageChannel(new_image,
1952             (ChannelType) (channel & ~SyncChannels), LightenCompositeOp,
1953             old_image, 0, 0);
1954           if ( GetImageArtifact(image,"verbose") != (const char *) NULL )
1955             fprintf(stderr, "Morphology %s:%lu.%lu => Changed %lu\n",
1956                   MagickOptionToMnemonic(MagickMorphologyOptions, curr_method),
1957                   count, kernel_number, changed);
1958           this_kernel = this_kernel->next;
1959           kernel_number++;
1960         }
1961       old_image=DestroyImage(old_image);
1962     }
1963     goto exit_cleanup;
1964   }
1965
1966   /* Repeat the low-level morphology over all kernels
1967      until iteration count limit or no change from any kernel is found */
1968   if ( ( count < limit && changed > 0 ) ||
1969        curr_kernel->next != (KernelInfo *) NULL ) {
1970
1971     /* create a second working image */
1972     old_image = CloneImage(image,0,0,MagickTrue,exception);
1973     if (old_image == (Image *) NULL)
1974       goto error_cleanup;
1975     if (SetImageStorageClass(old_image,DirectClass) == MagickFalse)
1976       {
1977         InheritException(exception,&old_image->exception);
1978         goto exit_cleanup;
1979       }
1980
1981     /* reset variables for the first/next iteration, or next kernel) */
1982     kernel_number = 0;
1983     this_kernel = curr_kernel;
1984     total_changed = count != 0 ? changed : 0;
1985     if ( count != 0 && this_kernel != (KernelInfo *) NULL ) {
1986       count = 0;  /* first iteration is not yet finished! */
1987       this_kernel = curr_kernel->next;
1988       kernel_number = 1;
1989       total_changed = changed;
1990     }
1991
1992     while ( count < limit ) {
1993       count++;
1994       while ( this_kernel != (KernelInfo *) NULL ) {
1995         Image *tmp = old_image;
1996         old_image = new_image;
1997         new_image = tmp;
1998         changed = MorphologyPrimative(old_image,new_image,curr_method,channel,
1999                           this_kernel,exception);
2000         if ( GetImageArtifact(image,"verbose") != (const char *) NULL )
2001           fprintf(stderr, "Morphology %s:%lu.%lu => Changed %lu\n",
2002                 MagickOptionToMnemonic(MagickMorphologyOptions, curr_method),
2003                 count, kernel_number, changed);
2004         total_changed += changed;
2005         this_kernel = this_kernel->next;
2006         kernel_number++;
2007       }
2008       if ( total_changed == 0 )
2009         break;  /* no changes after processing all kernels - ABORT */
2010       /* prepare for next loop */
2011       total_changed = 0;
2012       kernel_number = 0;
2013       this_kernel = curr_kernel;
2014     }
2015     old_image=DestroyImage(old_image);
2016   }
2017
2018   /* finished with kernel - destary any copy that was made */
2019   if ( curr_kernel != kernel )
2020     curr_kernel=DestroyKernelInfo(curr_kernel);
2021
2022   /* Third-level Subtractive methods post-processing
2023   **
2024   ** The removal of any 'Sync' channel flag in the Image Compositon below
2025   ** ensures the compose method is applied in a purely mathematical way, only
2026   ** the selected channels, without any normal 'alpha blending' normally
2027   ** associated with the compose method.
2028   **
2029   ** Note "method" here is the 'original' morphological method, and not the
2030   ** 'current' morphological method used above to generate "new_image".
2031   */
2032   switch( method ) {
2033     case EdgeOutMorphology:
2034     case EdgeInMorphology:
2035     case TopHatMorphology:
2036     case BottomHatMorphology:
2037       /* Get Difference relative to the original image */
2038       (void) CompositeImageChannel(new_image, (ChannelType) (channel & ~SyncChannels),
2039            DifferenceCompositeOp, image, 0, 0);
2040       break;
2041     case EdgeMorphology:
2042       /* Difference the Eroded image from the saved Dilated image */
2043       (void) CompositeImageChannel(new_image, (ChannelType) (channel & ~SyncChannels),
2044            DifferenceCompositeOp, grad_image, 0, 0);
2045       grad_image=DestroyImage(grad_image);
2046       break;
2047     default:
2048       break;
2049   }
2050
2051   return(new_image);
2052
2053   /* Yes goto's are bad, but in this case it makes cleanup lot more efficient */
2054 error_cleanup:
2055   if ( new_image != (Image *) NULL )
2056     DestroyImage(new_image);
2057 exit_cleanup:
2058   if ( old_image != (Image *) NULL )
2059     DestroyImage(old_image);
2060   if ( curr_kernel != (KernelInfo *) NULL &&  curr_kernel != kernel )
2061     curr_kernel=DestroyKernelInfo(curr_kernel);
2062   return(new_image);
2063 }
2064 \f
2065 /*
2066 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2067 %                                                                             %
2068 %                                                                             %
2069 %                                                                             %
2070 +     R o t a t e K e r n e l I n f o                                         %
2071 %                                                                             %
2072 %                                                                             %
2073 %                                                                             %
2074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2075 %
2076 %  RotateKernelInfo() rotates the kernel by the angle given.  Currently it is
2077 %  restricted to 90 degree angles, but this may be improved in the future.
2078 %
2079 %  The format of the RotateKernelInfo method is:
2080 %
2081 %      void RotateKernelInfo(KernelInfo *kernel, double angle)
2082 %
2083 %  A description of each parameter follows:
2084 %
2085 %    o kernel: the Morphology/Convolution kernel
2086 %
2087 %    o angle: angle to rotate in degrees
2088 %
2089 % This function is only internel to this module, as it is not finalized,
2090 % especially with regard to non-orthogonal angles, and rotation of larger
2091 % 2D kernels.
2092 */
2093 static void RotateKernelInfo(KernelInfo *kernel, double angle)
2094 {
2095   /* angle the lower kernels first */
2096   if ( kernel->next != (KernelInfo *) NULL)
2097     RotateKernelInfo(kernel->next, angle);
2098
2099   /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
2100   **
2101   ** TODO: expand beyond simple 90 degree rotates, flips and flops
2102   */
2103
2104   /* Modulus the angle */
2105   angle = fmod(angle, 360.0);
2106   if ( angle < 0 )
2107     angle += 360.0;
2108
2109   if ( 315.0 < angle || angle <= 45.0 )
2110     return;   /* no change! - At least at this time */
2111
2112   switch (kernel->type) {
2113     /* These built-in kernels are cylindrical kernels, rotating is useless */
2114     case GaussianKernel:
2115     case LaplacianKernel:
2116     case LOGKernel:
2117     case DOGKernel:
2118     case DiskKernel:
2119     case ChebyshevKernel:
2120     case ManhattenKernel:
2121     case EuclideanKernel:
2122       return;
2123
2124     /* These may be rotatable at non-90 angles in the future */
2125     /* but simply rotating them in multiples of 90 degrees is useless */
2126     case SquareKernel:
2127     case DiamondKernel:
2128     case PlusKernel:
2129       return;
2130
2131     /* These only allows a +/-90 degree rotation (by transpose) */
2132     /* A 180 degree rotation is useless */
2133     case BlurKernel:
2134     case RectangleKernel:
2135       if ( 135.0 < angle && angle <= 225.0 )
2136         return;
2137       if ( 225.0 < angle && angle <= 315.0 )
2138         angle -= 180;
2139       break;
2140
2141     /* these are freely rotatable in 90 degree units */
2142     case CometKernel:
2143     case UndefinedKernel:
2144     case UserDefinedKernel:
2145       break;
2146   }
2147   if ( 135.0 < angle && angle <= 225.0 )
2148     {
2149       /* For a 180 degree rotation - also know as a reflection */
2150       /* This is actually a very very common operation! */
2151       /* Basically all that is needed is a reversal of the kernel data! */
2152       unsigned long
2153         i,j;
2154       register double
2155         *k,t;
2156
2157       k=kernel->values;
2158       for ( i=0, j=kernel->width*kernel->height-1;  i<j;  i++, j--)
2159         t=k[i],  k[i]=k[j],  k[j]=t;
2160
2161       kernel->x = (long) kernel->width  - kernel->x - 1;
2162       kernel->y = (long) kernel->height - kernel->y - 1;
2163       angle = fmod(angle+180.0, 360.0);
2164     }
2165   if ( 45.0 < angle && angle <= 135.0 )
2166     { /* Do a transpose and a flop, of the image, which results in a 90
2167        * degree rotation using two mirror operations.
2168        *
2169        * WARNING: this assumes the original image was a 1 dimentional image
2170        * but currently that is the only built-ins it is applied to.
2171        */
2172       long
2173         t;
2174       t = (long) kernel->width;
2175       kernel->width = kernel->height;
2176       kernel->height = (unsigned long) t;
2177       t = kernel->x;
2178       kernel->x = kernel->y;
2179       kernel->y = t;
2180       angle = fmod(450.0 - angle, 360.0);
2181     }
2182   /* At this point angle should be between -45 (315) and +45 degrees
2183    * In the future some form of non-orthogonal angled rotates could be
2184    * performed here, posibily with a linear kernel restriction.
2185    */
2186
2187 #if 0
2188     Not currently in use!
2189     { /* Do a flop, this assumes kernel is horizontally symetrical.
2190        * Each row of the kernel needs to be reversed!
2191        */
2192       unsigned long
2193         y;
2194       register long
2195         x,r;
2196       register double
2197         *k,t;
2198
2199       for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2200         for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2201           t=k[x],  k[x]=k[r],  k[r]=t;
2202
2203       kernel->x = kernel->width - kernel->x - 1;
2204       angle = fmod(angle+180.0, 360.0);
2205     }
2206 #endif
2207   return;
2208 }
2209 \f
2210 /*
2211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2212 %                                                                             %
2213 %                                                                             %
2214 %                                                                             %
2215 %     S c a l e K e r n e l I n f o                                           %
2216 %                                                                             %
2217 %                                                                             %
2218 %                                                                             %
2219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2220 %
2221 %  ScaleKernelInfo() scales the given kernel list by the given amount, with or
2222 %  without normalization of the sum of the kernel values (as per given flags).
2223 %
2224 %  By default (no flags given) the values within the kernel is scaled
2225 %  directly using given scaling factor without change.
2226 %
2227 %  If any 'normalize_flags' are given the kernel will first be normalized and
2228 %  then further scaled by the scaling factor value given.  A 'PercentValue'
2229 %  flag will cause the given scaling factor to be divided by one hundred
2230 %  percent.
2231 %
2232 %  Kernel normalization ('normalize_flags' given) is designed to ensure that
2233 %  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
2234 %  morphology methods will fall into -1.0 to +1.0 range.  Note that for
2235 %  non-HDRI versions of IM this may cause images to have any negative results
2236 %  clipped, unless some 'bias' is used.
2237 %
2238 %  More specifically.  Kernels which only contain positive values (such as a
2239 %  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
2240 %  ensuring a 0.0 to +1.0 output range for non-HDRI images.
2241 %
2242 %  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
2243 %  the kernel will be scaled by the absolute of the sum of kernel values, so
2244 %  that it will generally fall within the +/- 1.0 range.
2245 %
2246 %  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
2247 %  will be scaled by just the sum of the postive values, so that its output
2248 %  range will again fall into the  +/- 1.0 range.
2249 %
2250 %  For special kernels designed for locating shapes using 'Correlate', (often
2251 %  only containing +1 and -1 values, representing foreground/brackground
2252 %  matching) a special normalization method is provided to scale the positive
2253 %  values seperatally to those of the negative values, so the kernel will be
2254 %  forced to become a zero-sum kernel better suited to such searches.
2255 %
2256 %  WARNING: Correct normalization of the kernel assumes that the '*_range'
2257 %  attributes within the kernel structure have been correctly set during the
2258 %  kernels creation.
2259 %
2260 %  NOTE: The values used for 'normalize_flags' have been selected specifically
2261 %  to match the use of geometry options, so that '!' means NormalizeValue,
2262 %  '^' means CorrelateNormalizeValue, and '%' means PercentValue.  All other
2263 %  GeometryFlags values are ignored.
2264 %
2265 %  The format of the ScaleKernelInfo method is:
2266 %
2267 %      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
2268 %               const MagickStatusType normalize_flags )
2269 %
2270 %  A description of each parameter follows:
2271 %
2272 %    o kernel: the Morphology/Convolution kernel
2273 %
2274 %    o scaling_factor:
2275 %             multiply all values (after normalization) by this factor if not
2276 %             zero.  If the kernel is normalized regardless of any flags.
2277 %
2278 %    o normalize_flags:
2279 %             GeometryFlags defining normalization method to use.
2280 %             specifically: NormalizeValue, CorrelateNormalizeValue,
2281 %                           and/or PercentValue
2282 %
2283 % This function is internal to this module only at this time, but can be
2284 % exported to other modules if needed.
2285 */
2286 MagickExport void ScaleKernelInfo(KernelInfo *kernel,
2287   const double scaling_factor,const GeometryFlags normalize_flags)
2288 {
2289   register long
2290     i;
2291
2292   register double
2293     pos_scale,
2294     neg_scale;
2295
2296   /* scale the lower kernels first */
2297   if ( kernel->next != (KernelInfo *) NULL)
2298     ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
2299
2300   pos_scale = 1.0;
2301   if ( (normalize_flags&NormalizeValue) != 0 ) {
2302     /* normalize kernel appropriately */
2303     if ( fabs(kernel->positive_range + kernel->negative_range) > MagickEpsilon )
2304       pos_scale = fabs(kernel->positive_range + kernel->negative_range);
2305     else
2306       pos_scale = kernel->positive_range; /* special zero-summing kernel */
2307   }
2308   /* force kernel into being a normalized zero-summing kernel */
2309   if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
2310     pos_scale = ( fabs(kernel->positive_range) > MagickEpsilon )
2311                  ? kernel->positive_range : 1.0;
2312     neg_scale = ( fabs(kernel->negative_range) > MagickEpsilon )
2313                  ? -kernel->negative_range : 1.0;
2314   }
2315   else
2316     neg_scale = pos_scale;
2317
2318   /* finialize scaling_factor for positive and negative components */
2319   pos_scale = scaling_factor/pos_scale;
2320   neg_scale = scaling_factor/neg_scale;
2321   if ( (normalize_flags&PercentValue) != 0 ) {
2322     pos_scale /= 100.0;
2323     neg_scale /= 100.0;
2324   }
2325
2326   for (i=0; i < (long) (kernel->width*kernel->height); i++)
2327     if ( ! IsNan(kernel->values[i]) )
2328       kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
2329
2330   /* convolution output range */
2331   kernel->positive_range *= pos_scale;
2332   kernel->negative_range *= neg_scale;
2333   /* maximum and minimum values in kernel */
2334   kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
2335   kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
2336
2337   /* swap kernel settings if user scaling factor is negative */
2338   if ( scaling_factor < MagickEpsilon ) {
2339     double t;
2340     t = kernel->positive_range;
2341     kernel->positive_range = kernel->negative_range;
2342     kernel->negative_range = t;
2343     t = kernel->maximum;
2344     kernel->maximum = kernel->minimum;
2345     kernel->minimum = 1;
2346   }
2347
2348   return;
2349 }
2350 \f
2351 /*
2352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2353 %                                                                             %
2354 %                                                                             %
2355 %                                                                             %
2356 +     S h o w K e r n e l I n f o                                             %
2357 %                                                                             %
2358 %                                                                             %
2359 %                                                                             %
2360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2361 %
2362 %  ShowKernelInfo() outputs the details of the given kernel defination to
2363 %  standard error, generally due to a users 'showkernel' option request.
2364 %
2365 %  The format of the ShowKernel method is:
2366 %
2367 %      void ShowKernelInfo(KernelInfo *kernel)
2368 %
2369 %  A description of each parameter follows:
2370 %
2371 %    o kernel: the Morphology/Convolution kernel
2372 %
2373 % This function is internal to this module only at this time. That may change
2374 % in the future.
2375 */
2376 MagickExport void ShowKernelInfo(KernelInfo *kernel)
2377 {
2378   KernelInfo
2379     *k;
2380
2381   long
2382     c, i, u, v;
2383
2384   for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
2385
2386     fprintf(stderr, "Kernel ");
2387     if ( kernel->next != (KernelInfo *) NULL )
2388       fprintf(stderr, " #%ld", c );
2389     fprintf(stderr, " \"%s\" of size %lux%lu%+ld%+ld ",
2390           MagickOptionToMnemonic(MagickKernelOptions, k->type),
2391           kernel->width, k->height,
2392           kernel->x, kernel->y );
2393     fprintf(stderr,
2394           " with values from %.*lg to %.*lg\n",
2395           GetMagickPrecision(), k->minimum,
2396           GetMagickPrecision(), k->maximum);
2397     fprintf(stderr, "Forming convolution output range from %.*lg to %.*lg%s\n",
2398           GetMagickPrecision(), k->negative_range,
2399           GetMagickPrecision(), k->positive_range,
2400           /*kernel->normalized == MagickTrue ? " (normalized)" : */ "" );
2401     for (i=v=0; v < (long) k->height; v++) {
2402       fprintf(stderr,"%2ld:",v);
2403       for (u=0; u < (long) k->width; u++, i++)
2404         if ( IsNan(k->values[i]) )
2405           fprintf(stderr," %*s", GetMagickPrecision()+2, "nan");
2406         else
2407           fprintf(stderr," %*.*lg", GetMagickPrecision()+2,
2408               GetMagickPrecision(), k->values[i]);
2409       fprintf(stderr,"\n");
2410     }
2411   }
2412 }
2413 \f
2414 /*
2415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2416 %                                                                             %
2417 %                                                                             %
2418 %                                                                             %
2419 +     Z e r o K e r n e l N a n s                                             %
2420 %                                                                             %
2421 %                                                                             %
2422 %                                                                             %
2423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2424 %
2425 %  ZeroKernelNans() replaces any special 'nan' value that may be present in
2426 %  the kernel with a zero value.  This is typically done when the kernel will
2427 %  be used in special hardware (GPU) convolution processors, to simply
2428 %  matters.
2429 %
2430 %  The format of the ZeroKernelNans method is:
2431 %
2432 %      voidZeroKernelNans (KernelInfo *kernel)
2433 %
2434 %  A description of each parameter follows:
2435 %
2436 %    o kernel: the Morphology/Convolution kernel
2437 %
2438 % FUTURE: return the information in a string for API usage.
2439 */
2440 MagickExport void ZeroKernelNans(KernelInfo *kernel)
2441 {
2442   register long
2443     i;
2444
2445   /* scale the lower kernels first */
2446   if ( kernel->next != (KernelInfo *) NULL)
2447     ZeroKernelNans(kernel->next);
2448
2449   for (i=0; i < (long) (kernel->width*kernel->height); i++)
2450     if ( IsNan(kernel->values[i]) )
2451       kernel->values[i] = 0.0;
2452
2453   return;
2454 }