]> granicus.if.org Git - imagemagick/commitdiff
(no commit message)
authorcristy <urban-warrior@git.imagemagick.org>
Sat, 19 Apr 2014 16:23:30 +0000 (16:23 +0000)
committercristy <urban-warrior@git.imagemagick.org>
Sat, 19 Apr 2014 16:23:30 +0000 (16:23 +0000)
MagickCore/effect.c
MagickCore/effect.h
MagickCore/feature.c
MagickCore/feature.h
MagickWand/convert.c
utilities/convert.1.in
utilities/mogrify.1.in

index cf6d59439350ade463233d6cf5fbadac0865854b..c406f3d2ab06bdc5c39b6fab73d35315109bc65f 100644 (file)
@@ -850,463 +850,6 @@ MagickExport Image *BlurImage(const Image *image,const double radius,
 %                                                                             %
 %                                                                             %
 %                                                                             %
-%     C a n n y E d g e I m a g e                                             %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  CannyEdgeImage() uses a multi-stage algorithm to detect a wide range of
-%  edges in images.
-%
-%  The format of the EdgeImage method is:
-%
-%      Image *CannyEdgeImage(const Image *image,const double radius,
-%        const double sigma,const double lower_percent,
-%        const double upper_percent,ExceptionInfo *exception)
-%
-%  A description of each parameter follows:
-%
-%    o image: the image.
-%
-%    o radius: the radius of the gaussian smoothing filter.
-%
-%    o sigma: the sigma of the gaussian smoothing filter.
-%
-%    o lower_precent: percentage of edge pixels in the lower threshold.
-%
-%    o upper_percent: percentage of edge pixels in the upper threshold.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-
-typedef struct _CannyInfo
-{
-  double
-    magnitude,
-    intensity;
-
-  int
-    orientation;
-
-  ssize_t
-    x,
-    y;
-} CannyInfo;
-
-static inline MagickBooleanType IsAuthenticPixel(const Image *image,
-  const ssize_t x,const ssize_t y)
-{
-  if ((x < 0) || (x >= (ssize_t) image->columns))
-    return(MagickFalse);
-  if ((y < 0) || (y >= (ssize_t) image->rows))
-    return(MagickFalse);
-  return(MagickTrue);
-}
-
-static MagickBooleanType TraceEdges(Image *edge_image,CacheView *edge_view,
-  MatrixInfo *canny_cache,const ssize_t x,const ssize_t y,
-  const double lower_threshold,ExceptionInfo *exception)
-{
-  CannyInfo
-    edge,
-    pixel;
-
-  MagickBooleanType
-    status;
-
-  register Quantum
-    *q;
-
-  register ssize_t
-    i;
-
-  q=GetCacheViewAuthenticPixels(edge_view,x,y,1,1,exception);
-  if (q == (Quantum *) NULL)
-    return(MagickFalse);
-  *q=QuantumRange;
-  status=SyncCacheViewAuthenticPixels(edge_view,exception);
-  if (status == MagickFalse)
-    return(MagickFalse);;
-  if (GetMatrixElement(canny_cache,0,0,&edge) == MagickFalse)
-    return(MagickFalse);
-  edge.x=x;
-  edge.y=y;
-  if (SetMatrixElement(canny_cache,0,0,&edge) == MagickFalse)
-    return(MagickFalse);
-  for (i=1; i != 0; )
-  {
-    ssize_t
-      v;
-
-    i--;
-    status=GetMatrixElement(canny_cache,i,0,&edge);
-    if (status == MagickFalse)
-      return(MagickFalse);
-    for (v=(-1); v <= 1; v++)
-    {
-      ssize_t
-        u;
-
-      for (u=(-1); u <= 1; u++)
-      {
-        if ((u == 0) && (v == 0))
-          continue;
-        if (IsAuthenticPixel(edge_image,edge.x+u,edge.y+v) == MagickFalse)
-          continue;
-        /*
-          Not an edge if gradient value is below the lower threshold.
-        */
-        q=GetCacheViewAuthenticPixels(edge_view,edge.x+u,edge.y+v,1,1,
-          exception);
-        if (q == (Quantum *) NULL)
-          return(MagickFalse);
-        status=GetMatrixElement(canny_cache,edge.x+u,edge.y+v,&pixel);
-        if (status == MagickFalse)
-          return(MagickFalse);
-        if ((GetPixelIntensity(edge_image,q) == 0.0) &&
-            (pixel.intensity >= lower_threshold))
-          {
-            *q=QuantumRange;
-            status=SyncCacheViewAuthenticPixels(edge_view,exception);
-            if (status == MagickFalse)
-              return(MagickFalse);
-            edge.x+=u;
-            edge.y+=v;
-            status=SetMatrixElement(canny_cache,i,0,&edge);
-            if (status == MagickFalse)
-              return(MagickFalse);
-            i++;
-          }
-      }
-    }
-  }
-  return(MagickTrue);
-}
-
-MagickExport Image *CannyEdgeImage(const Image *image,const double radius,
-  const double sigma,const double lower_percent,const double upper_percent,
-  ExceptionInfo *exception)
-{
-  CacheView
-    *edge_view;
-
-  CannyInfo
-    pixel;
-
-  char
-    geometry[MaxTextExtent];
-
-  double
-    lower_threshold,
-    max,
-    min,
-    upper_threshold;
-
-  Image
-    *edge_image;
-
-  KernelInfo
-    *kernel_info;
-
-  MagickBooleanType
-    status;
-
-  MatrixInfo
-    *canny_cache;
-
-  ssize_t
-    y;
-
-  assert(image != (const Image *) NULL);
-  assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  assert(exception != (ExceptionInfo *) NULL);
-  assert(exception->signature == MagickSignature);
-  /*
-    Filter out noise.
-  */
-  (void) FormatLocaleString(geometry,MaxTextExtent,
-    "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
-  kernel_info=AcquireKernelInfo(geometry);
-  if (kernel_info == (KernelInfo *) NULL)
-    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
-  edge_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
-    UndefinedCompositeOp,0.0,exception);
-  kernel_info=DestroyKernelInfo(kernel_info);
-  if (edge_image == (Image *) NULL)
-    return((Image *) NULL);
-  if (SetImageColorspace(edge_image,GRAYColorspace,exception) == MagickFalse)
-    {
-      edge_image=DestroyImage(edge_image);
-      return((Image *) NULL);
-    }
-  /*
-    Find the intensity gradient of the image.
-  */
-  canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows,
-    sizeof(CannyInfo),exception);
-  if (canny_cache == (MatrixInfo *) NULL)
-    {
-      edge_image=DestroyImage(edge_image);
-      return((Image *) NULL);
-    }
-  status=MagickTrue;
-  edge_view=AcquireVirtualCacheView(edge_image,exception);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(static,4) shared(status) \
-    magick_threads(edge_image,edge_image,edge_image->rows,1)
-#endif
-  for (y=0; y < (ssize_t) edge_image->rows; y++)
-  {
-    register const Quantum
-      *restrict p;
-
-    register ssize_t
-      x;
-
-    if (status == MagickFalse)
-      continue;
-    p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns+1,2,
-      exception);
-    if (p == (const Quantum *) NULL)
-      {
-        status=MagickFalse;
-        continue;
-      }
-    for (x=0; x < (ssize_t) edge_image->columns; x++)
-    {
-      CannyInfo
-        pixel;
-
-      double
-        dx,
-        dy;
-
-      register const Quantum
-        *restrict kernel_pixels;
-
-      ssize_t
-        v;
-
-      static double
-        Gx[2][2] =
-        {
-          { -1.0,  +1.0 },
-          { -1.0,  +1.0 }
-        },
-        Gy[2][2] =
-        {
-          { +1.0, +1.0 },
-          { -1.0, -1.0 }
-        };
-
-      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
-      dx=0.0;
-      dy=0.0;
-      kernel_pixels=p;
-      for (v=0; v < 2; v++)
-      {
-        ssize_t
-          u;
-
-        for (u=0; u < 2; u++)
-        {
-          double
-            intensity;
-
-          intensity=GetPixelIntensity(edge_image,kernel_pixels+u);
-          dx+=0.5*Gx[v][u]*intensity;
-          dy+=0.5*Gy[v][u]*intensity;
-        }
-        kernel_pixels+=edge_image->columns+1;
-      }
-      pixel.magnitude=hypot(dx,dy);
-      pixel.orientation=0;
-      if (fabs(dx) > MagickEpsilon)
-        {
-          double
-            slope;
-
-          slope=dy/dx;
-          if (slope < 0.0)
-            {
-              if (slope < -2.41421356237)
-                pixel.orientation=0;
-              else
-                if (slope < -0.414213562373)
-                  pixel.orientation=1;
-                else
-                  pixel.orientation=2;
-            }
-          else
-            {
-              if (slope > 2.41421356237)
-                pixel.orientation=0;
-              else
-                if (slope > 0.414213562373)
-                  pixel.orientation=3;
-                else
-                  pixel.orientation=2;
-            }
-        }
-      if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse)
-        continue;
-      p+=GetPixelChannels(edge_image);
-    }
-  }
-  edge_view=DestroyCacheView(edge_view);
-  /*
-    Non-maxima suppression, remove pixels that are not considered to be part
-    of an edge.
-  */
-  (void) GetMatrixElement(canny_cache,0,0,&pixel);
-  max=pixel.intensity;
-  min=pixel.intensity;
-  edge_view=AcquireAuthenticCacheView(edge_image,exception);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(static,4) shared(status) \
-    magick_threads(edge_image,edge_image,edge_image->rows,1)
-#endif
-  for (y=0; y < (ssize_t) edge_image->rows; y++)
-  {
-    register Quantum
-      *restrict q;
-
-    register ssize_t
-      x;
-
-    if (status == MagickFalse)
-      continue;
-    q=GetCacheViewAuthenticPixels(edge_view,0,y,edge_image->columns,1,
-      exception);
-    if (q == (Quantum *) NULL)
-      {
-        status=MagickFalse;
-        continue;
-      }
-    for (x=0; x < (ssize_t) edge_image->columns; x++)
-    {
-      CannyInfo
-        alpha_pixel,
-        beta_pixel,
-        pixel;
-
-      (void) GetMatrixElement(canny_cache,x,y,&pixel);
-      switch (pixel.orientation)
-      {
-        case 0:
-        {
-          /*
-            0 degrees, north and south.
-          */
-          (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel);
-          (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel);
-          break;
-        }
-        case 1:
-        {
-          /*
-            45 degrees, northwest and southeast.
-          */
-          (void) GetMatrixElement(canny_cache,x-1,y-1,&alpha_pixel);
-          (void) GetMatrixElement(canny_cache,x+1,y+1,&beta_pixel);
-          break;
-        }
-        case 2:
-        {
-          /*
-            90 degrees, east and west.
-          */
-          (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel);
-          (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel);
-          break;
-        }
-        case 3:
-        {
-          /*
-            135 degrees, northeast and southwest.
-          */
-          (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel);
-          (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel);
-          break;
-        }
-      }
-      pixel.intensity=pixel.magnitude;
-      if ((pixel.magnitude < alpha_pixel.magnitude) ||
-          (pixel.magnitude < beta_pixel.magnitude))
-        pixel.intensity=0;
-      (void) SetMatrixElement(canny_cache,x,y,&pixel);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-      #pragma omp critical (MagickCore_CannyEdgeImage)
-#endif
-      {
-        if (pixel.intensity < min)
-          min=pixel.intensity;
-        if (pixel.intensity > max)
-          max=pixel.intensity;
-      }
-      *q=0;
-      q+=GetPixelChannels(edge_image);
-    }
-    if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse)
-      status=MagickFalse;
-  }
-  edge_view=DestroyCacheView(edge_view);
-  /*
-    Estimate hysteresis threshold.
-  */
-  lower_threshold=lower_percent*(max-min)+min;
-  upper_threshold=upper_percent*(max-min)+min;
-  /*
-    Hysteresis threshold.
-  */
-  edge_view=AcquireAuthenticCacheView(edge_image,exception);
-  for (y=0; y < (ssize_t) edge_image->rows; y++)
-  {
-    register ssize_t
-      x;
-
-    if (status == MagickFalse)
-      continue;
-    for (x=0; x < (ssize_t) edge_image->columns; x++)
-    {
-      CannyInfo
-        pixel;
-
-      register const Quantum
-        *restrict p;
-
-      /*
-        Edge if pixel gradient higher than upper threshold.
-      */
-      p=GetCacheViewVirtualPixels(edge_view,x,y,1,1,exception);
-      if (p == (const Quantum *) NULL)
-        continue;
-      status=GetMatrixElement(canny_cache,x,y,&pixel);
-      if (status == MagickFalse)
-        continue;
-      if ((GetPixelIntensity(edge_image,p) == 0.0) &&
-          (pixel.intensity >= upper_threshold))
-        status=TraceEdges(edge_image,edge_view,canny_cache,x,y,lower_threshold,
-          exception);
-    }
-  }
-  edge_view=DestroyCacheView(edge_view);
-  /*
-    Free resources.
-  */
-  canny_cache=DestroyMatrixInfo(canny_cache);
-  return(edge_image);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
 %     C o n v o l v e I m a g e                                               %
 %                                                                             %
 %                                                                             %
