]> granicus.if.org Git - imagemagick/commitdiff
(no commit message)
authorcristy <urban-warrior@git.imagemagick.org>
Sat, 26 Apr 2014 14:13:53 +0000 (14:13 +0000)
committercristy <urban-warrior@git.imagemagick.org>
Sat, 26 Apr 2014 14:13:53 +0000 (14:13 +0000)
12 files changed:
MagickCore/feature.c
MagickCore/feature.h
MagickCore/option.c
MagickWand/convert.c
MagickWand/mogrify.c
MagickWand/operation.c
PerlMagick/Magick.xs
PerlMagick/quantum/quantum.xs.in
utilities/convert.1
utilities/convert.1.in
utilities/mogrify.1
utilities/mogrify.1.in

index 589af52b207fb3974f9aaf5f3d1f6d40cc90e666..fa1e9c5bfd3d10079959d8e9c3625b26f8368d85 100644 (file)
@@ -556,112 +556,146 @@ MagickExport Image *CannyEdgeImage(const Image *image,const double radius,
 %                                                                             %
 %                                                                             %
 %                                                                             %
-%     H o u g h L i n e s I m a g e                                           %
+%   G e t I m a g e F e a t u r e s                                           %
 %                                                                             %
 %                                                                             %
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  HoughLinesImage() identifies lines in the image.
+%  GetImageFeatures() returns features for each channel in the image in
+%  each of four directions (horizontal, vertical, left and right diagonals)
+%  for the specified distance.  The features include the angular second
+%  moment, contrast, correlation, sum of squares: variance, inverse difference
+%  moment, sum average, sum varience, sum entropy, entropy, difference variance,%  difference entropy, information measures of correlation 1, information
+%  measures of correlation 2, and maximum correlation coefficient.  You can
+%  access the red channel contrast, for example, like this:
+%
+%      channel_features=GetImageFeatures(image,1,exception);
+%      contrast=channel_features[RedPixelChannel].contrast[0];
+%
+%  Use MagickRelinquishMemory() to free the features buffer.
 %
-%  The format of the HoughLinesImage method is:
+%  The format of the GetImageFeatures method is:
 %
-%      Image *HoughLinesImage(const Image *image,const size_t width,
-%        const size_t height,const size_t threshold,ExceptionInfo *exception)
+%      ChannelFeatures *GetImageFeatures(const Image *image,
+%        const size_t distance,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 distance: the distance.
 %
 %    o exception: return any errors or warnings in this structure.
 %
 */
 
-static inline double MagickRound(double x)
+static inline ssize_t MagickAbsoluteValue(const ssize_t x)
 {
-  /*
-    Round the fraction to nearest integer.
-  */
-  if ((x-floor(x)) < (ceil(x)-x))
-    return(floor(x));
-  return(ceil(x));
+  if (x < 0)
+    return(-x);
+  return(x);
 }
 
-MagickExport Image *HoughLinesImage(const Image *image,const size_t width,
-  const size_t height,const size_t threshold,ExceptionInfo *exception)
+static inline double MagickLog10(const double x)
 {
-  CacheView
-    *image_view;
+#define Log10Epsilon  (1.0e-11)
 
-  char
-    message[MaxTextExtent],
-    path[MaxTextExtent];
+ if (fabs(x) < Log10Epsilon)
+   return(log10(Log10Epsilon));
+ return(log10(fabs(x)));
+}
 
-  const char
-    *artifact;
+MagickExport ChannelFeatures *GetImageFeatures(const Image *image,
+  const size_t distance,ExceptionInfo *exception)
+{
+  typedef struct _ChannelStatistics
+  {
+    PixelInfo
+      direction[4];  /* horizontal, vertical, left and right diagonals */
+  } ChannelStatistics;
 
-  double
-    hough_height;
+  CacheView
+    *image_view;
 
-  Image
-    *lines_image = NULL;
+  ChannelFeatures
+    *channel_features;
 
-  ImageInfo
-    *image_info;
+  ChannelStatistics
+    **cooccurrence,
+    correlation,
+    *density_x,
+    *density_xy,
+    *density_y,
+    entropy_x,
+    entropy_xy,
+    entropy_xy1,
+    entropy_xy2,
+    entropy_y,
+    mean,
+    **Q,
+    *sum,
+    sum_squares,
+    variance;
 
-  int
-    file;
+  PixelPacket
+    gray,
+    *grays;
 
   MagickBooleanType
     status;
 
-  MatrixInfo
-    *accumulator;
+  register ssize_t
+    i;
 
-  PointInfo
-    center;
+  size_t
+    length;
 
-  register ssize_t
+  ssize_t
     y;
 
-  size_t
-    accumulator_height,
-    accumulator_width,
-    line_count;
+  unsigned int
+    number_grays;
 
-  /*
-    Create the accumulator.
-  */
-  assert(image != (const Image *) NULL);
+  assert(image != (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);
-  accumulator_width=180;
-  hough_height=((sqrt(2.0)*(double) (image->rows > image->columns ?
-    image->rows : image->columns))/2.0);
-  accumulator_height=(size_t) (2.0*hough_height);
-  accumulator=AcquireMatrixInfo(accumulator_width,accumulator_height,
-    sizeof(double),exception);
-  if (accumulator == (MatrixInfo *) NULL)
-    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
-  if (NullMatrix(accumulator) == MagickFalse)
-    {
-      accumulator=DestroyMatrixInfo(accumulator);
-      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
-    }
+  if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
+    return((ChannelFeatures *) NULL);
+  length=CompositeChannels+1UL;
+  channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
+    sizeof(*channel_features));
+  if (channel_features == (ChannelFeatures *) NULL)
+    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+  (void) ResetMagickMemory(channel_features,0,length*
+    sizeof(*channel_features));
   /*
-    Populate the accumulator.
+    Form grays.
   */
+  grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
+  if (grays == (PixelPacket *) NULL)
+    {
+      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
+        channel_features);
+      (void) ThrowMagickException(exception,GetMagickModule(),
+        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+      return(channel_features);
+    }
+  for (i=0; i <= (ssize_t) MaxMap; i++)
+  {
+    grays[i].red=(~0U);
+    grays[i].green=(~0U);
+    grays[i].blue=(~0U);
+    grays[i].alpha=(~0U);
+    grays[i].black=(~0U);
+  }
   status=MagickTrue;
-  center.x=(double) image->columns/2.0;
-  center.y=(double) image->rows/2.0;
   image_view=AcquireVirtualCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(static,4) shared(status) \
+    magick_threads(image,image,image->rows,1)
+#endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
     register const Quantum
@@ -673,416 +707,82 @@ MagickExport Image *HoughLinesImage(const Image *image,const size_t width,
     if (status == MagickFalse)
       continue;
     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
-    if (p == (Quantum *) NULL)
+    if (p == (const Quantum *) NULL)
       {
         status=MagickFalse;
         continue;
       }
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      if (GetPixelIntensity(image,p) > (QuantumRange/2))
-        {
-          register ssize_t
-            i;
-
-          for (i=0; i < 180; i++)
-          {
-            double
-              count,
-              radius;
-
-            radius=(((double) x-center.x)*cos(DegreesToRadians((double) i)))+
-              (((double) y-center.y)*sin(DegreesToRadians((double) i)));
-            (void) GetMatrixElement(accumulator,i,(ssize_t)
-              MagickRound(radius+hough_height),&count);
-            count++;
-            (void) SetMatrixElement(accumulator,i,(ssize_t)
-              MagickRound(radius+hough_height),&count);
-          }
-        }
+      grays[ScaleQuantumToMap(GetPixelRed(image,p))].red=
+        ScaleQuantumToMap(GetPixelRed(image,p));
+      grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green=
+        ScaleQuantumToMap(GetPixelGreen(image,p));
+      grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue=
+        ScaleQuantumToMap(GetPixelBlue(image,p));
+      if (image->colorspace == CMYKColorspace)
+        grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black=
+          ScaleQuantumToMap(GetPixelBlack(image,p));
+      if (image->alpha_trait == BlendPixelTrait)
+        grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha=
+          ScaleQuantumToMap(GetPixelAlpha(image,p));
       p+=GetPixelChannels(image);
     }
   }
   image_view=DestroyCacheView(image_view);
   if (status == MagickFalse)
     {
-      accumulator=DestroyMatrixInfo(accumulator);
-      return((Image *) NULL);
+      grays=(PixelPacket *) RelinquishMagickMemory(grays);
+      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
+        channel_features);
+      return(channel_features);
     }
+  (void) ResetMagickMemory(&gray,0,sizeof(gray));
+  for (i=0; i <= (ssize_t) MaxMap; i++)
+  {
+    if (grays[i].red != ~0U)
+      grays[gray.red++].red=grays[i].red;
+    if (grays[i].green != ~0U)
+      grays[gray.green++].green=grays[i].green;
+    if (grays[i].blue != ~0U)
+      grays[gray.blue++].blue=grays[i].blue;
+    if (image->colorspace == CMYKColorspace)
+      if (grays[i].black != ~0U)
+        grays[gray.black++].black=grays[i].black;
+    if (image->alpha_trait == BlendPixelTrait)
+      if (grays[i].alpha != ~0U)
+        grays[gray.alpha++].alpha=grays[i].alpha;
+  }
   /*
-    Generate line segments from accumulator.
+    Allocate spatial dependence matrix.
   */
-  file=AcquireUniqueFileResource(path);
-  if (file == -1)
-    {
-      accumulator=DestroyMatrixInfo(accumulator);
-      return((Image *) NULL);
-    }
-  (void) FormatLocaleString(message,MaxTextExtent,
-    "# Hough line transform: %.20gx%.20g%+.20g\n",(double) width,
-    (double) height,(double) threshold);
-  (void) write(file,message,strlen(message));
-  (void) FormatLocaleString(message,MaxTextExtent,"viewbox 0 0 %.20g %.20g\n",
-    (double) image->columns,(double) image->rows);
-  (void) write(file,message,strlen(message));
-  line_count=image->columns > image->rows ? image->columns/4 : image->rows/4;
-  if (threshold != 0)
-    line_count=threshold;
-  for (y=0; y < (ssize_t) accumulator_height; y++)
-  {
-    register ssize_t
-      x;
-
-    for (x=0; x < (ssize_t) accumulator_width; x++)
-    {
-      double
-        count;
-
-      (void) GetMatrixElement(accumulator,x,y,&count);
-      if (count >= (double) line_count)
-        {
-          double
-            maxima;
-
-          SegmentInfo
-            line;
-
-          ssize_t
-            v;
-
-          /*
-            Is point a local maxima?
-          */
-          maxima=count;
-          for (v=(-((ssize_t) height/2)); v < (((ssize_t) height/2)); v++)
-          {
-            ssize_t
-              u;
-
-            for (u=(-((ssize_t) width/2)); u < (((ssize_t) width/2)); u++)
-            {
-              if ((u != 0) || (v !=0))
-                {
-                  (void) GetMatrixElement(accumulator,x+u,y+v,&count);
-                  if (count > maxima)
-                    {
-                      maxima=count;
-                      break;
-                    }
-                }
-            }
-            if (u < (ssize_t) (width/2))
-              break;
-          }
-          (void) GetMatrixElement(accumulator,x,y,&count);
-          if (maxima > count)
-            continue;
-          if ((x >= 45) && (x <= 135))
-            {
-              /*
-                y = (r-x cos(t))/sin(t)
-              */
-              line.x1=0.0;
-              line.y1=((double) (y-(accumulator_height/2.0))-((line.x1-
-                (image->columns/2.0))*cos(DegreesToRadians((double) x))))/
-                sin(DegreesToRadians((double) x))+(image->rows/2.0);
-              line.x2=(double) image->columns;
-              line.y2=((double) (y-(accumulator_height/2.0))-((line.x2-
-                (image->columns/2.0))*cos(DegreesToRadians((double) x))))/
-                sin(DegreesToRadians((double) x))+(image->rows/2.0);
-            }
-          else
-            {
-              /*
-                x = (r-y cos(t))/sin(t)
-              */
-              line.y1=0.0;
-              line.x1=((double) (y-(accumulator_height/2.0))-((line.y1-
-                (image->rows/2.0))*sin(DegreesToRadians((double) x))))/
-                cos(DegreesToRadians((double) x))+(image->columns/2.0);
-              line.y2=(double) image->rows;
-              line.x2=((double) (y-(accumulator_height/2.0))-((line.y2-
-                (image->rows/2.0))*sin(DegreesToRadians((double) x))))/
-                cos(DegreesToRadians((double) x))+(image->columns/2.0);
-            }
-          (void) FormatLocaleString(message,MaxTextExtent,
-            "line %g,%g %g,%g  # %g\n",line.x1,line.y1,line.x2,line.y2,maxima);
-          (void) write(file,message,strlen(message));
-        }
-    }
-  }
-  (void) close(file);
-  /*
-    Render lines to image canvas.
-  */
-  image_info=AcquireImageInfo();
-  image_info->background_color=image->background_color;
-  (void) FormatLocaleString(image_info->filename,MaxTextExtent,"mvg:%s",path);
-  artifact=GetImageArtifact(image,"background");
-  if (artifact != (const char *) NULL)
-    (void) SetImageOption(image_info,"background",artifact);
-  artifact=GetImageArtifact(image,"fill");
-  if (artifact != (const char *) NULL)
-    (void) SetImageOption(image_info,"fill",artifact);
-  artifact=GetImageArtifact(image,"stroke");
-  if (artifact != (const char *) NULL)
-    (void) SetImageOption(image_info,"stroke",artifact);
-  artifact=GetImageArtifact(image,"strokewidth");
-  if (artifact != (const char *) NULL)
-    (void) SetImageOption(image_info,"strokewidth",artifact);
-  lines_image=ReadImage(image_info,exception);
-  artifact=GetImageArtifact(image,"hough-lines:accumulator");
-  if ((lines_image != (Image *) NULL) &&
-      (IsStringTrue(artifact) != MagickFalse))
-    {
-      Image
-        *accumulator_image;
-
-      accumulator_image=MatrixToImage(accumulator,exception);
-      if (accumulator_image != (Image *) NULL)
-        AppendImageToList(&lines_image,accumulator_image);
-    }
-  /*
-    Free resources.
-  */
-  accumulator=DestroyMatrixInfo(accumulator);
-  image_info=DestroyImageInfo(image_info);
-  (void) RelinquishUniqueFileResource(path);
-  return(GetFirstImageInList(lines_image));
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%   G e t I m a g e F e a t u r e s                                           %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  GetImageFeatures() returns features for each channel in the image in
-%  each of four directions (horizontal, vertical, left and right diagonals)
-%  for the specified distance.  The features include the angular second
-%  moment, contrast, correlation, sum of squares: variance, inverse difference
-%  moment, sum average, sum varience, sum entropy, entropy, difference variance,%  difference entropy, information measures of correlation 1, information
-%  measures of correlation 2, and maximum correlation coefficient.  You can
-%  access the red channel contrast, for example, like this:
-%
-%      channel_features=GetImageFeatures(image,1,exception);
-%      contrast=channel_features[RedPixelChannel].contrast[0];
-%
-%  Use MagickRelinquishMemory() to free the features buffer.
-%
-%  The format of the GetImageFeatures method is:
-%
-%      ChannelFeatures *GetImageFeatures(const Image *image,
-%        const size_t distance,ExceptionInfo *exception)
-%
-%  A description of each parameter follows:
-%
-%    o image: the image.
-%
-%    o distance: the distance.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-
-static inline ssize_t MagickAbsoluteValue(const ssize_t x)
-{
-  if (x < 0)
-    return(-x);
-  return(x);
-}
-
-static inline double MagickLog10(const double x)
-{
-#define Log10Epsilon  (1.0e-11)
-
- if (fabs(x) < Log10Epsilon)
-   return(log10(Log10Epsilon));
- return(log10(fabs(x)));
-}
-
-MagickExport ChannelFeatures *GetImageFeatures(const Image *image,
-  const size_t distance,ExceptionInfo *exception)
-{
-  typedef struct _ChannelStatistics
-  {
-    PixelInfo
-      direction[4];  /* horizontal, vertical, left and right diagonals */
-  } ChannelStatistics;
-
-  CacheView
-    *image_view;
-
-  ChannelFeatures
-    *channel_features;
-
-  ChannelStatistics
-    **cooccurrence,
-    correlation,
-    *density_x,
-    *density_xy,
-    *density_y,
-    entropy_x,
-    entropy_xy,
-    entropy_xy1,
-    entropy_xy2,
-    entropy_y,
-    mean,
-    **Q,
-    *sum,
-    sum_squares,
-    variance;
-
-  PixelPacket
-    gray,
-    *grays;
-
-  MagickBooleanType
-    status;
-
-  register ssize_t
-    i;
-
-  size_t
-    length;
-
-  ssize_t
-    y;
-
-  unsigned int
-    number_grays;
-
-  assert(image != (Image *) NULL);
-  assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  if ((image->columns < (distance+1)) || (image->rows < (distance+1)))
-    return((ChannelFeatures *) NULL);
-  length=CompositeChannels+1UL;
-  channel_features=(ChannelFeatures *) AcquireQuantumMemory(length,
-    sizeof(*channel_features));
-  if (channel_features == (ChannelFeatures *) NULL)
-    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
-  (void) ResetMagickMemory(channel_features,0,length*
-    sizeof(*channel_features));
-  /*
-    Form grays.
-  */
-  grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays));
-  if (grays == (PixelPacket *) NULL)
-    {
-      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
-        channel_features);
-      (void) ThrowMagickException(exception,GetMagickModule(),
-        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
-      return(channel_features);
-    }
-  for (i=0; i <= (ssize_t) MaxMap; i++)
-  {
-    grays[i].red=(~0U);
-    grays[i].green=(~0U);
-    grays[i].blue=(~0U);
-    grays[i].alpha=(~0U);
-    grays[i].black=(~0U);
-  }
-  status=MagickTrue;
-  image_view=AcquireVirtualCacheView(image,exception);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(static,4) shared(status) \
-    magick_threads(image,image,image->rows,1)
-#endif
-  for (y=0; y < (ssize_t) image->rows; y++)
-  {
-    register const Quantum
-      *restrict p;
-
-    register ssize_t
-      x;
-
-    if (status == MagickFalse)
-      continue;
-    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
-    if (p == (const Quantum *) NULL)
-      {
-        status=MagickFalse;
-        continue;
-      }
-    for (x=0; x < (ssize_t) image->columns; x++)
-    {
-      grays[ScaleQuantumToMap(GetPixelRed(image,p))].red=
-        ScaleQuantumToMap(GetPixelRed(image,p));
-      grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green=
-        ScaleQuantumToMap(GetPixelGreen(image,p));
-      grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue=
-        ScaleQuantumToMap(GetPixelBlue(image,p));
-      if (image->colorspace == CMYKColorspace)
-        grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black=
-          ScaleQuantumToMap(GetPixelBlack(image,p));
-      if (image->alpha_trait == BlendPixelTrait)
-        grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha=
-          ScaleQuantumToMap(GetPixelAlpha(image,p));
-      p+=GetPixelChannels(image);
-    }
-  }
-  image_view=DestroyCacheView(image_view);
-  if (status == MagickFalse)
-    {
-      grays=(PixelPacket *) RelinquishMagickMemory(grays);
-      channel_features=(ChannelFeatures *) RelinquishMagickMemory(
-        channel_features);
-      return(channel_features);
-    }
-  (void) ResetMagickMemory(&gray,0,sizeof(gray));
-  for (i=0; i <= (ssize_t) MaxMap; i++)
-  {
-    if (grays[i].red != ~0U)
-      grays[gray.red++].red=grays[i].red;
-    if (grays[i].green != ~0U)
-      grays[gray.green++].green=grays[i].green;
-    if (grays[i].blue != ~0U)
-      grays[gray.blue++].blue=grays[i].blue;
-    if (image->colorspace == CMYKColorspace)
-      if (grays[i].black != ~0U)
-        grays[gray.black++].black=grays[i].black;
-    if (image->alpha_trait == BlendPixelTrait)
-      if (grays[i].alpha != ~0U)
-        grays[gray.alpha++].alpha=grays[i].alpha;
-  }
-  /*
-    Allocate spatial dependence matrix.
-  */
-  number_grays=gray.red;
-  if (gray.green > number_grays)
-    number_grays=gray.green;
-  if (gray.blue > number_grays)
-    number_grays=gray.blue;
-  if (image->colorspace == CMYKColorspace)
-    if (gray.black > number_grays)
-      number_grays=gray.black;
-  if (image->alpha_trait == BlendPixelTrait)
-    if (gray.alpha > number_grays)
-      number_grays=gray.alpha;
-  cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
-    sizeof(*cooccurrence));
-  density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
-    sizeof(*density_x));
-  density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
-    sizeof(*density_xy));
-  density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
-    sizeof(*density_y));
-  Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q));
-  sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
-  if ((cooccurrence == (ChannelStatistics **) NULL) ||
-      (density_x == (ChannelStatistics *) NULL) ||
-      (density_xy == (ChannelStatistics *) NULL) ||
-      (density_y == (ChannelStatistics *) NULL) ||
-      (Q == (ChannelStatistics **) NULL) ||
-      (sum == (ChannelStatistics *) NULL))
+  number_grays=gray.red;
+  if (gray.green > number_grays)
+    number_grays=gray.green;
+  if (gray.blue > number_grays)
+    number_grays=gray.blue;
+  if (image->colorspace == CMYKColorspace)
+    if (gray.black > number_grays)
+      number_grays=gray.black;
+  if (image->alpha_trait == BlendPixelTrait)
+    if (gray.alpha > number_grays)
+      number_grays=gray.alpha;
+  cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays,
+    sizeof(*cooccurrence));
+  density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
+    sizeof(*density_x));
+  density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
+    sizeof(*density_xy));
+  density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1),
+    sizeof(*density_y));
+  Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q));
+  sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum));
+  if ((cooccurrence == (ChannelStatistics **) NULL) ||
+      (density_x == (ChannelStatistics *) NULL) ||
+      (density_xy == (ChannelStatistics *) NULL) ||
+      (density_y == (ChannelStatistics *) NULL) ||
+      (Q == (ChannelStatistics **) NULL) ||
+      (sum == (ChannelStatistics *) NULL))
     {
       if (Q != (ChannelStatistics **) NULL)
         {
@@ -1889,114 +1589,453 @@ MagickExport ChannelFeatures *GetImageFeatures(const Image *image,
         entropy_xy.direction[i].alpha)))));
   }
   /*
-    Compute more texture features.
+    Compute more texture features.
+  */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(static,4) shared(status) \
+    magick_threads(image,image,number_grays,1)
+#endif
+  for (i=0; i < 4; i++)
+  {
+    ssize_t
+      z;
+
+    for (z=0; z < (ssize_t) number_grays; z++)
+    {
+      register ssize_t
+        y;
+
+      ChannelStatistics
+        pixel;
+
+      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
+      for (y=0; y < (ssize_t) number_grays; y++)
+      {
+        register ssize_t
+          x;
+
+        for (x=0; x < (ssize_t) number_grays; x++)
+        {
+          /*
+            Contrast:  amount of local variations present in an image.
+          */
+          if (((y-x) == z) || ((x-y) == z))
+            {
+              pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
+              pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
+              pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
+              if (image->colorspace == CMYKColorspace)
+                pixel.direction[i].black+=cooccurrence[x][y].direction[i].black;
+              if (image->alpha_trait == BlendPixelTrait)
+                pixel.direction[i].alpha+=
+                  cooccurrence[x][y].direction[i].alpha;
+            }
+          /*
+            Maximum Correlation Coefficient.
+          */
+          Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red*
+            cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/
+            density_y[x].direction[i].red;
+          Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green*
+            cooccurrence[y][x].direction[i].green/
+            density_x[z].direction[i].green/density_y[x].direction[i].red;
+          Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue*
+            cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/
+            density_y[x].direction[i].blue;
+          if (image->colorspace == CMYKColorspace)
+            Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black*
+              cooccurrence[y][x].direction[i].black/
+              density_x[z].direction[i].black/density_y[x].direction[i].black;
+          if (image->alpha_trait == BlendPixelTrait)
+            Q[z][y].direction[i].alpha+=
+              cooccurrence[z][x].direction[i].alpha*
+              cooccurrence[y][x].direction[i].alpha/
+              density_x[z].direction[i].alpha/
+              density_y[x].direction[i].alpha;
+        }
+      }
+      channel_features[RedPixelChannel].contrast[i]+=z*z*
+        pixel.direction[i].red;
+      channel_features[GreenPixelChannel].contrast[i]+=z*z*
+        pixel.direction[i].green;
+      channel_features[BluePixelChannel].contrast[i]+=z*z*
+        pixel.direction[i].blue;
+      if (image->colorspace == CMYKColorspace)
+        channel_features[BlackPixelChannel].contrast[i]+=z*z*
+          pixel.direction[i].black;
+      if (image->alpha_trait == BlendPixelTrait)
+        channel_features[AlphaPixelChannel].contrast[i]+=z*z*
+          pixel.direction[i].alpha;
+    }
+    /*
+      Maximum Correlation Coefficient.
+      Future: return second largest eigenvalue of Q.
+    */
+    channel_features[RedPixelChannel].maximum_correlation_coefficient[i]=
+      sqrt((double) -1.0);
+    channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]=
+      sqrt((double) -1.0);
+    channel_features[BluePixelChannel].maximum_correlation_coefficient[i]=
+      sqrt((double) -1.0);
+    if (image->colorspace == CMYKColorspace)
+      channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]=
+        sqrt((double) -1.0);
+    if (image->alpha_trait == BlendPixelTrait)
+      channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]=
+        sqrt((double) -1.0);
+  }
+  /*
+    Relinquish resources.
+  */
+  sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
+  for (i=0; i < (ssize_t) number_grays; i++)
+    Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
+  Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
+  density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
+  density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
+  density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
+  for (i=0; i < (ssize_t) number_grays; i++)
+    cooccurrence[i]=(ChannelStatistics *)
+      RelinquishMagickMemory(cooccurrence[i]);
+  cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
+  return(channel_features);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%     H o u g h L i n e I m a g e                                             %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  HoughLineImage() identifies lines in the image.
+%
+%  The format of the HoughLineImage method is:
+%
+%      Image *HoughLineImage(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.
+%
+*/
+
+static inline double MagickRound(double x)
+{
+  /*
+    Round the fraction to nearest integer.
+  */
+  if ((x-floor(x)) < (ceil(x)-x))
+    return(floor(x));
+  return(ceil(x));
+}
+
+MagickExport Image *HoughLineImage(const Image *image,const size_t width,
+  const size_t height,const size_t threshold,ExceptionInfo *exception)
+{
+  CacheView
+    *image_view;
+
+  char
+    message[MaxTextExtent],
+    path[MaxTextExtent];
+
+  const char
+    *artifact;
+
+  double
+    hough_height;
+
+  Image
+    *lines_image = NULL;
+
+  ImageInfo
+    *image_info;
+
+  int
+    file;
+
+  MagickBooleanType
+    status;
+
+  MatrixInfo
+    *accumulator;
+
+  PointInfo
+    center;
+
+  register ssize_t
+    y;
+
+  size_t
+    accumulator_height,
+    accumulator_width,
+    line_count;
+
+  /*
+    Create the accumulator.
+  */
+  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);
+  accumulator_width=180;
+  hough_height=((sqrt(2.0)*(double) (image->rows > image->columns ?
+    image->rows : image->columns))/2.0);
+  accumulator_height=(size_t) (2.0*hough_height);
+  accumulator=AcquireMatrixInfo(accumulator_width,accumulator_height,
+    sizeof(double),exception);
+  if (accumulator == (MatrixInfo *) NULL)
+    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+  if (NullMatrix(accumulator) == MagickFalse)
+    {
+      accumulator=DestroyMatrixInfo(accumulator);
+      ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+    }
+  /*
+    Populate the accumulator.
+  */
+  status=MagickTrue;
+  center.x=(double) image->columns/2.0;
+  center.y=(double) image->rows/2.0;
+  image_view=AcquireVirtualCacheView(image,exception);
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    register const Quantum
+      *restrict p;
+
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+    if (p == (Quantum *) NULL)
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      if (GetPixelIntensity(image,p) > (QuantumRange/2))
+        {
+          register ssize_t
+            i;
+
+          for (i=0; i < 180; i++)
+          {
+            double
+              count,
+              radius;
+
+            radius=(((double) x-center.x)*cos(DegreesToRadians((double) i)))+
+              (((double) y-center.y)*sin(DegreesToRadians((double) i)));
+            (void) GetMatrixElement(accumulator,i,(ssize_t)
+              MagickRound(radius+hough_height),&count);
+            count++;
+            (void) SetMatrixElement(accumulator,i,(ssize_t)
+              MagickRound(radius+hough_height),&count);
+          }
+        }
+      p+=GetPixelChannels(image);
+    }
+  }
+  image_view=DestroyCacheView(image_view);
+  if (status == MagickFalse)
+    {
+      accumulator=DestroyMatrixInfo(accumulator);
+      return((Image *) NULL);
+    }
+  /*
+    Generate line segments from accumulator.
   */
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(static,4) shared(status) \
-    magick_threads(image,image,number_grays,1)
-#endif
-  for (i=0; i < 4; i++)
+  file=AcquireUniqueFileResource(path);
+  if (file == -1)
+    {
+      accumulator=DestroyMatrixInfo(accumulator);
+      return((Image *) NULL);
+    }
+  (void) FormatLocaleString(message,MaxTextExtent,
+    "# Hough line transform: %.20gx%.20g%+.20g\n",(double) width,
+    (double) height,(double) threshold);
+  (void) write(file,message,strlen(message));
+  (void) FormatLocaleString(message,MaxTextExtent,"viewbox 0 0 %.20g %.20g\n",
+    (double) image->columns,(double) image->rows);
+  (void) write(file,message,strlen(message));
+  line_count=image->columns > image->rows ? image->columns/4 : image->rows/4;
+  if (threshold != 0)
+    line_count=threshold;
+  for (y=0; y < (ssize_t) accumulator_height; y++)
   {
-    ssize_t
-      z;
+    register ssize_t
+      x;
 
-    for (z=0; z < (ssize_t) number_grays; z++)
+    for (x=0; x < (ssize_t) accumulator_width; x++)
     {
-      register ssize_t
-        y;
+      double
+        count;
 
-      ChannelStatistics
-        pixel;
+      (void) GetMatrixElement(accumulator,x,y,&count);
+      if (count >= (double) line_count)
+        {
+          double
+            maxima;
 
-      (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
-      for (y=0; y < (ssize_t) number_grays; y++)
-      {
-        register ssize_t
-          x;
+          SegmentInfo
+            line;
+
+          ssize_t
+            v;
 
-        for (x=0; x < (ssize_t) number_grays; x++)
-        {
           /*
-            Contrast:  amount of local variations present in an image.
+            Is point a local maxima?
           */
-          if (((y-x) == z) || ((x-y) == z))
+          maxima=count;
+          for (v=(-((ssize_t) height/2)); v < (((ssize_t) height/2)); v++)
+          {
+            ssize_t
+              u;
+
+            for (u=(-((ssize_t) width/2)); u < (((ssize_t) width/2)); u++)
             {
-              pixel.direction[i].red+=cooccurrence[x][y].direction[i].red;
-              pixel.direction[i].green+=cooccurrence[x][y].direction[i].green;
-              pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue;
-              if (image->colorspace == CMYKColorspace)
-                pixel.direction[i].black+=cooccurrence[x][y].direction[i].black;
-              if (image->alpha_trait == BlendPixelTrait)
-                pixel.direction[i].alpha+=
-                  cooccurrence[x][y].direction[i].alpha;
+              if ((u != 0) || (v !=0))
+                {
+                  (void) GetMatrixElement(accumulator,x+u,y+v,&count);
+                  if (count > maxima)
+                    {
+                      maxima=count;
+                      break;
+                    }
+                }
             }
-          /*
-            Maximum Correlation Coefficient.
-          */
-          Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red*
-            cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/
-            density_y[x].direction[i].red;
-          Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green*
-            cooccurrence[y][x].direction[i].green/
-            density_x[z].direction[i].green/density_y[x].direction[i].red;
-          Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue*
-            cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/
-            density_y[x].direction[i].blue;
-          if (image->colorspace == CMYKColorspace)
-            Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black*
-              cooccurrence[y][x].direction[i].black/
-              density_x[z].direction[i].black/density_y[x].direction[i].black;
-          if (image->alpha_trait == BlendPixelTrait)
-            Q[z][y].direction[i].alpha+=
-              cooccurrence[z][x].direction[i].alpha*
-              cooccurrence[y][x].direction[i].alpha/
-              density_x[z].direction[i].alpha/
-              density_y[x].direction[i].alpha;
+            if (u < (ssize_t) (width/2))
+              break;
+          }
+          (void) GetMatrixElement(accumulator,x,y,&count);
+          if (maxima > count)
+            continue;
+          if ((x >= 45) && (x <= 135))
+            {
+              /*
+                y = (r-x cos(t))/sin(t)
+              */
+              line.x1=0.0;
+              line.y1=((double) (y-(accumulator_height/2.0))-((line.x1-
+                (image->columns/2.0))*cos(DegreesToRadians((double) x))))/
+                sin(DegreesToRadians((double) x))+(image->rows/2.0);
+              line.x2=(double) image->columns;
+              line.y2=((double) (y-(accumulator_height/2.0))-((line.x2-
+                (image->columns/2.0))*cos(DegreesToRadians((double) x))))/
+                sin(DegreesToRadians((double) x))+(image->rows/2.0);
+            }
+          else
+            {
+              /*
+                x = (r-y cos(t))/sin(t)
+              */
+              line.y1=0.0;
+              line.x1=((double) (y-(accumulator_height/2.0))-((line.y1-
+                (image->rows/2.0))*sin(DegreesToRadians((double) x))))/
+                cos(DegreesToRadians((double) x))+(image->columns/2.0);
+              line.y2=(double) image->rows;
+              line.x2=((double) (y-(accumulator_height/2.0))-((line.y2-
+                (image->rows/2.0))*sin(DegreesToRadians((double) x))))/
+                cos(DegreesToRadians((double) x))+(image->columns/2.0);
+            }
+          (void) FormatLocaleString(message,MaxTextExtent,
+            "line %g,%g %g,%g  # %g\n",line.x1,line.y1,line.x2,line.y2,maxima);
+          (void) write(file,message,strlen(message));
         }
-      }
-      channel_features[RedPixelChannel].contrast[i]+=z*z*
-        pixel.direction[i].red;
-      channel_features[GreenPixelChannel].contrast[i]+=z*z*
-        pixel.direction[i].green;
-      channel_features[BluePixelChannel].contrast[i]+=z*z*
-        pixel.direction[i].blue;
-      if (image->colorspace == CMYKColorspace)
-        channel_features[BlackPixelChannel].contrast[i]+=z*z*
-          pixel.direction[i].black;
-      if (image->alpha_trait == BlendPixelTrait)
-        channel_features[AlphaPixelChannel].contrast[i]+=z*z*
-          pixel.direction[i].alpha;
     }
-    /*
-      Maximum Correlation Coefficient.
-      Future: return second largest eigenvalue of Q.
-    */
-    channel_features[RedPixelChannel].maximum_correlation_coefficient[i]=
-      sqrt((double) -1.0);
-    channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]=
-      sqrt((double) -1.0);
-    channel_features[BluePixelChannel].maximum_correlation_coefficient[i]=
-      sqrt((double) -1.0);
-    if (image->colorspace == CMYKColorspace)
-      channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]=
-        sqrt((double) -1.0);
-    if (image->alpha_trait == BlendPixelTrait)
-      channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]=
-        sqrt((double) -1.0);
   }
