]> granicus.if.org Git - imagemagick/commitdiff
(no commit message)
authorcristy <urban-warrior@git.imagemagick.org>
Fri, 4 Apr 2014 00:55:24 +0000 (00:55 +0000)
committercristy <urban-warrior@git.imagemagick.org>
Fri, 4 Apr 2014 00:55:24 +0000 (00:55 +0000)
MagickCore/effect.c

index ee645be4eff7210e72455e8ed54f5ad82620bbf3..5b16f6348027ecb42bf8d4828491e5c8e6bf431d 100644 (file)
@@ -62,6 +62,7 @@
 #include "MagickCore/image-private.h"
 #include "MagickCore/list.h"
 #include "MagickCore/log.h"
+#include "MagickCore/matrix.h"
 #include "MagickCore/memory_.h"
 #include "MagickCore/memory-private.h"
 #include "MagickCore/monitor.h"
@@ -861,7 +862,7 @@ MagickExport Image *BlurImage(const Image *image,const double radius,
 %  The format of the EdgeImage method is:
 %
 %      Image *CannyEdgeImage(const Image *image,const double radius,
-%        const double sigma,const double low_factor,const double high_factor,
+%        const double sigma,const double low_percent,const double high_percent,
 %        const size_t threshold,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
@@ -874,27 +875,408 @@ MagickExport Image *BlurImage(const Image *image,const double radius,
 %
 %    o sigma: the sigma of the gaussian smoothing filter.
 %
-%    o low_factor: use this low factor in hysteresis.
+%    o low_percent: percentage of pixels in the low threshold.
 %
-%    o hight_factor: use this high factor in hysteresis.
+%    o high_percent: percentage of pixels in the high threshold.
 %
 %    o exception: return any errors or warnings in this structure.
 %
 */
+
+typedef struct _CannyInfo
+{
+  double
+    Dx,
+    Dy,
+    magnitude,
+    intensity;
+} CannyInfo;
+
+static 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 TraceEdge(Image *edge_image,CacheView *edge_view,
+  MatrixInfo *pixel_cache,const ssize_t x,const ssize_t y,
+  const double threshold,ExceptionInfo *exception)
+{
+  CannyInfo
+    pixel;
+
+  Quantum
+    *q;
+
+  MagickBooleanType
+    status;
+
+  q=GetCacheViewAuthenticPixels(edge_view,x,y,1,1,exception);
+  if ((q != (Quantum *) NULL) && (GetPixelIntensity(edge_image,q) == 0))
+    {
+      ssize_t
+        v;
+
+      *q=QuantumRange;
+      status=SyncCacheViewAuthenticPixels(edge_view,exception);
+      if (status != MagickFalse)
+        {
+          for (v=(-1); v <= 1; v++)
+          {
+            ssize_t
+              u;
+
+            for (u=(-1); u <= 1; u++)
+            {
+              if (((y != 0) || (u != 0)) &&
+                  (IsAuthenticPixel(edge_image,x+u,y+v) != MagickFalse))
+                {
+                  (void) GetMatrixElement(pixel_cache,x+u,y+v,&pixel);
+                  if (pixel.intensity >= threshold)
+                    {
+                      status=TraceEdge(edge_image,edge_view,pixel_cache,x+u,y+v,
+                        threshold,exception);
+                      if (status != MagickFalse)
+                        return(MagickTrue);
+                    }
+                }
+            }
+          }
+          return(MagickTrue);
+        }
+    }
+  return(MagickFalse);
+}
+
 MagickExport Image *CannyEdgeImage(const Image *image,const double radius,
-  const double sigma,const double low_factor,const double high_factor,
+  const double sigma,const double low_percent,const double high_percent,
   ExceptionInfo *exception)
 {
+  CacheView
+    *edge_view;
+
+  char
+    geometry[MaxTextExtent];
+
+  double
+    high_threshold,
+    low_threshold;
+
   Image
     *edge_image;
 
+  KernelInfo
+    *kernel_info;
+
+  MagickBooleanType
+    status;
+
+  MatrixInfo
+    *pixel_cache;
+
+  register ssize_t
+    i;
+
+  size_t
+    *histogram,
+    number_pixels;
+
+  ssize_t
+    count,
+    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);
-  edge_image=(Image *) NULL;
+  /*
+    Filter out noise before trying to locate and detect any edges.
+  */
+  (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 edge strength by taking the gradient of the image.
+  */
+  pixel_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows,
+    sizeof(CannyInfo),exception);
+  if (pixel_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,-1,y-1,edge_image->columns+2,3,
+      exception);
+    if (p == (const Quantum *) NULL)
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) edge_image->columns; x++)
+    {
+      CannyInfo
+        pixel;
+
+      register const Quantum
+        *restrict kernel_pixels;
+
+      ssize_t
+        v;
+
+      static double
+        Gx[3][3] =
+        {
+          { -1.0,  0.0, +1.0 },
+          { -2.0,  0.0, +2.0 },
+          { -1.0,  0.0, +1.0 }
+        },
+        Gy[3][3] =
+        {
+          { +1.0, +2.0, +1.0 },
+          {  0.0,  0.0,  0.0 },
+          { -1.0, -2.0, -1.0 }
+        };
+
+      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
+      kernel_pixels=p;
+      for (v=0; v < 3; v++)
+      {
+        ssize_t
+          u;
+
+        for (u=0; u < 3; u++)
+        {
+          double
+            intensity;
+
+          intensity=GetPixelIntensity(edge_image,kernel_pixels+u);
+          pixel.Dx+=Gx[v][u]*intensity;
+          pixel.Dy+=Gy[v][u]*intensity;
+        }
+        kernel_pixels+=edge_image->columns+2;
+      }
+      pixel.magnitude=sqrt(pixel.Dx*pixel.Dx+pixel.Dy*pixel.Dy);
+      if (SetMatrixElement(pixel_cache,x,y,&pixel) == MagickFalse)
+        continue;
+      p+=GetPixelChannels(edge_image);
+    }
+  }
+  edge_view=DestroyCacheView(edge_view);
+  /*
+    Non-maxima suppression; reset edge image.
+  */
+  histogram=(size_t *) AcquireQuantumMemory(65536,sizeof(*histogram));
+  if (histogram == (size_t *) NULL)
+    {
+      pixel_cache=DestroyMatrixInfo(pixel_cache);
+      edge_image=DestroyImage(edge_image);
+      return((Image *) NULL);
+    }
+  (void) ResetMagickMemory(histogram,0,65536*sizeof(*histogram));
+  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;
+
+      ssize_t
+        direction;
+
+      (void) GetMatrixElement(pixel_cache,x,y,&pixel);
+      direction=2;
+      if (pixel.Dx != 0.0)
+        {
+          double
+            sector;
+
+          sector=pixel.Dy/pixel.Dx;
+          if (sector < 0.0)
+            {
+              if (sector < -2.41421356237)
+                direction=0;
+              else
+                if (sector < -0.414213562373)
+                  direction=1;
+                else
+                  direction=2;
+            }
+          else
+            {
+              if (sector > 2.41421356237)
+                direction=0;
+              else
+                if (sector > 0.414213562373)
+                  direction=3;
+                else
+                  direction=2;
+            }
+        }
+      switch (direction)
+      {
+        case 0:
+        {
+          /*
+            0 degrees.
+          */
+          (void) GetMatrixElement(pixel_cache,x,y-1,&alpha_pixel);
+          (void) GetMatrixElement(pixel_cache,x,y+1,&beta_pixel);
+          break;
+        }
+        case 1:
+        {
+          /*
+            45 degrees.
+          */
+          (void) GetMatrixElement(pixel_cache,x-1,y-1,&alpha_pixel);
+          (void) GetMatrixElement(pixel_cache,x+1,y+1,&beta_pixel);
+          break;
+        }
+        case 2:
+        {
+          /*
+            90 degrees.
+          */
+          (void) GetMatrixElement(pixel_cache,x-1,y,&alpha_pixel);
+          (void) GetMatrixElement(pixel_cache,x+1,y,&beta_pixel);
+        }
+        case 3:
+        {
+          /*
+            135 degrees.
+          */
+          (void) GetMatrixElement(pixel_cache,x+1,y-1,&alpha_pixel);
+          (void) GetMatrixElement(pixel_cache,x-1,y+1,&beta_pixel);
+        }
+      }
+      pixel.intensity=pixel.magnitude;
+      if ((pixel.magnitude < alpha_pixel.magnitude) ||
+          (pixel.magnitude < beta_pixel.magnitude))
+        pixel.intensity=0;
+      else
+        if (pixel.magnitude > QuantumRange)
+          pixel.intensity=QuantumRange;
+      (void) SetMatrixElement(pixel_cache,x,y,&pixel);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+      #pragma omp critical (MagickCore_CannyEdgeImage)
+#endif
+      histogram[ScaleQuantumToShort(ClampToQuantum(pixel.intensity))]++;
+      *q=0;
+      q+=GetPixelChannels(edge_image);
+    }
+    if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse)
+      status=MagickFalse;
+  }
+  edge_view=DestroyCacheView(edge_view);
+  /*
+    Estimate hysteresis threshold.
+  */
+  number_pixels=(size_t) (low_percent*(image->columns*image->rows-
+    histogram[0]));
+  count=0;
+  for (i=65535; count < (ssize_t) number_pixels; i--)
+    count+=histogram[i];
+  high_threshold=(double) ScaleShortToQuantum((unsigned short) i);
+  for (i=0; histogram[i] == 0; i++) ;
+  low_threshold=high_percent*(high_threshold+
+    ScaleShortToQuantum((unsigned short) i));
+  histogram=(size_t *) RelinquishMagickMemory(histogram);
+  /*
+    Hysteresis thresholding.
+  */
+  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
+        pixel;
+
+      (void) GetMatrixElement(pixel_cache,x,y,&pixel);
+      if (pixel.intensity >= high_threshold)
+        (void) TraceEdge(edge_image,edge_view,pixel_cache,x,y,low_threshold,
+          exception);
+    }
+    if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse)
+      status=MagickFalse;
+  }
+  edge_view=DestroyCacheView(edge_view);
+  pixel_cache=DestroyMatrixInfo(pixel_cache);
   return(edge_image);
 }
 \f