1701db3105315e7d7d9cf2734ae94524c6bc38e80cristy/*
2701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
4701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
5701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
6701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    M   M    OOO    RRRR   PPPP   H   H   OOO   L       OOO    GGGG  Y   Y   %
7701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    MM MM   O   O   R   R  P   P  H   H  O   O  L      O   O  G       Y Y    %
8701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    M M M   O   O   RRRR   PPPP   HHHHH  O   O  L      O   O  G GGG    Y     %
9701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    M   M   O   O   R R    P      H   H  O   O  L      O   O  G   G    Y     %
10701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    M   M    OOO    R  R   P      H   H   OOO   LLLLL   OOO    GGG     Y     %
11701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
12701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
13701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                        MagickCore Morphology Methods                        %
14701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
15701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                              Software Design                                %
16701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                              Anthony Thyssen                                %
17c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                               January 2010                                  %
18701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
19701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
207ce65e7125a4e1df1a274ce373c537a9df9c16cdCristy%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
21701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  dedicated to making software imaging solutions freely available.           %
22701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
23701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  You may not use this file except in compliance with the License.  You may  %
24701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  obtain a copy of the License at                                            %
25701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
26701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    http://www.imagemagick.org/script/license.php                            %
27701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
28701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  Unless required by applicable law or agreed to in writing, software        %
29701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  distributed under the License is distributed on an "AS IS" BASIS,          %
30701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  See the License for the specific language governing permissions and        %
32701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  limitations under the License.                                             %
33701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
34701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%
36804b9e61604d30ea056306b18231b4fb4576f540cristy% Morphology is the application of various kernels, of any size or shape, to an
37400fbea5c7e8899f099534561c86b6eb70f3784bcristy% image in various ways (typically binary, but not always).
38701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%
39602ab9b30b644a78a4057da93d838a77391ec0acanthony% Convolution (weighted sum or average) is just one specific type of
40602ab9b30b644a78a4057da93d838a77391ec0acanthony% morphology. Just one that is very common for image bluring and sharpening
41602ab9b30b644a78a4057da93d838a77391ec0acanthony% effects.  Not only 2D Gaussian blurring, but also 2-pass 1D Blurring.
42602ab9b30b644a78a4057da93d838a77391ec0acanthony%
43602ab9b30b644a78a4057da93d838a77391ec0acanthony% This module provides not only a general morphology function, and the ability
44602ab9b30b644a78a4057da93d838a77391ec0acanthony% to apply more advanced or iterative morphologies, but also functions for the
45602ab9b30b644a78a4057da93d838a77391ec0acanthony% generation of many different types of kernel arrays from user supplied
46602ab9b30b644a78a4057da93d838a77391ec0acanthony% arguments. Prehaps even the generation of a kernel from a small image.
47701db3105315e7d7d9cf2734ae94524c6bc38e80cristy*/
48701db3105315e7d7d9cf2734ae94524c6bc38e80cristy
49701db3105315e7d7d9cf2734ae94524c6bc38e80cristy/*
50701db3105315e7d7d9cf2734ae94524c6bc38e80cristy  Include declarations.
51701db3105315e7d7d9cf2734ae94524c6bc38e80cristy*/
524c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/studio.h"
534c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/artifact.h"
544c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/cache-view.h"
556a2180cee55312a7c0c633670803f9face88a82acristy#include "MagickCore/channel.h"
564c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/color-private.h"
574c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/enhance.h"
584c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception.h"
594c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception-private.h"
604c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/gem.h"
618ea81224e9ff022e56eb2cddb12860a8b2e90411cristy#include "MagickCore/gem-private.h"
624c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image.h"
634c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image-private.h"
64abed7e293f2e8a83e8036df7f2a3e1d9859e5fb2dirk#include "MagickCore/linked-list.h"
654c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/list.h"
664c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/magick.h"
674c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/memory_.h"
68e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy#include "MagickCore/memory-private.h"
694c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/monitor-private.h"
704c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology.h"
714c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology-private.h"
724c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/option.h"
734c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel-accessor.h"
74f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy#include "MagickCore/pixel-private.h"
754c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/prepress.h"
764c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/quantize.h"
77ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy#include "MagickCore/resource_.h"
784c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/registry.h"
794c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/semaphore.h"
804c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/splay-tree.h"
814c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/statistic.h"
824c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string_.h"
834c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string-private.h"
8416881e68c6165c6191fc44151a8a4320e3dd1ffdcristy#include "MagickCore/thread-private.h"
854c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/token.h"
864c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/utility.h"
87d1dd6e4fefa0810b9893e6ac9418f79c97c1b39acristy#include "MagickCore/utility-private.h"
88a29d45f897949f04a47bb3da077395969f13dcbacristy
89c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony/*
90a29d45f897949f04a47bb3da077395969f13dcbacristy  Other global definitions used by module.
91a29d45f897949f04a47bb3da077395969f13dcbacristy*/
9229188a8682a98d4b7882cca434b170517555fc7danthony#define Minimize(assign,value) assign=MagickMin(assign,value)
9329188a8682a98d4b7882cca434b170517555fc7danthony#define Maximize(assign,value) assign=MagickMax(assign,value)
9429188a8682a98d4b7882cca434b170517555fc7danthony
9540ca0b982379d4ab2716435a46603d56b5b218b1anthony/* Integer Factorial Function - for a Binomial kernel */
9640ca0b982379d4ab2716435a46603d56b5b218b1anthony#if 1
9740ca0b982379d4ab2716435a46603d56b5b218b1anthonystatic inline size_t fact(size_t n)
9840ca0b982379d4ab2716435a46603d56b5b218b1anthony{
99f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy  size_t f,l;
10040ca0b982379d4ab2716435a46603d56b5b218b1anthony  for(f=1, l=2; l <= n; f=f*l, l++);
10140ca0b982379d4ab2716435a46603d56b5b218b1anthony  return(f);
10240ca0b982379d4ab2716435a46603d56b5b218b1anthony}
10340ca0b982379d4ab2716435a46603d56b5b218b1anthony#elif 1 /* glibc floating point alternatives */
10440ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)tgamma((double)n+1))
10540ca0b982379d4ab2716435a46603d56b5b218b1anthony#else
10640ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)lgamma((double)n+1))
10740ca0b982379d4ab2716435a46603d56b5b218b1anthony#endif
10840ca0b982379d4ab2716435a46603d56b5b218b1anthony
10940ca0b982379d4ab2716435a46603d56b5b218b1anthony
110c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthony/* Currently these are only internal to this module */
111c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthonystatic void
11246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(KernelInfo *),
113bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandMirrorKernelInfo(KernelInfo *),
114bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandRotateKernelInfo(KernelInfo *, const double),
115ef656913b0b30d713ae94c82c47693c9dc69c9f4cristy  RotateKernelInfo(KernelInfo *, double);
116602ab9b30b644a78a4057da93d838a77391ec0acanthony
1173dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
1183dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony/* Quick function to find last kernel in a kernel list */
1193dd0f620e7a1d12f747ce167844cd7269bfa9f12anthonystatic inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
1203dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony{
1213dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  while (kernel->next != (KernelInfo *) NULL)
122c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk    kernel=kernel->next;
1233dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  return(kernel);
1243dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony}
1253dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
126602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
127602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
128602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
129602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
130602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
13183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     A c q u i r e K e r n e l I n f o                                       %
132602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
133602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
134602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
135602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1372be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%  AcquireKernelInfo() takes the given string (generally supplied by the
138602ab9b30b644a78a4057da93d838a77391ec0acanthony%  user) and converts it into a Morphology/Convolution Kernel.  This allows
139602ab9b30b644a78a4057da93d838a77391ec0acanthony%  users to specify a kernel from a number of pre-defined kernels, or to fully
140602ab9b30b644a78a4057da93d838a77391ec0acanthony%  specify their own kernel for a specific Convolution or Morphology
141602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Operation.
142602ab9b30b644a78a4057da93d838a77391ec0acanthony%
143602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The kernel so generated can be any rectangular array of floating point
144602ab9b30b644a78a4057da93d838a77391ec0acanthony%  values (doubles) with the 'control point' or 'pixel being affected'
145602ab9b30b644a78a4057da93d838a77391ec0acanthony%  anywhere within that array of values.
146602ab9b30b644a78a4057da93d838a77391ec0acanthony%
14783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  Previously IM was restricted to a square of odd size using the exact
14819910ef25dd3d99d1981a9e42c934133170ee714anthony%  center as origin, this is no longer the case, and any rectangular kernel
14983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  with any value being declared the origin. This in turn allows the use of
15083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  highly asymmetrical kernels.
151602ab9b30b644a78a4057da93d838a77391ec0acanthony%
152602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The floating point values in the kernel can also include a special value
15383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  known as 'nan' or 'not a number' to indicate that this value is not part
15483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  of the kernel array. This allows you to shaped the kernel within its
15583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  rectangular area. That is 'nan' values provide a 'mask' for the kernel
15683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  shape.  However at least one non-nan value must be provided for correct
15783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  working of a kernel.
158602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1597a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  The returned kernel should be freed using the DestroyKernelInfo() when you
1607a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  are finished with it.  Do not free this memory yourself.
161602ab9b30b644a78a4057da93d838a77391ec0acanthony%
162602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Input kernel defintion strings can consist of any of three types.
163602ab9b30b644a78a4057da93d838a77391ec0acanthony%
164bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "name:args[[@><]"
16529188a8682a98d4b7882cca434b170517555fc7danthony%         Select from one of the built in kernels, using the name and
16629188a8682a98d4b7882cca434b170517555fc7danthony%         geometry arguments supplied.  See AcquireKernelBuiltIn()
167602ab9b30b644a78a4057da93d838a77391ec0acanthony%
168bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "WxH[+X+Y][@><]:num, num, num ..."
1691b2bc0a7da432e6e1cc0480280402df213faa940anthony%         a kernel of size W by H, with W*H floating point numbers following.
170602ab9b30b644a78a4057da93d838a77391ec0acanthony%         the 'center' can be optionally be defined at +X+Y (such that +0+0
17129188a8682a98d4b7882cca434b170517555fc7danthony%         is top left corner). If not defined the pixel in the center, for
17229188a8682a98d4b7882cca434b170517555fc7danthony%         odd sizes, or to the immediate top or left of center for even sizes
17329188a8682a98d4b7882cca434b170517555fc7danthony%         is automatically selected.
174602ab9b30b644a78a4057da93d838a77391ec0acanthony%
17529188a8682a98d4b7882cca434b170517555fc7danthony%    "num, num, num, num, ..."
17629188a8682a98d4b7882cca434b170517555fc7danthony%         list of floating point numbers defining an 'old style' odd sized
17729188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel.  At least 9 values should be provided for a 3x3
17829188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
17929188a8682a98d4b7882cca434b170517555fc7danthony%         Values can be space or comma separated.  This is not recommended.
180602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1817a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  You can define a 'list of kernels' which can be used by some morphology
1821e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  operators A list is defined as a semi-colon separated list kernels.
1837a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
184dbc8989a61339951c6434d9a43e7b6fefb5da374anthony%     " kernel ; kernel ; kernel ; "
1857a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
1861dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%  Any extra ';' characters, at start, end or between kernel defintions are
18743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  simply ignored.
18843c4925e5305a26e48d68f7893e94f55d0831c39anthony%
189bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The special flags will expand a single kernel, into a list of rotated
190bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
191bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
192bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The '<' also exands using 90-degree rotates, but giving a 180-degree
193bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  reflected kernel before the +/- 90-degree rotations, which can be important
194bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  for Thinning operations.
195bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
19643c4925e5305a26e48d68f7893e94f55d0831c39anthony%  Note that 'name' kernels will start with an alphabetic character while the
19743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  new kernel specification has a ':' character in its specification string.
19843c4925e5305a26e48d68f7893e94f55d0831c39anthony%  If neither is the case, it is assumed an old style of a simple list of
19943c4925e5305a26e48d68f7893e94f55d0831c39anthony%  numbers generating a odd-sized square kernel has been given.
2007a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
201602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernal method is:
202602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2032be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelInfo(const char *kernel_string)
204602ab9b30b644a78a4057da93d838a77391ec0acanthony%
205602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
206602ab9b30b644a78a4057da93d838a77391ec0acanthony%
207602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel_string: the Morphology/Convolution kernel wanted.
208602ab9b30b644a78a4057da93d838a77391ec0acanthony%
209602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
210602ab9b30b644a78a4057da93d838a77391ec0acanthony
211c84dce50867229e4872193e8eed5dbab58eb9f02anthony/* This was separated so that it could be used as a separate
2125ef8e94ff55717be2387d537bd49025780a1a558anthony** array input handling function, such as for -color-matrix
213c84dce50867229e4872193e8eed5dbab58eb9f02anthony*/
2145ef8e94ff55717be2387d537bd49025780a1a558anthonystatic KernelInfo *ParseKernelArray(const char *kernel_string)
215602ab9b30b644a78a4057da93d838a77391ec0acanthony{
2162be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
217602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
218602ab9b30b644a78a4057da93d838a77391ec0acanthony
219602ab9b30b644a78a4057da93d838a77391ec0acanthony  char
220151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    token[MagickPathExtent];
221602ab9b30b644a78a4057da93d838a77391ec0acanthony
222602ab9b30b644a78a4057da93d838a77391ec0acanthony  const char
2235ef8e94ff55717be2387d537bd49025780a1a558anthony    *p,
2245ef8e94ff55717be2387d537bd49025780a1a558anthony    *end;
225602ab9b30b644a78a4057da93d838a77391ec0acanthony
226bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
227c84dce50867229e4872193e8eed5dbab58eb9f02anthony    i;
228602ab9b30b644a78a4057da93d838a77391ec0acanthony
22929188a8682a98d4b7882cca434b170517555fc7danthony  double
23029188a8682a98d4b7882cca434b170517555fc7danthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
23129188a8682a98d4b7882cca434b170517555fc7danthony
23243c4925e5305a26e48d68f7893e94f55d0831c39anthony  MagickStatusType
23343c4925e5305a26e48d68f7893e94f55d0831c39anthony    flags;
23443c4925e5305a26e48d68f7893e94f55d0831c39anthony
23543c4925e5305a26e48d68f7893e94f55d0831c39anthony  GeometryInfo
23643c4925e5305a26e48d68f7893e94f55d0831c39anthony    args;
23743c4925e5305a26e48d68f7893e94f55d0831c39anthony
238a64b85d7873d5e540fe6e2941aa98ec7653a4e2dcristy  kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
239f432c635c526259b858c9aad3d409c5c44545686cristy  if (kernel == (KernelInfo *) NULL)
240602ab9b30b644a78a4057da93d838a77391ec0acanthony    return(kernel);
241602ab9b30b644a78a4057da93d838a77391ec0acanthony  (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
24243c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->minimum = kernel->maximum = kernel->angle = 0.0;
2437a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->negative_range = kernel->positive_range = 0.0;
244602ab9b30b644a78a4057da93d838a77391ec0acanthony  kernel->type = UserDefinedKernel;
2457a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->next = (KernelInfo *) NULL;
246e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  kernel->signature=MagickCoreSignature;
2475e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
2485e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(kernel);
249602ab9b30b644a78a4057da93d838a77391ec0acanthony
2505ef8e94ff55717be2387d537bd49025780a1a558anthony  /* find end of this specific kernel definition string */
2515ef8e94ff55717be2387d537bd49025780a1a558anthony  end = strchr(kernel_string, ';');
2525ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( end == (char *) NULL )
2535ef8e94ff55717be2387d537bd49025780a1a558anthony    end = strchr(kernel_string, '\0');
2545ef8e94ff55717be2387d537bd49025780a1a558anthony
255a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* clear flags - for Expanding kernel lists thorugh rotations */
25643c4925e5305a26e48d68f7893e94f55d0831c39anthony   flags = NoValue;
25743c4925e5305a26e48d68f7893e94f55d0831c39anthony
258f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony  /* Has a ':' in argument - New user kernel specification
259f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony     FUTURE: this split on ':' could be done by StringToken()
260f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony   */
261602ab9b30b644a78a4057da93d838a77391ec0acanthony  p = strchr(kernel_string, ':');
2625ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( p != (char *) NULL && p < end)
263602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
264602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* ParseGeometry() needs the geometry separated! -- Arrgghh */
265150989ed67ef9da53141a65e5f3ebdb05dd025abcristy      memcpy(token, kernel_string, (size_t) (p-kernel_string));
266602ab9b30b644a78a4057da93d838a77391ec0acanthony      token[p-kernel_string] = '\0';
267c84dce50867229e4872193e8eed5dbab58eb9f02anthony      SetGeometryInfo(&args);
268602ab9b30b644a78a4057da93d838a77391ec0acanthony      flags = ParseGeometry(token, &args);
269602ab9b30b644a78a4057da93d838a77391ec0acanthony
27029188a8682a98d4b7882cca434b170517555fc7danthony      /* Size handling and checks of geometry settings */
271602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
272602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.rho = args.sigma;         /* then  width = height */
273602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.rho < 1.0 )            /* if width too small */
274602ab9b30b644a78a4057da93d838a77391ec0acanthony         args.rho = 1.0;               /* then  width = 1 */
275602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.sigma < 1.0 )          /* if height too small */
276602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.sigma = args.rho;         /* then  height = width */
277bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = (size_t)args.rho;
278bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->height = (size_t)args.sigma;
279602ab9b30b644a78a4057da93d838a77391ec0acanthony
280602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* Offset Handling and Checks */
281602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.xi  < 0.0 || args.psi < 0.0 )
28283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
283bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
284ea068a53d23d6dca08f1bce44c8937d54f83b983anthony                                        : (ssize_t) (kernel->width-1)/2;
285bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
286ea068a53d23d6dca08f1bce44c8937d54f83b983anthony                                        : (ssize_t) (kernel->height-1)/2;
287bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      if ( kernel->x >= (ssize_t) kernel->width ||
288bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy           kernel->y >= (ssize_t) kernel->height )
28983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
290602ab9b30b644a78a4057da93d838a77391ec0acanthony
291602ab9b30b644a78a4057da93d838a77391ec0acanthony      p++; /* advance beyond the ':' */
292602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
293602ab9b30b644a78a4057da93d838a77391ec0acanthony  else
294c84dce50867229e4872193e8eed5dbab58eb9f02anthony    { /* ELSE - Old old specification, forming odd-square kernel */
295602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* count up number of values given */
296602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
297a699b171eff7e0178463e8f271b35a3cbb995f0ecristy      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
29829188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
2995ef8e94ff55717be2387d537bd49025780a1a558anthony      for (i=0; p < end; i++)
300602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
3018bedb4edca01599dfd0612cb0daa35b09c67d736Cristy        GetNextToken(p,&p,MagickPathExtent,token);
302602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (*token == ',')
3038bedb4edca01599dfd0612cb0daa35b09c67d736Cristy          GetNextToken(p,&p,MagickPathExtent,token);
304602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
305602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* set the size of the kernel - old sized square */
306bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
307bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
308602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
30929188a8682a98d4b7882cca434b170517555fc7danthony      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
31029188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
311602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
312602ab9b30b644a78a4057da93d838a77391ec0acanthony
313602ab9b30b644a78a4057da93d838a77391ec0acanthony  /* Read in the kernel values from rest of input string argument */
314e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
315e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy    kernel->width,kernel->height*sizeof(*kernel->values)));
316d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if (kernel->values == (MagickRealType *) NULL)
31783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    return(DestroyKernelInfo(kernel));
3180283b31ed43486c1b3c6d903f6992b6402419c40cristy  kernel->minimum=MagickMaximumValue;
3190283b31ed43486c1b3c6d903f6992b6402419c40cristy  kernel->maximum=(-MagickMaximumValue);
320c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->negative_range = kernel->positive_range = 0.0;
321bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
322602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
3238bedb4edca01599dfd0612cb0daa35b09c67d736Cristy    GetNextToken(p,&p,MagickPathExtent,token);
324602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (*token == ',')
3258bedb4edca01599dfd0612cb0daa35b09c67d736Cristy      GetNextToken(p,&p,MagickPathExtent,token);
32629188a8682a98d4b7882cca434b170517555fc7danthony    if (    LocaleCompare("nan",token) == 0
327c84dce50867229e4872193e8eed5dbab58eb9f02anthony        || LocaleCompare("-",token) == 0 ) {
328ea068a53d23d6dca08f1bce44c8937d54f83b983anthony      kernel->values[i] = nan; /* this value is not part of neighbourhood */
32929188a8682a98d4b7882cca434b170517555fc7danthony    }
33029188a8682a98d4b7882cca434b170517555fc7danthony    else {
3310ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony      kernel->values[i] = StringToDouble(token,(char **) NULL);
33229188a8682a98d4b7882cca434b170517555fc7danthony      ( kernel->values[i] < 0)
333c99304fe3c8d9c617da792b40b57c118bb1249afcristy          ?  ( kernel->negative_range += kernel->values[i] )
334c99304fe3c8d9c617da792b40b57c118bb1249afcristy          :  ( kernel->positive_range += kernel->values[i] );
335c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Minimize(kernel->minimum, kernel->values[i]);
336c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Maximize(kernel->maximum, kernel->values[i]);
33729188a8682a98d4b7882cca434b170517555fc7danthony    }
33829188a8682a98d4b7882cca434b170517555fc7danthony  }
33929188a8682a98d4b7882cca434b170517555fc7danthony
3405ef8e94ff55717be2387d537bd49025780a1a558anthony  /* sanity check -- no more values in kernel definition */
3418bedb4edca01599dfd0612cb0daa35b09c67d736Cristy  GetNextToken(p,&p,MagickPathExtent,token);
3425ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( *token != '\0' && *token != ';' && *token != '\'' )
3435ef8e94ff55717be2387d537bd49025780a1a558anthony    return(DestroyKernelInfo(kernel));
3445ef8e94ff55717be2387d537bd49025780a1a558anthony
345c84dce50867229e4872193e8eed5dbab58eb9f02anthony#if 0
346c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* this was the old method of handling a incomplete kernel */
347bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) ) {
348c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Minimize(kernel->minimum, kernel->values[i]);
349c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Maximize(kernel->maximum, kernel->values[i]);
350bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
35129188a8682a98d4b7882cca434b170517555fc7danthony      kernel->values[i]=0.0;
352602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
353c84dce50867229e4872193e8eed5dbab58eb9f02anthony#else
354c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Number of values for kernel was not enough - Report Error */
355bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) )
356c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
357c84dce50867229e4872193e8eed5dbab58eb9f02anthony#endif
358c84dce50867229e4872193e8eed5dbab58eb9f02anthony
359c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* check that we recieved at least one real (non-nan) value! */
3600283b31ed43486c1b3c6d903f6992b6402419c40cristy  if (kernel->minimum == MagickMaximumValue)
361c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
362602ab9b30b644a78a4057da93d838a77391ec0acanthony
36343c4925e5305a26e48d68f7893e94f55d0831c39anthony  if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
364bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
365bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
366bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
367bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
368bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
36943c4925e5305a26e48d68f7893e94f55d0831c39anthony
370602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
371602ab9b30b644a78a4057da93d838a77391ec0acanthony}
372c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3732c57b74e160f9b605d74dec24081309f28b83899cristystatic KernelInfo *ParseKernelName(const char *kernel_string,
3742c57b74e160f9b605d74dec24081309f28b83899cristy  ExceptionInfo *exception)
375c84dce50867229e4872193e8eed5dbab58eb9f02anthony{
376c84dce50867229e4872193e8eed5dbab58eb9f02anthony  char
377151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    token[MagickPathExtent];
378c84dce50867229e4872193e8eed5dbab58eb9f02anthony
379c84dce50867229e4872193e8eed5dbab58eb9f02anthony  const char
3807a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *p,
3817a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *end;
382c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3839d314ff2c17a77996c05413c2013880387e50f0ecristy  GeometryInfo
3849d314ff2c17a77996c05413c2013880387e50f0ecristy    args;
3859d314ff2c17a77996c05413c2013880387e50f0ecristy
3869d314ff2c17a77996c05413c2013880387e50f0ecristy  KernelInfo
3879d314ff2c17a77996c05413c2013880387e50f0ecristy    *kernel;
3889d314ff2c17a77996c05413c2013880387e50f0ecristy
389c84dce50867229e4872193e8eed5dbab58eb9f02anthony  MagickStatusType
390c84dce50867229e4872193e8eed5dbab58eb9f02anthony    flags;
391c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3929d314ff2c17a77996c05413c2013880387e50f0ecristy  ssize_t
3939d314ff2c17a77996c05413c2013880387e50f0ecristy    type;
394c84dce50867229e4872193e8eed5dbab58eb9f02anthony
395c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Parse special 'named' kernel */
3968bedb4edca01599dfd0612cb0daa35b09c67d736Cristy  GetNextToken(kernel_string,&p,MagickPathExtent,token);
397042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy  type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
398c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( type < 0 || type == UserDefinedKernel )
399f432c635c526259b858c9aad3d409c5c44545686cristy    return((KernelInfo *) NULL);  /* not a valid named kernel */
400c84dce50867229e4872193e8eed5dbab58eb9f02anthony
401c84dce50867229e4872193e8eed5dbab58eb9f02anthony  while (((isspace((int) ((unsigned char) *p)) != 0) ||
4025ef8e94ff55717be2387d537bd49025780a1a558anthony          (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
403c84dce50867229e4872193e8eed5dbab58eb9f02anthony    p++;
4047a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4057a01dcf50ce12cb2a789bedff51e9345f022432eanthony  end = strchr(p, ';'); /* end of this kernel defintion */
4067a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( end == (char *) NULL )
4077a01dcf50ce12cb2a789bedff51e9345f022432eanthony    end = strchr(p, '\0');
4087a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4097a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* ParseGeometry() needs the geometry separated! -- Arrgghh */
4107a01dcf50ce12cb2a789bedff51e9345f022432eanthony  memcpy(token, p, (size_t) (end-p));
4117a01dcf50ce12cb2a789bedff51e9345f022432eanthony  token[end-p] = '\0';
412c84dce50867229e4872193e8eed5dbab58eb9f02anthony  SetGeometryInfo(&args);
4137a01dcf50ce12cb2a789bedff51e9345f022432eanthony  flags = ParseGeometry(token, &args);
414c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4153c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#if 0
4163c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* For Debugging Geometry Input */
4175acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4181e604812fad85bb96f757a2393015ae3d061c39acristy    flags, args.rho, args.sigma, args.xi, args.psi );
4193c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#endif
4203c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
421c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* special handling of missing values in input string */
422c84dce50867229e4872193e8eed5dbab58eb9f02anthony  switch( type ) {
423a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Shape Kernel Defaults */
424529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
425529482f4b494010a13338a74446c510712f670b3anthony      if ( (flags & WidthValue) == 0 )
426a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
4275ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4285ef8e94ff55717be2387d537bd49025780a1a558anthony    case SquareKernel:
4295ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiamondKernel:
4301ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
4315ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiskKernel:
4325ef8e94ff55717be2387d537bd49025780a1a558anthony    case PlusKernel:
4333dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
4345ef8e94ff55717be2387d537bd49025780a1a558anthony      if ( (flags & HeightValue) == 0 )
435a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
4365ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
437c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
438c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if ( (flags & XValue) == 0 )
439a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
440c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
441a9892d898acb81e1ec73106d892855fdc5a69427anthony    case RectangleKernel:    /* Rectangle - set size defaults */
442a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
443a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = args.sigma;         /* then  width = height */
444a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.rho < 1.0 )            /* if width too small */
445a9892d898acb81e1ec73106d892855fdc5a69427anthony          args.rho = 3;                /* then  width = 3 */
446a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.sigma < 1.0 )          /* if height too small */
447a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = args.rho;         /* then  height = width */
448a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & XValue) == 0 )     /* center offset if not defined */
449a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = (double)(((ssize_t)args.rho-1)/2);
450a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & YValue) == 0 )
451a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.psi = (double)(((ssize_t)args.sigma-1)/2);
452a9892d898acb81e1ec73106d892855fdc5a69427anthony      break;
453a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Distance Kernel Defaults */
4545ef8e94ff55717be2387d537bd49025780a1a558anthony    case ChebyshevKernel:
455bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
4561ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonalKernel:
4575ef8e94ff55717be2387d537bd49025780a1a558anthony    case EuclideanKernel:
45843c4925e5305a26e48d68f7893e94f55d0831c39anthony      if ( (flags & HeightValue) == 0 )           /* no distance scale */
45943c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = 100.0;                       /* default distance scaling */
46043c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
46143c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
46243c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
46343c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma *= QuantumRange/100.0;         /* percentage of color range */
4645ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4655ef8e94ff55717be2387d537bd49025780a1a558anthony    default:
4665ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
467c84dce50867229e4872193e8eed5dbab58eb9f02anthony  }
468c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4692c57b74e160f9b605d74dec24081309f28b83899cristy  kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args, exception);
470529482f4b494010a13338a74446c510712f670b3anthony  if ( kernel == (KernelInfo *) NULL )
471529482f4b494010a13338a74446c510712f670b3anthony    return(kernel);
472f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
473f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  /* global expand to rotated kernel list - only for single kernels */
474f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  if ( kernel->next == (KernelInfo *) NULL ) {
475f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony    if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
476bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 45.0);
477bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
478bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 90.0);
479bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
480bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandMirrorKernelInfo(kernel);
481f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  }
482f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
483f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  return(kernel);
484c84dce50867229e4872193e8eed5dbab58eb9f02anthony}
485c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4862c57b74e160f9b605d74dec24081309f28b83899cristyMagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string,
4872c57b74e160f9b605d74dec24081309f28b83899cristy  ExceptionInfo *exception)
4885ef8e94ff55717be2387d537bd49025780a1a558anthony{
4897a01dcf50ce12cb2a789bedff51e9345f022432eanthony  KernelInfo
490dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *kernel,
49143c4925e5305a26e48d68f7893e94f55d0831c39anthony    *new_kernel;
4927a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4935ef8e94ff55717be2387d537bd49025780a1a558anthony  char
4942c57b74e160f9b605d74dec24081309f28b83899cristy    *kernel_cache,
495151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    token[MagickPathExtent];
4965ef8e94ff55717be2387d537bd49025780a1a558anthony
4977a01dcf50ce12cb2a789bedff51e9345f022432eanthony  const char
498dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *p;
4997a01dcf50ce12cb2a789bedff51e9345f022432eanthony
5005e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
5015e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(ParseKernelArray(kernel_string));
502c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk  p=kernel_string;
5032c57b74e160f9b605d74dec24081309f28b83899cristy  kernel_cache=(char *) NULL;
5042c57b74e160f9b605d74dec24081309f28b83899cristy  if (*kernel_string == '@')
5052c57b74e160f9b605d74dec24081309f28b83899cristy    {
5062c57b74e160f9b605d74dec24081309f28b83899cristy      kernel_cache=FileToString(kernel_string+1,~0UL,exception);
5072c57b74e160f9b605d74dec24081309f28b83899cristy      if (kernel_cache == (char *) NULL)
5082c57b74e160f9b605d74dec24081309f28b83899cristy        return((KernelInfo *) NULL);
5092c57b74e160f9b605d74dec24081309f28b83899cristy      p=(const char *) kernel_cache;
5102c57b74e160f9b605d74dec24081309f28b83899cristy    }
511c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk  kernel=NULL;
5128bedb4edca01599dfd0612cb0daa35b09c67d736Cristy  while (GetNextToken(p,(const char **) NULL,MagickPathExtent,token), *token != '\0')
513c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk  {
5141e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp    /* ignore extra or multiple ';' kernel separators */
515c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk    if (*token != ';')
516c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk      {
517c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk        /* tokens starting with alpha is a Named kernel */
518c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk        if (isalpha((int) ((unsigned char) *token)) != 0)
5192c57b74e160f9b605d74dec24081309f28b83899cristy          new_kernel=ParseKernelName(p,exception);
520c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk        else /* otherwise a user defined kernel array */
521c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk          new_kernel=ParseKernelArray(p);
522c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk
523c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk        /* Error handling -- this is not proper error handling! */
524c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk        if (new_kernel == (KernelInfo *) NULL)
525c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk          {
526c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk            if (kernel != (KernelInfo *) NULL)
527c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk              kernel=DestroyKernelInfo(kernel);
528c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk            return((KernelInfo *) NULL);
529c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk          }
530e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
531c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk        /* initialise or append the kernel list */
532c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk        if (kernel == (KernelInfo *) NULL)
533c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk          kernel=new_kernel;
534c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk        else
535c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk          LastKernelInfo(kernel)->next=new_kernel;
536c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk      }
537dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
538dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    /* look for the next kernel in list */
539c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk    p=strchr(p,';');
540c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk    if (p == (char *) NULL)
541dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      break;
542dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p++;
543dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  }
5442c57b74e160f9b605d74dec24081309f28b83899cristy  if (kernel_cache != (char *) NULL)
5452c57b74e160f9b605d74dec24081309f28b83899cristy    kernel_cache=DestroyString(kernel_cache);
5467a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(kernel);
5475ef8e94ff55717be2387d537bd49025780a1a558anthony}
548602ab9b30b644a78a4057da93d838a77391ec0acanthony
549602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
550602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
551602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
552602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
553602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
554602ab9b30b644a78a4057da93d838a77391ec0acanthony%     A c q u i r e K e r n e l B u i l t I n                                 %
555602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
556602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
557602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
558602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559602ab9b30b644a78a4057da93d838a77391ec0acanthony%
560602ab9b30b644a78a4057da93d838a77391ec0acanthony%  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
561602ab9b30b644a78a4057da93d838a77391ec0acanthony%  kernels used for special purposes such as gaussian blurring, skeleton
562602ab9b30b644a78a4057da93d838a77391ec0acanthony%  pruning, and edge distance determination.
563602ab9b30b644a78a4057da93d838a77391ec0acanthony%
564602ab9b30b644a78a4057da93d838a77391ec0acanthony%  They take a KernelType, and a set of geometry style arguments, which were
565602ab9b30b644a78a4057da93d838a77391ec0acanthony%  typically decoded from a user supplied string, or from a more complex
566602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Morphology Method that was requested.
567602ab9b30b644a78a4057da93d838a77391ec0acanthony%
568602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernalBuiltIn method is:
569602ab9b30b644a78a4057da93d838a77391ec0acanthony%
5702be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
571602ab9b30b644a78a4057da93d838a77391ec0acanthony%           const GeometryInfo args)
572602ab9b30b644a78a4057da93d838a77391ec0acanthony%
573602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
574602ab9b30b644a78a4057da93d838a77391ec0acanthony%
575602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o type: the pre-defined type of kernel wanted
576602ab9b30b644a78a4057da93d838a77391ec0acanthony%
577602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o args: arguments defining or modifying the kernel
578602ab9b30b644a78a4057da93d838a77391ec0acanthony%
579602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Convolution Kernels
580602ab9b30b644a78a4057da93d838a77391ec0acanthony%
58146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    Unity
582529482f4b494010a13338a74446c510712f670b3anthony%       The a No-Op or Scaling single element kernel.
58346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
5843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Gaussian:{radius},{sigma}
5852489f53a1153c2b619b1c9a6744602e8840bd9a9glennrp%       Generate a two-dimensional gaussian kernel, as used by -gaussian.
586c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The sigma for the curve is required.  The resulting kernel is
587c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       normalized,
588c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
589c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
590602ab9b30b644a78a4057da93d838a77391ec0acanthony%
591602ab9b30b644a78a4057da93d838a77391ec0acanthony%       NOTE: that the 'radius' is optional, but if provided can limit (clip)
592602ab9b30b644a78a4057da93d838a77391ec0acanthony%       the final size of the resulting kernel to a square 2*radius+1 in size.
593602ab9b30b644a78a4057da93d838a77391ec0acanthony%       The radius should be at least 2 times that of the sigma value, or
594602ab9b30b644a78a4057da93d838a77391ec0acanthony%       sever clipping and aliasing may result.  If not given or set to 0 the
595602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius will be determined so as to produce the best minimal error
596602ab9b30b644a78a4057da93d838a77391ec0acanthony%       result, which is usally much larger than is normally needed.
597602ab9b30b644a78a4057da93d838a77391ec0acanthony%
598501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    LoG:{radius},{sigma}
599501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
600501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        The supposed ideal edge detection, zero-summing kernel.
601501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
602501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
603501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        approx 1.6 (according to wikipedia).
604501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
605501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    DoG:{radius},{sigma1},{sigma2}
606c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        "Difference of Gaussians" Kernel.
607c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
608c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
609c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        The result is a zero-summing kernel.
610c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
611c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Blur:{radius},{sigma}[,{angle}]
6124c08aed51c5899665ade97263692328eea4af106cristy%       Generates a 1 dimensional or linear gaussian blur, at the angle given
613c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       (current restricted to orthogonal angles).  If a 'radius' is given the
614c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
615c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       by a 90 degree angle.
616c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
617c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
618c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
619c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Note that two convolutions with two "Blur" kernels perpendicular to
620f0a92fd8deb68d411304359906b12679b675691fglennrp%       each other, is equivalent to a far larger "Gaussian" kernel with the
621c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       same sigma value, However it is much faster to apply. This is how the
622c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       "-blur" operator actually works.
623c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
6243c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Comet:{width},{sigma},{angle}
6253c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Blur in one direction only, much like how a bright object leaves
626602ab9b30b644a78a4057da93d838a77391ec0acanthony%       a comet like trail.  The Kernel is actually half a gaussian curve,
6273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Adding two such blurs in opposite directions produces a Blur Kernel.
6283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Angle can be rotated in multiples of 90 degrees.
629602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6303c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Note that the first argument is the width of the kernel and not the
631602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius of the kernel.
632602ab9b30b644a78a4057da93d838a77391ec0acanthony%
63340ca0b982379d4ab2716435a46603d56b5b218b1anthony%    Binomial:[{radius}]
63440ca0b982379d4ab2716435a46603d56b5b218b1anthony%       Generate a discrete kernel using a 2 dimentional Pascel's Triangle
635eef684ff80c7d5aca1493f4755426c88b3d3accdanthony%       of values. Used for special forma of image filters.
63640ca0b982379d4ab2716435a46603d56b5b218b1anthony%
637602ab9b30b644a78a4057da93d838a77391ec0acanthony%    # Still to be implemented...
638602ab9b30b644a78a4057da93d838a77391ec0acanthony%    #
6394fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter2D
6404fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter1D
6414fd27e21043be809d66c8202e779255e5b660d2danthony%    #    Set kernel values using a resize filter, and given scale (sigma)
642dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%    #    Cylindrical or Linear.   Is this possible with an image?
6434fd27e21043be809d66c8202e779255e5b660d2danthony%    #
644602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  Named Constant Convolution Kernels
6463c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
647c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  All these are unscaled, zero-summing kernels by default. As such for
648c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  non-HDRI version of ImageMagick some form of normalization, user scaling,
649c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  and biasing the results is recommended, to prevent the resulting image
650c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  being 'clipped'.
651c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
652c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  The 3x3 kernels (most of these) can be circularly rotated in multiples of
653c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  45 degrees to generate the 8 angled varients of each of the kernels.
6543c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
6553c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Laplacian:{type}
65643c4925e5305a26e48d68f7893e94f55d0831c39anthony%      Discrete Lapacian Kernels, (without normalization)
657c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
658c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
6599eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 2 :  3x3 with center:4 edge:1 corner:-2
6609eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 3 :  3x3 with center:4 edge:-2 corner:1
6619eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 5 :  5x5 laplacian
6629eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 7 :  7x7 laplacian
663501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 15 : 5x5 LoG (sigma approx 1.4)
664501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 19 : 9x9 LoG (sigma approx 1.4)
665c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
666c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Sobel:{angle}
66746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Sobel 'Edge' convolution kernel (3x3)
668c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
669c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -2, 0,-2 |
670c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
671c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
672c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Roberts:{angle}
67346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Roberts convolution kernel (3x3)
674c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
675c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 0 |
676c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
677c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
678c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Prewitt:{angle}
679c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%      Prewitt Edge convolution kernel (3x3)
680c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
681c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
682c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
683c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6849eb4f74649b23c053b308ce1152dce51239450baanthony%    Compass:{angle}
6859eb4f74649b23c053b308ce1152dce51239450baanthony%      Prewitt's "Compass" convolution kernel (3x3)
686c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
687c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1,-2, 1 |
688c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
689c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6909eb4f74649b23c053b308ce1152dce51239450baanthony%    Kirsch:{angle}
6919eb4f74649b23c053b308ce1152dce51239450baanthony%      Kirsch's "Compass" convolution kernel (3x3)
692c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
693c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3, 0, 5 |
694c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
6953c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
696c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{angle}
6971d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Frei-Chen Edge Detector is based on a kernel that is similar to
6981d5e67090dc7232b35bfcc71b31266c20838defcanthony%      the Sobel Kernel, but is designed to be isotropic. That is it takes
6991d5e67090dc7232b35bfcc71b31266c20838defcanthony%      into account the distance of the diagonal in the kernel.
700c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
701c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
702c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) |
703c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
704c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
705c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{type},{angle}
706c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
707c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      Frei-Chen Pre-weighted kernels...
708c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
709c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 0:  default un-nomalized version shown above.
710c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
711c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 1: Orthogonal Kernel (same as type 11 below)
712c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
713c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
714c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
715c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
716c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 2: Diagonal form of Kernel...
717c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     sqrt(2),    0     |
718c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
719c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   0,    -sqrt(2)    -1     |
720c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7211d5e67090dc7232b35bfcc71b31266c20838defcanthony%      However this kernel is als at the heart of the FreiChen Edge Detection
7221d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Process which uses a set of 9 specially weighted kernel.  These 9
7231d5e67090dc7232b35bfcc71b31266c20838defcanthony%      kernels not be normalized, but directly applied to the image. The
7241d5e67090dc7232b35bfcc71b31266c20838defcanthony%      results is then added together, to produce the intensity of an edge in
7251d5e67090dc7232b35bfcc71b31266c20838defcanthony%      a specific direction.  The square root of the pixel value can then be
7261d5e67090dc7232b35bfcc71b31266c20838defcanthony%      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
7271d5e67090dc7232b35bfcc71b31266c20838defcanthony%      from each other, both the direction and the strength of the edge can be
7281d5e67090dc7232b35bfcc71b31266c20838defcanthony%      determined.
7291d5e67090dc7232b35bfcc71b31266c20838defcanthony%
730c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 10: All 9 of the following pre-weighted kernels...
731c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
732c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 11: |   1,     0,   -1     |
733c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
734c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   1,     0,   -1     |
735e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
736c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 12: | 1, sqrt(2), 1 |
737c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0,   0,     0 | / 2*sqrt(2)
738c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, sqrt(2), 1 |
739e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
740c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 13: | sqrt(2), -1,    0     |
741c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  -1,      0,    1     | / 2*sqrt(2)
742c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   0,      1, -sqrt(2) |
743e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
744c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 14: |    0,     1, -sqrt(2) |
745c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   -1,     0,     1    | / 2*sqrt(2)
746c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), -1,     0    |
747e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
748c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 15: | 0, -1, 0 |
749c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1,  0, 1 | / 2
750c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0, -1, 0 |
751e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
752c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 16: |  1, 0, -1 |
753c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  0, 0,  0 | / 2
754c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, 0,  1 |
755e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
756c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 17: |  1, -2,  1 |
757c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2,  4, -2 | / 6
758c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, -2,  1 |
759501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
760c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 18: | -2, 1, -2 |
761c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  1, 4,  1 | / 6
762c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2, 1, -2 |
763e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
764c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 19: | 1, 1, 1 |
765c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 | / 3
766c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 |
767e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
768e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      The first 4 are for edge detection, the next 4 are for line detection
769e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      and the last is to add a average component to the results.
770e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
771c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      Using a special type of '-1' will return all 9 pre-weighted kernels
772c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      as a multi-kernel list, so that you can use them directly (without
773c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      normalization) with the special "-set option:morphology:compose Plus"
774c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      setting to apply the full FreiChen Edge Detection Technique.
775c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7761dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      If 'type' is large it will be taken to be an actual rotation angle for
7771dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
7781dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
7791dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%
780501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      WARNING: The above was layed out as per
781501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
782501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      But rotated 90 degrees so direction is from left rather than the top.
783501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      I have yet to find any secondary confirmation of the above. The only
784501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      other source found was actual source code at
785501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
786501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      Neigher paper defineds the kernels in a way that looks locical or
787501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      correct when taken as a whole.
788e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
789602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Boolean Kernels
790602ab9b30b644a78a4057da93d838a77391ec0acanthony%
7913c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Diamond:[{radius}[,{scale}]]
7921b2bc0a7da432e6e1cc0480280402df213faa940anthony%       Generate a diamond shaped kernel with given radius to the points.
793602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Kernel size will again be radius*2+1 square and defaults to radius 1,
794602ab9b30b644a78a4057da93d838a77391ec0acanthony%       generating a 3x3 kernel that is slightly larger than a square.
795602ab9b30b644a78a4057da93d838a77391ec0acanthony%
7963c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Square:[{radius}[,{scale}]]
797602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Generate a square shaped kernel of size radius*2+1, and defaulting
798602ab9b30b644a78a4057da93d838a77391ec0acanthony%       to a 3x3 (radius 1).
799602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8001ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagon:[{radius}[,{scale}]]
8011ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate octagonal shaped kernel of given radius and constant scale.
8020bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
8031ef941fea2534a0d20ba7d71307d35040247decbanthony%       in "Diamond" kernel.
8041ef941fea2534a0d20ba7d71307d35040247decbanthony%
8053c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Disk:[{radius}[,{scale}]]
8061ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate a binary disk, thresholded at the radius given, the radius
8071ef941fea2534a0d20ba7d71307d35040247decbanthony%       may be a float-point value. Final Kernel size is floor(radius)*2+1
8080bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       square. A radius of 5.3 is the default.
8091ef941fea2534a0d20ba7d71307d35040247decbanthony%
8101ef941fea2534a0d20ba7d71307d35040247decbanthony%       NOTE: That a low radii Disk kernels produce the same results as
8111ef941fea2534a0d20ba7d71307d35040247decbanthony%       many of the previously defined kernels, but differ greatly at larger
8121ef941fea2534a0d20ba7d71307d35040247decbanthony%       radii.  Here is a table of equivalences...
8131ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
8141ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1.5"  => "Square"
8151ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2"    => "Diamond:2"
8161ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.5"  => "Octagon"
8171ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.9"  => "Square:2"
8180bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%          "Disk:3.5"  => "Octagon:3"
8191ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:4.5"  => "Octagon:4"
8201ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:5.4"  => "Octagon:5"
8211ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:6.4"  => "Octagon:6"
8221ef941fea2534a0d20ba7d71307d35040247decbanthony%       All other Disk shapes are unique to this kernel, but because a "Disk"
8231ef941fea2534a0d20ba7d71307d35040247decbanthony%       is more circular when using a larger radius, using a larger radius is
8241ef941fea2534a0d20ba7d71307d35040247decbanthony%       preferred over iterating the morphological operation.
825602ab9b30b644a78a4057da93d838a77391ec0acanthony%
826a9892d898acb81e1ec73106d892855fdc5a69427anthony%    Rectangle:{geometry}
827a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Simply generate a rectangle of 1's with the size given. You can also
828a9892d898acb81e1ec73106d892855fdc5a69427anthony%       specify the location of the 'control point', otherwise the closest
829a9892d898acb81e1ec73106d892855fdc5a69427anthony%       pixel to the center of the rectangle is selected.
830a9892d898acb81e1ec73106d892855fdc5a69427anthony%
831a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Properly centered and odd sized rectangles work the best.
832a9892d898acb81e1ec73106d892855fdc5a69427anthony%
833c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  Symbol Dilation Kernels
834c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
835c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    These kernel is not a good general morphological kernel, but is used
836c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    more for highlighting and marking any single pixels in an image using,
837c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a "Dilate" method as appropriate.
838c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
839c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    For the same reasons iterating these kernels does not produce the
840c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    same result as using a larger radius for the symbol.
841c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
8423c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Plus:[{radius}[,{scale}]]
8433dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Cross:[{radius}[,{scale}]]
844c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Generate a kernel in the shape of a 'plus' or a 'cross' with
845c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       a each arm the length of the given radius (default 2).
8463dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
847f0a92fd8deb68d411304359906b12679b675691fglennrp%       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
848602ab9b30b644a78a4057da93d838a77391ec0acanthony%
849c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Ring:{radius1},{radius2}[,{scale}]
850c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       A ring of the values given that falls between the two radii.
851c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
852c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       This is the 'edge' pixels of the default "Disk" kernel,
853c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
8543dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
8553dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%  Hit and Miss Kernels
856602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8573dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Peak:radius1,radius2
858c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Find any peak larger than the pixels the fall between the two radii.
859c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The default ring of pixels is as per "Ring".
86043c4925e5305a26e48d68f7893e94f55d0831c39anthony%    Edges
861694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find flat orthogonal edges of a binary shape
8623dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Corners
863694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find 90 degree corners of a binary shape
864529482f4b494010a13338a74446c510712f670b3anthony%    Diagonals:type
865529482f4b494010a13338a74446c510712f670b3anthony%       A special kernel to thin the 'outside' of diagonals
866694934fa79dd310f727588b1d0a7481fa6170f1danthony%    LineEnds:type
8673dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Find end points of lines (for pruning a skeletion)
868694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Two types of lines ends (default to both) can be searched for
869694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: All line ends
870694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: single kernel for 4-conneected line ends
871694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: single kernel for simple line ends
8723dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    LineJunctions
87343c4925e5305a26e48d68f7893e94f55d0831c39anthony%       Find three line junctions (within a skeletion)
874694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: all line junctions
875694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Y Junction kernel
876694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Diagonal T Junction kernel
877694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 3: Orthogonal T Junction kernel
878694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 4: Diagonal X Junction kernel
879694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 5: Orthogonal + Junction kernel
880694934fa79dd310f727588b1d0a7481fa6170f1danthony%    Ridges:type
881694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find single pixel ridges or thin lines
882694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Fine single pixel thick lines and ridges
883694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Find two pixel thick lines and ridges
8843dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    ConvexHull
885a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
886c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    Skeleton:type
887c40ac1e79923a1516075ba1197ae4ed90244af9banthony%       Traditional skeleton generating kernels.
888694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Tradional Skeleton kernel (4 connected skeleton)
889694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
890e816a586a13717bab2d6839ced6e5c3828a37f19anthony%         Type 3: Thinning skeleton based on a ressearch paper by
8912b2290b46c246ce1f14cb78f1695394e4c4a3ddfanthony%                 Dan S. Bloomberg (Default Type)
892e816a586a13717bab2d6839ced6e5c3828a37f19anthony%    ThinSE:type
893e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       A huge variety of Thinning Kernels designed to preserve conectivity.
894e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       many other kernel sets use these kernels as source definitions.
895e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       Type numbers are 41-49, 81-89, 481, and 482 which are based on
896e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       the super and sub notations used in the source research paper.
897602ab9b30b644a78a4057da93d838a77391ec0acanthony%
898602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Distance Measuring Kernels
899602ab9b30b644a78a4057da93d838a77391ec0acanthony%
900c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Different types of distance measuring methods, which are used with the
901c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a 'Distance' morphology method for generating a gradient based on
902c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    distance from an edge of a binary shape, though there is a technique
903c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    for handling a anti-aliased shape.
904602ab9b30b644a78a4057da93d838a77391ec0acanthony%
905c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    See the 'Distance' Morphological Method, for information of how it is
906c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    applied.
907602ab9b30b644a78a4057da93d838a77391ec0acanthony%
908c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Chebyshev:[{radius}][x{scale}[%!]]
9091ef941fea2534a0d20ba7d71307d35040247decbanthony%       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
9101ef941fea2534a0d20ba7d71307d35040247decbanthony%       is a value of one to any neighbour, orthogonal or diagonal. One why
9111ef941fea2534a0d20ba7d71307d35040247decbanthony%       of thinking of it is the number of squares a 'King' or 'Queen' in
9121ef941fea2534a0d20ba7d71307d35040247decbanthony%       chess needs to traverse reach any other position on a chess board.
9131ef941fea2534a0d20ba7d71307d35040247decbanthony%       It results in a 'square' like distance function, but one where
9141ef941fea2534a0d20ba7d71307d35040247decbanthony%       diagonals are given a value that is closer than expected.
915c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
916bee715c4c0fd9efe6e21d8627ae8664434df7750anthony%    Manhattan:[{radius}][x{scale}[%!]]
9171ef941fea2534a0d20ba7d71307d35040247decbanthony%       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
9181ef941fea2534a0d20ba7d71307d35040247decbanthony%       Cab distance metric), it is the distance needed when you can only
9191ef941fea2534a0d20ba7d71307d35040247decbanthony%       travel in horizontal or vertical directions only.  It is the
9201ef941fea2534a0d20ba7d71307d35040247decbanthony%       distance a 'Rook' in chess would have to travel, and results in a
9211ef941fea2534a0d20ba7d71307d35040247decbanthony%       diamond like distances, where diagonals are further than expected.
9221ef941fea2534a0d20ba7d71307d35040247decbanthony%
9231ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagonal:[{radius}][x{scale}[%!]]
9241ef941fea2534a0d20ba7d71307d35040247decbanthony%       An interleving of Manhatten and Chebyshev metrics producing an
9251ef941fea2534a0d20ba7d71307d35040247decbanthony%       increasing octagonally shaped distance.  Distances matches those of
9261ef941fea2534a0d20ba7d71307d35040247decbanthony%       the "Octagon" shaped kernel of the same radius.  The minimum radius
9271ef941fea2534a0d20ba7d71307d35040247decbanthony%       and default is 2, producing a 5x5 kernel.
928c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
929c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Euclidean:[{radius}][x{scale}[%!]]
9301ef941fea2534a0d20ba7d71307d35040247decbanthony%       Euclidean distance is the 'direct' or 'as the crow flys' distance.
931c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       However by default the kernel size only has a radius of 1, which
932c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       limits the distance to 'Knight' like moves, with only orthogonal and
933c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       diagonal measurements being correct.  As such for the default kernel
9341ef941fea2534a0d20ba7d71307d35040247decbanthony%       you will get octagonal like distance function.
9351ef941fea2534a0d20ba7d71307d35040247decbanthony%
9361ef941fea2534a0d20ba7d71307d35040247decbanthony%       However using a larger radius such as "Euclidean:4" you will get a
9371ef941fea2534a0d20ba7d71307d35040247decbanthony%       much smoother distance gradient from the edge of the shape. Especially
9381ef941fea2534a0d20ba7d71307d35040247decbanthony%       if the image is pre-processed to include any anti-aliasing pixels.
9391ef941fea2534a0d20ba7d71307d35040247decbanthony%       Of course a larger kernel is slower to use, and not always needed.
9401ef941fea2534a0d20ba7d71307d35040247decbanthony%
9411ef941fea2534a0d20ba7d71307d35040247decbanthony%    The first three Distance Measuring Kernels will only generate distances
9421ef941fea2534a0d20ba7d71307d35040247decbanthony%    of exact multiples of {scale} in binary images. As such you can use a
9431ef941fea2534a0d20ba7d71307d35040247decbanthony%    scale of 1 without loosing any information.  However you also need some
9441ef941fea2534a0d20ba7d71307d35040247decbanthony%    scaling when handling non-binary anti-aliased shapes.
9451ef941fea2534a0d20ba7d71307d35040247decbanthony%
9461ef941fea2534a0d20ba7d71307d35040247decbanthony%    The "Euclidean" Distance Kernel however does generate a non-integer
9471ef941fea2534a0d20ba7d71307d35040247decbanthony%    fractional results, and as such scaling is vital even for binary shapes.
948c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
949602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
950602ab9b30b644a78a4057da93d838a77391ec0acanthony
9512be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristyMagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
9522c57b74e160f9b605d74dec24081309f28b83899cristy  const GeometryInfo *args,ExceptionInfo *exception)
953602ab9b30b644a78a4057da93d838a77391ec0acanthony{
9542be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
955602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
956602ab9b30b644a78a4057da93d838a77391ec0acanthony
957bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
958602ab9b30b644a78a4057da93d838a77391ec0acanthony    i;
959602ab9b30b644a78a4057da93d838a77391ec0acanthony
960bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
961602ab9b30b644a78a4057da93d838a77391ec0acanthony    u,
962602ab9b30b644a78a4057da93d838a77391ec0acanthony    v;
963602ab9b30b644a78a4057da93d838a77391ec0acanthony
964602ab9b30b644a78a4057da93d838a77391ec0acanthony  double
965602ab9b30b644a78a4057da93d838a77391ec0acanthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
966602ab9b30b644a78a4057da93d838a77391ec0acanthony
967c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  /* Generate a new empty kernel if needed */
968e96405a0f45f803fb9c26f75e7bdee252437febbcristy  kernel=(KernelInfo *) NULL;
969c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  switch(type) {
9701dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case UndefinedKernel:    /* These should not call this function */
9719eb4f74649b23c053b308ce1152dce51239450baanthony    case UserDefinedKernel:
972f432c635c526259b858c9aad3d409c5c44545686cristy      assert("Should not call this function" != (char *) NULL);
9739eb4f74649b23c053b308ce1152dce51239450baanthony      break;
974529482f4b494010a13338a74446c510712f670b3anthony    case LaplacianKernel:   /* Named Descrete Convolution Kernels */
975529482f4b494010a13338a74446c510712f670b3anthony    case SobelKernel:       /* these are defined using other kernels */
9769eb4f74649b23c053b308ce1152dce51239450baanthony    case RobertsKernel:
9779eb4f74649b23c053b308ce1152dce51239450baanthony    case PrewittKernel:
9789eb4f74649b23c053b308ce1152dce51239450baanthony    case CompassKernel:
9799eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
9801dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case FreiChenKernel:
981694934fa79dd310f727588b1d0a7481fa6170f1danthony    case EdgesKernel:       /* Hit and Miss kernels */
982694934fa79dd310f727588b1d0a7481fa6170f1danthony    case CornersKernel:
983529482f4b494010a13338a74446c510712f670b3anthony    case DiagonalsKernel:
9849eb4f74649b23c053b308ce1152dce51239450baanthony    case LineEndsKernel:
9859eb4f74649b23c053b308ce1152dce51239450baanthony    case LineJunctionsKernel:
9861dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case RidgesKernel:
9879eb4f74649b23c053b308ce1152dce51239450baanthony    case ConvexHullKernel:
9889eb4f74649b23c053b308ce1152dce51239450baanthony    case SkeletonKernel:
9899a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony    case ThinSEKernel:
990c40ac1e79923a1516075ba1197ae4ed90244af9banthony      break;               /* A pre-generated kernel is not needed */
991c40ac1e79923a1516075ba1197ae4ed90244af9banthony#if 0
992c40ac1e79923a1516075ba1197ae4ed90244af9banthony    /* set to 1 to do a compile-time check that we haven't missed anything */
993529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
994c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case GaussianKernel:
995501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
996501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
997c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case BlurKernel:
998c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CometKernel:
99940ca0b982379d4ab2716435a46603d56b5b218b1anthony    case BinomialKernel:
1000c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1001c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1002c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
10031ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
1004c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiskKernel:
1005c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PlusKernel:
1006c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CrossKernel:
1007c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
1008c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PeaksKernel:
1009c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case ChebyshevKernel:
1010bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
10111ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctangonalKernel:
1012c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case EuclideanKernel:
10131dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#else
10149eb4f74649b23c053b308ce1152dce51239450baanthony    default:
10151dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#endif
10169eb4f74649b23c053b308ce1152dce51239450baanthony      /* Generate the base Kernel Structure */
1017c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1018c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if (kernel == (KernelInfo *) NULL)
1019c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        return(kernel);
1020c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
102143c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1022c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->negative_range = kernel->positive_range = 0.0;
1023c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->type = type;
1024c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->next = (KernelInfo *) NULL;
1025e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy      kernel->signature=MagickCoreSignature;
1026c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
1027c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  }
1028602ab9b30b644a78a4057da93d838a77391ec0acanthony
1029602ab9b30b644a78a4057da93d838a77391ec0acanthony  switch(type) {
1030529482f4b494010a13338a74446c510712f670b3anthony    /*
1031529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels
1032529482f4b494010a13338a74446c510712f670b3anthony    */
1033529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
1034529482f4b494010a13338a74446c510712f670b3anthony      {
1035529482f4b494010a13338a74446c510712f670b3anthony        kernel->height = kernel->width = (size_t) 1;
1036529482f4b494010a13338a74446c510712f670b3anthony        kernel->x = kernel->y = (ssize_t) 0;
1037e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1038e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(1,sizeof(*kernel->values)));
1039d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
1040529482f4b494010a13338a74446c510712f670b3anthony          return(DestroyKernelInfo(kernel));
1041529482f4b494010a13338a74446c510712f670b3anthony        kernel->maximum = kernel->values[0] = args->rho;
1042529482f4b494010a13338a74446c510712f670b3anthony        break;
1043529482f4b494010a13338a74446c510712f670b3anthony      }
1044529482f4b494010a13338a74446c510712f670b3anthony      break;
1045602ab9b30b644a78a4057da93d838a77391ec0acanthony    case GaussianKernel:
1046501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
1047501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
1048602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1049c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1050c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma2 = fabs(args->xi),
10519eb4f74649b23c053b308ce1152dce51239450baanthony          A, B, R;
1052c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1053c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1054bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1055501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        else if ( (type != DoGKernel) || (sigma >= sigma2) )
1056c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1057c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1058c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1059c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->height = kernel->width;
1060bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1061e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1062e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1063e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1064d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
106583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1066602ab9b30b644a78a4057da93d838a77391ec0acanthony
106746a369d839971ab627bdb31a93d8bd63e81b65a3anthony        /* WARNING: The following generates a 'sampled gaussian' kernel.
10689eb4f74649b23c053b308ce1152dce51239450baanthony         * What we really want is a 'discrete gaussian' kernel.
106946a369d839971ab627bdb31a93d8bd63e81b65a3anthony         *
1070529482f4b494010a13338a74446c510712f670b3anthony         * How to do this is I don't know, but appears to be basied on the
1071529482f4b494010a13338a74446c510712f670b3anthony         * Error Function 'erf()' (intergral of a gaussian)
10729eb4f74649b23c053b308ce1152dce51239450baanthony         */
10739eb4f74649b23c053b308ce1152dce51239450baanthony
1074501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == GaussianKernel || type == DoGKernel )
1075501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { /* Calculate a Gaussian,  OR positive half of a DoG */
10769eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
10779eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
107855a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1079bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1080bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
10819eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
10829eb4f74649b23c053b308ce1152dce51239450baanthony              }
10839eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
10849eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
108575920b23a8a3883792cbb69d569c33cc789cf1b5cristy                  kernel->width*kernel->height*sizeof(*kernel->values));
10869eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
10879eb4f74649b23c053b308ce1152dce51239450baanthony              }
1088c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
10899eb4f74649b23c053b308ce1152dce51239450baanthony
1090501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == DoGKernel )
1091c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
1092c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            if ( sigma2 > MagickEpsilon )
1093c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              { sigma = sigma2;                /* simplify loop expressions */
10949eb4f74649b23c053b308ce1152dce51239450baanthony                A = 1.0/(2.0*sigma*sigma);
109555a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1096bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1097bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
10989eb4f74649b23c053b308ce1152dce51239450baanthony                    kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1099c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              }
11009eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
1101c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1102c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
11039eb4f74649b23c053b308ce1152dce51239450baanthony
1104501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == LoGKernel )
11059eb4f74649b23c053b308ce1152dce51239450baanthony          { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
11069eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
11079eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
110855a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1109bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1110bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11119eb4f74649b23c053b308ce1152dce51239450baanthony                    { R = ((double)(u*u+v*v))*A;
11129eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = (1-R)*exp(-R)*B;
11139eb4f74649b23c053b308ce1152dce51239450baanthony                    }
11149eb4f74649b23c053b308ce1152dce51239450baanthony              }
11159eb4f74649b23c053b308ce1152dce51239450baanthony            else /* special case - generate a unity kernel */
11169eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
111775920b23a8a3883792cbb69d569c33cc789cf1b5cristy                  kernel->width*kernel->height*sizeof(*kernel->values));
11189eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
11199eb4f74649b23c053b308ce1152dce51239450baanthony              }
11209eb4f74649b23c053b308ce1152dce51239450baanthony          }
11219eb4f74649b23c053b308ce1152dce51239450baanthony
11229eb4f74649b23c053b308ce1152dce51239450baanthony        /* Note the above kernels may have been 'clipped' by a user defined
1123c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
1124c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** sigma's (> 0.1) the central value becomes larger than one, and thus
1125c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** producing a very bright kernel.
1126c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1127c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** Normalization will still be needed.
1128c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        */
1129602ab9b30b644a78a4057da93d838a77391ec0acanthony
11303dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* Normalize the 2D Gaussian Kernel
11313dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        **
1132c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1133c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
11343dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        */
113546a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1136c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1137602ab9b30b644a78a4057da93d838a77391ec0acanthony
1138602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1139602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1140602ab9b30b644a78a4057da93d838a77391ec0acanthony    case BlurKernel:
1141602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1142c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1143501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          alpha, beta;
1144602ab9b30b644a78a4057da93d838a77391ec0acanthony
1145c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1146bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1147c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1148501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1149602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1150bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = (ssize_t) (kernel->width-1)/2;
1151c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->y = 0;
1152c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
1153e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1154e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1155e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1156d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
115783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1158602ab9b30b644a78a4057da93d838a77391ec0acanthony
1159602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1160602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1161602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
1162602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** It generates a gaussian 3 times the width, and compresses it into
1163602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** the expected range.  This produces a closer normalization of the
1164602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** resulting kernel, especially for very low sigma values.
1165602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** As such while wierd it is prefered.
1166602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1167602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** I am told this method originally came from Photoshop.
11689eb4f74649b23c053b308ce1152dce51239450baanthony        **
11699eb4f74649b23c053b308ce1152dce51239450baanthony        ** A properly normalized curve is generated (apart from edge clipping)
11709eb4f74649b23c053b308ce1152dce51239450baanthony        ** even though we later normalize the result (for edge clipping)
11719eb4f74649b23c053b308ce1152dce51239450baanthony        ** to allow the correct generation of a "Difference of Blurs".
1172602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1173c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1174c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* initialize */
1175bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
11769eb4f74649b23c053b308ce1152dce51239450baanthony        (void) ResetMagickMemory(kernel->values,0, (size_t)
117775920b23a8a3883792cbb69d569c33cc789cf1b5cristy          kernel->width*kernel->height*sizeof(*kernel->values));
1178c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive 1D Gaussian */
1179c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1180c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { sigma *= KernelRank;               /* simplify loop expressions */
1181501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            alpha = 1.0/(2.0*sigma*sigma);
118255a91cddcdea3aa002893186a773e1704884a9dfcristy            beta= (double) (1.0/(MagickSQ2PI*sigma ));
1183c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            for ( u=-v; u <= v; u++) {
1184501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[(u+v)/KernelRank] +=
1185501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                              exp(-((double)(u*u))*alpha)*beta;
1186c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            }
1187c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1188c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1189c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1190602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
119153f576d128f0bb744a82e7f9c0d8f05b2923972canthony        /* Direct calculation without curve averaging
119253f576d128f0bb744a82e7f9c0d8f05b2923972canthony           This is equivelent to a KernelRank of 1 */
1193c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1194c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive Gaussian */
1195c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1196501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
1197501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            beta = 1.0/(MagickSQ2PI*sigma);
1198bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1199501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1200c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1201c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1202c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
120375920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*kernel->height*sizeof(*kernel->values));
1204c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1205c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1206602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
1207c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Note the above kernel may have been 'clipped' by a user defined
1208cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
120953f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** sigma's (> 0.1) the central value becomes larger than one, as a
121053f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** result of not generating a actual 'discrete' kernel, and thus
121153f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** producing a very bright 'impulse'.
1212c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
121353f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** Becuase of these two factors Normalization is required!
1214602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1215cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1216602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Normalize the 1D Gaussian Kernel
1217602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1218c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1219c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
1220602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
122146a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
122246a369d839971ab627bdb31a93d8bd63e81b65a3anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1223cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1224c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* rotate the 1D kernel by given angle */
1225501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        RotateKernelInfo(kernel, args->xi );
1226602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1227602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1228602ab9b30b644a78a4057da93d838a77391ec0acanthony    case CometKernel:
1229602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
12309eb4f74649b23c053b308ce1152dce51239450baanthony          sigma = fabs(args->sigma),
12319eb4f74649b23c053b308ce1152dce51239450baanthony          A;
1232602ab9b30b644a78a4057da93d838a77391ec0acanthony
1233602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( args->rho < 1.0 )
1234e1cf9465864144e8b8043d522906c1e47bbf6192anthony          kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1235602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
1236bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho;
1237c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->x = kernel->y = 0;
1238602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1239c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
1240e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1241e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1242e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1243d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
124483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1245602ab9b30b644a78a4057da93d838a77391ec0acanthony
1246c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* A comet blur is half a 1D gaussian curve, so that the object is
1247602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** blurred in one direction only.  This may not be quite the right
12483dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** curve to use so may change in the future. The function must be
12493dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** normalised after generation, which also resolves any clipping.
1250c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1251c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** As we are normalizing and not subtracting gaussians,
1252c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there is no need for a divisor in the gaussian formula
1253c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
125443c4925e5305a26e48d68f7893e94f55d0831c39anthony        ** It is less comples
1255602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
12569eb4f74649b23c053b308ce1152dce51239450baanthony        if ( sigma > MagickEpsilon )
12579eb4f74649b23c053b308ce1152dce51239450baanthony          {
1258602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1259602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1260bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            v = (ssize_t) kernel->width*KernelRank; /* start/end points */
12619eb4f74649b23c053b308ce1152dce51239450baanthony            (void) ResetMagickMemory(kernel->values,0, (size_t)
126275920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*sizeof(*kernel->values));
12639eb4f74649b23c053b308ce1152dce51239450baanthony            sigma *= KernelRank;            /* simplify the loop expression */
12649eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);
12659eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
12669eb4f74649b23c053b308ce1152dce51239450baanthony            for ( u=0; u < v; u++) {
12679eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->values[u/KernelRank] +=
12689eb4f74649b23c053b308ce1152dce51239450baanthony                  exp(-((double)(u*u))*A);
12699eb4f74649b23c053b308ce1152dce51239450baanthony              /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
12709eb4f74649b23c053b308ce1152dce51239450baanthony            }
1271bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (i=0; i < (ssize_t) kernel->width; i++)
12729eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range += kernel->values[i];
1273602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
12749eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
12759eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
1276bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0; i < (ssize_t) kernel->width; i++)
12779eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range +=
127853f576d128f0bb744a82e7f9c0d8f05b2923972canthony                kernel->values[i] = exp(-((double)(i*i))*A);
12799eb4f74649b23c053b308ce1152dce51239450baanthony                /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1280602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
12819eb4f74649b23c053b308ce1152dce51239450baanthony          }
12829eb4f74649b23c053b308ce1152dce51239450baanthony        else /* special case - generate a unity kernel */
12839eb4f74649b23c053b308ce1152dce51239450baanthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
128475920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*kernel->height*sizeof(*kernel->values));
12859eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
12869eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->positive_range = 1.0;
12879eb4f74649b23c053b308ce1152dce51239450baanthony          }
128846a369d839971ab627bdb31a93d8bd63e81b65a3anthony
128946a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->minimum = 0.0;
1290c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
129146a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->negative_range = 0.0;
1292602ab9b30b644a78a4057da93d838a77391ec0acanthony
1293999bb2c20aa9d42875bb5adba44951988d4ae354anthony        ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
1294999bb2c20aa9d42875bb5adba44951988d4ae354anthony        RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
1295602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1296602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
129740ca0b982379d4ab2716435a46603d56b5b218b1anthony    case BinomialKernel:
129840ca0b982379d4ab2716435a46603d56b5b218b1anthony      {
129940ca0b982379d4ab2716435a46603d56b5b218b1anthony        size_t
130040ca0b982379d4ab2716435a46603d56b5b218b1anthony          order_f;
130140ca0b982379d4ab2716435a46603d56b5b218b1anthony
130240ca0b982379d4ab2716435a46603d56b5b218b1anthony        if (args->rho < 1.0)
130340ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
130440ca0b982379d4ab2716435a46603d56b5b218b1anthony        else
130540ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
130640ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
130740ca0b982379d4ab2716435a46603d56b5b218b1anthony
130840ca0b982379d4ab2716435a46603d56b5b218b1anthony        order_f = fact(kernel->width-1);
130940ca0b982379d4ab2716435a46603d56b5b218b1anthony
1310e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1311e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1312e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1313d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
131440ca0b982379d4ab2716435a46603d56b5b218b1anthony          return(DestroyKernelInfo(kernel));
131540ca0b982379d4ab2716435a46603d56b5b218b1anthony
131640ca0b982379d4ab2716435a46603d56b5b218b1anthony        /* set all kernel values within diamond area to scale given */
131740ca0b982379d4ab2716435a46603d56b5b218b1anthony        for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
131840ca0b982379d4ab2716435a46603d56b5b218b1anthony          { size_t
1319f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy              alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
132040ca0b982379d4ab2716435a46603d56b5b218b1anthony            for ( u=0; u < (ssize_t)kernel->width; u++, i++)
132140ca0b982379d4ab2716435a46603d56b5b218b1anthony              kernel->positive_range += kernel->values[i] = (double)
1322f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy                (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
132340ca0b982379d4ab2716435a46603d56b5b218b1anthony          }
132440ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->minimum = 1.0;
132540ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
132640ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->negative_range = 0.0;
132740ca0b982379d4ab2716435a46603d56b5b218b1anthony        break;
132840ca0b982379d4ab2716435a46603d56b5b218b1anthony      }
1329c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1330529482f4b494010a13338a74446c510712f670b3anthony    /*
1331529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels - Well Known Named Constant Kernels
1332529482f4b494010a13338a74446c510712f670b3anthony    */
13333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case LaplacianKernel:
1334e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      { switch ( (int) args->rho ) {
13353dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony          case 0:
13369eb4f74649b23c053b308ce1152dce51239450baanthony          default: /* laplacian square filter -- default */
1337c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
13383dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            break;
13399eb4f74649b23c053b308ce1152dce51239450baanthony          case 1:  /* laplacian diamond filter */
1340c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
13413c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13423c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          case 2:
13439eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
13449eb4f74649b23c053b308ce1152dce51239450baanthony            break;
13459eb4f74649b23c053b308ce1152dce51239450baanthony          case 3:
1346c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
13473c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13489eb4f74649b23c053b308ce1152dce51239450baanthony          case 5:   /* a 5x5 laplacian */
13493c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
13509eb4f74649b23c053b308ce1152dce51239450baanthony              "5: -4,-1,0,-1,-4  -1,2,3,2,-1  0,3,4,3,0  -1,2,3,2,-1  -4,-1,0,-1,-4");
13513c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13529eb4f74649b23c053b308ce1152dce51239450baanthony          case 7:   /* a 7x7 laplacian */
13533c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
1354c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              "7:-10,-5,-2,-1,-2,-5,-10 -5,0,3,4,3,0,-5 -2,3,6,7,6,3,-2 -1,4,7,8,7,4,-1 -2,3,6,7,6,3,-2 -5,0,3,4,3,0,-5 -10,-5,-2,-1,-2,-5,-10" );
13553c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
1356501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 15:  /* a 5x5 LoG (sigma approx 1.4) */
13579eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray(
13589eb4f74649b23c053b308ce1152dce51239450baanthony              "5: 0,0,-1,0,0  0,-1,-2,-1,0  -1,-2,16,-2,-1  0,-1,-2,-1,0  0,0,-1,0,0");
13599eb4f74649b23c053b308ce1152dce51239450baanthony            break;
1360501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 19:  /* a 9x9 LoG (sigma approx 1.4) */
136143c4925e5305a26e48d68f7893e94f55d0831c39anthony            /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
136243c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel=ParseKernelArray(
1363bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony              "9: 0,-1,-1,-2,-2,-2,-1,-1,0  -1,-2,-4,-5,-5,-5,-4,-2,-1  -1,-4,-5,-3,-0,-3,-5,-4,-1  -2,-5,-3,12,24,12,-3,-5,-2  -2,-5,-0,24,40,24,-0,-5,-2  -2,-5,-3,12,24,12,-3,-5,-2  -1,-4,-5,-3,-0,-3,-5,-4,-1  -1,-2,-4,-5,-5,-5,-4,-2,-1  0,-1,-1,-2,-2,-2,-1,-1,0");
136443c4925e5305a26e48d68f7893e94f55d0831c39anthony            break;
13653c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
13663c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        if (kernel == (KernelInfo *) NULL)
13673c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          return(kernel);
13683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        kernel->type = type;
13693c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        break;
13703c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      }
1371c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SobelKernel:
1372cceb6f05c2016ea3854b29e7770ec5ff49034ecfanthony      { /* Simple Sobel Kernel */
1373dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1374dcc2a474c98415e565434fe52f630acba36fa0c1anthony        if (kernel == (KernelInfo *) NULL)
1375dcc2a474c98415e565434fe52f630acba36fa0c1anthony          return(kernel);
1376dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel->type = type;
1377dcc2a474c98415e565434fe52f630acba36fa0c1anthony        RotateKernelInfo(kernel, args->rho);
1378dcc2a474c98415e565434fe52f630acba36fa0c1anthony        break;
1379dcc2a474c98415e565434fe52f630acba36fa0c1anthony      }
1380c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RobertsKernel:
1381c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1382501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
1383c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1384c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1385c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
138646a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1387c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1388c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1389c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PrewittKernel:
1390c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1391501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
1392c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1393c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1394c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
139546a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1396c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1397c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1398c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CompassKernel:
1399c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1400501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
1401c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1402c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1403c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
140446a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1405c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1406c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
14079eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
14089eb4f74649b23c053b308ce1152dce51239450baanthony      {
1409501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
14109eb4f74649b23c053b308ce1152dce51239450baanthony        if (kernel == (KernelInfo *) NULL)
14119eb4f74649b23c053b308ce1152dce51239450baanthony          return(kernel);
14129eb4f74649b23c053b308ce1152dce51239450baanthony        kernel->type = type;
141346a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
14149eb4f74649b23c053b308ce1152dce51239450baanthony        break;
14159eb4f74649b23c053b308ce1152dce51239450baanthony      }
1416e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony    case FreiChenKernel:
1417501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* Direction is set to be left to right positive */
1418501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
1419501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
14201dd091ae3bc17edc26c16cc47f436a24bd48412aanthony      { switch ( (int) args->rho ) {
1421e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          default:
1422c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 0:
1423501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1424c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            if (kernel == (KernelInfo *) NULL)
1425c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1426ef33d9f66f955c1f6f5f7105e164cc2d5c5e2a41anthony            kernel->type = type;
1427d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[3] = +(MagickRealType) MagickSQ2;
1428d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = -(MagickRealType) MagickSQ2;
1429c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1430c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            break;
1431c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 2:
1432c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
1433c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1434c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1435c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->type = type;
1436d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
1437d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
1438c40ac1e79923a1516075ba1197ae4ed90244af9banthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1439d4e3ffa7f64077da0f32c2d8a599737ee8d115ddcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1440c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1441c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 10:
14422c57b74e160f9b605d74dec24081309f28b83899cristy          {
14432c57b74e160f9b605d74dec24081309f28b83899cristy            kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19",exception);
1444c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1445c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1446c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
14472c57b74e160f9b605d74dec24081309f28b83899cristy          }
1448e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          case 1:
1449c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 11:
1450501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1451e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1452e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1453c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1454d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[3] = +(MagickRealType) MagickSQ2;
1455d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = -(MagickRealType) MagickSQ2;
1456e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
145755a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1458e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1459c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 12:
1460501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
1461e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1462e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1463c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1464d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel->values[1] = +(MagickRealType) MagickSQ2;
1465d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel->values[7] = +(MagickRealType) MagickSQ2;
1466e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
146755a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1468e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1469c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 13:
1470501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
1471e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1472e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1473c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1474d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[0] = +(MagickRealType) MagickSQ2;
1475d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[8] = -(MagickRealType) MagickSQ2;
1476e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
147755a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1478e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1479c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 14:
14801d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
1481e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1482e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1483c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1484d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[2] = -(MagickRealType) MagickSQ2;
1485d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[6] = +(MagickRealType) MagickSQ2;
1486e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
148755a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1488e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1489c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 15:
1490501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
1491e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1492e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1493c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1494e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1495e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1496c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 16:
14971d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
1498e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1499e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1500c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1501e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1502e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1503c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 17:
1504501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
1505e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1506e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1507c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1508e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1509e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1510c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 18:
1511501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1512e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1513e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1514c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1515e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1516e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1517c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 19:
1518c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
1519e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1520e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1521c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1522e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1523e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1524e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        }
1525b978e458a8e1f210bcb580951cf623687236b2fecristy        if ( fabs(args->sigma) >= MagickEpsilon )
1526c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by correctly supplied 'angle' */
1527c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->sigma);
1528c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony        else if ( args->rho > 30.0 || args->rho < -30.0 )
1529c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by out of bounds 'type' */
1530c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->rho);
1531e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        break;
1532e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      }
1533e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony
1534529482f4b494010a13338a74446c510712f670b3anthony    /*
1535529482f4b494010a13338a74446c510712f670b3anthony      Boolean or Shaped Kernels
1536529482f4b494010a13338a74446c510712f670b3anthony    */
1537c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1538602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
1539c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (args->rho < 1.0)
1540c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
1541c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1542bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1543bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1544c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1545e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1546e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1547e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1548d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
1549c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(DestroyKernelInfo(kernel));
1550c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1551c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* set all kernel values within diamond area to scale given */
1552bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1553bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
15541d5e67090dc7232b35bfcc71b31266c20838defcanthony            if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1555c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->positive_range += kernel->values[i] = args->sigma;
1556c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            else
1557c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[i] = nan;
1558c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1559c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1560c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1561c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1562c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
1563c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      { double
1564c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          scale;
1565602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( type == SquareKernel )
1566602ab9b30b644a78a4057da93d838a77391ec0acanthony          {
1567602ab9b30b644a78a4057da93d838a77391ec0acanthony            if (args->rho < 1.0)
1568c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony              kernel->width = kernel->height = 3;  /* default radius = 1 */
1569602ab9b30b644a78a4057da93d838a77391ec0acanthony            else
1570bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              kernel->width = kernel->height = (size_t) (2*args->rho+1);
1571bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
15724fd27e21043be809d66c8202e779255e5b660d2danthony            scale = args->sigma;
1573602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1574602ab9b30b644a78a4057da93d838a77391ec0acanthony        else {
15752be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy            /* NOTE: user defaults set in "AcquireKernelInfo()" */
1576602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->rho < 1.0 || args->sigma < 1.0 )
157783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1578bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->width = (size_t)args->rho;
1579bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->height = (size_t)args->sigma;
1580602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
1581602ab9b30b644a78a4057da93d838a77391ec0acanthony                 args->psi < 0.0 || args->psi > (double)kernel->height )
158283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1583bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = (ssize_t) args->xi;
1584bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->y = (ssize_t) args->psi;
15854fd27e21043be809d66c8202e779255e5b660d2danthony            scale = 1.0;
1586602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1587e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1588e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1589e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1590d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
159183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1592602ab9b30b644a78a4057da93d838a77391ec0acanthony
15933dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* set all kernel values to scale given */
1594eaedf06777741da32408da72c1e512975c600c48cristy        u=(ssize_t) (kernel->width*kernel->height);
1595150989ed67ef9da53141a65e5f3ebdb05dd025abcristy        for ( i=0; i < u; i++)
15964fd27e21043be809d66c8202e779255e5b660d2danthony            kernel->values[i] = scale;
15974fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->minimum = kernel->maximum = scale;   /* a flat shape */
15984fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->positive_range = scale*u;
1599cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        break;
1600602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
16011ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonKernel:
16021ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16031ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
1604a9892d898acb81e1ec73106d892855fdc5a69427anthony            kernel->width = kernel->height = 5;  /* default radius = 2 */
16051ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16061ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16071ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16081ef941fea2534a0d20ba7d71307d35040247decbanthony
1609e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1610e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1611e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1612d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16131ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16141ef941fea2534a0d20ba7d71307d35040247decbanthony
16151ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16161ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16171ef941fea2534a0d20ba7d71307d35040247decbanthony              if ( (labs((long) u)+labs((long) v)) <=
16181ef941fea2534a0d20ba7d71307d35040247decbanthony                        ((long)kernel->x + (long)(kernel->x/2)) )
16191ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16201ef941fea2534a0d20ba7d71307d35040247decbanthony              else
16211ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->values[i] = nan;
1622a9892d898acb81e1ec73106d892855fdc5a69427anthony          kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
16231ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16241ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16251ef941fea2534a0d20ba7d71307d35040247decbanthony      case DiskKernel:
16261ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16271ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
16280bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony            limit = (ssize_t)(args->rho*args->rho);
16291ef941fea2534a0d20ba7d71307d35040247decbanthony
16309bf68d590429a72aa70894f6aea7fc3c94876e54anthony          if (args->rho < 0.4)           /* default radius approx 4.3 */
16319bf68d590429a72aa70894f6aea7fc3c94876e54anthony            kernel->width = kernel->height = 9L, limit = 18L;
16321ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16331ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
16341ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16351ef941fea2534a0d20ba7d71307d35040247decbanthony
1636e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1637e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1638e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1639d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16401ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16411ef941fea2534a0d20ba7d71307d35040247decbanthony
16421ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16431ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16441ef941fea2534a0d20ba7d71307d35040247decbanthony              if ((u*u+v*v) <= limit)
16451ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16463dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony              else
16473dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony                kernel->values[i] = nan;
16481ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16491ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16501ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16511ef941fea2534a0d20ba7d71307d35040247decbanthony      case PlusKernel:
16521ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16531ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16541ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16551ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16561ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16571ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16581ef941fea2534a0d20ba7d71307d35040247decbanthony
1659e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1660e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1661e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1662d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16631ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16641ef941fea2534a0d20ba7d71307d35040247decbanthony
16651ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16661ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16671ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16681ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
16691ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16701ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16711ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16721ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16731ef941fea2534a0d20ba7d71307d35040247decbanthony      case CrossKernel:
16741ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16751ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16761ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16771ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16781ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16791ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16801ef941fea2534a0d20ba7d71307d35040247decbanthony
1681e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1682e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1683e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1684d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16851ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16861ef941fea2534a0d20ba7d71307d35040247decbanthony
16871ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16881ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16891ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16901ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
16911ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16921ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16931ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16941ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1695529482f4b494010a13338a74446c510712f670b3anthony      /*
1696529482f4b494010a13338a74446c510712f670b3anthony        HitAndMiss Kernels
1697529482f4b494010a13338a74446c510712f670b3anthony      */
16981ef941fea2534a0d20ba7d71307d35040247decbanthony      case RingKernel:
16991ef941fea2534a0d20ba7d71307d35040247decbanthony      case PeaksKernel:
17001ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17011ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
17021ef941fea2534a0d20ba7d71307d35040247decbanthony            limit1,
17031ef941fea2534a0d20ba7d71307d35040247decbanthony            limit2,
17041ef941fea2534a0d20ba7d71307d35040247decbanthony            scale;
17051ef941fea2534a0d20ba7d71307d35040247decbanthony
17061ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < args->sigma)
17071ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17081ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->sigma)*2+1;
17091ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->rho*args->rho);
17101ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->sigma*args->sigma);
17113dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            }
17121ef941fea2534a0d20ba7d71307d35040247decbanthony          else
17131ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17141ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->rho)*2+1;
17151ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->sigma*args->sigma);
17161ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->rho*args->rho);
17171ef941fea2534a0d20ba7d71307d35040247decbanthony            }
17181ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( limit2 <= 0 )
17191ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = 7L, limit1 = 7L, limit2 = 11L;
17201ef941fea2534a0d20ba7d71307d35040247decbanthony
17211ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->height = kernel->width;
17221ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1723e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1724e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1725e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1726d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
17271ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
17281ef941fea2534a0d20ba7d71307d35040247decbanthony
17291ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
17301ef941fea2534a0d20ba7d71307d35040247decbanthony          scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
17311ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
17321ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
17331ef941fea2534a0d20ba7d71307d35040247decbanthony              { ssize_t radius=u*u+v*v;
17341ef941fea2534a0d20ba7d71307d35040247decbanthony                if (limit1 < radius && radius <= limit2)
17351ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->positive_range += kernel->values[i] = (double) scale;
17361ef941fea2534a0d20ba7d71307d35040247decbanthony                else
17371ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->values[i] = nan;
17381ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17391ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = (double) scale;
17401ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( type == PeaksKernel ) {
17411ef941fea2534a0d20ba7d71307d35040247decbanthony            /* set the central point in the middle */
17421ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
17431ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->positive_range = 1.0;
17441ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->maximum = 1.0;
17451ef941fea2534a0d20ba7d71307d35040247decbanthony          }
17461ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1747c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        }
17481ef941fea2534a0d20ba7d71307d35040247decbanthony      case EdgesKernel:
17491ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17502c57b74e160f9b605d74dec24081309f28b83899cristy          kernel=AcquireKernelInfo("ThinSE:482",exception);
17511ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17521ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17531ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
1754529482f4b494010a13338a74446c510712f670b3anthony          ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
17551ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17561ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17571ef941fea2534a0d20ba7d71307d35040247decbanthony      case CornersKernel:
17581ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17592c57b74e160f9b605d74dec24081309f28b83899cristy          kernel=AcquireKernelInfo("ThinSE:87",exception);
17601ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17611ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17621ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
17631ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
17641ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17651ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1766529482f4b494010a13338a74446c510712f670b3anthony      case DiagonalsKernel:
17671ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17681ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
17691ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
17701ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17711ef941fea2534a0d20ba7d71307d35040247decbanthony              { KernelInfo
17721ef941fea2534a0d20ba7d71307d35040247decbanthony                  *new_kernel;
1773529482f4b494010a13338a74446c510712f670b3anthony                kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17741ef941fea2534a0d20ba7d71307d35040247decbanthony                if (kernel == (KernelInfo *) NULL)
17751ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(kernel);
17761ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->type = type;
1777529482f4b494010a13338a74446c510712f670b3anthony                new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17781ef941fea2534a0d20ba7d71307d35040247decbanthony                if (new_kernel == (KernelInfo *) NULL)
17791ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(DestroyKernelInfo(kernel));
17801ef941fea2534a0d20ba7d71307d35040247decbanthony                new_kernel->type = type;
17811ef941fea2534a0d20ba7d71307d35040247decbanthony                LastKernelInfo(kernel)->next = new_kernel;
17821ef941fea2534a0d20ba7d71307d35040247decbanthony                ExpandMirrorKernelInfo(kernel);
1783529482f4b494010a13338a74446c510712f670b3anthony                return(kernel);
17841ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17851ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
1786529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17871ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17881ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
1789529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17901ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17911ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1792529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1793529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1794529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1795529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
17961ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17971ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17981ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineEndsKernel:
17991ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Kernels for finding the end of thin lines */
18001ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18011ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
18021ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18031ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all end of lines */
18042c57b74e160f9b605d74dec24081309f28b83899cristy              return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>",exception));
18051ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18061ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel for 4-connected line ends - no rotation */
18071ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
18081ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18091ef941fea2534a0d20ba7d71307d35040247decbanthony          case 2:
18101ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for 8-connected lines - no rotation */
18111ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
18121ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18131ef941fea2534a0d20ba7d71307d35040247decbanthony          case 3:
18141ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for orthogonal line ends - does not find corners */
18151ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
18161ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18171ef941fea2534a0d20ba7d71307d35040247decbanthony          case 4:
18181ef941fea2534a0d20ba7d71307d35040247decbanthony              /* traditional line end - fails on last T end */
18191ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
18201ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18211ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1822529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1823529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1824529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1825529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18261ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18271ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18281ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineJunctionsKernel:
18291ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* kernels for finding the junctions of multiple lines */
18301ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18311ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
18321ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18331ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all line junctions */
18342c57b74e160f9b605d74dec24081309f28b83899cristy              return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>",exception));
18351ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18361ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Y Junction */
18371ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
18381ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18391ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18401ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal T Junctions */
18411ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
18421ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18431ef941fea2534a0d20ba7d71307d35040247decbanthony            case 3:
18441ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal T Junctions */
18451ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
18461ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18471ef941fea2534a0d20ba7d71307d35040247decbanthony            case 4:
18481ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal X Junctions */
18491ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
18501ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18511ef941fea2534a0d20ba7d71307d35040247decbanthony            case 5:
18521ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal X Junctions - minimal diamond kernel */
18531ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
18541ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18551ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1856529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1857529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1858529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1859529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18601ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18611ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18621ef941fea2534a0d20ba7d71307d35040247decbanthony      case RidgesKernel:
18631ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Ridges - Ridge finding kernels */
18641ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
18651ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
18661ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18671ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18681ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18691ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3x1:0,1,0");
18701ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18711ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18721ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18731ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
18741ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18751ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18761ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("4x1:0,1,1,0");
18771ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18781ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18791ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18801ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
18811ef941fea2534a0d20ba7d71307d35040247decbanthony
18821ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
18831ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Unfortunatally we can not yet rotate a non-square kernel */
18841ef941fea2534a0d20ba7d71307d35040247decbanthony              /* But then we can't flip a non-symetrical kernel either */
18851ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
18861ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18871ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18881ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18891ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18901ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
18911ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18921ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18931ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18941ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18951ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
18961ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18971ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18981ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18991ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19001ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
19011ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19021ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19031ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19041ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19051ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
19061ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19071ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19081ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19091ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19101ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
19111ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19121ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19131ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19141ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19151ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
19161ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19171ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19181ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19191ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19201ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
192168cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              if (new_kernel == (KernelInfo *) NULL)
192268cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony                return(DestroyKernelInfo(kernel));
192368cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              new_kernel->type = type;
192468cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              LastKernelInfo(kernel)->next = new_kernel;
192568cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              break;
19261ef941fea2534a0d20ba7d71307d35040247decbanthony          }
19271ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
192868cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony        }
19291ef941fea2534a0d20ba7d71307d35040247decbanthony      case ConvexHullKernel:
19301ef941fea2534a0d20ba7d71307d35040247decbanthony        {
19311ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
19321ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
19331ef941fea2534a0d20ba7d71307d35040247decbanthony          /* first set of 8 kernels */
19341ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
19351ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
19361ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
19371ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
19381ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0);
19391ef941fea2534a0d20ba7d71307d35040247decbanthony          /* append the mirror versions too - no flip function yet */
19401ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
19411ef941fea2534a0d20ba7d71307d35040247decbanthony          if (new_kernel == (KernelInfo *) NULL)
19421ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
19431ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel->type = type;
19441ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(new_kernel, 90.0);
19451ef941fea2534a0d20ba7d71307d35040247decbanthony          LastKernelInfo(kernel)->next = new_kernel;
19461ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1947694934fa79dd310f727588b1d0a7481fa6170f1danthony        }
19489a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony      case SkeletonKernel:
19499a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        {
19509a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          switch ( (int) args->rho ) {
19519a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 1:
19529a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            default:
19539a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Traditional Skeleton...
19549a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** A cyclically rotated single kernel
19559a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19562c57b74e160f9b605d74dec24081309f28b83899cristy              kernel=AcquireKernelInfo("ThinSE:482",exception);
19579a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19589a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19599a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19609a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
19619a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19629a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 2:
19639a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* HIPR Variation of the cyclic skeleton
19649a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** Corners of the traditional method made more forgiving,
19659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** but the retain the same cyclic order.
19669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19672c57b74e160f9b605d74dec24081309f28b83899cristy              kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;",exception);
19689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19699a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19709a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel->next == (KernelInfo *) NULL)
19719a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(DestroyKernelInfo(kernel));
19729a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19739a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19749a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
19759a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19769a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 3:
19779a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
19789a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** "Connectivity-Preserving Morphological Image Thransformations"
19799a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
19809a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              **   http://www.leptonica.com/papers/conn.pdf
19819a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19822c57b74e160f9b605d74dec24081309f28b83899cristy              kernel=AcquireKernelInfo("ThinSE:41; ThinSE:42; ThinSE:43",
19832c57b74e160f9b605d74dec24081309f28b83899cristy                exception);
19849a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19859a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19869a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19879a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19889a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->next->type = type;
19899a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
19909a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19919a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony           }
19929a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          break;
19939a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        }
1994529482f4b494010a13338a74446c510712f670b3anthony      case ThinSEKernel:
1995529482f4b494010a13338a74446c510712f670b3anthony        { /* Special kernels for general thinning, while preserving connections
1996529482f4b494010a13338a74446c510712f670b3anthony          ** "Connectivity-Preserving Morphological Image Thransformations"
1997529482f4b494010a13338a74446c510712f670b3anthony          ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
1998529482f4b494010a13338a74446c510712f670b3anthony          **   http://www.leptonica.com/papers/conn.pdf
1999529482f4b494010a13338a74446c510712f670b3anthony          ** And
2000529482f4b494010a13338a74446c510712f670b3anthony          **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
2001529482f4b494010a13338a74446c510712f670b3anthony          **
2002529482f4b494010a13338a74446c510712f670b3anthony          ** Note kernels do not specify the origin pixel, allowing them
2003529482f4b494010a13338a74446c510712f670b3anthony          ** to be used for both thickening and thinning operations.
2004529482f4b494010a13338a74446c510712f670b3anthony          */
2005529482f4b494010a13338a74446c510712f670b3anthony          switch ( (int) args->rho ) {
2006529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 4-connected thinning */
2007529482f4b494010a13338a74446c510712f670b3anthony            case 41: /* SE_4_1 */
2008529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
2009529482f4b494010a13338a74446c510712f670b3anthony              break;
2010529482f4b494010a13338a74446c510712f670b3anthony            case 42: /* SE_4_2 */
2011529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
2012529482f4b494010a13338a74446c510712f670b3anthony              break;
2013529482f4b494010a13338a74446c510712f670b3anthony            case 43: /* SE_4_3 */
2014529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
2015529482f4b494010a13338a74446c510712f670b3anthony              break;
2016529482f4b494010a13338a74446c510712f670b3anthony            case 44: /* SE_4_4 */
2017529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
2018529482f4b494010a13338a74446c510712f670b3anthony              break;
2019529482f4b494010a13338a74446c510712f670b3anthony            case 45: /* SE_4_5 */
2020529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
2021529482f4b494010a13338a74446c510712f670b3anthony              break;
2022529482f4b494010a13338a74446c510712f670b3anthony            case 46: /* SE_4_6 */
2023529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
2024529482f4b494010a13338a74446c510712f670b3anthony              break;
2025529482f4b494010a13338a74446c510712f670b3anthony            case 47: /* SE_4_7 */
2026529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
2027529482f4b494010a13338a74446c510712f670b3anthony              break;
2028529482f4b494010a13338a74446c510712f670b3anthony            case 48: /* SE_4_8 */
2029529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
2030529482f4b494010a13338a74446c510712f670b3anthony              break;
2031529482f4b494010a13338a74446c510712f670b3anthony            case 49: /* SE_4_9 */
2032529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
2033529482f4b494010a13338a74446c510712f670b3anthony              break;
2034529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 8-connected thinning - negatives of the above */
2035529482f4b494010a13338a74446c510712f670b3anthony            case 81: /* SE_8_0 */
2036529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
2037529482f4b494010a13338a74446c510712f670b3anthony              break;
2038529482f4b494010a13338a74446c510712f670b3anthony            case 82: /* SE_8_2 */
2039529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
2040529482f4b494010a13338a74446c510712f670b3anthony              break;
2041529482f4b494010a13338a74446c510712f670b3anthony            case 83: /* SE_8_3 */
2042529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
2043529482f4b494010a13338a74446c510712f670b3anthony              break;
2044529482f4b494010a13338a74446c510712f670b3anthony            case 84: /* SE_8_4 */
2045529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
2046529482f4b494010a13338a74446c510712f670b3anthony              break;
2047529482f4b494010a13338a74446c510712f670b3anthony            case 85: /* SE_8_5 */
2048529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
2049529482f4b494010a13338a74446c510712f670b3anthony              break;
2050529482f4b494010a13338a74446c510712f670b3anthony            case 86: /* SE_8_6 */
2051529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
2052529482f4b494010a13338a74446c510712f670b3anthony              break;
2053529482f4b494010a13338a74446c510712f670b3anthony            case 87: /* SE_8_7 */
2054529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
2055529482f4b494010a13338a74446c510712f670b3anthony              break;
2056529482f4b494010a13338a74446c510712f670b3anthony            case 88: /* SE_8_8 */
2057529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
2058529482f4b494010a13338a74446c510712f670b3anthony              break;
2059529482f4b494010a13338a74446c510712f670b3anthony            case 89: /* SE_8_9 */
2060529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
2061529482f4b494010a13338a74446c510712f670b3anthony              break;
2062529482f4b494010a13338a74446c510712f670b3anthony            /* Special combined SE kernels */
20639a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
20649a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
20659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
20669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
20679a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
20689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
2069529482f4b494010a13338a74446c510712f670b3anthony            case 481: /* SE_48_1 - General Connected Corner Kernel */
2070529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
2071529482f4b494010a13338a74446c510712f670b3anthony              break;
2072529482f4b494010a13338a74446c510712f670b3anthony            default:
2073529482f4b494010a13338a74446c510712f670b3anthony            case 482: /* SE_48_2 - General Edge Kernel */
2074529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
2075529482f4b494010a13338a74446c510712f670b3anthony              break;
2076529482f4b494010a13338a74446c510712f670b3anthony          }
2077529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
2078529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
2079529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
2080529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
2081529482f4b494010a13338a74446c510712f670b3anthony          break;
2082529482f4b494010a13338a74446c510712f670b3anthony        }
2083529482f4b494010a13338a74446c510712f670b3anthony      /*
2084529482f4b494010a13338a74446c510712f670b3anthony        Distance Measuring Kernels
2085529482f4b494010a13338a74446c510712f670b3anthony      */
20861ef941fea2534a0d20ba7d71307d35040247decbanthony      case ChebyshevKernel:
20871ef941fea2534a0d20ba7d71307d35040247decbanthony        {
20881ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
20891ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
20901ef941fea2534a0d20ba7d71307d35040247decbanthony          else
20911ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
20921ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
20931ef941fea2534a0d20ba7d71307d35040247decbanthony
2094e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
2095e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
2096e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
2097d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
20981ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
20991ef941fea2534a0d20ba7d71307d35040247decbanthony
21001ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
21011ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21021ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
21031ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
21041ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21051ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2106c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
21071ef941fea2534a0d20ba7d71307d35040247decbanthony      case ManhattanKernel:
21081ef941fea2534a0d20ba7d71307d35040247decbanthony        {
21091ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
21101ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
21111ef941fea2534a0d20ba7d71307d35040247decbanthony          else
21121ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
21131ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
21141ef941fea2534a0d20ba7d71307d35040247decbanthony
2115e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
2116e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
2117e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
2118d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
21191ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
21201ef941fea2534a0d20ba7d71307d35040247decbanthony
21211ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
21221ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21231ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
21241ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*(labs((long) u)+labs((long) v)) );
21251ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21261ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2127c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
21281ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonalKernel:
2129602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
21301ef941fea2534a0d20ba7d71307d35040247decbanthony        if (args->rho < 2.0)
21311ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
2132602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
2133bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2134bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2135602ab9b30b644a78a4057da93d838a77391ec0acanthony
2136e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
2137e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
2138e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
2139d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
214083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2141602ab9b30b644a78a4057da93d838a77391ec0acanthony
2142bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2143bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21441ef941fea2534a0d20ba7d71307d35040247decbanthony            {
21451ef941fea2534a0d20ba7d71307d35040247decbanthony              double
21461ef941fea2534a0d20ba7d71307d35040247decbanthony                r1 = MagickMax(fabs((double)u),fabs((double)v)),
21471ef941fea2534a0d20ba7d71307d35040247decbanthony                r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
21481ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += kernel->values[i] =
21491ef941fea2534a0d20ba7d71307d35040247decbanthony                        args->sigma*MagickMax(r1,r2);
21501ef941fea2534a0d20ba7d71307d35040247decbanthony            }
2151c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2152602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2153602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2154602ab9b30b644a78a4057da93d838a77391ec0acanthony    case EuclideanKernel:
2155602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2156602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (args->rho < 1.0)
2157c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
2158602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
21591ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2160bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2161602ab9b30b644a78a4057da93d838a77391ec0acanthony
2162e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
2163e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
2164e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
2165d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
216683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2167602ab9b30b644a78a4057da93d838a77391ec0acanthony
2168bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2169bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2170c99304fe3c8d9c617da792b40b57c118bb1249afcristy            kernel->positive_range += ( kernel->values[i] =
21710ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony              args->sigma*sqrt((double)(u*u+v*v)) );
2172c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2173602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2174602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2175602ab9b30b644a78a4057da93d838a77391ec0acanthony    default:
2176c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
2177529482f4b494010a13338a74446c510712f670b3anthony        /* No-Op Kernel - Basically just a single pixel on its own */
21783ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony        kernel=ParseKernelArray("1:1");
2179c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
2180c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
2181529482f4b494010a13338a74446c510712f670b3anthony        kernel->type = UndefinedKernel;
2182c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
2183c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
2184602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
2185602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
2186602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2187602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2188c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2189602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
2190602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2191602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2192602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2193602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
21946771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     C l o n e K e r n e l I n f o                                           %
21954fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21964fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21974fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21984fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21994fd27e21043be809d66c8202e779255e5b660d2danthony%
22001b2bc0a7da432e6e1cc0480280402df213faa940anthony%  CloneKernelInfo() creates a new clone of the given Kernel List so that its
22011b2bc0a7da432e6e1cc0480280402df213faa940anthony%  can be modified without effecting the original.  The cloned kernel should
22020ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony%  be destroyed using DestoryKernelInfo() when no longer needed.
22037a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
2204e636559dfadfdb115cc93f223315052a1ee89238cristy%  The format of the CloneKernelInfo method is:
22054fd27e21043be809d66c8202e779255e5b660d2danthony%
2206930be614b4595b97cd79ee864a394796740f76adanthony%      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22074fd27e21043be809d66c8202e779255e5b660d2danthony%
22084fd27e21043be809d66c8202e779255e5b660d2danthony%  A description of each parameter follows:
22094fd27e21043be809d66c8202e779255e5b660d2danthony%
22104fd27e21043be809d66c8202e779255e5b660d2danthony%    o kernel: the Morphology/Convolution kernel to be cloned
22114fd27e21043be809d66c8202e779255e5b660d2danthony%
22124fd27e21043be809d66c8202e779255e5b660d2danthony*/
2213ef656913b0b30d713ae94c82c47693c9dc69c9f4cristyMagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22144fd27e21043be809d66c8202e779255e5b660d2danthony{
2215bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
22164fd27e21043be809d66c8202e779255e5b660d2danthony    i;
22174fd27e21043be809d66c8202e779255e5b660d2danthony
221819eb64195ef744f61293025952df1e5e6de66524cristy  KernelInfo
22197a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *new_kernel;
22204fd27e21043be809d66c8202e779255e5b660d2danthony
22214fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
22227a01dcf50ce12cb2a789bedff51e9345f022432eanthony  new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
22237a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if (new_kernel == (KernelInfo *) NULL)
22247a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(new_kernel);
22257a01dcf50ce12cb2a789bedff51e9345f022432eanthony  *new_kernel=(*kernel); /* copy values in structure */
22267a01dcf50ce12cb2a789bedff51e9345f022432eanthony
22277a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* replace the values with a copy of the values */
2228e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  new_kernel->values=(MagickRealType *) MagickAssumeAligned(
2229e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy    AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
2230d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if (new_kernel->values == (MagickRealType *) NULL)
22317a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(DestroyKernelInfo(new_kernel));
2232bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
22337a01dcf50ce12cb2a789bedff51e9345f022432eanthony    new_kernel->values[i]=kernel->values[i];
22341b2bc0a7da432e6e1cc0480280402df213faa940anthony
22351b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* Also clone the next kernel in the kernel list */
22361b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL ) {
22371b2bc0a7da432e6e1cc0480280402df213faa940anthony    new_kernel->next = CloneKernelInfo(kernel->next);
22381b2bc0a7da432e6e1cc0480280402df213faa940anthony    if ( new_kernel->next == (KernelInfo *) NULL )
22391b2bc0a7da432e6e1cc0480280402df213faa940anthony      return(DestroyKernelInfo(new_kernel));
22401b2bc0a7da432e6e1cc0480280402df213faa940anthony  }
22411b2bc0a7da432e6e1cc0480280402df213faa940anthony
22427a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(new_kernel);
22434fd27e21043be809d66c8202e779255e5b660d2danthony}
22444fd27e21043be809d66c8202e779255e5b660d2danthony
22454fd27e21043be809d66c8202e779255e5b660d2danthony/*
22464fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22474fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22484fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22494fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
225083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     D e s t r o y K e r n e l I n f o                                       %
2251602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2252602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2253602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2254602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2255602ab9b30b644a78a4057da93d838a77391ec0acanthony%
225683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
225783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  kernel.
2258602ab9b30b644a78a4057da93d838a77391ec0acanthony%
225983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the DestroyKernelInfo method is:
2260602ab9b30b644a78a4057da93d838a77391ec0acanthony%
226183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2262602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2263602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2264602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2265602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: the Morphology/Convolution kernel to be destroyed
2266602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2267602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
226883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthonyMagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2269602ab9b30b644a78a4057da93d838a77391ec0acanthony{
22702be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  assert(kernel != (KernelInfo *) NULL);
2271c4a4878e8962f8b885b3df3fe928ed6c20f91066dirk  if (kernel->next != (KernelInfo *) NULL)
22729f752c092332bf2c4e599ea49e9422c13f3fb11bcristy    kernel->next=DestroyKernelInfo(kernel->next);
2273d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
22749f752c092332bf2c4e599ea49e9422c13f3fb11bcristy  kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
2275602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2276602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2277c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2278c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony/*
2279c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2280c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2281c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2282c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2283ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d M i r r o r K e r n e l I n f o                             %
22843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22863c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2289bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
2290bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  sequence of 90-degree rotated kernels but providing a reflected 180
2291bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  rotatation, before the -/+ 90-degree rotations.
2292bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2293bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  This special rotation order produces a better, more symetrical thinning of
2294bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  objects.
2295bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2296bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandMirrorKernelInfo method is:
2297bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2298bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandMirrorKernelInfo(KernelInfo *kernel)
2299bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2300bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  A description of each parameter follows:
2301bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2302bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    o kernel: the Morphology/Convolution kernel
2303bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2304bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% This function is only internel to this module, as it is not finalized,
2305bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% especially with regard to non-orthogonal angles, and rotation of larger
2306bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% 2D kernels.
2307bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony*/
2308bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2309bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#if 0
2310bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void FlopKernelInfo(KernelInfo *kernel)
2311bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    { /* Do a Flop by reversing each row. */
2312bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      size_t
2313bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        y;
2314bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register ssize_t
2315bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        x,r;
2316bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register double
2317bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        *k,t;
2318bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2319bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2320bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2321bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          t=k[x],  k[x]=k[r],  k[r]=t;
2322bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2323bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      kernel->x = kernel->width - kernel->x - 1;
2324bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      angle = fmod(angle+180.0, 360.0);
2325bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    }
2326bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#endif
2327bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2328bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandMirrorKernelInfo(KernelInfo *kernel)
2329bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony{
2330bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  KernelInfo
2331bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *clone,
2332bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *last;
2333bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2334bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = kernel;
2335bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2336bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2337bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);   /* flip */
2338bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2339bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2340bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2341bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2342bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 90);   /* transpose */
2343bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2344bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2345bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2346bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2347bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);  /* flop */
2348bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2349bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2350bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  return;
2351bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony}
2352bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2353bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony/*
2354bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2355bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2356bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2357bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2358ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d R o t a t e K e r n e l I n f o                             %
2359bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2360bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2361bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2362bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2363bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2364bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2365529482f4b494010a13338a74446c510712f670b3anthony%  incrementally by the angle given, until the kernel repeats.
23663c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23673c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  WARNING: 45 degree rotations only works for 3x3 kernels.
23683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  While 90 degree roatations only works for linear and square kernels
23693c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2370bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandRotateKernelInfo method is:
23713c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2372bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
23733c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23743c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  A description of each parameter follows:
23753c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23763c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o kernel: the Morphology/Convolution kernel
23773c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23783c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o angle: angle to rotate in degrees
23793c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23803c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% This function is only internel to this module, as it is not finalized,
23813c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% especially with regard to non-orthogonal angles, and rotation of larger
23823c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% 2D kernels.
23833c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony*/
238447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
238547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony/* Internal Routine - Return true if two kernels are the same */
238647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthonystatic MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
238747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony     const KernelInfo *kernel2)
238847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony{
2389bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
239047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    i;
23911d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
23921d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check size and origin location */
23931d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  if (    kernel1->width != kernel2->width
23941d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->height != kernel2->height
23951d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->x != kernel2->x
23961d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->y != kernel2->y )
239747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return MagickFalse;
23981d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
23991d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check actual kernel values */
240047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  for (i=0; i < (kernel1->width*kernel1->height); i++) {
2401f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test for Nan equivalence */
24023667479b817685c0289edd0fd597d07bc2c6b462dirk    if ( IsNaN(kernel1->values[i]) && !IsNaN(kernel2->values[i]) )
240347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
24043667479b817685c0289edd0fd597d07bc2c6b462dirk    if ( IsNaN(kernel2->values[i]) && !IsNaN(kernel1->values[i]) )
240547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
2406f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test actual values are equivalent */
2407b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
240847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
240947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
24101d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
241147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return MagickTrue;
241247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony}
241347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
2414bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
24153c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony{
24163c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  KernelInfo
241784d9b5596c0900609dea18795861e2b0936b21c6cristy    *clone,
24183c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    *last;
2419a9a61ad96c5112acd968f97b689bd42ca392d70bcristy
24203c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  last = kernel;
242193b02b797c4127ce2b06dbd3b2d75ecc33fca759dirkDisableMSCWarning(4127)
242247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while(1) {
242393b02b797c4127ce2b06dbd3b2d75ecc33fca759dirkRestoreMSCWarning
242484d9b5596c0900609dea18795861e2b0936b21c6cristy    clone = CloneKernelInfo(last);
242584d9b5596c0900609dea18795861e2b0936b21c6cristy    RotateKernelInfo(clone, angle);
2426cd8b331760407523f2a59cc65c1cd9c3d4422bafcristy    if ( SameKernelInfo(kernel, clone) != MagickFalse )
242747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
2428bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    LastKernelInfo(last)->next = clone;
242984d9b5596c0900609dea18795861e2b0936b21c6cristy    last = clone;
24303c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  }
2431bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
243247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return;
24333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony}
24343c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
24353c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony/*
24363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24383c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24393c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
244046a369d839971ab627bdb31a93d8bd63e81b65a3anthony+     C a l c M e t a K e r n a l I n f o                                     %
244146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
244646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
2447dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  using the kernel values.  This should only ne used if it is not possible to
244846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  calculate that meta-data in some easier way.
244946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It is important that the meta-data is correct before ScaleKernelInfo() is
245146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  used to perform kernel normalization.
245246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the CalcKernelMetaData method is:
245446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
245646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
245846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
246046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Minimum and Maximum values are assumed to include zero, even if
246246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  zero is not part of the kernel (as in Gaussian Derived kernels). This
246346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  however is not true for flat-shaped morphological kernels.
246446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Only the specific kernel pointed to is modified, not a list of
246646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  multiple kernels.
246746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246846a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This is an internal function and not expected to be useful outside this
246946a369d839971ab627bdb31a93d8bd63e81b65a3anthony% module.  This could change however.
247046a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
247146a369d839971ab627bdb31a93d8bd63e81b65a3anthonystatic void CalcKernelMetaData(KernelInfo *kernel)
247246a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
2473bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
247446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    i;
247546a369d839971ab627bdb31a93d8bd63e81b65a3anthony
247646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->minimum = kernel->maximum = 0.0;
247746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->negative_range = kernel->positive_range = 0.0;
247846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  for (i=0; i < (kernel->width*kernel->height); i++)
247946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    {
248046a369d839971ab627bdb31a93d8bd63e81b65a3anthony      if ( fabs(kernel->values[i]) < MagickEpsilon )
248146a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->values[i] = 0.0;
248246a369d839971ab627bdb31a93d8bd63e81b65a3anthony      ( kernel->values[i] < 0)
248346a369d839971ab627bdb31a93d8bd63e81b65a3anthony          ?  ( kernel->negative_range += kernel->values[i] )
248446a369d839971ab627bdb31a93d8bd63e81b65a3anthony          :  ( kernel->positive_range += kernel->values[i] );
248546a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Minimize(kernel->minimum, kernel->values[i]);
248646a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Maximize(kernel->maximum, kernel->values[i]);
248746a369d839971ab627bdb31a93d8bd63e81b65a3anthony    }
248846a369d839971ab627bdb31a93d8bd63e81b65a3anthony
248946a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
249046a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
249146a369d839971ab627bdb31a93d8bd63e81b65a3anthony
249246a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
249346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
24979eb4f74649b23c053b308ce1152dce51239450baanthony%     M o r p h o l o g y A p p l y                                           %
2498602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2499602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2500602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2501602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2502602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25039eb4f74649b23c053b308ce1152dce51239450baanthony%  MorphologyApply() applies a morphological method, multiple times using
250422de2722b682eb405b60ec6022a7546df994674eanthony%  a list of multiple kernels.  This is the method that should be called by
250522de2722b682eb405b60ec6022a7546df994674eanthony%  other 'operators' that internally use morphology operations as part of
250622de2722b682eb405b60ec6022a7546df994674eanthony%  their processing.
2507602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2508ec9ace44c23c302f336f6e672f6857312747d29bcristy%  It is basically equivalent to as MorphologyImage() (see below) but without
2509ec9ace44c23c302f336f6e672f6857312747d29bcristy%  any user controls.  This allows internel programs to use this method to
2510ec9ace44c23c302f336f6e672f6857312747d29bcristy%  perform a specific task without possible interference by any API user
2511ec9ace44c23c302f336f6e672f6857312747d29bcristy%  supplied settings.
2512e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%
2513f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  It is MorphologyImage() task to extract any such user controls, and
2514e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  pass them to this function for processing.
25159eb4f74649b23c053b308ce1152dce51239450baanthony%
251622de2722b682eb405b60ec6022a7546df994674eanthony%  More specifically all given kernels should already be scaled, normalised,
251722de2722b682eb405b60ec6022a7546df994674eanthony%  and blended appropriatally before being parred to this routine. The
251822de2722b682eb405b60ec6022a7546df994674eanthony%  appropriate bias, and compose (typically 'UndefinedComposeOp') given.
2519602ab9b30b644a78a4057da93d838a77391ec0acanthony%
252047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%  The format of the MorphologyApply method is:
2521602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25229eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyApply(const Image *image,MorphologyMethod method,
2523f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%        const ssize_t iterations,const KernelInfo *kernel,
2524f46d42620631d2581e0b6a56456e203e17c427c8anthony%        const CompositeMethod compose,const double bias,
2525f46d42620631d2581e0b6a56456e203e17c427c8anthony%        ExceptionInfo *exception)
2526602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2527602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2528602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25298d18850dee4bed193a64866a6d2353eeeb73e145anthony%    o image: the source image
2530602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2531602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o method: the morphology method to be applied.
2532602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2533602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o iterations: apply the operation this many times (or no change).
2534602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  A value of -1 means loop until no change found.
2535602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  How this is applied may depend on the morphology method.
2536602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  Typically this is a value of 1.
2537602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2538602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o channel: the channel type.
2539602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2540602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: An array of double representing the morphology kernel.
2541602ab9b30b644a78a4057da93d838a77391ec0acanthony%
254247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%    o compose: How to handle or merge multi-kernel results.
25438d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'UndefinedCompositeOp' use default for the Morphology method.
25448d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'NoCompositeOp' force image to be re-iterated by each kernel.
25458d18850dee4bed193a64866a6d2353eeeb73e145anthony%          Otherwise merge the results using the compose method given.
254647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%
2547f46d42620631d2581e0b6a56456e203e17c427c8anthony%    o bias: Convolution Output Bias.
2548f46d42620631d2581e0b6a56456e203e17c427c8anthony%
25499eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
2550602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2551602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
2552f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristystatic ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
2553f46d42620631d2581e0b6a56456e203e17c427c8anthony  const MorphologyMethod method,const KernelInfo *kernel,const double bias,
2554f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
2555602ab9b30b644a78a4057da93d838a77391ec0acanthony{
25562be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy#define MorphologyTag  "Morphology/Image"
2557602ab9b30b644a78a4057da93d838a77391ec0acanthony
25585f959473f334e196c6bf39b740c12cb4963fceebcristy  CacheView
25594c08aed51c5899665ade97263692328eea4af106cristy    *image_view,
25604c08aed51c5899665ade97263692328eea4af106cristy    *morphology_view;
25615f959473f334e196c6bf39b740c12cb4963fceebcristy
2562ec9ace44c23c302f336f6e672f6857312747d29bcristy  OffsetInfo
2563ec9ace44c23c302f336f6e672f6857312747d29bcristy    offset;
2564ec9ace44c23c302f336f6e672f6857312747d29bcristy
2565c5876076825b539bd230ddadab1d876805c18291cristy  register ssize_t
2566aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    j,
2567d9435776384a479c2f0d621b4bef12a35592fd5dcristy    y;
2568d9435776384a479c2f0d621b4bef12a35592fd5dcristy
2569a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
2570306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    *changes,
2571306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    changed,
2572306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    width;
2573602ab9b30b644a78a4057da93d838a77391ec0acanthony
2574d9435776384a479c2f0d621b4bef12a35592fd5dcristy  MagickBooleanType
2575d9435776384a479c2f0d621b4bef12a35592fd5dcristy    status;
2576d9435776384a479c2f0d621b4bef12a35592fd5dcristy
2577d9435776384a479c2f0d621b4bef12a35592fd5dcristy  MagickOffsetType
2578d9435776384a479c2f0d621b4bef12a35592fd5dcristy    progress;
2579602ab9b30b644a78a4057da93d838a77391ec0acanthony
2580e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image != (Image *) NULL);
2581e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
25824c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image != (Image *) NULL);
2583e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(morphology_image->signature == MagickCoreSignature);
2584e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel != (KernelInfo *) NULL);
2585e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(kernel->signature == MagickCoreSignature);
2586e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception != (ExceptionInfo *) NULL);
2587e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
2588602ab9b30b644a78a4057da93d838a77391ec0acanthony  status=MagickTrue;
2589602ab9b30b644a78a4057da93d838a77391ec0acanthony  progress=0;
2590d9435776384a479c2f0d621b4bef12a35592fd5dcristy  image_view=AcquireVirtualCacheView(image,exception);
2591d9435776384a479c2f0d621b4bef12a35592fd5dcristy  morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
2592d9435776384a479c2f0d621b4bef12a35592fd5dcristy  width=image->columns+kernel->width-1;
25939f36ba7e4919cc3b03ba53776a8423995db60714cristy  offset.x=0;
25949f36ba7e4919cc3b03ba53776a8423995db60714cristy  offset.y=0;
2595ec9ace44c23c302f336f6e672f6857312747d29bcristy  switch (method)
2596ec9ace44c23c302f336f6e672f6857312747d29bcristy  {
2597930be614b4595b97cd79ee864a394796740f76adanthony    case ConvolveMorphology:
2598930be614b4595b97cd79ee864a394796740f76adanthony    case DilateMorphology:
2599930be614b4595b97cd79ee864a394796740f76adanthony    case DilateIntensityMorphology:
2600f34d9b2df49a407af764c79e07d587af0600983aanthony    case IterativeDistanceMorphology:
2601ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2602ec9ace44c23c302f336f6e672f6857312747d29bcristy      /*
2603ec9ace44c23c302f336f6e672f6857312747d29bcristy        Kernel needs to used with reflection about origin.
2604ec9ace44c23c302f336f6e672f6857312747d29bcristy      */
2605ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=(ssize_t) kernel->width-kernel->x-1;
2606ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=(ssize_t) kernel->height-kernel->y-1;
260729188a8682a98d4b7882cca434b170517555fc7danthony      break;
2608ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
26095ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeMorphology:
26105ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeIntensityMorphology:
26115ef8e94ff55717be2387d537bd49025780a1a558anthony    case HitAndMissMorphology:
26125ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThinningMorphology:
26135ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThickenMorphology:
2614ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2615ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=kernel->x;
2616ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=kernel->y;
26175ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
2618ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
2619930be614b4595b97cd79ee864a394796740f76adanthony    default:
2620ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2621d9435776384a479c2f0d621b4bef12a35592fd5dcristy      assert("Not a Primitive Morphology Method" != (char *) NULL);
2622930be614b4595b97cd79ee864a394796740f76adanthony      break;
2623ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
262429188a8682a98d4b7882cca434b170517555fc7danthony  }
2625c5876076825b539bd230ddadab1d876805c18291cristy  changed=0;
2626306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  changes=(size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
2627306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    sizeof(*changes));
2628306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  if (changes == (size_t *) NULL)
2629306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
2630c93932ae0fce511e693d0cf9b969af16a3e50175dirk  for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
2631c93932ae0fce511e693d0cf9b969af16a3e50175dirk    changes[j]=0;
26325da2007c76eadcfef018a30848f826c9137c21ceCristy
2633ec9ace44c23c302f336f6e672f6857312747d29bcristy  if ((method == ConvolveMorphology) && (kernel->width == 1))
2634780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    {
2635d9435776384a479c2f0d621b4bef12a35592fd5dcristy      register ssize_t
2636780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        x;
2637fd1175952254cf1ac848ddb441e483c5e33d517fcristy
2638780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      /*
2639d9435776384a479c2f0d621b4bef12a35592fd5dcristy        Special handling (for speed) of vertical (blur) kernels.  This performs
2640d9435776384a479c2f0d621b4bef12a35592fd5dcristy        its handling in columns rather than in rows.  This is only done
2641d9435776384a479c2f0d621b4bef12a35592fd5dcristy        for convolve as it is the only method that generates very large 1-D
2642d9435776384a479c2f0d621b4bef12a35592fd5dcristy        vertical kernels (such as a 'BlurKernel')
2643d9435776384a479c2f0d621b4bef12a35592fd5dcristy     */
26448d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2645d9435776384a479c2f0d621b4bef12a35592fd5dcristy     #pragma omp parallel for schedule(static,4) shared(progress,status) \
2646d9435776384a479c2f0d621b4bef12a35592fd5dcristy       magick_threads(image,morphology_image,image->columns,1)
26478d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
2648780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      for (x=0; x < (ssize_t) image->columns; x++)
2649780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      {
2650d9435776384a479c2f0d621b4bef12a35592fd5dcristy        const int
2651d9435776384a479c2f0d621b4bef12a35592fd5dcristy          id = GetOpenMPThreadId();
2652d9435776384a479c2f0d621b4bef12a35592fd5dcristy
2653780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register const Quantum
265405d2ff7ebf21f659f5b11e45afb294e152f4330cdirk          *magick_restrict p;
26558d18850dee4bed193a64866a6d2353eeeb73e145anthony
2656780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register Quantum
265705d2ff7ebf21f659f5b11e45afb294e152f4330cdirk          *magick_restrict q;
26588d18850dee4bed193a64866a6d2353eeeb73e145anthony
2659780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register ssize_t
2660aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk          r;
26618d18850dee4bed193a64866a6d2353eeeb73e145anthony
2662780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        ssize_t
2663780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          center;
26648d18850dee4bed193a64866a6d2353eeeb73e145anthony
2665780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (status == MagickFalse)
26668d18850dee4bed193a64866a6d2353eeeb73e145anthony          continue;
2667ec9ace44c23c302f336f6e672f6857312747d29bcristy        p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
2668780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          kernel->height-1,exception);
2669780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2670780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          morphology_image->rows,exception);
2671780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2672780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2673780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            status=MagickFalse;
2674780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            continue;
2675780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
2676ec9ace44c23c302f336f6e672f6857312747d29bcristy        center=(ssize_t) GetPixelChannels(image)*offset.y;
2677aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk        for (r=0; r < (ssize_t) image->rows; r++)
2678f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy        {
2679d9435776384a479c2f0d621b4bef12a35592fd5dcristy          register ssize_t
2680d9435776384a479c2f0d621b4bef12a35592fd5dcristy            i;
26818d18850dee4bed193a64866a6d2353eeeb73e145anthony
2682d9435776384a479c2f0d621b4bef12a35592fd5dcristy          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2683d9435776384a479c2f0d621b4bef12a35592fd5dcristy          {
2684d9435776384a479c2f0d621b4bef12a35592fd5dcristy            double
2685d9435776384a479c2f0d621b4bef12a35592fd5dcristy              alpha,
2686d9435776384a479c2f0d621b4bef12a35592fd5dcristy              gamma,
2687d9435776384a479c2f0d621b4bef12a35592fd5dcristy              pixel;
26888d18850dee4bed193a64866a6d2353eeeb73e145anthony
2689d9435776384a479c2f0d621b4bef12a35592fd5dcristy            PixelChannel
2690d9435776384a479c2f0d621b4bef12a35592fd5dcristy              channel;
26918d18850dee4bed193a64866a6d2353eeeb73e145anthony
2692d9435776384a479c2f0d621b4bef12a35592fd5dcristy            PixelTrait
2693d9435776384a479c2f0d621b4bef12a35592fd5dcristy              morphology_traits,
2694d9435776384a479c2f0d621b4bef12a35592fd5dcristy              traits;
269517f57990dae485e8d3e5ea44e624882692424669cristy
2696d9435776384a479c2f0d621b4bef12a35592fd5dcristy            register const MagickRealType
269705d2ff7ebf21f659f5b11e45afb294e152f4330cdirk              *magick_restrict k;
269817f57990dae485e8d3e5ea44e624882692424669cristy
2699d9435776384a479c2f0d621b4bef12a35592fd5dcristy            register const Quantum
270005d2ff7ebf21f659f5b11e45afb294e152f4330cdirk              *magick_restrict pixels;
2701f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy
270209347642526e7e61646b7c549e410d21790e5e50cristy            register ssize_t
27035da2007c76eadcfef018a30848f826c9137c21ceCristy              v;
2704e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy
2705d9435776384a479c2f0d621b4bef12a35592fd5dcristy            size_t
2706d9435776384a479c2f0d621b4bef12a35592fd5dcristy              count;
2707d9435776384a479c2f0d621b4bef12a35592fd5dcristy
2708d9435776384a479c2f0d621b4bef12a35592fd5dcristy            channel=GetPixelChannelChannel(image,i);
2709d9435776384a479c2f0d621b4bef12a35592fd5dcristy            traits=GetPixelChannelTraits(image,channel);
2710d9435776384a479c2f0d621b4bef12a35592fd5dcristy            morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2711d9435776384a479c2f0d621b4bef12a35592fd5dcristy            if ((traits == UndefinedPixelTrait) ||
2712d9435776384a479c2f0d621b4bef12a35592fd5dcristy                (morphology_traits == UndefinedPixelTrait))
2713f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy              continue;
27146aeeb023536796b4af0b5f5ea370df33953a750ecristy            if (((traits & CopyPixelTrait) != 0) ||
27156aeeb023536796b4af0b5f5ea370df33953a750ecristy                (GetPixelReadMask(image,p+center) == 0))
2716f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy              {
2717d9435776384a479c2f0d621b4bef12a35592fd5dcristy                SetPixelChannel(morphology_image,channel,p[center+i],q);
2718780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                continue;
2719780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              }
27205da2007c76eadcfef018a30848f826c9137c21ceCristy            k=(&kernel->values[kernel->height-1]);
2721d9435776384a479c2f0d621b4bef12a35592fd5dcristy            pixels=p;
2722d9435776384a479c2f0d621b4bef12a35592fd5dcristy            pixel=bias;
2723d9435776384a479c2f0d621b4bef12a35592fd5dcristy            gamma=0.0;
2724d9435776384a479c2f0d621b4bef12a35592fd5dcristy            count=0;
2725d9435776384a479c2f0d621b4bef12a35592fd5dcristy            if ((morphology_traits & BlendPixelTrait) == 0)
2726d9435776384a479c2f0d621b4bef12a35592fd5dcristy              for (v=0; v < (ssize_t) kernel->height; v++)
2727d9435776384a479c2f0d621b4bef12a35592fd5dcristy              {
27285da2007c76eadcfef018a30848f826c9137c21ceCristy                if (!IsNaN(*k))
27295da2007c76eadcfef018a30848f826c9137c21ceCristy                  {
27305da2007c76eadcfef018a30848f826c9137c21ceCristy                    pixel+=(*k)*pixels[i];
27315da2007c76eadcfef018a30848f826c9137c21ceCristy                    gamma+=(*k);
27325da2007c76eadcfef018a30848f826c9137c21ceCristy                    count++;
27335da2007c76eadcfef018a30848f826c9137c21ceCristy                  }
27345da2007c76eadcfef018a30848f826c9137c21ceCristy                k--;
27355da2007c76eadcfef018a30848f826c9137c21ceCristy                pixels+=GetPixelChannels(image);
2736d9435776384a479c2f0d621b4bef12a35592fd5dcristy              }
2737d9435776384a479c2f0d621b4bef12a35592fd5dcristy            else
2738d9435776384a479c2f0d621b4bef12a35592fd5dcristy              for (v=0; v < (ssize_t) kernel->height; v++)
2739d9435776384a479c2f0d621b4bef12a35592fd5dcristy              {
27405da2007c76eadcfef018a30848f826c9137c21ceCristy                if (!IsNaN(*k))
27415da2007c76eadcfef018a30848f826c9137c21ceCristy                  {
27425da2007c76eadcfef018a30848f826c9137c21ceCristy                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
27435da2007c76eadcfef018a30848f826c9137c21ceCristy                    pixel+=alpha*(*k)*pixels[i];
27445da2007c76eadcfef018a30848f826c9137c21ceCristy                    gamma+=alpha*(*k);
27455da2007c76eadcfef018a30848f826c9137c21ceCristy                    count++;
27465da2007c76eadcfef018a30848f826c9137c21ceCristy                  }
27475da2007c76eadcfef018a30848f826c9137c21ceCristy                k--;
27485da2007c76eadcfef018a30848f826c9137c21ceCristy                pixels+=GetPixelChannels(image);
2749d9435776384a479c2f0d621b4bef12a35592fd5dcristy              }
2750d9435776384a479c2f0d621b4bef12a35592fd5dcristy            if (fabs(pixel-p[center+i]) > MagickEpsilon)
2751c5876076825b539bd230ddadab1d876805c18291cristy              changes[id]++;
2752d9435776384a479c2f0d621b4bef12a35592fd5dcristy            gamma=PerceptibleReciprocal(gamma);
2753d9435776384a479c2f0d621b4bef12a35592fd5dcristy            if (count != 0)
27545da2007c76eadcfef018a30848f826c9137c21ceCristy              gamma*=(double) kernel->height/count;
2755d9435776384a479c2f0d621b4bef12a35592fd5dcristy            SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
2756d9435776384a479c2f0d621b4bef12a35592fd5dcristy              pixel),q);
27578d18850dee4bed193a64866a6d2353eeeb73e145anthony          }
2758780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          p+=GetPixelChannels(image);
2759780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          q+=GetPixelChannels(morphology_image);
2760ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
2761780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
2762780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          status=MagickFalse;
2763780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (image->progress_monitor != (MagickProgressMonitor) NULL)
2764780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2765780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            MagickBooleanType
2766780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              proceed;
27678d18850dee4bed193a64866a6d2353eeeb73e145anthony
27688d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2769954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy            #pragma omp critical (MagickCore_MorphologyPrimitive)
27708d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
2771ec9ace44c23c302f336f6e672f6857312747d29bcristy            proceed=SetImageProgress(image,MorphologyTag,progress++,
2772ec9ace44c23c302f336f6e672f6857312747d29bcristy              image->rows);
2773780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (proceed == MagickFalse)
2774780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              status=MagickFalse;
2775780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
2776780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      }
2777780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      morphology_image->type=image->type;
2778780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      morphology_view=DestroyCacheView(morphology_view);
2779780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      image_view=DestroyCacheView(image_view);
2780c93932ae0fce511e693d0cf9b969af16a3e50175dirk      for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
2781c93932ae0fce511e693d0cf9b969af16a3e50175dirk        changed+=changes[j];
2782306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy      changes=(size_t *) RelinquishMagickMemory(changes);
2783780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      return(status ? (ssize_t) changed : 0);
2784780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    }
27858d18850dee4bed193a64866a6d2353eeeb73e145anthony  /*
2786780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    Normal handling of horizontal or rectangular kernels (row by row).
27878d18850dee4bed193a64866a6d2353eeeb73e145anthony  */
2788602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2789c5876076825b539bd230ddadab1d876805c18291cristy  #pragma omp parallel for schedule(static,4) shared(progress,status) \
27905e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,morphology_image,image->rows,1)
2791602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
2792bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (y=0; y < (ssize_t) image->rows; y++)
2793602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
2794c5876076825b539bd230ddadab1d876805c18291cristy    const int
2795c5876076825b539bd230ddadab1d876805c18291cristy      id = GetOpenMPThreadId();
2796c5876076825b539bd230ddadab1d876805c18291cristy
27974c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
279805d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
2799602ab9b30b644a78a4057da93d838a77391ec0acanthony
28004c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
280105d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict q;
2802602ab9b30b644a78a4057da93d838a77391ec0acanthony
2803bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
2804602ab9b30b644a78a4057da93d838a77391ec0acanthony      x;
2805602ab9b30b644a78a4057da93d838a77391ec0acanthony
2806ec9ace44c23c302f336f6e672f6857312747d29bcristy    ssize_t
2807ec9ace44c23c302f336f6e672f6857312747d29bcristy      center;
2808602ab9b30b644a78a4057da93d838a77391ec0acanthony
2809602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (status == MagickFalse)
2810602ab9b30b644a78a4057da93d838a77391ec0acanthony      continue;
2811ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
2812af43471dfedff5d1314cd674fac6508d230df1bccristy      kernel->height,exception);
2813af43471dfedff5d1314cd674fac6508d230df1bccristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
2814af43471dfedff5d1314cd674fac6508d230df1bccristy      1,exception);
28154c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2816602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2817602ab9b30b644a78a4057da93d838a77391ec0acanthony        status=MagickFalse;
2818602ab9b30b644a78a4057da93d838a77391ec0acanthony        continue;
2819602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2820ec9ace44c23c302f336f6e672f6857312747d29bcristy    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
2821ec9ace44c23c302f336f6e672f6857312747d29bcristy      GetPixelChannels(image)*offset.x);
2822bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) image->columns; x++)
2823602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
2824ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
2825ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
2826602ab9b30b644a78a4057da93d838a77391ec0acanthony
2827ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2828ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
2829ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
2830ec9ace44c23c302f336f6e672f6857312747d29bcristy          alpha,
2831e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy          gamma,
2832e1860cabc49c5a3e1297312a204d936a19459d06dirk          intensity,
2833ec9ace44c23c302f336f6e672f6857312747d29bcristy          maximum,
2834ec9ace44c23c302f336f6e672f6857312747d29bcristy          minimum,
2835ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
2836602ab9b30b644a78a4057da93d838a77391ec0acanthony
2837ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelChannel
2838ec9ace44c23c302f336f6e672f6857312747d29bcristy          channel;
2839602ab9b30b644a78a4057da93d838a77391ec0acanthony
2840ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
2841ec9ace44c23c302f336f6e672f6857312747d29bcristy          morphology_traits,
2842ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
2843ec9ace44c23c302f336f6e672f6857312747d29bcristy
2844ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
284505d2ff7ebf21f659f5b11e45afb294e152f4330cdirk          *magick_restrict k;
2846ec9ace44c23c302f336f6e672f6857312747d29bcristy
2847ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
284805d2ff7ebf21f659f5b11e45afb294e152f4330cdirk          *magick_restrict pixels;
2849ec9ace44c23c302f336f6e672f6857312747d29bcristy
2850ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
2851ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
2852602ab9b30b644a78a4057da93d838a77391ec0acanthony
2853e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy        size_t
2854e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy          count;
2855e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy
2856ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
2857ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
2858ec9ace44c23c302f336f6e672f6857312747d29bcristy
2859ec9ace44c23c302f336f6e672f6857312747d29bcristy        channel=GetPixelChannelChannel(image,i);
2860ec9ace44c23c302f336f6e672f6857312747d29bcristy        traits=GetPixelChannelTraits(image,channel);
2861ec9ace44c23c302f336f6e672f6857312747d29bcristy        morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2862ec9ace44c23c302f336f6e672f6857312747d29bcristy        if ((traits == UndefinedPixelTrait) ||
2863ec9ace44c23c302f336f6e672f6857312747d29bcristy            (morphology_traits == UndefinedPixelTrait))
2864ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
28656aeeb023536796b4af0b5f5ea370df33953a750ecristy        if (((traits & CopyPixelTrait) != 0) ||
28666aeeb023536796b4af0b5f5ea370df33953a750ecristy            (GetPixelReadMask(image,p+center) == 0))
2867ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2868ec9ace44c23c302f336f6e672f6857312747d29bcristy            SetPixelChannel(morphology_image,channel,p[center+i],q);
2869ec9ace44c23c302f336f6e672f6857312747d29bcristy            continue;
2870ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2871ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
2872ec9ace44c23c302f336f6e672f6857312747d29bcristy        maximum=0.0;
2873ec9ace44c23c302f336f6e672f6857312747d29bcristy        minimum=(double) QuantumRange;
2874e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy        count=kernel->width*kernel->height;
2875ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
2876ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2877ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ConvolveMorphology: pixel=bias; break;
2878ec9ace44c23c302f336f6e672f6857312747d29bcristy          case HitAndMissMorphology: pixel=(double) QuantumRange; break;
2879ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThinningMorphology: pixel=(double) QuantumRange; break;
2880ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThickenMorphology: pixel=(double) QuantumRange; break;
2881ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeMorphology: pixel=(double) QuantumRange; break;
2882ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateMorphology: pixel=0.0; break;
2883ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeIntensityMorphology:
2884ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateIntensityMorphology:
2885ec9ace44c23c302f336f6e672f6857312747d29bcristy          case IterativeDistanceMorphology:
2886ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2887ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixel=(double) p[center+i];
2888ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
2889ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2890ec9ace44c23c302f336f6e672f6857312747d29bcristy          default: pixel=0; break;
2891ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
28928e6e9602b3a7805e8093f3e54cfc2f02fb89a4f1cristy        gamma=1.0;
2893ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
2894ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2895ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ConvolveMorphology:
2896ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2897ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2898ec9ace44c23c302f336f6e672f6857312747d29bcristy               Weighted Average of pixels using reflected kernel
2899ec9ace44c23c302f336f6e672f6857312747d29bcristy
2900d9435776384a479c2f0d621b4bef12a35592fd5dcristy               For correct working of this operation for asymetrical kernels,
2901d9435776384a479c2f0d621b4bef12a35592fd5dcristy               the kernel needs to be applied in its reflected form.  That is
2902d9435776384a479c2f0d621b4bef12a35592fd5dcristy               its values needs to be reversed.
2903ec9ace44c23c302f336f6e672f6857312747d29bcristy
2904ec9ace44c23c302f336f6e672f6857312747d29bcristy               Correlation is actually the same as this but without reflecting
2905d9435776384a479c2f0d621b4bef12a35592fd5dcristy               the kernel, and thus 'lower-level' that Convolution.  However as
2906d9435776384a479c2f0d621b4bef12a35592fd5dcristy               Convolution is the more common method used, and it does not
2907ec9ace44c23c302f336f6e672f6857312747d29bcristy               really cost us much in terms of processing to use a reflected
2908ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernel, so it is Convolution that is implemented.
2909ec9ace44c23c302f336f6e672f6857312747d29bcristy
2910d9435776384a479c2f0d621b4bef12a35592fd5dcristy               Correlation will have its kernel reflected before calling this
2911d9435776384a479c2f0d621b4bef12a35592fd5dcristy               function to do a Convolve.
2912ec9ace44c23c302f336f6e672f6857312747d29bcristy
2913ec9ace44c23c302f336f6e672f6857312747d29bcristy               For more details of Correlation vs Convolution see
2914ec9ace44c23c302f336f6e672f6857312747d29bcristy                 http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
2915930be614b4595b97cd79ee864a394796740f76adanthony            */
2916ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
2917d9435776384a479c2f0d621b4bef12a35592fd5dcristy            count=0;
291809347642526e7e61646b7c549e410d21790e5e50cristy            if ((morphology_traits & BlendPixelTrait) == 0)
291909347642526e7e61646b7c549e410d21790e5e50cristy              {
292009347642526e7e61646b7c549e410d21790e5e50cristy                /*
292109347642526e7e61646b7c549e410d21790e5e50cristy                  No alpha blending.
292209347642526e7e61646b7c549e410d21790e5e50cristy                */
292309347642526e7e61646b7c549e410d21790e5e50cristy                for (v=0; v < (ssize_t) kernel->height; v++)
292409347642526e7e61646b7c549e410d21790e5e50cristy                {
292509347642526e7e61646b7c549e410d21790e5e50cristy                  for (u=0; u < (ssize_t) kernel->width; u++)
292609347642526e7e61646b7c549e410d21790e5e50cristy                  {
29273667479b817685c0289edd0fd597d07bc2c6b462dirk                    if (!IsNaN(*k))
2928d9435776384a479c2f0d621b4bef12a35592fd5dcristy                      {
2929d9435776384a479c2f0d621b4bef12a35592fd5dcristy                        pixel+=(*k)*pixels[i];
2930d9435776384a479c2f0d621b4bef12a35592fd5dcristy                        count++;
2931d9435776384a479c2f0d621b4bef12a35592fd5dcristy                      }
293209347642526e7e61646b7c549e410d21790e5e50cristy                    k--;
293309347642526e7e61646b7c549e410d21790e5e50cristy                    pixels+=GetPixelChannels(image);
293409347642526e7e61646b7c549e410d21790e5e50cristy                  }
2935aca597f51ca91ee434bba81525c47e32419becb4cristy                  pixels+=(image->columns-1)*GetPixelChannels(image);
293609347642526e7e61646b7c549e410d21790e5e50cristy                }
293709347642526e7e61646b7c549e410d21790e5e50cristy                break;
293809347642526e7e61646b7c549e410d21790e5e50cristy              }
293909347642526e7e61646b7c549e410d21790e5e50cristy            /*
294009347642526e7e61646b7c549e410d21790e5e50cristy              Alpha blending.
294109347642526e7e61646b7c549e410d21790e5e50cristy            */
2942d737c6e45ce145abf4d25a3e084cdec930a7edd1cristy            gamma=0.0;
294333c51e0d3335ba3b35248454fe61c1805f65a115cristy            for (v=0; v < (ssize_t) kernel->height; v++)
2944ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2945ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2946ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
29473667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k))
2948ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
294909347642526e7e61646b7c549e410d21790e5e50cristy                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2950cb228bb3ff8da73ec82aa72dee9d4a6309bc7eedcristy                    pixel+=alpha*(*k)*pixels[i];
2951cb228bb3ff8da73ec82aa72dee9d4a6309bc7eedcristy                    gamma+=alpha*(*k);
2952e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy                    count++;
2953602ab9b30b644a78a4057da93d838a77391ec0acanthony                  }
2954ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
2955ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
2956602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
2957aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
2958ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
2959602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
2960ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2961ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeMorphology:
2962ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2963ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2964ec9ace44c23c302f336f6e672f6857312747d29bcristy              Minimum value within kernel neighbourhood.
2965602ab9b30b644a78a4057da93d838a77391ec0acanthony
2966ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.  In normal
2967ec9ace44c23c302f336f6e672f6857312747d29bcristy              Greyscale Morphology, the kernel value should be added
2968ec9ace44c23c302f336f6e672f6857312747d29bcristy              to the real value, this is currently not done, due to the
2969ec9ace44c23c302f336f6e672f6857312747d29bcristy              nature of the boolean kernels being used.
2970930be614b4595b97cd79ee864a394796740f76adanthony            */
2971ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
2972ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
2973ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2974ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2975ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
29763667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k) && (*k >= 0.5))
2977ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2978ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((double) pixels[i] < pixel)
2979ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i];
2980ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
2981ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
2982ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
2983602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
2984aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
2985602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
2986602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
2987ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2988ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateMorphology:
2989ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2990ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2991ec9ace44c23c302f336f6e672f6857312747d29bcristy               Maximum value within kernel neighbourhood.
2992ec9ace44c23c302f336f6e672f6857312747d29bcristy
2993ec9ace44c23c302f336f6e672f6857312747d29bcristy               For correct working of this operation for asymetrical kernels,
2994ec9ace44c23c302f336f6e672f6857312747d29bcristy               the kernel needs to be applied in its reflected form.  That is
2995ec9ace44c23c302f336f6e672f6857312747d29bcristy               its values needs to be reversed.
2996602ab9b30b644a78a4057da93d838a77391ec0acanthony
2997ec9ace44c23c302f336f6e672f6857312747d29bcristy               In normal Greyscale Morphology, the kernel value should be
2998ec9ace44c23c302f336f6e672f6857312747d29bcristy               added to the real value, this is currently not done, due to the
2999ec9ace44c23c302f336f6e672f6857312747d29bcristy               nature of the boolean kernels being used.
3000930be614b4595b97cd79ee864a394796740f76adanthony            */
3001e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy            count=0;
3002ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3003ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3004ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3005ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3006ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
30073667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k) && (*k > 0.5))
3008ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3009ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((double) pixels[i] > pixel)
3010ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i];
3011ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3012ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3013ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3014602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3015aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3016602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3017602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3018ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3019ec9ace44c23c302f336f6e672f6857312747d29bcristy          case HitAndMissMorphology:
3020ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThinningMorphology:
3021ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThickenMorphology:
3022ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3023ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3024ec9ace44c23c302f336f6e672f6857312747d29bcristy               Minimum of foreground pixel minus maxumum of background pixels.
3025602ab9b30b644a78a4057da93d838a77391ec0acanthony
3026ec9ace44c23c302f336f6e672f6857312747d29bcristy               The kernel is not reflected for this operation, and consists
3027ec9ace44c23c302f336f6e672f6857312747d29bcristy               of both foreground and background pixel neighbourhoods, 0.0 for
3028ec9ace44c23c302f336f6e672f6857312747d29bcristy               background, and 1.0 for foreground with either Nan or 0.5 values
3029ec9ace44c23c302f336f6e672f6857312747d29bcristy               for don't care.
3030ec9ace44c23c302f336f6e672f6857312747d29bcristy
3031ec9ace44c23c302f336f6e672f6857312747d29bcristy               This never produces a meaningless negative result.  Such results
3032ec9ace44c23c302f336f6e672f6857312747d29bcristy               cause Thinning/Thicken to not work correctly when used against a
3033ec9ace44c23c302f336f6e672f6857312747d29bcristy               greyscale image.
30345ef8e94ff55717be2387d537bd49025780a1a558anthony            */
3035e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy            count=0;
3036ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
3037ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3038ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3039ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3040ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
30413667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k))
3042ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3043ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (*k > 0.7)
3044ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3045ec9ace44c23c302f336f6e672f6857312747d29bcristy                        if ((double) pixels[i] < pixel)
3046ec9ace44c23c302f336f6e672f6857312747d29bcristy                          pixel=(double) pixels[i];
3047ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3048ec9ace44c23c302f336f6e672f6857312747d29bcristy                    else
3049ec9ace44c23c302f336f6e672f6857312747d29bcristy                      if (*k < 0.3)
3050ec9ace44c23c302f336f6e672f6857312747d29bcristy                        {
3051ec9ace44c23c302f336f6e672f6857312747d29bcristy                          if ((double) pixels[i] > maximum)
3052ec9ace44c23c302f336f6e672f6857312747d29bcristy                            maximum=(double) pixels[i];
3053ec9ace44c23c302f336f6e672f6857312747d29bcristy                        }
3054e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy                    count++;
3055ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3056ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
3057ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
30585ef8e94ff55717be2387d537bd49025780a1a558anthony              }
3059aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
30605ef8e94ff55717be2387d537bd49025780a1a558anthony            }
3061ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixel-=maximum;
3062ec9ace44c23c302f336f6e672f6857312747d29bcristy            if (pixel < 0.0)
3063ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixel=0.0;
3064ec9ace44c23c302f336f6e672f6857312747d29bcristy            if (method ==  ThinningMorphology)
3065ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixel=(double) p[center+i]-pixel;
3066ec9ace44c23c302f336f6e672f6857312747d29bcristy            else
3067ec9ace44c23c302f336f6e672f6857312747d29bcristy              if (method ==  ThickenMorphology)
3068ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixel+=(double) p[center+i]+pixel;
30695ef8e94ff55717be2387d537bd49025780a1a558anthony            break;
3070ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3071ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeIntensityMorphology:
3072ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3073ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3074ec9ace44c23c302f336f6e672f6857312747d29bcristy              Select pixel with minimum intensity within kernel neighbourhood.
30756fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3076ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.
3077930be614b4595b97cd79ee864a394796740f76adanthony            */
3078e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy            count=0;
3079ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
3080ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3081ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3082ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3083ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
30843667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k) && (*k >= 0.5))
3085ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3086e1860cabc49c5a3e1297312a204d936a19459d06dirk                    intensity=(double) GetPixelIntensity(image,pixels);
3087e1860cabc49c5a3e1297312a204d936a19459d06dirk                    if (intensity < minimum)
3088ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3089ec9ace44c23c302f336f6e672f6857312747d29bcristy                        pixel=(double) pixels[i];
3090e1860cabc49c5a3e1297312a204d936a19459d06dirk                        minimum=intensity;
3091ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3092e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy                    count++;
3093ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3094ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
3095ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3096602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3097aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3098602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3099602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3100ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3101ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateIntensityMorphology:
3102ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3103ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3104ec9ace44c23c302f336f6e672f6857312747d29bcristy              Select pixel with maximum intensity within kernel neighbourhood.
31056fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3106ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.
3107930be614b4595b97cd79ee864a394796740f76adanthony            */
3108e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy            count=0;
3109ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3110ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3111ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3112ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3113ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
31143667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k) && (*k >= 0.5))
3115ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3116e1860cabc49c5a3e1297312a204d936a19459d06dirk                    intensity=(double) GetPixelIntensity(image,pixels);
3117e1860cabc49c5a3e1297312a204d936a19459d06dirk                    if (intensity > maximum)
3118ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3119ec9ace44c23c302f336f6e672f6857312747d29bcristy                        pixel=(double) pixels[i];
3120e1860cabc49c5a3e1297312a204d936a19459d06dirk                        maximum=intensity;
3121ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3122e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy                    count++;
3123ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3124ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3125ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3126602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3127aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3128602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3129602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3130ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3131ec9ace44c23c302f336f6e672f6857312747d29bcristy          case IterativeDistanceMorphology:
3132ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3133ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3134ec9ace44c23c302f336f6e672f6857312747d29bcristy               Compute th iterative distance from black edge of a white image
3135ec9ace44c23c302f336f6e672f6857312747d29bcristy               shape.  Essentually white values are decreased to the smallest
3136ec9ace44c23c302f336f6e672f6857312747d29bcristy               'distance from edge' it can find.
31376fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3138ec9ace44c23c302f336f6e672f6857312747d29bcristy               It works by adding kernel values to the neighbourhood, and and
3139ec9ace44c23c302f336f6e672f6857312747d29bcristy               select the minimum value found. The kernel is rotated before
3140ec9ace44c23c302f336f6e672f6857312747d29bcristy               use, so kernel distances match resulting distances, when a user
3141ec9ace44c23c302f336f6e672f6857312747d29bcristy               provided asymmetric kernel is applied.
31426fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3143ec9ace44c23c302f336f6e672f6857312747d29bcristy               This code is nearly identical to True GrayScale Morphology but
3144ec9ace44c23c302f336f6e672f6857312747d29bcristy               not quite.
31456fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3146ec9ace44c23c302f336f6e672f6857312747d29bcristy               GreyDilate Kernel values added, maximum value found Kernel is
3147ec9ace44c23c302f336f6e672f6857312747d29bcristy               rotated before use.
31486fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3149ec9ace44c23c302f336f6e672f6857312747d29bcristy               GrayErode:  Kernel values subtracted and minimum value found No
3150ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernel rotation used.
31516fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3152ec9ace44c23c302f336f6e672f6857312747d29bcristy               Note the the Iterative Distance method is essentially a
3153ec9ace44c23c302f336f6e672f6857312747d29bcristy               GrayErode, but with negative kernel values, and kernel rotation
3154ec9ace44c23c302f336f6e672f6857312747d29bcristy               applied.
3155930be614b4595b97cd79ee864a394796740f76adanthony            */
3156e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy            count=0;
3157ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3158ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3159ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3160ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3161ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
31623667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k))
3163ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3164ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3165ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3166e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy                    count++;
3167ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3168ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3169ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3170602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3171aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3172602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3173602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3174ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3175ec9ace44c23c302f336f6e672f6857312747d29bcristy          case UndefinedMorphology:
3176ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3177ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3178ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
3179ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (fabs(pixel-p[center+i]) > MagickEpsilon)
3180c5876076825b539bd230ddadab1d876805c18291cristy          changes[id]++;
31818e6e9602b3a7805e8093f3e54cfc2f02fb89a4f1cristy        gamma=PerceptibleReciprocal(gamma);
3182a8fe15914fb2ad831f8f62c65f82150d53481eddcristy        if (count != 0)
3183a8fe15914fb2ad831f8f62c65f82150d53481eddcristy          gamma*=(double) kernel->height*kernel->width/count;
3184e6e92c4a3b6e06f6557d710e85a41baa3f9389b9cristy        SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*pixel),q);
318583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      }
3186ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image);
3187ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(morphology_image);
3188ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3189cb228bb3ff8da73ec82aa72dee9d4a6309bc7eedcristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3190602ab9b30b644a78a4057da93d838a77391ec0acanthony      status=MagickFalse;
3191602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3192602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
3193602ab9b30b644a78a4057da93d838a77391ec0acanthony        MagickBooleanType
3194602ab9b30b644a78a4057da93d838a77391ec0acanthony          proceed;
3195602ab9b30b644a78a4057da93d838a77391ec0acanthony
3196602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
3197954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        #pragma omp critical (MagickCore_MorphologyPrimitive)
3198602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
3199602ab9b30b644a78a4057da93d838a77391ec0acanthony        proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
3200602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (proceed == MagickFalse)
3201602ab9b30b644a78a4057da93d838a77391ec0acanthony          status=MagickFalse;
3202602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
3203ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
32044c08aed51c5899665ade97263692328eea4af106cristy  morphology_view=DestroyCacheView(morphology_view);
32054c08aed51c5899665ade97263692328eea4af106cristy  image_view=DestroyCacheView(image_view);
3206c93932ae0fce511e693d0cf9b969af16a3e50175dirk  for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
3207c93932ae0fce511e693d0cf9b969af16a3e50175dirk    changed+=changes[j];
3208306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  changes=(size_t *) RelinquishMagickMemory(changes);
3209c5876076825b539bd230ddadab1d876805c18291cristy  return(status ? (ssize_t) changed : -1);
3210602ab9b30b644a78a4057da93d838a77391ec0acanthony}
3211602ab9b30b644a78a4057da93d838a77391ec0acanthony
3212ec9ace44c23c302f336f6e672f6857312747d29bcristy/*
3213ec9ace44c23c302f336f6e672f6857312747d29bcristy  This is almost identical to the MorphologyPrimative() function above, but
3214ec9ace44c23c302f336f6e672f6857312747d29bcristy  applies the primitive directly to the actual image using two passes, once in
3215ec9ace44c23c302f336f6e672f6857312747d29bcristy  each direction, with the results of the previous (and current) row being
3216ec9ace44c23c302f336f6e672f6857312747d29bcristy  re-used.
3217ec9ace44c23c302f336f6e672f6857312747d29bcristy
3218ec9ace44c23c302f336f6e672f6857312747d29bcristy  That is after each row is 'Sync'ed' into the image, the next row makes use of
3219ec9ace44c23c302f336f6e672f6857312747d29bcristy  those values as part of the calculation of the next row.  It repeats, but
3220ec9ace44c23c302f336f6e672f6857312747d29bcristy  going in the oppisite (bottom-up) direction.
3221ec9ace44c23c302f336f6e672f6857312747d29bcristy
3222ec9ace44c23c302f336f6e672f6857312747d29bcristy  Because of this 're-use of results' this function can not make use of multi-
3223ec9ace44c23c302f336f6e672f6857312747d29bcristy  threaded, parellel processing.
3224a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3225e698a255629ba03cd125572de7b35b5e21c4ee5danthonystatic ssize_t MorphologyPrimitiveDirect(Image *image,
3226f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const KernelInfo *kernel,
3227f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3228a8843c1f815ffad2568ec592d5b446cb1476aab5anthony{
3229a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  CacheView
3230ec9ace44c23c302f336f6e672f6857312747d29bcristy    *morphology_view,
3231ec9ace44c23c302f336f6e672f6857312747d29bcristy    *image_view;
3232a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3233a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickBooleanType
3234a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    status;
3235a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3236a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickOffsetType
3237a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    progress;
3238a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3239ec9ace44c23c302f336f6e672f6857312747d29bcristy  OffsetInfo
3240ec9ace44c23c302f336f6e672f6857312747d29bcristy    offset;
3241a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3242a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
3243ec9ace44c23c302f336f6e672f6857312747d29bcristy    width,
3244a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;
3245a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3246ec9ace44c23c302f336f6e672f6857312747d29bcristy  ssize_t
3247ec9ace44c23c302f336f6e672f6857312747d29bcristy    y;
3248a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3249a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image != (Image *) NULL);
3250e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
3251a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel != (KernelInfo *) NULL);
3252e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(kernel->signature == MagickCoreSignature);
3253a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception != (ExceptionInfo *) NULL);
3254e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
3255ec9ace44c23c302f336f6e672f6857312747d29bcristy  status=MagickTrue;
3256ec9ace44c23c302f336f6e672f6857312747d29bcristy  changed=0;
3257ec9ace44c23c302f336f6e672f6857312747d29bcristy  progress=0;
3258ec9ace44c23c302f336f6e672f6857312747d29bcristy  switch(method)
3259ec9ace44c23c302f336f6e672f6857312747d29bcristy  {
3260a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3261e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3262ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3263ec9ace44c23c302f336f6e672f6857312747d29bcristy      /*
3264ec9ace44c23c302f336f6e672f6857312747d29bcristy        Kernel reflected about origin.
3265ec9ace44c23c302f336f6e672f6857312747d29bcristy      */
3266ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=(ssize_t) kernel->width-kernel->x-1;
3267ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=(ssize_t) kernel->height-kernel->y-1;
3268a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3269ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3270a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    default:
3271ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3272ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=kernel->x;
3273ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=kernel->y;
3274a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3275ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3276a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  }
3277ec9ace44c23c302f336f6e672f6857312747d29bcristy  /*
3278ec9ace44c23c302f336f6e672f6857312747d29bcristy    Two views into same image, do not thread.
3279ec9ace44c23c302f336f6e672f6857312747d29bcristy  */
3280ec9ace44c23c302f336f6e672f6857312747d29bcristy  image_view=AcquireVirtualCacheView(image,exception);
3281ec9ace44c23c302f336f6e672f6857312747d29bcristy  morphology_view=AcquireAuthenticCacheView(image,exception);
3282ec9ace44c23c302f336f6e672f6857312747d29bcristy  width=image->columns+kernel->width-1;
3283a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  for (y=0; y < (ssize_t) image->rows; y++)
3284a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
32854c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
328605d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
3287a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
32884c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
328905d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict q;
3290a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3291a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3292a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3293a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
329427be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    ssize_t
329527be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      center;
329627be28f6ee6dcb0c20268851b6359b50fbee71fbcristy
3297ec9ace44c23c302f336f6e672f6857312747d29bcristy    /*
3298ec9ace44c23c302f336f6e672f6857312747d29bcristy      Read virtual pixels, and authentic pixels, from the same image!  We read
3299ec9ace44c23c302f336f6e672f6857312747d29bcristy      using virtual to get virtual pixel handling, but write back into the same
3300ec9ace44c23c302f336f6e672f6857312747d29bcristy      image.
3301ec9ace44c23c302f336f6e672f6857312747d29bcristy
3302ec9ace44c23c302f336f6e672f6857312747d29bcristy      Only top half of kernel is processed as we do a single pass downward
3303ec9ace44c23c302f336f6e672f6857312747d29bcristy      through the image iterating the distance function as we go.
3304a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    */
3305a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3306954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      continue;
3307ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(size_t)
3308ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y+1,exception);
33096fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
33104c08aed51c5899665ade97263692328eea4af106cristy      exception);
33114c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3312954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      {
3313954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        status=MagickFalse;
3314954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        continue;
3315954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      }
331627be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
331727be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      GetPixelChannels(image)*offset.x);
3318a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    for (x=0; x < (ssize_t) image->columns; x++)
3319a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    {
3320ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
3321ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
3322a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3323ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3324ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3325ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
3326ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
3327a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3328ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
3329ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
3330ec9ace44c23c302f336f6e672f6857312747d29bcristy
3331ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
333205d2ff7ebf21f659f5b11e45afb294e152f4330cdirk          *magick_restrict k;
3333ec9ace44c23c302f336f6e672f6857312747d29bcristy
3334ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
333505d2ff7ebf21f659f5b11e45afb294e152f4330cdirk          *magick_restrict pixels;
3336ec9ace44c23c302f336f6e672f6857312747d29bcristy
3337ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
3338ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
3339ec9ace44c23c302f336f6e672f6857312747d29bcristy
3340ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
3341ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
3342ec9ace44c23c302f336f6e672f6857312747d29bcristy
3343ee1bdaac725f5188c568ab27f07917156b98081ccristy        traits=GetPixelChannelTraits(image,(PixelChannel) i);
3344ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (traits == UndefinedPixelTrait)
3345ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
334627be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        if (((traits & CopyPixelTrait) != 0) ||
3347883fde11debec15cedb05dc5d7228d8588066bc0cristy            (GetPixelReadMask(image,p+center) == 0))
3348ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3349ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
335027be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        pixel=(double) QuantumRange;
3351ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
3352ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
3353ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DistanceMorphology:
3354ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3355ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3356ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v <= offset.y; v++)
3357ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3358ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3359ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
33603667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k))
3361e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3362ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3363ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3364e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3365ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3366ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3367e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3368aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3369ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3370ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3371ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3372ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=0; u < offset.x; u++)
3373ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
33743667479b817685c0289edd0fd597d07bc2c6b462dirk              if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
3375ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3376ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3377ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3378ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3379ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3380ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3381e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3382ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3383ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3384ec9ace44c23c302f336f6e672f6857312747d29bcristy          case VoronoiMorphology:
3385ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3386ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3387ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < offset.y; v++)
3388ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3389ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3390ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
33913667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k))
3392e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3393ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3394ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3395e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3396ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3397ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3398e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3399aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3400ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3401ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3402ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3403ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=0; u < offset.x; u++)
3404ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
34053667479b817685c0289edd0fd597d07bc2c6b462dirk              if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
3406ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3407ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3408ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3409ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3410ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3411ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3412ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3413e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3414ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3415ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3416ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3417ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
34186fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (fabs(pixel-q[i]) > MagickEpsilon)
3419ec9ace44c23c302f336f6e672f6857312747d29bcristy          changed++;
34206fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        q[i]=ClampToQuantum(pixel);
3421e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3422ec9ace44c23c302f336f6e672f6857312747d29bcristy      p+=GetPixelChannels(image);
3423ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(image);
3424ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
34256fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3426a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3427a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3428ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3429ec9ace44c23c302f336f6e672f6857312747d29bcristy        MagickBooleanType
3430ec9ace44c23c302f336f6e672f6857312747d29bcristy          proceed;
3431a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34326fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
3433ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (proceed == MagickFalse)
3434ec9ace44c23c302f336f6e672f6857312747d29bcristy          status=MagickFalse;
3435ec9ace44c23c302f336f6e672f6857312747d29bcristy      }
3436ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
34376fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  morphology_view=DestroyCacheView(morphology_view);
34386fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  image_view=DestroyCacheView(image_view);
3439ec9ace44c23c302f336f6e672f6857312747d29bcristy  /*
3440ec9ace44c23c302f336f6e672f6857312747d29bcristy    Do the reverse pass through the image.
3441ec9ace44c23c302f336f6e672f6857312747d29bcristy  */
34426fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  image_view=AcquireVirtualCacheView(image,exception);
34436fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  morphology_view=AcquireAuthenticCacheView(image,exception);
3444ec9ace44c23c302f336f6e672f6857312747d29bcristy  for (y=(ssize_t) image->rows-1; y >= 0; y--)
3445a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
34464c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
344705d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
3448a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34494c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
345005d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict q;
3451a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3452a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3453a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3454a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
345527be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    ssize_t
345627be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      center;
345727be28f6ee6dcb0c20268851b6359b50fbee71fbcristy
3458ec9ace44c23c302f336f6e672f6857312747d29bcristy    /*
3459ec9ace44c23c302f336f6e672f6857312747d29bcristy       Read virtual pixels, and authentic pixels, from the same image.  We
3460ec9ace44c23c302f336f6e672f6857312747d29bcristy       read using virtual to get virtual pixel handling, but write back
3461ec9ace44c23c302f336f6e672f6857312747d29bcristy       into the same image.
3462a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3463ec9ace44c23c302f336f6e672f6857312747d29bcristy       Only the bottom half of the kernel is processed as we up the image.
3464ec9ace44c23c302f336f6e672f6857312747d29bcristy    */
3465db60568e12574785101a4ae8d8da076227a0a889anthony    if (status == MagickFalse)
3466954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      continue;
3467ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(size_t)
34684c08aed51c5899665ade97263692328eea4af106cristy      kernel->y+1,exception);
34696fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
34704c08aed51c5899665ade97263692328eea4af106cristy      exception);
34714c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3472954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      {
3473954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        status=MagickFalse;
3474954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        continue;
3475954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      }
3476aca597f51ca91ee434bba81525c47e32419becb4cristy    p+=(image->columns-1)*GetPixelChannels(image);
3477aca597f51ca91ee434bba81525c47e32419becb4cristy    q+=(image->columns-1)*GetPixelChannels(image);
347827be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    center=(ssize_t) (offset.x*GetPixelChannels(image));
34796fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    for (x=(ssize_t) image->columns-1; x >= 0; x--)
3480ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3481ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
3482ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
3483a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3484ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3485ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3486ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
3487ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
3488a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3489ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
3490ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
3491a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3492ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
349305d2ff7ebf21f659f5b11e45afb294e152f4330cdirk          *magick_restrict k;
3494a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3495ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
349605d2ff7ebf21f659f5b11e45afb294e152f4330cdirk          *magick_restrict pixels;
3497a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3498ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
3499ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
3500ec9ace44c23c302f336f6e672f6857312747d29bcristy
3501ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
3502ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
3503ec9ace44c23c302f336f6e672f6857312747d29bcristy
3504ee1bdaac725f5188c568ab27f07917156b98081ccristy        traits=GetPixelChannelTraits(image,(PixelChannel) i);
3505ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (traits == UndefinedPixelTrait)
3506ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
350727be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        if (((traits & CopyPixelTrait) != 0) ||
3508c3a5802c0bea29c2a308d441dfa233b3059938c1cristy            (GetPixelReadMask(image,p+center) == 0))
3509ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3510ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
351127be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        pixel=(double) QuantumRange;
3512ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
3513ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
3514ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DistanceMorphology:
3515ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3516ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3517ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=offset.y; v < (ssize_t) kernel->height; v++)
3518ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3519ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3520ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
35213667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k))
3522e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3523ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3524ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3525e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3526ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3527ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3528e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3529aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3530e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3531ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
3532d1fafd1f34ef8248b9968d58a15774b3fe22eb18cristy            pixels=q;
3533ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3534ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
35354df11b67f7d54d8e954907de45195449a0c1cff4cristy              pixels+=GetPixelChannels(image);
35363667479b817685c0289edd0fd597d07bc2c6b462dirk              if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
3537ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3538ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3539ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3540ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3541ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3542ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3543ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3544ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3545ec9ace44c23c302f336f6e672f6857312747d29bcristy          case VoronoiMorphology:
3546ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3547ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3548ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=offset.y; v < (ssize_t) kernel->height; v++)
3549ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3550ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3551ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
35523667479b817685c0289edd0fd597d07bc2c6b462dirk                if (!IsNaN(*k))
3553e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3554ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3555ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3556e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3557ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3558ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3559e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3560aca597f51ca91ee434bba81525c47e32419becb4cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3561ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3562ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3563d1fafd1f34ef8248b9968d58a15774b3fe22eb18cristy            pixels=q;
3564ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3565ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
35664df11b67f7d54d8e954907de45195449a0c1cff4cristy              pixels+=GetPixelChannels(image);
35673667479b817685c0289edd0fd597d07bc2c6b462dirk              if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
3568ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3569ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3570ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3571ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3572ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3573ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3574e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3575ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3576ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3577ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3578ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
35796fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (fabs(pixel-q[i]) > MagickEpsilon)
3580ec9ace44c23c302f336f6e672f6857312747d29bcristy          changed++;
35816fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        q[i]=ClampToQuantum(pixel);
3582e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
35836fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      p-=GetPixelChannels(image);
35846fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      q-=GetPixelChannels(image);
3585ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
35866fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
35876fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      status=MagickFalse;
35886fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
35896fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      {
35906fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        MagickBooleanType
35916fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy          proceed;
35926fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
35936fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
35946fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (proceed == MagickFalse)
35956fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy          status=MagickFalse;
35966fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      }
3597ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
3598ec9ace44c23c302f336f6e672f6857312747d29bcristy  morphology_view=DestroyCacheView(morphology_view);
3599ec9ace44c23c302f336f6e672f6857312747d29bcristy  image_view=DestroyCacheView(image_view);
3600aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  return(status ? (ssize_t) changed : -1);
3601a8843c1f815ffad2568ec592d5b446cb1476aab5anthony}
3602a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3603ec9ace44c23c302f336f6e672f6857312747d29bcristy/*
3604ec9ace44c23c302f336f6e672f6857312747d29bcristy  Apply a Morphology by calling one of the above low level primitive
3605ec9ace44c23c302f336f6e672f6857312747d29bcristy  application functions.  This function handles any iteration loops,
3606ec9ace44c23c302f336f6e672f6857312747d29bcristy  composition or re-iteration of results, and compound morphology methods that
3607ec9ace44c23c302f336f6e672f6857312747d29bcristy  is based on multiple low-level (staged) morphology methods.
3608ec9ace44c23c302f336f6e672f6857312747d29bcristy
3609ec9ace44c23c302f336f6e672f6857312747d29bcristy  Basically this provides the complex glue between the requested morphology
3610ec9ace44c23c302f336f6e672f6857312747d29bcristy  method and raw low-level implementation (above).
3611a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3612cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate Image *MorphologyApply(const Image *image,
3613f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method, const ssize_t iterations,
3614f46d42620631d2581e0b6a56456e203e17c427c8anthony  const KernelInfo *kernel, const CompositeOperator compose,const double bias,
3615f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3616602ab9b30b644a78a4057da93d838a77391ec0acanthony{
36171cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  CompositeOperator
36181cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy    curr_compose;
36191cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy
3620602ab9b30b644a78a4057da93d838a77391ec0acanthony  Image
362147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *curr_image,    /* Image we are working with or iterating */
3622a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *work_image,    /* secondary image for primitive iteration */
362347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *save_image,    /* saved image - for 'edge' method only */
362447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rslt_image;    /* resultant image - after multi-kernel handling */
3625602ab9b30b644a78a4057da93d838a77391ec0acanthony
36264fd27e21043be809d66c8202e779255e5b660d2danthony  KernelInfo
362747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *reflected_kernel, /* A reflected copy of the kernel (if needed) */
362847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *norm_kernel,      /* the current normal un-reflected kernel */
362947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rflt_kernel,      /* the current reflected kernel (if needed) */
363047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *this_kernel;      /* the kernel being applied */
36314fd27e21043be809d66c8202e779255e5b660d2danthony
36324fd27e21043be809d66c8202e779255e5b660d2danthony  MorphologyMethod
3633a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    primitive;      /* the current morphology primitive being applied */
36349eb4f74649b23c053b308ce1152dce51239450baanthony
36359eb4f74649b23c053b308ce1152dce51239450baanthony  CompositeOperator
363647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose;   /* multi-kernel compose method for results to use */
363747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
363847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  MagickBooleanType
3639e698a255629ba03cd125572de7b35b5e21c4ee5danthony    special,        /* do we use a direct modify function? */
364047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    verbose;        /* verbose output of results */
36414fd27e21043be809d66c8202e779255e5b660d2danthony
3642bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
3643a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    method_loop,    /* Loop 1: number of compound method iterations (norm 1) */
364447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_limit,   /*         maximum number of compound method iterations */
364547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number,  /* Loop 2: the kernel number being applied */
3646a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_loop,     /* Loop 3: primitive loop for compound morphology */
3647a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_limit,    /*         how many primitives are in this compound */
3648a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    kernel_loop,    /* Loop 4: iterate the kernel over image */
364947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_limit,   /*         number of times to iterate kernel */
3650a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    count,          /* total count of primitive steps applied */
365147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_changed, /* total count of changed using iterated kernel */
365247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed; /* total count of changed over method iteration */
365347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3654a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  ssize_t
3655a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;        /* number pixels changed by last primitive operation */
3656a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
365747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  char
3658151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    v_info[MagickPathExtent];
36591b2bc0a7da432e6e1cc0480280402df213faa940anthony
3660602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image != (Image *) NULL);
3661e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
36624fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
3663e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(kernel->signature == MagickCoreSignature);
3664602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception != (ExceptionInfo *) NULL);
3665e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
3666602ab9b30b644a78a4057da93d838a77391ec0acanthony
3667a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  count = 0;      /* number of low-level morphology primitives performed */
3668602ab9b30b644a78a4057da93d838a77391ec0acanthony  if ( iterations == 0 )
3669f432c635c526259b858c9aad3d409c5c44545686cristy    return((Image *) NULL);   /* null operation - nothing to do! */
3670602ab9b30b644a78a4057da93d838a77391ec0acanthony
3671bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  kernel_limit = (size_t) iterations;
367247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
3673e698a255629ba03cd125572de7b35b5e21c4ee5danthony     kernel_limit = image->columns>image->rows ? image->columns : image->rows;
367428ad1d779b6ca95852e860514185a7a97e06af77anthony
36758b520b45d3002ab6d4e6127172b080119137fb65cristy  verbose = IsStringTrue(GetImageArtifact(image,"debug"));
36764f1dcb76c95ef6410f2957ca9e7e1d391cee0d02anthony
36779eb4f74649b23c053b308ce1152dce51239450baanthony  /* initialise for cleanup */
367847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  curr_image = (Image *) image;
36791cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  curr_compose = image->compose;
3680aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  (void) curr_compose;
368147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  work_image = save_image = rslt_image = (Image *) NULL;
368247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  reflected_kernel = (KernelInfo *) NULL;
36834fd27e21043be809d66c8202e779255e5b660d2danthony
368447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Initialize specific methods
368547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + which loop should use the given iteratations
3686a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   * + how many primitives make up the compound morphology
368747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + multi-kernel compose method to use (by default)
368847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
368947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_limit = 1;       /* just do method once, unless otherwise set */
3690ea61f01656bb0f9074677452017cc559e54093faanthony  stage_limit = 1;        /* assume method is not a compound */
36914ee950098ad0166bbbc85c3a59bc079cd321384aglennrp  special = MagickFalse;   /* assume it is NOT a direct modify primitive */
369247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  rslt_compose = compose; /* and we are composing multi-kernels as given */
36939eb4f74649b23c053b308ce1152dce51239450baanthony  switch( method ) {
3694a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case SmoothMorphology:  /* 4 primitive compound morphology */
369547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 4;
3696602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
3697a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case OpenMorphology:    /* 2 primitive compound morphology */
3698930be614b4595b97cd79ee864a394796740f76adanthony    case OpenIntensityMorphology:
369947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case TopHatMorphology:
370047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
37014fd27e21043be809d66c8202e779255e5b660d2danthony    case CloseIntensityMorphology:
370247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
370347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case EdgeMorphology:
370447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 2;
3705602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
37069eb4f74649b23c053b308ce1152dce51239450baanthony    case HitAndMissMorphology:
370747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
37083ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      /* FALL THUR */
3709c3e48258f3253188894e783dcdfd03562f7ab2c5anthony    case ThinningMorphology:
37109eb4f74649b23c053b308ce1152dce51239450baanthony    case ThickenMorphology:
37113ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      method_limit = kernel_limit;  /* iterate the whole method */
3712c3e48258f3253188894e783dcdfd03562f7ab2c5anthony      kernel_limit = 1;             /* do not do kernel iteration  */
371347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
3714a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3715e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3716f34d9b2df49a407af764c79e07d587af0600983aanthony      special = MagickTrue;         /* use special direct primative */
3717a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
371847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
37199eb4f74649b23c053b308ce1152dce51239450baanthony      break;
37209eb4f74649b23c053b308ce1152dce51239450baanthony  }
3721602ab9b30b644a78a4057da93d838a77391ec0acanthony
3722e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Apply special methods with special requirments
3723e698a255629ba03cd125572de7b35b5e21c4ee5danthony  ** For example, single run only, or post-processing requirements
3724e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
3725cd8b331760407523f2a59cc65c1cd9c3d4422bafcristy  if ( special != MagickFalse )
3726e698a255629ba03cd125572de7b35b5e21c4ee5danthony    {
3727e698a255629ba03cd125572de7b35b5e21c4ee5danthony      rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3728e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if (rslt_image == (Image *) NULL)
3729e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3730574cc26500992189f637cd1cdf93d0654e7df7aecristy      if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3731574cc26500992189f637cd1cdf93d0654e7df7aecristy        goto error_cleanup;
3732e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3733a325e90b7583eab5e70f0ceebb8257670e652f0fcristy      changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
3734e698a255629ba03cd125572de7b35b5e21c4ee5danthony
373511a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk      if (verbose != MagickFalse)
37365acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) (void) FormatLocaleFile(stderr,
37371e604812fad85bb96f757a2393015ae3d061c39acristy          "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
37381e604812fad85bb96f757a2393015ae3d061c39acristy          CommandOptionToMnemonic(MagickMorphologyOptions, method),
37391e604812fad85bb96f757a2393015ae3d061c39acristy          1.0,0.0,1.0, (double) changed);
3740e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3741e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( changed < 0 )
3742e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3743e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3744e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method == VoronoiMorphology ) {
3745a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony        /* Preserve the alpha channel of input image - but turned it off */
374663240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
374763240888c3975789a09c2494a4654b523931df96cristy          exception);
3748feb3e9695150978a5d2372d3fe2f60466a7c8066cristy        (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
374939172408bad7ef2ef00a815fa9abf9979e7857cbcristy          MagickTrue,0,0,exception);
375063240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
375163240888c3975789a09c2494a4654b523931df96cristy          exception);
3752e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3753e698a255629ba03cd125572de7b35b5e21c4ee5danthony      goto exit_cleanup;
3754e698a255629ba03cd125572de7b35b5e21c4ee5danthony    }
3755e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3756c3e48258f3253188894e783dcdfd03562f7ab2c5anthony  /* Handle user (caller) specified multi-kernel composition method */
375747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( compose != UndefinedCompositeOp )
375847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = compose;  /* override default composition for method */
375947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_compose == UndefinedCompositeOp )
376047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
37614fd27e21043be809d66c8202e779255e5b660d2danthony
3762a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Some methods require a reflected kernel to use with primitives.
3763c3e48258f3253188894e783dcdfd03562f7ab2c5anthony   * Create the reflected kernel for those methods. */
376447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  switch ( method ) {
376547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CorrelateMorphology:
376647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
376747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseIntensityMorphology:
376847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
376947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case SmoothMorphology:
377047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      reflected_kernel = CloneKernelInfo(kernel);
377147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if (reflected_kernel == (KernelInfo *) NULL)
377247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        goto error_cleanup;
377347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      RotateKernelInfo(reflected_kernel,180);
377447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
377547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
377647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
377747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
3778602ab9b30b644a78a4057da93d838a77391ec0acanthony
3779e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Loops around more primitive morpholgy methods
3780e698a255629ba03cd125572de7b35b5e21c4ee5danthony  **  erose, dilate, open, close, smooth, edge, etc...
3781e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
378247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Loop 1:  iterate the compound method */
378347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_loop = 0;
378447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_changed = 1;
378547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while ( method_loop < method_limit && method_changed > 0 ) {
378647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_loop++;
378747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed = 0;
378847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
378947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    /* Loop 2:  iterate over each kernel in a multi-kernel list */
379047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    norm_kernel = (KernelInfo *) kernel;
3791f2faecf9facdbbb14fcba373365f9f691a9658e0cristy    this_kernel = (KernelInfo *) kernel;
379247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rflt_kernel = reflected_kernel;
3793e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony
379447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number = 0;
379547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    while ( norm_kernel != NULL ) {
379647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
379747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* Loop 3: Compound Morphology Staging - Select Primative to apply */
379847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_loop = 0;          /* the compound morphology stage number */
379947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      while ( stage_loop < stage_limit ) {
380047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        stage_loop++;   /* The stage of the compound morphology */
380147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3802a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Select primitive morphology for this stage of compound method */
380347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        this_kernel = norm_kernel; /* default use unreflected kernel */
3804a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        primitive = method;        /* Assume method is a primitive */
380547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        switch( method ) {
380647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case ErodeMorphology:      /* just erode */
380747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeInMorphology:     /* erode and image difference */
3808a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
380947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
381047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case DilateMorphology:     /* just dilate */
381147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeOutMorphology:    /* dilate and image difference */
3812a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
381347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
381447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenMorphology:       /* erode then dialate */
381547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case TopHatMorphology:     /* open and image difference */
3816a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
381747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3818a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateMorphology;
381947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
382047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenIntensityMorphology:
3821a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeIntensityMorphology;
382247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3823a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateIntensityMorphology;
3824e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony            break;
382547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseMorphology:      /* dilate, then erode */
382647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case BottomHatMorphology:  /* close and image difference */
382747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3828a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
382947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3830a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
383147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
383247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseIntensityMorphology:
383347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3834a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateIntensityMorphology;
383547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3836a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeIntensityMorphology;
383747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
383847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case SmoothMorphology:         /* open, close */
383947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            switch ( stage_loop ) {
384047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 1: /* start an open method, which starts with Erode */
3841a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
384247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
384347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 2:  /* now Dilate the Erode */
3844a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
384547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
384647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 3:  /* Reflect kernel a close */
384747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
3848a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
384947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
385047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 4:  /* Finish the Close */
385147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
3852a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
385347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
385447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
385547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
385647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeMorphology:        /* dilate and erode difference */
3857a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
385847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 ) {
385947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              save_image = curr_image;      /* save the image difference */
386047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              curr_image = (Image *) image;
3861a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
386247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
386347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
386447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CorrelateMorphology:
386547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            /* A Correlation is a Convolution with a reflected kernel.
386647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** However a Convolution is a weighted sum using a reflected
386747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel.  It may seem stange to convert a Correlation into a
386847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution as the Correlation is the simplier method, but
386947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution is much more commonly used, and it makes sense to
387047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** implement it directly so as to avoid the need to duplicate the
387147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel when it is not required (which is typically the
387247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** default).
387347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            */
387447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3875a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ConvolveMorphology;
387647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
387747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          default:
387847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
387947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
3880e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony        assert( this_kernel != (KernelInfo *) NULL );
38817a01dcf50ce12cb2a789bedff51e9345f022432eanthony
388247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        /* Extra information for debugging compound operations */
388311a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk        if (verbose != MagickFalse) {
388447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( stage_limit > 1 )
3885151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy            (void) FormatLocaleString(v_info,MagickPathExtent,"%s:%.20g.%.20g -> ",
3886042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy             CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
3887e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy             method_loop,(double) stage_loop);
3888a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          else if ( primitive != method )
3889151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy            (void) FormatLocaleString(v_info, MagickPathExtent, "%s:%.20g -> ",
3890042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
3891e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              method_loop);
389247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          else
389347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            v_info[0] = '\0';
389447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
38959eb4f74649b23c053b308ce1152dce51239450baanthony
3896a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Loop 4: Iterate the kernel with primitive */
389747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_loop = 0;
389847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_changed = 0;
389947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        changed = 1;
390047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        while ( kernel_loop < kernel_limit && changed > 0 ) {
390147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          kernel_loop++;     /* the iteration of this kernel */
39029eb4f74649b23c053b308ce1152dce51239450baanthony
3903a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* Create a clone as the destination image, if not yet defined */
39049eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == (Image *) NULL )
39059eb4f74649b23c053b308ce1152dce51239450baanthony            {
39069eb4f74649b23c053b308ce1152dce51239450baanthony              work_image=CloneImage(image,0,0,MagickTrue,exception);
39079eb4f74649b23c053b308ce1152dce51239450baanthony              if (work_image == (Image *) NULL)
39089eb4f74649b23c053b308ce1152dce51239450baanthony                goto error_cleanup;
3909574cc26500992189f637cd1cdf93d0654e7df7aecristy              if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
3910574cc26500992189f637cd1cdf93d0654e7df7aecristy                goto error_cleanup;
39119eb4f74649b23c053b308ce1152dce51239450baanthony            }
39129eb4f74649b23c053b308ce1152dce51239450baanthony
3913501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
39149eb4f74649b23c053b308ce1152dce51239450baanthony          count++;
3915e698a255629ba03cd125572de7b35b5e21c4ee5danthony          changed = MorphologyPrimitive(curr_image, work_image, primitive,
3916f46d42620631d2581e0b6a56456e203e17c427c8anthony                       this_kernel, bias, exception);
391711a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk          if (verbose != MagickFalse) {
391847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( kernel_loop > 1 )
39195acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
39205acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) (void) FormatLocaleFile(stderr,
39211e604812fad85bb96f757a2393015ae3d061c39acristy              "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
3922042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
3923a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive),(this_kernel == rflt_kernel ) ? "*" : "",
3924e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) (method_loop+kernel_loop-1),(double) kernel_number,
3925e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) count,(double) changed);
392647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          }
3927a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          if ( changed < 0 )
3928a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            goto error_cleanup;
3929a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          kernel_changed += changed;
3930a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          method_changed += changed;
3931a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
39329eb4f74649b23c053b308ce1152dce51239450baanthony          /* prepare next loop */
39339eb4f74649b23c053b308ce1152dce51239450baanthony          { Image *tmp = work_image;   /* swap images for iteration */
39349eb4f74649b23c053b308ce1152dce51239450baanthony            work_image = curr_image;
39359eb4f74649b23c053b308ce1152dce51239450baanthony            curr_image = tmp;
39369eb4f74649b23c053b308ce1152dce51239450baanthony          }
39379eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == image )
393847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            work_image = (Image *) NULL; /* replace input 'image' */
39399eb4f74649b23c053b308ce1152dce51239450baanthony
3940a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        } /* End Loop 4: Iterate the kernel with primitive */
39417a01dcf50ce12cb2a789bedff51e9345f022432eanthony
394211a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk        if (verbose != MagickFalse && kernel_changed != (size_t)changed)
39435acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
394411a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk        if (verbose != MagickFalse && stage_loop < stage_limit)
39455acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
39469eb4f74649b23c053b308ce1152dce51239450baanthony
394747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#if 0
39485acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
39495acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
39505acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      work =0x%lx\n", (unsigned long)work_image);
39515acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      save =0x%lx\n", (unsigned long)save_image);
39525acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
395347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#endif
39541b2bc0a7da432e6e1cc0480280402df213faa940anthony
395547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
39561b2bc0a7da432e6e1cc0480280402df213faa940anthony
395747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /*  Final Post-processing for some Compound Methods
395847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      **
395947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** The removal of any 'Sync' channel flag in the Image Compositon
396047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** below ensures the methematical compose method is applied in a
396147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** purely mathematical way, and only to the selected channels.
396247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** Turn off SVG composition 'alpha blending'.
396347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      */
396447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      switch( method ) {
396547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeOutMorphology:
396647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeInMorphology:
396747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case TopHatMorphology:
396847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case BottomHatMorphology:
396911a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk          if (verbose != MagickFalse)
3970e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy            (void) FormatLocaleFile(stderr,
3971e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              "\n%s: Difference with original image",CommandOptionToMnemonic(
3972e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              MagickMorphologyOptions, method) );
3973feb3e9695150978a5d2372d3fe2f60466a7c8066cristy          (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
397439172408bad7ef2ef00a815fa9abf9979e7857cbcristy            MagickTrue,0,0,exception);
397547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
397647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeMorphology:
397711a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk          if (verbose != MagickFalse)
3978e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy            (void) FormatLocaleFile(stderr,
3979e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
3980e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              MagickMorphologyOptions, method) );
3981feb3e9695150978a5d2372d3fe2f60466a7c8066cristy          (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
398239172408bad7ef2ef00a815fa9abf9979e7857cbcristy            MagickTrue,0,0,exception);
398347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          save_image = DestroyImage(save_image); /* finished with save image */
398447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
398547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        default:
398647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
3987602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
39889eb4f74649b23c053b308ce1152dce51239450baanthony
398947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* multi-kernel handling:  re-iterate, or compose results */
399047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( kernel->next == (KernelInfo *) NULL )
3991c3e48258f3253188894e783dcdfd03562f7ab2c5anthony        rslt_image = curr_image;   /* just return the resulting image */
399247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_compose == NoCompositeOp )
399311a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk        { if (verbose != MagickFalse) {
3994c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            if ( this_kernel->next != (KernelInfo *) NULL )
39955acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (re-iterate)");
3996c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            else
39975acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (done)");
3998c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          }
3999c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          rslt_image = curr_image; /* return result, and re-iterate */
40009eb4f74649b23c053b308ce1152dce51239450baanthony        }
400147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_image == (Image *) NULL)
400211a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk        { if (verbose != MagickFalse)
40035acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (save for compose)");
400447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          rslt_image = curr_image;
400547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
40069eb4f74649b23c053b308ce1152dce51239450baanthony        }
400747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else
4008ea61f01656bb0f9074677452017cc559e54093faanthony        { /* Add the new 'current' result to the composition
400947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          **
401047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** The removal of any 'Sync' channel flag in the Image Compositon
401147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** below ensures the methematical compose method is applied in a
401247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** purely mathematical way, and only to the selected channels.
4013ea61f01656bb0f9074677452017cc559e54093faanthony          ** IE: Turn off SVG composition 'alpha blending'.
401447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          */
401511a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk          if (verbose != MagickFalse)
40165acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (compose \"%s\")",
4017feb3e9695150978a5d2372d3fe2f60466a7c8066cristy              CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
401839172408bad7ef2ef00a815fa9abf9979e7857cbcristy          (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
4019feb3e9695150978a5d2372d3fe2f60466a7c8066cristy            0,0,exception);
40200bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony          curr_image = DestroyImage(curr_image);
402147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
402247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
402311a7cb75e76e53a2e23eff142605ebd2f8b603f3dirk      if (verbose != MagickFalse)
40245acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "\n");
40254fd27e21043be809d66c8202e779255e5b660d2danthony
402647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* loop to the next kernel in a multi-kernel list */
402747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      norm_kernel = norm_kernel->next;
402847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( rflt_kernel != (KernelInfo *) NULL )
402947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        rflt_kernel = rflt_kernel->next;
403047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      kernel_number++;
403147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    } /* End Loop 2: Loop over each kernel */
40329eb4f74649b23c053b308ce1152dce51239450baanthony
403347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  } /* End Loop 1: compound method interation */
4034602ab9b30b644a78a4057da93d838a77391ec0acanthony
40359eb4f74649b23c053b308ce1152dce51239450baanthony  goto exit_cleanup;
40361b2bc0a7da432e6e1cc0480280402df213faa940anthony
403747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Yes goto's are bad, but it makes cleanup lot more efficient */
40381b2bc0a7da432e6e1cc0480280402df213faa940anthonyerror_cleanup:
4039ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image )
4040ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
404147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_image != (Image *) NULL )
404247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_image = DestroyImage(rslt_image);
40431b2bc0a7da432e6e1cc0480280402df213faa940anthonyexit_cleanup:
4044ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image || curr_image == image )
4045ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
4046ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image != (Image *) NULL )
404747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    curr_image = DestroyImage(curr_image);
40489eb4f74649b23c053b308ce1152dce51239450baanthony  if ( work_image != (Image *) NULL )
404947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    work_image = DestroyImage(work_image);
40509eb4f74649b23c053b308ce1152dce51239450baanthony  if ( save_image != (Image *) NULL )
405147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    save_image = DestroyImage(save_image);
405247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( reflected_kernel != (KernelInfo *) NULL )
405347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    reflected_kernel = DestroyKernelInfo(reflected_kernel);
405447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return(rslt_image);
40559eb4f74649b23c053b308ce1152dce51239450baanthony}
4056a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
40579eb4f74649b23c053b308ce1152dce51239450baanthony
40589eb4f74649b23c053b308ce1152dce51239450baanthony/*
40599eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40609eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40619eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40629eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
4063f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%     M o r p h o l o g y I m a g e                                           %
40649eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40659eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40669eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40679eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40689eb4f74649b23c053b308ce1152dce51239450baanthony%
4069a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony%  MorphologyImage() applies a user supplied kernel to the image according to
4070a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony%  the given mophology method.
40719eb4f74649b23c053b308ce1152dce51239450baanthony%
40729eb4f74649b23c053b308ce1152dce51239450baanthony%  This function applies any and all user defined settings before calling
40739eb4f74649b23c053b308ce1152dce51239450baanthony%  the above internal function MorphologyApply().
40749eb4f74649b23c053b308ce1152dce51239450baanthony%
40759eb4f74649b23c053b308ce1152dce51239450baanthony%  User defined settings include...
407622de2722b682eb405b60ec6022a7546df994674eanthony%    * Output Bias for Convolution and correlation ("-define convolve:bias=??")
4077972c2e42a2171341bbf55a48cb36882616a591b1dirk%    * Kernel Scale/normalize settings            ("-define convolve:scale=??")
407846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      This can also includes the addition of a scaled unity kernel.
4079972c2e42a2171341bbf55a48cb36882616a591b1dirk%    * Show Kernel being applied            ("-define morphology:showkernel=1")
408022de2722b682eb405b60ec6022a7546df994674eanthony%
408122de2722b682eb405b60ec6022a7546df994674eanthony%  Other operators that do not want user supplied options interfering,
4082972c2e42a2171341bbf55a48cb36882616a591b1dirk%  especially "convolve:bias" and "morphology:showkernel" should use
4083972c2e42a2171341bbf55a48cb36882616a591b1dirk%  MorphologyApply() directly.
40849eb4f74649b23c053b308ce1152dce51239450baanthony%
40859eb4f74649b23c053b308ce1152dce51239450baanthony%  The format of the MorphologyImage method is:
40869eb4f74649b23c053b308ce1152dce51239450baanthony%
40879eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyImage(const Image *image,MorphologyMethod method,
4088bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
40899eb4f74649b23c053b308ce1152dce51239450baanthony%
40909eb4f74649b23c053b308ce1152dce51239450baanthony%  A description of each parameter follows:
40919eb4f74649b23c053b308ce1152dce51239450baanthony%
40929eb4f74649b23c053b308ce1152dce51239450baanthony%    o image: the image.
40939eb4f74649b23c053b308ce1152dce51239450baanthony%
40949eb4f74649b23c053b308ce1152dce51239450baanthony%    o method: the morphology method to be applied.
40959eb4f74649b23c053b308ce1152dce51239450baanthony%
40969eb4f74649b23c053b308ce1152dce51239450baanthony%    o iterations: apply the operation this many times (or no change).
40979eb4f74649b23c053b308ce1152dce51239450baanthony%                  A value of -1 means loop until no change found.
40989eb4f74649b23c053b308ce1152dce51239450baanthony%                  How this is applied may depend on the morphology method.
40999eb4f74649b23c053b308ce1152dce51239450baanthony%                  Typically this is a value of 1.
41009eb4f74649b23c053b308ce1152dce51239450baanthony%
41019eb4f74649b23c053b308ce1152dce51239450baanthony%    o kernel: An array of double representing the morphology kernel.
41029eb4f74649b23c053b308ce1152dce51239450baanthony%              Warning: kernel may be normalized for the Convolve method.
41039eb4f74649b23c053b308ce1152dce51239450baanthony%
41049eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
41059eb4f74649b23c053b308ce1152dce51239450baanthony%
41069eb4f74649b23c053b308ce1152dce51239450baanthony*/
4107f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristyMagickExport Image *MorphologyImage(const Image *image,
4108f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const ssize_t iterations,
4109f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const KernelInfo *kernel,ExceptionInfo *exception)
41109eb4f74649b23c053b308ce1152dce51239450baanthony{
4111972c2e42a2171341bbf55a48cb36882616a591b1dirk  const char
4112972c2e42a2171341bbf55a48cb36882616a591b1dirk    *artifact;
41139eb4f74649b23c053b308ce1152dce51239450baanthony
411447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  CompositeOperator
411547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    compose;
411647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
4117972c2e42a2171341bbf55a48cb36882616a591b1dirk  double
4118972c2e42a2171341bbf55a48cb36882616a591b1dirk    bias;
4119972c2e42a2171341bbf55a48cb36882616a591b1dirk
41209eb4f74649b23c053b308ce1152dce51239450baanthony  Image
41219eb4f74649b23c053b308ce1152dce51239450baanthony    *morphology_image;
41229eb4f74649b23c053b308ce1152dce51239450baanthony
4123972c2e42a2171341bbf55a48cb36882616a591b1dirk  KernelInfo
4124972c2e42a2171341bbf55a48cb36882616a591b1dirk    *curr_kernel;
41259eb4f74649b23c053b308ce1152dce51239450baanthony
412622de2722b682eb405b60ec6022a7546df994674eanthony  curr_kernel = (KernelInfo *) kernel;
412722de2722b682eb405b60ec6022a7546df994674eanthony  bias=0.0;
4128d228c03fa334bae897eee6c2d8721fa48e1577bacristy  compose = UndefinedCompositeOp;  /* use default for method */
412922de2722b682eb405b60ec6022a7546df994674eanthony
413046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Apply Convolve/Correlate Normalization and Scaling Factors.
413146a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * This is done BEFORE the ShowKernelInfo() function is called so that
413246a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * users can see the results of the 'option:convolve:scale' option.
41339eb4f74649b23c053b308ce1152dce51239450baanthony   */
4134d9435776384a479c2f0d621b4bef12a35592fd5dcristy  if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
413522de2722b682eb405b60ec6022a7546df994674eanthony      /* Get the bias value as it will be needed */
413622de2722b682eb405b60ec6022a7546df994674eanthony      artifact = GetImageArtifact(image,"convolve:bias");
413722de2722b682eb405b60ec6022a7546df994674eanthony      if ( artifact != (const char *) NULL) {
41385d0a441fe7d90e3449f2103ed88c32cec28595e6dirk        if (IsGeometry(artifact) == MagickFalse)
413922de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
414022de2722b682eb405b60ec6022a7546df994674eanthony               OptionWarning,"InvalidSetting","'%s' '%s'",
414122de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:bias",artifact);
414222de2722b682eb405b60ec6022a7546df994674eanthony        else
414322de2722b682eb405b60ec6022a7546df994674eanthony          bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
414422de2722b682eb405b60ec6022a7546df994674eanthony      }
414522de2722b682eb405b60ec6022a7546df994674eanthony
414622de2722b682eb405b60ec6022a7546df994674eanthony      /* Scale kernel according to user wishes */
41479eb4f74649b23c053b308ce1152dce51239450baanthony      artifact = GetImageArtifact(image,"convolve:scale");
4148f432c635c526259b858c9aad3d409c5c44545686cristy      if ( artifact != (const char *) NULL ) {
41495d0a441fe7d90e3449f2103ed88c32cec28595e6dirk        if (IsGeometry(artifact) == MagickFalse)
415022de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
415122de2722b682eb405b60ec6022a7546df994674eanthony               OptionWarning,"InvalidSetting","'%s' '%s'",
415222de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:scale",artifact);
415322de2722b682eb405b60ec6022a7546df994674eanthony        else {
415422de2722b682eb405b60ec6022a7546df994674eanthony          if ( curr_kernel == kernel )
415522de2722b682eb405b60ec6022a7546df994674eanthony            curr_kernel = CloneKernelInfo(kernel);
415622de2722b682eb405b60ec6022a7546df994674eanthony          if (curr_kernel == (KernelInfo *) NULL)
415722de2722b682eb405b60ec6022a7546df994674eanthony            return((Image *) NULL);
415822de2722b682eb405b60ec6022a7546df994674eanthony          ScaleGeometryKernelInfo(curr_kernel, artifact);
41599eb4f74649b23c053b308ce1152dce51239450baanthony        }
41609eb4f74649b23c053b308ce1152dce51239450baanthony      }
41619eb4f74649b23c053b308ce1152dce51239450baanthony    }
41629eb4f74649b23c053b308ce1152dce51239450baanthony
41639eb4f74649b23c053b308ce1152dce51239450baanthony  /* display the (normalized) kernel via stderr */
4164972c2e42a2171341bbf55a48cb36882616a591b1dirk  artifact=GetImageArtifact(image,"morphology:showkernel");
4165972c2e42a2171341bbf55a48cb36882616a591b1dirk  if (IsStringTrue(artifact) != MagickFalse)
41669eb4f74649b23c053b308ce1152dce51239450baanthony    ShowKernelInfo(curr_kernel);
41679eb4f74649b23c053b308ce1152dce51239450baanthony
41683206678d008425bc56dd2dbad002f2bb26299dc2anthony  /* Override the default handling of multi-kernel morphology results
41693206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'Undefined' use the default method
41703206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'None' (default for 'Convolve') re-iterate previous result
41713206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Otherwise merge resulting images using compose method given.
41723206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Default for 'HitAndMiss' is 'Lighten'.
417347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
4174972c2e42a2171341bbf55a48cb36882616a591b1dirk  {
417522de2722b682eb405b60ec6022a7546df994674eanthony    ssize_t
417622de2722b682eb405b60ec6022a7546df994674eanthony      parse;
417722de2722b682eb405b60ec6022a7546df994674eanthony
4178f46d42620631d2581e0b6a56456e203e17c427c8anthony    artifact = GetImageArtifact(image,"morphology:compose");
417922de2722b682eb405b60ec6022a7546df994674eanthony    if ( artifact != (const char *) NULL) {
418022de2722b682eb405b60ec6022a7546df994674eanthony      parse=ParseCommandOption(MagickComposeOptions,
418170b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy        MagickFalse,artifact);
418222de2722b682eb405b60ec6022a7546df994674eanthony      if ( parse < 0 )
418322de2722b682eb405b60ec6022a7546df994674eanthony        (void) ThrowMagickException(exception,GetMagickModule(),
418422de2722b682eb405b60ec6022a7546df994674eanthony             OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
418522de2722b682eb405b60ec6022a7546df994674eanthony             "morphology:compose",artifact);
418622de2722b682eb405b60ec6022a7546df994674eanthony      else
418722de2722b682eb405b60ec6022a7546df994674eanthony        compose=(CompositeOperator)parse;
418822de2722b682eb405b60ec6022a7546df994674eanthony    }
418928ad1d779b6ca95852e860514185a7a97e06af77anthony  }
41909eb4f74649b23c053b308ce1152dce51239450baanthony  /* Apply the Morphology */
4191f46d42620631d2581e0b6a56456e203e17c427c8anthony  morphology_image = MorphologyApply(image,method,iterations,
4192f46d42620631d2581e0b6a56456e203e17c427c8anthony    curr_kernel,compose,bias,exception);
41939eb4f74649b23c053b308ce1152dce51239450baanthony
41949eb4f74649b23c053b308ce1152dce51239450baanthony  /* Cleanup and Exit */
41959eb4f74649b23c053b308ce1152dce51239450baanthony  if ( curr_kernel != kernel )
41961b2bc0a7da432e6e1cc0480280402df213faa940anthony    curr_kernel=DestroyKernelInfo(curr_kernel);
41979eb4f74649b23c053b308ce1152dce51239450baanthony  return(morphology_image);
41989eb4f74649b23c053b308ce1152dce51239450baanthony}
419983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
420083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
420183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
420383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
420483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
42054fd27e21043be809d66c8202e779255e5b660d2danthony+     R o t a t e K e r n e l I n f o                                         %
420683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
420783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
420883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
420983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
421146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  RotateKernelInfo() rotates the kernel by the angle given.
421246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
421346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  Currently it is restricted to 90 degree angles, of either 1D kernels
421446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
421546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It will ignore usless rotations for specific 'named' built-in kernels.
421683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
42174fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the RotateKernelInfo method is:
421883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
42194fd27e21043be809d66c8202e779255e5b660d2danthony%      void RotateKernelInfo(KernelInfo *kernel, double angle)
422083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
422183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
422283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
422383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
422483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
422583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o angle: angle to rotate in degrees
422683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
422746a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This function is currently internal to this module only, but can be exported
422846a369d839971ab627bdb31a93d8bd63e81b65a3anthony% to other modules if needed.
422983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
42304fd27e21043be809d66c8202e779255e5b660d2danthonystatic void RotateKernelInfo(KernelInfo *kernel, double angle)
423183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
42321b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* angle the lower kernels first */
42331b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
42341b2bc0a7da432e6e1cc0480280402df213faa940anthony    RotateKernelInfo(kernel->next, angle);
42351b2bc0a7da432e6e1cc0480280402df213faa940anthony
423683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
423783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  **
423883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  ** TODO: expand beyond simple 90 degree rotates, flips and flops
423983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  */
424083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
424183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* Modulus the angle */
424283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  angle = fmod(angle, 360.0);
424383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( angle < 0 )
424483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    angle += 360.0;
424583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42463c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 337.5 < angle || angle <= 22.5 )
424743c4925e5305a26e48d68f7893e94f55d0831c39anthony    return;   /* Near zero angle - no change! - At least not at this time */
424883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42493dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  /* Handle special cases */
425083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  switch (kernel->type) {
425183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These built-in kernels are cylindrical kernels, rotating is useless */
425283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case GaussianKernel:
4253501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
4254501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
425583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiskKernel:
42563dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case PeaksKernel:
42573dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case LaplacianKernel:
425883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case ChebyshevKernel:
4259bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
426083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case EuclideanKernel:
426183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
426283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
426383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These may be rotatable at non-90 angles in the future */
426483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* but simply rotating them in multiples of 90 degrees is useless */
426583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case SquareKernel:
426683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiamondKernel:
426783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case PlusKernel:
42683dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
426983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
427083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
427183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These only allows a +/-90 degree rotation (by transpose) */
427283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* A 180 degree rotation is useless */
427383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case BlurKernel:
427483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 135.0 < angle && angle <= 225.0 )
427583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return;
427683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 225.0 < angle && angle <= 315.0 )
427783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        angle -= 180;
427883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
427983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42803dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    default:
428183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
428283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
428357fe7a498c1302232dac8466864e84b12fad0807anthony  /* Attempt rotations by 45 degrees  -- 3x3 kernels only */
42843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
42853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
42863c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 3 && kernel->height == 3 )
42873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a 3x3 square by 45 degree angle */
4288a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy          double t  = kernel->values[0];
428943c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[0] = kernel->values[3];
429043c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[3] = kernel->values[6];
429143c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[6] = kernel->values[7];
429243c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[7] = kernel->values[8];
429343c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[8] = kernel->values[5];
429443c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[5] = kernel->values[2];
429543c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[2] = kernel->values[1];
429643c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[1] = t;
42971d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate non-centered origin */
42981d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          if ( kernel->x != 1 || kernel->y != 1 ) {
4299bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            ssize_t x,y;
4300bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            x = (ssize_t) kernel->x-1;
4301bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            y = (ssize_t) kernel->y-1;
43021d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                 if ( x == y  ) x = 0;
43031d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == 0  ) x = -y;
43041d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == -y ) y = 0;
43051d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( y == 0  ) y = x;
4306ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) x+1;
4307ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) y+1;
43081d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
430943c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
431043c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+45.0, 360.0);
43113c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
43123c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
43133c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate non-3x3 kernel by 45 degrees");
43143c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
43153c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
43163c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
43173c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 1 || kernel->height == 1 )
43184c08aed51c5899665ade97263692328eea4af106cristy        { /* Do a transpose of a 1 dimensional kernel,
4319bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          ** which results in a fast 90 degree rotation of some type.
43203c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          */
4321bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          ssize_t
43223c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            t;
4323bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          t = (ssize_t) kernel->width;
43243c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->width = kernel->height;
4325bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->height = (size_t) t;
43263c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          t = kernel->x;
43273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->x = kernel->y;
43283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->y = t;
432943c4925e5305a26e48d68f7893e94f55d0831c39anthony          if ( kernel->width == 1 ) {
433043c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
433143c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+90.0, 360.0);
433243c4925e5305a26e48d68f7893e94f55d0831c39anthony          } else {
433343c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
433443c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+270.0, 360.0);
433543c4925e5305a26e48d68f7893e94f55d0831c39anthony          }
43363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
43373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else if ( kernel->width == kernel->height )
43383c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a square array of values by 90 degrees */
4339d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          { register ssize_t
43401d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              i,j,x,y;
4341d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
4342d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            register MagickRealType
43431d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              *k,t;
4344d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
43451d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            k=kernel->values;
4346d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            for( i=0, x=(ssize_t) kernel->width-1;  i<=x;   i++, x--)
4347d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              for( j=0, y=(ssize_t) kernel->height-1;  j<y;   j++, y--)
43481d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                { t                    = k[i+j*kernel->width];
43491d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[i+j*kernel->width] = k[j+x*kernel->width];
43501d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[j+x*kernel->width] = k[x+y*kernel->width];
43511d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[x+y*kernel->width] = k[y+i*kernel->width];
43521d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[y+i*kernel->width] = t;
43531d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                }
43541d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
43551d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate the origin - relative to center of array */
4356bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          { register ssize_t x,y;
4357eaedf06777741da32408da72c1e512975c600c48cristy            x = (ssize_t) (kernel->x*2-kernel->width+1);
4358eaedf06777741da32408da72c1e512975c600c48cristy            y = (ssize_t) (kernel->y*2-kernel->height+1);
4359ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4360ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
43611d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
436243c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
436343c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+90.0, 360.0);
43643c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
43653c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
43663c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
43673c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
436883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( 135.0 < angle && angle <= 225.0 )
436983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    {
437043c4925e5305a26e48d68f7893e94f55d0831c39anthony      /* For a 180 degree rotation - also know as a reflection
437143c4925e5305a26e48d68f7893e94f55d0831c39anthony       * This is actually a very very common operation!
437243c4925e5305a26e48d68f7893e94f55d0831c39anthony       * Basically all that is needed is a reversal of the kernel data!
437343c4925e5305a26e48d68f7893e94f55d0831c39anthony       * And a reflection of the origon
437443c4925e5305a26e48d68f7893e94f55d0831c39anthony       */
4375a23c649213358645994ad489b8c9a317c60111afcristy      MagickRealType
4376a96f2494a8e79144a225056be9545cc75e868137cristy        t;
4377a96f2494a8e79144a225056be9545cc75e868137cristy
4378d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register MagickRealType
4379a96f2494a8e79144a225056be9545cc75e868137cristy        *k;
4380a96f2494a8e79144a225056be9545cc75e868137cristy
4381a96f2494a8e79144a225056be9545cc75e868137cristy      ssize_t
4382a96f2494a8e79144a225056be9545cc75e868137cristy        i,
4383a96f2494a8e79144a225056be9545cc75e868137cristy        j;
438483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
438583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      k=kernel->values;
4386e42f658533644aecb733785ffd91b286d6778deacristy      j=(ssize_t) (kernel->width*kernel->height-1);
4387e42f658533644aecb733785ffd91b286d6778deacristy      for (i=0;  i < j;  i++, j--)
438883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        t=k[i],  k[i]=k[j],  k[j]=t;
438983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
4390bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
4391bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = (ssize_t) kernel->height - kernel->y - 1;
439243c4925e5305a26e48d68f7893e94f55d0831c39anthony      angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
439343c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->angle = fmod(kernel->angle+180.0, 360.0);
439483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    }
43953c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* At this point angle should at least between -45 (315) and +45 degrees
439683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * In the future some form of non-orthogonal angled rotates could be
439783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * performed here, posibily with a linear kernel restriction.
439883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   */
439983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
440083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  return;
440183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
440283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
440383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
440483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
440583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
440683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
440783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
440846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S c a l e G e o m e t r y K e r n e l I n f o                           %
440946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
441046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
441146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
441246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
441446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleGeometryKernelInfo() takes a geometry argument string, typically
441546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  provided as a  "-set option:convolve:scale {geometry}" user setting,
441646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  and modifies the kernel according to the parsed arguments of that setting.
441746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
441846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The first argument (and any normalization flags) are passed to
441946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
442046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  is then passed to UnityAddKernelInfo() to add a scled unity kernel
442146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  into the scaled/normalized kernel.
442246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4423ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%  The format of the ScaleGeometryKernelInfo method is:
442446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4425ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%      void ScaleGeometryKernelInfo(KernelInfo *kernel,
4426ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%        const double scaling_factor,const MagickStatusType normalize_flags)
442746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
442846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
442946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
443046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
443146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
443246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o geometry:
443346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             The geometry string to parse, typically from the user provided
443446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             "-set option:convolve:scale {geometry}" setting.
443546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
443646a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
443746a369d839971ab627bdb31a93d8bd63e81b65a3anthonyMagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
4438954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  const char *geometry)
443946a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
4440ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  MagickStatusType
444146a369d839971ab627bdb31a93d8bd63e81b65a3anthony    flags;
4442ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy
444346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  GeometryInfo
444446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args;
444546a369d839971ab627bdb31a93d8bd63e81b65a3anthony
444646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  SetGeometryInfo(&args);
444722de2722b682eb405b60ec6022a7546df994674eanthony  flags = ParseGeometry(geometry, &args);
444846a369d839971ab627bdb31a93d8bd63e81b65a3anthony
444946a369d839971ab627bdb31a93d8bd63e81b65a3anthony#if 0
445046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* For Debugging Geometry Input */
44515acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
445246a369d839971ab627bdb31a93d8bd63e81b65a3anthony       flags, args.rho, args.sigma, args.xi, args.psi );
445346a369d839971ab627bdb31a93d8bd63e81b65a3anthony#endif
445446a369d839971ab627bdb31a93d8bd63e81b65a3anthony
445546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
445646a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho *= 0.01,  args.sigma *= 0.01;
445746a369d839971ab627bdb31a93d8bd63e81b65a3anthony
445846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
445946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho = 1.0;
446046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) == 0 )
446146a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.sigma = 0.0;
446246a369d839971ab627bdb31a93d8bd63e81b65a3anthony
446346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Scale/Normalize the input kernel */
4464d228c03fa334bae897eee6c2d8721fa48e1577bacristy  ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
446546a369d839971ab627bdb31a93d8bd63e81b65a3anthony
446646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add Unity Kernel, for blending with original */
446746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) != 0 )
446846a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel, args.sigma);
446946a369d839971ab627bdb31a93d8bd63e81b65a3anthony
447046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
447146a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
447246a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
447346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
447546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
447646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
44776771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     S c a l e K e r n e l I n f o                                           %
4478cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4479cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4480cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4481cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4482cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
44831b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ScaleKernelInfo() scales the given kernel list by the given amount, with or
44841b2bc0a7da432e6e1cc0480280402df213faa940anthony%  without normalization of the sum of the kernel values (as per given flags).
4485999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4486999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  By default (no flags given) the values within the kernel is scaled
44871b2bc0a7da432e6e1cc0480280402df213faa940anthony%  directly using given scaling factor without change.
4488999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
448946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  If either of the two 'normalize_flags' are given the kernel will first be
449046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  normalized and then further scaled by the scaling factor value given.
4491999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4492999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  Kernel normalization ('normalize_flags' given) is designed to ensure that
4493999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
44941b2bc0a7da432e6e1cc0480280402df213faa940anthony%  morphology methods will fall into -1.0 to +1.0 range.  Note that for
44951b2bc0a7da432e6e1cc0480280402df213faa940anthony%  non-HDRI versions of IM this may cause images to have any negative results
44961b2bc0a7da432e6e1cc0480280402df213faa940anthony%  clipped, unless some 'bias' is used.
4497999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4498999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  More specifically.  Kernels which only contain positive values (such as a
4499999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
45001b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ensuring a 0.0 to +1.0 output range for non-HDRI images.
4501999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4502999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
4503999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  the kernel will be scaled by the absolute of the sum of kernel values, so
4504999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  that it will generally fall within the +/- 1.0 range.
4505cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4506999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
4507999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  will be scaled by just the sum of the postive values, so that its output
4508999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  range will again fall into the  +/- 1.0 range.
4509cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4510999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For special kernels designed for locating shapes using 'Correlate', (often
4511999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  only containing +1 and -1 values, representing foreground/brackground
4512999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  matching) a special normalization method is provided to scale the positive
45131e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  values separately to those of the negative values, so the kernel will be
4514999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  forced to become a zero-sum kernel better suited to such searches.
4515999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
45161b2bc0a7da432e6e1cc0480280402df213faa940anthony%  WARNING: Correct normalization of the kernel assumes that the '*_range'
4517999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  attributes within the kernel structure have been correctly set during the
4518999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  kernels creation.
4519999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4520999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  NOTE: The values used for 'normalize_flags' have been selected specifically
452146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  to match the use of geometry options, so that '!' means NormalizeValue, '^'
452246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
4523cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
45244fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the ScaleKernelInfo method is:
4525cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4526999bb2c20aa9d42875bb5adba44951988d4ae354anthony%      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
4527999bb2c20aa9d42875bb5adba44951988d4ae354anthony%               const MagickStatusType normalize_flags )
4528cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4529cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4530cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4531cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4532cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4533999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o scaling_factor:
4534999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             multiply all values (after normalization) by this factor if not
4535999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             zero.  If the kernel is normalized regardless of any flags.
4536999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4537999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o normalize_flags:
4538999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             GeometryFlags defining normalization method to use.
4539999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             specifically: NormalizeValue, CorrelateNormalizeValue,
4540999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                           and/or PercentValue
4541cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4542cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
45436771f1e8987fa49f52d4176281a2e8524b8e31cbcristyMagickExport void ScaleKernelInfo(KernelInfo *kernel,
45446771f1e8987fa49f52d4176281a2e8524b8e31cbcristy  const double scaling_factor,const GeometryFlags normalize_flags)
4545cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4546999bb2c20aa9d42875bb5adba44951988d4ae354anthony  register double
4547999bb2c20aa9d42875bb5adba44951988d4ae354anthony    pos_scale,
4548999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale;
4549999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4550954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  register ssize_t
4551954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy    i;
4552954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy
455346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
45541b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
45551b2bc0a7da432e6e1cc0480280402df213faa940anthony    ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
45561b2bc0a7da432e6e1cc0480280402df213faa940anthony
455746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Normalization of Kernel */
4558999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = 1.0;
4559999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&NormalizeValue) != 0 ) {
4560b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4561f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* non-zero-summing kernel (generally positive) */
4562999bb2c20aa9d42875bb5adba44951988d4ae354anthony      pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4563cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    else
4564f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* zero-summing kernel */
4565f4e0031305baeb01c89cfd2842cbbec021883550anthony      pos_scale = kernel->positive_range;
4566999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
456746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Force kernel into a normalized zero-summing kernel */
4568999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4569b978e458a8e1f210bcb580951cf623687236b2fecristy    pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4570999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? kernel->positive_range : 1.0;
4571b978e458a8e1f210bcb580951cf623687236b2fecristy    neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4572999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? -kernel->negative_range : 1.0;
4573999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4574999bb2c20aa9d42875bb5adba44951988d4ae354anthony  else
4575999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale = pos_scale;
4576999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4577999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* finialize scaling_factor for positive and negative components */
4578999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = scaling_factor/pos_scale;
4579999bb2c20aa9d42875bb5adba44951988d4ae354anthony  neg_scale = scaling_factor/neg_scale;
4580cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4581bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
45823667479b817685c0289edd0fd597d07bc2c6b462dirk    if (!IsNaN(kernel->values[i]))
4583999bb2c20aa9d42875bb5adba44951988d4ae354anthony      kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4584999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4585999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* convolution output range */
4586999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->positive_range *= pos_scale;
4587999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->negative_range *= neg_scale;
4588999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* maximum and minimum values in kernel */
4589999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4590999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4591999bb2c20aa9d42875bb5adba44951988d4ae354anthony
459246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* swap kernel settings if user's scaling factor is negative */
4593999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( scaling_factor < MagickEpsilon ) {
4594999bb2c20aa9d42875bb5adba44951988d4ae354anthony    double t;
4595999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->positive_range;
4596999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->positive_range = kernel->negative_range;
4597999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->negative_range = t;
4598999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->maximum;
4599999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->maximum = kernel->minimum;
4600999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->minimum = 1;
4601999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4602cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4603cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4604cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4605cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4606cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4607cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4608cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4609cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4610cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
461146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S h o w K e r n e l I n f o                                             %
461283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
461383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
461483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
461583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
46174fd27e21043be809d66c8202e779255e5b660d2danthony%  ShowKernelInfo() outputs the details of the given kernel defination to
4618972c2e42a2171341bbf55a48cb36882616a591b1dirk%  standard error, generally due to a users 'morphology:showkernel' option
4619972c2e42a2171341bbf55a48cb36882616a591b1dirk%  request.
462083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
462183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the ShowKernel method is:
462283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
462357fe7a498c1302232dac8466864e84b12fad0807anthony%      void ShowKernelInfo(const KernelInfo *kernel)
462483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
462583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
462683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
462783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
462883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
462983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
4630433d11887841b922ec5e6805f9fdd240c320b92ecristyMagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
463183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
463257fe7a498c1302232dac8466864e84b12fad0807anthony  const KernelInfo
46337a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *k;
46347a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4635bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
46367a01dcf50ce12cb2a789bedff51e9345f022432eanthony    c, i, u, v;
46377a01dcf50ce12cb2a789bedff51e9345f022432eanthony
46387a01dcf50ce12cb2a789bedff51e9345f022432eanthony  for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
46397a01dcf50ce12cb2a789bedff51e9345f022432eanthony
46405acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Kernel");
46417a01dcf50ce12cb2a789bedff51e9345f022432eanthony    if ( kernel->next != (KernelInfo *) NULL )
46425acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
46435acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, " \"%s",
4644042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy          CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4645b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(k->angle) >= MagickEpsilon )
46465acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "@%lg", k->angle);
46475acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
46481e604812fad85bb96f757a2393015ae3d061c39acristy      k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
46495acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr,
46507a01dcf50ce12cb2a789bedff51e9345f022432eanthony          " with values from %.*lg to %.*lg\n",
46517a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->minimum,
46527a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->maximum);
46535acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Forming a output range from %.*lg to %.*lg",
46547a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->negative_range,
465546a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range);
465646a369d839971ab627bdb31a93d8bd63e81b65a3anthony    if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
46575acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Zero-Summing)\n");
465846a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
46595acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Normalized)\n");
466046a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else
46615acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Sum %.*lg)\n",
466246a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range+k->negative_range);
466343c4925e5305a26e48d68f7893e94f55d0831c39anthony    for (i=v=0; v < k->height; v++) {
46645acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
466543c4925e5305a26e48d68f7893e94f55d0831c39anthony      for (u=0; u < k->width; u++, i++)
46663667479b817685c0289edd0fd597d07bc2c6b462dirk        if (IsNaN(k->values[i]))
46675acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
46687a01dcf50ce12cb2a789bedff51e9345f022432eanthony        else
46695acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
4670d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              GetMagickPrecision(), (double) k->values[i]);
46715acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr,"\n");
46727a01dcf50ce12cb2a789bedff51e9345f022432eanthony    }
467383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
467483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
4675cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4676cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4677cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4678cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4679cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4680cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
468143c4925e5305a26e48d68f7893e94f55d0831c39anthony%     U n i t y A d d K e r n a l I n f o                                     %
468243c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
468343c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
468443c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
468543c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
468643c4925e5305a26e48d68f7893e94f55d0831c39anthony%
468743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
468843c4925e5305a26e48d68f7893e94f55d0831c39anthony%  to the given pre-scaled and normalized Kernel.  This in effect adds that
468943c4925e5305a26e48d68f7893e94f55d0831c39anthony%  amount of the original image into the resulting convolution kernel.  This
469043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  value is usually provided by the user as a percentage value in the
469143c4925e5305a26e48d68f7893e94f55d0831c39anthony%  'convolve:scale' setting.
469243c4925e5305a26e48d68f7893e94f55d0831c39anthony%
4693501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  The resulting effect is to convert the defined kernels into blended
4694501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  soft-blurs, unsharp kernels or into sharpening kernels.
469543c4925e5305a26e48d68f7893e94f55d0831c39anthony%
469646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the UnityAdditionKernelInfo method is:
469743c4925e5305a26e48d68f7893e94f55d0831c39anthony%
469843c4925e5305a26e48d68f7893e94f55d0831c39anthony%      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
469943c4925e5305a26e48d68f7893e94f55d0831c39anthony%
470043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  A description of each parameter follows:
470143c4925e5305a26e48d68f7893e94f55d0831c39anthony%
470243c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o kernel: the Morphology/Convolution kernel
470343c4925e5305a26e48d68f7893e94f55d0831c39anthony%
470443c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o scale:
470543c4925e5305a26e48d68f7893e94f55d0831c39anthony%             scaling factor for the unity kernel to be added to
470643c4925e5305a26e48d68f7893e94f55d0831c39anthony%             the given kernel.
470743c4925e5305a26e48d68f7893e94f55d0831c39anthony%
470843c4925e5305a26e48d68f7893e94f55d0831c39anthony*/
470943c4925e5305a26e48d68f7893e94f55d0831c39anthonyMagickExport void UnityAddKernelInfo(KernelInfo *kernel,
471043c4925e5305a26e48d68f7893e94f55d0831c39anthony  const double scale)
471143c4925e5305a26e48d68f7893e94f55d0831c39anthony{
471246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
471346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( kernel->next != (KernelInfo *) NULL)
471446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel->next, scale);
471543c4925e5305a26e48d68f7893e94f55d0831c39anthony
471646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add the scaled unity kernel to the existing kernel */
471743c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->values[kernel->x+kernel->y*kernel->width] += scale;
471846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(kernel);  /* recalculate the meta-data */
471943c4925e5305a26e48d68f7893e94f55d0831c39anthony
472043c4925e5305a26e48d68f7893e94f55d0831c39anthony  return;
472143c4925e5305a26e48d68f7893e94f55d0831c39anthony}
472243c4925e5305a26e48d68f7893e94f55d0831c39anthony
472343c4925e5305a26e48d68f7893e94f55d0831c39anthony/*
472443c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472543c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
472643c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
472743c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
472843c4925e5305a26e48d68f7893e94f55d0831c39anthony%     Z e r o K e r n e l N a n s                                             %
4729cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4730cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4731cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4732cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4733cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4734cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  ZeroKernelNans() replaces any special 'nan' value that may be present in
4735cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  the kernel with a zero value.  This is typically done when the kernel will
4736cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  be used in special hardware (GPU) convolution processors, to simply
4737cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  matters.
4738cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4739cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  The format of the ZeroKernelNans method is:
4740cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
474146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void ZeroKernelNans (KernelInfo *kernel)
4742cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4743cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4744cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4745cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4746cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4747cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
4748cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate void ZeroKernelNans(KernelInfo *kernel)
4749cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4750bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
4751cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4752cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
475346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
47543667479b817685c0289edd0fd597d07bc2c6b462dirk  if (kernel->next != (KernelInfo *) NULL)
47551b2bc0a7da432e6e1cc0480280402df213faa940anthony    ZeroKernelNans(kernel->next);
47561b2bc0a7da432e6e1cc0480280402df213faa940anthony
475743c4925e5305a26e48d68f7893e94f55d0831c39anthony  for (i=0; i < (kernel->width*kernel->height); i++)
47583667479b817685c0289edd0fd597d07bc2c6b462dirk    if (IsNaN(kernel->values[i]))
47593667479b817685c0289edd0fd597d07bc2c6b462dirk      kernel->values[i]=0.0;
4760cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4761cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4762cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4763