morphology.c revision 8ea81224e9ff022e56eb2cddb12860a8b2e90411
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%                                                                             %
207e41fe84a841d7b9d7b36b245b65e9dcb3314943cristy%  Copyright 1999-2011 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"
674c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/monitor-private.h"
684c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology.h"
694c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology-private.h"
704c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/option.h"
714c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel-accessor.h"
724c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/prepress.h"
734c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/quantize.h"
744c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/registry.h"
754c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/semaphore.h"
764c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/splay-tree.h"
774c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/statistic.h"
784c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string_.h"
794c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string-private.h"
804c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/token.h"
814c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/utility.h"
82d1dd6e4fefa0810b9893e6ac9418f79c97c1b39acristy#include "MagickCore/utility-private.h"
83a29d45f897949f04a47bb3da077395969f13dcbacristy
84c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
85c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony/*
86c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** The following test is for special floating point numbers of value NaN (not
87c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** a number), that may be used within a Kernel Definition.  NaN's are defined
88c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** as part of the IEEE standard for floating point number representation.
89c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony**
90c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** These are used as a Kernel value to mean that this kernel position is not
91c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** part of the kernel neighbourhood for convolution or morphology processing,
92c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** and thus should be ignored.  This allows the use of 'shaped' kernels.
93c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony**
94c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** The special properity that two NaN's are never equal, even if they are from
95c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** the same variable allow you to test if a value is special NaN value.
96c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony**
97c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** This macro  IsNaN() is thus is only true if the value given is NaN.
98a29d45f897949f04a47bb3da077395969f13dcbacristy*/
99602ab9b30b644a78a4057da93d838a77391ec0acanthony#define IsNan(a)   ((a)!=(a))
100602ab9b30b644a78a4057da93d838a77391ec0acanthony
10129188a8682a98d4b7882cca434b170517555fc7danthony/*
102a29d45f897949f04a47bb3da077395969f13dcbacristy  Other global definitions used by module.
103a29d45f897949f04a47bb3da077395969f13dcbacristy*/
10429188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMin(const double x,const double y)
10529188a8682a98d4b7882cca434b170517555fc7danthony{
10629188a8682a98d4b7882cca434b170517555fc7danthony  return( x < y ? x : y);
10729188a8682a98d4b7882cca434b170517555fc7danthony}
10829188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMax(const double x,const double y)
10929188a8682a98d4b7882cca434b170517555fc7danthony{
11029188a8682a98d4b7882cca434b170517555fc7danthony  return( x > y ? x : y);
11129188a8682a98d4b7882cca434b170517555fc7danthony}
11229188a8682a98d4b7882cca434b170517555fc7danthony#define Minimize(assign,value) assign=MagickMin(assign,value)
11329188a8682a98d4b7882cca434b170517555fc7danthony#define Maximize(assign,value) assign=MagickMax(assign,value)
11429188a8682a98d4b7882cca434b170517555fc7danthony
115c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthony/* Currently these are only internal to this module */
116c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthonystatic void
11746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(KernelInfo *),
118bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandMirrorKernelInfo(KernelInfo *),
119bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandRotateKernelInfo(KernelInfo *, const double),
120ef656913b0b30d713ae94c82c47693c9dc69c9f4cristy  RotateKernelInfo(KernelInfo *, double);
121602ab9b30b644a78a4057da93d838a77391ec0acanthony
1223dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
1233dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony/* Quick function to find last kernel in a kernel list */
1243dd0f620e7a1d12f747ce167844cd7269bfa9f12anthonystatic inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
1253dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony{
1263dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  while (kernel->next != (KernelInfo *) NULL)
1273dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    kernel = kernel->next;
1283dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  return(kernel);
1293dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony}
1303dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
131602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
132602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
134602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
135602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
13683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     A c q u i r e K e r n e l I n f o                                       %
137602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
138602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
139602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
140602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1422be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%  AcquireKernelInfo() takes the given string (generally supplied by the
143602ab9b30b644a78a4057da93d838a77391ec0acanthony%  user) and converts it into a Morphology/Convolution Kernel.  This allows
144602ab9b30b644a78a4057da93d838a77391ec0acanthony%  users to specify a kernel from a number of pre-defined kernels, or to fully
145602ab9b30b644a78a4057da93d838a77391ec0acanthony%  specify their own kernel for a specific Convolution or Morphology
146602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Operation.
147602ab9b30b644a78a4057da93d838a77391ec0acanthony%
148602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The kernel so generated can be any rectangular array of floating point
149602ab9b30b644a78a4057da93d838a77391ec0acanthony%  values (doubles) with the 'control point' or 'pixel being affected'
150602ab9b30b644a78a4057da93d838a77391ec0acanthony%  anywhere within that array of values.
151602ab9b30b644a78a4057da93d838a77391ec0acanthony%
15283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  Previously IM was restricted to a square of odd size using the exact
15319910ef25dd3d99d1981a9e42c934133170ee714anthony%  center as origin, this is no longer the case, and any rectangular kernel
15483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  with any value being declared the origin. This in turn allows the use of
15583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  highly asymmetrical kernels.
156602ab9b30b644a78a4057da93d838a77391ec0acanthony%
157602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The floating point values in the kernel can also include a special value
15883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  known as 'nan' or 'not a number' to indicate that this value is not part
15983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  of the kernel array. This allows you to shaped the kernel within its
16083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  rectangular area. That is 'nan' values provide a 'mask' for the kernel
16183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  shape.  However at least one non-nan value must be provided for correct
16283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  working of a kernel.
163602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1647a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  The returned kernel should be freed using the DestroyKernelInfo() when you
1657a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  are finished with it.  Do not free this memory yourself.
166602ab9b30b644a78a4057da93d838a77391ec0acanthony%
167602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Input kernel defintion strings can consist of any of three types.
168602ab9b30b644a78a4057da93d838a77391ec0acanthony%
169bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "name:args[[@><]"
17029188a8682a98d4b7882cca434b170517555fc7danthony%         Select from one of the built in kernels, using the name and
17129188a8682a98d4b7882cca434b170517555fc7danthony%         geometry arguments supplied.  See AcquireKernelBuiltIn()
172602ab9b30b644a78a4057da93d838a77391ec0acanthony%
173bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "WxH[+X+Y][@><]:num, num, num ..."
1741b2bc0a7da432e6e1cc0480280402df213faa940anthony%         a kernel of size W by H, with W*H floating point numbers following.
175602ab9b30b644a78a4057da93d838a77391ec0acanthony%         the 'center' can be optionally be defined at +X+Y (such that +0+0
17629188a8682a98d4b7882cca434b170517555fc7danthony%         is top left corner). If not defined the pixel in the center, for
17729188a8682a98d4b7882cca434b170517555fc7danthony%         odd sizes, or to the immediate top or left of center for even sizes
17829188a8682a98d4b7882cca434b170517555fc7danthony%         is automatically selected.
179602ab9b30b644a78a4057da93d838a77391ec0acanthony%
18029188a8682a98d4b7882cca434b170517555fc7danthony%    "num, num, num, num, ..."
18129188a8682a98d4b7882cca434b170517555fc7danthony%         list of floating point numbers defining an 'old style' odd sized
18229188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel.  At least 9 values should be provided for a 3x3
18329188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
18429188a8682a98d4b7882cca434b170517555fc7danthony%         Values can be space or comma separated.  This is not recommended.
185602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1867a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  You can define a 'list of kernels' which can be used by some morphology
1871e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  operators A list is defined as a semi-colon separated list kernels.
1887a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
189dbc8989a61339951c6434d9a43e7b6fefb5da374anthony%     " kernel ; kernel ; kernel ; "
1907a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
1911dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%  Any extra ';' characters, at start, end or between kernel defintions are
19243c4925e5305a26e48d68f7893e94f55d0831c39anthony%  simply ignored.
19343c4925e5305a26e48d68f7893e94f55d0831c39anthony%
194bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The special flags will expand a single kernel, into a list of rotated
195bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
196bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
197bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The '<' also exands using 90-degree rotates, but giving a 180-degree
198bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  reflected kernel before the +/- 90-degree rotations, which can be important
199bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  for Thinning operations.
200bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
20143c4925e5305a26e48d68f7893e94f55d0831c39anthony%  Note that 'name' kernels will start with an alphabetic character while the
20243c4925e5305a26e48d68f7893e94f55d0831c39anthony%  new kernel specification has a ':' character in its specification string.
20343c4925e5305a26e48d68f7893e94f55d0831c39anthony%  If neither is the case, it is assumed an old style of a simple list of
20443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  numbers generating a odd-sized square kernel has been given.
2057a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
206602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernal method is:
207602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2082be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelInfo(const char *kernel_string)
209602ab9b30b644a78a4057da93d838a77391ec0acanthony%
210602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
211602ab9b30b644a78a4057da93d838a77391ec0acanthony%
212602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel_string: the Morphology/Convolution kernel wanted.
213602ab9b30b644a78a4057da93d838a77391ec0acanthony%
214602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
215602ab9b30b644a78a4057da93d838a77391ec0acanthony
216c84dce50867229e4872193e8eed5dbab58eb9f02anthony/* This was separated so that it could be used as a separate
2175ef8e94ff55717be2387d537bd49025780a1a558anthony** array input handling function, such as for -color-matrix
218c84dce50867229e4872193e8eed5dbab58eb9f02anthony*/
2195ef8e94ff55717be2387d537bd49025780a1a558anthonystatic KernelInfo *ParseKernelArray(const char *kernel_string)
220602ab9b30b644a78a4057da93d838a77391ec0acanthony{
2212be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
222602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
223602ab9b30b644a78a4057da93d838a77391ec0acanthony
224602ab9b30b644a78a4057da93d838a77391ec0acanthony  char
225602ab9b30b644a78a4057da93d838a77391ec0acanthony    token[MaxTextExtent];
226602ab9b30b644a78a4057da93d838a77391ec0acanthony
227602ab9b30b644a78a4057da93d838a77391ec0acanthony  const char
2285ef8e94ff55717be2387d537bd49025780a1a558anthony    *p,
2295ef8e94ff55717be2387d537bd49025780a1a558anthony    *end;
230602ab9b30b644a78a4057da93d838a77391ec0acanthony
231bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
232c84dce50867229e4872193e8eed5dbab58eb9f02anthony    i;
233602ab9b30b644a78a4057da93d838a77391ec0acanthony
23429188a8682a98d4b7882cca434b170517555fc7danthony  double
23529188a8682a98d4b7882cca434b170517555fc7danthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
23629188a8682a98d4b7882cca434b170517555fc7danthony
23743c4925e5305a26e48d68f7893e94f55d0831c39anthony  MagickStatusType
23843c4925e5305a26e48d68f7893e94f55d0831c39anthony    flags;
23943c4925e5305a26e48d68f7893e94f55d0831c39anthony
24043c4925e5305a26e48d68f7893e94f55d0831c39anthony  GeometryInfo
24143c4925e5305a26e48d68f7893e94f55d0831c39anthony    args;
24243c4925e5305a26e48d68f7893e94f55d0831c39anthony
2432be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
2442be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  if (kernel == (KernelInfo *)NULL)
245602ab9b30b644a78a4057da93d838a77391ec0acanthony    return(kernel);
246602ab9b30b644a78a4057da93d838a77391ec0acanthony  (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
24743c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->minimum = kernel->maximum = kernel->angle = 0.0;
2487a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->negative_range = kernel->positive_range = 0.0;
249602ab9b30b644a78a4057da93d838a77391ec0acanthony  kernel->type = UserDefinedKernel;
2507a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->next = (KernelInfo *) NULL;
251d43a46bc9598004091eae232bc7938e009b494a1cristy  kernel->signature = MagickSignature;
2525e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
2535e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(kernel);
254602ab9b30b644a78a4057da93d838a77391ec0acanthony
2555ef8e94ff55717be2387d537bd49025780a1a558anthony  /* find end of this specific kernel definition string */
2565ef8e94ff55717be2387d537bd49025780a1a558anthony  end = strchr(kernel_string, ';');
2575ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( end == (char *) NULL )
2585ef8e94ff55717be2387d537bd49025780a1a558anthony    end = strchr(kernel_string, '\0');
2595ef8e94ff55717be2387d537bd49025780a1a558anthony
260a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* clear flags - for Expanding kernel lists thorugh rotations */
26143c4925e5305a26e48d68f7893e94f55d0831c39anthony   flags = NoValue;
26243c4925e5305a26e48d68f7893e94f55d0831c39anthony
263602ab9b30b644a78a4057da93d838a77391ec0acanthony  /* Has a ':' in argument - New user kernel specification */
264602ab9b30b644a78a4057da93d838a77391ec0acanthony  p = strchr(kernel_string, ':');
2655ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( p != (char *) NULL && p < end)
266602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
267602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* ParseGeometry() needs the geometry separated! -- Arrgghh */
268150989ed67ef9da53141a65e5f3ebdb05dd025abcristy      memcpy(token, kernel_string, (size_t) (p-kernel_string));
269602ab9b30b644a78a4057da93d838a77391ec0acanthony      token[p-kernel_string] = '\0';
270c84dce50867229e4872193e8eed5dbab58eb9f02anthony      SetGeometryInfo(&args);
271602ab9b30b644a78a4057da93d838a77391ec0acanthony      flags = ParseGeometry(token, &args);
272602ab9b30b644a78a4057da93d838a77391ec0acanthony
27329188a8682a98d4b7882cca434b170517555fc7danthony      /* Size handling and checks of geometry settings */
274602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
275602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.rho = args.sigma;         /* then  width = height */
276602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.rho < 1.0 )            /* if width too small */
277602ab9b30b644a78a4057da93d838a77391ec0acanthony         args.rho = 1.0;               /* then  width = 1 */
278602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.sigma < 1.0 )          /* if height too small */
279602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.sigma = args.rho;         /* then  height = width */
280bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = (size_t)args.rho;
281bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->height = (size_t)args.sigma;
282602ab9b30b644a78a4057da93d838a77391ec0acanthony
283602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* Offset Handling and Checks */
284602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.xi  < 0.0 || args.psi < 0.0 )
28583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
286bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
287bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                                               : (ssize_t) (kernel->width-1)/2;
288bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
289bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                                               : (ssize_t) (kernel->height-1)/2;
290bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      if ( kernel->x >= (ssize_t) kernel->width ||
291bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy           kernel->y >= (ssize_t) kernel->height )
29283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
293602ab9b30b644a78a4057da93d838a77391ec0acanthony
294602ab9b30b644a78a4057da93d838a77391ec0acanthony      p++; /* advance beyond the ':' */
295602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
296602ab9b30b644a78a4057da93d838a77391ec0acanthony  else
297c84dce50867229e4872193e8eed5dbab58eb9f02anthony    { /* ELSE - Old old specification, forming odd-square kernel */
298602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* count up number of values given */
299602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
300a699b171eff7e0178463e8f271b35a3cbb995f0ecristy      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
30129188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
3025ef8e94ff55717be2387d537bd49025780a1a558anthony      for (i=0; p < end; i++)
303602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
304602ab9b30b644a78a4057da93d838a77391ec0acanthony        GetMagickToken(p,&p,token);
305602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (*token == ',')
306602ab9b30b644a78a4057da93d838a77391ec0acanthony          GetMagickToken(p,&p,token);
307602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
308602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* set the size of the kernel - old sized square */
309bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
310bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
311602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
31229188a8682a98d4b7882cca434b170517555fc7danthony      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
31329188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
314602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
315602ab9b30b644a78a4057da93d838a77391ec0acanthony
316602ab9b30b644a78a4057da93d838a77391ec0acanthony  /* Read in the kernel values from rest of input string argument */
3175e0444305cde9e2ef31f16111899f14a257a6a54cristy  kernel->values=(double *) AcquireAlignedMemory(kernel->width,
318602ab9b30b644a78a4057da93d838a77391ec0acanthony                        kernel->height*sizeof(double));
319602ab9b30b644a78a4057da93d838a77391ec0acanthony  if (kernel->values == (double *) NULL)
32083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    return(DestroyKernelInfo(kernel));
321602ab9b30b644a78a4057da93d838a77391ec0acanthony
322c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->minimum = +MagickHuge;
323c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->maximum = -MagickHuge;
324c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->negative_range = kernel->positive_range = 0.0;
325c84dce50867229e4872193e8eed5dbab58eb9f02anthony
326bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
327602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
328602ab9b30b644a78a4057da93d838a77391ec0acanthony    GetMagickToken(p,&p,token);
329602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (*token == ',')
330602ab9b30b644a78a4057da93d838a77391ec0acanthony      GetMagickToken(p,&p,token);
33129188a8682a98d4b7882cca434b170517555fc7danthony    if (    LocaleCompare("nan",token) == 0
332c84dce50867229e4872193e8eed5dbab58eb9f02anthony        || LocaleCompare("-",token) == 0 ) {
33329188a8682a98d4b7882cca434b170517555fc7danthony      kernel->values[i] = nan; /* do not include this value in kernel */
33429188a8682a98d4b7882cca434b170517555fc7danthony    }
33529188a8682a98d4b7882cca434b170517555fc7danthony    else {
336c1acd8495db5b00e9ced313e86dfdbe8c60c111dcristy      kernel->values[i] = InterpretLocaleValue(token,(char **) NULL);
33729188a8682a98d4b7882cca434b170517555fc7danthony      ( kernel->values[i] < 0)
338c99304fe3c8d9c617da792b40b57c118bb1249afcristy          ?  ( kernel->negative_range += kernel->values[i] )
339c99304fe3c8d9c617da792b40b57c118bb1249afcristy          :  ( kernel->positive_range += kernel->values[i] );
340c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Minimize(kernel->minimum, kernel->values[i]);
341c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Maximize(kernel->maximum, kernel->values[i]);
34229188a8682a98d4b7882cca434b170517555fc7danthony    }
34329188a8682a98d4b7882cca434b170517555fc7danthony  }
34429188a8682a98d4b7882cca434b170517555fc7danthony
3455ef8e94ff55717be2387d537bd49025780a1a558anthony  /* sanity check -- no more values in kernel definition */
3465ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(p,&p,token);
3475ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( *token != '\0' && *token != ';' && *token != '\'' )
3485ef8e94ff55717be2387d537bd49025780a1a558anthony    return(DestroyKernelInfo(kernel));
3495ef8e94ff55717be2387d537bd49025780a1a558anthony
350c84dce50867229e4872193e8eed5dbab58eb9f02anthony#if 0
351c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* this was the old method of handling a incomplete kernel */
352bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) ) {
353c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Minimize(kernel->minimum, kernel->values[i]);
354c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Maximize(kernel->maximum, kernel->values[i]);
355bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
35629188a8682a98d4b7882cca434b170517555fc7danthony      kernel->values[i]=0.0;
357602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
358c84dce50867229e4872193e8eed5dbab58eb9f02anthony#else
359c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Number of values for kernel was not enough - Report Error */
360bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) )
361c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
362c84dce50867229e4872193e8eed5dbab58eb9f02anthony#endif
363c84dce50867229e4872193e8eed5dbab58eb9f02anthony
364c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* check that we recieved at least one real (non-nan) value! */
365c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( kernel->minimum == MagickHuge )
366c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
367602ab9b30b644a78a4057da93d838a77391ec0acanthony
36843c4925e5305a26e48d68f7893e94f55d0831c39anthony  if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
369bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
370bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
371bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
372bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
373bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
37443c4925e5305a26e48d68f7893e94f55d0831c39anthony
375602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
376602ab9b30b644a78a4057da93d838a77391ec0acanthony}
377c84dce50867229e4872193e8eed5dbab58eb9f02anthony
37843c4925e5305a26e48d68f7893e94f55d0831c39anthonystatic KernelInfo *ParseKernelName(const char *kernel_string)
379c84dce50867229e4872193e8eed5dbab58eb9f02anthony{
380c84dce50867229e4872193e8eed5dbab58eb9f02anthony  char
381c84dce50867229e4872193e8eed5dbab58eb9f02anthony    token[MaxTextExtent];
382c84dce50867229e4872193e8eed5dbab58eb9f02anthony
383c84dce50867229e4872193e8eed5dbab58eb9f02anthony  const char
3847a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *p,
3857a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *end;
386c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3879d314ff2c17a77996c05413c2013880387e50f0ecristy  GeometryInfo
3889d314ff2c17a77996c05413c2013880387e50f0ecristy    args;
3899d314ff2c17a77996c05413c2013880387e50f0ecristy
3909d314ff2c17a77996c05413c2013880387e50f0ecristy  KernelInfo
3919d314ff2c17a77996c05413c2013880387e50f0ecristy    *kernel;
3929d314ff2c17a77996c05413c2013880387e50f0ecristy
393c84dce50867229e4872193e8eed5dbab58eb9f02anthony  MagickStatusType
394c84dce50867229e4872193e8eed5dbab58eb9f02anthony    flags;
395c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3969d314ff2c17a77996c05413c2013880387e50f0ecristy  ssize_t
3979d314ff2c17a77996c05413c2013880387e50f0ecristy    type;
398c84dce50867229e4872193e8eed5dbab58eb9f02anthony
399c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Parse special 'named' kernel */
4005ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(kernel_string,&p,token);
401042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy  type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
402c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( type < 0 || type == UserDefinedKernel )
4035ef8e94ff55717be2387d537bd49025780a1a558anthony    return((KernelInfo *)NULL);  /* not a valid named kernel */
404c84dce50867229e4872193e8eed5dbab58eb9f02anthony
405c84dce50867229e4872193e8eed5dbab58eb9f02anthony  while (((isspace((int) ((unsigned char) *p)) != 0) ||
4065ef8e94ff55717be2387d537bd49025780a1a558anthony          (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
407c84dce50867229e4872193e8eed5dbab58eb9f02anthony    p++;
4087a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4097a01dcf50ce12cb2a789bedff51e9345f022432eanthony  end = strchr(p, ';'); /* end of this kernel defintion */
4107a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( end == (char *) NULL )
4117a01dcf50ce12cb2a789bedff51e9345f022432eanthony    end = strchr(p, '\0');
4127a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4137a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* ParseGeometry() needs the geometry separated! -- Arrgghh */
4147a01dcf50ce12cb2a789bedff51e9345f022432eanthony  memcpy(token, p, (size_t) (end-p));
4157a01dcf50ce12cb2a789bedff51e9345f022432eanthony  token[end-p] = '\0';
416c84dce50867229e4872193e8eed5dbab58eb9f02anthony  SetGeometryInfo(&args);
4177a01dcf50ce12cb2a789bedff51e9345f022432eanthony  flags = ParseGeometry(token, &args);
418c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4193c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#if 0
4203c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* For Debugging Geometry Input */
4215acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4221e604812fad85bb96f757a2393015ae3d061c39acristy    flags, args.rho, args.sigma, args.xi, args.psi );
4233c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#endif
4243c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
425c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* special handling of missing values in input string */
426c84dce50867229e4872193e8eed5dbab58eb9f02anthony  switch( type ) {
427a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Shape Kernel Defaults */
428529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
429529482f4b494010a13338a74446c510712f670b3anthony      if ( (flags & WidthValue) == 0 )
430a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
4315ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4325ef8e94ff55717be2387d537bd49025780a1a558anthony    case SquareKernel:
4335ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiamondKernel:
4341ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
4355ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiskKernel:
4365ef8e94ff55717be2387d537bd49025780a1a558anthony    case PlusKernel:
4373dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
4385ef8e94ff55717be2387d537bd49025780a1a558anthony      if ( (flags & HeightValue) == 0 )
439a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
4405ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
441c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
442c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if ( (flags & XValue) == 0 )
443a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
444c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
445a9892d898acb81e1ec73106d892855fdc5a69427anthony    case RectangleKernel:    /* Rectangle - set size defaults */
446a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
447a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = args.sigma;         /* then  width = height */
448a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.rho < 1.0 )            /* if width too small */
449a9892d898acb81e1ec73106d892855fdc5a69427anthony          args.rho = 3;                /* then  width = 3 */
450a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.sigma < 1.0 )          /* if height too small */
451a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = args.rho;         /* then  height = width */
452a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & XValue) == 0 )     /* center offset if not defined */
453a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = (double)(((ssize_t)args.rho-1)/2);
454a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & YValue) == 0 )
455a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.psi = (double)(((ssize_t)args.sigma-1)/2);
456a9892d898acb81e1ec73106d892855fdc5a69427anthony      break;
457a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Distance Kernel Defaults */
4585ef8e94ff55717be2387d537bd49025780a1a558anthony    case ChebyshevKernel:
459bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
4601ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonalKernel:
4615ef8e94ff55717be2387d537bd49025780a1a558anthony    case EuclideanKernel:
46243c4925e5305a26e48d68f7893e94f55d0831c39anthony      if ( (flags & HeightValue) == 0 )           /* no distance scale */
46343c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = 100.0;                       /* default distance scaling */
46443c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
46543c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
46643c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
46743c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma *= QuantumRange/100.0;         /* percentage of color range */
4685ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4695ef8e94ff55717be2387d537bd49025780a1a558anthony    default:
4705ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
471c84dce50867229e4872193e8eed5dbab58eb9f02anthony  }
472c84dce50867229e4872193e8eed5dbab58eb9f02anthony
473f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
474529482f4b494010a13338a74446c510712f670b3anthony  if ( kernel == (KernelInfo *) NULL )
475529482f4b494010a13338a74446c510712f670b3anthony    return(kernel);
476f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
477f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  /* global expand to rotated kernel list - only for single kernels */
478f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  if ( kernel->next == (KernelInfo *) NULL ) {
479f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony    if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
480bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 45.0);
481bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
482bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 90.0);
483bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
484bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandMirrorKernelInfo(kernel);
485f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  }
486f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
487f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  return(kernel);
488c84dce50867229e4872193e8eed5dbab58eb9f02anthony}
489c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4905ef8e94ff55717be2387d537bd49025780a1a558anthonyMagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
4915ef8e94ff55717be2387d537bd49025780a1a558anthony{
4927a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4937a01dcf50ce12cb2a789bedff51e9345f022432eanthony  KernelInfo
494dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *kernel,
49543c4925e5305a26e48d68f7893e94f55d0831c39anthony    *new_kernel;
4967a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4975ef8e94ff55717be2387d537bd49025780a1a558anthony  char
4985ef8e94ff55717be2387d537bd49025780a1a558anthony    token[MaxTextExtent];
4995ef8e94ff55717be2387d537bd49025780a1a558anthony
5007a01dcf50ce12cb2a789bedff51e9345f022432eanthony  const char
501dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *p;
5027a01dcf50ce12cb2a789bedff51e9345f022432eanthony
503bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
504e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony    kernel_number;
505e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
5065e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
5075e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(ParseKernelArray(kernel_string));
508dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  p = kernel_string;
50943c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel = NULL;
510e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony  kernel_number = 0;
5117a01dcf50ce12cb2a789bedff51e9345f022432eanthony
512dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
5137a01dcf50ce12cb2a789bedff51e9345f022432eanthony
5141e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp    /* ignore extra or multiple ';' kernel separators */
515dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    if ( *token != ';' ) {
5167a01dcf50ce12cb2a789bedff51e9345f022432eanthony
517dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      /* tokens starting with alpha is a Named kernel */
51843c4925e5305a26e48d68f7893e94f55d0831c39anthony      if (isalpha((int) *token) != 0)
51943c4925e5305a26e48d68f7893e94f55d0831c39anthony        new_kernel = ParseKernelName(p);
520dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      else /* otherwise a user defined kernel array */
52143c4925e5305a26e48d68f7893e94f55d0831c39anthony        new_kernel = ParseKernelArray(p);
522dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
523e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      /* Error handling -- this is not proper error handling! */
524e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      if ( new_kernel == (KernelInfo *) NULL ) {
5255acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Failed to parse kernel number #%.20g\n",
5261e604812fad85bb96f757a2393015ae3d061c39acristy          (double) kernel_number);
527e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        if ( kernel != (KernelInfo *) NULL )
528e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony          kernel=DestroyKernelInfo(kernel);
529e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        return((KernelInfo *) NULL);
530dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      }
531e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
532e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      /* initialise or append the kernel list */
5333dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony      if ( kernel == (KernelInfo *) NULL )
5343dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        kernel = new_kernel;
5353dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony      else
53643c4925e5305a26e48d68f7893e94f55d0831c39anthony        LastKernelInfo(kernel)->next = new_kernel;
537dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    }
538dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
539dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    /* look for the next kernel in list */
540dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p = strchr(p, ';');
541dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    if ( p == (char *) NULL )
542dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      break;
543dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p++;
5445ef8e94ff55717be2387d537bd49025780a1a558anthony
545dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  }
5467a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(kernel);
5475ef8e94ff55717be2387d537bd49025780a1a558anthony}
5485ef8e94ff55717be2387d537bd49025780a1a558anthony
549602ab9b30b644a78a4057da93d838a77391ec0acanthony
550602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
551602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
552602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
553602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
554602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
555602ab9b30b644a78a4057da93d838a77391ec0acanthony%     A c q u i r e K e r n e l B u i l t I n                                 %
556602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
557602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
558602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
559602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560602ab9b30b644a78a4057da93d838a77391ec0acanthony%
561602ab9b30b644a78a4057da93d838a77391ec0acanthony%  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
562602ab9b30b644a78a4057da93d838a77391ec0acanthony%  kernels used for special purposes such as gaussian blurring, skeleton
563602ab9b30b644a78a4057da93d838a77391ec0acanthony%  pruning, and edge distance determination.
564602ab9b30b644a78a4057da93d838a77391ec0acanthony%
565602ab9b30b644a78a4057da93d838a77391ec0acanthony%  They take a KernelType, and a set of geometry style arguments, which were
566602ab9b30b644a78a4057da93d838a77391ec0acanthony%  typically decoded from a user supplied string, or from a more complex
567602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Morphology Method that was requested.
568602ab9b30b644a78a4057da93d838a77391ec0acanthony%
569602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernalBuiltIn method is:
570602ab9b30b644a78a4057da93d838a77391ec0acanthony%
5712be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
572602ab9b30b644a78a4057da93d838a77391ec0acanthony%           const GeometryInfo args)
573602ab9b30b644a78a4057da93d838a77391ec0acanthony%
574602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
575602ab9b30b644a78a4057da93d838a77391ec0acanthony%
576602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o type: the pre-defined type of kernel wanted
577602ab9b30b644a78a4057da93d838a77391ec0acanthony%
578602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o args: arguments defining or modifying the kernel
579602ab9b30b644a78a4057da93d838a77391ec0acanthony%
580602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Convolution Kernels
581602ab9b30b644a78a4057da93d838a77391ec0acanthony%
58246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    Unity
583529482f4b494010a13338a74446c510712f670b3anthony%       The a No-Op or Scaling single element kernel.
58446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
5853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Gaussian:{radius},{sigma}
5862489f53a1153c2b619b1c9a6744602e8840bd9a9glennrp%       Generate a two-dimensional gaussian kernel, as used by -gaussian.
587c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The sigma for the curve is required.  The resulting kernel is
588c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       normalized,
589c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
590c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
591602ab9b30b644a78a4057da93d838a77391ec0acanthony%
592602ab9b30b644a78a4057da93d838a77391ec0acanthony%       NOTE: that the 'radius' is optional, but if provided can limit (clip)
593602ab9b30b644a78a4057da93d838a77391ec0acanthony%       the final size of the resulting kernel to a square 2*radius+1 in size.
594602ab9b30b644a78a4057da93d838a77391ec0acanthony%       The radius should be at least 2 times that of the sigma value, or
595602ab9b30b644a78a4057da93d838a77391ec0acanthony%       sever clipping and aliasing may result.  If not given or set to 0 the
596602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius will be determined so as to produce the best minimal error
597602ab9b30b644a78a4057da93d838a77391ec0acanthony%       result, which is usally much larger than is normally needed.
598602ab9b30b644a78a4057da93d838a77391ec0acanthony%
599501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    LoG:{radius},{sigma}
600501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
601501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        The supposed ideal edge detection, zero-summing kernel.
602501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
603501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
604501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        approx 1.6 (according to wikipedia).
605501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
606501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    DoG:{radius},{sigma1},{sigma2}
607c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        "Difference of Gaussians" Kernel.
608c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
609c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
610c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        The result is a zero-summing kernel.
611c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
612c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Blur:{radius},{sigma}[,{angle}]
6134c08aed51c5899665ade97263692328eea4af106cristy%       Generates a 1 dimensional or linear gaussian blur, at the angle given
614c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       (current restricted to orthogonal angles).  If a 'radius' is given the
615c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
616c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       by a 90 degree angle.
617c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
618c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
619c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
620c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Note that two convolutions with two "Blur" kernels perpendicular to
621f0a92fd8deb68d411304359906b12679b675691fglennrp%       each other, is equivalent to a far larger "Gaussian" kernel with the
622c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       same sigma value, However it is much faster to apply. This is how the
623c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       "-blur" operator actually works.
624c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
6253c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Comet:{width},{sigma},{angle}
6263c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Blur in one direction only, much like how a bright object leaves
627602ab9b30b644a78a4057da93d838a77391ec0acanthony%       a comet like trail.  The Kernel is actually half a gaussian curve,
6283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Adding two such blurs in opposite directions produces a Blur Kernel.
6293c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Angle can be rotated in multiples of 90 degrees.
630602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6313c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Note that the first argument is the width of the kernel and not the
632602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius of the kernel.
633602ab9b30b644a78a4057da93d838a77391ec0acanthony%
634602ab9b30b644a78a4057da93d838a77391ec0acanthony%    # Still to be implemented...
635602ab9b30b644a78a4057da93d838a77391ec0acanthony%    #
6364fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter2D
6374fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter1D
6384fd27e21043be809d66c8202e779255e5b660d2danthony%    #    Set kernel values using a resize filter, and given scale (sigma)
639dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%    #    Cylindrical or Linear.   Is this possible with an image?
6404fd27e21043be809d66c8202e779255e5b660d2danthony%    #
641602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6423c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  Named Constant Convolution Kernels
6433c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
644c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  All these are unscaled, zero-summing kernels by default. As such for
645c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  non-HDRI version of ImageMagick some form of normalization, user scaling,
646c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  and biasing the results is recommended, to prevent the resulting image
647c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  being 'clipped'.
648c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
649c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  The 3x3 kernels (most of these) can be circularly rotated in multiples of
650c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  45 degrees to generate the 8 angled varients of each of the kernels.
6513c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
6523c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Laplacian:{type}
65343c4925e5305a26e48d68f7893e94f55d0831c39anthony%      Discrete Lapacian Kernels, (without normalization)
654c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
655c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
6569eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 2 :  3x3 with center:4 edge:1 corner:-2
6579eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 3 :  3x3 with center:4 edge:-2 corner:1
6589eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 5 :  5x5 laplacian
6599eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 7 :  7x7 laplacian
660501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 15 : 5x5 LoG (sigma approx 1.4)
661501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 19 : 9x9 LoG (sigma approx 1.4)
662c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
663c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Sobel:{angle}
66446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Sobel 'Edge' convolution kernel (3x3)
665c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
666c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -2, 0,-2 |
667c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
668c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
669c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Roberts:{angle}
67046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Roberts convolution kernel (3x3)
671c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
672c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 0 |
673c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
674c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
675c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Prewitt:{angle}
676c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%      Prewitt Edge convolution kernel (3x3)
677c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
678c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
679c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
680c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6819eb4f74649b23c053b308ce1152dce51239450baanthony%    Compass:{angle}
6829eb4f74649b23c053b308ce1152dce51239450baanthony%      Prewitt's "Compass" convolution kernel (3x3)
683c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
684c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1,-2, 1 |
685c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
686c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6879eb4f74649b23c053b308ce1152dce51239450baanthony%    Kirsch:{angle}
6889eb4f74649b23c053b308ce1152dce51239450baanthony%      Kirsch's "Compass" convolution kernel (3x3)
689c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
690c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3, 0, 5 |
691c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
6923c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
693c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{angle}
6941d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Frei-Chen Edge Detector is based on a kernel that is similar to
6951d5e67090dc7232b35bfcc71b31266c20838defcanthony%      the Sobel Kernel, but is designed to be isotropic. That is it takes
6961d5e67090dc7232b35bfcc71b31266c20838defcanthony%      into account the distance of the diagonal in the kernel.
697c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
698c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
699c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) |
700c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
701c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
702c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{type},{angle}
703c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
704c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      Frei-Chen Pre-weighted kernels...
705c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
706c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 0:  default un-nomalized version shown above.
707c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
708c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 1: Orthogonal Kernel (same as type 11 below)
709c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
710c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
711c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
712c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
713c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 2: Diagonal form of Kernel...
714c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     sqrt(2),    0     |
715c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
716c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   0,    -sqrt(2)    -1     |
717c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7181d5e67090dc7232b35bfcc71b31266c20838defcanthony%      However this kernel is als at the heart of the FreiChen Edge Detection
7191d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Process which uses a set of 9 specially weighted kernel.  These 9
7201d5e67090dc7232b35bfcc71b31266c20838defcanthony%      kernels not be normalized, but directly applied to the image. The
7211d5e67090dc7232b35bfcc71b31266c20838defcanthony%      results is then added together, to produce the intensity of an edge in
7221d5e67090dc7232b35bfcc71b31266c20838defcanthony%      a specific direction.  The square root of the pixel value can then be
7231d5e67090dc7232b35bfcc71b31266c20838defcanthony%      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
7241d5e67090dc7232b35bfcc71b31266c20838defcanthony%      from each other, both the direction and the strength of the edge can be
7251d5e67090dc7232b35bfcc71b31266c20838defcanthony%      determined.
7261d5e67090dc7232b35bfcc71b31266c20838defcanthony%
727c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 10: All 9 of the following pre-weighted kernels...
728c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
729c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 11: |   1,     0,   -1     |
730c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
731c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   1,     0,   -1     |
732e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
733c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 12: | 1, sqrt(2), 1 |
734c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0,   0,     0 | / 2*sqrt(2)
735c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, sqrt(2), 1 |
736e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
737c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 13: | sqrt(2), -1,    0     |
738c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  -1,      0,    1     | / 2*sqrt(2)
739c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   0,      1, -sqrt(2) |
740e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
741c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 14: |    0,     1, -sqrt(2) |
742c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   -1,     0,     1    | / 2*sqrt(2)
743c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), -1,     0    |
744e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
745c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 15: | 0, -1, 0 |
746c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1,  0, 1 | / 2
747c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0, -1, 0 |
748e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
749c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 16: |  1, 0, -1 |
750c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  0, 0,  0 | / 2
751c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, 0,  1 |
752e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
753c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 17: |  1, -2,  1 |
754c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2,  4, -2 | / 6
755c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, -2,  1 |
756501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
757c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 18: | -2, 1, -2 |
758c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  1, 4,  1 | / 6
759c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2, 1, -2 |
760e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
761c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 19: | 1, 1, 1 |
762c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 | / 3
763c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 |
764e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
765e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      The first 4 are for edge detection, the next 4 are for line detection
766e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      and the last is to add a average component to the results.
767e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
768c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      Using a special type of '-1' will return all 9 pre-weighted kernels
769c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      as a multi-kernel list, so that you can use them directly (without
770c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      normalization) with the special "-set option:morphology:compose Plus"
771c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      setting to apply the full FreiChen Edge Detection Technique.
772c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7731dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      If 'type' is large it will be taken to be an actual rotation angle for
7741dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
7751dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
7761dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%
777501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      WARNING: The above was layed out as per
778501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
779501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      But rotated 90 degrees so direction is from left rather than the top.
780501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      I have yet to find any secondary confirmation of the above. The only
781501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      other source found was actual source code at
782501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
783501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      Neigher paper defineds the kernels in a way that looks locical or
784501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      correct when taken as a whole.
785e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
786602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Boolean Kernels
787602ab9b30b644a78a4057da93d838a77391ec0acanthony%
7883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Diamond:[{radius}[,{scale}]]
7891b2bc0a7da432e6e1cc0480280402df213faa940anthony%       Generate a diamond shaped kernel with given radius to the points.
790602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Kernel size will again be radius*2+1 square and defaults to radius 1,
791602ab9b30b644a78a4057da93d838a77391ec0acanthony%       generating a 3x3 kernel that is slightly larger than a square.
792602ab9b30b644a78a4057da93d838a77391ec0acanthony%
7933c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Square:[{radius}[,{scale}]]
794602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Generate a square shaped kernel of size radius*2+1, and defaulting
795602ab9b30b644a78a4057da93d838a77391ec0acanthony%       to a 3x3 (radius 1).
796602ab9b30b644a78a4057da93d838a77391ec0acanthony%
7971ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagon:[{radius}[,{scale}]]
7981ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate octagonal shaped kernel of given radius and constant scale.
7990bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
8001ef941fea2534a0d20ba7d71307d35040247decbanthony%       in "Diamond" kernel.
8011ef941fea2534a0d20ba7d71307d35040247decbanthony%
8023c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Disk:[{radius}[,{scale}]]
8031ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate a binary disk, thresholded at the radius given, the radius
8041ef941fea2534a0d20ba7d71307d35040247decbanthony%       may be a float-point value. Final Kernel size is floor(radius)*2+1
8050bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       square. A radius of 5.3 is the default.
8061ef941fea2534a0d20ba7d71307d35040247decbanthony%
8071ef941fea2534a0d20ba7d71307d35040247decbanthony%       NOTE: That a low radii Disk kernels produce the same results as
8081ef941fea2534a0d20ba7d71307d35040247decbanthony%       many of the previously defined kernels, but differ greatly at larger
8091ef941fea2534a0d20ba7d71307d35040247decbanthony%       radii.  Here is a table of equivalences...
8101ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
8111ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1.5"  => "Square"
8121ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2"    => "Diamond:2"
8131ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.5"  => "Octagon"
8141ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.9"  => "Square:2"
8150bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%          "Disk:3.5"  => "Octagon:3"
8161ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:4.5"  => "Octagon:4"
8171ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:5.4"  => "Octagon:5"
8181ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:6.4"  => "Octagon:6"
8191ef941fea2534a0d20ba7d71307d35040247decbanthony%       All other Disk shapes are unique to this kernel, but because a "Disk"
8201ef941fea2534a0d20ba7d71307d35040247decbanthony%       is more circular when using a larger radius, using a larger radius is
8211ef941fea2534a0d20ba7d71307d35040247decbanthony%       preferred over iterating the morphological operation.
822602ab9b30b644a78a4057da93d838a77391ec0acanthony%
823a9892d898acb81e1ec73106d892855fdc5a69427anthony%    Rectangle:{geometry}
824a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Simply generate a rectangle of 1's with the size given. You can also
825a9892d898acb81e1ec73106d892855fdc5a69427anthony%       specify the location of the 'control point', otherwise the closest
826a9892d898acb81e1ec73106d892855fdc5a69427anthony%       pixel to the center of the rectangle is selected.
827a9892d898acb81e1ec73106d892855fdc5a69427anthony%
828a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Properly centered and odd sized rectangles work the best.
829a9892d898acb81e1ec73106d892855fdc5a69427anthony%
830c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  Symbol Dilation Kernels
831c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
832c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    These kernel is not a good general morphological kernel, but is used
833c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    more for highlighting and marking any single pixels in an image using,
834c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a "Dilate" method as appropriate.
835c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
836c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    For the same reasons iterating these kernels does not produce the
837c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    same result as using a larger radius for the symbol.
838c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
8393c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Plus:[{radius}[,{scale}]]
8403dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Cross:[{radius}[,{scale}]]
841c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Generate a kernel in the shape of a 'plus' or a 'cross' with
842c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       a each arm the length of the given radius (default 2).
8433dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
844f0a92fd8deb68d411304359906b12679b675691fglennrp%       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
845602ab9b30b644a78a4057da93d838a77391ec0acanthony%
846c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Ring:{radius1},{radius2}[,{scale}]
847c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       A ring of the values given that falls between the two radii.
848c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
849c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       This is the 'edge' pixels of the default "Disk" kernel,
850c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
8513dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
8523dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%  Hit and Miss Kernels
853602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8543dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Peak:radius1,radius2
855c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Find any peak larger than the pixels the fall between the two radii.
856c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The default ring of pixels is as per "Ring".
85743c4925e5305a26e48d68f7893e94f55d0831c39anthony%    Edges
858694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find flat orthogonal edges of a binary shape
8593dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Corners
860694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find 90 degree corners of a binary shape
861529482f4b494010a13338a74446c510712f670b3anthony%    Diagonals:type
862529482f4b494010a13338a74446c510712f670b3anthony%       A special kernel to thin the 'outside' of diagonals
863694934fa79dd310f727588b1d0a7481fa6170f1danthony%    LineEnds:type
8643dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Find end points of lines (for pruning a skeletion)
865694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Two types of lines ends (default to both) can be searched for
866694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: All line ends
867694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: single kernel for 4-conneected line ends
868694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: single kernel for simple line ends
8693dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    LineJunctions
87043c4925e5305a26e48d68f7893e94f55d0831c39anthony%       Find three line junctions (within a skeletion)
871694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: all line junctions
872694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Y Junction kernel
873694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Diagonal T Junction kernel
874694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 3: Orthogonal T Junction kernel
875694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 4: Diagonal X Junction kernel
876694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 5: Orthogonal + Junction kernel
877694934fa79dd310f727588b1d0a7481fa6170f1danthony%    Ridges:type
878694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find single pixel ridges or thin lines
879694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Fine single pixel thick lines and ridges
880694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Find two pixel thick lines and ridges
8813dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    ConvexHull
882a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
883c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    Skeleton:type
884c40ac1e79923a1516075ba1197ae4ed90244af9banthony%       Traditional skeleton generating kernels.
885694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Tradional Skeleton kernel (4 connected skeleton)
886694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
887e816a586a13717bab2d6839ced6e5c3828a37f19anthony%         Type 3: Thinning skeleton based on a ressearch paper by
8882b2290b46c246ce1f14cb78f1695394e4c4a3ddfanthony%                 Dan S. Bloomberg (Default Type)
889e816a586a13717bab2d6839ced6e5c3828a37f19anthony%    ThinSE:type
890e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       A huge variety of Thinning Kernels designed to preserve conectivity.
891e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       many other kernel sets use these kernels as source definitions.
892e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       Type numbers are 41-49, 81-89, 481, and 482 which are based on
893e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       the super and sub notations used in the source research paper.
894602ab9b30b644a78a4057da93d838a77391ec0acanthony%
895602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Distance Measuring Kernels
896602ab9b30b644a78a4057da93d838a77391ec0acanthony%
897c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Different types of distance measuring methods, which are used with the
898c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a 'Distance' morphology method for generating a gradient based on
899c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    distance from an edge of a binary shape, though there is a technique
900c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    for handling a anti-aliased shape.
901602ab9b30b644a78a4057da93d838a77391ec0acanthony%
902c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    See the 'Distance' Morphological Method, for information of how it is
903c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    applied.
904602ab9b30b644a78a4057da93d838a77391ec0acanthony%
905c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Chebyshev:[{radius}][x{scale}[%!]]
9061ef941fea2534a0d20ba7d71307d35040247decbanthony%       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
9071ef941fea2534a0d20ba7d71307d35040247decbanthony%       is a value of one to any neighbour, orthogonal or diagonal. One why
9081ef941fea2534a0d20ba7d71307d35040247decbanthony%       of thinking of it is the number of squares a 'King' or 'Queen' in
9091ef941fea2534a0d20ba7d71307d35040247decbanthony%       chess needs to traverse reach any other position on a chess board.
9101ef941fea2534a0d20ba7d71307d35040247decbanthony%       It results in a 'square' like distance function, but one where
9111ef941fea2534a0d20ba7d71307d35040247decbanthony%       diagonals are given a value that is closer than expected.
912c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
913bee715c4c0fd9efe6e21d8627ae8664434df7750anthony%    Manhattan:[{radius}][x{scale}[%!]]
9141ef941fea2534a0d20ba7d71307d35040247decbanthony%       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
9151ef941fea2534a0d20ba7d71307d35040247decbanthony%       Cab distance metric), it is the distance needed when you can only
9161ef941fea2534a0d20ba7d71307d35040247decbanthony%       travel in horizontal or vertical directions only.  It is the
9171ef941fea2534a0d20ba7d71307d35040247decbanthony%       distance a 'Rook' in chess would have to travel, and results in a
9181ef941fea2534a0d20ba7d71307d35040247decbanthony%       diamond like distances, where diagonals are further than expected.
9191ef941fea2534a0d20ba7d71307d35040247decbanthony%
9201ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagonal:[{radius}][x{scale}[%!]]
9211ef941fea2534a0d20ba7d71307d35040247decbanthony%       An interleving of Manhatten and Chebyshev metrics producing an
9221ef941fea2534a0d20ba7d71307d35040247decbanthony%       increasing octagonally shaped distance.  Distances matches those of
9231ef941fea2534a0d20ba7d71307d35040247decbanthony%       the "Octagon" shaped kernel of the same radius.  The minimum radius
9241ef941fea2534a0d20ba7d71307d35040247decbanthony%       and default is 2, producing a 5x5 kernel.
925c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
926c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Euclidean:[{radius}][x{scale}[%!]]
9271ef941fea2534a0d20ba7d71307d35040247decbanthony%       Euclidean distance is the 'direct' or 'as the crow flys' distance.
928c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       However by default the kernel size only has a radius of 1, which
929c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       limits the distance to 'Knight' like moves, with only orthogonal and
930c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       diagonal measurements being correct.  As such for the default kernel
9311ef941fea2534a0d20ba7d71307d35040247decbanthony%       you will get octagonal like distance function.
9321ef941fea2534a0d20ba7d71307d35040247decbanthony%
9331ef941fea2534a0d20ba7d71307d35040247decbanthony%       However using a larger radius such as "Euclidean:4" you will get a
9341ef941fea2534a0d20ba7d71307d35040247decbanthony%       much smoother distance gradient from the edge of the shape. Especially
9351ef941fea2534a0d20ba7d71307d35040247decbanthony%       if the image is pre-processed to include any anti-aliasing pixels.
9361ef941fea2534a0d20ba7d71307d35040247decbanthony%       Of course a larger kernel is slower to use, and not always needed.
9371ef941fea2534a0d20ba7d71307d35040247decbanthony%
9381ef941fea2534a0d20ba7d71307d35040247decbanthony%    The first three Distance Measuring Kernels will only generate distances
9391ef941fea2534a0d20ba7d71307d35040247decbanthony%    of exact multiples of {scale} in binary images. As such you can use a
9401ef941fea2534a0d20ba7d71307d35040247decbanthony%    scale of 1 without loosing any information.  However you also need some
9411ef941fea2534a0d20ba7d71307d35040247decbanthony%    scaling when handling non-binary anti-aliased shapes.
9421ef941fea2534a0d20ba7d71307d35040247decbanthony%
9431ef941fea2534a0d20ba7d71307d35040247decbanthony%    The "Euclidean" Distance Kernel however does generate a non-integer
9441ef941fea2534a0d20ba7d71307d35040247decbanthony%    fractional results, and as such scaling is vital even for binary shapes.
945c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
946602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
947602ab9b30b644a78a4057da93d838a77391ec0acanthony
9482be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristyMagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
949602ab9b30b644a78a4057da93d838a77391ec0acanthony   const GeometryInfo *args)
950602ab9b30b644a78a4057da93d838a77391ec0acanthony{
9512be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
952602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
953602ab9b30b644a78a4057da93d838a77391ec0acanthony
954bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
955602ab9b30b644a78a4057da93d838a77391ec0acanthony    i;
956602ab9b30b644a78a4057da93d838a77391ec0acanthony
957bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
958602ab9b30b644a78a4057da93d838a77391ec0acanthony    u,
959602ab9b30b644a78a4057da93d838a77391ec0acanthony    v;
960602ab9b30b644a78a4057da93d838a77391ec0acanthony
961602ab9b30b644a78a4057da93d838a77391ec0acanthony  double
962602ab9b30b644a78a4057da93d838a77391ec0acanthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
963602ab9b30b644a78a4057da93d838a77391ec0acanthony
964c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  /* Generate a new empty kernel if needed */
965e96405a0f45f803fb9c26f75e7bdee252437febbcristy  kernel=(KernelInfo *) NULL;
966c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  switch(type) {
9671dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case UndefinedKernel:    /* These should not call this function */
9689eb4f74649b23c053b308ce1152dce51239450baanthony    case UserDefinedKernel:
969529482f4b494010a13338a74446c510712f670b3anthony      assert("Should not call this function" != (char *)NULL);
9709eb4f74649b23c053b308ce1152dce51239450baanthony      break;
971529482f4b494010a13338a74446c510712f670b3anthony    case LaplacianKernel:   /* Named Descrete Convolution Kernels */
972529482f4b494010a13338a74446c510712f670b3anthony    case SobelKernel:       /* these are defined using other kernels */
9739eb4f74649b23c053b308ce1152dce51239450baanthony    case RobertsKernel:
9749eb4f74649b23c053b308ce1152dce51239450baanthony    case PrewittKernel:
9759eb4f74649b23c053b308ce1152dce51239450baanthony    case CompassKernel:
9769eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
9771dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case FreiChenKernel:
978694934fa79dd310f727588b1d0a7481fa6170f1danthony    case EdgesKernel:       /* Hit and Miss kernels */
979694934fa79dd310f727588b1d0a7481fa6170f1danthony    case CornersKernel:
980529482f4b494010a13338a74446c510712f670b3anthony    case DiagonalsKernel:
9819eb4f74649b23c053b308ce1152dce51239450baanthony    case LineEndsKernel:
9829eb4f74649b23c053b308ce1152dce51239450baanthony    case LineJunctionsKernel:
9831dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case RidgesKernel:
9849eb4f74649b23c053b308ce1152dce51239450baanthony    case ConvexHullKernel:
9859eb4f74649b23c053b308ce1152dce51239450baanthony    case SkeletonKernel:
9869a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony    case ThinSEKernel:
987c40ac1e79923a1516075ba1197ae4ed90244af9banthony      break;               /* A pre-generated kernel is not needed */
988c40ac1e79923a1516075ba1197ae4ed90244af9banthony#if 0
989c40ac1e79923a1516075ba1197ae4ed90244af9banthony    /* set to 1 to do a compile-time check that we haven't missed anything */
990529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
991c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case GaussianKernel:
992501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
993501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
994c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case BlurKernel:
995c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CometKernel:
996c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
997c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
998c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
9991ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
1000c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiskKernel:
1001c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PlusKernel:
1002c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CrossKernel:
1003c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
1004c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PeaksKernel:
1005c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case ChebyshevKernel:
1006bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
10071ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctangonalKernel:
1008c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case EuclideanKernel:
10091dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#else
10109eb4f74649b23c053b308ce1152dce51239450baanthony    default:
10111dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#endif
10129eb4f74649b23c053b308ce1152dce51239450baanthony      /* Generate the base Kernel Structure */
1013c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1014c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if (kernel == (KernelInfo *) NULL)
1015c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        return(kernel);
1016c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
101743c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1018c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->negative_range = kernel->positive_range = 0.0;
1019c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->type = type;
1020c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->next = (KernelInfo *) NULL;
1021c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->signature = MagickSignature;
1022c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
1023c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  }
1024602ab9b30b644a78a4057da93d838a77391ec0acanthony
1025602ab9b30b644a78a4057da93d838a77391ec0acanthony  switch(type) {
1026529482f4b494010a13338a74446c510712f670b3anthony    /*
1027529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels
1028529482f4b494010a13338a74446c510712f670b3anthony    */
1029529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
1030529482f4b494010a13338a74446c510712f670b3anthony      {
1031529482f4b494010a13338a74446c510712f670b3anthony        kernel->height = kernel->width = (size_t) 1;
1032529482f4b494010a13338a74446c510712f670b3anthony        kernel->x = kernel->y = (ssize_t) 0;
10335e0444305cde9e2ef31f16111899f14a257a6a54cristy        kernel->values=(double *) AcquireAlignedMemory(1,sizeof(double));
1034529482f4b494010a13338a74446c510712f670b3anthony        if (kernel->values == (double *) NULL)
1035529482f4b494010a13338a74446c510712f670b3anthony          return(DestroyKernelInfo(kernel));
1036529482f4b494010a13338a74446c510712f670b3anthony        kernel->maximum = kernel->values[0] = args->rho;
1037529482f4b494010a13338a74446c510712f670b3anthony        break;
1038529482f4b494010a13338a74446c510712f670b3anthony      }
1039529482f4b494010a13338a74446c510712f670b3anthony      break;
1040602ab9b30b644a78a4057da93d838a77391ec0acanthony    case GaussianKernel:
1041501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
1042501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
1043602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1044c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1045c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma2 = fabs(args->xi),
10469eb4f74649b23c053b308ce1152dce51239450baanthony          A, B, R;
1047c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1048c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1049bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1050501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        else if ( (type != DoGKernel) || (sigma >= sigma2) )
1051c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1052c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1053c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1054c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->height = kernel->width;
1055bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
10565e0444305cde9e2ef31f16111899f14a257a6a54cristy        kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1057602ab9b30b644a78a4057da93d838a77391ec0acanthony                              kernel->height*sizeof(double));
1058602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (kernel->values == (double *) NULL)
105983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1060602ab9b30b644a78a4057da93d838a77391ec0acanthony
106146a369d839971ab627bdb31a93d8bd63e81b65a3anthony        /* WARNING: The following generates a 'sampled gaussian' kernel.
10629eb4f74649b23c053b308ce1152dce51239450baanthony         * What we really want is a 'discrete gaussian' kernel.
106346a369d839971ab627bdb31a93d8bd63e81b65a3anthony         *
1064529482f4b494010a13338a74446c510712f670b3anthony         * How to do this is I don't know, but appears to be basied on the
1065529482f4b494010a13338a74446c510712f670b3anthony         * Error Function 'erf()' (intergral of a gaussian)
10669eb4f74649b23c053b308ce1152dce51239450baanthony         */
10679eb4f74649b23c053b308ce1152dce51239450baanthony
1068501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == GaussianKernel || type == DoGKernel )
1069501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { /* Calculate a Gaussian,  OR positive half of a DoG */
10709eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
10719eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
107255a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1073bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1074bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
10759eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
10769eb4f74649b23c053b308ce1152dce51239450baanthony              }
10779eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
10789eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
10799eb4f74649b23c053b308ce1152dce51239450baanthony                            kernel->width*kernel->height*sizeof(double));
10809eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
10819eb4f74649b23c053b308ce1152dce51239450baanthony              }
1082c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
10839eb4f74649b23c053b308ce1152dce51239450baanthony
1084501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == DoGKernel )
1085c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
1086c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            if ( sigma2 > MagickEpsilon )
1087c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              { sigma = sigma2;                /* simplify loop expressions */
10889eb4f74649b23c053b308ce1152dce51239450baanthony                A = 1.0/(2.0*sigma*sigma);
108955a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1090bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1091bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
10929eb4f74649b23c053b308ce1152dce51239450baanthony                    kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1093c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              }
10949eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
1095c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1096c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
10979eb4f74649b23c053b308ce1152dce51239450baanthony
1098501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == LoGKernel )
10999eb4f74649b23c053b308ce1152dce51239450baanthony          { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
11009eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
11019eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
110255a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1103bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1104bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11059eb4f74649b23c053b308ce1152dce51239450baanthony                    { R = ((double)(u*u+v*v))*A;
11069eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = (1-R)*exp(-R)*B;
11079eb4f74649b23c053b308ce1152dce51239450baanthony                    }
11089eb4f74649b23c053b308ce1152dce51239450baanthony              }
11099eb4f74649b23c053b308ce1152dce51239450baanthony            else /* special case - generate a unity kernel */
11109eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
11119eb4f74649b23c053b308ce1152dce51239450baanthony                            kernel->width*kernel->height*sizeof(double));
11129eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
11139eb4f74649b23c053b308ce1152dce51239450baanthony              }
11149eb4f74649b23c053b308ce1152dce51239450baanthony          }
11159eb4f74649b23c053b308ce1152dce51239450baanthony
11169eb4f74649b23c053b308ce1152dce51239450baanthony        /* Note the above kernels may have been 'clipped' by a user defined
1117c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
1118c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** sigma's (> 0.1) the central value becomes larger than one, and thus
1119c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** producing a very bright kernel.
1120c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1121c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** Normalization will still be needed.
1122c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        */
1123602ab9b30b644a78a4057da93d838a77391ec0acanthony
11243dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* Normalize the 2D Gaussian Kernel
11253dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        **
1126c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1127c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
11283dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        */
112946a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1130c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1131602ab9b30b644a78a4057da93d838a77391ec0acanthony
1132602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1133602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1134602ab9b30b644a78a4057da93d838a77391ec0acanthony    case BlurKernel:
1135602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1136c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1137501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          alpha, beta;
1138602ab9b30b644a78a4057da93d838a77391ec0acanthony
1139c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1140bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1141c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1142501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1143602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1144bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = (ssize_t) (kernel->width-1)/2;
1145c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->y = 0;
1146c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
11475e0444305cde9e2ef31f16111899f14a257a6a54cristy        kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1148602ab9b30b644a78a4057da93d838a77391ec0acanthony                              kernel->height*sizeof(double));
1149602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (kernel->values == (double *) NULL)
115083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1151602ab9b30b644a78a4057da93d838a77391ec0acanthony
1152602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1153602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1154602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
1155602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** It generates a gaussian 3 times the width, and compresses it into
1156602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** the expected range.  This produces a closer normalization of the
1157602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** resulting kernel, especially for very low sigma values.
1158602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** As such while wierd it is prefered.
1159602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1160602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** I am told this method originally came from Photoshop.
11619eb4f74649b23c053b308ce1152dce51239450baanthony        **
11629eb4f74649b23c053b308ce1152dce51239450baanthony        ** A properly normalized curve is generated (apart from edge clipping)
11639eb4f74649b23c053b308ce1152dce51239450baanthony        ** even though we later normalize the result (for edge clipping)
11649eb4f74649b23c053b308ce1152dce51239450baanthony        ** to allow the correct generation of a "Difference of Blurs".
1165602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1166c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1167c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* initialize */
1168bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
11699eb4f74649b23c053b308ce1152dce51239450baanthony        (void) ResetMagickMemory(kernel->values,0, (size_t)
11709eb4f74649b23c053b308ce1152dce51239450baanthony                     kernel->width*kernel->height*sizeof(double));
1171c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive 1D Gaussian */
1172c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1173c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { sigma *= KernelRank;               /* simplify loop expressions */
1174501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            alpha = 1.0/(2.0*sigma*sigma);
117555a91cddcdea3aa002893186a773e1704884a9dfcristy            beta= (double) (1.0/(MagickSQ2PI*sigma ));
1176c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            for ( u=-v; u <= v; u++) {
1177501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[(u+v)/KernelRank] +=
1178501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                              exp(-((double)(u*u))*alpha)*beta;
1179c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            }
1180c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1181c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1182c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1183602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
1184c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Direct calculation without curve averaging */
1185c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1186c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive Gaussian */
1187c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1188501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
1189501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            beta = 1.0/(MagickSQ2PI*sigma);
1190bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1191501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1192c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1193c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1194c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
1195c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony                         kernel->width*kernel->height*sizeof(double));
1196c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1197c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1198602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
1199c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Note the above kernel may have been 'clipped' by a user defined
1200cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
1201cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        ** sigma's (> 0.1) the central value becomes larger than one, and thus
1202cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        ** producing a very bright kernel.
1203c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1204c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** Normalization will still be needed.
1205602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1206cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1207602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Normalize the 1D Gaussian Kernel
1208602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1209c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1210c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
1211602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
121246a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
121346a369d839971ab627bdb31a93d8bd63e81b65a3anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1214cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1215c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* rotate the 1D kernel by given angle */
1216501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        RotateKernelInfo(kernel, args->xi );
1217602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1218602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1219602ab9b30b644a78a4057da93d838a77391ec0acanthony    case CometKernel:
1220602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
12219eb4f74649b23c053b308ce1152dce51239450baanthony          sigma = fabs(args->sigma),
12229eb4f74649b23c053b308ce1152dce51239450baanthony          A;
1223602ab9b30b644a78a4057da93d838a77391ec0acanthony
1224602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( args->rho < 1.0 )
1225e1cf9465864144e8b8043d522906c1e47bbf6192anthony          kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1226602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
1227bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho;
1228c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->x = kernel->y = 0;
1229602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1230c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
12315e0444305cde9e2ef31f16111899f14a257a6a54cristy        kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1232602ab9b30b644a78a4057da93d838a77391ec0acanthony                              kernel->height*sizeof(double));
1233602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (kernel->values == (double *) NULL)
123483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1235602ab9b30b644a78a4057da93d838a77391ec0acanthony
1236c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* A comet blur is half a 1D gaussian curve, so that the object is
1237602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** blurred in one direction only.  This may not be quite the right
12383dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** curve to use so may change in the future. The function must be
12393dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** normalised after generation, which also resolves any clipping.
1240c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1241c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** As we are normalizing and not subtracting gaussians,
1242c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there is no need for a divisor in the gaussian formula
1243c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
124443c4925e5305a26e48d68f7893e94f55d0831c39anthony        ** It is less comples
1245602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
12469eb4f74649b23c053b308ce1152dce51239450baanthony        if ( sigma > MagickEpsilon )
12479eb4f74649b23c053b308ce1152dce51239450baanthony          {
1248602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1249602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1250bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            v = (ssize_t) kernel->width*KernelRank; /* start/end points */
12519eb4f74649b23c053b308ce1152dce51239450baanthony            (void) ResetMagickMemory(kernel->values,0, (size_t)
12529eb4f74649b23c053b308ce1152dce51239450baanthony                          kernel->width*sizeof(double));
12539eb4f74649b23c053b308ce1152dce51239450baanthony            sigma *= KernelRank;            /* simplify the loop expression */
12549eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);
12559eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
12569eb4f74649b23c053b308ce1152dce51239450baanthony            for ( u=0; u < v; u++) {
12579eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->values[u/KernelRank] +=
12589eb4f74649b23c053b308ce1152dce51239450baanthony                  exp(-((double)(u*u))*A);
12599eb4f74649b23c053b308ce1152dce51239450baanthony              /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
12609eb4f74649b23c053b308ce1152dce51239450baanthony            }
1261bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (i=0; i < (ssize_t) kernel->width; i++)
12629eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range += kernel->values[i];
1263602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
12649eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
12659eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
1266bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0; i < (ssize_t) kernel->width; i++)
12679eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range +=
12689eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[i] =
12699eb4f74649b23c053b308ce1152dce51239450baanthony                  exp(-((double)(i*i))*A);
12709eb4f74649b23c053b308ce1152dce51239450baanthony                /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1271602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
12729eb4f74649b23c053b308ce1152dce51239450baanthony          }
12739eb4f74649b23c053b308ce1152dce51239450baanthony        else /* special case - generate a unity kernel */
12749eb4f74649b23c053b308ce1152dce51239450baanthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
12759eb4f74649b23c053b308ce1152dce51239450baanthony                         kernel->width*kernel->height*sizeof(double));
12769eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
12779eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->positive_range = 1.0;
12789eb4f74649b23c053b308ce1152dce51239450baanthony          }
127946a369d839971ab627bdb31a93d8bd63e81b65a3anthony
128046a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->minimum = 0.0;
1281c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
128246a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->negative_range = 0.0;
1283602ab9b30b644a78a4057da93d838a77391ec0acanthony
1284999bb2c20aa9d42875bb5adba44951988d4ae354anthony        ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
1285999bb2c20aa9d42875bb5adba44951988d4ae354anthony        RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
1286602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1287602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1288c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1289529482f4b494010a13338a74446c510712f670b3anthony    /*
1290529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels - Well Known Named Constant Kernels
1291529482f4b494010a13338a74446c510712f670b3anthony    */
12923c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case LaplacianKernel:
1293e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      { switch ( (int) args->rho ) {
12943dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony          case 0:
12959eb4f74649b23c053b308ce1152dce51239450baanthony          default: /* laplacian square filter -- default */
1296c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
12973dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            break;
12989eb4f74649b23c053b308ce1152dce51239450baanthony          case 1:  /* laplacian diamond filter */
1299c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
13003c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13013c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          case 2:
13029eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
13039eb4f74649b23c053b308ce1152dce51239450baanthony            break;
13049eb4f74649b23c053b308ce1152dce51239450baanthony          case 3:
1305c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
13063c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13079eb4f74649b23c053b308ce1152dce51239450baanthony          case 5:   /* a 5x5 laplacian */
13083c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
13099eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13103c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13119eb4f74649b23c053b308ce1152dce51239450baanthony          case 7:   /* a 7x7 laplacian */
13123c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
1313c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              "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" );
13143c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
1315501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 15:  /* a 5x5 LoG (sigma approx 1.4) */
13169eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray(
13179eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13189eb4f74649b23c053b308ce1152dce51239450baanthony            break;
1319501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 19:  /* a 9x9 LoG (sigma approx 1.4) */
132043c4925e5305a26e48d68f7893e94f55d0831c39anthony            /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
132143c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel=ParseKernelArray(
1322bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony              "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");
132343c4925e5305a26e48d68f7893e94f55d0831c39anthony            break;
13243c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
13253c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        if (kernel == (KernelInfo *) NULL)
13263c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          return(kernel);
13273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        kernel->type = type;
13283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        break;
13293c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      }
1330c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SobelKernel:
1331cceb6f05c2016ea3854b29e7770ec5ff49034ecfanthony      { /* Simple Sobel Kernel */
1332dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1333dcc2a474c98415e565434fe52f630acba36fa0c1anthony        if (kernel == (KernelInfo *) NULL)
1334dcc2a474c98415e565434fe52f630acba36fa0c1anthony          return(kernel);
1335dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel->type = type;
1336dcc2a474c98415e565434fe52f630acba36fa0c1anthony        RotateKernelInfo(kernel, args->rho);
1337dcc2a474c98415e565434fe52f630acba36fa0c1anthony        break;
1338dcc2a474c98415e565434fe52f630acba36fa0c1anthony      }
1339c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RobertsKernel:
1340c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1341501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
1342c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1343c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1344c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
134546a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1346c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1347c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1348c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PrewittKernel:
1349c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1350501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
1351c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1352c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1353c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
135446a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1355c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1356c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1357c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CompassKernel:
1358c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1359501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
1360c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1361c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1362c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
136346a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1364c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1365c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
13669eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
13679eb4f74649b23c053b308ce1152dce51239450baanthony      {
1368501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
13699eb4f74649b23c053b308ce1152dce51239450baanthony        if (kernel == (KernelInfo *) NULL)
13709eb4f74649b23c053b308ce1152dce51239450baanthony          return(kernel);
13719eb4f74649b23c053b308ce1152dce51239450baanthony        kernel->type = type;
137246a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
13739eb4f74649b23c053b308ce1152dce51239450baanthony        break;
13749eb4f74649b23c053b308ce1152dce51239450baanthony      }
1375e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony    case FreiChenKernel:
1376501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* Direction is set to be left to right positive */
1377501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
1378501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
13791dd091ae3bc17edc26c16cc47f436a24bd48412aanthony      { switch ( (int) args->rho ) {
1380e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          default:
1381c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 0:
1382501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1383c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            if (kernel == (KernelInfo *) NULL)
1384c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1385ef33d9f66f955c1f6f5f7105e164cc2d5c5e2a41anthony            kernel->type = type;
1386501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel->values[3] = +MagickSQ2;
1387501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel->values[5] = -MagickSQ2;
1388c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1389c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            break;
1390c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 2:
1391c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
1392c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1393c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1394c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->type = type;
1395c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->values[1] = kernel->values[3] = +MagickSQ2;
1396c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->values[5] = kernel->values[7] = -MagickSQ2;
1397c40ac1e79923a1516075ba1197ae4ed90244af9banthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1398d4e3ffa7f64077da0f32c2d8a599737ee8d115ddcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1399c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1400c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 10:
1401c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
1402c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1403c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1404c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1405e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          case 1:
1406c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 11:
1407501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1408e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1409e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1410c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1411501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel->values[3] = +MagickSQ2;
1412501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel->values[5] = -MagickSQ2;
1413e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
141455a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1415e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1416c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 12:
1417501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
1418e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1419e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1420c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
14211d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel->values[1] = +MagickSQ2;
14221d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel->values[7] = +MagickSQ2;
1423e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
142455a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1425e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1426c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 13:
1427501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
1428e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1429e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1430c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1431501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel->values[0] = +MagickSQ2;
1432501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel->values[8] = -MagickSQ2;
1433e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
143455a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1435e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1436c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 14:
14371d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
1438e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1439e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1440c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
14411d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel->values[2] = -MagickSQ2;
14421d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel->values[6] = +MagickSQ2;
1443e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
144455a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1445e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1446c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 15:
1447501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
1448e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1449e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1450c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1451e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1452e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1453c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 16:
14541d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
1455e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1456e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1457c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1458e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1459e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1460c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 17:
1461501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
1462e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1463e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1464c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1465e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1466e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1467c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 18:
1468501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1469e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1470e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1471c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1472e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1473e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1474c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 19:
1475c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
1476e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1477e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1478c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1479e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1480e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1481e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        }
1482c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony        if ( fabs(args->sigma) > MagickEpsilon )
1483c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by correctly supplied 'angle' */
1484c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->sigma);
1485c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony        else if ( args->rho > 30.0 || args->rho < -30.0 )
1486c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by out of bounds 'type' */
1487c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->rho);
1488e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        break;
1489e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      }
1490e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony
1491529482f4b494010a13338a74446c510712f670b3anthony    /*
1492529482f4b494010a13338a74446c510712f670b3anthony      Boolean or Shaped Kernels
1493529482f4b494010a13338a74446c510712f670b3anthony    */
1494c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1495602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
1496c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (args->rho < 1.0)
1497c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
1498c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1499bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1500bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1501c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
15025e0444305cde9e2ef31f16111899f14a257a6a54cristy        kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1503c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony                              kernel->height*sizeof(double));
1504c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel->values == (double *) NULL)
1505c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(DestroyKernelInfo(kernel));
1506c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1507c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* set all kernel values within diamond area to scale given */
1508bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1509bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
15101d5e67090dc7232b35bfcc71b31266c20838defcanthony            if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1511c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->positive_range += kernel->values[i] = args->sigma;
1512c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            else
1513c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[i] = nan;
1514c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1515c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1516c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1517c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1518c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
1519c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      { double
1520c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          scale;
1521602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( type == SquareKernel )
1522602ab9b30b644a78a4057da93d838a77391ec0acanthony          {
1523602ab9b30b644a78a4057da93d838a77391ec0acanthony            if (args->rho < 1.0)
1524c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony              kernel->width = kernel->height = 3;  /* default radius = 1 */
1525602ab9b30b644a78a4057da93d838a77391ec0acanthony            else
1526bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              kernel->width = kernel->height = (size_t) (2*args->rho+1);
1527bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
15284fd27e21043be809d66c8202e779255e5b660d2danthony            scale = args->sigma;
1529602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1530602ab9b30b644a78a4057da93d838a77391ec0acanthony        else {
15312be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy            /* NOTE: user defaults set in "AcquireKernelInfo()" */
1532602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->rho < 1.0 || args->sigma < 1.0 )
153383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1534bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->width = (size_t)args->rho;
1535bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->height = (size_t)args->sigma;
1536602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
1537602ab9b30b644a78a4057da93d838a77391ec0acanthony                 args->psi < 0.0 || args->psi > (double)kernel->height )
153883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1539bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = (ssize_t) args->xi;
1540bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->y = (ssize_t) args->psi;
15414fd27e21043be809d66c8202e779255e5b660d2danthony            scale = 1.0;
1542602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
15435e0444305cde9e2ef31f16111899f14a257a6a54cristy        kernel->values=(double *) AcquireAlignedMemory(kernel->width,
1544602ab9b30b644a78a4057da93d838a77391ec0acanthony                              kernel->height*sizeof(double));
1545602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (kernel->values == (double *) NULL)
154683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1547602ab9b30b644a78a4057da93d838a77391ec0acanthony
15483dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* set all kernel values to scale given */
1549eaedf06777741da32408da72c1e512975c600c48cristy        u=(ssize_t) (kernel->width*kernel->height);
1550150989ed67ef9da53141a65e5f3ebdb05dd025abcristy        for ( i=0; i < u; i++)
15514fd27e21043be809d66c8202e779255e5b660d2danthony            kernel->values[i] = scale;
15524fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->minimum = kernel->maximum = scale;   /* a flat shape */
15534fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->positive_range = scale*u;
1554cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        break;
1555602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
15561ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonKernel:
15571ef941fea2534a0d20ba7d71307d35040247decbanthony        {
15581ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
1559a9892d898acb81e1ec73106d892855fdc5a69427anthony            kernel->width = kernel->height = 5;  /* default radius = 2 */
15601ef941fea2534a0d20ba7d71307d35040247decbanthony          else
15611ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
15621ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
15631ef941fea2534a0d20ba7d71307d35040247decbanthony
15645e0444305cde9e2ef31f16111899f14a257a6a54cristy          kernel->values=(double *) AcquireAlignedMemory(kernel->width,
15651ef941fea2534a0d20ba7d71307d35040247decbanthony                                kernel->height*sizeof(double));
15661ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (double *) NULL)
15671ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
15681ef941fea2534a0d20ba7d71307d35040247decbanthony
15691ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
15701ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
15711ef941fea2534a0d20ba7d71307d35040247decbanthony              if ( (labs((long) u)+labs((long) v)) <=
15721ef941fea2534a0d20ba7d71307d35040247decbanthony                        ((long)kernel->x + (long)(kernel->x/2)) )
15731ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
15741ef941fea2534a0d20ba7d71307d35040247decbanthony              else
15751ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->values[i] = nan;
1576a9892d898acb81e1ec73106d892855fdc5a69427anthony          kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
15771ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
15781ef941fea2534a0d20ba7d71307d35040247decbanthony        }
15791ef941fea2534a0d20ba7d71307d35040247decbanthony      case DiskKernel:
15801ef941fea2534a0d20ba7d71307d35040247decbanthony        {
15811ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
15820bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony            limit = (ssize_t)(args->rho*args->rho);
15831ef941fea2534a0d20ba7d71307d35040247decbanthony
15849bf68d590429a72aa70894f6aea7fc3c94876e54anthony          if (args->rho < 0.4)           /* default radius approx 4.3 */
15859bf68d590429a72aa70894f6aea7fc3c94876e54anthony            kernel->width = kernel->height = 9L, limit = 18L;
15861ef941fea2534a0d20ba7d71307d35040247decbanthony          else
15871ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
15881ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
15891ef941fea2534a0d20ba7d71307d35040247decbanthony
15905e0444305cde9e2ef31f16111899f14a257a6a54cristy          kernel->values=(double *) AcquireAlignedMemory(kernel->width,
15911ef941fea2534a0d20ba7d71307d35040247decbanthony                                kernel->height*sizeof(double));
15921ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (double *) NULL)
15931ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
15941ef941fea2534a0d20ba7d71307d35040247decbanthony
15951ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
15961ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
15971ef941fea2534a0d20ba7d71307d35040247decbanthony              if ((u*u+v*v) <= limit)
15981ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
15993dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony              else
16003dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony                kernel->values[i] = nan;
16011ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16021ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16031ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16041ef941fea2534a0d20ba7d71307d35040247decbanthony      case PlusKernel:
16051ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16061ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16071ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16081ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16091ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16101ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16111ef941fea2534a0d20ba7d71307d35040247decbanthony
16125e0444305cde9e2ef31f16111899f14a257a6a54cristy          kernel->values=(double *) AcquireAlignedMemory(kernel->width,
16131ef941fea2534a0d20ba7d71307d35040247decbanthony                                kernel->height*sizeof(double));
16141ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (double *) NULL)
16151ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16161ef941fea2534a0d20ba7d71307d35040247decbanthony
16171ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16181ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16191ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16201ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
16211ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16221ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16231ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16241ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16251ef941fea2534a0d20ba7d71307d35040247decbanthony      case CrossKernel:
16261ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16271ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16281ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16291ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16301ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16311ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16321ef941fea2534a0d20ba7d71307d35040247decbanthony
16335e0444305cde9e2ef31f16111899f14a257a6a54cristy          kernel->values=(double *) AcquireAlignedMemory(kernel->width,
16341ef941fea2534a0d20ba7d71307d35040247decbanthony                                kernel->height*sizeof(double));
16351ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (double *) NULL)
16361ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16371ef941fea2534a0d20ba7d71307d35040247decbanthony
16381ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16391ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16401ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16411ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
16421ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16431ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16441ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16451ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1646529482f4b494010a13338a74446c510712f670b3anthony      /*
1647529482f4b494010a13338a74446c510712f670b3anthony        HitAndMiss Kernels
1648529482f4b494010a13338a74446c510712f670b3anthony      */
16491ef941fea2534a0d20ba7d71307d35040247decbanthony      case RingKernel:
16501ef941fea2534a0d20ba7d71307d35040247decbanthony      case PeaksKernel:
16511ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16521ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
16531ef941fea2534a0d20ba7d71307d35040247decbanthony            limit1,
16541ef941fea2534a0d20ba7d71307d35040247decbanthony            limit2,
16551ef941fea2534a0d20ba7d71307d35040247decbanthony            scale;
16561ef941fea2534a0d20ba7d71307d35040247decbanthony
16571ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < args->sigma)
16581ef941fea2534a0d20ba7d71307d35040247decbanthony            {
16591ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->sigma)*2+1;
16601ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->rho*args->rho);
16611ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->sigma*args->sigma);
16623dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            }
16631ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16641ef941fea2534a0d20ba7d71307d35040247decbanthony            {
16651ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->rho)*2+1;
16661ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->sigma*args->sigma);
16671ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->rho*args->rho);
16681ef941fea2534a0d20ba7d71307d35040247decbanthony            }
16691ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( limit2 <= 0 )
16701ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = 7L, limit1 = 7L, limit2 = 11L;
16711ef941fea2534a0d20ba7d71307d35040247decbanthony
16721ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->height = kernel->width;
16731ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16745e0444305cde9e2ef31f16111899f14a257a6a54cristy          kernel->values=(double *) AcquireAlignedMemory(kernel->width,
16751ef941fea2534a0d20ba7d71307d35040247decbanthony                                kernel->height*sizeof(double));
16761ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (double *) NULL)
16771ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16781ef941fea2534a0d20ba7d71307d35040247decbanthony
16791ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
16801ef941fea2534a0d20ba7d71307d35040247decbanthony          scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
16811ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
16821ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16831ef941fea2534a0d20ba7d71307d35040247decbanthony              { ssize_t radius=u*u+v*v;
16841ef941fea2534a0d20ba7d71307d35040247decbanthony                if (limit1 < radius && radius <= limit2)
16851ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->positive_range += kernel->values[i] = (double) scale;
16861ef941fea2534a0d20ba7d71307d35040247decbanthony                else
16871ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->values[i] = nan;
16881ef941fea2534a0d20ba7d71307d35040247decbanthony              }
16891ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = (double) scale;
16901ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( type == PeaksKernel ) {
16911ef941fea2534a0d20ba7d71307d35040247decbanthony            /* set the central point in the middle */
16921ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
16931ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->positive_range = 1.0;
16941ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->maximum = 1.0;
16951ef941fea2534a0d20ba7d71307d35040247decbanthony          }
16961ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1697c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        }
16981ef941fea2534a0d20ba7d71307d35040247decbanthony      case EdgesKernel:
16991ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1700529482f4b494010a13338a74446c510712f670b3anthony          kernel=AcquireKernelInfo("ThinSE:482");
17011ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17021ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17031ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
1704529482f4b494010a13338a74446c510712f670b3anthony          ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
17051ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17061ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17071ef941fea2534a0d20ba7d71307d35040247decbanthony      case CornersKernel:
17081ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1709529482f4b494010a13338a74446c510712f670b3anthony          kernel=AcquireKernelInfo("ThinSE:87");
17101ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17111ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17121ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
17131ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
17141ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17151ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1716529482f4b494010a13338a74446c510712f670b3anthony      case DiagonalsKernel:
17171ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17181ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
17191ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
17201ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17211ef941fea2534a0d20ba7d71307d35040247decbanthony              { KernelInfo
17221ef941fea2534a0d20ba7d71307d35040247decbanthony                  *new_kernel;
1723529482f4b494010a13338a74446c510712f670b3anthony                kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17241ef941fea2534a0d20ba7d71307d35040247decbanthony                if (kernel == (KernelInfo *) NULL)
17251ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(kernel);
17261ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->type = type;
1727529482f4b494010a13338a74446c510712f670b3anthony                new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17281ef941fea2534a0d20ba7d71307d35040247decbanthony                if (new_kernel == (KernelInfo *) NULL)
17291ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(DestroyKernelInfo(kernel));
17301ef941fea2534a0d20ba7d71307d35040247decbanthony                new_kernel->type = type;
17311ef941fea2534a0d20ba7d71307d35040247decbanthony                LastKernelInfo(kernel)->next = new_kernel;
17321ef941fea2534a0d20ba7d71307d35040247decbanthony                ExpandMirrorKernelInfo(kernel);
1733529482f4b494010a13338a74446c510712f670b3anthony                return(kernel);
17341ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17351ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
1736529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17371ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17381ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
1739529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17401ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17411ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1742529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1743529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1744529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1745529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
17461ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17471ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17481ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineEndsKernel:
17491ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Kernels for finding the end of thin lines */
17501ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
17511ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
17521ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17531ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all end of lines */
1754529482f4b494010a13338a74446c510712f670b3anthony              return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
17551ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
17561ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel for 4-connected line ends - no rotation */
17571ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
17581ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17591ef941fea2534a0d20ba7d71307d35040247decbanthony          case 2:
17601ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for 8-connected lines - no rotation */
17611ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
17621ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17631ef941fea2534a0d20ba7d71307d35040247decbanthony          case 3:
17641ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for orthogonal line ends - does not find corners */
17651ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
17661ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17671ef941fea2534a0d20ba7d71307d35040247decbanthony          case 4:
17681ef941fea2534a0d20ba7d71307d35040247decbanthony              /* traditional line end - fails on last T end */
17691ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
17701ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17711ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1772529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1773529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1774529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1775529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
17761ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17771ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17781ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineJunctionsKernel:
17791ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* kernels for finding the junctions of multiple lines */
17801ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
17811ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
17821ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17831ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all line junctions */
1784529482f4b494010a13338a74446c510712f670b3anthony              return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
17851ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
17861ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Y Junction */
17871ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
17881ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17891ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
17901ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal T Junctions */
17911ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
17921ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17931ef941fea2534a0d20ba7d71307d35040247decbanthony            case 3:
17941ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal T Junctions */
17951ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
17961ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17971ef941fea2534a0d20ba7d71307d35040247decbanthony            case 4:
17981ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal X Junctions */
17991ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
18001ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18011ef941fea2534a0d20ba7d71307d35040247decbanthony            case 5:
18021ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal X Junctions - minimal diamond kernel */
18031ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
18041ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18051ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1806529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1807529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1808529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1809529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18101ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18111ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18121ef941fea2534a0d20ba7d71307d35040247decbanthony      case RidgesKernel:
18131ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Ridges - Ridge finding kernels */
18141ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
18151ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
18161ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18171ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18181ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18191ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3x1:0,1,0");
18201ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18211ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18221ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18231ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
18241ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18251ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18261ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("4x1:0,1,1,0");
18271ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18281ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18291ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18301ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
18311ef941fea2534a0d20ba7d71307d35040247decbanthony
18321ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
18331ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Unfortunatally we can not yet rotate a non-square kernel */
18341ef941fea2534a0d20ba7d71307d35040247decbanthony              /* But then we can't flip a non-symetrical kernel either */
18351ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
18361ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18371ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18381ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18391ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18401ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
18411ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18421ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18431ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18441ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18451ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
18461ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18471ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18481ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18491ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18501ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
18511ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18521ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18531ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18541ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18551ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
18561ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18571ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18581ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18591ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18601ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
18611ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18621ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18631ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18641ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18651ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
18661ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18671ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18681ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18691ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18701ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
187168cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              if (new_kernel == (KernelInfo *) NULL)
187268cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony                return(DestroyKernelInfo(kernel));
187368cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              new_kernel->type = type;
187468cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              LastKernelInfo(kernel)->next = new_kernel;
187568cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              break;
18761ef941fea2534a0d20ba7d71307d35040247decbanthony          }
18771ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
187868cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony        }
18791ef941fea2534a0d20ba7d71307d35040247decbanthony      case ConvexHullKernel:
18801ef941fea2534a0d20ba7d71307d35040247decbanthony        {
18811ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
18821ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
18831ef941fea2534a0d20ba7d71307d35040247decbanthony          /* first set of 8 kernels */
18841ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
18851ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
18861ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
18871ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
18881ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0);
18891ef941fea2534a0d20ba7d71307d35040247decbanthony          /* append the mirror versions too - no flip function yet */
18901ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
18911ef941fea2534a0d20ba7d71307d35040247decbanthony          if (new_kernel == (KernelInfo *) NULL)
18921ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
18931ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel->type = type;
18941ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(new_kernel, 90.0);
18951ef941fea2534a0d20ba7d71307d35040247decbanthony          LastKernelInfo(kernel)->next = new_kernel;
18961ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1897694934fa79dd310f727588b1d0a7481fa6170f1danthony        }
18989a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony      case SkeletonKernel:
18999a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        {
19009a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          switch ( (int) args->rho ) {
19019a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 1:
19029a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            default:
19039a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Traditional Skeleton...
19049a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** A cyclically rotated single kernel
19059a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19069a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482");
19079a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19089a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19099a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19109a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
19119a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19129a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 2:
19139a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* HIPR Variation of the cyclic skeleton
19149a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** Corners of the traditional method made more forgiving,
19159a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** but the retain the same cyclic order.
19169a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19179a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
19189a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19199a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19209a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel->next == (KernelInfo *) NULL)
19219a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(DestroyKernelInfo(kernel));
19229a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19239a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19249a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
19259a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19269a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 3:
19279a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
19289a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** "Connectivity-Preserving Morphological Image Thransformations"
19299a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
19309a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              **   http://www.leptonica.com/papers/conn.pdf
19319a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19329a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo(
19339a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                            "ThinSE:41; ThinSE:42; ThinSE:43");
19349a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19359a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19369a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19379a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19389a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->next->type = type;
19399a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
19409a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19419a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony           }
19429a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          break;
19439a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        }
1944529482f4b494010a13338a74446c510712f670b3anthony      case ThinSEKernel:
1945529482f4b494010a13338a74446c510712f670b3anthony        { /* Special kernels for general thinning, while preserving connections
1946529482f4b494010a13338a74446c510712f670b3anthony          ** "Connectivity-Preserving Morphological Image Thransformations"
1947529482f4b494010a13338a74446c510712f670b3anthony          ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
1948529482f4b494010a13338a74446c510712f670b3anthony          **   http://www.leptonica.com/papers/conn.pdf
1949529482f4b494010a13338a74446c510712f670b3anthony          ** And
1950529482f4b494010a13338a74446c510712f670b3anthony          **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
1951529482f4b494010a13338a74446c510712f670b3anthony          **
1952529482f4b494010a13338a74446c510712f670b3anthony          ** Note kernels do not specify the origin pixel, allowing them
1953529482f4b494010a13338a74446c510712f670b3anthony          ** to be used for both thickening and thinning operations.
1954529482f4b494010a13338a74446c510712f670b3anthony          */
1955529482f4b494010a13338a74446c510712f670b3anthony          switch ( (int) args->rho ) {
1956529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 4-connected thinning */
1957529482f4b494010a13338a74446c510712f670b3anthony            case 41: /* SE_4_1 */
1958529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
1959529482f4b494010a13338a74446c510712f670b3anthony              break;
1960529482f4b494010a13338a74446c510712f670b3anthony            case 42: /* SE_4_2 */
1961529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
1962529482f4b494010a13338a74446c510712f670b3anthony              break;
1963529482f4b494010a13338a74446c510712f670b3anthony            case 43: /* SE_4_3 */
1964529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
1965529482f4b494010a13338a74446c510712f670b3anthony              break;
1966529482f4b494010a13338a74446c510712f670b3anthony            case 44: /* SE_4_4 */
1967529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
1968529482f4b494010a13338a74446c510712f670b3anthony              break;
1969529482f4b494010a13338a74446c510712f670b3anthony            case 45: /* SE_4_5 */
1970529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
1971529482f4b494010a13338a74446c510712f670b3anthony              break;
1972529482f4b494010a13338a74446c510712f670b3anthony            case 46: /* SE_4_6 */
1973529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
1974529482f4b494010a13338a74446c510712f670b3anthony              break;
1975529482f4b494010a13338a74446c510712f670b3anthony            case 47: /* SE_4_7 */
1976529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
1977529482f4b494010a13338a74446c510712f670b3anthony              break;
1978529482f4b494010a13338a74446c510712f670b3anthony            case 48: /* SE_4_8 */
1979529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
1980529482f4b494010a13338a74446c510712f670b3anthony              break;
1981529482f4b494010a13338a74446c510712f670b3anthony            case 49: /* SE_4_9 */
1982529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
1983529482f4b494010a13338a74446c510712f670b3anthony              break;
1984529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 8-connected thinning - negatives of the above */
1985529482f4b494010a13338a74446c510712f670b3anthony            case 81: /* SE_8_0 */
1986529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
1987529482f4b494010a13338a74446c510712f670b3anthony              break;
1988529482f4b494010a13338a74446c510712f670b3anthony            case 82: /* SE_8_2 */
1989529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
1990529482f4b494010a13338a74446c510712f670b3anthony              break;
1991529482f4b494010a13338a74446c510712f670b3anthony            case 83: /* SE_8_3 */
1992529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
1993529482f4b494010a13338a74446c510712f670b3anthony              break;
1994529482f4b494010a13338a74446c510712f670b3anthony            case 84: /* SE_8_4 */
1995529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
1996529482f4b494010a13338a74446c510712f670b3anthony              break;
1997529482f4b494010a13338a74446c510712f670b3anthony            case 85: /* SE_8_5 */
1998529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
1999529482f4b494010a13338a74446c510712f670b3anthony              break;
2000529482f4b494010a13338a74446c510712f670b3anthony            case 86: /* SE_8_6 */
2001529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
2002529482f4b494010a13338a74446c510712f670b3anthony              break;
2003529482f4b494010a13338a74446c510712f670b3anthony            case 87: /* SE_8_7 */
2004529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
2005529482f4b494010a13338a74446c510712f670b3anthony              break;
2006529482f4b494010a13338a74446c510712f670b3anthony            case 88: /* SE_8_8 */
2007529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
2008529482f4b494010a13338a74446c510712f670b3anthony              break;
2009529482f4b494010a13338a74446c510712f670b3anthony            case 89: /* SE_8_9 */
2010529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
2011529482f4b494010a13338a74446c510712f670b3anthony              break;
2012529482f4b494010a13338a74446c510712f670b3anthony            /* Special combined SE kernels */
20139a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
20149a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
20159a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
20169a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
20179a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
20189a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
2019529482f4b494010a13338a74446c510712f670b3anthony            case 481: /* SE_48_1 - General Connected Corner Kernel */
2020529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
2021529482f4b494010a13338a74446c510712f670b3anthony              break;
2022529482f4b494010a13338a74446c510712f670b3anthony            default:
2023529482f4b494010a13338a74446c510712f670b3anthony            case 482: /* SE_48_2 - General Edge Kernel */
2024529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
2025529482f4b494010a13338a74446c510712f670b3anthony              break;
2026529482f4b494010a13338a74446c510712f670b3anthony          }
2027529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
2028529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
2029529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
2030529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
2031529482f4b494010a13338a74446c510712f670b3anthony          break;
2032529482f4b494010a13338a74446c510712f670b3anthony        }
2033529482f4b494010a13338a74446c510712f670b3anthony      /*
2034529482f4b494010a13338a74446c510712f670b3anthony        Distance Measuring Kernels
2035529482f4b494010a13338a74446c510712f670b3anthony      */
20361ef941fea2534a0d20ba7d71307d35040247decbanthony      case ChebyshevKernel:
20371ef941fea2534a0d20ba7d71307d35040247decbanthony        {
20381ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
20391ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
20401ef941fea2534a0d20ba7d71307d35040247decbanthony          else
20411ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
20421ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
20431ef941fea2534a0d20ba7d71307d35040247decbanthony
20445e0444305cde9e2ef31f16111899f14a257a6a54cristy          kernel->values=(double *) AcquireAlignedMemory(kernel->width,
20451ef941fea2534a0d20ba7d71307d35040247decbanthony                                kernel->height*sizeof(double));
20461ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (double *) NULL)
20471ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
20481ef941fea2534a0d20ba7d71307d35040247decbanthony
20491ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
20501ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
20511ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
20521ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
20531ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
20541ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2055c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
20561ef941fea2534a0d20ba7d71307d35040247decbanthony      case ManhattanKernel:
20571ef941fea2534a0d20ba7d71307d35040247decbanthony        {
20581ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
20591ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
20601ef941fea2534a0d20ba7d71307d35040247decbanthony          else
20611ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
20621ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
20631ef941fea2534a0d20ba7d71307d35040247decbanthony
20645e0444305cde9e2ef31f16111899f14a257a6a54cristy          kernel->values=(double *) AcquireAlignedMemory(kernel->width,
20651ef941fea2534a0d20ba7d71307d35040247decbanthony                                kernel->height*sizeof(double));
20661ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel->values == (double *) NULL)
20671ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
20681ef941fea2534a0d20ba7d71307d35040247decbanthony
20691ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
20701ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
20711ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
20721ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*(labs((long) u)+labs((long) v)) );
20731ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
20741ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2075c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
20761ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonalKernel:
2077602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
20781ef941fea2534a0d20ba7d71307d35040247decbanthony        if (args->rho < 2.0)
20791ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
2080602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
2081bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2082bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2083602ab9b30b644a78a4057da93d838a77391ec0acanthony
20845e0444305cde9e2ef31f16111899f14a257a6a54cristy        kernel->values=(double *) AcquireAlignedMemory(kernel->width,
2085602ab9b30b644a78a4057da93d838a77391ec0acanthony                              kernel->height*sizeof(double));
2086602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (kernel->values == (double *) NULL)
208783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2088602ab9b30b644a78a4057da93d838a77391ec0acanthony
2089bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2090bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
20911ef941fea2534a0d20ba7d71307d35040247decbanthony            {
20921ef941fea2534a0d20ba7d71307d35040247decbanthony              double
20931ef941fea2534a0d20ba7d71307d35040247decbanthony                r1 = MagickMax(fabs((double)u),fabs((double)v)),
20941ef941fea2534a0d20ba7d71307d35040247decbanthony                r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
20951ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += kernel->values[i] =
20961ef941fea2534a0d20ba7d71307d35040247decbanthony                        args->sigma*MagickMax(r1,r2);
20971ef941fea2534a0d20ba7d71307d35040247decbanthony            }
2098c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2099602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2100602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2101602ab9b30b644a78a4057da93d838a77391ec0acanthony    case EuclideanKernel:
2102602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2103602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (args->rho < 1.0)
2104c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
2105602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
21061ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2107bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2108602ab9b30b644a78a4057da93d838a77391ec0acanthony
21095e0444305cde9e2ef31f16111899f14a257a6a54cristy        kernel->values=(double *) AcquireAlignedMemory(kernel->width,
2110602ab9b30b644a78a4057da93d838a77391ec0acanthony                              kernel->height*sizeof(double));
2111602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (kernel->values == (double *) NULL)
211283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2113602ab9b30b644a78a4057da93d838a77391ec0acanthony
2114bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2115bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2116c99304fe3c8d9c617da792b40b57c118bb1249afcristy            kernel->positive_range += ( kernel->values[i] =
2117c84dce50867229e4872193e8eed5dbab58eb9f02anthony                 args->sigma*sqrt((double)(u*u+v*v)) );
2118c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2119602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2120602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2121602ab9b30b644a78a4057da93d838a77391ec0acanthony    default:
2122c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
2123529482f4b494010a13338a74446c510712f670b3anthony        /* No-Op Kernel - Basically just a single pixel on its own */
21243ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony        kernel=ParseKernelArray("1:1");
2125c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
2126c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
2127529482f4b494010a13338a74446c510712f670b3anthony        kernel->type = UndefinedKernel;
2128c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
2129c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
2130602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
2131602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
2132602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2133602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2134c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2135602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
2136602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2137602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2138602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2139602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
21406771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     C l o n e K e r n e l I n f o                                           %
21414fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21424fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21434fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21444fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21454fd27e21043be809d66c8202e779255e5b660d2danthony%
21461b2bc0a7da432e6e1cc0480280402df213faa940anthony%  CloneKernelInfo() creates a new clone of the given Kernel List so that its
21471b2bc0a7da432e6e1cc0480280402df213faa940anthony%  can be modified without effecting the original.  The cloned kernel should
214819910ef25dd3d99d1981a9e42c934133170ee714anthony%  be destroyed using DestoryKernelInfo() when no longer needed.
21497a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
2150e636559dfadfdb115cc93f223315052a1ee89238cristy%  The format of the CloneKernelInfo method is:
21514fd27e21043be809d66c8202e779255e5b660d2danthony%
2152930be614b4595b97cd79ee864a394796740f76adanthony%      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
21534fd27e21043be809d66c8202e779255e5b660d2danthony%
21544fd27e21043be809d66c8202e779255e5b660d2danthony%  A description of each parameter follows:
21554fd27e21043be809d66c8202e779255e5b660d2danthony%
21564fd27e21043be809d66c8202e779255e5b660d2danthony%    o kernel: the Morphology/Convolution kernel to be cloned
21574fd27e21043be809d66c8202e779255e5b660d2danthony%
21584fd27e21043be809d66c8202e779255e5b660d2danthony*/
2159ef656913b0b30d713ae94c82c47693c9dc69c9f4cristyMagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
21604fd27e21043be809d66c8202e779255e5b660d2danthony{
2161bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
21624fd27e21043be809d66c8202e779255e5b660d2danthony    i;
21634fd27e21043be809d66c8202e779255e5b660d2danthony
216419eb64195ef744f61293025952df1e5e6de66524cristy  KernelInfo
21657a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *new_kernel;
21664fd27e21043be809d66c8202e779255e5b660d2danthony
21674fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
21687a01dcf50ce12cb2a789bedff51e9345f022432eanthony  new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
21697a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if (new_kernel == (KernelInfo *) NULL)
21707a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(new_kernel);
21717a01dcf50ce12cb2a789bedff51e9345f022432eanthony  *new_kernel=(*kernel); /* copy values in structure */
21727a01dcf50ce12cb2a789bedff51e9345f022432eanthony
21737a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* replace the values with a copy of the values */
21745e0444305cde9e2ef31f16111899f14a257a6a54cristy  new_kernel->values=(double *) AcquireAlignedMemory(kernel->width,
217519eb64195ef744f61293025952df1e5e6de66524cristy    kernel->height*sizeof(double));
21767a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if (new_kernel->values == (double *) NULL)
21777a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(DestroyKernelInfo(new_kernel));
2178bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
21797a01dcf50ce12cb2a789bedff51e9345f022432eanthony    new_kernel->values[i]=kernel->values[i];
21801b2bc0a7da432e6e1cc0480280402df213faa940anthony
21811b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* Also clone the next kernel in the kernel list */
21821b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL ) {
21831b2bc0a7da432e6e1cc0480280402df213faa940anthony    new_kernel->next = CloneKernelInfo(kernel->next);
21841b2bc0a7da432e6e1cc0480280402df213faa940anthony    if ( new_kernel->next == (KernelInfo *) NULL )
21851b2bc0a7da432e6e1cc0480280402df213faa940anthony      return(DestroyKernelInfo(new_kernel));
21861b2bc0a7da432e6e1cc0480280402df213faa940anthony  }
21871b2bc0a7da432e6e1cc0480280402df213faa940anthony
21887a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(new_kernel);
21894fd27e21043be809d66c8202e779255e5b660d2danthony}
21904fd27e21043be809d66c8202e779255e5b660d2danthony
21914fd27e21043be809d66c8202e779255e5b660d2danthony/*
21924fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21934fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21944fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21954fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
219683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     D e s t r o y K e r n e l I n f o                                       %
2197602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2198602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2199602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2200602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2201602ab9b30b644a78a4057da93d838a77391ec0acanthony%
220283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
220383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  kernel.
2204602ab9b30b644a78a4057da93d838a77391ec0acanthony%
220583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the DestroyKernelInfo method is:
2206602ab9b30b644a78a4057da93d838a77391ec0acanthony%
220783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2208602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2209602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2210602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2211602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: the Morphology/Convolution kernel to be destroyed
2212602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2213602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
221483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthonyMagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2215602ab9b30b644a78a4057da93d838a77391ec0acanthony{
22162be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  assert(kernel != (KernelInfo *) NULL);
22174fd27e21043be809d66c8202e779255e5b660d2danthony
22187a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( kernel->next != (KernelInfo *) NULL )
22197a01dcf50ce12cb2a789bedff51e9345f022432eanthony    kernel->next = DestroyKernelInfo(kernel->next);
22207a01dcf50ce12cb2a789bedff51e9345f022432eanthony
22217a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->values = (double *)RelinquishMagickMemory(kernel->values);
22227a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel = (KernelInfo *) RelinquishMagickMemory(kernel);
2223602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2224602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2225c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2226c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony/*
2227c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2228c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2229c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2230c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2231ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d M i r r o r K e r n e l I n f o                             %
22323c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22343c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22353c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2237bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
2238bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  sequence of 90-degree rotated kernels but providing a reflected 180
2239bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  rotatation, before the -/+ 90-degree rotations.
2240bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2241bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  This special rotation order produces a better, more symetrical thinning of
2242bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  objects.
2243bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2244bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandMirrorKernelInfo method is:
2245bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2246bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandMirrorKernelInfo(KernelInfo *kernel)
2247bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2248bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  A description of each parameter follows:
2249bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2250bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    o kernel: the Morphology/Convolution kernel
2251bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2252bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% This function is only internel to this module, as it is not finalized,
2253bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% especially with regard to non-orthogonal angles, and rotation of larger
2254bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% 2D kernels.
2255bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony*/
2256bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2257bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#if 0
2258bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void FlopKernelInfo(KernelInfo *kernel)
2259bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    { /* Do a Flop by reversing each row. */
2260bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      size_t
2261bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        y;
2262bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register ssize_t
2263bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        x,r;
2264bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register double
2265bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        *k,t;
2266bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2267bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2268bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2269bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          t=k[x],  k[x]=k[r],  k[r]=t;
2270bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2271bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      kernel->x = kernel->width - kernel->x - 1;
2272bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      angle = fmod(angle+180.0, 360.0);
2273bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    }
2274bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#endif
2275bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2276bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandMirrorKernelInfo(KernelInfo *kernel)
2277bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony{
2278bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  KernelInfo
2279bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *clone,
2280bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *last;
2281bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2282bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = kernel;
2283bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2284bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2285bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);   /* flip */
2286bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2287bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2288bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2289bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2290bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 90);   /* transpose */
2291bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2292bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2293bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2294bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2295bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);  /* flop */
2296bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2297bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2298bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  return;
2299bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony}
2300bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2301bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony/*
2302bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2303bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2304bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2305bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2306ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d R o t a t e K e r n e l I n f o                             %
2307bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2308bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2309bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2310bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2311bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2312bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2313529482f4b494010a13338a74446c510712f670b3anthony%  incrementally by the angle given, until the kernel repeats.
23143c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23153c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  WARNING: 45 degree rotations only works for 3x3 kernels.
23163c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  While 90 degree roatations only works for linear and square kernels
23173c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2318bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandRotateKernelInfo method is:
23193c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2320bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
23213c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23223c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  A description of each parameter follows:
23233c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23243c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o kernel: the Morphology/Convolution kernel
23253c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23263c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o angle: angle to rotate in degrees
23273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% This function is only internel to this module, as it is not finalized,
23293c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% especially with regard to non-orthogonal angles, and rotation of larger
23303c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% 2D kernels.
23313c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony*/
233247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
233347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony/* Internal Routine - Return true if two kernels are the same */
233447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthonystatic MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
233547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony     const KernelInfo *kernel2)
233647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony{
2337bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
233847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    i;
23391d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
23401d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check size and origin location */
23411d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  if (    kernel1->width != kernel2->width
23421d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->height != kernel2->height
23431d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->x != kernel2->x
23441d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->y != kernel2->y )
234547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return MagickFalse;
23461d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
23471d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check actual kernel values */
234847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  for (i=0; i < (kernel1->width*kernel1->height); i++) {
2349f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test for Nan equivalence */
235047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    if ( IsNan(kernel1->values[i]) && !IsNan(kernel2->values[i]) )
235147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
235247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    if ( IsNan(kernel2->values[i]) && !IsNan(kernel1->values[i]) )
235347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
2354f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test actual values are equivalent */
235547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    if ( fabs(kernel1->values[i] - kernel2->values[i]) > MagickEpsilon )
235647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
235747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
23581d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
235947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return MagickTrue;
236047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony}
236147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
2362bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
23633c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony{
23643c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  KernelInfo
236584d9b5596c0900609dea18795861e2b0936b21c6cristy    *clone,
23663c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    *last;
2367a9a61ad96c5112acd968f97b689bd42ca392d70bcristy
23683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  last = kernel;
236947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while(1) {
237084d9b5596c0900609dea18795861e2b0936b21c6cristy    clone = CloneKernelInfo(last);
237184d9b5596c0900609dea18795861e2b0936b21c6cristy    RotateKernelInfo(clone, angle);
237284d9b5596c0900609dea18795861e2b0936b21c6cristy    if ( SameKernelInfo(kernel, clone) == MagickTrue )
237347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
2374bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    LastKernelInfo(last)->next = clone;
237584d9b5596c0900609dea18795861e2b0936b21c6cristy    last = clone;
23763c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  }
2377bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
237847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return;
23793c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony}
23803c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
23813c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony/*
23823c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23833c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
23843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
23853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
238646a369d839971ab627bdb31a93d8bd63e81b65a3anthony+     C a l c M e t a K e r n a l I n f o                                     %
238746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
238846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
238946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
239046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
239246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
2393dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  using the kernel values.  This should only ne used if it is not possible to
239446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  calculate that meta-data in some easier way.
239546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
239646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It is important that the meta-data is correct before ScaleKernelInfo() is
239746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  used to perform kernel normalization.
239846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
239946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the CalcKernelMetaData method is:
240046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
240146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
240246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
240346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
240446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
240546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
240646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
240746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Minimum and Maximum values are assumed to include zero, even if
240846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  zero is not part of the kernel (as in Gaussian Derived kernels). This
240946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  however is not true for flat-shaped morphological kernels.
241046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
241146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Only the specific kernel pointed to is modified, not a list of
241246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  multiple kernels.
241346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
241446a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This is an internal function and not expected to be useful outside this
241546a369d839971ab627bdb31a93d8bd63e81b65a3anthony% module.  This could change however.
241646a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
241746a369d839971ab627bdb31a93d8bd63e81b65a3anthonystatic void CalcKernelMetaData(KernelInfo *kernel)
241846a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
2419bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
242046a369d839971ab627bdb31a93d8bd63e81b65a3anthony    i;
242146a369d839971ab627bdb31a93d8bd63e81b65a3anthony
242246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->minimum = kernel->maximum = 0.0;
242346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->negative_range = kernel->positive_range = 0.0;
242446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  for (i=0; i < (kernel->width*kernel->height); i++)
242546a369d839971ab627bdb31a93d8bd63e81b65a3anthony    {
242646a369d839971ab627bdb31a93d8bd63e81b65a3anthony      if ( fabs(kernel->values[i]) < MagickEpsilon )
242746a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->values[i] = 0.0;
242846a369d839971ab627bdb31a93d8bd63e81b65a3anthony      ( kernel->values[i] < 0)
242946a369d839971ab627bdb31a93d8bd63e81b65a3anthony          ?  ( kernel->negative_range += kernel->values[i] )
243046a369d839971ab627bdb31a93d8bd63e81b65a3anthony          :  ( kernel->positive_range += kernel->values[i] );
243146a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Minimize(kernel->minimum, kernel->values[i]);
243246a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Maximize(kernel->maximum, kernel->values[i]);
243346a369d839971ab627bdb31a93d8bd63e81b65a3anthony    }
243446a369d839971ab627bdb31a93d8bd63e81b65a3anthony
243546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
243646a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
243746a369d839971ab627bdb31a93d8bd63e81b65a3anthony
243846a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
243946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
24439eb4f74649b23c053b308ce1152dce51239450baanthony%     M o r p h o l o g y A p p l y                                           %
2444602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2445602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2446602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2447602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2448602ab9b30b644a78a4057da93d838a77391ec0acanthony%
24499eb4f74649b23c053b308ce1152dce51239450baanthony%  MorphologyApply() applies a morphological method, multiple times using
24509eb4f74649b23c053b308ce1152dce51239450baanthony%  a list of multiple kernels.
2451602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2452f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  It is basically equivalent to as MorphologyImage() (see below) but
2453e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  without any user controls.  This allows internel programs to use this
2454dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  function, to actually perform a specific task without possible interference
2455e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  by any API user supplied settings.
2456e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%
2457f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  It is MorphologyImage() task to extract any such user controls, and
2458e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  pass them to this function for processing.
24599eb4f74649b23c053b308ce1152dce51239450baanthony%
24609eb4f74649b23c053b308ce1152dce51239450baanthony%  More specifically kernels are not normalized/scaled/blended by the
2461e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  'convolve:scale' Image Artifact (setting), nor is the convolve bias
2462e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  (-bias setting or image->bias) loooked at, but must be supplied from the
2463e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  function arguments.
2464602ab9b30b644a78a4057da93d838a77391ec0acanthony%
246547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%  The format of the MorphologyApply method is:
2466602ab9b30b644a78a4057da93d838a77391ec0acanthony%
24679eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyApply(const Image *image,MorphologyMethod method,
2468f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%        const ssize_t iterations,const KernelInfo *kernel,
2469f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%        const CompositeMethod compose,const double bias,
2470f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%        ExceptionInfo *exception)
2471602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2472602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2473602ab9b30b644a78a4057da93d838a77391ec0acanthony%
24748d18850dee4bed193a64866a6d2353eeeb73e145anthony%    o image: the source image
2475602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2476602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o method: the morphology method to be applied.
2477602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2478602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o iterations: apply the operation this many times (or no change).
2479602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  A value of -1 means loop until no change found.
2480602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  How this is applied may depend on the morphology method.
2481602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  Typically this is a value of 1.
2482602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2483602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o channel: the channel type.
2484602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2485602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: An array of double representing the morphology kernel.
2486602ab9b30b644a78a4057da93d838a77391ec0acanthony%
248747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%    o compose: How to handle or merge multi-kernel results.
24888d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'UndefinedCompositeOp' use default for the Morphology method.
24898d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'NoCompositeOp' force image to be re-iterated by each kernel.
24908d18850dee4bed193a64866a6d2353eeeb73e145anthony%          Otherwise merge the results using the compose method given.
249147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%
249247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%    o bias: Convolution Output Bias.
2493602ab9b30b644a78a4057da93d838a77391ec0acanthony%
24949eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
2495602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2496602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
2497602ab9b30b644a78a4057da93d838a77391ec0acanthony
24989eb4f74649b23c053b308ce1152dce51239450baanthony/* Apply a Morphology Primative to an image using the given kernel.
2499a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** Two pre-created images must be provided, and no image is created.
2500ea61f01656bb0f9074677452017cc559e54093faanthony** It returns the number of pixels that changed between the images
2501a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** for result convergence determination.
25029eb4f74649b23c053b308ce1152dce51239450baanthony*/
2503f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristystatic ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
2504f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const KernelInfo *kernel,const double bias,
2505f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
2506602ab9b30b644a78a4057da93d838a77391ec0acanthony{
25072be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy#define MorphologyTag  "Morphology/Image"
2508602ab9b30b644a78a4057da93d838a77391ec0acanthony
25095f959473f334e196c6bf39b740c12cb4963fceebcristy  CacheView
25104c08aed51c5899665ade97263692328eea4af106cristy    *image_view,
25114c08aed51c5899665ade97263692328eea4af106cristy    *morphology_view;
25125f959473f334e196c6bf39b740c12cb4963fceebcristy
2513bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  ssize_t
2514a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    y, offx, offy;
2515a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
2516a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
2517db60568e12574785101a4ae8d8da076227a0a889anthony    virt_width,
2518602ab9b30b644a78a4057da93d838a77391ec0acanthony    changed;
2519602ab9b30b644a78a4057da93d838a77391ec0acanthony
2520602ab9b30b644a78a4057da93d838a77391ec0acanthony  MagickBooleanType
2521602ab9b30b644a78a4057da93d838a77391ec0acanthony    status;
2522602ab9b30b644a78a4057da93d838a77391ec0acanthony
25235f959473f334e196c6bf39b740c12cb4963fceebcristy  MagickOffsetType
25245f959473f334e196c6bf39b740c12cb4963fceebcristy    progress;
2525602ab9b30b644a78a4057da93d838a77391ec0acanthony
2526e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image != (Image *) NULL);
2527e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image->signature == MagickSignature);
25284c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image != (Image *) NULL);
25294c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image->signature == MagickSignature);
2530e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel != (KernelInfo *) NULL);
2531e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel->signature == MagickSignature);
2532e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception != (ExceptionInfo *) NULL);
2533e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception->signature == MagickSignature);
2534e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony
2535602ab9b30b644a78a4057da93d838a77391ec0acanthony  status=MagickTrue;
2536602ab9b30b644a78a4057da93d838a77391ec0acanthony  changed=0;
2537602ab9b30b644a78a4057da93d838a77391ec0acanthony  progress=0;
2538602ab9b30b644a78a4057da93d838a77391ec0acanthony
25394c08aed51c5899665ade97263692328eea4af106cristy  image_view=AcquireCacheView(image);
25404c08aed51c5899665ade97263692328eea4af106cristy  morphology_view=AcquireCacheView(morphology_image);
25416eac8d74fdd2c99429aba3b8404008909a44261fanthony  virt_width=image->columns+kernel->width-1;
254229188a8682a98d4b7882cca434b170517555fc7danthony
2543cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  /* Some methods (including convolve) needs use a reflected kernel.
25449eb4f74649b23c053b308ce1152dce51239450baanthony   * Adjust 'origin' offsets to loop though kernel as a reflection.
254529188a8682a98d4b7882cca434b170517555fc7danthony   */
2546c99304fe3c8d9c617da792b40b57c118bb1249afcristy  offx = kernel->x;
2547c99304fe3c8d9c617da792b40b57c118bb1249afcristy  offy = kernel->y;
254829188a8682a98d4b7882cca434b170517555fc7danthony  switch(method) {
2549930be614b4595b97cd79ee864a394796740f76adanthony    case ConvolveMorphology:
2550930be614b4595b97cd79ee864a394796740f76adanthony    case DilateMorphology:
2551930be614b4595b97cd79ee864a394796740f76adanthony    case DilateIntensityMorphology:
2552a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /*case DistanceMorphology:*/
25535ef8e94ff55717be2387d537bd49025780a1a558anthony      /* kernel needs to used with reflection about origin */
2554bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      offx = (ssize_t) kernel->width-offx-1;
2555bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      offy = (ssize_t) kernel->height-offy-1;
255629188a8682a98d4b7882cca434b170517555fc7danthony      break;
25575ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeMorphology:
25585ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeIntensityMorphology:
25595ef8e94ff55717be2387d537bd49025780a1a558anthony    case HitAndMissMorphology:
25605ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThinningMorphology:
25615ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThickenMorphology:
25623ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      /* kernel is used as is, without reflection */
25635ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
2564930be614b4595b97cd79ee864a394796740f76adanthony    default:
25659eb4f74649b23c053b308ce1152dce51239450baanthony      assert("Not a Primitive Morphology Method" != (char *) NULL);
2566930be614b4595b97cd79ee864a394796740f76adanthony      break;
256729188a8682a98d4b7882cca434b170517555fc7danthony  }
256829188a8682a98d4b7882cca434b170517555fc7danthony
25698d18850dee4bed193a64866a6d2353eeeb73e145anthony  if ( method == ConvolveMorphology && kernel->width == 1 )
25708d18850dee4bed193a64866a6d2353eeeb73e145anthony  { /* Special handling (for speed) of vertical (blur) kernels.
25718d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** This performs its handling in columns rather than in rows.
2572ea61f01656bb0f9074677452017cc559e54093faanthony    ** This is only done for convolve as it is the only method that
25738d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** generates very large 1-D vertical kernels (such as a 'BlurKernel')
25748d18850dee4bed193a64866a6d2353eeeb73e145anthony    **
25758d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** Timing tests (on single CPU laptop)
25768d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** Using a vertical 1-d Blue with normal row-by-row (below)
25778d18850dee4bed193a64866a6d2353eeeb73e145anthony    **   time convert logo: -morphology Convolve Blur:0x10+90 null:
25788d18850dee4bed193a64866a6d2353eeeb73e145anthony    **      0.807u
25798d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** Using this column method
25808d18850dee4bed193a64866a6d2353eeeb73e145anthony    **   time convert logo: -morphology Convolve Blur:0x10+90 null:
25818d18850dee4bed193a64866a6d2353eeeb73e145anthony    **      0.620u
25828d18850dee4bed193a64866a6d2353eeeb73e145anthony    **
25838d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** Anthony Thyssen, 14 June 2010
25848d18850dee4bed193a64866a6d2353eeeb73e145anthony    */
2585fd1175952254cf1ac848ddb441e483c5e33d517fcristy    register ssize_t
2586fd1175952254cf1ac848ddb441e483c5e33d517fcristy      x;
2587fd1175952254cf1ac848ddb441e483c5e33d517fcristy
25888d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
25898d18850dee4bed193a64866a6d2353eeeb73e145anthony#pragma omp parallel for schedule(dynamic,4) shared(progress,status)
25908d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
25918d18850dee4bed193a64866a6d2353eeeb73e145anthony    for (x=0; x < (ssize_t) image->columns; x++)
25928d18850dee4bed193a64866a6d2353eeeb73e145anthony    {
25934c08aed51c5899665ade97263692328eea4af106cristy      register const Quantum
25948d18850dee4bed193a64866a6d2353eeeb73e145anthony        *restrict p;
25958d18850dee4bed193a64866a6d2353eeeb73e145anthony
25964c08aed51c5899665ade97263692328eea4af106cristy      register Quantum
25978d18850dee4bed193a64866a6d2353eeeb73e145anthony        *restrict q;
25988d18850dee4bed193a64866a6d2353eeeb73e145anthony
25998d18850dee4bed193a64866a6d2353eeeb73e145anthony      register ssize_t
26008d18850dee4bed193a64866a6d2353eeeb73e145anthony        y;
26018d18850dee4bed193a64866a6d2353eeeb73e145anthony
260255a91cddcdea3aa002893186a773e1704884a9dfcristy      ssize_t
26038d18850dee4bed193a64866a6d2353eeeb73e145anthony        r;
26048d18850dee4bed193a64866a6d2353eeeb73e145anthony
26058d18850dee4bed193a64866a6d2353eeeb73e145anthony      if (status == MagickFalse)
26068d18850dee4bed193a64866a6d2353eeeb73e145anthony        continue;
2607c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy      p=GetCacheViewVirtualPixels(image_view,x,-offy,1,image->rows+
2608c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy        kernel->height-1,exception);
2609c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy      q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2610c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy        morphology_image->rows,exception);
26114c08aed51c5899665ade97263692328eea4af106cristy      if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
26128d18850dee4bed193a64866a6d2353eeeb73e145anthony        {
26138d18850dee4bed193a64866a6d2353eeeb73e145anthony          status=MagickFalse;
26148d18850dee4bed193a64866a6d2353eeeb73e145anthony          continue;
26158d18850dee4bed193a64866a6d2353eeeb73e145anthony        }
2616a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* offset to origin in 'p'. while 'q' points to it directly */
2617a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      r = offy;
26188d18850dee4bed193a64866a6d2353eeeb73e145anthony
26198d18850dee4bed193a64866a6d2353eeeb73e145anthony      for (y=0; y < (ssize_t) image->rows; y++)
26208d18850dee4bed193a64866a6d2353eeeb73e145anthony      {
262170b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy        PixelInfo
262270b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy          result;
262370b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy
26248d18850dee4bed193a64866a6d2353eeeb73e145anthony        register ssize_t
26258d18850dee4bed193a64866a6d2353eeeb73e145anthony          v;
26268d18850dee4bed193a64866a6d2353eeeb73e145anthony
26278d18850dee4bed193a64866a6d2353eeeb73e145anthony        register const double
26288d18850dee4bed193a64866a6d2353eeeb73e145anthony          *restrict k;
26298d18850dee4bed193a64866a6d2353eeeb73e145anthony
26304c08aed51c5899665ade97263692328eea4af106cristy        register const Quantum
26318d18850dee4bed193a64866a6d2353eeeb73e145anthony          *restrict k_pixels;
26328d18850dee4bed193a64866a6d2353eeeb73e145anthony
26338d18850dee4bed193a64866a6d2353eeeb73e145anthony        /* Copy input image to the output image for unused channels
26348d18850dee4bed193a64866a6d2353eeeb73e145anthony        * This removes need for 'cloning' a new image every iteration
26358d18850dee4bed193a64866a6d2353eeeb73e145anthony        */
26364c08aed51c5899665ade97263692328eea4af106cristy        SetPixelRed(morphology_image,GetPixelRed(image,p+r*
2637ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          GetPixelChannels(image)),q);
26384c08aed51c5899665ade97263692328eea4af106cristy        SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
2639ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          GetPixelChannels(image)),q);
26404c08aed51c5899665ade97263692328eea4af106cristy        SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
2641ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          GetPixelChannels(image)),q);
26428d18850dee4bed193a64866a6d2353eeeb73e145anthony        if (image->colorspace == CMYKColorspace)
26434c08aed51c5899665ade97263692328eea4af106cristy          SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
2644ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            GetPixelChannels(image)),q);
26458d18850dee4bed193a64866a6d2353eeeb73e145anthony
26468d18850dee4bed193a64866a6d2353eeeb73e145anthony        /* Set the bias of the weighted average output */
26478d18850dee4bed193a64866a6d2353eeeb73e145anthony        result.red     =
26488d18850dee4bed193a64866a6d2353eeeb73e145anthony        result.green   =
26498d18850dee4bed193a64866a6d2353eeeb73e145anthony        result.blue    =
26504c08aed51c5899665ade97263692328eea4af106cristy        result.alpha =
26514c08aed51c5899665ade97263692328eea4af106cristy        result.black   = bias;
26528d18850dee4bed193a64866a6d2353eeeb73e145anthony
26538d18850dee4bed193a64866a6d2353eeeb73e145anthony
26548d18850dee4bed193a64866a6d2353eeeb73e145anthony        /* Weighted Average of pixels using reflected kernel
26558d18850dee4bed193a64866a6d2353eeeb73e145anthony        **
26568d18850dee4bed193a64866a6d2353eeeb73e145anthony        ** NOTE for correct working of this operation for asymetrical
26578d18850dee4bed193a64866a6d2353eeeb73e145anthony        ** kernels, the kernel needs to be applied in its reflected form.
26588d18850dee4bed193a64866a6d2353eeeb73e145anthony        ** That is its values needs to be reversed.
26598d18850dee4bed193a64866a6d2353eeeb73e145anthony        */
26608d18850dee4bed193a64866a6d2353eeeb73e145anthony        k = &kernel->values[ kernel->height-1 ];
26618d18850dee4bed193a64866a6d2353eeeb73e145anthony        k_pixels = p;
2662c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy        if ( (image->sync == MagickFalse) || (image->matte == MagickFalse) )
26638d18850dee4bed193a64866a6d2353eeeb73e145anthony          { /* No 'Sync' involved.
26648d18850dee4bed193a64866a6d2353eeeb73e145anthony            ** Convolution is simple greyscale channel operation
26658d18850dee4bed193a64866a6d2353eeeb73e145anthony            */
26668d18850dee4bed193a64866a6d2353eeeb73e145anthony            for (v=0; v < (ssize_t) kernel->height; v++) {
26678d18850dee4bed193a64866a6d2353eeeb73e145anthony              if ( IsNan(*k) ) continue;
26684c08aed51c5899665ade97263692328eea4af106cristy              result.red     += (*k)*GetPixelRed(image,k_pixels);
26694c08aed51c5899665ade97263692328eea4af106cristy              result.green   += (*k)*GetPixelGreen(image,k_pixels);
26704c08aed51c5899665ade97263692328eea4af106cristy              result.blue    += (*k)*GetPixelBlue(image,k_pixels);
26714c08aed51c5899665ade97263692328eea4af106cristy              if (image->colorspace == CMYKColorspace)
26724c08aed51c5899665ade97263692328eea4af106cristy                result.black+=(*k)*GetPixelBlack(image,k_pixels);
26734c08aed51c5899665ade97263692328eea4af106cristy              result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
26748d18850dee4bed193a64866a6d2353eeeb73e145anthony              k--;
2675ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels+=GetPixelChannels(image);
26768d18850dee4bed193a64866a6d2353eeeb73e145anthony            }
2677ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
26784c08aed51c5899665ade97263692328eea4af106cristy              SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
2679ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
26804c08aed51c5899665ade97263692328eea4af106cristy              SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
2681ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
26824c08aed51c5899665ade97263692328eea4af106cristy              SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
2683ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
26844c08aed51c5899665ade97263692328eea4af106cristy                (image->colorspace == CMYKColorspace))
26854c08aed51c5899665ade97263692328eea4af106cristy              SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
2686ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
26874c08aed51c5899665ade97263692328eea4af106cristy                (image->matte == MagickTrue))
26884c08aed51c5899665ade97263692328eea4af106cristy              SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
26898d18850dee4bed193a64866a6d2353eeeb73e145anthony          }
26908d18850dee4bed193a64866a6d2353eeeb73e145anthony        else
26918d18850dee4bed193a64866a6d2353eeeb73e145anthony          { /* Channel 'Sync' Flag, and Alpha Channel enabled.
26928d18850dee4bed193a64866a6d2353eeeb73e145anthony            ** Weight the color channels with Alpha Channel so that
26938d18850dee4bed193a64866a6d2353eeeb73e145anthony            ** transparent pixels are not part of the results.
26948d18850dee4bed193a64866a6d2353eeeb73e145anthony            */
26958d18850dee4bed193a64866a6d2353eeeb73e145anthony            MagickRealType
26968d18850dee4bed193a64866a6d2353eeeb73e145anthony              alpha,  /* alpha weighting of colors : kernel*alpha  */
26978d18850dee4bed193a64866a6d2353eeeb73e145anthony              gamma;  /* divisor, sum of color weighting values */
26988d18850dee4bed193a64866a6d2353eeeb73e145anthony
26998d18850dee4bed193a64866a6d2353eeeb73e145anthony            gamma=0.0;
27008d18850dee4bed193a64866a6d2353eeeb73e145anthony            for (v=0; v < (ssize_t) kernel->height; v++) {
27018d18850dee4bed193a64866a6d2353eeeb73e145anthony              if ( IsNan(*k) ) continue;
27024c08aed51c5899665ade97263692328eea4af106cristy              alpha=(*k)*(QuantumScale*GetPixelAlpha(image,k_pixels));
27038d18850dee4bed193a64866a6d2353eeeb73e145anthony              gamma += alpha;
27044c08aed51c5899665ade97263692328eea4af106cristy              result.red     += alpha*GetPixelRed(image,k_pixels);
27054c08aed51c5899665ade97263692328eea4af106cristy              result.green   += alpha*GetPixelGreen(image,k_pixels);
27064c08aed51c5899665ade97263692328eea4af106cristy              result.blue    += alpha*GetPixelBlue(image,k_pixels);
27074c08aed51c5899665ade97263692328eea4af106cristy              if (image->colorspace == CMYKColorspace)
27084c08aed51c5899665ade97263692328eea4af106cristy                result.black += alpha*GetPixelBlack(image,k_pixels);
27094c08aed51c5899665ade97263692328eea4af106cristy              result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
27108d18850dee4bed193a64866a6d2353eeeb73e145anthony              k--;
2711ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels+=GetPixelChannels(image);
27128d18850dee4bed193a64866a6d2353eeeb73e145anthony            }
27138d18850dee4bed193a64866a6d2353eeeb73e145anthony            /* Sync'ed channels, all channels are modified */
27148d18850dee4bed193a64866a6d2353eeeb73e145anthony            gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
27154c08aed51c5899665ade97263692328eea4af106cristy            SetPixelRed(morphology_image,
27164c08aed51c5899665ade97263692328eea4af106cristy              ClampToQuantum(gamma*result.red),q);
27174c08aed51c5899665ade97263692328eea4af106cristy            SetPixelGreen(morphology_image,
27184c08aed51c5899665ade97263692328eea4af106cristy              ClampToQuantum(gamma*result.green),q);
27194c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlue(morphology_image,
27204c08aed51c5899665ade97263692328eea4af106cristy              ClampToQuantum(gamma*result.blue),q);
27218d18850dee4bed193a64866a6d2353eeeb73e145anthony            if (image->colorspace == CMYKColorspace)
27224c08aed51c5899665ade97263692328eea4af106cristy              SetPixelBlack(morphology_image,
27234c08aed51c5899665ade97263692328eea4af106cristy                ClampToQuantum(gamma*result.black),q);
27244c08aed51c5899665ade97263692328eea4af106cristy            SetPixelAlpha(morphology_image,
27254c08aed51c5899665ade97263692328eea4af106cristy              ClampToQuantum(result.alpha),q);
27268d18850dee4bed193a64866a6d2353eeeb73e145anthony          }
27278d18850dee4bed193a64866a6d2353eeeb73e145anthony
27288d18850dee4bed193a64866a6d2353eeeb73e145anthony        /* Count up changed pixels */
2729ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q))
2730ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q))
2731ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q))
2732ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q))
27334c08aed51c5899665ade97263692328eea4af106cristy            || ((image->colorspace == CMYKColorspace) &&
2734ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
27358d18850dee4bed193a64866a6d2353eeeb73e145anthony          changed++;  /* The pixel was changed in some way! */
2736ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        p+=GetPixelChannels(image);
2737ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        q+=GetPixelChannels(morphology_image);
27388d18850dee4bed193a64866a6d2353eeeb73e145anthony      } /* y */
27394c08aed51c5899665ade97263692328eea4af106cristy      if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
27408d18850dee4bed193a64866a6d2353eeeb73e145anthony        status=MagickFalse;
27418d18850dee4bed193a64866a6d2353eeeb73e145anthony      if (image->progress_monitor != (MagickProgressMonitor) NULL)
27428d18850dee4bed193a64866a6d2353eeeb73e145anthony        {
27438d18850dee4bed193a64866a6d2353eeeb73e145anthony          MagickBooleanType
27448d18850dee4bed193a64866a6d2353eeeb73e145anthony            proceed;
27458d18850dee4bed193a64866a6d2353eeeb73e145anthony
27468d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
27478d18850dee4bed193a64866a6d2353eeeb73e145anthony  #pragma omp critical (MagickCore_MorphologyImage)
27488d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
27498d18850dee4bed193a64866a6d2353eeeb73e145anthony          proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
27508d18850dee4bed193a64866a6d2353eeeb73e145anthony          if (proceed == MagickFalse)
27518d18850dee4bed193a64866a6d2353eeeb73e145anthony            status=MagickFalse;
27528d18850dee4bed193a64866a6d2353eeeb73e145anthony        }
27538d18850dee4bed193a64866a6d2353eeeb73e145anthony    } /* x */
27544c08aed51c5899665ade97263692328eea4af106cristy    morphology_image->type=image->type;
27554c08aed51c5899665ade97263692328eea4af106cristy    morphology_view=DestroyCacheView(morphology_view);
27564c08aed51c5899665ade97263692328eea4af106cristy    image_view=DestroyCacheView(image_view);
2757aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy    return(status ? (ssize_t) changed : 0);
27588d18850dee4bed193a64866a6d2353eeeb73e145anthony  }
27598d18850dee4bed193a64866a6d2353eeeb73e145anthony
27608d18850dee4bed193a64866a6d2353eeeb73e145anthony  /*
27618d18850dee4bed193a64866a6d2353eeeb73e145anthony  ** Normal handling of horizontal or rectangular kernels (row by row)
27628d18850dee4bed193a64866a6d2353eeeb73e145anthony  */
2763602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2764602ab9b30b644a78a4057da93d838a77391ec0acanthony  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2765602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
2766bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (y=0; y < (ssize_t) image->rows; y++)
2767602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
27684c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
2769602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict p;
2770602ab9b30b644a78a4057da93d838a77391ec0acanthony
27714c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
2772602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict q;
2773602ab9b30b644a78a4057da93d838a77391ec0acanthony
2774bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
2775602ab9b30b644a78a4057da93d838a77391ec0acanthony      x;
2776602ab9b30b644a78a4057da93d838a77391ec0acanthony
2777bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    size_t
2778602ab9b30b644a78a4057da93d838a77391ec0acanthony      r;
2779602ab9b30b644a78a4057da93d838a77391ec0acanthony
2780602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (status == MagickFalse)
2781602ab9b30b644a78a4057da93d838a77391ec0acanthony      continue;
27824c08aed51c5899665ade97263692328eea4af106cristy    p=GetCacheViewVirtualPixels(image_view, -offx, y-offy, virt_width,
2783c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy      kernel->height,  exception);
2784c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,
2785c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy      morphology_image->columns,1,exception);
27864c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2787602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2788602ab9b30b644a78a4057da93d838a77391ec0acanthony        status=MagickFalse;
2789602ab9b30b644a78a4057da93d838a77391ec0acanthony        continue;
2790602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2791a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* offset to origin in 'p'. while 'q' points to it directly */
2792db60568e12574785101a4ae8d8da076227a0a889anthony    r = virt_width*offy + offx;
279329188a8682a98d4b7882cca434b170517555fc7danthony
2794bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) image->columns; x++)
2795602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
2796bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy       ssize_t
2797602ab9b30b644a78a4057da93d838a77391ec0acanthony        v;
2798602ab9b30b644a78a4057da93d838a77391ec0acanthony
2799bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
2800602ab9b30b644a78a4057da93d838a77391ec0acanthony        u;
2801602ab9b30b644a78a4057da93d838a77391ec0acanthony
2802602ab9b30b644a78a4057da93d838a77391ec0acanthony      register const double
2803602ab9b30b644a78a4057da93d838a77391ec0acanthony        *restrict k;
2804602ab9b30b644a78a4057da93d838a77391ec0acanthony
28054c08aed51c5899665ade97263692328eea4af106cristy      register const Quantum
2806602ab9b30b644a78a4057da93d838a77391ec0acanthony        *restrict k_pixels;
2807602ab9b30b644a78a4057da93d838a77391ec0acanthony
28084c08aed51c5899665ade97263692328eea4af106cristy      PixelInfo
28095ef8e94ff55717be2387d537bd49025780a1a558anthony        result,
28105ef8e94ff55717be2387d537bd49025780a1a558anthony        min,
28115ef8e94ff55717be2387d537bd49025780a1a558anthony        max;
2812602ab9b30b644a78a4057da93d838a77391ec0acanthony
2813c406ea496196b962e4bc865ed8f6a4491241c6edanthony      /* Copy input image to the output image for unused channels
281483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony       * This removes need for 'cloning' a new image every iteration
281529188a8682a98d4b7882cca434b170517555fc7danthony       */
28164c08aed51c5899665ade97263692328eea4af106cristy      SetPixelRed(morphology_image,GetPixelRed(image,p+r*
2817ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        GetPixelChannels(image)),q);
28184c08aed51c5899665ade97263692328eea4af106cristy      SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
2819ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        GetPixelChannels(image)),q);
28204c08aed51c5899665ade97263692328eea4af106cristy      SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
2821ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        GetPixelChannels(image)),q);
2822602ab9b30b644a78a4057da93d838a77391ec0acanthony      if (image->colorspace == CMYKColorspace)
28234c08aed51c5899665ade97263692328eea4af106cristy        SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
2824ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          GetPixelChannels(image)),q);
2825602ab9b30b644a78a4057da93d838a77391ec0acanthony
28265ef8e94ff55717be2387d537bd49025780a1a558anthony      /* Defaults */
28275ef8e94ff55717be2387d537bd49025780a1a558anthony      min.red     =
28285ef8e94ff55717be2387d537bd49025780a1a558anthony      min.green   =
28295ef8e94ff55717be2387d537bd49025780a1a558anthony      min.blue    =
28304c08aed51c5899665ade97263692328eea4af106cristy      min.alpha =
28314c08aed51c5899665ade97263692328eea4af106cristy      min.black   = (MagickRealType) QuantumRange;
28325ef8e94ff55717be2387d537bd49025780a1a558anthony      max.red     =
28335ef8e94ff55717be2387d537bd49025780a1a558anthony      max.green   =
28345ef8e94ff55717be2387d537bd49025780a1a558anthony      max.blue    =
28354c08aed51c5899665ade97263692328eea4af106cristy      max.alpha =
28364c08aed51c5899665ade97263692328eea4af106cristy      max.black   = (MagickRealType) 0;
28379eb4f74649b23c053b308ce1152dce51239450baanthony      /* default result is the original pixel value */
2838ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      result.red     = (MagickRealType) GetPixelRed(image,p+r*GetPixelChannels(image));
2839ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      result.green   = (MagickRealType) GetPixelGreen(image,p+r*GetPixelChannels(image));
2840ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      result.blue    = (MagickRealType) GetPixelBlue(image,p+r*GetPixelChannels(image));
28414c08aed51c5899665ade97263692328eea4af106cristy      result.black   = 0.0;
28424c08aed51c5899665ade97263692328eea4af106cristy      if (image->colorspace == CMYKColorspace)
2843ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        result.black = (MagickRealType) GetPixelBlack(image,p+r*GetPixelChannels(image));
2844ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      result.alpha=(MagickRealType) GetPixelAlpha(image,p+r*GetPixelChannels(image));
28455ef8e94ff55717be2387d537bd49025780a1a558anthony
2846602ab9b30b644a78a4057da93d838a77391ec0acanthony      switch (method) {
2847602ab9b30b644a78a4057da93d838a77391ec0acanthony        case ConvolveMorphology:
28488d18850dee4bed193a64866a6d2353eeeb73e145anthony          /* Set the bias of the weighted average output */
28499eb4f74649b23c053b308ce1152dce51239450baanthony          result.red     =
28509eb4f74649b23c053b308ce1152dce51239450baanthony          result.green   =
28519eb4f74649b23c053b308ce1152dce51239450baanthony          result.blue    =
28524c08aed51c5899665ade97263692328eea4af106cristy          result.alpha =
28534c08aed51c5899665ade97263692328eea4af106cristy          result.black   = bias;
2854930be614b4595b97cd79ee864a394796740f76adanthony          break;
28554fd27e21043be809d66c8202e779255e5b660d2danthony        case DilateIntensityMorphology:
28564fd27e21043be809d66c8202e779255e5b660d2danthony        case ErodeIntensityMorphology:
28579eb4f74649b23c053b308ce1152dce51239450baanthony          /* use a boolean flag indicating when first match found */
28589eb4f74649b23c053b308ce1152dce51239450baanthony          result.red = 0.0;  /* result is not used otherwise */
28594fd27e21043be809d66c8202e779255e5b660d2danthony          break;
2860602ab9b30b644a78a4057da93d838a77391ec0acanthony        default:
2861602ab9b30b644a78a4057da93d838a77391ec0acanthony          break;
2862602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2863602ab9b30b644a78a4057da93d838a77391ec0acanthony
2864602ab9b30b644a78a4057da93d838a77391ec0acanthony      switch ( method ) {
2865602ab9b30b644a78a4057da93d838a77391ec0acanthony        case ConvolveMorphology:
2866930be614b4595b97cd79ee864a394796740f76adanthony            /* Weighted Average of pixels using reflected kernel
2867930be614b4595b97cd79ee864a394796740f76adanthony            **
2868930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE for correct working of this operation for asymetrical
2869930be614b4595b97cd79ee864a394796740f76adanthony            ** kernels, the kernel needs to be applied in its reflected form.
2870930be614b4595b97cd79ee864a394796740f76adanthony            ** That is its values needs to be reversed.
2871930be614b4595b97cd79ee864a394796740f76adanthony            **
2872930be614b4595b97cd79ee864a394796740f76adanthony            ** Correlation is actually the same as this but without reflecting
2873930be614b4595b97cd79ee864a394796740f76adanthony            ** the kernel, and thus 'lower-level' that Convolution.  However
2874930be614b4595b97cd79ee864a394796740f76adanthony            ** as Convolution is the more common method used, and it does not
2875930be614b4595b97cd79ee864a394796740f76adanthony            ** really cost us much in terms of processing to use a reflected
28765ef8e94ff55717be2387d537bd49025780a1a558anthony            ** kernel, so it is Convolution that is implemented.
2877930be614b4595b97cd79ee864a394796740f76adanthony            **
2878930be614b4595b97cd79ee864a394796740f76adanthony            ** Correlation will have its kernel reflected before calling
2879930be614b4595b97cd79ee864a394796740f76adanthony            ** this function to do a Convolve.
2880930be614b4595b97cd79ee864a394796740f76adanthony            **
2881930be614b4595b97cd79ee864a394796740f76adanthony            ** For more details of Correlation vs Convolution see
2882930be614b4595b97cd79ee864a394796740f76adanthony            **   http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
2883930be614b4595b97cd79ee864a394796740f76adanthony            */
28848d18850dee4bed193a64866a6d2353eeeb73e145anthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
28858d18850dee4bed193a64866a6d2353eeeb73e145anthony            k_pixels = p;
2886f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy            if ( (image->sync == MagickFalse) ||
2887f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy                 (image->matte == MagickFalse) )
28888d18850dee4bed193a64866a6d2353eeeb73e145anthony              { /* No 'Sync' involved.
28898d18850dee4bed193a64866a6d2353eeeb73e145anthony                ** Convolution is simple greyscale channel operation
28908d18850dee4bed193a64866a6d2353eeeb73e145anthony                */
28918d18850dee4bed193a64866a6d2353eeeb73e145anthony                for (v=0; v < (ssize_t) kernel->height; v++) {
28928d18850dee4bed193a64866a6d2353eeeb73e145anthony                  for (u=0; u < (ssize_t) kernel->width; u++, k--) {
28938d18850dee4bed193a64866a6d2353eeeb73e145anthony                    if ( IsNan(*k) ) continue;
28944c08aed51c5899665ade97263692328eea4af106cristy                    result.red     += (*k)*
2895ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
28964c08aed51c5899665ade97263692328eea4af106cristy                    result.green   += (*k)*
2897ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
28984c08aed51c5899665ade97263692328eea4af106cristy                    result.blue    += (*k)*
2899ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
29004c08aed51c5899665ade97263692328eea4af106cristy                    if (image->colorspace == CMYKColorspace)
29014c08aed51c5899665ade97263692328eea4af106cristy                      result.black += (*k)*
2902ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                        GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
29034c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += (*k)*
2904ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
29058d18850dee4bed193a64866a6d2353eeeb73e145anthony                  }
2906ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  k_pixels += virt_width*GetPixelChannels(image);
29078d18850dee4bed193a64866a6d2353eeeb73e145anthony                }
2908ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
29092b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelRed(morphology_image,ClampToQuantum(result.red),
29102b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
2911ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
29122b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelGreen(morphology_image,ClampToQuantum(result.green),
29132b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
2914ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
29152b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelBlue(morphology_image,ClampToQuantum(result.blue),
29162b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
2917ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
29184c08aed51c5899665ade97263692328eea4af106cristy                    (image->colorspace == CMYKColorspace))
29192b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelBlack(morphology_image,ClampToQuantum(result.black),
29202b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
2921ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
29224c08aed51c5899665ade97263692328eea4af106cristy                    (image->matte == MagickTrue))
29232b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),
29242b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
29258d18850dee4bed193a64866a6d2353eeeb73e145anthony              }
29268d18850dee4bed193a64866a6d2353eeeb73e145anthony            else
29278d18850dee4bed193a64866a6d2353eeeb73e145anthony              { /* Channel 'Sync' Flag, and Alpha Channel enabled.
29285ef8e94ff55717be2387d537bd49025780a1a558anthony                ** Weight the color channels with Alpha Channel so that
29295ef8e94ff55717be2387d537bd49025780a1a558anthony                ** transparent pixels are not part of the results.
29305ef8e94ff55717be2387d537bd49025780a1a558anthony                */
2931602ab9b30b644a78a4057da93d838a77391ec0acanthony                MagickRealType
2932c406ea496196b962e4bc865ed8f6a4491241c6edanthony                  alpha,  /* alpha weighting of colors : kernel*alpha  */
2933c406ea496196b962e4bc865ed8f6a4491241c6edanthony                  gamma;  /* divisor, sum of color weighting values */
2934602ab9b30b644a78a4057da93d838a77391ec0acanthony
2935602ab9b30b644a78a4057da93d838a77391ec0acanthony                gamma=0.0;
2936bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for (v=0; v < (ssize_t) kernel->height; v++) {
2937bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2938602ab9b30b644a78a4057da93d838a77391ec0acanthony                    if ( IsNan(*k) ) continue;
2939c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                    alpha=(*k)*(QuantumScale*GetPixelAlpha(image,k_pixels+u*
2940ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelChannels(image)));
2941602ab9b30b644a78a4057da93d838a77391ec0acanthony                    gamma += alpha;
29424c08aed51c5899665ade97263692328eea4af106cristy                    result.red     += alpha*
2943ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
29444c08aed51c5899665ade97263692328eea4af106cristy                    result.green   += alpha*
2945ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
29464c08aed51c5899665ade97263692328eea4af106cristy                    result.blue    += alpha*
2947ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
29484c08aed51c5899665ade97263692328eea4af106cristy                    if (image->colorspace == CMYKColorspace)
29494c08aed51c5899665ade97263692328eea4af106cristy                      result.black+=alpha*
2950ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                        GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
29514c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += (*k)*
2952ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
2953602ab9b30b644a78a4057da93d838a77391ec0acanthony                  }
2954ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  k_pixels += virt_width*GetPixelChannels(image);
2955602ab9b30b644a78a4057da93d838a77391ec0acanthony                }
2956c406ea496196b962e4bc865ed8f6a4491241c6edanthony                /* Sync'ed channels, all channels are modified */
2957602ab9b30b644a78a4057da93d838a77391ec0acanthony                gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
29584c08aed51c5899665ade97263692328eea4af106cristy                SetPixelRed(morphology_image,
29594c08aed51c5899665ade97263692328eea4af106cristy                  ClampToQuantum(gamma*result.red),q);
29604c08aed51c5899665ade97263692328eea4af106cristy                SetPixelGreen(morphology_image,
29614c08aed51c5899665ade97263692328eea4af106cristy                  ClampToQuantum(gamma*result.green),q);
29624c08aed51c5899665ade97263692328eea4af106cristy                SetPixelBlue(morphology_image,
29634c08aed51c5899665ade97263692328eea4af106cristy                  ClampToQuantum(gamma*result.blue),q);
2964c406ea496196b962e4bc865ed8f6a4491241c6edanthony                if (image->colorspace == CMYKColorspace)
29654c08aed51c5899665ade97263692328eea4af106cristy                  SetPixelBlack(morphology_image,
29664c08aed51c5899665ade97263692328eea4af106cristy                    ClampToQuantum(gamma*result.black),q);
29674c08aed51c5899665ade97263692328eea4af106cristy                SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
2968602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
2969602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
2970602ab9b30b644a78a4057da93d838a77391ec0acanthony
29714fd27e21043be809d66c8202e779255e5b660d2danthony        case ErodeMorphology:
29725ef8e94ff55717be2387d537bd49025780a1a558anthony            /* Minimum Value within kernel neighbourhood
2973930be614b4595b97cd79ee864a394796740f76adanthony            **
2974930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE that the kernel is not reflected for this operation!
2975930be614b4595b97cd79ee864a394796740f76adanthony            **
2976930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE: in normal Greyscale Morphology, the kernel value should
2977930be614b4595b97cd79ee864a394796740f76adanthony            ** be added to the real value, this is currently not done, due to
2978930be614b4595b97cd79ee864a394796740f76adanthony            ** the nature of the boolean kernels being used.
2979930be614b4595b97cd79ee864a394796740f76adanthony            */
29804fd27e21043be809d66c8202e779255e5b660d2danthony            k = kernel->values;
2981602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
2982bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
2983bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
2984602ab9b30b644a78a4057da93d838a77391ec0acanthony                if ( IsNan(*k) || (*k) < 0.5 ) continue;
29854c08aed51c5899665ade97263692328eea4af106cristy                Minimize(min.red,     (double)
2986ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
29874c08aed51c5899665ade97263692328eea4af106cristy                Minimize(min.green,   (double)
2988ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
29894c08aed51c5899665ade97263692328eea4af106cristy                Minimize(min.blue,    (double)
2990ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
29914c08aed51c5899665ade97263692328eea4af106cristy                if (image->colorspace == CMYKColorspace)
29924c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(min.black,(double)
2993ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
29944c08aed51c5899665ade97263692328eea4af106cristy                Minimize(min.alpha,(double)
2995ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
2996602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
2997ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
2998602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
2999602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3000602ab9b30b644a78a4057da93d838a77391ec0acanthony
30014fd27e21043be809d66c8202e779255e5b660d2danthony        case DilateMorphology:
30025ef8e94ff55717be2387d537bd49025780a1a558anthony            /* Maximum Value within kernel neighbourhood
3003930be614b4595b97cd79ee864a394796740f76adanthony            **
3004930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE for correct working of this operation for asymetrical
3005930be614b4595b97cd79ee864a394796740f76adanthony            ** kernels, the kernel needs to be applied in its reflected form.
3006930be614b4595b97cd79ee864a394796740f76adanthony            ** That is its values needs to be reversed.
3007930be614b4595b97cd79ee864a394796740f76adanthony            **
3008930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE: in normal Greyscale Morphology, the kernel value should
3009930be614b4595b97cd79ee864a394796740f76adanthony            ** be added to the real value, this is currently not done, due to
3010930be614b4595b97cd79ee864a394796740f76adanthony            ** the nature of the boolean kernels being used.
3011930be614b4595b97cd79ee864a394796740f76adanthony            **
3012930be614b4595b97cd79ee864a394796740f76adanthony            */
30134fd27e21043be809d66c8202e779255e5b660d2danthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3014602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3015bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3016bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3017602ab9b30b644a78a4057da93d838a77391ec0acanthony                if ( IsNan(*k) || (*k) < 0.5 ) continue;
30184c08aed51c5899665ade97263692328eea4af106cristy                Maximize(max.red,     (double)
3019ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
30204c08aed51c5899665ade97263692328eea4af106cristy                Maximize(max.green,   (double)
3021ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
30224c08aed51c5899665ade97263692328eea4af106cristy                Maximize(max.blue,    (double)
3023ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
30244c08aed51c5899665ade97263692328eea4af106cristy                if (image->colorspace == CMYKColorspace)
30254c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.black,   (double)
3026ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
30274c08aed51c5899665ade97263692328eea4af106cristy                Maximize(max.alpha,(double)
3028ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3029602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3030ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3031602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3032602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3033602ab9b30b644a78a4057da93d838a77391ec0acanthony
30345ef8e94ff55717be2387d537bd49025780a1a558anthony        case HitAndMissMorphology:
30355ef8e94ff55717be2387d537bd49025780a1a558anthony        case ThinningMorphology:
30365ef8e94ff55717be2387d537bd49025780a1a558anthony        case ThickenMorphology:
30375ef8e94ff55717be2387d537bd49025780a1a558anthony            /* Minimum of Foreground Pixel minus Maxumum of Background Pixels
30385ef8e94ff55717be2387d537bd49025780a1a558anthony            **
30395ef8e94ff55717be2387d537bd49025780a1a558anthony            ** NOTE that the kernel is not reflected for this operation,
30405ef8e94ff55717be2387d537bd49025780a1a558anthony            ** and consists of both foreground and background pixel
30415ef8e94ff55717be2387d537bd49025780a1a558anthony            ** neighbourhoods, 0.0 for background, and 1.0 for foreground
30425ef8e94ff55717be2387d537bd49025780a1a558anthony            ** with either Nan or 0.5 values for don't care.
30435ef8e94ff55717be2387d537bd49025780a1a558anthony            **
30444c827ef1be3d42450ff45cedad271e6568be0ea9anthony            ** Note that this will never produce a meaningless negative
30454c827ef1be3d42450ff45cedad271e6568be0ea9anthony            ** result.  Such results can cause Thinning/Thicken to not work
30464c827ef1be3d42450ff45cedad271e6568be0ea9anthony            ** correctly when used against a greyscale image.
30475ef8e94ff55717be2387d537bd49025780a1a558anthony            */
30485ef8e94ff55717be2387d537bd49025780a1a558anthony            k = kernel->values;
30495ef8e94ff55717be2387d537bd49025780a1a558anthony            k_pixels = p;
3050bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3051bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
30525ef8e94ff55717be2387d537bd49025780a1a558anthony                if ( IsNan(*k) ) continue;
30535ef8e94ff55717be2387d537bd49025780a1a558anthony                if ( (*k) > 0.7 )
30545ef8e94ff55717be2387d537bd49025780a1a558anthony                { /* minimim of foreground pixels */
30554c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(min.red,     (double)
3056ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
30574c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(min.green,   (double)
3058ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
30594c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(min.blue,    (double)
3060ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
30615ef8e94ff55717be2387d537bd49025780a1a558anthony                  if ( image->colorspace == CMYKColorspace)
30624c08aed51c5899665ade97263692328eea4af106cristy                    Minimize(min.black,(double)
3063ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
30644c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(min.alpha,(double)
3065ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
30665ef8e94ff55717be2387d537bd49025780a1a558anthony                }
30675ef8e94ff55717be2387d537bd49025780a1a558anthony                else if ( (*k) < 0.3 )
30685ef8e94ff55717be2387d537bd49025780a1a558anthony                { /* maximum of background pixels */
30694c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.red,     (double)
3070ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
30714c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.green,   (double)
3072ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
30734c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.blue,    (double)
3074ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
30754c08aed51c5899665ade97263692328eea4af106cristy                  if (image->colorspace == CMYKColorspace)
30764c08aed51c5899665ade97263692328eea4af106cristy                    Maximize(max.black,   (double)
3077ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
30784c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.alpha,(double)
3079ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
30805ef8e94ff55717be2387d537bd49025780a1a558anthony                }
30815ef8e94ff55717be2387d537bd49025780a1a558anthony              }
3082ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
30835ef8e94ff55717be2387d537bd49025780a1a558anthony            }
30844c827ef1be3d42450ff45cedad271e6568be0ea9anthony            /* Pattern Match if difference is positive */
30855ef8e94ff55717be2387d537bd49025780a1a558anthony            min.red     -= max.red;     Maximize( min.red,     0.0 );
30865ef8e94ff55717be2387d537bd49025780a1a558anthony            min.green   -= max.green;   Maximize( min.green,   0.0 );
30875ef8e94ff55717be2387d537bd49025780a1a558anthony            min.blue    -= max.blue;    Maximize( min.blue,    0.0 );
30884c08aed51c5899665ade97263692328eea4af106cristy            min.black   -= max.black;   Maximize( min.black,   0.0 );
308970b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy            min.alpha -= max.alpha; Maximize( min.alpha, 0.0 );
30905ef8e94ff55717be2387d537bd49025780a1a558anthony            break;
30915ef8e94ff55717be2387d537bd49025780a1a558anthony
30924fd27e21043be809d66c8202e779255e5b660d2danthony        case ErodeIntensityMorphology:
3093930be614b4595b97cd79ee864a394796740f76adanthony            /* Select Pixel with Minimum Intensity within kernel neighbourhood
3094930be614b4595b97cd79ee864a394796740f76adanthony            **
3095930be614b4595b97cd79ee864a394796740f76adanthony            ** WARNING: the intensity test fails for CMYK and does not
3096c406ea496196b962e4bc865ed8f6a4491241c6edanthony            ** take into account the moderating effect of the alpha channel
3097930be614b4595b97cd79ee864a394796740f76adanthony            ** on the intensity.
3098930be614b4595b97cd79ee864a394796740f76adanthony            **
3099930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE that the kernel is not reflected for this operation!
3100930be614b4595b97cd79ee864a394796740f76adanthony            */
31014fd27e21043be809d66c8202e779255e5b660d2danthony            k = kernel->values;
3102602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3103bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3104bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
31054fd27e21043be809d66c8202e779255e5b660d2danthony                if ( IsNan(*k) || (*k) < 0.5 ) continue;
310629188a8682a98d4b7882cca434b170517555fc7danthony                if ( result.red == 0.0 ||
3107ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                     GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) < GetPixelIntensity(morphology_image,q) ) {
310829188a8682a98d4b7882cca434b170517555fc7danthony                  /* copy the whole pixel - no channel selection */
3109c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelRed(morphology_image,GetPixelRed(image,
3110ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3111c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelGreen(morphology_image,GetPixelGreen(image,
3112ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3113c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelBlue(morphology_image,GetPixelBlue(image,
3114ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3115c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelAlpha(morphology_image,GetPixelAlpha(image,
3116ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
311729188a8682a98d4b7882cca434b170517555fc7danthony                  if ( result.red > 0.0 ) changed++;
311829188a8682a98d4b7882cca434b170517555fc7danthony                  result.red = 1.0;
311929188a8682a98d4b7882cca434b170517555fc7danthony                }
3120602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3121ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3122602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3123602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3124602ab9b30b644a78a4057da93d838a77391ec0acanthony
31254fd27e21043be809d66c8202e779255e5b660d2danthony        case DilateIntensityMorphology:
3126930be614b4595b97cd79ee864a394796740f76adanthony            /* Select Pixel with Maximum Intensity within kernel neighbourhood
3127930be614b4595b97cd79ee864a394796740f76adanthony            **
3128930be614b4595b97cd79ee864a394796740f76adanthony            ** WARNING: the intensity test fails for CMYK and does not
31299eb4f74649b23c053b308ce1152dce51239450baanthony            ** take into account the moderating effect of the alpha channel
31309eb4f74649b23c053b308ce1152dce51239450baanthony            ** on the intensity (yet).
3131930be614b4595b97cd79ee864a394796740f76adanthony            **
3132930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE for correct working of this operation for asymetrical
3133930be614b4595b97cd79ee864a394796740f76adanthony            ** kernels, the kernel needs to be applied in its reflected form.
3134930be614b4595b97cd79ee864a394796740f76adanthony            ** That is its values needs to be reversed.
3135930be614b4595b97cd79ee864a394796740f76adanthony            */
31364fd27e21043be809d66c8202e779255e5b660d2danthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3137602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3138bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3139bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
31404fd27e21043be809d66c8202e779255e5b660d2danthony                if ( IsNan(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
314129188a8682a98d4b7882cca434b170517555fc7danthony                if ( result.red == 0.0 ||
3142ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                     GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) > GetPixelIntensity(morphology_image,q) ) {
314329188a8682a98d4b7882cca434b170517555fc7danthony                  /* copy the whole pixel - no channel selection */
3144c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelRed(morphology_image,GetPixelRed(image,
3145ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3146c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelGreen(morphology_image,GetPixelGreen(image,
3147ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3148c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelBlue(morphology_image,GetPixelBlue(image,
3149ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3150c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelAlpha(morphology_image,GetPixelAlpha(image,
3151ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
315229188a8682a98d4b7882cca434b170517555fc7danthony                  if ( result.red > 0.0 ) changed++;
315329188a8682a98d4b7882cca434b170517555fc7danthony                  result.red = 1.0;
315429188a8682a98d4b7882cca434b170517555fc7danthony                }
3155602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3156ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3157602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3158602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3159a8843c1f815ffad2568ec592d5b446cb1476aab5anthony#if 0
3160db60568e12574785101a4ae8d8da076227a0a889anthony  This code has been obsoleted by the MorphologyPrimitiveDirect() function.
3161db60568e12574785101a4ae8d8da076227a0a889anthony  However it is still (almost) correct coding for Grayscale Morphology.
3162db60568e12574785101a4ae8d8da076227a0a889anthony  That is...
3163db60568e12574785101a4ae8d8da076227a0a889anthony
3164f0a92fd8deb68d411304359906b12679b675691fglennrp  GrayErode    is equivalent but with kernel values subtracted from pixels
3165db60568e12574785101a4ae8d8da076227a0a889anthony               without the kernel rotation
3166f0a92fd8deb68d411304359906b12679b675691fglennrp  GreyDilate   is equivalent but using Maximum() instead of Minimum()
316757fe7a498c1302232dac8466864e84b12fad0807anthony               using kernel rotation
316857fe7a498c1302232dac8466864e84b12fad0807anthony
316957fe7a498c1302232dac8466864e84b12fad0807anthony  It has thus been preserved for future implementation of those methods.
3170db60568e12574785101a4ae8d8da076227a0a889anthony
3171602ab9b30b644a78a4057da93d838a77391ec0acanthony        case DistanceMorphology:
3172930be614b4595b97cd79ee864a394796740f76adanthony            /* Add kernel Value and select the minimum value found.
3173930be614b4595b97cd79ee864a394796740f76adanthony            ** The result is a iterative distance from edge of image shape.
3174930be614b4595b97cd79ee864a394796740f76adanthony            **
3175930be614b4595b97cd79ee864a394796740f76adanthony            ** All Distance Kernels are symetrical, but that may not always
3176930be614b4595b97cd79ee864a394796740f76adanthony            ** be the case. For example how about a distance from left edges?
3177930be614b4595b97cd79ee864a394796740f76adanthony            ** To work correctly with asymetrical kernels the reflected kernel
3178930be614b4595b97cd79ee864a394796740f76adanthony            ** needs to be applied.
3179930be614b4595b97cd79ee864a394796740f76adanthony            */
318029188a8682a98d4b7882cca434b170517555fc7danthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3181602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3182bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3183bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3184602ab9b30b644a78a4057da93d838a77391ec0acanthony                if ( IsNan(*k) ) continue;
3185602ab9b30b644a78a4057da93d838a77391ec0acanthony                Minimize(result.red,     (*k)+k_pixels[u].red);
3186602ab9b30b644a78a4057da93d838a77391ec0acanthony                Minimize(result.green,   (*k)+k_pixels[u].green);
3187602ab9b30b644a78a4057da93d838a77391ec0acanthony                Minimize(result.blue,    (*k)+k_pixels[u].blue);
31884c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha, (*k)+k_pixels[u].alpha);
3189602ab9b30b644a78a4057da93d838a77391ec0acanthony                if ( image->colorspace == CMYKColorspace)
31904c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,(*k)+GetPixelBlack(p_image,k_indexes+u));
3191602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3192ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3193602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3194602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3195a8843c1f815ffad2568ec592d5b446cb1476aab5anthony#endif
3196602ab9b30b644a78a4057da93d838a77391ec0acanthony        case UndefinedMorphology:
3197602ab9b30b644a78a4057da93d838a77391ec0acanthony        default:
3198602ab9b30b644a78a4057da93d838a77391ec0acanthony            break; /* Do nothing */
319983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      }
32005ef8e94ff55717be2387d537bd49025780a1a558anthony      /* Final mathematics of results (combine with original image?)
32015ef8e94ff55717be2387d537bd49025780a1a558anthony      **
32025ef8e94ff55717be2387d537bd49025780a1a558anthony      ** NOTE: Difference Morphology operators Edge* and *Hat could also
32035ef8e94ff55717be2387d537bd49025780a1a558anthony      ** be done here but works better with iteration as a image difference
32045ef8e94ff55717be2387d537bd49025780a1a558anthony      ** in the controling function (below).  Thicken and Thinning however
32055ef8e94ff55717be2387d537bd49025780a1a558anthony      ** should be done here so thay can be iterated correctly.
32065ef8e94ff55717be2387d537bd49025780a1a558anthony      */
32075ef8e94ff55717be2387d537bd49025780a1a558anthony      switch ( method ) {
32085ef8e94ff55717be2387d537bd49025780a1a558anthony        case HitAndMissMorphology:
32095ef8e94ff55717be2387d537bd49025780a1a558anthony        case ErodeMorphology:
32105ef8e94ff55717be2387d537bd49025780a1a558anthony          result = min;    /* minimum of neighbourhood */
32115ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
32125ef8e94ff55717be2387d537bd49025780a1a558anthony        case DilateMorphology:
32135ef8e94ff55717be2387d537bd49025780a1a558anthony          result = max;    /* maximum of neighbourhood */
32145ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
32155ef8e94ff55717be2387d537bd49025780a1a558anthony        case ThinningMorphology:
32165ef8e94ff55717be2387d537bd49025780a1a558anthony          /* subtract pattern match from original */
32175ef8e94ff55717be2387d537bd49025780a1a558anthony          result.red     -= min.red;
32185ef8e94ff55717be2387d537bd49025780a1a558anthony          result.green   -= min.green;
32195ef8e94ff55717be2387d537bd49025780a1a558anthony          result.blue    -= min.blue;
32204c08aed51c5899665ade97263692328eea4af106cristy          result.black   -= min.black;
322170b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy          result.alpha -= min.alpha;
32225ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
32235ef8e94ff55717be2387d537bd49025780a1a558anthony        case ThickenMorphology:
32244c827ef1be3d42450ff45cedad271e6568be0ea9anthony          /* Add the pattern matchs to the original */
32254c827ef1be3d42450ff45cedad271e6568be0ea9anthony          result.red     += min.red;
32264c827ef1be3d42450ff45cedad271e6568be0ea9anthony          result.green   += min.green;
32274c827ef1be3d42450ff45cedad271e6568be0ea9anthony          result.blue    += min.blue;
32284c08aed51c5899665ade97263692328eea4af106cristy          result.black   += min.black;
322970b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy          result.alpha += min.alpha;
32305ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
32315ef8e94ff55717be2387d537bd49025780a1a558anthony        default:
32325ef8e94ff55717be2387d537bd49025780a1a558anthony          /* result directly calculated or assigned */
32335ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
32345ef8e94ff55717be2387d537bd49025780a1a558anthony      }
32355ef8e94ff55717be2387d537bd49025780a1a558anthony      /* Assign the resulting pixel values - Clamping Result */
323683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      switch ( method ) {
323783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        case UndefinedMorphology:
3238c406ea496196b962e4bc865ed8f6a4491241c6edanthony        case ConvolveMorphology:
323983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        case DilateIntensityMorphology:
324083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        case ErodeIntensityMorphology:
3241930be614b4595b97cd79ee864a394796740f76adanthony          break;  /* full pixel was directly assigned - not a channel method */
324283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        default:
3243ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
32444c08aed51c5899665ade97263692328eea4af106cristy            SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
3245ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
32464c08aed51c5899665ade97263692328eea4af106cristy            SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
3247ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
32484c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
3249ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
32504c08aed51c5899665ade97263692328eea4af106cristy              (image->colorspace == CMYKColorspace))
32514c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
3252ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
32534c08aed51c5899665ade97263692328eea4af106cristy              (image->matte == MagickTrue))
32544c08aed51c5899665ade97263692328eea4af106cristy            SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
325583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          break;
325683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      }
32575ef8e94ff55717be2387d537bd49025780a1a558anthony      /* Count up changed pixels */
3258ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q)) ||
3259ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q)) ||
3260ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q)) ||
3261ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q)) ||
32624c08aed51c5899665ade97263692328eea4af106cristy          ((image->colorspace == CMYKColorspace) &&
3263ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy           (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
3264c406ea496196b962e4bc865ed8f6a4491241c6edanthony        changed++;  /* The pixel was changed in some way! */
3265ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image);
3266ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(morphology_image);
326783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    } /* x */
32684c08aed51c5899665ade97263692328eea4af106cristy    if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3269602ab9b30b644a78a4057da93d838a77391ec0acanthony      status=MagickFalse;
3270602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3271602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
3272602ab9b30b644a78a4057da93d838a77391ec0acanthony        MagickBooleanType
3273602ab9b30b644a78a4057da93d838a77391ec0acanthony          proceed;
3274602ab9b30b644a78a4057da93d838a77391ec0acanthony
3275602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
3276602ab9b30b644a78a4057da93d838a77391ec0acanthony  #pragma omp critical (MagickCore_MorphologyImage)
3277602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
3278602ab9b30b644a78a4057da93d838a77391ec0acanthony        proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
3279602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (proceed == MagickFalse)
3280602ab9b30b644a78a4057da93d838a77391ec0acanthony          status=MagickFalse;
3281602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
328283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  } /* y */
32834c08aed51c5899665ade97263692328eea4af106cristy  morphology_view=DestroyCacheView(morphology_view);
32844c08aed51c5899665ade97263692328eea4af106cristy  image_view=DestroyCacheView(image_view);
3285a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  return(status ? (ssize_t)changed : -1);
3286602ab9b30b644a78a4057da93d838a77391ec0acanthony}
3287602ab9b30b644a78a4057da93d838a77391ec0acanthony
3288a8843c1f815ffad2568ec592d5b446cb1476aab5anthony/* This is almost identical to the MorphologyPrimative() function above,
3289a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** but will apply the primitive directly to the image in two passes.
3290a8843c1f815ffad2568ec592d5b446cb1476aab5anthony**
3291a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** That is after each row is 'Sync'ed' into the image, the next row will
3292a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** make use of those values as part of the calculation of the next row.
3293a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** It then repeats, but going in the oppisite (bottom-up) direction.
3294a8843c1f815ffad2568ec592d5b446cb1476aab5anthony**
3295a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** Because of this 'iterative' handling this function can not make use
3296a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** of multi-threaded, parellel processing.
3297a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3298e698a255629ba03cd125572de7b35b5e21c4ee5danthonystatic ssize_t MorphologyPrimitiveDirect(Image *image,
3299f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const KernelInfo *kernel,
3300f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3301a8843c1f815ffad2568ec592d5b446cb1476aab5anthony{
3302a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  CacheView
3303a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *auth_view,
3304a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *virt_view;
3305a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3306a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickBooleanType
3307a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    status;
3308a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3309a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickOffsetType
3310a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    progress;
3311a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3312a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  ssize_t
3313a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    y, offx, offy;
3314a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3315a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
3316db60568e12574785101a4ae8d8da076227a0a889anthony    virt_width,
3317a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;
3318a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3319a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  status=MagickTrue;
3320a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  changed=0;
3321a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  progress=0;
3322a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3323a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image != (Image *) NULL);
3324a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image->signature == MagickSignature);
3325a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel != (KernelInfo *) NULL);
3326a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel->signature == MagickSignature);
3327a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception != (ExceptionInfo *) NULL);
3328a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception->signature == MagickSignature);
3329a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3330a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Some methods (including convolve) needs use a reflected kernel.
3331a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   * Adjust 'origin' offsets to loop though kernel as a reflection.
3332a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   */
3333a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  offx = kernel->x;
3334a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  offy = kernel->y;
3335a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  switch(method) {
3336a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3337e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3338a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* kernel needs to used with reflection about origin */
3339a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      offx = (ssize_t) kernel->width-offx-1;
3340a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      offy = (ssize_t) kernel->height-offy-1;
3341a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3342a8843c1f815ffad2568ec592d5b446cb1476aab5anthony#if 0
3343a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case ?????Morphology:
3344a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* kernel is used as is, without reflection */
3345a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3346a8843c1f815ffad2568ec592d5b446cb1476aab5anthony#endif
3347a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    default:
3348a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      assert("Not a PrimativeDirect Morphology Method" != (char *) NULL);
3349a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3350a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  }
3351a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3352a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* DO NOT THREAD THIS CODE! */
3353a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* two views into same image (virtual, and actual) */
3354a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  virt_view=AcquireCacheView(image);
3355a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  auth_view=AcquireCacheView(image);
3356db60568e12574785101a4ae8d8da076227a0a889anthony  virt_width=image->columns+kernel->width-1;
3357a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3358a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  for (y=0; y < (ssize_t) image->rows; y++)
3359a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
33604c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
3361a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict p;
3362a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
33634c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
3364a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict q;
3365a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3366a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3367a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3368a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3369a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ssize_t
3370a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      r;
3371a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3372a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* NOTE read virtual pixels, and authentic pixels, from the same image!
3373a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** we read using virtual to get virtual pixel handling, but write back
3374a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** into the same image.
3375a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    **
3376a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** Only top half of kernel is processed as we do a single pass downward
3377a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** through the image iterating the distance function as we go.
3378a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    */
3379a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3380a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
33814c08aed51c5899665ade97263692328eea4af106cristy    p=GetCacheViewVirtualPixels(virt_view,-offx,y-offy,virt_width,(size_t)
33824c08aed51c5899665ade97263692328eea4af106cristy      offy+1,exception);
3383db60568e12574785101a4ae8d8da076227a0a889anthony    q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
33844c08aed51c5899665ade97263692328eea4af106cristy      exception);
33854c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3386a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3387a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3388a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3389a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3390a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* offset to origin in 'p'. while 'q' points to it directly */
3391aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy    r = (ssize_t) virt_width*offy + offx;
3392a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3393a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    for (x=0; x < (ssize_t) image->columns; x++)
3394a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    {
3395a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      ssize_t
3396a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        v;
3397a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3398a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      register ssize_t
3399a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        u;
3400a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3401a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      register const double
3402a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        *restrict k;
3403a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34044c08aed51c5899665ade97263692328eea4af106cristy      register const Quantum
3405a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        *restrict k_pixels;
3406a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34074c08aed51c5899665ade97263692328eea4af106cristy      PixelInfo
3408a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        result;
3409a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3410e698a255629ba03cd125572de7b35b5e21c4ee5danthony      /* Starting Defaults */
34114c08aed51c5899665ade97263692328eea4af106cristy      GetPixelInfo(image,&result);
34124c08aed51c5899665ade97263692328eea4af106cristy      SetPixelInfo(image,q,&result);
3413e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method != VoronoiMorphology )
34144c08aed51c5899665ade97263692328eea4af106cristy        result.alpha = QuantumRange - result.alpha;
3415a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3416a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      switch ( method ) {
3417a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        case DistanceMorphology:
3418a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* Add kernel Value and select the minimum value found. */
3419a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3420a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            k_pixels = p;
3421a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            for (v=0; v <= (ssize_t) offy; v++) {
3422a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3423a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( IsNan(*k) ) continue;
34244c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.red,     (*k)+
3425ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
34264c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.green,   (*k)+
3427ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
34284c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.blue,    (*k)+
3429ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
34304c08aed51c5899665ade97263692328eea4af106cristy                if (image->colorspace == CMYKColorspace)
34314c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,(*k)+
3432ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
34334c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha, (*k)+
3434ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3435a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              }
3436ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3437a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            }
3438a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* repeat with the just processed pixels of this row */
3439db60568e12574785101a4ae8d8da076227a0a889anthony            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3440ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            k_pixels = q-offx*GetPixelChannels(image);
3441a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              for (u=0; u < (ssize_t) offx; u++, k--) {
3442a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( x+u-offx < 0 ) continue;  /* off the edge! */
3443a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( IsNan(*k) ) continue;
34444c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.red,     (*k)+
3445ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
34464c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.green,   (*k)+
3447ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
34484c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.blue,    (*k)+
3449ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
34504c08aed51c5899665ade97263692328eea4af106cristy                if (image->colorspace == CMYKColorspace)
34514c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,(*k)+
3452ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
34534c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha,(*k)+
3454ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3455a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              }
3456a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            break;
3457e698a255629ba03cd125572de7b35b5e21c4ee5danthony        case VoronoiMorphology:
3458e698a255629ba03cd125572de7b35b5e21c4ee5danthony            /* Apply Distance to 'Matte' channel, coping the closest color.
3459e698a255629ba03cd125572de7b35b5e21c4ee5danthony            **
3460e698a255629ba03cd125572de7b35b5e21c4ee5danthony            ** This is experimental, and realy the 'alpha' component should
3461e698a255629ba03cd125572de7b35b5e21c4ee5danthony            ** be completely separate 'masking' channel.
3462e698a255629ba03cd125572de7b35b5e21c4ee5danthony            */
3463e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3464e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k_pixels = p;
3465e698a255629ba03cd125572de7b35b5e21c4ee5danthony            for (v=0; v <= (ssize_t) offy; v++) {
3466e698a255629ba03cd125572de7b35b5e21c4ee5danthony              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3467e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( IsNan(*k) ) continue;
3468ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3469e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3470ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    SetPixelInfo(image,k_pixels+u*GetPixelChannels(image),
34714a06a14c06f675b6a2052a3c814e7da54877a733cristy                      &result);
34724c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += *k;
3473e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3474e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3475ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3476e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3477e698a255629ba03cd125572de7b35b5e21c4ee5danthony            /* repeat with the just processed pixels of this row */
3478e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3479ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            k_pixels = q-offx*GetPixelChannels(image);
3480e698a255629ba03cd125572de7b35b5e21c4ee5danthony              for (u=0; u < (ssize_t) offx; u++, k--) {
3481e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( x+u-offx < 0 ) continue;  /* off the edge! */
3482e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( IsNan(*k) ) continue;
3483ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3484e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3485ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    SetPixelInfo(image,k_pixels+u*GetPixelChannels(image),
34864a06a14c06f675b6a2052a3c814e7da54877a733cristy                      &result);
34874c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += *k;
3488e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3489e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3490e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3491a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        default:
3492a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* result directly calculated or assigned */
3493a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          break;
3494a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      }
3495a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Assign the resulting pixel values - Clamping Result */
3496e698a255629ba03cd125572de7b35b5e21c4ee5danthony      switch ( method ) {
3497e698a255629ba03cd125572de7b35b5e21c4ee5danthony        case VoronoiMorphology:
34984c08aed51c5899665ade97263692328eea4af106cristy          SetPixelPixelInfo(image,&result,q);
3499e698a255629ba03cd125572de7b35b5e21c4ee5danthony          break;
3500e698a255629ba03cd125572de7b35b5e21c4ee5danthony        default:
3501ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
35024c08aed51c5899665ade97263692328eea4af106cristy            SetPixelRed(image,ClampToQuantum(result.red),q);
3503ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
35044c08aed51c5899665ade97263692328eea4af106cristy            SetPixelGreen(image,ClampToQuantum(result.green),q);
3505ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
35064c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlue(image,ClampToQuantum(result.blue),q);
3507ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
35084c08aed51c5899665ade97263692328eea4af106cristy              (image->colorspace == CMYKColorspace))
35094c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlack(image,ClampToQuantum(result.black),q);
3510ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
35112b9582a27910c7baaeb04b7e969638328fa70095cristy              (image->matte == MagickTrue))
35124c08aed51c5899665ade97263692328eea4af106cristy            SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
3513e698a255629ba03cd125572de7b35b5e21c4ee5danthony          break;
3514e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3515a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Count up changed pixels */
3516ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q)) ||
3517ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q)) ||
3518ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q)) ||
3519ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q)) ||
35204c08aed51c5899665ade97263692328eea4af106cristy          ((image->colorspace == CMYKColorspace) &&
3521ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy           (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
3522a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        changed++;  /* The pixel was changed in some way! */
3523a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3524ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image); /* increment pixel buffers */
3525ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(image);
3526a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    } /* x */
35274fd27e21043be809d66c8202e779255e5b660d2danthony
3528a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3529a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3530a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3531a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
3532a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                == MagickFalse )
3533a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        status=MagickFalse;
3534a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3535a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  } /* y */
3536a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3537a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Do the reversed pass through the image */
3538a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  for (y=(ssize_t)image->rows-1; y >= 0; y--)
3539a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
35404c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
3541a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict p;
3542a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
35434c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
3544a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict q;
3545a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3546a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3547a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3548a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3549a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ssize_t
3550a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      r;
3551a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3552db60568e12574785101a4ae8d8da076227a0a889anthony    if (status == MagickFalse)
3553db60568e12574785101a4ae8d8da076227a0a889anthony      break;
3554a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* NOTE read virtual pixels, and authentic pixels, from the same image!
3555a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** we read using virtual to get virtual pixel handling, but write back
3556a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** into the same image.
3557a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    **
3558a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** Only the bottom half of the kernel will be processes as we
3559a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** up the image.
3560a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    */
35614c08aed51c5899665ade97263692328eea4af106cristy    p=GetCacheViewVirtualPixels(virt_view,-offx,y,virt_width,(size_t)
35624c08aed51c5899665ade97263692328eea4af106cristy      kernel->y+1,exception);
3563db60568e12574785101a4ae8d8da076227a0a889anthony    q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
35644c08aed51c5899665ade97263692328eea4af106cristy      exception);
35654c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3566a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3567a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3568a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3569a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3570a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* adjust positions to end of row */
3571ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy    p += (image->columns-1)*GetPixelChannels(image);
3572ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy    q += (image->columns-1)*GetPixelChannels(image);
3573a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3574a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* offset to origin in 'p'. while 'q' points to it directly */
3575a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    r = offx;
3576a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3577a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    for (x=(ssize_t)image->columns-1; x >= 0; x--)
3578a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    {
3579a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      ssize_t
3580a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        v;
3581a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3582a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      register ssize_t
3583a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        u;
3584a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3585a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      register const double
3586a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        *restrict k;
3587a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
35884c08aed51c5899665ade97263692328eea4af106cristy      register const Quantum
3589a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        *restrict k_pixels;
3590a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
35914c08aed51c5899665ade97263692328eea4af106cristy      PixelInfo
3592a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        result;
3593a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3594e698a255629ba03cd125572de7b35b5e21c4ee5danthony      /* Default - previously modified pixel */
35954c08aed51c5899665ade97263692328eea4af106cristy      GetPixelInfo(image,&result);
35964c08aed51c5899665ade97263692328eea4af106cristy      SetPixelInfo(image,q,&result);
3597e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method != VoronoiMorphology )
35984c08aed51c5899665ade97263692328eea4af106cristy        result.alpha = QuantumRange - result.alpha;
3599a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3600a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      switch ( method ) {
3601a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        case DistanceMorphology:
3602a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* Add kernel Value and select the minimum value found. */
3603db60568e12574785101a4ae8d8da076227a0a889anthony            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3604a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            k_pixels = p;
3605a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            for (v=offy; v < (ssize_t) kernel->height; v++) {
3606a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3607a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( IsNan(*k) ) continue;
36084c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.red,     (*k)+
3609ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
36104c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.green,   (*k)+
3611ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
36124c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.blue,    (*k)+
3613ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3614a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( image->colorspace == CMYKColorspace)
36154c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,(*k)+
3616ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
36174c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha, (*k)+
3618ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3619a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              }
3620ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3621a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            }
3622a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* repeat with the just processed pixels of this row */
3623db60568e12574785101a4ae8d8da076227a0a889anthony            k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3624ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            k_pixels = q-offx*GetPixelChannels(image);
3625a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3626e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
3627a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( IsNan(*k) ) continue;
36284c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.red,     (*k)+
3629ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
36304c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.green,   (*k)+
3631ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
36324c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.blue,    (*k)+
3633ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3634a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( image->colorspace == CMYKColorspace)
36354c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,   (*k)+
3636ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
36374c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha, (*k)+
3638ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3639a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              }
3640a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            break;
3641e698a255629ba03cd125572de7b35b5e21c4ee5danthony        case VoronoiMorphology:
3642e698a255629ba03cd125572de7b35b5e21c4ee5danthony            /* Apply Distance to 'Matte' channel, coping the closest color.
3643e698a255629ba03cd125572de7b35b5e21c4ee5danthony            **
3644e698a255629ba03cd125572de7b35b5e21c4ee5danthony            ** This is experimental, and realy the 'alpha' component should
3645e698a255629ba03cd125572de7b35b5e21c4ee5danthony            ** be completely separate 'masking' channel.
3646e698a255629ba03cd125572de7b35b5e21c4ee5danthony            */
3647e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3648e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k_pixels = p;
3649e698a255629ba03cd125572de7b35b5e21c4ee5danthony            for (v=offy; v < (ssize_t) kernel->height; v++) {
3650e698a255629ba03cd125572de7b35b5e21c4ee5danthony              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3651e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( IsNan(*k) ) continue;
3652ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3653e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3654ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    SetPixelInfo(image,k_pixels+u*GetPixelChannels(image),
36554a06a14c06f675b6a2052a3c814e7da54877a733cristy                      &result);
36564c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += *k;
3657e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3658e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3659ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3660e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3661e698a255629ba03cd125572de7b35b5e21c4ee5danthony            /* repeat with the just processed pixels of this row */
3662e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3663ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            k_pixels = q-offx*GetPixelChannels(image);
3664e698a255629ba03cd125572de7b35b5e21c4ee5danthony              for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3665e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
3666e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( IsNan(*k) ) continue;
3667ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3668e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3669ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    SetPixelInfo(image,k_pixels+u*GetPixelChannels(image),
36704a06a14c06f675b6a2052a3c814e7da54877a733cristy                      &result);
36714c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += *k;
3672e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3673e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3674e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3675a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        default:
3676a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* result directly calculated or assigned */
3677a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          break;
3678a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      }
3679a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Assign the resulting pixel values - Clamping Result */
3680e698a255629ba03cd125572de7b35b5e21c4ee5danthony      switch ( method ) {
3681e698a255629ba03cd125572de7b35b5e21c4ee5danthony        case VoronoiMorphology:
36824c08aed51c5899665ade97263692328eea4af106cristy          SetPixelPixelInfo(image,&result,q);
3683e698a255629ba03cd125572de7b35b5e21c4ee5danthony          break;
3684e698a255629ba03cd125572de7b35b5e21c4ee5danthony        default:
3685ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
36864c08aed51c5899665ade97263692328eea4af106cristy            SetPixelRed(image,ClampToQuantum(result.red),q);
3687ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
36884c08aed51c5899665ade97263692328eea4af106cristy            SetPixelGreen(image,ClampToQuantum(result.green),q);
3689ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
36904c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlue(image,ClampToQuantum(result.blue),q);
3691ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
36924c08aed51c5899665ade97263692328eea4af106cristy              (image->colorspace == CMYKColorspace))
36934c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlack(image,ClampToQuantum(result.black),q);
3694ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
36952b9582a27910c7baaeb04b7e969638328fa70095cristy              (image->matte == MagickTrue))
36964c08aed51c5899665ade97263692328eea4af106cristy            SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
3697e698a255629ba03cd125572de7b35b5e21c4ee5danthony          break;
3698e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3699a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Count up changed pixels */
3700ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      if (   (GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q))
3701ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q))
3702ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q))
3703ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q))
37044c08aed51c5899665ade97263692328eea4af106cristy          || ((image->colorspace == CMYKColorspace) &&
3705ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
3706a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        changed++;  /* The pixel was changed in some way! */
3707a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3708ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p-=GetPixelChannels(image); /* go backward through pixel buffers */
3709ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q-=GetPixelChannels(image);
3710a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    } /* x */
3711a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3712a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3713a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3714a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
3715a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                == MagickFalse )
3716a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        status=MagickFalse;
3717a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3718a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  } /* y */
3719e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3720a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  auth_view=DestroyCacheView(auth_view);
3721a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  virt_view=DestroyCacheView(virt_view);
3722aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  return(status ? (ssize_t) changed : -1);
3723a8843c1f815ffad2568ec592d5b446cb1476aab5anthony}
3724a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3725a8843c1f815ffad2568ec592d5b446cb1476aab5anthony/* Apply a Morphology by calling theabove low level primitive application
3726a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** functions.  This function handles any iteration loops, composition or
3727a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** re-iteration of results, and compound morphology methods that is based
3728a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** on multiple low-level (staged) morphology methods.
3729a8843c1f815ffad2568ec592d5b446cb1476aab5anthony**
3730a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** Basically this provides the complex grue between the requested morphology
3731a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** method and raw low-level implementation (above).
3732a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3733cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate Image *MorphologyApply(const Image *image,
3734f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method, const ssize_t iterations,
3735f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const KernelInfo *kernel, const CompositeOperator compose,const double bias,
3736f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3737602ab9b30b644a78a4057da93d838a77391ec0acanthony{
37381cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  CompositeOperator
37391cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy    curr_compose;
37401cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy
3741602ab9b30b644a78a4057da93d838a77391ec0acanthony  Image
374247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *curr_image,    /* Image we are working with or iterating */
3743a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *work_image,    /* secondary image for primitive iteration */
374447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *save_image,    /* saved image - for 'edge' method only */
374547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rslt_image;    /* resultant image - after multi-kernel handling */
3746602ab9b30b644a78a4057da93d838a77391ec0acanthony
37474fd27e21043be809d66c8202e779255e5b660d2danthony  KernelInfo
374847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *reflected_kernel, /* A reflected copy of the kernel (if needed) */
374947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *norm_kernel,      /* the current normal un-reflected kernel */
375047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rflt_kernel,      /* the current reflected kernel (if needed) */
375147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *this_kernel;      /* the kernel being applied */
37524fd27e21043be809d66c8202e779255e5b660d2danthony
37534fd27e21043be809d66c8202e779255e5b660d2danthony  MorphologyMethod
3754a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    primitive;      /* the current morphology primitive being applied */
37559eb4f74649b23c053b308ce1152dce51239450baanthony
37569eb4f74649b23c053b308ce1152dce51239450baanthony  CompositeOperator
375747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose;   /* multi-kernel compose method for results to use */
375847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
375947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  MagickBooleanType
3760e698a255629ba03cd125572de7b35b5e21c4ee5danthony    special,        /* do we use a direct modify function? */
376147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    verbose;        /* verbose output of results */
37624fd27e21043be809d66c8202e779255e5b660d2danthony
3763bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
3764a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    method_loop,    /* Loop 1: number of compound method iterations (norm 1) */
376547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_limit,   /*         maximum number of compound method iterations */
376647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number,  /* Loop 2: the kernel number being applied */
3767a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_loop,     /* Loop 3: primitive loop for compound morphology */
3768a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_limit,    /*         how many primitives are in this compound */
3769a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    kernel_loop,    /* Loop 4: iterate the kernel over image */
377047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_limit,   /*         number of times to iterate kernel */
3771a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    count,          /* total count of primitive steps applied */
377247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_changed, /* total count of changed using iterated kernel */
377347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed; /* total count of changed over method iteration */
377447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3775a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  ssize_t
3776a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;        /* number pixels changed by last primitive operation */
3777a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
377847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  char
377947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    v_info[80];
37801b2bc0a7da432e6e1cc0480280402df213faa940anthony
3781602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image != (Image *) NULL);
3782602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image->signature == MagickSignature);
37834fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
37844fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel->signature == MagickSignature);
3785602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception != (ExceptionInfo *) NULL);
3786602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception->signature == MagickSignature);
3787602ab9b30b644a78a4057da93d838a77391ec0acanthony
3788a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  count = 0;      /* number of low-level morphology primitives performed */
3789602ab9b30b644a78a4057da93d838a77391ec0acanthony  if ( iterations == 0 )
379047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return((Image *)NULL);   /* null operation - nothing to do! */
3791602ab9b30b644a78a4057da93d838a77391ec0acanthony
3792bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  kernel_limit = (size_t) iterations;
379347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
3794e698a255629ba03cd125572de7b35b5e21c4ee5danthony     kernel_limit = image->columns>image->rows ? image->columns : image->rows;
379528ad1d779b6ca95852e860514185a7a97e06af77anthony
379628ad1d779b6ca95852e860514185a7a97e06af77anthony  verbose = IsMagickTrue(GetImageArtifact(image,"verbose"));
37974f1dcb76c95ef6410f2957ca9e7e1d391cee0d02anthony
37989eb4f74649b23c053b308ce1152dce51239450baanthony  /* initialise for cleanup */
379947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  curr_image = (Image *) image;
38001cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  curr_compose = image->compose;
3801aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  (void) curr_compose;
380247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  work_image = save_image = rslt_image = (Image *) NULL;
380347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  reflected_kernel = (KernelInfo *) NULL;
38044fd27e21043be809d66c8202e779255e5b660d2danthony
380547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Initialize specific methods
380647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + which loop should use the given iteratations
3807a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   * + how many primitives make up the compound morphology
380847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + multi-kernel compose method to use (by default)
380947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
381047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_limit = 1;       /* just do method once, unless otherwise set */
3811ea61f01656bb0f9074677452017cc559e54093faanthony  stage_limit = 1;        /* assume method is not a compound */
38124ee950098ad0166bbbc85c3a59bc079cd321384aglennrp  special = MagickFalse;   /* assume it is NOT a direct modify primitive */
381347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  rslt_compose = compose; /* and we are composing multi-kernels as given */
38149eb4f74649b23c053b308ce1152dce51239450baanthony  switch( method ) {
3815a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case SmoothMorphology:  /* 4 primitive compound morphology */
381647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 4;
3817602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
3818a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case OpenMorphology:    /* 2 primitive compound morphology */
3819930be614b4595b97cd79ee864a394796740f76adanthony    case OpenIntensityMorphology:
382047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case TopHatMorphology:
382147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
38224fd27e21043be809d66c8202e779255e5b660d2danthony    case CloseIntensityMorphology:
382347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
382447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case EdgeMorphology:
382547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 2;
3826602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
38279eb4f74649b23c053b308ce1152dce51239450baanthony    case HitAndMissMorphology:
382847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
38293ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      /* FALL THUR */
3830c3e48258f3253188894e783dcdfd03562f7ab2c5anthony    case ThinningMorphology:
38319eb4f74649b23c053b308ce1152dce51239450baanthony    case ThickenMorphology:
38323ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      method_limit = kernel_limit;  /* iterate the whole method */
3833c3e48258f3253188894e783dcdfd03562f7ab2c5anthony      kernel_limit = 1;             /* do not do kernel iteration  */
383447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
3835a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3836e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3837e698a255629ba03cd125572de7b35b5e21c4ee5danthony      special = MagickTrue;
3838a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
383947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
38409eb4f74649b23c053b308ce1152dce51239450baanthony      break;
38419eb4f74649b23c053b308ce1152dce51239450baanthony  }
3842602ab9b30b644a78a4057da93d838a77391ec0acanthony
3843e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Apply special methods with special requirments
3844e698a255629ba03cd125572de7b35b5e21c4ee5danthony  ** For example, single run only, or post-processing requirements
3845e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
3846e698a255629ba03cd125572de7b35b5e21c4ee5danthony  if ( special == MagickTrue )
3847e698a255629ba03cd125572de7b35b5e21c4ee5danthony    {
3848e698a255629ba03cd125572de7b35b5e21c4ee5danthony      rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3849e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if (rslt_image == (Image *) NULL)
3850e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3851574cc26500992189f637cd1cdf93d0654e7df7aecristy      if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3852574cc26500992189f637cd1cdf93d0654e7df7aecristy        goto error_cleanup;
3853e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3854e698a255629ba03cd125572de7b35b5e21c4ee5danthony      changed = MorphologyPrimitiveDirect(rslt_image, method,
3855f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy         kernel, exception);
3856e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3857e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( verbose == MagickTrue )
38585acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) (void) FormatLocaleFile(stderr,
38591e604812fad85bb96f757a2393015ae3d061c39acristy          "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
38601e604812fad85bb96f757a2393015ae3d061c39acristy          CommandOptionToMnemonic(MagickMorphologyOptions, method),
38611e604812fad85bb96f757a2393015ae3d061c39acristy          1.0,0.0,1.0, (double) changed);
3862e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3863e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( changed < 0 )
3864e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3865e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3866e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method == VoronoiMorphology ) {
3867e698a255629ba03cd125572de7b35b5e21c4ee5danthony        /* Preserve the alpha channel of input image - but turned off */
386863240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
386963240888c3975789a09c2494a4654b523931df96cristy          exception);
3870f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy        (void) CompositeImage(rslt_image, CopyOpacityCompositeOp, image, 0, 0);
387163240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
387263240888c3975789a09c2494a4654b523931df96cristy          exception);
3873e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3874e698a255629ba03cd125572de7b35b5e21c4ee5danthony      goto exit_cleanup;
3875e698a255629ba03cd125572de7b35b5e21c4ee5danthony    }
3876e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3877c3e48258f3253188894e783dcdfd03562f7ab2c5anthony  /* Handle user (caller) specified multi-kernel composition method */
387847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( compose != UndefinedCompositeOp )
387947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = compose;  /* override default composition for method */
388047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_compose == UndefinedCompositeOp )
388147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
38824fd27e21043be809d66c8202e779255e5b660d2danthony
3883a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Some methods require a reflected kernel to use with primitives.
3884c3e48258f3253188894e783dcdfd03562f7ab2c5anthony   * Create the reflected kernel for those methods. */
388547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  switch ( method ) {
388647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CorrelateMorphology:
388747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
388847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseIntensityMorphology:
388947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
389047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case SmoothMorphology:
389147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      reflected_kernel = CloneKernelInfo(kernel);
389247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if (reflected_kernel == (KernelInfo *) NULL)
389347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        goto error_cleanup;
389447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      RotateKernelInfo(reflected_kernel,180);
389547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
389647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
389747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
389847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
3899602ab9b30b644a78a4057da93d838a77391ec0acanthony
3900e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Loops around more primitive morpholgy methods
3901e698a255629ba03cd125572de7b35b5e21c4ee5danthony  **  erose, dilate, open, close, smooth, edge, etc...
3902e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
390347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Loop 1:  iterate the compound method */
390447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_loop = 0;
390547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_changed = 1;
390647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while ( method_loop < method_limit && method_changed > 0 ) {
390747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_loop++;
390847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed = 0;
390947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
391047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    /* Loop 2:  iterate over each kernel in a multi-kernel list */
391147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    norm_kernel = (KernelInfo *) kernel;
3912f2faecf9facdbbb14fcba373365f9f691a9658e0cristy    this_kernel = (KernelInfo *) kernel;
391347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rflt_kernel = reflected_kernel;
3914e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony
391547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number = 0;
391647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    while ( norm_kernel != NULL ) {
391747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
391847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* Loop 3: Compound Morphology Staging - Select Primative to apply */
391947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_loop = 0;          /* the compound morphology stage number */
392047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      while ( stage_loop < stage_limit ) {
392147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        stage_loop++;   /* The stage of the compound morphology */
392247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3923a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Select primitive morphology for this stage of compound method */
392447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        this_kernel = norm_kernel; /* default use unreflected kernel */
3925a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        primitive = method;        /* Assume method is a primitive */
392647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        switch( method ) {
392747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case ErodeMorphology:      /* just erode */
392847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeInMorphology:     /* erode and image difference */
3929a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
393047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
393147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case DilateMorphology:     /* just dilate */
393247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeOutMorphology:    /* dilate and image difference */
3933a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
393447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
393547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenMorphology:       /* erode then dialate */
393647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case TopHatMorphology:     /* open and image difference */
3937a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
393847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3939a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateMorphology;
394047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
394147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenIntensityMorphology:
3942a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeIntensityMorphology;
394347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3944a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateIntensityMorphology;
3945e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony            break;
394647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseMorphology:      /* dilate, then erode */
394747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case BottomHatMorphology:  /* close and image difference */
394847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3949a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
395047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3951a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
395247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
395347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseIntensityMorphology:
395447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3955a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateIntensityMorphology;
395647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3957a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeIntensityMorphology;
395847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
395947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case SmoothMorphology:         /* open, close */
396047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            switch ( stage_loop ) {
396147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 1: /* start an open method, which starts with Erode */
3962a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
396347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
396447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 2:  /* now Dilate the Erode */
3965a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
396647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
396747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 3:  /* Reflect kernel a close */
396847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
3969a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
397047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
397147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 4:  /* Finish the Close */
397247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
3973a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
397447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
397547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
397647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
397747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeMorphology:        /* dilate and erode difference */
3978a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
397947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 ) {
398047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              save_image = curr_image;      /* save the image difference */
398147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              curr_image = (Image *) image;
3982a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
398347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
398447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
398547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CorrelateMorphology:
398647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            /* A Correlation is a Convolution with a reflected kernel.
398747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** However a Convolution is a weighted sum using a reflected
398847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel.  It may seem stange to convert a Correlation into a
398947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution as the Correlation is the simplier method, but
399047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution is much more commonly used, and it makes sense to
399147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** implement it directly so as to avoid the need to duplicate the
399247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel when it is not required (which is typically the
399347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** default).
399447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            */
399547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3996a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ConvolveMorphology;
399747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
399847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          default:
399947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
400047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
4001e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony        assert( this_kernel != (KernelInfo *) NULL );
40027a01dcf50ce12cb2a789bedff51e9345f022432eanthony
400347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        /* Extra information for debugging compound operations */
400447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        if ( verbose == MagickTrue ) {
400547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( stage_limit > 1 )
4006b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            (void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
4007042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy             CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
4008e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy             method_loop,(double) stage_loop);
4009a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          else if ( primitive != method )
4010b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            (void) FormatLocaleString(v_info, MaxTextExtent, "%s:%.20g -> ",
4011042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
4012e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              method_loop);
401347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          else
401447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            v_info[0] = '\0';
401547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
40169eb4f74649b23c053b308ce1152dce51239450baanthony
4017a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Loop 4: Iterate the kernel with primitive */
401847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_loop = 0;
401947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_changed = 0;
402047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        changed = 1;
402147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        while ( kernel_loop < kernel_limit && changed > 0 ) {
402247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          kernel_loop++;     /* the iteration of this kernel */
40239eb4f74649b23c053b308ce1152dce51239450baanthony
4024a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* Create a clone as the destination image, if not yet defined */
40259eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == (Image *) NULL )
40269eb4f74649b23c053b308ce1152dce51239450baanthony            {
40279eb4f74649b23c053b308ce1152dce51239450baanthony              work_image=CloneImage(image,0,0,MagickTrue,exception);
40289eb4f74649b23c053b308ce1152dce51239450baanthony              if (work_image == (Image *) NULL)
40299eb4f74649b23c053b308ce1152dce51239450baanthony                goto error_cleanup;
4030574cc26500992189f637cd1cdf93d0654e7df7aecristy              if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
4031574cc26500992189f637cd1cdf93d0654e7df7aecristy                goto error_cleanup;
4032db60568e12574785101a4ae8d8da076227a0a889anthony              /* work_image->type=image->type; ??? */
40339eb4f74649b23c053b308ce1152dce51239450baanthony            }
40349eb4f74649b23c053b308ce1152dce51239450baanthony
4035501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
40369eb4f74649b23c053b308ce1152dce51239450baanthony          count++;
4037e698a255629ba03cd125572de7b35b5e21c4ee5danthony          changed = MorphologyPrimitive(curr_image, work_image, primitive,
4038f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy                       this_kernel, bias, exception);
403947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
404047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( verbose == MagickTrue ) {
404147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( kernel_loop > 1 )
40425acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
40435acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) (void) FormatLocaleFile(stderr,
40441e604812fad85bb96f757a2393015ae3d061c39acristy              "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
4045042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
4046a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive),(this_kernel == rflt_kernel ) ? "*" : "",
4047e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) (method_loop+kernel_loop-1),(double) kernel_number,
4048e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) count,(double) changed);
404947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          }
4050a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          if ( changed < 0 )
4051a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            goto error_cleanup;
4052a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          kernel_changed += changed;
4053a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          method_changed += changed;
4054a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
40559eb4f74649b23c053b308ce1152dce51239450baanthony          /* prepare next loop */
40569eb4f74649b23c053b308ce1152dce51239450baanthony          { Image *tmp = work_image;   /* swap images for iteration */
40579eb4f74649b23c053b308ce1152dce51239450baanthony            work_image = curr_image;
40589eb4f74649b23c053b308ce1152dce51239450baanthony            curr_image = tmp;
40599eb4f74649b23c053b308ce1152dce51239450baanthony          }
40609eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == image )
406147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            work_image = (Image *) NULL; /* replace input 'image' */
40629eb4f74649b23c053b308ce1152dce51239450baanthony
4063a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        } /* End Loop 4: Iterate the kernel with primitive */
40647a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4065a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        if ( verbose == MagickTrue && kernel_changed != (size_t)changed )
40665acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
406747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        if ( verbose == MagickTrue && stage_loop < stage_limit )
40685acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
40699eb4f74649b23c053b308ce1152dce51239450baanthony
407047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#if 0
40715acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
40725acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
40735acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      work =0x%lx\n", (unsigned long)work_image);
40745acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      save =0x%lx\n", (unsigned long)save_image);
40755acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
407647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#endif
40771b2bc0a7da432e6e1cc0480280402df213faa940anthony
407847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
40791b2bc0a7da432e6e1cc0480280402df213faa940anthony
408047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /*  Final Post-processing for some Compound Methods
408147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      **
408247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** The removal of any 'Sync' channel flag in the Image Compositon
408347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** below ensures the methematical compose method is applied in a
408447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** purely mathematical way, and only to the selected channels.
408547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** Turn off SVG composition 'alpha blending'.
408647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      */
408747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      switch( method ) {
408847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeOutMorphology:
408947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeInMorphology:
409047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case TopHatMorphology:
409147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case BottomHatMorphology:
40929eb4f74649b23c053b308ce1152dce51239450baanthony          if ( verbose == MagickTrue )
40935acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, "\n%s: Difference with original image",
4094042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy                 CommandOptionToMnemonic(MagickMorphologyOptions, method) );
4095f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy          curr_image->sync=MagickFalse;
4096f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy          (void) CompositeImage(curr_image,DifferenceCompositeOp,image,0,0);
409747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
409847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeMorphology:
409947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( verbose == MagickTrue )
41005acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, "\n%s: Difference of Dilate and Erode",
4101042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy                 CommandOptionToMnemonic(MagickMorphologyOptions, method) );
4102f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy          curr_image->sync=MagickFalse;
4103f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy          (void) CompositeImage(curr_image,DifferenceCompositeOp,save_image,0,
4104f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy            0);
410547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          save_image = DestroyImage(save_image); /* finished with save image */
410647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
410747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        default:
410847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
4109602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
41109eb4f74649b23c053b308ce1152dce51239450baanthony
411147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* multi-kernel handling:  re-iterate, or compose results */
411247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( kernel->next == (KernelInfo *) NULL )
4113c3e48258f3253188894e783dcdfd03562f7ab2c5anthony        rslt_image = curr_image;   /* just return the resulting image */
411447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_compose == NoCompositeOp )
4115c3e48258f3253188894e783dcdfd03562f7ab2c5anthony        { if ( verbose == MagickTrue ) {
4116c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            if ( this_kernel->next != (KernelInfo *) NULL )
41175acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (re-iterate)");
4118c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            else
41195acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (done)");
4120c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          }
4121c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          rslt_image = curr_image; /* return result, and re-iterate */
41229eb4f74649b23c053b308ce1152dce51239450baanthony        }
412347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_image == (Image *) NULL)
412447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        { if ( verbose == MagickTrue )
41255acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (save for compose)");
412647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          rslt_image = curr_image;
412747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
41289eb4f74649b23c053b308ce1152dce51239450baanthony        }
412947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else
4130ea61f01656bb0f9074677452017cc559e54093faanthony        { /* Add the new 'current' result to the composition
413147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          **
413247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** The removal of any 'Sync' channel flag in the Image Compositon
413347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** below ensures the methematical compose method is applied in a
413447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** purely mathematical way, and only to the selected channels.
4135ea61f01656bb0f9074677452017cc559e54093faanthony          ** IE: Turn off SVG composition 'alpha blending'.
413647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          */
413747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( verbose == MagickTrue )
41385acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (compose \"%s\")",
4139042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy                 CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
4140f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy          rslt_image->sync=MagickFalse;
4141f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy          (void) CompositeImage(rslt_image, rslt_compose, curr_image, 0, 0);
41420bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony          curr_image = DestroyImage(curr_image);
414347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
414447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
414547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( verbose == MagickTrue )
41465acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "\n");
41474fd27e21043be809d66c8202e779255e5b660d2danthony
414847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* loop to the next kernel in a multi-kernel list */
414947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      norm_kernel = norm_kernel->next;
415047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( rflt_kernel != (KernelInfo *) NULL )
415147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        rflt_kernel = rflt_kernel->next;
415247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      kernel_number++;
415347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    } /* End Loop 2: Loop over each kernel */
41549eb4f74649b23c053b308ce1152dce51239450baanthony
415547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  } /* End Loop 1: compound method interation */
4156602ab9b30b644a78a4057da93d838a77391ec0acanthony
41579eb4f74649b23c053b308ce1152dce51239450baanthony  goto exit_cleanup;
41581b2bc0a7da432e6e1cc0480280402df213faa940anthony
415947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Yes goto's are bad, but it makes cleanup lot more efficient */
41601b2bc0a7da432e6e1cc0480280402df213faa940anthonyerror_cleanup:
4161ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image )
4162ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
416347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_image != (Image *) NULL )
416447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_image = DestroyImage(rslt_image);
41651b2bc0a7da432e6e1cc0480280402df213faa940anthonyexit_cleanup:
4166ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image || curr_image == image )
4167ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
4168ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image != (Image *) NULL )
416947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    curr_image = DestroyImage(curr_image);
41709eb4f74649b23c053b308ce1152dce51239450baanthony  if ( work_image != (Image *) NULL )
417147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    work_image = DestroyImage(work_image);
41729eb4f74649b23c053b308ce1152dce51239450baanthony  if ( save_image != (Image *) NULL )
417347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    save_image = DestroyImage(save_image);
417447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( reflected_kernel != (KernelInfo *) NULL )
417547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    reflected_kernel = DestroyKernelInfo(reflected_kernel);
417647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return(rslt_image);
41779eb4f74649b23c053b308ce1152dce51239450baanthony}
4178a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
41799eb4f74649b23c053b308ce1152dce51239450baanthony
41809eb4f74649b23c053b308ce1152dce51239450baanthony/*
41819eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41829eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
41839eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
41849eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
4185f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%     M o r p h o l o g y I m a g e                                           %
41869eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
41879eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
41889eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
41899eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41909eb4f74649b23c053b308ce1152dce51239450baanthony%
4191f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  MorphologyImage() applies a user supplied kernel to the image
41929eb4f74649b23c053b308ce1152dce51239450baanthony%  according to the given mophology method.
41939eb4f74649b23c053b308ce1152dce51239450baanthony%
41949eb4f74649b23c053b308ce1152dce51239450baanthony%  This function applies any and all user defined settings before calling
41959eb4f74649b23c053b308ce1152dce51239450baanthony%  the above internal function MorphologyApply().
41969eb4f74649b23c053b308ce1152dce51239450baanthony%
41979eb4f74649b23c053b308ce1152dce51239450baanthony%  User defined settings include...
419846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    * Output Bias for Convolution and correlation   ("-bias")
419946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    * Kernel Scale/normalize settings     ("-set 'option:convolve:scale'")
420046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      This can also includes the addition of a scaled unity kernel.
420146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    * Show Kernel being applied           ("-set option:showkernel 1")
42029eb4f74649b23c053b308ce1152dce51239450baanthony%
42039eb4f74649b23c053b308ce1152dce51239450baanthony%  The format of the MorphologyImage method is:
42049eb4f74649b23c053b308ce1152dce51239450baanthony%
42059eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyImage(const Image *image,MorphologyMethod method,
4206bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
42079eb4f74649b23c053b308ce1152dce51239450baanthony%
4208f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%      Image *MorphologyImage(const Image *image, const ChannelType
4209bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        channel,MorphologyMethod method,const ssize_t iterations,
42109eb4f74649b23c053b308ce1152dce51239450baanthony%        KernelInfo *kernel,ExceptionInfo *exception)
42119eb4f74649b23c053b308ce1152dce51239450baanthony%
42129eb4f74649b23c053b308ce1152dce51239450baanthony%  A description of each parameter follows:
42139eb4f74649b23c053b308ce1152dce51239450baanthony%
42149eb4f74649b23c053b308ce1152dce51239450baanthony%    o image: the image.
42159eb4f74649b23c053b308ce1152dce51239450baanthony%
42169eb4f74649b23c053b308ce1152dce51239450baanthony%    o method: the morphology method to be applied.
42179eb4f74649b23c053b308ce1152dce51239450baanthony%
42189eb4f74649b23c053b308ce1152dce51239450baanthony%    o iterations: apply the operation this many times (or no change).
42199eb4f74649b23c053b308ce1152dce51239450baanthony%                  A value of -1 means loop until no change found.
42209eb4f74649b23c053b308ce1152dce51239450baanthony%                  How this is applied may depend on the morphology method.
42219eb4f74649b23c053b308ce1152dce51239450baanthony%                  Typically this is a value of 1.
42229eb4f74649b23c053b308ce1152dce51239450baanthony%
42239eb4f74649b23c053b308ce1152dce51239450baanthony%    o kernel: An array of double representing the morphology kernel.
42249eb4f74649b23c053b308ce1152dce51239450baanthony%              Warning: kernel may be normalized for the Convolve method.
42259eb4f74649b23c053b308ce1152dce51239450baanthony%
42269eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
42279eb4f74649b23c053b308ce1152dce51239450baanthony%
42289eb4f74649b23c053b308ce1152dce51239450baanthony*/
4229f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristyMagickExport Image *MorphologyImage(const Image *image,
4230f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const ssize_t iterations,
4231f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const KernelInfo *kernel,ExceptionInfo *exception)
42329eb4f74649b23c053b308ce1152dce51239450baanthony{
42339eb4f74649b23c053b308ce1152dce51239450baanthony  KernelInfo
42349eb4f74649b23c053b308ce1152dce51239450baanthony    *curr_kernel;
42359eb4f74649b23c053b308ce1152dce51239450baanthony
423647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  CompositeOperator
423747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    compose;
423847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
42399eb4f74649b23c053b308ce1152dce51239450baanthony  Image
42409eb4f74649b23c053b308ce1152dce51239450baanthony    *morphology_image;
42419eb4f74649b23c053b308ce1152dce51239450baanthony
42429eb4f74649b23c053b308ce1152dce51239450baanthony
424346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Apply Convolve/Correlate Normalization and Scaling Factors.
424446a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * This is done BEFORE the ShowKernelInfo() function is called so that
424546a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * users can see the results of the 'option:convolve:scale' option.
42469eb4f74649b23c053b308ce1152dce51239450baanthony   */
42479eb4f74649b23c053b308ce1152dce51239450baanthony  curr_kernel = (KernelInfo *) kernel;
4248f71ca294b7a21e80549dab84941ab149f7cdb300anthony  if ( method == ConvolveMorphology ||  method == CorrelateMorphology )
42499eb4f74649b23c053b308ce1152dce51239450baanthony    {
425028ad1d779b6ca95852e860514185a7a97e06af77anthony      const char
425128ad1d779b6ca95852e860514185a7a97e06af77anthony        *artifact;
42529eb4f74649b23c053b308ce1152dce51239450baanthony      artifact = GetImageArtifact(image,"convolve:scale");
4253e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony      if ( artifact != (const char *)NULL ) {
42549eb4f74649b23c053b308ce1152dce51239450baanthony        if ( curr_kernel == kernel )
42559eb4f74649b23c053b308ce1152dce51239450baanthony          curr_kernel = CloneKernelInfo(kernel);
42569eb4f74649b23c053b308ce1152dce51239450baanthony        if (curr_kernel == (KernelInfo *) NULL) {
42579eb4f74649b23c053b308ce1152dce51239450baanthony          curr_kernel=DestroyKernelInfo(curr_kernel);
42589eb4f74649b23c053b308ce1152dce51239450baanthony          return((Image *) NULL);
42599eb4f74649b23c053b308ce1152dce51239450baanthony        }
426046a369d839971ab627bdb31a93d8bd63e81b65a3anthony        ScaleGeometryKernelInfo(curr_kernel, artifact);
42619eb4f74649b23c053b308ce1152dce51239450baanthony      }
42629eb4f74649b23c053b308ce1152dce51239450baanthony    }
42639eb4f74649b23c053b308ce1152dce51239450baanthony
42649eb4f74649b23c053b308ce1152dce51239450baanthony  /* display the (normalized) kernel via stderr */
426528ad1d779b6ca95852e860514185a7a97e06af77anthony  if ( IsMagickTrue(GetImageArtifact(image,"showkernel"))
426628ad1d779b6ca95852e860514185a7a97e06af77anthony    || IsMagickTrue(GetImageArtifact(image,"convolve:showkernel"))
426728ad1d779b6ca95852e860514185a7a97e06af77anthony    || IsMagickTrue(GetImageArtifact(image,"morphology:showkernel")) )
42689eb4f74649b23c053b308ce1152dce51239450baanthony    ShowKernelInfo(curr_kernel);
42699eb4f74649b23c053b308ce1152dce51239450baanthony
42703206678d008425bc56dd2dbad002f2bb26299dc2anthony  /* Override the default handling of multi-kernel morphology results
42713206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'Undefined' use the default method
42723206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'None' (default for 'Convolve') re-iterate previous result
42733206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Otherwise merge resulting images using compose method given.
42743206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Default for 'HitAndMiss' is 'Lighten'.
427547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
427628ad1d779b6ca95852e860514185a7a97e06af77anthony  { const char
427728ad1d779b6ca95852e860514185a7a97e06af77anthony      *artifact;
427828ad1d779b6ca95852e860514185a7a97e06af77anthony    artifact = GetImageArtifact(image,"morphology:compose");
427928ad1d779b6ca95852e860514185a7a97e06af77anthony    compose = UndefinedCompositeOp;  /* use default for method */
428028ad1d779b6ca95852e860514185a7a97e06af77anthony    if ( artifact != (const char *) NULL)
428170b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy      compose=(CompositeOperator) ParseCommandOption(MagickComposeOptions,
428270b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy        MagickFalse,artifact);
428328ad1d779b6ca95852e860514185a7a97e06af77anthony  }
42849eb4f74649b23c053b308ce1152dce51239450baanthony  /* Apply the Morphology */
4285f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  morphology_image = MorphologyApply(image, method, iterations,
4286f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy    curr_kernel, compose, image->bias, exception);
42879eb4f74649b23c053b308ce1152dce51239450baanthony
42889eb4f74649b23c053b308ce1152dce51239450baanthony  /* Cleanup and Exit */
42899eb4f74649b23c053b308ce1152dce51239450baanthony  if ( curr_kernel != kernel )
42901b2bc0a7da432e6e1cc0480280402df213faa940anthony    curr_kernel=DestroyKernelInfo(curr_kernel);
42919eb4f74649b23c053b308ce1152dce51239450baanthony  return(morphology_image);
42929eb4f74649b23c053b308ce1152dce51239450baanthony}
429383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
429483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
429583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
429783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
429883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
42994fd27e21043be809d66c8202e779255e5b660d2danthony+     R o t a t e K e r n e l I n f o                                         %
430083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
430183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
430283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
430383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
430483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
430546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  RotateKernelInfo() rotates the kernel by the angle given.
430646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
430746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  Currently it is restricted to 90 degree angles, of either 1D kernels
430846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
430946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It will ignore usless rotations for specific 'named' built-in kernels.
431083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
43114fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the RotateKernelInfo method is:
431283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
43134fd27e21043be809d66c8202e779255e5b660d2danthony%      void RotateKernelInfo(KernelInfo *kernel, double angle)
431483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
431583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
431683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
431783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
431883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
431983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o angle: angle to rotate in degrees
432083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
432146a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This function is currently internal to this module only, but can be exported
432246a369d839971ab627bdb31a93d8bd63e81b65a3anthony% to other modules if needed.
432383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
43244fd27e21043be809d66c8202e779255e5b660d2danthonystatic void RotateKernelInfo(KernelInfo *kernel, double angle)
432583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
43261b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* angle the lower kernels first */
43271b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
43281b2bc0a7da432e6e1cc0480280402df213faa940anthony    RotateKernelInfo(kernel->next, angle);
43291b2bc0a7da432e6e1cc0480280402df213faa940anthony
433083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
433183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  **
433283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  ** TODO: expand beyond simple 90 degree rotates, flips and flops
433383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  */
433483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
433583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* Modulus the angle */
433683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  angle = fmod(angle, 360.0);
433783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( angle < 0 )
433883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    angle += 360.0;
433983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
43403c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 337.5 < angle || angle <= 22.5 )
434143c4925e5305a26e48d68f7893e94f55d0831c39anthony    return;   /* Near zero angle - no change! - At least not at this time */
434283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
43433dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  /* Handle special cases */
434483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  switch (kernel->type) {
434583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These built-in kernels are cylindrical kernels, rotating is useless */
434683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case GaussianKernel:
4347501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
4348501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
434983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiskKernel:
43503dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case PeaksKernel:
43513dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case LaplacianKernel:
435283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case ChebyshevKernel:
4353bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
435483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case EuclideanKernel:
435583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
435683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
435783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These may be rotatable at non-90 angles in the future */
435883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* but simply rotating them in multiples of 90 degrees is useless */
435983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case SquareKernel:
436083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiamondKernel:
436183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case PlusKernel:
43623dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
436383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
436483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
436583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These only allows a +/-90 degree rotation (by transpose) */
436683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* A 180 degree rotation is useless */
436783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case BlurKernel:
436883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 135.0 < angle && angle <= 225.0 )
436983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return;
437083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 225.0 < angle && angle <= 315.0 )
437183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        angle -= 180;
437283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
437383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
43743dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    default:
437583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
437683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
437757fe7a498c1302232dac8466864e84b12fad0807anthony  /* Attempt rotations by 45 degrees  -- 3x3 kernels only */
43783c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
43793c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
43803c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 3 && kernel->height == 3 )
43813c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a 3x3 square by 45 degree angle */
43823c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          MagickRealType t  = kernel->values[0];
438343c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[0] = kernel->values[3];
438443c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[3] = kernel->values[6];
438543c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[6] = kernel->values[7];
438643c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[7] = kernel->values[8];
438743c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[8] = kernel->values[5];
438843c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[5] = kernel->values[2];
438943c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[2] = kernel->values[1];
439043c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[1] = t;
43911d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate non-centered origin */
43921d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          if ( kernel->x != 1 || kernel->y != 1 ) {
4393bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            ssize_t x,y;
4394bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            x = (ssize_t) kernel->x-1;
4395bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            y = (ssize_t) kernel->y-1;
43961d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                 if ( x == y  ) x = 0;
43971d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == 0  ) x = -y;
43981d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == -y ) y = 0;
43991d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( y == 0  ) y = x;
4400ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) x+1;
4401ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) y+1;
44021d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
440343c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
440443c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+45.0, 360.0);
44053c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
44063c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
44073c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate non-3x3 kernel by 45 degrees");
44083c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
44093c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
44103c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
44113c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 1 || kernel->height == 1 )
44124c08aed51c5899665ade97263692328eea4af106cristy        { /* Do a transpose of a 1 dimensional kernel,
4413bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          ** which results in a fast 90 degree rotation of some type.
44143c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          */
4415bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          ssize_t
44163c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            t;
4417bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          t = (ssize_t) kernel->width;
44183c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->width = kernel->height;
4419bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->height = (size_t) t;
44203c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          t = kernel->x;
44213c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->x = kernel->y;
44223c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->y = t;
442343c4925e5305a26e48d68f7893e94f55d0831c39anthony          if ( kernel->width == 1 ) {
442443c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
442543c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+90.0, 360.0);
442643c4925e5305a26e48d68f7893e94f55d0831c39anthony          } else {
442743c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
442843c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+270.0, 360.0);
442943c4925e5305a26e48d68f7893e94f55d0831c39anthony          }
44303c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
44313c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else if ( kernel->width == kernel->height )
44323c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a square array of values by 90 degrees */
4433bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          { register size_t
44341d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              i,j,x,y;
44351d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            register MagickRealType
44361d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              *k,t;
44371d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            k=kernel->values;
44381d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            for( i=0, x=kernel->width-1;  i<=x;   i++, x--)
44391d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              for( j=0, y=kernel->height-1;  j<y;   j++, y--)
44401d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                { t                    = k[i+j*kernel->width];
44411d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[i+j*kernel->width] = k[j+x*kernel->width];
44421d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[j+x*kernel->width] = k[x+y*kernel->width];
44431d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[x+y*kernel->width] = k[y+i*kernel->width];
44441d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[y+i*kernel->width] = t;
44451d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                }
44461d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
44471d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate the origin - relative to center of array */
4448bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          { register ssize_t x,y;
4449eaedf06777741da32408da72c1e512975c600c48cristy            x = (ssize_t) (kernel->x*2-kernel->width+1);
4450eaedf06777741da32408da72c1e512975c600c48cristy            y = (ssize_t) (kernel->y*2-kernel->height+1);
4451ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4452ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
44531d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
445443c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
445543c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+90.0, 360.0);
44563c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
44573c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
44583c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
44593c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
446083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( 135.0 < angle && angle <= 225.0 )
446183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    {
446243c4925e5305a26e48d68f7893e94f55d0831c39anthony      /* For a 180 degree rotation - also know as a reflection
446343c4925e5305a26e48d68f7893e94f55d0831c39anthony       * This is actually a very very common operation!
446443c4925e5305a26e48d68f7893e94f55d0831c39anthony       * Basically all that is needed is a reversal of the kernel data!
446543c4925e5305a26e48d68f7893e94f55d0831c39anthony       * And a reflection of the origon
446643c4925e5305a26e48d68f7893e94f55d0831c39anthony       */
4467bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      size_t
446883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        i,j;
446983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      register double
447083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        *k,t;
447183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
447283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      k=kernel->values;
447383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      for ( i=0, j=kernel->width*kernel->height-1;  i<j;  i++, j--)
447483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        t=k[i],  k[i]=k[j],  k[j]=t;
447583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
4476bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
4477bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = (ssize_t) kernel->height - kernel->y - 1;
447843c4925e5305a26e48d68f7893e94f55d0831c39anthony      angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
447943c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->angle = fmod(kernel->angle+180.0, 360.0);
448083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    }
44813c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* At this point angle should at least between -45 (315) and +45 degrees
448283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * In the future some form of non-orthogonal angled rotates could be
448383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * performed here, posibily with a linear kernel restriction.
448483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   */
448583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
448683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  return;
448783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
448883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
448983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
449083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
449283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
449383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
449446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S c a l e G e o m e t r y K e r n e l I n f o                           %
449546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
449646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
449746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
449846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
450046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleGeometryKernelInfo() takes a geometry argument string, typically
450146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  provided as a  "-set option:convolve:scale {geometry}" user setting,
450246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  and modifies the kernel according to the parsed arguments of that setting.
450346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
450446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The first argument (and any normalization flags) are passed to
450546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
450646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  is then passed to UnityAddKernelInfo() to add a scled unity kernel
450746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  into the scaled/normalized kernel.
450846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4509ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%  The format of the ScaleGeometryKernelInfo method is:
451046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4511ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%      void ScaleGeometryKernelInfo(KernelInfo *kernel,
4512ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%        const double scaling_factor,const MagickStatusType normalize_flags)
451346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
451446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
451546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
451646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
451746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
451846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o geometry:
451946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             The geometry string to parse, typically from the user provided
452046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             "-set option:convolve:scale {geometry}" setting.
452146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
452246a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
452346a369d839971ab627bdb31a93d8bd63e81b65a3anthonyMagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
452446a369d839971ab627bdb31a93d8bd63e81b65a3anthony     const char *geometry)
452546a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
452646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  GeometryFlags
452746a369d839971ab627bdb31a93d8bd63e81b65a3anthony    flags;
452846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  GeometryInfo
452946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args;
453046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
453146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  SetGeometryInfo(&args);
453246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  flags = (GeometryFlags) ParseGeometry(geometry, &args);
453346a369d839971ab627bdb31a93d8bd63e81b65a3anthony
453446a369d839971ab627bdb31a93d8bd63e81b65a3anthony#if 0
453546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* For Debugging Geometry Input */
45365acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
453746a369d839971ab627bdb31a93d8bd63e81b65a3anthony       flags, args.rho, args.sigma, args.xi, args.psi );
453846a369d839971ab627bdb31a93d8bd63e81b65a3anthony#endif
453946a369d839971ab627bdb31a93d8bd63e81b65a3anthony
454046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
454146a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho *= 0.01,  args.sigma *= 0.01;
454246a369d839971ab627bdb31a93d8bd63e81b65a3anthony
454346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
454446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho = 1.0;
454546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) == 0 )
454646a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.sigma = 0.0;
454746a369d839971ab627bdb31a93d8bd63e81b65a3anthony
454846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Scale/Normalize the input kernel */
454946a369d839971ab627bdb31a93d8bd63e81b65a3anthony  ScaleKernelInfo(kernel, args.rho, flags);
455046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
455146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add Unity Kernel, for blending with original */
455246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) != 0 )
455346a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel, args.sigma);
455446a369d839971ab627bdb31a93d8bd63e81b65a3anthony
455546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
455646a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
455746a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
455846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
455946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
456046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
456146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
45626771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     S c a l e K e r n e l I n f o                                           %
4563cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4564cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4565cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4566cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4567cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
45681b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ScaleKernelInfo() scales the given kernel list by the given amount, with or
45691b2bc0a7da432e6e1cc0480280402df213faa940anthony%  without normalization of the sum of the kernel values (as per given flags).
4570999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4571999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  By default (no flags given) the values within the kernel is scaled
45721b2bc0a7da432e6e1cc0480280402df213faa940anthony%  directly using given scaling factor without change.
4573999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
457446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  If either of the two 'normalize_flags' are given the kernel will first be
457546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  normalized and then further scaled by the scaling factor value given.
4576999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4577999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  Kernel normalization ('normalize_flags' given) is designed to ensure that
4578999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
45791b2bc0a7da432e6e1cc0480280402df213faa940anthony%  morphology methods will fall into -1.0 to +1.0 range.  Note that for
45801b2bc0a7da432e6e1cc0480280402df213faa940anthony%  non-HDRI versions of IM this may cause images to have any negative results
45811b2bc0a7da432e6e1cc0480280402df213faa940anthony%  clipped, unless some 'bias' is used.
4582999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4583999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  More specifically.  Kernels which only contain positive values (such as a
4584999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
45851b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ensuring a 0.0 to +1.0 output range for non-HDRI images.
4586999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4587999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
4588999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  the kernel will be scaled by the absolute of the sum of kernel values, so
4589999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  that it will generally fall within the +/- 1.0 range.
4590cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4591999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
4592999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  will be scaled by just the sum of the postive values, so that its output
4593999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  range will again fall into the  +/- 1.0 range.
4594cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4595999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For special kernels designed for locating shapes using 'Correlate', (often
4596999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  only containing +1 and -1 values, representing foreground/brackground
4597999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  matching) a special normalization method is provided to scale the positive
45981e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  values separately to those of the negative values, so the kernel will be
4599999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  forced to become a zero-sum kernel better suited to such searches.
4600999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
46011b2bc0a7da432e6e1cc0480280402df213faa940anthony%  WARNING: Correct normalization of the kernel assumes that the '*_range'
4602999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  attributes within the kernel structure have been correctly set during the
4603999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  kernels creation.
4604999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4605999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  NOTE: The values used for 'normalize_flags' have been selected specifically
460646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  to match the use of geometry options, so that '!' means NormalizeValue, '^'
460746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
4608cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
46094fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the ScaleKernelInfo method is:
4610cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4611999bb2c20aa9d42875bb5adba44951988d4ae354anthony%      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
4612999bb2c20aa9d42875bb5adba44951988d4ae354anthony%               const MagickStatusType normalize_flags )
4613cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4614cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4615cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4616cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4617cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4618999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o scaling_factor:
4619999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             multiply all values (after normalization) by this factor if not
4620999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             zero.  If the kernel is normalized regardless of any flags.
4621999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4622999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o normalize_flags:
4623999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             GeometryFlags defining normalization method to use.
4624999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             specifically: NormalizeValue, CorrelateNormalizeValue,
4625999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                           and/or PercentValue
4626cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4627cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
46286771f1e8987fa49f52d4176281a2e8524b8e31cbcristyMagickExport void ScaleKernelInfo(KernelInfo *kernel,
46296771f1e8987fa49f52d4176281a2e8524b8e31cbcristy  const double scaling_factor,const GeometryFlags normalize_flags)
4630cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4631bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
4632cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4633cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4634999bb2c20aa9d42875bb5adba44951988d4ae354anthony  register double
4635999bb2c20aa9d42875bb5adba44951988d4ae354anthony    pos_scale,
4636999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale;
4637999bb2c20aa9d42875bb5adba44951988d4ae354anthony
463846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
46391b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
46401b2bc0a7da432e6e1cc0480280402df213faa940anthony    ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
46411b2bc0a7da432e6e1cc0480280402df213faa940anthony
464246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Normalization of Kernel */
4643999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = 1.0;
4644999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&NormalizeValue) != 0 ) {
4645999bb2c20aa9d42875bb5adba44951988d4ae354anthony    if ( fabs(kernel->positive_range + kernel->negative_range) > MagickEpsilon )
4646f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* non-zero-summing kernel (generally positive) */
4647999bb2c20aa9d42875bb5adba44951988d4ae354anthony      pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4648cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    else
4649f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* zero-summing kernel */
4650f4e0031305baeb01c89cfd2842cbbec021883550anthony      pos_scale = kernel->positive_range;
4651999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
465246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Force kernel into a normalized zero-summing kernel */
4653999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4654999bb2c20aa9d42875bb5adba44951988d4ae354anthony    pos_scale = ( fabs(kernel->positive_range) > MagickEpsilon )
4655999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? kernel->positive_range : 1.0;
4656999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale = ( fabs(kernel->negative_range) > MagickEpsilon )
4657999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? -kernel->negative_range : 1.0;
4658999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4659999bb2c20aa9d42875bb5adba44951988d4ae354anthony  else
4660999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale = pos_scale;
4661999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4662999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* finialize scaling_factor for positive and negative components */
4663999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = scaling_factor/pos_scale;
4664999bb2c20aa9d42875bb5adba44951988d4ae354anthony  neg_scale = scaling_factor/neg_scale;
4665cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4666bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4667cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    if ( ! IsNan(kernel->values[i]) )
4668999bb2c20aa9d42875bb5adba44951988d4ae354anthony      kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4669999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4670999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* convolution output range */
4671999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->positive_range *= pos_scale;
4672999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->negative_range *= neg_scale;
4673999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* maximum and minimum values in kernel */
4674999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4675999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4676999bb2c20aa9d42875bb5adba44951988d4ae354anthony
467746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* swap kernel settings if user's scaling factor is negative */
4678999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( scaling_factor < MagickEpsilon ) {
4679999bb2c20aa9d42875bb5adba44951988d4ae354anthony    double t;
4680999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->positive_range;
4681999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->positive_range = kernel->negative_range;
4682999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->negative_range = t;
4683999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->maximum;
4684999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->maximum = kernel->minimum;
4685999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->minimum = 1;
4686999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4687cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4688cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4689cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4690cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4691cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4692cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4693cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4694cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4695cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
469646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S h o w K e r n e l I n f o                                             %
469783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
469883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
469983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
470083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
47024fd27e21043be809d66c8202e779255e5b660d2danthony%  ShowKernelInfo() outputs the details of the given kernel defination to
47034fd27e21043be809d66c8202e779255e5b660d2danthony%  standard error, generally due to a users 'showkernel' option request.
470483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
470583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the ShowKernel method is:
470683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
470757fe7a498c1302232dac8466864e84b12fad0807anthony%      void ShowKernelInfo(const KernelInfo *kernel)
470883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
470983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
471083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
471183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
471283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
471383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
471457fe7a498c1302232dac8466864e84b12fad0807anthonyMagickExport void ShowKernelInfo(const KernelInfo *kernel)
471583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
471657fe7a498c1302232dac8466864e84b12fad0807anthony  const KernelInfo
47177a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *k;
47187a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4719bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
47207a01dcf50ce12cb2a789bedff51e9345f022432eanthony    c, i, u, v;
47217a01dcf50ce12cb2a789bedff51e9345f022432eanthony
47227a01dcf50ce12cb2a789bedff51e9345f022432eanthony  for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
47237a01dcf50ce12cb2a789bedff51e9345f022432eanthony
47245acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Kernel");
47257a01dcf50ce12cb2a789bedff51e9345f022432eanthony    if ( kernel->next != (KernelInfo *) NULL )
47265acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
47275acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, " \"%s",
4728042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy          CommandOptionToMnemonic(MagickKernelOptions, k->type) );
472943c4925e5305a26e48d68f7893e94f55d0831c39anthony    if ( fabs(k->angle) > MagickEpsilon )
47305acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "@%lg", k->angle);
47315acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
47321e604812fad85bb96f757a2393015ae3d061c39acristy      k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
47335acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr,
47347a01dcf50ce12cb2a789bedff51e9345f022432eanthony          " with values from %.*lg to %.*lg\n",
47357a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->minimum,
47367a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->maximum);
47375acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Forming a output range from %.*lg to %.*lg",
47387a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->negative_range,
473946a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range);
474046a369d839971ab627bdb31a93d8bd63e81b65a3anthony    if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
47415acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Zero-Summing)\n");
474246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
47435acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Normalized)\n");
474446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else
47455acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Sum %.*lg)\n",
474646a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range+k->negative_range);
474743c4925e5305a26e48d68f7893e94f55d0831c39anthony    for (i=v=0; v < k->height; v++) {
47485acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
474943c4925e5305a26e48d68f7893e94f55d0831c39anthony      for (u=0; u < k->width; u++, i++)
47507a01dcf50ce12cb2a789bedff51e9345f022432eanthony        if ( IsNan(k->values[i]) )
47515acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
47527a01dcf50ce12cb2a789bedff51e9345f022432eanthony        else
47535acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
47547a01dcf50ce12cb2a789bedff51e9345f022432eanthony              GetMagickPrecision(), k->values[i]);
47555acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr,"\n");
47567a01dcf50ce12cb2a789bedff51e9345f022432eanthony    }
475783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
475883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
4759cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4760cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4761cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4762cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4763cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4764cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
476543c4925e5305a26e48d68f7893e94f55d0831c39anthony%     U n i t y A d d K e r n a l I n f o                                     %
476643c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
476743c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
476843c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
476943c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477043c4925e5305a26e48d68f7893e94f55d0831c39anthony%
477143c4925e5305a26e48d68f7893e94f55d0831c39anthony%  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
477243c4925e5305a26e48d68f7893e94f55d0831c39anthony%  to the given pre-scaled and normalized Kernel.  This in effect adds that
477343c4925e5305a26e48d68f7893e94f55d0831c39anthony%  amount of the original image into the resulting convolution kernel.  This
477443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  value is usually provided by the user as a percentage value in the
477543c4925e5305a26e48d68f7893e94f55d0831c39anthony%  'convolve:scale' setting.
477643c4925e5305a26e48d68f7893e94f55d0831c39anthony%
4777501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  The resulting effect is to convert the defined kernels into blended
4778501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  soft-blurs, unsharp kernels or into sharpening kernels.
477943c4925e5305a26e48d68f7893e94f55d0831c39anthony%
478046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the UnityAdditionKernelInfo method is:
478143c4925e5305a26e48d68f7893e94f55d0831c39anthony%
478243c4925e5305a26e48d68f7893e94f55d0831c39anthony%      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
478343c4925e5305a26e48d68f7893e94f55d0831c39anthony%
478443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  A description of each parameter follows:
478543c4925e5305a26e48d68f7893e94f55d0831c39anthony%
478643c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o kernel: the Morphology/Convolution kernel
478743c4925e5305a26e48d68f7893e94f55d0831c39anthony%
478843c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o scale:
478943c4925e5305a26e48d68f7893e94f55d0831c39anthony%             scaling factor for the unity kernel to be added to
479043c4925e5305a26e48d68f7893e94f55d0831c39anthony%             the given kernel.
479143c4925e5305a26e48d68f7893e94f55d0831c39anthony%
479243c4925e5305a26e48d68f7893e94f55d0831c39anthony*/
479343c4925e5305a26e48d68f7893e94f55d0831c39anthonyMagickExport void UnityAddKernelInfo(KernelInfo *kernel,
479443c4925e5305a26e48d68f7893e94f55d0831c39anthony  const double scale)
479543c4925e5305a26e48d68f7893e94f55d0831c39anthony{
479646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
479746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( kernel->next != (KernelInfo *) NULL)
479846a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel->next, scale);
479943c4925e5305a26e48d68f7893e94f55d0831c39anthony
480046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add the scaled unity kernel to the existing kernel */
480143c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->values[kernel->x+kernel->y*kernel->width] += scale;
480246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(kernel);  /* recalculate the meta-data */
480343c4925e5305a26e48d68f7893e94f55d0831c39anthony
480443c4925e5305a26e48d68f7893e94f55d0831c39anthony  return;
480543c4925e5305a26e48d68f7893e94f55d0831c39anthony}
480643c4925e5305a26e48d68f7893e94f55d0831c39anthony
480743c4925e5305a26e48d68f7893e94f55d0831c39anthony/*
480843c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480943c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
481043c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
481143c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
481243c4925e5305a26e48d68f7893e94f55d0831c39anthony%     Z e r o K e r n e l N a n s                                             %
4813cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4814cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4815cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4816cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4817cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4818cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  ZeroKernelNans() replaces any special 'nan' value that may be present in
4819cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  the kernel with a zero value.  This is typically done when the kernel will
4820cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  be used in special hardware (GPU) convolution processors, to simply
4821cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  matters.
4822cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4823cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  The format of the ZeroKernelNans method is:
4824cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
482546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void ZeroKernelNans (KernelInfo *kernel)
4826cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4827cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4828cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4829cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4830cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4831cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
4832cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate void ZeroKernelNans(KernelInfo *kernel)
4833cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4834bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
4835cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4836cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
483746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
48381b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
48391b2bc0a7da432e6e1cc0480280402df213faa940anthony    ZeroKernelNans(kernel->next);
48401b2bc0a7da432e6e1cc0480280402df213faa940anthony
484143c4925e5305a26e48d68f7893e94f55d0831c39anthony  for (i=0; i < (kernel->width*kernel->height); i++)
4842cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    if ( IsNan(kernel->values[i]) )
4843cc6c836da2a53b6023b716e4973090a6714dc3b0anthony      kernel->values[i] = 0.0;
4844cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4845cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4846cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4847