feature.c revision 05d2ff7ebf21f659f5b11e45afb294e152f4330c
13e2860cb0796fe77659325ec3d540d8766d54f49cristy/*
23e2860cb0796fe77659325ec3d540d8766d54f49cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
43e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
53e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
63e2860cb0796fe77659325ec3d540d8766d54f49cristy%               FFFFF  EEEEE   AAA   TTTTT  U   U  RRRR   EEEEE               %
73e2860cb0796fe77659325ec3d540d8766d54f49cristy%               F      E      A   A    T    U   U  R   R  E                   %
83e2860cb0796fe77659325ec3d540d8766d54f49cristy%               FFF    EEE    AAAAA    T    U   U  RRRR   EEE                 %
93e2860cb0796fe77659325ec3d540d8766d54f49cristy%               F      E      A   A    T    U   U  R R    E                   %
103e2860cb0796fe77659325ec3d540d8766d54f49cristy%               F      EEEEE  A   A    T     UUU   R  R   EEEEE               %
113e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
123e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
133e2860cb0796fe77659325ec3d540d8766d54f49cristy%                      MagickCore Image Feature Methods                       %
143e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
153e2860cb0796fe77659325ec3d540d8766d54f49cristy%                              Software Design                                %
16de984cdc3631106b1cbbb8d3972b76a0fc27e8e8cristy%                                   Cristy                                    %
173e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                 July 1992                                   %
183e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
193e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
20b56bb24a985ca4366713bcd8ffdfacbb48a98a2fcristy%  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
213e2860cb0796fe77659325ec3d540d8766d54f49cristy%  dedicated to making software imaging solutions freely available.           %
223e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
233e2860cb0796fe77659325ec3d540d8766d54f49cristy%  You may not use this file except in compliance with the License.  You may  %
243e2860cb0796fe77659325ec3d540d8766d54f49cristy%  obtain a copy of the License at                                            %
253e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
263e2860cb0796fe77659325ec3d540d8766d54f49cristy%    http://www.imagemagick.org/script/license.php                            %
273e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
283e2860cb0796fe77659325ec3d540d8766d54f49cristy%  Unless required by applicable law or agreed to in writing, software        %
293e2860cb0796fe77659325ec3d540d8766d54f49cristy%  distributed under the License is distributed on an "AS IS" BASIS,          %
303e2860cb0796fe77659325ec3d540d8766d54f49cristy%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
313e2860cb0796fe77659325ec3d540d8766d54f49cristy%  See the License for the specific language governing permissions and        %
323e2860cb0796fe77659325ec3d540d8766d54f49cristy%  limitations under the License.                                             %
333e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
343e2860cb0796fe77659325ec3d540d8766d54f49cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
353e2860cb0796fe77659325ec3d540d8766d54f49cristy%
363e2860cb0796fe77659325ec3d540d8766d54f49cristy%
373e2860cb0796fe77659325ec3d540d8766d54f49cristy%
383e2860cb0796fe77659325ec3d540d8766d54f49cristy*/
393e2860cb0796fe77659325ec3d540d8766d54f49cristy
403e2860cb0796fe77659325ec3d540d8766d54f49cristy/*
413e2860cb0796fe77659325ec3d540d8766d54f49cristy  Include declarations.
423e2860cb0796fe77659325ec3d540d8766d54f49cristy*/
434c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/studio.h"
444c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/animate.h"
450f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy#include "MagickCore/artifact.h"
464c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/blob.h"
474c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/blob-private.h"
484c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/cache.h"
494c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/cache-private.h"
504c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/cache-view.h"
5131e75937db91fd53e7deb9e3544646d0459f2196cristy#include "MagickCore/channel.h"
524c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/client.h"
534c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/color.h"
544c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/color-private.h"
554c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/colorspace.h"
564c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/colorspace-private.h"
574c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/composite.h"
584c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/composite-private.h"
594c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/compress.h"
604c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/constitute.h"
614c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/display.h"
624c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/draw.h"
634c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/enhance.h"
644c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception.h"
654c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception-private.h"
664c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/feature.h"
674c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/gem.h"
684c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/geometry.h"
694c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/list.h"
704c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image-private.h"
714c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/magic.h"
724c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/magick.h"
73c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#include "MagickCore/matrix.h"
744c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/memory_.h"
754c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/module.h"
764c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/monitor.h"
774c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/monitor-private.h"
78c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#include "MagickCore/morphology-private.h"
794c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/option.h"
804c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/paint.h"
814c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel-accessor.h"
824c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/profile.h"
830f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy#include "MagickCore/property.h"
844c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/quantize.h"
854c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/quantum-private.h"
864c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/random_.h"
87ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy#include "MagickCore/resource_.h"
884c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/segment.h"
894c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/semaphore.h"
904c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/signature-private.h"
914c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string_.h"
924c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/thread-private.h"
934c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/timer.h"
944c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/utility.h"
954c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/version.h"
963e2860cb0796fe77659325ec3d540d8766d54f49cristy
973e2860cb0796fe77659325ec3d540d8766d54f49cristy/*
983e2860cb0796fe77659325ec3d540d8766d54f49cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
993e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
1003e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
1013e2860cb0796fe77659325ec3d540d8766d54f49cristy%                                                                             %
102c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%     C a n n y E d g e I m a g e                                             %
103c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
104c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
105c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
106c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
108c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%  CannyEdgeImage() uses a multi-stage algorithm to detect a wide range of
109c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%  edges in images.
110c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
111233483c36e4f75e4337766b87cae1ab13d8b92a8cristy%  The format of the CannyEdgeImage method is:
112c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
113c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%      Image *CannyEdgeImage(const Image *image,const double radius,
114c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%        const double sigma,const double lower_percent,
115c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%        const double upper_percent,ExceptionInfo *exception)
116c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
117c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%  A description of each parameter follows:
118c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
119c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o image: the image.
120c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
121c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o radius: the radius of the gaussian smoothing filter.
122c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
123c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o sigma: the sigma of the gaussian smoothing filter.
124c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
125c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o lower_precent: percentage of edge pixels in the lower threshold.
126c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
127c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o upper_percent: percentage of edge pixels in the upper threshold.
128c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
129c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o exception: return any errors or warnings in this structure.
130c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
131c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy*/
132c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
133c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristytypedef struct _CannyInfo
134c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy{
135c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  double
136c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    magnitude,
137c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    intensity;
138c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
139c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  int
140c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    orientation;
141c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
142c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  ssize_t
143c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    x,
144c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    y;
145c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy} CannyInfo;
146c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
147c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristystatic inline MagickBooleanType IsAuthenticPixel(const Image *image,
148c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  const ssize_t x,const ssize_t y)
149c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy{
150c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if ((x < 0) || (x >= (ssize_t) image->columns))
151c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    return(MagickFalse);
152c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if ((y < 0) || (y >= (ssize_t) image->rows))
153c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    return(MagickFalse);
154c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  return(MagickTrue);
155c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy}
156c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
157c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristystatic MagickBooleanType TraceEdges(Image *edge_image,CacheView *edge_view,
158c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  MatrixInfo *canny_cache,const ssize_t x,const ssize_t y,
159c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  const double lower_threshold,ExceptionInfo *exception)
160c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy{
161c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  CannyInfo
162c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    edge,
163c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    pixel;
164c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
165c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  MagickBooleanType
166c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    status;
167c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
168c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  register Quantum
169c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    *q;
170c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
171c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  register ssize_t
172c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    i;
173c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
174c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  q=GetCacheViewAuthenticPixels(edge_view,x,y,1,1,exception);
175c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (q == (Quantum *) NULL)
176c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    return(MagickFalse);
177c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  *q=QuantumRange;
178c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  status=SyncCacheViewAuthenticPixels(edge_view,exception);
179c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (status == MagickFalse)
180c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    return(MagickFalse);;
181c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (GetMatrixElement(canny_cache,0,0,&edge) == MagickFalse)
182c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    return(MagickFalse);
183c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge.x=x;
184c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge.y=y;
185c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (SetMatrixElement(canny_cache,0,0,&edge) == MagickFalse)
186c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    return(MagickFalse);
187c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  for (i=1; i != 0; )
188c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  {
189c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    ssize_t
190c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      v;
191c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
192c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    i--;
193c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    status=GetMatrixElement(canny_cache,i,0,&edge);
194c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (status == MagickFalse)
195c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      return(MagickFalse);
196c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    for (v=(-1); v <= 1; v++)
197c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
198c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      ssize_t
199c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        u;
200c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
201c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      for (u=(-1); u <= 1; u++)
202c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
203c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if ((u == 0) && (v == 0))
204c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          continue;
205c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if (IsAuthenticPixel(edge_image,edge.x+u,edge.y+v) == MagickFalse)
206c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          continue;
207c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        /*
208c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          Not an edge if gradient value is below the lower threshold.
209c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        */
210c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        q=GetCacheViewAuthenticPixels(edge_view,edge.x+u,edge.y+v,1,1,
211c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          exception);
212c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if (q == (Quantum *) NULL)
213c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          return(MagickFalse);
214c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        status=GetMatrixElement(canny_cache,edge.x+u,edge.y+v,&pixel);
215c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if (status == MagickFalse)
216c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          return(MagickFalse);
217c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if ((GetPixelIntensity(edge_image,q) == 0.0) &&
218c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            (pixel.intensity >= lower_threshold))
219c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          {
220c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            *q=QuantumRange;
221c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            status=SyncCacheViewAuthenticPixels(edge_view,exception);
222c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            if (status == MagickFalse)
223c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              return(MagickFalse);
224c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            edge.x+=u;
225c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            edge.y+=v;
226c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            status=SetMatrixElement(canny_cache,i,0,&edge);
227c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            if (status == MagickFalse)
228c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              return(MagickFalse);
229c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            i++;
230c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          }
231c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
232c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
233c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  }
234c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  return(MagickTrue);
235c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy}
236c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
237c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristyMagickExport Image *CannyEdgeImage(const Image *image,const double radius,
238c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  const double sigma,const double lower_percent,const double upper_percent,
239c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  ExceptionInfo *exception)
240c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy{
2418fbcf1ad6a1d07a92ef39435f679143697be4edacristy#define CannyEdgeImageTag  "CannyEdge/Image"
2428fbcf1ad6a1d07a92ef39435f679143697be4edacristy
243c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  CacheView
244c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    *edge_view;
245c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
246c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  CannyInfo
247aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    element;
248c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
249c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  char
250151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    geometry[MagickPathExtent];
251c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
252c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  double
253c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    lower_threshold,
254c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    max,
255c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    min,
256c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    upper_threshold;
257c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
258c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  Image
259c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    *edge_image;
260c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
261c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  KernelInfo
262c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    *kernel_info;
263c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
264c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  MagickBooleanType
265c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    status;
266c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
2678fbcf1ad6a1d07a92ef39435f679143697be4edacristy  MagickOffsetType
2688fbcf1ad6a1d07a92ef39435f679143697be4edacristy    progress;
2698fbcf1ad6a1d07a92ef39435f679143697be4edacristy
270c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  MatrixInfo
271c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    *canny_cache;
272c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
273c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  ssize_t
274c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    y;
275c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
276c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  assert(image != (const Image *) NULL);
277e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
278c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (image->debug != MagickFalse)
279c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
280c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  assert(exception != (ExceptionInfo *) NULL);
281e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
282c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
283c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Filter out noise.
284c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
285151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy  (void) FormatLocaleString(geometry,MagickPathExtent,
286c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
2872c57b74e160f9b605d74dec24081309f28b83899cristy  kernel_info=AcquireKernelInfo(geometry,exception);
288c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (kernel_info == (KernelInfo *) NULL)
289c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
290c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
291c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    UndefinedCompositeOp,0.0,exception);
292c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  kernel_info=DestroyKernelInfo(kernel_info);
293c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (edge_image == (Image *) NULL)
294c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    return((Image *) NULL);
295c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (SetImageColorspace(edge_image,GRAYColorspace,exception) == MagickFalse)
296c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
297c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      edge_image=DestroyImage(edge_image);
298c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      return((Image *) NULL);
299c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
30031e75937db91fd53e7deb9e3544646d0459f2196cristy  (void) SetImageAlphaChannel(edge_image,OffAlphaChannel,exception);
301c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
302c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Find the intensity gradient of the image.
303c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
304c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows,
305c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    sizeof(CannyInfo),exception);
306c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (canny_cache == (MatrixInfo *) NULL)
307c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
308c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      edge_image=DestroyImage(edge_image);
309c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      return((Image *) NULL);
310c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
311c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  status=MagickTrue;
312c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=AcquireVirtualCacheView(edge_image,exception);
313c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
314c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  #pragma omp parallel for schedule(static,4) shared(status) \
315c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    magick_threads(edge_image,edge_image,edge_image->rows,1)
316c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#endif
317c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  for (y=0; y < (ssize_t) edge_image->rows; y++)
318c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  {
319c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register const Quantum
32005d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
321c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
322c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register ssize_t
323c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      x;
324c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
325c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (status == MagickFalse)
326c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      continue;
327c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns+1,2,
328c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      exception);
329c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (p == (const Quantum *) NULL)
330c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
331c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        status=MagickFalse;
332c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
333c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
334c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    for (x=0; x < (ssize_t) edge_image->columns; x++)
335c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
336c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      CannyInfo
337c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        pixel;
338c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
339c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      double
340c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        dx,
341c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        dy;
342c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
343c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      register const Quantum
34405d2ff7ebf21f659f5b11e45afb294e152f4330cdirk        *magick_restrict kernel_pixels;
345c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
346c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      ssize_t
347c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        v;
348c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
349c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      static double
350c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        Gx[2][2] =
351c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
352c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          { -1.0,  +1.0 },
353c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          { -1.0,  +1.0 }
354c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        },
355c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        Gy[2][2] =
356c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
357c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          { +1.0, +1.0 },
358c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          { -1.0, -1.0 }
359c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        };
360c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
361c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
362c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      dx=0.0;
363c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      dy=0.0;
364c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      kernel_pixels=p;
365c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      for (v=0; v < 2; v++)
366c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
367c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        ssize_t
368c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          u;
369c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
370c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        for (u=0; u < 2; u++)
371c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
372c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          double
373c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            intensity;
374c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
375c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          intensity=GetPixelIntensity(edge_image,kernel_pixels+u);
376c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          dx+=0.5*Gx[v][u]*intensity;
377c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          dy+=0.5*Gy[v][u]*intensity;
378c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
379c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        kernel_pixels+=edge_image->columns+1;
380c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
381c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      pixel.magnitude=hypot(dx,dy);
382c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      pixel.orientation=0;
383c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if (fabs(dx) > MagickEpsilon)
384c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
385c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          double
386c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            slope;
387c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
388c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          slope=dy/dx;
389c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          if (slope < 0.0)
390c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            {
391c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              if (slope < -2.41421356237)
392c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                pixel.orientation=0;
393c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              else
394c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                if (slope < -0.414213562373)
395c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                  pixel.orientation=1;
396c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                else
397c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                  pixel.orientation=2;
398c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            }
399c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          else
400c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            {
401c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              if (slope > 2.41421356237)
402c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                pixel.orientation=0;
403c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              else
404c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                if (slope > 0.414213562373)
405c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                  pixel.orientation=3;
406c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                else
407c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                  pixel.orientation=2;
408c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            }
409c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
410c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse)
411c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
412c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      p+=GetPixelChannels(edge_image);
413c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
414c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  }
415c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=DestroyCacheView(edge_view);
416c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
417c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Non-maxima suppression, remove pixels that are not considered to be part
418c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    of an edge.
419c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
4208fbcf1ad6a1d07a92ef39435f679143697be4edacristy  progress=0;
421aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  (void) GetMatrixElement(canny_cache,0,0,&element);
422aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  max=element.intensity;
423aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  min=element.intensity;
424c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=AcquireAuthenticCacheView(edge_image,exception);
425c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
426c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  #pragma omp parallel for schedule(static,4) shared(status) \
427c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    magick_threads(edge_image,edge_image,edge_image->rows,1)
428c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#endif
429c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  for (y=0; y < (ssize_t) edge_image->rows; y++)
430c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  {
431c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register Quantum
43205d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict q;
433c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
434c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register ssize_t
435c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      x;
436c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
437c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (status == MagickFalse)
438c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      continue;
439c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    q=GetCacheViewAuthenticPixels(edge_view,0,y,edge_image->columns,1,
440c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      exception);
441c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (q == (Quantum *) NULL)
442c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
443c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        status=MagickFalse;
444c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
445c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
446c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    for (x=0; x < (ssize_t) edge_image->columns; x++)
447c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
448c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      CannyInfo
449c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        alpha_pixel,
450c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        beta_pixel,
451c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        pixel;
452c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
453c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      (void) GetMatrixElement(canny_cache,x,y,&pixel);
454c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      switch (pixel.orientation)
455c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
456c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        case 0:
457b6ecf44155ec6e79fac7d9c51ed5f56962f6eda4dirk        default:
458c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
459c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          /*
460c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            0 degrees, north and south.
461c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          */
462c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel);
463c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel);
464c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          break;
465c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
466c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        case 1:
467c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
468c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          /*
469c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            45 degrees, northwest and southeast.
470c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          */
471c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x-1,y-1,&alpha_pixel);
472c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x+1,y+1,&beta_pixel);
473c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          break;
474c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
475c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        case 2:
476c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
477c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          /*
478c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            90 degrees, east and west.
479c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          */
480c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel);
481c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel);
482c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          break;
483c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
484c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        case 3:
485c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
486c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          /*
487c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            135 degrees, northeast and southwest.
488c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          */
489c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel);
490c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel);
491c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          break;
492c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
493c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
494c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      pixel.intensity=pixel.magnitude;
495c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if ((pixel.magnitude < alpha_pixel.magnitude) ||
496c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (pixel.magnitude < beta_pixel.magnitude))
497c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        pixel.intensity=0;
498c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      (void) SetMatrixElement(canny_cache,x,y,&pixel);
499c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
500c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      #pragma omp critical (MagickCore_CannyEdgeImage)
501c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#endif
502c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
503c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if (pixel.intensity < min)
504c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          min=pixel.intensity;
505c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if (pixel.intensity > max)
506c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          max=pixel.intensity;
507c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
508c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      *q=0;
509c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      q+=GetPixelChannels(edge_image);
510c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
511c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse)
512c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      status=MagickFalse;
513c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  }
514c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=DestroyCacheView(edge_view);
515c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
516c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Estimate hysteresis threshold.
517c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
518c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  lower_threshold=lower_percent*(max-min)+min;
519c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  upper_threshold=upper_percent*(max-min)+min;
520c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
521c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Hysteresis threshold.
522c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
523c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=AcquireAuthenticCacheView(edge_image,exception);
524c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  for (y=0; y < (ssize_t) edge_image->rows; y++)
525c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  {
526c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register ssize_t
527c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      x;
528c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
529c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (status == MagickFalse)
530c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      continue;
531c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    for (x=0; x < (ssize_t) edge_image->columns; x++)
532c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
533c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      CannyInfo
534c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        pixel;
535c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
536c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      register const Quantum
53705d2ff7ebf21f659f5b11e45afb294e152f4330cdirk        *magick_restrict p;
538c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
539c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      /*
540c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        Edge if pixel gradient higher than upper threshold.
541c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      */
542c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      p=GetCacheViewVirtualPixels(edge_view,x,y,1,1,exception);
543c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if (p == (const Quantum *) NULL)
544c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
545c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      status=GetMatrixElement(canny_cache,x,y,&pixel);
546c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if (status == MagickFalse)
547c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
548c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if ((GetPixelIntensity(edge_image,p) == 0.0) &&
549c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (pixel.intensity >= upper_threshold))
550c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        status=TraceEdges(edge_image,edge_view,canny_cache,x,y,lower_threshold,
551c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          exception);
552c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
5538fbcf1ad6a1d07a92ef39435f679143697be4edacristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
5548fbcf1ad6a1d07a92ef39435f679143697be4edacristy      {
5558fbcf1ad6a1d07a92ef39435f679143697be4edacristy        MagickBooleanType
5568fbcf1ad6a1d07a92ef39435f679143697be4edacristy          proceed;
5578fbcf1ad6a1d07a92ef39435f679143697be4edacristy
5588fbcf1ad6a1d07a92ef39435f679143697be4edacristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
5598fbcf1ad6a1d07a92ef39435f679143697be4edacristy        #pragma omp critical (MagickCore_CannyEdgeImage)
5608fbcf1ad6a1d07a92ef39435f679143697be4edacristy#endif
5618fbcf1ad6a1d07a92ef39435f679143697be4edacristy        proceed=SetImageProgress(image,CannyEdgeImageTag,progress++,
5628fbcf1ad6a1d07a92ef39435f679143697be4edacristy          image->rows);
5638fbcf1ad6a1d07a92ef39435f679143697be4edacristy        if (proceed == MagickFalse)
5648fbcf1ad6a1d07a92ef39435f679143697be4edacristy          status=MagickFalse;
5658fbcf1ad6a1d07a92ef39435f679143697be4edacristy      }
566c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  }
567c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=DestroyCacheView(edge_view);
568c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
569c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Free resources.
570c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
571c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  canny_cache=DestroyMatrixInfo(canny_cache);
572c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  return(edge_image);
573c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy}
574c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
575c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy/*
576c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
578c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
579c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
5802fc10e5aedab9144531a4668dc7526e3caf514e1cristy%   G e t I m a g e F e a t u r e s                                           %
581c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
582c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
583c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
584c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
585c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
5862fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  GetImageFeatures() returns features for each channel in the image in
5872fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  each of four directions (horizontal, vertical, left and right diagonals)
5882fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  for the specified distance.  The features include the angular second
5892fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  moment, contrast, correlation, sum of squares: variance, inverse difference
5902fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  moment, sum average, sum varience, sum entropy, entropy, difference variance,%  difference entropy, information measures of correlation 1, information
5912fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  measures of correlation 2, and maximum correlation coefficient.  You can
5922fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  access the red channel contrast, for example, like this:
5932fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
5942fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      channel_features=GetImageFeatures(image,1,exception);
5952fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      contrast=channel_features[RedPixelChannel].contrast[0];
5962fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
5972fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  Use MagickRelinquishMemory() to free the features buffer.
598c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
5992fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  The format of the GetImageFeatures method is:
600c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
6012fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      ChannelFeatures *GetImageFeatures(const Image *image,
6022fc10e5aedab9144531a4668dc7526e3caf514e1cristy%        const size_t distance,ExceptionInfo *exception)
603c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
604c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%  A description of each parameter follows:
605c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
606c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o image: the image.
607c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
6082fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o distance: the distance.
609c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
610c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o exception: return any errors or warnings in this structure.
611c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
612c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy*/
6130f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6142fc10e5aedab9144531a4668dc7526e3caf514e1cristystatic inline double MagickLog10(const double x)
615c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy{
6162fc10e5aedab9144531a4668dc7526e3caf514e1cristy#define Log10Epsilon  (1.0e-11)
6170f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6182fc10e5aedab9144531a4668dc7526e3caf514e1cristy if (fabs(x) < Log10Epsilon)
6192fc10e5aedab9144531a4668dc7526e3caf514e1cristy   return(log10(Log10Epsilon));
6202fc10e5aedab9144531a4668dc7526e3caf514e1cristy return(log10(fabs(x)));
6212fc10e5aedab9144531a4668dc7526e3caf514e1cristy}
6220f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6232fc10e5aedab9144531a4668dc7526e3caf514e1cristyMagickExport ChannelFeatures *GetImageFeatures(const Image *image,
6242fc10e5aedab9144531a4668dc7526e3caf514e1cristy  const size_t distance,ExceptionInfo *exception)
6252fc10e5aedab9144531a4668dc7526e3caf514e1cristy{
6262fc10e5aedab9144531a4668dc7526e3caf514e1cristy  typedef struct _ChannelStatistics
6272fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
6282fc10e5aedab9144531a4668dc7526e3caf514e1cristy    PixelInfo
6292fc10e5aedab9144531a4668dc7526e3caf514e1cristy      direction[4];  /* horizontal, vertical, left and right diagonals */
6302fc10e5aedab9144531a4668dc7526e3caf514e1cristy  } ChannelStatistics;
6310f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6322fc10e5aedab9144531a4668dc7526e3caf514e1cristy  CacheView
6332fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *image_view;
6340f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6352fc10e5aedab9144531a4668dc7526e3caf514e1cristy  ChannelFeatures
6362fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *channel_features;
6370f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6382fc10e5aedab9144531a4668dc7526e3caf514e1cristy  ChannelStatistics
6392fc10e5aedab9144531a4668dc7526e3caf514e1cristy    **cooccurrence,
6402fc10e5aedab9144531a4668dc7526e3caf514e1cristy    correlation,
6412fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *density_x,
6422fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *density_xy,
6432fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *density_y,
6442fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_x,
6452fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_xy,
6462fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_xy1,
6472fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_xy2,
6482fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_y,
6492fc10e5aedab9144531a4668dc7526e3caf514e1cristy    mean,
6502fc10e5aedab9144531a4668dc7526e3caf514e1cristy    **Q,
6512fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *sum,
6522fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sum_squares,
6532fc10e5aedab9144531a4668dc7526e3caf514e1cristy    variance;
6540f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6552fc10e5aedab9144531a4668dc7526e3caf514e1cristy  PixelPacket
6562fc10e5aedab9144531a4668dc7526e3caf514e1cristy    gray,
6572fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *grays;
6580f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6590f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  MagickBooleanType
6600f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    status;
6610f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6622fc10e5aedab9144531a4668dc7526e3caf514e1cristy  register ssize_t
663aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    i,
664aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    r;
6650f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6662fc10e5aedab9144531a4668dc7526e3caf514e1cristy  size_t
6672fc10e5aedab9144531a4668dc7526e3caf514e1cristy    length;
6680f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6692fc10e5aedab9144531a4668dc7526e3caf514e1cristy  unsigned int
6702fc10e5aedab9144531a4668dc7526e3caf514e1cristy    number_grays;
6710f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6722fc10e5aedab9144531a4668dc7526e3caf514e1cristy  assert(image != (Image *) NULL);
673e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
6740f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  if (image->debug != MagickFalse)
6750f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
6762fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
6772fc10e5aedab9144531a4668dc7526e3caf514e1cristy    return((ChannelFeatures *) NULL);
6782fc10e5aedab9144531a4668dc7526e3caf514e1cristy  length=CompositeChannels+1UL;
6792fc10e5aedab9144531a4668dc7526e3caf514e1cristy  channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
6802fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*channel_features));
6812fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (channel_features == (ChannelFeatures *) NULL)
6822fc10e5aedab9144531a4668dc7526e3caf514e1cristy    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
6832fc10e5aedab9144531a4668dc7526e3caf514e1cristy  (void) ResetMagickMemory(channel_features,0,length*
6842fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*channel_features));
6850f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  /*
6862fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Form grays.
6870f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  */
6882fc10e5aedab9144531a4668dc7526e3caf514e1cristy  grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
6892fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (grays == (PixelPacket *) NULL)
6902fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
6912fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
6922fc10e5aedab9144531a4668dc7526e3caf514e1cristy        channel_features);
6932fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (void) ThrowMagickException(exception,GetMagickModule(),
6942fc10e5aedab9144531a4668dc7526e3caf514e1cristy        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
6952fc10e5aedab9144531a4668dc7526e3caf514e1cristy      return(channel_features);
6962fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
6972fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i <= (ssize_t) MaxMap; i++)
6982fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
6992fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].red=(~0U);
7002fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].green=(~0U);
7012fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].blue=(~0U);
7022fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].alpha=(~0U);
7032fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].black=(~0U);
7042fc10e5aedab9144531a4668dc7526e3caf514e1cristy  }
7050f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  status=MagickTrue;
7060f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  image_view=AcquireVirtualCacheView(image,exception);
7072fc10e5aedab9144531a4668dc7526e3caf514e1cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
7082fc10e5aedab9144531a4668dc7526e3caf514e1cristy  #pragma omp parallel for schedule(static,4) shared(status) \
7092fc10e5aedab9144531a4668dc7526e3caf514e1cristy    magick_threads(image,image,image->rows,1)
7102fc10e5aedab9144531a4668dc7526e3caf514e1cristy#endif
711aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  for (r=0; r < (ssize_t) image->rows; r++)
7120f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  {
7130f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    register const Quantum
71405d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
7150f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
7160f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    register ssize_t
7170f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      x;
7180f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
7190f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    if (status == MagickFalse)
7200f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      continue;
721aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    p=GetCacheViewVirtualPixels(image_view,0,r,image->columns,1,exception);
7222fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (p == (const Quantum *) NULL)
7230f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      {
7240f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy        status=MagickFalse;
7250f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy        continue;
7260f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      }
7270f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    for (x=0; x < (ssize_t) image->columns; x++)
7280f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    {
7292fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[ScaleQuantumToMap(GetPixelRed(image,p))].red=
7302fc10e5aedab9144531a4668dc7526e3caf514e1cristy        ScaleQuantumToMap(GetPixelRed(image,p));
7312fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green=
7322fc10e5aedab9144531a4668dc7526e3caf514e1cristy        ScaleQuantumToMap(GetPixelGreen(image,p));
7332fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue=
7342fc10e5aedab9144531a4668dc7526e3caf514e1cristy        ScaleQuantumToMap(GetPixelBlue(image,p));
7352fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (image->colorspace == CMYKColorspace)
7362fc10e5aedab9144531a4668dc7526e3caf514e1cristy        grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black=
7372fc10e5aedab9144531a4668dc7526e3caf514e1cristy          ScaleQuantumToMap(GetPixelBlack(image,p));
73817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
7392fc10e5aedab9144531a4668dc7526e3caf514e1cristy        grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha=
7402fc10e5aedab9144531a4668dc7526e3caf514e1cristy          ScaleQuantumToMap(GetPixelAlpha(image,p));
7410f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      p+=GetPixelChannels(image);
7420f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    }
7430f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  }
7440f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  image_view=DestroyCacheView(image_view);
7450f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  if (status == MagickFalse)
7460f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    {
7472fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays=(PixelPacket *) RelinquishMagickMemory(grays);
7482fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
7492fc10e5aedab9144531a4668dc7526e3caf514e1cristy        channel_features);
7502fc10e5aedab9144531a4668dc7526e3caf514e1cristy      return(channel_features);
7510f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    }
7522fc10e5aedab9144531a4668dc7526e3caf514e1cristy  (void) ResetMagickMemory(&gray,0,sizeof(gray));
7532fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i <= (ssize_t) MaxMap; i++)
7542fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
7552fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (grays[i].red != ~0U)
7562fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[gray.red++].red=grays[i].red;
7572fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (grays[i].green != ~0U)
7582fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[gray.green++].green=grays[i].green;
7592fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (grays[i].blue != ~0U)
7602fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[gray.blue++].blue=grays[i].blue;
7612fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (image->colorspace == CMYKColorspace)
7622fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (grays[i].black != ~0U)
7632fc10e5aedab9144531a4668dc7526e3caf514e1cristy        grays[gray.black++].black=grays[i].black;
76417f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
7652fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (grays[i].alpha != ~0U)
7662fc10e5aedab9144531a4668dc7526e3caf514e1cristy        grays[gray.alpha++].alpha=grays[i].alpha;
7672fc10e5aedab9144531a4668dc7526e3caf514e1cristy  }
7680f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  /*
7692fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Allocate spatial dependence matrix.
7700f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  */
7712fc10e5aedab9144531a4668dc7526e3caf514e1cristy  number_grays=gray.red;
7722fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (gray.green > number_grays)
7732fc10e5aedab9144531a4668dc7526e3caf514e1cristy    number_grays=gray.green;
7742fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (gray.blue > number_grays)
7752fc10e5aedab9144531a4668dc7526e3caf514e1cristy    number_grays=gray.blue;
7762fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (image->colorspace == CMYKColorspace)
7772fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (gray.black > number_grays)
7782fc10e5aedab9144531a4668dc7526e3caf514e1cristy      number_grays=gray.black;
77917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy  if (image->alpha_trait != UndefinedPixelTrait)
7802fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (gray.alpha > number_grays)
7812fc10e5aedab9144531a4668dc7526e3caf514e1cristy      number_grays=gray.alpha;
7822fc10e5aedab9144531a4668dc7526e3caf514e1cristy  cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
7832fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*cooccurrence));
7842fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
7852fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*density_x));
7862fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
7872fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*density_xy));
7882fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
7892fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*density_y));
7902fc10e5aedab9144531a4668dc7526e3caf514e1cristy  Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q));
7912fc10e5aedab9144531a4668dc7526e3caf514e1cristy  sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
7922fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if ((cooccurrence == (ChannelStatistics **) NULL) ||
7932fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (density_x == (ChannelStatistics *) NULL) ||
7942fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (density_xy == (ChannelStatistics *) NULL) ||
7952fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (density_y == (ChannelStatistics *) NULL) ||
7962fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (Q == (ChannelStatistics **) NULL) ||
7972fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (sum == (ChannelStatistics *) NULL))
798f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    {
799ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (Q != (ChannelStatistics **) NULL)
800ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        {
801bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for (i=0; i < (ssize_t) number_grays; i++)
802ffa10d0fe53ef0f0db63ee506c52695595976b99cristy            Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
803ffa10d0fe53ef0f0db63ee506c52695595976b99cristy          Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
804ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        }
805ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (sum != (ChannelStatistics *) NULL)
806ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
807ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (density_y != (ChannelStatistics *) NULL)
808ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
809ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (density_xy != (ChannelStatistics *) NULL)
810ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
811ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (density_x != (ChannelStatistics *) NULL)
812ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
813ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (cooccurrence != (ChannelStatistics **) NULL)
814ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        {
815bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for (i=0; i < (ssize_t) number_grays; i++)
816ffa10d0fe53ef0f0db63ee506c52695595976b99cristy            cooccurrence[i]=(ChannelStatistics *)
817ffa10d0fe53ef0f0db63ee506c52695595976b99cristy              RelinquishMagickMemory(cooccurrence[i]);
818ffa10d0fe53ef0f0db63ee506c52695595976b99cristy          cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(
819ffa10d0fe53ef0f0db63ee506c52695595976b99cristy            cooccurrence);
820ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        }
821101ab708b0574518ac5715da4d3915400e9df79acristy      grays=(PixelPacket *) RelinquishMagickMemory(grays);
822f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
823f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        channel_features);
824e18977973bff5866de9aa6ed097aea40e27570d6cristy      (void) ThrowMagickException(exception,GetMagickModule(),
825efe601ce9ea5ad34ad0e8ad6e61d9be9b148b2a3cristy        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
826f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      return(channel_features);
827f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    }
828ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
82977173e5c9ab59c80128f74d92b01da7e8a64de8dcristy  (void) ResetMagickMemory(density_x,0,2*(number_grays+1)*sizeof(*density_x));
83077173e5c9ab59c80128f74d92b01da7e8a64de8dcristy  (void) ResetMagickMemory(density_xy,0,2*(number_grays+1)*sizeof(*density_xy));
83177173e5c9ab59c80128f74d92b01da7e8a64de8dcristy  (void) ResetMagickMemory(density_y,0,2*(number_grays+1)*sizeof(*density_y));
832ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&mean,0,sizeof(mean));
833ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
834ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
835ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(density_xy,0,2*number_grays*sizeof(*density_xy));
836ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_x,0,sizeof(entropy_x));
837ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_xy,0,sizeof(entropy_xy));
838ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_xy1,0,sizeof(entropy_xy1));
839ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_xy2,0,sizeof(entropy_xy2));
840ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_y,0,sizeof(entropy_y));
841ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&variance,0,sizeof(variance));
842bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) number_grays; i++)
843f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  {
8447396d88d575791c136bc6d060e613bdfbfb58f95cristy    cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
8457396d88d575791c136bc6d060e613bdfbfb58f95cristy      sizeof(**cooccurrence));
846ffa10d0fe53ef0f0db63ee506c52695595976b99cristy    Q[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(**Q));
847ffa10d0fe53ef0f0db63ee506c52695595976b99cristy    if ((cooccurrence[i] == (ChannelStatistics *) NULL) ||
848ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        (Q[i] == (ChannelStatistics *) NULL))
849f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      break;
8507396d88d575791c136bc6d060e613bdfbfb58f95cristy    (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
8513749be456a491c22c19b273d1871e896bd8eae79cristy      sizeof(**cooccurrence));
8523749be456a491c22c19b273d1871e896bd8eae79cristy    (void) ResetMagickMemory(Q[i],0,number_grays*sizeof(**Q));
853f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  }
854bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if (i < (ssize_t) number_grays)
855f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    {
856f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      for (i--; i >= 0; i--)
857ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      {
858ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        if (Q[i] != (ChannelStatistics *) NULL)
859ffa10d0fe53ef0f0db63ee506c52695595976b99cristy          Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
860ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        if (cooccurrence[i] != (ChannelStatistics *) NULL)
861ffa10d0fe53ef0f0db63ee506c52695595976b99cristy          cooccurrence[i]=(ChannelStatistics *)
862ffa10d0fe53ef0f0db63ee506c52695595976b99cristy            RelinquishMagickMemory(cooccurrence[i]);
863ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      }
864ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
8657396d88d575791c136bc6d060e613bdfbfb58f95cristy      cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
866ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
867ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
868ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
869ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
870101ab708b0574518ac5715da4d3915400e9df79acristy      grays=(PixelPacket *) RelinquishMagickMemory(grays);
871f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
872f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        channel_features);
873e18977973bff5866de9aa6ed097aea40e27570d6cristy      (void) ThrowMagickException(exception,GetMagickModule(),
874efe601ce9ea5ad34ad0e8ad6e61d9be9b148b2a3cristy        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
875f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      return(channel_features);
876f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    }
877f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  /*
878f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    Initialize spatial dependence matrix.
879f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  */
880f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  status=MagickTrue;
88146ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  image_view=AcquireVirtualCacheView(image,exception);
882aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  for (r=0; r < (ssize_t) image->rows; r++)
883f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  {
8844c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
88505d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
886f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy
887bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
888f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      x;
889f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy
8907e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy    ssize_t
8919d314ff2c17a77996c05413c2013880387e50f0ecristy      offset,
8929d314ff2c17a77996c05413c2013880387e50f0ecristy      u,
8939d314ff2c17a77996c05413c2013880387e50f0ecristy      v;
8947e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy
895f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    if (status == MagickFalse)
896f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      continue;
897aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    p=GetCacheViewVirtualPixels(image_view,-(ssize_t) distance,r,image->columns+
898a1d2bd306531d35abea0440a3008714d36b5c50ccristy      2*distance,distance+2,exception);
8994c08aed51c5899665ade97263692328eea4af106cristy    if (p == (const Quantum *) NULL)
900f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      {
901f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        status=MagickFalse;
902f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        continue;
903f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      }
9048a11cb146177680bfc1b7d89a8cd0c8f2befe468cristy    p+=distance*GetPixelChannels(image);;
905bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) image->columns; x++)
906f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    {
907f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      for (i=0; i < 4; i++)
908f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      {
9097e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        switch (i)
9107e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        {
9117e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          case 0:
912549a37e6cd4593dcb997230cd3584c5afead5552cristy          default:
9137e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9147e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            /*
9157396d88d575791c136bc6d060e613bdfbfb58f95cristy              Horizontal adjacency.
9167e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            */
9177e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            offset=(ssize_t) distance;
9187e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            break;
9197e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
9207e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          case 1:
9217e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9227e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            /*
9237396d88d575791c136bc6d060e613bdfbfb58f95cristy              Vertical adjacency.
9247e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            */
9257396d88d575791c136bc6d060e613bdfbfb58f95cristy            offset=(ssize_t) (image->columns+2*distance);
9267e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            break;
9277e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
9287e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          case 2:
9297e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9307e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            /*
9317396d88d575791c136bc6d060e613bdfbfb58f95cristy              Right diagonal adjacency.
9327e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            */
933d99b096901994c291fdd5b648c5ec9c12d675947cristy            offset=(ssize_t) ((image->columns+2*distance)-distance);
9347e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            break;
9357e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
9367e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          case 3:
9377e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9387e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            /*
9397396d88d575791c136bc6d060e613bdfbfb58f95cristy              Left diagonal adjacency.
9407e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            */
941d99b096901994c291fdd5b648c5ec9c12d675947cristy            offset=(ssize_t) ((image->columns+2*distance)+distance);
9427e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            break;
9437e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
9447e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        }
9457e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        u=0;
9467e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        v=0;
9474c08aed51c5899665ade97263692328eea4af106cristy        while (grays[u].red != ScaleQuantumToMap(GetPixelRed(image,p)))
9487e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          u++;
949ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        while (grays[v].red != ScaleQuantumToMap(GetPixelRed(image,p+offset*GetPixelChannels(image))))
9507e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          v++;
9517396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[u][v].direction[i].red++;
9527396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[v][u].direction[i].red++;
9537e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        u=0;
9547e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        v=0;
9554c08aed51c5899665ade97263692328eea4af106cristy        while (grays[u].green != ScaleQuantumToMap(GetPixelGreen(image,p)))
9567e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          u++;
957ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        while (grays[v].green != ScaleQuantumToMap(GetPixelGreen(image,p+offset*GetPixelChannels(image))))
9587e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          v++;
9597396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[u][v].direction[i].green++;
9607396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[v][u].direction[i].green++;
9617e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        u=0;
9627e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        v=0;
9634c08aed51c5899665ade97263692328eea4af106cristy        while (grays[u].blue != ScaleQuantumToMap(GetPixelBlue(image,p)))
9647e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          u++;
965ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        while (grays[v].blue != ScaleQuantumToMap(GetPixelBlue(image,p+offset*GetPixelChannels(image))))
9667e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          v++;
9677396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[u][v].direction[i].blue++;
9687396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[v][u].direction[i].blue++;
96953a727d91e1edeb3a67fa7d923db42974bba3b64cristy        if (image->colorspace == CMYKColorspace)
9707e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9717e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            u=0;
9727e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            v=0;
9734c08aed51c5899665ade97263692328eea4af106cristy            while (grays[u].black != ScaleQuantumToMap(GetPixelBlack(image,p)))
9747e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy              u++;
975ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            while (grays[v].black != ScaleQuantumToMap(GetPixelBlack(image,p+offset*GetPixelChannels(image))))
9767e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy              v++;
9774c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[u][v].direction[i].black++;
9784c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[v][u].direction[i].black++;
9797e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
98017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
9817e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9827e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            u=0;
9837e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            v=0;
9844c08aed51c5899665ade97263692328eea4af106cristy            while (grays[u].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p)))
9857e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy              u++;
986ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            while (grays[v].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p+offset*GetPixelChannels(image))))
9877e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy              v++;
9884c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[u][v].direction[i].alpha++;
9894c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[v][u].direction[i].alpha++;
9907e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
991f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      }
992ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image);
993f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    }
994f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  }
995101ab708b0574518ac5715da4d3915400e9df79acristy  grays=(PixelPacket *) RelinquishMagickMemory(grays);
996f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  image_view=DestroyCacheView(image_view);
997f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  if (status == MagickFalse)
998f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    {
999bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (i=0; i < (ssize_t) number_grays; i++)
10007396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[i]=(ChannelStatistics *)
10017396d88d575791c136bc6d060e613bdfbfb58f95cristy          RelinquishMagickMemory(cooccurrence[i]);
10027396d88d575791c136bc6d060e613bdfbfb58f95cristy      cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
1003f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
1004f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        channel_features);
1005e18977973bff5866de9aa6ed097aea40e27570d6cristy      (void) ThrowMagickException(exception,GetMagickModule(),
1006efe601ce9ea5ad34ad0e8ad6e61d9be9b148b2a3cristy        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1007f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      return(channel_features);
1008f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    }
1009f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  /*
10107e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy    Normalize spatial dependence matrix.
10117e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy  */
1012bd82207b3816471fba4de916fca7336f38b70493cristy  for (i=0; i < 4; i++)
10137e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy  {
1014549a37e6cd4593dcb997230cd3584c5afead5552cristy    double
1015549a37e6cd4593dcb997230cd3584c5afead5552cristy      normalize;
1016549a37e6cd4593dcb997230cd3584c5afead5552cristy
101753a727d91e1edeb3a67fa7d923db42974bba3b64cristy    register ssize_t
101853a727d91e1edeb3a67fa7d923db42974bba3b64cristy      y;
101953a727d91e1edeb3a67fa7d923db42974bba3b64cristy
1020bd82207b3816471fba4de916fca7336f38b70493cristy    switch (i)
10217e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy    {
1022bd82207b3816471fba4de916fca7336f38b70493cristy      case 0:
1023bd82207b3816471fba4de916fca7336f38b70493cristy      default:
1024bd82207b3816471fba4de916fca7336f38b70493cristy      {
1025bd82207b3816471fba4de916fca7336f38b70493cristy        /*
10267396d88d575791c136bc6d060e613bdfbfb58f95cristy          Horizontal adjacency.
1027bd82207b3816471fba4de916fca7336f38b70493cristy        */
1028bd82207b3816471fba4de916fca7336f38b70493cristy        normalize=2.0*image->rows*(image->columns-distance);
1029bd82207b3816471fba4de916fca7336f38b70493cristy        break;
1030bd82207b3816471fba4de916fca7336f38b70493cristy      }
1031bd82207b3816471fba4de916fca7336f38b70493cristy      case 1:
1032bd82207b3816471fba4de916fca7336f38b70493cristy      {
1033bd82207b3816471fba4de916fca7336f38b70493cristy        /*
10347396d88d575791c136bc6d060e613bdfbfb58f95cristy          Vertical adjacency.
1035bd82207b3816471fba4de916fca7336f38b70493cristy        */
10367396d88d575791c136bc6d060e613bdfbfb58f95cristy        normalize=2.0*(image->rows-distance)*image->columns;
1037bd82207b3816471fba4de916fca7336f38b70493cristy        break;
1038bd82207b3816471fba4de916fca7336f38b70493cristy      }
1039bd82207b3816471fba4de916fca7336f38b70493cristy      case 2:
1040bd82207b3816471fba4de916fca7336f38b70493cristy      {
1041bd82207b3816471fba4de916fca7336f38b70493cristy        /*
10427396d88d575791c136bc6d060e613bdfbfb58f95cristy          Right diagonal adjacency.
1043bd82207b3816471fba4de916fca7336f38b70493cristy        */
10447396d88d575791c136bc6d060e613bdfbfb58f95cristy        normalize=2.0*(image->rows-distance)*(image->columns-distance);
1045bd82207b3816471fba4de916fca7336f38b70493cristy        break;
1046bd82207b3816471fba4de916fca7336f38b70493cristy      }
1047bd82207b3816471fba4de916fca7336f38b70493cristy      case 3:
1048bd82207b3816471fba4de916fca7336f38b70493cristy      {
1049bd82207b3816471fba4de916fca7336f38b70493cristy        /*
10507396d88d575791c136bc6d060e613bdfbfb58f95cristy          Left diagonal adjacency.
1051bd82207b3816471fba4de916fca7336f38b70493cristy        */
1052bd82207b3816471fba4de916fca7336f38b70493cristy        normalize=2.0*(image->rows-distance)*(image->columns-distance);
1053bd82207b3816471fba4de916fca7336f38b70493cristy        break;
1054bd82207b3816471fba4de916fca7336f38b70493cristy      }
1055bd82207b3816471fba4de916fca7336f38b70493cristy    }
10563e3ec3afbb0782697f201cbe30a56794c10dc7efcristy    normalize=PerceptibleReciprocal(normalize);
1057bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (y=0; y < (ssize_t) number_grays; y++)
1058bd82207b3816471fba4de916fca7336f38b70493cristy    {
1059bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
1060bd82207b3816471fba4de916fca7336f38b70493cristy        x;
1061bd82207b3816471fba4de916fca7336f38b70493cristy
1062bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (x=0; x < (ssize_t) number_grays; x++)
10637e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy      {
106453a727d91e1edeb3a67fa7d923db42974bba3b64cristy        cooccurrence[x][y].direction[i].red*=normalize;
106553a727d91e1edeb3a67fa7d923db42974bba3b64cristy        cooccurrence[x][y].direction[i].green*=normalize;
106653a727d91e1edeb3a67fa7d923db42974bba3b64cristy        cooccurrence[x][y].direction[i].blue*=normalize;
1067549a37e6cd4593dcb997230cd3584c5afead5552cristy        if (image->colorspace == CMYKColorspace)
10684c08aed51c5899665ade97263692328eea4af106cristy          cooccurrence[x][y].direction[i].black*=normalize;
106917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
10704c08aed51c5899665ade97263692328eea4af106cristy          cooccurrence[x][y].direction[i].alpha*=normalize;
1071549a37e6cd4593dcb997230cd3584c5afead5552cristy      }
1072549a37e6cd4593dcb997230cd3584c5afead5552cristy    }
1073549a37e6cd4593dcb997230cd3584c5afead5552cristy  }
1074549a37e6cd4593dcb997230cd3584c5afead5552cristy  /*
1075549a37e6cd4593dcb997230cd3584c5afead5552cristy    Compute texture features.
1076549a37e6cd4593dcb997230cd3584c5afead5552cristy  */
10773a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
1078ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(status) \
10795e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,image,number_grays,1)
10803a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy#endif
1081bd82207b3816471fba4de916fca7336f38b70493cristy  for (i=0; i < 4; i++)
1082549a37e6cd4593dcb997230cd3584c5afead5552cristy  {
1083bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
1084bd82207b3816471fba4de916fca7336f38b70493cristy      y;
1085549a37e6cd4593dcb997230cd3584c5afead5552cristy
1086bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (y=0; y < (ssize_t) number_grays; y++)
1087549a37e6cd4593dcb997230cd3584c5afead5552cristy    {
1088bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
1089bd82207b3816471fba4de916fca7336f38b70493cristy        x;
1090bd82207b3816471fba4de916fca7336f38b70493cristy
1091bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (x=0; x < (ssize_t) number_grays; x++)
1092549a37e6cd4593dcb997230cd3584c5afead5552cristy      {
1093549a37e6cd4593dcb997230cd3584c5afead5552cristy        /*
10943a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy          Angular second moment:  measure of homogeneity of the image.
1095549a37e6cd4593dcb997230cd3584c5afead5552cristy        */
1096d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[RedPixelChannel].angular_second_moment[i]+=
10977396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].red*
10987396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].red;
1099d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[GreenPixelChannel].angular_second_moment[i]+=
11007396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].green*
11017396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].green;
1102d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BluePixelChannel].angular_second_moment[i]+=
11037396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].blue*
11047396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].blue;
1105549a37e6cd4593dcb997230cd3584c5afead5552cristy        if (image->colorspace == CMYKColorspace)
1106d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[BlackPixelChannel].angular_second_moment[i]+=
11074c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black*
11084c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
110917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
1110d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[AlphaPixelChannel].angular_second_moment[i]+=
11114c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha*
11124c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
11137396d88d575791c136bc6d060e613bdfbfb58f95cristy        /*
11147396d88d575791c136bc6d060e613bdfbfb58f95cristy          Correlation: measure of linear-dependencies in the image.
11157396d88d575791c136bc6d060e613bdfbfb58f95cristy        */
11167396d88d575791c136bc6d060e613bdfbfb58f95cristy        sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
11177396d88d575791c136bc6d060e613bdfbfb58f95cristy        sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
1118cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
1119cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        if (image->colorspace == CMYKColorspace)
11204c08aed51c5899665ade97263692328eea4af106cristy          sum[y].direction[i].black+=cooccurrence[x][y].direction[i].black;
112117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
11224c08aed51c5899665ade97263692328eea4af106cristy          sum[y].direction[i].alpha+=cooccurrence[x][y].direction[i].alpha;
1123cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
11247396d88d575791c136bc6d060e613bdfbfb58f95cristy        correlation.direction[i].green+=x*y*
11257396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].green;
11267396d88d575791c136bc6d060e613bdfbfb58f95cristy        correlation.direction[i].blue+=x*y*
11277396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].blue;
11287396d88d575791c136bc6d060e613bdfbfb58f95cristy        if (image->colorspace == CMYKColorspace)
11294c08aed51c5899665ade97263692328eea4af106cristy          correlation.direction[i].black+=x*y*
11304c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
113117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
11324c08aed51c5899665ade97263692328eea4af106cristy          correlation.direction[i].alpha+=x*y*
11334c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
1134cf5e649a2c6de215ffc88ee00987090773ba0722cristy        /*
1135cf5e649a2c6de215ffc88ee00987090773ba0722cristy          Inverse Difference Moment.
1136cf5e649a2c6de215ffc88ee00987090773ba0722cristy        */
1137d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[RedPixelChannel].inverse_difference_moment[i]+=
1138cf5e649a2c6de215ffc88ee00987090773ba0722cristy          cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1);
1139d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[GreenPixelChannel].inverse_difference_moment[i]+=
1140cf5e649a2c6de215ffc88ee00987090773ba0722cristy          cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1);
1141d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BluePixelChannel].inverse_difference_moment[i]+=
1142cf5e649a2c6de215ffc88ee00987090773ba0722cristy          cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1);
1143cf5e649a2c6de215ffc88ee00987090773ba0722cristy        if (image->colorspace == CMYKColorspace)
1144d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[BlackPixelChannel].inverse_difference_moment[i]+=
11454c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black/((y-x)*(y-x)+1);
114617f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
1147d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[AlphaPixelChannel].inverse_difference_moment[i]+=
11484c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha/((y-x)*(y-x)+1);
1149e18977973bff5866de9aa6ed097aea40e27570d6cristy        /*
1150e18977973bff5866de9aa6ed097aea40e27570d6cristy          Sum average.
1151e18977973bff5866de9aa6ed097aea40e27570d6cristy        */
1152ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[y+x+2].direction[i].red+=
1153e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].red;
1154ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[y+x+2].direction[i].green+=
1155e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].green;
1156ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[y+x+2].direction[i].blue+=
1157e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].blue;
1158e18977973bff5866de9aa6ed097aea40e27570d6cristy        if (image->colorspace == CMYKColorspace)
11594c08aed51c5899665ade97263692328eea4af106cristy          density_xy[y+x+2].direction[i].black+=
11604c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
116117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
11624c08aed51c5899665ade97263692328eea4af106cristy          density_xy[y+x+2].direction[i].alpha+=
11634c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
1164e18977973bff5866de9aa6ed097aea40e27570d6cristy        /*
1165e18977973bff5866de9aa6ed097aea40e27570d6cristy          Entropy.
1166e18977973bff5866de9aa6ed097aea40e27570d6cristy        */
1167d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[RedPixelChannel].entropy[i]-=
1168e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].red*
11690633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].red);
1170d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[GreenPixelChannel].entropy[i]-=
1171e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].green*
11720633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].green);
1173d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BluePixelChannel].entropy[i]-=
1174e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].blue*
11750633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].blue);
1176e18977973bff5866de9aa6ed097aea40e27570d6cristy        if (image->colorspace == CMYKColorspace)
1177d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[BlackPixelChannel].entropy[i]-=
11784c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black*
11790633a1f35940363594ecb272bb113e045fd65f93cristy            MagickLog10(cooccurrence[x][y].direction[i].black);
118017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
1181d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[AlphaPixelChannel].entropy[i]-=
11824c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha*
11830633a1f35940363594ecb272bb113e045fd65f93cristy            MagickLog10(cooccurrence[x][y].direction[i].alpha);
1184e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        /*
1185e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy          Information Measures of Correlation.
1186e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        */
1187ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_x[x].direction[i].red+=cooccurrence[x][y].direction[i].red;
1188ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_x[x].direction[i].green+=cooccurrence[x][y].direction[i].green;
1189ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_x[x].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
119017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
11914c08aed51c5899665ade97263692328eea4af106cristy          density_x[x].direction[i].alpha+=
11924c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
11934c08aed51c5899665ade97263692328eea4af106cristy        if (image->colorspace == CMYKColorspace)
11944c08aed51c5899665ade97263692328eea4af106cristy          density_x[x].direction[i].black+=
11954c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
1196ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_y[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
1197ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_y[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
1198ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_y[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
1199e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        if (image->colorspace == CMYKColorspace)
12004c08aed51c5899665ade97263692328eea4af106cristy          density_y[y].direction[i].black+=
12014c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
120217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
12034c08aed51c5899665ade97263692328eea4af106cristy          density_y[y].direction[i].alpha+=
12044c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
12057e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy      }
1206cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].red+=y*sum[y].direction[i].red;
1207cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
1208cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].green+=y*sum[y].direction[i].green;
1209cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
1210cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].blue+=y*sum[y].direction[i].blue;
1211cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
1212cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      if (image->colorspace == CMYKColorspace)
1213cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        {
12144c08aed51c5899665ade97263692328eea4af106cristy          mean.direction[i].black+=y*sum[y].direction[i].black;
12154c08aed51c5899665ade97263692328eea4af106cristy          sum_squares.direction[i].black+=y*y*sum[y].direction[i].black;
1216cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        }
121717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
121853a727d91e1edeb3a67fa7d923db42974bba3b64cristy        {
12194c08aed51c5899665ade97263692328eea4af106cristy          mean.direction[i].alpha+=y*sum[y].direction[i].alpha;
12204c08aed51c5899665ade97263692328eea4af106cristy          sum_squares.direction[i].alpha+=y*y*sum[y].direction[i].alpha;
122153a727d91e1edeb3a67fa7d923db42974bba3b64cristy        }
12227e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy    }
1223cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy    /*
1224cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      Correlation: measure of linear-dependencies in the image.
1225cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy    */
1226d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].correlation[i]=
1227cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (correlation.direction[i].red-mean.direction[i].red*
1228cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].red)/(sqrt(sum_squares.direction[i].red-
1229cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (mean.direction[i].red*mean.direction[i].red))*sqrt(
1230cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].red-(mean.direction[i].red*
1231cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].red)));
1232d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].correlation[i]=
1233cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (correlation.direction[i].green-mean.direction[i].green*
1234cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].green)/(sqrt(sum_squares.direction[i].green-
1235cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (mean.direction[i].green*mean.direction[i].green))*sqrt(
1236cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].green-(mean.direction[i].green*
1237cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].green)));
1238d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].correlation[i]=
1239cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (correlation.direction[i].blue-mean.direction[i].blue*
1240cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue-
1241cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (mean.direction[i].blue*mean.direction[i].blue))*sqrt(
1242cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].blue-(mean.direction[i].blue*
1243cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].blue)));
1244cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy    if (image->colorspace == CMYKColorspace)
1245d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].correlation[i]=
12464c08aed51c5899665ade97263692328eea4af106cristy        (correlation.direction[i].black-mean.direction[i].black*
12474c08aed51c5899665ade97263692328eea4af106cristy        mean.direction[i].black)/(sqrt(sum_squares.direction[i].black-
12484c08aed51c5899665ade97263692328eea4af106cristy        (mean.direction[i].black*mean.direction[i].black))*sqrt(
12494c08aed51c5899665ade97263692328eea4af106cristy        sum_squares.direction[i].black-(mean.direction[i].black*
12504c08aed51c5899665ade97263692328eea4af106cristy        mean.direction[i].black)));
125117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1252d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].correlation[i]=
12534c08aed51c5899665ade97263692328eea4af106cristy        (correlation.direction[i].alpha-mean.direction[i].alpha*
12544c08aed51c5899665ade97263692328eea4af106cristy        mean.direction[i].alpha)/(sqrt(sum_squares.direction[i].alpha-
12554c08aed51c5899665ade97263692328eea4af106cristy        (mean.direction[i].alpha*mean.direction[i].alpha))*sqrt(
12564c08aed51c5899665ade97263692328eea4af106cristy        sum_squares.direction[i].alpha-(mean.direction[i].alpha*
12574c08aed51c5899665ade97263692328eea4af106cristy        mean.direction[i].alpha)));
12587e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy  }
1259cf5e649a2c6de215ffc88ee00987090773ba0722cristy  /*
1260cf5e649a2c6de215ffc88ee00987090773ba0722cristy    Compute more texture features.
1261cf5e649a2c6de215ffc88ee00987090773ba0722cristy  */
1262e18977973bff5866de9aa6ed097aea40e27570d6cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
1263ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(status) \
12645e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,image,number_grays,1)
1265e18977973bff5866de9aa6ed097aea40e27570d6cristy#endif
1266e18977973bff5866de9aa6ed097aea40e27570d6cristy  for (i=0; i < 4; i++)
1267e18977973bff5866de9aa6ed097aea40e27570d6cristy  {
1268bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
1269e18977973bff5866de9aa6ed097aea40e27570d6cristy      x;
1270e18977973bff5866de9aa6ed097aea40e27570d6cristy
1271bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=2; x < (ssize_t) (2*number_grays); x++)
1272e18977973bff5866de9aa6ed097aea40e27570d6cristy    {
1273e18977973bff5866de9aa6ed097aea40e27570d6cristy      /*
1274e18977973bff5866de9aa6ed097aea40e27570d6cristy        Sum average.
1275e18977973bff5866de9aa6ed097aea40e27570d6cristy      */
1276d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[RedPixelChannel].sum_average[i]+=
1277ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        x*density_xy[x].direction[i].red;
1278d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[GreenPixelChannel].sum_average[i]+=
1279ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        x*density_xy[x].direction[i].green;
1280d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BluePixelChannel].sum_average[i]+=
1281ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        x*density_xy[x].direction[i].blue;
1282e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
1283d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BlackPixelChannel].sum_average[i]+=
12844c08aed51c5899665ade97263692328eea4af106cristy          x*density_xy[x].direction[i].black;
128517f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
1286d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[AlphaPixelChannel].sum_average[i]+=
12874c08aed51c5899665ade97263692328eea4af106cristy          x*density_xy[x].direction[i].alpha;
1288e18977973bff5866de9aa6ed097aea40e27570d6cristy      /*
1289e18977973bff5866de9aa6ed097aea40e27570d6cristy        Sum entropy.
1290e18977973bff5866de9aa6ed097aea40e27570d6cristy      */
1291d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[RedPixelChannel].sum_entropy[i]-=
1292ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].red*
12930633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].red);
1294d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[GreenPixelChannel].sum_entropy[i]-=
1295ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].green*
12960633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].green);
1297d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BluePixelChannel].sum_entropy[i]-=
1298ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].blue*
12990633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].blue);
1300e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
1301d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BlackPixelChannel].sum_entropy[i]-=
13024c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].black*
13030633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_xy[x].direction[i].black);
130417f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
1305d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[AlphaPixelChannel].sum_entropy[i]-=
13064c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].alpha*
13070633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_xy[x].direction[i].alpha);
1308e18977973bff5866de9aa6ed097aea40e27570d6cristy      /*
1309e18977973bff5866de9aa6ed097aea40e27570d6cristy        Sum variance.
1310e18977973bff5866de9aa6ed097aea40e27570d6cristy      */
1311d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[RedPixelChannel].sum_variance[i]+=
1312d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[RedPixelChannel].sum_entropy[i])*
1313d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[RedPixelChannel].sum_entropy[i])*
1314ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].red;
1315d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[GreenPixelChannel].sum_variance[i]+=
1316d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[GreenPixelChannel].sum_entropy[i])*
1317d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[GreenPixelChannel].sum_entropy[i])*
1318ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].green;
1319d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BluePixelChannel].sum_variance[i]+=
1320d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[BluePixelChannel].sum_entropy[i])*
1321d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[BluePixelChannel].sum_entropy[i])*
1322ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].blue;
1323e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
1324d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BlackPixelChannel].sum_variance[i]+=
1325d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          (x-channel_features[BlackPixelChannel].sum_entropy[i])*
1326d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          (x-channel_features[BlackPixelChannel].sum_entropy[i])*
13274c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].black;
132817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
1329d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[AlphaPixelChannel].sum_variance[i]+=
1330d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          (x-channel_features[AlphaPixelChannel].sum_entropy[i])*
1331d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          (x-channel_features[AlphaPixelChannel].sum_entropy[i])*
13324c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].alpha;
1333e18977973bff5866de9aa6ed097aea40e27570d6cristy    }
1334e18977973bff5866de9aa6ed097aea40e27570d6cristy  }
1335e18977973bff5866de9aa6ed097aea40e27570d6cristy  /*
1336e18977973bff5866de9aa6ed097aea40e27570d6cristy    Compute more texture features.
1337e18977973bff5866de9aa6ed097aea40e27570d6cristy  */
1338cf5e649a2c6de215ffc88ee00987090773ba0722cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
1339ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(status) \
13405e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,image,number_grays,1)
1341cf5e649a2c6de215ffc88ee00987090773ba0722cristy#endif
1342cf5e649a2c6de215ffc88ee00987090773ba0722cristy  for (i=0; i < 4; i++)
1343cf5e649a2c6de215ffc88ee00987090773ba0722cristy  {
1344bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
1345cf5e649a2c6de215ffc88ee00987090773ba0722cristy      y;
1346cf5e649a2c6de215ffc88ee00987090773ba0722cristy
1347bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (y=0; y < (ssize_t) number_grays; y++)
1348cf5e649a2c6de215ffc88ee00987090773ba0722cristy    {
1349bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
1350cf5e649a2c6de215ffc88ee00987090773ba0722cristy        x;
1351cf5e649a2c6de215ffc88ee00987090773ba0722cristy
1352bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (x=0; x < (ssize_t) number_grays; x++)
1353cf5e649a2c6de215ffc88ee00987090773ba0722cristy      {
1354cf5e649a2c6de215ffc88ee00987090773ba0722cristy        /*
1355cf5e649a2c6de215ffc88ee00987090773ba0722cristy          Sum of Squares: Variance
1356cf5e649a2c6de215ffc88ee00987090773ba0722cristy        */
1357cf5e649a2c6de215ffc88ee00987090773ba0722cristy        variance.direction[i].red+=(y-mean.direction[i].red+1)*
1358cf5e649a2c6de215ffc88ee00987090773ba0722cristy          (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red;
1359cf5e649a2c6de215ffc88ee00987090773ba0722cristy        variance.direction[i].green+=(y-mean.direction[i].green+1)*
1360cf5e649a2c6de215ffc88ee00987090773ba0722cristy          (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green;
1361cf5e649a2c6de215ffc88ee00987090773ba0722cristy        variance.direction[i].blue+=(y-mean.direction[i].blue+1)*
1362cf5e649a2c6de215ffc88ee00987090773ba0722cristy          (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue;
136353a727d91e1edeb3a67fa7d923db42974bba3b64cristy        if (image->colorspace == CMYKColorspace)
13644c08aed51c5899665ade97263692328eea4af106cristy          variance.direction[i].black+=(y-mean.direction[i].black+1)*
13654c08aed51c5899665ade97263692328eea4af106cristy            (y-mean.direction[i].black+1)*cooccurrence[x][y].direction[i].black;
136617f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
13674c08aed51c5899665ade97263692328eea4af106cristy          variance.direction[i].alpha+=(y-mean.direction[i].alpha+1)*
13684c08aed51c5899665ade97263692328eea4af106cristy            (y-mean.direction[i].alpha+1)*
13694c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
1370e18977973bff5866de9aa6ed097aea40e27570d6cristy        /*
1371e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy          Sum average / Difference Variance.
1372e18977973bff5866de9aa6ed097aea40e27570d6cristy        */
1373ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[MagickAbsoluteValue(y-x)].direction[i].red+=
1374e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].red;
1375ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[MagickAbsoluteValue(y-x)].direction[i].green+=
1376e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].green;
1377ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[MagickAbsoluteValue(y-x)].direction[i].blue+=
1378e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].blue;
1379e18977973bff5866de9aa6ed097aea40e27570d6cristy        if (image->colorspace == CMYKColorspace)
13804c08aed51c5899665ade97263692328eea4af106cristy          density_xy[MagickAbsoluteValue(y-x)].direction[i].black+=
13814c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
138217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
13834c08aed51c5899665ade97263692328eea4af106cristy          density_xy[MagickAbsoluteValue(y-x)].direction[i].alpha+=
13844c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
1385e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        /*
1386e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy          Information Measures of Correlation.
1387e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        */
1388ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy.direction[i].red-=cooccurrence[x][y].direction[i].red*
13890633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].red);
1390ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy.direction[i].green-=cooccurrence[x][y].direction[i].green*
13910633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].green);
1392ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy.direction[i].blue-=cooccurrence[x][y].direction[i].blue*
13930633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].blue);
139453a727d91e1edeb3a67fa7d923db42974bba3b64cristy        if (image->colorspace == CMYKColorspace)
13954c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy.direction[i].black-=cooccurrence[x][y].direction[i].black*
13960633a1f35940363594ecb272bb113e045fd65f93cristy            MagickLog10(cooccurrence[x][y].direction[i].black);
139717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
13984c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy.direction[i].alpha-=
13990633a1f35940363594ecb272bb113e045fd65f93cristy            cooccurrence[x][y].direction[i].alpha*MagickLog10(
14000633a1f35940363594ecb272bb113e045fd65f93cristy            cooccurrence[x][y].direction[i].alpha);
1401ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy1.direction[i].red-=(cooccurrence[x][y].direction[i].red*
14020633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].red*density_y[y].direction[i].red));
1403ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy1.direction[i].green-=(cooccurrence[x][y].direction[i].green*
14040633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].green*
14050633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].green));
1406ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy1.direction[i].blue-=(cooccurrence[x][y].direction[i].blue*
14070633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].blue*density_y[y].direction[i].blue));
1408e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        if (image->colorspace == CMYKColorspace)
14094c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy1.direction[i].black-=(
14100633a1f35940363594ecb272bb113e045fd65f93cristy            cooccurrence[x][y].direction[i].black*MagickLog10(
14110633a1f35940363594ecb272bb113e045fd65f93cristy            density_x[x].direction[i].black*density_y[y].direction[i].black));
141217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
14134c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy1.direction[i].alpha-=(
14140633a1f35940363594ecb272bb113e045fd65f93cristy            cooccurrence[x][y].direction[i].alpha*MagickLog10(
14150633a1f35940363594ecb272bb113e045fd65f93cristy            density_x[x].direction[i].alpha*density_y[y].direction[i].alpha));
1416ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy2.direction[i].red-=(density_x[x].direction[i].red*
14170633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].red*MagickLog10(density_x[x].direction[i].red*
14180633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].red));
1419ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy2.direction[i].green-=(density_x[x].direction[i].green*
14200633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].green*MagickLog10(density_x[x].direction[i].green*
14210633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].green));
1422ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy2.direction[i].blue-=(density_x[x].direction[i].blue*
14230633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].blue*MagickLog10(density_x[x].direction[i].blue*
14240633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].blue));
1425e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        if (image->colorspace == CMYKColorspace)
14264c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy2.direction[i].black-=(density_x[x].direction[i].black*
14270633a1f35940363594ecb272bb113e045fd65f93cristy            density_y[y].direction[i].black*MagickLog10(
14280633a1f35940363594ecb272bb113e045fd65f93cristy            density_x[x].direction[i].black*density_y[y].direction[i].black));
142917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
14304c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy2.direction[i].alpha-=(density_x[x].direction[i].alpha*
14310633a1f35940363594ecb272bb113e045fd65f93cristy            density_y[y].direction[i].alpha*MagickLog10(
14320633a1f35940363594ecb272bb113e045fd65f93cristy            density_x[x].direction[i].alpha*density_y[y].direction[i].alpha));
1433cf5e649a2c6de215ffc88ee00987090773ba0722cristy      }
1434cf5e649a2c6de215ffc88ee00987090773ba0722cristy    }
1435d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].variance_sum_of_squares[i]=
1436cf5e649a2c6de215ffc88ee00987090773ba0722cristy      variance.direction[i].red;
1437d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].variance_sum_of_squares[i]=
1438cf5e649a2c6de215ffc88ee00987090773ba0722cristy      variance.direction[i].green;
1439d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].variance_sum_of_squares[i]=
1440cf5e649a2c6de215ffc88ee00987090773ba0722cristy      variance.direction[i].blue;
1441cf5e649a2c6de215ffc88ee00987090773ba0722cristy    if (image->colorspace == CMYKColorspace)
1442d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].variance_sum_of_squares[i]=
14434c08aed51c5899665ade97263692328eea4af106cristy        variance.direction[i].black;
144417f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1445d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].variance_sum_of_squares[i]=
14464c08aed51c5899665ade97263692328eea4af106cristy        variance.direction[i].alpha;
1447cf5e649a2c6de215ffc88ee00987090773ba0722cristy  }
1448cf5e649a2c6de215ffc88ee00987090773ba0722cristy  /*
1449cf5e649a2c6de215ffc88ee00987090773ba0722cristy    Compute more texture features.
1450cf5e649a2c6de215ffc88ee00987090773ba0722cristy  */
1451e18977973bff5866de9aa6ed097aea40e27570d6cristy  (void) ResetMagickMemory(&variance,0,sizeof(variance));
1452e18977973bff5866de9aa6ed097aea40e27570d6cristy  (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
1453e18977973bff5866de9aa6ed097aea40e27570d6cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
1454ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(status) \
14555e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,image,number_grays,1)
1456e18977973bff5866de9aa6ed097aea40e27570d6cristy#endif
1457e18977973bff5866de9aa6ed097aea40e27570d6cristy  for (i=0; i < 4; i++)
1458e18977973bff5866de9aa6ed097aea40e27570d6cristy  {
1459bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
1460e18977973bff5866de9aa6ed097aea40e27570d6cristy      x;
1461e18977973bff5866de9aa6ed097aea40e27570d6cristy
1462bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) number_grays; x++)
1463e18977973bff5866de9aa6ed097aea40e27570d6cristy    {
1464e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      /*
1465e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        Difference variance.
1466e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      */
1467ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      variance.direction[i].red+=density_xy[x].direction[i].red;
1468ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      variance.direction[i].green+=density_xy[x].direction[i].green;
1469ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      variance.direction[i].blue+=density_xy[x].direction[i].blue;
1470e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
14714c08aed51c5899665ade97263692328eea4af106cristy        variance.direction[i].black+=density_xy[x].direction[i].black;
147217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
14734c08aed51c5899665ade97263692328eea4af106cristy        variance.direction[i].alpha+=density_xy[x].direction[i].alpha;
1474ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      sum_squares.direction[i].red+=density_xy[x].direction[i].red*
1475ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].red;
1476ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      sum_squares.direction[i].green+=density_xy[x].direction[i].green*
1477ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].green;
1478ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      sum_squares.direction[i].blue+=density_xy[x].direction[i].blue*
1479ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].blue;
1480e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
14814c08aed51c5899665ade97263692328eea4af106cristy        sum_squares.direction[i].black+=density_xy[x].direction[i].black*
14824c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].black;
148317f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
14844c08aed51c5899665ade97263692328eea4af106cristy        sum_squares.direction[i].alpha+=density_xy[x].direction[i].alpha*
14854c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].alpha;
1486f6214de6bb7f45514a52bf72c3977591aeeddc94cristy      /*
1487f6214de6bb7f45514a52bf72c3977591aeeddc94cristy        Difference entropy.
1488f6214de6bb7f45514a52bf72c3977591aeeddc94cristy      */
1489d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[RedPixelChannel].difference_entropy[i]-=
1490ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].red*
14910633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].red);
1492d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[GreenPixelChannel].difference_entropy[i]-=
1493ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].green*
14940633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].green);
1495d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BluePixelChannel].difference_entropy[i]-=
1496ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].blue*
14970633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].blue);
1498f6214de6bb7f45514a52bf72c3977591aeeddc94cristy      if (image->colorspace == CMYKColorspace)
1499d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BlackPixelChannel].difference_entropy[i]-=
15004c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].black*
15010633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_xy[x].direction[i].black);
150217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
1503d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[AlphaPixelChannel].difference_entropy[i]-=
15044c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].alpha*
15050633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_xy[x].direction[i].alpha);
1506e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      /*
1507e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        Information Measures of Correlation.
1508e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      */
1509ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_x.direction[i].red-=(density_x[x].direction[i].red*
15100633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_x[x].direction[i].red));
1511ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_x.direction[i].green-=(density_x[x].direction[i].green*
15120633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_x[x].direction[i].green));
1513ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_x.direction[i].blue-=(density_x[x].direction[i].blue*
15140633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_x[x].direction[i].blue));
1515e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      if (image->colorspace == CMYKColorspace)
15164c08aed51c5899665ade97263692328eea4af106cristy        entropy_x.direction[i].black-=(density_x[x].direction[i].black*
15170633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].black));
151817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
15194c08aed51c5899665ade97263692328eea4af106cristy        entropy_x.direction[i].alpha-=(density_x[x].direction[i].alpha*
15200633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].alpha));
152153a727d91e1edeb3a67fa7d923db42974bba3b64cristy      entropy_y.direction[i].red-=(density_y[x].direction[i].red*
15220633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_y[x].direction[i].red));
152353a727d91e1edeb3a67fa7d923db42974bba3b64cristy      entropy_y.direction[i].green-=(density_y[x].direction[i].green*
15240633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_y[x].direction[i].green));
152553a727d91e1edeb3a67fa7d923db42974bba3b64cristy      entropy_y.direction[i].blue-=(density_y[x].direction[i].blue*
15260633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_y[x].direction[i].blue));
1527e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      if (image->colorspace == CMYKColorspace)
15284c08aed51c5899665ade97263692328eea4af106cristy        entropy_y.direction[i].black-=(density_y[x].direction[i].black*
15290633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_y[x].direction[i].black));
153017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
15314c08aed51c5899665ade97263692328eea4af106cristy        entropy_y.direction[i].alpha-=(density_y[x].direction[i].alpha*
15320633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_y[x].direction[i].alpha));
1533e18977973bff5866de9aa6ed097aea40e27570d6cristy    }
1534f6214de6bb7f45514a52bf72c3977591aeeddc94cristy    /*
1535f6214de6bb7f45514a52bf72c3977591aeeddc94cristy      Difference variance.
1536f6214de6bb7f45514a52bf72c3977591aeeddc94cristy    */
1537d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].difference_variance[i]=
1538e0e19dc554957d2413f7fca5063a75fd92b8a12bcristy      (((double) number_grays*number_grays*sum_squares.direction[i].red)-
1539e18977973bff5866de9aa6ed097aea40e27570d6cristy      (variance.direction[i].red*variance.direction[i].red))/
1540e18977973bff5866de9aa6ed097aea40e27570d6cristy      ((double) number_grays*number_grays*number_grays*number_grays);
1541d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].difference_variance[i]=
1542e0e19dc554957d2413f7fca5063a75fd92b8a12bcristy      (((double) number_grays*number_grays*sum_squares.direction[i].green)-
1543e18977973bff5866de9aa6ed097aea40e27570d6cristy      (variance.direction[i].green*variance.direction[i].green))/
1544e18977973bff5866de9aa6ed097aea40e27570d6cristy      ((double) number_grays*number_grays*number_grays*number_grays);
1545d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].difference_variance[i]=
1546e0e19dc554957d2413f7fca5063a75fd92b8a12bcristy      (((double) number_grays*number_grays*sum_squares.direction[i].blue)-
1547e18977973bff5866de9aa6ed097aea40e27570d6cristy      (variance.direction[i].blue*variance.direction[i].blue))/
1548e18977973bff5866de9aa6ed097aea40e27570d6cristy      ((double) number_grays*number_grays*number_grays*number_grays);
15494c08aed51c5899665ade97263692328eea4af106cristy    if (image->colorspace == CMYKColorspace)
1550d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].difference_variance[i]=
15514c08aed51c5899665ade97263692328eea4af106cristy        (((double) number_grays*number_grays*sum_squares.direction[i].black)-
15524c08aed51c5899665ade97263692328eea4af106cristy        (variance.direction[i].black*variance.direction[i].black))/
15534c08aed51c5899665ade97263692328eea4af106cristy        ((double) number_grays*number_grays*number_grays*number_grays);
155417f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1555d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].difference_variance[i]=
15564c08aed51c5899665ade97263692328eea4af106cristy        (((double) number_grays*number_grays*sum_squares.direction[i].alpha)-
15574c08aed51c5899665ade97263692328eea4af106cristy        (variance.direction[i].alpha*variance.direction[i].alpha))/
1558e18977973bff5866de9aa6ed097aea40e27570d6cristy        ((double) number_grays*number_grays*number_grays*number_grays);
1559e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy    /*
1560e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      Information Measures of Correlation.
1561e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy    */
1562d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].measure_of_correlation_1[i]=
1563ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_xy.direction[i].red-entropy_xy1.direction[i].red)/
1564ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_x.direction[i].red > entropy_y.direction[i].red ?
1565ffa10d0fe53ef0f0db63ee506c52695595976b99cristy       entropy_x.direction[i].red : entropy_y.direction[i].red);
1566d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].measure_of_correlation_1[i]=
1567ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_xy.direction[i].green-entropy_xy1.direction[i].green)/
1568ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_x.direction[i].green > entropy_y.direction[i].green ?
1569ffa10d0fe53ef0f0db63ee506c52695595976b99cristy       entropy_x.direction[i].green : entropy_y.direction[i].green);
1570d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].measure_of_correlation_1[i]=
1571ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_xy.direction[i].blue-entropy_xy1.direction[i].blue)/
1572ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_x.direction[i].blue > entropy_y.direction[i].blue ?
1573ffa10d0fe53ef0f0db63ee506c52695595976b99cristy       entropy_x.direction[i].blue : entropy_y.direction[i].blue);
1574e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy    if (image->colorspace == CMYKColorspace)
1575d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].measure_of_correlation_1[i]=
15764c08aed51c5899665ade97263692328eea4af106cristy        (entropy_xy.direction[i].black-entropy_xy1.direction[i].black)/
15774c08aed51c5899665ade97263692328eea4af106cristy        (entropy_x.direction[i].black > entropy_y.direction[i].black ?
15784c08aed51c5899665ade97263692328eea4af106cristy         entropy_x.direction[i].black : entropy_y.direction[i].black);
157917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1580d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].measure_of_correlation_1[i]=
15814c08aed51c5899665ade97263692328eea4af106cristy        (entropy_xy.direction[i].alpha-entropy_xy1.direction[i].alpha)/
15824c08aed51c5899665ade97263692328eea4af106cristy        (entropy_x.direction[i].alpha > entropy_y.direction[i].alpha ?
15834c08aed51c5899665ade97263692328eea4af106cristy         entropy_x.direction[i].alpha : entropy_y.direction[i].alpha);
1584d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].measure_of_correlation_2[i]=
15853bdd925dbb0804df99e548c50667670319655816cristy      (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].red-
1586ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_xy.direction[i].red)))));
1587d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].measure_of_correlation_2[i]=
15883bdd925dbb0804df99e548c50667670319655816cristy      (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].green-
1589ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_xy.direction[i].green)))));
1590d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].measure_of_correlation_2[i]=
15913bdd925dbb0804df99e548c50667670319655816cristy      (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].blue-
1592ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_xy.direction[i].blue)))));
1593e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy    if (image->colorspace == CMYKColorspace)
1594d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].measure_of_correlation_2[i]=
15953bdd925dbb0804df99e548c50667670319655816cristy        (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].black-
15964c08aed51c5899665ade97263692328eea4af106cristy        entropy_xy.direction[i].black)))));
159717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1598d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].measure_of_correlation_2[i]=
15993bdd925dbb0804df99e548c50667670319655816cristy        (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].alpha-
16004c08aed51c5899665ade97263692328eea4af106cristy        entropy_xy.direction[i].alpha)))));
1601e18977973bff5866de9aa6ed097aea40e27570d6cristy  }
1602e18977973bff5866de9aa6ed097aea40e27570d6cristy  /*
16032fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Compute more texture features.
16042fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
16052fc10e5aedab9144531a4668dc7526e3caf514e1cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
16062fc10e5aedab9144531a4668dc7526e3caf514e1cristy  #pragma omp parallel for schedule(static,4) shared(status) \
16072fc10e5aedab9144531a4668dc7526e3caf514e1cristy    magick_threads(image,image,number_grays,1)
16082fc10e5aedab9144531a4668dc7526e3caf514e1cristy#endif
16092fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i < 4; i++)
16102fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
16112fc10e5aedab9144531a4668dc7526e3caf514e1cristy    ssize_t
16122fc10e5aedab9144531a4668dc7526e3caf514e1cristy      z;
16132fc10e5aedab9144531a4668dc7526e3caf514e1cristy
16142fc10e5aedab9144531a4668dc7526e3caf514e1cristy    for (z=0; z < (ssize_t) number_grays; z++)
16152fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
16162fc10e5aedab9144531a4668dc7526e3caf514e1cristy      register ssize_t
16172fc10e5aedab9144531a4668dc7526e3caf514e1cristy        y;
16182fc10e5aedab9144531a4668dc7526e3caf514e1cristy
16192fc10e5aedab9144531a4668dc7526e3caf514e1cristy      ChannelStatistics
16202fc10e5aedab9144531a4668dc7526e3caf514e1cristy        pixel;
16212fc10e5aedab9144531a4668dc7526e3caf514e1cristy
16222fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
16232fc10e5aedab9144531a4668dc7526e3caf514e1cristy      for (y=0; y < (ssize_t) number_grays; y++)
16242fc10e5aedab9144531a4668dc7526e3caf514e1cristy      {
16252fc10e5aedab9144531a4668dc7526e3caf514e1cristy        register ssize_t
16262fc10e5aedab9144531a4668dc7526e3caf514e1cristy          x;
16272fc10e5aedab9144531a4668dc7526e3caf514e1cristy
16282fc10e5aedab9144531a4668dc7526e3caf514e1cristy        for (x=0; x < (ssize_t) number_grays; x++)
16292fc10e5aedab9144531a4668dc7526e3caf514e1cristy        {
16302fc10e5aedab9144531a4668dc7526e3caf514e1cristy          /*
16312fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Contrast:  amount of local variations present in an image.
16322fc10e5aedab9144531a4668dc7526e3caf514e1cristy          */
16332fc10e5aedab9144531a4668dc7526e3caf514e1cristy          if (((y-x) == z) || ((x-y) == z))
16342fc10e5aedab9144531a4668dc7526e3caf514e1cristy            {
16352fc10e5aedab9144531a4668dc7526e3caf514e1cristy              pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
16362fc10e5aedab9144531a4668dc7526e3caf514e1cristy              pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
16372fc10e5aedab9144531a4668dc7526e3caf514e1cristy              pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
16382fc10e5aedab9144531a4668dc7526e3caf514e1cristy              if (image->colorspace == CMYKColorspace)
16392fc10e5aedab9144531a4668dc7526e3caf514e1cristy                pixel.direction[i].black+=cooccurrence[x][y].direction[i].black;
164017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy              if (image->alpha_trait != UndefinedPixelTrait)
16412fc10e5aedab9144531a4668dc7526e3caf514e1cristy                pixel.direction[i].alpha+=
16422fc10e5aedab9144531a4668dc7526e3caf514e1cristy                  cooccurrence[x][y].direction[i].alpha;
16432fc10e5aedab9144531a4668dc7526e3caf514e1cristy            }
16442fc10e5aedab9144531a4668dc7526e3caf514e1cristy          /*
16452fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Maximum Correlation Coefficient.
16462fc10e5aedab9144531a4668dc7526e3caf514e1cristy          */
16472fc10e5aedab9144531a4668dc7526e3caf514e1cristy          Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red*
16482fc10e5aedab9144531a4668dc7526e3caf514e1cristy            cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/
16492fc10e5aedab9144531a4668dc7526e3caf514e1cristy            density_y[x].direction[i].red;
16502fc10e5aedab9144531a4668dc7526e3caf514e1cristy          Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green*
16512fc10e5aedab9144531a4668dc7526e3caf514e1cristy            cooccurrence[y][x].direction[i].green/
16522fc10e5aedab9144531a4668dc7526e3caf514e1cristy            density_x[z].direction[i].green/density_y[x].direction[i].red;
16532fc10e5aedab9144531a4668dc7526e3caf514e1cristy          Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue*
16542fc10e5aedab9144531a4668dc7526e3caf514e1cristy            cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/
16552fc10e5aedab9144531a4668dc7526e3caf514e1cristy            density_y[x].direction[i].blue;
16562fc10e5aedab9144531a4668dc7526e3caf514e1cristy          if (image->colorspace == CMYKColorspace)
16572fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black*
16582fc10e5aedab9144531a4668dc7526e3caf514e1cristy              cooccurrence[y][x].direction[i].black/
16592fc10e5aedab9144531a4668dc7526e3caf514e1cristy              density_x[z].direction[i].black/density_y[x].direction[i].black;
166017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy          if (image->alpha_trait != UndefinedPixelTrait)
16612fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Q[z][y].direction[i].alpha+=
16622fc10e5aedab9144531a4668dc7526e3caf514e1cristy              cooccurrence[z][x].direction[i].alpha*
16632fc10e5aedab9144531a4668dc7526e3caf514e1cristy              cooccurrence[y][x].direction[i].alpha/
16642fc10e5aedab9144531a4668dc7526e3caf514e1cristy              density_x[z].direction[i].alpha/
16652fc10e5aedab9144531a4668dc7526e3caf514e1cristy              density_y[x].direction[i].alpha;
16662fc10e5aedab9144531a4668dc7526e3caf514e1cristy        }
16672fc10e5aedab9144531a4668dc7526e3caf514e1cristy      }
16682fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[RedPixelChannel].contrast[i]+=z*z*
16692fc10e5aedab9144531a4668dc7526e3caf514e1cristy        pixel.direction[i].red;
16702fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[GreenPixelChannel].contrast[i]+=z*z*
16712fc10e5aedab9144531a4668dc7526e3caf514e1cristy        pixel.direction[i].green;
16722fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[BluePixelChannel].contrast[i]+=z*z*
16732fc10e5aedab9144531a4668dc7526e3caf514e1cristy        pixel.direction[i].blue;
16742fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (image->colorspace == CMYKColorspace)
16752fc10e5aedab9144531a4668dc7526e3caf514e1cristy        channel_features[BlackPixelChannel].contrast[i]+=z*z*
16762fc10e5aedab9144531a4668dc7526e3caf514e1cristy          pixel.direction[i].black;
167717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
16782fc10e5aedab9144531a4668dc7526e3caf514e1cristy        channel_features[AlphaPixelChannel].contrast[i]+=z*z*
16792fc10e5aedab9144531a4668dc7526e3caf514e1cristy          pixel.direction[i].alpha;
16802fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
16812fc10e5aedab9144531a4668dc7526e3caf514e1cristy    /*
16822fc10e5aedab9144531a4668dc7526e3caf514e1cristy      Maximum Correlation Coefficient.
16832fc10e5aedab9144531a4668dc7526e3caf514e1cristy      Future: return second largest eigenvalue of Q.
16842fc10e5aedab9144531a4668dc7526e3caf514e1cristy    */
16852fc10e5aedab9144531a4668dc7526e3caf514e1cristy    channel_features[RedPixelChannel].maximum_correlation_coefficient[i]=
16862fc10e5aedab9144531a4668dc7526e3caf514e1cristy      sqrt((double) -1.0);
16872fc10e5aedab9144531a4668dc7526e3caf514e1cristy    channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]=
16882fc10e5aedab9144531a4668dc7526e3caf514e1cristy      sqrt((double) -1.0);
16892fc10e5aedab9144531a4668dc7526e3caf514e1cristy    channel_features[BluePixelChannel].maximum_correlation_coefficient[i]=
16902fc10e5aedab9144531a4668dc7526e3caf514e1cristy      sqrt((double) -1.0);
16912fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (image->colorspace == CMYKColorspace)
16922fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]=
16932fc10e5aedab9144531a4668dc7526e3caf514e1cristy        sqrt((double) -1.0);
169417f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
16952fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]=
16962fc10e5aedab9144531a4668dc7526e3caf514e1cristy        sqrt((double) -1.0);
16972fc10e5aedab9144531a4668dc7526e3caf514e1cristy  }
16982fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
16992fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Relinquish resources.
17002fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
17012fc10e5aedab9144531a4668dc7526e3caf514e1cristy  sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
17022fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i < (ssize_t) number_grays; i++)
17032fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
17042fc10e5aedab9144531a4668dc7526e3caf514e1cristy  Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
17052fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
17062fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
17072fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
17082fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i < (ssize_t) number_grays; i++)
17092fc10e5aedab9144531a4668dc7526e3caf514e1cristy    cooccurrence[i]=(ChannelStatistics *)
17102fc10e5aedab9144531a4668dc7526e3caf514e1cristy      RelinquishMagickMemory(cooccurrence[i]);
17112fc10e5aedab9144531a4668dc7526e3caf514e1cristy  cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
17122fc10e5aedab9144531a4668dc7526e3caf514e1cristy  return(channel_features);
17132fc10e5aedab9144531a4668dc7526e3caf514e1cristy}
17142fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17152fc10e5aedab9144531a4668dc7526e3caf514e1cristy/*
17162fc10e5aedab9144531a4668dc7526e3caf514e1cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17172fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17182fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17192fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17202fc10e5aedab9144531a4668dc7526e3caf514e1cristy%     H o u g h L i n e I m a g e                                             %
17212fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17222fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17232fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17242fc10e5aedab9144531a4668dc7526e3caf514e1cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17252fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
1726ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  Use HoughLineImage() in conjunction with any binary edge extracted image (we
1727ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  recommand Canny) to identify lines in the image.  The algorithm accumulates
1728ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  counts for every white pixel for every possible orientation (for angles from
1729ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  0 to 179 in 1 degree increments) and distance from the center of the image to
1730ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  the corner (in 1 px increments) and stores the counts in an accumulator matrix
1731ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  of angle vs distance. The size of the accumulator is 180x(diagonal/2). Next
1732ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  it searches this space for peaks in counts and converts the locations of the
1733ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  peaks to slope and intercept in the normal x,y input image space. Use the
1734ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  slope/intercepts to find the endpoints clipped to the bounds of the image. The
1735ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  lines are then drawn. The counts are a measure of the length of the lines
17362fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17372fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  The format of the HoughLineImage method is:
17382fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17392fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      Image *HoughLineImage(const Image *image,const size_t width,
17402fc10e5aedab9144531a4668dc7526e3caf514e1cristy%        const size_t height,const size_t threshold,ExceptionInfo *exception)
17412fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17422fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  A description of each parameter follows:
17432fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17442fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o image: the image.
17452fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17462fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o width, height: find line pairs as local maxima in this neighborhood.
17472fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17482fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o threshold: the line count threshold.
17492fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17502fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o exception: return any errors or warnings in this structure.
17512fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17522fc10e5aedab9144531a4668dc7526e3caf514e1cristy*/
17532fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17542fc10e5aedab9144531a4668dc7526e3caf514e1cristystatic inline double MagickRound(double x)
17552fc10e5aedab9144531a4668dc7526e3caf514e1cristy{
17562fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
17572fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Round the fraction to nearest integer.
17582fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
17592fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if ((x-floor(x)) < (ceil(x)-x))
17602fc10e5aedab9144531a4668dc7526e3caf514e1cristy    return(floor(x));
17612fc10e5aedab9144531a4668dc7526e3caf514e1cristy  return(ceil(x));
17622fc10e5aedab9144531a4668dc7526e3caf514e1cristy}
17632fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17642fc10e5aedab9144531a4668dc7526e3caf514e1cristyMagickExport Image *HoughLineImage(const Image *image,const size_t width,
17652fc10e5aedab9144531a4668dc7526e3caf514e1cristy  const size_t height,const size_t threshold,ExceptionInfo *exception)
17662fc10e5aedab9144531a4668dc7526e3caf514e1cristy{
17678fbcf1ad6a1d07a92ef39435f679143697be4edacristy#define HoughLineImageTag  "HoughLine/Image"
17688fbcf1ad6a1d07a92ef39435f679143697be4edacristy
17692fc10e5aedab9144531a4668dc7526e3caf514e1cristy  CacheView
17702fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *image_view;
17712fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17722fc10e5aedab9144531a4668dc7526e3caf514e1cristy  char
1773151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    message[MagickPathExtent],
1774151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    path[MagickPathExtent];
17752fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17762fc10e5aedab9144531a4668dc7526e3caf514e1cristy  const char
17772fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *artifact;
17782fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17792fc10e5aedab9144531a4668dc7526e3caf514e1cristy  double
17802fc10e5aedab9144531a4668dc7526e3caf514e1cristy    hough_height;
17812fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17822fc10e5aedab9144531a4668dc7526e3caf514e1cristy  Image
17832fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *lines_image = NULL;
17842fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17852fc10e5aedab9144531a4668dc7526e3caf514e1cristy  ImageInfo
17862fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *image_info;
17872fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17882fc10e5aedab9144531a4668dc7526e3caf514e1cristy  int
17892fc10e5aedab9144531a4668dc7526e3caf514e1cristy    file;
17902fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17912fc10e5aedab9144531a4668dc7526e3caf514e1cristy  MagickBooleanType
17922fc10e5aedab9144531a4668dc7526e3caf514e1cristy    status;
17932fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17948fbcf1ad6a1d07a92ef39435f679143697be4edacristy  MagickOffsetType
17958fbcf1ad6a1d07a92ef39435f679143697be4edacristy    progress;
17968fbcf1ad6a1d07a92ef39435f679143697be4edacristy
17972fc10e5aedab9144531a4668dc7526e3caf514e1cristy  MatrixInfo
17982fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *accumulator;
17992fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18002fc10e5aedab9144531a4668dc7526e3caf514e1cristy  PointInfo
18012fc10e5aedab9144531a4668dc7526e3caf514e1cristy    center;
18022fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18032fc10e5aedab9144531a4668dc7526e3caf514e1cristy  register ssize_t
18042fc10e5aedab9144531a4668dc7526e3caf514e1cristy    y;
18052fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18062fc10e5aedab9144531a4668dc7526e3caf514e1cristy  size_t
18072fc10e5aedab9144531a4668dc7526e3caf514e1cristy    accumulator_height,
18082fc10e5aedab9144531a4668dc7526e3caf514e1cristy    accumulator_width,
18092fc10e5aedab9144531a4668dc7526e3caf514e1cristy    line_count;
18102fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18112fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
18122fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Create the accumulator.
18132fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
18142fc10e5aedab9144531a4668dc7526e3caf514e1cristy  assert(image != (const Image *) NULL);
1815e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
18162fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (image->debug != MagickFalse)
18172fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
18182fc10e5aedab9144531a4668dc7526e3caf514e1cristy  assert(exception != (ExceptionInfo *) NULL);
1819e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
18202fc10e5aedab9144531a4668dc7526e3caf514e1cristy  accumulator_width=180;
18212fc10e5aedab9144531a4668dc7526e3caf514e1cristy  hough_height=((sqrt(2.0)*(double) (image->rows > image->columns ?
18222fc10e5aedab9144531a4668dc7526e3caf514e1cristy    image->rows : image->columns))/2.0);
18232fc10e5aedab9144531a4668dc7526e3caf514e1cristy  accumulator_height=(size_t) (2.0*hough_height);
18242fc10e5aedab9144531a4668dc7526e3caf514e1cristy  accumulator=AcquireMatrixInfo(accumulator_width,accumulator_height,
18252fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(double),exception);
18262fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (accumulator == (MatrixInfo *) NULL)
18272fc10e5aedab9144531a4668dc7526e3caf514e1cristy    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
18282fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (NullMatrix(accumulator) == MagickFalse)
18292fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
18302fc10e5aedab9144531a4668dc7526e3caf514e1cristy      accumulator=DestroyMatrixInfo(accumulator);
18312fc10e5aedab9144531a4668dc7526e3caf514e1cristy      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
18322fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
18332fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
18342fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Populate the accumulator.
18352fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
18362fc10e5aedab9144531a4668dc7526e3caf514e1cristy  status=MagickTrue;
18378fbcf1ad6a1d07a92ef39435f679143697be4edacristy  progress=0;
18382fc10e5aedab9144531a4668dc7526e3caf514e1cristy  center.x=(double) image->columns/2.0;
18392fc10e5aedab9144531a4668dc7526e3caf514e1cristy  center.y=(double) image->rows/2.0;
18402fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_view=AcquireVirtualCacheView(image,exception);
18412fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (y=0; y < (ssize_t) image->rows; y++)
18422fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
18432fc10e5aedab9144531a4668dc7526e3caf514e1cristy    register const Quantum
184405d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
18452fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18462fc10e5aedab9144531a4668dc7526e3caf514e1cristy    register ssize_t
18472fc10e5aedab9144531a4668dc7526e3caf514e1cristy      x;
18482fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18492fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (status == MagickFalse)
18502fc10e5aedab9144531a4668dc7526e3caf514e1cristy      continue;
18512fc10e5aedab9144531a4668dc7526e3caf514e1cristy    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
18522fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (p == (Quantum *) NULL)
18532fc10e5aedab9144531a4668dc7526e3caf514e1cristy      {
18542fc10e5aedab9144531a4668dc7526e3caf514e1cristy        status=MagickFalse;
18552fc10e5aedab9144531a4668dc7526e3caf514e1cristy        continue;
18562fc10e5aedab9144531a4668dc7526e3caf514e1cristy      }
18572fc10e5aedab9144531a4668dc7526e3caf514e1cristy    for (x=0; x < (ssize_t) image->columns; x++)
18582fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
18592e03529bdd5a322e00e9b1d4655181c6f09ca768cristy      if (GetPixelIntensity(image,p) > (QuantumRange/2.0))
18602fc10e5aedab9144531a4668dc7526e3caf514e1cristy        {
18612fc10e5aedab9144531a4668dc7526e3caf514e1cristy          register ssize_t
18622fc10e5aedab9144531a4668dc7526e3caf514e1cristy            i;
18632fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18642fc10e5aedab9144531a4668dc7526e3caf514e1cristy          for (i=0; i < 180; i++)
18652fc10e5aedab9144531a4668dc7526e3caf514e1cristy          {
18662fc10e5aedab9144531a4668dc7526e3caf514e1cristy            double
18672fc10e5aedab9144531a4668dc7526e3caf514e1cristy              count,
18682fc10e5aedab9144531a4668dc7526e3caf514e1cristy              radius;
18692fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18702fc10e5aedab9144531a4668dc7526e3caf514e1cristy            radius=(((double) x-center.x)*cos(DegreesToRadians((double) i)))+
18712fc10e5aedab9144531a4668dc7526e3caf514e1cristy              (((double) y-center.y)*sin(DegreesToRadians((double) i)));
18722fc10e5aedab9144531a4668dc7526e3caf514e1cristy            (void) GetMatrixElement(accumulator,i,(ssize_t)
18732fc10e5aedab9144531a4668dc7526e3caf514e1cristy              MagickRound(radius+hough_height),&count);
18742fc10e5aedab9144531a4668dc7526e3caf514e1cristy            count++;
18752fc10e5aedab9144531a4668dc7526e3caf514e1cristy            (void) SetMatrixElement(accumulator,i,(ssize_t)
18762fc10e5aedab9144531a4668dc7526e3caf514e1cristy              MagickRound(radius+hough_height),&count);
18772fc10e5aedab9144531a4668dc7526e3caf514e1cristy          }
18782fc10e5aedab9144531a4668dc7526e3caf514e1cristy        }
18792fc10e5aedab9144531a4668dc7526e3caf514e1cristy      p+=GetPixelChannels(image);
18802fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
18818fbcf1ad6a1d07a92ef39435f679143697be4edacristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
18828fbcf1ad6a1d07a92ef39435f679143697be4edacristy      {
18838fbcf1ad6a1d07a92ef39435f679143697be4edacristy        MagickBooleanType
18848fbcf1ad6a1d07a92ef39435f679143697be4edacristy          proceed;
18858fbcf1ad6a1d07a92ef39435f679143697be4edacristy
18868fbcf1ad6a1d07a92ef39435f679143697be4edacristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
18878fbcf1ad6a1d07a92ef39435f679143697be4edacristy        #pragma omp critical (MagickCore_CannyEdgeImage)
18888fbcf1ad6a1d07a92ef39435f679143697be4edacristy#endif
18898fbcf1ad6a1d07a92ef39435f679143697be4edacristy        proceed=SetImageProgress(image,CannyEdgeImageTag,progress++,
18908fbcf1ad6a1d07a92ef39435f679143697be4edacristy          image->rows);
18918fbcf1ad6a1d07a92ef39435f679143697be4edacristy        if (proceed == MagickFalse)
18928fbcf1ad6a1d07a92ef39435f679143697be4edacristy          status=MagickFalse;
18938fbcf1ad6a1d07a92ef39435f679143697be4edacristy      }
18942fc10e5aedab9144531a4668dc7526e3caf514e1cristy  }
18952fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_view=DestroyCacheView(image_view);
18962fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (status == MagickFalse)
18972fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
18982fc10e5aedab9144531a4668dc7526e3caf514e1cristy      accumulator=DestroyMatrixInfo(accumulator);
18992fc10e5aedab9144531a4668dc7526e3caf514e1cristy      return((Image *) NULL);
19002fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
19012fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
19022fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Generate line segments from accumulator.
1903e18977973bff5866de9aa6ed097aea40e27570d6cristy  */
19042fc10e5aedab9144531a4668dc7526e3caf514e1cristy  file=AcquireUniqueFileResource(path);
19052fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (file == -1)
19062fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
19072fc10e5aedab9144531a4668dc7526e3caf514e1cristy      accumulator=DestroyMatrixInfo(accumulator);
19082fc10e5aedab9144531a4668dc7526e3caf514e1cristy      return((Image *) NULL);
19092fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
1910151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy  (void) FormatLocaleString(message,MagickPathExtent,
19112fc10e5aedab9144531a4668dc7526e3caf514e1cristy    "# Hough line transform: %.20gx%.20g%+.20g\n",(double) width,
19122fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (double) height,(double) threshold);
191396498423d48af3032b4ad554b12e347e0231eb45cristy  if (write(file,message,strlen(message)) != (ssize_t) strlen(message))
1914233483c36e4f75e4337766b87cae1ab13d8b92a8cristy    status=MagickFalse;
1915151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy  (void) FormatLocaleString(message,MagickPathExtent,"viewbox 0 0 %.20g %.20g\n",
19162fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (double) image->columns,(double) image->rows);
191796498423d48af3032b4ad554b12e347e0231eb45cristy  if (write(file,message,strlen(message)) != (ssize_t) strlen(message))
1918233483c36e4f75e4337766b87cae1ab13d8b92a8cristy    status=MagickFalse;
19192fc10e5aedab9144531a4668dc7526e3caf514e1cristy  line_count=image->columns > image->rows ? image->columns/4 : image->rows/4;
19202fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (threshold != 0)
19212fc10e5aedab9144531a4668dc7526e3caf514e1cristy    line_count=threshold;
19222fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (y=0; y < (ssize_t) accumulator_height; y++)
19233a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy  {
19242fc10e5aedab9144531a4668dc7526e3caf514e1cristy    register ssize_t
19252fc10e5aedab9144531a4668dc7526e3caf514e1cristy      x;
1926564a56979706a30a3d0f920fd5f538a408efd4f1cristy
19272fc10e5aedab9144531a4668dc7526e3caf514e1cristy    for (x=0; x < (ssize_t) accumulator_width; x++)
19283a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy    {
19292fc10e5aedab9144531a4668dc7526e3caf514e1cristy      double
19302fc10e5aedab9144531a4668dc7526e3caf514e1cristy        count;
19313a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy
19322fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (void) GetMatrixElement(accumulator,x,y,&count);
19332fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (count >= (double) line_count)
19342fc10e5aedab9144531a4668dc7526e3caf514e1cristy        {
19352fc10e5aedab9144531a4668dc7526e3caf514e1cristy          double
19362fc10e5aedab9144531a4668dc7526e3caf514e1cristy            maxima;
1937bd82207b3816471fba4de916fca7336f38b70493cristy
19382fc10e5aedab9144531a4668dc7526e3caf514e1cristy          SegmentInfo
19392fc10e5aedab9144531a4668dc7526e3caf514e1cristy            line;
19402fc10e5aedab9144531a4668dc7526e3caf514e1cristy
19412fc10e5aedab9144531a4668dc7526e3caf514e1cristy          ssize_t
19422fc10e5aedab9144531a4668dc7526e3caf514e1cristy            v;
1943bd82207b3816471fba4de916fca7336f38b70493cristy
19443a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy          /*
19452fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Is point a local maxima?
19463a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy          */
19472fc10e5aedab9144531a4668dc7526e3caf514e1cristy          maxima=count;
19482494e3d5cea70ebef8e12498b10bc2e24da4803fcristy          for (v=(-((ssize_t) height/2)); v <= (((ssize_t) height/2)); v++)
19492fc10e5aedab9144531a4668dc7526e3caf514e1cristy          {
19502fc10e5aedab9144531a4668dc7526e3caf514e1cristy            ssize_t
19512fc10e5aedab9144531a4668dc7526e3caf514e1cristy              u;
19522fc10e5aedab9144531a4668dc7526e3caf514e1cristy
19532494e3d5cea70ebef8e12498b10bc2e24da4803fcristy            for (u=(-((ssize_t) width/2)); u <= (((ssize_t) width/2)); u++)
19543a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy            {
19552fc10e5aedab9144531a4668dc7526e3caf514e1cristy              if ((u != 0) || (v !=0))
19562fc10e5aedab9144531a4668dc7526e3caf514e1cristy                {
19572fc10e5aedab9144531a4668dc7526e3caf514e1cristy                  (void) GetMatrixElement(accumulator,x+u,y+v,&count);
19582fc10e5aedab9144531a4668dc7526e3caf514e1cristy                  if (count > maxima)
19592fc10e5aedab9144531a4668dc7526e3caf514e1cristy                    {
19602fc10e5aedab9144531a4668dc7526e3caf514e1cristy                      maxima=count;
19612fc10e5aedab9144531a4668dc7526e3caf514e1cristy                      break;
19622fc10e5aedab9144531a4668dc7526e3caf514e1cristy                    }
19632fc10e5aedab9144531a4668dc7526e3caf514e1cristy                }
19643a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy            }
19652fc10e5aedab9144531a4668dc7526e3caf514e1cristy            if (u < (ssize_t) (width/2))
19662fc10e5aedab9144531a4668dc7526e3caf514e1cristy              break;
19672fc10e5aedab9144531a4668dc7526e3caf514e1cristy          }
19682fc10e5aedab9144531a4668dc7526e3caf514e1cristy          (void) GetMatrixElement(accumulator,x,y,&count);
19692fc10e5aedab9144531a4668dc7526e3caf514e1cristy          if (maxima > count)
19702fc10e5aedab9144531a4668dc7526e3caf514e1cristy            continue;
19712fc10e5aedab9144531a4668dc7526e3caf514e1cristy          if ((x >= 45) && (x <= 135))
19722fc10e5aedab9144531a4668dc7526e3caf514e1cristy            {
19732fc10e5aedab9144531a4668dc7526e3caf514e1cristy              /*
19742fc10e5aedab9144531a4668dc7526e3caf514e1cristy                y = (r-x cos(t))/sin(t)
19752fc10e5aedab9144531a4668dc7526e3caf514e1cristy              */
19762fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.x1=0.0;
19772fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.y1=((double) (y-(accumulator_height/2.0))-((line.x1-
19782fc10e5aedab9144531a4668dc7526e3caf514e1cristy                (image->columns/2.0))*cos(DegreesToRadians((double) x))))/
19792fc10e5aedab9144531a4668dc7526e3caf514e1cristy                sin(DegreesToRadians((double) x))+(image->rows/2.0);
19802fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.x2=(double) image->columns;
19812fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.y2=((double) (y-(accumulator_height/2.0))-((line.x2-
19822fc10e5aedab9144531a4668dc7526e3caf514e1cristy                (image->columns/2.0))*cos(DegreesToRadians((double) x))))/
19832fc10e5aedab9144531a4668dc7526e3caf514e1cristy                sin(DegreesToRadians((double) x))+(image->rows/2.0);
19842fc10e5aedab9144531a4668dc7526e3caf514e1cristy            }
19852fc10e5aedab9144531a4668dc7526e3caf514e1cristy          else
19862fc10e5aedab9144531a4668dc7526e3caf514e1cristy            {
19872fc10e5aedab9144531a4668dc7526e3caf514e1cristy              /*
19882fc10e5aedab9144531a4668dc7526e3caf514e1cristy                x = (r-y cos(t))/sin(t)
19892fc10e5aedab9144531a4668dc7526e3caf514e1cristy              */
19902fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.y1=0.0;
19912fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.x1=((double) (y-(accumulator_height/2.0))-((line.y1-
19922fc10e5aedab9144531a4668dc7526e3caf514e1cristy                (image->rows/2.0))*sin(DegreesToRadians((double) x))))/
19932fc10e5aedab9144531a4668dc7526e3caf514e1cristy                cos(DegreesToRadians((double) x))+(image->columns/2.0);
19942fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.y2=(double) image->rows;
19952fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.x2=((double) (y-(accumulator_height/2.0))-((line.y2-
19962fc10e5aedab9144531a4668dc7526e3caf514e1cristy                (image->rows/2.0))*sin(DegreesToRadians((double) x))))/
19972fc10e5aedab9144531a4668dc7526e3caf514e1cristy                cos(DegreesToRadians((double) x))+(image->columns/2.0);
19982fc10e5aedab9144531a4668dc7526e3caf514e1cristy            }
1999151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy          (void) FormatLocaleString(message,MagickPathExtent,
20002fc10e5aedab9144531a4668dc7526e3caf514e1cristy            "line %g,%g %g,%g  # %g\n",line.x1,line.y1,line.x2,line.y2,maxima);
200196498423d48af3032b4ad554b12e347e0231eb45cristy          if (write(file,message,strlen(message)) != (ssize_t) strlen(message))
2002233483c36e4f75e4337766b87cae1ab13d8b92a8cristy            status=MagickFalse;
20033a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy        }
20043a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy    }
20053a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy  }
20062fc10e5aedab9144531a4668dc7526e3caf514e1cristy  (void) close(file);
20077e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy  /*
20082fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Render lines to image canvas.
2009f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  */
20102fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_info=AcquireImageInfo();
20112fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_info->background_color=image->background_color;
2012151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy  (void) FormatLocaleString(image_info->filename,MagickPathExtent,"mvg:%s",path);
20132fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"background");
20142fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (artifact != (const char *) NULL)
20152fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) SetImageOption(image_info,"background",artifact);
20162fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"fill");
20172fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (artifact != (const char *) NULL)
20182fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) SetImageOption(image_info,"fill",artifact);
20192fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"stroke");
20202fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (artifact != (const char *) NULL)
20212fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) SetImageOption(image_info,"stroke",artifact);
20222fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"strokewidth");
20232fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (artifact != (const char *) NULL)
20242fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) SetImageOption(image_info,"strokewidth",artifact);
20252fc10e5aedab9144531a4668dc7526e3caf514e1cristy  lines_image=ReadImage(image_info,exception);
20262fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"hough-lines:accumulator");
20272fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if ((lines_image != (Image *) NULL) &&
20282fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (IsStringTrue(artifact) != MagickFalse))
20292fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
20302fc10e5aedab9144531a4668dc7526e3caf514e1cristy      Image
20312fc10e5aedab9144531a4668dc7526e3caf514e1cristy        *accumulator_image;
20322fc10e5aedab9144531a4668dc7526e3caf514e1cristy
20332fc10e5aedab9144531a4668dc7526e3caf514e1cristy      accumulator_image=MatrixToImage(accumulator,exception);
20342fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (accumulator_image != (Image *) NULL)
20352fc10e5aedab9144531a4668dc7526e3caf514e1cristy        AppendImageToList(&lines_image,accumulator_image);
20362fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
20372fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
20382fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Free resources.
20392fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
20402fc10e5aedab9144531a4668dc7526e3caf514e1cristy  accumulator=DestroyMatrixInfo(accumulator);
20412fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_info=DestroyImageInfo(image_info);
20422fc10e5aedab9144531a4668dc7526e3caf514e1cristy  (void) RelinquishUniqueFileResource(path);
20432fc10e5aedab9144531a4668dc7526e3caf514e1cristy  return(GetFirstImageInList(lines_image));
20442fc10e5aedab9144531a4668dc7526e3caf514e1cristy}
20452fc10e5aedab9144531a4668dc7526e3caf514e1cristy
20462fc10e5aedab9144531a4668dc7526e3caf514e1cristy/*
20472fc10e5aedab9144531a4668dc7526e3caf514e1cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
20482fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
20492fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
20502fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
20512fc10e5aedab9144531a4668dc7526e3caf514e1cristy%     M e a n S h i f t I m a g e                                             %
20522fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
20532fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
20542fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
20552fc10e5aedab9144531a4668dc7526e3caf514e1cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
20562fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
2057c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  MeanShiftImage() delineate arbitrarily shaped clusters in the image. For
2058c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  each pixel, it visits all the pixels in the neighborhood specified by
2059c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  the window centered at the pixel and excludes those that are outside the
2060c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  radius=(window-1)/2 surrounding the pixel. From those pixels, it finds those
2061c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  that are within the specified color distance from the current mean, and
2062c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  computes a new x,y centroid from those coordinates and a new mean. This new
2063c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  x,y centroid is used as the center for a new window. This process iterates
2064c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  until it converges and the final mean is replaces the (original window
2065c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  center) pixel value. It repeats this process for the next pixel, etc.,
2066c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  until it processes all pixels in the image. Results are typically better with
2067c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  colorspaces other than sRGB. We recommend YIQ, YUV or YCbCr.
20682fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
20692fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  The format of the MeanShiftImage method is:
20702fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
20712fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      Image *MeanShiftImage(const Image *image,const size_t width,
20726aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy%        const size_t height,const double color_distance,
20736aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy%        ExceptionInfo *exception)
20742fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
20752fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  A description of each parameter follows:
20762fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
20772fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o image: the image.
20782fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
2079d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy%    o width, height: find pixels in this neighborhood.
20802fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
20816aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy%    o color_distance: the color distance.
20822fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
20832fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o exception: return any errors or warnings in this structure.
20842fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
20852fc10e5aedab9144531a4668dc7526e3caf514e1cristy*/
20862fc10e5aedab9144531a4668dc7526e3caf514e1cristyMagickExport Image *MeanShiftImage(const Image *image,const size_t width,
20876aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  const size_t height,const double color_distance,ExceptionInfo *exception)
20882fc10e5aedab9144531a4668dc7526e3caf514e1cristy{
20896aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy#define MaxMeanShiftIterations  100
20904c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy#define MeanShiftImageTag  "MeanShift/Image"
20916aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
20926aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  CacheView
20936aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    *image_view,
20946aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    *mean_view,
20956aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    *pixel_view;
20966aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
20976aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  Image
20986aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    *mean_image;
20996aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21006aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  MagickBooleanType
21016aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    status;
21026aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21034c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy  MagickOffsetType
21044c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy    progress;
21054c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy
21066aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  ssize_t
21076aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    y;
21086aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21096aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  assert(image != (const Image *) NULL);
2110e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
21116aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  if (image->debug != MagickFalse)
21126aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
21136aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  assert(exception != (ExceptionInfo *) NULL);
2114e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
21156aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  mean_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
21166aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  if (mean_image == (Image *) NULL)
21176aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    return((Image *) NULL);
21186aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  if (SetImageStorageClass(mean_image,DirectClass,exception) == MagickFalse)
21196aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    {
21206aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      mean_image=DestroyImage(mean_image);
21216aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      return((Image *) NULL);
21226aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    }
21236aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  status=MagickTrue;
21244c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy  progress=0;
21256aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  image_view=AcquireVirtualCacheView(image,exception);
21266aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  pixel_view=AcquireVirtualCacheView(image,exception);
21276aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  mean_view=AcquireAuthenticCacheView(mean_image,exception);
21286aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
21294c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy  #pragma omp parallel for schedule(static,4) shared(status,progress) \
21306aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    magick_threads(mean_image,mean_image,mean_image->rows,1)
21316aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy#endif
21326aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  for (y=0; y < (ssize_t) mean_image->rows; y++)
21336aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  {
21346aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    register const Quantum
213505d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
21366aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21376aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    register Quantum
213805d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict q;
21396aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21406aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    register ssize_t
21416aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      x;
21426aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21436aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    if (status == MagickFalse)
21446aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      continue;
21456aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
21466aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    q=GetCacheViewAuthenticPixels(mean_view,0,y,mean_image->columns,1,
21476aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      exception);
21486aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
21496aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      {
21506aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        status=MagickFalse;
21516aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        continue;
21526aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      }
21536aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    for (x=0; x < (ssize_t) mean_image->columns; x++)
21546aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    {
21556aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      PixelInfo
21566aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel,
21576aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        previous_pixel;
21586aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21596aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      PointInfo
21606aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_location,
21616aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        previous_location;
21626aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21636aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      register ssize_t
21646aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        i;
21656aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21666aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      GetPixelInfo(image,&mean_pixel);
21676aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      GetPixelInfoPixel(image,p,&mean_pixel);
21686aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      mean_location.x=(double) x;
21696aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      mean_location.y=(double) y;
21706aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      for (i=0; i < MaxMeanShiftIterations; i++)
21716aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      {
21726aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        double
21736aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          distance,
21746aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          gamma;
21756aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21766aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        PixelInfo
21776aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          sum_pixel;
21786aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21796aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        PointInfo
21806aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          sum_location;
21816aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21826aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        ssize_t
21836aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          count,
21846aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          v;
21856aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21866aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        sum_location.x=0.0;
21876aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        sum_location.y=0.0;
21886aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        GetPixelInfo(image,&sum_pixel);
2189f89d1dd0b5189cf4df3163fc495f0493f8ddcc7ecristy        previous_location=mean_location;
21906aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        previous_pixel=mean_pixel;
21916aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        count=0;
21922494e3d5cea70ebef8e12498b10bc2e24da4803fcristy        for (v=(-((ssize_t) height/2)); v <= (((ssize_t) height/2)); v++)
21936aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        {
21946aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          ssize_t
21956aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy            u;
21966aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21972494e3d5cea70ebef8e12498b10bc2e24da4803fcristy          for (u=(-((ssize_t) width/2)); u <= (((ssize_t) width/2)); u++)
21986aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          {
2199e5ecaa83494b57d5fd8956f8b4931d58cfd6e8fccristy            if ((v*v+u*u) <= (ssize_t) ((width/2)*(height/2)))
22006aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy              {
2201983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                PixelInfo
2202983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                  pixel;
2203983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy
2204983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                status=GetOneCacheViewVirtualPixelInfo(pixel_view,(ssize_t)
22058c2a8442c506836270be8c418c87f031400edf1bcristy                  MagickRound(mean_location.x+u),(ssize_t) MagickRound(
22068c2a8442c506836270be8c418c87f031400edf1bcristy                  mean_location.y+v),&pixel,exception);
2207d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy                distance=(mean_pixel.red-pixel.red)*(mean_pixel.red-pixel.red)+
2208d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy                  (mean_pixel.green-pixel.green)*(mean_pixel.green-pixel.green)+
2209d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy                  (mean_pixel.blue-pixel.blue)*(mean_pixel.blue-pixel.blue);
2210983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                if (distance <= (color_distance*color_distance))
2211983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                  {
2212983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_location.x+=mean_location.x+u;
2213983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_location.y+=mean_location.y+v;
2214983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_pixel.red+=pixel.red;
2215983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_pixel.green+=pixel.green;
2216983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_pixel.blue+=pixel.blue;
2217983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_pixel.alpha+=pixel.alpha;
2218983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    count++;
2219983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                  }
22206aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy              }
22216aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          }
22226aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        }
22236aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        gamma=1.0/count;
22248c2a8442c506836270be8c418c87f031400edf1bcristy        mean_location.x=gamma*sum_location.x;
22258c2a8442c506836270be8c418c87f031400edf1bcristy        mean_location.y=gamma*sum_location.y;
22266aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel.red=gamma*sum_pixel.red;
22276aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel.green=gamma*sum_pixel.green;
22286aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel.blue=gamma*sum_pixel.blue;
22296aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel.alpha=gamma*sum_pixel.alpha;
22306aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        distance=(mean_location.x-previous_location.x)*
22316aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          (mean_location.x-previous_location.x)+
22326aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          (mean_location.y-previous_location.y)*
22336aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          (mean_location.y-previous_location.y)+
223421a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.red-previous_pixel.red)*
223521a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.red-previous_pixel.red)+
223621a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.green-previous_pixel.green)*
223721a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.green-previous_pixel.green)+
223821a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.blue-previous_pixel.blue)*
223921a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.blue-previous_pixel.blue);
2240d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy        if (distance <= 3.0)
22416aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          break;
22426aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      }
22436aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      SetPixelRed(mean_image,ClampToQuantum(mean_pixel.red),q);
22446aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      SetPixelGreen(mean_image,ClampToQuantum(mean_pixel.green),q);
22456aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      SetPixelBlue(mean_image,ClampToQuantum(mean_pixel.blue),q);
22466aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      SetPixelAlpha(mean_image,ClampToQuantum(mean_pixel.alpha),q);
22476aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      p+=GetPixelChannels(image);
22486aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      q+=GetPixelChannels(mean_image);
22496aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    }
22506aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    if (SyncCacheViewAuthenticPixels(mean_view,exception) == MagickFalse)
22516aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      status=MagickFalse;
22524c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
22534c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy      {
22544c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy        MagickBooleanType
22554c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy          proceed;
22564c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy
22574c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
22584c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy        #pragma omp critical (MagickCore_MeanShiftImage)
22594c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy#endif
22604c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy        proceed=SetImageProgress(image,MeanShiftImageTag,progress++,
22614c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy          image->rows);
22624c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy        if (proceed == MagickFalse)
22634c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy          status=MagickFalse;
22644c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy      }
22656aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  }
22666aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  mean_view=DestroyCacheView(mean_view);
22676aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  pixel_view=DestroyCacheView(pixel_view);
22686aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  image_view=DestroyCacheView(image_view);
22696aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  return(mean_image);
22703e2860cb0796fe77659325ec3d540d8766d54f49cristy}
2271