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