+  (void) close(file);
   /*
-    Relinquish resources.
+    Render lines to image canvas.
   */
-  sum=(ChannelStatistics *) RelinquishMagickMemory(sum);
-  for (i=0; i < (ssize_t) number_grays; i++)
-    Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]);
-  Q=(ChannelStatistics **) RelinquishMagickMemory(Q);
-  density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y);
-  density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy);
-  density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x);
-  for (i=0; i < (ssize_t) number_grays; i++)
-    cooccurrence[i]=(ChannelStatistics *)
-      RelinquishMagickMemory(cooccurrence[i]);
-  cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence);
-  return(channel_features);
+  image_info=AcquireImageInfo();
+  image_info->background_color=image->background_color;
+  (void) FormatLocaleString(image_info->filename,MaxTextExtent,"mvg:%s",path);
+  artifact=GetImageArtifact(image,"background");
+  if (artifact != (const char *) NULL)
+    (void) SetImageOption(image_info,"background",artifact);
+  artifact=GetImageArtifact(image,"fill");
+  if (artifact != (const char *) NULL)
+    (void) SetImageOption(image_info,"fill",artifact);
+  artifact=GetImageArtifact(image,"stroke");
+  if (artifact != (const char *) NULL)
+    (void) SetImageOption(image_info,"stroke",artifact);
+  artifact=GetImageArtifact(image,"strokewidth");
+  if (artifact != (const char *) NULL)
+    (void) SetImageOption(image_info,"strokewidth",artifact);
+  lines_image=ReadImage(image_info,exception);
+  artifact=GetImageArtifact(image,"hough-lines:accumulator");
+  if ((lines_image != (Image *) NULL) &&
+      (IsStringTrue(artifact) != MagickFalse))
+    {
+      Image
+        *accumulator_image;
+
+      accumulator_image=MatrixToImage(accumulator,exception);
+      if (accumulator_image != (Image *) NULL)
+        AppendImageToList(&lines_image,accumulator_image);
+    }
+  /*
+    Free resources.
+  */
+  accumulator=DestroyMatrixInfo(accumulator);
+  image_info=DestroyImageInfo(image_info);
+  (void) RelinquishUniqueFileResource(path);
+  return(GetFirstImageInList(lines_image));
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%     M e a n S h i f t I m a g e                                             %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  MeanShiftImage() delineate arbitrarily shaped clusters in the image.
+%
+%  The format of the MeanShiftImage method is:
+%
+%      Image *MeanShiftImage(const Image *image,const size_t width,
+%        const size_t height,const size_t shift,const size_t iterations,
+%        ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o width, height: find clusters as local maxima in this neighborhood.
+%
+%    o shift: the shift threshold.
+%
+%    o iterations: maximum iteration to find local maxima.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *MeanShiftImage(const Image *image,const size_t width,
+  const size_t height,const size_t shift,const size_t iterations,
+  ExceptionInfo *exception)
+{
+  return((Image *) NULL);
 }
index b3267c54e9ab1f91d54f9b690cfb22fadb9e886e..8e5e311dda471e685280226b5c73f0ea0cdbb52d 100644 (file)
@@ -50,8 +50,10 @@ extern MagickExport ChannelFeatures
 extern MagickExport Image
   *CannyEdgeImage(const Image *,const double,const double,const double,
     const double,ExceptionInfo *),
-  *HoughLinesImage(const Image *,const size_t,const size_t,const size_t,
-    ExceptionInfo *);
+  *HoughLineImage(const Image *,const size_t,const size_t,const size_t,
+    ExceptionInfo *),
+  *MeanShiftImage(const Image *,const size_t,const size_t,const size_t,
+    const size_t,ExceptionInfo *);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }
index 295aebdcbd572e4f936d8baa5e23a38cac01da9f..75719d2eb10557143da1ee014a7c8c9763716533 100644 (file)
@@ -259,6 +259,7 @@ static const OptionInfo
     { "-brightness-contrast", 1L, SimpleOperatorFlag, MagickFalse },
     { "+cache", 0L, GlobalOptionFlag, MagickFalse },
     { "-cache", 1L, GlobalOptionFlag, MagickFalse },