index 37c06a3ebcbdd51396bf4232d6b0a535040a3e38..91344d0fff7ab9eeccb33ea4a7e5d58044e7871b 100644 (file)
@@ -63,8 +63,6 @@ extern MagickExport Image
   *AdaptiveSharpenImage(const Image *,const double,const double,
      ExceptionInfo *),
   *BlurImage(const Image *,const double,const double,ExceptionInfo *),
-  *CannyEdgeImage(const Image *,const double,const double,const double,
-    const double,ExceptionInfo *),
   *ConvolveImage(const Image *,const KernelInfo *,ExceptionInfo *),
   *DespeckleImage(const Image *,ExceptionInfo *),
   *EdgeImage(const Image *,const double,ExceptionInfo *),
index 308288cecc292543fb3806640ccd6d8ee5344cb1..c68d417a42fb795e5c658e6a1d462809f9dbf022 100644 (file)
 #include "MagickCore/image-private.h"
 #include "MagickCore/magic.h"
 #include "MagickCore/magick.h"
+#include "MagickCore/matrix.h"
 #include "MagickCore/memory_.h"
 #include "MagickCore/module.h"
 #include "MagickCore/monitor.h"
 #include "MagickCore/monitor-private.h"
+#include "MagickCore/morphology-private.h"
 #include "MagickCore/option.h"
 #include "MagickCore/paint.h"
 #include "MagickCore/pixel-accessor.h"
 #include "MagickCore/timer.h"
 #include "MagickCore/utility.h"
 #include "MagickCore/version.h"
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%     C a n n y E d g e I m a g e                                             %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  CannyEdgeImage() uses a multi-stage algorithm to detect a wide range of
+%  edges in images.
+%
+%  The format of the EdgeImage method is:
+%
+%      Image *CannyEdgeImage(const Image *image,const double radius,
+%        const double sigma,const double lower_percent,
+%        const double upper_percent,ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o radius: the radius of the gaussian smoothing filter.
+%
+%    o sigma: the sigma of the gaussian smoothing filter.
+%
+%    o lower_precent: percentage of edge pixels in the lower threshold.
+%
+%    o upper_percent: percentage of edge pixels in the upper threshold.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+
+typedef struct _CannyInfo
+{
+  double
+    magnitude,
+    intensity;
+
+  int
+    orientation;
+
+  ssize_t
+    x,
+    y;
+} CannyInfo;
+
+static inline MagickBooleanType IsAuthenticPixel(const Image *image,
+  const ssize_t x,const ssize_t y)
+{
+  if ((x < 0) || (x >= (ssize_t) image->columns))
+    return(MagickFalse);
+  if ((y < 0) || (y >= (ssize_t) image->rows))
+    return(MagickFalse);
+  return(MagickTrue);
+}
+
+static MagickBooleanType TraceEdges(Image *edge_image,CacheView *edge_view,
+  MatrixInfo *canny_cache,const ssize_t x,const ssize_t y,
+  const double lower_threshold,ExceptionInfo *exception)
+{
+  CannyInfo
+    edge,
+    pixel;
+
+  MagickBooleanType
+    status;
+
+  register Quantum
+    *q;
+
+  register ssize_t
+    i;
+
+  q=GetCacheViewAuthenticPixels(edge_view,x,y,1,1,exception);
+  if (q == (Quantum *) NULL)
+    return(MagickFalse);
+  *q=QuantumRange;
+  status=SyncCacheViewAuthenticPixels(edge_view,exception);
+  if (status == MagickFalse)
+    return(MagickFalse);;
+  if (GetMatrixElement(canny_cache,0,0,&edge) == MagickFalse)
+    return(MagickFalse);
+  edge.x=x;
+  edge.y=y;
+  if (SetMatrixElement(canny_cache,0,0,&edge) == MagickFalse)
+    return(MagickFalse);
+  for (i=1; i != 0; )
+  {
+    ssize_t
+      v;
+
+    i--;
+    status=GetMatrixElement(canny_cache,i,0,&edge);
+    if (status == MagickFalse)
+      return(MagickFalse);
+    for (v=(-1); v <= 1; v++)
+    {
+      ssize_t
+        u;
+
+      for (u=(-1); u <= 1; u++)
+      {
+        if ((u == 0) && (v == 0))
+          continue;
+        if (IsAuthenticPixel(edge_image,edge.x+u,edge.y+v) == MagickFalse)
+          continue;
+        /*
+          Not an edge if gradient value is below the lower threshold.
+        */
+        q=GetCacheViewAuthenticPixels(edge_view,edge.x+u,edge.y+v,1,1,
+          exception);
+        if (q == (Quantum *) NULL)
+          return(MagickFalse);
+        status=GetMatrixElement(canny_cache,edge.x+u,edge.y+v,&pixel);
+        if (status == MagickFalse)
+          return(MagickFalse);
+        if ((GetPixelIntensity(edge_image,q) == 0.0) &&
+            (pixel.intensity >= lower_threshold))
+          {
+            *q=QuantumRange;
+            status=SyncCacheViewAuthenticPixels(edge_view,exception);
+            if (status == MagickFalse)
+              return(MagickFalse);
+            edge.x+=u;
+            edge.y+=v;
+            status=SetMatrixElement(canny_cache,i,0,&edge);
+            if (status == MagickFalse)
+              return(MagickFalse);
+            i++;
+          }
+      }
+    }
+  }
+  return(MagickTrue);
+}
+
+MagickExport Image *CannyEdgeImage(const Image *image,const double radius,
+  const double sigma,const double lower_percent,const double upper_percent,
+  ExceptionInfo *exception)
+{
+  CacheView
+    *edge_view;
+
+  CannyInfo
+    pixel;
+
+  char
+    geometry[MaxTextExtent];
+
+  double
+    lower_threshold,
+    max,
+    min,
+    upper_threshold;
+
+  Image
+    *edge_image;
+
+  KernelInfo
+    *kernel_info;
+
+  MagickBooleanType
+    status;
+
+  MatrixInfo
+    *canny_cache;
+
+  ssize_t
+    y;
+
+  assert(image != (const Image *) NULL);
+  assert(image->signature == MagickSignature);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  assert(exception != (ExceptionInfo *) NULL);
+  assert(exception->signature == MagickSignature);
+  /*
+    Filter out noise.
+  */
+  (void) FormatLocaleString(geometry,MaxTextExtent,
+    "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
+  kernel_info=AcquireKernelInfo(geometry);
+  if (kernel_info == (KernelInfo *) NULL)
+    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+  edge_image=MorphologyApply(image,ConvolveMorphology,1,kernel_info,
+    UndefinedCompositeOp,0.0,exception);
+  kernel_info=DestroyKernelInfo(kernel_info);
+  if (edge_image == (Image *) NULL)
+    return((Image *) NULL);
+  if (SetImageColorspace(edge_image,GRAYColorspace,exception) == MagickFalse)
+    {
+      edge_image=DestroyImage(edge_image);
+      return((Image *) NULL);
+    }
+  /*
+    Find the intensity gradient of the image.
+  */
+  canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows,
+    sizeof(CannyInfo),exception);
+  if (canny_cache == (MatrixInfo *) NULL)
+    {
+      edge_image=DestroyImage(edge_image);
+      return((Image *) NULL);
+    }
+  status=MagickTrue;
+  edge_view=AcquireVirtualCacheView(edge_image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(static,4) shared(status) \
+    magick_threads(edge_image,edge_image,edge_image->rows,1)
+#endif
+  for (y=0; y < (ssize_t) edge_image->rows; y++)
+  {
+    register const Quantum
+      *restrict p;
+
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns+1,2,
+      exception);
+    if (p == (const Quantum *) NULL)
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) edge_image->columns; x++)
+    {
+      CannyInfo
+        pixel;
+
+      double
+        dx,
+        dy;
+
+      register const Quantum
+        *restrict kernel_pixels;
+
+      ssize_t
+        v;
+
+      static double
+        Gx[2][2] =
+        {
+          { -1.0,  +1.0 },
+          { -1.0,  +1.0 }
+        },
+        Gy[2][2] =
+        {
+          { +1.0, +1.0 },
+          { -1.0, -1.0 }
+        };
+
+      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
+      dx=0.0;
+      dy=0.0;
+      kernel_pixels=p;
+      for (v=0; v < 2; v++)
+      {
+        ssize_t
+          u;
+
+        for (u=0; u < 2; u++)
+        {
+          double
+            intensity;
+
+          intensity=GetPixelIntensity(edge_image,kernel_pixels+u);
+          dx+=0.5*Gx[v][u]*intensity;
+          dy+=0.5*Gy[v][u]*intensity;
+        }
+        kernel_pixels+=edge_image->columns+1;
+      }
+      pixel.magnitude=hypot(dx,dy);
+      pixel.orientation=0;
+      if (fabs(dx) > MagickEpsilon)
+        {
+          double
+            slope;
+
+          slope=dy/dx;
+          if (slope < 0.0)
+            {
+              if (slope < -2.41421356237)
+                pixel.orientation=0;
+              else
+                if (slope < -0.414213562373)
+                  pixel.orientation=1;
+                else
+                  pixel.orientation=2;
+            }
+          else
+            {
+              if (slope > 2.41421356237)
+                pixel.orientation=0;
+              else
+                if (slope > 0.414213562373)
+                  pixel.orientation=3;
+                else
+                  pixel.orientation=2;
+            }
+        }
+      if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse)
+        continue;
+      p+=GetPixelChannels(edge_image);
+    }
+  }
+  edge_view=DestroyCacheView(edge_view);
+  /*
+    Non-maxima suppression, remove pixels that are not considered to be part
+    of an edge.
+  */
+  (void) GetMatrixElement(canny_cache,0,0,&pixel);
+  max=pixel.intensity;
+  min=pixel.intensity;
+  edge_view=AcquireAuthenticCacheView(edge_image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(static,4) shared(status) \
+    magick_threads(edge_image,edge_image,edge_image->rows,1)
+#endif
+  for (y=0; y < (ssize_t) edge_image->rows; y++)
+  {
+    register Quantum
+      *restrict q;
+
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    q=GetCacheViewAuthenticPixels(edge_view,0,y,edge_image->columns,1,
+      exception);
+    if (q == (Quantum *) NULL)
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) edge_image->columns; x++)
+    {
+      CannyInfo
+        alpha_pixel,
+        beta_pixel,
+        pixel;
+
+      (void) GetMatrixElement(canny_cache,x,y,&pixel);
+      switch (pixel.orientation)
+      {
+        case 0:
+        {
+          /*
+            0 degrees, north and south.
+          */
+          (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel);
+          (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel);
+          break;
+        }
+        case 1:
+        {
+          /*
+            45 degrees, northwest and southeast.
+          */
+          (void) GetMatrixElement(canny_cache,x-1,y-1,&alpha_pixel);
+          (void) GetMatrixElement(canny_cache,x+1,y+1,&beta_pixel);
+          break;
+        }
+        case 2:
+        {
+          /*
+            90 degrees, east and west.
+          */
+          (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel);
+          (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel);
+          break;
+        }
+        case 3:
+        {
+          /*
+            135 degrees, northeast and southwest.
+          */
+          (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel);
+          (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel);
+          break;
+        }
+      }
+      pixel.intensity=pixel.magnitude;
+      if ((pixel.magnitude < alpha_pixel.magnitude) ||
+          (pixel.magnitude < beta_pixel.magnitude))
+        pixel.intensity=0;
+      (void) SetMatrixElement(canny_cache,x,y,&pixel);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+      #pragma omp critical (MagickCore_CannyEdgeImage)
+#endif
+      {
+        if (pixel.intensity < min)
+          min=pixel.intensity;
+        if (pixel.intensity > max)
+          max=pixel.intensity;
+      }
+      *q=0;
+      q+=GetPixelChannels(edge_image);
+    }
+    if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse)
+      status=MagickFalse;
+  }
+  edge_view=DestroyCacheView(edge_view);
+  /*
+    Estimate hysteresis threshold.
+  */
+  lower_threshold=lower_percent*(max-min)+min;
+  upper_threshold=upper_percent*(max-min)+min;
+  /*
+    Hysteresis threshold.
+  */
+  edge_view=AcquireAuthenticCacheView(edge_image,exception);
+  for (y=0; y < (ssize_t) edge_image->rows; y++)
+  {
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    for (x=0; x < (ssize_t) edge_image->columns; x++)
+    {
+      CannyInfo
+        pixel;
+
+      register const Quantum
+        *restrict p;
+
+      /*
+        Edge if pixel gradient higher than upper threshold.
+      */
+      p=GetCacheViewVirtualPixels(edge_view,x,y,1,1,exception);
+      if (p == (const Quantum *) NULL)
+        continue;
+      status=GetMatrixElement(canny_cache,x,y,&pixel);
+      if (status == MagickFalse)
+        continue;
+      if ((GetPixelIntensity(edge_image,p) == 0.0) &&
+          (pixel.intensity >= upper_threshold))
+        status=TraceEdges(edge_image,edge_view,canny_cache,x,y,lower_threshold,
+          exception);
+    }
+  }
+  edge_view=DestroyCacheView(edge_view);
+  /*
+    Free resources.
+  */
+  canny_cache=DestroyMatrixInfo(canny_cache);
+  return(edge_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%     H o u g h T r a n s f o r m I m a g e                                   %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  HoughTransformImage() detects lines in an image.
+%
+%  The format of the HoughTransformImage method is:
+%
+%      Image *HoughTransformImage(const Image *image,const size_t width,
+%        const size_t height,const size_t threshold,ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o width, height: find line pairs as local maxima in this neighborhood.
+%
+%    o threshold: the line count threshold.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *HoughTransformImage(const Image *image,const size_t width,
+  const size_t height,const size_t threshold,ExceptionInfo *exception)
+{
+  return((Image *) NULL);
+}
+
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
index bfa205e20e696f190e5f561a41df26613391111f..625369505265f0d005d00802d015cf60120217de 100644 (file)
@@ -47,6 +47,12 @@ typedef struct _ChannelFeatures
 extern MagickExport ChannelFeatures
   *GetImageFeatures(const Image *,const size_t,ExceptionInfo *);
 
+extern MagickExport Image
+  *CannyEdgeImage(const Image *,const double,const double,const double,
+    const double,ExceptionInfo *),
+  *HoughTransformImage(const Image *,const size_t,const size_t,const size_t,
+    ExceptionInfo *);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index d7ab8da2aab47f7ef97df06a323bfc6aa774237d..62738ae87e836d304b374fd00277061341517490 100644 (file)
@@ -218,6 +218,8 @@ static MagickBooleanType ConvertUsage(void)
       "                     reduce image noise and reduce detail levels",
       "-geometry geometry   preferred size or location of the image",
       "-grayscale method    convert image to grayscale",
+      "-hough-lines geometry",
+      "                     identify lines in the image",
       "-identify            identify the format and characteristics of the image",
       "-ift                 implements the inverse discrete Fourier transform (DFT)",
       "-implode amount      implode image pixels about the center",
index f80930c8523f5fbae6a9f13d6195bdd89fae7895..7e7992f7fc3a6f1c39ffc5aa7ca744799ee85150 100644 (file)
@@ -175,6 +175,8 @@ Image Operators:
                        reduce image noise and reduce detail levels
   \-geometry geometry   preferred size or location of the image
   \-grayscale method    convert image to grayscale
+  \-hough-lines geometry
+                        identify lines in the image
   \-identify            identify the format and characteristics of the image
   \-ift                 implements the inverse discrete Fourier transform (DFT)
   \-implode amount      implode image pixels about the center
index 00af63ee21e92f167dfa7ea134104a085eb42127..40b2788c7d262d1432cdce3d1ac01da2dff35efe 100644 (file)
@@ -172,9 +172,11 @@ Image Operators:
                        reduce image noise and reduce detail levels
   \-geometry geometry   preferred size or location of the image
   \-grayscale method    convert image to grayscale
-  \-ift                 implements the inverse discrete Fourier transform (DFT)
   \-help                print program options
+  \-hough-lines geometry
+                        identify lines in the image
   \-identify            identify the format and characteristics of the image
+  \-ift                 implements the inverse discrete Fourier transform (DFT)
   \-implode amount      implode image pixels about the center
   \-interpolative-resize geometry
                        resize image using interpolation