morphology.c revision 09347642526e7e61646b7c549e410d21790e5e50
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%                                                                             %
2045ef08fd6a09813e4a8f5ddadf85ba9e0ec2cdc7cristy%  Copyright 1999-2014 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%
361b2bc0a7da432e6e1cc0480280402df213faa940anthony% Morpology is the the application of various kernels, of any size and even
37602ab9b30b644a78a4057da93d838a77391ec0acanthony% shape, to a 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"
554c08aed51c5899665ade97263692328eea4af106cristy#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"
608ea81224e9ff022e56eb2cddb12860a8b2e90411cristy#include "MagickCore/gem.h"
614c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/gem-private.h"
624c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/hashmap.h"
634c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image.h"
644c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image-private.h"
654c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/list.h"
664c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/magick.h"
67e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy#include "MagickCore/memory_.h"
684c08aed51c5899665ade97263692328eea4af106cristy#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"
73f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy#include "MagickCore/pixel-accessor.h"
744c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel-private.h"
754c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/prepress.h"
76ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy#include "MagickCore/quantize.h"
774c08aed51c5899665ade97263692328eea4af106cristy#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"
8316881e68c6165c6191fc44151a8a4320e3dd1ffdcristy#include "MagickCore/string-private.h"
844c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/thread-private.h"
854c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/token.h"
86d1dd6e4fefa0810b9893e6ac9418f79c97c1b39acristy#include "MagickCore/utility.h"
87a29d45f897949f04a47bb3da077395969f13dcbacristy#include "MagickCore/utility-private.h"
88c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony
89a29d45f897949f04a47bb3da077395969f13dcbacristy/*
90a29d45f897949f04a47bb3da077395969f13dcbacristy  Other global definitions used by module.
9129188a8682a98d4b7882cca434b170517555fc7danthony*/
9229188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMin(const double x,const double y)
9329188a8682a98d4b7882cca434b170517555fc7danthony{
9429188a8682a98d4b7882cca434b170517555fc7danthony  return( x < y ? x : y);
9529188a8682a98d4b7882cca434b170517555fc7danthony}
9629188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMax(const double x,const double y)
9729188a8682a98d4b7882cca434b170517555fc7danthony{
9829188a8682a98d4b7882cca434b170517555fc7danthony  return( x > y ? x : y);
9929188a8682a98d4b7882cca434b170517555fc7danthony}
10029188a8682a98d4b7882cca434b170517555fc7danthony#define Minimize(assign,value) assign=MagickMin(assign,value)
10129188a8682a98d4b7882cca434b170517555fc7danthony#define Maximize(assign,value) assign=MagickMax(assign,value)
10240ca0b982379d4ab2716435a46603d56b5b218b1anthony
10340ca0b982379d4ab2716435a46603d56b5b218b1anthony/* Integer Factorial Function - for a Binomial kernel */
10440ca0b982379d4ab2716435a46603d56b5b218b1anthony#if 1
10540ca0b982379d4ab2716435a46603d56b5b218b1anthonystatic inline size_t fact(size_t n)
106f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy{
10740ca0b982379d4ab2716435a46603d56b5b218b1anthony  size_t f,l;
10840ca0b982379d4ab2716435a46603d56b5b218b1anthony  for(f=1, l=2; l <= n; f=f*l, l++);
10940ca0b982379d4ab2716435a46603d56b5b218b1anthony  return(f);
11040ca0b982379d4ab2716435a46603d56b5b218b1anthony}
11140ca0b982379d4ab2716435a46603d56b5b218b1anthony#elif 1 /* glibc floating point alternatives */
11240ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)tgamma((double)n+1))
11340ca0b982379d4ab2716435a46603d56b5b218b1anthony#else
11440ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)lgamma((double)n+1))
11540ca0b982379d4ab2716435a46603d56b5b218b1anthony#endif
11640ca0b982379d4ab2716435a46603d56b5b218b1anthony
117c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthony
118c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthony/* Currently these are only internal to this module */
11946a369d839971ab627bdb31a93d8bd63e81b65a3anthonystatic void
120bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  CalcKernelMetaData(KernelInfo *),
121bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandMirrorKernelInfo(KernelInfo *),
122ef656913b0b30d713ae94c82c47693c9dc69c9f4cristy  ExpandRotateKernelInfo(KernelInfo *, const double),
123602ab9b30b644a78a4057da93d838a77391ec0acanthony  RotateKernelInfo(KernelInfo *, double);
1243dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
1253dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
1263dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony/* Quick function to find last kernel in a kernel list */
1273dd0f620e7a1d12f747ce167844cd7269bfa9f12anthonystatic inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
1283dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony{
1293dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  while (kernel->next != (KernelInfo *) NULL)
1303dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    kernel=kernel->next;
1313dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  return(kernel);
1323dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony}
133602ab9b30b644a78a4057da93d838a77391ec0acanthony
134602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
135602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
137602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
13883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
139602ab9b30b644a78a4057da93d838a77391ec0acanthony%     A c q u i r e K e r n e l I n f o                                       %
140602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
141602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
142602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
143602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1442be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%
145602ab9b30b644a78a4057da93d838a77391ec0acanthony%  AcquireKernelInfo() takes the given string (generally supplied by the
146602ab9b30b644a78a4057da93d838a77391ec0acanthony%  user) and converts it into a Morphology/Convolution Kernel.  This allows
147602ab9b30b644a78a4057da93d838a77391ec0acanthony%  users to specify a kernel from a number of pre-defined kernels, or to fully
148602ab9b30b644a78a4057da93d838a77391ec0acanthony%  specify their own kernel for a specific Convolution or Morphology
149602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Operation.
150602ab9b30b644a78a4057da93d838a77391ec0acanthony%
151602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The kernel so generated can be any rectangular array of floating point
152602ab9b30b644a78a4057da93d838a77391ec0acanthony%  values (doubles) with the 'control point' or 'pixel being affected'
153602ab9b30b644a78a4057da93d838a77391ec0acanthony%  anywhere within that array of values.
15483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
15519910ef25dd3d99d1981a9e42c934133170ee714anthony%  Previously IM was restricted to a square of odd size using the exact
15683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  center as origin, this is no longer the case, and any rectangular kernel
15783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  with any value being declared the origin. This in turn allows the use of
158602ab9b30b644a78a4057da93d838a77391ec0acanthony%  highly asymmetrical kernels.
159602ab9b30b644a78a4057da93d838a77391ec0acanthony%
16083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The floating point values in the kernel can also include a special value
16183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  known as 'nan' or 'not a number' to indicate that this value is not part
16283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  of the kernel array. This allows you to shaped the kernel within its
16383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  rectangular area. That is 'nan' values provide a 'mask' for the kernel
16483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  shape.  However at least one non-nan value must be provided for correct
165602ab9b30b644a78a4057da93d838a77391ec0acanthony%  working of a kernel.
1667a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
1677a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  The returned kernel should be freed using the DestroyKernelInfo() when you
168602ab9b30b644a78a4057da93d838a77391ec0acanthony%  are finished with it.  Do not free this memory yourself.
169602ab9b30b644a78a4057da93d838a77391ec0acanthony%
170602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Input kernel defintion strings can consist of any of three types.
171bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
17229188a8682a98d4b7882cca434b170517555fc7danthony%    "name:args[[@><]"
17329188a8682a98d4b7882cca434b170517555fc7danthony%         Select from one of the built in kernels, using the name and
174602ab9b30b644a78a4057da93d838a77391ec0acanthony%         geometry arguments supplied.  See AcquireKernelBuiltIn()
175bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
1761b2bc0a7da432e6e1cc0480280402df213faa940anthony%    "WxH[+X+Y][@><]:num, num, num ..."
177602ab9b30b644a78a4057da93d838a77391ec0acanthony%         a kernel of size W by H, with W*H floating point numbers following.
17829188a8682a98d4b7882cca434b170517555fc7danthony%         the 'center' can be optionally be defined at +X+Y (such that +0+0
17929188a8682a98d4b7882cca434b170517555fc7danthony%         is top left corner). If not defined the pixel in the center, for
18029188a8682a98d4b7882cca434b170517555fc7danthony%         odd sizes, or to the immediate top or left of center for even sizes
181602ab9b30b644a78a4057da93d838a77391ec0acanthony%         is automatically selected.
18229188a8682a98d4b7882cca434b170517555fc7danthony%
18329188a8682a98d4b7882cca434b170517555fc7danthony%    "num, num, num, num, ..."
18429188a8682a98d4b7882cca434b170517555fc7danthony%         list of floating point numbers defining an 'old style' odd sized
18529188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel.  At least 9 values should be provided for a 3x3
18629188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
187602ab9b30b644a78a4057da93d838a77391ec0acanthony%         Values can be space or comma separated.  This is not recommended.
1887a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
1891e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  You can define a 'list of kernels' which can be used by some morphology
1907a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  operators A list is defined as a semi-colon separated list kernels.
191dbc8989a61339951c6434d9a43e7b6fefb5da374anthony%
1927a01dcf50ce12cb2a789bedff51e9345f022432eanthony%     " kernel ; kernel ; kernel ; "
1931dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%
19443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  Any extra ';' characters, at start, end or between kernel defintions are
19543c4925e5305a26e48d68f7893e94f55d0831c39anthony%  simply ignored.
196bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
197bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The special flags will expand a single kernel, into a list of rotated
198bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
199bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
200bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The '<' also exands using 90-degree rotates, but giving a 180-degree
201bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  reflected kernel before the +/- 90-degree rotations, which can be important
202bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  for Thinning operations.
20343c4925e5305a26e48d68f7893e94f55d0831c39anthony%
20443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  Note that 'name' kernels will start with an alphabetic character while the
20543c4925e5305a26e48d68f7893e94f55d0831c39anthony%  new kernel specification has a ':' character in its specification string.
20643c4925e5305a26e48d68f7893e94f55d0831c39anthony%  If neither is the case, it is assumed an old style of a simple list of
2077a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  numbers generating a odd-sized square kernel has been given.
208602ab9b30b644a78a4057da93d838a77391ec0acanthony%
209602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernal method is:
2102be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%
211602ab9b30b644a78a4057da93d838a77391ec0acanthony%      KernelInfo *AcquireKernelInfo(const char *kernel_string)
212602ab9b30b644a78a4057da93d838a77391ec0acanthony%
213602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
214602ab9b30b644a78a4057da93d838a77391ec0acanthony%
215602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel_string: the Morphology/Convolution kernel wanted.
216602ab9b30b644a78a4057da93d838a77391ec0acanthony%
217602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
218c84dce50867229e4872193e8eed5dbab58eb9f02anthony
2195ef8e94ff55717be2387d537bd49025780a1a558anthony/* This was separated so that it could be used as a separate
220c84dce50867229e4872193e8eed5dbab58eb9f02anthony** array input handling function, such as for -color-matrix
2215ef8e94ff55717be2387d537bd49025780a1a558anthony*/
222602ab9b30b644a78a4057da93d838a77391ec0acanthonystatic KernelInfo *ParseKernelArray(const char *kernel_string)
2232be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy{
224602ab9b30b644a78a4057da93d838a77391ec0acanthony  KernelInfo
225602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
226602ab9b30b644a78a4057da93d838a77391ec0acanthony
227602ab9b30b644a78a4057da93d838a77391ec0acanthony  char
228602ab9b30b644a78a4057da93d838a77391ec0acanthony    token[MaxTextExtent];
229602ab9b30b644a78a4057da93d838a77391ec0acanthony
2305ef8e94ff55717be2387d537bd49025780a1a558anthony  const char
2315ef8e94ff55717be2387d537bd49025780a1a558anthony    *p,
232602ab9b30b644a78a4057da93d838a77391ec0acanthony    *end;
233bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy
234c84dce50867229e4872193e8eed5dbab58eb9f02anthony  register ssize_t
235602ab9b30b644a78a4057da93d838a77391ec0acanthony    i;
23629188a8682a98d4b7882cca434b170517555fc7danthony
23729188a8682a98d4b7882cca434b170517555fc7danthony  double
23829188a8682a98d4b7882cca434b170517555fc7danthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
23943c4925e5305a26e48d68f7893e94f55d0831c39anthony
24043c4925e5305a26e48d68f7893e94f55d0831c39anthony  MagickStatusType
24143c4925e5305a26e48d68f7893e94f55d0831c39anthony    flags;
24243c4925e5305a26e48d68f7893e94f55d0831c39anthony
24343c4925e5305a26e48d68f7893e94f55d0831c39anthony  GeometryInfo
24443c4925e5305a26e48d68f7893e94f55d0831c39anthony    args;
245a64b85d7873d5e540fe6e2941aa98ec7653a4e2dcristy
2462be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
247602ab9b30b644a78a4057da93d838a77391ec0acanthony  if (kernel == (KernelInfo *)NULL)
248602ab9b30b644a78a4057da93d838a77391ec0acanthony    return(kernel);
24943c4925e5305a26e48d68f7893e94f55d0831c39anthony  (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
2507a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->minimum = kernel->maximum = kernel->angle = 0.0;
251602ab9b30b644a78a4057da93d838a77391ec0acanthony  kernel->negative_range = kernel->positive_range = 0.0;
2527a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->type = UserDefinedKernel;
253d43a46bc9598004091eae232bc7938e009b494a1cristy  kernel->next = (KernelInfo *) NULL;
2545e6be1e6a77c230e4a204fa9163d873104730c35cristy  kernel->signature = MagickSignature;
2555e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
256602ab9b30b644a78a4057da93d838a77391ec0acanthony    return(kernel);
2575ef8e94ff55717be2387d537bd49025780a1a558anthony
2585ef8e94ff55717be2387d537bd49025780a1a558anthony  /* find end of this specific kernel definition string */
2595ef8e94ff55717be2387d537bd49025780a1a558anthony  end = strchr(kernel_string, ';');
2605ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( end == (char *) NULL )
2615ef8e94ff55717be2387d537bd49025780a1a558anthony    end = strchr(kernel_string, '\0');
262a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
26343c4925e5305a26e48d68f7893e94f55d0831c39anthony  /* clear flags - for Expanding kernel lists thorugh rotations */
26443c4925e5305a26e48d68f7893e94f55d0831c39anthony   flags = NoValue;
265f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony
266f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony  /* Has a ':' in argument - New user kernel specification
267f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony     FUTURE: this split on ':' could be done by StringToken()
268602ab9b30b644a78a4057da93d838a77391ec0acanthony   */
2695ef8e94ff55717be2387d537bd49025780a1a558anthony  p = strchr(kernel_string, ':');
270602ab9b30b644a78a4057da93d838a77391ec0acanthony  if ( p != (char *) NULL && p < end)
271602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
272150989ed67ef9da53141a65e5f3ebdb05dd025abcristy      /* ParseGeometry() needs the geometry separated! -- Arrgghh */
273602ab9b30b644a78a4057da93d838a77391ec0acanthony      memcpy(token, kernel_string, (size_t) (p-kernel_string));
274c84dce50867229e4872193e8eed5dbab58eb9f02anthony      token[p-kernel_string] = '\0';
275602ab9b30b644a78a4057da93d838a77391ec0acanthony      SetGeometryInfo(&args);
276602ab9b30b644a78a4057da93d838a77391ec0acanthony      flags = ParseGeometry(token, &args);
27729188a8682a98d4b7882cca434b170517555fc7danthony
278602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* Size handling and checks of geometry settings */
279602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
280602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.rho = args.sigma;         /* then  width = height */
281602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.rho < 1.0 )            /* if width too small */
282602ab9b30b644a78a4057da93d838a77391ec0acanthony         args.rho = 1.0;               /* then  width = 1 */
283602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.sigma < 1.0 )          /* if height too small */
284bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        args.sigma = args.rho;         /* then  height = width */
285bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = (size_t)args.rho;
286602ab9b30b644a78a4057da93d838a77391ec0acanthony      kernel->height = (size_t)args.sigma;
287602ab9b30b644a78a4057da93d838a77391ec0acanthony
288602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* Offset Handling and Checks */
28983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( args.xi  < 0.0 || args.psi < 0.0 )
290bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        return(DestroyKernelInfo(kernel));
291ea068a53d23d6dca08f1bce44c8937d54f83b983anthony      kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
292bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                                        : (ssize_t) (kernel->width-1)/2;
293ea068a53d23d6dca08f1bce44c8937d54f83b983anthony      kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
294bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                                        : (ssize_t) (kernel->height-1)/2;
295bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      if ( kernel->x >= (ssize_t) kernel->width ||
29683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony           kernel->y >= (ssize_t) kernel->height )
297602ab9b30b644a78a4057da93d838a77391ec0acanthony        return(DestroyKernelInfo(kernel));
298602ab9b30b644a78a4057da93d838a77391ec0acanthony
299602ab9b30b644a78a4057da93d838a77391ec0acanthony      p++; /* advance beyond the ':' */
300602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
301c84dce50867229e4872193e8eed5dbab58eb9f02anthony  else
302602ab9b30b644a78a4057da93d838a77391ec0acanthony    { /* ELSE - Old old specification, forming odd-square kernel */
303602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* count up number of values given */
304a699b171eff7e0178463e8f271b35a3cbb995f0ecristy      p=(const char *) kernel_string;
30529188a8682a98d4b7882cca434b170517555fc7danthony      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
3065ef8e94ff55717be2387d537bd49025780a1a558anthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
307602ab9b30b644a78a4057da93d838a77391ec0acanthony      for (i=0; p < end; i++)
308602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
309602ab9b30b644a78a4057da93d838a77391ec0acanthony        GetMagickToken(p,&p,token);
310602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (*token == ',')
311602ab9b30b644a78a4057da93d838a77391ec0acanthony          GetMagickToken(p,&p,token);
312602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
313bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      /* set the size of the kernel - old sized square */
314bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
315602ab9b30b644a78a4057da93d838a77391ec0acanthony      kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
31629188a8682a98d4b7882cca434b170517555fc7danthony      p=(const char *) kernel_string;
31729188a8682a98d4b7882cca434b170517555fc7danthony      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
318602ab9b30b644a78a4057da93d838a77391ec0acanthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
319602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
320602ab9b30b644a78a4057da93d838a77391ec0acanthony
321e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  /* Read in the kernel values from rest of input string argument */
322e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
323d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy    kernel->width,kernel->height*sizeof(*kernel->values)));
32483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if (kernel->values == (MagickRealType *) NULL)
325c99304fe3c8d9c617da792b40b57c118bb1249afcristy    return(DestroyKernelInfo(kernel));
326c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->minimum=MagickMaximumValue;
327c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->maximum=(-MagickMaximumValue);
328bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  kernel->negative_range = kernel->positive_range = 0.0;
329602ab9b30b644a78a4057da93d838a77391ec0acanthony  for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
330602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
331602ab9b30b644a78a4057da93d838a77391ec0acanthony    GetMagickToken(p,&p,token);
332602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (*token == ',')
33329188a8682a98d4b7882cca434b170517555fc7danthony      GetMagickToken(p,&p,token);
334c84dce50867229e4872193e8eed5dbab58eb9f02anthony    if (    LocaleCompare("nan",token) == 0
335ea068a53d23d6dca08f1bce44c8937d54f83b983anthony        || LocaleCompare("-",token) == 0 ) {
33629188a8682a98d4b7882cca434b170517555fc7danthony      kernel->values[i] = nan; /* this value is not part of neighbourhood */
33729188a8682a98d4b7882cca434b170517555fc7danthony    }
3380ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony    else {
33929188a8682a98d4b7882cca434b170517555fc7danthony      kernel->values[i] = StringToDouble(token,(char **) NULL);
340c99304fe3c8d9c617da792b40b57c118bb1249afcristy      ( kernel->values[i] < 0)
341c99304fe3c8d9c617da792b40b57c118bb1249afcristy          ?  ( kernel->negative_range += kernel->values[i] )
342c99304fe3c8d9c617da792b40b57c118bb1249afcristy          :  ( kernel->positive_range += kernel->values[i] );
343c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Minimize(kernel->minimum, kernel->values[i]);
34429188a8682a98d4b7882cca434b170517555fc7danthony      Maximize(kernel->maximum, kernel->values[i]);
34529188a8682a98d4b7882cca434b170517555fc7danthony    }
34629188a8682a98d4b7882cca434b170517555fc7danthony  }
3475ef8e94ff55717be2387d537bd49025780a1a558anthony
3485ef8e94ff55717be2387d537bd49025780a1a558anthony  /* sanity check -- no more values in kernel definition */
3495ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(p,&p,token);
3505ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( *token != '\0' && *token != ';' && *token != '\'' )
3515ef8e94ff55717be2387d537bd49025780a1a558anthony    return(DestroyKernelInfo(kernel));
352c84dce50867229e4872193e8eed5dbab58eb9f02anthony
353c84dce50867229e4872193e8eed5dbab58eb9f02anthony#if 0
354bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  /* this was the old method of handling a incomplete kernel */
355c99304fe3c8d9c617da792b40b57c118bb1249afcristy  if ( i < (ssize_t) (kernel->width*kernel->height) ) {
356c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Minimize(kernel->minimum, kernel->values[i]);
357bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    Maximize(kernel->maximum, kernel->values[i]);
35829188a8682a98d4b7882cca434b170517555fc7danthony    for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
359602ab9b30b644a78a4057da93d838a77391ec0acanthony      kernel->values[i]=0.0;
360c84dce50867229e4872193e8eed5dbab58eb9f02anthony  }
361c84dce50867229e4872193e8eed5dbab58eb9f02anthony#else
362bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  /* Number of values for kernel was not enough - Report Error */
363c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( i < (ssize_t) (kernel->width*kernel->height) )
364c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
365c84dce50867229e4872193e8eed5dbab58eb9f02anthony#endif
366c84dce50867229e4872193e8eed5dbab58eb9f02anthony
367c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* check that we recieved at least one real (non-nan) value! */
368c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if (kernel->minimum == MagickMaximumValue)
369602ab9b30b644a78a4057da93d838a77391ec0acanthony    return(DestroyKernelInfo(kernel));
37043c4925e5305a26e48d68f7893e94f55d0831c39anthony
371bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
372bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
373bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
374bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
375bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
37643c4925e5305a26e48d68f7893e94f55d0831c39anthony    ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
377602ab9b30b644a78a4057da93d838a77391ec0acanthony
378602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
379c84dce50867229e4872193e8eed5dbab58eb9f02anthony}
38043c4925e5305a26e48d68f7893e94f55d0831c39anthony
381c84dce50867229e4872193e8eed5dbab58eb9f02anthonystatic KernelInfo *ParseKernelName(const char *kernel_string)
382c84dce50867229e4872193e8eed5dbab58eb9f02anthony{
383c84dce50867229e4872193e8eed5dbab58eb9f02anthony  char
384c84dce50867229e4872193e8eed5dbab58eb9f02anthony    token[MaxTextExtent];
385c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3867a01dcf50ce12cb2a789bedff51e9345f022432eanthony  const char
3877a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *p,
388c84dce50867229e4872193e8eed5dbab58eb9f02anthony    *end;
3899d314ff2c17a77996c05413c2013880387e50f0ecristy
3909d314ff2c17a77996c05413c2013880387e50f0ecristy  GeometryInfo
3919d314ff2c17a77996c05413c2013880387e50f0ecristy    args;
3929d314ff2c17a77996c05413c2013880387e50f0ecristy
3939d314ff2c17a77996c05413c2013880387e50f0ecristy  KernelInfo
3949d314ff2c17a77996c05413c2013880387e50f0ecristy    *kernel;
395c84dce50867229e4872193e8eed5dbab58eb9f02anthony
396c84dce50867229e4872193e8eed5dbab58eb9f02anthony  MagickStatusType
397c84dce50867229e4872193e8eed5dbab58eb9f02anthony    flags;
3989d314ff2c17a77996c05413c2013880387e50f0ecristy
3999d314ff2c17a77996c05413c2013880387e50f0ecristy  ssize_t
400c84dce50867229e4872193e8eed5dbab58eb9f02anthony    type;
401c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4025ef8e94ff55717be2387d537bd49025780a1a558anthony  /* Parse special 'named' kernel */
403042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy  GetMagickToken(kernel_string,&p,token);
404c84dce50867229e4872193e8eed5dbab58eb9f02anthony  type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
4055ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( type < 0 || type == UserDefinedKernel )
406c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return((KernelInfo *)NULL);  /* not a valid named kernel */
407c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4085ef8e94ff55717be2387d537bd49025780a1a558anthony  while (((isspace((int) ((unsigned char) *p)) != 0) ||
409c84dce50867229e4872193e8eed5dbab58eb9f02anthony          (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
4107a01dcf50ce12cb2a789bedff51e9345f022432eanthony    p++;
4117a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4127a01dcf50ce12cb2a789bedff51e9345f022432eanthony  end = strchr(p, ';'); /* end of this kernel defintion */
4137a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( end == (char *) NULL )
4147a01dcf50ce12cb2a789bedff51e9345f022432eanthony    end = strchr(p, '\0');
4157a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4167a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* ParseGeometry() needs the geometry separated! -- Arrgghh */
4177a01dcf50ce12cb2a789bedff51e9345f022432eanthony  memcpy(token, p, (size_t) (end-p));
418c84dce50867229e4872193e8eed5dbab58eb9f02anthony  token[end-p] = '\0';
4197a01dcf50ce12cb2a789bedff51e9345f022432eanthony  SetGeometryInfo(&args);
420c84dce50867229e4872193e8eed5dbab58eb9f02anthony  flags = ParseGeometry(token, &args);
4213c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
4223c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#if 0
4235acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  /* For Debugging Geometry Input */
4241e604812fad85bb96f757a2393015ae3d061c39acristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4253c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    flags, args.rho, args.sigma, args.xi, args.psi );
4263c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#endif
427c84dce50867229e4872193e8eed5dbab58eb9f02anthony
428c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* special handling of missing values in input string */
429a9892d898acb81e1ec73106d892855fdc5a69427anthony  switch( type ) {
430529482f4b494010a13338a74446c510712f670b3anthony    /* Shape Kernel Defaults */
431529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
432a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & WidthValue) == 0 )
4335ef8e94ff55717be2387d537bd49025780a1a558anthony        args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
4345ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4355ef8e94ff55717be2387d537bd49025780a1a558anthony    case SquareKernel:
4361ef941fea2534a0d20ba7d71307d35040247decbanthony    case DiamondKernel:
4375ef8e94ff55717be2387d537bd49025780a1a558anthony    case OctagonKernel:
4385ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiskKernel:
4393dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case PlusKernel:
4405ef8e94ff55717be2387d537bd49025780a1a558anthony    case CrossKernel:
441a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & HeightValue) == 0 )
4425ef8e94ff55717be2387d537bd49025780a1a558anthony        args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
443c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
444c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
445a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & XValue) == 0 )
446c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
447a9892d898acb81e1ec73106d892855fdc5a69427anthony      break;
448a9892d898acb81e1ec73106d892855fdc5a69427anthony    case RectangleKernel:    /* Rectangle - set size defaults */
449a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
450a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = args.sigma;         /* then  width = height */
451a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.rho < 1.0 )            /* if width too small */
452a9892d898acb81e1ec73106d892855fdc5a69427anthony          args.rho = 3;                /* then  width = 3 */
453a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.sigma < 1.0 )          /* if height too small */
454a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = args.rho;         /* then  height = width */
455a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & XValue) == 0 )     /* center offset if not defined */
456a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = (double)(((ssize_t)args.rho-1)/2);
457a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & YValue) == 0 )
458a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.psi = (double)(((ssize_t)args.sigma-1)/2);
459a9892d898acb81e1ec73106d892855fdc5a69427anthony      break;
4605ef8e94ff55717be2387d537bd49025780a1a558anthony    /* Distance Kernel Defaults */
461bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ChebyshevKernel:
4621ef941fea2534a0d20ba7d71307d35040247decbanthony    case ManhattanKernel:
4635ef8e94ff55717be2387d537bd49025780a1a558anthony    case OctagonalKernel:
46443c4925e5305a26e48d68f7893e94f55d0831c39anthony    case EuclideanKernel:
46543c4925e5305a26e48d68f7893e94f55d0831c39anthony      if ( (flags & HeightValue) == 0 )           /* no distance scale */
46643c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = 100.0;                       /* default distance scaling */
46743c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
46843c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
46943c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
4705ef8e94ff55717be2387d537bd49025780a1a558anthony        args.sigma *= QuantumRange/100.0;         /* percentage of color range */
4715ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4725ef8e94ff55717be2387d537bd49025780a1a558anthony    default:
473c84dce50867229e4872193e8eed5dbab58eb9f02anthony      break;
474c84dce50867229e4872193e8eed5dbab58eb9f02anthony  }
475f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
476529482f4b494010a13338a74446c510712f670b3anthony  kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
477529482f4b494010a13338a74446c510712f670b3anthony  if ( kernel == (KernelInfo *) NULL )
478f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony    return(kernel);
479f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
480f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  /* global expand to rotated kernel list - only for single kernels */
481f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  if ( kernel->next == (KernelInfo *) NULL ) {
482bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
483bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 45.0);
484bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
485bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 90.0);
486bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
487f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony      ExpandMirrorKernelInfo(kernel);
488f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  }
489f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
490c84dce50867229e4872193e8eed5dbab58eb9f02anthony  return(kernel);
491c84dce50867229e4872193e8eed5dbab58eb9f02anthony}
4925ef8e94ff55717be2387d537bd49025780a1a558anthony
4935ef8e94ff55717be2387d537bd49025780a1a558anthonyMagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
4947a01dcf50ce12cb2a789bedff51e9345f022432eanthony{
4957a01dcf50ce12cb2a789bedff51e9345f022432eanthony  KernelInfo
496dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *kernel,
49743c4925e5305a26e48d68f7893e94f55d0831c39anthony    *new_kernel;
4987a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4995ef8e94ff55717be2387d537bd49025780a1a558anthony  char
5005ef8e94ff55717be2387d537bd49025780a1a558anthony    token[MaxTextExtent];
5015ef8e94ff55717be2387d537bd49025780a1a558anthony
5027a01dcf50ce12cb2a789bedff51e9345f022432eanthony  const char
503dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *p;
5047a01dcf50ce12cb2a789bedff51e9345f022432eanthony
505bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if (kernel_string == (const char *) NULL)
506e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony    return(ParseKernelArray(kernel_string));
507e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony  p=kernel_string;
5085e6be1e6a77c230e4a204fa9163d873104730c35cristy  kernel=NULL;
5095e6be1e6a77c230e4a204fa9163d873104730c35cristy
510dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  while (GetMagickToken(p,NULL,token), *token != '\0')
51143c4925e5305a26e48d68f7893e94f55d0831c39anthony  {
512e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony    /* ignore extra or multiple ';' kernel separators */
5137a01dcf50ce12cb2a789bedff51e9345f022432eanthony    if (*token != ';')
514dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      {
5157a01dcf50ce12cb2a789bedff51e9345f022432eanthony        /* tokens starting with alpha is a Named kernel */
5161e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp        if (isalpha((int) ((unsigned char) *token)) != 0)
517dbc8989a61339951c6434d9a43e7b6fefb5da374anthony          new_kernel=ParseKernelName(p);
5187a01dcf50ce12cb2a789bedff51e9345f022432eanthony        else /* otherwise a user defined kernel array */
519dbc8989a61339951c6434d9a43e7b6fefb5da374anthony          new_kernel=ParseKernelArray(p);
52043c4925e5305a26e48d68f7893e94f55d0831c39anthony
52143c4925e5305a26e48d68f7893e94f55d0831c39anthony        /* Error handling -- this is not proper error handling! */
522dbc8989a61339951c6434d9a43e7b6fefb5da374anthony        if (new_kernel == (KernelInfo *) NULL)
52343c4925e5305a26e48d68f7893e94f55d0831c39anthony          {
524dbc8989a61339951c6434d9a43e7b6fefb5da374anthony            if (kernel != (KernelInfo *) NULL)
525e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony              kernel=DestroyKernelInfo(kernel);
526e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony            return((KernelInfo *) NULL);
52775920b23a8a3883792cbb69d569c33cc789cf1b5cristy          }
5281e604812fad85bb96f757a2393015ae3d061c39acristy
529e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        /* initialise or append the kernel list */
530e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        if (kernel == (KernelInfo *) NULL)
531e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony          kernel=new_kernel;
532dbc8989a61339951c6434d9a43e7b6fefb5da374anthony        else
533e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony          LastKernelInfo(kernel)->next=new_kernel;
534e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      }
5353dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
5363dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    /* look for the next kernel in list */
5373dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    p=strchr(p,';');
53843c4925e5305a26e48d68f7893e94f55d0831c39anthony    if (p == (char *) NULL)
539dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      break;
540dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p++;
541dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  }
542dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  return(kernel);
543dbc8989a61339951c6434d9a43e7b6fefb5da374anthony}
544dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
545dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
5465ef8e94ff55717be2387d537bd49025780a1a558anthony/*
547dbc8989a61339951c6434d9a43e7b6fefb5da374anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5487a01dcf50ce12cb2a789bedff51e9345f022432eanthony%                                                                             %
5495ef8e94ff55717be2387d537bd49025780a1a558anthony%                                                                             %
5505ef8e94ff55717be2387d537bd49025780a1a558anthony%                                                                             %
551602ab9b30b644a78a4057da93d838a77391ec0acanthony%     A c q u i r e K e r n e l B u i l t I n                                 %
552602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
553602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
554602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
555602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
556602ab9b30b644a78a4057da93d838a77391ec0acanthony%
557602ab9b30b644a78a4057da93d838a77391ec0acanthony%  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
558602ab9b30b644a78a4057da93d838a77391ec0acanthony%  kernels used for special purposes such as gaussian blurring, skeleton
559602ab9b30b644a78a4057da93d838a77391ec0acanthony%  pruning, and edge distance determination.
560602ab9b30b644a78a4057da93d838a77391ec0acanthony%
561602ab9b30b644a78a4057da93d838a77391ec0acanthony%  They take a KernelType, and a set of geometry style arguments, which were
562602ab9b30b644a78a4057da93d838a77391ec0acanthony%  typically decoded from a user supplied string, or from a more complex
563602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Morphology Method that was requested.
564602ab9b30b644a78a4057da93d838a77391ec0acanthony%
565602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernalBuiltIn method is:
566602ab9b30b644a78a4057da93d838a77391ec0acanthony%
567602ab9b30b644a78a4057da93d838a77391ec0acanthony%      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
568602ab9b30b644a78a4057da93d838a77391ec0acanthony%           const GeometryInfo args)
569602ab9b30b644a78a4057da93d838a77391ec0acanthony%
570602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
571602ab9b30b644a78a4057da93d838a77391ec0acanthony%
572602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o type: the pre-defined type of kernel wanted
5732be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%
574602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o args: arguments defining or modifying the kernel
575602ab9b30b644a78a4057da93d838a77391ec0acanthony%
576602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Convolution Kernels
577602ab9b30b644a78a4057da93d838a77391ec0acanthony%
578602ab9b30b644a78a4057da93d838a77391ec0acanthony%    Unity
579602ab9b30b644a78a4057da93d838a77391ec0acanthony%       The a No-Op or Scaling single element kernel.
580602ab9b30b644a78a4057da93d838a77391ec0acanthony%
581602ab9b30b644a78a4057da93d838a77391ec0acanthony%    Gaussian:{radius},{sigma}
582602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Generate a two-dimensional gaussian kernel, as used by -gaussian.
583602ab9b30b644a78a4057da93d838a77391ec0acanthony%       The sigma for the curve is required.  The resulting kernel is
58446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%       normalized,
585529482f4b494010a13338a74446c510712f670b3anthony%
58646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
5873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
5882489f53a1153c2b619b1c9a6744602e8840bd9a9glennrp%       NOTE: that the 'radius' is optional, but if provided can limit (clip)
589c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       the final size of the resulting kernel to a square 2*radius+1 in size.
590c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The radius should be at least 2 times that of the sigma value, or
591c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       sever clipping and aliasing may result.  If not given or set to 0 the
592c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       radius will be determined so as to produce the best minimal error
593602ab9b30b644a78a4057da93d838a77391ec0acanthony%       result, which is usally much larger than is normally needed.
594602ab9b30b644a78a4057da93d838a77391ec0acanthony%
595602ab9b30b644a78a4057da93d838a77391ec0acanthony%    LoG:{radius},{sigma}
596602ab9b30b644a78a4057da93d838a77391ec0acanthony%        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
597602ab9b30b644a78a4057da93d838a77391ec0acanthony%        The supposed ideal edge detection, zero-summing kernel.
598602ab9b30b644a78a4057da93d838a77391ec0acanthony%
599602ab9b30b644a78a4057da93d838a77391ec0acanthony%        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
600602ab9b30b644a78a4057da93d838a77391ec0acanthony%        approx 1.6 (according to wikipedia).
601501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
602501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    DoG:{radius},{sigma1},{sigma2}
603501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        "Difference of Gaussians" Kernel.
604501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
605501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
606501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        The result is a zero-summing kernel.
607501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
608501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    Blur:{radius},{sigma}[,{angle}]
609c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Generates a 1 dimensional or linear gaussian blur, at the angle given
610c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       (current restricted to orthogonal angles).  If a 'radius' is given the
611c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
612c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       by a 90 degree angle.
613c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
614c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
6154c08aed51c5899665ade97263692328eea4af106cristy%
616c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Note that two convolutions with two "Blur" kernels perpendicular to
617c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       each other, is equivalent to a far larger "Gaussian" kernel with the
618c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       same sigma value, However it is much faster to apply. This is how the
619c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       "-blur" operator actually works.
620c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
621c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Comet:{width},{sigma},{angle}
622c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Blur in one direction only, much like how a bright object leaves
623f0a92fd8deb68d411304359906b12679b675691fglennrp%       a comet like trail.  The Kernel is actually half a gaussian curve,
624c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Adding two such blurs in opposite directions produces a Blur Kernel.
625c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Angle can be rotated in multiples of 90 degrees.
626c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
6273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Note that the first argument is the width of the kernel and not the
6283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       radius of the kernel.
629602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6303c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Binomial:[{radius}]
6313c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Generate a discrete kernel using a 2 dimentional Pascel's Triangle
632602ab9b30b644a78a4057da93d838a77391ec0acanthony%       of values. Used for special forma of image filters.
6333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
634602ab9b30b644a78a4057da93d838a77391ec0acanthony%    # Still to be implemented...
635602ab9b30b644a78a4057da93d838a77391ec0acanthony%    #
63640ca0b982379d4ab2716435a46603d56b5b218b1anthony%    # Filter2D
63740ca0b982379d4ab2716435a46603d56b5b218b1anthony%    # Filter1D
638eef684ff80c7d5aca1493f4755426c88b3d3accdanthony%    #    Set kernel values using a resize filter, and given scale (sigma)
63940ca0b982379d4ab2716435a46603d56b5b218b1anthony%    #    Cylindrical or Linear.   Is this possible with an image?
640602ab9b30b644a78a4057da93d838a77391ec0acanthony%    #
641602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6424fd27e21043be809d66c8202e779255e5b660d2danthony%  Named Constant Convolution Kernels
6434fd27e21043be809d66c8202e779255e5b660d2danthony%
6444fd27e21043be809d66c8202e779255e5b660d2danthony%  All these are unscaled, zero-summing kernels by default. As such for
645dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  non-HDRI version of ImageMagick some form of normalization, user scaling,
6464fd27e21043be809d66c8202e779255e5b660d2danthony%  and biasing the results is recommended, to prevent the resulting image
647602ab9b30b644a78a4057da93d838a77391ec0acanthony%  being 'clipped'.
6483c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
6493c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  The 3x3 kernels (most of these) can be circularly rotated in multiples of
650c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  45 degrees to generate the 8 angled varients of each of the kernels.
651c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
652c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Laplacian:{type}
653c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%      Discrete Lapacian Kernels, (without normalization)
654c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
655c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
656c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 2 :  3x3 with center:4 edge:1 corner:-2
6573c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%        Type 3 :  3x3 with center:4 edge:-2 corner:1
6583c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%        Type 5 :  5x5 laplacian
65943c4925e5305a26e48d68f7893e94f55d0831c39anthony%        Type 7 :  7x7 laplacian
660c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 15 : 5x5 LoG (sigma approx 1.4)
661c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 19 : 9x9 LoG (sigma approx 1.4)
6629eb4f74649b23c053b308ce1152dce51239450baanthony%
6639eb4f74649b23c053b308ce1152dce51239450baanthony%    Sobel:{angle}
6649eb4f74649b23c053b308ce1152dce51239450baanthony%      Sobel 'Edge' convolution kernel (3x3)
6659eb4f74649b23c053b308ce1152dce51239450baanthony%          | -1, 0, 1 |
666501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          | -2, 0,-2 |
667501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          | -1, 0, 1 |
668c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
669c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Roberts:{angle}
67046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Roberts convolution kernel (3x3)
671c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
672c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 0 |
673c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
674c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
675c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Prewitt:{angle}
67646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Prewitt Edge convolution kernel (3x3)
677c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
678c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
679c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
680c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
681c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Compass:{angle}
682c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%      Prewitt's "Compass" convolution kernel (3x3)
683c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
684c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1,-2, 1 |
685c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
686c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6879eb4f74649b23c053b308ce1152dce51239450baanthony%    Kirsch:{angle}
6889eb4f74649b23c053b308ce1152dce51239450baanthony%      Kirsch's "Compass" convolution kernel (3x3)
689c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
690c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3, 0, 5 |
691c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
692c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6939eb4f74649b23c053b308ce1152dce51239450baanthony%    FreiChen:{angle}
6949eb4f74649b23c053b308ce1152dce51239450baanthony%      Frei-Chen Edge Detector is based on a kernel that is similar to
695c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      the Sobel Kernel, but is designed to be isotropic. That is it takes
696c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      into account the distance of the diagonal in the kernel.
697c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6983c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%          |   1,     0,   -1     |
699c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) |
7001d5e67090dc7232b35bfcc71b31266c20838defcanthony%          |   1,     0,   -1     |
7011d5e67090dc7232b35bfcc71b31266c20838defcanthony%
7021d5e67090dc7232b35bfcc71b31266c20838defcanthony%    FreiChen:{type},{angle}
703c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
704c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      Frei-Chen Pre-weighted kernels...
705c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
706c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 0:  default un-nomalized version shown above.
707c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
708c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 1: Orthogonal Kernel (same as type 11 below)
709c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
710c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
711c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
712c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
713c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 2: Diagonal form of Kernel...
714c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     sqrt(2),    0     |
715c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
716c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   0,    -sqrt(2)    -1     |
717c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
718c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      However this kernel is als at the heart of the FreiChen Edge Detection
719c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      Process which uses a set of 9 specially weighted kernel.  These 9
720c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      kernels not be normalized, but directly applied to the image. The
721c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      results is then added together, to produce the intensity of an edge in
722c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      a specific direction.  The square root of the pixel value can then be
723c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
7241d5e67090dc7232b35bfcc71b31266c20838defcanthony%      from each other, both the direction and the strength of the edge can be
7251d5e67090dc7232b35bfcc71b31266c20838defcanthony%      determined.
7261d5e67090dc7232b35bfcc71b31266c20838defcanthony%
7271d5e67090dc7232b35bfcc71b31266c20838defcanthony%        Type 10: All 9 of the following pre-weighted kernels...
7281d5e67090dc7232b35bfcc71b31266c20838defcanthony%
7291d5e67090dc7232b35bfcc71b31266c20838defcanthony%        Type 11: |   1,     0,   -1     |
7301d5e67090dc7232b35bfcc71b31266c20838defcanthony%                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
7311d5e67090dc7232b35bfcc71b31266c20838defcanthony%                 |   1,     0,   -1     |
7321d5e67090dc7232b35bfcc71b31266c20838defcanthony%
733c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 12: | 1, sqrt(2), 1 |
734c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0,   0,     0 | / 2*sqrt(2)
735c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, sqrt(2), 1 |
736c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
737c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 13: | sqrt(2), -1,    0     |
738e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%                 |  -1,      0,    1     | / 2*sqrt(2)
739c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   0,      1, -sqrt(2) |
740c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
741c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 14: |    0,     1, -sqrt(2) |
742e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%                 |   -1,     0,     1    | / 2*sqrt(2)
743c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), -1,     0    |
744c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
745c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 15: | 0, -1, 0 |
746e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%                 | 1,  0, 1 | / 2
747c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0, -1, 0 |
748c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
749c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 16: |  1, 0, -1 |
750e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%                 |  0, 0,  0 | / 2
751c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, 0,  1 |
752c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
753c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 17: |  1, -2,  1 |
754e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%                 | -2,  4, -2 | / 6
755c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, -2,  1 |
756c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
757c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 18: | -2, 1, -2 |
758e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%                 |  1, 4,  1 | / 6
759c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2, 1, -2 |
760c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
761c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 19: | 1, 1, 1 |
762501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%                 | 1, 1, 1 | / 3
763c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 |
764c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
765c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      The first 4 are for edge detection, the next 4 are for line detection
766e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      and the last is to add a average component to the results.
767c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
768c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      Using a special type of '-1' will return all 9 pre-weighted kernels
769c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      as a multi-kernel list, so that you can use them directly (without
770e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      normalization) with the special "-set option:morphology:compose Plus"
771e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      setting to apply the full FreiChen Edge Detection Technique.
772e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
773e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      If 'type' is large it will be taken to be an actual rotation angle for
774c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
775c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
776c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
777c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      WARNING: The above was layed out as per
778c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
7791dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      But rotated 90 degrees so direction is from left rather than the top.
7801dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      I have yet to find any secondary confirmation of the above. The only
7811dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      other source found was actual source code at
7821dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
783501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      Neigher paper defineds the kernels in a way that looks locical or
784501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      correct when taken as a whole.
785501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
786501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  Boolean Kernels
787501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
788501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    Diamond:[{radius}[,{scale}]]
789501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%       Generate a diamond shaped kernel with given radius to the points.
790501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%       Kernel size will again be radius*2+1 square and defaults to radius 1,
791e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%       generating a 3x3 kernel that is slightly larger than a square.
792602ab9b30b644a78a4057da93d838a77391ec0acanthony%
793602ab9b30b644a78a4057da93d838a77391ec0acanthony%    Square:[{radius}[,{scale}]]
7943c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Generate a square shaped kernel of size radius*2+1, and defaulting
7951b2bc0a7da432e6e1cc0480280402df213faa940anthony%       to a 3x3 (radius 1).
796602ab9b30b644a78a4057da93d838a77391ec0acanthony%
797602ab9b30b644a78a4057da93d838a77391ec0acanthony%    Octagon:[{radius}[,{scale}]]
798602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Generate octagonal shaped kernel of given radius and constant scale.
7993c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
800602ab9b30b644a78a4057da93d838a77391ec0acanthony%       in "Diamond" kernel.
801602ab9b30b644a78a4057da93d838a77391ec0acanthony%
802602ab9b30b644a78a4057da93d838a77391ec0acanthony%    Disk:[{radius}[,{scale}]]
8031ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate a binary disk, thresholded at the radius given, the radius
8041ef941fea2534a0d20ba7d71307d35040247decbanthony%       may be a float-point value. Final Kernel size is floor(radius)*2+1
8050bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       square. A radius of 5.3 is the default.
8061ef941fea2534a0d20ba7d71307d35040247decbanthony%
8071ef941fea2534a0d20ba7d71307d35040247decbanthony%       NOTE: That a low radii Disk kernels produce the same results as
8083c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       many of the previously defined kernels, but differ greatly at larger
8091ef941fea2534a0d20ba7d71307d35040247decbanthony%       radii.  Here is a table of equivalences...
8101ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
8110bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%          "Disk:1.5"  => "Square"
8121ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2"    => "Diamond:2"
8131ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.5"  => "Octagon"
8141ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.9"  => "Square:2"
8151ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:3.5"  => "Octagon:3"
8161ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:4.5"  => "Octagon:4"
8171ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:5.4"  => "Octagon:5"
8181ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:6.4"  => "Octagon:6"
8191ef941fea2534a0d20ba7d71307d35040247decbanthony%       All other Disk shapes are unique to this kernel, but because a "Disk"
8201ef941fea2534a0d20ba7d71307d35040247decbanthony%       is more circular when using a larger radius, using a larger radius is
8210bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       preferred over iterating the morphological operation.
8221ef941fea2534a0d20ba7d71307d35040247decbanthony%
8231ef941fea2534a0d20ba7d71307d35040247decbanthony%    Rectangle:{geometry}
8241ef941fea2534a0d20ba7d71307d35040247decbanthony%       Simply generate a rectangle of 1's with the size given. You can also
8251ef941fea2534a0d20ba7d71307d35040247decbanthony%       specify the location of the 'control point', otherwise the closest
8261ef941fea2534a0d20ba7d71307d35040247decbanthony%       pixel to the center of the rectangle is selected.
8271ef941fea2534a0d20ba7d71307d35040247decbanthony%
828602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Properly centered and odd sized rectangles work the best.
829a9892d898acb81e1ec73106d892855fdc5a69427anthony%
830a9892d898acb81e1ec73106d892855fdc5a69427anthony%  Symbol Dilation Kernels
831a9892d898acb81e1ec73106d892855fdc5a69427anthony%
832a9892d898acb81e1ec73106d892855fdc5a69427anthony%    These kernel is not a good general morphological kernel, but is used
833a9892d898acb81e1ec73106d892855fdc5a69427anthony%    more for highlighting and marking any single pixels in an image using,
834a9892d898acb81e1ec73106d892855fdc5a69427anthony%    a "Dilate" method as appropriate.
835a9892d898acb81e1ec73106d892855fdc5a69427anthony%
836c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    For the same reasons iterating these kernels does not produce the
837c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    same result as using a larger radius for the symbol.
838c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
839c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Plus:[{radius}[,{scale}]]
840c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Cross:[{radius}[,{scale}]]
841c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Generate a kernel in the shape of a 'plus' or a 'cross' with
842c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       a each arm the length of the given radius (default 2).
843c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
844c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
8453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
8463dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Ring:{radius1},{radius2}[,{scale}]
847c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       A ring of the values given that falls between the two radii.
848c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
8493dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       This is the 'edge' pixels of the default "Disk" kernel,
850f0a92fd8deb68d411304359906b12679b675691fglennrp%       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
851602ab9b30b644a78a4057da93d838a77391ec0acanthony%
852c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  Hit and Miss Kernels
853c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
854c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Peak:radius1,radius2
855c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Find any peak larger than the pixels the fall between the two radii.
856c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The default ring of pixels is as per "Ring".
8573dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Edges
8583dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Find flat orthogonal edges of a binary shape
859602ab9b30b644a78a4057da93d838a77391ec0acanthony%    Corners
8603dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Find 90 degree corners of a binary shape
861c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Diagonals:type
862c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       A special kernel to thin the 'outside' of diagonals
86343c4925e5305a26e48d68f7893e94f55d0831c39anthony%    LineEnds:type
864694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find end points of lines (for pruning a skeletion)
8653dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Two types of lines ends (default to both) can be searched for
866694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: All line ends
867529482f4b494010a13338a74446c510712f670b3anthony%         Type 1: single kernel for 4-conneected line ends
868529482f4b494010a13338a74446c510712f670b3anthony%         Type 2: single kernel for simple line ends
869694934fa79dd310f727588b1d0a7481fa6170f1danthony%    LineJunctions
8703dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Find three line junctions (within a skeletion)
871694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: all line junctions
872694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Y Junction kernel
873694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Diagonal T Junction kernel
874694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 3: Orthogonal T Junction kernel
8753dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%         Type 4: Diagonal X Junction kernel
87643c4925e5305a26e48d68f7893e94f55d0831c39anthony%         Type 5: Orthogonal + Junction kernel
877694934fa79dd310f727588b1d0a7481fa6170f1danthony%    Ridges:type
878694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find single pixel ridges or thin lines
879694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Fine single pixel thick lines and ridges
880694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Find two pixel thick lines and ridges
881694934fa79dd310f727588b1d0a7481fa6170f1danthony%    ConvexHull
882694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
883694934fa79dd310f727588b1d0a7481fa6170f1danthony%    Skeleton:type
884694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Traditional skeleton generating kernels.
885694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Tradional Skeleton kernel (4 connected skeleton)
886694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
8873dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%         Type 3: Thinning skeleton based on a ressearch paper by
888a9892d898acb81e1ec73106d892855fdc5a69427anthony%                 Dan S. Bloomberg (Default Type)
889c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    ThinSE:type
890c40ac1e79923a1516075ba1197ae4ed90244af9banthony%       A huge variety of Thinning Kernels designed to preserve conectivity.
891694934fa79dd310f727588b1d0a7481fa6170f1danthony%       many other kernel sets use these kernels as source definitions.
892694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Type numbers are 41-49, 81-89, 481, and 482 which are based on
893e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       the super and sub notations used in the source research paper.
8942b2290b46c246ce1f14cb78f1695394e4c4a3ddfanthony%
895e816a586a13717bab2d6839ced6e5c3828a37f19anthony%  Distance Measuring Kernels
896e816a586a13717bab2d6839ced6e5c3828a37f19anthony%
897e816a586a13717bab2d6839ced6e5c3828a37f19anthony%    Different types of distance measuring methods, which are used with the
898e816a586a13717bab2d6839ced6e5c3828a37f19anthony%    a 'Distance' morphology method for generating a gradient based on
899e816a586a13717bab2d6839ced6e5c3828a37f19anthony%    distance from an edge of a binary shape, though there is a technique
900602ab9b30b644a78a4057da93d838a77391ec0acanthony%    for handling a anti-aliased shape.
901602ab9b30b644a78a4057da93d838a77391ec0acanthony%
902602ab9b30b644a78a4057da93d838a77391ec0acanthony%    See the 'Distance' Morphological Method, for information of how it is
903c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    applied.
904c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
905c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Chebyshev:[{radius}][x{scale}[%!]]
906c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
907602ab9b30b644a78a4057da93d838a77391ec0acanthony%       is a value of one to any neighbour, orthogonal or diagonal. One why
908c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       of thinking of it is the number of squares a 'King' or 'Queen' in
909c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       chess needs to traverse reach any other position on a chess board.
910602ab9b30b644a78a4057da93d838a77391ec0acanthony%       It results in a 'square' like distance function, but one where
911c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       diagonals are given a value that is closer than expected.
9121ef941fea2534a0d20ba7d71307d35040247decbanthony%
9131ef941fea2534a0d20ba7d71307d35040247decbanthony%    Manhattan:[{radius}][x{scale}[%!]]
9141ef941fea2534a0d20ba7d71307d35040247decbanthony%       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
9151ef941fea2534a0d20ba7d71307d35040247decbanthony%       Cab distance metric), it is the distance needed when you can only
9161ef941fea2534a0d20ba7d71307d35040247decbanthony%       travel in horizontal or vertical directions only.  It is the
9171ef941fea2534a0d20ba7d71307d35040247decbanthony%       distance a 'Rook' in chess would have to travel, and results in a
918c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       diamond like distances, where diagonals are further than expected.
919bee715c4c0fd9efe6e21d8627ae8664434df7750anthony%
9201ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagonal:[{radius}][x{scale}[%!]]
9211ef941fea2534a0d20ba7d71307d35040247decbanthony%       An interleving of Manhatten and Chebyshev metrics producing an
9221ef941fea2534a0d20ba7d71307d35040247decbanthony%       increasing octagonally shaped distance.  Distances matches those of
9231ef941fea2534a0d20ba7d71307d35040247decbanthony%       the "Octagon" shaped kernel of the same radius.  The minimum radius
9241ef941fea2534a0d20ba7d71307d35040247decbanthony%       and default is 2, producing a 5x5 kernel.
9251ef941fea2534a0d20ba7d71307d35040247decbanthony%
9261ef941fea2534a0d20ba7d71307d35040247decbanthony%    Euclidean:[{radius}][x{scale}[%!]]
9271ef941fea2534a0d20ba7d71307d35040247decbanthony%       Euclidean distance is the 'direct' or 'as the crow flys' distance.
9281ef941fea2534a0d20ba7d71307d35040247decbanthony%       However by default the kernel size only has a radius of 1, which
9291ef941fea2534a0d20ba7d71307d35040247decbanthony%       limits the distance to 'Knight' like moves, with only orthogonal and
9301ef941fea2534a0d20ba7d71307d35040247decbanthony%       diagonal measurements being correct.  As such for the default kernel
931c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       you will get octagonal like distance function.
932c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
9331ef941fea2534a0d20ba7d71307d35040247decbanthony%       However using a larger radius such as "Euclidean:4" you will get a
934c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       much smoother distance gradient from the edge of the shape. Especially
935c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       if the image is pre-processed to include any anti-aliasing pixels.
936c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       Of course a larger kernel is slower to use, and not always needed.
9371ef941fea2534a0d20ba7d71307d35040247decbanthony%
9381ef941fea2534a0d20ba7d71307d35040247decbanthony%    The first three Distance Measuring Kernels will only generate distances
9391ef941fea2534a0d20ba7d71307d35040247decbanthony%    of exact multiples of {scale} in binary images. As such you can use a
9401ef941fea2534a0d20ba7d71307d35040247decbanthony%    scale of 1 without loosing any information.  However you also need some
9411ef941fea2534a0d20ba7d71307d35040247decbanthony%    scaling when handling non-binary anti-aliased shapes.
9421ef941fea2534a0d20ba7d71307d35040247decbanthony%
9431ef941fea2534a0d20ba7d71307d35040247decbanthony%    The "Euclidean" Distance Kernel however does generate a non-integer
9441ef941fea2534a0d20ba7d71307d35040247decbanthony%    fractional results, and as such scaling is vital even for binary shapes.
9451ef941fea2534a0d20ba7d71307d35040247decbanthony%
9461ef941fea2534a0d20ba7d71307d35040247decbanthony*/
9471ef941fea2534a0d20ba7d71307d35040247decbanthony
9481ef941fea2534a0d20ba7d71307d35040247decbanthonyMagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
9491ef941fea2534a0d20ba7d71307d35040247decbanthony   const GeometryInfo *args)
9501ef941fea2534a0d20ba7d71307d35040247decbanthony{
951c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony  KernelInfo
952602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
953602ab9b30b644a78a4057da93d838a77391ec0acanthony
9542be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  register ssize_t
955602ab9b30b644a78a4057da93d838a77391ec0acanthony    i;
956602ab9b30b644a78a4057da93d838a77391ec0acanthony
9572be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  register ssize_t
958602ab9b30b644a78a4057da93d838a77391ec0acanthony    u,
959602ab9b30b644a78a4057da93d838a77391ec0acanthony    v;
960bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy
961602ab9b30b644a78a4057da93d838a77391ec0acanthony  double
962602ab9b30b644a78a4057da93d838a77391ec0acanthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
963bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy
964602ab9b30b644a78a4057da93d838a77391ec0acanthony  /* Generate a new empty kernel if needed */
965602ab9b30b644a78a4057da93d838a77391ec0acanthony  kernel=(KernelInfo *) NULL;
966602ab9b30b644a78a4057da93d838a77391ec0acanthony  switch(type) {
967602ab9b30b644a78a4057da93d838a77391ec0acanthony    case UndefinedKernel:    /* These should not call this function */
968602ab9b30b644a78a4057da93d838a77391ec0acanthony    case UserDefinedKernel:
969602ab9b30b644a78a4057da93d838a77391ec0acanthony      assert("Should not call this function" != (char *)NULL);
970c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
971e96405a0f45f803fb9c26f75e7bdee252437febbcristy    case LaplacianKernel:   /* Named Descrete Convolution Kernels */
972c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SobelKernel:       /* these are defined using other kernels */
9731dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case RobertsKernel:
9749eb4f74649b23c053b308ce1152dce51239450baanthony    case PrewittKernel:
975529482f4b494010a13338a74446c510712f670b3anthony    case CompassKernel:
9769eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
977529482f4b494010a13338a74446c510712f670b3anthony    case FreiChenKernel:
978529482f4b494010a13338a74446c510712f670b3anthony    case EdgesKernel:       /* Hit and Miss kernels */
9799eb4f74649b23c053b308ce1152dce51239450baanthony    case CornersKernel:
9809eb4f74649b23c053b308ce1152dce51239450baanthony    case DiagonalsKernel:
9819eb4f74649b23c053b308ce1152dce51239450baanthony    case LineEndsKernel:
9829eb4f74649b23c053b308ce1152dce51239450baanthony    case LineJunctionsKernel:
9831dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case RidgesKernel:
984694934fa79dd310f727588b1d0a7481fa6170f1danthony    case ConvexHullKernel:
985694934fa79dd310f727588b1d0a7481fa6170f1danthony    case SkeletonKernel:
986529482f4b494010a13338a74446c510712f670b3anthony    case ThinSEKernel:
9879eb4f74649b23c053b308ce1152dce51239450baanthony      break;               /* A pre-generated kernel is not needed */
9889eb4f74649b23c053b308ce1152dce51239450baanthony#if 0
9891dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    /* set to 1 to do a compile-time check that we haven't missed anything */
9909eb4f74649b23c053b308ce1152dce51239450baanthony    case UnityKernel:
9919eb4f74649b23c053b308ce1152dce51239450baanthony    case GaussianKernel:
9929a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony    case DoGKernel:
993c40ac1e79923a1516075ba1197ae4ed90244af9banthony    case LoGKernel:
994c40ac1e79923a1516075ba1197ae4ed90244af9banthony    case BlurKernel:
995c40ac1e79923a1516075ba1197ae4ed90244af9banthony    case CometKernel:
996529482f4b494010a13338a74446c510712f670b3anthony    case BinomialKernel:
997c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
998501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case SquareKernel:
999501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case RectangleKernel:
1000c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case OctagonKernel:
1001c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiskKernel:
100240ca0b982379d4ab2716435a46603d56b5b218b1anthony    case PlusKernel:
1003c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CrossKernel:
1004c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
1005c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PeaksKernel:
10061ef941fea2534a0d20ba7d71307d35040247decbanthony    case ChebyshevKernel:
1007c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case ManhattanKernel:
1008c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case OctangonalKernel:
1009c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case EuclideanKernel:
1010c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony#else
1011c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    default:
1012c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony#endif
1013bee715c4c0fd9efe6e21d8627ae8664434df7750anthony      /* Generate the base Kernel Structure */
10141ef941fea2534a0d20ba7d71307d35040247decbanthony      kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1015c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if (kernel == (KernelInfo *) NULL)
10161dd091ae3bc17edc26c16cc47f436a24bd48412aanthony        return(kernel);
10179eb4f74649b23c053b308ce1152dce51239450baanthony      (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
10181dd091ae3bc17edc26c16cc47f436a24bd48412aanthony      kernel->minimum = kernel->maximum = kernel->angle = 0.0;
10199eb4f74649b23c053b308ce1152dce51239450baanthony      kernel->negative_range = kernel->positive_range = 0.0;
1020c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->type = type;
1021c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->next = (KernelInfo *) NULL;
1022c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->signature = MagickSignature;
1023c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
102443c4925e5305a26e48d68f7893e94f55d0831c39anthony  }
1025c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1026c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  switch(type) {
1027c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    /*
1028c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      Convolution Kernels
1029c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    */
1030c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case UnityKernel:
1031602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
1032602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = kernel->width = (size_t) 1;
1033529482f4b494010a13338a74446c510712f670b3anthony        kernel->x = kernel->y = (ssize_t) 0;
1034529482f4b494010a13338a74446c510712f670b3anthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
1035529482f4b494010a13338a74446c510712f670b3anthony          AcquireAlignedMemory(1,sizeof(*kernel->values)));
1036529482f4b494010a13338a74446c510712f670b3anthony        if (kernel->values == (MagickRealType *) NULL)
1037529482f4b494010a13338a74446c510712f670b3anthony          return(DestroyKernelInfo(kernel));
1038529482f4b494010a13338a74446c510712f670b3anthony        kernel->maximum = kernel->values[0] = args->rho;
1039529482f4b494010a13338a74446c510712f670b3anthony        break;
1040e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy      }
1041e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy      break;
1042d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy    case GaussianKernel:
1043529482f4b494010a13338a74446c510712f670b3anthony    case DoGKernel:
1044529482f4b494010a13338a74446c510712f670b3anthony    case LoGKernel:
1045529482f4b494010a13338a74446c510712f670b3anthony      { double
1046529482f4b494010a13338a74446c510712f670b3anthony          sigma = fabs(args->sigma),
1047529482f4b494010a13338a74446c510712f670b3anthony          sigma2 = fabs(args->xi),
1048602ab9b30b644a78a4057da93d838a77391ec0acanthony          A, B, R;
1049501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony
1050501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( args->rho >= 1.0 )
1051602ab9b30b644a78a4057da93d838a77391ec0acanthony          kernel->width = (size_t)args->rho*2+1;
1052c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else if ( (type != DoGKernel) || (sigma >= sigma2) )
1053c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
10549eb4f74649b23c053b308ce1152dce51239450baanthony        else
1055c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1056c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->height = kernel->width;
1057bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1058501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
1059c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          AcquireAlignedMemory(kernel->width,kernel->height*
1060c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sizeof(*kernel->values)));
1061c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel->values == (MagickRealType *) NULL)
1062c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(DestroyKernelInfo(kernel));
1063bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy
1064e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        /* WARNING: The following generates a 'sampled gaussian' kernel.
1065e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy         * What we really want is a 'discrete gaussian' kernel.
1066e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy         *
1067d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy         * How to do this is I don't know, but appears to be basied on the
106883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony         * Error Function 'erf()' (intergral of a gaussian)
1069602ab9b30b644a78a4057da93d838a77391ec0acanthony         */
107046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
10719eb4f74649b23c053b308ce1152dce51239450baanthony        if ( type == GaussianKernel || type == DoGKernel )
107246a369d839971ab627bdb31a93d8bd63e81b65a3anthony          { /* Calculate a Gaussian,  OR positive half of a DoG */
1073529482f4b494010a13338a74446c510712f670b3anthony            if ( sigma > MagickEpsilon )
1074529482f4b494010a13338a74446c510712f670b3anthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
10759eb4f74649b23c053b308ce1152dce51239450baanthony                B = (double) (1.0/(Magick2PI*sigma*sigma));
10769eb4f74649b23c053b308ce1152dce51239450baanthony                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1077501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1078501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                      kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
10799eb4f74649b23c053b308ce1152dce51239450baanthony              }
10809eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
108155a91cddcdea3aa002893186a773e1704884a9dfcristy              { (void) ResetMagickMemory(kernel->values,0, (size_t)
1082bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  kernel->width*kernel->height*sizeof(*kernel->values));
1083bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
10849eb4f74649b23c053b308ce1152dce51239450baanthony              }
10859eb4f74649b23c053b308ce1152dce51239450baanthony          }
10869eb4f74649b23c053b308ce1152dce51239450baanthony
10879eb4f74649b23c053b308ce1152dce51239450baanthony        if ( type == DoGKernel )
108875920b23a8a3883792cbb69d569c33cc789cf1b5cristy          { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
10899eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma2 > MagickEpsilon )
10909eb4f74649b23c053b308ce1152dce51239450baanthony              { sigma = sigma2;                /* simplify loop expressions */
1091c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony                A = 1.0/(2.0*sigma*sigma);
10929eb4f74649b23c053b308ce1152dce51239450baanthony                B = (double) (1.0/(Magick2PI*sigma*sigma));
1093501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1094c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1095c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony                    kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1096c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              }
10979eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
109855a91cddcdea3aa002893186a773e1704884a9dfcristy              kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1099bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          }
1100bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy
11019eb4f74649b23c053b308ce1152dce51239450baanthony        if ( type == LoGKernel )
1102c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
11039eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
1104c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
1105c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony                B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
11069eb4f74649b23c053b308ce1152dce51239450baanthony                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1107501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11089eb4f74649b23c053b308ce1152dce51239450baanthony                    { R = ((double)(u*u+v*v))*A;
11099eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = (1-R)*exp(-R)*B;
11109eb4f74649b23c053b308ce1152dce51239450baanthony                    }
111155a91cddcdea3aa002893186a773e1704884a9dfcristy              }
1112bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            else /* special case - generate a unity kernel */
1113bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              { (void) ResetMagickMemory(kernel->values,0, (size_t)
11149eb4f74649b23c053b308ce1152dce51239450baanthony                  kernel->width*kernel->height*sizeof(*kernel->values));
11159eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
11169eb4f74649b23c053b308ce1152dce51239450baanthony              }
11179eb4f74649b23c053b308ce1152dce51239450baanthony          }
11189eb4f74649b23c053b308ce1152dce51239450baanthony
11199eb4f74649b23c053b308ce1152dce51239450baanthony        /* Note the above kernels may have been 'clipped' by a user defined
112075920b23a8a3883792cbb69d569c33cc789cf1b5cristy        ** radius, producing a smaller (darker) kernel.  Also for very small
11219eb4f74649b23c053b308ce1152dce51239450baanthony        ** sigma's (> 0.1) the central value becomes larger than one, and thus
11229eb4f74649b23c053b308ce1152dce51239450baanthony        ** producing a very bright kernel.
11239eb4f74649b23c053b308ce1152dce51239450baanthony        **
11249eb4f74649b23c053b308ce1152dce51239450baanthony        ** Normalization will still be needed.
11259eb4f74649b23c053b308ce1152dce51239450baanthony        */
1126c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1127c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Normalize the 2D Gaussian Kernel
1128c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1129c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1130c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
1131c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        */
1132602ab9b30b644a78a4057da93d838a77391ec0acanthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
11333dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
11343dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
1135c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1136c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
11373dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case BlurKernel:
113846a369d839971ab627bdb31a93d8bd63e81b65a3anthony      { double
1139c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1140602ab9b30b644a78a4057da93d838a77391ec0acanthony          alpha, beta;
1141602ab9b30b644a78a4057da93d838a77391ec0acanthony
1142602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( args->rho >= 1.0 )
1143602ab9b30b644a78a4057da93d838a77391ec0acanthony          kernel->width = (size_t)args->rho*2+1;
1144602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
1145c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1146501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel->height = 1;
1147602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->x = (ssize_t) (kernel->width-1)/2;
1148c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->y = 0;
1149bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->negative_range = kernel->positive_range = 0.0;
1150c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
1151501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          AcquireAlignedMemory(kernel->width,kernel->height*
1152602ab9b30b644a78a4057da93d838a77391ec0acanthony          sizeof(*kernel->values)));
1153bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        if (kernel->values == (MagickRealType *) NULL)
1154c99304fe3c8d9c617da792b40b57c118bb1249afcristy          return(DestroyKernelInfo(kernel));
1155c99304fe3c8d9c617da792b40b57c118bb1249afcristy
1156e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy#if 1
1157e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy#define KernelRank 3
1158e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
1159d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        ** It generates a gaussian 3 times the width, and compresses it into
116083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        ** the expected range.  This produces a closer normalization of the
1161602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** resulting kernel, especially for very low sigma values.
1162602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** As such while wierd it is prefered.
1163602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1164602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** I am told this method originally came from Photoshop.
1165602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1166602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** A properly normalized curve is generated (apart from edge clipping)
1167602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** even though we later normalize the result (for edge clipping)
1168602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** to allow the correct generation of a "Difference of Blurs".
1169602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1170602ab9b30b644a78a4057da93d838a77391ec0acanthony
11719eb4f74649b23c053b308ce1152dce51239450baanthony        /* initialize */
11729eb4f74649b23c053b308ce1152dce51239450baanthony        v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
11739eb4f74649b23c053b308ce1152dce51239450baanthony        (void) ResetMagickMemory(kernel->values,0, (size_t)
11749eb4f74649b23c053b308ce1152dce51239450baanthony          kernel->width*kernel->height*sizeof(*kernel->values));
1175602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Calculate a Positive 1D Gaussian */
1176c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1177c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { sigma *= KernelRank;               /* simplify loop expressions */
1178bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            alpha = 1.0/(2.0*sigma*sigma);
11799eb4f74649b23c053b308ce1152dce51239450baanthony            beta= (double) (1.0/(MagickSQ2PI*sigma ));
118075920b23a8a3883792cbb69d569c33cc789cf1b5cristy            for ( u=-v; u <= v; u++) {
1181c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[(u+v)/KernelRank] +=
1182c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony                              exp(-((double)(u*u))*alpha)*beta;
1183c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            }
1184501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          }
118555a91cddcdea3aa002893186a773e1704884a9dfcristy        else /* special case - generate a unity kernel */
1186c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1187501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony#else
1188501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        /* Direct calculation without curve averaging
1189c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony           This is equivelent to a KernelRank of 1 */
1190c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1191c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive Gaussian */
1192c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1193602ab9b30b644a78a4057da93d838a77391ec0acanthony          { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
119453f576d128f0bb744a82e7f9c0d8f05b2923972canthony            beta = 1.0/(MagickSQ2PI*sigma);
119553f576d128f0bb744a82e7f9c0d8f05b2923972canthony            for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1196c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1197c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1198c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1199501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
1200501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->width*kernel->height*sizeof(*kernel->values));
1201bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1202501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          }
1203c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony#endif
1204c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Note the above kernel may have been 'clipped' by a user defined
1205c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
120675920b23a8a3883792cbb69d569c33cc789cf1b5cristy        ** sigma's (> 0.1) the central value becomes larger than one, as a
1207c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** result of not generating a actual 'discrete' kernel, and thus
1208c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** producing a very bright 'impulse'.
1209602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1210c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** Becuase of these two factors Normalization is required!
1211cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        */
121253f576d128f0bb744a82e7f9c0d8f05b2923972canthony
121353f576d128f0bb744a82e7f9c0d8f05b2923972canthony        /* Normalize the 1D Gaussian Kernel
121453f576d128f0bb744a82e7f9c0d8f05b2923972canthony        **
1215c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
121653f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** there are no negative values.
1217602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1218cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1219602ab9b30b644a78a4057da93d838a77391ec0acanthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1220602ab9b30b644a78a4057da93d838a77391ec0acanthony
1221c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* rotate the 1D kernel by given angle */
1222c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        RotateKernelInfo(kernel, args->xi );
1223602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
122446a369d839971ab627bdb31a93d8bd63e81b65a3anthony      }
122546a369d839971ab627bdb31a93d8bd63e81b65a3anthony    case CometKernel:
1226cc6c836da2a53b6023b716e4973090a6714dc3b0anthony      { double
1227c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1228501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          A;
1229602ab9b30b644a78a4057da93d838a77391ec0acanthony
1230602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( args->rho < 1.0 )
1231602ab9b30b644a78a4057da93d838a77391ec0acanthony          kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1232602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
12339eb4f74649b23c053b308ce1152dce51239450baanthony          kernel->width = (size_t)args->rho;
12349eb4f74649b23c053b308ce1152dce51239450baanthony        kernel->x = kernel->y = 0;
1235602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1236602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->negative_range = kernel->positive_range = 0.0;
1237e1cf9465864144e8b8043d522906c1e47bbf6192anthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
1238602ab9b30b644a78a4057da93d838a77391ec0acanthony          AcquireAlignedMemory(kernel->width,kernel->height*
1239bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          sizeof(*kernel->values)));
1240c99304fe3c8d9c617da792b40b57c118bb1249afcristy        if (kernel->values == (MagickRealType *) NULL)
1241602ab9b30b644a78a4057da93d838a77391ec0acanthony          return(DestroyKernelInfo(kernel));
1242c99304fe3c8d9c617da792b40b57c118bb1249afcristy
1243e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        /* A comet blur is half a 1D gaussian curve, so that the object is
1244e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        ** blurred in one direction only.  This may not be quite the right
1245e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        ** curve to use so may change in the future. The function must be
1246d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        ** normalised after generation, which also resolves any clipping.
124783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        **
1248602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** As we are normalizing and not subtracting gaussians,
1249c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there is no need for a divisor in the gaussian formula
1250602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
12513dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** It is less comples
12523dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        */
1253c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1254c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          {
1255c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony#if 1
1256c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony#define KernelRank 3
125743c4925e5305a26e48d68f7893e94f55d0831c39anthony            v = (ssize_t) kernel->width*KernelRank; /* start/end points */
1258602ab9b30b644a78a4057da93d838a77391ec0acanthony            (void) ResetMagickMemory(kernel->values,0, (size_t)
12599eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->width*sizeof(*kernel->values));
12609eb4f74649b23c053b308ce1152dce51239450baanthony            sigma *= KernelRank;            /* simplify the loop expression */
1261602ab9b30b644a78a4057da93d838a77391ec0acanthony            A = 1.0/(2.0*sigma*sigma);
1262602ab9b30b644a78a4057da93d838a77391ec0acanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
1263bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( u=0; u < v; u++) {
12649eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->values[u/KernelRank] +=
126575920b23a8a3883792cbb69d569c33cc789cf1b5cristy                  exp(-((double)(u*u))*A);
12669eb4f74649b23c053b308ce1152dce51239450baanthony              /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
12679eb4f74649b23c053b308ce1152dce51239450baanthony            }
12689eb4f74649b23c053b308ce1152dce51239450baanthony            for (i=0; i < (ssize_t) kernel->width; i++)
12699eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range += kernel->values[i];
12709eb4f74649b23c053b308ce1152dce51239450baanthony#else
12719eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
12729eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
12739eb4f74649b23c053b308ce1152dce51239450baanthony            for ( i=0; i < (ssize_t) kernel->width; i++)
1274bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              kernel->positive_range +=
12759eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[i] = exp(-((double)(i*i))*A);
1276602ab9b30b644a78a4057da93d838a77391ec0acanthony                /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
12779eb4f74649b23c053b308ce1152dce51239450baanthony#endif
12789eb4f74649b23c053b308ce1152dce51239450baanthony          }
1279bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        else /* special case - generate a unity kernel */
12809eb4f74649b23c053b308ce1152dce51239450baanthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
128153f576d128f0bb744a82e7f9c0d8f05b2923972canthony              kernel->width*kernel->height*sizeof(*kernel->values));
12829eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1283602ab9b30b644a78a4057da93d838a77391ec0acanthony            kernel->positive_range = 1.0;
12849eb4f74649b23c053b308ce1152dce51239450baanthony          }
12859eb4f74649b23c053b308ce1152dce51239450baanthony
12869eb4f74649b23c053b308ce1152dce51239450baanthony        kernel->minimum = 0.0;
128775920b23a8a3883792cbb69d569c33cc789cf1b5cristy        kernel->maximum = kernel->values[0];
12889eb4f74649b23c053b308ce1152dce51239450baanthony        kernel->negative_range = 0.0;
12899eb4f74649b23c053b308ce1152dce51239450baanthony
12909eb4f74649b23c053b308ce1152dce51239450baanthony        ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
129146a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
129246a369d839971ab627bdb31a93d8bd63e81b65a3anthony        break;
1293c99304fe3c8d9c617da792b40b57c118bb1249afcristy      }
129446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    case BinomialKernel:
1295602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
1296999bb2c20aa9d42875bb5adba44951988d4ae354anthony        size_t
1297999bb2c20aa9d42875bb5adba44951988d4ae354anthony          order_f;
1298602ab9b30b644a78a4057da93d838a77391ec0acanthony
1299602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (args->rho < 1.0)
130040ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
130140ca0b982379d4ab2716435a46603d56b5b218b1anthony        else
130240ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
130340ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
130440ca0b982379d4ab2716435a46603d56b5b218b1anthony
130540ca0b982379d4ab2716435a46603d56b5b218b1anthony        order_f = fact(kernel->width-1);
130640ca0b982379d4ab2716435a46603d56b5b218b1anthony
130740ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
130840ca0b982379d4ab2716435a46603d56b5b218b1anthony          AcquireAlignedMemory(kernel->width,kernel->height*
130940ca0b982379d4ab2716435a46603d56b5b218b1anthony          sizeof(*kernel->values)));
131040ca0b982379d4ab2716435a46603d56b5b218b1anthony        if (kernel->values == (MagickRealType *) NULL)
131140ca0b982379d4ab2716435a46603d56b5b218b1anthony          return(DestroyKernelInfo(kernel));
131240ca0b982379d4ab2716435a46603d56b5b218b1anthony
1313e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        /* set all kernel values within diamond area to scale given */
1314e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
1315e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          { size_t
1316d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
131740ca0b982379d4ab2716435a46603d56b5b218b1anthony            for ( u=0; u < (ssize_t)kernel->width; u++, i++)
131840ca0b982379d4ab2716435a46603d56b5b218b1anthony              kernel->positive_range += kernel->values[i] = (double)
131940ca0b982379d4ab2716435a46603d56b5b218b1anthony                (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
132040ca0b982379d4ab2716435a46603d56b5b218b1anthony          }
132140ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->minimum = 1.0;
1322f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy        kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
132340ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->negative_range = 0.0;
132440ca0b982379d4ab2716435a46603d56b5b218b1anthony        break;
1325f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy      }
132640ca0b982379d4ab2716435a46603d56b5b218b1anthony
132740ca0b982379d4ab2716435a46603d56b5b218b1anthony    /*
132840ca0b982379d4ab2716435a46603d56b5b218b1anthony      Convolution Kernels - Well Known Named Constant Kernels
132940ca0b982379d4ab2716435a46603d56b5b218b1anthony    */
133040ca0b982379d4ab2716435a46603d56b5b218b1anthony    case LaplacianKernel:
133140ca0b982379d4ab2716435a46603d56b5b218b1anthony      { switch ( (int) args->rho ) {
1332c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          case 0:
1333529482f4b494010a13338a74446c510712f670b3anthony          default: /* laplacian square filter -- default */
1334529482f4b494010a13338a74446c510712f670b3anthony            kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
1335529482f4b494010a13338a74446c510712f670b3anthony            break;
13363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          case 1:  /* laplacian diamond filter */
1337e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
13383dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            break;
13399eb4f74649b23c053b308ce1152dce51239450baanthony          case 2:
1340c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
13413dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            break;
13429eb4f74649b23c053b308ce1152dce51239450baanthony          case 3:
1343c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
13443c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          case 5:   /* a 5x5 laplacian */
13469eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray(
13479eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13489eb4f74649b23c053b308ce1152dce51239450baanthony            break;
1349c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          case 7:   /* a 7x7 laplacian */
13503c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
13519eb4f74649b23c053b308ce1152dce51239450baanthony              "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" );
13523c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13539eb4f74649b23c053b308ce1152dce51239450baanthony          case 15:  /* a 5x5 LoG (sigma approx 1.4) */
13543c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
13559eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13563c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
1357c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          case 19:  /* a 9x9 LoG (sigma approx 1.4) */
13583c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
1359501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray(
13609eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13619eb4f74649b23c053b308ce1152dce51239450baanthony            break;
13629eb4f74649b23c053b308ce1152dce51239450baanthony        }
1363501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if (kernel == (KernelInfo *) NULL)
136443c4925e5305a26e48d68f7893e94f55d0831c39anthony          return(kernel);
136543c4925e5305a26e48d68f7893e94f55d0831c39anthony        kernel->type = type;
1366bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        break;
136743c4925e5305a26e48d68f7893e94f55d0831c39anthony      }
13683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case SobelKernel:
13693c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      { /* Simple Sobel Kernel */
13703c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
13713c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        if (kernel == (KernelInfo *) NULL)
13723c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          return(kernel);
13733c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        kernel->type = type;
1374c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        RotateKernelInfo(kernel, args->rho);
1375cceb6f05c2016ea3854b29e7770ec5ff49034ecfanthony        break;
1376dcc2a474c98415e565434fe52f630acba36fa0c1anthony      }
1377dcc2a474c98415e565434fe52f630acba36fa0c1anthony    case RobertsKernel:
1378dcc2a474c98415e565434fe52f630acba36fa0c1anthony      {
1379dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
1380dcc2a474c98415e565434fe52f630acba36fa0c1anthony        if (kernel == (KernelInfo *) NULL)
1381dcc2a474c98415e565434fe52f630acba36fa0c1anthony          return(kernel);
1382dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel->type = type;
1383c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        RotateKernelInfo(kernel, args->rho);
1384c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1385501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      }
1386c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PrewittKernel:
1387c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1388c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
138946a369d839971ab627bdb31a93d8bd63e81b65a3anthony        if (kernel == (KernelInfo *) NULL)
1390c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1391c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
1392c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        RotateKernelInfo(kernel, args->rho);
1393c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1394501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      }
1395c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CompassKernel:
1396c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1397c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
139846a369d839971ab627bdb31a93d8bd63e81b65a3anthony        if (kernel == (KernelInfo *) NULL)
1399c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1400c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
1401c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        RotateKernelInfo(kernel, args->rho);
1402c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1403501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      }
1404c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case KirschKernel:
1405c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1406c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
140746a369d839971ab627bdb31a93d8bd63e81b65a3anthony        if (kernel == (KernelInfo *) NULL)
1408c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1409c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
14109eb4f74649b23c053b308ce1152dce51239450baanthony        RotateKernelInfo(kernel, args->rho);
14119eb4f74649b23c053b308ce1152dce51239450baanthony        break;
1412501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      }
14139eb4f74649b23c053b308ce1152dce51239450baanthony    case FreiChenKernel:
14149eb4f74649b23c053b308ce1152dce51239450baanthony      /* Direction is set to be left to right positive */
14159eb4f74649b23c053b308ce1152dce51239450baanthony      /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
141646a369d839971ab627bdb31a93d8bd63e81b65a3anthony      /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
14179eb4f74649b23c053b308ce1152dce51239450baanthony      { switch ( (int) args->rho ) {
14189eb4f74649b23c053b308ce1152dce51239450baanthony          default:
1419e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          case 0:
1420501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1421501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            if (kernel == (KernelInfo *) NULL)
1422501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              return(kernel);
14231dd091ae3bc17edc26c16cc47f436a24bd48412aanthony            kernel->type = type;
1424e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->values[3] = +(MagickRealType) MagickSQ2;
1425c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->values[5] = -(MagickRealType) MagickSQ2;
1426501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1427c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            break;
1428c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 2:
1429ef33d9f66f955c1f6f5f7105e164cc2d5c5e2a41anthony            kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
1430d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            if (kernel == (KernelInfo *) NULL)
1431d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              return(kernel);
1432c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1433c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
1434c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
1435c40ac1e79923a1516075ba1197ae4ed90244af9banthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1436c40ac1e79923a1516075ba1197ae4ed90244af9banthony            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1437c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1438c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 10:
1439d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
1440d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            if (kernel == (KernelInfo *) NULL)
1441c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1442d4e3ffa7f64077da0f32c2d8a599737ee8d115ddcristy            break;
1443c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 1:
1444c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 11:
1445c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1446c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1447c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1448c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->type = type;
1449e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->values[3] = +(MagickRealType) MagickSQ2;
1450c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->values[5] = -(MagickRealType) MagickSQ2;
1451501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1452e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1453e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1454c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 12:
1455d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
1456d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            if (kernel == (KernelInfo *) NULL)
1457e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
145855a91cddcdea3aa002893186a773e1704884a9dfcristy            kernel->type = type;
1459e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->values[1] = +(MagickRealType) MagickSQ2;
1460c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->values[7] = +(MagickRealType) MagickSQ2;
1461501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            CalcKernelMetaData(kernel);
1462e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1463e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1464c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 13:
1465d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
1466d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            if (kernel == (KernelInfo *) NULL)
1467e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
146855a91cddcdea3aa002893186a773e1704884a9dfcristy            kernel->type = type;
1469e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->values[0] = +(MagickRealType) MagickSQ2;
1470c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->values[8] = -(MagickRealType) MagickSQ2;
1471501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            CalcKernelMetaData(kernel);
1472e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1473e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1474c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 14:
1475d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
1476d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            if (kernel == (KernelInfo *) NULL)
1477e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
147855a91cddcdea3aa002893186a773e1704884a9dfcristy            kernel->type = type;
1479e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->values[2] = -(MagickRealType) MagickSQ2;
1480c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->values[6] = +(MagickRealType) MagickSQ2;
14811d5e67090dc7232b35bfcc71b31266c20838defcanthony            CalcKernelMetaData(kernel);
1482e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1483e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1484c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 15:
1485d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
1486d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            if (kernel == (KernelInfo *) NULL)
1487e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
148855a91cddcdea3aa002893186a773e1704884a9dfcristy            kernel->type = type;
1489e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1490c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1491501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 16:
1492e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
1493e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1494c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1495e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->type = type;
1496e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1497c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
14981d5e67090dc7232b35bfcc71b31266c20838defcanthony          case 17:
1499e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
1500e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1501c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1502e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->type = type;
1503e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1504c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1505501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 18:
1506e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1507e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1508c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1509e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->type = type;
1510e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1511c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1512501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 19:
1513e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
1514e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1515c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1516e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            kernel->type = type;
1517e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1518c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1519c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony        }
1520e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        if ( fabs(args->sigma) >= MagickEpsilon )
1521e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          /* Rotate by correctly supplied 'angle' */
1522c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->sigma);
1523e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        else if ( args->rho > 30.0 || args->rho < -30.0 )
1524e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          /* Rotate by out of bounds 'type' */
1525e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          RotateKernelInfo(kernel, args->rho);
1526b978e458a8e1f210bcb580951cf623687236b2fecristy        break;
1527c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony      }
1528c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony
1529c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony    /*
1530c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony      Boolean or Shaped Kernels
1531c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony    */
1532e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony    case DiamondKernel:
1533e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      {
1534e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        if (args->rho < 1.0)
1535529482f4b494010a13338a74446c510712f670b3anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
1536529482f4b494010a13338a74446c510712f670b3anthony        else
1537529482f4b494010a13338a74446c510712f670b3anthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1538c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1539602ab9b30b644a78a4057da93d838a77391ec0acanthony
1540c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
1541c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          AcquireAlignedMemory(kernel->width,kernel->height*
1542c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sizeof(*kernel->values)));
1543bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        if (kernel->values == (MagickRealType *) NULL)
1544bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          return(DestroyKernelInfo(kernel));
1545c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1546e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        /* set all kernel values within diamond area to scale given */
1547e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1548e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1549d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1550c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->positive_range += kernel->values[i] = args->sigma;
1551c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            else
1552c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[i] = nan;
1553bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1554bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        break;
15551d5e67090dc7232b35bfcc71b31266c20838defcanthony      }
1556c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1557c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
1558c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      { double
1559c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          scale;
1560c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( type == SquareKernel )
1561c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          {
1562c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            if (args->rho < 1.0)
1563c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->width = kernel->height = 3;  /* default radius = 1 */
1564c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            else
1565c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->width = kernel->height = (size_t) (2*args->rho+1);
1566602ab9b30b644a78a4057da93d838a77391ec0acanthony            kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1567602ab9b30b644a78a4057da93d838a77391ec0acanthony            scale = args->sigma;
1568602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1569c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony        else {
1570602ab9b30b644a78a4057da93d838a77391ec0acanthony            /* NOTE: user defaults set in "AcquireKernelInfo()" */
1571bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            if ( args->rho < 1.0 || args->sigma < 1.0 )
1572bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              return(DestroyKernelInfo(kernel));    /* invalid args given */
15734fd27e21043be809d66c8202e779255e5b660d2danthony            kernel->width = (size_t)args->rho;
1574602ab9b30b644a78a4057da93d838a77391ec0acanthony            kernel->height = (size_t)args->sigma;
1575602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
15762be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy                 args->psi < 0.0 || args->psi > (double)kernel->height )
1577602ab9b30b644a78a4057da93d838a77391ec0acanthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
157883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony            kernel->x = (ssize_t) args->xi;
1579bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->y = (ssize_t) args->psi;
1580bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            scale = 1.0;
1581602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1582602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
158383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          AcquireAlignedMemory(kernel->width,kernel->height*
1584bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          sizeof(*kernel->values)));
1585bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        if (kernel->values == (MagickRealType *) NULL)
15864fd27e21043be809d66c8202e779255e5b660d2danthony          return(DestroyKernelInfo(kernel));
1587602ab9b30b644a78a4057da93d838a77391ec0acanthony
1588e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        /* set all kernel values to scale given */
1589e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        u=(ssize_t) (kernel->width*kernel->height);
1590e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        for ( i=0; i < u; i++)
1591d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[i] = scale;
159283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        kernel->minimum = kernel->maximum = scale;   /* a flat shape */
1593602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->positive_range = scale*u;
15943dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        break;
1595eaedf06777741da32408da72c1e512975c600c48cristy      }
1596150989ed67ef9da53141a65e5f3ebdb05dd025abcristy      case OctagonKernel:
15974fd27e21043be809d66c8202e779255e5b660d2danthony        {
15984fd27e21043be809d66c8202e779255e5b660d2danthony          if (args->rho < 1.0)
15994fd27e21043be809d66c8202e779255e5b660d2danthony            kernel->width = kernel->height = 5;  /* default radius = 2 */
1600cc6c836da2a53b6023b716e4973090a6714dc3b0anthony          else
1601602ab9b30b644a78a4057da93d838a77391ec0acanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16021ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16031ef941fea2534a0d20ba7d71307d35040247decbanthony
16041ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->values=(MagickRealType *) MagickAssumeAligned(
1605a9892d898acb81e1ec73106d892855fdc5a69427anthony            AcquireAlignedMemory(kernel->width,kernel->height*
16061ef941fea2534a0d20ba7d71307d35040247decbanthony            sizeof(*kernel->values)));
16071ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (MagickRealType *) NULL)
16081ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16091ef941fea2534a0d20ba7d71307d35040247decbanthony
1610e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1611e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1612e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy              if ( (labs((long) u)+labs((long) v)) <=
1613d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy                        ((long)kernel->x + (long)(kernel->x/2)) )
16141ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16151ef941fea2534a0d20ba7d71307d35040247decbanthony              else
16161ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->values[i] = nan;
16171ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
16181ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16191ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16201ef941fea2534a0d20ba7d71307d35040247decbanthony      case DiskKernel:
16211ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16221ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
1623a9892d898acb81e1ec73106d892855fdc5a69427anthony            limit = (ssize_t)(args->rho*args->rho);
16241ef941fea2534a0d20ba7d71307d35040247decbanthony
16251ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 0.4)           /* default radius approx 4.3 */
16261ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 9L, limit = 18L;
16271ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16281ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
16290bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16301ef941fea2534a0d20ba7d71307d35040247decbanthony
16319bf68d590429a72aa70894f6aea7fc3c94876e54anthony          kernel->values=(MagickRealType *) MagickAssumeAligned(
16329bf68d590429a72aa70894f6aea7fc3c94876e54anthony            AcquireAlignedMemory(kernel->width,kernel->height*
16331ef941fea2534a0d20ba7d71307d35040247decbanthony            sizeof(*kernel->values)));
16341ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (MagickRealType *) NULL)
16351ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16361ef941fea2534a0d20ba7d71307d35040247decbanthony
1637e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1638e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1639e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy              if ((u*u+v*v) <= limit)
1640d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy                kernel->positive_range += kernel->values[i] = args->sigma;
16411ef941fea2534a0d20ba7d71307d35040247decbanthony              else
16421ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->values[i] = nan;
16431ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16441ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16451ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16461ef941fea2534a0d20ba7d71307d35040247decbanthony      case PlusKernel:
16473dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        {
16483dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony          if (args->rho < 1.0)
16491ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16501ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16511ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16521ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16531ef941fea2534a0d20ba7d71307d35040247decbanthony
16541ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->values=(MagickRealType *) MagickAssumeAligned(
16551ef941fea2534a0d20ba7d71307d35040247decbanthony            AcquireAlignedMemory(kernel->width,kernel->height*
16561ef941fea2534a0d20ba7d71307d35040247decbanthony            sizeof(*kernel->values)));
16571ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (MagickRealType *) NULL)
16581ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16591ef941fea2534a0d20ba7d71307d35040247decbanthony
1660e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          /* set all kernel values along axises to given scale */
1661e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1662e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1663d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
16641ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16651ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16661ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16671ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16681ef941fea2534a0d20ba7d71307d35040247decbanthony      case CrossKernel:
16691ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16701ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16711ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16721ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16731ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16741ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16751ef941fea2534a0d20ba7d71307d35040247decbanthony
16761ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->values=(MagickRealType *) MagickAssumeAligned(
16771ef941fea2534a0d20ba7d71307d35040247decbanthony            AcquireAlignedMemory(kernel->width,kernel->height*
16781ef941fea2534a0d20ba7d71307d35040247decbanthony            sizeof(*kernel->values)));
16791ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (MagickRealType *) NULL)
16801ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16811ef941fea2534a0d20ba7d71307d35040247decbanthony
1682e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          /* set all kernel values along axises to given scale */
1683e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1684e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1685d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
16861ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16871ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16881ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16891ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16901ef941fea2534a0d20ba7d71307d35040247decbanthony      /*
16911ef941fea2534a0d20ba7d71307d35040247decbanthony        HitAndMiss Kernels
16921ef941fea2534a0d20ba7d71307d35040247decbanthony      */
16931ef941fea2534a0d20ba7d71307d35040247decbanthony      case RingKernel:
16941ef941fea2534a0d20ba7d71307d35040247decbanthony      case PeaksKernel:
16951ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1696529482f4b494010a13338a74446c510712f670b3anthony          ssize_t
1697529482f4b494010a13338a74446c510712f670b3anthony            limit1,
1698529482f4b494010a13338a74446c510712f670b3anthony            limit2,
16991ef941fea2534a0d20ba7d71307d35040247decbanthony            scale;
17001ef941fea2534a0d20ba7d71307d35040247decbanthony
17011ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < args->sigma)
17021ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17031ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->sigma)*2+1;
17041ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->rho*args->rho);
17051ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->sigma*args->sigma);
17061ef941fea2534a0d20ba7d71307d35040247decbanthony            }
17071ef941fea2534a0d20ba7d71307d35040247decbanthony          else
17081ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17091ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->rho)*2+1;
17101ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->sigma*args->sigma);
17111ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->rho*args->rho);
17123dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            }
17131ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( limit2 <= 0 )
17141ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = 7L, limit1 = 7L, limit2 = 11L;
17151ef941fea2534a0d20ba7d71307d35040247decbanthony
17161ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->height = kernel->width;
17171ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
17181ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->values=(MagickRealType *) MagickAssumeAligned(
17191ef941fea2534a0d20ba7d71307d35040247decbanthony            AcquireAlignedMemory(kernel->width,kernel->height*
17201ef941fea2534a0d20ba7d71307d35040247decbanthony            sizeof(*kernel->values)));
17211ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (MagickRealType *) NULL)
17221ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
17231ef941fea2534a0d20ba7d71307d35040247decbanthony
1724e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
1725e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
1726e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
1727d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
17281ef941fea2534a0d20ba7d71307d35040247decbanthony              { ssize_t radius=u*u+v*v;
17291ef941fea2534a0d20ba7d71307d35040247decbanthony                if (limit1 < radius && radius <= limit2)
17301ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->positive_range += kernel->values[i] = (double) scale;
17311ef941fea2534a0d20ba7d71307d35040247decbanthony                else
17321ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->values[i] = nan;
17331ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17341ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = (double) scale;
17351ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( type == PeaksKernel ) {
17361ef941fea2534a0d20ba7d71307d35040247decbanthony            /* set the central point in the middle */
17371ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
17381ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->positive_range = 1.0;
17391ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->maximum = 1.0;
17401ef941fea2534a0d20ba7d71307d35040247decbanthony          }
17411ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17421ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17431ef941fea2534a0d20ba7d71307d35040247decbanthony      case EdgesKernel:
17441ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17451ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel=AcquireKernelInfo("ThinSE:482");
17461ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17471ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
1748c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->type = type;
17491ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
17501ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1751529482f4b494010a13338a74446c510712f670b3anthony        }
17521ef941fea2534a0d20ba7d71307d35040247decbanthony      case CornersKernel:
17531ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17541ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel=AcquireKernelInfo("ThinSE:87");
1755529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
17561ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17571ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
17581ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
17591ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1760529482f4b494010a13338a74446c510712f670b3anthony        }
17611ef941fea2534a0d20ba7d71307d35040247decbanthony      case DiagonalsKernel:
17621ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17631ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
17641ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
17651ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17661ef941fea2534a0d20ba7d71307d35040247decbanthony              { KernelInfo
1767529482f4b494010a13338a74446c510712f670b3anthony                  *new_kernel;
17681ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17691ef941fea2534a0d20ba7d71307d35040247decbanthony                if (kernel == (KernelInfo *) NULL)
17701ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(kernel);
17711ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->type = type;
17721ef941fea2534a0d20ba7d71307d35040247decbanthony                new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17731ef941fea2534a0d20ba7d71307d35040247decbanthony                if (new_kernel == (KernelInfo *) NULL)
1774529482f4b494010a13338a74446c510712f670b3anthony                  return(DestroyKernelInfo(kernel));
17751ef941fea2534a0d20ba7d71307d35040247decbanthony                new_kernel->type = type;
17761ef941fea2534a0d20ba7d71307d35040247decbanthony                LastKernelInfo(kernel)->next = new_kernel;
17771ef941fea2534a0d20ba7d71307d35040247decbanthony                ExpandMirrorKernelInfo(kernel);
1778529482f4b494010a13338a74446c510712f670b3anthony                return(kernel);
17791ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17801ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
17811ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17821ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17831ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
1784529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17851ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17861ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1787529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
17881ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17891ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
1790529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
17911ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17921ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1793529482f4b494010a13338a74446c510712f670b3anthony      case LineEndsKernel:
1794529482f4b494010a13338a74446c510712f670b3anthony        { /* Kernels for finding the end of thin lines */
1795529482f4b494010a13338a74446c510712f670b3anthony          switch ( (int) args->rho ) {
1796529482f4b494010a13338a74446c510712f670b3anthony            case 0:
17971ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17981ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all end of lines */
17991ef941fea2534a0d20ba7d71307d35040247decbanthony              return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
18001ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18011ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel for 4-connected line ends - no rotation */
18021ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
18031ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18041ef941fea2534a0d20ba7d71307d35040247decbanthony          case 2:
1805529482f4b494010a13338a74446c510712f670b3anthony              /* kernel to add for 8-connected lines - no rotation */
18061ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
18071ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18081ef941fea2534a0d20ba7d71307d35040247decbanthony          case 3:
18091ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for orthogonal line ends - does not find corners */
18101ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
18111ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18121ef941fea2534a0d20ba7d71307d35040247decbanthony          case 4:
18131ef941fea2534a0d20ba7d71307d35040247decbanthony              /* traditional line end - fails on last T end */
18141ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
18151ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18161ef941fea2534a0d20ba7d71307d35040247decbanthony          }
18171ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
18181ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
18191ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
18201ef941fea2534a0d20ba7d71307d35040247decbanthony          RotateKernelInfo(kernel, args->sigma);
18211ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18221ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1823529482f4b494010a13338a74446c510712f670b3anthony      case LineJunctionsKernel:
1824529482f4b494010a13338a74446c510712f670b3anthony        { /* kernels for finding the junctions of multiple lines */
1825529482f4b494010a13338a74446c510712f670b3anthony          switch ( (int) args->rho ) {
1826529482f4b494010a13338a74446c510712f670b3anthony            case 0:
18271ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18281ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all line junctions */
18291ef941fea2534a0d20ba7d71307d35040247decbanthony              return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
18301ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18311ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Y Junction */
18321ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
18331ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18341ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
1835529482f4b494010a13338a74446c510712f670b3anthony              /* Diagonal T Junctions */
18361ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
18371ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18381ef941fea2534a0d20ba7d71307d35040247decbanthony            case 3:
18391ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal T Junctions */
18401ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
18411ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18421ef941fea2534a0d20ba7d71307d35040247decbanthony            case 4:
18431ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal X Junctions */
18441ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
18451ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18461ef941fea2534a0d20ba7d71307d35040247decbanthony            case 5:
18471ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal X Junctions - minimal diamond kernel */
18481ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
18491ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18501ef941fea2534a0d20ba7d71307d35040247decbanthony          }
18511ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
18521ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
18531ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
18541ef941fea2534a0d20ba7d71307d35040247decbanthony          RotateKernelInfo(kernel, args->sigma);
18551ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18561ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1857529482f4b494010a13338a74446c510712f670b3anthony      case RidgesKernel:
1858529482f4b494010a13338a74446c510712f670b3anthony        { /* Ridges - Ridge finding kernels */
1859529482f4b494010a13338a74446c510712f670b3anthony          KernelInfo
1860529482f4b494010a13338a74446c510712f670b3anthony            *new_kernel;
18611ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18621ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18631ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18641ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3x1:0,1,0");
18651ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18661ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18671ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18681ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
18691ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18701ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18711ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("4x1:0,1,1,0");
18721ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18731ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18741ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18751ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
18761ef941fea2534a0d20ba7d71307d35040247decbanthony
18771ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
18781ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Unfortunatally we can not yet rotate a non-square kernel */
18791ef941fea2534a0d20ba7d71307d35040247decbanthony              /* But then we can't flip a non-symetrical kernel either */
18801ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
18811ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18821ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18831ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18841ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18851ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+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+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
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+2+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("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
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+2: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+1:-,-,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+2:-,-,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              break;
19211ef941fea2534a0d20ba7d71307d35040247decbanthony          }
192268cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony          break;
192368cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony        }
192468cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony      case ConvexHullKernel:
192568cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony        {
192668cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony          KernelInfo
19271ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
19281ef941fea2534a0d20ba7d71307d35040247decbanthony          /* first set of 8 kernels */
192968cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony          kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
19301ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
19311ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
19321ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
19331ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0);
19341ef941fea2534a0d20ba7d71307d35040247decbanthony          /* append the mirror versions too - no flip function yet */
19351ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
19361ef941fea2534a0d20ba7d71307d35040247decbanthony          if (new_kernel == (KernelInfo *) NULL)
19371ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
19381ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel->type = type;
19391ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(new_kernel, 90.0);
19401ef941fea2534a0d20ba7d71307d35040247decbanthony          LastKernelInfo(kernel)->next = new_kernel;
19411ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
19421ef941fea2534a0d20ba7d71307d35040247decbanthony        }
19431ef941fea2534a0d20ba7d71307d35040247decbanthony      case SkeletonKernel:
19441ef941fea2534a0d20ba7d71307d35040247decbanthony        {
19451ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
19461ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
19471ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
1948694934fa79dd310f727588b1d0a7481fa6170f1danthony              /* Traditional Skeleton...
19499a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** A cyclically rotated single kernel
19509a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19519a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482");
19529a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19539a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19549a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19559a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
19569a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19579a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 2:
19589a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* HIPR Variation of the cyclic skeleton
19599a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** Corners of the traditional method made more forgiving,
19609a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** but the retain the same cyclic order.
19619a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19629a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
19639a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19649a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel->next == (KernelInfo *) NULL)
19669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(DestroyKernelInfo(kernel));
19679a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19699a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
19709a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19719a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 3:
19729a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
19739a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** "Connectivity-Preserving Morphological Image Thransformations"
19749a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
19759a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              **   http://www.leptonica.com/papers/conn.pdf
19769a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19779a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo(
19789a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                            "ThinSE:41; ThinSE:42; ThinSE:43");
19799a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19809a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19819a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19829a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19839a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->next->type = type;
19849a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
19859a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19869a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony           }
19879a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          break;
19889a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        }
19899a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony      case ThinSEKernel:
19909a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        { /* Special kernels for general thinning, while preserving connections
19919a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          ** "Connectivity-Preserving Morphological Image Thransformations"
19929a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
19939a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          **   http://www.leptonica.com/papers/conn.pdf
19949a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          ** And
1995529482f4b494010a13338a74446c510712f670b3anthony          **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
1996529482f4b494010a13338a74446c510712f670b3anthony          **
1997529482f4b494010a13338a74446c510712f670b3anthony          ** Note kernels do not specify the origin pixel, allowing them
1998529482f4b494010a13338a74446c510712f670b3anthony          ** to be used for both thickening and thinning operations.
1999529482f4b494010a13338a74446c510712f670b3anthony          */
2000529482f4b494010a13338a74446c510712f670b3anthony          switch ( (int) args->rho ) {
2001529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 4-connected thinning */
2002529482f4b494010a13338a74446c510712f670b3anthony            case 41: /* SE_4_1 */
2003529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
2004529482f4b494010a13338a74446c510712f670b3anthony              break;
2005529482f4b494010a13338a74446c510712f670b3anthony            case 42: /* SE_4_2 */
2006529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
2007529482f4b494010a13338a74446c510712f670b3anthony              break;
2008529482f4b494010a13338a74446c510712f670b3anthony            case 43: /* SE_4_3 */
2009529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
2010529482f4b494010a13338a74446c510712f670b3anthony              break;
2011529482f4b494010a13338a74446c510712f670b3anthony            case 44: /* SE_4_4 */
2012529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
2013529482f4b494010a13338a74446c510712f670b3anthony              break;
2014529482f4b494010a13338a74446c510712f670b3anthony            case 45: /* SE_4_5 */
2015529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
2016529482f4b494010a13338a74446c510712f670b3anthony              break;
2017529482f4b494010a13338a74446c510712f670b3anthony            case 46: /* SE_4_6 */
2018529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
2019529482f4b494010a13338a74446c510712f670b3anthony              break;
2020529482f4b494010a13338a74446c510712f670b3anthony            case 47: /* SE_4_7 */
2021529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
2022529482f4b494010a13338a74446c510712f670b3anthony              break;
2023529482f4b494010a13338a74446c510712f670b3anthony            case 48: /* SE_4_8 */
2024529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
2025529482f4b494010a13338a74446c510712f670b3anthony              break;
2026529482f4b494010a13338a74446c510712f670b3anthony            case 49: /* SE_4_9 */
2027529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
2028529482f4b494010a13338a74446c510712f670b3anthony              break;
2029529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 8-connected thinning - negatives of the above */
2030529482f4b494010a13338a74446c510712f670b3anthony            case 81: /* SE_8_0 */
2031529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
2032529482f4b494010a13338a74446c510712f670b3anthony              break;
2033529482f4b494010a13338a74446c510712f670b3anthony            case 82: /* SE_8_2 */
2034529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
2035529482f4b494010a13338a74446c510712f670b3anthony              break;
2036529482f4b494010a13338a74446c510712f670b3anthony            case 83: /* SE_8_3 */
2037529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
2038529482f4b494010a13338a74446c510712f670b3anthony              break;
2039529482f4b494010a13338a74446c510712f670b3anthony            case 84: /* SE_8_4 */
2040529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
2041529482f4b494010a13338a74446c510712f670b3anthony              break;
2042529482f4b494010a13338a74446c510712f670b3anthony            case 85: /* SE_8_5 */
2043529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
2044529482f4b494010a13338a74446c510712f670b3anthony              break;
2045529482f4b494010a13338a74446c510712f670b3anthony            case 86: /* SE_8_6 */
2046529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
2047529482f4b494010a13338a74446c510712f670b3anthony              break;
2048529482f4b494010a13338a74446c510712f670b3anthony            case 87: /* SE_8_7 */
2049529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
2050529482f4b494010a13338a74446c510712f670b3anthony              break;
2051529482f4b494010a13338a74446c510712f670b3anthony            case 88: /* SE_8_8 */
2052529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
2053529482f4b494010a13338a74446c510712f670b3anthony              break;
2054529482f4b494010a13338a74446c510712f670b3anthony            case 89: /* SE_8_9 */
2055529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
2056529482f4b494010a13338a74446c510712f670b3anthony              break;
2057529482f4b494010a13338a74446c510712f670b3anthony            /* Special combined SE kernels */
2058529482f4b494010a13338a74446c510712f670b3anthony            case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
2059529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
2060529482f4b494010a13338a74446c510712f670b3anthony              break;
2061529482f4b494010a13338a74446c510712f670b3anthony            case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
2062529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
2063529482f4b494010a13338a74446c510712f670b3anthony              break;
20649a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 481: /* SE_48_1 - General Connected Corner Kernel */
20659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
20669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
20679a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            default:
20689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 482: /* SE_48_2 - General Edge Kernel */
20699a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
2070529482f4b494010a13338a74446c510712f670b3anthony              break;
2071529482f4b494010a13338a74446c510712f670b3anthony          }
2072529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
2073529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
2074529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
2075529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
2076529482f4b494010a13338a74446c510712f670b3anthony          break;
2077529482f4b494010a13338a74446c510712f670b3anthony        }
2078529482f4b494010a13338a74446c510712f670b3anthony      /*
2079529482f4b494010a13338a74446c510712f670b3anthony        Distance Measuring Kernels
2080529482f4b494010a13338a74446c510712f670b3anthony      */
2081529482f4b494010a13338a74446c510712f670b3anthony      case ChebyshevKernel:
2082529482f4b494010a13338a74446c510712f670b3anthony        {
2083529482f4b494010a13338a74446c510712f670b3anthony          if (args->rho < 1.0)
2084529482f4b494010a13338a74446c510712f670b3anthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
2085529482f4b494010a13338a74446c510712f670b3anthony          else
2086529482f4b494010a13338a74446c510712f670b3anthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
20871ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
20881ef941fea2534a0d20ba7d71307d35040247decbanthony
20891ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->values=(MagickRealType *) MagickAssumeAligned(
20901ef941fea2534a0d20ba7d71307d35040247decbanthony            AcquireAlignedMemory(kernel->width,kernel->height*
20911ef941fea2534a0d20ba7d71307d35040247decbanthony            sizeof(*kernel->values)));
20921ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (MagickRealType *) NULL)
20931ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
20941ef941fea2534a0d20ba7d71307d35040247decbanthony
2095e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2096e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2097e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy              kernel->positive_range += ( kernel->values[i] =
2098d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy                  args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
20991ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21001ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
21011ef941fea2534a0d20ba7d71307d35040247decbanthony        }
21021ef941fea2534a0d20ba7d71307d35040247decbanthony      case ManhattanKernel:
21031ef941fea2534a0d20ba7d71307d35040247decbanthony        {
21041ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
21051ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
21061ef941fea2534a0d20ba7d71307d35040247decbanthony          else
2107c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
21081ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
21091ef941fea2534a0d20ba7d71307d35040247decbanthony
21101ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->values=(MagickRealType *) MagickAssumeAligned(
21111ef941fea2534a0d20ba7d71307d35040247decbanthony            AcquireAlignedMemory(kernel->width,kernel->height*
21121ef941fea2534a0d20ba7d71307d35040247decbanthony            sizeof(*kernel->values)));
21131ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (MagickRealType *) NULL)
21141ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
21151ef941fea2534a0d20ba7d71307d35040247decbanthony
2116e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2117e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2118e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy              kernel->positive_range += ( kernel->values[i] =
2119d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy                  args->sigma*(labs((long) u)+labs((long) v)) );
21201ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21211ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
21221ef941fea2534a0d20ba7d71307d35040247decbanthony        }
21231ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonalKernel:
21241ef941fea2534a0d20ba7d71307d35040247decbanthony      {
21251ef941fea2534a0d20ba7d71307d35040247decbanthony        if (args->rho < 2.0)
21261ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
21271ef941fea2534a0d20ba7d71307d35040247decbanthony        else
2128c40ac1e79923a1516075ba1197ae4ed90244af9banthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
21291ef941fea2534a0d20ba7d71307d35040247decbanthony        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2130602ab9b30b644a78a4057da93d838a77391ec0acanthony
21311ef941fea2534a0d20ba7d71307d35040247decbanthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
21321ef941fea2534a0d20ba7d71307d35040247decbanthony          AcquireAlignedMemory(kernel->width,kernel->height*
2133602ab9b30b644a78a4057da93d838a77391ec0acanthony          sizeof(*kernel->values)));
2134bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        if (kernel->values == (MagickRealType *) NULL)
2135bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          return(DestroyKernelInfo(kernel));
2136602ab9b30b644a78a4057da93d838a77391ec0acanthony
2137e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2138e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2139e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            {
2140d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              double
214183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony                r1 = MagickMax(fabs((double)u),fabs((double)v)),
2142602ab9b30b644a78a4057da93d838a77391ec0acanthony                r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
2143bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              kernel->positive_range += kernel->values[i] =
2144bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                        args->sigma*MagickMax(r1,r2);
21451ef941fea2534a0d20ba7d71307d35040247decbanthony            }
21461ef941fea2534a0d20ba7d71307d35040247decbanthony        kernel->maximum = kernel->values[0];
21471ef941fea2534a0d20ba7d71307d35040247decbanthony        break;
21481ef941fea2534a0d20ba7d71307d35040247decbanthony      }
21491ef941fea2534a0d20ba7d71307d35040247decbanthony    case EuclideanKernel:
21501ef941fea2534a0d20ba7d71307d35040247decbanthony      {
21511ef941fea2534a0d20ba7d71307d35040247decbanthony        if (args->rho < 1.0)
2152c99304fe3c8d9c617da792b40b57c118bb1249afcristy          kernel->width = kernel->height = 3;  /* default radius = 1 */
2153602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
2154602ab9b30b644a78a4057da93d838a77391ec0acanthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2155602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2156602ab9b30b644a78a4057da93d838a77391ec0acanthony
2157602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->values=(MagickRealType *) MagickAssumeAligned(
2158c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony          AcquireAlignedMemory(kernel->width,kernel->height*
2159602ab9b30b644a78a4057da93d838a77391ec0acanthony          sizeof(*kernel->values)));
21601ef941fea2534a0d20ba7d71307d35040247decbanthony        if (kernel->values == (MagickRealType *) NULL)
2161bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          return(DestroyKernelInfo(kernel));
2162602ab9b30b644a78a4057da93d838a77391ec0acanthony
2163e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2164e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2165e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            kernel->positive_range += ( kernel->values[i] =
2166d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              args->sigma*sqrt((double)(u*u+v*v)) );
216783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        kernel->maximum = kernel->values[0];
2168602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2169bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      }
2170bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    default:
2171c99304fe3c8d9c617da792b40b57c118bb1249afcristy      {
21720ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony        /* No-Op Kernel - Basically just a single pixel on its own */
2173c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel=ParseKernelArray("1:1");
2174602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (kernel == (KernelInfo *) NULL)
2175602ab9b30b644a78a4057da93d838a77391ec0acanthony          return(kernel);
2176602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->type = UndefinedKernel;
2177c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
2178529482f4b494010a13338a74446c510712f670b3anthony      }
21793ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      break;
2180c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  }
2181c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  return(kernel);
2182529482f4b494010a13338a74446c510712f670b3anthony}
2183c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
2184c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony/*
2185602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2186602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2187602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2188602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2189c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%     C l o n e K e r n e l I n f o                                           %
2190602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2191602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2192602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2193602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2194602ab9b30b644a78a4057da93d838a77391ec0acanthony%
21956771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%  CloneKernelInfo() creates a new clone of the given Kernel List so that its
21964fd27e21043be809d66c8202e779255e5b660d2danthony%  can be modified without effecting the original.  The cloned kernel should
21974fd27e21043be809d66c8202e779255e5b660d2danthony%  be destroyed using DestoryKernelInfo() when no longer needed.
21984fd27e21043be809d66c8202e779255e5b660d2danthony%
21994fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the CloneKernelInfo method is:
22004fd27e21043be809d66c8202e779255e5b660d2danthony%
22011b2bc0a7da432e6e1cc0480280402df213faa940anthony%      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22021b2bc0a7da432e6e1cc0480280402df213faa940anthony%
22030ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony%  A description of each parameter follows:
22047a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
2205e636559dfadfdb115cc93f223315052a1ee89238cristy%    o kernel: the Morphology/Convolution kernel to be cloned
22064fd27e21043be809d66c8202e779255e5b660d2danthony%
2207930be614b4595b97cd79ee864a394796740f76adanthony*/
22084fd27e21043be809d66c8202e779255e5b660d2danthonyMagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22094fd27e21043be809d66c8202e779255e5b660d2danthony{
22104fd27e21043be809d66c8202e779255e5b660d2danthony  register ssize_t
22114fd27e21043be809d66c8202e779255e5b660d2danthony    i;
22124fd27e21043be809d66c8202e779255e5b660d2danthony
22134fd27e21043be809d66c8202e779255e5b660d2danthony  KernelInfo
2214ef656913b0b30d713ae94c82c47693c9dc69c9f4cristy    *new_kernel;
22154fd27e21043be809d66c8202e779255e5b660d2danthony
2216bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  assert(kernel != (KernelInfo *) NULL);
22174fd27e21043be809d66c8202e779255e5b660d2danthony  new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
22184fd27e21043be809d66c8202e779255e5b660d2danthony  if (new_kernel == (KernelInfo *) NULL)
221919eb64195ef744f61293025952df1e5e6de66524cristy    return(new_kernel);
22207a01dcf50ce12cb2a789bedff51e9345f022432eanthony  *new_kernel=(*kernel); /* copy values in structure */
22214fd27e21043be809d66c8202e779255e5b660d2danthony
22224fd27e21043be809d66c8202e779255e5b660d2danthony  /* replace the values with a copy of the values */
22237a01dcf50ce12cb2a789bedff51e9345f022432eanthony  new_kernel->values=(MagickRealType *) MagickAssumeAligned(
22247a01dcf50ce12cb2a789bedff51e9345f022432eanthony    AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
22257a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if (new_kernel->values == (MagickRealType *) NULL)
22267a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(DestroyKernelInfo(new_kernel));
22277a01dcf50ce12cb2a789bedff51e9345f022432eanthony  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
22287a01dcf50ce12cb2a789bedff51e9345f022432eanthony    new_kernel->values[i]=kernel->values[i];
2229e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy
2230e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  /* Also clone the next kernel in the kernel list */
2231d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if ( kernel->next != (KernelInfo *) NULL ) {
22327a01dcf50ce12cb2a789bedff51e9345f022432eanthony    new_kernel->next = CloneKernelInfo(kernel->next);
2233bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    if ( new_kernel->next == (KernelInfo *) NULL )
22347a01dcf50ce12cb2a789bedff51e9345f022432eanthony      return(DestroyKernelInfo(new_kernel));
22351b2bc0a7da432e6e1cc0480280402df213faa940anthony  }
22361b2bc0a7da432e6e1cc0480280402df213faa940anthony
22371b2bc0a7da432e6e1cc0480280402df213faa940anthony  return(new_kernel);
22381b2bc0a7da432e6e1cc0480280402df213faa940anthony}
22391b2bc0a7da432e6e1cc0480280402df213faa940anthony
22401b2bc0a7da432e6e1cc0480280402df213faa940anthony/*
22411b2bc0a7da432e6e1cc0480280402df213faa940anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22421b2bc0a7da432e6e1cc0480280402df213faa940anthony%                                                                             %
22437a01dcf50ce12cb2a789bedff51e9345f022432eanthony%                                                                             %
22444fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22454fd27e21043be809d66c8202e779255e5b660d2danthony%     D e s t r o y K e r n e l I n f o                                       %
22464fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22474fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22484fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22494fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22504fd27e21043be809d66c8202e779255e5b660d2danthony%
225183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
2252602ab9b30b644a78a4057da93d838a77391ec0acanthony%  kernel.
2253602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2254602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the DestroyKernelInfo method is:
2255602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2256602ab9b30b644a78a4057da93d838a77391ec0acanthony%      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
225783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
225883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
2259602ab9b30b644a78a4057da93d838a77391ec0acanthony%
226083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel to be destroyed
2261602ab9b30b644a78a4057da93d838a77391ec0acanthony%
226283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
2263602ab9b30b644a78a4057da93d838a77391ec0acanthonyMagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2264602ab9b30b644a78a4057da93d838a77391ec0acanthony{
2265602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(kernel != (KernelInfo *) NULL);
2266602ab9b30b644a78a4057da93d838a77391ec0acanthony  if (kernel->next != (KernelInfo *) NULL)
2267602ab9b30b644a78a4057da93d838a77391ec0acanthony    kernel->next=DestroyKernelInfo(kernel->next);
2268602ab9b30b644a78a4057da93d838a77391ec0acanthony  kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
226983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
2270602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
22712be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy}
22727a01dcf50ce12cb2a789bedff51e9345f022432eanthony
22739f752c092332bf2c4e599ea49e9422c13f3fb11bcristy/*
2274d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22759f752c092332bf2c4e599ea49e9422c13f3fb11bcristy%                                                                             %
2276602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2277602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2278c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony+     E x p a n d M i r r o r K e r n e l I n f o                             %
2279c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2280c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2281c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2282c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2283c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
2284ce0fd95b65b90e1dc307600bb14b346b777e8137anthony%  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
22853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  sequence of 90-degree rotated kernels but providing a reflected 180
22863c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  rotatation, before the -/+ 90-degree rotations.
22873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
22883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  This special rotation order produces a better, more symetrical thinning of
22893c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  objects.
2290bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2291bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandMirrorKernelInfo method is:
2292bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2293bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandMirrorKernelInfo(KernelInfo *kernel)
2294bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2295bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  A description of each parameter follows:
2296bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2297bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    o kernel: the Morphology/Convolution kernel
2298bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2299bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% This function is only internel to this module, as it is not finalized,
2300bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% especially with regard to non-orthogonal angles, and rotation of larger
2301bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% 2D kernels.
2302bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony*/
2303bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2304bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#if 0
2305bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void FlopKernelInfo(KernelInfo *kernel)
2306bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    { /* Do a Flop by reversing each row. */
2307bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      size_t
2308bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        y;
2309bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register ssize_t
2310bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        x,r;
2311bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register double
2312bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        *k,t;
2313bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2314bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2315bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2316bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          t=k[x],  k[x]=k[r],  k[r]=t;
2317bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2318bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      kernel->x = kernel->width - kernel->x - 1;
2319bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      angle = fmod(angle+180.0, 360.0);
2320bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    }
2321bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#endif
2322bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2323bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandMirrorKernelInfo(KernelInfo *kernel)
2324bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony{
2325bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  KernelInfo
2326bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *clone,
2327bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *last;
2328bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2329bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = kernel;
2330bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2331bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2332bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);   /* flip */
2333bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2334bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2335bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2336bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2337bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 90);   /* transpose */
2338bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2339bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2340bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2341bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2342bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);  /* flop */
2343bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2344bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2345bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  return;
2346bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony}
2347bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2348bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony/*
2349bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2350bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2351bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2352bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2353bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony+     E x p a n d R o t a t e K e r n e l I n f o                             %
2354bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2355bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2356bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2357bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2358bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2359ce0fd95b65b90e1dc307600bb14b346b777e8137anthony%  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2360bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  incrementally by the angle given, until the kernel repeats.
2361bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2362bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  WARNING: 45 degree rotations only works for 3x3 kernels.
2363bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  While 90 degree roatations only works for linear and square kernels
2364bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2365bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandRotateKernelInfo method is:
2366529482f4b494010a13338a74446c510712f670b3anthony%
23673c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
23683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23693c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  A description of each parameter follows:
23703c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2371bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    o kernel: the Morphology/Convolution kernel
23723c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2373bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    o angle: angle to rotate in degrees
23743c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23753c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% This function is only internel to this module, as it is not finalized,
23763c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% especially with regard to non-orthogonal angles, and rotation of larger
23773c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% 2D kernels.
23783c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony*/
23793c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
23803c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony/* Internal Routine - Return true if two kernels are the same */
23813c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthonystatic MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
23823c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony     const KernelInfo *kernel2)
23833c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony{
23843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  register size_t
238547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    i;
238647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
238747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* check size and origin location */
238847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if (    kernel1->width != kernel2->width
238947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony       || kernel1->height != kernel2->height
2390bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy       || kernel1->x != kernel2->x
239147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony       || kernel1->y != kernel2->y )
23921d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony    return MagickFalse;
23931d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
23941d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check actual kernel values */
23951d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  for (i=0; i < (kernel1->width*kernel1->height); i++) {
23961d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony    /* Test for Nan equivalence */
23971d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony    if ( IfNaN(kernel1->values[i]) && !IfNaN(kernel2->values[i]) )
239847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
23991d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony    if ( IfNaN(kernel2->values[i]) && !IfNaN(kernel1->values[i]) )
24001d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony      return MagickFalse;
240147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    /* Test actual values are equivalent */
2402f0a92fd8deb68d411304359906b12679b675691fglennrp    if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
2403a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony      return MagickFalse;
240447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
2405a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony
240647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return MagickTrue;
2407f0a92fd8deb68d411304359906b12679b675691fglennrp}
2408b978e458a8e1f210bcb580951cf623687236b2fecristy
240947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthonystatic void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
241047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony{
24111d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  KernelInfo
241247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *clone,
241347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *last;
241447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
2415bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = kernel;
24163c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthonyDisableMSCWarning(4127)
24173c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  while(1) {
241884d9b5596c0900609dea18795861e2b0936b21c6cristyRestoreMSCWarning
24193c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    clone = CloneKernelInfo(last);
2420a9a61ad96c5112acd968f97b689bd42ca392d70bcristy    RotateKernelInfo(clone, angle);
24213c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    if ( SameKernelInfo(kernel, clone) != MagickFalse )
242247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
242384d9b5596c0900609dea18795861e2b0936b21c6cristy    LastKernelInfo(last)->next = clone;
242484d9b5596c0900609dea18795861e2b0936b21c6cristy    last = clone;
242584d9b5596c0900609dea18795861e2b0936b21c6cristy  }
242647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
2427bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  return;
242884d9b5596c0900609dea18795861e2b0936b21c6cristy}
24293c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
2430bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony/*
243147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24323c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24343c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24353c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony+     C a l c M e t a K e r n a l I n f o                                     %
24363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24383c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
243946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
244146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
244246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  using the kernel values.  This should only ne used if it is not possible to
244346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  calculate that meta-data in some easier way.
244446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
244546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It is important that the meta-data is correct before ScaleKernelInfo() is
2446dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  used to perform kernel normalization.
244746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
244846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the CalcKernelMetaData method is:
244946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
245146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
245346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
245546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Minimum and Maximum values are assumed to include zero, even if
245746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  zero is not part of the kernel (as in Gaussian Derived kernels). This
245846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  however is not true for flat-shaped morphological kernels.
245946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Only the specific kernel pointed to is modified, not a list of
246146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  multiple kernels.
246246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246346a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This is an internal function and not expected to be useful outside this
246446a369d839971ab627bdb31a93d8bd63e81b65a3anthony% module.  This could change however.
246546a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
246646a369d839971ab627bdb31a93d8bd63e81b65a3anthonystatic void CalcKernelMetaData(KernelInfo *kernel)
246746a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
246846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  register size_t
246946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    i;
247046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
247146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->minimum = kernel->maximum = 0.0;
2472bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  kernel->negative_range = kernel->positive_range = 0.0;
247346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  for (i=0; i < (kernel->width*kernel->height); i++)
247446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    {
247546a369d839971ab627bdb31a93d8bd63e81b65a3anthony      if ( fabs(kernel->values[i]) < MagickEpsilon )
247646a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->values[i] = 0.0;
247746a369d839971ab627bdb31a93d8bd63e81b65a3anthony      ( kernel->values[i] < 0)
247846a369d839971ab627bdb31a93d8bd63e81b65a3anthony          ?  ( kernel->negative_range += kernel->values[i] )
247946a369d839971ab627bdb31a93d8bd63e81b65a3anthony          :  ( kernel->positive_range += kernel->values[i] );
248046a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Minimize(kernel->minimum, kernel->values[i]);
248146a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Maximize(kernel->maximum, kernel->values[i]);
248246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    }
248346a369d839971ab627bdb31a93d8bd63e81b65a3anthony
248446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
248546a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
248646a369d839971ab627bdb31a93d8bd63e81b65a3anthony
248746a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
248846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     M o r p h o l o g y A p p l y                                           %
249346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
24969eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2497602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2498602ab9b30b644a78a4057da93d838a77391ec0acanthony%  MorphologyApply() applies a morphological method, multiple times using
2499602ab9b30b644a78a4057da93d838a77391ec0acanthony%  a list of multiple kernels.  This is the method that should be called by
2500602ab9b30b644a78a4057da93d838a77391ec0acanthony%  other 'operators' that internally use morphology operations as part of
2501602ab9b30b644a78a4057da93d838a77391ec0acanthony%  their processing.
25029eb4f74649b23c053b308ce1152dce51239450baanthony%
250322de2722b682eb405b60ec6022a7546df994674eanthony%  It is basically equivalent to as MorphologyImage() (see below) but without
250422de2722b682eb405b60ec6022a7546df994674eanthony%  any user controls.  This allows internel programs to use this method to
250522de2722b682eb405b60ec6022a7546df994674eanthony%  perform a specific task without possible interference by any API user
2506602ab9b30b644a78a4057da93d838a77391ec0acanthony%  supplied settings.
2507ec9ace44c23c302f336f6e672f6857312747d29bcristy%
2508ec9ace44c23c302f336f6e672f6857312747d29bcristy%  It is MorphologyImage() task to extract any such user controls, and
2509ec9ace44c23c302f336f6e672f6857312747d29bcristy%  pass them to this function for processing.
2510ec9ace44c23c302f336f6e672f6857312747d29bcristy%
2511e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  More specifically all given kernels should already be scaled, normalised,
2512f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  and blended appropriatally before being parred to this routine. The
2513e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  appropriate bias, and compose (typically 'UndefinedComposeOp') given.
25149eb4f74649b23c053b308ce1152dce51239450baanthony%
251522de2722b682eb405b60ec6022a7546df994674eanthony%  The format of the MorphologyApply method is:
251622de2722b682eb405b60ec6022a7546df994674eanthony%
251722de2722b682eb405b60ec6022a7546df994674eanthony%      Image *MorphologyApply(const Image *image,MorphologyMethod method,
2518602ab9b30b644a78a4057da93d838a77391ec0acanthony%        const ssize_t iterations,const KernelInfo *kernel,
251947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%        const CompositeMethod compose,const double bias,
2520602ab9b30b644a78a4057da93d838a77391ec0acanthony%        ExceptionInfo *exception)
25219eb4f74649b23c053b308ce1152dce51239450baanthony%
2522f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  A description of each parameter follows:
2523f46d42620631d2581e0b6a56456e203e17c427c8anthony%
2524f46d42620631d2581e0b6a56456e203e17c427c8anthony%    o image: the source image
2525602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2526602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o method: the morphology method to be applied.
2527602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25288d18850dee4bed193a64866a6d2353eeeb73e145anthony%    o iterations: apply the operation this many times (or no change).
2529602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  A value of -1 means loop until no change found.
2530602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  How this is applied may depend on the morphology method.
2531602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  Typically this is a value of 1.
2532602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2533602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o channel: the channel type.
2534602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2535602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: An array of double representing the morphology kernel.
2536602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2537602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o compose: How to handle or merge multi-kernel results.
2538602ab9b30b644a78a4057da93d838a77391ec0acanthony%          If 'UndefinedCompositeOp' use default for the Morphology method.
2539602ab9b30b644a78a4057da93d838a77391ec0acanthony%          If 'NoCompositeOp' force image to be re-iterated by each kernel.
2540602ab9b30b644a78a4057da93d838a77391ec0acanthony%          Otherwise merge the results using the compose method given.
254147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%
25428d18850dee4bed193a64866a6d2353eeeb73e145anthony%    o bias: Convolution Output Bias.
25438d18850dee4bed193a64866a6d2353eeeb73e145anthony%
25448d18850dee4bed193a64866a6d2353eeeb73e145anthony%    o exception: return any errors or warnings in this structure.
254547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%
2546f46d42620631d2581e0b6a56456e203e17c427c8anthony*/
2547f46d42620631d2581e0b6a56456e203e17c427c8anthonystatic ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
25489eb4f74649b23c053b308ce1152dce51239450baanthony  const MorphologyMethod method,const KernelInfo *kernel,const double bias,
2549602ab9b30b644a78a4057da93d838a77391ec0acanthony  ExceptionInfo *exception)
2550602ab9b30b644a78a4057da93d838a77391ec0acanthony{
2551f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy#define MorphologyTag  "Morphology/Image"
2552f46d42620631d2581e0b6a56456e203e17c427c8anthony
2553f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  CacheView
2554602ab9b30b644a78a4057da93d838a77391ec0acanthony    *image_view,
25552be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy    *morphology_view;
2556602ab9b30b644a78a4057da93d838a77391ec0acanthony
25575f959473f334e196c6bf39b740c12cb4963fceebcristy  OffsetInfo
25584c08aed51c5899665ade97263692328eea4af106cristy    offset;
25594c08aed51c5899665ade97263692328eea4af106cristy
25605f959473f334e196c6bf39b740c12cb4963fceebcristy  register ssize_t
2561ec9ace44c23c302f336f6e672f6857312747d29bcristy    i;
2562ec9ace44c23c302f336f6e672f6857312747d29bcristy
2563ec9ace44c23c302f336f6e672f6857312747d29bcristy  ssize_t
2564c5876076825b539bd230ddadab1d876805c18291cristy    y;
2565c5876076825b539bd230ddadab1d876805c18291cristy
2566c5876076825b539bd230ddadab1d876805c18291cristy  size_t
2567bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    *changes,
2568ec9ace44c23c302f336f6e672f6857312747d29bcristy    changed,
2569a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    width;
2570a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
2571306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  MagickBooleanType
2572306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    status;
2573306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy
2574602ab9b30b644a78a4057da93d838a77391ec0acanthony  MagickOffsetType
2575602ab9b30b644a78a4057da93d838a77391ec0acanthony    progress;
2576602ab9b30b644a78a4057da93d838a77391ec0acanthony
2577602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image != (Image *) NULL);
25785f959473f334e196c6bf39b740c12cb4963fceebcristy  assert(image->signature == MagickSignature);
25795f959473f334e196c6bf39b740c12cb4963fceebcristy  assert(morphology_image != (Image *) NULL);
2580602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(morphology_image->signature == MagickSignature);
2581e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel != (KernelInfo *) NULL);
2582e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel->signature == MagickSignature);
25834c08aed51c5899665ade97263692328eea4af106cristy  assert(exception != (ExceptionInfo *) NULL);
25844c08aed51c5899665ade97263692328eea4af106cristy  assert(exception->signature == MagickSignature);
2585e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  status=MagickTrue;
2586e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  progress=0;
2587e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  image_view=AcquireVirtualCacheView(image,exception);
2588e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
2589602ab9b30b644a78a4057da93d838a77391ec0acanthony  width=image->columns+kernel->width-1;
2590602ab9b30b644a78a4057da93d838a77391ec0acanthony  offset.x=0.0;
259146ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  offset.y=0.0;
259246ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  switch (method)
2593ec9ace44c23c302f336f6e672f6857312747d29bcristy  {
2594ec9ace44c23c302f336f6e672f6857312747d29bcristy    case ConvolveMorphology:
2595ec9ace44c23c302f336f6e672f6857312747d29bcristy    case DilateMorphology:
2596930be614b4595b97cd79ee864a394796740f76adanthony    case DilateIntensityMorphology:
2597930be614b4595b97cd79ee864a394796740f76adanthony    case IterativeDistanceMorphology:
2598930be614b4595b97cd79ee864a394796740f76adanthony    {
2599f34d9b2df49a407af764c79e07d587af0600983aanthony      /*
2600ec9ace44c23c302f336f6e672f6857312747d29bcristy        Kernel needs to used with reflection about origin.
2601ec9ace44c23c302f336f6e672f6857312747d29bcristy      */
2602ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=(ssize_t) kernel->width-kernel->x-1;
2603ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=(ssize_t) kernel->height-kernel->y-1;
2604ec9ace44c23c302f336f6e672f6857312747d29bcristy      break;
2605ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
260629188a8682a98d4b7882cca434b170517555fc7danthony    case ErodeMorphology:
2607ec9ace44c23c302f336f6e672f6857312747d29bcristy    case ErodeIntensityMorphology:
26085ef8e94ff55717be2387d537bd49025780a1a558anthony    case HitAndMissMorphology:
26095ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThinningMorphology:
26105ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThickenMorphology:
26115ef8e94ff55717be2387d537bd49025780a1a558anthony    {
26125ef8e94ff55717be2387d537bd49025780a1a558anthony      offset.x=kernel->x;
2613ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=kernel->y;
2614ec9ace44c23c302f336f6e672f6857312747d29bcristy      break;
2615ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
26165ef8e94ff55717be2387d537bd49025780a1a558anthony    default:
2617ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2618930be614b4595b97cd79ee864a394796740f76adanthony      assert("Not a Primitive Morphology Method" != (char *) NULL);
2619ec9ace44c23c302f336f6e672f6857312747d29bcristy      break;
26209eb4f74649b23c053b308ce1152dce51239450baanthony    }
2621930be614b4595b97cd79ee864a394796740f76adanthony  }
2622ec9ace44c23c302f336f6e672f6857312747d29bcristy  changed=0;
262329188a8682a98d4b7882cca434b170517555fc7danthony  changes=(size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
2624c5876076825b539bd230ddadab1d876805c18291cristy    sizeof(*changes));
2625306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  if (changes == (size_t *) NULL)
2626306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
2627306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2628306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    changes[i]=0;
2629c5876076825b539bd230ddadab1d876805c18291cristy  if ((method == ConvolveMorphology) && (kernel->width == 1))
2630c5876076825b539bd230ddadab1d876805c18291cristy    {
2631ec9ace44c23c302f336f6e672f6857312747d29bcristy      const int
2632780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        id = GetOpenMPThreadId();
2633c5876076825b539bd230ddadab1d876805c18291cristy
2634c5876076825b539bd230ddadab1d876805c18291cristy      register ssize_t
2635c5876076825b539bd230ddadab1d876805c18291cristy        x;
2636780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2637780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      /*
2638fd1175952254cf1ac848ddb441e483c5e33d517fcristy        Special handling (for speed) of vertical (blur) kernels.  This performs
2639780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        its handling in columns rather than in rows.  This is only done
2640ec9ace44c23c302f336f6e672f6857312747d29bcristy        for convolve as it is the only method that generates very large 1-D
2641ec9ace44c23c302f336f6e672f6857312747d29bcristy        vertical kernels (such as a 'BlurKernel')
2642ec9ace44c23c302f336f6e672f6857312747d29bcristy     */
2643ec9ace44c23c302f336f6e672f6857312747d29bcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
2644780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy     #pragma omp parallel for schedule(static,4) shared(progress,status) \
26458d18850dee4bed193a64866a6d2353eeeb73e145anthony       magick_threads(image,morphology_image,image->columns,1)
2646c5876076825b539bd230ddadab1d876805c18291cristy#endif
2647780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      for (x=0; x < (ssize_t) image->columns; x++)
26488d18850dee4bed193a64866a6d2353eeeb73e145anthony      {
2649780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register const Quantum
2650780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          *restrict p;
2651780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2652780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register Quantum
26538d18850dee4bed193a64866a6d2353eeeb73e145anthony          *restrict q;
2654780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2655780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register ssize_t
26568d18850dee4bed193a64866a6d2353eeeb73e145anthony          y;
2657780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2658780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        ssize_t
26598d18850dee4bed193a64866a6d2353eeeb73e145anthony          center;
2660780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2661780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (status == MagickFalse)
26628d18850dee4bed193a64866a6d2353eeeb73e145anthony          continue;
2663780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
26648d18850dee4bed193a64866a6d2353eeeb73e145anthony          kernel->height-1,exception);
2665ec9ace44c23c302f336f6e672f6857312747d29bcristy        q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2666780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          morphology_image->rows,exception);
2667780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2668780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2669780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            status=MagickFalse;
2670780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            continue;
2671780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
2672780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        center=(ssize_t) GetPixelChannels(image)*offset.y;
2673780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        for (y=0; y < (ssize_t) image->rows; y++)
2674ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2675780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          register ssize_t
2676f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy            i;
2677780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2678780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
26798d18850dee4bed193a64866a6d2353eeeb73e145anthony          {
2680780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            double
2681780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              alpha,
2682780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              gamma,
2683780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              pixel;
2684780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
26858d18850dee4bed193a64866a6d2353eeeb73e145anthony            PixelChannel
2686780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              channel;
2687780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2688d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            PixelTrait
2689780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              morphology_traits,
2690780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              traits;
2691780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
26928d18850dee4bed193a64866a6d2353eeeb73e145anthony            register const MagickRealType
2693780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              *restrict k;
2694780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
26958d18850dee4bed193a64866a6d2353eeeb73e145anthony            register const Quantum
2696780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              *restrict pixels;
2697780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
26988d18850dee4bed193a64866a6d2353eeeb73e145anthony            register ssize_t
2699780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              u;
2700780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2701f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy            size_t
2702780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              count;
2703780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2704780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            ssize_t
2705780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              v;
2706780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2707780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            channel=GetPixelChannelChannel(image,i);
2708780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            traits=GetPixelChannelTraits(image,channel);
2709780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2710f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy            if ((traits == UndefinedPixelTrait) ||
2711780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                (morphology_traits == UndefinedPixelTrait))
2712883fde11debec15cedb05dc5d7228d8588066bc0cristy              continue;
2713f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy            if (GetPixelReadMask(image,p+center) == 0)
2714780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              {
2715780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                SetPixelChannel(morphology_image,channel,p[center+i],q);
2716780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                continue;
2717ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
2718780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            k=(&kernel->values[kernel->width*kernel->height-1]);
2719780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            pixels=p;
2720780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            pixel=bias;
2721780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if ((morphology_traits & BlendPixelTrait) == 0)
2722780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              {
2723780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                /*
2724780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  No alpha blending.
2725780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                */
2726f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy                for (v=0; v < (ssize_t) kernel->height; v++)
2727780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                {
2728780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  for (u=0; u < (ssize_t) kernel->width; u++)
2729a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                  {
2730a62cd0834ed954440c88fb0772413489f1cbd0afcristy                    if (IfNaN(*k) == MagickFalse)
2731780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                      pixel+=(*k)*pixels[i];
2732780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    k--;
2733780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    pixels+=GetPixelChannels(image);
2734f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy                  }
2735780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                }
2736c5876076825b539bd230ddadab1d876805c18291cristy                if (fabs(pixel-p[center+i]) > MagickEpsilon)
2737780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  changes[id]++;
2738780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),
2739780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  q);
2740f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy                continue;
2741780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              }
2742780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            /*
2743780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              Alpha blending.
274433c51e0d3335ba3b35248454fe61c1805f65a115cristy            */
2745f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy            gamma=0.0;
2746780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            count=0;
2747780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            for (v=0; v < (ssize_t) kernel->height; v++)
2748a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony            {
2749a62cd0834ed954440c88fb0772413489f1cbd0afcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2750a62cd0834ed954440c88fb0772413489f1cbd0afcristy              {
2751a62cd0834ed954440c88fb0772413489f1cbd0afcristy                if (IfNaN(*k) == MagickFalse)
2752a62cd0834ed954440c88fb0772413489f1cbd0afcristy                  {
2753780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2754780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    pixel+=(*k)*alpha*pixels[i];
2755780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    gamma+=(*k)*alpha;
27568d18850dee4bed193a64866a6d2353eeeb73e145anthony                    count++;
2757780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  }
2758c5876076825b539bd230ddadab1d876805c18291cristy                k--;
2759780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                pixels+=GetPixelChannels(image);
27608d18850dee4bed193a64866a6d2353eeeb73e145anthony              }
2761780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            }
2762780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (fabs(pixel-p[center+i]) > MagickEpsilon)
2763ec9ace44c23c302f336f6e672f6857312747d29bcristy              changes[id]++;
2764780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            gamma=PerceptibleReciprocal(gamma);
2765780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (count != 0)
2766780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              gamma*=(double) kernel->height*kernel->width/count;
2767780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
2768780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              pixel),q);
2769780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
27708d18850dee4bed193a64866a6d2353eeeb73e145anthony          p+=GetPixelChannels(image);
27718d18850dee4bed193a64866a6d2353eeeb73e145anthony          q+=GetPixelChannels(morphology_image);
2772954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        }
27738d18850dee4bed193a64866a6d2353eeeb73e145anthony        if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
2774ec9ace44c23c302f336f6e672f6857312747d29bcristy          status=MagickFalse;
2775ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (image->progress_monitor != (MagickProgressMonitor) NULL)
2776780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2777780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            MagickBooleanType
2778780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              proceed;
2779780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2780780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
2781780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            #pragma omp critical (MagickCore_MorphologyPrimitive)
2782780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy#endif
2783c5876076825b539bd230ddadab1d876805c18291cristy            proceed=SetImageProgress(image,MorphologyTag,progress++,
2784c5876076825b539bd230ddadab1d876805c18291cristy              image->rows);
2785306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy            if (proceed == MagickFalse)
2786780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              status=MagickFalse;
2787780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
27888d18850dee4bed193a64866a6d2353eeeb73e145anthony      }
2789780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      morphology_image->type=image->type;
27908d18850dee4bed193a64866a6d2353eeeb73e145anthony      morphology_view=DestroyCacheView(morphology_view);
2791602ab9b30b644a78a4057da93d838a77391ec0acanthony      image_view=DestroyCacheView(image_view);
2792c5876076825b539bd230ddadab1d876805c18291cristy      for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
27935e6b259130f9dbe0da4666f734937017babe573acristy        changed+=changes[i];
2794602ab9b30b644a78a4057da93d838a77391ec0acanthony      changes=(size_t *) RelinquishMagickMemory(changes);
2795bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      return(status ? (ssize_t) changed : 0);
2796602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
2797c5876076825b539bd230ddadab1d876805c18291cristy  /*
2798c5876076825b539bd230ddadab1d876805c18291cristy    Normal handling of horizontal or rectangular kernels (row by row).
2799c5876076825b539bd230ddadab1d876805c18291cristy  */
28004c08aed51c5899665ade97263692328eea4af106cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
2801602ab9b30b644a78a4057da93d838a77391ec0acanthony  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2802602ab9b30b644a78a4057da93d838a77391ec0acanthony    magick_threads(image,morphology_image,image->rows,1)
28034c08aed51c5899665ade97263692328eea4af106cristy#endif
2804602ab9b30b644a78a4057da93d838a77391ec0acanthony  for (y=0; y < (ssize_t) image->rows; y++)
2805602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
2806bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    const int
2807602ab9b30b644a78a4057da93d838a77391ec0acanthony      id = GetOpenMPThreadId();
2808602ab9b30b644a78a4057da93d838a77391ec0acanthony
2809ec9ace44c23c302f336f6e672f6857312747d29bcristy    register const Quantum
2810ec9ace44c23c302f336f6e672f6857312747d29bcristy      *restrict p;
2811602ab9b30b644a78a4057da93d838a77391ec0acanthony
2812602ab9b30b644a78a4057da93d838a77391ec0acanthony    register Quantum
2813602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict q;
2814ec9ace44c23c302f336f6e672f6857312747d29bcristy
2815af43471dfedff5d1314cd674fac6508d230df1bccristy    register ssize_t
2816af43471dfedff5d1314cd674fac6508d230df1bccristy      x;
2817af43471dfedff5d1314cd674fac6508d230df1bccristy
28184c08aed51c5899665ade97263692328eea4af106cristy    ssize_t
2819602ab9b30b644a78a4057da93d838a77391ec0acanthony      center;
2820602ab9b30b644a78a4057da93d838a77391ec0acanthony
2821602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (status == MagickFalse)
2822602ab9b30b644a78a4057da93d838a77391ec0acanthony      continue;
2823ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
2824ec9ace44c23c302f336f6e672f6857312747d29bcristy      kernel->height,exception);
2825bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
2826602ab9b30b644a78a4057da93d838a77391ec0acanthony      1,exception);
2827ec9ace44c23c302f336f6e672f6857312747d29bcristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2828ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
2829602ab9b30b644a78a4057da93d838a77391ec0acanthony        status=MagickFalse;
2830ec9ace44c23c302f336f6e672f6857312747d29bcristy        continue;
2831ec9ace44c23c302f336f6e672f6857312747d29bcristy      }
2832ec9ace44c23c302f336f6e672f6857312747d29bcristy    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
2833ec9ace44c23c302f336f6e672f6857312747d29bcristy      GetPixelChannels(image)*offset.x);
2834ec9ace44c23c302f336f6e672f6857312747d29bcristy    for (x=0; x < (ssize_t) image->columns; x++)
2835ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2836ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
2837602ab9b30b644a78a4057da93d838a77391ec0acanthony        i;
2838ec9ace44c23c302f336f6e672f6857312747d29bcristy
2839ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2840602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2841ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
2842ec9ace44c23c302f336f6e672f6857312747d29bcristy          alpha,
2843ec9ace44c23c302f336f6e672f6857312747d29bcristy          gamma,
2844ec9ace44c23c302f336f6e672f6857312747d29bcristy          maximum,
2845ec9ace44c23c302f336f6e672f6857312747d29bcristy          minimum,
2846ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
2847ec9ace44c23c302f336f6e672f6857312747d29bcristy
2848ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelChannel
2849ec9ace44c23c302f336f6e672f6857312747d29bcristy          channel;
2850ec9ace44c23c302f336f6e672f6857312747d29bcristy
2851ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
2852ec9ace44c23c302f336f6e672f6857312747d29bcristy          morphology_traits,
2853602ab9b30b644a78a4057da93d838a77391ec0acanthony          traits;
2854ec9ace44c23c302f336f6e672f6857312747d29bcristy
2855ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
2856ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
2857ec9ace44c23c302f336f6e672f6857312747d29bcristy
2858ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
2859ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
2860ec9ace44c23c302f336f6e672f6857312747d29bcristy
2861ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
2862ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
2863ec9ace44c23c302f336f6e672f6857312747d29bcristy
2864883fde11debec15cedb05dc5d7228d8588066bc0cristy        size_t
2865ec9ace44c23c302f336f6e672f6857312747d29bcristy          count;
2866ec9ace44c23c302f336f6e672f6857312747d29bcristy
2867ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
2868ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
2869ec9ace44c23c302f336f6e672f6857312747d29bcristy
2870ec9ace44c23c302f336f6e672f6857312747d29bcristy        channel=GetPixelChannelChannel(image,i);
2871ec9ace44c23c302f336f6e672f6857312747d29bcristy        traits=GetPixelChannelTraits(image,channel);
2872ec9ace44c23c302f336f6e672f6857312747d29bcristy        morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2873ec9ace44c23c302f336f6e672f6857312747d29bcristy        if ((traits == UndefinedPixelTrait) ||
2874ec9ace44c23c302f336f6e672f6857312747d29bcristy            (morphology_traits == UndefinedPixelTrait))
2875ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
2876ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (GetPixelReadMask(image,p+center) == 0)
2877ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2878ec9ace44c23c302f336f6e672f6857312747d29bcristy            SetPixelChannel(morphology_image,channel,p[center+i],q);
2879ec9ace44c23c302f336f6e672f6857312747d29bcristy            continue;
2880ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2881ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
2882ec9ace44c23c302f336f6e672f6857312747d29bcristy        maximum=0.0;
2883ec9ace44c23c302f336f6e672f6857312747d29bcristy        minimum=(double) QuantumRange;
2884ec9ace44c23c302f336f6e672f6857312747d29bcristy        count=kernel->width*kernel->height;
2885ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
2886ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2887ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ConvolveMorphology: pixel=bias; break;
2888ec9ace44c23c302f336f6e672f6857312747d29bcristy          case HitAndMissMorphology: pixel=(double) QuantumRange; break;
2889ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThinningMorphology: pixel=(double) QuantumRange; break;
2890ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThickenMorphology: pixel=(double) QuantumRange; break;
2891ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeMorphology: pixel=(double) QuantumRange; break;
2892ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateMorphology: pixel=0.0; break;
2893ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeIntensityMorphology:
2894ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateIntensityMorphology:
2895ec9ace44c23c302f336f6e672f6857312747d29bcristy          case IterativeDistanceMorphology:
2896ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2897ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixel=(double) p[center+i];
2898ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
2899ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2900ec9ace44c23c302f336f6e672f6857312747d29bcristy          default: pixel=0; break;
2901ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
2902ec9ace44c23c302f336f6e672f6857312747d29bcristy        gamma=1.0;
2903ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
2904ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2905ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ConvolveMorphology:
2906ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2907ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2908ec9ace44c23c302f336f6e672f6857312747d29bcristy               Weighted Average of pixels using reflected kernel
2909ec9ace44c23c302f336f6e672f6857312747d29bcristy
2910ec9ace44c23c302f336f6e672f6857312747d29bcristy               For correct working of this operation for asymetrical
2911930be614b4595b97cd79ee864a394796740f76adanthony               kernels, the kernel needs to be applied in its reflected form.
2912ec9ace44c23c302f336f6e672f6857312747d29bcristy               That is its values needs to be reversed.
2913ec9ace44c23c302f336f6e672f6857312747d29bcristy
2914ec9ace44c23c302f336f6e672f6857312747d29bcristy               Correlation is actually the same as this but without reflecting
2915ec9ace44c23c302f336f6e672f6857312747d29bcristy               the kernel, and thus 'lower-level' that Convolution.  However
2916ec9ace44c23c302f336f6e672f6857312747d29bcristy               as Convolution is the more common method used, and it does not
29178d18850dee4bed193a64866a6d2353eeeb73e145anthony               really cost us much in terms of processing to use a reflected
2918ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernel, so it is Convolution that is implemented.
2919ec9ace44c23c302f336f6e672f6857312747d29bcristy
2920ec9ace44c23c302f336f6e672f6857312747d29bcristy               Correlation will have its kernel reflected before calling
2921ec9ace44c23c302f336f6e672f6857312747d29bcristy               this function to do a Convolve.
2922a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony
292313ffe4b75e0c768d1e6506c4d5e5881bb9124763cristy               For more details of Correlation vs Convolution see
2924ec9ace44c23c302f336f6e672f6857312747d29bcristy                 http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
2925ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
29268d18850dee4bed193a64866a6d2353eeeb73e145anthony            k=(&kernel->values[kernel->width*kernel->height-1]);
2927b772effec6594fbd2b304bb685cfa6226f48780bcristy            if ((morphology_traits & BlendPixelTrait) == 0)
29288d18850dee4bed193a64866a6d2353eeeb73e145anthony              {
29298693d3f324310fc2ca933cd239aa053d0651e0b6cristy                /*
29308d18850dee4bed193a64866a6d2353eeeb73e145anthony                  No alpha blending.
2931ec9ace44c23c302f336f6e672f6857312747d29bcristy                */
2932ec9ace44c23c302f336f6e672f6857312747d29bcristy                for (v=0; v < (ssize_t) kernel->height; v++)
2933ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
293433c51e0d3335ba3b35248454fe61c1805f65a115cristy                  for (u=0; u < (ssize_t) kernel->width; u++)
2935ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2936ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (IfNaN(*k) == MagickFalse)
2937ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel+=(*k)*pixels[i];
2938a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                    k--;
2939ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixels+=GetPixelChannels(image);
2940ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
2941ec9ace44c23c302f336f6e672f6857312747d29bcristy                  pixels+=(image->columns-1)*GetPixelChannels(image);
2942602ab9b30b644a78a4057da93d838a77391ec0acanthony                }
2943ec9ace44c23c302f336f6e672f6857312747d29bcristy                break;
2944ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
2945602ab9b30b644a78a4057da93d838a77391ec0acanthony            /*
2946b772effec6594fbd2b304bb685cfa6226f48780bcristy              Alpha blending.
2947ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
2948602ab9b30b644a78a4057da93d838a77391ec0acanthony            count=0;
2949ec9ace44c23c302f336f6e672f6857312747d29bcristy            gamma=0.0;
2950ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
2951ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2952ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2953ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2954602ab9b30b644a78a4057da93d838a77391ec0acanthony                if (IfNaN(*k) == MagickFalse)
2955ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2956ec9ace44c23c302f336f6e672f6857312747d29bcristy                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2957ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel+=alpha*(*k)*pixels[i];
2958ec9ace44c23c302f336f6e672f6857312747d29bcristy                    gamma+=alpha*(*k);
2959930be614b4595b97cd79ee864a394796740f76adanthony                    count++;
2960ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
2961ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
2962ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
2963ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
2964ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
2965a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony            }
2966ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
2967ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2968ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeMorphology:
2969ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2970ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2971ec9ace44c23c302f336f6e672f6857312747d29bcristy              Minimum value within kernel neighbourhood.
2972602ab9b30b644a78a4057da93d838a77391ec0acanthony
2973b772effec6594fbd2b304bb685cfa6226f48780bcristy              The kernel is not reflected for this operation.  In normal
2974602ab9b30b644a78a4057da93d838a77391ec0acanthony              Greyscale Morphology, the kernel value should be added
2975602ab9b30b644a78a4057da93d838a77391ec0acanthony              to the real value, this is currently not done, due to the
2976ec9ace44c23c302f336f6e672f6857312747d29bcristy              nature of the boolean kernels being used.
2977ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
2978ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
2979ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
2980ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2981ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2982ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2983ec9ace44c23c302f336f6e672f6857312747d29bcristy                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
2984ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2985602ab9b30b644a78a4057da93d838a77391ec0acanthony                    if ((double) pixels[i] < pixel)
2986ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i];
2987ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
2988ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
2989930be614b4595b97cd79ee864a394796740f76adanthony                pixels+=GetPixelChannels(image);
2990ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
2991ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
2992ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
2993ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
2994ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2995a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony          case DilateMorphology:
2996ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2997ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2998ec9ace44c23c302f336f6e672f6857312747d29bcristy               Maximum value within kernel neighbourhood.
2999ec9ace44c23c302f336f6e672f6857312747d29bcristy
3000ec9ace44c23c302f336f6e672f6857312747d29bcristy               For correct working of this operation for asymetrical kernels,
3001ec9ace44c23c302f336f6e672f6857312747d29bcristy               the kernel needs to be applied in its reflected form.  That is
3002602ab9b30b644a78a4057da93d838a77391ec0acanthony               its values needs to be reversed.
3003b772effec6594fbd2b304bb685cfa6226f48780bcristy
3004602ab9b30b644a78a4057da93d838a77391ec0acanthony               In normal Greyscale Morphology, the kernel value should be
3005602ab9b30b644a78a4057da93d838a77391ec0acanthony               added to the real value, this is currently not done, due to the
3006ec9ace44c23c302f336f6e672f6857312747d29bcristy               nature of the boolean kernels being used.
3007ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
3008ec9ace44c23c302f336f6e672f6857312747d29bcristy            count=0;
3009ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3010ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3011ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3012ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3013602ab9b30b644a78a4057da93d838a77391ec0acanthony              {
3014ec9ace44c23c302f336f6e672f6857312747d29bcristy                if ((IfNaN(*k) == MagickFalse) && (*k > 0.5))
3015ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3016ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((double) pixels[i] > pixel)
3017ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i];
3018ec9ace44c23c302f336f6e672f6857312747d29bcristy                    count++;
3019ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3020ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3021ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
30225ef8e94ff55717be2387d537bd49025780a1a558anthony              }
3023ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3024ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3025ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3026ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3027ec9ace44c23c302f336f6e672f6857312747d29bcristy          case HitAndMissMorphology:
3028a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony          case ThinningMorphology:
3029ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThickenMorphology:
3030ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3031ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3032ec9ace44c23c302f336f6e672f6857312747d29bcristy               Minimum of foreground pixel minus maxumum of background pixels.
3033ec9ace44c23c302f336f6e672f6857312747d29bcristy
3034ec9ace44c23c302f336f6e672f6857312747d29bcristy               The kernel is not reflected for this operation, and consists
3035ec9ace44c23c302f336f6e672f6857312747d29bcristy               of both foreground and background pixel neighbourhoods, 0.0 for
3036ec9ace44c23c302f336f6e672f6857312747d29bcristy               background, and 1.0 for foreground with either Nan or 0.5 values
3037ec9ace44c23c302f336f6e672f6857312747d29bcristy               for don't care.
3038ec9ace44c23c302f336f6e672f6857312747d29bcristy
3039ec9ace44c23c302f336f6e672f6857312747d29bcristy               This never produces a meaningless negative result.  Such results
3040ec9ace44c23c302f336f6e672f6857312747d29bcristy               cause Thinning/Thicken to not work correctly when used against a
3041ec9ace44c23c302f336f6e672f6857312747d29bcristy               greyscale image.
3042ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
3043ec9ace44c23c302f336f6e672f6857312747d29bcristy            count=0;
30445ef8e94ff55717be2387d537bd49025780a1a558anthony            k=kernel->values;
3045b772effec6594fbd2b304bb685cfa6226f48780bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
30465ef8e94ff55717be2387d537bd49025780a1a558anthony            {
3047ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3048ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3049ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IfNaN(*k) == MagickFalse)
3050ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3051ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (*k > 0.7)
3052ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3053ec9ace44c23c302f336f6e672f6857312747d29bcristy                        if ((double) pixels[i] < pixel)
3054ec9ace44c23c302f336f6e672f6857312747d29bcristy                          pixel=(double) pixels[i];
30555ef8e94ff55717be2387d537bd49025780a1a558anthony                      }
3056ec9ace44c23c302f336f6e672f6857312747d29bcristy                    else
3057ec9ace44c23c302f336f6e672f6857312747d29bcristy                      if (*k < 0.3)
3058ec9ace44c23c302f336f6e672f6857312747d29bcristy                        {
3059ec9ace44c23c302f336f6e672f6857312747d29bcristy                          if ((double) pixels[i] > maximum)
3060ec9ace44c23c302f336f6e672f6857312747d29bcristy                            maximum=(double) pixels[i];
30616fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                        }
3062ec9ace44c23c302f336f6e672f6857312747d29bcristy                    count++;
3063930be614b4595b97cd79ee864a394796740f76adanthony                  }
3064ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
3065ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3066ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
3067ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3068ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3069a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony            pixel-=maximum;
3070ec9ace44c23c302f336f6e672f6857312747d29bcristy            if (pixel < 0.0)
3071ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixel=0.0;
3072ec9ace44c23c302f336f6e672f6857312747d29bcristy            if (method ==  ThinningMorphology)
3073ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixel=(double) p[center+i]-pixel;
3074ec9ace44c23c302f336f6e672f6857312747d29bcristy            else
3075ec9ace44c23c302f336f6e672f6857312747d29bcristy              if (method ==  ThickenMorphology)
3076ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixel+=(double) p[center+i]+pixel;
3077ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3078ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3079602ab9b30b644a78a4057da93d838a77391ec0acanthony          case ErodeIntensityMorphology:
3080b772effec6594fbd2b304bb685cfa6226f48780bcristy          {
3081602ab9b30b644a78a4057da93d838a77391ec0acanthony            /*
3082602ab9b30b644a78a4057da93d838a77391ec0acanthony              Select pixel with minimum intensity within kernel neighbourhood.
3083ec9ace44c23c302f336f6e672f6857312747d29bcristy
3084ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.
3085ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
3086ec9ace44c23c302f336f6e672f6857312747d29bcristy            count=0;
3087ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
30886fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            for (v=0; v < (ssize_t) kernel->height; v++)
3089ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3090930be614b4595b97cd79ee864a394796740f76adanthony              for (u=0; u < (ssize_t) kernel->width; u++)
3091ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3092ec9ace44c23c302f336f6e672f6857312747d29bcristy                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
3093ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3094ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (GetPixelIntensity(image,pixels) < minimum)
3095ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3096a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                        pixel=(double) pixels[i];
3097ec9ace44c23c302f336f6e672f6857312747d29bcristy                        minimum=GetPixelIntensity(image,pixels);
3098ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3099ec9ace44c23c302f336f6e672f6857312747d29bcristy                    count++;
3100ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3101ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
3102ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3103ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
3104ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3105ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3106602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3107b772effec6594fbd2b304bb685cfa6226f48780bcristy          }
3108602ab9b30b644a78a4057da93d838a77391ec0acanthony          case DilateIntensityMorphology:
3109602ab9b30b644a78a4057da93d838a77391ec0acanthony          {
3110ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3111ec9ace44c23c302f336f6e672f6857312747d29bcristy              Select pixel with maximum intensity within kernel neighbourhood.
3112ec9ace44c23c302f336f6e672f6857312747d29bcristy
3113ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.
3114ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
3115ec9ace44c23c302f336f6e672f6857312747d29bcristy            count=0;
3116ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
31176fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            for (v=0; v < (ssize_t) kernel->height; v++)
3118ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3119ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3120ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3121ec9ace44c23c302f336f6e672f6857312747d29bcristy                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
31226fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                  {
3123ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (GetPixelIntensity(image,pixels) > maximum)
3124ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
31256fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                        pixel=(double) pixels[i];
3126ec9ace44c23c302f336f6e672f6857312747d29bcristy                        maximum=GetPixelIntensity(image,pixels);
3127ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
31286fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                    count++;
3129ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3130ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
31316fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                pixels+=GetPixelChannels(image);
3132ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
3133ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3134ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3135930be614b4595b97cd79ee864a394796740f76adanthony            break;
3136ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3137ec9ace44c23c302f336f6e672f6857312747d29bcristy          case IterativeDistanceMorphology:
3138ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3139ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3140ec9ace44c23c302f336f6e672f6857312747d29bcristy               Compute th iterative distance from black edge of a white image
3141a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony               shape.  Essentually white values are decreased to the smallest
3142ec9ace44c23c302f336f6e672f6857312747d29bcristy               'distance from edge' it can find.
3143ec9ace44c23c302f336f6e672f6857312747d29bcristy
3144ec9ace44c23c302f336f6e672f6857312747d29bcristy               It works by adding kernel values to the neighbourhood, and and
3145ec9ace44c23c302f336f6e672f6857312747d29bcristy               select the minimum value found. The kernel is rotated before
3146ec9ace44c23c302f336f6e672f6857312747d29bcristy               use, so kernel distances match resulting distances, when a user
3147ec9ace44c23c302f336f6e672f6857312747d29bcristy               provided asymmetric kernel is applied.
3148602ab9b30b644a78a4057da93d838a77391ec0acanthony
3149b772effec6594fbd2b304bb685cfa6226f48780bcristy               This code is nearly identical to True GrayScale Morphology but
3150602ab9b30b644a78a4057da93d838a77391ec0acanthony               not quite.
3151602ab9b30b644a78a4057da93d838a77391ec0acanthony
3152ec9ace44c23c302f336f6e672f6857312747d29bcristy               GreyDilate Kernel values added, maximum value found Kernel is
3153ec9ace44c23c302f336f6e672f6857312747d29bcristy               rotated before use.
3154ec9ace44c23c302f336f6e672f6857312747d29bcristy
3155ec9ace44c23c302f336f6e672f6857312747d29bcristy               GrayErode:  Kernel values subtracted and minimum value found No
3156ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernel rotation used.
3157ec9ace44c23c302f336f6e672f6857312747d29bcristy
3158c5876076825b539bd230ddadab1d876805c18291cristy               Note the the Iterative Distance method is essentially a
3159ec9ace44c23c302f336f6e672f6857312747d29bcristy               GrayErode, but with negative kernel values, and kernel rotation
316083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony               applied.
3161ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            */
3162ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            count=0;
3163ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
31644c08aed51c5899665ade97263692328eea4af106cristy            for (v=0; v < (ssize_t) kernel->height; v++)
3165602ab9b30b644a78a4057da93d838a77391ec0acanthony            {
3166602ab9b30b644a78a4057da93d838a77391ec0acanthony              for (u=0; u < (ssize_t) kernel->width; u++)
3167602ab9b30b644a78a4057da93d838a77391ec0acanthony              {
3168602ab9b30b644a78a4057da93d838a77391ec0acanthony                if (IfNaN(*k) == MagickFalse)
3169602ab9b30b644a78a4057da93d838a77391ec0acanthony                  {
3170602ab9b30b644a78a4057da93d838a77391ec0acanthony                    if ((pixels[i]+(*k)) < pixel)
3171602ab9b30b644a78a4057da93d838a77391ec0acanthony                      pixel=(double) pixels[i]+(*k);
3172954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy                    count++;
3173602ab9b30b644a78a4057da93d838a77391ec0acanthony                  }
3174602ab9b30b644a78a4057da93d838a77391ec0acanthony                k--;
3175602ab9b30b644a78a4057da93d838a77391ec0acanthony                pixels+=GetPixelChannels(image);
3176602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3177602ab9b30b644a78a4057da93d838a77391ec0acanthony              pixels+=(image->columns-1)*GetPixelChannels(image);
3178ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
31794c08aed51c5899665ade97263692328eea4af106cristy            break;
31804c08aed51c5899665ade97263692328eea4af106cristy          }
3181c5876076825b539bd230ddadab1d876805c18291cristy          case UndefinedMorphology:
3182c5876076825b539bd230ddadab1d876805c18291cristy          default:
3183306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy            break;
3184c5876076825b539bd230ddadab1d876805c18291cristy        }
3185602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (fabs(pixel-p[center+i]) > MagickEpsilon)
3186602ab9b30b644a78a4057da93d838a77391ec0acanthony          changes[id]++;
3187ec9ace44c23c302f336f6e672f6857312747d29bcristy        gamma=PerceptibleReciprocal(gamma);
3188ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (count != 0)
3189ec9ace44c23c302f336f6e672f6857312747d29bcristy          gamma*=(double) kernel->height*kernel->width/count;
3190ec9ace44c23c302f336f6e672f6857312747d29bcristy        SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*pixel),q);
3191ec9ace44c23c302f336f6e672f6857312747d29bcristy      }
3192ec9ace44c23c302f336f6e672f6857312747d29bcristy      p+=GetPixelChannels(image);
3193ec9ace44c23c302f336f6e672f6857312747d29bcristy      q+=GetPixelChannels(morphology_image);
3194ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3195ec9ace44c23c302f336f6e672f6857312747d29bcristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3196ec9ace44c23c302f336f6e672f6857312747d29bcristy      status=MagickFalse;
3197ec9ace44c23c302f336f6e672f6857312747d29bcristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3198ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3199a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        MagickBooleanType
3200e698a255629ba03cd125572de7b35b5e21c4ee5danthony          proceed;
3201f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy
3202f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
3203a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        #pragma omp critical (MagickCore_MorphologyPrimitive)
3204a8843c1f815ffad2568ec592d5b446cb1476aab5anthony#endif
3205ec9ace44c23c302f336f6e672f6857312747d29bcristy        proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
3206ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (proceed == MagickFalse)
3207a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          status=MagickFalse;
3208a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      }
3209a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  }
3210a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  morphology_view=DestroyCacheView(morphology_view);
3211a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  image_view=DestroyCacheView(image_view);
3212a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3213a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed+=changes[i];
3214ec9ace44c23c302f336f6e672f6857312747d29bcristy  changes=(size_t *) RelinquishMagickMemory(changes);
3215ec9ace44c23c302f336f6e672f6857312747d29bcristy  return(status ? (ssize_t) changed : -1);
3216a8843c1f815ffad2568ec592d5b446cb1476aab5anthony}
3217a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3218ec9ace44c23c302f336f6e672f6857312747d29bcristy/*
3219a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  This is almost identical to the MorphologyPrimative() function above, but
3220a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  applies the primitive directly to the actual image using two passes, once in
3221ec9ace44c23c302f336f6e672f6857312747d29bcristy  each direction, with the results of the previous (and current) row being
3222ec9ace44c23c302f336f6e672f6857312747d29bcristy  re-used.
3223a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3224a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  That is after each row is 'Sync'ed' into the image, the next row makes use of
3225a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  those values as part of the calculation of the next row.  It repeats, but
3226a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  going in the oppisite (bottom-up) direction.
3227a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3228a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  Because of this 're-use of results' this function can not make use of multi-
3229a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  threaded, parellel processing.
3230ec9ace44c23c302f336f6e672f6857312747d29bcristy*/
3231ec9ace44c23c302f336f6e672f6857312747d29bcristystatic ssize_t MorphologyPrimitiveDirect(Image *image,
3232ec9ace44c23c302f336f6e672f6857312747d29bcristy  const MorphologyMethod method,const KernelInfo *kernel,
3233ec9ace44c23c302f336f6e672f6857312747d29bcristy  ExceptionInfo *exception)
3234ec9ace44c23c302f336f6e672f6857312747d29bcristy{
3235a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  CacheView
3236e698a255629ba03cd125572de7b35b5e21c4ee5danthony    *morphology_view,
3237ec9ace44c23c302f336f6e672f6857312747d29bcristy    *image_view;
3238ec9ace44c23c302f336f6e672f6857312747d29bcristy
3239ec9ace44c23c302f336f6e672f6857312747d29bcristy  MagickBooleanType
3240ec9ace44c23c302f336f6e672f6857312747d29bcristy    status;
3241ec9ace44c23c302f336f6e672f6857312747d29bcristy
3242ec9ace44c23c302f336f6e672f6857312747d29bcristy  MagickOffsetType
3243a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    progress;
3244ec9ace44c23c302f336f6e672f6857312747d29bcristy
3245a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  OffsetInfo
3246ec9ace44c23c302f336f6e672f6857312747d29bcristy    offset;
3247ec9ace44c23c302f336f6e672f6857312747d29bcristy
3248ec9ace44c23c302f336f6e672f6857312747d29bcristy  size_t
3249a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    width,
3250ec9ace44c23c302f336f6e672f6857312747d29bcristy    changed;
3251a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3252ec9ace44c23c302f336f6e672f6857312747d29bcristy  ssize_t
3253ec9ace44c23c302f336f6e672f6857312747d29bcristy    y;
3254ec9ace44c23c302f336f6e672f6857312747d29bcristy
3255ec9ace44c23c302f336f6e672f6857312747d29bcristy  assert(image != (Image *) NULL);
3256ec9ace44c23c302f336f6e672f6857312747d29bcristy  assert(image->signature == MagickSignature);
3257ec9ace44c23c302f336f6e672f6857312747d29bcristy  assert(kernel != (KernelInfo *) NULL);
3258a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel->signature == MagickSignature);
3259a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception != (ExceptionInfo *) NULL);
32604c08aed51c5899665ade97263692328eea4af106cristy  assert(exception->signature == MagickSignature);
3261a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  status=MagickTrue;
3262a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  changed=0;
32634c08aed51c5899665ade97263692328eea4af106cristy  progress=0;
3264a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  switch(method)
3265a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
3266a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3267a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case VoronoiMorphology:
3268a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    {
326927be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      /*
327027be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        Kernel reflected about origin.
327127be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      */
3272ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=(ssize_t) kernel->width-kernel->x-1;
3273ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=(ssize_t) kernel->height-kernel->y-1;
3274ec9ace44c23c302f336f6e672f6857312747d29bcristy      break;
3275ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3276ec9ace44c23c302f336f6e672f6857312747d29bcristy    default:
3277ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3278ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=kernel->x;
3279a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      offset.y=kernel->y;
3280a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3281954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy    }
3282ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
3283ec9ace44c23c302f336f6e672f6857312747d29bcristy  /*
32846fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    Two views into same image, do not thread.
32854c08aed51c5899665ade97263692328eea4af106cristy  */
32864c08aed51c5899665ade97263692328eea4af106cristy  image_view=AcquireVirtualCacheView(image,exception);
3287954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  morphology_view=AcquireAuthenticCacheView(image,exception);
3288954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  width=image->columns+kernel->width-1;
3289954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  for (y=0; y < (ssize_t) image->rows; y++)
3290954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  {
329127be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    register const Quantum
329227be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      *restrict p;
3293a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3294a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register Quantum
3295ec9ace44c23c302f336f6e672f6857312747d29bcristy      *restrict q;
3296ec9ace44c23c302f336f6e672f6857312747d29bcristy
3297a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3298ec9ace44c23c302f336f6e672f6857312747d29bcristy      x;
3299ec9ace44c23c302f336f6e672f6857312747d29bcristy
3300ec9ace44c23c302f336f6e672f6857312747d29bcristy    ssize_t
3301ec9ace44c23c302f336f6e672f6857312747d29bcristy      center;
3302a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3303ec9ace44c23c302f336f6e672f6857312747d29bcristy    /*
3304ec9ace44c23c302f336f6e672f6857312747d29bcristy      Read virtual pixels, and authentic pixels, from the same image!  We read
3305ec9ace44c23c302f336f6e672f6857312747d29bcristy      using virtual to get virtual pixel handling, but write back into the same
3306ec9ace44c23c302f336f6e672f6857312747d29bcristy      image.
3307ec9ace44c23c302f336f6e672f6857312747d29bcristy
3308ec9ace44c23c302f336f6e672f6857312747d29bcristy      Only top half of kernel is processed as we do a single pass downward
3309ec9ace44c23c302f336f6e672f6857312747d29bcristy      through the image iterating the distance function as we go.
3310ec9ace44c23c302f336f6e672f6857312747d29bcristy    */
3311ec9ace44c23c302f336f6e672f6857312747d29bcristy    if (status == MagickFalse)
3312ec9ace44c23c302f336f6e672f6857312747d29bcristy      continue;
3313ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(size_t)
3314ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y+1,exception);
3315ec9ace44c23c302f336f6e672f6857312747d29bcristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
3316ec9ace44c23c302f336f6e672f6857312747d29bcristy      exception);
3317ec9ace44c23c302f336f6e672f6857312747d29bcristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
33186fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      {
3319ec9ace44c23c302f336f6e672f6857312747d29bcristy        status=MagickFalse;
3320ec9ace44c23c302f336f6e672f6857312747d29bcristy        continue;
332127be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      }
3322883fde11debec15cedb05dc5d7228d8588066bc0cristy    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
3323ec9ace44c23c302f336f6e672f6857312747d29bcristy      GetPixelChannels(image)*offset.x);
3324ec9ace44c23c302f336f6e672f6857312747d29bcristy    for (x=0; x < (ssize_t) image->columns; x++)
332527be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    {
3326ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
3327ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
3328ec9ace44c23c302f336f6e672f6857312747d29bcristy
3329ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3330ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3331ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
3332ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
3333ec9ace44c23c302f336f6e672f6857312747d29bcristy
3334ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
3335a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony          traits;
3336e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3337ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
3338ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
3339e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3340ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
3341ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
3342e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3343cfb71b157cd8f18b2e595d0a4a7c3c9403d8014dcristy        register ssize_t
3344ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
3345ec9ace44c23c302f336f6e672f6857312747d29bcristy
3346ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
3347ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
3348ec9ace44c23c302f336f6e672f6857312747d29bcristy
3349a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony        traits=GetPixelChannelTraits(image,(PixelChannel) i);
3350ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (traits == UndefinedPixelTrait)
3351ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3352ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (((traits & CopyPixelTrait) != 0) ||
3353ec9ace44c23c302f336f6e672f6857312747d29bcristy            (GetPixelReadMask(image,p+center) == 0))
3354ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3355ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
3356e698a255629ba03cd125572de7b35b5e21c4ee5danthony        pixel=(double) QuantumRange;
3357ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
3358ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
3359ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DistanceMorphology:
3360ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3361ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3362ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v <= offset.y; v++)
3363ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3364ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3365ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3366a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
3367e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3368ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3369ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3370e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3371ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3372ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3373e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3374cfb71b157cd8f18b2e595d0a4a7c3c9403d8014dcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3375ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3376ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3377ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3378ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=0; u < offset.x; u++)
3379ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3380a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony              if ((IfNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
3381ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3382ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3383ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3384ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3385ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3386ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3387ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3388e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3389ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3390ec9ace44c23c302f336f6e672f6857312747d29bcristy          case VoronoiMorphology:
3391ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3392ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
33936fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            for (v=0; v < offset.y; v++)
3394ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
33956fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              for (u=0; u < (ssize_t) kernel->width; u++)
3396e698a255629ba03cd125572de7b35b5e21c4ee5danthony              {
3397ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IfNaN(*k) == MagickFalse)
3398ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  {
3399ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
34006fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                      pixel=(double) pixels[i]+(*k);
3401a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                  }
3402a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                k--;
3403ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3404ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
3405ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3406a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            }
34076fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3408ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3409ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=0; u < offset.x; u++)
3410ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3411ec9ace44c23c302f336f6e672f6857312747d29bcristy              if ((IfNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
34126fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                {
34136fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                  if ((pixels[i]+(*k)) < pixel)
3414ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3415ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3416ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
34176fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              pixels+=GetPixelChannels(image);
34186fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            }
3419ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3420a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          }
34214c08aed51c5899665ade97263692328eea4af106cristy          default:
3422a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            break;
3423a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        }
34244c08aed51c5899665ade97263692328eea4af106cristy        if (fabs(pixel-q[i]) > MagickEpsilon)
3425a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          changed++;
3426a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        q[i]=ClampToQuantum(pixel);
3427a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      }
3428a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      p+=GetPixelChannels(image);
3429a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      q+=GetPixelChannels(image);
343027be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    }
343127be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
343227be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      status=MagickFalse;
3433ec9ace44c23c302f336f6e672f6857312747d29bcristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3434ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3435ec9ace44c23c302f336f6e672f6857312747d29bcristy        MagickBooleanType
3436ec9ace44c23c302f336f6e672f6857312747d29bcristy          proceed;
3437a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3438ec9ace44c23c302f336f6e672f6857312747d29bcristy        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
3439ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (proceed == MagickFalse)
3440db60568e12574785101a4ae8d8da076227a0a889anthony          status=MagickFalse;
3441954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      }
3442ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
34434c08aed51c5899665ade97263692328eea4af106cristy  morphology_view=DestroyCacheView(morphology_view);
34446fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  image_view=DestroyCacheView(image_view);
34454c08aed51c5899665ade97263692328eea4af106cristy  /*
34464c08aed51c5899665ade97263692328eea4af106cristy    Do the reverse pass through the image.
3447954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  */
3448954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  image_view=AcquireVirtualCacheView(image,exception);
3449954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  morphology_view=AcquireAuthenticCacheView(image,exception);
3450954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  for (y=(ssize_t) image->rows-1; y >= 0; y--)
3451ec9ace44c23c302f336f6e672f6857312747d29bcristy  {
3452ec9ace44c23c302f336f6e672f6857312747d29bcristy    register const Quantum
345327be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      *restrict p;
34546fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3455ec9ace44c23c302f336f6e672f6857312747d29bcristy    register Quantum
3456ec9ace44c23c302f336f6e672f6857312747d29bcristy      *restrict q;
3457ec9ace44c23c302f336f6e672f6857312747d29bcristy
3458a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3459ec9ace44c23c302f336f6e672f6857312747d29bcristy      x;
3460ec9ace44c23c302f336f6e672f6857312747d29bcristy
3461ec9ace44c23c302f336f6e672f6857312747d29bcristy    ssize_t
3462ec9ace44c23c302f336f6e672f6857312747d29bcristy      center;
3463a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3464ec9ace44c23c302f336f6e672f6857312747d29bcristy    /*
3465ec9ace44c23c302f336f6e672f6857312747d29bcristy       Read virtual pixels, and authentic pixels, from the same image.  We
3466a8843c1f815ffad2568ec592d5b446cb1476aab5anthony       read using virtual to get virtual pixel handling, but write back
3467ec9ace44c23c302f336f6e672f6857312747d29bcristy       into the same image.
3468ec9ace44c23c302f336f6e672f6857312747d29bcristy
3469a8843c1f815ffad2568ec592d5b446cb1476aab5anthony       Only the bottom half of the kernel is processed as we up the image.
3470ec9ace44c23c302f336f6e672f6857312747d29bcristy    */
3471ec9ace44c23c302f336f6e672f6857312747d29bcristy    if (status == MagickFalse)
3472a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      continue;
3473ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(size_t)
3474ec9ace44c23c302f336f6e672f6857312747d29bcristy      kernel->y+1,exception);
3475ec9ace44c23c302f336f6e672f6857312747d29bcristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
3476ec9ace44c23c302f336f6e672f6857312747d29bcristy      exception);
3477ec9ace44c23c302f336f6e672f6857312747d29bcristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3478ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
34796fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        status=MagickFalse;
3480ec9ace44c23c302f336f6e672f6857312747d29bcristy        continue;
3481ec9ace44c23c302f336f6e672f6857312747d29bcristy      }
348227be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    p+=(image->columns-1)*GetPixelChannels(image);
3483883fde11debec15cedb05dc5d7228d8588066bc0cristy    q+=(image->columns-1)*GetPixelChannels(image);
3484ec9ace44c23c302f336f6e672f6857312747d29bcristy    center=(ssize_t) (offset.x*GetPixelChannels(image));
3485ec9ace44c23c302f336f6e672f6857312747d29bcristy    for (x=(ssize_t) image->columns-1; x >= 0; x--)
348627be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    {
3487ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
3488ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
3489ec9ace44c23c302f336f6e672f6857312747d29bcristy
3490ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3491ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3492ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
3493ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
3494ec9ace44c23c302f336f6e672f6857312747d29bcristy
3495ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
3496a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony          traits;
3497e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3498ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
3499ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
3500e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3501ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
3502ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
3503e698a255629ba03cd125572de7b35b5e21c4ee5danthony
350427be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        register ssize_t
3505e698a255629ba03cd125572de7b35b5e21c4ee5danthony          u;
3506ec9ace44c23c302f336f6e672f6857312747d29bcristy
3507ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
3508ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
3509ec9ace44c23c302f336f6e672f6857312747d29bcristy
3510a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony        traits=GetPixelChannelTraits(image,(PixelChannel) i);
35116fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (traits == UndefinedPixelTrait)
3512ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3513ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (((traits & CopyPixelTrait) != 0) ||
3514ec9ace44c23c302f336f6e672f6857312747d29bcristy            (GetPixelReadMask(image,p+center) == 0))
3515ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3516ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
3517ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixel=(double) QuantumRange;
3518ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
3519ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
3520ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DistanceMorphology:
3521ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3522ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3523ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=offset.y; v < (ssize_t) kernel->height; v++)
3524ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3525ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3526ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3527ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IfNaN(*k) == MagickFalse)
3528a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                  {
3529e698a255629ba03cd125572de7b35b5e21c4ee5danthony                    if ((pixels[i]+(*k)) < pixel)
3530ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3531ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3532e698a255629ba03cd125572de7b35b5e21c4ee5danthony                k--;
3533ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3534ec9ace44c23c302f336f6e672f6857312747d29bcristy              }
3535e698a255629ba03cd125572de7b35b5e21c4ee5danthony              pixels+=(image->columns-1)*GetPixelChannels(image);
353627be28f6ee6dcb0c20268851b6359b50fbee71fbcristy            }
3537ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
3538ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3539ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3540ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3541ec9ace44c23c302f336f6e672f6857312747d29bcristy              if ((IfNaN(*k) == MagickFalse) &&
3542a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                  ((x+u-offset.x) < (ssize_t) image->columns))
35436fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                {
3544ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3545ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3546ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3547ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3548ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3549ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3550ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3551e698a255629ba03cd125572de7b35b5e21c4ee5danthony          }
3552ec9ace44c23c302f336f6e672f6857312747d29bcristy          case VoronoiMorphology:
3553ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3554ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3555ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=offset.y; v < (ssize_t) kernel->height; v++)
35566fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            {
3557ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
35586fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              {
3559e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if (IfNaN(*k) == MagickFalse)
35606fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                  {
35616fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                    if ((pixels[i]+(*k)) < pixel)
3562ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
35636fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                  }
35646fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                k--;
35656fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                pixels+=GetPixelChannels(image);
35666fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              }
35676fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              pixels+=(image->columns-1)*GetPixelChannels(image);
35686fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            }
35696fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
35706fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            pixels=q-offset.x*GetPixelChannels(image);
35716fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
35726fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy            {
35736fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              if ((IfNaN(*k) == MagickFalse) &&
3574ec9ace44c23c302f336f6e672f6857312747d29bcristy                  ((x+u-offset.x) < (ssize_t) image->columns))
3575ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3576ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3577aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy                    pixel=(double) pixels[i]+(*k);
3578a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                }
3579a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              k--;
3580ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3581ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3582ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3583ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3584ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3585ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3586ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
3587ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (fabs(pixel-q[i]) > MagickEpsilon)
3588a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          changed++;
3589cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristy        q[i]=ClampToQuantum(pixel);
3590f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy      }
3591f46d42620631d2581e0b6a56456e203e17c427c8anthony      p-=GetPixelChannels(image);
3592f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy      q-=GetPixelChannels(image);
3593602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
35941cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
35951cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy      status=MagickFalse;
35961cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3597602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
359847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        MagickBooleanType
3599a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          proceed;
360047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
360147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
3602602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (proceed == MagickFalse)
36034fd27e21043be809d66c8202e779255e5b660d2danthony          status=MagickFalse;
360447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      }
360547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
360647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  morphology_view=DestroyCacheView(morphology_view);
360747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  image_view=DestroyCacheView(image_view);
36084fd27e21043be809d66c8202e779255e5b660d2danthony  return(status ? (ssize_t) changed : -1);
36094fd27e21043be809d66c8202e779255e5b660d2danthony}
3610a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
36119eb4f74649b23c053b308ce1152dce51239450baanthony/*
36129eb4f74649b23c053b308ce1152dce51239450baanthony  Apply a Morphology by calling one of the above low level primitive
361347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  application functions.  This function handles any iteration loops,
361447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  composition or re-iteration of results, and compound morphology methods that
361547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  is based on multiple low-level (staged) morphology methods.
3616e698a255629ba03cd125572de7b35b5e21c4ee5danthony
361747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  Basically this provides the complex glue between the requested morphology
36184fd27e21043be809d66c8202e779255e5b660d2danthony  method and raw low-level implementation (above).
3619bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy*/
3620a8843c1f815ffad2568ec592d5b446cb1476aab5anthonyMagickPrivate Image *MorphologyApply(const Image *image,
362147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  const MorphologyMethod method, const ssize_t iterations,
362247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  const KernelInfo *kernel, const CompositeOperator compose,const double bias,
3623a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  ExceptionInfo *exception)
3624a8843c1f815ffad2568ec592d5b446cb1476aab5anthony{
3625a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  CompositeOperator
362647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    curr_compose;
3627a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
362847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  Image
362947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *curr_image,    /* Image we are working with or iterating */
363047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *work_image,    /* secondary image for primitive iteration */
3631a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *save_image,    /* saved image - for 'edge' method only */
3632a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *rslt_image;    /* resultant image - after multi-kernel handling */
3633a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
363447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  KernelInfo
363547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *reflected_kernel, /* A reflected copy of the kernel (if needed) */
36361b2bc0a7da432e6e1cc0480280402df213faa940anthony    *norm_kernel,      /* the current normal un-reflected kernel */
3637602ab9b30b644a78a4057da93d838a77391ec0acanthony    *rflt_kernel,      /* the current reflected kernel (if needed) */
3638602ab9b30b644a78a4057da93d838a77391ec0acanthony    *this_kernel;      /* the kernel being applied */
36394fd27e21043be809d66c8202e779255e5b660d2danthony
36404fd27e21043be809d66c8202e779255e5b660d2danthony  MorphologyMethod
3641602ab9b30b644a78a4057da93d838a77391ec0acanthony    primitive;      /* the current morphology primitive being applied */
3642602ab9b30b644a78a4057da93d838a77391ec0acanthony
3643602ab9b30b644a78a4057da93d838a77391ec0acanthony  CompositeOperator
3644a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    rslt_compose;   /* multi-kernel compose method for results to use */
3645602ab9b30b644a78a4057da93d838a77391ec0acanthony
364647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  MagickBooleanType
3647602ab9b30b644a78a4057da93d838a77391ec0acanthony    special,        /* do we use a direct modify function? */
3648bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    verbose;        /* verbose output of results */
364947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3650e698a255629ba03cd125572de7b35b5e21c4ee5danthony  size_t
365128ad1d779b6ca95852e860514185a7a97e06af77anthony    method_loop,    /* Loop 1: number of compound method iterations (norm 1) */
36526f2013165d72f7d8ef5f66bb9453126d88113809anthony    method_limit,   /*         maximum number of compound method iterations */
36534f1dcb76c95ef6410f2957ca9e7e1d391cee0d02anthony    kernel_number,  /* Loop 2: the kernel number being applied */
36549eb4f74649b23c053b308ce1152dce51239450baanthony    stage_loop,     /* Loop 3: primitive loop for compound morphology */
365547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    stage_limit,    /*         how many primitives are in this compound */
36561cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy    kernel_loop,    /* Loop 4: iterate the kernel over image */
3657aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy    kernel_limit,   /*         number of times to iterate kernel */
365847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    count,          /* total count of primitive steps applied */
365947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_changed, /* total count of changed using iterated kernel */
36604fd27e21043be809d66c8202e779255e5b660d2danthony    method_changed; /* total count of changed over method iteration */
366147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
366247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  ssize_t
3663a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;        /* number pixels changed by last primitive operation */
366447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
366547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  char
366647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    v_info[MaxTextExtent];
3667ea61f01656bb0f9074677452017cc559e54093faanthony
36684ee950098ad0166bbbc85c3a59bc079cd321384aglennrp  assert(image != (Image *) NULL);
366947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  assert(image->signature == MagickSignature);
36709eb4f74649b23c053b308ce1152dce51239450baanthony  assert(kernel != (KernelInfo *) NULL);
3671a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel->signature == MagickSignature);
367247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  assert(exception != (ExceptionInfo *) NULL);
3673602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception->signature == MagickSignature);
3674a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3675930be614b4595b97cd79ee864a394796740f76adanthony  count = 0;      /* number of low-level morphology primitives performed */
367647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( iterations == 0 )
367747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return((Image *)NULL);   /* null operation - nothing to do! */
36784fd27e21043be809d66c8202e779255e5b660d2danthony
367947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  kernel_limit = (size_t) iterations;
368047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
368147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony     kernel_limit = image->columns>image->rows ? image->columns : image->rows;
3682602ab9b30b644a78a4057da93d838a77391ec0acanthony
36839eb4f74649b23c053b308ce1152dce51239450baanthony  verbose = IsStringTrue(GetImageArtifact(image,"debug"));
368447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
36853ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony  /* initialise for cleanup */
3686c3e48258f3253188894e783dcdfd03562f7ab2c5anthony  curr_image = (Image *) image;
36879eb4f74649b23c053b308ce1152dce51239450baanthony  curr_compose = image->compose;
36883ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony  (void) curr_compose;
3689c3e48258f3253188894e783dcdfd03562f7ab2c5anthony  work_image = save_image = rslt_image = (Image *) NULL;
369047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  reflected_kernel = (KernelInfo *) NULL;
3691a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3692e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Initialize specific methods
3693f34d9b2df49a407af764c79e07d587af0600983aanthony   * + which loop should use the given iteratations
3694a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   * + how many primitives make up the compound morphology
369547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + multi-kernel compose method to use (by default)
36969eb4f74649b23c053b308ce1152dce51239450baanthony   */
36979eb4f74649b23c053b308ce1152dce51239450baanthony  method_limit = 1;       /* just do method once, unless otherwise set */
3698602ab9b30b644a78a4057da93d838a77391ec0acanthony  stage_limit = 1;        /* assume method is not a compound */
3699e698a255629ba03cd125572de7b35b5e21c4ee5danthony  special = MagickFalse;   /* assume it is NOT a direct modify primitive */
3700e698a255629ba03cd125572de7b35b5e21c4ee5danthony  rslt_compose = compose; /* and we are composing multi-kernels as given */
3701e698a255629ba03cd125572de7b35b5e21c4ee5danthony  switch( method ) {
3702e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case SmoothMorphology:  /* 4 primitive compound morphology */
3703e698a255629ba03cd125572de7b35b5e21c4ee5danthony      stage_limit = 4;
3704e698a255629ba03cd125572de7b35b5e21c4ee5danthony      break;
3705e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case OpenMorphology:    /* 2 primitive compound morphology */
3706e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case OpenIntensityMorphology:
3707574cc26500992189f637cd1cdf93d0654e7df7aecristy    case TopHatMorphology:
3708574cc26500992189f637cd1cdf93d0654e7df7aecristy    case CloseMorphology:
3709e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case CloseIntensityMorphology:
3710a325e90b7583eab5e70f0ceebb8257670e652f0fcristy    case BottomHatMorphology:
3711e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case EdgeMorphology:
37127bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony      stage_limit = 2;
37135acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      break;
37141e604812fad85bb96f757a2393015ae3d061c39acristy    case HitAndMissMorphology:
37151e604812fad85bb96f757a2393015ae3d061c39acristy      rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
37161e604812fad85bb96f757a2393015ae3d061c39acristy      /* FALL THUR */
3717e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case ThinningMorphology:
3718e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case ThickenMorphology:
3719e698a255629ba03cd125572de7b35b5e21c4ee5danthony      method_limit = kernel_limit;  /* iterate the whole method */
3720e698a255629ba03cd125572de7b35b5e21c4ee5danthony      kernel_limit = 1;             /* do not do kernel iteration  */
3721e698a255629ba03cd125572de7b35b5e21c4ee5danthony      break;
3722a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony    case DistanceMorphology:
372363240888c3975789a09c2494a4654b523931df96cristy    case VoronoiMorphology:
372463240888c3975789a09c2494a4654b523931df96cristy      special = MagickTrue;         /* use special direct primative */
3725feb3e9695150978a5d2372d3fe2f60466a7c8066cristy      break;
372639172408bad7ef2ef00a815fa9abf9979e7857cbcristy    default:
372763240888c3975789a09c2494a4654b523931df96cristy      break;
372863240888c3975789a09c2494a4654b523931df96cristy  }
3729e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3730e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Apply special methods with special requirments
3731e698a255629ba03cd125572de7b35b5e21c4ee5danthony  ** For example, single run only, or post-processing requirements
3732e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
3733c3e48258f3253188894e783dcdfd03562f7ab2c5anthony  if ( special != MagickFalse )
373447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    {
373547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      rslt_image=CloneImage(image,0,0,MagickTrue,exception);
373647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if (rslt_image == (Image *) NULL)
373747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        goto error_cleanup;
37384fd27e21043be809d66c8202e779255e5b660d2danthony      if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3739a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        goto error_cleanup;
3740c3e48258f3253188894e783dcdfd03562f7ab2c5anthony
374147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
374247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
374347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( IfMagickTrue(verbose) )
374447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        (void) (void) FormatLocaleFile(stderr,
374547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
374647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          CommandOptionToMnemonic(MagickMorphologyOptions, method),
374747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          1.0,0.0,1.0, (double) changed);
374847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
374947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( changed < 0 )
375047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        goto error_cleanup;
375147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
375247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( method == VoronoiMorphology ) {
375347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        /* Preserve the alpha channel of input image - but turned it off */
375447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3755602ab9b30b644a78a4057da93d838a77391ec0acanthony          exception);
3756e698a255629ba03cd125572de7b35b5e21c4ee5danthony        (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
3757e698a255629ba03cd125572de7b35b5e21c4ee5danthony          MagickTrue,0,0,exception);
3758e698a255629ba03cd125572de7b35b5e21c4ee5danthony        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
375947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          exception);
376047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      }
376147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      goto exit_cleanup;
376247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    }
376347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
376447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Handle user (caller) specified multi-kernel composition method */
376547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( compose != UndefinedCompositeOp )
376647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = compose;  /* override default composition for method */
376747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_compose == UndefinedCompositeOp )
3768f2faecf9facdbbb14fcba373365f9f691a9658e0cristy    rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
376947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3770e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  /* Some methods require a reflected kernel to use with primitives.
377147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * Create the reflected kernel for those methods. */
377247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  switch ( method ) {
377347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CorrelateMorphology:
377447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
377547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseIntensityMorphology:
377647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
377747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case SmoothMorphology:
377847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      reflected_kernel = CloneKernelInfo(kernel);
3779a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      if (reflected_kernel == (KernelInfo *) NULL)
378047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        goto error_cleanup;
3781a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      RotateKernelInfo(reflected_kernel,180);
378247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
378347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
378447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
3785a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  }
378647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
378747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Loops around more primitive morpholgy methods
378847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  **  erose, dilate, open, close, smooth, edge, etc...
3789a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  */
379047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Loop 1:  iterate the compound method */
379147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_loop = 0;
379247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_changed = 1;
3793a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  while ( method_loop < method_limit && method_changed > 0 ) {
379447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_loop++;
3795a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    method_changed = 0;
379647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
379747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    /* Loop 2:  iterate over each kernel in a multi-kernel list */
3798a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    norm_kernel = (KernelInfo *) kernel;
379947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    this_kernel = (KernelInfo *) kernel;
3800a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    rflt_kernel = reflected_kernel;
3801e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony
380247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number = 0;
380347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    while ( norm_kernel != NULL ) {
380447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3805a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Loop 3: Compound Morphology Staging - Select Primative to apply */
380647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_loop = 0;          /* the compound morphology stage number */
3807a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      while ( stage_loop < stage_limit ) {
380847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        stage_loop++;   /* The stage of the compound morphology */
380947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
381047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        /* Select primitive morphology for this stage of compound method */
3811a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        this_kernel = norm_kernel; /* default use unreflected kernel */
381247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        primitive = method;        /* Assume method is a primitive */
3813a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        switch( method ) {
381447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case ErodeMorphology:      /* just erode */
381547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeInMorphology:     /* erode and image difference */
381647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            primitive = ErodeMorphology;
381747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
3818a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          case DilateMorphology:     /* just dilate */
381947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeOutMorphology:    /* dilate and image difference */
382047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            primitive = DilateMorphology;
3821a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            break;
382247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenMorphology:       /* erode then dialate */
382347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case TopHatMorphology:     /* open and image difference */
382447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            primitive = ErodeMorphology;
3825a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            if ( stage_loop == 2 )
382647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              primitive = DilateMorphology;
382747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
382847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenIntensityMorphology:
3829a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeIntensityMorphology;
383047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
383147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              primitive = DilateIntensityMorphology;
383247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
383347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseMorphology:      /* dilate, then erode */
3834a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          case BottomHatMorphology:  /* close and image difference */
383547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
383647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            primitive = DilateMorphology;
383747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3838a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
383947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
384047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseIntensityMorphology:
384147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
384247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            primitive = DilateIntensityMorphology;
384347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
384447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              primitive = ErodeIntensityMorphology;
384547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
384647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case SmoothMorphology:         /* open, close */
384747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            switch ( stage_loop ) {
384847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 1: /* start an open method, which starts with Erode */
384947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                primitive = ErodeMorphology;
385047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
385147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 2:  /* now Dilate the Erode */
3852a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
385347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
385447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 3:  /* Reflect kernel a close */
385547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
385647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                primitive = DilateMorphology;
3857e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony                break;
38587a01dcf50ce12cb2a789bedff51e9345f022432eanthony              case 4:  /* Finish the Close */
385947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
38607bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony                primitive = ErodeMorphology;
386147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
3862b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            }
3863042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy            break;
3864e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy          case EdgeMorphology:        /* dilate and erode difference */
3865a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
3866b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            if ( stage_loop == 2 ) {
3867042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              save_image = curr_image;      /* save the image difference */
3868e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              curr_image = (Image *) image;
386947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              primitive = ErodeMorphology;
387047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
387147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
38729eb4f74649b23c053b308ce1152dce51239450baanthony          case CorrelateMorphology:
3873a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* A Correlation is a Convolution with a reflected kernel.
387447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** However a Convolution is a weighted sum using a reflected
387547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel.  It may seem stange to convert a Correlation into a
387647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution as the Correlation is the simplier method, but
387747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution is much more commonly used, and it makes sense to
387847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** implement it directly so as to avoid the need to duplicate the
38799eb4f74649b23c053b308ce1152dce51239450baanthony            ** kernel when it is not required (which is typically the
3880a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            ** default).
38819eb4f74649b23c053b308ce1152dce51239450baanthony            */
38829eb4f74649b23c053b308ce1152dce51239450baanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
38839eb4f74649b23c053b308ce1152dce51239450baanthony            primitive = ConvolveMorphology;
38849eb4f74649b23c053b308ce1152dce51239450baanthony            break;
38859eb4f74649b23c053b308ce1152dce51239450baanthony          default:
3886574cc26500992189f637cd1cdf93d0654e7df7aecristy            break;
3887574cc26500992189f637cd1cdf93d0654e7df7aecristy        }
38889eb4f74649b23c053b308ce1152dce51239450baanthony        assert( this_kernel != (KernelInfo *) NULL );
38899eb4f74649b23c053b308ce1152dce51239450baanthony
3890501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        /* Extra information for debugging compound operations */
38919eb4f74649b23c053b308ce1152dce51239450baanthony        if ( IfMagickTrue(verbose) ) {
3892e698a255629ba03cd125572de7b35b5e21c4ee5danthony          if ( stage_limit > 1 )
3893f46d42620631d2581e0b6a56456e203e17c427c8anthony            (void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
389447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony             CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
38957bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony             method_loop,(double) stage_loop);
389647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          else if ( primitive != method )
38975acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleString(v_info, MaxTextExtent, "%s:%.20g -> ",
38985acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
38991e604812fad85bb96f757a2393015ae3d061c39acristy              method_loop);
3900042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy          else
3901a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            v_info[0] = '\0';
3902e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy        }
3903e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy
390447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        /* Loop 4: Iterate the kernel with primitive */
3905a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        kernel_loop = 0;
3906a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        kernel_changed = 0;
3907a325e90b7583eab5e70f0ceebb8257670e652f0fcristy        changed = 1;
3908a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        while ( kernel_loop < kernel_limit && changed > 0 ) {
3909a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          kernel_loop++;     /* the iteration of this kernel */
3910a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
39119eb4f74649b23c053b308ce1152dce51239450baanthony          /* Create a clone as the destination image, if not yet defined */
39129eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == (Image *) NULL )
39139eb4f74649b23c053b308ce1152dce51239450baanthony            {
39149eb4f74649b23c053b308ce1152dce51239450baanthony              work_image=CloneImage(image,0,0,MagickTrue,exception);
39159eb4f74649b23c053b308ce1152dce51239450baanthony              if (work_image == (Image *) NULL)
39169eb4f74649b23c053b308ce1152dce51239450baanthony                goto error_cleanup;
391747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
39189eb4f74649b23c053b308ce1152dce51239450baanthony                goto error_cleanup;
3919a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            }
39207a01dcf50ce12cb2a789bedff51e9345f022432eanthony
39217bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
39225acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          count++;
39237bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          changed = MorphologyPrimitive(curr_image, work_image, primitive,
39245acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy                       this_kernel, bias, exception);
39259eb4f74649b23c053b308ce1152dce51239450baanthony          if ( IfMagickTrue(verbose) ) {
392647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( kernel_loop > 1 )
39275acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
39285acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) (void) FormatLocaleFile(stderr,
39295acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
39305acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
39315acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              primitive),(this_kernel == rflt_kernel ) ? "*" : "",
393247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              (double) (method_loop+kernel_loop-1),(double) kernel_number,
39331b2bc0a7da432e6e1cc0480280402df213faa940anthony              (double) count,(double) changed);
393447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          }
39351b2bc0a7da432e6e1cc0480280402df213faa940anthony          if ( changed < 0 )
393647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            goto error_cleanup;
393747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          kernel_changed += changed;
393847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          method_changed += changed;
393947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
394047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          /* prepare next loop */
394147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          { Image *tmp = work_image;   /* swap images for iteration */
394247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            work_image = curr_image;
394347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            curr_image = tmp;
394447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          }
394547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( work_image == image )
394647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            work_image = (Image *) NULL; /* replace input 'image' */
394747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
39487bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        } /* End Loop 4: Iterate the kernel with primitive */
3949e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy
3950e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy        if ( IfMagickTrue(verbose) && kernel_changed != (size_t)changed )
3951e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy          (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
3952feb3e9695150978a5d2372d3fe2f60466a7c8066cristy        if ( IfMagickTrue(verbose) && stage_loop < stage_limit )
395339172408bad7ef2ef00a815fa9abf9979e7857cbcristy          (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
395447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
395547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#if 0
39567bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony    (void) FormatLocaleFile(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
3957e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy    (void) FormatLocaleFile(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
3958e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy    (void) FormatLocaleFile(stderr, "      work =0x%lx\n", (unsigned long)work_image);
3959e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy    (void) FormatLocaleFile(stderr, "      save =0x%lx\n", (unsigned long)save_image);
3960feb3e9695150978a5d2372d3fe2f60466a7c8066cristy    (void) FormatLocaleFile(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
396139172408bad7ef2ef00a815fa9abf9979e7857cbcristy#endif
396247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
396347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
396447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
396547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /*  Final Post-processing for some Compound Methods
3966602ab9b30b644a78a4057da93d838a77391ec0acanthony      **
39679eb4f74649b23c053b308ce1152dce51239450baanthony      ** The removal of any 'Sync' channel flag in the Image Compositon
396847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** below ensures the methematical compose method is applied in a
396947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** purely mathematical way, and only to the selected channels.
3970c3e48258f3253188894e783dcdfd03562f7ab2c5anthony      ** Turn off SVG composition 'alpha blending'.
397147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      */
39727bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony      switch( method ) {
3973c3e48258f3253188894e783dcdfd03562f7ab2c5anthony        case EdgeOutMorphology:
39745acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        case EdgeInMorphology:
3975c3e48258f3253188894e783dcdfd03562f7ab2c5anthony        case TopHatMorphology:
39765acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        case BottomHatMorphology:
3977c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          if ( IfMagickTrue(verbose) )
3978c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            (void) FormatLocaleFile(stderr,
39799eb4f74649b23c053b308ce1152dce51239450baanthony              "\n%s: Difference with original image",CommandOptionToMnemonic(
398047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              MagickMorphologyOptions, method) );
39817bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
39825acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            MagickTrue,0,0,exception);
398347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
398447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeMorphology:
39859eb4f74649b23c053b308ce1152dce51239450baanthony          if ( IfMagickTrue(verbose) )
398647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            (void) FormatLocaleFile(stderr,
3987ea61f01656bb0f9074677452017cc559e54093faanthony              "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
398847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              MagickMorphologyOptions, method) );
398947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
399047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            MagickTrue,0,0,exception);
399147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          save_image = DestroyImage(save_image); /* finished with save image */
3992ea61f01656bb0f9074677452017cc559e54093faanthony          break;
399347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        default:
39947bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          break;
39955acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      }
3996feb3e9695150978a5d2372d3fe2f60466a7c8066cristy
399739172408bad7ef2ef00a815fa9abf9979e7857cbcristy      /* multi-kernel handling:  re-iterate, or compose results */
3998feb3e9695150978a5d2372d3fe2f60466a7c8066cristy      if ( kernel->next == (KernelInfo *) NULL )
39990bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony        rslt_image = curr_image;   /* just return the resulting image */
400047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_compose == NoCompositeOp )
400147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        { if ( IfMagickTrue(verbose) ) {
40027bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony            if ( this_kernel->next != (KernelInfo *) NULL )
40035acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (re-iterate)");
40044fd27e21043be809d66c8202e779255e5b660d2danthony            else
400547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              (void) FormatLocaleFile(stderr, " (done)");
400647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          }
400747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          rslt_image = curr_image; /* return result, and re-iterate */
400847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
400947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_image == (Image *) NULL)
401047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        { if ( IfMagickTrue(verbose) )
40119eb4f74649b23c053b308ce1152dce51239450baanthony            (void) FormatLocaleFile(stderr, " (save for compose)");
401247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          rslt_image = curr_image;
4013602ab9b30b644a78a4057da93d838a77391ec0acanthony          curr_image = (Image *) image;  /* continue with original image */
40149eb4f74649b23c053b308ce1152dce51239450baanthony        }
40151b2bc0a7da432e6e1cc0480280402df213faa940anthony      else
401647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        { /* Add the new 'current' result to the composition
40171b2bc0a7da432e6e1cc0480280402df213faa940anthony          **
4018ea61f01656bb0f9074677452017cc559e54093faanthony          ** The removal of any 'Sync' channel flag in the Image Compositon
4019ea61f01656bb0f9074677452017cc559e54093faanthony          ** below ensures the methematical compose method is applied in a
402047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** purely mathematical way, and only to the selected channels.
402147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** IE: Turn off SVG composition 'alpha blending'.
40221b2bc0a7da432e6e1cc0480280402df213faa940anthony          */
4023ea61f01656bb0f9074677452017cc559e54093faanthony          if ( IfMagickTrue(verbose) )
4024ea61f01656bb0f9074677452017cc559e54093faanthony            (void) FormatLocaleFile(stderr, " (compose \"%s\")",
4025ea61f01656bb0f9074677452017cc559e54093faanthony              CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
402647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
40279eb4f74649b23c053b308ce1152dce51239450baanthony            0,0,exception);
402847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = DestroyImage(curr_image);
40299eb4f74649b23c053b308ce1152dce51239450baanthony          curr_image = (Image *) image;  /* continue with original image */
403047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
403147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( IfMagickTrue(verbose) )
403247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        (void) FormatLocaleFile(stderr, "\n");
403347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
40349eb4f74649b23c053b308ce1152dce51239450baanthony      /* loop to the next kernel in a multi-kernel list */
4035a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      norm_kernel = norm_kernel->next;
40369eb4f74649b23c053b308ce1152dce51239450baanthony      if ( rflt_kernel != (KernelInfo *) NULL )
40379eb4f74649b23c053b308ce1152dce51239450baanthony        rflt_kernel = rflt_kernel->next;
40389eb4f74649b23c053b308ce1152dce51239450baanthony      kernel_number++;
40399eb4f74649b23c053b308ce1152dce51239450baanthony    } /* End Loop 2: Loop over each kernel */
40409eb4f74649b23c053b308ce1152dce51239450baanthony
40419eb4f74649b23c053b308ce1152dce51239450baanthony  } /* End Loop 1: compound method interation */
4042f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy
40439eb4f74649b23c053b308ce1152dce51239450baanthony  goto exit_cleanup;
40449eb4f74649b23c053b308ce1152dce51239450baanthony
40459eb4f74649b23c053b308ce1152dce51239450baanthony  /* Yes goto's are bad, but it makes cleanup lot more efficient */
40469eb4f74649b23c053b308ce1152dce51239450baanthonyerror_cleanup:
40479eb4f74649b23c053b308ce1152dce51239450baanthony  if ( curr_image == rslt_image )
4048a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony    curr_image = (Image *) NULL;
4049a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony  if ( rslt_image != (Image *) NULL )
40509eb4f74649b23c053b308ce1152dce51239450baanthony    rslt_image = DestroyImage(rslt_image);
40519eb4f74649b23c053b308ce1152dce51239450baanthonyexit_cleanup:
40529eb4f74649b23c053b308ce1152dce51239450baanthony  if ( curr_image == rslt_image || curr_image == image )
40539eb4f74649b23c053b308ce1152dce51239450baanthony    curr_image = (Image *) NULL;
40549eb4f74649b23c053b308ce1152dce51239450baanthony  if ( curr_image != (Image *) NULL )
405522de2722b682eb405b60ec6022a7546df994674eanthony    curr_image = DestroyImage(curr_image);
405622de2722b682eb405b60ec6022a7546df994674eanthony  if ( work_image != (Image *) NULL )
405746a369d839971ab627bdb31a93d8bd63e81b65a3anthony    work_image = DestroyImage(work_image);
405822de2722b682eb405b60ec6022a7546df994674eanthony  if ( save_image != (Image *) NULL )
405922de2722b682eb405b60ec6022a7546df994674eanthony    save_image = DestroyImage(save_image);
406022de2722b682eb405b60ec6022a7546df994674eanthony  if ( reflected_kernel != (KernelInfo *) NULL )
406122de2722b682eb405b60ec6022a7546df994674eanthony    reflected_kernel = DestroyKernelInfo(reflected_kernel);
406222de2722b682eb405b60ec6022a7546df994674eanthony  return(rslt_image);
40639eb4f74649b23c053b308ce1152dce51239450baanthony}
40649eb4f74649b23c053b308ce1152dce51239450baanthony
40659eb4f74649b23c053b308ce1152dce51239450baanthony
40669eb4f74649b23c053b308ce1152dce51239450baanthony/*
4067bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40689eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40699eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40709eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40719eb4f74649b23c053b308ce1152dce51239450baanthony%     M o r p h o l o g y I m a g e                                           %
40729eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40739eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40749eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40759eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40769eb4f74649b23c053b308ce1152dce51239450baanthony%
40779eb4f74649b23c053b308ce1152dce51239450baanthony%  MorphologyImage() applies a user supplied kernel to the image according to
40789eb4f74649b23c053b308ce1152dce51239450baanthony%  the given mophology method.
40799eb4f74649b23c053b308ce1152dce51239450baanthony%
40809eb4f74649b23c053b308ce1152dce51239450baanthony%  This function applies any and all user defined settings before calling
40819eb4f74649b23c053b308ce1152dce51239450baanthony%  the above internal function MorphologyApply().
40829eb4f74649b23c053b308ce1152dce51239450baanthony%
40839eb4f74649b23c053b308ce1152dce51239450baanthony%  User defined settings include...
40849eb4f74649b23c053b308ce1152dce51239450baanthony%    * Output Bias for Convolution and correlation ("-define convolve:bias=??")
40859eb4f74649b23c053b308ce1152dce51239450baanthony%    * Kernel Scale/normalize settings             ("-define convolve:scale=??")
4086f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%      This can also includes the addition of a scaled unity kernel.
4087f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%    * Show Kernel being applied                   ("-define showkernel=1")
4088f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%
40899eb4f74649b23c053b308ce1152dce51239450baanthony%  Other operators that do not want user supplied options interfering,
40909eb4f74649b23c053b308ce1152dce51239450baanthony%  especially "convolve:bias" and "showkernel" should use MorphologyApply()
40919eb4f74649b23c053b308ce1152dce51239450baanthony%  directly.
40929eb4f74649b23c053b308ce1152dce51239450baanthony%
409347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%  The format of the MorphologyImage method is:
409447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%
409547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%      Image *MorphologyImage(const Image *image,MorphologyMethod method,
40969eb4f74649b23c053b308ce1152dce51239450baanthony%        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
40979eb4f74649b23c053b308ce1152dce51239450baanthony%
40989eb4f74649b23c053b308ce1152dce51239450baanthony%  A description of each parameter follows:
4099f46d42620631d2581e0b6a56456e203e17c427c8anthony%
4100f46d42620631d2581e0b6a56456e203e17c427c8anthony%    o image: the image.
41019eb4f74649b23c053b308ce1152dce51239450baanthony%
410222de2722b682eb405b60ec6022a7546df994674eanthony%    o method: the morphology method to be applied.
410322de2722b682eb405b60ec6022a7546df994674eanthony%
4104d228c03fa334bae897eee6c2d8721fa48e1577bacristy%    o iterations: apply the operation this many times (or no change).
410522de2722b682eb405b60ec6022a7546df994674eanthony%                  A value of -1 means loop until no change found.
410646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                  How this is applied may depend on the morphology method.
410746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                  Typically this is a value of 1.
410846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
41099eb4f74649b23c053b308ce1152dce51239450baanthony%    o kernel: An array of double representing the morphology kernel.
411022de2722b682eb405b60ec6022a7546df994674eanthony%              Warning: kernel may be normalized for the Convolve method.
411128ad1d779b6ca95852e860514185a7a97e06af77anthony%
411228ad1d779b6ca95852e860514185a7a97e06af77anthony%    o exception: return any errors or warnings in this structure.
4113f46d42620631d2581e0b6a56456e203e17c427c8anthony%
411422de2722b682eb405b60ec6022a7546df994674eanthony*/
411522de2722b682eb405b60ec6022a7546df994674eanthonyMagickExport Image *MorphologyImage(const Image *image,
411622de2722b682eb405b60ec6022a7546df994674eanthony  const MorphologyMethod method,const ssize_t iterations,
411722de2722b682eb405b60ec6022a7546df994674eanthony  const KernelInfo *kernel,ExceptionInfo *exception)
411822de2722b682eb405b60ec6022a7546df994674eanthony{
411922de2722b682eb405b60ec6022a7546df994674eanthony  KernelInfo
412022de2722b682eb405b60ec6022a7546df994674eanthony    *curr_kernel;
412122de2722b682eb405b60ec6022a7546df994674eanthony
412222de2722b682eb405b60ec6022a7546df994674eanthony  CompositeOperator
412322de2722b682eb405b60ec6022a7546df994674eanthony    compose;
412422de2722b682eb405b60ec6022a7546df994674eanthony
412522de2722b682eb405b60ec6022a7546df994674eanthony  Image
41269eb4f74649b23c053b308ce1152dce51239450baanthony    *morphology_image;
4127e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony
412822de2722b682eb405b60ec6022a7546df994674eanthony  double
412922de2722b682eb405b60ec6022a7546df994674eanthony    bias;
413022de2722b682eb405b60ec6022a7546df994674eanthony
413122de2722b682eb405b60ec6022a7546df994674eanthony  curr_kernel = (KernelInfo *) kernel;
413222de2722b682eb405b60ec6022a7546df994674eanthony  bias=0.0;
413322de2722b682eb405b60ec6022a7546df994674eanthony  compose = UndefinedCompositeOp;  /* use default for method */
413422de2722b682eb405b60ec6022a7546df994674eanthony
413522de2722b682eb405b60ec6022a7546df994674eanthony  /* Apply Convolve/Correlate Normalization and Scaling Factors.
413622de2722b682eb405b60ec6022a7546df994674eanthony   * This is done BEFORE the ShowKernelInfo() function is called so that
413722de2722b682eb405b60ec6022a7546df994674eanthony   * users can see the results of the 'option:convolve:scale' option.
41389eb4f74649b23c053b308ce1152dce51239450baanthony   */
41399eb4f74649b23c053b308ce1152dce51239450baanthony  if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
41409eb4f74649b23c053b308ce1152dce51239450baanthony      const char
41419eb4f74649b23c053b308ce1152dce51239450baanthony        *artifact;
41429eb4f74649b23c053b308ce1152dce51239450baanthony
414348656f2d1ca9ac9979eac32052e4cdc4958c9010cristy      /* Get the bias value as it will be needed */
414448656f2d1ca9ac9979eac32052e4cdc4958c9010cristy      artifact = GetImageArtifact(image,"convolve:bias");
414548656f2d1ca9ac9979eac32052e4cdc4958c9010cristy      if ( artifact != (const char *) NULL) {
41469eb4f74649b23c053b308ce1152dce51239450baanthony        if (IfMagickFalse(IsGeometry(artifact)))
41479eb4f74649b23c053b308ce1152dce51239450baanthony          (void) ThrowMagickException(exception,GetMagickModule(),
41483206678d008425bc56dd2dbad002f2bb26299dc2anthony               OptionWarning,"InvalidSetting","'%s' '%s'",
41493206678d008425bc56dd2dbad002f2bb26299dc2anthony               "convolve:bias",artifact);
41503206678d008425bc56dd2dbad002f2bb26299dc2anthony        else
41513206678d008425bc56dd2dbad002f2bb26299dc2anthony          bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
41523206678d008425bc56dd2dbad002f2bb26299dc2anthony      }
415347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
415428ad1d779b6ca95852e860514185a7a97e06af77anthony      /* Scale kernel according to user wishes */
415528ad1d779b6ca95852e860514185a7a97e06af77anthony      artifact = GetImageArtifact(image,"convolve:scale");
415622de2722b682eb405b60ec6022a7546df994674eanthony      if ( artifact != (const char *)NULL ) {
415722de2722b682eb405b60ec6022a7546df994674eanthony        if (IfMagickFalse(IsGeometry(artifact)))
415822de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
4159f46d42620631d2581e0b6a56456e203e17c427c8anthony               OptionWarning,"InvalidSetting","'%s' '%s'",
416022de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:scale",artifact);
416122de2722b682eb405b60ec6022a7546df994674eanthony        else {
416270b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy          if ( curr_kernel == kernel )
416322de2722b682eb405b60ec6022a7546df994674eanthony            curr_kernel = CloneKernelInfo(kernel);
416422de2722b682eb405b60ec6022a7546df994674eanthony          if (curr_kernel == (KernelInfo *) NULL)
416522de2722b682eb405b60ec6022a7546df994674eanthony            return((Image *) NULL);
416622de2722b682eb405b60ec6022a7546df994674eanthony          ScaleGeometryKernelInfo(curr_kernel, artifact);
416722de2722b682eb405b60ec6022a7546df994674eanthony        }
416822de2722b682eb405b60ec6022a7546df994674eanthony      }
416922de2722b682eb405b60ec6022a7546df994674eanthony    }
417028ad1d779b6ca95852e860514185a7a97e06af77anthony
41719eb4f74649b23c053b308ce1152dce51239450baanthony  /* display the (normalized) kernel via stderr */
4172f46d42620631d2581e0b6a56456e203e17c427c8anthony  if ( IfStringTrue(GetImageArtifact(image,"showkernel"))
4173f46d42620631d2581e0b6a56456e203e17c427c8anthony    || IfStringTrue(GetImageArtifact(image,"convolve:showkernel"))
41749eb4f74649b23c053b308ce1152dce51239450baanthony    || IfStringTrue(GetImageArtifact(image,"morphology:showkernel")) )
41759eb4f74649b23c053b308ce1152dce51239450baanthony    ShowKernelInfo(curr_kernel);
41769eb4f74649b23c053b308ce1152dce51239450baanthony
41771b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* Override the default handling of multi-kernel morphology results
41789eb4f74649b23c053b308ce1152dce51239450baanthony   * If 'Undefined' use the default method
41799eb4f74649b23c053b308ce1152dce51239450baanthony   * If 'None' (default for 'Convolve') re-iterate previous result
418083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * Otherwise merge resulting images using compose method given.
418183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * Default for 'HitAndMiss' is 'Lighten'.
418283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   */
418383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  { const char
418483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      *artifact;
418583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    ssize_t
41864fd27e21043be809d66c8202e779255e5b660d2danthony      parse;
418783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
418883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    artifact = GetImageArtifact(image,"morphology:compose");
418983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    if ( artifact != (const char *) NULL) {
419083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      parse=ParseCommandOption(MagickComposeOptions,
419183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        MagickFalse,artifact);
419246a369d839971ab627bdb31a93d8bd63e81b65a3anthony      if ( parse < 0 )
419346a369d839971ab627bdb31a93d8bd63e81b65a3anthony        (void) ThrowMagickException(exception,GetMagickModule(),
419446a369d839971ab627bdb31a93d8bd63e81b65a3anthony             OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
419546a369d839971ab627bdb31a93d8bd63e81b65a3anthony             "morphology:compose",artifact);
419646a369d839971ab627bdb31a93d8bd63e81b65a3anthony      else
419783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        compose=(CompositeOperator)parse;
41984fd27e21043be809d66c8202e779255e5b660d2danthony    }
419983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
42004fd27e21043be809d66c8202e779255e5b660d2danthony  /* Apply the Morphology */
420183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  morphology_image = MorphologyApply(image,method,iterations,
420283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    curr_kernel,compose,bias,exception);
420383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
420483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* Cleanup and Exit */
420583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( curr_kernel != kernel )
420683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    curr_kernel=DestroyKernelInfo(curr_kernel);
420783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  return(morphology_image);
420846a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
420946a369d839971ab627bdb31a93d8bd63e81b65a3anthony
421083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
42114fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
42131b2bc0a7da432e6e1cc0480280402df213faa940anthony%                                                                             %
42141b2bc0a7da432e6e1cc0480280402df213faa940anthony%                                                                             %
42151b2bc0a7da432e6e1cc0480280402df213faa940anthony+     R o t a t e K e r n e l I n f o                                         %
42161b2bc0a7da432e6e1cc0480280402df213faa940anthony%                                                                             %
421783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
421883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
421983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
422183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  RotateKernelInfo() rotates the kernel by the angle given.
422283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
422383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  Currently it is restricted to 90 degree angles, of either 1D kernels
422483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
422583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  It will ignore usless rotations for specific 'named' built-in kernels.
422683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
42273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  The format of the RotateKernelInfo method is:
422843c4925e5305a26e48d68f7893e94f55d0831c39anthony%
422983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%      void RotateKernelInfo(KernelInfo *kernel, double angle)
42303dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
423183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
423283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
423383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
4234501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
4235501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    o angle: angle to rotate in degrees
423683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
42373dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony% This function is currently internal to this module only, but can be exported
42383dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony% to other modules if needed.
423983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
4240bee715c4c0fd9efe6e21d8627ae8664434df7750anthonystatic void RotateKernelInfo(KernelInfo *kernel, double angle)
424183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
424283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* angle the lower kernels first */
424383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( kernel->next != (KernelInfo *) NULL)
424483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    RotateKernelInfo(kernel->next, angle);
424583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
424683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
424783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  **
424883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  ** TODO: expand beyond simple 90 degree rotates, flips and flops
42493dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  */
425083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
425183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* Modulus the angle */
425283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  angle = fmod(angle, 360.0);
425383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( angle < 0 )
425483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    angle += 360.0;
425583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
425683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( 337.5 < angle || angle <= 22.5 )
425783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    return;   /* Near zero angle - no change! - At least not at this time */
425883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
425983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* Handle special cases */
426083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  switch (kernel->type) {
42613dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    /* These built-in kernels are cylindrical kernels, rotating is useless */
426283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case GaussianKernel:
426383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DoGKernel:
426457fe7a498c1302232dac8466864e84b12fad0807anthony    case LoGKernel:
42653c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case DiskKernel:
42663c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case PeaksKernel:
42673c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case LaplacianKernel:
42683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case ChebyshevKernel:
4269a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy    case ManhattanKernel:
427043c4925e5305a26e48d68f7893e94f55d0831c39anthony    case EuclideanKernel:
427143c4925e5305a26e48d68f7893e94f55d0831c39anthony      return;
427243c4925e5305a26e48d68f7893e94f55d0831c39anthony
427343c4925e5305a26e48d68f7893e94f55d0831c39anthony    /* These may be rotatable at non-90 angles in the future */
427443c4925e5305a26e48d68f7893e94f55d0831c39anthony    /* but simply rotating them in multiples of 90 degrees is useless */
427543c4925e5305a26e48d68f7893e94f55d0831c39anthony    case SquareKernel:
427643c4925e5305a26e48d68f7893e94f55d0831c39anthony    case DiamondKernel:
427743c4925e5305a26e48d68f7893e94f55d0831c39anthony    case PlusKernel:
42781d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony    case CrossKernel:
42791d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony      return;
4280bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy
4281bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    /* These only allows a +/-90 degree rotation (by transpose) */
4282bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    /* A 180 degree rotation is useless */
42831d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony    case BlurKernel:
42841d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony      if ( 135.0 < angle && angle <= 225.0 )
42851d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony        return;
42861d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony      if ( 225.0 < angle && angle <= 315.0 )
4287ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy        angle -= 180;
4288ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy      break;
42891d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
429043c4925e5305a26e48d68f7893e94f55d0831c39anthony    default:
429143c4925e5305a26e48d68f7893e94f55d0831c39anthony      break;
42923c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  }
42933c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* Attempt rotations by 45 degrees  -- 3x3 kernels only */
42943c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
42953c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
42963c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 3 && kernel->height == 3 )
42973c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a 3x3 square by 45 degree angle */
42983c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          double t  = kernel->values[0];
42994c08aed51c5899665ade97263692328eea4af106cristy          kernel->values[0] = kernel->values[3];
4300bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          kernel->values[3] = kernel->values[6];
43013c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->values[6] = kernel->values[7];
4302bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->values[7] = kernel->values[8];
43033c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->values[8] = kernel->values[5];
4304bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->values[5] = kernel->values[2];
43053c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->values[2] = kernel->values[1];
4306bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->values[1] = t;
43073c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          /* rotate non-centered origin */
43083c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          if ( kernel->x != 1 || kernel->y != 1 ) {
43093c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            ssize_t x,y;
431043c4925e5305a26e48d68f7893e94f55d0831c39anthony            x = (ssize_t) kernel->x-1;
431143c4925e5305a26e48d68f7893e94f55d0831c39anthony            y = (ssize_t) kernel->y-1;
431243c4925e5305a26e48d68f7893e94f55d0831c39anthony                 if ( x == y  ) x = 0;
431343c4925e5305a26e48d68f7893e94f55d0831c39anthony            else if ( x == 0  ) x = -y;
431443c4925e5305a26e48d68f7893e94f55d0831c39anthony            else if ( x == -y ) y = 0;
431543c4925e5305a26e48d68f7893e94f55d0831c39anthony            else if ( y == 0  ) y = x;
431643c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->x = (ssize_t) x+1;
43173c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel->y = (ssize_t) y+1;
43183c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          }
43193c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
4320d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          kernel->angle = fmod(kernel->angle+45.0, 360.0);
43211d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony        }
4322d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      else
4323d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        perror("Unable to rotate non-3x3 kernel by 45 degrees");
43241d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony    }
4325d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
43261d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony    {
4327d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      if ( kernel->width == 1 || kernel->height == 1 )
4328d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        { /* Do a transpose of a 1 dimensional kernel,
43291d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          ** which results in a fast 90 degree rotation of some type.
43301d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          */
43311d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          ssize_t
43321d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            t;
43331d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          t = (ssize_t) kernel->width;
43341d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          kernel->width = kernel->height;
43351d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          kernel->height = (size_t) t;
43361d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          t = kernel->x;
4337bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->x = kernel->y;
4338eaedf06777741da32408da72c1e512975c600c48cristy          kernel->y = t;
4339eaedf06777741da32408da72c1e512975c600c48cristy          if ( kernel->width == 1 ) {
4340ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
4341ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->angle = fmod(kernel->angle+90.0, 360.0);
43421d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          } else {
434343c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
434443c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+270.0, 360.0);
43453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          }
43463c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
43473c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else if ( kernel->width == kernel->height )
43483c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a square array of values by 90 degrees */
434983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          { register ssize_t
435083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              i,j,x,y;
435143c4925e5305a26e48d68f7893e94f55d0831c39anthony
435243c4925e5305a26e48d68f7893e94f55d0831c39anthony            register MagickRealType
435343c4925e5305a26e48d68f7893e94f55d0831c39anthony              *k,t;
435443c4925e5305a26e48d68f7893e94f55d0831c39anthony
435543c4925e5305a26e48d68f7893e94f55d0831c39anthony            k=kernel->values;
4356a23c649213358645994ad489b8c9a317c60111afcristy            for( i=0, x=(ssize_t) kernel->width-1;  i<=x;   i++, x--)
4357a96f2494a8e79144a225056be9545cc75e868137cristy              for( j=0, y=(ssize_t) kernel->height-1;  j<y;   j++, y--)
4358a96f2494a8e79144a225056be9545cc75e868137cristy                { t                    = k[i+j*kernel->width];
4359d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy                  k[i+j*kernel->width] = k[j+x*kernel->width];
4360a96f2494a8e79144a225056be9545cc75e868137cristy                  k[j+x*kernel->width] = k[x+y*kernel->width];
4361a96f2494a8e79144a225056be9545cc75e868137cristy                  k[x+y*kernel->width] = k[y+i*kernel->width];
4362a96f2494a8e79144a225056be9545cc75e868137cristy                  k[y+i*kernel->width] = t;
4363a96f2494a8e79144a225056be9545cc75e868137cristy                }
4364a96f2494a8e79144a225056be9545cc75e868137cristy          }
436583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          /* rotate the origin - relative to center of array */
436683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          { register ssize_t x,y;
4367e42f658533644aecb733785ffd91b286d6778deacristy            x = (ssize_t) (kernel->x*2-kernel->width+1);
4368e42f658533644aecb733785ffd91b286d6778deacristy            y = (ssize_t) (kernel->y*2-kernel->height+1);
436983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony            kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
437083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony            kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
4371bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          }
4372bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
437343c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+90.0, 360.0);
437443c4925e5305a26e48d68f7893e94f55d0831c39anthony        }
437583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      else
43763c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
437783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    }
437883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( 135.0 < angle && angle <= 225.0 )
437983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    {
438083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      /* For a 180 degree rotation - also know as a reflection
438183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony       * This is actually a very very common operation!
438283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony       * Basically all that is needed is a reversal of the kernel data!
438383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony       * And a reflection of the origon
438483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony       */
438583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      MagickRealType
438683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        t;
438783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
438883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      register MagickRealType
438946a369d839971ab627bdb31a93d8bd63e81b65a3anthony        *k;
439046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
439146a369d839971ab627bdb31a93d8bd63e81b65a3anthony      ssize_t
439246a369d839971ab627bdb31a93d8bd63e81b65a3anthony        i,
439346a369d839971ab627bdb31a93d8bd63e81b65a3anthony        j;
439446a369d839971ab627bdb31a93d8bd63e81b65a3anthony
439546a369d839971ab627bdb31a93d8bd63e81b65a3anthony      k=kernel->values;
439646a369d839971ab627bdb31a93d8bd63e81b65a3anthony      j=(ssize_t) (kernel->width*kernel->height-1);
439746a369d839971ab627bdb31a93d8bd63e81b65a3anthony      for (i=0;  i < j;  i++, j--)
439846a369d839971ab627bdb31a93d8bd63e81b65a3anthony        t=k[i],  k[i]=k[j],  k[j]=t;
439946a369d839971ab627bdb31a93d8bd63e81b65a3anthony
440046a369d839971ab627bdb31a93d8bd63e81b65a3anthony      kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
440146a369d839971ab627bdb31a93d8bd63e81b65a3anthony      kernel->y = (ssize_t) kernel->height - kernel->y - 1;
440246a369d839971ab627bdb31a93d8bd63e81b65a3anthony      angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
440346a369d839971ab627bdb31a93d8bd63e81b65a3anthony      kernel->angle = fmod(kernel->angle+180.0, 360.0);
4404ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy    }
440546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* At this point angle should at least between -45 (315) and +45 degrees
4406ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy   * In the future some form of non-orthogonal angled rotates could be
4407ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy   * performed here, posibily with a linear kernel restriction.
440846a369d839971ab627bdb31a93d8bd63e81b65a3anthony   */
440946a369d839971ab627bdb31a93d8bd63e81b65a3anthony
441046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
441146a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
441246a369d839971ab627bdb31a93d8bd63e81b65a3anthony
441346a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
441446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
441646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
441746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
441846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S c a l e G e o m e t r y K e r n e l I n f o                           %
4419954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy%                                                                             %
442046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
4421ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy%                                                                             %
442246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4423ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy%
442446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleGeometryKernelInfo() takes a geometry argument string, typically
442546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  provided as a  "-set option:convolve:scale {geometry}" user setting,
442646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  and modifies the kernel according to the parsed arguments of that setting.
442746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
442822de2722b682eb405b60ec6022a7546df994674eanthony%  The first argument (and any normalization flags) are passed to
442946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
443046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  is then passed to UnityAddKernelInfo() to add a scled unity kernel
443146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  into the scaled/normalized kernel.
44325acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%
443346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the ScaleGeometryKernelInfo method is:
443446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
443546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void ScaleGeometryKernelInfo(KernelInfo *kernel,
443646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%        const double scaling_factor,const MagickStatusType normalize_flags)
443746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
443846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
443946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
444046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
444146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
444246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o geometry:
444346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             The geometry string to parse, typically from the user provided
444446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             "-set option:convolve:scale {geometry}" setting.
4445d228c03fa334bae897eee6c2d8721fa48e1577bacristy%
444646a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
444746a369d839971ab627bdb31a93d8bd63e81b65a3anthonyMagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
444846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  const char *geometry)
444946a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
445046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  MagickStatusType
445146a369d839971ab627bdb31a93d8bd63e81b65a3anthony    flags;
445246a369d839971ab627bdb31a93d8bd63e81b65a3anthony
445346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  GeometryInfo
445446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args;
445546a369d839971ab627bdb31a93d8bd63e81b65a3anthony
445646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  SetGeometryInfo(&args);
445746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  flags = ParseGeometry(geometry, &args);
44586771f1e8987fa49f52d4176281a2e8524b8e31cbcristy
4459cc6c836da2a53b6023b716e4973090a6714dc3b0anthony#if 0
4460cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  /* For Debugging Geometry Input */
4461cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4462cc6c836da2a53b6023b716e4973090a6714dc3b0anthony       flags, args.rho, args.sigma, args.xi, args.psi );
4463cc6c836da2a53b6023b716e4973090a6714dc3b0anthony#endif
44641b2bc0a7da432e6e1cc0480280402df213faa940anthony
44651b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
4466999bb2c20aa9d42875bb5adba44951988d4ae354anthony    args.rho *= 0.01,  args.sigma *= 0.01;
4467999bb2c20aa9d42875bb5adba44951988d4ae354anthony
44681b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
4469999bb2c20aa9d42875bb5adba44951988d4ae354anthony    args.rho = 1.0;
447046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) == 0 )
447146a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.sigma = 0.0;
4472999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4473999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* Scale/Normalize the input kernel */
4474999bb2c20aa9d42875bb5adba44951988d4ae354anthony  ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
44751b2bc0a7da432e6e1cc0480280402df213faa940anthony
44761b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* Add Unity Kernel, for blending with original */
44771b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( (flags & SigmaValue) != 0 )
4478999bb2c20aa9d42875bb5adba44951988d4ae354anthony    UnityAddKernelInfo(kernel, args.sigma);
4479999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4480999bb2c20aa9d42875bb5adba44951988d4ae354anthony  return;
44811b2bc0a7da432e6e1cc0480280402df213faa940anthony}
4482999bb2c20aa9d42875bb5adba44951988d4ae354anthony/*
4483999bb2c20aa9d42875bb5adba44951988d4ae354anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4484999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                                                                             %
4485999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                                                                             %
4486cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4487999bb2c20aa9d42875bb5adba44951988d4ae354anthony%     S c a l e K e r n e l I n f o                                           %
4488999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                                                                             %
4489999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                                                                             %
4490cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4491999bb2c20aa9d42875bb5adba44951988d4ae354anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4492999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4493999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  ScaleKernelInfo() scales the given kernel list by the given amount, with or
44941e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  without normalization of the sum of the kernel values (as per given flags).
4495999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4496999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  By default (no flags given) the values within the kernel is scaled
44971b2bc0a7da432e6e1cc0480280402df213faa940anthony%  directly using given scaling factor without change.
4498999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4499999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  If either of the two 'normalize_flags' are given the kernel will first be
4500999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  normalized and then further scaled by the scaling factor value given.
4501999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
450246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  Kernel normalization ('normalize_flags' given) is designed to ensure that
450346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
4504cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  morphology methods will fall into -1.0 to +1.0 range.  Note that for
45054fd27e21043be809d66c8202e779255e5b660d2danthony%  non-HDRI versions of IM this may cause images to have any negative results
4506cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  clipped, unless some 'bias' is used.
4507999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4508999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  More specifically.  Kernels which only contain positive values (such as a
4509cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
4510cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  ensuring a 0.0 to +1.0 output range for non-HDRI images.
4511cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4512cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
4513cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  the kernel will be scaled by the absolute of the sum of kernel values, so
4514999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  that it will generally fall within the +/- 1.0 range.
4515999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4516999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
4517999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  will be scaled by just the sum of the postive values, so that its output
4518999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  range will again fall into the  +/- 1.0 range.
4519999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4520999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For special kernels designed for locating shapes using 'Correlate', (often
4521999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  only containing +1 and -1 values, representing foreground/brackground
4522cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  matching) a special normalization method is provided to scale the positive
4523cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  values separately to those of the negative values, so the kernel will be
45246771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%  forced to become a zero-sum kernel better suited to such searches.
45256771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%
4526cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  WARNING: Correct normalization of the kernel assumes that the '*_range'
4527999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  attributes within the kernel structure have been correctly set during the
4528999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  kernels creation.
4529999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4530999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  NOTE: The values used for 'normalize_flags' have been selected specifically
4531954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy%  to match the use of geometry options, so that '!' means NormalizeValue, '^'
4532954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy%  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
4533954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy%
453446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the ScaleKernelInfo method is:
45351b2bc0a7da432e6e1cc0480280402df213faa940anthony%
45361b2bc0a7da432e6e1cc0480280402df213faa940anthony%      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
45371b2bc0a7da432e6e1cc0480280402df213faa940anthony%               const MagickStatusType normalize_flags )
453846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4539999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  A description of each parameter follows:
4540999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4541b978e458a8e1f210bcb580951cf623687236b2fecristy%    o kernel: the Morphology/Convolution kernel
4542f4e0031305baeb01c89cfd2842cbbec021883550anthony%
4543999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o scaling_factor:
4544cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%             multiply all values (after normalization) by this factor if not
4545f4e0031305baeb01c89cfd2842cbbec021883550anthony%             zero.  If the kernel is normalized regardless of any flags.
4546f4e0031305baeb01c89cfd2842cbbec021883550anthony%
4547999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o normalize_flags:
454846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             GeometryFlags defining normalization method to use.
4549999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             specifically: NormalizeValue, CorrelateNormalizeValue,
4550b978e458a8e1f210bcb580951cf623687236b2fecristy%                           and/or PercentValue
4551999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4552b978e458a8e1f210bcb580951cf623687236b2fecristy*/
4553999bb2c20aa9d42875bb5adba44951988d4ae354anthonyMagickExport void ScaleKernelInfo(KernelInfo *kernel,
4554999bb2c20aa9d42875bb5adba44951988d4ae354anthony  const double scaling_factor,const GeometryFlags normalize_flags)
4555999bb2c20aa9d42875bb5adba44951988d4ae354anthony{
4556999bb2c20aa9d42875bb5adba44951988d4ae354anthony  register double
4557999bb2c20aa9d42875bb5adba44951988d4ae354anthony    pos_scale,
4558999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale;
4559999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4560999bb2c20aa9d42875bb5adba44951988d4ae354anthony  register ssize_t
4561cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4562bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy
4563a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony  /* do the other kernels in a multi-kernel list first */
4564999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( kernel->next != (KernelInfo *) NULL)
4565999bb2c20aa9d42875bb5adba44951988d4ae354anthony    ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
4566999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4567999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* Normalization of Kernel */
4568999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = 1.0;
4569999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&NormalizeValue) != 0 ) {
4570999bb2c20aa9d42875bb5adba44951988d4ae354anthony    if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4571999bb2c20aa9d42875bb5adba44951988d4ae354anthony      /* non-zero-summing kernel (generally positive) */
4572999bb2c20aa9d42875bb5adba44951988d4ae354anthony      pos_scale = fabs(kernel->positive_range + kernel->negative_range);
457346a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else
4574999bb2c20aa9d42875bb5adba44951988d4ae354anthony      /* zero-summing kernel */
4575999bb2c20aa9d42875bb5adba44951988d4ae354anthony      pos_scale = kernel->positive_range;
4576999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4577999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* Force kernel into a normalized zero-summing kernel */
4578999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4579999bb2c20aa9d42875bb5adba44951988d4ae354anthony    pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4580999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? kernel->positive_range : 1.0;
4581999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4582999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? -kernel->negative_range : 1.0;
4583cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  }
4584cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  else
4585cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    neg_scale = pos_scale;
4586cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4587cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  /* finialize scaling_factor for positive and negative components */
4588cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  pos_scale = scaling_factor/pos_scale;
4589cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  neg_scale = scaling_factor/neg_scale;
4590cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4591cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
459246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    if ( ! IfNaN(kernel->values[i]) )
459383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
459483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
459583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* convolution output range */
459683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  kernel->positive_range *= pos_scale;
459783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  kernel->negative_range *= neg_scale;
45984fd27e21043be809d66c8202e779255e5b660d2danthony  /* maximum and minimum values in kernel */
45994fd27e21043be809d66c8202e779255e5b660d2danthony  kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
460083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
460183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
460283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* swap kernel settings if user's scaling factor is negative */
460357fe7a498c1302232dac8466864e84b12fad0807anthony  if ( scaling_factor < MagickEpsilon ) {
460483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    double t;
460583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    t = kernel->positive_range;
460683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    kernel->positive_range = kernel->negative_range;
460783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    kernel->negative_range = t;
460883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    t = kernel->maximum;
460983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    kernel->maximum = kernel->minimum;
4610433d11887841b922ec5e6805f9fdd240c320b92ecristy    kernel->minimum = 1;
461183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
461257fe7a498c1302232dac8466864e84b12fad0807anthony
46137a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return;
46147a01dcf50ce12cb2a789bedff51e9345f022432eanthony}
4615bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy
46167a01dcf50ce12cb2a789bedff51e9345f022432eanthony/*
46177a01dcf50ce12cb2a789bedff51e9345f022432eanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46187a01dcf50ce12cb2a789bedff51e9345f022432eanthony%                                                                             %
46197a01dcf50ce12cb2a789bedff51e9345f022432eanthony%                                                                             %
46205acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%                                                                             %
46217a01dcf50ce12cb2a789bedff51e9345f022432eanthony%     S h o w K e r n e l I n f o                                             %
46225acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%                                                                             %
46235acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%                                                                             %
4624042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy%                                                                             %
4625b978e458a8e1f210bcb580951cf623687236b2fecristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46265acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%
46275acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%  ShowKernelInfo() outputs the details of the given kernel defination to
46281e604812fad85bb96f757a2393015ae3d061c39acristy%  standard error, generally due to a users 'showkernel' option request.
46295acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%
46307a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  The format of the ShowKernel method is:
46317a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
46327a01dcf50ce12cb2a789bedff51e9345f022432eanthony%      void ShowKernelInfo(const KernelInfo *kernel)
46335acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%
46347a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  A description of each parameter follows:
463546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
463646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel
46375acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy%
463846a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
46395acdd94fa3dabd964fcad53b4948ed7cac2886b1cristyMagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
464046a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
46415acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  const KernelInfo
464246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    *k;
464343c4925e5305a26e48d68f7893e94f55d0831c39anthony
46445acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  size_t
464543c4925e5305a26e48d68f7893e94f55d0831c39anthony    c, i, u, v;
4646a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony
46475acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
46487a01dcf50ce12cb2a789bedff51e9345f022432eanthony
46495acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Kernel");
4650d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy    if ( kernel->next != (KernelInfo *) NULL )
46515acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
46527a01dcf50ce12cb2a789bedff51e9345f022432eanthony    (void) FormatLocaleFile(stderr, " \"%s",
465383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          CommandOptionToMnemonic(MagickKernelOptions, k->type) );
465483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    if ( fabs(k->angle) >= MagickEpsilon )
4655cc6c836da2a53b6023b716e4973090a6714dc3b0anthony      (void) FormatLocaleFile(stderr, "@%lg", k->angle);
4656cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
4657cc6c836da2a53b6023b716e4973090a6714dc3b0anthony      k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
4658cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    (void) FormatLocaleFile(stderr,
4659cc6c836da2a53b6023b716e4973090a6714dc3b0anthony          " with values from %.*lg to %.*lg\n",
4660cc6c836da2a53b6023b716e4973090a6714dc3b0anthony          GetMagickPrecision(), k->minimum,
466143c4925e5305a26e48d68f7893e94f55d0831c39anthony          GetMagickPrecision(), k->maximum);
466243c4925e5305a26e48d68f7893e94f55d0831c39anthony    (void) FormatLocaleFile(stderr, "Forming a output range from %.*lg to %.*lg",
466343c4925e5305a26e48d68f7893e94f55d0831c39anthony          GetMagickPrecision(), k->negative_range,
466443c4925e5305a26e48d68f7893e94f55d0831c39anthony          GetMagickPrecision(), k->positive_range);
466543c4925e5305a26e48d68f7893e94f55d0831c39anthony    if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
466643c4925e5305a26e48d68f7893e94f55d0831c39anthony      (void) FormatLocaleFile(stderr, " (Zero-Summing)\n");
466743c4925e5305a26e48d68f7893e94f55d0831c39anthony    else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
466843c4925e5305a26e48d68f7893e94f55d0831c39anthony      (void) FormatLocaleFile(stderr, " (Normalized)\n");
466943c4925e5305a26e48d68f7893e94f55d0831c39anthony    else
467043c4925e5305a26e48d68f7893e94f55d0831c39anthony      (void) FormatLocaleFile(stderr, " (Sum %.*lg)\n",
467143c4925e5305a26e48d68f7893e94f55d0831c39anthony          GetMagickPrecision(), k->positive_range+k->negative_range);
467243c4925e5305a26e48d68f7893e94f55d0831c39anthony    for (i=v=0; v < k->height; v++) {
4673501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
4674501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      for (u=0; u < k->width; u++, i++)
467543c4925e5305a26e48d68f7893e94f55d0831c39anthony        if ( IfNaN(k->values[i]) )
467646a369d839971ab627bdb31a93d8bd63e81b65a3anthony          (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
467743c4925e5305a26e48d68f7893e94f55d0831c39anthony        else
467843c4925e5305a26e48d68f7893e94f55d0831c39anthony          (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
467943c4925e5305a26e48d68f7893e94f55d0831c39anthony              GetMagickPrecision(), (double) k->values[i]);
468043c4925e5305a26e48d68f7893e94f55d0831c39anthony      (void) FormatLocaleFile(stderr,"\n");
468143c4925e5305a26e48d68f7893e94f55d0831c39anthony    }
468243c4925e5305a26e48d68f7893e94f55d0831c39anthony  }
468343c4925e5305a26e48d68f7893e94f55d0831c39anthony}
468443c4925e5305a26e48d68f7893e94f55d0831c39anthony
468543c4925e5305a26e48d68f7893e94f55d0831c39anthony/*
468643c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
468743c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
468843c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
468943c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
469043c4925e5305a26e48d68f7893e94f55d0831c39anthony%     U n i t y A d d K e r n a l I n f o                                     %
469143c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
469246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
469346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
469446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469543c4925e5305a26e48d68f7893e94f55d0831c39anthony%
469646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
469743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  to the given pre-scaled and normalized Kernel.  This in effect adds that
469846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  amount of the original image into the resulting convolution kernel.  This
469943c4925e5305a26e48d68f7893e94f55d0831c39anthony%  value is usually provided by the user as a percentage value in the
470043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  'convolve:scale' setting.
470143c4925e5305a26e48d68f7893e94f55d0831c39anthony%
470243c4925e5305a26e48d68f7893e94f55d0831c39anthony%  The resulting effect is to convert the defined kernels into blended
470343c4925e5305a26e48d68f7893e94f55d0831c39anthony%  soft-blurs, unsharp kernels or into sharpening kernels.
470443c4925e5305a26e48d68f7893e94f55d0831c39anthony%
470543c4925e5305a26e48d68f7893e94f55d0831c39anthony%  The format of the UnityAdditionKernelInfo method is:
470643c4925e5305a26e48d68f7893e94f55d0831c39anthony%
470743c4925e5305a26e48d68f7893e94f55d0831c39anthony%      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
470843c4925e5305a26e48d68f7893e94f55d0831c39anthony%
4709cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4710cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4711cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4712cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4713cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o scale:
4714cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%             scaling factor for the unity kernel to be added to
4715cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%             the given kernel.
4716cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4717cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
4718cc6c836da2a53b6023b716e4973090a6714dc3b0anthonyMagickExport void UnityAddKernelInfo(KernelInfo *kernel,
4719cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  const double scale)
4720cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
472146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
4722cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  if ( kernel->next != (KernelInfo *) NULL)
4723cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    UnityAddKernelInfo(kernel->next, scale);
4724cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4725cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  /* Add the scaled unity kernel to the existing kernel */
4726cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  kernel->values[kernel->x+kernel->y*kernel->width] += scale;
4727cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  CalcKernelMetaData(kernel);  /* recalculate the meta-data */
4728cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristy
4729cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4730bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy}
4731cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4732cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
473346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
47341b2bc0a7da432e6e1cc0480280402df213faa940anthony%                                                                             %
47351b2bc0a7da432e6e1cc0480280402df213faa940anthony%                                                                             %
47361b2bc0a7da432e6e1cc0480280402df213faa940anthony%                                                                             %
473743c4925e5305a26e48d68f7893e94f55d0831c39anthony%     Z e r o K e r n e l N a n s                                             %
4738a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony%                                                                             %
4739cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4740cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4741cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4742cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4743%  ZeroKernelNans() replaces any special 'nan' value that may be present in
4744%  the kernel with a zero value.  This is typically done when the kernel will
4745%  be used in special hardware (GPU) convolution processors, to simply
4746%  matters.
4747%
4748%  The format of the ZeroKernelNans method is:
4749%
4750%      void ZeroKernelNans (KernelInfo *kernel)
4751%
4752%  A description of each parameter follows:
4753%
4754%    o kernel: the Morphology/Convolution kernel
4755%
4756*/
4757MagickPrivate void ZeroKernelNans(KernelInfo *kernel)
4758{
4759  register size_t
4760    i;
4761
4762  /* do the other kernels in a multi-kernel list first */
4763  if ( kernel->next != (KernelInfo *) NULL)
4764    ZeroKernelNans(kernel->next);
4765
4766  for (i=0; i < (kernel->width*kernel->height); i++)
4767    if ( IfNaN(kernel->values[i]) )
4768      kernel->values[i] = 0.0;
4769
4770  return;
4771}
4772