]> granicus.if.org Git - imagemagick/blob - magick/morphology.c
Morphology update with requested changes and bug fixes
[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 kernals, 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
80
81 /*
82  * The following test is for special floating point numbers of value NaN (not
83  * a number), that may be used within a Kernel Definition.  NaN's are defined
84  * as part of the IEEE standard for floating point number representation.
85  *
86  * These are used a Kernel value of NaN means that that kernal position is not
87  * part of the normal convolution or morphology process, and thus allowing the
88  * use of 'shaped' kernels.
89  *
90  * Special Properities Two NaN's are never equal, even if they are from the
91  * same variable That is the IsNaN() macro is only true if the value is NaN.
92  */
93 #define IsNan(a)   ((a)!=(a))
94
95 /*
96  * Other global definitions used by module
97  */
98 static inline double MagickMin(const double x,const double y)
99 {
100   return( x < y ? x : y);
101 }
102 static inline double MagickMax(const double x,const double y)
103 {
104   return( x > y ? x : y);
105 }
106 #define Minimize(assign,value) assign=MagickMin(assign,value)
107 #define Maximize(assign,value) assign=MagickMax(assign,value)
108
109 /* Currently these are internal to this module
110  * Eventually these may become 'private' to library
111  * OR may become externally available to API's
112  */
113 static MagickExport void
114   NormalizeKernel(KernelInfo *),
115   RotateKernel(KernelInfo *, double),
116   ShowKernel(KernelInfo *);
117
118 \f
119 /*
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 %                                                                             %
122 %                                                                             %
123 %                                                                             %
124 %     A c q u i r e K e r n e l I n f o                                       %
125 %                                                                             %
126 %                                                                             %
127 %                                                                             %
128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129 %
130 %  AcquireKernelInfo() takes the given string (generally supplied by the
131 %  user) and converts it into a Morphology/Convolution Kernel.  This allows
132 %  users to specify a kernel from a number of pre-defined kernels, or to fully
133 %  specify their own kernel for a specific Convolution or Morphology
134 %  Operation.
135 %
136 %  The kernel so generated can be any rectangular array of floating point
137 %  values (doubles) with the 'control point' or 'pixel being affected'
138 %  anywhere within that array of values.
139 %
140 %  Previously IM was restricted to a square of odd size using the exact
141 %  center as origin, this is no longer the case, and any rectangular kernel
142 %  with any value being declared the origin. This in turn allows the use of
143 %  highly asymmetrical kernels.
144 %
145 %  The floating point values in the kernel can also include a special value
146 %  known as 'nan' or 'not a number' to indicate that this value is not part
147 %  of the kernel array. This allows you to shaped the kernel within its
148 %  rectangular area. That is 'nan' values provide a 'mask' for the kernel
149 %  shape.  However at least one non-nan value must be provided for correct
150 %  working of a kernel.
151 %
152 %  The returned kernel should be free using the DestroyKernelInfo() when you
153 %  are finished with it.
154 %
155 %  Input kernel defintion strings can consist of any of three types.
156 %
157 %    "name:args"
158 %         Select from one of the built in kernels, using the name and
159 %         geometry arguments supplied.  See AcquireKernelBuiltIn()
160 %
161 %    "WxH[+X+Y]:num, num, num ..."
162 %         a kernal of size W by H, with W*H floating point numbers following.
163 %         the 'center' can be optionally be defined at +X+Y (such that +0+0
164 %         is top left corner). If not defined the pixel in the center, for
165 %         odd sizes, or to the immediate top or left of center for even sizes
166 %         is automatically selected.
167 %
168 %    "num, num, num, num, ..."
169 %         list of floating point numbers defining an 'old style' odd sized
170 %         square kernel.  At least 9 values should be provided for a 3x3
171 %         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
172 %         Values can be space or comma separated.  This is not recommended.
173 %
174 %  Note that 'name' kernels will start with an alphabetic character while the
175 %  new kernel specification has a ':' character in its specification string.
176 %  If neither is the case, it is assumed an old style of a simple list of
177 %  numbers generating a odd-sized square kernel has been given.
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 MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
190 {
191   KernelInfo
192     *kernel;
193
194   char
195     token[MaxTextExtent];
196
197   register unsigned long
198     i;
199
200   const char
201     *p;
202
203   MagickStatusType
204     flags;
205
206   GeometryInfo
207     args;
208
209   double
210     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
211
212   assert(kernel_string != (const char *) NULL);
213   SetGeometryInfo(&args);
214
215   /* does it start with an alpha - Return a builtin kernel */
216   GetMagickToken(kernel_string,&p,token);
217   if ( isalpha((int)token[0]) )
218   {
219     long
220       type;
221
222     type=ParseMagickOption(MagickKernelOptions,MagickFalse,token);
223     if ( type < 0 || type == UserDefinedKernel )
224       return((KernelInfo *)NULL);
225
226     while (((isspace((int) ((unsigned char) *p)) != 0) ||
227            (*p == ',') || (*p == ':' )) && (*p != '\0'))
228       p++;
229     flags = ParseGeometry(p, &args);
230
231     /* special handling of missing values in input string */
232     if ( type == RectangleKernel ) {
233       if ( (flags & WidthValue) == 0 ) /* if no width then */
234         args.rho = args.sigma;         /* then  width = height */
235       if ( args.rho < 1.0 )            /* if width too small */
236          args.rho = 3;                 /* then  width = 3 */
237       if ( args.sigma < 1.0 )          /* if height too small */
238         args.sigma = args.rho;         /* then  height = width */
239       if ( (flags & XValue) == 0 )     /* center offset if not defined */
240         args.xi = (double)(((long)args.rho-1)/2);
241       if ( (flags & YValue) == 0 )
242         args.psi = (double)(((long)args.sigma-1)/2);
243     }
244
245     return(AcquireKernelBuiltIn((KernelInfoType)type, &args));
246   }
247
248   kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
249   if (kernel == (KernelInfo *)NULL)
250     return(kernel);
251   (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
252   kernel->type = UserDefinedKernel;
253   kernel->signature = MagickSignature;
254
255   /* Has a ':' in argument - New user kernel specification */
256   p = strchr(kernel_string, ':');
257   if ( p != (char *) NULL)
258     {
259       /* ParseGeometry() needs the geometry separated! -- Arrgghh */
260       memcpy(token, kernel_string, p-kernel_string);
261       token[p-kernel_string] = '\0';
262       flags = ParseGeometry(token, &args);
263
264       /* Size handling and checks of geometry settings */
265       if ( (flags & WidthValue) == 0 ) /* if no width then */
266         args.rho = args.sigma;         /* then  width = height */
267       if ( args.rho < 1.0 )            /* if width too small */
268          args.rho = 1.0;               /* then  width = 1 */
269       if ( args.sigma < 1.0 )          /* if height too small */
270         args.sigma = args.rho;         /* then  height = width */
271       kernel->width = (unsigned long)args.rho;
272       kernel->height = (unsigned long)args.sigma;
273
274       /* Offset Handling and Checks */
275       if ( args.xi  < 0.0 || args.psi < 0.0 )
276         return(DestroyKernelInfo(kernel));
277       kernel->offset_x = ((flags & XValue)!=0) ? (unsigned long)args.xi
278                                                : (kernel->width-1)/2;
279       kernel->offset_y = ((flags & YValue)!=0) ? (unsigned long)args.psi
280                                                : (kernel->height-1)/2;
281       if ( kernel->offset_x >= kernel->width ||
282            kernel->offset_y >= kernel->height )
283         return(DestroyKernelInfo(kernel));
284
285       p++; /* advance beyond the ':' */
286     }
287   else
288     { /* ELSE - Old old kernel specification, forming odd-square kernel */
289       /* count up number of values given */
290       p=(const char *) kernel_string;
291       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
292         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
293       for (i=0; *p != '\0'; i++)
294       {
295         GetMagickToken(p,&p,token);
296         if (*token == ',')
297           GetMagickToken(p,&p,token);
298       }
299       /* set the size of the kernel - old sized square */
300       kernel->width = kernel->height= (unsigned long) sqrt((double) i+1.0);
301       kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
302       p=(const char *) kernel_string;
303       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
304         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
305     }
306
307   /* Read in the kernel values from rest of input string argument */
308   kernel->values=(double *) AcquireQuantumMemory(kernel->width,
309                         kernel->height*sizeof(double));
310   if (kernel->values == (double *) NULL)
311     return(DestroyKernelInfo(kernel));
312
313   kernel->value_min = +MagickHuge;
314   kernel->value_max = -MagickHuge;
315   kernel->range_neg = kernel->range_pos = 0.0;
316   for (i=0; (i < kernel->width*kernel->height) && (*p != '\0'); i++)
317   {
318     GetMagickToken(p,&p,token);
319     if (*token == ',')
320       GetMagickToken(p,&p,token);
321     if (    LocaleCompare("nan",token) == 0
322          || LocaleCompare("-",token) == 0 ) {
323       kernel->values[i] = nan; /* do not include this value in kernel */
324     }
325     else {
326       kernel->values[i] = StringToDouble(token);
327       ( kernel->values[i] < 0)
328           ?  ( kernel->range_neg += kernel->values[i] )
329           :  ( kernel->range_pos += kernel->values[i] );
330       Minimize(kernel->value_min, kernel->values[i]);
331       Maximize(kernel->value_max, kernel->values[i]);
332     }
333   }
334
335   /* This should not be needed for a fully defined defined kernel
336    * Perhaps an error should be reported instead!
337    */
338   if ( i < kernel->width*kernel->height ) {
339     Minimize(kernel->value_min, kernel->values[i]);
340     Maximize(kernel->value_max, kernel->values[i]);
341     for ( ; i < kernel->width*kernel->height; i++)
342       kernel->values[i]=0.0;
343   }
344
345   return(kernel);
346 }
347 \f
348 /*
349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
350 %                                                                             %
351 %                                                                             %
352 %                                                                             %
353 %     A c q u i r e K e r n e l B u i l t I n                                 %
354 %                                                                             %
355 %                                                                             %
356 %                                                                             %
357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
358 %
359 %  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
360 %  kernels used for special purposes such as gaussian blurring, skeleton
361 %  pruning, and edge distance determination.
362 %
363 %  They take a KernelType, and a set of geometry style arguments, which were
364 %  typically decoded from a user supplied string, or from a more complex
365 %  Morphology Method that was requested.
366 %
367 %  The format of the AcquireKernalBuiltIn method is:
368 %
369 %      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
370 %           const GeometryInfo args)
371 %
372 %  A description of each parameter follows:
373 %
374 %    o type: the pre-defined type of kernel wanted
375 %
376 %    o args: arguments defining or modifying the kernel
377 %
378 %  Convolution Kernels
379 %
380 %    Gaussian  "[{radius}]x{sigma}"
381 %       Generate a two-dimentional gaussian kernel, as used by -gaussian
382 %       A sigma is required, (with the 'x'), due to historical reasons.
383 %
384 %       NOTE: that the 'radius' is optional, but if provided can limit (clip)
385 %       the final size of the resulting kernel to a square 2*radius+1 in size.
386 %       The radius should be at least 2 times that of the sigma value, or
387 %       sever clipping and aliasing may result.  If not given or set to 0 the
388 %       radius will be determined so as to produce the best minimal error
389 %       result, which is usally much larger than is normally needed.
390 %
391 %    Blur  "[{radius}]x{sigma}[+angle]"
392 %       As per Gaussian, but generates a 1 dimensional or linear gaussian
393 %       blur, at the angle given (current restricted to orthogonal angles).
394 %       If a 'radius' is given the kernel is clipped to a width of 2*radius+1.
395 %
396 %       NOTE that two such blurs perpendicular to each other is equivelent to
397 %       -blur and the previous gaussian, but is often 10 or more times faster.
398 %
399 %    Comet  "[{width}]x{sigma}[+angle]"
400 %       Blur in one direction only, mush like how a bright object leaves
401 %       a comet like trail.  The Kernel is actually half a gaussian curve,
402 %       Adding two such blurs in oppiste directions produces a Linear Blur.
403 %
404 %       NOTE: that the first argument is the width of the kernel and not the
405 %       radius of the kernel.
406 %
407 %    # Still to be implemented...
408 %    #
409 %    # Laplacian  "{radius}x{sigma}"
410 %    #    Laplacian (a mexican hat like) Function
411 %    #
412 %    # LOG  "{radius},{sigma1},{sigma2}
413 %    #    Laplacian of Gaussian
414 %    #
415 %    # DOG  "{radius},{sigma1},{sigma2}
416 %    #    Difference of Gaussians
417 %
418 %  Boolean Kernels
419 %
420 %    Rectangle "{geometry}"
421 %       Simply generate a rectangle of 1's with the size given. You can also
422 %       specify the location of the 'control point', otherwise the closest
423 %       pixel to the center of the rectangle is selected.
424 %
425 %       Properly centered and odd sized rectangles work the best.
426 %
427 %    Diamond  "[{radius}]"
428 %       Generate a diamond shaped kernal with given radius to the points.
429 %       Kernel size will again be radius*2+1 square and defaults to radius 1,
430 %       generating a 3x3 kernel that is slightly larger than a square.
431 %
432 %    Square  "[{radius}]"
433 %       Generate a square shaped kernel of size radius*2+1, and defaulting
434 %       to a 3x3 (radius 1).
435 %
436 %       Note that using a larger radius for the "Square" or the "Diamond"
437 %       is also equivelent to iterating the basic morphological method
438 %       that many times. However However iterating with the smaller radius 1
439 %       default is actually faster than using a larger kernel radius.
440 %
441 %    Disk   "[{radius}]
442 %       Generate a binary disk of the radius given, radius may be a float.
443 %       Kernel size will be ceil(radius)*2+1 square.
444 %       NOTE: Here are some disk shapes of specific interest
445 %          "disk:1"    => "diamond" or "cross:1"
446 %          "disk:1.5"  => "square"
447 %          "disk:2"    => "diamond:2"
448 %          "disk:2.5"  => a general disk shape of radius 2
449 %          "disk:2.9"  => "square:2"
450 %          "disk:3.5"  => default - octagonal/disk shape of radius 3
451 %          "disk:4.2"  => roughly octagonal shape of radius 4
452 %          "disk:4.3"  => a general disk shape of radius 4
453 %       After this all the kernel shape becomes more and more circular.
454 %
455 %       Because a "disk" is more circular when using a larger radius, using a
456 %       larger radius is preferred over iterating the morphological operation.
457 %
458 %    Plus  "[{radius}]"
459 %       Generate a kernel in the shape of a 'plus' sign. The length of each
460 %       arm is also the radius, which defaults to 2.
461 %
462 %       This kernel is not a good general morphological kernel, but is used
463 %       more for highlighting and marking any single pixels in an image using,
464 %       a "Dilate" or "Erode" method as appropriate.
465 %
466 %       NOTE: "plus:1" is equivelent to a "Diamond" kernel.
467 %
468 %       Note that unlike other kernels iterating a plus does not produce the
469 %       same result as using a larger radius for the cross.
470 %
471 %  Distance Measuring Kernels
472 %
473 %    Chebyshev "[{radius}][x{scale}]"   largest x or y distance (default r=1)
474 %    Manhatten "[{radius}][x{scale}]"   square grid distance    (default r=1)
475 %    Euclidean "[{radius}][x{scale}]"   direct distance         (default r=1)
476 %
477 %       Different types of distance measuring methods, which are used with the
478 %       a 'Distance' morphology method for generating a gradient based on
479 %       distance from an edge of a binary shape, though there is a technique
480 %       for handling a anti-aliased shape.
481 %
482 %       Chebyshev Distance (also known as Tchebychev Distance) is a value of
483 %       one to any neighbour, orthogonal or diagonal. One why of thinking of
484 %       it is the number of squares a 'King' or 'Queen' in chess needs to
485 %       traverse reach any other position on a chess board.  It results in a
486 %       'square' like distance function, but one where diagonals are closer
487 %       than expected.
488 %
489 %       Manhatten Distance (also known as Rectilinear Distance, or the Taxi
490 %       Cab metric), is the distance needed when you can only travel in
491 %       orthogonal (horizontal or vertical) only.  It is the distance a 'Rook'
492 %       in chess would travel. It results in a diamond like distances, where
493 %       diagonals are further than expected.
494 %
495 %       Euclidean Distance is the 'direct' or 'as the crow flys distance.
496 %       However by default the kernel size only has a radius of 1, which
497 %       limits the distance to 'Knight' like moves, with only orthogonal and
498 %       diagonal measurements being correct.  As such for the default kernel
499 %       you will get octagonal like distance function, which is reasonally
500 %       accurate.
501 %
502 %       However if you use a larger radius such as "Euclidean:4" you will
503 %       get a much smoother distance gradient from the edge of the shape.
504 %       Of course a larger kernel is slower to use, and generally not needed.
505 %
506 %       To allow the use of fractional distances that you get with diagonals
507 %       the actual distance is scaled by a fixed value which the user can
508 %       provide.  This is not actually nessary for either ""Chebyshev" or
509 %       "Manhatten" distance kernels, but is done for all three distance
510 %       kernels.  If no scale is provided it is set to a value of 100,
511 %       allowing for a maximum distance measurement of 655 pixels using a Q16
512 %       version of IM, from any edge.  However for small images this can
513 %       result in quite a dark gradient.
514 %
515 %       See the 'Distance' Morphological Method, for information of how it is
516 %       applied.
517 %
518 */
519
520 MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
521    const GeometryInfo *args)
522 {
523   KernelInfo
524     *kernel;
525
526   register unsigned long
527     i;
528
529   register long
530     u,
531     v;
532
533   double
534     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
535
536   kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
537   if (kernel == (KernelInfo *) NULL)
538     return(kernel);
539   (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
540   kernel->value_min = kernel->value_max = 0.0;
541   kernel->range_neg = kernel->range_pos = 0.0;
542   kernel->type = type;
543   kernel->signature = MagickSignature;
544
545   switch(type) {
546     /* Convolution Kernels */
547     case GaussianKernel:
548       { double
549           sigma = fabs(args->sigma);
550
551         sigma = (sigma <= MagickEpsilon) ? 1.0 : sigma;
552
553         kernel->width = kernel->height =
554                             GetOptimalKernelWidth2D(args->rho,sigma);
555         kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
556         kernel->range_neg = kernel->range_pos = 0.0;
557         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
558                               kernel->height*sizeof(double));
559         if (kernel->values == (double *) NULL)
560           return(DestroyKernelInfo(kernel));
561
562         sigma = 2.0*sigma*sigma; /* simplify the expression */
563         for ( i=0, v=-kernel->offset_y; v <= (long)kernel->offset_y; v++)
564           for ( u=-kernel->offset_x; u <= (long)kernel->offset_x; u++, i++)
565             kernel->range_pos += (
566               kernel->values[i] =
567                  exp(-((double)(u*u+v*v))/sigma)
568                        /*  / (MagickPI*sigma)  */ );
569         kernel->value_min = 0;
570         kernel->value_max = kernel->values[
571                          kernel->offset_y*kernel->width+kernel->offset_x ];
572
573         NormalizeKernel(kernel);
574
575         break;
576       }
577     case BlurKernel:
578       { double
579           sigma = fabs(args->sigma);
580
581         sigma = (sigma <= MagickEpsilon) ? 1.0 : sigma;
582
583         kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
584         kernel->offset_x = (kernel->width-1)/2;
585         kernel->height = 1;
586         kernel->offset_y = 0;
587         kernel->range_neg = kernel->range_pos = 0.0;
588         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
589                               kernel->height*sizeof(double));
590         if (kernel->values == (double *) NULL)
591           return(DestroyKernelInfo(kernel));
592
593 #if 1
594 #define KernelRank 3
595         /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
596         ** It generates a gaussian 3 times the width, and compresses it into
597         ** the expected range.  This produces a closer normalization of the
598         ** resulting kernel, especially for very low sigma values.
599         ** As such while wierd it is prefered.
600         **
601         ** I am told this method originally came from Photoshop.
602         */
603         sigma *= KernelRank;                /* simplify expanded curve */
604         v = (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
605         (void) ResetMagickMemory(kernel->values,0, (size_t)
606                        kernel->width*sizeof(double));
607         for ( u=-v; u <= v; u++) {
608           kernel->values[(u+v)/KernelRank] +=
609                 exp(-((double)(u*u))/(2.0*sigma*sigma))
610                        /*   / (MagickSQ2PI*sigma/KernelRank)  */ ;
611         }
612         for (i=0; i < kernel->width; i++)
613           kernel->range_pos += kernel->values[i];
614 #else
615         for ( i=0, u=-kernel->offset_x; i < kernel->width; i++, u++)
616           kernel->range_pos += (
617               kernel->values[i] =
618                 exp(-((double)(u*u))/(2.0*sigma*sigma))
619                        /*  / (MagickSQ2PI*sigma)  */ );
620 #endif
621         kernel->value_min = 0;
622         kernel->value_max = kernel->values[ kernel->offset_x ];
623         /* Note that both the above methods do not generate a normalized
624         ** kernel, though it gets close. The kernel may be 'clipped' by a user
625         ** defined radius, producing a smaller (darker) kernel.  Also for very
626         ** small sigma's (> 0.1) the central value becomes larger than one,
627         ** and thus producing a very bright kernel.
628         */
629 #if 1
630         /* Normalize the 1D Gaussian Kernel
631         **
632         ** Because of this the divisor in the above kernel generator is
633         ** not needed, so is not done above.
634         */
635         NormalizeKernel(kernel);
636 #endif
637         /* rotate the kernel by given angle */
638         RotateKernel(kernel, args->xi);
639         break;
640       }
641     case CometKernel:
642       { double
643           sigma = fabs(args->sigma);
644
645         sigma = (sigma <= MagickEpsilon) ? 1.0 : sigma;
646
647         if ( args->rho < 1.0 )
648           kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
649         else
650           kernel->width = (unsigned long)args->rho;
651         kernel->offset_x = kernel->offset_y = 0;
652         kernel->height = 1;
653         kernel->range_neg = kernel->range_pos = 0.0;
654         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
655                               kernel->height*sizeof(double));
656         if (kernel->values == (double *) NULL)
657           return(DestroyKernelInfo(kernel));
658
659         /* A comet blur is half a gaussian curve, so that the object is
660         ** blurred in one direction only.  This may not be quite the right
661         ** curve so may change in the future. The function must be normalised.
662         */
663 #if 1
664 #define KernelRank 3
665         sigma *= KernelRank;                /* simplify expanded curve */
666         v = kernel->width*KernelRank; /* start/end points to fit range */
667         (void) ResetMagickMemory(kernel->values,0, (size_t)
668                        kernel->width*sizeof(double));
669         for ( u=0; u < v; u++) {
670           kernel->values[u/KernelRank] +=
671                exp(-((double)(u*u))/(2.0*sigma*sigma))
672                        /*   / (MagickSQ2PI*sigma/KernelRank)  */ ;
673         }
674         for (i=0; i < kernel->width; i++)
675           kernel->range_pos += kernel->values[i];
676 #else
677         for ( i=0; i < kernel->width; i++)
678           kernel->range_pos += (
679             kernel->values[i] =
680                exp(-((double)(i*i))/(2.0*sigma*sigma))
681                        /*  / (MagickSQ2PI*sigma)  */ );
682 #endif
683         kernel->value_min = 0;
684         kernel->value_max = kernel->values[0];
685
686         NormalizeKernel(kernel);
687         RotateKernel(kernel, args->xi);
688         break;
689       }
690     /* Boolean Kernels */
691     case RectangleKernel:
692     case SquareKernel:
693       {
694         if ( type == SquareKernel )
695           {
696             if (args->rho < 1.0)
697               kernel->width = kernel->height = 3;  /* default radius = 1 */
698             else
699               kernel->width = kernel->height = 2*(long)args->rho+1;
700             kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
701           }
702         else {
703             /* NOTE: user defaults set in "AcquireKernelInfo()" */
704             if ( args->rho < 1.0 || args->sigma < 1.0 )
705               return(DestroyKernelInfo(kernel));    /* invalid args given */
706             kernel->width = (unsigned long)args->rho;
707             kernel->height = (unsigned long)args->sigma;
708             if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
709                  args->psi < 0.0 || args->psi > (double)kernel->height )
710               return(DestroyKernelInfo(kernel));    /* invalid args given */
711             kernel->offset_x = (unsigned long)args->xi;
712             kernel->offset_y = (unsigned long)args->psi;
713           }
714         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
715                               kernel->height*sizeof(double));
716         if (kernel->values == (double *) NULL)
717           return(DestroyKernelInfo(kernel));
718
719         u=kernel->width*kernel->height;
720         for ( i=0; i < (unsigned long)u; i++)
721             kernel->values[i] = 1.0;
722         break;
723         kernel->value_min = kernel->value_max = 1.0; /* a flat kernel */
724         kernel->range_pos = (double) u;
725       }
726     case DiamondKernel:
727       {
728         if (args->rho < 1.0)
729           kernel->width = kernel->height = 3;  /* default radius = 1 */
730         else
731           kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
732         kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
733
734         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
735                               kernel->height*sizeof(double));
736         if (kernel->values == (double *) NULL)
737           return(DestroyKernelInfo(kernel));
738
739         for ( i=0, v=-kernel->offset_y; v <= (long)kernel->offset_y; v++)
740           for ( u=-kernel->offset_x; u <= (long)kernel->offset_x; u++, i++)
741             if ((labs(u)+labs(v)) <= (long)kernel->offset_x)
742               kernel->range_pos += kernel->values[i] = 1.0;
743             else
744               kernel->values[i] = nan;
745         kernel->value_min = kernel->value_max = 1.0; /* a flat kernel */
746         break;
747       }
748     case DiskKernel:
749       {
750         long
751           limit;
752
753         limit = (long)(args->rho*args->rho);
754         if (args->rho < 0.1)             /* default radius approx 3.5 */
755           kernel->width = kernel->height = 7L, limit = 10L;
756         else
757            kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
758         kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
759
760         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
761                               kernel->height*sizeof(double));
762         if (kernel->values == (double *) NULL)
763           return(DestroyKernelInfo(kernel));
764
765         for ( i=0, v=-kernel->offset_y; v <= (long)kernel->offset_y; v++)
766           for ( u=-kernel->offset_x; u <= (long)kernel->offset_x; u++, i++)
767             if ((u*u+v*v) <= limit)
768               kernel->range_pos += kernel->values[i] = 1.0;
769             else
770               kernel->values[i] = nan;
771         kernel->value_min = kernel->value_max = 1.0; /* a flat kernel */
772         break;
773       }
774     case PlusKernel:
775       {
776         if (args->rho < 1.0)
777           kernel->width = kernel->height = 5;  /* default radius 2 */
778         else
779            kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
780         kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
781
782         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
783                               kernel->height*sizeof(double));
784         if (kernel->values == (double *) NULL)
785           return(DestroyKernelInfo(kernel));
786
787         for ( i=0, v=-kernel->offset_y; v <= (long)kernel->offset_y; v++)
788           for ( u=-kernel->offset_x; u <= (long)kernel->offset_x; u++, i++)
789             kernel->values[i] = (u == 0 || v == 0) ? 1.0 : nan;
790         kernel->value_min = kernel->value_max = 1.0; /* a flat kernel */
791         kernel->range_pos = kernel->width*2.0 - 1.0;
792         break;
793       }
794     /* Distance Measuring Kernels */
795     case ChebyshevKernel:
796       {
797         double
798           scale;
799
800         if (args->rho < 1.0)
801           kernel->width = kernel->height = 3;  /* default radius = 1 */
802         else
803           kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
804         kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
805
806         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
807                               kernel->height*sizeof(double));
808         if (kernel->values == (double *) NULL)
809           return(DestroyKernelInfo(kernel));
810
811         scale = (args->sigma < 1.0) ? 100.0 : args->sigma;
812         for ( i=0, v=-kernel->offset_y; v <= (long)kernel->offset_y; v++)
813           for ( u=-kernel->offset_x; u <= (long)kernel->offset_x; u++, i++)
814             kernel->range_pos += ( kernel->values[i] =
815                  scale*((labs(u)>labs(v)) ? labs(u) : labs(v)) );
816         kernel->value_max = kernel->values[0];
817         break;
818       }
819     case ManhattenKernel:
820       {
821         double
822           scale;
823
824         if (args->rho < 1.0)
825           kernel->width = kernel->height = 3;  /* default radius = 1 */
826         else
827            kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
828         kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
829
830         kernel->values=(double *) AcquireQuantumMemory(kernel->width,
831                               kernel->height*sizeof(double));
832         if (kernel->values == (double *) NULL)
833           return(DestroyKernelInfo(kernel));
834
835         scale = (args->sigma < 1.0) ? 100.0 : args->sigma;
836         for ( i=0, v=-kernel->offset_y; v <= (long)kernel->offset_y; v++)
837           for ( u=-kernel->offset_x; u <= (long)kernel->offset_x; u++, i++)
838             kernel->range_pos += ( kernel->values[i] =
839                  scale*(labs(u)+labs(v)) );
840         kernel->value_max = kernel->values[0];
841         break;
842       }
843     case EuclideanKernel:
844       {
845         double
846           scale;
847
848         if (args->rho < 1.0)
849           kernel->width = kernel->height = 3;  /* default radius = 1 */
850         else
851            kernel->width = kernel->height = ((unsigned long)args->rho)*2+1;
852         kernel->offset_x = kernel->offset_y = (kernel->width-1)/2;
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         scale = (args->sigma < 1.0) ? 100.0 : args->sigma;
860         for ( i=0, v=-kernel->offset_y; v <= (long)kernel->offset_y; v++)
861           for ( u=-kernel->offset_x; u <= (long)kernel->offset_x; u++, i++)
862             kernel->range_pos += ( kernel->values[i] =
863                  scale*sqrt((double)(u*u+v*v)) );
864         kernel->value_max = kernel->values[0];
865         break;
866       }
867     /* Undefined Kernels */
868     case LaplacianKernel:
869     case LOGKernel:
870     case DOGKernel:
871       assert("Kernel Type has not been defined yet");
872       /* FALL THRU */
873     default:
874       /* Generate a No-Op minimal kernel - 1x1 pixel */
875       kernel->values=(double *)AcquireQuantumMemory((size_t)1,sizeof(double));
876       if (kernel->values == (double *) NULL)
877         return(DestroyKernelInfo(kernel));
878       kernel->width = kernel->height = 1;
879       kernel->offset_x = kernel->offset_x = 0;
880       kernel->type = UndefinedKernel;
881       kernel->value_max =
882         kernel->range_pos =
883           kernel->values[0] = 1.0;  /* a flat single-point no-op kernel! */
884       break;
885   }
886
887   return(kernel);
888 }
889 \f
890 /*
891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
892 %                                                                             %
893 %                                                                             %
894 %                                                                             %
895 %     D e s t r o y K e r n e l I n f o                                       %
896 %                                                                             %
897 %                                                                             %
898 %                                                                             %
899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 %
901 %  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
902 %  kernel.
903 %
904 %  The format of the DestroyKernelInfo method is:
905 %
906 %      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
907 %
908 %  A description of each parameter follows:
909 %
910 %    o kernel: the Morphology/Convolution kernel to be destroyed
911 %
912 */
913
914 MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
915 {
916   assert(kernel != (KernelInfo *) NULL);
917   kernel->values=(double *)RelinquishMagickMemory(kernel->values);
918   kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
919   return(kernel);
920 }
921 \f
922 /*
923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924 %                                                                             %
925 %                                                                             %
926 %                                                                             %
927 %     M o r p h o l o g y I m a g e C h a n n e l                             %
928 %                                                                             %
929 %                                                                             %
930 %                                                                             %
931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
932 %
933 %  MorphologyImageChannel() applies a user supplied kernel to the image
934 %  according to the given mophology method.
935 %
936 %  The given kernel is assumed to have been pre-scaled appropriatally, usally
937 %  by the kernel generator.
938 %
939 %  The format of the MorphologyImage method is:
940 %
941 %      Image *MorphologyImage(const Image *image, MorphologyMethod method,
942 %        const long iterations, KernelInfo *kernel, ExceptionInfo *exception)
943 %      Image *MorphologyImageChannel(const Image *image, const ChannelType
944 %        channel, MorphologyMethod method, const long iterations, KernelInfo
945 %        *kernel, ExceptionInfo *exception)
946 %
947 %  A description of each parameter follows:
948 %
949 %    o image: the image.
950 %
951 %    o method: the morphology method to be applied.
952 %
953 %    o iterations: apply the operation this many times (or no change).
954 %                  A value of -1 means loop until no change found.
955 %                  How this is applied may depend on the morphology method.
956 %                  Typically this is a value of 1.
957 %
958 %    o channel: the channel type.
959 %
960 %    o kernel: An array of double representing the morphology kernel.
961 %              Warning: kernel may be normalized for the Convolve method.
962 %
963 %    o exception: return any errors or warnings in this structure.
964 %
965 %
966 % TODO: bias and auto-scale handling of the kernel for convolution
967 %     The given kernel is assumed to have been pre-scaled appropriatally, usally
968 %     by the kernel generator.
969 %
970 */
971
972 /* Internal function
973  * Apply the Morphology method with the given Kernel
974  * And return the number of pixels that changed.
975  */
976 static unsigned long MorphologyApply(const Image *image, Image
977      *result_image, const MorphologyMethod method, const ChannelType channel,
978      const KernelInfo *kernel, ExceptionInfo *exception)
979 {
980 #define MorphologyTag  "Morphology/Image"
981
982   long
983     progress;
984
985   unsigned long
986     y, offx, offy,
987     changed;
988
989   MagickBooleanType
990     status;
991
992   MagickPixelPacket
993     bias;
994
995   CacheView
996     *p_view,
997     *q_view;
998
999   /*
1000     Apply Morphology to Image.
1001   */
1002   status=MagickTrue;
1003   changed=0;
1004   progress=0;
1005
1006   GetMagickPixelPacket(image,&bias);
1007   SetMagickPixelPacketBias(image,&bias);
1008
1009   p_view=AcquireCacheView(image);
1010   q_view=AcquireCacheView(result_image);
1011
1012   /* some methods (including convolve) needs to apply a reflected kernel
1013    * the offset for getting the kernel view needs to be adjusted for this
1014    * situation.
1015    */
1016   offx = kernel->offset_x;
1017   offy = kernel->offset_y;
1018   switch(method) {
1019     case ErodeMorphology:
1020     case ErodeIntensityMorphology:
1021       /* kernel is not reflected */
1022       break;
1023     default:
1024       /* kernel needs to be reflected */
1025       offx = kernel->width-offx-1;
1026       offy = kernel->height-offy-1;
1027       break;
1028   }
1029
1030 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1031   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1032 #endif
1033   for (y=0; y < image->rows; y++)
1034   {
1035     MagickBooleanType
1036       sync;
1037
1038     register const PixelPacket
1039       *restrict p;
1040
1041     register const IndexPacket
1042       *restrict p_indexes;
1043
1044     register PixelPacket
1045       *restrict q;
1046
1047     register IndexPacket
1048       *restrict q_indexes;
1049
1050     register unsigned long
1051       x;
1052
1053     unsigned long
1054       r;
1055
1056     if (status == MagickFalse)
1057       continue;
1058     p=GetCacheViewVirtualPixels(p_view, -offx,  y-offy,
1059          image->columns+kernel->width,  kernel->height,  exception);
1060     q=GetCacheViewAuthenticPixels(q_view,0,y,result_image->columns,1,
1061          exception);
1062     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1063       {
1064         status=MagickFalse;
1065         continue;
1066       }
1067     p_indexes=GetCacheViewVirtualIndexQueue(p_view);
1068     q_indexes=GetCacheViewAuthenticIndexQueue(q_view);
1069     r = (image->columns+kernel->width)*offy+offx; /* constant */
1070
1071     for (x=0; x < image->columns; x++)
1072     {
1073        unsigned long
1074         v;
1075
1076       register unsigned long
1077         u;
1078
1079       register const double
1080         *restrict k;
1081
1082       register const PixelPacket
1083         *restrict k_pixels;
1084
1085       register const IndexPacket
1086         *restrict k_indexes;
1087
1088       MagickPixelPacket
1089         result;
1090
1091       /* Copy input to ouput image for unused channels
1092        * This removes need for 'cloning' a new image every iteration
1093        */
1094       *q = p[r];
1095       if (image->colorspace == CMYKColorspace)
1096         q_indexes[x] = p_indexes[r];
1097
1098       result.index=0; /* stop compiler warnings */
1099       switch (method) {
1100         case ConvolveMorphology:
1101           result=bias;
1102           break;  /* default result is the convolution bias */
1103 #if 1
1104         case DilateMorphology:
1105           result.red     =
1106           result.green   =
1107           result.blue    =
1108           result.opacity =
1109           result.index   = -MagickHuge;
1110           break;
1111         case ErodeMorphology:
1112           result.red     =
1113           result.green   =
1114           result.blue    =
1115           result.opacity =
1116           result.index   = +MagickHuge;
1117           break;
1118 #endif
1119         default:
1120           /* Otherwise just start with the original pixel value */
1121           result.red     = p[r].red;
1122           result.green   = p[r].green;
1123           result.blue    = p[r].blue;
1124           result.opacity = QuantumRange - p[r].opacity;
1125           if ( image->colorspace == CMYKColorspace)
1126              result.index   = p_indexes[r];
1127           break;
1128       }
1129
1130       switch ( method ) {
1131         case ConvolveMorphology:
1132             /* Weighted Average of pixels
1133              *
1134              * NOTE for correct working of this operation for asymetrical
1135              * kernels, the kernel needs to be applied in its reflected form.
1136              * That is its values needs to be reversed.
1137              */
1138             if (((channel & OpacityChannel) == 0) ||
1139                       (image->matte == MagickFalse))
1140               {
1141                 /* Convolution (no transparency) */
1142                 k = &kernel->values[ kernel->width*kernel->height-1 ];
1143                 k_pixels = p;
1144                 k_indexes = p_indexes;
1145                 for (v=0; v < kernel->height; v++) {
1146                   for (u=0; u < kernel->width; u++, k--) {
1147                     if ( IsNan(*k) ) continue;
1148                     result.red     += (*k)*k_pixels[u].red;
1149                     result.green   += (*k)*k_pixels[u].green;
1150                     result.blue    += (*k)*k_pixels[u].blue;
1151                     /* result.opacity += not involved here */
1152                     if ( image->colorspace == CMYKColorspace)
1153                       result.index   += (*k)*k_indexes[u];
1154                   }
1155                   k_pixels += image->columns+kernel->width;
1156                   k_indexes += image->columns+kernel->width;
1157                 }
1158               }
1159             else
1160               { /* Kernel & Alpha weighted Convolution */
1161                 MagickRealType
1162                   alpha,  /* alpha value * kernel weighting */
1163                   gamma;  /* weighting divisor */
1164
1165                 gamma=0.0;
1166                 k = &kernel->values[ kernel->width*kernel->height-1 ];
1167                 k_pixels = p;
1168                 k_indexes = p_indexes;
1169                 for (v=0; v < kernel->height; v++) {
1170                   for (u=0; u < kernel->width; u++, k--) {
1171                     if ( IsNan(*k) ) continue;
1172                     alpha=(*k)*(QuantumScale*(QuantumRange-
1173                                           k_pixels[u].opacity));
1174                     gamma += alpha;
1175                     result.red     += alpha*k_pixels[u].red;
1176                     result.green   += alpha*k_pixels[u].green;
1177                     result.blue    += alpha*k_pixels[u].blue;
1178                     result.opacity += (*k)*(QuantumRange-k_pixels[u].opacity);
1179                     if ( image->colorspace == CMYKColorspace)
1180                       result.index   += alpha*k_indexes[u];
1181                   }
1182                   k_pixels += image->columns+kernel->width;
1183                   k_indexes += image->columns+kernel->width;
1184                 }
1185                 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1186                 result.red *= gamma;
1187                 result.green *= gamma;
1188                 result.blue *= gamma;
1189                 result.opacity *= gamma;
1190                 result.index *= gamma;
1191               }
1192             break;
1193
1194         case DilateMorphology:
1195             /* Maximize Value within kernel shape
1196              *
1197              * NOTE for correct working of this operation for asymetrical
1198              * kernels, the kernel needs to be applied in its reflected form.
1199              * That is its values needs to be reversed.
1200              *
1201              * NOTE: in normal Greyscale Morphology, the kernel value should
1202              * be added to the real value, this is currently not done, due to
1203              * the nature of the boolean kernels being used.
1204              *
1205              */
1206             k = &kernel->values[ kernel->width*kernel->height-1 ];
1207             k_pixels = p;
1208             k_indexes = p_indexes;
1209             for (v=0; v < kernel->height; v++) {
1210               for (u=0; u < kernel->width; u++, k--) {
1211                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
1212                 Maximize(result.red,     k_pixels[u].red);
1213                 Maximize(result.green,   k_pixels[u].green);
1214                 Maximize(result.blue,    k_pixels[u].blue);
1215                 Maximize(result.opacity, QuantumRange-k_pixels[u].opacity);
1216                 if ( image->colorspace == CMYKColorspace)
1217                   Maximize(result.index,   k_indexes[u]);
1218               }
1219               k_pixels += image->columns+kernel->width;
1220               k_indexes += image->columns+kernel->width;
1221             }
1222             break;
1223
1224         case ErodeMorphology:
1225             /* Minimize Value within kernel shape
1226              *
1227              * NOTE that the kernel is not reflected for this operation!
1228              *
1229              * NOTE: in normal Greyscale Morphology, the kernel value should
1230              * be added to the real value, this is currently not done, due to
1231              * the nature of the boolean kernels being used.
1232              */
1233             k = kernel->values;
1234             k_pixels = p;
1235             k_indexes = p_indexes;
1236             for (v=0; v < kernel->height; v++) {
1237               for (u=0; u < kernel->width; u++, k++) {
1238                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
1239                 Minimize(result.red,     k_pixels[u].red);
1240                 Minimize(result.green,   k_pixels[u].green);
1241                 Minimize(result.blue,    k_pixels[u].blue);
1242                 Minimize(result.opacity, QuantumRange-k_pixels[u].opacity);
1243                 if ( image->colorspace == CMYKColorspace)
1244                   Minimize(result.index,   k_indexes[u]);
1245               }
1246               k_pixels += image->columns+kernel->width;
1247               k_indexes += image->columns+kernel->width;
1248             }
1249             break;
1250
1251         case DilateIntensityMorphology:
1252             /* Select pixel with maximum intensity within kernel shape
1253              *
1254              * WARNING: the intensity test fails for CMYK and does not
1255              * take into account the moderating effect of teh alpha channel
1256              * on the intensity.
1257              *
1258              * NOTE for correct working of this operation for asymetrical
1259              * kernels, the kernel needs to be applied in its reflected form.
1260              * That is its values needs to be reversed.
1261              */
1262             k = &kernel->values[ kernel->width*kernel->height-1 ];
1263             k_pixels = p;
1264             k_indexes = p_indexes;
1265             for (v=0; v < kernel->height; v++) {
1266               for (u=0; u < kernel->width; u++, k--) {
1267                 if ( IsNan(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
1268                 if ( result.red == 0.0 ||
1269                      PixelIntensity(&(k_pixels[u])) > PixelIntensity(q) ) {
1270                   /* copy the whole pixel - no channel selection */
1271                   *q = k_pixels[u];
1272                   if ( result.red > 0.0 ) changed++;
1273                   result.red = 1.0;
1274                 }
1275               }
1276               k_pixels += image->columns+kernel->width;
1277               k_indexes += image->columns+kernel->width;
1278             }
1279             break;
1280
1281         case ErodeIntensityMorphology:
1282             /* Select pixel with mimimum intensity within kernel shape
1283              *
1284              * WARNING: the intensity test fails for CMYK and does not
1285              * take into account the moderating effect of teh alpha channel
1286              * on the intensity.
1287              *
1288              * NOTE that the kernel is not reflected for this operation!
1289              */
1290             k = kernel->values;
1291             k_pixels = p;
1292             k_indexes = p_indexes;
1293             for (v=0; v < kernel->height; v++) {
1294               for (u=0; u < kernel->width; u++, k++) {
1295                 if ( IsNan(*k) || (*k) < 0.5 ) continue;
1296                 if ( result.red == 0.0 ||
1297                      PixelIntensity(&(k_pixels[u])) < PixelIntensity(q) ) {
1298                   /* copy the whole pixel - no channel selection */
1299                   *q = k_pixels[u];
1300                   if ( result.red > 0.0 ) changed++;
1301                   result.red = 1.0;
1302                 }
1303               }
1304               k_pixels += image->columns+kernel->width;
1305               k_indexes += image->columns+kernel->width;
1306             }
1307             break;
1308
1309         case DistanceMorphology:
1310           /* Add kernel value and select the minimum value found.
1311            * The result is a iterative distance from edge function.
1312            *
1313            * All Distance Kernels are symetrical, but that may not always
1314            * be the case. For example how about a distance from left edges?
1315            * To make it work correctly for asymetrical kernels the reflected
1316            * kernel needs to be applied.
1317            */
1318 #if 0
1319           /* No need to do distance morphology if original value is zero
1320            * Unfortunatally I have not been able to get this right
1321            * when channel selection also becomes involved. -- Arrgghhh
1322            */
1323           if (   ((channel & RedChannel) == 0 && p[r].red == 0)
1324               || ((channel & GreenChannel) == 0 && p[r].green == 0)
1325               || ((channel & BlueChannel) == 0 && p[r].blue == 0)
1326               || ((channel & OpacityChannel) == 0 && p[r].opacity == 0)
1327               || (( (channel & IndexChannel) == 0
1328                   || image->colorspace != CMYKColorspace
1329                                                ) && p_indexes[x] ==0 )
1330              )
1331             break;
1332 #endif
1333             k = &kernel->values[ kernel->width*kernel->height-1 ];
1334             k_pixels = p;
1335             k_indexes = p_indexes;
1336             for (v=0; v < kernel->height; v++) {
1337               for (u=0; u < kernel->width; u++, k--) {
1338                 if ( IsNan(*k) ) continue;
1339                 Minimize(result.red,     (*k)+k_pixels[u].red);
1340                 Minimize(result.green,   (*k)+k_pixels[u].green);
1341                 Minimize(result.blue,    (*k)+k_pixels[u].blue);
1342                 Minimize(result.opacity, (*k)+QuantumRange-k_pixels[u].opacity);
1343                 if ( image->colorspace == CMYKColorspace)
1344                   Minimize(result.index,   (*k)+k_indexes[u]);
1345               }
1346               k_pixels += image->columns+kernel->width;
1347               k_indexes += image->columns+kernel->width;
1348             }
1349             break;
1350
1351         case UndefinedMorphology:
1352         default:
1353             break; /* Do nothing */
1354       }
1355       switch ( method ) {
1356         case UndefinedMorphology:
1357         case DilateIntensityMorphology:
1358         case ErodeIntensityMorphology:
1359           break;  /* full pixel was directly assigned */
1360         default:
1361           /* Assign the results */
1362           if ((channel & RedChannel) != 0)
1363             q->red = ClampToQuantum(result.red);
1364           if ((channel & GreenChannel) != 0)
1365             q->green = ClampToQuantum(result.green);
1366           if ((channel & BlueChannel) != 0)
1367             q->blue = ClampToQuantum(result.blue);
1368           if ((channel & OpacityChannel) != 0
1369               && image->matte == MagickTrue )
1370             q->opacity = ClampToQuantum(QuantumRange-result.opacity);
1371           if ((channel & IndexChannel) != 0
1372               && image->colorspace == CMYKColorspace)
1373             q_indexes[x] = ClampToQuantum(result.index);
1374           break;
1375       }
1376       if (   ( p[r].red != q->red )
1377           || ( p[r].green != q->green )
1378           || ( p[r].blue != q->blue )
1379           || ( p[r].opacity != q->opacity )
1380           || ( image->colorspace == CMYKColorspace &&
1381                   p_indexes[r] != q_indexes[x] ) )
1382         changed++;  /* The pixel had some value changed! */
1383       p++;
1384       q++;
1385     } /* x */
1386     sync=SyncCacheViewAuthenticPixels(q_view,exception);
1387     if (sync == MagickFalse)
1388       status=MagickFalse;
1389     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1390       {
1391         MagickBooleanType
1392           proceed;
1393
1394 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1395   #pragma omp critical (MagickCore_MorphologyImage)
1396 #endif
1397         proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
1398         if (proceed == MagickFalse)
1399           status=MagickFalse;
1400       }
1401   } /* y */
1402   result_image->type=image->type;
1403   q_view=DestroyCacheView(q_view);
1404   p_view=DestroyCacheView(p_view);
1405   return(status ? changed : 0);
1406 }
1407
1408 MagickExport Image *MorphologyImage(const Image *image,MorphologyMethod method,
1409   const long iterations,KernelInfo *kernel, ExceptionInfo *exception)
1410 {
1411   Image
1412     *morphology_image;
1413
1414   morphology_image=MorphologyImageChannel(image,DefaultChannels,method,
1415     iterations,kernel,exception);
1416   return(morphology_image);
1417 }
1418
1419 MagickExport Image *MorphologyImageChannel(const Image *image,
1420   const ChannelType channel, MorphologyMethod method, const long iterations,
1421   KernelInfo *kernel, ExceptionInfo *exception)
1422 {
1423   unsigned long
1424     count,
1425     limit,
1426     changed;
1427
1428   Image
1429     *new_image,
1430     *old_image;
1431
1432   assert(image != (Image *) NULL);
1433   assert(image->signature == MagickSignature);
1434   assert(exception != (ExceptionInfo *) NULL);
1435   assert(exception->signature == MagickSignature);
1436
1437   if ( GetImageArtifact(image,"showkernel") != (const char *) NULL)
1438     ShowKernel(kernel);  /* request to display the kernel to stderr */
1439
1440   if ( iterations == 0 )
1441     return((Image *)NULL); /* null operation - nothing to do! */
1442
1443   /* kernel must be valid at this point
1444    * (except maybe for posible future morphology methods like "Prune"
1445    */
1446   assert(kernel != (KernelInfo *)NULL);
1447
1448   count = 0;
1449   changed = 1;
1450   limit = iterations;
1451   if ( iterations < 0 )
1452     limit = image->columns > image->rows ? image->columns : image->rows;
1453
1454   /* Special morphology cases */
1455   switch( method ) {
1456     case CloseMorphology:
1457       new_image = MorphologyImageChannel(image, channel, DilateMorphology,
1458             iterations, kernel, exception);
1459       if (new_image == (Image *) NULL)
1460         return((Image *) NULL);
1461       method = ErodeMorphology;
1462       break;
1463     case OpenMorphology:
1464       new_image = MorphologyImageChannel(image, channel, ErodeMorphology,
1465             iterations, kernel, exception);
1466       if (new_image == (Image *) NULL)
1467         return((Image *) NULL);
1468       method = DilateMorphology;
1469       break;
1470     case CloseIntensityMorphology:
1471       new_image = MorphologyImageChannel(image, channel, DilateIntensityMorphology,
1472             iterations, kernel, exception);
1473       if (new_image == (Image *) NULL)
1474         return((Image *) NULL);
1475       method = ErodeIntensityMorphology;
1476       break;
1477     case OpenIntensityMorphology:
1478       new_image = MorphologyImageChannel(image, channel, ErodeIntensityMorphology,
1479             iterations, kernel, exception);
1480       if (new_image == (Image *) NULL)
1481         return((Image *) NULL);
1482       method = DilateIntensityMorphology;
1483       break;
1484
1485     case ConvolveMorphology:
1486       /* NormalizeKernel(kernel);  removed, by default use kernel as defined */
1487       /* TODO: auto-bias, auto-scaling and user-scaling of kernel according
1488        * to expert user settings */
1489       /* FALL-THRU */
1490     default:
1491       /* Do a morphology just once at this point!
1492         This ensures a new_image has been generated, but allows us
1493         to skip the creation of 'old_image' if it isn't needed.
1494       */
1495       new_image=CloneImage(image,0,0,MagickTrue,exception);
1496       if (new_image == (Image *) NULL)
1497         return((Image *) NULL);
1498       if (SetImageStorageClass(new_image,DirectClass) == MagickFalse)
1499         {
1500           InheritException(exception,&new_image->exception);
1501           new_image=DestroyImage(new_image);
1502           return((Image *) NULL);
1503         }
1504       changed = MorphologyApply(image,new_image,method,channel,kernel,
1505             exception);
1506       count++;
1507       if ( GetImageArtifact(image,"verbose") != (const char *) NULL )
1508         fprintf(stderr, "Morphology %s:%lu => Changed %lu\n",
1509               MagickOptionToMnemonic(MagickMorphologyOptions, method),
1510               count, changed);
1511   }
1512
1513   /* Repeat the interative morphology until count or no change */
1514   if ( count < limit && changed > 0 ) {
1515     old_image = CloneImage(new_image,0,0,MagickTrue,exception);
1516     if (old_image == (Image *) NULL)
1517         return(DestroyImage(new_image));
1518     if (SetImageStorageClass(old_image,DirectClass) == MagickFalse)
1519       {
1520         InheritException(exception,&old_image->exception);
1521         old_image=DestroyImage(old_image);
1522         return(DestroyImage(new_image));
1523       }
1524     while( count < limit && changed != 0 )
1525       {
1526         Image *tmp = old_image;
1527         old_image = new_image;
1528         new_image = tmp;
1529         changed = MorphologyApply(old_image,new_image,method,channel,kernel,
1530               exception);
1531         count++;
1532         if ( GetImageArtifact(image,"verbose") != (const char *) NULL )
1533           fprintf(stderr, "Morphology %s:%lu => Changed %lu\n",
1534                 MagickOptionToMnemonic(MagickMorphologyOptions, method),
1535                 count, changed);
1536       }
1537     DestroyImage(old_image);
1538   }
1539
1540   return(new_image);
1541 }
1542 \f
1543 /*
1544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1545 %                                                                             %
1546 %                                                                             %
1547 %                                                                             %
1548 +     N o r m a l i z e K e r n e l                                           %
1549 %                                                                             %
1550 %                                                                             %
1551 %                                                                             %
1552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553 %
1554 %  NormalizeKernel() normalize the kernel so its convolution output will
1555 %  be over a unit range.
1556 %
1557 %  Assumes the 'range_*' attributes of the kernel structure have been
1558 %  correctly set during the kernel creation.
1559 %
1560 %  The format of the NormalizeKernel method is:
1561 %
1562 %      void NormalizeKernel(KernelInfo *kernel)
1563 %
1564 %  A description of each parameter follows:
1565 %
1566 %    o kernel: the Morphology/Convolution kernel
1567 %
1568 */
1569 static void NormalizeKernel(KernelInfo *kernel)
1570 {
1571   register unsigned long
1572     i;
1573
1574   for (i=0; i < kernel->width*kernel->height; i++)
1575     kernel->values[i] /= (kernel->range_pos - kernel->range_neg);
1576
1577   kernel->range_pos /= (kernel->range_pos - kernel->range_neg);
1578   kernel->range_neg /= (kernel->range_pos - kernel->range_neg);
1579   kernel->value_max /= (kernel->range_pos - kernel->range_neg);
1580   kernel->value_min /= (kernel->range_pos - kernel->range_neg);
1581
1582   return;
1583 }
1584 \f
1585 /*
1586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1587 %                                                                             %
1588 %                                                                             %
1589 %                                                                             %
1590 %     R o t a t e K e r n e l                                                 %
1591 %                                                                             %
1592 %                                                                             %
1593 %                                                                             %
1594 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1595 %
1596 %  RotateKernel() rotates the kernel by the angle given.  Currently it is
1597 %  restricted to 90 degree angles, but this may be improved in the future.
1598 %
1599 %  The format of the RotateKernel method is:
1600 %
1601 %      void RotateKernel(KernelInfo *kernel, double angle)
1602 %
1603 %  A description of each parameter follows:
1604 %
1605 %    o kernel: the Morphology/Convolution kernel
1606 %
1607 %    o angle: angle to rotate in degrees
1608 %
1609 */
1610 static void RotateKernel(KernelInfo *kernel, double angle)
1611 {
1612   /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
1613   **
1614   ** TODO: expand beyond simple 90 degree rotates, flips and flops
1615   */
1616
1617   /* Modulus the angle */
1618   angle = fmod(angle, 360.0);
1619   if ( angle < 0 )
1620     angle += 360.0;
1621
1622   if ( 315.0 < angle || angle <= 45.0 )
1623     return;   /* no change! - At least at this time */
1624
1625   switch (kernel->type) {
1626     /* These built-in kernels are cylindrical kernels, rotating is useless */
1627     case GaussianKernel:
1628     case LaplacianKernel:
1629     case LOGKernel:
1630     case DOGKernel:
1631     case DiskKernel:
1632     case ChebyshevKernel:
1633     case ManhattenKernel:
1634     case EuclideanKernel:
1635       return;
1636
1637     /* These may be rotatable at non-90 angles in the future */
1638     /* but simply rotating them in multiples of 90 degrees is useless */
1639     case SquareKernel:
1640     case DiamondKernel:
1641     case PlusKernel:
1642       return;
1643
1644     /* These only allows a +/-90 degree rotation (by transpose) */
1645     /* A 180 degree rotation is useless */
1646     case BlurKernel:
1647     case RectangleKernel:
1648       if ( 135.0 < angle && angle <= 225.0 )
1649         return;
1650       if ( 225.0 < angle && angle <= 315.0 )
1651         angle -= 180;
1652       break;
1653
1654     /* these are freely rotatable in 90 degree units */
1655     case CometKernel:
1656     case UndefinedKernel:
1657     case UserDefinedKernel:
1658       break;
1659   }
1660   if ( 135.0 < angle && angle <= 225.0 )
1661     {
1662       /* For a 180 degree rotation - also know as a reflection */
1663       /* This is actually a very very common operation! */
1664       /* Basically all that is needed is a reversal of the kernel data! */
1665       unsigned long
1666         i,j;
1667       register double
1668         *k,t;
1669
1670       k=kernel->values;
1671       for ( i=0, j=kernel->width*kernel->height-1;  i<j;  i++, j--)
1672         t=k[i],  k[i]=k[j],  k[j]=t;
1673
1674       kernel->offset_x = kernel->width - kernel->offset_x - 1;
1675       kernel->offset_y = kernel->width - kernel->offset_y - 1;
1676       angle = fmod(angle+180.0, 360.0);
1677     }
1678   if ( 45.0 < angle && angle <= 135.0 )
1679     { /* Do a transpose and a flop, of the image, which results in a 90
1680        * degree rotation using two mirror operations.
1681        *
1682        * WARNING: this assumes the original image was a 1 dimentional image
1683        * but currently that is the only built-ins it is applied to.
1684        */
1685       unsigned long
1686         t;
1687       t = kernel->width;
1688       kernel->width = kernel->height;
1689       kernel->height = t;
1690       t = kernel->offset_x;
1691       kernel->offset_x = kernel->offset_y;
1692       kernel->offset_y = t;
1693       angle = fmod(450.0 - angle, 360.0);
1694     }
1695   /* At this point angle should be between -45 (315) and +45 degrees
1696    * In the future some form of non-orthogonal angled rotates could be
1697    * performed here, posibily with a linear kernel restriction.
1698    */
1699
1700 #if 0
1701     Not currently in use!
1702     { /* Do a flop, this assumes kernel is horizontally symetrical.
1703        * Each row of the kernel needs to be reversed!
1704        */
1705       unsigned long
1706         y;
1707       register unsigned long
1708         x,r;
1709       register double
1710         *k,t;
1711
1712       for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
1713         for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
1714           t=k[x],  k[x]=k[r],  k[r]=t;
1715
1716       kernel->offset_x = kernel->width - kernel->offset_x - 1;
1717       angle = fmod(angle+180.0, 360.0);
1718     }
1719 #endif
1720   return;
1721 }
1722 \f
1723 /*
1724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1725 %                                                                             %
1726 %                                                                             %
1727 %                                                                             %
1728 %     S h o w K e r n e l                                                     %
1729 %                                                                             %
1730 %                                                                             %
1731 %                                                                             %
1732 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1733 %
1734 %  ShowKernel() Output the details of the given kernel defination to
1735 %  standard error, as per a users 'showkernel' option request.
1736 %
1737 %  The format of the ShowKernel method is:
1738 %
1739 %      void KernelPrint (KernelInfo *kernel)
1740 %
1741 %  A description of each parameter follows:
1742 %
1743 %    o kernel: the Morphology/Convolution kernel
1744 %
1745 % FUTURE: return the information in a string for API usage.
1746 */
1747 static void ShowKernel(KernelInfo *kernel)
1748 {
1749   unsigned long
1750     i, u, v;
1751
1752   fprintf(stderr,
1753         "Kernel \"%s\" of size %lux%lu%+ld%+ld  with values from %lg to %lg\n",
1754         MagickOptionToMnemonic(MagickKernelOptions, kernel->type),
1755         kernel->width, kernel->height,
1756         kernel->offset_x, kernel->offset_y,
1757         kernel->value_min, kernel->value_max);
1758   fprintf(stderr, "Forming convolution output range from %lg to %lg%s\n",
1759         kernel->range_neg, kernel->range_pos,
1760         kernel->normalized == MagickTrue ? " (normalized)" : "" );
1761   for (i=v=0; v < kernel->height; v++) {
1762     fprintf(stderr,"%2ld:",v);
1763     for (u=0; u < kernel->width; u++, i++)
1764       if ( IsNan(kernel->values[i]) )
1765         fprintf(stderr," %*s", GetMagickPrecision()+2, "nan");
1766       else
1767         fprintf(stderr," %*.*lg", GetMagickPrecision()+2,
1768              GetMagickPrecision(), kernel->values[i]);
1769     fprintf(stderr,"\n");
1770   }
1771 }