+    { "+canny", 1L, DeprecateOptionFlag, MagickTrue },
     { "-canny", 1L, SimpleOperatorFlag, MagickTrue },
     { "+caption", 0L, ImageInfoOptionFlag | NeverInterpretArgsFlag, MagickFalse },
     { "-caption", 1L, ImageInfoOptionFlag | NeverInterpretArgsFlag, MagickFalse },
@@ -438,6 +439,7 @@ static const OptionInfo
     { "-hald-clut", 0L, ListOperatorFlag | FireOptionFlag, MagickFalse },
     { "+highlight-color", 0L, NonMagickOptionFlag | ImageInfoOptionFlag, MagickFalse },
     { "-highlight-color", 1L, NonMagickOptionFlag | ImageInfoOptionFlag, MagickFalse },
+    { "+hough-lines", 1L, DeprecateOptionFlag, MagickTrue },
     { "-hough-lines", 1L, SimpleOperatorFlag, MagickTrue },
     { "+iconGeometry", 0L, NonMagickOptionFlag, MagickFalse },
     { "-iconGeometry", 1L, NonMagickOptionFlag, MagickFalse },
@@ -505,6 +507,8 @@ static const OptionInfo
     { "-mattecolor", 1L, ImageInfoOptionFlag, MagickFalse },
     { "+maximum", 0L, DeprecateOptionFlag | FireOptionFlag, MagickTrue },
     { "-maximum", 0L, DeprecateOptionFlag | FireOptionFlag, MagickTrue },
