morphology.c revision 8693d3f324310fc2ca933cd239aa053d0651e0b6
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-2013 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/color-private.h"
564c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/enhance.h"
574c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception.h"
584c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception-private.h"
594c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/gem.h"
608ea81224e9ff022e56eb2cddb12860a8b2e90411cristy#include "MagickCore/gem-private.h"
614c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/hashmap.h"
624c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image.h"
634c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image-private.h"
644c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/list.h"
654c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/magick.h"
664c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/memory_.h"
67e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy#include "MagickCore/memory-private.h"
684c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/monitor-private.h"
694c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology.h"
704c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology-private.h"
714c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/option.h"
724c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel-accessor.h"
73f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy#include "MagickCore/pixel-private.h"
744c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/prepress.h"
754c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/quantize.h"
76ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy#include "MagickCore/resource_.h"
774c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/registry.h"
784c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/semaphore.h"
794c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/splay-tree.h"
804c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/statistic.h"
814c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string_.h"
824c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string-private.h"
8316881e68c6165c6191fc44151a8a4320e3dd1ffdcristy#include "MagickCore/thread-private.h"
844c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/token.h"
854c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/utility.h"
86d1dd6e4fefa0810b9893e6ac9418f79c97c1b39acristy#include "MagickCore/utility-private.h"
87a29d45f897949f04a47bb3da077395969f13dcbacristy
88c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony/*
89a29d45f897949f04a47bb3da077395969f13dcbacristy  Other global definitions used by module.
90a29d45f897949f04a47bb3da077395969f13dcbacristy*/
9129188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMin(const double x,const double y)
9229188a8682a98d4b7882cca434b170517555fc7danthony{
9329188a8682a98d4b7882cca434b170517555fc7danthony  return( x < y ? x : y);
9429188a8682a98d4b7882cca434b170517555fc7danthony}
9529188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMax(const double x,const double y)
9629188a8682a98d4b7882cca434b170517555fc7danthony{
9729188a8682a98d4b7882cca434b170517555fc7danthony  return( x > y ? x : y);
9829188a8682a98d4b7882cca434b170517555fc7danthony}
9929188a8682a98d4b7882cca434b170517555fc7danthony#define Minimize(assign,value) assign=MagickMin(assign,value)
10029188a8682a98d4b7882cca434b170517555fc7danthony#define Maximize(assign,value) assign=MagickMax(assign,value)
10129188a8682a98d4b7882cca434b170517555fc7danthony
10240ca0b982379d4ab2716435a46603d56b5b218b1anthony/* Integer Factorial Function - for a Binomial kernel */
10340ca0b982379d4ab2716435a46603d56b5b218b1anthony#if 1
10440ca0b982379d4ab2716435a46603d56b5b218b1anthonystatic inline size_t fact(size_t n)
10540ca0b982379d4ab2716435a46603d56b5b218b1anthony{
106f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy  size_t f,l;
10740ca0b982379d4ab2716435a46603d56b5b218b1anthony  for(f=1, l=2; l <= n; f=f*l, l++);
10840ca0b982379d4ab2716435a46603d56b5b218b1anthony  return(f);
10940ca0b982379d4ab2716435a46603d56b5b218b1anthony}
11040ca0b982379d4ab2716435a46603d56b5b218b1anthony#elif 1 /* glibc floating point alternatives */
11140ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)tgamma((double)n+1))
11240ca0b982379d4ab2716435a46603d56b5b218b1anthony#else
11340ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)lgamma((double)n+1))
11440ca0b982379d4ab2716435a46603d56b5b218b1anthony#endif
11540ca0b982379d4ab2716435a46603d56b5b218b1anthony
11640ca0b982379d4ab2716435a46603d56b5b218b1anthony
117c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthony/* Currently these are only internal to this module */
118c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthonystatic void
11946a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(KernelInfo *),
120bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandMirrorKernelInfo(KernelInfo *),
121bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandRotateKernelInfo(KernelInfo *, const double),
122ef656913b0b30d713ae94c82c47693c9dc69c9f4cristy  RotateKernelInfo(KernelInfo *, double);
123602ab9b30b644a78a4057da93d838a77391ec0acanthony
1243dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
1253dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony/* Quick function to find last kernel in a kernel list */
1263dd0f620e7a1d12f747ce167844cd7269bfa9f12anthonystatic inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
1273dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony{
1283dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  while (kernel->next != (KernelInfo *) NULL)
1293dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    kernel = kernel->next;
1303dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  return(kernel);
1313dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony}
1323dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
133602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
134602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
136602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
137602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
13883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     A c q u i r e K e r n e l I n f o                                       %
139602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
140602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
141602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
142602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1442be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%  AcquireKernelInfo() takes the given string (generally supplied by the
145602ab9b30b644a78a4057da93d838a77391ec0acanthony%  user) and converts it into a Morphology/Convolution Kernel.  This allows
146602ab9b30b644a78a4057da93d838a77391ec0acanthony%  users to specify a kernel from a number of pre-defined kernels, or to fully
147602ab9b30b644a78a4057da93d838a77391ec0acanthony%  specify their own kernel for a specific Convolution or Morphology
148602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Operation.
149602ab9b30b644a78a4057da93d838a77391ec0acanthony%
150602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The kernel so generated can be any rectangular array of floating point
151602ab9b30b644a78a4057da93d838a77391ec0acanthony%  values (doubles) with the 'control point' or 'pixel being affected'
152602ab9b30b644a78a4057da93d838a77391ec0acanthony%  anywhere within that array of values.
153602ab9b30b644a78a4057da93d838a77391ec0acanthony%
15483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  Previously IM was restricted to a square of odd size using the exact
15519910ef25dd3d99d1981a9e42c934133170ee714anthony%  center as origin, this is no longer the case, and any rectangular kernel
15683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  with any value being declared the origin. This in turn allows the use of
15783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  highly asymmetrical kernels.
158602ab9b30b644a78a4057da93d838a77391ec0acanthony%
159602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The floating point values in the kernel can also include a special value
16083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  known as 'nan' or 'not a number' to indicate that this value is not part
16183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  of the kernel array. This allows you to shaped the kernel within its
16283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  rectangular area. That is 'nan' values provide a 'mask' for the kernel
16383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  shape.  However at least one non-nan value must be provided for correct
16483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  working of a kernel.
165602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1667a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  The returned kernel should be freed using the DestroyKernelInfo() when you
1677a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  are finished with it.  Do not free this memory yourself.
168602ab9b30b644a78a4057da93d838a77391ec0acanthony%
169602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Input kernel defintion strings can consist of any of three types.
170602ab9b30b644a78a4057da93d838a77391ec0acanthony%
171bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "name:args[[@><]"
17229188a8682a98d4b7882cca434b170517555fc7danthony%         Select from one of the built in kernels, using the name and
17329188a8682a98d4b7882cca434b170517555fc7danthony%         geometry arguments supplied.  See AcquireKernelBuiltIn()
174602ab9b30b644a78a4057da93d838a77391ec0acanthony%
175bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "WxH[+X+Y][@><]:num, num, num ..."
1761b2bc0a7da432e6e1cc0480280402df213faa940anthony%         a kernel of size W by H, with W*H floating point numbers following.
177602ab9b30b644a78a4057da93d838a77391ec0acanthony%         the 'center' can be optionally be defined at +X+Y (such that +0+0
17829188a8682a98d4b7882cca434b170517555fc7danthony%         is top left corner). If not defined the pixel in the center, for
17929188a8682a98d4b7882cca434b170517555fc7danthony%         odd sizes, or to the immediate top or left of center for even sizes
18029188a8682a98d4b7882cca434b170517555fc7danthony%         is automatically selected.
181602ab9b30b644a78a4057da93d838a77391ec0acanthony%
18229188a8682a98d4b7882cca434b170517555fc7danthony%    "num, num, num, num, ..."
18329188a8682a98d4b7882cca434b170517555fc7danthony%         list of floating point numbers defining an 'old style' odd sized
18429188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel.  At least 9 values should be provided for a 3x3
18529188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
18629188a8682a98d4b7882cca434b170517555fc7danthony%         Values can be space or comma separated.  This is not recommended.
187602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1887a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  You can define a 'list of kernels' which can be used by some morphology
1891e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  operators A list is defined as a semi-colon separated list kernels.
1907a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
191dbc8989a61339951c6434d9a43e7b6fefb5da374anthony%     " kernel ; kernel ; kernel ; "
1927a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
1931dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%  Any extra ';' characters, at start, end or between kernel defintions are
19443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  simply ignored.
19543c4925e5305a26e48d68f7893e94f55d0831c39anthony%
196bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The special flags will expand a single kernel, into a list of rotated
197bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
198bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
199bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The '<' also exands using 90-degree rotates, but giving a 180-degree
200bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  reflected kernel before the +/- 90-degree rotations, which can be important
201bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  for Thinning operations.
202bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
20343c4925e5305a26e48d68f7893e94f55d0831c39anthony%  Note that 'name' kernels will start with an alphabetic character while the
20443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  new kernel specification has a ':' character in its specification string.
20543c4925e5305a26e48d68f7893e94f55d0831c39anthony%  If neither is the case, it is assumed an old style of a simple list of
20643c4925e5305a26e48d68f7893e94f55d0831c39anthony%  numbers generating a odd-sized square kernel has been given.
2077a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
208602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernal method is:
209602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2102be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelInfo(const char *kernel_string)
211602ab9b30b644a78a4057da93d838a77391ec0acanthony%
212602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
213602ab9b30b644a78a4057da93d838a77391ec0acanthony%
214602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel_string: the Morphology/Convolution kernel wanted.
215602ab9b30b644a78a4057da93d838a77391ec0acanthony%
216602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
217602ab9b30b644a78a4057da93d838a77391ec0acanthony
218c84dce50867229e4872193e8eed5dbab58eb9f02anthony/* This was separated so that it could be used as a separate
2195ef8e94ff55717be2387d537bd49025780a1a558anthony** array input handling function, such as for -color-matrix
220c84dce50867229e4872193e8eed5dbab58eb9f02anthony*/
2215ef8e94ff55717be2387d537bd49025780a1a558anthonystatic KernelInfo *ParseKernelArray(const char *kernel_string)
222602ab9b30b644a78a4057da93d838a77391ec0acanthony{
2232be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
224602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
225602ab9b30b644a78a4057da93d838a77391ec0acanthony
226602ab9b30b644a78a4057da93d838a77391ec0acanthony  char
227602ab9b30b644a78a4057da93d838a77391ec0acanthony    token[MaxTextExtent];
228602ab9b30b644a78a4057da93d838a77391ec0acanthony
229602ab9b30b644a78a4057da93d838a77391ec0acanthony  const char
2305ef8e94ff55717be2387d537bd49025780a1a558anthony    *p,
2315ef8e94ff55717be2387d537bd49025780a1a558anthony    *end;
232602ab9b30b644a78a4057da93d838a77391ec0acanthony
233bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
234c84dce50867229e4872193e8eed5dbab58eb9f02anthony    i;
235602ab9b30b644a78a4057da93d838a77391ec0acanthony
23629188a8682a98d4b7882cca434b170517555fc7danthony  double
23729188a8682a98d4b7882cca434b170517555fc7danthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
23829188a8682a98d4b7882cca434b170517555fc7danthony
23943c4925e5305a26e48d68f7893e94f55d0831c39anthony  MagickStatusType
24043c4925e5305a26e48d68f7893e94f55d0831c39anthony    flags;
24143c4925e5305a26e48d68f7893e94f55d0831c39anthony
24243c4925e5305a26e48d68f7893e94f55d0831c39anthony  GeometryInfo
24343c4925e5305a26e48d68f7893e94f55d0831c39anthony    args;
24443c4925e5305a26e48d68f7893e94f55d0831c39anthony
245a64b85d7873d5e540fe6e2941aa98ec7653a4e2dcristy  kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
2462be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  if (kernel == (KernelInfo *)NULL)
247602ab9b30b644a78a4057da93d838a77391ec0acanthony    return(kernel);
248602ab9b30b644a78a4057da93d838a77391ec0acanthony  (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
24943c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->minimum = kernel->maximum = kernel->angle = 0.0;
2507a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->negative_range = kernel->positive_range = 0.0;
251602ab9b30b644a78a4057da93d838a77391ec0acanthony  kernel->type = UserDefinedKernel;
2527a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->next = (KernelInfo *) NULL;
253d43a46bc9598004091eae232bc7938e009b494a1cristy  kernel->signature = MagickSignature;
2545e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
2555e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(kernel);
256602ab9b30b644a78a4057da93d838a77391ec0acanthony
2575ef8e94ff55717be2387d537bd49025780a1a558anthony  /* find end of this specific kernel definition string */
2585ef8e94ff55717be2387d537bd49025780a1a558anthony  end = strchr(kernel_string, ';');
2595ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( end == (char *) NULL )
2605ef8e94ff55717be2387d537bd49025780a1a558anthony    end = strchr(kernel_string, '\0');
2615ef8e94ff55717be2387d537bd49025780a1a558anthony
262a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* clear flags - for Expanding kernel lists thorugh rotations */
26343c4925e5305a26e48d68f7893e94f55d0831c39anthony   flags = NoValue;
26443c4925e5305a26e48d68f7893e94f55d0831c39anthony
265f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony  /* Has a ':' in argument - New user kernel specification
266f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony     FUTURE: this split on ':' could be done by StringToken()
267f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony   */
268602ab9b30b644a78a4057da93d838a77391ec0acanthony  p = strchr(kernel_string, ':');
2695ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( p != (char *) NULL && p < end)
270602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
271602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* ParseGeometry() needs the geometry separated! -- Arrgghh */
272150989ed67ef9da53141a65e5f3ebdb05dd025abcristy      memcpy(token, kernel_string, (size_t) (p-kernel_string));
273602ab9b30b644a78a4057da93d838a77391ec0acanthony      token[p-kernel_string] = '\0';
274c84dce50867229e4872193e8eed5dbab58eb9f02anthony      SetGeometryInfo(&args);
275602ab9b30b644a78a4057da93d838a77391ec0acanthony      flags = ParseGeometry(token, &args);
276602ab9b30b644a78a4057da93d838a77391ec0acanthony
27729188a8682a98d4b7882cca434b170517555fc7danthony      /* Size handling and checks of geometry settings */
278602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
279602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.rho = args.sigma;         /* then  width = height */
280602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.rho < 1.0 )            /* if width too small */
281602ab9b30b644a78a4057da93d838a77391ec0acanthony         args.rho = 1.0;               /* then  width = 1 */
282602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.sigma < 1.0 )          /* if height too small */
283602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.sigma = args.rho;         /* then  height = width */
284bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = (size_t)args.rho;
285bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->height = (size_t)args.sigma;
286602ab9b30b644a78a4057da93d838a77391ec0acanthony
287602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* Offset Handling and Checks */
288602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.xi  < 0.0 || args.psi < 0.0 )
28983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
290bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
291ea068a53d23d6dca08f1bce44c8937d54f83b983anthony                                        : (ssize_t) (kernel->width-1)/2;
292bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
293ea068a53d23d6dca08f1bce44c8937d54f83b983anthony                                        : (ssize_t) (kernel->height-1)/2;
294bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      if ( kernel->x >= (ssize_t) kernel->width ||
295bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy           kernel->y >= (ssize_t) kernel->height )
29683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
297602ab9b30b644a78a4057da93d838a77391ec0acanthony
298602ab9b30b644a78a4057da93d838a77391ec0acanthony      p++; /* advance beyond the ':' */
299602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
300602ab9b30b644a78a4057da93d838a77391ec0acanthony  else
301c84dce50867229e4872193e8eed5dbab58eb9f02anthony    { /* ELSE - Old old specification, forming odd-square kernel */
302602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* count up number of values given */
303602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
304a699b171eff7e0178463e8f271b35a3cbb995f0ecristy      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
30529188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
3065ef8e94ff55717be2387d537bd49025780a1a558anthony      for (i=0; p < end; i++)
307602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
308602ab9b30b644a78a4057da93d838a77391ec0acanthony        GetMagickToken(p,&p,token);
309602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (*token == ',')
310602ab9b30b644a78a4057da93d838a77391ec0acanthony          GetMagickToken(p,&p,token);
311602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
312602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* set the size of the kernel - old sized square */
313bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
314bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
315602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
31629188a8682a98d4b7882cca434b170517555fc7danthony      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
31729188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
318602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
319602ab9b30b644a78a4057da93d838a77391ec0acanthony
320602ab9b30b644a78a4057da93d838a77391ec0acanthony  /* Read in the kernel values from rest of input string argument */
321e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
322e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy    kernel->width,kernel->height*sizeof(*kernel->values)));
323d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if (kernel->values == (MagickRealType *) NULL)
32483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    return(DestroyKernelInfo(kernel));
325c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->minimum = +MagickHuge;
326c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->maximum = -MagickHuge;
327c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->negative_range = kernel->positive_range = 0.0;
328bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
329602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
330602ab9b30b644a78a4057da93d838a77391ec0acanthony    GetMagickToken(p,&p,token);
331602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (*token == ',')
332602ab9b30b644a78a4057da93d838a77391ec0acanthony      GetMagickToken(p,&p,token);
33329188a8682a98d4b7882cca434b170517555fc7danthony    if (    LocaleCompare("nan",token) == 0
334c84dce50867229e4872193e8eed5dbab58eb9f02anthony        || LocaleCompare("-",token) == 0 ) {
335ea068a53d23d6dca08f1bce44c8937d54f83b983anthony      kernel->values[i] = nan; /* this value is not part of neighbourhood */
33629188a8682a98d4b7882cca434b170517555fc7danthony    }
33729188a8682a98d4b7882cca434b170517555fc7danthony    else {
3380ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony      kernel->values[i] = StringToDouble(token,(char **) NULL);
33929188a8682a98d4b7882cca434b170517555fc7danthony      ( kernel->values[i] < 0)
340c99304fe3c8d9c617da792b40b57c118bb1249afcristy          ?  ( kernel->negative_range += kernel->values[i] )
341c99304fe3c8d9c617da792b40b57c118bb1249afcristy          :  ( kernel->positive_range += kernel->values[i] );
342c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Minimize(kernel->minimum, kernel->values[i]);
343c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Maximize(kernel->maximum, kernel->values[i]);
34429188a8682a98d4b7882cca434b170517555fc7danthony    }
34529188a8682a98d4b7882cca434b170517555fc7danthony  }
34629188a8682a98d4b7882cca434b170517555fc7danthony
3475ef8e94ff55717be2387d537bd49025780a1a558anthony  /* sanity check -- no more values in kernel definition */
3485ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(p,&p,token);
3495ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( *token != '\0' && *token != ';' && *token != '\'' )
3505ef8e94ff55717be2387d537bd49025780a1a558anthony    return(DestroyKernelInfo(kernel));
3515ef8e94ff55717be2387d537bd49025780a1a558anthony
352c84dce50867229e4872193e8eed5dbab58eb9f02anthony#if 0
353c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* this was the old method of handling a incomplete kernel */
354bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) ) {
355c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Minimize(kernel->minimum, kernel->values[i]);
356c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Maximize(kernel->maximum, kernel->values[i]);
357bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
35829188a8682a98d4b7882cca434b170517555fc7danthony      kernel->values[i]=0.0;
359602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
360c84dce50867229e4872193e8eed5dbab58eb9f02anthony#else
361c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Number of values for kernel was not enough - Report Error */
362bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) )
363c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
364c84dce50867229e4872193e8eed5dbab58eb9f02anthony#endif
365c84dce50867229e4872193e8eed5dbab58eb9f02anthony
366c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* check that we recieved at least one real (non-nan) value! */
367c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( kernel->minimum == MagickHuge )
368c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
369602ab9b30b644a78a4057da93d838a77391ec0acanthony
37043c4925e5305a26e48d68f7893e94f55d0831c39anthony  if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
371bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
372bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
373bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
374bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
375bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
37643c4925e5305a26e48d68f7893e94f55d0831c39anthony
377602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
378602ab9b30b644a78a4057da93d838a77391ec0acanthony}
379c84dce50867229e4872193e8eed5dbab58eb9f02anthony
38043c4925e5305a26e48d68f7893e94f55d0831c39anthonystatic KernelInfo *ParseKernelName(const char *kernel_string)
381c84dce50867229e4872193e8eed5dbab58eb9f02anthony{
382c84dce50867229e4872193e8eed5dbab58eb9f02anthony  char
383c84dce50867229e4872193e8eed5dbab58eb9f02anthony    token[MaxTextExtent];
384c84dce50867229e4872193e8eed5dbab58eb9f02anthony
385c84dce50867229e4872193e8eed5dbab58eb9f02anthony  const char
3867a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *p,
3877a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *end;
388c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3899d314ff2c17a77996c05413c2013880387e50f0ecristy  GeometryInfo
3909d314ff2c17a77996c05413c2013880387e50f0ecristy    args;
3919d314ff2c17a77996c05413c2013880387e50f0ecristy
3929d314ff2c17a77996c05413c2013880387e50f0ecristy  KernelInfo
3939d314ff2c17a77996c05413c2013880387e50f0ecristy    *kernel;
3949d314ff2c17a77996c05413c2013880387e50f0ecristy
395c84dce50867229e4872193e8eed5dbab58eb9f02anthony  MagickStatusType
396c84dce50867229e4872193e8eed5dbab58eb9f02anthony    flags;
397c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3989d314ff2c17a77996c05413c2013880387e50f0ecristy  ssize_t
3999d314ff2c17a77996c05413c2013880387e50f0ecristy    type;
400c84dce50867229e4872193e8eed5dbab58eb9f02anthony
401c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Parse special 'named' kernel */
4025ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(kernel_string,&p,token);
403042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy  type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
404c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( type < 0 || type == UserDefinedKernel )
4055ef8e94ff55717be2387d537bd49025780a1a558anthony    return((KernelInfo *)NULL);  /* not a valid named kernel */
406c84dce50867229e4872193e8eed5dbab58eb9f02anthony
407c84dce50867229e4872193e8eed5dbab58eb9f02anthony  while (((isspace((int) ((unsigned char) *p)) != 0) ||
4085ef8e94ff55717be2387d537bd49025780a1a558anthony          (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
409c84dce50867229e4872193e8eed5dbab58eb9f02anthony    p++;
4107a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4117a01dcf50ce12cb2a789bedff51e9345f022432eanthony  end = strchr(p, ';'); /* end of this kernel defintion */
4127a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( end == (char *) NULL )
4137a01dcf50ce12cb2a789bedff51e9345f022432eanthony    end = strchr(p, '\0');
4147a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4157a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* ParseGeometry() needs the geometry separated! -- Arrgghh */
4167a01dcf50ce12cb2a789bedff51e9345f022432eanthony  memcpy(token, p, (size_t) (end-p));
4177a01dcf50ce12cb2a789bedff51e9345f022432eanthony  token[end-p] = '\0';
418c84dce50867229e4872193e8eed5dbab58eb9f02anthony  SetGeometryInfo(&args);
4197a01dcf50ce12cb2a789bedff51e9345f022432eanthony  flags = ParseGeometry(token, &args);
420c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4213c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#if 0
4223c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* For Debugging Geometry Input */
4235acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4241e604812fad85bb96f757a2393015ae3d061c39acristy    flags, args.rho, args.sigma, args.xi, args.psi );
4253c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#endif
4263c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
427c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* special handling of missing values in input string */
428c84dce50867229e4872193e8eed5dbab58eb9f02anthony  switch( type ) {
429a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Shape Kernel Defaults */
430529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
431529482f4b494010a13338a74446c510712f670b3anthony      if ( (flags & WidthValue) == 0 )
432a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
4335ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4345ef8e94ff55717be2387d537bd49025780a1a558anthony    case SquareKernel:
4355ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiamondKernel:
4361ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
4375ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiskKernel:
4385ef8e94ff55717be2387d537bd49025780a1a558anthony    case PlusKernel:
4393dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
4405ef8e94ff55717be2387d537bd49025780a1a558anthony      if ( (flags & HeightValue) == 0 )
441a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
4425ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
443c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
444c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if ( (flags & XValue) == 0 )
445a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
446c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
447a9892d898acb81e1ec73106d892855fdc5a69427anthony    case RectangleKernel:    /* Rectangle - set size defaults */
448a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
449a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = args.sigma;         /* then  width = height */
450a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.rho < 1.0 )            /* if width too small */
451a9892d898acb81e1ec73106d892855fdc5a69427anthony          args.rho = 3;                /* then  width = 3 */
452a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.sigma < 1.0 )          /* if height too small */
453a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = args.rho;         /* then  height = width */
454a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & XValue) == 0 )     /* center offset if not defined */
455a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = (double)(((ssize_t)args.rho-1)/2);
456a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & YValue) == 0 )
457a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.psi = (double)(((ssize_t)args.sigma-1)/2);
458a9892d898acb81e1ec73106d892855fdc5a69427anthony      break;
459a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Distance Kernel Defaults */
4605ef8e94ff55717be2387d537bd49025780a1a558anthony    case ChebyshevKernel:
461bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
4621ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonalKernel:
4635ef8e94ff55717be2387d537bd49025780a1a558anthony    case EuclideanKernel:
46443c4925e5305a26e48d68f7893e94f55d0831c39anthony      if ( (flags & HeightValue) == 0 )           /* no distance scale */
46543c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = 100.0;                       /* default distance scaling */
46643c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
46743c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
46843c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
46943c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma *= QuantumRange/100.0;         /* percentage of color range */
4705ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4715ef8e94ff55717be2387d537bd49025780a1a558anthony    default:
4725ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
473c84dce50867229e4872193e8eed5dbab58eb9f02anthony  }
474c84dce50867229e4872193e8eed5dbab58eb9f02anthony
475f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
476529482f4b494010a13338a74446c510712f670b3anthony  if ( kernel == (KernelInfo *) NULL )
477529482f4b494010a13338a74446c510712f670b3anthony    return(kernel);
478f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
479f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  /* global expand to rotated kernel list - only for single kernels */
480f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  if ( kernel->next == (KernelInfo *) NULL ) {
481f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony    if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
482bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 45.0);
483bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
484bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 90.0);
485bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
486bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandMirrorKernelInfo(kernel);
487f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  }
488f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
489f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  return(kernel);
490c84dce50867229e4872193e8eed5dbab58eb9f02anthony}
491c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4925ef8e94ff55717be2387d537bd49025780a1a558anthonyMagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
4935ef8e94ff55717be2387d537bd49025780a1a558anthony{
4947a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4957a01dcf50ce12cb2a789bedff51e9345f022432eanthony  KernelInfo
496dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *kernel,
49743c4925e5305a26e48d68f7893e94f55d0831c39anthony    *new_kernel;
4987a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4995ef8e94ff55717be2387d537bd49025780a1a558anthony  char
5005ef8e94ff55717be2387d537bd49025780a1a558anthony    token[MaxTextExtent];
5015ef8e94ff55717be2387d537bd49025780a1a558anthony
5027a01dcf50ce12cb2a789bedff51e9345f022432eanthony  const char
503dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *p;
5047a01dcf50ce12cb2a789bedff51e9345f022432eanthony
505bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
506e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony    kernel_number;
507e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
5085e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
5095e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(ParseKernelArray(kernel_string));
510dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  p = kernel_string;
51143c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel = NULL;
512e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony  kernel_number = 0;
5137a01dcf50ce12cb2a789bedff51e9345f022432eanthony
514dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
5157a01dcf50ce12cb2a789bedff51e9345f022432eanthony
5161e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp    /* ignore extra or multiple ';' kernel separators */
517dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    if ( *token != ';' ) {
5187a01dcf50ce12cb2a789bedff51e9345f022432eanthony
519dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      /* tokens starting with alpha is a Named kernel */
52043c4925e5305a26e48d68f7893e94f55d0831c39anthony      if (isalpha((int) *token) != 0)
52143c4925e5305a26e48d68f7893e94f55d0831c39anthony        new_kernel = ParseKernelName(p);
522dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      else /* otherwise a user defined kernel array */
52343c4925e5305a26e48d68f7893e94f55d0831c39anthony        new_kernel = ParseKernelArray(p);
524dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
525e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      /* Error handling -- this is not proper error handling! */
526e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      if ( new_kernel == (KernelInfo *) NULL ) {
52775920b23a8a3883792cbb69d569c33cc789cf1b5cristy        (void) FormatLocaleFile(stderr,"Failed to parse kernel number #%.20g\n",
5281e604812fad85bb96f757a2393015ae3d061c39acristy          (double) kernel_number);
529e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        if ( kernel != (KernelInfo *) NULL )
530e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony          kernel=DestroyKernelInfo(kernel);
531e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        return((KernelInfo *) NULL);
532dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      }
533e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
534e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      /* initialise or append the kernel list */
5353dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony      if ( kernel == (KernelInfo *) NULL )
5363dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        kernel = new_kernel;
5373dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony      else
53843c4925e5305a26e48d68f7893e94f55d0831c39anthony        LastKernelInfo(kernel)->next = new_kernel;
539dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    }
540dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
541dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    /* look for the next kernel in list */
542dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p = strchr(p, ';');
543dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    if ( p == (char *) NULL )
544dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      break;
545dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p++;
5465ef8e94ff55717be2387d537bd49025780a1a558anthony
547dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  }
5487a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(kernel);
5495ef8e94ff55717be2387d537bd49025780a1a558anthony}
5505ef8e94ff55717be2387d537bd49025780a1a558anthony
551602ab9b30b644a78a4057da93d838a77391ec0acanthony
552602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
553602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
555602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
556602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
557602ab9b30b644a78a4057da93d838a77391ec0acanthony%     A c q u i r e K e r n e l B u i l t I n                                 %
558602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
559602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
560602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
561602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562602ab9b30b644a78a4057da93d838a77391ec0acanthony%
563602ab9b30b644a78a4057da93d838a77391ec0acanthony%  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
564602ab9b30b644a78a4057da93d838a77391ec0acanthony%  kernels used for special purposes such as gaussian blurring, skeleton
565602ab9b30b644a78a4057da93d838a77391ec0acanthony%  pruning, and edge distance determination.
566602ab9b30b644a78a4057da93d838a77391ec0acanthony%
567602ab9b30b644a78a4057da93d838a77391ec0acanthony%  They take a KernelType, and a set of geometry style arguments, which were
568602ab9b30b644a78a4057da93d838a77391ec0acanthony%  typically decoded from a user supplied string, or from a more complex
569602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Morphology Method that was requested.
570602ab9b30b644a78a4057da93d838a77391ec0acanthony%
571602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernalBuiltIn method is:
572602ab9b30b644a78a4057da93d838a77391ec0acanthony%
5732be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
574602ab9b30b644a78a4057da93d838a77391ec0acanthony%           const GeometryInfo args)
575602ab9b30b644a78a4057da93d838a77391ec0acanthony%
576602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
577602ab9b30b644a78a4057da93d838a77391ec0acanthony%
578602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o type: the pre-defined type of kernel wanted
579602ab9b30b644a78a4057da93d838a77391ec0acanthony%
580602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o args: arguments defining or modifying the kernel
581602ab9b30b644a78a4057da93d838a77391ec0acanthony%
582602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Convolution Kernels
583602ab9b30b644a78a4057da93d838a77391ec0acanthony%
58446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    Unity
585529482f4b494010a13338a74446c510712f670b3anthony%       The a No-Op or Scaling single element kernel.
58646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
5873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Gaussian:{radius},{sigma}
5882489f53a1153c2b619b1c9a6744602e8840bd9a9glennrp%       Generate a two-dimensional gaussian kernel, as used by -gaussian.
589c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The sigma for the curve is required.  The resulting kernel is
590c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       normalized,
591c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
592c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
593602ab9b30b644a78a4057da93d838a77391ec0acanthony%
594602ab9b30b644a78a4057da93d838a77391ec0acanthony%       NOTE: that the 'radius' is optional, but if provided can limit (clip)
595602ab9b30b644a78a4057da93d838a77391ec0acanthony%       the final size of the resulting kernel to a square 2*radius+1 in size.
596602ab9b30b644a78a4057da93d838a77391ec0acanthony%       The radius should be at least 2 times that of the sigma value, or
597602ab9b30b644a78a4057da93d838a77391ec0acanthony%       sever clipping and aliasing may result.  If not given or set to 0 the
598602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius will be determined so as to produce the best minimal error
599602ab9b30b644a78a4057da93d838a77391ec0acanthony%       result, which is usally much larger than is normally needed.
600602ab9b30b644a78a4057da93d838a77391ec0acanthony%
601501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    LoG:{radius},{sigma}
602501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
603501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        The supposed ideal edge detection, zero-summing kernel.
604501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
605501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
606501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        approx 1.6 (according to wikipedia).
607501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
608501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    DoG:{radius},{sigma1},{sigma2}
609c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        "Difference of Gaussians" Kernel.
610c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
611c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
612c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        The result is a zero-summing kernel.
613c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
614c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Blur:{radius},{sigma}[,{angle}]
6154c08aed51c5899665ade97263692328eea4af106cristy%       Generates a 1 dimensional or linear gaussian blur, at the angle given
616c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       (current restricted to orthogonal angles).  If a 'radius' is given the
617c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
618c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       by a 90 degree angle.
619c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
620c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
621c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
622c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Note that two convolutions with two "Blur" kernels perpendicular to
623f0a92fd8deb68d411304359906b12679b675691fglennrp%       each other, is equivalent to a far larger "Gaussian" kernel with the
624c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       same sigma value, However it is much faster to apply. This is how the
625c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       "-blur" operator actually works.
626c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
6273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Comet:{width},{sigma},{angle}
6283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Blur in one direction only, much like how a bright object leaves
629602ab9b30b644a78a4057da93d838a77391ec0acanthony%       a comet like trail.  The Kernel is actually half a gaussian curve,
6303c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Adding two such blurs in opposite directions produces a Blur Kernel.
6313c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Angle can be rotated in multiples of 90 degrees.
632602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Note that the first argument is the width of the kernel and not the
634602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius of the kernel.
635602ab9b30b644a78a4057da93d838a77391ec0acanthony%
63640ca0b982379d4ab2716435a46603d56b5b218b1anthony%    Binomial:[{radius}]
63740ca0b982379d4ab2716435a46603d56b5b218b1anthony%       Generate a discrete kernel using a 2 dimentional Pascel's Triangle
638eef684ff80c7d5aca1493f4755426c88b3d3accdanthony%       of values. Used for special forma of image filters.
63940ca0b982379d4ab2716435a46603d56b5b218b1anthony%
640602ab9b30b644a78a4057da93d838a77391ec0acanthony%    # Still to be implemented...
641602ab9b30b644a78a4057da93d838a77391ec0acanthony%    #
6424fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter2D
6434fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter1D
6444fd27e21043be809d66c8202e779255e5b660d2danthony%    #    Set kernel values using a resize filter, and given scale (sigma)
645dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%    #    Cylindrical or Linear.   Is this possible with an image?
6464fd27e21043be809d66c8202e779255e5b660d2danthony%    #
647602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6483c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  Named Constant Convolution Kernels
6493c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
650c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  All these are unscaled, zero-summing kernels by default. As such for
651c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  non-HDRI version of ImageMagick some form of normalization, user scaling,
652c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  and biasing the results is recommended, to prevent the resulting image
653c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  being 'clipped'.
654c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
655c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  The 3x3 kernels (most of these) can be circularly rotated in multiples of
656c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  45 degrees to generate the 8 angled varients of each of the kernels.
6573c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
6583c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Laplacian:{type}
65943c4925e5305a26e48d68f7893e94f55d0831c39anthony%      Discrete Lapacian Kernels, (without normalization)
660c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
661c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
6629eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 2 :  3x3 with center:4 edge:1 corner:-2
6639eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 3 :  3x3 with center:4 edge:-2 corner:1
6649eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 5 :  5x5 laplacian
6659eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 7 :  7x7 laplacian
666501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 15 : 5x5 LoG (sigma approx 1.4)
667501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 19 : 9x9 LoG (sigma approx 1.4)
668c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
669c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Sobel:{angle}
67046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Sobel 'Edge' convolution kernel (3x3)
671c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
672c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -2, 0,-2 |
673c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
674c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
675c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Roberts:{angle}
67646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Roberts convolution kernel (3x3)
677c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
678c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 0 |
679c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
680c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
681c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Prewitt:{angle}
682c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%      Prewitt Edge convolution kernel (3x3)
683c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
684c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
685c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
686c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6879eb4f74649b23c053b308ce1152dce51239450baanthony%    Compass:{angle}
6889eb4f74649b23c053b308ce1152dce51239450baanthony%      Prewitt's "Compass" convolution kernel (3x3)
689c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
690c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1,-2, 1 |
691c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
692c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6939eb4f74649b23c053b308ce1152dce51239450baanthony%    Kirsch:{angle}
6949eb4f74649b23c053b308ce1152dce51239450baanthony%      Kirsch's "Compass" convolution kernel (3x3)
695c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
696c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3, 0, 5 |
697c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
6983c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
699c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{angle}
7001d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Frei-Chen Edge Detector is based on a kernel that is similar to
7011d5e67090dc7232b35bfcc71b31266c20838defcanthony%      the Sobel Kernel, but is designed to be isotropic. That is it takes
7021d5e67090dc7232b35bfcc71b31266c20838defcanthony%      into account the distance of the diagonal in the kernel.
703c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
704c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
705c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) |
706c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
707c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
708c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{type},{angle}
709c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
710c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      Frei-Chen Pre-weighted kernels...
711c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
712c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 0:  default un-nomalized version shown above.
713c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
714c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 1: Orthogonal Kernel (same as type 11 below)
715c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
716c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
717c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
718c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
719c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 2: Diagonal form of Kernel...
720c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     sqrt(2),    0     |
721c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
722c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   0,    -sqrt(2)    -1     |
723c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7241d5e67090dc7232b35bfcc71b31266c20838defcanthony%      However this kernel is als at the heart of the FreiChen Edge Detection
7251d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Process which uses a set of 9 specially weighted kernel.  These 9
7261d5e67090dc7232b35bfcc71b31266c20838defcanthony%      kernels not be normalized, but directly applied to the image. The
7271d5e67090dc7232b35bfcc71b31266c20838defcanthony%      results is then added together, to produce the intensity of an edge in
7281d5e67090dc7232b35bfcc71b31266c20838defcanthony%      a specific direction.  The square root of the pixel value can then be
7291d5e67090dc7232b35bfcc71b31266c20838defcanthony%      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
7301d5e67090dc7232b35bfcc71b31266c20838defcanthony%      from each other, both the direction and the strength of the edge can be
7311d5e67090dc7232b35bfcc71b31266c20838defcanthony%      determined.
7321d5e67090dc7232b35bfcc71b31266c20838defcanthony%
733c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 10: All 9 of the following pre-weighted kernels...
734c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
735c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 11: |   1,     0,   -1     |
736c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
737c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   1,     0,   -1     |
738e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
739c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 12: | 1, sqrt(2), 1 |
740c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0,   0,     0 | / 2*sqrt(2)
741c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, sqrt(2), 1 |
742e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
743c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 13: | sqrt(2), -1,    0     |
744c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  -1,      0,    1     | / 2*sqrt(2)
745c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   0,      1, -sqrt(2) |
746e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
747c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 14: |    0,     1, -sqrt(2) |
748c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   -1,     0,     1    | / 2*sqrt(2)
749c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), -1,     0    |
750e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
751c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 15: | 0, -1, 0 |
752c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1,  0, 1 | / 2
753c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0, -1, 0 |
754e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
755c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 16: |  1, 0, -1 |
756c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  0, 0,  0 | / 2
757c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, 0,  1 |
758e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
759c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 17: |  1, -2,  1 |
760c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2,  4, -2 | / 6
761c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, -2,  1 |
762501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
763c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 18: | -2, 1, -2 |
764c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  1, 4,  1 | / 6
765c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2, 1, -2 |
766e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
767c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 19: | 1, 1, 1 |
768c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 | / 3
769c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 |
770e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
771e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      The first 4 are for edge detection, the next 4 are for line detection
772e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      and the last is to add a average component to the results.
773e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
774c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      Using a special type of '-1' will return all 9 pre-weighted kernels
775c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      as a multi-kernel list, so that you can use them directly (without
776c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      normalization) with the special "-set option:morphology:compose Plus"
777c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      setting to apply the full FreiChen Edge Detection Technique.
778c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7791dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      If 'type' is large it will be taken to be an actual rotation angle for
7801dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
7811dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
7821dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%
783501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      WARNING: The above was layed out as per
784501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
785501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      But rotated 90 degrees so direction is from left rather than the top.
786501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      I have yet to find any secondary confirmation of the above. The only
787501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      other source found was actual source code at
788501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
789501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      Neigher paper defineds the kernels in a way that looks locical or
790501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      correct when taken as a whole.
791e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
792602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Boolean Kernels
793602ab9b30b644a78a4057da93d838a77391ec0acanthony%
7943c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Diamond:[{radius}[,{scale}]]
7951b2bc0a7da432e6e1cc0480280402df213faa940anthony%       Generate a diamond shaped kernel with given radius to the points.
796602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Kernel size will again be radius*2+1 square and defaults to radius 1,
797602ab9b30b644a78a4057da93d838a77391ec0acanthony%       generating a 3x3 kernel that is slightly larger than a square.
798602ab9b30b644a78a4057da93d838a77391ec0acanthony%
7993c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Square:[{radius}[,{scale}]]
800602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Generate a square shaped kernel of size radius*2+1, and defaulting
801602ab9b30b644a78a4057da93d838a77391ec0acanthony%       to a 3x3 (radius 1).
802602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8031ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagon:[{radius}[,{scale}]]
8041ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate octagonal shaped kernel of given radius and constant scale.
8050bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
8061ef941fea2534a0d20ba7d71307d35040247decbanthony%       in "Diamond" kernel.
8071ef941fea2534a0d20ba7d71307d35040247decbanthony%
8083c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Disk:[{radius}[,{scale}]]
8091ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate a binary disk, thresholded at the radius given, the radius
8101ef941fea2534a0d20ba7d71307d35040247decbanthony%       may be a float-point value. Final Kernel size is floor(radius)*2+1
8110bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       square. A radius of 5.3 is the default.
8121ef941fea2534a0d20ba7d71307d35040247decbanthony%
8131ef941fea2534a0d20ba7d71307d35040247decbanthony%       NOTE: That a low radii Disk kernels produce the same results as
8141ef941fea2534a0d20ba7d71307d35040247decbanthony%       many of the previously defined kernels, but differ greatly at larger
8151ef941fea2534a0d20ba7d71307d35040247decbanthony%       radii.  Here is a table of equivalences...
8161ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
8171ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1.5"  => "Square"
8181ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2"    => "Diamond:2"
8191ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.5"  => "Octagon"
8201ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.9"  => "Square:2"
8210bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%          "Disk:3.5"  => "Octagon:3"
8221ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:4.5"  => "Octagon:4"
8231ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:5.4"  => "Octagon:5"
8241ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:6.4"  => "Octagon:6"
8251ef941fea2534a0d20ba7d71307d35040247decbanthony%       All other Disk shapes are unique to this kernel, but because a "Disk"
8261ef941fea2534a0d20ba7d71307d35040247decbanthony%       is more circular when using a larger radius, using a larger radius is
8271ef941fea2534a0d20ba7d71307d35040247decbanthony%       preferred over iterating the morphological operation.
828602ab9b30b644a78a4057da93d838a77391ec0acanthony%
829a9892d898acb81e1ec73106d892855fdc5a69427anthony%    Rectangle:{geometry}
830a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Simply generate a rectangle of 1's with the size given. You can also
831a9892d898acb81e1ec73106d892855fdc5a69427anthony%       specify the location of the 'control point', otherwise the closest
832a9892d898acb81e1ec73106d892855fdc5a69427anthony%       pixel to the center of the rectangle is selected.
833a9892d898acb81e1ec73106d892855fdc5a69427anthony%
834a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Properly centered and odd sized rectangles work the best.
835a9892d898acb81e1ec73106d892855fdc5a69427anthony%
836c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  Symbol Dilation Kernels
837c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
838c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    These kernel is not a good general morphological kernel, but is used
839c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    more for highlighting and marking any single pixels in an image using,
840c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a "Dilate" method as appropriate.
841c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
842c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    For the same reasons iterating these kernels does not produce the
843c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    same result as using a larger radius for the symbol.
844c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
8453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Plus:[{radius}[,{scale}]]
8463dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Cross:[{radius}[,{scale}]]
847c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Generate a kernel in the shape of a 'plus' or a 'cross' with
848c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       a each arm the length of the given radius (default 2).
8493dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
850f0a92fd8deb68d411304359906b12679b675691fglennrp%       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
851602ab9b30b644a78a4057da93d838a77391ec0acanthony%
852c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Ring:{radius1},{radius2}[,{scale}]
853c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       A ring of the values given that falls between the two radii.
854c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
855c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       This is the 'edge' pixels of the default "Disk" kernel,
856c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
8573dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
8583dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%  Hit and Miss Kernels
859602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8603dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Peak:radius1,radius2
861c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Find any peak larger than the pixels the fall between the two radii.
862c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The default ring of pixels is as per "Ring".
86343c4925e5305a26e48d68f7893e94f55d0831c39anthony%    Edges
864694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find flat orthogonal edges of a binary shape
8653dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Corners
866694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find 90 degree corners of a binary shape
867529482f4b494010a13338a74446c510712f670b3anthony%    Diagonals:type
868529482f4b494010a13338a74446c510712f670b3anthony%       A special kernel to thin the 'outside' of diagonals
869694934fa79dd310f727588b1d0a7481fa6170f1danthony%    LineEnds:type
8703dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Find end points of lines (for pruning a skeletion)
871694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Two types of lines ends (default to both) can be searched for
872694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: All line ends
873694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: single kernel for 4-conneected line ends
874694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: single kernel for simple line ends
8753dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    LineJunctions
87643c4925e5305a26e48d68f7893e94f55d0831c39anthony%       Find three line junctions (within a skeletion)
877694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: all line junctions
878694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Y Junction kernel
879694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Diagonal T Junction kernel
880694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 3: Orthogonal T Junction kernel
881694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 4: Diagonal X Junction kernel
882694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 5: Orthogonal + Junction kernel
883694934fa79dd310f727588b1d0a7481fa6170f1danthony%    Ridges:type
884694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find single pixel ridges or thin lines
885694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Fine single pixel thick lines and ridges
886694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Find two pixel thick lines and ridges
8873dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    ConvexHull
888a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
889c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    Skeleton:type
890c40ac1e79923a1516075ba1197ae4ed90244af9banthony%       Traditional skeleton generating kernels.
891694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Tradional Skeleton kernel (4 connected skeleton)
892694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
893e816a586a13717bab2d6839ced6e5c3828a37f19anthony%         Type 3: Thinning skeleton based on a ressearch paper by
8942b2290b46c246ce1f14cb78f1695394e4c4a3ddfanthony%                 Dan S. Bloomberg (Default Type)
895e816a586a13717bab2d6839ced6e5c3828a37f19anthony%    ThinSE:type
896e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       A huge variety of Thinning Kernels designed to preserve conectivity.
897e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       many other kernel sets use these kernels as source definitions.
898e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       Type numbers are 41-49, 81-89, 481, and 482 which are based on
899e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       the super and sub notations used in the source research paper.
900602ab9b30b644a78a4057da93d838a77391ec0acanthony%
901602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Distance Measuring Kernels
902602ab9b30b644a78a4057da93d838a77391ec0acanthony%
903c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Different types of distance measuring methods, which are used with the
904c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a 'Distance' morphology method for generating a gradient based on
905c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    distance from an edge of a binary shape, though there is a technique
906c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    for handling a anti-aliased shape.
907602ab9b30b644a78a4057da93d838a77391ec0acanthony%
908c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    See the 'Distance' Morphological Method, for information of how it is
909c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    applied.
910602ab9b30b644a78a4057da93d838a77391ec0acanthony%
911c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Chebyshev:[{radius}][x{scale}[%!]]
9121ef941fea2534a0d20ba7d71307d35040247decbanthony%       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
9131ef941fea2534a0d20ba7d71307d35040247decbanthony%       is a value of one to any neighbour, orthogonal or diagonal. One why
9141ef941fea2534a0d20ba7d71307d35040247decbanthony%       of thinking of it is the number of squares a 'King' or 'Queen' in
9151ef941fea2534a0d20ba7d71307d35040247decbanthony%       chess needs to traverse reach any other position on a chess board.
9161ef941fea2534a0d20ba7d71307d35040247decbanthony%       It results in a 'square' like distance function, but one where
9171ef941fea2534a0d20ba7d71307d35040247decbanthony%       diagonals are given a value that is closer than expected.
918c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
919bee715c4c0fd9efe6e21d8627ae8664434df7750anthony%    Manhattan:[{radius}][x{scale}[%!]]
9201ef941fea2534a0d20ba7d71307d35040247decbanthony%       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
9211ef941fea2534a0d20ba7d71307d35040247decbanthony%       Cab distance metric), it is the distance needed when you can only
9221ef941fea2534a0d20ba7d71307d35040247decbanthony%       travel in horizontal or vertical directions only.  It is the
9231ef941fea2534a0d20ba7d71307d35040247decbanthony%       distance a 'Rook' in chess would have to travel, and results in a
9241ef941fea2534a0d20ba7d71307d35040247decbanthony%       diamond like distances, where diagonals are further than expected.
9251ef941fea2534a0d20ba7d71307d35040247decbanthony%
9261ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagonal:[{radius}][x{scale}[%!]]
9271ef941fea2534a0d20ba7d71307d35040247decbanthony%       An interleving of Manhatten and Chebyshev metrics producing an
9281ef941fea2534a0d20ba7d71307d35040247decbanthony%       increasing octagonally shaped distance.  Distances matches those of
9291ef941fea2534a0d20ba7d71307d35040247decbanthony%       the "Octagon" shaped kernel of the same radius.  The minimum radius
9301ef941fea2534a0d20ba7d71307d35040247decbanthony%       and default is 2, producing a 5x5 kernel.
931c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
932c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Euclidean:[{radius}][x{scale}[%!]]
9331ef941fea2534a0d20ba7d71307d35040247decbanthony%       Euclidean distance is the 'direct' or 'as the crow flys' distance.
934c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       However by default the kernel size only has a radius of 1, which
935c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       limits the distance to 'Knight' like moves, with only orthogonal and
936c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       diagonal measurements being correct.  As such for the default kernel
9371ef941fea2534a0d20ba7d71307d35040247decbanthony%       you will get octagonal like distance function.
9381ef941fea2534a0d20ba7d71307d35040247decbanthony%
9391ef941fea2534a0d20ba7d71307d35040247decbanthony%       However using a larger radius such as "Euclidean:4" you will get a
9401ef941fea2534a0d20ba7d71307d35040247decbanthony%       much smoother distance gradient from the edge of the shape. Especially
9411ef941fea2534a0d20ba7d71307d35040247decbanthony%       if the image is pre-processed to include any anti-aliasing pixels.
9421ef941fea2534a0d20ba7d71307d35040247decbanthony%       Of course a larger kernel is slower to use, and not always needed.
9431ef941fea2534a0d20ba7d71307d35040247decbanthony%
9441ef941fea2534a0d20ba7d71307d35040247decbanthony%    The first three Distance Measuring Kernels will only generate distances
9451ef941fea2534a0d20ba7d71307d35040247decbanthony%    of exact multiples of {scale} in binary images. As such you can use a
9461ef941fea2534a0d20ba7d71307d35040247decbanthony%    scale of 1 without loosing any information.  However you also need some
9471ef941fea2534a0d20ba7d71307d35040247decbanthony%    scaling when handling non-binary anti-aliased shapes.
9481ef941fea2534a0d20ba7d71307d35040247decbanthony%
9491ef941fea2534a0d20ba7d71307d35040247decbanthony%    The "Euclidean" Distance Kernel however does generate a non-integer
9501ef941fea2534a0d20ba7d71307d35040247decbanthony%    fractional results, and as such scaling is vital even for binary shapes.
951c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
952602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
953602ab9b30b644a78a4057da93d838a77391ec0acanthony
9542be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristyMagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
955602ab9b30b644a78a4057da93d838a77391ec0acanthony   const GeometryInfo *args)
956602ab9b30b644a78a4057da93d838a77391ec0acanthony{
9572be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
958602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
959602ab9b30b644a78a4057da93d838a77391ec0acanthony
960bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
961602ab9b30b644a78a4057da93d838a77391ec0acanthony    i;
962602ab9b30b644a78a4057da93d838a77391ec0acanthony
963bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
964602ab9b30b644a78a4057da93d838a77391ec0acanthony    u,
965602ab9b30b644a78a4057da93d838a77391ec0acanthony    v;
966602ab9b30b644a78a4057da93d838a77391ec0acanthony
967602ab9b30b644a78a4057da93d838a77391ec0acanthony  double
968602ab9b30b644a78a4057da93d838a77391ec0acanthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
969602ab9b30b644a78a4057da93d838a77391ec0acanthony
970c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  /* Generate a new empty kernel if needed */
971e96405a0f45f803fb9c26f75e7bdee252437febbcristy  kernel=(KernelInfo *) NULL;
972c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  switch(type) {
9731dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case UndefinedKernel:    /* These should not call this function */
9749eb4f74649b23c053b308ce1152dce51239450baanthony    case UserDefinedKernel:
975529482f4b494010a13338a74446c510712f670b3anthony      assert("Should not call this function" != (char *)NULL);
9769eb4f74649b23c053b308ce1152dce51239450baanthony      break;
977529482f4b494010a13338a74446c510712f670b3anthony    case LaplacianKernel:   /* Named Descrete Convolution Kernels */
978529482f4b494010a13338a74446c510712f670b3anthony    case SobelKernel:       /* these are defined using other kernels */
9799eb4f74649b23c053b308ce1152dce51239450baanthony    case RobertsKernel:
9809eb4f74649b23c053b308ce1152dce51239450baanthony    case PrewittKernel:
9819eb4f74649b23c053b308ce1152dce51239450baanthony    case CompassKernel:
9829eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
9831dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case FreiChenKernel:
984694934fa79dd310f727588b1d0a7481fa6170f1danthony    case EdgesKernel:       /* Hit and Miss kernels */
985694934fa79dd310f727588b1d0a7481fa6170f1danthony    case CornersKernel:
986529482f4b494010a13338a74446c510712f670b3anthony    case DiagonalsKernel:
9879eb4f74649b23c053b308ce1152dce51239450baanthony    case LineEndsKernel:
9889eb4f74649b23c053b308ce1152dce51239450baanthony    case LineJunctionsKernel:
9891dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case RidgesKernel:
9909eb4f74649b23c053b308ce1152dce51239450baanthony    case ConvexHullKernel:
9919eb4f74649b23c053b308ce1152dce51239450baanthony    case SkeletonKernel:
9929a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony    case ThinSEKernel:
993c40ac1e79923a1516075ba1197ae4ed90244af9banthony      break;               /* A pre-generated kernel is not needed */
994c40ac1e79923a1516075ba1197ae4ed90244af9banthony#if 0
995c40ac1e79923a1516075ba1197ae4ed90244af9banthony    /* set to 1 to do a compile-time check that we haven't missed anything */
996529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
997c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case GaussianKernel:
998501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
999501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
1000c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case BlurKernel:
1001c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CometKernel:
100240ca0b982379d4ab2716435a46603d56b5b218b1anthony    case BinomialKernel:
1003c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1004c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1005c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
10061ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
1007c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiskKernel:
1008c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PlusKernel:
1009c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CrossKernel:
1010c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
1011c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PeaksKernel:
1012c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case ChebyshevKernel:
1013bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
10141ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctangonalKernel:
1015c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case EuclideanKernel:
10161dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#else
10179eb4f74649b23c053b308ce1152dce51239450baanthony    default:
10181dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#endif
10199eb4f74649b23c053b308ce1152dce51239450baanthony      /* Generate the base Kernel Structure */
1020c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1021c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if (kernel == (KernelInfo *) NULL)
1022c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        return(kernel);
1023c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
102443c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1025c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->negative_range = kernel->positive_range = 0.0;
1026c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->type = type;
1027c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->next = (KernelInfo *) NULL;
1028c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->signature = MagickSignature;
1029c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
1030c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  }
1031602ab9b30b644a78a4057da93d838a77391ec0acanthony
1032602ab9b30b644a78a4057da93d838a77391ec0acanthony  switch(type) {
1033529482f4b494010a13338a74446c510712f670b3anthony    /*
1034529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels
1035529482f4b494010a13338a74446c510712f670b3anthony    */
1036529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
1037529482f4b494010a13338a74446c510712f670b3anthony      {
1038529482f4b494010a13338a74446c510712f670b3anthony        kernel->height = kernel->width = (size_t) 1;
1039529482f4b494010a13338a74446c510712f670b3anthony        kernel->x = kernel->y = (ssize_t) 0;
1040e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1041e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(1,sizeof(*kernel->values)));
1042d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
1043529482f4b494010a13338a74446c510712f670b3anthony          return(DestroyKernelInfo(kernel));
1044529482f4b494010a13338a74446c510712f670b3anthony        kernel->maximum = kernel->values[0] = args->rho;
1045529482f4b494010a13338a74446c510712f670b3anthony        break;
1046529482f4b494010a13338a74446c510712f670b3anthony      }
1047529482f4b494010a13338a74446c510712f670b3anthony      break;
1048602ab9b30b644a78a4057da93d838a77391ec0acanthony    case GaussianKernel:
1049501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
1050501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
1051602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1052c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1053c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma2 = fabs(args->xi),
10549eb4f74649b23c053b308ce1152dce51239450baanthony          A, B, R;
1055c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1056c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1057bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1058501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        else if ( (type != DoGKernel) || (sigma >= sigma2) )
1059c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1060c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1061c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1062c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->height = kernel->width;
1063bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1064e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1065e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1066e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1067d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
106883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1069602ab9b30b644a78a4057da93d838a77391ec0acanthony
107046a369d839971ab627bdb31a93d8bd63e81b65a3anthony        /* WARNING: The following generates a 'sampled gaussian' kernel.
10719eb4f74649b23c053b308ce1152dce51239450baanthony         * What we really want is a 'discrete gaussian' kernel.
107246a369d839971ab627bdb31a93d8bd63e81b65a3anthony         *
1073529482f4b494010a13338a74446c510712f670b3anthony         * How to do this is I don't know, but appears to be basied on the
1074529482f4b494010a13338a74446c510712f670b3anthony         * Error Function 'erf()' (intergral of a gaussian)
10759eb4f74649b23c053b308ce1152dce51239450baanthony         */
10769eb4f74649b23c053b308ce1152dce51239450baanthony
1077501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == GaussianKernel || type == DoGKernel )
1078501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { /* Calculate a Gaussian,  OR positive half of a DoG */
10799eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
10809eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
108155a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1082bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1083bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
10849eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
10859eb4f74649b23c053b308ce1152dce51239450baanthony              }
10869eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
10879eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
108875920b23a8a3883792cbb69d569c33cc789cf1b5cristy                  kernel->width*kernel->height*sizeof(*kernel->values));
10899eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
10909eb4f74649b23c053b308ce1152dce51239450baanthony              }
1091c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
10929eb4f74649b23c053b308ce1152dce51239450baanthony
1093501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == DoGKernel )
1094c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
1095c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            if ( sigma2 > MagickEpsilon )
1096c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              { sigma = sigma2;                /* simplify loop expressions */
10979eb4f74649b23c053b308ce1152dce51239450baanthony                A = 1.0/(2.0*sigma*sigma);
109855a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1099bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1100bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11019eb4f74649b23c053b308ce1152dce51239450baanthony                    kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1102c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              }
11039eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
1104c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1105c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
11069eb4f74649b23c053b308ce1152dce51239450baanthony
1107501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == LoGKernel )
11089eb4f74649b23c053b308ce1152dce51239450baanthony          { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
11099eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
11109eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
111155a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1112bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1113bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11149eb4f74649b23c053b308ce1152dce51239450baanthony                    { R = ((double)(u*u+v*v))*A;
11159eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = (1-R)*exp(-R)*B;
11169eb4f74649b23c053b308ce1152dce51239450baanthony                    }
11179eb4f74649b23c053b308ce1152dce51239450baanthony              }
11189eb4f74649b23c053b308ce1152dce51239450baanthony            else /* special case - generate a unity kernel */
11199eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
112075920b23a8a3883792cbb69d569c33cc789cf1b5cristy                  kernel->width*kernel->height*sizeof(*kernel->values));
11219eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
11229eb4f74649b23c053b308ce1152dce51239450baanthony              }
11239eb4f74649b23c053b308ce1152dce51239450baanthony          }
11249eb4f74649b23c053b308ce1152dce51239450baanthony
11259eb4f74649b23c053b308ce1152dce51239450baanthony        /* Note the above kernels may have been 'clipped' by a user defined
1126c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
1127c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** sigma's (> 0.1) the central value becomes larger than one, and thus
1128c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** producing a very bright kernel.
1129c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1130c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** Normalization will still be needed.
1131c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        */
1132602ab9b30b644a78a4057da93d838a77391ec0acanthony
11333dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* Normalize the 2D Gaussian Kernel
11343dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        **
1135c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1136c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
11373dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        */
113846a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1139c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1140602ab9b30b644a78a4057da93d838a77391ec0acanthony
1141602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1142602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1143602ab9b30b644a78a4057da93d838a77391ec0acanthony    case BlurKernel:
1144602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1145c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1146501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          alpha, beta;
1147602ab9b30b644a78a4057da93d838a77391ec0acanthony
1148c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1149bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1150c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1151501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1152602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1153bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = (ssize_t) (kernel->width-1)/2;
1154c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->y = 0;
1155c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
1156e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1157e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1158e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1159d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
116083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1161602ab9b30b644a78a4057da93d838a77391ec0acanthony
1162602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1163602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1164602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
1165602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** It generates a gaussian 3 times the width, and compresses it into
1166602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** the expected range.  This produces a closer normalization of the
1167602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** resulting kernel, especially for very low sigma values.
1168602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** As such while wierd it is prefered.
1169602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1170602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** I am told this method originally came from Photoshop.
11719eb4f74649b23c053b308ce1152dce51239450baanthony        **
11729eb4f74649b23c053b308ce1152dce51239450baanthony        ** A properly normalized curve is generated (apart from edge clipping)
11739eb4f74649b23c053b308ce1152dce51239450baanthony        ** even though we later normalize the result (for edge clipping)
11749eb4f74649b23c053b308ce1152dce51239450baanthony        ** to allow the correct generation of a "Difference of Blurs".
1175602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1176c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1177c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* initialize */
1178bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
11799eb4f74649b23c053b308ce1152dce51239450baanthony        (void) ResetMagickMemory(kernel->values,0, (size_t)
118075920b23a8a3883792cbb69d569c33cc789cf1b5cristy          kernel->width*kernel->height*sizeof(*kernel->values));
1181c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive 1D Gaussian */
1182c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1183c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { sigma *= KernelRank;               /* simplify loop expressions */
1184501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            alpha = 1.0/(2.0*sigma*sigma);
118555a91cddcdea3aa002893186a773e1704884a9dfcristy            beta= (double) (1.0/(MagickSQ2PI*sigma ));
1186c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            for ( u=-v; u <= v; u++) {
1187501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[(u+v)/KernelRank] +=
1188501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                              exp(-((double)(u*u))*alpha)*beta;
1189c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            }
1190c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1191c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1192c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1193602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
119453f576d128f0bb744a82e7f9c0d8f05b2923972canthony        /* Direct calculation without curve averaging
119553f576d128f0bb744a82e7f9c0d8f05b2923972canthony           This is equivelent to a KernelRank of 1 */
1196c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1197c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive Gaussian */
1198c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1199501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
1200501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            beta = 1.0/(MagickSQ2PI*sigma);
1201bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1202501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1203c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1204c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1205c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
120675920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*kernel->height*sizeof(*kernel->values));
1207c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1208c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1209602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
1210c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Note the above kernel may have been 'clipped' by a user defined
1211cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
121253f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** sigma's (> 0.1) the central value becomes larger than one, as a
121353f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** result of not generating a actual 'discrete' kernel, and thus
121453f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** producing a very bright 'impulse'.
1215c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
121653f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** Becuase of these two factors Normalization is required!
1217602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1218cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1219602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Normalize the 1D Gaussian Kernel
1220602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1221c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1222c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
1223602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
122446a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
122546a369d839971ab627bdb31a93d8bd63e81b65a3anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1226cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1227c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* rotate the 1D kernel by given angle */
1228501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        RotateKernelInfo(kernel, args->xi );
1229602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1230602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1231602ab9b30b644a78a4057da93d838a77391ec0acanthony    case CometKernel:
1232602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
12339eb4f74649b23c053b308ce1152dce51239450baanthony          sigma = fabs(args->sigma),
12349eb4f74649b23c053b308ce1152dce51239450baanthony          A;
1235602ab9b30b644a78a4057da93d838a77391ec0acanthony
1236602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( args->rho < 1.0 )
1237e1cf9465864144e8b8043d522906c1e47bbf6192anthony          kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1238602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
1239bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho;
1240c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->x = kernel->y = 0;
1241602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1242c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
1243e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1244e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1245e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1246d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
124783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1248602ab9b30b644a78a4057da93d838a77391ec0acanthony
1249c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* A comet blur is half a 1D gaussian curve, so that the object is
1250602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** blurred in one direction only.  This may not be quite the right
12513dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** curve to use so may change in the future. The function must be
12523dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** normalised after generation, which also resolves any clipping.
1253c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1254c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** As we are normalizing and not subtracting gaussians,
1255c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there is no need for a divisor in the gaussian formula
1256c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
125743c4925e5305a26e48d68f7893e94f55d0831c39anthony        ** It is less comples
1258602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
12599eb4f74649b23c053b308ce1152dce51239450baanthony        if ( sigma > MagickEpsilon )
12609eb4f74649b23c053b308ce1152dce51239450baanthony          {
1261602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1262602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1263bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            v = (ssize_t) kernel->width*KernelRank; /* start/end points */
12649eb4f74649b23c053b308ce1152dce51239450baanthony            (void) ResetMagickMemory(kernel->values,0, (size_t)
126575920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*sizeof(*kernel->values));
12669eb4f74649b23c053b308ce1152dce51239450baanthony            sigma *= KernelRank;            /* simplify the loop expression */
12679eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);
12689eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
12699eb4f74649b23c053b308ce1152dce51239450baanthony            for ( u=0; u < v; u++) {
12709eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->values[u/KernelRank] +=
12719eb4f74649b23c053b308ce1152dce51239450baanthony                  exp(-((double)(u*u))*A);
12729eb4f74649b23c053b308ce1152dce51239450baanthony              /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
12739eb4f74649b23c053b308ce1152dce51239450baanthony            }
1274bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (i=0; i < (ssize_t) kernel->width; i++)
12759eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range += kernel->values[i];
1276602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
12779eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
12789eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
1279bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0; i < (ssize_t) kernel->width; i++)
12809eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range +=
128153f576d128f0bb744a82e7f9c0d8f05b2923972canthony                kernel->values[i] = exp(-((double)(i*i))*A);
12829eb4f74649b23c053b308ce1152dce51239450baanthony                /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1283602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
12849eb4f74649b23c053b308ce1152dce51239450baanthony          }
12859eb4f74649b23c053b308ce1152dce51239450baanthony        else /* special case - generate a unity kernel */
12869eb4f74649b23c053b308ce1152dce51239450baanthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
128775920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*kernel->height*sizeof(*kernel->values));
12889eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
12899eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->positive_range = 1.0;
12909eb4f74649b23c053b308ce1152dce51239450baanthony          }
129146a369d839971ab627bdb31a93d8bd63e81b65a3anthony
129246a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->minimum = 0.0;
1293c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
129446a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->negative_range = 0.0;
1295602ab9b30b644a78a4057da93d838a77391ec0acanthony
1296999bb2c20aa9d42875bb5adba44951988d4ae354anthony        ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
1297999bb2c20aa9d42875bb5adba44951988d4ae354anthony        RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
1298602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1299602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
130040ca0b982379d4ab2716435a46603d56b5b218b1anthony    case BinomialKernel:
130140ca0b982379d4ab2716435a46603d56b5b218b1anthony      {
130240ca0b982379d4ab2716435a46603d56b5b218b1anthony        size_t
130340ca0b982379d4ab2716435a46603d56b5b218b1anthony          order_f;
130440ca0b982379d4ab2716435a46603d56b5b218b1anthony
130540ca0b982379d4ab2716435a46603d56b5b218b1anthony        if (args->rho < 1.0)
130640ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
130740ca0b982379d4ab2716435a46603d56b5b218b1anthony        else
130840ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
130940ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
131040ca0b982379d4ab2716435a46603d56b5b218b1anthony
131140ca0b982379d4ab2716435a46603d56b5b218b1anthony        order_f = fact(kernel->width-1);
131240ca0b982379d4ab2716435a46603d56b5b218b1anthony
1313e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1314e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1315e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1316d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
131740ca0b982379d4ab2716435a46603d56b5b218b1anthony          return(DestroyKernelInfo(kernel));
131840ca0b982379d4ab2716435a46603d56b5b218b1anthony
131940ca0b982379d4ab2716435a46603d56b5b218b1anthony        /* set all kernel values within diamond area to scale given */
132040ca0b982379d4ab2716435a46603d56b5b218b1anthony        for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
132140ca0b982379d4ab2716435a46603d56b5b218b1anthony          { size_t
1322f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy              alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
132340ca0b982379d4ab2716435a46603d56b5b218b1anthony            for ( u=0; u < (ssize_t)kernel->width; u++, i++)
132440ca0b982379d4ab2716435a46603d56b5b218b1anthony              kernel->positive_range += kernel->values[i] = (double)
1325f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy                (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
132640ca0b982379d4ab2716435a46603d56b5b218b1anthony          }
132740ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->minimum = 1.0;
132840ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
132940ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->negative_range = 0.0;
133040ca0b982379d4ab2716435a46603d56b5b218b1anthony        break;
133140ca0b982379d4ab2716435a46603d56b5b218b1anthony      }
1332c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1333529482f4b494010a13338a74446c510712f670b3anthony    /*
1334529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels - Well Known Named Constant Kernels
1335529482f4b494010a13338a74446c510712f670b3anthony    */
13363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case LaplacianKernel:
1337e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      { switch ( (int) args->rho ) {
13383dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony          case 0:
13399eb4f74649b23c053b308ce1152dce51239450baanthony          default: /* laplacian square filter -- default */
1340c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
13413dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            break;
13429eb4f74649b23c053b308ce1152dce51239450baanthony          case 1:  /* laplacian diamond filter */
1343c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
13443c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          case 2:
13469eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
13479eb4f74649b23c053b308ce1152dce51239450baanthony            break;
13489eb4f74649b23c053b308ce1152dce51239450baanthony          case 3:
1349c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
13503c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13519eb4f74649b23c053b308ce1152dce51239450baanthony          case 5:   /* a 5x5 laplacian */
13523c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
13539eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13543c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13559eb4f74649b23c053b308ce1152dce51239450baanthony          case 7:   /* a 7x7 laplacian */
13563c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
1357c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              "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" );
13583c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
1359501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 15:  /* a 5x5 LoG (sigma approx 1.4) */
13609eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray(
13619eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13629eb4f74649b23c053b308ce1152dce51239450baanthony            break;
1363501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 19:  /* a 9x9 LoG (sigma approx 1.4) */
136443c4925e5305a26e48d68f7893e94f55d0831c39anthony            /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
136543c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel=ParseKernelArray(
1366bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony              "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");
136743c4925e5305a26e48d68f7893e94f55d0831c39anthony            break;
13683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
13693c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        if (kernel == (KernelInfo *) NULL)
13703c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          return(kernel);
13713c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        kernel->type = type;
13723c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        break;
13733c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      }
1374c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SobelKernel:
1375cceb6f05c2016ea3854b29e7770ec5ff49034ecfanthony      { /* Simple Sobel Kernel */
1376dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1377dcc2a474c98415e565434fe52f630acba36fa0c1anthony        if (kernel == (KernelInfo *) NULL)
1378dcc2a474c98415e565434fe52f630acba36fa0c1anthony          return(kernel);
1379dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel->type = type;
1380dcc2a474c98415e565434fe52f630acba36fa0c1anthony        RotateKernelInfo(kernel, args->rho);
1381dcc2a474c98415e565434fe52f630acba36fa0c1anthony        break;
1382dcc2a474c98415e565434fe52f630acba36fa0c1anthony      }
1383c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RobertsKernel:
1384c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1385501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
1386c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1387c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1388c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
138946a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1390c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1391c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1392c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PrewittKernel:
1393c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1394501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
1395c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1396c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1397c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
139846a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1399c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1400c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1401c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CompassKernel:
1402c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1403501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
1404c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1405c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1406c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
140746a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1408c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1409c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
14109eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
14119eb4f74649b23c053b308ce1152dce51239450baanthony      {
1412501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
14139eb4f74649b23c053b308ce1152dce51239450baanthony        if (kernel == (KernelInfo *) NULL)
14149eb4f74649b23c053b308ce1152dce51239450baanthony          return(kernel);
14159eb4f74649b23c053b308ce1152dce51239450baanthony        kernel->type = type;
141646a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
14179eb4f74649b23c053b308ce1152dce51239450baanthony        break;
14189eb4f74649b23c053b308ce1152dce51239450baanthony      }
1419e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony    case FreiChenKernel:
1420501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* Direction is set to be left to right positive */
1421501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
1422501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
14231dd091ae3bc17edc26c16cc47f436a24bd48412aanthony      { switch ( (int) args->rho ) {
1424e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          default:
1425c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 0:
1426501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1427c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            if (kernel == (KernelInfo *) NULL)
1428c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1429ef33d9f66f955c1f6f5f7105e164cc2d5c5e2a41anthony            kernel->type = type;
1430d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[3] = +(MagickRealType) MagickSQ2;
1431d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = -(MagickRealType) MagickSQ2;
1432c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1433c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            break;
1434c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 2:
1435c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
1436c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1437c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1438c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->type = type;
1439d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
1440d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
1441c40ac1e79923a1516075ba1197ae4ed90244af9banthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1442d4e3ffa7f64077da0f32c2d8a599737ee8d115ddcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1443c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1444c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 10:
1445c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
1446c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1447c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1448c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1449e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          case 1:
1450c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 11:
1451501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1452e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1453e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1454c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1455d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[3] = +(MagickRealType) MagickSQ2;
1456d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = -(MagickRealType) MagickSQ2;
1457e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
145855a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1459e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1460c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 12:
1461501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
1462e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1463e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1464c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1465d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel->values[1] = +(MagickRealType) MagickSQ2;
1466d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel->values[7] = +(MagickRealType) MagickSQ2;
1467e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
146855a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1469e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1470c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 13:
1471501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
1472e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1473e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1474c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1475d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[0] = +(MagickRealType) MagickSQ2;
1476d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[8] = -(MagickRealType) MagickSQ2;
1477e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
147855a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1479e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1480c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 14:
14811d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
1482e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1483e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1484c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1485d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[2] = -(MagickRealType) MagickSQ2;
1486d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[6] = +(MagickRealType) MagickSQ2;
1487e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
148855a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1489e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1490c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 15:
1491501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
1492e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1493e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1494c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1495e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1496e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1497c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 16:
14981d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
1499e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1500e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1501c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1502e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1503e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1504c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 17:
1505501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
1506e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1507e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1508c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1509e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1510e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1511c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 18:
1512501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1513e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1514e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1515c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1516e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1517e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1518c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 19:
1519c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
1520e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1521e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1522c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1523e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1524e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1525e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        }
1526b978e458a8e1f210bcb580951cf623687236b2fecristy        if ( fabs(args->sigma) >= MagickEpsilon )
1527c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by correctly supplied 'angle' */
1528c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->sigma);
1529c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony        else if ( args->rho > 30.0 || args->rho < -30.0 )
1530c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by out of bounds 'type' */
1531c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->rho);
1532e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        break;
1533e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      }
1534e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony
1535529482f4b494010a13338a74446c510712f670b3anthony    /*
1536529482f4b494010a13338a74446c510712f670b3anthony      Boolean or Shaped Kernels
1537529482f4b494010a13338a74446c510712f670b3anthony    */
1538c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1539602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
1540c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (args->rho < 1.0)
1541c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
1542c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1543bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1544bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1545c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1546e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1547e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1548e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1549d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
1550c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(DestroyKernelInfo(kernel));
1551c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1552c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* set all kernel values within diamond area to scale given */
1553bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1554bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
15551d5e67090dc7232b35bfcc71b31266c20838defcanthony            if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1556c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->positive_range += kernel->values[i] = args->sigma;
1557c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            else
1558c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[i] = nan;
1559c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1560c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1561c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1562c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1563c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
1564c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      { double
1565c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          scale;
1566602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( type == SquareKernel )
1567602ab9b30b644a78a4057da93d838a77391ec0acanthony          {
1568602ab9b30b644a78a4057da93d838a77391ec0acanthony            if (args->rho < 1.0)
1569c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony              kernel->width = kernel->height = 3;  /* default radius = 1 */
1570602ab9b30b644a78a4057da93d838a77391ec0acanthony            else
1571bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              kernel->width = kernel->height = (size_t) (2*args->rho+1);
1572bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
15734fd27e21043be809d66c8202e779255e5b660d2danthony            scale = args->sigma;
1574602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1575602ab9b30b644a78a4057da93d838a77391ec0acanthony        else {
15762be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy            /* NOTE: user defaults set in "AcquireKernelInfo()" */
1577602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->rho < 1.0 || args->sigma < 1.0 )
157883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1579bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->width = (size_t)args->rho;
1580bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->height = (size_t)args->sigma;
1581602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
1582602ab9b30b644a78a4057da93d838a77391ec0acanthony                 args->psi < 0.0 || args->psi > (double)kernel->height )
158383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1584bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = (ssize_t) args->xi;
1585bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->y = (ssize_t) args->psi;
15864fd27e21043be809d66c8202e779255e5b660d2danthony            scale = 1.0;
1587602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1588e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1589e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1590e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1591d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
159283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1593602ab9b30b644a78a4057da93d838a77391ec0acanthony
15943dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* set all kernel values to scale given */
1595eaedf06777741da32408da72c1e512975c600c48cristy        u=(ssize_t) (kernel->width*kernel->height);
1596150989ed67ef9da53141a65e5f3ebdb05dd025abcristy        for ( i=0; i < u; i++)
15974fd27e21043be809d66c8202e779255e5b660d2danthony            kernel->values[i] = scale;
15984fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->minimum = kernel->maximum = scale;   /* a flat shape */
15994fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->positive_range = scale*u;
1600cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        break;
1601602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
16021ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonKernel:
16031ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16041ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
1605a9892d898acb81e1ec73106d892855fdc5a69427anthony            kernel->width = kernel->height = 5;  /* default radius = 2 */
16061ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16071ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16081ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16091ef941fea2534a0d20ba7d71307d35040247decbanthony
1610e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1611e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1612e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1613d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16141ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16151ef941fea2534a0d20ba7d71307d35040247decbanthony
16161ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16171ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16181ef941fea2534a0d20ba7d71307d35040247decbanthony              if ( (labs((long) u)+labs((long) v)) <=
16191ef941fea2534a0d20ba7d71307d35040247decbanthony                        ((long)kernel->x + (long)(kernel->x/2)) )
16201ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16211ef941fea2534a0d20ba7d71307d35040247decbanthony              else
16221ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->values[i] = nan;
1623a9892d898acb81e1ec73106d892855fdc5a69427anthony          kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
16241ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16251ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16261ef941fea2534a0d20ba7d71307d35040247decbanthony      case DiskKernel:
16271ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16281ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
16290bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony            limit = (ssize_t)(args->rho*args->rho);
16301ef941fea2534a0d20ba7d71307d35040247decbanthony
16319bf68d590429a72aa70894f6aea7fc3c94876e54anthony          if (args->rho < 0.4)           /* default radius approx 4.3 */
16329bf68d590429a72aa70894f6aea7fc3c94876e54anthony            kernel->width = kernel->height = 9L, limit = 18L;
16331ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16341ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
16351ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16361ef941fea2534a0d20ba7d71307d35040247decbanthony
1637e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1638e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1639e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1640d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16411ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16421ef941fea2534a0d20ba7d71307d35040247decbanthony
16431ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16441ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16451ef941fea2534a0d20ba7d71307d35040247decbanthony              if ((u*u+v*v) <= limit)
16461ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16473dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony              else
16483dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony                kernel->values[i] = nan;
16491ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16501ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16511ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16521ef941fea2534a0d20ba7d71307d35040247decbanthony      case PlusKernel:
16531ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16541ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16551ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16561ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16571ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16581ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16591ef941fea2534a0d20ba7d71307d35040247decbanthony
1660e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1661e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1662e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1663d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16641ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16651ef941fea2534a0d20ba7d71307d35040247decbanthony
16661ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16671ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16681ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16691ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
16701ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16711ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16721ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16731ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16741ef941fea2534a0d20ba7d71307d35040247decbanthony      case CrossKernel:
16751ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16761ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16771ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16781ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16791ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16801ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16811ef941fea2534a0d20ba7d71307d35040247decbanthony
1682e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1683e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1684e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1685d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16861ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16871ef941fea2534a0d20ba7d71307d35040247decbanthony
16881ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16891ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16901ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16911ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
16921ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16931ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16941ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16951ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1696529482f4b494010a13338a74446c510712f670b3anthony      /*
1697529482f4b494010a13338a74446c510712f670b3anthony        HitAndMiss Kernels
1698529482f4b494010a13338a74446c510712f670b3anthony      */
16991ef941fea2534a0d20ba7d71307d35040247decbanthony      case RingKernel:
17001ef941fea2534a0d20ba7d71307d35040247decbanthony      case PeaksKernel:
17011ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17021ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
17031ef941fea2534a0d20ba7d71307d35040247decbanthony            limit1,
17041ef941fea2534a0d20ba7d71307d35040247decbanthony            limit2,
17051ef941fea2534a0d20ba7d71307d35040247decbanthony            scale;
17061ef941fea2534a0d20ba7d71307d35040247decbanthony
17071ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < args->sigma)
17081ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17091ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->sigma)*2+1;
17101ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->rho*args->rho);
17111ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->sigma*args->sigma);
17123dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            }
17131ef941fea2534a0d20ba7d71307d35040247decbanthony          else
17141ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17151ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->rho)*2+1;
17161ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->sigma*args->sigma);
17171ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->rho*args->rho);
17181ef941fea2534a0d20ba7d71307d35040247decbanthony            }
17191ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( limit2 <= 0 )
17201ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = 7L, limit1 = 7L, limit2 = 11L;
17211ef941fea2534a0d20ba7d71307d35040247decbanthony
17221ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->height = kernel->width;
17231ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1724e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1725e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1726e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1727d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
17281ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
17291ef941fea2534a0d20ba7d71307d35040247decbanthony
17301ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
17311ef941fea2534a0d20ba7d71307d35040247decbanthony          scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
17321ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
17331ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
17341ef941fea2534a0d20ba7d71307d35040247decbanthony              { ssize_t radius=u*u+v*v;
17351ef941fea2534a0d20ba7d71307d35040247decbanthony                if (limit1 < radius && radius <= limit2)
17361ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->positive_range += kernel->values[i] = (double) scale;
17371ef941fea2534a0d20ba7d71307d35040247decbanthony                else
17381ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->values[i] = nan;
17391ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17401ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = (double) scale;
17411ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( type == PeaksKernel ) {
17421ef941fea2534a0d20ba7d71307d35040247decbanthony            /* set the central point in the middle */
17431ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
17441ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->positive_range = 1.0;
17451ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->maximum = 1.0;
17461ef941fea2534a0d20ba7d71307d35040247decbanthony          }
17471ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1748c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        }
17491ef941fea2534a0d20ba7d71307d35040247decbanthony      case EdgesKernel:
17501ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1751529482f4b494010a13338a74446c510712f670b3anthony          kernel=AcquireKernelInfo("ThinSE:482");
17521ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17531ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17541ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
1755529482f4b494010a13338a74446c510712f670b3anthony          ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
17561ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17571ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17581ef941fea2534a0d20ba7d71307d35040247decbanthony      case CornersKernel:
17591ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1760529482f4b494010a13338a74446c510712f670b3anthony          kernel=AcquireKernelInfo("ThinSE:87");
17611ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17621ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17631ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
17641ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
17651ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17661ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1767529482f4b494010a13338a74446c510712f670b3anthony      case DiagonalsKernel:
17681ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17691ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
17701ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
17711ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17721ef941fea2534a0d20ba7d71307d35040247decbanthony              { KernelInfo
17731ef941fea2534a0d20ba7d71307d35040247decbanthony                  *new_kernel;
1774529482f4b494010a13338a74446c510712f670b3anthony                kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17751ef941fea2534a0d20ba7d71307d35040247decbanthony                if (kernel == (KernelInfo *) NULL)
17761ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(kernel);
17771ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->type = type;
1778529482f4b494010a13338a74446c510712f670b3anthony                new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17791ef941fea2534a0d20ba7d71307d35040247decbanthony                if (new_kernel == (KernelInfo *) NULL)
17801ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(DestroyKernelInfo(kernel));
17811ef941fea2534a0d20ba7d71307d35040247decbanthony                new_kernel->type = type;
17821ef941fea2534a0d20ba7d71307d35040247decbanthony                LastKernelInfo(kernel)->next = new_kernel;
17831ef941fea2534a0d20ba7d71307d35040247decbanthony                ExpandMirrorKernelInfo(kernel);
1784529482f4b494010a13338a74446c510712f670b3anthony                return(kernel);
17851ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17861ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
1787529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17881ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17891ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
1790529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17911ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17921ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1793529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1794529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1795529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1796529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
17971ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17981ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17991ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineEndsKernel:
18001ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Kernels for finding the end of thin lines */
18011ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18021ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
18031ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18041ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all end of lines */
1805529482f4b494010a13338a74446c510712f670b3anthony              return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
18061ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18071ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel for 4-connected line ends - no rotation */
18081ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
18091ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18101ef941fea2534a0d20ba7d71307d35040247decbanthony          case 2:
18111ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for 8-connected lines - no rotation */
18121ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
18131ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18141ef941fea2534a0d20ba7d71307d35040247decbanthony          case 3:
18151ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for orthogonal line ends - does not find corners */
18161ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
18171ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18181ef941fea2534a0d20ba7d71307d35040247decbanthony          case 4:
18191ef941fea2534a0d20ba7d71307d35040247decbanthony              /* traditional line end - fails on last T end */
18201ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
18211ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18221ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1823529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1824529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1825529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1826529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18271ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18281ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18291ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineJunctionsKernel:
18301ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* kernels for finding the junctions of multiple lines */
18311ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18321ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
18331ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18341ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all line junctions */
1835529482f4b494010a13338a74446c510712f670b3anthony              return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
18361ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18371ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Y Junction */
18381ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
18391ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18401ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18411ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal T Junctions */
18421ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
18431ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18441ef941fea2534a0d20ba7d71307d35040247decbanthony            case 3:
18451ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal T Junctions */
18461ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
18471ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18481ef941fea2534a0d20ba7d71307d35040247decbanthony            case 4:
18491ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal X Junctions */
18501ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
18511ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18521ef941fea2534a0d20ba7d71307d35040247decbanthony            case 5:
18531ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal X Junctions - minimal diamond kernel */
18541ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
18551ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18561ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1857529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1858529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1859529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1860529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18611ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18621ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18631ef941fea2534a0d20ba7d71307d35040247decbanthony      case RidgesKernel:
18641ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Ridges - Ridge finding kernels */
18651ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
18661ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
18671ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18681ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18691ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18701ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3x1:0,1,0");
18711ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18721ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18731ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18741ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
18751ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18761ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18771ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("4x1:0,1,1,0");
18781ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18791ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18801ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18811ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
18821ef941fea2534a0d20ba7d71307d35040247decbanthony
18831ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
18841ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Unfortunatally we can not yet rotate a non-square kernel */
18851ef941fea2534a0d20ba7d71307d35040247decbanthony              /* But then we can't flip a non-symetrical kernel either */
18861ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
18871ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18881ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18891ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18901ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18911ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
18921ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18931ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18941ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18951ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18961ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
18971ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18981ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18991ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19001ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19011ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
19021ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19031ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19041ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19051ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19061ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
19071ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19081ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19091ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19101ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19111ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
19121ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19131ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19141ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19151ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19161ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
19171ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19181ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19191ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19201ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19211ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
192268cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              if (new_kernel == (KernelInfo *) NULL)
192368cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony                return(DestroyKernelInfo(kernel));
192468cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              new_kernel->type = type;
192568cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              LastKernelInfo(kernel)->next = new_kernel;
192668cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              break;
19271ef941fea2534a0d20ba7d71307d35040247decbanthony          }
19281ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
192968cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony        }
19301ef941fea2534a0d20ba7d71307d35040247decbanthony      case ConvexHullKernel:
19311ef941fea2534a0d20ba7d71307d35040247decbanthony        {
19321ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
19331ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
19341ef941fea2534a0d20ba7d71307d35040247decbanthony          /* first set of 8 kernels */
19351ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
19361ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
19371ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
19381ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
19391ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0);
19401ef941fea2534a0d20ba7d71307d35040247decbanthony          /* append the mirror versions too - no flip function yet */
19411ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
19421ef941fea2534a0d20ba7d71307d35040247decbanthony          if (new_kernel == (KernelInfo *) NULL)
19431ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
19441ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel->type = type;
19451ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(new_kernel, 90.0);
19461ef941fea2534a0d20ba7d71307d35040247decbanthony          LastKernelInfo(kernel)->next = new_kernel;
19471ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1948694934fa79dd310f727588b1d0a7481fa6170f1danthony        }
19499a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony      case SkeletonKernel:
19509a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        {
19519a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          switch ( (int) args->rho ) {
19529a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 1:
19539a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            default:
19549a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Traditional Skeleton...
19559a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** A cyclically rotated single kernel
19569a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19579a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482");
19589a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19599a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19609a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19619a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
19629a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19639a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 2:
19649a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* HIPR Variation of the cyclic skeleton
19659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** Corners of the traditional method made more forgiving,
19669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** but the retain the same cyclic order.
19679a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
19699a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19709a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19719a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel->next == (KernelInfo *) NULL)
19729a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(DestroyKernelInfo(kernel));
19739a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19749a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19759a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
19769a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19779a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 3:
19789a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
19799a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** "Connectivity-Preserving Morphological Image Thransformations"
19809a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
19819a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              **   http://www.leptonica.com/papers/conn.pdf
19829a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19839a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo(
19849a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                            "ThinSE:41; ThinSE:42; ThinSE:43");
19859a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19869a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19879a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19889a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19899a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->next->type = type;
19909a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
19919a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19929a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony           }
19939a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          break;
19949a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        }
1995529482f4b494010a13338a74446c510712f670b3anthony      case ThinSEKernel:
1996529482f4b494010a13338a74446c510712f670b3anthony        { /* Special kernels for general thinning, while preserving connections
1997529482f4b494010a13338a74446c510712f670b3anthony          ** "Connectivity-Preserving Morphological Image Thransformations"
1998529482f4b494010a13338a74446c510712f670b3anthony          ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
1999529482f4b494010a13338a74446c510712f670b3anthony          **   http://www.leptonica.com/papers/conn.pdf
2000529482f4b494010a13338a74446c510712f670b3anthony          ** And
2001529482f4b494010a13338a74446c510712f670b3anthony          **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
2002529482f4b494010a13338a74446c510712f670b3anthony          **
2003529482f4b494010a13338a74446c510712f670b3anthony          ** Note kernels do not specify the origin pixel, allowing them
2004529482f4b494010a13338a74446c510712f670b3anthony          ** to be used for both thickening and thinning operations.
2005529482f4b494010a13338a74446c510712f670b3anthony          */
2006529482f4b494010a13338a74446c510712f670b3anthony          switch ( (int) args->rho ) {
2007529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 4-connected thinning */
2008529482f4b494010a13338a74446c510712f670b3anthony            case 41: /* SE_4_1 */
2009529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
2010529482f4b494010a13338a74446c510712f670b3anthony              break;
2011529482f4b494010a13338a74446c510712f670b3anthony            case 42: /* SE_4_2 */
2012529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
2013529482f4b494010a13338a74446c510712f670b3anthony              break;
2014529482f4b494010a13338a74446c510712f670b3anthony            case 43: /* SE_4_3 */
2015529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
2016529482f4b494010a13338a74446c510712f670b3anthony              break;
2017529482f4b494010a13338a74446c510712f670b3anthony            case 44: /* SE_4_4 */
2018529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
2019529482f4b494010a13338a74446c510712f670b3anthony              break;
2020529482f4b494010a13338a74446c510712f670b3anthony            case 45: /* SE_4_5 */
2021529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
2022529482f4b494010a13338a74446c510712f670b3anthony              break;
2023529482f4b494010a13338a74446c510712f670b3anthony            case 46: /* SE_4_6 */
2024529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
2025529482f4b494010a13338a74446c510712f670b3anthony              break;
2026529482f4b494010a13338a74446c510712f670b3anthony            case 47: /* SE_4_7 */
2027529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
2028529482f4b494010a13338a74446c510712f670b3anthony              break;
2029529482f4b494010a13338a74446c510712f670b3anthony            case 48: /* SE_4_8 */
2030529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
2031529482f4b494010a13338a74446c510712f670b3anthony              break;
2032529482f4b494010a13338a74446c510712f670b3anthony            case 49: /* SE_4_9 */
2033529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
2034529482f4b494010a13338a74446c510712f670b3anthony              break;
2035529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 8-connected thinning - negatives of the above */
2036529482f4b494010a13338a74446c510712f670b3anthony            case 81: /* SE_8_0 */
2037529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
2038529482f4b494010a13338a74446c510712f670b3anthony              break;
2039529482f4b494010a13338a74446c510712f670b3anthony            case 82: /* SE_8_2 */
2040529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
2041529482f4b494010a13338a74446c510712f670b3anthony              break;
2042529482f4b494010a13338a74446c510712f670b3anthony            case 83: /* SE_8_3 */
2043529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
2044529482f4b494010a13338a74446c510712f670b3anthony              break;
2045529482f4b494010a13338a74446c510712f670b3anthony            case 84: /* SE_8_4 */
2046529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
2047529482f4b494010a13338a74446c510712f670b3anthony              break;
2048529482f4b494010a13338a74446c510712f670b3anthony            case 85: /* SE_8_5 */
2049529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
2050529482f4b494010a13338a74446c510712f670b3anthony              break;
2051529482f4b494010a13338a74446c510712f670b3anthony            case 86: /* SE_8_6 */
2052529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
2053529482f4b494010a13338a74446c510712f670b3anthony              break;
2054529482f4b494010a13338a74446c510712f670b3anthony            case 87: /* SE_8_7 */
2055529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
2056529482f4b494010a13338a74446c510712f670b3anthony              break;
2057529482f4b494010a13338a74446c510712f670b3anthony            case 88: /* SE_8_8 */
2058529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
2059529482f4b494010a13338a74446c510712f670b3anthony              break;
2060529482f4b494010a13338a74446c510712f670b3anthony            case 89: /* SE_8_9 */
2061529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
2062529482f4b494010a13338a74446c510712f670b3anthony              break;
2063529482f4b494010a13338a74446c510712f670b3anthony            /* Special combined SE kernels */
20649a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
20659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
20669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
20679a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
20689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
20699a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
2070529482f4b494010a13338a74446c510712f670b3anthony            case 481: /* SE_48_1 - General Connected Corner Kernel */
2071529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
2072529482f4b494010a13338a74446c510712f670b3anthony              break;
2073529482f4b494010a13338a74446c510712f670b3anthony            default:
2074529482f4b494010a13338a74446c510712f670b3anthony            case 482: /* SE_48_2 - General Edge Kernel */
2075529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
2076529482f4b494010a13338a74446c510712f670b3anthony              break;
2077529482f4b494010a13338a74446c510712f670b3anthony          }
2078529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
2079529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
2080529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
2081529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
2082529482f4b494010a13338a74446c510712f670b3anthony          break;
2083529482f4b494010a13338a74446c510712f670b3anthony        }
2084529482f4b494010a13338a74446c510712f670b3anthony      /*
2085529482f4b494010a13338a74446c510712f670b3anthony        Distance Measuring Kernels
2086529482f4b494010a13338a74446c510712f670b3anthony      */
20871ef941fea2534a0d20ba7d71307d35040247decbanthony      case ChebyshevKernel:
20881ef941fea2534a0d20ba7d71307d35040247decbanthony        {
20891ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
20901ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
20911ef941fea2534a0d20ba7d71307d35040247decbanthony          else
20921ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
20931ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
20941ef941fea2534a0d20ba7d71307d35040247decbanthony
2095e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
2096e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
2097e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
2098d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
20991ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
21001ef941fea2534a0d20ba7d71307d35040247decbanthony
21011ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
21021ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21031ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
21041ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
21051ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21061ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2107c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
21081ef941fea2534a0d20ba7d71307d35040247decbanthony      case ManhattanKernel:
21091ef941fea2534a0d20ba7d71307d35040247decbanthony        {
21101ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
21111ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
21121ef941fea2534a0d20ba7d71307d35040247decbanthony          else
21131ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
21141ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
21151ef941fea2534a0d20ba7d71307d35040247decbanthony
2116e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
2117e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
2118e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
2119d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
21201ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
21211ef941fea2534a0d20ba7d71307d35040247decbanthony
21221ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
21231ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21241ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
21251ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*(labs((long) u)+labs((long) v)) );
21261ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21271ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2128c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
21291ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonalKernel:
2130602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
21311ef941fea2534a0d20ba7d71307d35040247decbanthony        if (args->rho < 2.0)
21321ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
2133602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
2134bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2135bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2136602ab9b30b644a78a4057da93d838a77391ec0acanthony
2137e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
2138e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
2139e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
2140d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
214183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2142602ab9b30b644a78a4057da93d838a77391ec0acanthony
2143bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2144bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21451ef941fea2534a0d20ba7d71307d35040247decbanthony            {
21461ef941fea2534a0d20ba7d71307d35040247decbanthony              double
21471ef941fea2534a0d20ba7d71307d35040247decbanthony                r1 = MagickMax(fabs((double)u),fabs((double)v)),
21481ef941fea2534a0d20ba7d71307d35040247decbanthony                r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
21491ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += kernel->values[i] =
21501ef941fea2534a0d20ba7d71307d35040247decbanthony                        args->sigma*MagickMax(r1,r2);
21511ef941fea2534a0d20ba7d71307d35040247decbanthony            }
2152c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2153602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2154602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2155602ab9b30b644a78a4057da93d838a77391ec0acanthony    case EuclideanKernel:
2156602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2157602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (args->rho < 1.0)
2158c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
2159602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
21601ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2161bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2162602ab9b30b644a78a4057da93d838a77391ec0acanthony
2163e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
2164e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
2165e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
2166d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
216783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2168602ab9b30b644a78a4057da93d838a77391ec0acanthony
2169bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2170bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2171c99304fe3c8d9c617da792b40b57c118bb1249afcristy            kernel->positive_range += ( kernel->values[i] =
21720ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony              args->sigma*sqrt((double)(u*u+v*v)) );
2173c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2174602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2175602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2176602ab9b30b644a78a4057da93d838a77391ec0acanthony    default:
2177c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
2178529482f4b494010a13338a74446c510712f670b3anthony        /* No-Op Kernel - Basically just a single pixel on its own */
21793ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony        kernel=ParseKernelArray("1:1");
2180c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
2181c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
2182529482f4b494010a13338a74446c510712f670b3anthony        kernel->type = UndefinedKernel;
2183c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
2184c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
2185602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
2186602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
2187602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2188602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2189c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2190602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
2191602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2192602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2193602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2194602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
21956771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     C l o n e K e r n e l I n f o                                           %
21964fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21974fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21984fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21994fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22004fd27e21043be809d66c8202e779255e5b660d2danthony%
22011b2bc0a7da432e6e1cc0480280402df213faa940anthony%  CloneKernelInfo() creates a new clone of the given Kernel List so that its
22021b2bc0a7da432e6e1cc0480280402df213faa940anthony%  can be modified without effecting the original.  The cloned kernel should
22030ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony%  be destroyed using DestoryKernelInfo() when no longer needed.
22047a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
2205e636559dfadfdb115cc93f223315052a1ee89238cristy%  The format of the CloneKernelInfo method is:
22064fd27e21043be809d66c8202e779255e5b660d2danthony%
2207930be614b4595b97cd79ee864a394796740f76adanthony%      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22084fd27e21043be809d66c8202e779255e5b660d2danthony%
22094fd27e21043be809d66c8202e779255e5b660d2danthony%  A description of each parameter follows:
22104fd27e21043be809d66c8202e779255e5b660d2danthony%
22114fd27e21043be809d66c8202e779255e5b660d2danthony%    o kernel: the Morphology/Convolution kernel to be cloned
22124fd27e21043be809d66c8202e779255e5b660d2danthony%
22134fd27e21043be809d66c8202e779255e5b660d2danthony*/
2214ef656913b0b30d713ae94c82c47693c9dc69c9f4cristyMagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22154fd27e21043be809d66c8202e779255e5b660d2danthony{
2216bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
22174fd27e21043be809d66c8202e779255e5b660d2danthony    i;
22184fd27e21043be809d66c8202e779255e5b660d2danthony
221919eb64195ef744f61293025952df1e5e6de66524cristy  KernelInfo
22207a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *new_kernel;
22214fd27e21043be809d66c8202e779255e5b660d2danthony
22224fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
22237a01dcf50ce12cb2a789bedff51e9345f022432eanthony  new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
22247a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if (new_kernel == (KernelInfo *) NULL)
22257a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(new_kernel);
22267a01dcf50ce12cb2a789bedff51e9345f022432eanthony  *new_kernel=(*kernel); /* copy values in structure */
22277a01dcf50ce12cb2a789bedff51e9345f022432eanthony
22287a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* replace the values with a copy of the values */
2229e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  new_kernel->values=(MagickRealType *) MagickAssumeAligned(
2230e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy    AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
2231d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if (new_kernel->values == (MagickRealType *) NULL)
22327a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(DestroyKernelInfo(new_kernel));
2233bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
22347a01dcf50ce12cb2a789bedff51e9345f022432eanthony    new_kernel->values[i]=kernel->values[i];
22351b2bc0a7da432e6e1cc0480280402df213faa940anthony
22361b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* Also clone the next kernel in the kernel list */
22371b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL ) {
22381b2bc0a7da432e6e1cc0480280402df213faa940anthony    new_kernel->next = CloneKernelInfo(kernel->next);
22391b2bc0a7da432e6e1cc0480280402df213faa940anthony    if ( new_kernel->next == (KernelInfo *) NULL )
22401b2bc0a7da432e6e1cc0480280402df213faa940anthony      return(DestroyKernelInfo(new_kernel));
22411b2bc0a7da432e6e1cc0480280402df213faa940anthony  }
22421b2bc0a7da432e6e1cc0480280402df213faa940anthony
22437a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(new_kernel);
22444fd27e21043be809d66c8202e779255e5b660d2danthony}
22454fd27e21043be809d66c8202e779255e5b660d2danthony
22464fd27e21043be809d66c8202e779255e5b660d2danthony/*
22474fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22484fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22494fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22504fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
225183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     D e s t r o y K e r n e l I n f o                                       %
2252602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2253602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2254602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2255602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2256602ab9b30b644a78a4057da93d838a77391ec0acanthony%
225783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
225883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  kernel.
2259602ab9b30b644a78a4057da93d838a77391ec0acanthony%
226083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the DestroyKernelInfo method is:
2261602ab9b30b644a78a4057da93d838a77391ec0acanthony%
226283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2263602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2264602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2265602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2266602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: the Morphology/Convolution kernel to be destroyed
2267602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2268602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
226983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthonyMagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2270602ab9b30b644a78a4057da93d838a77391ec0acanthony{
22712be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  assert(kernel != (KernelInfo *) NULL);
22727a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( kernel->next != (KernelInfo *) NULL )
22739f752c092332bf2c4e599ea49e9422c13f3fb11bcristy    kernel->next=DestroyKernelInfo(kernel->next);
2274d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
22759f752c092332bf2c4e599ea49e9422c13f3fb11bcristy  kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
2276602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2277602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2278c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2279c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony/*
2280c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2281c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2282c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2283c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2284ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d M i r r o r K e r n e l I n f o                             %
22853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22863c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22893c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2290bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
2291bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  sequence of 90-degree rotated kernels but providing a reflected 180
2292bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  rotatation, before the -/+ 90-degree rotations.
2293bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2294bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  This special rotation order produces a better, more symetrical thinning of
2295bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  objects.
2296bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2297bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandMirrorKernelInfo method is:
2298bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2299bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandMirrorKernelInfo(KernelInfo *kernel)
2300bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2301bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  A description of each parameter follows:
2302bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2303bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    o kernel: the Morphology/Convolution kernel
2304bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2305bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% This function is only internel to this module, as it is not finalized,
2306bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% especially with regard to non-orthogonal angles, and rotation of larger
2307bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% 2D kernels.
2308bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony*/
2309bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2310bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#if 0
2311bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void FlopKernelInfo(KernelInfo *kernel)
2312bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    { /* Do a Flop by reversing each row. */
2313bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      size_t
2314bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        y;
2315bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register ssize_t
2316bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        x,r;
2317bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register double
2318bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        *k,t;
2319bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2320bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2321bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2322bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          t=k[x],  k[x]=k[r],  k[r]=t;
2323bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2324bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      kernel->x = kernel->width - kernel->x - 1;
2325bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      angle = fmod(angle+180.0, 360.0);
2326bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    }
2327bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#endif
2328bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2329bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandMirrorKernelInfo(KernelInfo *kernel)
2330bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony{
2331bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  KernelInfo
2332bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *clone,
2333bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *last;
2334bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2335bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = kernel;
2336bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2337bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2338bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);   /* flip */
2339bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2340bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2341bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2342bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2343bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 90);   /* transpose */
2344bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2345bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2346bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2347bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2348bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);  /* flop */
2349bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2350bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2351bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  return;
2352bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony}
2353bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2354bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony/*
2355bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2356bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2357bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2358bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2359ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d R o t a t e K e r n e l I n f o                             %
2360bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2361bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2362bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2363bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2364bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2365bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2366529482f4b494010a13338a74446c510712f670b3anthony%  incrementally by the angle given, until the kernel repeats.
23673c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  WARNING: 45 degree rotations only works for 3x3 kernels.
23693c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  While 90 degree roatations only works for linear and square kernels
23703c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2371bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandRotateKernelInfo method is:
23723c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2373bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
23743c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23753c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  A description of each parameter follows:
23763c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23773c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o kernel: the Morphology/Convolution kernel
23783c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23793c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o angle: angle to rotate in degrees
23803c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23813c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% This function is only internel to this module, as it is not finalized,
23823c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% especially with regard to non-orthogonal angles, and rotation of larger
23833c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% 2D kernels.
23843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony*/
238547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
238647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony/* Internal Routine - Return true if two kernels are the same */
238747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthonystatic MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
238847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony     const KernelInfo *kernel2)
238947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony{
2390bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
239147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    i;
23921d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
23931d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check size and origin location */
23941d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  if (    kernel1->width != kernel2->width
23951d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->height != kernel2->height
23961d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->x != kernel2->x
23971d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->y != kernel2->y )
239847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return MagickFalse;
23991d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
24001d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check actual kernel values */
240147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  for (i=0; i < (kernel1->width*kernel1->height); i++) {
2402f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test for Nan equivalence */
24037f71c558beaa2cdf586a5e1358a79f9085e49c35cristy    if ( IsNaN(kernel1->values[i]) && !IsNaN(kernel2->values[i]) )
240447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
24057f71c558beaa2cdf586a5e1358a79f9085e49c35cristy    if ( IsNaN(kernel2->values[i]) && !IsNaN(kernel1->values[i]) )
240647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
2407f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test actual values are equivalent */
2408b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
240947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
241047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
24111d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
241247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return MagickTrue;
241347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony}
241447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
2415bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
24163c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony{
24173c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  KernelInfo
241884d9b5596c0900609dea18795861e2b0936b21c6cristy    *clone,
24193c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    *last;
2420a9a61ad96c5112acd968f97b689bd42ca392d70bcristy
24213c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  last = kernel;
242247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while(1) {
242384d9b5596c0900609dea18795861e2b0936b21c6cristy    clone = CloneKernelInfo(last);
242484d9b5596c0900609dea18795861e2b0936b21c6cristy    RotateKernelInfo(clone, angle);
242584d9b5596c0900609dea18795861e2b0936b21c6cristy    if ( SameKernelInfo(kernel, clone) == MagickTrue )
242647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
2427bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    LastKernelInfo(last)->next = clone;
242884d9b5596c0900609dea18795861e2b0936b21c6cristy    last = clone;
24293c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  }
2430bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
243147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return;
24323c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony}
24333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
24343c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony/*
24353c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24383c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
243946a369d839971ab627bdb31a93d8bd63e81b65a3anthony+     C a l c M e t a K e r n a l I n f o                                     %
244046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
244546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
2446dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  using the kernel values.  This should only ne used if it is not possible to
244746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  calculate that meta-data in some easier way.
244846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
244946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It is important that the meta-data is correct before ScaleKernelInfo() is
245046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  used to perform kernel normalization.
245146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the CalcKernelMetaData method is:
245346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
245546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
245746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
245946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Minimum and Maximum values are assumed to include zero, even if
246146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  zero is not part of the kernel (as in Gaussian Derived kernels). This
246246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  however is not true for flat-shaped morphological kernels.
246346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Only the specific kernel pointed to is modified, not a list of
246546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  multiple kernels.
246646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246746a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This is an internal function and not expected to be useful outside this
246846a369d839971ab627bdb31a93d8bd63e81b65a3anthony% module.  This could change however.
246946a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
247046a369d839971ab627bdb31a93d8bd63e81b65a3anthonystatic void CalcKernelMetaData(KernelInfo *kernel)
247146a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
2472bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
247346a369d839971ab627bdb31a93d8bd63e81b65a3anthony    i;
247446a369d839971ab627bdb31a93d8bd63e81b65a3anthony
247546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->minimum = kernel->maximum = 0.0;
247646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->negative_range = kernel->positive_range = 0.0;
247746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  for (i=0; i < (kernel->width*kernel->height); i++)
247846a369d839971ab627bdb31a93d8bd63e81b65a3anthony    {
247946a369d839971ab627bdb31a93d8bd63e81b65a3anthony      if ( fabs(kernel->values[i]) < MagickEpsilon )
248046a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->values[i] = 0.0;
248146a369d839971ab627bdb31a93d8bd63e81b65a3anthony      ( kernel->values[i] < 0)
248246a369d839971ab627bdb31a93d8bd63e81b65a3anthony          ?  ( kernel->negative_range += kernel->values[i] )
248346a369d839971ab627bdb31a93d8bd63e81b65a3anthony          :  ( kernel->positive_range += kernel->values[i] );
248446a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Minimize(kernel->minimum, kernel->values[i]);
248546a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Maximize(kernel->maximum, kernel->values[i]);
248646a369d839971ab627bdb31a93d8bd63e81b65a3anthony    }
248746a369d839971ab627bdb31a93d8bd63e81b65a3anthony
248846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
248946a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
249046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
249146a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
249246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
24969eb4f74649b23c053b308ce1152dce51239450baanthony%     M o r p h o l o g y A p p l y                                           %
2497602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2498602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2499602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2500602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2501602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25029eb4f74649b23c053b308ce1152dce51239450baanthony%  MorphologyApply() applies a morphological method, multiple times using
250322de2722b682eb405b60ec6022a7546df994674eanthony%  a list of multiple kernels.  This is the method that should be called by
250422de2722b682eb405b60ec6022a7546df994674eanthony%  other 'operators' that internally use morphology operations as part of
250522de2722b682eb405b60ec6022a7546df994674eanthony%  their processing.
2506602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2507ec9ace44c23c302f336f6e672f6857312747d29bcristy%  It is basically equivalent to as MorphologyImage() (see below) but without
2508ec9ace44c23c302f336f6e672f6857312747d29bcristy%  any user controls.  This allows internel programs to use this method to
2509ec9ace44c23c302f336f6e672f6857312747d29bcristy%  perform a specific task without possible interference by any API user
2510ec9ace44c23c302f336f6e672f6857312747d29bcristy%  supplied settings.
2511e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%
2512f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  It is MorphologyImage() task to extract any such user controls, and
2513e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  pass them to this function for processing.
25149eb4f74649b23c053b308ce1152dce51239450baanthony%
251522de2722b682eb405b60ec6022a7546df994674eanthony%  More specifically all given kernels should already be scaled, normalised,
251622de2722b682eb405b60ec6022a7546df994674eanthony%  and blended appropriatally before being parred to this routine. The
251722de2722b682eb405b60ec6022a7546df994674eanthony%  appropriate bias, and compose (typically 'UndefinedComposeOp') given.
2518602ab9b30b644a78a4057da93d838a77391ec0acanthony%
251947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%  The format of the MorphologyApply method is:
2520602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25219eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyApply(const Image *image,MorphologyMethod method,
2522f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%        const ssize_t iterations,const KernelInfo *kernel,
2523f46d42620631d2581e0b6a56456e203e17c427c8anthony%        const CompositeMethod compose,const double bias,
2524f46d42620631d2581e0b6a56456e203e17c427c8anthony%        ExceptionInfo *exception)
2525602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2526602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2527602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25288d18850dee4bed193a64866a6d2353eeeb73e145anthony%    o image: the source image
2529602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2530602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o method: the morphology method to be applied.
2531602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2532602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o iterations: apply the operation this many times (or no change).
2533602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  A value of -1 means loop until no change found.
2534602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  How this is applied may depend on the morphology method.
2535602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  Typically this is a value of 1.
2536602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2537602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o channel: the channel type.
2538602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2539602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: An array of double representing the morphology kernel.
2540602ab9b30b644a78a4057da93d838a77391ec0acanthony%
254147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%    o compose: How to handle or merge multi-kernel results.
25428d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'UndefinedCompositeOp' use default for the Morphology method.
25438d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'NoCompositeOp' force image to be re-iterated by each kernel.
25448d18850dee4bed193a64866a6d2353eeeb73e145anthony%          Otherwise merge the results using the compose method given.
254547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%
2546f46d42620631d2581e0b6a56456e203e17c427c8anthony%    o bias: Convolution Output Bias.
2547f46d42620631d2581e0b6a56456e203e17c427c8anthony%
25489eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
2549602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2550602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
2551f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristystatic ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
2552f46d42620631d2581e0b6a56456e203e17c427c8anthony  const MorphologyMethod method,const KernelInfo *kernel,const double bias,
2553f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
2554602ab9b30b644a78a4057da93d838a77391ec0acanthony{
25552be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy#define MorphologyTag  "Morphology/Image"
2556602ab9b30b644a78a4057da93d838a77391ec0acanthony
25575f959473f334e196c6bf39b740c12cb4963fceebcristy  CacheView
25584c08aed51c5899665ade97263692328eea4af106cristy    *image_view,
25594c08aed51c5899665ade97263692328eea4af106cristy    *morphology_view;
25605f959473f334e196c6bf39b740c12cb4963fceebcristy
2561ec9ace44c23c302f336f6e672f6857312747d29bcristy  OffsetInfo
2562ec9ace44c23c302f336f6e672f6857312747d29bcristy    offset;
2563ec9ace44c23c302f336f6e672f6857312747d29bcristy
2564bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  ssize_t
2565ec9ace44c23c302f336f6e672f6857312747d29bcristy    y;
2566a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
2567a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
2568ec9ace44c23c302f336f6e672f6857312747d29bcristy    width,
2569602ab9b30b644a78a4057da93d838a77391ec0acanthony    changed;
2570602ab9b30b644a78a4057da93d838a77391ec0acanthony
2571602ab9b30b644a78a4057da93d838a77391ec0acanthony  MagickBooleanType
2572602ab9b30b644a78a4057da93d838a77391ec0acanthony    status;
2573602ab9b30b644a78a4057da93d838a77391ec0acanthony
25745f959473f334e196c6bf39b740c12cb4963fceebcristy  MagickOffsetType
25755f959473f334e196c6bf39b740c12cb4963fceebcristy    progress;
2576602ab9b30b644a78a4057da93d838a77391ec0acanthony
2577e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image != (Image *) NULL);
2578e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image->signature == MagickSignature);
25794c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image != (Image *) NULL);
25804c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image->signature == MagickSignature);
2581e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel != (KernelInfo *) NULL);
2582e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel->signature == MagickSignature);
2583e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception != (ExceptionInfo *) NULL);
2584e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception->signature == MagickSignature);
2585602ab9b30b644a78a4057da93d838a77391ec0acanthony  status=MagickTrue;
2586602ab9b30b644a78a4057da93d838a77391ec0acanthony  changed=0;
2587602ab9b30b644a78a4057da93d838a77391ec0acanthony  progress=0;
258846ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  image_view=AcquireVirtualCacheView(image,exception);
258946ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
2590ec9ace44c23c302f336f6e672f6857312747d29bcristy  width=image->columns+kernel->width-1;
2591ec9ace44c23c302f336f6e672f6857312747d29bcristy  switch (method)
2592ec9ace44c23c302f336f6e672f6857312747d29bcristy  {
2593930be614b4595b97cd79ee864a394796740f76adanthony    case ConvolveMorphology:
2594930be614b4595b97cd79ee864a394796740f76adanthony    case DilateMorphology:
2595930be614b4595b97cd79ee864a394796740f76adanthony    case DilateIntensityMorphology:
2596f34d9b2df49a407af764c79e07d587af0600983aanthony    case IterativeDistanceMorphology:
2597ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2598ec9ace44c23c302f336f6e672f6857312747d29bcristy      /*
2599ec9ace44c23c302f336f6e672f6857312747d29bcristy        Kernel needs to used with reflection about origin.
2600ec9ace44c23c302f336f6e672f6857312747d29bcristy      */
2601ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=(ssize_t) kernel->width-kernel->x-1;
2602ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=(ssize_t) kernel->height-kernel->y-1;
260329188a8682a98d4b7882cca434b170517555fc7danthony      break;
2604ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
26055ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeMorphology:
26065ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeIntensityMorphology:
26075ef8e94ff55717be2387d537bd49025780a1a558anthony    case HitAndMissMorphology:
26085ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThinningMorphology:
26095ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThickenMorphology:
2610ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2611ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=kernel->x;
2612ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=kernel->y;
26135ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
2614ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
2615930be614b4595b97cd79ee864a394796740f76adanthony    default:
2616ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
26179eb4f74649b23c053b308ce1152dce51239450baanthony      assert("Not a Primitive Morphology Method" != (char *) NULL);
2618930be614b4595b97cd79ee864a394796740f76adanthony      break;
2619ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
262029188a8682a98d4b7882cca434b170517555fc7danthony  }
2621ec9ace44c23c302f336f6e672f6857312747d29bcristy  if ((method == ConvolveMorphology) && (kernel->width == 1))
2622780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    {
2623780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      register ssize_t
2624780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        x;
2625fd1175952254cf1ac848ddb441e483c5e33d517fcristy
2626780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      /*
2627ec9ace44c23c302f336f6e672f6857312747d29bcristy        Special handling (for speed) of vertical (blur) kernels.  This performs
2628ec9ace44c23c302f336f6e672f6857312747d29bcristy        its handling in columns rather than in rows.  This is only done
2629ec9ace44c23c302f336f6e672f6857312747d29bcristy        for convolve as it is the only method that generates very large 1-D
2630ec9ace44c23c302f336f6e672f6857312747d29bcristy        vertical kernels (such as a 'BlurKernel')
2631780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy     */
26328d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2633780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy     #pragma omp parallel for schedule(static,4) shared(progress,status) \
2634780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy       magick_threads(image,morphology_image,image->columns,1)
26358d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
2636780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      for (x=0; x < (ssize_t) image->columns; x++)
2637780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      {
2638780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register const Quantum
2639780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          *restrict p;
26408d18850dee4bed193a64866a6d2353eeeb73e145anthony
2641780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register Quantum
2642780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          *restrict q;
26438d18850dee4bed193a64866a6d2353eeeb73e145anthony
2644780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register ssize_t
2645780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          y;
26468d18850dee4bed193a64866a6d2353eeeb73e145anthony
2647780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        ssize_t
2648780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          center;
26498d18850dee4bed193a64866a6d2353eeeb73e145anthony
2650780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (status == MagickFalse)
26518d18850dee4bed193a64866a6d2353eeeb73e145anthony          continue;
2652ec9ace44c23c302f336f6e672f6857312747d29bcristy        p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
2653780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          kernel->height-1,exception);
2654780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2655780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          morphology_image->rows,exception);
2656780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2657780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2658780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            status=MagickFalse;
2659780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            continue;
2660780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
2661ec9ace44c23c302f336f6e672f6857312747d29bcristy        center=(ssize_t) GetPixelChannels(image)*offset.y;
2662780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        for (y=0; y < (ssize_t) image->rows; y++)
2663f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy        {
2664780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          register ssize_t
2665780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            i;
26668d18850dee4bed193a64866a6d2353eeeb73e145anthony
2667780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2668780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2669780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            double
2670780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              alpha,
2671780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              gamma,
2672780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              pixel;
26738d18850dee4bed193a64866a6d2353eeeb73e145anthony
2674780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            PixelChannel
2675780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              channel;
2676d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
2677780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            PixelTrait
2678780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              morphology_traits,
2679780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              traits;
26808d18850dee4bed193a64866a6d2353eeeb73e145anthony
2681780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            register const MagickRealType
2682780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              *restrict k;
26838d18850dee4bed193a64866a6d2353eeeb73e145anthony
2684780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            register const Quantum
2685780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              *restrict pixels;
26868d18850dee4bed193a64866a6d2353eeeb73e145anthony
2687780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            register ssize_t
2688780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              u;
2689f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy
2690780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            ssize_t
2691780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              v;
2692780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2693780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            channel=GetPixelChannelChannel(image,i);
2694780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            traits=GetPixelChannelTraits(image,channel);
2695780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2696780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if ((traits == UndefinedPixelTrait) ||
2697780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                (morphology_traits == UndefinedPixelTrait))
2698f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy              continue;
2699780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (((morphology_traits & CopyPixelTrait) != 0) ||
27008ae6b64581e48a0c876e739e4097107679509e37cristy                (GetPixelMask(image,p+center) != 0))
2701f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy              {
2702780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                SetPixelChannel(morphology_image,channel,p[center+i],q);
2703780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                continue;
2704780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              }
2705ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
2706780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            pixels=p;
2707780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            pixel=bias;
2708780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            gamma=0.0;
2709780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if ((morphology_traits & BlendPixelTrait) == 0)
2710780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              {
2711780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                /*
2712780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  No alpha blending.
2713780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                */
2714780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                for (v=0; v < (ssize_t) kernel->height; v++)
2715f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy                {
2716780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  for (u=0; u < (ssize_t) kernel->width; u++)
2717780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  {
2718780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    if (IsNaN(*k) != MagickFalse)
2719780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                      continue;
2720780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    pixel+=(*k)*pixels[i];
2721780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    gamma+=(*k);
2722780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    k--;
2723780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    pixels+=GetPixelChannels(image);
2724780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  }
2725f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy                }
2726780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                gamma=PerceptibleReciprocal(gamma);
2727780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                pixel*=gamma;
2728780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                if (fabs(pixel-p[center+i]) > MagickEpsilon)
2729780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  changed++;
2730780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),
2731780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  q);
2732780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                continue;
2733f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy              }
2734780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            /*
2735780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              Alpha blending.
2736780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            */
2737780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            for (v=0; v < (ssize_t) kernel->width; v++)
2738f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy            {
2739780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              for (u=0; u < (ssize_t) kernel->width; u++)
2740780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              {
2741780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                if (IsNaN(*k) != MagickFalse)
2742780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  continue;
2743780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2744780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                pixel+=(*k)*alpha*pixels[i];
2745780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                gamma+=(*k)*alpha;
2746780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                k--;
2747780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                pixels+=GetPixelChannels(image);
2748780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              }
27498d18850dee4bed193a64866a6d2353eeeb73e145anthony            }
2750780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            gamma=PerceptibleReciprocal(gamma);
2751780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            pixel*=gamma;
2752780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (fabs(pixel-p[center+i]) > MagickEpsilon)
2753780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              changed++;
2754780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),q);
27558d18850dee4bed193a64866a6d2353eeeb73e145anthony          }
2756780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          p+=GetPixelChannels(image);
2757780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          q+=GetPixelChannels(morphology_image);
2758ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
2759780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
2760780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          status=MagickFalse;
2761780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (image->progress_monitor != (MagickProgressMonitor) NULL)
2762780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2763780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            MagickBooleanType
2764780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              proceed;
27658d18850dee4bed193a64866a6d2353eeeb73e145anthony
27668d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2767780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            #pragma omp critical (MagickCore_MorphologyImage)
27688d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
2769ec9ace44c23c302f336f6e672f6857312747d29bcristy            proceed=SetImageProgress(image,MorphologyTag,progress++,
2770ec9ace44c23c302f336f6e672f6857312747d29bcristy              image->rows);
2771780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (proceed == MagickFalse)
2772780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              status=MagickFalse;
2773780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
2774780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      }
2775780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      morphology_image->type=image->type;
2776780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      morphology_view=DestroyCacheView(morphology_view);
2777780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      image_view=DestroyCacheView(image_view);
2778780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      return(status ? (ssize_t) changed : 0);
2779780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    }
27808d18850dee4bed193a64866a6d2353eeeb73e145anthony  /*
2781780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    Normal handling of horizontal or rectangular kernels (row by row).
27828d18850dee4bed193a64866a6d2353eeeb73e145anthony  */
2783602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2784ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(progress,status) \
27855e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,morphology_image,image->rows,1)
2786602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
2787bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (y=0; y < (ssize_t) image->rows; y++)
2788602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
27894c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
2790602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict p;
2791602ab9b30b644a78a4057da93d838a77391ec0acanthony
27924c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
2793602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict q;
2794602ab9b30b644a78a4057da93d838a77391ec0acanthony
2795bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
2796602ab9b30b644a78a4057da93d838a77391ec0acanthony      x;
2797602ab9b30b644a78a4057da93d838a77391ec0acanthony
2798ec9ace44c23c302f336f6e672f6857312747d29bcristy    ssize_t
2799ec9ace44c23c302f336f6e672f6857312747d29bcristy      center;
2800602ab9b30b644a78a4057da93d838a77391ec0acanthony
2801602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (status == MagickFalse)
2802602ab9b30b644a78a4057da93d838a77391ec0acanthony      continue;
2803ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
2804af43471dfedff5d1314cd674fac6508d230df1bccristy      kernel->height,exception);
2805af43471dfedff5d1314cd674fac6508d230df1bccristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
2806af43471dfedff5d1314cd674fac6508d230df1bccristy      1,exception);
28074c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2808602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2809602ab9b30b644a78a4057da93d838a77391ec0acanthony        status=MagickFalse;
2810602ab9b30b644a78a4057da93d838a77391ec0acanthony        continue;
2811602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2812ec9ace44c23c302f336f6e672f6857312747d29bcristy    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
2813ec9ace44c23c302f336f6e672f6857312747d29bcristy      GetPixelChannels(image)*offset.x);
2814bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) image->columns; x++)
2815602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
2816ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
2817ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
2818602ab9b30b644a78a4057da93d838a77391ec0acanthony
2819ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2820ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
2821ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
2822ec9ace44c23c302f336f6e672f6857312747d29bcristy          alpha,
2823ec9ace44c23c302f336f6e672f6857312747d29bcristy          gamma,
2824ec9ace44c23c302f336f6e672f6857312747d29bcristy          maximum,
2825ec9ace44c23c302f336f6e672f6857312747d29bcristy          minimum,
2826ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
2827602ab9b30b644a78a4057da93d838a77391ec0acanthony
2828ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelChannel
2829ec9ace44c23c302f336f6e672f6857312747d29bcristy          channel;
2830602ab9b30b644a78a4057da93d838a77391ec0acanthony
2831ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
2832ec9ace44c23c302f336f6e672f6857312747d29bcristy          morphology_traits,
2833ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
2834ec9ace44c23c302f336f6e672f6857312747d29bcristy
2835ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
2836ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
2837ec9ace44c23c302f336f6e672f6857312747d29bcristy
2838ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
2839ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
2840ec9ace44c23c302f336f6e672f6857312747d29bcristy
2841ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
2842ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
2843602ab9b30b644a78a4057da93d838a77391ec0acanthony
2844ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
2845ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
2846ec9ace44c23c302f336f6e672f6857312747d29bcristy
2847ec9ace44c23c302f336f6e672f6857312747d29bcristy        channel=GetPixelChannelChannel(image,i);
2848ec9ace44c23c302f336f6e672f6857312747d29bcristy        traits=GetPixelChannelTraits(image,channel);
2849ec9ace44c23c302f336f6e672f6857312747d29bcristy        morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2850ec9ace44c23c302f336f6e672f6857312747d29bcristy        if ((traits == UndefinedPixelTrait) ||
2851ec9ace44c23c302f336f6e672f6857312747d29bcristy            (morphology_traits == UndefinedPixelTrait))
2852ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
2853ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (((morphology_traits & CopyPixelTrait) != 0) ||
28548ae6b64581e48a0c876e739e4097107679509e37cristy            (GetPixelMask(image,p+center) != 0))
2855ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2856ec9ace44c23c302f336f6e672f6857312747d29bcristy            SetPixelChannel(morphology_image,channel,p[center+i],q);
2857ec9ace44c23c302f336f6e672f6857312747d29bcristy            continue;
2858ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2859ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
2860ec9ace44c23c302f336f6e672f6857312747d29bcristy        maximum=0.0;
2861ec9ace44c23c302f336f6e672f6857312747d29bcristy        minimum=(double) QuantumRange;
2862ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
2863ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2864ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ConvolveMorphology: pixel=bias; break;
2865ec9ace44c23c302f336f6e672f6857312747d29bcristy          case HitAndMissMorphology: pixel=(double) QuantumRange; break;
2866ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThinningMorphology: pixel=(double) QuantumRange; break;
2867ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThickenMorphology: pixel=(double) QuantumRange; break;
2868ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeMorphology: pixel=(double) QuantumRange; break;
2869ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateMorphology: pixel=0.0; break;
2870ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeIntensityMorphology:
2871ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateIntensityMorphology:
2872ec9ace44c23c302f336f6e672f6857312747d29bcristy          case IterativeDistanceMorphology:
2873ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2874ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixel=(double) p[center+i];
2875ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
2876ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2877ec9ace44c23c302f336f6e672f6857312747d29bcristy          default: pixel=0; break;
2878ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
2879ec9ace44c23c302f336f6e672f6857312747d29bcristy        gamma=0.0;
2880ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
2881ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2882ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ConvolveMorphology:
2883ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2884ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2885ec9ace44c23c302f336f6e672f6857312747d29bcristy               Weighted Average of pixels using reflected kernel
2886ec9ace44c23c302f336f6e672f6857312747d29bcristy
2887ec9ace44c23c302f336f6e672f6857312747d29bcristy               For correct working of this operation for asymetrical
2888ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernels, the kernel needs to be applied in its reflected form.
2889ec9ace44c23c302f336f6e672f6857312747d29bcristy               That is its values needs to be reversed.
2890ec9ace44c23c302f336f6e672f6857312747d29bcristy
2891ec9ace44c23c302f336f6e672f6857312747d29bcristy               Correlation is actually the same as this but without reflecting
2892ec9ace44c23c302f336f6e672f6857312747d29bcristy               the kernel, and thus 'lower-level' that Convolution.  However
2893ec9ace44c23c302f336f6e672f6857312747d29bcristy               as Convolution is the more common method used, and it does not
2894ec9ace44c23c302f336f6e672f6857312747d29bcristy               really cost us much in terms of processing to use a reflected
2895ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernel, so it is Convolution that is implemented.
2896ec9ace44c23c302f336f6e672f6857312747d29bcristy
2897ec9ace44c23c302f336f6e672f6857312747d29bcristy               Correlation will have its kernel reflected before calling
2898ec9ace44c23c302f336f6e672f6857312747d29bcristy               this function to do a Convolve.
2899ec9ace44c23c302f336f6e672f6857312747d29bcristy
2900ec9ace44c23c302f336f6e672f6857312747d29bcristy               For more details of Correlation vs Convolution see
2901ec9ace44c23c302f336f6e672f6857312747d29bcristy                 http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
2902930be614b4595b97cd79ee864a394796740f76adanthony            */
2903ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
2904ec9ace44c23c302f336f6e672f6857312747d29bcristy            if ((morphology_traits & BlendPixelTrait) == 0)
2905ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2906ec9ace44c23c302f336f6e672f6857312747d29bcristy                /*
2907ec9ace44c23c302f336f6e672f6857312747d29bcristy                  No alpha blending.
29088d18850dee4bed193a64866a6d2353eeeb73e145anthony                */
2909ec9ace44c23c302f336f6e672f6857312747d29bcristy                for (v=0; v < (ssize_t) kernel->height; v++)
2910ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
2911ec9ace44c23c302f336f6e672f6857312747d29bcristy                  for (u=0; u < (ssize_t) kernel->width; u++)
2912ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2913ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (IsNaN(*k) == MagickFalse)
2914ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
2915ec9ace44c23c302f336f6e672f6857312747d29bcristy                        pixel+=(*k)*pixels[i];
2916ec9ace44c23c302f336f6e672f6857312747d29bcristy                        gamma+=(*k);
2917ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
2918ec9ace44c23c302f336f6e672f6857312747d29bcristy                    k--;
2919ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixels+=GetPixelChannels(image);
29208d18850dee4bed193a64866a6d2353eeeb73e145anthony                  }
2921ec9ace44c23c302f336f6e672f6857312747d29bcristy                  pixels+=image->columns*GetPixelChannels(image);
29228d18850dee4bed193a64866a6d2353eeeb73e145anthony                }
2923ec9ace44c23c302f336f6e672f6857312747d29bcristy                gamma=PerceptibleReciprocal(gamma);
2924ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixel*=gamma;
29258693d3f324310fc2ca933cd239aa053d0651e0b6cristy                break;
29268d18850dee4bed193a64866a6d2353eeeb73e145anthony              }
2927ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2928ec9ace44c23c302f336f6e672f6857312747d29bcristy              Alpha blending.
2929ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
2930ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->width; v++)
2931ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2932ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2933ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2934ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IsNaN(*k) == MagickFalse)
2935ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2936ec9ace44c23c302f336f6e672f6857312747d29bcristy                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2937ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel+=(*k)*alpha*pixels[i];
2938ec9ace44c23c302f336f6e672f6857312747d29bcristy                    gamma+=(*k)*alpha;
2939602ab9b30b644a78a4057da93d838a77391ec0acanthony                  }
2940ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
2941ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
2942602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
2943ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=image->columns*GetPixelChannels(image);
2944ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
2945ec9ace44c23c302f336f6e672f6857312747d29bcristy            gamma=PerceptibleReciprocal(gamma);
2946ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixel*=gamma;
2947602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
2948ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2949ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeMorphology:
2950ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2951ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2952ec9ace44c23c302f336f6e672f6857312747d29bcristy              Minimum value within kernel neighbourhood.
2953602ab9b30b644a78a4057da93d838a77391ec0acanthony
2954ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.  In normal
2955ec9ace44c23c302f336f6e672f6857312747d29bcristy              Greyscale Morphology, the kernel value should be added
2956ec9ace44c23c302f336f6e672f6857312747d29bcristy              to the real value, this is currently not done, due to the
2957ec9ace44c23c302f336f6e672f6857312747d29bcristy              nature of the boolean kernels being used.
2958930be614b4595b97cd79ee864a394796740f76adanthony            */
2959ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
2960ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
2961ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2962ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2963ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2964ec9ace44c23c302f336f6e672f6857312747d29bcristy                if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
2965ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2966ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((double) pixels[i] < pixel)
2967ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i];
2968ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
2969ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
2970ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
2971602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
2972ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=image->columns*GetPixelChannels(image);
2973602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
2974602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
2975ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2976ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateMorphology:
2977ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2978ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2979ec9ace44c23c302f336f6e672f6857312747d29bcristy               Maximum value within kernel neighbourhood.
2980ec9ace44c23c302f336f6e672f6857312747d29bcristy
2981ec9ace44c23c302f336f6e672f6857312747d29bcristy               For correct working of this operation for asymetrical kernels,
2982ec9ace44c23c302f336f6e672f6857312747d29bcristy               the kernel needs to be applied in its reflected form.  That is
2983ec9ace44c23c302f336f6e672f6857312747d29bcristy               its values needs to be reversed.
2984602ab9b30b644a78a4057da93d838a77391ec0acanthony
2985ec9ace44c23c302f336f6e672f6857312747d29bcristy               In normal Greyscale Morphology, the kernel value should be
2986ec9ace44c23c302f336f6e672f6857312747d29bcristy               added to the real value, this is currently not done, due to the
2987ec9ace44c23c302f336f6e672f6857312747d29bcristy               nature of the boolean kernels being used.
2988930be614b4595b97cd79ee864a394796740f76adanthony            */
2989ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
2990ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
2991ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2992ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2993ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2994ec9ace44c23c302f336f6e672f6857312747d29bcristy                if ((IsNaN(*k) == MagickFalse) && (*k > 0.5))
2995ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2996ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((double) pixels[i] > pixel)
2997ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i];
2998ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
2999ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3000ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3001602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3002ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=image->columns*GetPixelChannels(image);
3003602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3004602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3005ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3006ec9ace44c23c302f336f6e672f6857312747d29bcristy          case HitAndMissMorphology:
3007ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThinningMorphology:
3008ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThickenMorphology:
3009ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3010ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3011ec9ace44c23c302f336f6e672f6857312747d29bcristy               Minimum of foreground pixel minus maxumum of background pixels.
3012602ab9b30b644a78a4057da93d838a77391ec0acanthony
3013ec9ace44c23c302f336f6e672f6857312747d29bcristy               The kernel is not reflected for this operation, and consists
3014ec9ace44c23c302f336f6e672f6857312747d29bcristy               of both foreground and background pixel neighbourhoods, 0.0 for
3015ec9ace44c23c302f336f6e672f6857312747d29bcristy               background, and 1.0 for foreground with either Nan or 0.5 values
3016ec9ace44c23c302f336f6e672f6857312747d29bcristy               for don't care.
3017ec9ace44c23c302f336f6e672f6857312747d29bcristy
3018ec9ace44c23c302f336f6e672f6857312747d29bcristy               This never produces a meaningless negative result.  Such results
3019ec9ace44c23c302f336f6e672f6857312747d29bcristy               cause Thinning/Thicken to not work correctly when used against a
3020ec9ace44c23c302f336f6e672f6857312747d29bcristy               greyscale image.
30215ef8e94ff55717be2387d537bd49025780a1a558anthony            */
3022ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
3023ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3024ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3025ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3026ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3027ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IsNaN(*k) == MagickFalse)
3028ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3029ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (*k > 0.7)
3030ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3031ec9ace44c23c302f336f6e672f6857312747d29bcristy                        if ((double) pixels[i] < pixel)
3032ec9ace44c23c302f336f6e672f6857312747d29bcristy                          pixel=(double) pixels[i];
3033ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3034ec9ace44c23c302f336f6e672f6857312747d29bcristy                    else
3035ec9ace44c23c302f336f6e672f6857312747d29bcristy                      if (*k < 0.3)
3036ec9ace44c23c302f336f6e672f6857312747d29bcristy                        {
3037ec9ace44c23c302f336f6e672f6857312747d29bcristy                          if ((double) pixels[i] > maximum)
3038ec9ace44c23c302f336f6e672f6857312747d29bcristy                            maximum=(double) pixels[i];
3039ec9ace44c23c302f336f6e672f6857312747d29bcristy                        }
3040ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3041ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
3042ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
30435ef8e94ff55717be2387d537bd49025780a1a558anthony              }
3044ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=image->columns*GetPixelChannels(image);
30455ef8e94ff55717be2387d537bd49025780a1a558anthony            }
3046ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixel-=maximum;
3047ec9ace44c23c302f336f6e672f6857312747d29bcristy            if (pixel < 0.0)
3048ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixel=0.0;
3049ec9ace44c23c302f336f6e672f6857312747d29bcristy            if (method ==  ThinningMorphology)
3050ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixel=(double) p[center+i]-pixel;
3051ec9ace44c23c302f336f6e672f6857312747d29bcristy            else
3052ec9ace44c23c302f336f6e672f6857312747d29bcristy              if (method ==  ThickenMorphology)
3053ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixel+=(double) p[center+i]+pixel;
30545ef8e94ff55717be2387d537bd49025780a1a558anthony            break;
3055ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3056ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeIntensityMorphology:
3057ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3058ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3059ec9ace44c23c302f336f6e672f6857312747d29bcristy              Select pixel with minimum intensity within kernel neighbourhood.
30606fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3061ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.
3062930be614b4595b97cd79ee864a394796740f76adanthony            */
3063ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
3064ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3065ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3066ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3067ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3068ec9ace44c23c302f336f6e672f6857312747d29bcristy                if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
3069ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3070ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (GetPixelIntensity(image,pixels) < minimum)
3071ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3072ec9ace44c23c302f336f6e672f6857312747d29bcristy                        pixel=(double) pixels[i];
3073ec9ace44c23c302f336f6e672f6857312747d29bcristy                        minimum=GetPixelIntensity(image,pixels);
3074ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3075ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3076ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
3077ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3078602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3079ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=image->columns*GetPixelChannels(image);
3080602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3081602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3082ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3083ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateIntensityMorphology:
3084ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3085ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3086ec9ace44c23c302f336f6e672f6857312747d29bcristy              Select pixel with maximum intensity within kernel neighbourhood.
30876fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3088ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.
3089930be614b4595b97cd79ee864a394796740f76adanthony            */
3090ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3091ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3092ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3093ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3094ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3095ec9ace44c23c302f336f6e672f6857312747d29bcristy                if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
3096ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3097ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (GetPixelIntensity(image,pixels) > maximum)
3098ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3099ec9ace44c23c302f336f6e672f6857312747d29bcristy                        pixel=(double) pixels[i];
3100ec9ace44c23c302f336f6e672f6857312747d29bcristy                        maximum=GetPixelIntensity(image,pixels);
3101ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3102ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3103ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3104ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3105602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3106ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=image->columns*GetPixelChannels(image);
3107602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3108602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3109ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3110ec9ace44c23c302f336f6e672f6857312747d29bcristy          case IterativeDistanceMorphology:
3111ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3112ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3113ec9ace44c23c302f336f6e672f6857312747d29bcristy               Compute th iterative distance from black edge of a white image
3114ec9ace44c23c302f336f6e672f6857312747d29bcristy               shape.  Essentually white values are decreased to the smallest
3115ec9ace44c23c302f336f6e672f6857312747d29bcristy               'distance from edge' it can find.
31166fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3117ec9ace44c23c302f336f6e672f6857312747d29bcristy               It works by adding kernel values to the neighbourhood, and and
3118ec9ace44c23c302f336f6e672f6857312747d29bcristy               select the minimum value found. The kernel is rotated before
3119ec9ace44c23c302f336f6e672f6857312747d29bcristy               use, so kernel distances match resulting distances, when a user
3120ec9ace44c23c302f336f6e672f6857312747d29bcristy               provided asymmetric kernel is applied.
31216fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3122ec9ace44c23c302f336f6e672f6857312747d29bcristy               This code is nearly identical to True GrayScale Morphology but
3123ec9ace44c23c302f336f6e672f6857312747d29bcristy               not quite.
31246fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3125ec9ace44c23c302f336f6e672f6857312747d29bcristy               GreyDilate Kernel values added, maximum value found Kernel is
3126ec9ace44c23c302f336f6e672f6857312747d29bcristy               rotated before use.
31276fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3128ec9ace44c23c302f336f6e672f6857312747d29bcristy               GrayErode:  Kernel values subtracted and minimum value found No
3129ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernel rotation used.
31306fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3131ec9ace44c23c302f336f6e672f6857312747d29bcristy               Note the the Iterative Distance method is essentially a
3132ec9ace44c23c302f336f6e672f6857312747d29bcristy               GrayErode, but with negative kernel values, and kernel rotation
3133ec9ace44c23c302f336f6e672f6857312747d29bcristy               applied.
3134930be614b4595b97cd79ee864a394796740f76adanthony            */
3135ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3136ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3137ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3138ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3139ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3140ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IsNaN(*k) == MagickFalse)
3141ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3142ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3143ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3144ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3145ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3146ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3147602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3148ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=image->columns*GetPixelChannels(image);
3149602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3150602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3151ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3152ec9ace44c23c302f336f6e672f6857312747d29bcristy          case UndefinedMorphology:
3153ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3154ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3155ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
3156ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (fabs(pixel-p[center+i]) > MagickEpsilon)
3157ec9ace44c23c302f336f6e672f6857312747d29bcristy          changed++;
3158ec9ace44c23c302f336f6e672f6857312747d29bcristy        SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),q);
315983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      }
3160ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image);
3161ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(morphology_image);
3162ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
31634c08aed51c5899665ade97263692328eea4af106cristy    if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3164602ab9b30b644a78a4057da93d838a77391ec0acanthony      status=MagickFalse;
3165602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3166602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
3167602ab9b30b644a78a4057da93d838a77391ec0acanthony        MagickBooleanType
3168602ab9b30b644a78a4057da93d838a77391ec0acanthony          proceed;
3169602ab9b30b644a78a4057da93d838a77391ec0acanthony
3170602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
3171ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy        #pragma omp critical (MagickCore_MorphologyImage)
3172602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
3173602ab9b30b644a78a4057da93d838a77391ec0acanthony        proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
3174602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (proceed == MagickFalse)
3175602ab9b30b644a78a4057da93d838a77391ec0acanthony          status=MagickFalse;
3176602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
3177ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
31784c08aed51c5899665ade97263692328eea4af106cristy  morphology_view=DestroyCacheView(morphology_view);
31794c08aed51c5899665ade97263692328eea4af106cristy  image_view=DestroyCacheView(image_view);
3180a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  return(status ? (ssize_t)changed : -1);
3181602ab9b30b644a78a4057da93d838a77391ec0acanthony}
3182602ab9b30b644a78a4057da93d838a77391ec0acanthony
3183ec9ace44c23c302f336f6e672f6857312747d29bcristy/*
3184ec9ace44c23c302f336f6e672f6857312747d29bcristy  This is almost identical to the MorphologyPrimative() function above, but
3185ec9ace44c23c302f336f6e672f6857312747d29bcristy  applies the primitive directly to the actual image using two passes, once in
3186ec9ace44c23c302f336f6e672f6857312747d29bcristy  each direction, with the results of the previous (and current) row being
3187ec9ace44c23c302f336f6e672f6857312747d29bcristy  re-used.
3188ec9ace44c23c302f336f6e672f6857312747d29bcristy
3189ec9ace44c23c302f336f6e672f6857312747d29bcristy  That is after each row is 'Sync'ed' into the image, the next row makes use of
3190ec9ace44c23c302f336f6e672f6857312747d29bcristy  those values as part of the calculation of the next row.  It repeats, but
3191ec9ace44c23c302f336f6e672f6857312747d29bcristy  going in the oppisite (bottom-up) direction.
3192ec9ace44c23c302f336f6e672f6857312747d29bcristy
3193ec9ace44c23c302f336f6e672f6857312747d29bcristy  Because of this 're-use of results' this function can not make use of multi-
3194ec9ace44c23c302f336f6e672f6857312747d29bcristy  threaded, parellel processing.
3195a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3196e698a255629ba03cd125572de7b35b5e21c4ee5danthonystatic ssize_t MorphologyPrimitiveDirect(Image *image,
3197f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const KernelInfo *kernel,
3198f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3199a8843c1f815ffad2568ec592d5b446cb1476aab5anthony{
3200a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  CacheView
3201ec9ace44c23c302f336f6e672f6857312747d29bcristy    *morphology_view,
3202ec9ace44c23c302f336f6e672f6857312747d29bcristy    *image_view;
3203a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3204a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickBooleanType
3205a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    status;
3206a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3207a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickOffsetType
3208a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    progress;
3209a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3210ec9ace44c23c302f336f6e672f6857312747d29bcristy  OffsetInfo
3211ec9ace44c23c302f336f6e672f6857312747d29bcristy    offset;
3212a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3213a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
3214ec9ace44c23c302f336f6e672f6857312747d29bcristy    width,
3215a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;
3216a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3217ec9ace44c23c302f336f6e672f6857312747d29bcristy  ssize_t
3218ec9ace44c23c302f336f6e672f6857312747d29bcristy    y;
3219a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3220a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image != (Image *) NULL);
3221a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image->signature == MagickSignature);
3222a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel != (KernelInfo *) NULL);
3223a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel->signature == MagickSignature);
3224a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception != (ExceptionInfo *) NULL);
3225a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception->signature == MagickSignature);
3226ec9ace44c23c302f336f6e672f6857312747d29bcristy  status=MagickTrue;
3227ec9ace44c23c302f336f6e672f6857312747d29bcristy  changed=0;
3228ec9ace44c23c302f336f6e672f6857312747d29bcristy  progress=0;
3229ec9ace44c23c302f336f6e672f6857312747d29bcristy  switch(method)
3230ec9ace44c23c302f336f6e672f6857312747d29bcristy  {
3231a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3232e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3233ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3234ec9ace44c23c302f336f6e672f6857312747d29bcristy      /*
3235ec9ace44c23c302f336f6e672f6857312747d29bcristy        Kernel reflected about origin.
3236ec9ace44c23c302f336f6e672f6857312747d29bcristy      */
3237ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=(ssize_t) kernel->width-kernel->x-1;
3238ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=(ssize_t) kernel->height-kernel->y-1;
3239a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3240ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3241a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    default:
3242ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3243ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=kernel->x;
3244ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=kernel->y;
3245a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3246ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3247a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  }
3248ec9ace44c23c302f336f6e672f6857312747d29bcristy  /*
3249ec9ace44c23c302f336f6e672f6857312747d29bcristy    Two views into same image, do not thread.
3250ec9ace44c23c302f336f6e672f6857312747d29bcristy  */
3251ec9ace44c23c302f336f6e672f6857312747d29bcristy  image_view=AcquireVirtualCacheView(image,exception);
3252ec9ace44c23c302f336f6e672f6857312747d29bcristy  morphology_view=AcquireAuthenticCacheView(image,exception);
3253ec9ace44c23c302f336f6e672f6857312747d29bcristy  width=image->columns+kernel->width-1;
3254a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  for (y=0; y < (ssize_t) image->rows; y++)
3255a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
32564c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
3257a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict p;
3258a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
32594c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
3260a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict q;
3261a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3262a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3263a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3264a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3265ec9ace44c23c302f336f6e672f6857312747d29bcristy    /*
3266ec9ace44c23c302f336f6e672f6857312747d29bcristy      Read virtual pixels, and authentic pixels, from the same image!  We read
3267ec9ace44c23c302f336f6e672f6857312747d29bcristy      using virtual to get virtual pixel handling, but write back into the same
3268ec9ace44c23c302f336f6e672f6857312747d29bcristy      image.
3269ec9ace44c23c302f336f6e672f6857312747d29bcristy
3270ec9ace44c23c302f336f6e672f6857312747d29bcristy      Only top half of kernel is processed as we do a single pass downward
3271ec9ace44c23c302f336f6e672f6857312747d29bcristy      through the image iterating the distance function as we go.
3272a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    */
3273a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3274a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3275ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(size_t)
3276ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y+1,exception);
32776fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
32784c08aed51c5899665ade97263692328eea4af106cristy      exception);
32794c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3280a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3281a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3282a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3283a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    for (x=0; x < (ssize_t) image->columns; x++)
3284a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    {
3285ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
3286ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
3287a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3288ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3289ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3290ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
3291ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
3292a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3293ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
3294ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
3295ec9ace44c23c302f336f6e672f6857312747d29bcristy
3296ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
3297ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
3298ec9ace44c23c302f336f6e672f6857312747d29bcristy
3299ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
3300ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
3301ec9ace44c23c302f336f6e672f6857312747d29bcristy
3302ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
3303ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
3304ec9ace44c23c302f336f6e672f6857312747d29bcristy
3305ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
3306ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
3307ec9ace44c23c302f336f6e672f6857312747d29bcristy
33086fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        traits=GetPixelChannelTraits(image,i);
3309ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (traits == UndefinedPixelTrait)
3310ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3311ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (((traits & CopyPixelTrait) != 0) || (GetPixelMask(image,p) != 0))
3312ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3313ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
33146fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        pixel=(double) q[i];
3315ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
3316ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
3317ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DistanceMorphology:
3318ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3319ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3320ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v <= offset.y; v++)
3321ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3322ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3323ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3324ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IsNaN(*k) == MagickFalse)
3325e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3326ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3327ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3328e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3329ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3330ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3331e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3332ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=width*GetPixelChannels(image);
3333ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3334ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3335ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3336ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=0; u < offset.x; u++)
3337ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
33386fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              if ((IsNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
3339ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3340ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3341ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3342ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3343ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3344ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3345e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3346ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3347ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3348ec9ace44c23c302f336f6e672f6857312747d29bcristy          case VoronoiMorphology:
3349ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3350ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3351ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < offset.y; v++)
3352ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3353ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3354ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3355ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IsNaN(*k) == MagickFalse)
3356e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3357ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3358ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3359e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3360ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3361ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3362e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3363ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=width*GetPixelChannels(image);
3364ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3365ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3366ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3367ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=0; u < offset.x; u++)
3368ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
33696fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              if ((IsNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
3370ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3371ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3372ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3373ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3374ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3375ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3376ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3377e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3378ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3379ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3380ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3381ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
33826fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (fabs(pixel-q[i]) > MagickEpsilon)
3383ec9ace44c23c302f336f6e672f6857312747d29bcristy          changed++;
33846fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        q[i]=ClampToQuantum(pixel);
3385e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3386ec9ace44c23c302f336f6e672f6857312747d29bcristy      p+=GetPixelChannels(image);
3387ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(image);
3388ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
33896fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3390a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3391a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3392ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3393ec9ace44c23c302f336f6e672f6857312747d29bcristy        MagickBooleanType
3394ec9ace44c23c302f336f6e672f6857312747d29bcristy          proceed;
3395a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3396ec9ace44c23c302f336f6e672f6857312747d29bcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
3397ec9ace44c23c302f336f6e672f6857312747d29bcristy        #pragma omp critical (MagickCore_MorphologyImage)
3398ec9ace44c23c302f336f6e672f6857312747d29bcristy#endif
33996fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
3400ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (proceed == MagickFalse)
3401ec9ace44c23c302f336f6e672f6857312747d29bcristy          status=MagickFalse;
3402ec9ace44c23c302f336f6e672f6857312747d29bcristy      }
3403ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
34046fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  morphology_view=DestroyCacheView(morphology_view);
34056fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  image_view=DestroyCacheView(image_view);
3406ec9ace44c23c302f336f6e672f6857312747d29bcristy  /*
3407ec9ace44c23c302f336f6e672f6857312747d29bcristy    Do the reverse pass through the image.
3408ec9ace44c23c302f336f6e672f6857312747d29bcristy  */
34096fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  image_view=AcquireVirtualCacheView(image,exception);
34106fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  morphology_view=AcquireAuthenticCacheView(image,exception);
3411ec9ace44c23c302f336f6e672f6857312747d29bcristy  for (y=(ssize_t) image->rows-1; y >= 0; y--)
3412a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
34134c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
3414a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict p;
3415a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34164c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
3417a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict q;
3418a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3419a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3420a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3421a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3422ec9ace44c23c302f336f6e672f6857312747d29bcristy    /*
3423ec9ace44c23c302f336f6e672f6857312747d29bcristy       Read virtual pixels, and authentic pixels, from the same image.  We
3424ec9ace44c23c302f336f6e672f6857312747d29bcristy       read using virtual to get virtual pixel handling, but write back
3425ec9ace44c23c302f336f6e672f6857312747d29bcristy       into the same image.
3426a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3427ec9ace44c23c302f336f6e672f6857312747d29bcristy       Only the bottom half of the kernel is processed as we up the image.
3428ec9ace44c23c302f336f6e672f6857312747d29bcristy    */
3429db60568e12574785101a4ae8d8da076227a0a889anthony    if (status == MagickFalse)
3430db60568e12574785101a4ae8d8da076227a0a889anthony      break;
3431ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(size_t)
34324c08aed51c5899665ade97263692328eea4af106cristy      kernel->y+1,exception);
34336fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
34344c08aed51c5899665ade97263692328eea4af106cristy      exception);
34354c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3436a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3437a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3438a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3439ec9ace44c23c302f336f6e672f6857312747d29bcristy    p+=(image->columns-1)*GetPixelChannels(image);
3440ec9ace44c23c302f336f6e672f6857312747d29bcristy    q+=(image->columns-1)*GetPixelChannels(image);
34416fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    for (x=(ssize_t) image->columns-1; x >= 0; x--)
3442ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3443ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
3444ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
3445a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3446ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3447ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3448ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
3449ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
3450a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3451ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
3452ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
3453a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3454ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
3455ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
3456a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3457ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
3458ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
3459a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3460ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
3461ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
3462ec9ace44c23c302f336f6e672f6857312747d29bcristy
3463ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
3464ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
3465ec9ace44c23c302f336f6e672f6857312747d29bcristy
34666fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        traits=GetPixelChannelTraits(image,i);
3467ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (traits == UndefinedPixelTrait)
3468ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3469ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (((traits & CopyPixelTrait) != 0) || (GetPixelMask(image,p) != 0))
3470ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3471ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
34726fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        pixel=(double) q[i];
3473ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
3474ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
3475ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DistanceMorphology:
3476ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3477ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3478ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=offset.y; v < (ssize_t) kernel->height; v++)
3479ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3480ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3481ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3482ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IsNaN(*k) == MagickFalse)
3483e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3484ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3485ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3486e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3487ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3488ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3489e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3490ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=width*GetPixelChannels(image);
3491e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3492ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
3493ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3494ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3495ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
34966fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              if ((IsNaN(*k) == MagickFalse) &&
34976fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                  ((x+u-offset.x) < (ssize_t) image->columns))
3498ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3499ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3500ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3501ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3502ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3503ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3504ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3505ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3506ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3507ec9ace44c23c302f336f6e672f6857312747d29bcristy          case VoronoiMorphology:
3508ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3509ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3510ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=offset.y; v < (ssize_t) kernel->height; v++)
3511ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3512ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3513ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3514ec9ace44c23c302f336f6e672f6857312747d29bcristy                if (IsNaN(*k) == MagickFalse)
3515e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3516ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3517ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3518e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3519ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3520ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3521e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3522ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=width*GetPixelChannels(image);
3523ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3524ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3525ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3526ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3527ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
35286fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy              if ((IsNaN(*k) == MagickFalse) &&
35296fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                  ((x+u-offset.x) < (ssize_t) image->columns))
3530ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3531ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3532ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3533ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3534ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3535ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3536ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3537e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3538ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3539ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3540ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3541ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
35426fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (fabs(pixel-q[i]) > MagickEpsilon)
3543ec9ace44c23c302f336f6e672f6857312747d29bcristy          changed++;
35446fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        q[i]=ClampToQuantum(pixel);
3545e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
35466fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      p-=GetPixelChannels(image);
35476fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      q-=GetPixelChannels(image);
3548ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
35496fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
35506fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      status=MagickFalse;
35516fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
35526fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      {
35536fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        MagickBooleanType
35546fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy          proceed;
35556fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
35566fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
35576fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        #pragma omp critical (MagickCore_MorphologyImage)
35586fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy#endif
35596fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
35606fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (proceed == MagickFalse)
35616fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy          status=MagickFalse;
35626fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      }
3563ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
3564ec9ace44c23c302f336f6e672f6857312747d29bcristy  morphology_view=DestroyCacheView(morphology_view);
3565ec9ace44c23c302f336f6e672f6857312747d29bcristy  image_view=DestroyCacheView(image_view);
3566aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  return(status ? (ssize_t) changed : -1);
3567a8843c1f815ffad2568ec592d5b446cb1476aab5anthony}
3568a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3569ec9ace44c23c302f336f6e672f6857312747d29bcristy/*
3570ec9ace44c23c302f336f6e672f6857312747d29bcristy  Apply a Morphology by calling one of the above low level primitive
3571ec9ace44c23c302f336f6e672f6857312747d29bcristy  application functions.  This function handles any iteration loops,
3572ec9ace44c23c302f336f6e672f6857312747d29bcristy  composition or re-iteration of results, and compound morphology methods that
3573ec9ace44c23c302f336f6e672f6857312747d29bcristy  is based on multiple low-level (staged) morphology methods.
3574ec9ace44c23c302f336f6e672f6857312747d29bcristy
3575ec9ace44c23c302f336f6e672f6857312747d29bcristy  Basically this provides the complex glue between the requested morphology
3576ec9ace44c23c302f336f6e672f6857312747d29bcristy  method and raw low-level implementation (above).
3577a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3578cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate Image *MorphologyApply(const Image *image,
3579f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method, const ssize_t iterations,
3580f46d42620631d2581e0b6a56456e203e17c427c8anthony  const KernelInfo *kernel, const CompositeOperator compose,const double bias,
3581f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3582602ab9b30b644a78a4057da93d838a77391ec0acanthony{
35831cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  CompositeOperator
35841cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy    curr_compose;
35851cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy
3586602ab9b30b644a78a4057da93d838a77391ec0acanthony  Image
358747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *curr_image,    /* Image we are working with or iterating */
3588a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *work_image,    /* secondary image for primitive iteration */
358947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *save_image,    /* saved image - for 'edge' method only */
359047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rslt_image;    /* resultant image - after multi-kernel handling */
3591602ab9b30b644a78a4057da93d838a77391ec0acanthony
35924fd27e21043be809d66c8202e779255e5b660d2danthony  KernelInfo
359347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *reflected_kernel, /* A reflected copy of the kernel (if needed) */
359447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *norm_kernel,      /* the current normal un-reflected kernel */
359547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rflt_kernel,      /* the current reflected kernel (if needed) */
359647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *this_kernel;      /* the kernel being applied */
35974fd27e21043be809d66c8202e779255e5b660d2danthony
35984fd27e21043be809d66c8202e779255e5b660d2danthony  MorphologyMethod
3599a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    primitive;      /* the current morphology primitive being applied */
36009eb4f74649b23c053b308ce1152dce51239450baanthony
36019eb4f74649b23c053b308ce1152dce51239450baanthony  CompositeOperator
360247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose;   /* multi-kernel compose method for results to use */
360347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
360447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  MagickBooleanType
3605e698a255629ba03cd125572de7b35b5e21c4ee5danthony    special,        /* do we use a direct modify function? */
360647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    verbose;        /* verbose output of results */
36074fd27e21043be809d66c8202e779255e5b660d2danthony
3608bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
3609a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    method_loop,    /* Loop 1: number of compound method iterations (norm 1) */
361047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_limit,   /*         maximum number of compound method iterations */
361147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number,  /* Loop 2: the kernel number being applied */
3612a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_loop,     /* Loop 3: primitive loop for compound morphology */
3613a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_limit,    /*         how many primitives are in this compound */
3614a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    kernel_loop,    /* Loop 4: iterate the kernel over image */
361547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_limit,   /*         number of times to iterate kernel */
3616a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    count,          /* total count of primitive steps applied */
361747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_changed, /* total count of changed using iterated kernel */
361847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed; /* total count of changed over method iteration */
361947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3620a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  ssize_t
3621a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;        /* number pixels changed by last primitive operation */
3622a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
362347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  char
362447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    v_info[80];
36251b2bc0a7da432e6e1cc0480280402df213faa940anthony
3626602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image != (Image *) NULL);
3627602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image->signature == MagickSignature);
36284fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
36294fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel->signature == MagickSignature);
3630602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception != (ExceptionInfo *) NULL);
3631602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception->signature == MagickSignature);
3632602ab9b30b644a78a4057da93d838a77391ec0acanthony
3633a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  count = 0;      /* number of low-level morphology primitives performed */
3634602ab9b30b644a78a4057da93d838a77391ec0acanthony  if ( iterations == 0 )
363547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return((Image *)NULL);   /* null operation - nothing to do! */
3636602ab9b30b644a78a4057da93d838a77391ec0acanthony
3637bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  kernel_limit = (size_t) iterations;
363847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
3639e698a255629ba03cd125572de7b35b5e21c4ee5danthony     kernel_limit = image->columns>image->rows ? image->columns : image->rows;
364028ad1d779b6ca95852e860514185a7a97e06af77anthony
36416f2013165d72f7d8ef5f66bb9453126d88113809anthony  verbose = IsStringTrue(GetImageArtifact(image,"verbose"));
36424f1dcb76c95ef6410f2957ca9e7e1d391cee0d02anthony
36439eb4f74649b23c053b308ce1152dce51239450baanthony  /* initialise for cleanup */
364447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  curr_image = (Image *) image;
36451cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  curr_compose = image->compose;
3646aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  (void) curr_compose;
364747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  work_image = save_image = rslt_image = (Image *) NULL;
364847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  reflected_kernel = (KernelInfo *) NULL;
36494fd27e21043be809d66c8202e779255e5b660d2danthony
365047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Initialize specific methods
365147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + which loop should use the given iteratations
3652a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   * + how many primitives make up the compound morphology
365347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + multi-kernel compose method to use (by default)
365447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
365547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_limit = 1;       /* just do method once, unless otherwise set */
3656ea61f01656bb0f9074677452017cc559e54093faanthony  stage_limit = 1;        /* assume method is not a compound */
36574ee950098ad0166bbbc85c3a59bc079cd321384aglennrp  special = MagickFalse;   /* assume it is NOT a direct modify primitive */
365847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  rslt_compose = compose; /* and we are composing multi-kernels as given */
36599eb4f74649b23c053b308ce1152dce51239450baanthony  switch( method ) {
3660a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case SmoothMorphology:  /* 4 primitive compound morphology */
366147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 4;
3662602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
3663a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case OpenMorphology:    /* 2 primitive compound morphology */
3664930be614b4595b97cd79ee864a394796740f76adanthony    case OpenIntensityMorphology:
366547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case TopHatMorphology:
366647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
36674fd27e21043be809d66c8202e779255e5b660d2danthony    case CloseIntensityMorphology:
366847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
366947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case EdgeMorphology:
367047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 2;
3671602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
36729eb4f74649b23c053b308ce1152dce51239450baanthony    case HitAndMissMorphology:
367347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
36743ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      /* FALL THUR */
3675c3e48258f3253188894e783dcdfd03562f7ab2c5anthony    case ThinningMorphology:
36769eb4f74649b23c053b308ce1152dce51239450baanthony    case ThickenMorphology:
36773ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      method_limit = kernel_limit;  /* iterate the whole method */
3678c3e48258f3253188894e783dcdfd03562f7ab2c5anthony      kernel_limit = 1;             /* do not do kernel iteration  */
367947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
3680a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3681e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3682f34d9b2df49a407af764c79e07d587af0600983aanthony      special = MagickTrue;         /* use special direct primative */
3683a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
368447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
36859eb4f74649b23c053b308ce1152dce51239450baanthony      break;
36869eb4f74649b23c053b308ce1152dce51239450baanthony  }
3687602ab9b30b644a78a4057da93d838a77391ec0acanthony
3688e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Apply special methods with special requirments
3689e698a255629ba03cd125572de7b35b5e21c4ee5danthony  ** For example, single run only, or post-processing requirements
3690e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
3691e698a255629ba03cd125572de7b35b5e21c4ee5danthony  if ( special == MagickTrue )
3692e698a255629ba03cd125572de7b35b5e21c4ee5danthony    {
3693e698a255629ba03cd125572de7b35b5e21c4ee5danthony      rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3694e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if (rslt_image == (Image *) NULL)
3695e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3696574cc26500992189f637cd1cdf93d0654e7df7aecristy      if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3697574cc26500992189f637cd1cdf93d0654e7df7aecristy        goto error_cleanup;
3698e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3699e698a255629ba03cd125572de7b35b5e21c4ee5danthony      changed = MorphologyPrimitiveDirect(rslt_image, method,
3700f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy         kernel, exception);
3701e698a255629ba03cd125572de7b35b5e21c4ee5danthony
37027bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony      if ( IfMagickTrue(verbose) )
37035acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) (void) FormatLocaleFile(stderr,
37041e604812fad85bb96f757a2393015ae3d061c39acristy          "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
37051e604812fad85bb96f757a2393015ae3d061c39acristy          CommandOptionToMnemonic(MagickMorphologyOptions, method),
37061e604812fad85bb96f757a2393015ae3d061c39acristy          1.0,0.0,1.0, (double) changed);
3707e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3708e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( changed < 0 )
3709e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3710e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3711e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method == VoronoiMorphology ) {
3712e698a255629ba03cd125572de7b35b5e21c4ee5danthony        /* Preserve the alpha channel of input image - but turned off */
371363240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
371463240888c3975789a09c2494a4654b523931df96cristy          exception);
3715feb3e9695150978a5d2372d3fe2f60466a7c8066cristy        (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
371639172408bad7ef2ef00a815fa9abf9979e7857cbcristy          MagickTrue,0,0,exception);
371763240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
371863240888c3975789a09c2494a4654b523931df96cristy          exception);
3719e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3720e698a255629ba03cd125572de7b35b5e21c4ee5danthony      goto exit_cleanup;
3721e698a255629ba03cd125572de7b35b5e21c4ee5danthony    }
3722e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3723c3e48258f3253188894e783dcdfd03562f7ab2c5anthony  /* Handle user (caller) specified multi-kernel composition method */
372447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( compose != UndefinedCompositeOp )
372547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = compose;  /* override default composition for method */
372647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_compose == UndefinedCompositeOp )
372747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
37284fd27e21043be809d66c8202e779255e5b660d2danthony
3729a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Some methods require a reflected kernel to use with primitives.
3730c3e48258f3253188894e783dcdfd03562f7ab2c5anthony   * Create the reflected kernel for those methods. */
373147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  switch ( method ) {
373247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CorrelateMorphology:
373347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
373447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseIntensityMorphology:
373547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
373647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case SmoothMorphology:
373747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      reflected_kernel = CloneKernelInfo(kernel);
373847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if (reflected_kernel == (KernelInfo *) NULL)
373947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        goto error_cleanup;
374047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      RotateKernelInfo(reflected_kernel,180);
374147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
374247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
374347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
374447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
3745602ab9b30b644a78a4057da93d838a77391ec0acanthony
3746e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Loops around more primitive morpholgy methods
3747e698a255629ba03cd125572de7b35b5e21c4ee5danthony  **  erose, dilate, open, close, smooth, edge, etc...
3748e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
374947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Loop 1:  iterate the compound method */
375047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_loop = 0;
375147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_changed = 1;
375247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while ( method_loop < method_limit && method_changed > 0 ) {
375347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_loop++;
375447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed = 0;
375547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
375647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    /* Loop 2:  iterate over each kernel in a multi-kernel list */
375747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    norm_kernel = (KernelInfo *) kernel;
3758f2faecf9facdbbb14fcba373365f9f691a9658e0cristy    this_kernel = (KernelInfo *) kernel;
375947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rflt_kernel = reflected_kernel;
3760e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony
376147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number = 0;
376247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    while ( norm_kernel != NULL ) {
376347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
376447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* Loop 3: Compound Morphology Staging - Select Primative to apply */
376547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_loop = 0;          /* the compound morphology stage number */
376647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      while ( stage_loop < stage_limit ) {
376747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        stage_loop++;   /* The stage of the compound morphology */
376847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3769a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Select primitive morphology for this stage of compound method */
377047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        this_kernel = norm_kernel; /* default use unreflected kernel */
3771a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        primitive = method;        /* Assume method is a primitive */
377247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        switch( method ) {
377347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case ErodeMorphology:      /* just erode */
377447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeInMorphology:     /* erode and image difference */
3775a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
377647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
377747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case DilateMorphology:     /* just dilate */
377847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeOutMorphology:    /* dilate and image difference */
3779a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
378047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
378147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenMorphology:       /* erode then dialate */
378247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case TopHatMorphology:     /* open and image difference */
3783a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
378447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3785a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateMorphology;
378647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
378747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenIntensityMorphology:
3788a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeIntensityMorphology;
378947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3790a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateIntensityMorphology;
3791e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony            break;
379247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseMorphology:      /* dilate, then erode */
379347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case BottomHatMorphology:  /* close and image difference */
379447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3795a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
379647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3797a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
379847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
379947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseIntensityMorphology:
380047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3801a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateIntensityMorphology;
380247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3803a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeIntensityMorphology;
380447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
380547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case SmoothMorphology:         /* open, close */
380647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            switch ( stage_loop ) {
380747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 1: /* start an open method, which starts with Erode */
3808a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
380947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
381047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 2:  /* now Dilate the Erode */
3811a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
381247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
381347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 3:  /* Reflect kernel a close */
381447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
3815a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
381647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
381747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 4:  /* Finish the Close */
381847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
3819a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
382047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
382147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
382247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
382347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeMorphology:        /* dilate and erode difference */
3824a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
382547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 ) {
382647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              save_image = curr_image;      /* save the image difference */
382747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              curr_image = (Image *) image;
3828a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
382947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
383047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
383147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CorrelateMorphology:
383247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            /* A Correlation is a Convolution with a reflected kernel.
383347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** However a Convolution is a weighted sum using a reflected
383447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel.  It may seem stange to convert a Correlation into a
383547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution as the Correlation is the simplier method, but
383647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution is much more commonly used, and it makes sense to
383747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** implement it directly so as to avoid the need to duplicate the
383847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel when it is not required (which is typically the
383947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** default).
384047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            */
384147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3842a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ConvolveMorphology;
384347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
384447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          default:
384547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
384647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
3847e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony        assert( this_kernel != (KernelInfo *) NULL );
38487a01dcf50ce12cb2a789bedff51e9345f022432eanthony
384947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        /* Extra information for debugging compound operations */
38507bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) ) {
385147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( stage_limit > 1 )
3852b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            (void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
3853042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy             CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
3854e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy             method_loop,(double) stage_loop);
3855a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          else if ( primitive != method )
3856b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            (void) FormatLocaleString(v_info, MaxTextExtent, "%s:%.20g -> ",
3857042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
3858e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              method_loop);
385947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          else
386047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            v_info[0] = '\0';
386147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
38629eb4f74649b23c053b308ce1152dce51239450baanthony
3863a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Loop 4: Iterate the kernel with primitive */
386447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_loop = 0;
386547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_changed = 0;
386647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        changed = 1;
386747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        while ( kernel_loop < kernel_limit && changed > 0 ) {
386847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          kernel_loop++;     /* the iteration of this kernel */
38699eb4f74649b23c053b308ce1152dce51239450baanthony
3870a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* Create a clone as the destination image, if not yet defined */
38719eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == (Image *) NULL )
38729eb4f74649b23c053b308ce1152dce51239450baanthony            {
38739eb4f74649b23c053b308ce1152dce51239450baanthony              work_image=CloneImage(image,0,0,MagickTrue,exception);
38749eb4f74649b23c053b308ce1152dce51239450baanthony              if (work_image == (Image *) NULL)
38759eb4f74649b23c053b308ce1152dce51239450baanthony                goto error_cleanup;
3876574cc26500992189f637cd1cdf93d0654e7df7aecristy              if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
3877574cc26500992189f637cd1cdf93d0654e7df7aecristy                goto error_cleanup;
3878db60568e12574785101a4ae8d8da076227a0a889anthony              /* work_image->type=image->type; ??? */
38799eb4f74649b23c053b308ce1152dce51239450baanthony            }
38809eb4f74649b23c053b308ce1152dce51239450baanthony
3881501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
38829eb4f74649b23c053b308ce1152dce51239450baanthony          count++;
3883e698a255629ba03cd125572de7b35b5e21c4ee5danthony          changed = MorphologyPrimitive(curr_image, work_image, primitive,
3884f46d42620631d2581e0b6a56456e203e17c427c8anthony                       this_kernel, bias, exception);
388547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
38867bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) ) {
388747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( kernel_loop > 1 )
38885acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
38895acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) (void) FormatLocaleFile(stderr,
38901e604812fad85bb96f757a2393015ae3d061c39acristy              "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
3891042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
3892a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive),(this_kernel == rflt_kernel ) ? "*" : "",
3893e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) (method_loop+kernel_loop-1),(double) kernel_number,
3894e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) count,(double) changed);
389547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          }
3896a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          if ( changed < 0 )
3897a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            goto error_cleanup;
3898a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          kernel_changed += changed;
3899a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          method_changed += changed;
3900a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
39019eb4f74649b23c053b308ce1152dce51239450baanthony          /* prepare next loop */
39029eb4f74649b23c053b308ce1152dce51239450baanthony          { Image *tmp = work_image;   /* swap images for iteration */
39039eb4f74649b23c053b308ce1152dce51239450baanthony            work_image = curr_image;
39049eb4f74649b23c053b308ce1152dce51239450baanthony            curr_image = tmp;
39059eb4f74649b23c053b308ce1152dce51239450baanthony          }
39069eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == image )
390747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            work_image = (Image *) NULL; /* replace input 'image' */
39089eb4f74649b23c053b308ce1152dce51239450baanthony
3909a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        } /* End Loop 4: Iterate the kernel with primitive */
39107a01dcf50ce12cb2a789bedff51e9345f022432eanthony
39117bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) && kernel_changed != (size_t)changed )
39125acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
39137bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) && stage_loop < stage_limit )
39145acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
39159eb4f74649b23c053b308ce1152dce51239450baanthony
391647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#if 0
39175acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
39185acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
39195acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      work =0x%lx\n", (unsigned long)work_image);
39205acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      save =0x%lx\n", (unsigned long)save_image);
39215acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
392247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#endif
39231b2bc0a7da432e6e1cc0480280402df213faa940anthony
392447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
39251b2bc0a7da432e6e1cc0480280402df213faa940anthony
392647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /*  Final Post-processing for some Compound Methods
392747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      **
392847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** The removal of any 'Sync' channel flag in the Image Compositon
392947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** below ensures the methematical compose method is applied in a
393047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** purely mathematical way, and only to the selected channels.
393147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** Turn off SVG composition 'alpha blending'.
393247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      */
393347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      switch( method ) {
393447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeOutMorphology:
393547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeInMorphology:
393647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case TopHatMorphology:
393747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case BottomHatMorphology:
39387bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
3939e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy            (void) FormatLocaleFile(stderr,
3940e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              "\n%s: Difference with original image",CommandOptionToMnemonic(
3941e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              MagickMorphologyOptions, method) );
3942feb3e9695150978a5d2372d3fe2f60466a7c8066cristy          (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
394339172408bad7ef2ef00a815fa9abf9979e7857cbcristy            MagickTrue,0,0,exception);
394447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
394547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeMorphology:
39467bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
3947e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy            (void) FormatLocaleFile(stderr,
3948e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
3949e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              MagickMorphologyOptions, method) );
3950feb3e9695150978a5d2372d3fe2f60466a7c8066cristy          (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
395139172408bad7ef2ef00a815fa9abf9979e7857cbcristy            MagickTrue,0,0,exception);
395247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          save_image = DestroyImage(save_image); /* finished with save image */
395347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
395447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        default:
395547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
3956602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
39579eb4f74649b23c053b308ce1152dce51239450baanthony
395847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* multi-kernel handling:  re-iterate, or compose results */
395947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( kernel->next == (KernelInfo *) NULL )
3960c3e48258f3253188894e783dcdfd03562f7ab2c5anthony        rslt_image = curr_image;   /* just return the resulting image */
396147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_compose == NoCompositeOp )
39627bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        { if ( IfMagickTrue(verbose) ) {
3963c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            if ( this_kernel->next != (KernelInfo *) NULL )
39645acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (re-iterate)");
3965c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            else
39665acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (done)");
3967c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          }
3968c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          rslt_image = curr_image; /* return result, and re-iterate */
39699eb4f74649b23c053b308ce1152dce51239450baanthony        }
397047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_image == (Image *) NULL)
39717bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        { if ( IfMagickTrue(verbose) )
39725acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (save for compose)");
397347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          rslt_image = curr_image;
397447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
39759eb4f74649b23c053b308ce1152dce51239450baanthony        }
397647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else
3977ea61f01656bb0f9074677452017cc559e54093faanthony        { /* Add the new 'current' result to the composition
397847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          **
397947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** The removal of any 'Sync' channel flag in the Image Compositon
398047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** below ensures the methematical compose method is applied in a
398147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** purely mathematical way, and only to the selected channels.
3982ea61f01656bb0f9074677452017cc559e54093faanthony          ** IE: Turn off SVG composition 'alpha blending'.
398347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          */
39847bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
39855acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (compose \"%s\")",
3986feb3e9695150978a5d2372d3fe2f60466a7c8066cristy              CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
398739172408bad7ef2ef00a815fa9abf9979e7857cbcristy          (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
3988feb3e9695150978a5d2372d3fe2f60466a7c8066cristy            0,0,exception);
39890bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony          curr_image = DestroyImage(curr_image);
399047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
399147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
39927bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony      if ( IfMagickTrue(verbose) )
39935acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "\n");
39944fd27e21043be809d66c8202e779255e5b660d2danthony
399547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* loop to the next kernel in a multi-kernel list */
399647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      norm_kernel = norm_kernel->next;
399747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( rflt_kernel != (KernelInfo *) NULL )
399847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        rflt_kernel = rflt_kernel->next;
399947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      kernel_number++;
400047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    } /* End Loop 2: Loop over each kernel */
40019eb4f74649b23c053b308ce1152dce51239450baanthony
400247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  } /* End Loop 1: compound method interation */
4003602ab9b30b644a78a4057da93d838a77391ec0acanthony
40049eb4f74649b23c053b308ce1152dce51239450baanthony  goto exit_cleanup;
40051b2bc0a7da432e6e1cc0480280402df213faa940anthony
400647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Yes goto's are bad, but it makes cleanup lot more efficient */
40071b2bc0a7da432e6e1cc0480280402df213faa940anthonyerror_cleanup:
4008ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image )
4009ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
401047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_image != (Image *) NULL )
401147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_image = DestroyImage(rslt_image);
40121b2bc0a7da432e6e1cc0480280402df213faa940anthonyexit_cleanup:
4013ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image || curr_image == image )
4014ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
4015ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image != (Image *) NULL )
401647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    curr_image = DestroyImage(curr_image);
40179eb4f74649b23c053b308ce1152dce51239450baanthony  if ( work_image != (Image *) NULL )
401847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    work_image = DestroyImage(work_image);
40199eb4f74649b23c053b308ce1152dce51239450baanthony  if ( save_image != (Image *) NULL )
402047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    save_image = DestroyImage(save_image);
402147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( reflected_kernel != (KernelInfo *) NULL )
402247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    reflected_kernel = DestroyKernelInfo(reflected_kernel);
402347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return(rslt_image);
40249eb4f74649b23c053b308ce1152dce51239450baanthony}
4025a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
40269eb4f74649b23c053b308ce1152dce51239450baanthony
40279eb4f74649b23c053b308ce1152dce51239450baanthony/*
40289eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40299eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40309eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40319eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
4032f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%     M o r p h o l o g y I m a g e                                           %
40339eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40349eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40359eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40369eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40379eb4f74649b23c053b308ce1152dce51239450baanthony%
4038f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  MorphologyImage() applies a user supplied kernel to the image
40399eb4f74649b23c053b308ce1152dce51239450baanthony%  according to the given mophology method.
40409eb4f74649b23c053b308ce1152dce51239450baanthony%
40419eb4f74649b23c053b308ce1152dce51239450baanthony%  This function applies any and all user defined settings before calling
40429eb4f74649b23c053b308ce1152dce51239450baanthony%  the above internal function MorphologyApply().
40439eb4f74649b23c053b308ce1152dce51239450baanthony%
40449eb4f74649b23c053b308ce1152dce51239450baanthony%  User defined settings include...
404522de2722b682eb405b60ec6022a7546df994674eanthony%    * Output Bias for Convolution and correlation ("-define convolve:bias=??")
404622de2722b682eb405b60ec6022a7546df994674eanthony%    * Kernel Scale/normalize settings             ("-define convolve:scale=??")
404746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      This can also includes the addition of a scaled unity kernel.
404822de2722b682eb405b60ec6022a7546df994674eanthony%    * Show Kernel being applied                   ("-define showkernel=1")
404922de2722b682eb405b60ec6022a7546df994674eanthony%
405022de2722b682eb405b60ec6022a7546df994674eanthony%  Other operators that do not want user supplied options interfering,
405122de2722b682eb405b60ec6022a7546df994674eanthony%  especially "convolve:bias" and "showkernel" should use MorphologyApply()
405222de2722b682eb405b60ec6022a7546df994674eanthony%  directly.
40539eb4f74649b23c053b308ce1152dce51239450baanthony%
40549eb4f74649b23c053b308ce1152dce51239450baanthony%  The format of the MorphologyImage method is:
40559eb4f74649b23c053b308ce1152dce51239450baanthony%
40569eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyImage(const Image *image,MorphologyMethod method,
4057bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
40589eb4f74649b23c053b308ce1152dce51239450baanthony%
40599eb4f74649b23c053b308ce1152dce51239450baanthony%  A description of each parameter follows:
40609eb4f74649b23c053b308ce1152dce51239450baanthony%
40619eb4f74649b23c053b308ce1152dce51239450baanthony%    o image: the image.
40629eb4f74649b23c053b308ce1152dce51239450baanthony%
40639eb4f74649b23c053b308ce1152dce51239450baanthony%    o method: the morphology method to be applied.
40649eb4f74649b23c053b308ce1152dce51239450baanthony%
40659eb4f74649b23c053b308ce1152dce51239450baanthony%    o iterations: apply the operation this many times (or no change).
40669eb4f74649b23c053b308ce1152dce51239450baanthony%                  A value of -1 means loop until no change found.
40679eb4f74649b23c053b308ce1152dce51239450baanthony%                  How this is applied may depend on the morphology method.
40689eb4f74649b23c053b308ce1152dce51239450baanthony%                  Typically this is a value of 1.
40699eb4f74649b23c053b308ce1152dce51239450baanthony%
40709eb4f74649b23c053b308ce1152dce51239450baanthony%    o kernel: An array of double representing the morphology kernel.
40719eb4f74649b23c053b308ce1152dce51239450baanthony%              Warning: kernel may be normalized for the Convolve method.
40729eb4f74649b23c053b308ce1152dce51239450baanthony%
40739eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
40749eb4f74649b23c053b308ce1152dce51239450baanthony%
40759eb4f74649b23c053b308ce1152dce51239450baanthony*/
4076f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristyMagickExport Image *MorphologyImage(const Image *image,
4077f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const ssize_t iterations,
4078f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const KernelInfo *kernel,ExceptionInfo *exception)
40799eb4f74649b23c053b308ce1152dce51239450baanthony{
40809eb4f74649b23c053b308ce1152dce51239450baanthony  KernelInfo
40819eb4f74649b23c053b308ce1152dce51239450baanthony    *curr_kernel;
40829eb4f74649b23c053b308ce1152dce51239450baanthony
408347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  CompositeOperator
408447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    compose;
408547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
40869eb4f74649b23c053b308ce1152dce51239450baanthony  Image
40879eb4f74649b23c053b308ce1152dce51239450baanthony    *morphology_image;
40889eb4f74649b23c053b308ce1152dce51239450baanthony
4089f46d42620631d2581e0b6a56456e203e17c427c8anthony  double
4090f46d42620631d2581e0b6a56456e203e17c427c8anthony    bias;
40919eb4f74649b23c053b308ce1152dce51239450baanthony
409222de2722b682eb405b60ec6022a7546df994674eanthony  curr_kernel = (KernelInfo *) kernel;
409322de2722b682eb405b60ec6022a7546df994674eanthony  bias=0.0;
4094d228c03fa334bae897eee6c2d8721fa48e1577bacristy  compose = UndefinedCompositeOp;  /* use default for method */
409522de2722b682eb405b60ec6022a7546df994674eanthony
409646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Apply Convolve/Correlate Normalization and Scaling Factors.
409746a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * This is done BEFORE the ShowKernelInfo() function is called so that
409846a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * users can see the results of the 'option:convolve:scale' option.
40999eb4f74649b23c053b308ce1152dce51239450baanthony   */
410022de2722b682eb405b60ec6022a7546df994674eanthony  if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
410128ad1d779b6ca95852e860514185a7a97e06af77anthony      const char
410228ad1d779b6ca95852e860514185a7a97e06af77anthony        *artifact;
4103f46d42620631d2581e0b6a56456e203e17c427c8anthony
410422de2722b682eb405b60ec6022a7546df994674eanthony      /* Get the bias value as it will be needed */
410522de2722b682eb405b60ec6022a7546df994674eanthony      artifact = GetImageArtifact(image,"convolve:bias");
410622de2722b682eb405b60ec6022a7546df994674eanthony      if ( artifact != (const char *) NULL) {
410722de2722b682eb405b60ec6022a7546df994674eanthony        if (IfMagickFalse(IsGeometry(artifact)))
410822de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
410922de2722b682eb405b60ec6022a7546df994674eanthony               OptionWarning,"InvalidSetting","'%s' '%s'",
411022de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:bias",artifact);
411122de2722b682eb405b60ec6022a7546df994674eanthony        else
411222de2722b682eb405b60ec6022a7546df994674eanthony          bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
411322de2722b682eb405b60ec6022a7546df994674eanthony      }
411422de2722b682eb405b60ec6022a7546df994674eanthony
411522de2722b682eb405b60ec6022a7546df994674eanthony      /* Scale kernel according to user wishes */
41169eb4f74649b23c053b308ce1152dce51239450baanthony      artifact = GetImageArtifact(image,"convolve:scale");
4117e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony      if ( artifact != (const char *)NULL ) {
411822de2722b682eb405b60ec6022a7546df994674eanthony        if (IfMagickFalse(IsGeometry(artifact)))
411922de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
412022de2722b682eb405b60ec6022a7546df994674eanthony               OptionWarning,"InvalidSetting","'%s' '%s'",
412122de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:scale",artifact);
412222de2722b682eb405b60ec6022a7546df994674eanthony        else {
412322de2722b682eb405b60ec6022a7546df994674eanthony          if ( curr_kernel == kernel )
412422de2722b682eb405b60ec6022a7546df994674eanthony            curr_kernel = CloneKernelInfo(kernel);
412522de2722b682eb405b60ec6022a7546df994674eanthony          if (curr_kernel == (KernelInfo *) NULL)
412622de2722b682eb405b60ec6022a7546df994674eanthony            return((Image *) NULL);
412722de2722b682eb405b60ec6022a7546df994674eanthony          ScaleGeometryKernelInfo(curr_kernel, artifact);
41289eb4f74649b23c053b308ce1152dce51239450baanthony        }
41299eb4f74649b23c053b308ce1152dce51239450baanthony      }
41309eb4f74649b23c053b308ce1152dce51239450baanthony    }
41319eb4f74649b23c053b308ce1152dce51239450baanthony
41329eb4f74649b23c053b308ce1152dce51239450baanthony  /* display the (normalized) kernel via stderr */
413348656f2d1ca9ac9979eac32052e4cdc4958c9010cristy  if ( IfStringTrue(GetImageArtifact(image,"showkernel"))
413448656f2d1ca9ac9979eac32052e4cdc4958c9010cristy    || IfStringTrue(GetImageArtifact(image,"convolve:showkernel"))
413548656f2d1ca9ac9979eac32052e4cdc4958c9010cristy    || IfStringTrue(GetImageArtifact(image,"morphology:showkernel")) )
41369eb4f74649b23c053b308ce1152dce51239450baanthony    ShowKernelInfo(curr_kernel);
41379eb4f74649b23c053b308ce1152dce51239450baanthony
41383206678d008425bc56dd2dbad002f2bb26299dc2anthony  /* Override the default handling of multi-kernel morphology results
41393206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'Undefined' use the default method
41403206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'None' (default for 'Convolve') re-iterate previous result
41413206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Otherwise merge resulting images using compose method given.
41423206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Default for 'HitAndMiss' is 'Lighten'.
414347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
414428ad1d779b6ca95852e860514185a7a97e06af77anthony  { const char
414528ad1d779b6ca95852e860514185a7a97e06af77anthony      *artifact;
414622de2722b682eb405b60ec6022a7546df994674eanthony    ssize_t
414722de2722b682eb405b60ec6022a7546df994674eanthony      parse;
414822de2722b682eb405b60ec6022a7546df994674eanthony
4149f46d42620631d2581e0b6a56456e203e17c427c8anthony    artifact = GetImageArtifact(image,"morphology:compose");
415022de2722b682eb405b60ec6022a7546df994674eanthony    if ( artifact != (const char *) NULL) {
415122de2722b682eb405b60ec6022a7546df994674eanthony      parse=ParseCommandOption(MagickComposeOptions,
415270b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy        MagickFalse,artifact);
415322de2722b682eb405b60ec6022a7546df994674eanthony      if ( parse < 0 )
415422de2722b682eb405b60ec6022a7546df994674eanthony        (void) ThrowMagickException(exception,GetMagickModule(),
415522de2722b682eb405b60ec6022a7546df994674eanthony             OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
415622de2722b682eb405b60ec6022a7546df994674eanthony             "morphology:compose",artifact);
415722de2722b682eb405b60ec6022a7546df994674eanthony      else
415822de2722b682eb405b60ec6022a7546df994674eanthony        compose=(CompositeOperator)parse;
415922de2722b682eb405b60ec6022a7546df994674eanthony    }
416028ad1d779b6ca95852e860514185a7a97e06af77anthony  }
41619eb4f74649b23c053b308ce1152dce51239450baanthony  /* Apply the Morphology */
4162f46d42620631d2581e0b6a56456e203e17c427c8anthony  morphology_image = MorphologyApply(image,method,iterations,
4163f46d42620631d2581e0b6a56456e203e17c427c8anthony    curr_kernel,compose,bias,exception);
41649eb4f74649b23c053b308ce1152dce51239450baanthony
41659eb4f74649b23c053b308ce1152dce51239450baanthony  /* Cleanup and Exit */
41669eb4f74649b23c053b308ce1152dce51239450baanthony  if ( curr_kernel != kernel )
41671b2bc0a7da432e6e1cc0480280402df213faa940anthony    curr_kernel=DestroyKernelInfo(curr_kernel);
41689eb4f74649b23c053b308ce1152dce51239450baanthony  return(morphology_image);
41699eb4f74649b23c053b308ce1152dce51239450baanthony}
417083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
417183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
417283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
417383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
417483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
417583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
41764fd27e21043be809d66c8202e779255e5b660d2danthony+     R o t a t e K e r n e l I n f o                                         %
417783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
417883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
417983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
418083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
418246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  RotateKernelInfo() rotates the kernel by the angle given.
418346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
418446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  Currently it is restricted to 90 degree angles, of either 1D kernels
418546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
418646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It will ignore usless rotations for specific 'named' built-in kernels.
418783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
41884fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the RotateKernelInfo method is:
418983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
41904fd27e21043be809d66c8202e779255e5b660d2danthony%      void RotateKernelInfo(KernelInfo *kernel, double angle)
419183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
419283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
419383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
419483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
419583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
419683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o angle: angle to rotate in degrees
419783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
419846a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This function is currently internal to this module only, but can be exported
419946a369d839971ab627bdb31a93d8bd63e81b65a3anthony% to other modules if needed.
420083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
42014fd27e21043be809d66c8202e779255e5b660d2danthonystatic void RotateKernelInfo(KernelInfo *kernel, double angle)
420283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
42031b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* angle the lower kernels first */
42041b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
42051b2bc0a7da432e6e1cc0480280402df213faa940anthony    RotateKernelInfo(kernel->next, angle);
42061b2bc0a7da432e6e1cc0480280402df213faa940anthony
420783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
420883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  **
420983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  ** TODO: expand beyond simple 90 degree rotates, flips and flops
421083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  */
421183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
421283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* Modulus the angle */
421383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  angle = fmod(angle, 360.0);
421483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( angle < 0 )
421583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    angle += 360.0;
421683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42173c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 337.5 < angle || angle <= 22.5 )
421843c4925e5305a26e48d68f7893e94f55d0831c39anthony    return;   /* Near zero angle - no change! - At least not at this time */
421983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42203dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  /* Handle special cases */
422183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  switch (kernel->type) {
422283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These built-in kernels are cylindrical kernels, rotating is useless */
422383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case GaussianKernel:
4224501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
4225501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
422683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiskKernel:
42273dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case PeaksKernel:
42283dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case LaplacianKernel:
422983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case ChebyshevKernel:
4230bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
423183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case EuclideanKernel:
423283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
423383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
423483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These may be rotatable at non-90 angles in the future */
423583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* but simply rotating them in multiples of 90 degrees is useless */
423683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case SquareKernel:
423783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiamondKernel:
423883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case PlusKernel:
42393dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
424083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
424183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
424283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These only allows a +/-90 degree rotation (by transpose) */
424383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* A 180 degree rotation is useless */
424483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case BlurKernel:
424583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 135.0 < angle && angle <= 225.0 )
424683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return;
424783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 225.0 < angle && angle <= 315.0 )
424883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        angle -= 180;
424983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
425083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42513dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    default:
425283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
425383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
425457fe7a498c1302232dac8466864e84b12fad0807anthony  /* Attempt rotations by 45 degrees  -- 3x3 kernels only */
42553c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
42563c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
42573c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 3 && kernel->height == 3 )
42583c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a 3x3 square by 45 degree angle */
4259a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy          double t  = kernel->values[0];
426043c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[0] = kernel->values[3];
426143c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[3] = kernel->values[6];
426243c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[6] = kernel->values[7];
426343c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[7] = kernel->values[8];
426443c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[8] = kernel->values[5];
426543c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[5] = kernel->values[2];
426643c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[2] = kernel->values[1];
426743c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[1] = t;
42681d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate non-centered origin */
42691d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          if ( kernel->x != 1 || kernel->y != 1 ) {
4270bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            ssize_t x,y;
4271bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            x = (ssize_t) kernel->x-1;
4272bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            y = (ssize_t) kernel->y-1;
42731d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                 if ( x == y  ) x = 0;
42741d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == 0  ) x = -y;
42751d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == -y ) y = 0;
42761d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( y == 0  ) y = x;
4277ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) x+1;
4278ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) y+1;
42791d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
428043c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
428143c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+45.0, 360.0);
42823c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
42833c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
42843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate non-3x3 kernel by 45 degrees");
42853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
42863c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
42873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
42883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 1 || kernel->height == 1 )
42894c08aed51c5899665ade97263692328eea4af106cristy        { /* Do a transpose of a 1 dimensional kernel,
4290bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          ** which results in a fast 90 degree rotation of some type.
42913c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          */
4292bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          ssize_t
42933c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            t;
4294bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          t = (ssize_t) kernel->width;
42953c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->width = kernel->height;
4296bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->height = (size_t) t;
42973c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          t = kernel->x;
42983c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->x = kernel->y;
42993c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->y = t;
430043c4925e5305a26e48d68f7893e94f55d0831c39anthony          if ( kernel->width == 1 ) {
430143c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
430243c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+90.0, 360.0);
430343c4925e5305a26e48d68f7893e94f55d0831c39anthony          } else {
430443c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
430543c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+270.0, 360.0);
430643c4925e5305a26e48d68f7893e94f55d0831c39anthony          }
43073c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
43083c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else if ( kernel->width == kernel->height )
43093c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a square array of values by 90 degrees */
4310d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          { register ssize_t
43111d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              i,j,x,y;
4312d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
4313d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            register MagickRealType
43141d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              *k,t;
4315d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
43161d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            k=kernel->values;
4317d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            for( i=0, x=(ssize_t) kernel->width-1;  i<=x;   i++, x--)
4318d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              for( j=0, y=(ssize_t) kernel->height-1;  j<y;   j++, y--)
43191d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                { t                    = k[i+j*kernel->width];
43201d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[i+j*kernel->width] = k[j+x*kernel->width];
43211d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[j+x*kernel->width] = k[x+y*kernel->width];
43221d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[x+y*kernel->width] = k[y+i*kernel->width];
43231d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[y+i*kernel->width] = t;
43241d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                }
43251d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
43261d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate the origin - relative to center of array */
4327bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          { register ssize_t x,y;
4328eaedf06777741da32408da72c1e512975c600c48cristy            x = (ssize_t) (kernel->x*2-kernel->width+1);
4329eaedf06777741da32408da72c1e512975c600c48cristy            y = (ssize_t) (kernel->y*2-kernel->height+1);
4330ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4331ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
43321d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
433343c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
433443c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+90.0, 360.0);
43353c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
43363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
43373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
43383c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
433983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( 135.0 < angle && angle <= 225.0 )
434083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    {
434143c4925e5305a26e48d68f7893e94f55d0831c39anthony      /* For a 180 degree rotation - also know as a reflection
434243c4925e5305a26e48d68f7893e94f55d0831c39anthony       * This is actually a very very common operation!
434343c4925e5305a26e48d68f7893e94f55d0831c39anthony       * Basically all that is needed is a reversal of the kernel data!
434443c4925e5305a26e48d68f7893e94f55d0831c39anthony       * And a reflection of the origon
434543c4925e5305a26e48d68f7893e94f55d0831c39anthony       */
4346a96f2494a8e79144a225056be9545cc75e868137cristy      double
4347a96f2494a8e79144a225056be9545cc75e868137cristy        t;
4348a96f2494a8e79144a225056be9545cc75e868137cristy
4349d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register MagickRealType
4350a96f2494a8e79144a225056be9545cc75e868137cristy        *k;
4351a96f2494a8e79144a225056be9545cc75e868137cristy
4352a96f2494a8e79144a225056be9545cc75e868137cristy      ssize_t
4353a96f2494a8e79144a225056be9545cc75e868137cristy        i,
4354a96f2494a8e79144a225056be9545cc75e868137cristy        j;
435583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
435683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      k=kernel->values;
4357e42f658533644aecb733785ffd91b286d6778deacristy      j=(ssize_t) (kernel->width*kernel->height-1);
4358e42f658533644aecb733785ffd91b286d6778deacristy      for (i=0;  i < j;  i++, j--)
435983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        t=k[i],  k[i]=k[j],  k[j]=t;
436083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
4361bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
4362bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = (ssize_t) kernel->height - kernel->y - 1;
436343c4925e5305a26e48d68f7893e94f55d0831c39anthony      angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
436443c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->angle = fmod(kernel->angle+180.0, 360.0);
436583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    }
43663c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* At this point angle should at least between -45 (315) and +45 degrees
436783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * In the future some form of non-orthogonal angled rotates could be
436883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * performed here, posibily with a linear kernel restriction.
436983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   */
437083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
437183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  return;
437283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
437383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
437483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
437583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
437683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
437783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
437883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
437946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S c a l e G e o m e t r y K e r n e l I n f o                           %
438046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
438146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
438246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
438346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
438546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleGeometryKernelInfo() takes a geometry argument string, typically
438646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  provided as a  "-set option:convolve:scale {geometry}" user setting,
438746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  and modifies the kernel according to the parsed arguments of that setting.
438846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
438946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The first argument (and any normalization flags) are passed to
439046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
439146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  is then passed to UnityAddKernelInfo() to add a scled unity kernel
439246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  into the scaled/normalized kernel.
439346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4394ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%  The format of the ScaleGeometryKernelInfo method is:
439546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4396ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%      void ScaleGeometryKernelInfo(KernelInfo *kernel,
4397ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%        const double scaling_factor,const MagickStatusType normalize_flags)
439846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
439946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
440046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
440146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
440246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
440346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o geometry:
440446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             The geometry string to parse, typically from the user provided
440546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             "-set option:convolve:scale {geometry}" setting.
440646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
440746a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
440846a369d839971ab627bdb31a93d8bd63e81b65a3anthonyMagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
440946a369d839971ab627bdb31a93d8bd63e81b65a3anthony     const char *geometry)
441046a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
4411ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  MagickStatusType
441246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    flags;
4413ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy
441446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  GeometryInfo
441546a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args;
441646a369d839971ab627bdb31a93d8bd63e81b65a3anthony
441746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  SetGeometryInfo(&args);
441822de2722b682eb405b60ec6022a7546df994674eanthony  flags = ParseGeometry(geometry, &args);
441946a369d839971ab627bdb31a93d8bd63e81b65a3anthony
442046a369d839971ab627bdb31a93d8bd63e81b65a3anthony#if 0
442146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* For Debugging Geometry Input */
44225acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
442346a369d839971ab627bdb31a93d8bd63e81b65a3anthony       flags, args.rho, args.sigma, args.xi, args.psi );
442446a369d839971ab627bdb31a93d8bd63e81b65a3anthony#endif
442546a369d839971ab627bdb31a93d8bd63e81b65a3anthony
442646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
442746a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho *= 0.01,  args.sigma *= 0.01;
442846a369d839971ab627bdb31a93d8bd63e81b65a3anthony
442946a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
443046a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho = 1.0;
443146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) == 0 )
443246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.sigma = 0.0;
443346a369d839971ab627bdb31a93d8bd63e81b65a3anthony
443446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Scale/Normalize the input kernel */
4435d228c03fa334bae897eee6c2d8721fa48e1577bacristy  ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
443646a369d839971ab627bdb31a93d8bd63e81b65a3anthony
443746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add Unity Kernel, for blending with original */
443846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) != 0 )
443946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel, args.sigma);
444046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
444146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
444246a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
444346a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
444446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
444646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
444746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
44486771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     S c a l e K e r n e l I n f o                                           %
4449cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4450cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4451cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4452cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4453cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
44541b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ScaleKernelInfo() scales the given kernel list by the given amount, with or
44551b2bc0a7da432e6e1cc0480280402df213faa940anthony%  without normalization of the sum of the kernel values (as per given flags).
4456999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4457999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  By default (no flags given) the values within the kernel is scaled
44581b2bc0a7da432e6e1cc0480280402df213faa940anthony%  directly using given scaling factor without change.
4459999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
446046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  If either of the two 'normalize_flags' are given the kernel will first be
446146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  normalized and then further scaled by the scaling factor value given.
4462999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4463999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  Kernel normalization ('normalize_flags' given) is designed to ensure that
4464999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
44651b2bc0a7da432e6e1cc0480280402df213faa940anthony%  morphology methods will fall into -1.0 to +1.0 range.  Note that for
44661b2bc0a7da432e6e1cc0480280402df213faa940anthony%  non-HDRI versions of IM this may cause images to have any negative results
44671b2bc0a7da432e6e1cc0480280402df213faa940anthony%  clipped, unless some 'bias' is used.
4468999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4469999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  More specifically.  Kernels which only contain positive values (such as a
4470999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
44711b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ensuring a 0.0 to +1.0 output range for non-HDRI images.
4472999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4473999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
4474999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  the kernel will be scaled by the absolute of the sum of kernel values, so
4475999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  that it will generally fall within the +/- 1.0 range.
4476cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4477999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
4478999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  will be scaled by just the sum of the postive values, so that its output
4479999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  range will again fall into the  +/- 1.0 range.
4480cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4481999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For special kernels designed for locating shapes using 'Correlate', (often
4482999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  only containing +1 and -1 values, representing foreground/brackground
4483999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  matching) a special normalization method is provided to scale the positive
44841e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  values separately to those of the negative values, so the kernel will be
4485999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  forced to become a zero-sum kernel better suited to such searches.
4486999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
44871b2bc0a7da432e6e1cc0480280402df213faa940anthony%  WARNING: Correct normalization of the kernel assumes that the '*_range'
4488999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  attributes within the kernel structure have been correctly set during the
4489999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  kernels creation.
4490999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4491999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  NOTE: The values used for 'normalize_flags' have been selected specifically
449246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  to match the use of geometry options, so that '!' means NormalizeValue, '^'
449346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
4494cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
44954fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the ScaleKernelInfo method is:
4496cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4497999bb2c20aa9d42875bb5adba44951988d4ae354anthony%      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
4498999bb2c20aa9d42875bb5adba44951988d4ae354anthony%               const MagickStatusType normalize_flags )
4499cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4500cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4501cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4502cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4503cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4504999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o scaling_factor:
4505999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             multiply all values (after normalization) by this factor if not
4506999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             zero.  If the kernel is normalized regardless of any flags.
4507999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4508999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o normalize_flags:
4509999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             GeometryFlags defining normalization method to use.
4510999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             specifically: NormalizeValue, CorrelateNormalizeValue,
4511999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                           and/or PercentValue
4512cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4513cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
45146771f1e8987fa49f52d4176281a2e8524b8e31cbcristyMagickExport void ScaleKernelInfo(KernelInfo *kernel,
45156771f1e8987fa49f52d4176281a2e8524b8e31cbcristy  const double scaling_factor,const GeometryFlags normalize_flags)
4516cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4517bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
4518cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4519cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4520999bb2c20aa9d42875bb5adba44951988d4ae354anthony  register double
4521999bb2c20aa9d42875bb5adba44951988d4ae354anthony    pos_scale,
4522999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale;
4523999bb2c20aa9d42875bb5adba44951988d4ae354anthony
452446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
45251b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
45261b2bc0a7da432e6e1cc0480280402df213faa940anthony    ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
45271b2bc0a7da432e6e1cc0480280402df213faa940anthony
452846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Normalization of Kernel */
4529999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = 1.0;
4530999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&NormalizeValue) != 0 ) {
4531b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4532f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* non-zero-summing kernel (generally positive) */
4533999bb2c20aa9d42875bb5adba44951988d4ae354anthony      pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4534cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    else
4535f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* zero-summing kernel */
4536f4e0031305baeb01c89cfd2842cbbec021883550anthony      pos_scale = kernel->positive_range;
4537999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
453846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Force kernel into a normalized zero-summing kernel */
4539999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4540b978e458a8e1f210bcb580951cf623687236b2fecristy    pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4541999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? kernel->positive_range : 1.0;
4542b978e458a8e1f210bcb580951cf623687236b2fecristy    neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4543999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? -kernel->negative_range : 1.0;
4544999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4545999bb2c20aa9d42875bb5adba44951988d4ae354anthony  else
4546999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale = pos_scale;
4547999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4548999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* finialize scaling_factor for positive and negative components */
4549999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = scaling_factor/pos_scale;
4550999bb2c20aa9d42875bb5adba44951988d4ae354anthony  neg_scale = scaling_factor/neg_scale;
4551cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4552bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
45537f71c558beaa2cdf586a5e1358a79f9085e49c35cristy    if ( ! IsNaN(kernel->values[i]) )
4554999bb2c20aa9d42875bb5adba44951988d4ae354anthony      kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4555999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4556999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* convolution output range */
4557999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->positive_range *= pos_scale;
4558999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->negative_range *= neg_scale;
4559999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* maximum and minimum values in kernel */
4560999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4561999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4562999bb2c20aa9d42875bb5adba44951988d4ae354anthony
456346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* swap kernel settings if user's scaling factor is negative */
4564999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( scaling_factor < MagickEpsilon ) {
4565999bb2c20aa9d42875bb5adba44951988d4ae354anthony    double t;
4566999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->positive_range;
4567999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->positive_range = kernel->negative_range;
4568999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->negative_range = t;
4569999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->maximum;
4570999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->maximum = kernel->minimum;
4571999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->minimum = 1;
4572999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4573cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4574cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4575cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4576cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4577cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4578cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4579cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4580cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4581cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
458246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S h o w K e r n e l I n f o                                             %
458383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
458483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
458583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
458683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
458783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
45884fd27e21043be809d66c8202e779255e5b660d2danthony%  ShowKernelInfo() outputs the details of the given kernel defination to
45894fd27e21043be809d66c8202e779255e5b660d2danthony%  standard error, generally due to a users 'showkernel' option request.
459083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
459183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the ShowKernel method is:
459283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
459357fe7a498c1302232dac8466864e84b12fad0807anthony%      void ShowKernelInfo(const KernelInfo *kernel)
459483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
459583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
459683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
459783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
459883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
459983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
4600433d11887841b922ec5e6805f9fdd240c320b92ecristyMagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
460183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
460257fe7a498c1302232dac8466864e84b12fad0807anthony  const KernelInfo
46037a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *k;
46047a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4605bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
46067a01dcf50ce12cb2a789bedff51e9345f022432eanthony    c, i, u, v;
46077a01dcf50ce12cb2a789bedff51e9345f022432eanthony
46087a01dcf50ce12cb2a789bedff51e9345f022432eanthony  for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
46097a01dcf50ce12cb2a789bedff51e9345f022432eanthony
46105acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Kernel");
46117a01dcf50ce12cb2a789bedff51e9345f022432eanthony    if ( kernel->next != (KernelInfo *) NULL )
46125acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
46135acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, " \"%s",
4614042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy          CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4615b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(k->angle) >= MagickEpsilon )
46165acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "@%lg", k->angle);
46175acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
46181e604812fad85bb96f757a2393015ae3d061c39acristy      k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
46195acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr,
46207a01dcf50ce12cb2a789bedff51e9345f022432eanthony          " with values from %.*lg to %.*lg\n",
46217a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->minimum,
46227a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->maximum);
46235acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Forming a output range from %.*lg to %.*lg",
46247a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->negative_range,
462546a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range);
462646a369d839971ab627bdb31a93d8bd63e81b65a3anthony    if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
46275acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Zero-Summing)\n");
462846a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
46295acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Normalized)\n");
463046a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else
46315acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Sum %.*lg)\n",
463246a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range+k->negative_range);
463343c4925e5305a26e48d68f7893e94f55d0831c39anthony    for (i=v=0; v < k->height; v++) {
46345acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
463543c4925e5305a26e48d68f7893e94f55d0831c39anthony      for (u=0; u < k->width; u++, i++)
46367f71c558beaa2cdf586a5e1358a79f9085e49c35cristy        if ( IsNaN(k->values[i]) )
46375acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
46387a01dcf50ce12cb2a789bedff51e9345f022432eanthony        else
46395acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
4640d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              GetMagickPrecision(), (double) k->values[i]);
46415acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr,"\n");
46427a01dcf50ce12cb2a789bedff51e9345f022432eanthony    }
464383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
464483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
4645cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4646cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4647cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4648cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4649cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4650cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
465143c4925e5305a26e48d68f7893e94f55d0831c39anthony%     U n i t y A d d K e r n a l I n f o                                     %
465243c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
465343c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
465443c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
465543c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465643c4925e5305a26e48d68f7893e94f55d0831c39anthony%
465743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
465843c4925e5305a26e48d68f7893e94f55d0831c39anthony%  to the given pre-scaled and normalized Kernel.  This in effect adds that
465943c4925e5305a26e48d68f7893e94f55d0831c39anthony%  amount of the original image into the resulting convolution kernel.  This
466043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  value is usually provided by the user as a percentage value in the
466143c4925e5305a26e48d68f7893e94f55d0831c39anthony%  'convolve:scale' setting.
466243c4925e5305a26e48d68f7893e94f55d0831c39anthony%
4663501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  The resulting effect is to convert the defined kernels into blended
4664501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  soft-blurs, unsharp kernels or into sharpening kernels.
466543c4925e5305a26e48d68f7893e94f55d0831c39anthony%
466646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the UnityAdditionKernelInfo method is:
466743c4925e5305a26e48d68f7893e94f55d0831c39anthony%
466843c4925e5305a26e48d68f7893e94f55d0831c39anthony%      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
466943c4925e5305a26e48d68f7893e94f55d0831c39anthony%
467043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  A description of each parameter follows:
467143c4925e5305a26e48d68f7893e94f55d0831c39anthony%
467243c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o kernel: the Morphology/Convolution kernel
467343c4925e5305a26e48d68f7893e94f55d0831c39anthony%
467443c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o scale:
467543c4925e5305a26e48d68f7893e94f55d0831c39anthony%             scaling factor for the unity kernel to be added to
467643c4925e5305a26e48d68f7893e94f55d0831c39anthony%             the given kernel.
467743c4925e5305a26e48d68f7893e94f55d0831c39anthony%
467843c4925e5305a26e48d68f7893e94f55d0831c39anthony*/
467943c4925e5305a26e48d68f7893e94f55d0831c39anthonyMagickExport void UnityAddKernelInfo(KernelInfo *kernel,
468043c4925e5305a26e48d68f7893e94f55d0831c39anthony  const double scale)
468143c4925e5305a26e48d68f7893e94f55d0831c39anthony{
468246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
468346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( kernel->next != (KernelInfo *) NULL)
468446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel->next, scale);
468543c4925e5305a26e48d68f7893e94f55d0831c39anthony
468646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add the scaled unity kernel to the existing kernel */
468743c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->values[kernel->x+kernel->y*kernel->width] += scale;
468846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(kernel);  /* recalculate the meta-data */
468943c4925e5305a26e48d68f7893e94f55d0831c39anthony
469043c4925e5305a26e48d68f7893e94f55d0831c39anthony  return;
469143c4925e5305a26e48d68f7893e94f55d0831c39anthony}
469243c4925e5305a26e48d68f7893e94f55d0831c39anthony
469343c4925e5305a26e48d68f7893e94f55d0831c39anthony/*
469443c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469543c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
469643c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
469743c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
469843c4925e5305a26e48d68f7893e94f55d0831c39anthony%     Z e r o K e r n e l N a n s                                             %
4699cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4700cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4701cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4702cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4703cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4704cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  ZeroKernelNans() replaces any special 'nan' value that may be present in
4705cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  the kernel with a zero value.  This is typically done when the kernel will
4706cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  be used in special hardware (GPU) convolution processors, to simply
4707cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  matters.
4708cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4709cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  The format of the ZeroKernelNans method is:
4710cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
471146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void ZeroKernelNans (KernelInfo *kernel)
4712cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4713cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4714cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4715cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4716cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4717cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
4718cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate void ZeroKernelNans(KernelInfo *kernel)
4719cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4720bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
4721cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4722cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
472346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
47241b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
47251b2bc0a7da432e6e1cc0480280402df213faa940anthony    ZeroKernelNans(kernel->next);
47261b2bc0a7da432e6e1cc0480280402df213faa940anthony
472743c4925e5305a26e48d68f7893e94f55d0831c39anthony  for (i=0; i < (kernel->width*kernel->height); i++)
47287f71c558beaa2cdf586a5e1358a79f9085e49c35cristy    if ( IsNaN(kernel->values[i]) )
4729cc6c836da2a53b6023b716e4973090a6714dc3b0anthony      kernel->values[i] = 0.0;
4730cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4731cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4732cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4733