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%                                                                             %
207ce65e7125a4e1df1a274ce373c537a9df9c16cdCristy%  Copyright 1999-2016 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");
29094190b79d7abf8261e3a1af37aa7a7f86757b2d6Dusan Veljko  edge_image=ConvolveImage(image, kernel_info, exception);
291c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  kernel_info=DestroyKernelInfo(kernel_info);
292c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (edge_image == (Image *) NULL)
293c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    return((Image *) NULL);
294c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (SetImageColorspace(edge_image,GRAYColorspace,exception) == MagickFalse)
295c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
296c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      edge_image=DestroyImage(edge_image);
297c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      return((Image *) NULL);
298c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
29931e75937db91fd53e7deb9e3544646d0459f2196cristy  (void) SetImageAlphaChannel(edge_image,OffAlphaChannel,exception);
300c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
301c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Find the intensity gradient of the image.
302c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
303c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows,
304c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    sizeof(CannyInfo),exception);
305c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  if (canny_cache == (MatrixInfo *) NULL)
306c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
307c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      edge_image=DestroyImage(edge_image);
308c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      return((Image *) NULL);
309c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
310c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  status=MagickTrue;
311c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=AcquireVirtualCacheView(edge_image,exception);
312c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
313c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  #pragma omp parallel for schedule(static,4) shared(status) \
314c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    magick_threads(edge_image,edge_image,edge_image->rows,1)
315c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#endif
316c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  for (y=0; y < (ssize_t) edge_image->rows; y++)
317c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  {
318c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register const Quantum
31905d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
320c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
321c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register ssize_t
322c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      x;
323c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
324c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (status == MagickFalse)
325c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      continue;
326c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns+1,2,
327c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      exception);
328c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (p == (const Quantum *) NULL)
329c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
330c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        status=MagickFalse;
331c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
332c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
333c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    for (x=0; x < (ssize_t) edge_image->columns; x++)
334c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
335c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      CannyInfo
336c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        pixel;
337c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
338c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      double
339c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        dx,
340c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        dy;
341c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
342c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      register const Quantum
34305d2ff7ebf21f659f5b11e45afb294e152f4330cdirk        *magick_restrict kernel_pixels;
344c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
345c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      ssize_t
346c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        v;
347c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
348c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      static double
349c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        Gx[2][2] =
350c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
351c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          { -1.0,  +1.0 },
352c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          { -1.0,  +1.0 }
353c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        },
354c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        Gy[2][2] =
355c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
356c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          { +1.0, +1.0 },
357c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          { -1.0, -1.0 }
358c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        };
359c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
360c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
361c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      dx=0.0;
362c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      dy=0.0;
363c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      kernel_pixels=p;
364c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      for (v=0; v < 2; v++)
365c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
366c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        ssize_t
367c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          u;
368c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
369c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        for (u=0; u < 2; u++)
370c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
371c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          double
372c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            intensity;
373c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
374c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          intensity=GetPixelIntensity(edge_image,kernel_pixels+u);
375c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          dx+=0.5*Gx[v][u]*intensity;
376c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          dy+=0.5*Gy[v][u]*intensity;
377c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
378c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        kernel_pixels+=edge_image->columns+1;
379c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
380c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      pixel.magnitude=hypot(dx,dy);
381c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      pixel.orientation=0;
382c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if (fabs(dx) > MagickEpsilon)
383c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
384c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          double
385c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            slope;
386c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
387c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          slope=dy/dx;
388c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          if (slope < 0.0)
389c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            {
390c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              if (slope < -2.41421356237)
391c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                pixel.orientation=0;
392c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              else
393c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                if (slope < -0.414213562373)
394c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                  pixel.orientation=1;
395c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                else
396c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                  pixel.orientation=2;
397c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            }
398c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          else
399c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            {
400c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              if (slope > 2.41421356237)
401c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                pixel.orientation=0;
402c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy              else
403c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                if (slope > 0.414213562373)
404c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                  pixel.orientation=3;
405c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                else
406c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy                  pixel.orientation=2;
407c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            }
408c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
409c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse)
410c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
411c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      p+=GetPixelChannels(edge_image);
412c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
413c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  }
414c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=DestroyCacheView(edge_view);
415c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
416c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Non-maxima suppression, remove pixels that are not considered to be part
417c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    of an edge.
418c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
4198fbcf1ad6a1d07a92ef39435f679143697be4edacristy  progress=0;
420aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  (void) GetMatrixElement(canny_cache,0,0,&element);
421aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  max=element.intensity;
422aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  min=element.intensity;
423c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=AcquireAuthenticCacheView(edge_image,exception);
424c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
425c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  #pragma omp parallel for schedule(static,4) shared(status) \
426c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    magick_threads(edge_image,edge_image,edge_image->rows,1)
427c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#endif
428c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  for (y=0; y < (ssize_t) edge_image->rows; y++)
429c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  {
430c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register Quantum
43105d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict q;
432c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
433c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register ssize_t
434c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      x;
435c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
436c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (status == MagickFalse)
437c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      continue;
438c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    q=GetCacheViewAuthenticPixels(edge_view,0,y,edge_image->columns,1,
439c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      exception);
440c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (q == (Quantum *) NULL)
441c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
442c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        status=MagickFalse;
443c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
444c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
445c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    for (x=0; x < (ssize_t) edge_image->columns; x++)
446c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
447c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      CannyInfo
448c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        alpha_pixel,
449c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        beta_pixel,
450c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        pixel;
451c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
452c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      (void) GetMatrixElement(canny_cache,x,y,&pixel);
453c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      switch (pixel.orientation)
454c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
455c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        case 0:
456b6ecf44155ec6e79fac7d9c51ed5f56962f6eda4dirk        default:
457c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
458c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          /*
459c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            0 degrees, north and south.
460c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          */
461c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel);
462c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel);
463c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          break;
464c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
465c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        case 1:
466c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
467c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          /*
468c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            45 degrees, northwest and southeast.
469c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          */
470c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x-1,y-1,&alpha_pixel);
471c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x+1,y+1,&beta_pixel);
472c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          break;
473c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
474c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        case 2:
475c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
476c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          /*
477c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            90 degrees, east and west.
478c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          */
479c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel);
480c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel);
481c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          break;
482c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
483c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        case 3:
484c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        {
485c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          /*
486c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy            135 degrees, northeast and southwest.
487c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          */
488c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel);
489c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel);
490c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          break;
491c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        }
492c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
493c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      pixel.intensity=pixel.magnitude;
494c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if ((pixel.magnitude < alpha_pixel.magnitude) ||
495c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (pixel.magnitude < beta_pixel.magnitude))
496c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        pixel.intensity=0;
497c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      (void) SetMatrixElement(canny_cache,x,y,&pixel);
498c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
499c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      #pragma omp critical (MagickCore_CannyEdgeImage)
500c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy#endif
501c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      {
502c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if (pixel.intensity < min)
503c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          min=pixel.intensity;
504c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        if (pixel.intensity > max)
505c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          max=pixel.intensity;
506c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      }
507c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      *q=0;
508c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      q+=GetPixelChannels(edge_image);
509c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
510c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse)
511c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      status=MagickFalse;
512c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  }
513c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=DestroyCacheView(edge_view);
514c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
515c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Estimate hysteresis threshold.
516c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
517c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  lower_threshold=lower_percent*(max-min)+min;
518c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  upper_threshold=upper_percent*(max-min)+min;
519c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
520c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Hysteresis threshold.
521c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
522c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=AcquireAuthenticCacheView(edge_image,exception);
523c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  for (y=0; y < (ssize_t) edge_image->rows; y++)
524c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  {
525c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    register ssize_t
526c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      x;
527c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
528c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    if (status == MagickFalse)
529c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      continue;
530c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    for (x=0; x < (ssize_t) edge_image->columns; x++)
531c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    {
532c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      CannyInfo
533c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        pixel;
534c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
535c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      register const Quantum
53605d2ff7ebf21f659f5b11e45afb294e152f4330cdirk        *magick_restrict p;
537c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
538c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      /*
539c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        Edge if pixel gradient higher than upper threshold.
540c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      */
541c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      p=GetCacheViewVirtualPixels(edge_view,x,y,1,1,exception);
542c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if (p == (const Quantum *) NULL)
543c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
544c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      status=GetMatrixElement(canny_cache,x,y,&pixel);
545c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if (status == MagickFalse)
546c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        continue;
547c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy      if ((GetPixelIntensity(edge_image,p) == 0.0) &&
548c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          (pixel.intensity >= upper_threshold))
549c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy        status=TraceEdges(edge_image,edge_view,canny_cache,x,y,lower_threshold,
550c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy          exception);
551c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    }
5528fbcf1ad6a1d07a92ef39435f679143697be4edacristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
5538fbcf1ad6a1d07a92ef39435f679143697be4edacristy      {
5548fbcf1ad6a1d07a92ef39435f679143697be4edacristy        MagickBooleanType
5558fbcf1ad6a1d07a92ef39435f679143697be4edacristy          proceed;
5568fbcf1ad6a1d07a92ef39435f679143697be4edacristy
5578fbcf1ad6a1d07a92ef39435f679143697be4edacristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
5588fbcf1ad6a1d07a92ef39435f679143697be4edacristy        #pragma omp critical (MagickCore_CannyEdgeImage)
5598fbcf1ad6a1d07a92ef39435f679143697be4edacristy#endif
5608fbcf1ad6a1d07a92ef39435f679143697be4edacristy        proceed=SetImageProgress(image,CannyEdgeImageTag,progress++,
5618fbcf1ad6a1d07a92ef39435f679143697be4edacristy          image->rows);
5628fbcf1ad6a1d07a92ef39435f679143697be4edacristy        if (proceed == MagickFalse)
5638fbcf1ad6a1d07a92ef39435f679143697be4edacristy          status=MagickFalse;
5648fbcf1ad6a1d07a92ef39435f679143697be4edacristy      }
565c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  }
566c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  edge_view=DestroyCacheView(edge_view);
567c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  /*
568c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy    Free resources.
569c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  */
570c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  canny_cache=DestroyMatrixInfo(canny_cache);
571c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy  return(edge_image);
572c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy}
573c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy
574c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy/*
575c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
576c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
577c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
578c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
5792fc10e5aedab9144531a4668dc7526e3caf514e1cristy%   G e t I m a g e F e a t u r e s                                           %
580c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
581c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
582c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%                                                                             %
583c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
5852fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  GetImageFeatures() returns features for each channel in the image in
5862fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  each of four directions (horizontal, vertical, left and right diagonals)
5872fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  for the specified distance.  The features include the angular second
5882fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  moment, contrast, correlation, sum of squares: variance, inverse difference
5892fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  moment, sum average, sum varience, sum entropy, entropy, difference variance,%  difference entropy, information measures of correlation 1, information
5902fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  measures of correlation 2, and maximum correlation coefficient.  You can
5912fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  access the red channel contrast, for example, like this:
5922fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
5932fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      channel_features=GetImageFeatures(image,1,exception);
5942fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      contrast=channel_features[RedPixelChannel].contrast[0];
5952fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
5962fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  Use MagickRelinquishMemory() to free the features buffer.
597c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
5982fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  The format of the GetImageFeatures method is:
599c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
6002fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      ChannelFeatures *GetImageFeatures(const Image *image,
6012fc10e5aedab9144531a4668dc7526e3caf514e1cristy%        const size_t distance,ExceptionInfo *exception)
602c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
603c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%  A description of each parameter follows:
604c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
605c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o image: the image.
606c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
6072fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o distance: the distance.
608c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
609c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%    o exception: return any errors or warnings in this structure.
610c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy%
611c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy*/
6120f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6132fc10e5aedab9144531a4668dc7526e3caf514e1cristystatic inline double MagickLog10(const double x)
614c1510626cf0bd77ecc1a9681bd1e0f599108d1b3cristy{
6152fc10e5aedab9144531a4668dc7526e3caf514e1cristy#define Log10Epsilon  (1.0e-11)
6160f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6172fc10e5aedab9144531a4668dc7526e3caf514e1cristy if (fabs(x) < Log10Epsilon)
6182fc10e5aedab9144531a4668dc7526e3caf514e1cristy   return(log10(Log10Epsilon));
6192fc10e5aedab9144531a4668dc7526e3caf514e1cristy return(log10(fabs(x)));
6202fc10e5aedab9144531a4668dc7526e3caf514e1cristy}
6210f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6222fc10e5aedab9144531a4668dc7526e3caf514e1cristyMagickExport ChannelFeatures *GetImageFeatures(const Image *image,
6232fc10e5aedab9144531a4668dc7526e3caf514e1cristy  const size_t distance,ExceptionInfo *exception)
6242fc10e5aedab9144531a4668dc7526e3caf514e1cristy{
6252fc10e5aedab9144531a4668dc7526e3caf514e1cristy  typedef struct _ChannelStatistics
6262fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
6272fc10e5aedab9144531a4668dc7526e3caf514e1cristy    PixelInfo
6282fc10e5aedab9144531a4668dc7526e3caf514e1cristy      direction[4];  /* horizontal, vertical, left and right diagonals */
6292fc10e5aedab9144531a4668dc7526e3caf514e1cristy  } ChannelStatistics;
6300f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6312fc10e5aedab9144531a4668dc7526e3caf514e1cristy  CacheView
6322fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *image_view;
6330f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6342fc10e5aedab9144531a4668dc7526e3caf514e1cristy  ChannelFeatures
6352fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *channel_features;
6360f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6372fc10e5aedab9144531a4668dc7526e3caf514e1cristy  ChannelStatistics
6382fc10e5aedab9144531a4668dc7526e3caf514e1cristy    **cooccurrence,
6392fc10e5aedab9144531a4668dc7526e3caf514e1cristy    correlation,
6402fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *density_x,
6412fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *density_xy,
6422fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *density_y,
6432fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_x,
6442fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_xy,
6452fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_xy1,
6462fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_xy2,
6472fc10e5aedab9144531a4668dc7526e3caf514e1cristy    entropy_y,
6482fc10e5aedab9144531a4668dc7526e3caf514e1cristy    mean,
6492fc10e5aedab9144531a4668dc7526e3caf514e1cristy    **Q,
6502fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *sum,
6512fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sum_squares,
6522fc10e5aedab9144531a4668dc7526e3caf514e1cristy    variance;
6530f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6542fc10e5aedab9144531a4668dc7526e3caf514e1cristy  PixelPacket
6552fc10e5aedab9144531a4668dc7526e3caf514e1cristy    gray,
6562fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *grays;
6570f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6580f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  MagickBooleanType
6590f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    status;
6600f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6612fc10e5aedab9144531a4668dc7526e3caf514e1cristy  register ssize_t
662aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    i,
663aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    r;
6640f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6652fc10e5aedab9144531a4668dc7526e3caf514e1cristy  size_t
6662fc10e5aedab9144531a4668dc7526e3caf514e1cristy    length;
6670f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6682fc10e5aedab9144531a4668dc7526e3caf514e1cristy  unsigned int
6692fc10e5aedab9144531a4668dc7526e3caf514e1cristy    number_grays;
6700f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
6712fc10e5aedab9144531a4668dc7526e3caf514e1cristy  assert(image != (Image *) NULL);
672e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
6730f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  if (image->debug != MagickFalse)
6740f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
6752fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
6762fc10e5aedab9144531a4668dc7526e3caf514e1cristy    return((ChannelFeatures *) NULL);
67750debe5028731f7b7218c99c9c70e87ae5d43832Cristy  length=MaxPixelChannels+1UL;
6782fc10e5aedab9144531a4668dc7526e3caf514e1cristy  channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
6792fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*channel_features));
6802fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (channel_features == (ChannelFeatures *) NULL)
6812fc10e5aedab9144531a4668dc7526e3caf514e1cristy    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
6822fc10e5aedab9144531a4668dc7526e3caf514e1cristy  (void) ResetMagickMemory(channel_features,0,length*
6832fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*channel_features));
6840f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  /*
6852fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Form grays.
6860f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  */
6872fc10e5aedab9144531a4668dc7526e3caf514e1cristy  grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
6882fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (grays == (PixelPacket *) NULL)
6892fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
6902fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
6912fc10e5aedab9144531a4668dc7526e3caf514e1cristy        channel_features);
6922fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (void) ThrowMagickException(exception,GetMagickModule(),
6932fc10e5aedab9144531a4668dc7526e3caf514e1cristy        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
6942fc10e5aedab9144531a4668dc7526e3caf514e1cristy      return(channel_features);
6952fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
6962fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i <= (ssize_t) MaxMap; i++)
6972fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
6982fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].red=(~0U);
6992fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].green=(~0U);
7002fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].blue=(~0U);
7012fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].alpha=(~0U);
7022fc10e5aedab9144531a4668dc7526e3caf514e1cristy    grays[i].black=(~0U);
7032fc10e5aedab9144531a4668dc7526e3caf514e1cristy  }
7040f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  status=MagickTrue;
7050f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  image_view=AcquireVirtualCacheView(image,exception);
7062fc10e5aedab9144531a4668dc7526e3caf514e1cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
7072fc10e5aedab9144531a4668dc7526e3caf514e1cristy  #pragma omp parallel for schedule(static,4) shared(status) \
7082fc10e5aedab9144531a4668dc7526e3caf514e1cristy    magick_threads(image,image,image->rows,1)
7092fc10e5aedab9144531a4668dc7526e3caf514e1cristy#endif
710aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  for (r=0; r < (ssize_t) image->rows; r++)
7110f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  {
7120f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    register const Quantum
71305d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
7140f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
7150f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    register ssize_t
7160f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      x;
7170f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy
7180f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    if (status == MagickFalse)
7190f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      continue;
720aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    p=GetCacheViewVirtualPixels(image_view,0,r,image->columns,1,exception);
7212fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (p == (const Quantum *) NULL)
7220f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      {
7230f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy        status=MagickFalse;
7240f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy        continue;
7250f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      }
7260f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    for (x=0; x < (ssize_t) image->columns; x++)
7270f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    {
7282fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[ScaleQuantumToMap(GetPixelRed(image,p))].red=
7292fc10e5aedab9144531a4668dc7526e3caf514e1cristy        ScaleQuantumToMap(GetPixelRed(image,p));
7302fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green=
7312fc10e5aedab9144531a4668dc7526e3caf514e1cristy        ScaleQuantumToMap(GetPixelGreen(image,p));
7322fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue=
7332fc10e5aedab9144531a4668dc7526e3caf514e1cristy        ScaleQuantumToMap(GetPixelBlue(image,p));
7342fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (image->colorspace == CMYKColorspace)
7352fc10e5aedab9144531a4668dc7526e3caf514e1cristy        grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black=
7362fc10e5aedab9144531a4668dc7526e3caf514e1cristy          ScaleQuantumToMap(GetPixelBlack(image,p));
73717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
7382fc10e5aedab9144531a4668dc7526e3caf514e1cristy        grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha=
7392fc10e5aedab9144531a4668dc7526e3caf514e1cristy          ScaleQuantumToMap(GetPixelAlpha(image,p));
7400f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy      p+=GetPixelChannels(image);
7410f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    }
7420f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  }
7430f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  image_view=DestroyCacheView(image_view);
7440f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  if (status == MagickFalse)
7450f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    {
7462fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays=(PixelPacket *) RelinquishMagickMemory(grays);
7472fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
7482fc10e5aedab9144531a4668dc7526e3caf514e1cristy        channel_features);
7492fc10e5aedab9144531a4668dc7526e3caf514e1cristy      return(channel_features);
7500f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy    }
7512fc10e5aedab9144531a4668dc7526e3caf514e1cristy  (void) ResetMagickMemory(&gray,0,sizeof(gray));
7522fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i <= (ssize_t) MaxMap; i++)
7532fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
7542fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (grays[i].red != ~0U)
7552fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[gray.red++].red=grays[i].red;
7562fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (grays[i].green != ~0U)
7572fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[gray.green++].green=grays[i].green;
7582fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (grays[i].blue != ~0U)
7592fc10e5aedab9144531a4668dc7526e3caf514e1cristy      grays[gray.blue++].blue=grays[i].blue;
7602fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (image->colorspace == CMYKColorspace)
7612fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (grays[i].black != ~0U)
7622fc10e5aedab9144531a4668dc7526e3caf514e1cristy        grays[gray.black++].black=grays[i].black;
76317f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
7642fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (grays[i].alpha != ~0U)
7652fc10e5aedab9144531a4668dc7526e3caf514e1cristy        grays[gray.alpha++].alpha=grays[i].alpha;
7662fc10e5aedab9144531a4668dc7526e3caf514e1cristy  }
7670f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  /*
7682fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Allocate spatial dependence matrix.
7690f09a8ceb30556f22c8800ed7ff8faa260e4f6dccristy  */
7702fc10e5aedab9144531a4668dc7526e3caf514e1cristy  number_grays=gray.red;
7712fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (gray.green > number_grays)
7722fc10e5aedab9144531a4668dc7526e3caf514e1cristy    number_grays=gray.green;
7732fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (gray.blue > number_grays)
7742fc10e5aedab9144531a4668dc7526e3caf514e1cristy    number_grays=gray.blue;
7752fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (image->colorspace == CMYKColorspace)
7762fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (gray.black > number_grays)
7772fc10e5aedab9144531a4668dc7526e3caf514e1cristy      number_grays=gray.black;
77817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy  if (image->alpha_trait != UndefinedPixelTrait)
7792fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (gray.alpha > number_grays)
7802fc10e5aedab9144531a4668dc7526e3caf514e1cristy      number_grays=gray.alpha;
7812fc10e5aedab9144531a4668dc7526e3caf514e1cristy  cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
7822fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*cooccurrence));
7832fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
7842fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*density_x));
7852fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
7862fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*density_xy));
7872fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
7882fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(*density_y));
7892fc10e5aedab9144531a4668dc7526e3caf514e1cristy  Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q));
7902fc10e5aedab9144531a4668dc7526e3caf514e1cristy  sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
7912fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if ((cooccurrence == (ChannelStatistics **) NULL) ||
7922fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (density_x == (ChannelStatistics *) NULL) ||
7932fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (density_xy == (ChannelStatistics *) NULL) ||
7942fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (density_y == (ChannelStatistics *) NULL) ||
7952fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (Q == (ChannelStatistics **) NULL) ||
7962fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (sum == (ChannelStatistics *) NULL))
797f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    {
798ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (Q != (ChannelStatistics **) NULL)
799ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        {
800bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for (i=0; i < (ssize_t) number_grays; i++)
801ffa10d0fe53ef0f0db63ee506c52695595976b99cristy            Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
802ffa10d0fe53ef0f0db63ee506c52695595976b99cristy          Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
803ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        }
804ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (sum != (ChannelStatistics *) NULL)
805ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
806ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (density_y != (ChannelStatistics *) NULL)
807ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
808ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (density_xy != (ChannelStatistics *) NULL)
809ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
810ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (density_x != (ChannelStatistics *) NULL)
811ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
812ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      if (cooccurrence != (ChannelStatistics **) NULL)
813ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        {
814bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for (i=0; i < (ssize_t) number_grays; i++)
815ffa10d0fe53ef0f0db63ee506c52695595976b99cristy            cooccurrence[i]=(ChannelStatistics *)
816ffa10d0fe53ef0f0db63ee506c52695595976b99cristy              RelinquishMagickMemory(cooccurrence[i]);
817ffa10d0fe53ef0f0db63ee506c52695595976b99cristy          cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(
818ffa10d0fe53ef0f0db63ee506c52695595976b99cristy            cooccurrence);
819ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        }
820101ab708b0574518ac5715da4d3915400e9df79acristy      grays=(PixelPacket *) RelinquishMagickMemory(grays);
821f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
822f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        channel_features);
823e18977973bff5866de9aa6ed097aea40e27570d6cristy      (void) ThrowMagickException(exception,GetMagickModule(),
824efe601ce9ea5ad34ad0e8ad6e61d9be9b148b2a3cristy        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
825f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      return(channel_features);
826f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    }
827ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&correlation,0,sizeof(correlation));
82877173e5c9ab59c80128f74d92b01da7e8a64de8dcristy  (void) ResetMagickMemory(density_x,0,2*(number_grays+1)*sizeof(*density_x));
82977173e5c9ab59c80128f74d92b01da7e8a64de8dcristy  (void) ResetMagickMemory(density_xy,0,2*(number_grays+1)*sizeof(*density_xy));
83077173e5c9ab59c80128f74d92b01da7e8a64de8dcristy  (void) ResetMagickMemory(density_y,0,2*(number_grays+1)*sizeof(*density_y));
831ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&mean,0,sizeof(mean));
832ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(sum,0,number_grays*sizeof(*sum));
833ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
834ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(density_xy,0,2*number_grays*sizeof(*density_xy));
835ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_x,0,sizeof(entropy_x));
836ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_xy,0,sizeof(entropy_xy));
837ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_xy1,0,sizeof(entropy_xy1));
838ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_xy2,0,sizeof(entropy_xy2));
839ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&entropy_y,0,sizeof(entropy_y));
840ffa10d0fe53ef0f0db63ee506c52695595976b99cristy  (void) ResetMagickMemory(&variance,0,sizeof(variance));
841bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) number_grays; i++)
842f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  {
8437396d88d575791c136bc6d060e613bdfbfb58f95cristy    cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,
8447396d88d575791c136bc6d060e613bdfbfb58f95cristy      sizeof(**cooccurrence));
845ffa10d0fe53ef0f0db63ee506c52695595976b99cristy    Q[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(**Q));
846ffa10d0fe53ef0f0db63ee506c52695595976b99cristy    if ((cooccurrence[i] == (ChannelStatistics *) NULL) ||
847ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        (Q[i] == (ChannelStatistics *) NULL))
848f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      break;
8497396d88d575791c136bc6d060e613bdfbfb58f95cristy    (void) ResetMagickMemory(cooccurrence[i],0,number_grays*
8503749be456a491c22c19b273d1871e896bd8eae79cristy      sizeof(**cooccurrence));
8513749be456a491c22c19b273d1871e896bd8eae79cristy    (void) ResetMagickMemory(Q[i],0,number_grays*sizeof(**Q));
852f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  }
853bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if (i < (ssize_t) number_grays)
854f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    {
855f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      for (i--; i >= 0; i--)
856ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      {
857ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        if (Q[i] != (ChannelStatistics *) NULL)
858ffa10d0fe53ef0f0db63ee506c52695595976b99cristy          Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
859ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        if (cooccurrence[i] != (ChannelStatistics *) NULL)
860ffa10d0fe53ef0f0db63ee506c52695595976b99cristy          cooccurrence[i]=(ChannelStatistics *)
861ffa10d0fe53ef0f0db63ee506c52695595976b99cristy            RelinquishMagickMemory(cooccurrence[i]);
862ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      }
863ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
8647396d88d575791c136bc6d060e613bdfbfb58f95cristy      cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
865ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
866ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
867ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
868ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
869101ab708b0574518ac5715da4d3915400e9df79acristy      grays=(PixelPacket *) RelinquishMagickMemory(grays);
870f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
871f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        channel_features);
872e18977973bff5866de9aa6ed097aea40e27570d6cristy      (void) ThrowMagickException(exception,GetMagickModule(),
873efe601ce9ea5ad34ad0e8ad6e61d9be9b148b2a3cristy        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
874f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      return(channel_features);
875f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    }
876f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  /*
877f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    Initialize spatial dependence matrix.
878f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  */
879f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  status=MagickTrue;
88046ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  image_view=AcquireVirtualCacheView(image,exception);
881aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk  for (r=0; r < (ssize_t) image->rows; r++)
882f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  {
8834c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
88405d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
885f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy
886bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
887f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      x;
888f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy
8897e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy    ssize_t
8909d314ff2c17a77996c05413c2013880387e50f0ecristy      offset,
8919d314ff2c17a77996c05413c2013880387e50f0ecristy      u,
8929d314ff2c17a77996c05413c2013880387e50f0ecristy      v;
8937e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy
894f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    if (status == MagickFalse)
895f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      continue;
896aa6a11c893e812e2b1f1c425353eaf72d3501a9edirk    p=GetCacheViewVirtualPixels(image_view,-(ssize_t) distance,r,image->columns+
897a1d2bd306531d35abea0440a3008714d36b5c50ccristy      2*distance,distance+2,exception);
8984c08aed51c5899665ade97263692328eea4af106cristy    if (p == (const Quantum *) NULL)
899f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      {
900f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        status=MagickFalse;
901f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        continue;
902f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      }
9038a11cb146177680bfc1b7d89a8cd0c8f2befe468cristy    p+=distance*GetPixelChannels(image);;
904bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) image->columns; x++)
905f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    {
906f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      for (i=0; i < 4; i++)
907f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      {
9087e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        switch (i)
9097e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        {
9107e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          case 0:
911549a37e6cd4593dcb997230cd3584c5afead5552cristy          default:
9127e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9137e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            /*
9147396d88d575791c136bc6d060e613bdfbfb58f95cristy              Horizontal adjacency.
9157e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            */
9167e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            offset=(ssize_t) distance;
9177e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            break;
9187e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
9197e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          case 1:
9207e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9217e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            /*
9227396d88d575791c136bc6d060e613bdfbfb58f95cristy              Vertical adjacency.
9237e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            */
9247396d88d575791c136bc6d060e613bdfbfb58f95cristy            offset=(ssize_t) (image->columns+2*distance);
9257e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            break;
9267e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
9277e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          case 2:
9287e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9297e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            /*
9307396d88d575791c136bc6d060e613bdfbfb58f95cristy              Right diagonal adjacency.
9317e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            */
932d99b096901994c291fdd5b648c5ec9c12d675947cristy            offset=(ssize_t) ((image->columns+2*distance)-distance);
9337e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            break;
9347e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
9357e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          case 3:
9367e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9377e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            /*
9387396d88d575791c136bc6d060e613bdfbfb58f95cristy              Left diagonal adjacency.
9397e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            */
940d99b096901994c291fdd5b648c5ec9c12d675947cristy            offset=(ssize_t) ((image->columns+2*distance)+distance);
9417e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            break;
9427e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
9437e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        }
9447e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        u=0;
9457e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        v=0;
9464c08aed51c5899665ade97263692328eea4af106cristy        while (grays[u].red != ScaleQuantumToMap(GetPixelRed(image,p)))
9477e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          u++;
948ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        while (grays[v].red != ScaleQuantumToMap(GetPixelRed(image,p+offset*GetPixelChannels(image))))
9497e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          v++;
9507396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[u][v].direction[i].red++;
9517396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[v][u].direction[i].red++;
9527e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        u=0;
9537e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        v=0;
9544c08aed51c5899665ade97263692328eea4af106cristy        while (grays[u].green != ScaleQuantumToMap(GetPixelGreen(image,p)))
9557e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          u++;
956ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        while (grays[v].green != ScaleQuantumToMap(GetPixelGreen(image,p+offset*GetPixelChannels(image))))
9577e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          v++;
9587396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[u][v].direction[i].green++;
9597396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[v][u].direction[i].green++;
9607e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        u=0;
9617e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy        v=0;
9624c08aed51c5899665ade97263692328eea4af106cristy        while (grays[u].blue != ScaleQuantumToMap(GetPixelBlue(image,p)))
9637e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          u++;
964ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        while (grays[v].blue != ScaleQuantumToMap(GetPixelBlue(image,p+offset*GetPixelChannels(image))))
9657e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          v++;
9667396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[u][v].direction[i].blue++;
9677396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[v][u].direction[i].blue++;
96853a727d91e1edeb3a67fa7d923db42974bba3b64cristy        if (image->colorspace == CMYKColorspace)
9697e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9707e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            u=0;
9717e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            v=0;
9724c08aed51c5899665ade97263692328eea4af106cristy            while (grays[u].black != ScaleQuantumToMap(GetPixelBlack(image,p)))
9737e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy              u++;
974ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            while (grays[v].black != ScaleQuantumToMap(GetPixelBlack(image,p+offset*GetPixelChannels(image))))
9757e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy              v++;
9764c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[u][v].direction[i].black++;
9774c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[v][u].direction[i].black++;
9787e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
97917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
9807e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          {
9817e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            u=0;
9827e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy            v=0;
9834c08aed51c5899665ade97263692328eea4af106cristy            while (grays[u].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p)))
9847e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy              u++;
985ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            while (grays[v].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p+offset*GetPixelChannels(image))))
9867e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy              v++;
9874c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[u][v].direction[i].alpha++;
9884c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[v][u].direction[i].alpha++;
9897e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy          }
990f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      }
991ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image);
992f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    }
993f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  }
994101ab708b0574518ac5715da4d3915400e9df79acristy  grays=(PixelPacket *) RelinquishMagickMemory(grays);
995f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  image_view=DestroyCacheView(image_view);
996f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  if (status == MagickFalse)
997f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    {
998bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (i=0; i < (ssize_t) number_grays; i++)
9997396d88d575791c136bc6d060e613bdfbfb58f95cristy        cooccurrence[i]=(ChannelStatistics *)
10007396d88d575791c136bc6d060e613bdfbfb58f95cristy          RelinquishMagickMemory(cooccurrence[i]);
10017396d88d575791c136bc6d060e613bdfbfb58f95cristy      cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
1002f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
1003f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy        channel_features);
1004e18977973bff5866de9aa6ed097aea40e27570d6cristy      (void) ThrowMagickException(exception,GetMagickModule(),
1005efe601ce9ea5ad34ad0e8ad6e61d9be9b148b2a3cristy        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1006f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy      return(channel_features);
1007f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy    }
1008f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  /*
10097e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy    Normalize spatial dependence matrix.
10107e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy  */
1011bd82207b3816471fba4de916fca7336f38b70493cristy  for (i=0; i < 4; i++)
10127e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy  {
1013549a37e6cd4593dcb997230cd3584c5afead5552cristy    double
1014549a37e6cd4593dcb997230cd3584c5afead5552cristy      normalize;
1015549a37e6cd4593dcb997230cd3584c5afead5552cristy
101653a727d91e1edeb3a67fa7d923db42974bba3b64cristy    register ssize_t
101753a727d91e1edeb3a67fa7d923db42974bba3b64cristy      y;
101853a727d91e1edeb3a67fa7d923db42974bba3b64cristy
1019bd82207b3816471fba4de916fca7336f38b70493cristy    switch (i)
10207e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy    {
1021bd82207b3816471fba4de916fca7336f38b70493cristy      case 0:
1022bd82207b3816471fba4de916fca7336f38b70493cristy      default:
1023bd82207b3816471fba4de916fca7336f38b70493cristy      {
1024bd82207b3816471fba4de916fca7336f38b70493cristy        /*
10257396d88d575791c136bc6d060e613bdfbfb58f95cristy          Horizontal adjacency.
1026bd82207b3816471fba4de916fca7336f38b70493cristy        */
1027bd82207b3816471fba4de916fca7336f38b70493cristy        normalize=2.0*image->rows*(image->columns-distance);
1028bd82207b3816471fba4de916fca7336f38b70493cristy        break;
1029bd82207b3816471fba4de916fca7336f38b70493cristy      }
1030bd82207b3816471fba4de916fca7336f38b70493cristy      case 1:
1031bd82207b3816471fba4de916fca7336f38b70493cristy      {
1032bd82207b3816471fba4de916fca7336f38b70493cristy        /*
10337396d88d575791c136bc6d060e613bdfbfb58f95cristy          Vertical adjacency.
1034bd82207b3816471fba4de916fca7336f38b70493cristy        */
10357396d88d575791c136bc6d060e613bdfbfb58f95cristy        normalize=2.0*(image->rows-distance)*image->columns;
1036bd82207b3816471fba4de916fca7336f38b70493cristy        break;
1037bd82207b3816471fba4de916fca7336f38b70493cristy      }
1038bd82207b3816471fba4de916fca7336f38b70493cristy      case 2:
1039bd82207b3816471fba4de916fca7336f38b70493cristy      {
1040bd82207b3816471fba4de916fca7336f38b70493cristy        /*
10417396d88d575791c136bc6d060e613bdfbfb58f95cristy          Right diagonal adjacency.
1042bd82207b3816471fba4de916fca7336f38b70493cristy        */
10437396d88d575791c136bc6d060e613bdfbfb58f95cristy        normalize=2.0*(image->rows-distance)*(image->columns-distance);
1044bd82207b3816471fba4de916fca7336f38b70493cristy        break;
1045bd82207b3816471fba4de916fca7336f38b70493cristy      }
1046bd82207b3816471fba4de916fca7336f38b70493cristy      case 3:
1047bd82207b3816471fba4de916fca7336f38b70493cristy      {
1048bd82207b3816471fba4de916fca7336f38b70493cristy        /*
10497396d88d575791c136bc6d060e613bdfbfb58f95cristy          Left diagonal adjacency.
1050bd82207b3816471fba4de916fca7336f38b70493cristy        */
1051bd82207b3816471fba4de916fca7336f38b70493cristy        normalize=2.0*(image->rows-distance)*(image->columns-distance);
1052bd82207b3816471fba4de916fca7336f38b70493cristy        break;
1053bd82207b3816471fba4de916fca7336f38b70493cristy      }
1054bd82207b3816471fba4de916fca7336f38b70493cristy    }
10553e3ec3afbb0782697f201cbe30a56794c10dc7efcristy    normalize=PerceptibleReciprocal(normalize);
1056bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (y=0; y < (ssize_t) number_grays; y++)
1057bd82207b3816471fba4de916fca7336f38b70493cristy    {
1058bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
1059bd82207b3816471fba4de916fca7336f38b70493cristy        x;
1060bd82207b3816471fba4de916fca7336f38b70493cristy
1061bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (x=0; x < (ssize_t) number_grays; x++)
10627e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy      {
106353a727d91e1edeb3a67fa7d923db42974bba3b64cristy        cooccurrence[x][y].direction[i].red*=normalize;
106453a727d91e1edeb3a67fa7d923db42974bba3b64cristy        cooccurrence[x][y].direction[i].green*=normalize;
106553a727d91e1edeb3a67fa7d923db42974bba3b64cristy        cooccurrence[x][y].direction[i].blue*=normalize;
1066549a37e6cd4593dcb997230cd3584c5afead5552cristy        if (image->colorspace == CMYKColorspace)
10674c08aed51c5899665ade97263692328eea4af106cristy          cooccurrence[x][y].direction[i].black*=normalize;
106817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
10694c08aed51c5899665ade97263692328eea4af106cristy          cooccurrence[x][y].direction[i].alpha*=normalize;
1070549a37e6cd4593dcb997230cd3584c5afead5552cristy      }
1071549a37e6cd4593dcb997230cd3584c5afead5552cristy    }
1072549a37e6cd4593dcb997230cd3584c5afead5552cristy  }
1073549a37e6cd4593dcb997230cd3584c5afead5552cristy  /*
1074549a37e6cd4593dcb997230cd3584c5afead5552cristy    Compute texture features.
1075549a37e6cd4593dcb997230cd3584c5afead5552cristy  */
10763a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
1077ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(status) \
10785e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,image,number_grays,1)
10793a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy#endif
1080bd82207b3816471fba4de916fca7336f38b70493cristy  for (i=0; i < 4; i++)
1081549a37e6cd4593dcb997230cd3584c5afead5552cristy  {
1082bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
1083bd82207b3816471fba4de916fca7336f38b70493cristy      y;
1084549a37e6cd4593dcb997230cd3584c5afead5552cristy
1085bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (y=0; y < (ssize_t) number_grays; y++)
1086549a37e6cd4593dcb997230cd3584c5afead5552cristy    {
1087bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
1088bd82207b3816471fba4de916fca7336f38b70493cristy        x;
1089bd82207b3816471fba4de916fca7336f38b70493cristy
1090bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (x=0; x < (ssize_t) number_grays; x++)
1091549a37e6cd4593dcb997230cd3584c5afead5552cristy      {
1092549a37e6cd4593dcb997230cd3584c5afead5552cristy        /*
10933a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy          Angular second moment:  measure of homogeneity of the image.
1094549a37e6cd4593dcb997230cd3584c5afead5552cristy        */
1095d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[RedPixelChannel].angular_second_moment[i]+=
10967396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].red*
10977396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].red;
1098d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[GreenPixelChannel].angular_second_moment[i]+=
10997396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].green*
11007396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].green;
1101d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BluePixelChannel].angular_second_moment[i]+=
11027396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].blue*
11037396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].blue;
1104549a37e6cd4593dcb997230cd3584c5afead5552cristy        if (image->colorspace == CMYKColorspace)
1105d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[BlackPixelChannel].angular_second_moment[i]+=
11064c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black*
11074c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
110817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
1109d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[AlphaPixelChannel].angular_second_moment[i]+=
11104c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha*
11114c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
11127396d88d575791c136bc6d060e613bdfbfb58f95cristy        /*
11137396d88d575791c136bc6d060e613bdfbfb58f95cristy          Correlation: measure of linear-dependencies in the image.
11147396d88d575791c136bc6d060e613bdfbfb58f95cristy        */
11157396d88d575791c136bc6d060e613bdfbfb58f95cristy        sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
11167396d88d575791c136bc6d060e613bdfbfb58f95cristy        sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
1117cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
1118cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        if (image->colorspace == CMYKColorspace)
11194c08aed51c5899665ade97263692328eea4af106cristy          sum[y].direction[i].black+=cooccurrence[x][y].direction[i].black;
112017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
11214c08aed51c5899665ade97263692328eea4af106cristy          sum[y].direction[i].alpha+=cooccurrence[x][y].direction[i].alpha;
1122cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red;
11237396d88d575791c136bc6d060e613bdfbfb58f95cristy        correlation.direction[i].green+=x*y*
11247396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].green;
11257396d88d575791c136bc6d060e613bdfbfb58f95cristy        correlation.direction[i].blue+=x*y*
11267396d88d575791c136bc6d060e613bdfbfb58f95cristy          cooccurrence[x][y].direction[i].blue;
11277396d88d575791c136bc6d060e613bdfbfb58f95cristy        if (image->colorspace == CMYKColorspace)
11284c08aed51c5899665ade97263692328eea4af106cristy          correlation.direction[i].black+=x*y*
11294c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
113017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
11314c08aed51c5899665ade97263692328eea4af106cristy          correlation.direction[i].alpha+=x*y*
11324c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
1133cf5e649a2c6de215ffc88ee00987090773ba0722cristy        /*
1134cf5e649a2c6de215ffc88ee00987090773ba0722cristy          Inverse Difference Moment.
1135cf5e649a2c6de215ffc88ee00987090773ba0722cristy        */
1136d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[RedPixelChannel].inverse_difference_moment[i]+=
1137cf5e649a2c6de215ffc88ee00987090773ba0722cristy          cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1);
1138d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[GreenPixelChannel].inverse_difference_moment[i]+=
1139cf5e649a2c6de215ffc88ee00987090773ba0722cristy          cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1);
1140d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BluePixelChannel].inverse_difference_moment[i]+=
1141cf5e649a2c6de215ffc88ee00987090773ba0722cristy          cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1);
1142cf5e649a2c6de215ffc88ee00987090773ba0722cristy        if (image->colorspace == CMYKColorspace)
1143d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[BlackPixelChannel].inverse_difference_moment[i]+=
11444c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black/((y-x)*(y-x)+1);
114517f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
1146d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[AlphaPixelChannel].inverse_difference_moment[i]+=
11474c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha/((y-x)*(y-x)+1);
1148e18977973bff5866de9aa6ed097aea40e27570d6cristy        /*
1149e18977973bff5866de9aa6ed097aea40e27570d6cristy          Sum average.
1150e18977973bff5866de9aa6ed097aea40e27570d6cristy        */
1151ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[y+x+2].direction[i].red+=
1152e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].red;
1153ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[y+x+2].direction[i].green+=
1154e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].green;
1155ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[y+x+2].direction[i].blue+=
1156e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].blue;
1157e18977973bff5866de9aa6ed097aea40e27570d6cristy        if (image->colorspace == CMYKColorspace)
11584c08aed51c5899665ade97263692328eea4af106cristy          density_xy[y+x+2].direction[i].black+=
11594c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
116017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
11614c08aed51c5899665ade97263692328eea4af106cristy          density_xy[y+x+2].direction[i].alpha+=
11624c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
1163e18977973bff5866de9aa6ed097aea40e27570d6cristy        /*
1164e18977973bff5866de9aa6ed097aea40e27570d6cristy          Entropy.
1165e18977973bff5866de9aa6ed097aea40e27570d6cristy        */
1166d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[RedPixelChannel].entropy[i]-=
1167e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].red*
11680633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].red);
1169d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[GreenPixelChannel].entropy[i]-=
1170e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].green*
11710633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].green);
1172d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BluePixelChannel].entropy[i]-=
1173e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].blue*
11740633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].blue);
1175e18977973bff5866de9aa6ed097aea40e27570d6cristy        if (image->colorspace == CMYKColorspace)
1176d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[BlackPixelChannel].entropy[i]-=
11774c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black*
11780633a1f35940363594ecb272bb113e045fd65f93cristy            MagickLog10(cooccurrence[x][y].direction[i].black);
117917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
1180d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          channel_features[AlphaPixelChannel].entropy[i]-=
11814c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha*
11820633a1f35940363594ecb272bb113e045fd65f93cristy            MagickLog10(cooccurrence[x][y].direction[i].alpha);
1183e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        /*
1184e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy          Information Measures of Correlation.
1185e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        */
1186ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_x[x].direction[i].red+=cooccurrence[x][y].direction[i].red;
1187ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_x[x].direction[i].green+=cooccurrence[x][y].direction[i].green;
1188ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_x[x].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
118917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
11904c08aed51c5899665ade97263692328eea4af106cristy          density_x[x].direction[i].alpha+=
11914c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
11924c08aed51c5899665ade97263692328eea4af106cristy        if (image->colorspace == CMYKColorspace)
11934c08aed51c5899665ade97263692328eea4af106cristy          density_x[x].direction[i].black+=
11944c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
1195ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_y[y].direction[i].red+=cooccurrence[x][y].direction[i].red;
1196ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_y[y].direction[i].green+=cooccurrence[x][y].direction[i].green;
1197ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_y[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue;
1198e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        if (image->colorspace == CMYKColorspace)
11994c08aed51c5899665ade97263692328eea4af106cristy          density_y[y].direction[i].black+=
12004c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
120117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
12024c08aed51c5899665ade97263692328eea4af106cristy          density_y[y].direction[i].alpha+=
12034c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
12047e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy      }
1205cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].red+=y*sum[y].direction[i].red;
1206cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].red+=y*y*sum[y].direction[i].red;
1207cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].green+=y*sum[y].direction[i].green;
1208cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].green+=y*y*sum[y].direction[i].green;
1209cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].blue+=y*sum[y].direction[i].blue;
1210cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue;
1211cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      if (image->colorspace == CMYKColorspace)
1212cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        {
12134c08aed51c5899665ade97263692328eea4af106cristy          mean.direction[i].black+=y*sum[y].direction[i].black;
12144c08aed51c5899665ade97263692328eea4af106cristy          sum_squares.direction[i].black+=y*y*sum[y].direction[i].black;
1215cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy        }
121617f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
121753a727d91e1edeb3a67fa7d923db42974bba3b64cristy        {
12184c08aed51c5899665ade97263692328eea4af106cristy          mean.direction[i].alpha+=y*sum[y].direction[i].alpha;
12194c08aed51c5899665ade97263692328eea4af106cristy          sum_squares.direction[i].alpha+=y*y*sum[y].direction[i].alpha;
122053a727d91e1edeb3a67fa7d923db42974bba3b64cristy        }
12217e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy    }
1222cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy    /*
1223cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      Correlation: measure of linear-dependencies in the image.
1224cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy    */
1225d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].correlation[i]=
1226cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (correlation.direction[i].red-mean.direction[i].red*
1227cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].red)/(sqrt(sum_squares.direction[i].red-
1228cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (mean.direction[i].red*mean.direction[i].red))*sqrt(
1229cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].red-(mean.direction[i].red*
1230cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].red)));
1231d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].correlation[i]=
1232cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (correlation.direction[i].green-mean.direction[i].green*
1233cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].green)/(sqrt(sum_squares.direction[i].green-
1234cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (mean.direction[i].green*mean.direction[i].green))*sqrt(
1235cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].green-(mean.direction[i].green*
1236cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].green)));
1237d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].correlation[i]=
1238cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (correlation.direction[i].blue-mean.direction[i].blue*
1239cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue-
1240cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      (mean.direction[i].blue*mean.direction[i].blue))*sqrt(
1241cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      sum_squares.direction[i].blue-(mean.direction[i].blue*
1242cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy      mean.direction[i].blue)));
1243cdf8e1b14a1280a464eeaa3c8b37b8ece8b9cdb2cristy    if (image->colorspace == CMYKColorspace)
1244d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].correlation[i]=
12454c08aed51c5899665ade97263692328eea4af106cristy        (correlation.direction[i].black-mean.direction[i].black*
12464c08aed51c5899665ade97263692328eea4af106cristy        mean.direction[i].black)/(sqrt(sum_squares.direction[i].black-
12474c08aed51c5899665ade97263692328eea4af106cristy        (mean.direction[i].black*mean.direction[i].black))*sqrt(
12484c08aed51c5899665ade97263692328eea4af106cristy        sum_squares.direction[i].black-(mean.direction[i].black*
12494c08aed51c5899665ade97263692328eea4af106cristy        mean.direction[i].black)));
125017f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1251d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].correlation[i]=
12524c08aed51c5899665ade97263692328eea4af106cristy        (correlation.direction[i].alpha-mean.direction[i].alpha*
12534c08aed51c5899665ade97263692328eea4af106cristy        mean.direction[i].alpha)/(sqrt(sum_squares.direction[i].alpha-
12544c08aed51c5899665ade97263692328eea4af106cristy        (mean.direction[i].alpha*mean.direction[i].alpha))*sqrt(
12554c08aed51c5899665ade97263692328eea4af106cristy        sum_squares.direction[i].alpha-(mean.direction[i].alpha*
12564c08aed51c5899665ade97263692328eea4af106cristy        mean.direction[i].alpha)));
12577e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy  }
1258cf5e649a2c6de215ffc88ee00987090773ba0722cristy  /*
1259cf5e649a2c6de215ffc88ee00987090773ba0722cristy    Compute more texture features.
1260cf5e649a2c6de215ffc88ee00987090773ba0722cristy  */
1261e18977973bff5866de9aa6ed097aea40e27570d6cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
1262ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(status) \
12635e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,image,number_grays,1)
1264e18977973bff5866de9aa6ed097aea40e27570d6cristy#endif
1265e18977973bff5866de9aa6ed097aea40e27570d6cristy  for (i=0; i < 4; i++)
1266e18977973bff5866de9aa6ed097aea40e27570d6cristy  {
1267bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
1268e18977973bff5866de9aa6ed097aea40e27570d6cristy      x;
1269e18977973bff5866de9aa6ed097aea40e27570d6cristy
1270bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=2; x < (ssize_t) (2*number_grays); x++)
1271e18977973bff5866de9aa6ed097aea40e27570d6cristy    {
1272e18977973bff5866de9aa6ed097aea40e27570d6cristy      /*
1273e18977973bff5866de9aa6ed097aea40e27570d6cristy        Sum average.
1274e18977973bff5866de9aa6ed097aea40e27570d6cristy      */
1275d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[RedPixelChannel].sum_average[i]+=
1276ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        x*density_xy[x].direction[i].red;
1277d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[GreenPixelChannel].sum_average[i]+=
1278ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        x*density_xy[x].direction[i].green;
1279d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BluePixelChannel].sum_average[i]+=
1280ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        x*density_xy[x].direction[i].blue;
1281e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
1282d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BlackPixelChannel].sum_average[i]+=
12834c08aed51c5899665ade97263692328eea4af106cristy          x*density_xy[x].direction[i].black;
128417f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
1285d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[AlphaPixelChannel].sum_average[i]+=
12864c08aed51c5899665ade97263692328eea4af106cristy          x*density_xy[x].direction[i].alpha;
1287e18977973bff5866de9aa6ed097aea40e27570d6cristy      /*
1288e18977973bff5866de9aa6ed097aea40e27570d6cristy        Sum entropy.
1289e18977973bff5866de9aa6ed097aea40e27570d6cristy      */
1290d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[RedPixelChannel].sum_entropy[i]-=
1291ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].red*
12920633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].red);
1293d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[GreenPixelChannel].sum_entropy[i]-=
1294ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].green*
12950633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].green);
1296d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BluePixelChannel].sum_entropy[i]-=
1297ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].blue*
12980633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].blue);
1299e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
1300d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BlackPixelChannel].sum_entropy[i]-=
13014c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].black*
13020633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_xy[x].direction[i].black);
130317f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
1304d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[AlphaPixelChannel].sum_entropy[i]-=
13054c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].alpha*
13060633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_xy[x].direction[i].alpha);
1307e18977973bff5866de9aa6ed097aea40e27570d6cristy      /*
1308e18977973bff5866de9aa6ed097aea40e27570d6cristy        Sum variance.
1309e18977973bff5866de9aa6ed097aea40e27570d6cristy      */
1310d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[RedPixelChannel].sum_variance[i]+=
1311d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[RedPixelChannel].sum_entropy[i])*
1312d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[RedPixelChannel].sum_entropy[i])*
1313ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].red;
1314d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[GreenPixelChannel].sum_variance[i]+=
1315d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[GreenPixelChannel].sum_entropy[i])*
1316d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[GreenPixelChannel].sum_entropy[i])*
1317ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].green;
1318d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BluePixelChannel].sum_variance[i]+=
1319d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[BluePixelChannel].sum_entropy[i])*
1320d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        (x-channel_features[BluePixelChannel].sum_entropy[i])*
1321ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].blue;
1322e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
1323d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BlackPixelChannel].sum_variance[i]+=
1324d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          (x-channel_features[BlackPixelChannel].sum_entropy[i])*
1325d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          (x-channel_features[BlackPixelChannel].sum_entropy[i])*
13264c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].black;
132717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
1328d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[AlphaPixelChannel].sum_variance[i]+=
1329d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          (x-channel_features[AlphaPixelChannel].sum_entropy[i])*
1330d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy          (x-channel_features[AlphaPixelChannel].sum_entropy[i])*
13314c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].alpha;
1332e18977973bff5866de9aa6ed097aea40e27570d6cristy    }
1333e18977973bff5866de9aa6ed097aea40e27570d6cristy  }
1334e18977973bff5866de9aa6ed097aea40e27570d6cristy  /*
1335e18977973bff5866de9aa6ed097aea40e27570d6cristy    Compute more texture features.
1336e18977973bff5866de9aa6ed097aea40e27570d6cristy  */
1337cf5e649a2c6de215ffc88ee00987090773ba0722cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
1338ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(status) \
13395e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,image,number_grays,1)
1340cf5e649a2c6de215ffc88ee00987090773ba0722cristy#endif
1341cf5e649a2c6de215ffc88ee00987090773ba0722cristy  for (i=0; i < 4; i++)
1342cf5e649a2c6de215ffc88ee00987090773ba0722cristy  {
1343bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
1344cf5e649a2c6de215ffc88ee00987090773ba0722cristy      y;
1345cf5e649a2c6de215ffc88ee00987090773ba0722cristy
1346bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (y=0; y < (ssize_t) number_grays; y++)
1347cf5e649a2c6de215ffc88ee00987090773ba0722cristy    {
1348bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      register ssize_t
1349cf5e649a2c6de215ffc88ee00987090773ba0722cristy        x;
1350cf5e649a2c6de215ffc88ee00987090773ba0722cristy
1351bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      for (x=0; x < (ssize_t) number_grays; x++)
1352cf5e649a2c6de215ffc88ee00987090773ba0722cristy      {
1353cf5e649a2c6de215ffc88ee00987090773ba0722cristy        /*
1354cf5e649a2c6de215ffc88ee00987090773ba0722cristy          Sum of Squares: Variance
1355cf5e649a2c6de215ffc88ee00987090773ba0722cristy        */
1356cf5e649a2c6de215ffc88ee00987090773ba0722cristy        variance.direction[i].red+=(y-mean.direction[i].red+1)*
1357cf5e649a2c6de215ffc88ee00987090773ba0722cristy          (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red;
1358cf5e649a2c6de215ffc88ee00987090773ba0722cristy        variance.direction[i].green+=(y-mean.direction[i].green+1)*
1359cf5e649a2c6de215ffc88ee00987090773ba0722cristy          (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green;
1360cf5e649a2c6de215ffc88ee00987090773ba0722cristy        variance.direction[i].blue+=(y-mean.direction[i].blue+1)*
1361cf5e649a2c6de215ffc88ee00987090773ba0722cristy          (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue;
136253a727d91e1edeb3a67fa7d923db42974bba3b64cristy        if (image->colorspace == CMYKColorspace)
13634c08aed51c5899665ade97263692328eea4af106cristy          variance.direction[i].black+=(y-mean.direction[i].black+1)*
13644c08aed51c5899665ade97263692328eea4af106cristy            (y-mean.direction[i].black+1)*cooccurrence[x][y].direction[i].black;
136517f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
13664c08aed51c5899665ade97263692328eea4af106cristy          variance.direction[i].alpha+=(y-mean.direction[i].alpha+1)*
13674c08aed51c5899665ade97263692328eea4af106cristy            (y-mean.direction[i].alpha+1)*
13684c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
1369e18977973bff5866de9aa6ed097aea40e27570d6cristy        /*
1370e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy          Sum average / Difference Variance.
1371e18977973bff5866de9aa6ed097aea40e27570d6cristy        */
1372ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[MagickAbsoluteValue(y-x)].direction[i].red+=
1373e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].red;
1374ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[MagickAbsoluteValue(y-x)].direction[i].green+=
1375e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].green;
1376ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[MagickAbsoluteValue(y-x)].direction[i].blue+=
1377e18977973bff5866de9aa6ed097aea40e27570d6cristy          cooccurrence[x][y].direction[i].blue;
1378e18977973bff5866de9aa6ed097aea40e27570d6cristy        if (image->colorspace == CMYKColorspace)
13794c08aed51c5899665ade97263692328eea4af106cristy          density_xy[MagickAbsoluteValue(y-x)].direction[i].black+=
13804c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].black;
138117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
13824c08aed51c5899665ade97263692328eea4af106cristy          density_xy[MagickAbsoluteValue(y-x)].direction[i].alpha+=
13834c08aed51c5899665ade97263692328eea4af106cristy            cooccurrence[x][y].direction[i].alpha;
1384e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        /*
1385e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy          Information Measures of Correlation.
1386e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        */
1387ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy.direction[i].red-=cooccurrence[x][y].direction[i].red*
13880633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].red);
1389ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy.direction[i].green-=cooccurrence[x][y].direction[i].green*
13900633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].green);
1391ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy.direction[i].blue-=cooccurrence[x][y].direction[i].blue*
13920633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(cooccurrence[x][y].direction[i].blue);
139353a727d91e1edeb3a67fa7d923db42974bba3b64cristy        if (image->colorspace == CMYKColorspace)
13944c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy.direction[i].black-=cooccurrence[x][y].direction[i].black*
13950633a1f35940363594ecb272bb113e045fd65f93cristy            MagickLog10(cooccurrence[x][y].direction[i].black);
139617f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
13974c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy.direction[i].alpha-=
13980633a1f35940363594ecb272bb113e045fd65f93cristy            cooccurrence[x][y].direction[i].alpha*MagickLog10(
13990633a1f35940363594ecb272bb113e045fd65f93cristy            cooccurrence[x][y].direction[i].alpha);
1400ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy1.direction[i].red-=(cooccurrence[x][y].direction[i].red*
14010633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].red*density_y[y].direction[i].red));
1402ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy1.direction[i].green-=(cooccurrence[x][y].direction[i].green*
14030633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].green*
14040633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].green));
1405ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy1.direction[i].blue-=(cooccurrence[x][y].direction[i].blue*
14060633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].blue*density_y[y].direction[i].blue));
1407e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        if (image->colorspace == CMYKColorspace)
14084c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy1.direction[i].black-=(
14090633a1f35940363594ecb272bb113e045fd65f93cristy            cooccurrence[x][y].direction[i].black*MagickLog10(
14100633a1f35940363594ecb272bb113e045fd65f93cristy            density_x[x].direction[i].black*density_y[y].direction[i].black));
141117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
14124c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy1.direction[i].alpha-=(
14130633a1f35940363594ecb272bb113e045fd65f93cristy            cooccurrence[x][y].direction[i].alpha*MagickLog10(
14140633a1f35940363594ecb272bb113e045fd65f93cristy            density_x[x].direction[i].alpha*density_y[y].direction[i].alpha));
1415ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy2.direction[i].red-=(density_x[x].direction[i].red*
14160633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].red*MagickLog10(density_x[x].direction[i].red*
14170633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].red));
1418ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy2.direction[i].green-=(density_x[x].direction[i].green*
14190633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].green*MagickLog10(density_x[x].direction[i].green*
14200633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].green));
1421ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        entropy_xy2.direction[i].blue-=(density_x[x].direction[i].blue*
14220633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].blue*MagickLog10(density_x[x].direction[i].blue*
14230633a1f35940363594ecb272bb113e045fd65f93cristy          density_y[y].direction[i].blue));
1424e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        if (image->colorspace == CMYKColorspace)
14254c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy2.direction[i].black-=(density_x[x].direction[i].black*
14260633a1f35940363594ecb272bb113e045fd65f93cristy            density_y[y].direction[i].black*MagickLog10(
14270633a1f35940363594ecb272bb113e045fd65f93cristy            density_x[x].direction[i].black*density_y[y].direction[i].black));
142817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy        if (image->alpha_trait != UndefinedPixelTrait)
14294c08aed51c5899665ade97263692328eea4af106cristy          entropy_xy2.direction[i].alpha-=(density_x[x].direction[i].alpha*
14300633a1f35940363594ecb272bb113e045fd65f93cristy            density_y[y].direction[i].alpha*MagickLog10(
14310633a1f35940363594ecb272bb113e045fd65f93cristy            density_x[x].direction[i].alpha*density_y[y].direction[i].alpha));
1432cf5e649a2c6de215ffc88ee00987090773ba0722cristy      }
1433cf5e649a2c6de215ffc88ee00987090773ba0722cristy    }
1434d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].variance_sum_of_squares[i]=
1435cf5e649a2c6de215ffc88ee00987090773ba0722cristy      variance.direction[i].red;
1436d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].variance_sum_of_squares[i]=
1437cf5e649a2c6de215ffc88ee00987090773ba0722cristy      variance.direction[i].green;
1438d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].variance_sum_of_squares[i]=
1439cf5e649a2c6de215ffc88ee00987090773ba0722cristy      variance.direction[i].blue;
1440cf5e649a2c6de215ffc88ee00987090773ba0722cristy    if (image->colorspace == CMYKColorspace)
1441d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].variance_sum_of_squares[i]=
14424c08aed51c5899665ade97263692328eea4af106cristy        variance.direction[i].black;
144317f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1444d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].variance_sum_of_squares[i]=
14454c08aed51c5899665ade97263692328eea4af106cristy        variance.direction[i].alpha;
1446cf5e649a2c6de215ffc88ee00987090773ba0722cristy  }
1447cf5e649a2c6de215ffc88ee00987090773ba0722cristy  /*
1448cf5e649a2c6de215ffc88ee00987090773ba0722cristy    Compute more texture features.
1449cf5e649a2c6de215ffc88ee00987090773ba0722cristy  */
1450e18977973bff5866de9aa6ed097aea40e27570d6cristy  (void) ResetMagickMemory(&variance,0,sizeof(variance));
1451e18977973bff5866de9aa6ed097aea40e27570d6cristy  (void) ResetMagickMemory(&sum_squares,0,sizeof(sum_squares));
1452e18977973bff5866de9aa6ed097aea40e27570d6cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
1453ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(status) \
14545e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,image,number_grays,1)
1455e18977973bff5866de9aa6ed097aea40e27570d6cristy#endif
1456e18977973bff5866de9aa6ed097aea40e27570d6cristy  for (i=0; i < 4; i++)
1457e18977973bff5866de9aa6ed097aea40e27570d6cristy  {
1458bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
1459e18977973bff5866de9aa6ed097aea40e27570d6cristy      x;
1460e18977973bff5866de9aa6ed097aea40e27570d6cristy
1461bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) number_grays; x++)
1462e18977973bff5866de9aa6ed097aea40e27570d6cristy    {
1463e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      /*
1464e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        Difference variance.
1465e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      */
1466ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      variance.direction[i].red+=density_xy[x].direction[i].red;
1467ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      variance.direction[i].green+=density_xy[x].direction[i].green;
1468ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      variance.direction[i].blue+=density_xy[x].direction[i].blue;
1469e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
14704c08aed51c5899665ade97263692328eea4af106cristy        variance.direction[i].black+=density_xy[x].direction[i].black;
147117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
14724c08aed51c5899665ade97263692328eea4af106cristy        variance.direction[i].alpha+=density_xy[x].direction[i].alpha;
1473ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      sum_squares.direction[i].red+=density_xy[x].direction[i].red*
1474ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].red;
1475ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      sum_squares.direction[i].green+=density_xy[x].direction[i].green*
1476ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].green;
1477ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      sum_squares.direction[i].blue+=density_xy[x].direction[i].blue*
1478ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].blue;
1479e18977973bff5866de9aa6ed097aea40e27570d6cristy      if (image->colorspace == CMYKColorspace)
14804c08aed51c5899665ade97263692328eea4af106cristy        sum_squares.direction[i].black+=density_xy[x].direction[i].black*
14814c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].black;
148217f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
14834c08aed51c5899665ade97263692328eea4af106cristy        sum_squares.direction[i].alpha+=density_xy[x].direction[i].alpha*
14844c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].alpha;
1485f6214de6bb7f45514a52bf72c3977591aeeddc94cristy      /*
1486f6214de6bb7f45514a52bf72c3977591aeeddc94cristy        Difference entropy.
1487f6214de6bb7f45514a52bf72c3977591aeeddc94cristy      */
1488d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[RedPixelChannel].difference_entropy[i]-=
1489ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].red*
14900633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].red);
1491d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[GreenPixelChannel].difference_entropy[i]-=
1492ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].green*
14930633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].green);
1494d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BluePixelChannel].difference_entropy[i]-=
1495ffa10d0fe53ef0f0db63ee506c52695595976b99cristy        density_xy[x].direction[i].blue*
14960633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_xy[x].direction[i].blue);
1497f6214de6bb7f45514a52bf72c3977591aeeddc94cristy      if (image->colorspace == CMYKColorspace)
1498d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[BlackPixelChannel].difference_entropy[i]-=
14994c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].black*
15000633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_xy[x].direction[i].black);
150117f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
1502d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy        channel_features[AlphaPixelChannel].difference_entropy[i]-=
15034c08aed51c5899665ade97263692328eea4af106cristy          density_xy[x].direction[i].alpha*
15040633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_xy[x].direction[i].alpha);
1505e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      /*
1506e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy        Information Measures of Correlation.
1507e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      */
1508ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_x.direction[i].red-=(density_x[x].direction[i].red*
15090633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_x[x].direction[i].red));
1510ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_x.direction[i].green-=(density_x[x].direction[i].green*
15110633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_x[x].direction[i].green));
1512ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_x.direction[i].blue-=(density_x[x].direction[i].blue*
15130633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_x[x].direction[i].blue));
1514e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      if (image->colorspace == CMYKColorspace)
15154c08aed51c5899665ade97263692328eea4af106cristy        entropy_x.direction[i].black-=(density_x[x].direction[i].black*
15160633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].black));
151717f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
15184c08aed51c5899665ade97263692328eea4af106cristy        entropy_x.direction[i].alpha-=(density_x[x].direction[i].alpha*
15190633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_x[x].direction[i].alpha));
152053a727d91e1edeb3a67fa7d923db42974bba3b64cristy      entropy_y.direction[i].red-=(density_y[x].direction[i].red*
15210633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_y[x].direction[i].red));
152253a727d91e1edeb3a67fa7d923db42974bba3b64cristy      entropy_y.direction[i].green-=(density_y[x].direction[i].green*
15230633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_y[x].direction[i].green));
152453a727d91e1edeb3a67fa7d923db42974bba3b64cristy      entropy_y.direction[i].blue-=(density_y[x].direction[i].blue*
15250633a1f35940363594ecb272bb113e045fd65f93cristy        MagickLog10(density_y[x].direction[i].blue));
1526e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      if (image->colorspace == CMYKColorspace)
15274c08aed51c5899665ade97263692328eea4af106cristy        entropy_y.direction[i].black-=(density_y[x].direction[i].black*
15280633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_y[x].direction[i].black));
152917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
15304c08aed51c5899665ade97263692328eea4af106cristy        entropy_y.direction[i].alpha-=(density_y[x].direction[i].alpha*
15310633a1f35940363594ecb272bb113e045fd65f93cristy          MagickLog10(density_y[x].direction[i].alpha));
1532e18977973bff5866de9aa6ed097aea40e27570d6cristy    }
1533f6214de6bb7f45514a52bf72c3977591aeeddc94cristy    /*
1534f6214de6bb7f45514a52bf72c3977591aeeddc94cristy      Difference variance.
1535f6214de6bb7f45514a52bf72c3977591aeeddc94cristy    */
1536d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].difference_variance[i]=
1537e0e19dc554957d2413f7fca5063a75fd92b8a12bcristy      (((double) number_grays*number_grays*sum_squares.direction[i].red)-
1538e18977973bff5866de9aa6ed097aea40e27570d6cristy      (variance.direction[i].red*variance.direction[i].red))/
1539e18977973bff5866de9aa6ed097aea40e27570d6cristy      ((double) number_grays*number_grays*number_grays*number_grays);
1540d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].difference_variance[i]=
1541e0e19dc554957d2413f7fca5063a75fd92b8a12bcristy      (((double) number_grays*number_grays*sum_squares.direction[i].green)-
1542e18977973bff5866de9aa6ed097aea40e27570d6cristy      (variance.direction[i].green*variance.direction[i].green))/
1543e18977973bff5866de9aa6ed097aea40e27570d6cristy      ((double) number_grays*number_grays*number_grays*number_grays);
1544d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].difference_variance[i]=
1545e0e19dc554957d2413f7fca5063a75fd92b8a12bcristy      (((double) number_grays*number_grays*sum_squares.direction[i].blue)-
1546e18977973bff5866de9aa6ed097aea40e27570d6cristy      (variance.direction[i].blue*variance.direction[i].blue))/
1547e18977973bff5866de9aa6ed097aea40e27570d6cristy      ((double) number_grays*number_grays*number_grays*number_grays);
15484c08aed51c5899665ade97263692328eea4af106cristy    if (image->colorspace == CMYKColorspace)
1549d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].difference_variance[i]=
15504c08aed51c5899665ade97263692328eea4af106cristy        (((double) number_grays*number_grays*sum_squares.direction[i].black)-
15514c08aed51c5899665ade97263692328eea4af106cristy        (variance.direction[i].black*variance.direction[i].black))/
15524c08aed51c5899665ade97263692328eea4af106cristy        ((double) number_grays*number_grays*number_grays*number_grays);
155317f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1554d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].difference_variance[i]=
15554c08aed51c5899665ade97263692328eea4af106cristy        (((double) number_grays*number_grays*sum_squares.direction[i].alpha)-
15564c08aed51c5899665ade97263692328eea4af106cristy        (variance.direction[i].alpha*variance.direction[i].alpha))/
1557e18977973bff5866de9aa6ed097aea40e27570d6cristy        ((double) number_grays*number_grays*number_grays*number_grays);
1558e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy    /*
1559e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy      Information Measures of Correlation.
1560e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy    */
1561d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].measure_of_correlation_1[i]=
1562ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_xy.direction[i].red-entropy_xy1.direction[i].red)/
1563ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_x.direction[i].red > entropy_y.direction[i].red ?
1564ffa10d0fe53ef0f0db63ee506c52695595976b99cristy       entropy_x.direction[i].red : entropy_y.direction[i].red);
1565d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].measure_of_correlation_1[i]=
1566ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_xy.direction[i].green-entropy_xy1.direction[i].green)/
1567ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_x.direction[i].green > entropy_y.direction[i].green ?
1568ffa10d0fe53ef0f0db63ee506c52695595976b99cristy       entropy_x.direction[i].green : entropy_y.direction[i].green);
1569d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].measure_of_correlation_1[i]=
1570ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_xy.direction[i].blue-entropy_xy1.direction[i].blue)/
1571ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      (entropy_x.direction[i].blue > entropy_y.direction[i].blue ?
1572ffa10d0fe53ef0f0db63ee506c52695595976b99cristy       entropy_x.direction[i].blue : entropy_y.direction[i].blue);
1573e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy    if (image->colorspace == CMYKColorspace)
1574d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].measure_of_correlation_1[i]=
15754c08aed51c5899665ade97263692328eea4af106cristy        (entropy_xy.direction[i].black-entropy_xy1.direction[i].black)/
15764c08aed51c5899665ade97263692328eea4af106cristy        (entropy_x.direction[i].black > entropy_y.direction[i].black ?
15774c08aed51c5899665ade97263692328eea4af106cristy         entropy_x.direction[i].black : entropy_y.direction[i].black);
157817f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1579d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].measure_of_correlation_1[i]=
15804c08aed51c5899665ade97263692328eea4af106cristy        (entropy_xy.direction[i].alpha-entropy_xy1.direction[i].alpha)/
15814c08aed51c5899665ade97263692328eea4af106cristy        (entropy_x.direction[i].alpha > entropy_y.direction[i].alpha ?
15824c08aed51c5899665ade97263692328eea4af106cristy         entropy_x.direction[i].alpha : entropy_y.direction[i].alpha);
1583d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[RedPixelChannel].measure_of_correlation_2[i]=
15843bdd925dbb0804df99e548c50667670319655816cristy      (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].red-
1585ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_xy.direction[i].red)))));
1586d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[GreenPixelChannel].measure_of_correlation_2[i]=
15873bdd925dbb0804df99e548c50667670319655816cristy      (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].green-
1588ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_xy.direction[i].green)))));
1589d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy    channel_features[BluePixelChannel].measure_of_correlation_2[i]=
15903bdd925dbb0804df99e548c50667670319655816cristy      (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].blue-
1591ffa10d0fe53ef0f0db63ee506c52695595976b99cristy      entropy_xy.direction[i].blue)))));
1592e0acabf3ebf7da9eb716f98dfbbaa0d2f4d9d7e4cristy    if (image->colorspace == CMYKColorspace)
1593d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[BlackPixelChannel].measure_of_correlation_2[i]=
15943bdd925dbb0804df99e548c50667670319655816cristy        (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].black-
15954c08aed51c5899665ade97263692328eea4af106cristy        entropy_xy.direction[i].black)))));
159617f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
1597d3090f92b8f346df0a8191d5aa3f9d14be5a32d5cristy      channel_features[AlphaPixelChannel].measure_of_correlation_2[i]=
15983bdd925dbb0804df99e548c50667670319655816cristy        (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].alpha-
15994c08aed51c5899665ade97263692328eea4af106cristy        entropy_xy.direction[i].alpha)))));
1600e18977973bff5866de9aa6ed097aea40e27570d6cristy  }
1601e18977973bff5866de9aa6ed097aea40e27570d6cristy  /*
16022fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Compute more texture features.
16032fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
16042fc10e5aedab9144531a4668dc7526e3caf514e1cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
16052fc10e5aedab9144531a4668dc7526e3caf514e1cristy  #pragma omp parallel for schedule(static,4) shared(status) \
16062fc10e5aedab9144531a4668dc7526e3caf514e1cristy    magick_threads(image,image,number_grays,1)
16072fc10e5aedab9144531a4668dc7526e3caf514e1cristy#endif
16082fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i < 4; i++)
16092fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
16102fc10e5aedab9144531a4668dc7526e3caf514e1cristy    ssize_t
16112fc10e5aedab9144531a4668dc7526e3caf514e1cristy      z;
16122fc10e5aedab9144531a4668dc7526e3caf514e1cristy
16132fc10e5aedab9144531a4668dc7526e3caf514e1cristy    for (z=0; z < (ssize_t) number_grays; z++)
16142fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
16152fc10e5aedab9144531a4668dc7526e3caf514e1cristy      register ssize_t
16162fc10e5aedab9144531a4668dc7526e3caf514e1cristy        y;
16172fc10e5aedab9144531a4668dc7526e3caf514e1cristy
16182fc10e5aedab9144531a4668dc7526e3caf514e1cristy      ChannelStatistics
16192fc10e5aedab9144531a4668dc7526e3caf514e1cristy        pixel;
16202fc10e5aedab9144531a4668dc7526e3caf514e1cristy
16212fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
16222fc10e5aedab9144531a4668dc7526e3caf514e1cristy      for (y=0; y < (ssize_t) number_grays; y++)
16232fc10e5aedab9144531a4668dc7526e3caf514e1cristy      {
16242fc10e5aedab9144531a4668dc7526e3caf514e1cristy        register ssize_t
16252fc10e5aedab9144531a4668dc7526e3caf514e1cristy          x;
16262fc10e5aedab9144531a4668dc7526e3caf514e1cristy
16272fc10e5aedab9144531a4668dc7526e3caf514e1cristy        for (x=0; x < (ssize_t) number_grays; x++)
16282fc10e5aedab9144531a4668dc7526e3caf514e1cristy        {
16292fc10e5aedab9144531a4668dc7526e3caf514e1cristy          /*
16302fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Contrast:  amount of local variations present in an image.
16312fc10e5aedab9144531a4668dc7526e3caf514e1cristy          */
16322fc10e5aedab9144531a4668dc7526e3caf514e1cristy          if (((y-x) == z) || ((x-y) == z))
16332fc10e5aedab9144531a4668dc7526e3caf514e1cristy            {
16342fc10e5aedab9144531a4668dc7526e3caf514e1cristy              pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
16352fc10e5aedab9144531a4668dc7526e3caf514e1cristy              pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
16362fc10e5aedab9144531a4668dc7526e3caf514e1cristy              pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
16372fc10e5aedab9144531a4668dc7526e3caf514e1cristy              if (image->colorspace == CMYKColorspace)
16382fc10e5aedab9144531a4668dc7526e3caf514e1cristy                pixel.direction[i].black+=cooccurrence[x][y].direction[i].black;
163917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy              if (image->alpha_trait != UndefinedPixelTrait)
16402fc10e5aedab9144531a4668dc7526e3caf514e1cristy                pixel.direction[i].alpha+=
16412fc10e5aedab9144531a4668dc7526e3caf514e1cristy                  cooccurrence[x][y].direction[i].alpha;
16422fc10e5aedab9144531a4668dc7526e3caf514e1cristy            }
16432fc10e5aedab9144531a4668dc7526e3caf514e1cristy          /*
16442fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Maximum Correlation Coefficient.
16452fc10e5aedab9144531a4668dc7526e3caf514e1cristy          */
16462fc10e5aedab9144531a4668dc7526e3caf514e1cristy          Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red*
16472fc10e5aedab9144531a4668dc7526e3caf514e1cristy            cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/
16482fc10e5aedab9144531a4668dc7526e3caf514e1cristy            density_y[x].direction[i].red;
16492fc10e5aedab9144531a4668dc7526e3caf514e1cristy          Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green*
16502fc10e5aedab9144531a4668dc7526e3caf514e1cristy            cooccurrence[y][x].direction[i].green/
16512fc10e5aedab9144531a4668dc7526e3caf514e1cristy            density_x[z].direction[i].green/density_y[x].direction[i].red;
16522fc10e5aedab9144531a4668dc7526e3caf514e1cristy          Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue*
16532fc10e5aedab9144531a4668dc7526e3caf514e1cristy            cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/
16542fc10e5aedab9144531a4668dc7526e3caf514e1cristy            density_y[x].direction[i].blue;
16552fc10e5aedab9144531a4668dc7526e3caf514e1cristy          if (image->colorspace == CMYKColorspace)
16562fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black*
16572fc10e5aedab9144531a4668dc7526e3caf514e1cristy              cooccurrence[y][x].direction[i].black/
16582fc10e5aedab9144531a4668dc7526e3caf514e1cristy              density_x[z].direction[i].black/density_y[x].direction[i].black;
165917f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy          if (image->alpha_trait != UndefinedPixelTrait)
16602fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Q[z][y].direction[i].alpha+=
16612fc10e5aedab9144531a4668dc7526e3caf514e1cristy              cooccurrence[z][x].direction[i].alpha*
16622fc10e5aedab9144531a4668dc7526e3caf514e1cristy              cooccurrence[y][x].direction[i].alpha/
16632fc10e5aedab9144531a4668dc7526e3caf514e1cristy              density_x[z].direction[i].alpha/
16642fc10e5aedab9144531a4668dc7526e3caf514e1cristy              density_y[x].direction[i].alpha;
16652fc10e5aedab9144531a4668dc7526e3caf514e1cristy        }
16662fc10e5aedab9144531a4668dc7526e3caf514e1cristy      }
16672fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[RedPixelChannel].contrast[i]+=z*z*
16682fc10e5aedab9144531a4668dc7526e3caf514e1cristy        pixel.direction[i].red;
16692fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[GreenPixelChannel].contrast[i]+=z*z*
16702fc10e5aedab9144531a4668dc7526e3caf514e1cristy        pixel.direction[i].green;
16712fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[BluePixelChannel].contrast[i]+=z*z*
16722fc10e5aedab9144531a4668dc7526e3caf514e1cristy        pixel.direction[i].blue;
16732fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (image->colorspace == CMYKColorspace)
16742fc10e5aedab9144531a4668dc7526e3caf514e1cristy        channel_features[BlackPixelChannel].contrast[i]+=z*z*
16752fc10e5aedab9144531a4668dc7526e3caf514e1cristy          pixel.direction[i].black;
167617f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy      if (image->alpha_trait != UndefinedPixelTrait)
16772fc10e5aedab9144531a4668dc7526e3caf514e1cristy        channel_features[AlphaPixelChannel].contrast[i]+=z*z*
16782fc10e5aedab9144531a4668dc7526e3caf514e1cristy          pixel.direction[i].alpha;
16792fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
16802fc10e5aedab9144531a4668dc7526e3caf514e1cristy    /*
16812fc10e5aedab9144531a4668dc7526e3caf514e1cristy      Maximum Correlation Coefficient.
16822fc10e5aedab9144531a4668dc7526e3caf514e1cristy      Future: return second largest eigenvalue of Q.
16832fc10e5aedab9144531a4668dc7526e3caf514e1cristy    */
16842fc10e5aedab9144531a4668dc7526e3caf514e1cristy    channel_features[RedPixelChannel].maximum_correlation_coefficient[i]=
16852fc10e5aedab9144531a4668dc7526e3caf514e1cristy      sqrt((double) -1.0);
16862fc10e5aedab9144531a4668dc7526e3caf514e1cristy    channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]=
16872fc10e5aedab9144531a4668dc7526e3caf514e1cristy      sqrt((double) -1.0);
16882fc10e5aedab9144531a4668dc7526e3caf514e1cristy    channel_features[BluePixelChannel].maximum_correlation_coefficient[i]=
16892fc10e5aedab9144531a4668dc7526e3caf514e1cristy      sqrt((double) -1.0);
16902fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (image->colorspace == CMYKColorspace)
16912fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]=
16922fc10e5aedab9144531a4668dc7526e3caf514e1cristy        sqrt((double) -1.0);
169317f11b056210f082a6d0e54ac5d68e6d72fa76b2cristy    if (image->alpha_trait != UndefinedPixelTrait)
16942fc10e5aedab9144531a4668dc7526e3caf514e1cristy      channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]=
16952fc10e5aedab9144531a4668dc7526e3caf514e1cristy        sqrt((double) -1.0);
16962fc10e5aedab9144531a4668dc7526e3caf514e1cristy  }
16972fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
16982fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Relinquish resources.
16992fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
17002fc10e5aedab9144531a4668dc7526e3caf514e1cristy  sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
17012fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i < (ssize_t) number_grays; i++)
17022fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
17032fc10e5aedab9144531a4668dc7526e3caf514e1cristy  Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
17042fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
17052fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
17062fc10e5aedab9144531a4668dc7526e3caf514e1cristy  density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
17072fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (i=0; i < (ssize_t) number_grays; i++)
17082fc10e5aedab9144531a4668dc7526e3caf514e1cristy    cooccurrence[i]=(ChannelStatistics *)
17092fc10e5aedab9144531a4668dc7526e3caf514e1cristy      RelinquishMagickMemory(cooccurrence[i]);
17102fc10e5aedab9144531a4668dc7526e3caf514e1cristy  cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
17112fc10e5aedab9144531a4668dc7526e3caf514e1cristy  return(channel_features);
17122fc10e5aedab9144531a4668dc7526e3caf514e1cristy}
17132fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17142fc10e5aedab9144531a4668dc7526e3caf514e1cristy/*
17152fc10e5aedab9144531a4668dc7526e3caf514e1cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17162fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17172fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17182fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17192fc10e5aedab9144531a4668dc7526e3caf514e1cristy%     H o u g h L i n e I m a g e                                             %
17202fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17212fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17222fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
17232fc10e5aedab9144531a4668dc7526e3caf514e1cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17242fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
1725ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  Use HoughLineImage() in conjunction with any binary edge extracted image (we
1726ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  recommand Canny) to identify lines in the image.  The algorithm accumulates
1727ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  counts for every white pixel for every possible orientation (for angles from
1728ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  0 to 179 in 1 degree increments) and distance from the center of the image to
1729ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  the corner (in 1 px increments) and stores the counts in an accumulator matrix
1730ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  of angle vs distance. The size of the accumulator is 180x(diagonal/2). Next
1731ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  it searches this space for peaks in counts and converts the locations of the
1732ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  peaks to slope and intercept in the normal x,y input image space. Use the
1733ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  slope/intercepts to find the endpoints clipped to the bounds of the image. The
1734ec9a82a7d6938f4ff9993500e150cac3628c0e4bcristy%  lines are then drawn. The counts are a measure of the length of the lines
17352fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17362fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  The format of the HoughLineImage method is:
17372fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17382fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      Image *HoughLineImage(const Image *image,const size_t width,
17392fc10e5aedab9144531a4668dc7526e3caf514e1cristy%        const size_t height,const size_t threshold,ExceptionInfo *exception)
17402fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17412fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  A description of each parameter follows:
17422fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17432fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o image: the image.
17442fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17452fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o width, height: find line pairs as local maxima in this neighborhood.
17462fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17472fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o threshold: the line count threshold.
17482fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17492fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o exception: return any errors or warnings in this structure.
17502fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
17512fc10e5aedab9144531a4668dc7526e3caf514e1cristy*/
17522fc10e5aedab9144531a4668dc7526e3caf514e1cristy
17532fc10e5aedab9144531a4668dc7526e3caf514e1cristystatic inline double MagickRound(double x)
17542fc10e5aedab9144531a4668dc7526e3caf514e1cristy{
17552fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
17562fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Round the fraction to nearest integer.
17572fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
17582fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if ((x-floor(x)) < (ceil(x)-x))
17592fc10e5aedab9144531a4668dc7526e3caf514e1cristy    return(floor(x));
17602fc10e5aedab9144531a4668dc7526e3caf514e1cristy  return(ceil(x));
17612fc10e5aedab9144531a4668dc7526e3caf514e1cristy}
17622fc10e5aedab9144531a4668dc7526e3caf514e1cristy
1763cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristystatic Image *RenderHoughLines(const ImageInfo *image_info,const size_t columns,
1764cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  const size_t rows,ExceptionInfo *exception)
1765cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy{
1766cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy#define BoundingBox  "viewbox"
1767cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy
1768cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  DrawInfo
1769cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    *draw_info;
1770cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy
1771cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  Image
1772cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    *image;
1773cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy
1774cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  MagickBooleanType
1775cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    status;
1776cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy
1777cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  /*
1778cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    Open image.
1779cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  */
1780cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  image=AcquireImage(image_info,exception);
1781cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1782cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  if (status == MagickFalse)
1783cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    {
1784cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy      image=DestroyImageList(image);
1785cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy      return((Image *) NULL);
1786cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    }
1787cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  image->columns=columns;
1788cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  image->rows=rows;
1789cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
1790cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  draw_info->affine.sx=image->resolution.x == 0.0 ? 1.0 : image->resolution.x/
1791cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    DefaultResolution;
1792cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  draw_info->affine.sy=image->resolution.y == 0.0 ? 1.0 : image->resolution.y/
1793cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    DefaultResolution;
1794cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  image->columns=(size_t) (draw_info->affine.sx*image->columns);
1795cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  image->rows=(size_t) (draw_info->affine.sy*image->rows);
1796cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  status=SetImageExtent(image,image->columns,image->rows,exception);
1797cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  if (status == MagickFalse)
1798cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    return(DestroyImageList(image));
1799cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  if (SetImageBackgroundColor(image,exception) == MagickFalse)
1800cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    {
1801cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy      image=DestroyImageList(image);
1802cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy      return((Image *) NULL);
1803cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    }
1804cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  /*
1805cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    Render drawing.
1806cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  */
1807cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  if (GetBlobStreamData(image) == (unsigned char *) NULL)
1808cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    draw_info->primitive=FileToString(image->filename,~0UL,exception);
1809cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  else
1810cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    {
1811cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy      draw_info->primitive=(char *) AcquireMagickMemory((size_t)
1812cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy        GetBlobSize(image)+1);
1813cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy      if (draw_info->primitive != (char *) NULL)
1814cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy        {
1815cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy          (void) CopyMagickMemory(draw_info->primitive,GetBlobStreamData(image),
1816cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy            (size_t) GetBlobSize(image));
1817cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy          draw_info->primitive[GetBlobSize(image)]='\0';
1818cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy        }
1819cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy     }
1820cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  (void) DrawImage(image,draw_info,exception);
1821cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  draw_info=DestroyDrawInfo(draw_info);
1822cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  (void) CloseBlob(image);
1823cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  return(GetFirstImageInList(image));
1824cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy}
1825cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy
18262fc10e5aedab9144531a4668dc7526e3caf514e1cristyMagickExport Image *HoughLineImage(const Image *image,const size_t width,
18272fc10e5aedab9144531a4668dc7526e3caf514e1cristy  const size_t height,const size_t threshold,ExceptionInfo *exception)
18282fc10e5aedab9144531a4668dc7526e3caf514e1cristy{
18298fbcf1ad6a1d07a92ef39435f679143697be4edacristy#define HoughLineImageTag  "HoughLine/Image"
18308fbcf1ad6a1d07a92ef39435f679143697be4edacristy
18312fc10e5aedab9144531a4668dc7526e3caf514e1cristy  CacheView
18322fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *image_view;
18332fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18342fc10e5aedab9144531a4668dc7526e3caf514e1cristy  char
1835151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    message[MagickPathExtent],
1836151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy    path[MagickPathExtent];
18372fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18382fc10e5aedab9144531a4668dc7526e3caf514e1cristy  const char
18392fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *artifact;
18402fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18412fc10e5aedab9144531a4668dc7526e3caf514e1cristy  double
18422fc10e5aedab9144531a4668dc7526e3caf514e1cristy    hough_height;
18432fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18442fc10e5aedab9144531a4668dc7526e3caf514e1cristy  Image
18452fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *lines_image = NULL;
18462fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18472fc10e5aedab9144531a4668dc7526e3caf514e1cristy  ImageInfo
18482fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *image_info;
18492fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18502fc10e5aedab9144531a4668dc7526e3caf514e1cristy  int
18512fc10e5aedab9144531a4668dc7526e3caf514e1cristy    file;
18522fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18532fc10e5aedab9144531a4668dc7526e3caf514e1cristy  MagickBooleanType
18542fc10e5aedab9144531a4668dc7526e3caf514e1cristy    status;
18552fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18568fbcf1ad6a1d07a92ef39435f679143697be4edacristy  MagickOffsetType
18578fbcf1ad6a1d07a92ef39435f679143697be4edacristy    progress;
18588fbcf1ad6a1d07a92ef39435f679143697be4edacristy
18592fc10e5aedab9144531a4668dc7526e3caf514e1cristy  MatrixInfo
18602fc10e5aedab9144531a4668dc7526e3caf514e1cristy    *accumulator;
18612fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18622fc10e5aedab9144531a4668dc7526e3caf514e1cristy  PointInfo
18632fc10e5aedab9144531a4668dc7526e3caf514e1cristy    center;
18642fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18652fc10e5aedab9144531a4668dc7526e3caf514e1cristy  register ssize_t
18662fc10e5aedab9144531a4668dc7526e3caf514e1cristy    y;
18672fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18682fc10e5aedab9144531a4668dc7526e3caf514e1cristy  size_t
18692fc10e5aedab9144531a4668dc7526e3caf514e1cristy    accumulator_height,
18702fc10e5aedab9144531a4668dc7526e3caf514e1cristy    accumulator_width,
18712fc10e5aedab9144531a4668dc7526e3caf514e1cristy    line_count;
18722fc10e5aedab9144531a4668dc7526e3caf514e1cristy
18732fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
18742fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Create the accumulator.
18752fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
18762fc10e5aedab9144531a4668dc7526e3caf514e1cristy  assert(image != (const Image *) NULL);
1877e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
18782fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (image->debug != MagickFalse)
18792fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
18802fc10e5aedab9144531a4668dc7526e3caf514e1cristy  assert(exception != (ExceptionInfo *) NULL);
1881e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
18822fc10e5aedab9144531a4668dc7526e3caf514e1cristy  accumulator_width=180;
18832fc10e5aedab9144531a4668dc7526e3caf514e1cristy  hough_height=((sqrt(2.0)*(double) (image->rows > image->columns ?
18842fc10e5aedab9144531a4668dc7526e3caf514e1cristy    image->rows : image->columns))/2.0);
18852fc10e5aedab9144531a4668dc7526e3caf514e1cristy  accumulator_height=(size_t) (2.0*hough_height);
18862fc10e5aedab9144531a4668dc7526e3caf514e1cristy  accumulator=AcquireMatrixInfo(accumulator_width,accumulator_height,
18872fc10e5aedab9144531a4668dc7526e3caf514e1cristy    sizeof(double),exception);
18882fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (accumulator == (MatrixInfo *) NULL)
18892fc10e5aedab9144531a4668dc7526e3caf514e1cristy    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
18902fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (NullMatrix(accumulator) == MagickFalse)
18912fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
18922fc10e5aedab9144531a4668dc7526e3caf514e1cristy      accumulator=DestroyMatrixInfo(accumulator);
18932fc10e5aedab9144531a4668dc7526e3caf514e1cristy      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
18942fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
18952fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
18962fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Populate the accumulator.
18972fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
18982fc10e5aedab9144531a4668dc7526e3caf514e1cristy  status=MagickTrue;
18998fbcf1ad6a1d07a92ef39435f679143697be4edacristy  progress=0;
19002fc10e5aedab9144531a4668dc7526e3caf514e1cristy  center.x=(double) image->columns/2.0;
19012fc10e5aedab9144531a4668dc7526e3caf514e1cristy  center.y=(double) image->rows/2.0;
19022fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_view=AcquireVirtualCacheView(image,exception);
19032fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (y=0; y < (ssize_t) image->rows; y++)
19042fc10e5aedab9144531a4668dc7526e3caf514e1cristy  {
19052fc10e5aedab9144531a4668dc7526e3caf514e1cristy    register const Quantum
190605d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
19072fc10e5aedab9144531a4668dc7526e3caf514e1cristy
19082fc10e5aedab9144531a4668dc7526e3caf514e1cristy    register ssize_t
19092fc10e5aedab9144531a4668dc7526e3caf514e1cristy      x;
19102fc10e5aedab9144531a4668dc7526e3caf514e1cristy
19112fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (status == MagickFalse)
19122fc10e5aedab9144531a4668dc7526e3caf514e1cristy      continue;
19132fc10e5aedab9144531a4668dc7526e3caf514e1cristy    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
19142fc10e5aedab9144531a4668dc7526e3caf514e1cristy    if (p == (Quantum *) NULL)
19152fc10e5aedab9144531a4668dc7526e3caf514e1cristy      {
19162fc10e5aedab9144531a4668dc7526e3caf514e1cristy        status=MagickFalse;
19172fc10e5aedab9144531a4668dc7526e3caf514e1cristy        continue;
19182fc10e5aedab9144531a4668dc7526e3caf514e1cristy      }
19192fc10e5aedab9144531a4668dc7526e3caf514e1cristy    for (x=0; x < (ssize_t) image->columns; x++)
19202fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
19212e03529bdd5a322e00e9b1d4655181c6f09ca768cristy      if (GetPixelIntensity(image,p) > (QuantumRange/2.0))
19222fc10e5aedab9144531a4668dc7526e3caf514e1cristy        {
19232fc10e5aedab9144531a4668dc7526e3caf514e1cristy          register ssize_t
19242fc10e5aedab9144531a4668dc7526e3caf514e1cristy            i;
19252fc10e5aedab9144531a4668dc7526e3caf514e1cristy
19262fc10e5aedab9144531a4668dc7526e3caf514e1cristy          for (i=0; i < 180; i++)
19272fc10e5aedab9144531a4668dc7526e3caf514e1cristy          {
19282fc10e5aedab9144531a4668dc7526e3caf514e1cristy            double
19292fc10e5aedab9144531a4668dc7526e3caf514e1cristy              count,
19302fc10e5aedab9144531a4668dc7526e3caf514e1cristy              radius;
19312fc10e5aedab9144531a4668dc7526e3caf514e1cristy
19322fc10e5aedab9144531a4668dc7526e3caf514e1cristy            radius=(((double) x-center.x)*cos(DegreesToRadians((double) i)))+
19332fc10e5aedab9144531a4668dc7526e3caf514e1cristy              (((double) y-center.y)*sin(DegreesToRadians((double) i)));
19342fc10e5aedab9144531a4668dc7526e3caf514e1cristy            (void) GetMatrixElement(accumulator,i,(ssize_t)
19352fc10e5aedab9144531a4668dc7526e3caf514e1cristy              MagickRound(radius+hough_height),&count);
19362fc10e5aedab9144531a4668dc7526e3caf514e1cristy            count++;
19372fc10e5aedab9144531a4668dc7526e3caf514e1cristy            (void) SetMatrixElement(accumulator,i,(ssize_t)
19382fc10e5aedab9144531a4668dc7526e3caf514e1cristy              MagickRound(radius+hough_height),&count);
19392fc10e5aedab9144531a4668dc7526e3caf514e1cristy          }
19402fc10e5aedab9144531a4668dc7526e3caf514e1cristy        }
19412fc10e5aedab9144531a4668dc7526e3caf514e1cristy      p+=GetPixelChannels(image);
19422fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
19438fbcf1ad6a1d07a92ef39435f679143697be4edacristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
19448fbcf1ad6a1d07a92ef39435f679143697be4edacristy      {
19458fbcf1ad6a1d07a92ef39435f679143697be4edacristy        MagickBooleanType
19468fbcf1ad6a1d07a92ef39435f679143697be4edacristy          proceed;
19478fbcf1ad6a1d07a92ef39435f679143697be4edacristy
19488fbcf1ad6a1d07a92ef39435f679143697be4edacristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
19498fbcf1ad6a1d07a92ef39435f679143697be4edacristy        #pragma omp critical (MagickCore_CannyEdgeImage)
19508fbcf1ad6a1d07a92ef39435f679143697be4edacristy#endif
19518fbcf1ad6a1d07a92ef39435f679143697be4edacristy        proceed=SetImageProgress(image,CannyEdgeImageTag,progress++,
19528fbcf1ad6a1d07a92ef39435f679143697be4edacristy          image->rows);
19538fbcf1ad6a1d07a92ef39435f679143697be4edacristy        if (proceed == MagickFalse)
19548fbcf1ad6a1d07a92ef39435f679143697be4edacristy          status=MagickFalse;
19558fbcf1ad6a1d07a92ef39435f679143697be4edacristy      }
19562fc10e5aedab9144531a4668dc7526e3caf514e1cristy  }
19572fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_view=DestroyCacheView(image_view);
19582fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (status == MagickFalse)
19592fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
19602fc10e5aedab9144531a4668dc7526e3caf514e1cristy      accumulator=DestroyMatrixInfo(accumulator);
19612fc10e5aedab9144531a4668dc7526e3caf514e1cristy      return((Image *) NULL);
19622fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
19632fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
19642fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Generate line segments from accumulator.
1965e18977973bff5866de9aa6ed097aea40e27570d6cristy  */
19662fc10e5aedab9144531a4668dc7526e3caf514e1cristy  file=AcquireUniqueFileResource(path);
19672fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (file == -1)
19682fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
19692fc10e5aedab9144531a4668dc7526e3caf514e1cristy      accumulator=DestroyMatrixInfo(accumulator);
19702fc10e5aedab9144531a4668dc7526e3caf514e1cristy      return((Image *) NULL);
19712fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
1972151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy  (void) FormatLocaleString(message,MagickPathExtent,
19732fc10e5aedab9144531a4668dc7526e3caf514e1cristy    "# Hough line transform: %.20gx%.20g%+.20g\n",(double) width,
19742fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (double) height,(double) threshold);
197596498423d48af3032b4ad554b12e347e0231eb45cristy  if (write(file,message,strlen(message)) != (ssize_t) strlen(message))
1976233483c36e4f75e4337766b87cae1ab13d8b92a8cristy    status=MagickFalse;
1977cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  (void) FormatLocaleString(message,MagickPathExtent,
1978cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy    "viewbox 0 0 %.20g %.20g\n",(double) image->columns,(double) image->rows);
197996498423d48af3032b4ad554b12e347e0231eb45cristy  if (write(file,message,strlen(message)) != (ssize_t) strlen(message))
1980233483c36e4f75e4337766b87cae1ab13d8b92a8cristy    status=MagickFalse;
19812fc10e5aedab9144531a4668dc7526e3caf514e1cristy  line_count=image->columns > image->rows ? image->columns/4 : image->rows/4;
19822fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (threshold != 0)
19832fc10e5aedab9144531a4668dc7526e3caf514e1cristy    line_count=threshold;
19842fc10e5aedab9144531a4668dc7526e3caf514e1cristy  for (y=0; y < (ssize_t) accumulator_height; y++)
19853a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy  {
19862fc10e5aedab9144531a4668dc7526e3caf514e1cristy    register ssize_t
19872fc10e5aedab9144531a4668dc7526e3caf514e1cristy      x;
1988564a56979706a30a3d0f920fd5f538a408efd4f1cristy
19892fc10e5aedab9144531a4668dc7526e3caf514e1cristy    for (x=0; x < (ssize_t) accumulator_width; x++)
19903a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy    {
19912fc10e5aedab9144531a4668dc7526e3caf514e1cristy      double
19922fc10e5aedab9144531a4668dc7526e3caf514e1cristy        count;
19933a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy
19942fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (void) GetMatrixElement(accumulator,x,y,&count);
19952fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (count >= (double) line_count)
19962fc10e5aedab9144531a4668dc7526e3caf514e1cristy        {
19972fc10e5aedab9144531a4668dc7526e3caf514e1cristy          double
19982fc10e5aedab9144531a4668dc7526e3caf514e1cristy            maxima;
1999bd82207b3816471fba4de916fca7336f38b70493cristy
20002fc10e5aedab9144531a4668dc7526e3caf514e1cristy          SegmentInfo
20012fc10e5aedab9144531a4668dc7526e3caf514e1cristy            line;
20022fc10e5aedab9144531a4668dc7526e3caf514e1cristy
20032fc10e5aedab9144531a4668dc7526e3caf514e1cristy          ssize_t
20042fc10e5aedab9144531a4668dc7526e3caf514e1cristy            v;
2005bd82207b3816471fba4de916fca7336f38b70493cristy
20063a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy          /*
20072fc10e5aedab9144531a4668dc7526e3caf514e1cristy            Is point a local maxima?
20083a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy          */
20092fc10e5aedab9144531a4668dc7526e3caf514e1cristy          maxima=count;
20102494e3d5cea70ebef8e12498b10bc2e24da4803fcristy          for (v=(-((ssize_t) height/2)); v <= (((ssize_t) height/2)); v++)
20112fc10e5aedab9144531a4668dc7526e3caf514e1cristy          {
20122fc10e5aedab9144531a4668dc7526e3caf514e1cristy            ssize_t
20132fc10e5aedab9144531a4668dc7526e3caf514e1cristy              u;
20142fc10e5aedab9144531a4668dc7526e3caf514e1cristy
20152494e3d5cea70ebef8e12498b10bc2e24da4803fcristy            for (u=(-((ssize_t) width/2)); u <= (((ssize_t) width/2)); u++)
20163a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy            {
20172fc10e5aedab9144531a4668dc7526e3caf514e1cristy              if ((u != 0) || (v !=0))
20182fc10e5aedab9144531a4668dc7526e3caf514e1cristy                {
20192fc10e5aedab9144531a4668dc7526e3caf514e1cristy                  (void) GetMatrixElement(accumulator,x+u,y+v,&count);
20202fc10e5aedab9144531a4668dc7526e3caf514e1cristy                  if (count > maxima)
20212fc10e5aedab9144531a4668dc7526e3caf514e1cristy                    {
20222fc10e5aedab9144531a4668dc7526e3caf514e1cristy                      maxima=count;
20232fc10e5aedab9144531a4668dc7526e3caf514e1cristy                      break;
20242fc10e5aedab9144531a4668dc7526e3caf514e1cristy                    }
20252fc10e5aedab9144531a4668dc7526e3caf514e1cristy                }
20263a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy            }
20272fc10e5aedab9144531a4668dc7526e3caf514e1cristy            if (u < (ssize_t) (width/2))
20282fc10e5aedab9144531a4668dc7526e3caf514e1cristy              break;
20292fc10e5aedab9144531a4668dc7526e3caf514e1cristy          }
20302fc10e5aedab9144531a4668dc7526e3caf514e1cristy          (void) GetMatrixElement(accumulator,x,y,&count);
20312fc10e5aedab9144531a4668dc7526e3caf514e1cristy          if (maxima > count)
20322fc10e5aedab9144531a4668dc7526e3caf514e1cristy            continue;
20332fc10e5aedab9144531a4668dc7526e3caf514e1cristy          if ((x >= 45) && (x <= 135))
20342fc10e5aedab9144531a4668dc7526e3caf514e1cristy            {
20352fc10e5aedab9144531a4668dc7526e3caf514e1cristy              /*
20362fc10e5aedab9144531a4668dc7526e3caf514e1cristy                y = (r-x cos(t))/sin(t)
20372fc10e5aedab9144531a4668dc7526e3caf514e1cristy              */
20382fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.x1=0.0;
20392fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.y1=((double) (y-(accumulator_height/2.0))-((line.x1-
20402fc10e5aedab9144531a4668dc7526e3caf514e1cristy                (image->columns/2.0))*cos(DegreesToRadians((double) x))))/
20412fc10e5aedab9144531a4668dc7526e3caf514e1cristy                sin(DegreesToRadians((double) x))+(image->rows/2.0);
20422fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.x2=(double) image->columns;
20432fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.y2=((double) (y-(accumulator_height/2.0))-((line.x2-
20442fc10e5aedab9144531a4668dc7526e3caf514e1cristy                (image->columns/2.0))*cos(DegreesToRadians((double) x))))/
20452fc10e5aedab9144531a4668dc7526e3caf514e1cristy                sin(DegreesToRadians((double) x))+(image->rows/2.0);
20462fc10e5aedab9144531a4668dc7526e3caf514e1cristy            }
20472fc10e5aedab9144531a4668dc7526e3caf514e1cristy          else
20482fc10e5aedab9144531a4668dc7526e3caf514e1cristy            {
20492fc10e5aedab9144531a4668dc7526e3caf514e1cristy              /*
20502fc10e5aedab9144531a4668dc7526e3caf514e1cristy                x = (r-y cos(t))/sin(t)
20512fc10e5aedab9144531a4668dc7526e3caf514e1cristy              */
20522fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.y1=0.0;
20532fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.x1=((double) (y-(accumulator_height/2.0))-((line.y1-
20542fc10e5aedab9144531a4668dc7526e3caf514e1cristy                (image->rows/2.0))*sin(DegreesToRadians((double) x))))/
20552fc10e5aedab9144531a4668dc7526e3caf514e1cristy                cos(DegreesToRadians((double) x))+(image->columns/2.0);
20562fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.y2=(double) image->rows;
20572fc10e5aedab9144531a4668dc7526e3caf514e1cristy              line.x2=((double) (y-(accumulator_height/2.0))-((line.y2-
20582fc10e5aedab9144531a4668dc7526e3caf514e1cristy                (image->rows/2.0))*sin(DegreesToRadians((double) x))))/
20592fc10e5aedab9144531a4668dc7526e3caf514e1cristy                cos(DegreesToRadians((double) x))+(image->columns/2.0);
20602fc10e5aedab9144531a4668dc7526e3caf514e1cristy            }
2061151b66dffc9e3c2e8c4f8cdaca37ff987ca0f497cristy          (void) FormatLocaleString(message,MagickPathExtent,
20622fc10e5aedab9144531a4668dc7526e3caf514e1cristy            "line %g,%g %g,%g  # %g\n",line.x1,line.y1,line.x2,line.y2,maxima);
206396498423d48af3032b4ad554b12e347e0231eb45cristy          if (write(file,message,strlen(message)) != (ssize_t) strlen(message))
2064233483c36e4f75e4337766b87cae1ab13d8b92a8cristy            status=MagickFalse;
20653a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy        }
20663a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy    }
20673a82f25dfc8e47ea3049f3a474a8c79ebc268926cristy  }
20682fc10e5aedab9144531a4668dc7526e3caf514e1cristy  (void) close(file);
20697e9726d27d1b1e079b9b94b78f5ffd49352fcc91cristy  /*
20702fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Render lines to image canvas.
2071f2bf2c76eb0f9a013d0a4dd51d4b86d3d714ddf2cristy  */
20722fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_info=AcquireImageInfo();
20732fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_info->background_color=image->background_color;
2074cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  (void) FormatLocaleString(image_info->filename,MagickPathExtent,"%s",path);
20752fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"background");
20762fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (artifact != (const char *) NULL)
20772fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) SetImageOption(image_info,"background",artifact);
20782fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"fill");
20792fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (artifact != (const char *) NULL)
20802fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) SetImageOption(image_info,"fill",artifact);
20812fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"stroke");
20822fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (artifact != (const char *) NULL)
20832fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) SetImageOption(image_info,"stroke",artifact);
20842fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"strokewidth");
20852fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if (artifact != (const char *) NULL)
20862fc10e5aedab9144531a4668dc7526e3caf514e1cristy    (void) SetImageOption(image_info,"strokewidth",artifact);
2087cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy  lines_image=RenderHoughLines(image_info,image->columns,image->rows,exception);
20882fc10e5aedab9144531a4668dc7526e3caf514e1cristy  artifact=GetImageArtifact(image,"hough-lines:accumulator");
20892fc10e5aedab9144531a4668dc7526e3caf514e1cristy  if ((lines_image != (Image *) NULL) &&
20902fc10e5aedab9144531a4668dc7526e3caf514e1cristy      (IsStringTrue(artifact) != MagickFalse))
20912fc10e5aedab9144531a4668dc7526e3caf514e1cristy    {
20922fc10e5aedab9144531a4668dc7526e3caf514e1cristy      Image
20932fc10e5aedab9144531a4668dc7526e3caf514e1cristy        *accumulator_image;
20942fc10e5aedab9144531a4668dc7526e3caf514e1cristy
20952fc10e5aedab9144531a4668dc7526e3caf514e1cristy      accumulator_image=MatrixToImage(accumulator,exception);
20962fc10e5aedab9144531a4668dc7526e3caf514e1cristy      if (accumulator_image != (Image *) NULL)
20972fc10e5aedab9144531a4668dc7526e3caf514e1cristy        AppendImageToList(&lines_image,accumulator_image);
20982fc10e5aedab9144531a4668dc7526e3caf514e1cristy    }
20992fc10e5aedab9144531a4668dc7526e3caf514e1cristy  /*
21002fc10e5aedab9144531a4668dc7526e3caf514e1cristy    Free resources.
21012fc10e5aedab9144531a4668dc7526e3caf514e1cristy  */
21022fc10e5aedab9144531a4668dc7526e3caf514e1cristy  accumulator=DestroyMatrixInfo(accumulator);
21032fc10e5aedab9144531a4668dc7526e3caf514e1cristy  image_info=DestroyImageInfo(image_info);
21042fc10e5aedab9144531a4668dc7526e3caf514e1cristy  (void) RelinquishUniqueFileResource(path);
21052fc10e5aedab9144531a4668dc7526e3caf514e1cristy  return(GetFirstImageInList(lines_image));
21062fc10e5aedab9144531a4668dc7526e3caf514e1cristy}
21072fc10e5aedab9144531a4668dc7526e3caf514e1cristy
21082fc10e5aedab9144531a4668dc7526e3caf514e1cristy/*
21092fc10e5aedab9144531a4668dc7526e3caf514e1cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21102fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
21112fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
21122fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
21132fc10e5aedab9144531a4668dc7526e3caf514e1cristy%     M e a n S h i f t I m a g e                                             %
21142fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
21152fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
21162fc10e5aedab9144531a4668dc7526e3caf514e1cristy%                                                                             %
21172fc10e5aedab9144531a4668dc7526e3caf514e1cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21182fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
2119c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  MeanShiftImage() delineate arbitrarily shaped clusters in the image. For
2120c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  each pixel, it visits all the pixels in the neighborhood specified by
2121c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  the window centered at the pixel and excludes those that are outside the
2122c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  radius=(window-1)/2 surrounding the pixel. From those pixels, it finds those
2123c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  that are within the specified color distance from the current mean, and
2124c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  computes a new x,y centroid from those coordinates and a new mean. This new
2125c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  x,y centroid is used as the center for a new window. This process iterates
2126c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  until it converges and the final mean is replaces the (original window
2127cfbe890d0cfcd5d3b0f63744a6901e40e992e07cCristy%  center) pixel value. It repeats this process for the next pixel, etc.,
2128c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  until it processes all pixels in the image. Results are typically better with
2129c904cab8ecc6ca7aa6a24d3a0f74991d23494829cristy%  colorspaces other than sRGB. We recommend YIQ, YUV or YCbCr.
21302fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
21312fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  The format of the MeanShiftImage method is:
21322fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
21332fc10e5aedab9144531a4668dc7526e3caf514e1cristy%      Image *MeanShiftImage(const Image *image,const size_t width,
21346aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy%        const size_t height,const double color_distance,
21356aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy%        ExceptionInfo *exception)
21362fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
21372fc10e5aedab9144531a4668dc7526e3caf514e1cristy%  A description of each parameter follows:
21382fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
21392fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o image: the image.
21402fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
2141d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy%    o width, height: find pixels in this neighborhood.
21422fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
21436aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy%    o color_distance: the color distance.
21442fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
21452fc10e5aedab9144531a4668dc7526e3caf514e1cristy%    o exception: return any errors or warnings in this structure.
21462fc10e5aedab9144531a4668dc7526e3caf514e1cristy%
21472fc10e5aedab9144531a4668dc7526e3caf514e1cristy*/
21482fc10e5aedab9144531a4668dc7526e3caf514e1cristyMagickExport Image *MeanShiftImage(const Image *image,const size_t width,
21496aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  const size_t height,const double color_distance,ExceptionInfo *exception)
21502fc10e5aedab9144531a4668dc7526e3caf514e1cristy{
21516aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy#define MaxMeanShiftIterations  100
21524c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy#define MeanShiftImageTag  "MeanShift/Image"
21536aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21546aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  CacheView
21556aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    *image_view,
21566aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    *mean_view,
21576aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    *pixel_view;
21586aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21596aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  Image
21606aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    *mean_image;
21616aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21626aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  MagickBooleanType
21636aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    status;
21646aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21654c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy  MagickOffsetType
21664c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy    progress;
21674c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy
21686aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  ssize_t
21696aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    y;
21706aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21716aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  assert(image != (const Image *) NULL);
2172e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(image->signature == MagickCoreSignature);
21736aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  if (image->debug != MagickFalse)
21746aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
21756aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  assert(exception != (ExceptionInfo *) NULL);
2176e1c94d9d25db6b0dd7a5028ffee31d1057855d73cristy  assert(exception->signature == MagickCoreSignature);
21776aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  mean_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
21786aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  if (mean_image == (Image *) NULL)
21796aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    return((Image *) NULL);
21806aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  if (SetImageStorageClass(mean_image,DirectClass,exception) == MagickFalse)
21816aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    {
21826aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      mean_image=DestroyImage(mean_image);
21836aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      return((Image *) NULL);
21846aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    }
21856aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  status=MagickTrue;
21864c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy  progress=0;
21876aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  image_view=AcquireVirtualCacheView(image,exception);
21886aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  pixel_view=AcquireVirtualCacheView(image,exception);
21896aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  mean_view=AcquireAuthenticCacheView(mean_image,exception);
21906aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
21914c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy  #pragma omp parallel for schedule(static,4) shared(status,progress) \
21926aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    magick_threads(mean_image,mean_image,mean_image->rows,1)
21936aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy#endif
21946aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  for (y=0; y < (ssize_t) mean_image->rows; y++)
21956aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  {
21966aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    register const Quantum
219705d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict p;
21986aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
21996aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    register Quantum
220005d2ff7ebf21f659f5b11e45afb294e152f4330cdirk      *magick_restrict q;
22016aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22026aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    register ssize_t
22036aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      x;
22046aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22056aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    if (status == MagickFalse)
22066aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      continue;
22076aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
22086aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    q=GetCacheViewAuthenticPixels(mean_view,0,y,mean_image->columns,1,
22096aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      exception);
22106aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
22116aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      {
22126aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        status=MagickFalse;
22136aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        continue;
22146aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      }
22156aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    for (x=0; x < (ssize_t) mean_image->columns; x++)
22166aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    {
22176aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      PixelInfo
22186aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel,
22196aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        previous_pixel;
22206aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22216aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      PointInfo
22226aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_location,
22236aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        previous_location;
22246aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22256aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      register ssize_t
22266aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        i;
22276aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22286aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      GetPixelInfo(image,&mean_pixel);
22296aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      GetPixelInfoPixel(image,p,&mean_pixel);
22306aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      mean_location.x=(double) x;
22316aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      mean_location.y=(double) y;
22326aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      for (i=0; i < MaxMeanShiftIterations; i++)
22336aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      {
22346aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        double
22356aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          distance,
22366aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          gamma;
22376aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22386aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        PixelInfo
22396aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          sum_pixel;
22406aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22416aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        PointInfo
22426aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          sum_location;
22436aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22446aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        ssize_t
22456aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          count,
22466aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          v;
22476aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22486aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        sum_location.x=0.0;
22496aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        sum_location.y=0.0;
22506aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        GetPixelInfo(image,&sum_pixel);
2251f89d1dd0b5189cf4df3163fc495f0493f8ddcc7ecristy        previous_location=mean_location;
22526aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        previous_pixel=mean_pixel;
22536aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        count=0;
22542494e3d5cea70ebef8e12498b10bc2e24da4803fcristy        for (v=(-((ssize_t) height/2)); v <= (((ssize_t) height/2)); v++)
22556aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        {
22566aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          ssize_t
22576aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy            u;
22586aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy
22592494e3d5cea70ebef8e12498b10bc2e24da4803fcristy          for (u=(-((ssize_t) width/2)); u <= (((ssize_t) width/2)); u++)
22606aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          {
2261e5ecaa83494b57d5fd8956f8b4931d58cfd6e8fccristy            if ((v*v+u*u) <= (ssize_t) ((width/2)*(height/2)))
22626aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy              {
2263983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                PixelInfo
2264983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                  pixel;
2265983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy
2266983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                status=GetOneCacheViewVirtualPixelInfo(pixel_view,(ssize_t)
22678c2a8442c506836270be8c418c87f031400edf1bcristy                  MagickRound(mean_location.x+u),(ssize_t) MagickRound(
22688c2a8442c506836270be8c418c87f031400edf1bcristy                  mean_location.y+v),&pixel,exception);
2269d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy                distance=(mean_pixel.red-pixel.red)*(mean_pixel.red-pixel.red)+
2270d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy                  (mean_pixel.green-pixel.green)*(mean_pixel.green-pixel.green)+
2271d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy                  (mean_pixel.blue-pixel.blue)*(mean_pixel.blue-pixel.blue);
2272983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                if (distance <= (color_distance*color_distance))
2273983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                  {
2274983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_location.x+=mean_location.x+u;
2275983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_location.y+=mean_location.y+v;
2276983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_pixel.red+=pixel.red;
2277983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_pixel.green+=pixel.green;
2278983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_pixel.blue+=pixel.blue;
2279983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    sum_pixel.alpha+=pixel.alpha;
2280983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                    count++;
2281983aa7cfdd9f51bc2309c5b1cbe50c6c3ac23993cristy                  }
22826aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy              }
22836aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          }
22846aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        }
22856aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        gamma=1.0/count;
22868c2a8442c506836270be8c418c87f031400edf1bcristy        mean_location.x=gamma*sum_location.x;
22878c2a8442c506836270be8c418c87f031400edf1bcristy        mean_location.y=gamma*sum_location.y;
22886aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel.red=gamma*sum_pixel.red;
22896aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel.green=gamma*sum_pixel.green;
22906aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel.blue=gamma*sum_pixel.blue;
22916aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        mean_pixel.alpha=gamma*sum_pixel.alpha;
22926aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy        distance=(mean_location.x-previous_location.x)*
22936aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          (mean_location.x-previous_location.x)+
22946aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          (mean_location.y-previous_location.y)*
22956aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          (mean_location.y-previous_location.y)+
229621a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.red-previous_pixel.red)*
229721a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.red-previous_pixel.red)+
229821a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.green-previous_pixel.green)*
229921a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.green-previous_pixel.green)+
230021a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.blue-previous_pixel.blue)*
230121a2b741c3102c64fd63164026da2c5b785ec6f5cristy          255.0*QuantumScale*(mean_pixel.blue-previous_pixel.blue);
2302d70bd6ca959a16d8a10f4b2bc0988e91cbcad980cristy        if (distance <= 3.0)
23036aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy          break;
23046aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      }
23056aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      SetPixelRed(mean_image,ClampToQuantum(mean_pixel.red),q);
23066aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      SetPixelGreen(mean_image,ClampToQuantum(mean_pixel.green),q);
23076aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      SetPixelBlue(mean_image,ClampToQuantum(mean_pixel.blue),q);
23086aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      SetPixelAlpha(mean_image,ClampToQuantum(mean_pixel.alpha),q);
23096aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      p+=GetPixelChannels(image);
23106aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      q+=GetPixelChannels(mean_image);
23116aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    }
23126aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy    if (SyncCacheViewAuthenticPixels(mean_view,exception) == MagickFalse)
23136aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy      status=MagickFalse;
23144c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
23154c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy      {
23164c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy        MagickBooleanType
23174c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy          proceed;
23184c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy
23194c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy#if defined(MAGICKCORE_OPENMP_SUPPORT)
23204c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy        #pragma omp critical (MagickCore_MeanShiftImage)
23214c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy#endif
23224c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy        proceed=SetImageProgress(image,MeanShiftImageTag,progress++,
23234c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy          image->rows);
23244c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy        if (proceed == MagickFalse)
23254c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy          status=MagickFalse;
23264c8a3ecf917f56f107f2a7477fb60139d2861f3dcristy      }
23276aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  }
23286aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  mean_view=DestroyCacheView(mean_view);
23296aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  pixel_view=DestroyCacheView(pixel_view);
23306aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  image_view=DestroyCacheView(image_view);
23316aa40d3dfec558e9b5f4eae34b4cbd7f14944182cristy  return(mean_image);
23323e2860cb0796fe77659325ec3d540d8766d54f49cristy}
2333