+    { "+mean-shift", 1L, DeprecateOptionFlag, MagickTrue },
+    { "-mean-shift", 1L, SimpleOperatorFlag, MagickTrue },
     { "+median", 1L, DeprecateOptionFlag, MagickTrue },
     { "-median", 1L, ReplacedOptionFlag | SimpleOperatorFlag | FireOptionFlag, MagickTrue },
     { "+metric", 0L, DeprecateOptionFlag | FireOptionFlag, MagickFalse },
@@ -780,8 +784,6 @@ static const OptionInfo
     { "-window-group", 1L, NonMagickOptionFlag, MagickFalse },
     { "+write", 1L, NoImageOperatorFlag | NeverInterpretArgsFlag | FireOptionFlag, MagickFalse },
     { "-write", 1L, NoImageOperatorFlag | NeverInterpretArgsFlag | FireOptionFlag, MagickFalse },
-    { "+zzz", 0L, UndefinedOptionFlag, MagickTrue },  /* required by CLI */
-    { "-zzz", 0L, UndefinedOptionFlag, MagickFalse },  /* binary search */
     { (char *) NULL, 0L, UndefinedOptionFlag, MagickFalse }
   },
   ComposeOptions[] =
index 2940372b9c393525e15d46fd5d19e15545d7cc93..b750c23927eb87515db61c28136b254b5ca80e3e 100644 (file)
@@ -231,6 +231,7 @@ static MagickBooleanType ConvertUsage(void)
       "                     improve contrast by 'stretching with saturation'",
       "-liquid-rescale geometry",
       "                     rescale image with seam-carving",
+      "-mean-shift geometry delineate arbitrarily shaped clusters in the image",
       "-median geometry     apply a median filter to the image",
       "-mode geometry       make each pixel the 'predominant color' of the",
       "                     neighborhood",
@@ -2055,6 +2056,17 @@ WandExport MagickBooleanType ConvertImageCommand(ImageInfo *image_info,
           }
         if (LocaleCompare("maximum",option+1) == 0)
           break;
+        if (LocaleCompare("mean-shift",option+1) == 0)
+          {
+            if (*option == '+')
+              break;
+            i++;
+            if (i == (ssize_t) (argc-1))
+              ThrowConvertException(OptionError,"MissingArgument",option);
+            if (IsGeometry(argv[i]) == MagickFalse)
+              ThrowConvertInvalidArgumentException(option,argv[i]);
+            break;
+          }
         if (LocaleCompare("median",option+1) == 0)
           {
             if (*option == '+')
index ec0c8e4be33099cb52b59790e77ce46d7615ae22..cb982b93a8095052cb25f94df9baaae6cae3fd06 100644 (file)
@@ -1806,7 +1806,7 @@ WandExport MagickBooleanType MogrifyImage(ImageInfo *image_info,const int argc,
             flags=ParseGeometry(argv[i+1],&geometry_info);
             if ((flags & SigmaValue) == 0)
               geometry_info.sigma=geometry_info.rho;
-            mogrify_image=HoughLinesImage(*image,(size_t) geometry_info.rho,
+            mogrify_image=HoughLineImage(*image,(size_t) geometry_info.rho,
               (size_t) geometry_info.sigma,(size_t) geometry_info.xi,exception);
             break;
           }
@@ -2094,6 +2094,23 @@ WandExport MagickBooleanType MogrifyImage(ImageInfo *image_info,const int argc,
               SetAlphaChannel : DeactivateAlphaChannel,exception);
             break;
           }
+        if (LocaleCompare("mean-shift",option+1) == 0)
+          {
+            /*
+              Detect edges in the image.
+            */
+            (void) SyncImageSettings(mogrify_info,*image,exception);
+            flags=ParseGeometry(argv[i+1],&geometry_info);
+            if ((flags & SigmaValue) == 0)
+              geometry_info.sigma=geometry_info.rho;
+            if ((flags & PsiValue) == 0)
+              geometry_info.psi=3;
+            if ((flags & XiValue) == 0)
+              geometry_info.xi=100;
+            mogrify_image=MeanShiftImage(*image,(size_t) geometry_info.rho,
+              (size_t) geometry_info.sigma,(size_t) geometry_info.xi,exception);
+            break;
+          }
         if (LocaleCompare("median",option+1) == 0)
           {
             /*
@@ -3365,6 +3382,8 @@ static MagickBooleanType MogrifyUsage(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",
@@ -3379,6 +3398,7 @@ static MagickBooleanType MogrifyUsage(void)
       "-liquid-rescale geometry",
       "                     rescale image with seam-carving",
       "-magnify             double the size of the image with pixel art scaling",
+      "-mean-shift geometry delineate arbitrarily shaped clusters in the image",
       "-median geometry     apply a median filter to the image",
       "-mode geometry       make each pixel the 'predominant color' of the",
       "                     neighborhood",
@@ -4869,6 +4889,17 @@ WandExport MagickBooleanType MogrifyImageCommand(ImageInfo *image_info,
         if ((LocaleCompare("help",option+1) == 0) ||
             (LocaleCompare("-help",option+1) == 0))
           return(MogrifyUsage());
+        if (LocaleCompare("hough-lines",option+1) == 0)
+          {
+            if (*option == '+')
+              break;
+            i++;
+            if (i == (ssize_t) argc)
+              ThrowMogrifyException(OptionError,"MissingArgument",option);
+            if (IsGeometry(argv[i]) == MagickFalse)
+              ThrowMogrifyInvalidArgumentException(option,argv[i]);
+            break;
+          }
         ThrowMogrifyException(OptionError,"UnrecognizedOption",option)
       }
       case 'i':
@@ -5160,6 +5191,28 @@ WandExport MagickBooleanType MogrifyImageCommand(ImageInfo *image_info,
           }
         if (LocaleCompare("maximum",option+1) == 0)
           break;
+        if (LocaleCompare("mean-shift",option+1) == 0)
+          {
+            if (*option == '+')
+              break;
+            i++;
+            if (i == (ssize_t) argc)
+              ThrowMogrifyException(OptionError,"MissingArgument",option);
+            if (IsGeometry(argv[i]) == MagickFalse)
+              ThrowMogrifyInvalidArgumentException(option,argv[i]);
+            break;
+          }
+        if (LocaleCompare("median",option+1) == 0)
+          {
+            if (*option == '+')
+              break;
+            i++;
+            if (i == (ssize_t) argc)
+              ThrowMogrifyException(OptionError,"MissingArgument",option);
+            if (IsGeometry(argv[i]) == MagickFalse)
+              ThrowMogrifyInvalidArgumentException(option,argv[i]);
+            break;
+          }
         if (LocaleCompare("metric",option+1) == 0)
           {
             ssize_t
@@ -5189,17 +5242,6 @@ WandExport MagickBooleanType MogrifyImageCommand(ImageInfo *image_info,
               ThrowMogrifyInvalidArgumentException(option,argv[i]);
             break;
           }
-        if (LocaleCompare("median",option+1) == 0)
-          {
-            if (*option == '+')
-              break;
-            i++;
-            if (i == (ssize_t) argc)
-              ThrowMogrifyException(OptionError,"MissingArgument",option);
-            if (IsGeometry(argv[i]) == MagickFalse)
-              ThrowMogrifyInvalidArgumentException(option,argv[i]);
-            break;
-          }
         if (LocaleCompare("mode",option+1) == 0)
           {
             if (*option == '+')
index 082870036192c590fc4fe2ed355c7d1c308ee61b..96ad3327b31cfd24273f92ab6a7e4bb6c3d1d74b 100644 (file)
@@ -1922,9 +1922,9 @@ static MagickBooleanType CLISimpleOperatorImage(MagickCLI *cli_wand,
           if ((flags & SigmaValue) == 0)
             geometry_info.sigma=1.0;
           if ((flags & XiValue) == 0)
-            geometry_info.xi=0.10;
+            geometry_info.xi=10;
           if ((flags & PsiValue) == 0)
-            geometry_info.psi=0.30;
+            geometry_info.psi=30;
           if ((flags & PercentValue) != 0)
             {
               geometry_info.xi/=100.0;
@@ -2488,6 +2488,25 @@ static MagickBooleanType CLISimpleOperatorImage(MagickCLI *cli_wand,
         }
       CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
     }
+    case 'h':
+    {
+      if (LocaleCompare("hough-lines",option+1) == 0)
+        {
+          flags=ParseGeometry(arg1,&geometry_info);
+          if ((flags & (RhoValue|SigmaValue)) == 0)
+            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
+          if ((flags & SigmaValue) == 0)
+            geometry_info.sigma=1.0;
+          if ((flags & XiValue) == 0)
+            geometry_info.xi=3;
+          if ((flags & PsiValue) == 0)
+            geometry_info.psi10030;
+          new_image=HoughLineImage(_image,geometry_info.rho,
+            geometry_info.sigma,geometry_info.xi,geometry_info.psi,_exception);
+          break;
+        }
+      CLIWandExceptionBreak(OptionError,"UnrecognizedOption",option);
+    }
     case 'i':
     {
       if (LocaleCompare("identify",option+1) == 0)
@@ -2703,6 +2722,21 @@ static MagickBooleanType CLISimpleOperatorImage(MagickCLI *cli_wand,
                          DeactivateAlphaChannel, _exception);
           break;
         }
+      if (LocaleCompare("mean-shift",option+1) == 0)
+        {
+          flags=ParseGeometry(arg1,&geometry_info);
+          if ((flags & (RhoValue|SigmaValue)) == 0)
+            CLIWandExceptArgBreak(OptionError,"InvalidArgument",option,arg1);
+          if ((flags & SigmaValue) == 0)
+            geometry_info.sigma=1.0;
+          if ((flags & XiValue) == 0)
+            geometry_info.xi=3;
+          if ((flags & PsiValue) == 0)
+            geometry_info.psi10030;
+          new_image=MeanShiftImage(_image,geometry_info.rho,
+            geometry_info.sigma,geometry_info.xi,geometry_info.psi,_exception);
+          break;
+        }
       if (LocaleCompare("median",option+1) == 0)
         {
           CLIWandWarnReplaced("-statistic Median");
index 2df8ecd7639db29c46ec9cc5d5b3fee3c18529e2..8e8af9ef728c26698f5fd7763db5db27da054dd8 100644 (file)
@@ -547,9 +547,12 @@ static struct
     { "CannyEdge", { {"geometry", StringReference},
       {"radius", RealReference}, {"sigma", RealReference},
       {"lower-percent", RealReference}, {"upper-percent", RealReference} } },
-    { "HoughLines", { {"geometry", StringReference},
+    { "HoughLine", { {"geometry", StringReference},
       {"width", IntegerReference}, {"height", IntegerReference},
       {"threshold", IntegerReference} } },
+    { "MeanShift", { {"geometry", StringReference},
+      {"width", IntegerReference}, {"height", IntegerReference},
+      {"shift", IntegerReference}, {"iterations", IntegerReference} } },
   };
 
 static SplayTreeInfo
@@ -7533,8 +7536,10 @@ Mogrify(ref,...)
     GrayscaleImage     = 276
     CannyEdge          = 278
     CannyEdgeImage     = 279
-    HoughLines         = 280
-    HoughLinesImage    = 281
+    HoughLine          = 280
+    HoughLineImage     = 281
+    MeanShift          = 282
+    MeanShiftImage     = 283
     MogrifyRegion      = 666
   PPCODE:
   {
@@ -11137,7 +11142,26 @@ Mogrify(ref,...)
             (void) SetImageChannelMask(image,channel_mask);
           break;
         }
-        case 139:  /* HoughLines */
+        case 139:  /* HoughLine */
+        {
+          if (attribute_flag[0] != 0)
+            {
+              flags=ParseGeometry(argument_list[0].string_reference,
+                &geometry_info);
+              if ((flags & SigmaValue) == 0)
+                geometry_info.sigma=geometry_info.rho;
+            }
+          if (attribute_flag[1] != 0)
+            geometry_info.rho=(double) argument_list[1].integer_reference;
+          if (attribute_flag[2] != 0)
+            geometry_info.sigma=(double) argument_list[2].integer_reference;
+          if (attribute_flag[3] != 0)
+            geometry_info.xi=(double) argument_list[3].integer_reference;
+          image=HoughLineImage(image,(size_t) geometry_info.rho,(size_t)
+            geometry_info.sigma,(size_t) geometry_info.xi,exception);
+          break;
+        }
+        case 140:  /* MeanShift */
         {
           if (attribute_flag[0] != 0)
             {
@@ -11145,6 +11169,10 @@ Mogrify(ref,...)
                 &geometry_info);
               if ((flags & SigmaValue) == 0)
                 geometry_info.sigma=geometry_info.rho;
+              if ((flags & PsiValue) == 0)
+                geometry_info.psi=3;
+              if ((flags & XiValue) == 0)
+                geometry_info.xi=100;
             }
           if (attribute_flag[1] != 0)
             geometry_info.rho=(double) argument_list[1].integer_reference;
@@ -11152,7 +11180,7 @@ Mogrify(ref,...)
             geometry_info.sigma=(double) argument_list[2].integer_reference;
           if (attribute_flag[3] != 0)
             geometry_info.xi=(double) argument_list[3].integer_reference;
-          image=HoughLinesImage(image,(size_t) geometry_info.rho,(size_t)
+          image=MeanShiftImage(image,(size_t) geometry_info.rho,(size_t)
             geometry_info.sigma,(size_t) geometry_info.xi,exception);
           break;
         }
index db23d77677866584f873f1ff8e15710f8a59631f..c249599d72156a0d3022f45dac2d616b2fe94db6 100644 (file)
@@ -547,9 +547,12 @@ static struct
     { "CannyEdge", { {"geometry", StringReference},
       {"radius", RealReference}, {"sigma", RealReference},
       {"lower-percent", RealReference}, {"upper-percent", RealReference} } },
-    { "HoughLines", { {"geometry", StringReference},
+    { "HoughLine", { {"geometry", StringReference},
       {"width", IntegerReference}, {"height", IntegerReference},
       {"threshold", IntegerReference} } },
+    { "MeanShift", { {"geometry", StringReference},
+      {"width", IntegerReference}, {"height", IntegerReference},
+      {"shift", IntegerReference}, {"iterations", IntegerReference} } },
   };
 
 static SplayTreeInfo
@@ -7533,8 +7536,10 @@ Mogrify(ref,...)
     GrayscaleImage     = 276
     CannyEdge          = 278
     CannyEdgeImage     = 279
-    HoughLines         = 280
-    HoughLinesImage    = 281
+    HoughLine          = 280
+    HoughLineImage     = 281
+    MeanShift          = 282
+    MeanShiftImage     = 283
     MogrifyRegion      = 666
   PPCODE:
   {
@@ -11137,7 +11142,26 @@ Mogrify(ref,...)
             (void) SetImageChannelMask(image,channel_mask);
           break;
         }
-        case 139:  /* HoughLines */
+        case 139:  /* HoughLine */
+        {
+          if (attribute_flag[0] != 0)
+            {
+              flags=ParseGeometry(argument_list[0].string_reference,
+                &geometry_info);
+              if ((flags & SigmaValue) == 0)
+                geometry_info.sigma=geometry_info.rho;
+            }
+          if (attribute_flag[1] != 0)
+            geometry_info.rho=(double) argument_list[1].integer_reference;
+          if (attribute_flag[2] != 0)
+            geometry_info.sigma=(double) argument_list[2].integer_reference;
+          if (attribute_flag[3] != 0)
+            geometry_info.xi=(double) argument_list[3].integer_reference;
+          image=HoughLineImage(image,(size_t) geometry_info.rho,(size_t)
+            geometry_info.sigma,(size_t) geometry_info.xi,exception);
+          break;
+        }
+        case 140:  /* MeanShift */
         {
           if (attribute_flag[0] != 0)
             {
@@ -11145,6 +11169,10 @@ Mogrify(ref,...)
                 &geometry_info);
               if ((flags & SigmaValue) == 0)
                 geometry_info.sigma=geometry_info.rho;
+              if ((flags & PsiValue) == 0)
+                geometry_info.psi=3;
+              if ((flags & XiValue) == 0)
+                geometry_info.xi=100;
             }
           if (attribute_flag[1] != 0)
             geometry_info.rho=(double) argument_list[1].integer_reference;
@@ -11152,7 +11180,7 @@ Mogrify(ref,...)
             geometry_info.sigma=(double) argument_list[2].integer_reference;
           if (attribute_flag[3] != 0)
             geometry_info.xi=(double) argument_list[3].integer_reference;
-          image=HoughLinesImage(image,(size_t) geometry_info.rho,(size_t)
+          image=MeanShiftImage(image,(size_t) geometry_info.rho,(size_t)
             geometry_info.sigma,(size_t) geometry_info.xi,exception);
           break;
         }
index 8306cfa545ee273163bdc37196f52a4d2fa63564..1b25c9cfc72b64c5a2316910e42a0bc046883bc7 100644 (file)
@@ -176,7 +176,7 @@ Image Operators:
   \-geometry geometry   preferred size or location of the image
   \-grayscale method    convert image to grayscale
   \-hough-lines geometry
-                        identify lines in the image
+                       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
@@ -188,7 +188,8 @@ Image Operators:
   \-linear-stretch geometry
                        improve contrast by `stretching with saturation' the intensity range
   \-liquid-rescale geometry
-  \                     rescale image with seam-carving
+                       rescale image with seam-carving
+  \-mean-shift geometry delineate arbitrarily shaped clusters in the image
   \-median geometry     apply a median filter to the image
   \-mode geometry       make each pixel the 'predominant color' of the neighborhood
   \-modulate value      vary the brightness, saturation, and hue
index 7e7992f7fc3a6f1c39ffc5aa7ca744799ee85150..347df6798aa5df91db32e2c0d24ff468ae37e07c 100644 (file)
@@ -176,7 +176,7 @@ Image Operators:
   \-geometry geometry   preferred size or location of the image
   \-grayscale method    convert image to grayscale
   \-hough-lines geometry
-                        identify lines in the image
+                       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
@@ -188,7 +188,8 @@ Image Operators:
   \-linear-stretch geometry
                        improve contrast by `stretching with saturation' the intensity range
   \-liquid-rescale geometry
-  \                     rescale image with seam-carving
+                       rescale image with seam-carving
+  \-mean-shift geometry delineate arbitrarily shaped clusters in the image
   \-median geometry     apply a median filter to the image
   \-mode geometry       make each pixel the 'predominant color' of the neighborhood
   \-modulate value      vary the brightness, saturation, and hue
index e1346d2919d7f1aa5c6640bde5c310124fdb2beb..154df50fbda626a3dbc7ada4da053da078c2a877 100644 (file)
@@ -174,7 +174,7 @@ Image Operators:
   \-grayscale method    convert image to grayscale
   \-help                print program options
   \-hough-lines geometry
-                        identify lines in the image
+                       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
@@ -190,6 +190,7 @@ Image Operators:
   \-liquid-rescale geometry
                        rescale image with seam-carving
   \-magnify             double the size of the image with pixel art scaling
+  \-mean-shift geometry delineate arbitrarily shaped clusters in the image
   \-median geometry     apply a median filter to the image
   \-mode geometry       make each pixel the 'predominant color' of the neighborhood
   \-modulate value      vary the brightness, saturation, and hue
index 40b2788c7d262d1432cdce3d1ac01da2dff35efe..23acc6d5591ba9c5a90765f76616c7baea7d49bf 100644 (file)
@@ -174,7 +174,7 @@ Image Operators:
   \-grayscale method    convert image to grayscale
   \-help                print program options
   \-hough-lines geometry
-                        identify lines in the image
+                       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
@@ -190,6 +190,7 @@ Image Operators:
   \-liquid-rescale geometry
                        rescale image with seam-carving
   \-magnify             double the size of the image with pixel art scaling
+  \-mean-shift geometry delineate arbitrarily shaped clusters in the image
   \-median geometry     apply a median filter to the image
   \-mode geometry       make each pixel the 'predominant color' of the neighborhood
   \-modulate value      vary the brightness, saturation, and hue