% %
% %
% %
+% S t a t i s t i c I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StatisticImage() makes each pixel the min / max / median / mode / etc. of
+% the neighborhood of the specified radius.
+%
+% The format of the StatisticImage method is:
+%
+% Image *StatisticImage(const Image *image,const StatisticType type,
+% const double radius,ExceptionInfo *exception)
+% Image *StatisticImageChannel(const Image *image,
+% const ChannelType channel,const StatisticType type,
+% const double radius,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the image channel.
+%
+% o type: the statistic type (median, mode, etc.).
+%
+% o radius: the radius of the pixel neighborhood.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
+ const double radius,ExceptionInfo *exception)
+{
+ return(StatisticImageChannel(image,DefaultChannels,type,radius,exception));
+}
+
+MagickExport Image *StatisticImageChannel(const Image *image,
+ const ChannelType channel,const StatisticType type,const double radius,
+ ExceptionInfo *exception)
+{
+#define StatisticImageTag "Statistic/Image"
+
+ CacheView
+ *image_view,
+ *statistic_view;
+
+ Image
+ *statistic_image;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ progress;
+
+ PixelList
+ **restrict pixel_list;
+
+ size_t
+ width;
+
+ ssize_t
+ y;
+
+ /*
+ Initialize statistics image attributes.
+ */
+ 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);
+ width=GetOptimalKernelWidth2D(radius,0.5);
+ statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (statistic_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(statistic_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&statistic_image->exception);
+ statistic_image=DestroyImage(statistic_image);
+ return((Image *) NULL);
+ }
+ pixel_list=AcquirePixelListThreadSet(width);
+ if (pixel_list == (PixelList **) NULL)
+ {
+ statistic_image=DestroyImage(statistic_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Reduce statistics image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ statistic_view=AcquireCacheView(statistic_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (ssize_t) statistic_image->rows; y++)
+ {
+ const int
+ id = GetOpenMPThreadId();
+
+ register const IndexPacket
+ *restrict indexes;
+
+ register const PixelPacket
+ *restrict p;
+
+ register IndexPacket
+ *restrict statistic_indexes;
+
+ register PixelPacket
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
+ (width/2L),image->columns+width,width,exception);
+ q=QueueCacheViewAuthenticPixels(statistic_view,0,y,
+ statistic_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ statistic_indexes=GetCacheViewAuthenticIndexQueue(statistic_view);
+ for (x=0; x < (ssize_t) statistic_image->columns; x++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register const PixelPacket
+ *restrict r;
+
+ register const IndexPacket
+ *restrict s;
+
+ register ssize_t
+ u,
+ v;
+
+ r=p;
+ s=indexes+x;
+ ResetPixelList(pixel_list[id]);
+ for (v=0; v < (ssize_t) width; v++)
+ {
+ for (u=0; u < (ssize_t) width; u++)
+ InsertPixelList(image,r+u,s+u,pixel_list[id]);
+ r+=image->columns+width;
+ s+=image->columns+width;
+ }
+ switch (type)
+ {
+ case ModeStatistic: pixel=GetModePixelList(pixel_list[id]);
+ default: break;
+ }
+ if ((channel & RedChannel) != 0)
+ q->red=ClampToQuantum(pixel.red);
+ if ((channel & GreenChannel) != 0)
+ q->green=ClampToQuantum(pixel.green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=ClampToQuantum(pixel.blue);
+ if ((image->matte != MagickFalse) && ((channel & OpacityChannel) != 0))
+ q->opacity=ClampToQuantum(pixel.opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ statistic_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_StatisticImage)
+#endif
+ proceed=SetImageProgress(image,StatisticImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ statistic_view=DestroyCacheView(statistic_view);
+ image_view=DestroyCacheView(image_view);
+ pixel_list=DestroyPixelListThreadSet(pixel_list);
+ return(statistic_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
% U n s h a r p M a s k I m a g e %
% %
% %
JPEGPreview
} PreviewType;
+typedef enum
+{
+ UndefinedStatistic,
+ MaximumStatistic,
+ MedianStatistic,
+ MinimumStatistic,
+ ModeStatistic,
+ ReduceNoiseStatistic
+} StatisticType;
+
extern MagickExport Image
*AdaptiveBlurImage(const Image *,const double,const double,ExceptionInfo *),
*AdaptiveBlurImageChannel(const Image *,const ChannelType,const double,
*SharpenImageChannel(const Image *,const ChannelType,const double,
const double,ExceptionInfo *),
*SpreadImage(const Image *,const double,ExceptionInfo *),
+ *StatisticImage(const Image *,const StatisticType,const double,ExceptionInfo *),
+ *StatisticImageChannel(const Image *,const ChannelType,const StatisticType,
+ const double,ExceptionInfo *),
*UnsharpMaskImage(const Image *,const double,const double,const double,
const double,ExceptionInfo *),
*UnsharpMaskImageChannel(const Image *,const ChannelType,const double,
{ "-sparse-color", 2L, MagickFalse },
{ "+spread", 0L, MagickFalse },
{ "-spread", 1L, MagickFalse },
+ { "+statistic", 2L, MagickFalse },
+ { "-statistic", 2L, MagickFalse },
{ "+stegano", 0L, MagickFalse },
{ "-stegano", 1L, MagickFalse },
{ "+stereo", 0L, MagickFalse },
{ "Voronoi", (ssize_t) VoronoiColorInterpolate, MagickFalse },
{ (char *) NULL, (ssize_t) UndefinedResource, MagickFalse }
},
+ StatisticOptions[] =
+ {
+ { "Undefined", (ssize_t) UndefinedStatistic, MagickTrue },
+ { "Maximum", (ssize_t) MaximumStatistic, MagickFalse },
+ { "Median", (ssize_t) MedianStatistic, MagickFalse },
+ { "Minimum", (ssize_t) MinimumStatistic, MagickFalse },
+ { "Mode", (ssize_t) ModeStatistic, MagickFalse },
+ { (char *) NULL, (ssize_t) UndefinedMethod, MagickFalse }
+ },
StorageOptions[] =
{
{ "Undefined", (ssize_t) UndefinedPixel, MagickTrue },
case MagickResolutionOptions: return(ResolutionOptions);
case MagickResourceOptions: return(ResourceOptions);
case MagickSparseColorOptions: return(SparseColorOptions);
+ case MagickStatisticOptions: return(StatisticOptions);
case MagickStorageOptions: return(StorageOptions);
case MagickStretchOptions: return(StretchOptions);
case MagickStyleOptions: return(StyleOptions);
MagickResolutionOptions,
MagickResourceOptions,
MagickSparseColorOptions,
+ MagickStatisticOptions,
MagickStorageOptions,
MagickStretchOptions,
MagickStyleOptions,
\-scene value image scene number
\-seed value seed a new sequence of pseudo-random numbers
\-size geometry width and height of image
+ \-statistic type radius
+ replace each pixel with corresponding statistic from the neighborhood
\-stretch type render text with this font stretch
\-stroke color graphic primitive stroke color
\-strokewidth value graphic primitive stroke width
\-solarize threshold negate all pixels above the threshold level
\-splice geometry splice the background color into the image
\-spread amount displace image pixels by a random amount
+ \-statistic type radius
+ replace each pixel with corresponding statistic from the neighborhood
\-strip strip image of all profiles and comments
\-swirl degrees swirl image pixels about the center
\-threshold value threshold the image
" fill in a image based on a few color points",
"-splice geometry splice the background color into the image",
"-spread radius displace image pixels by a random amount",
+ "-statistic type radius",
+ " replace each pixel with corresponding statistic from the neighborhood",
"-strip strip image of all profiles and comments",
"-swirl degrees swirl image pixels about the center",
"-threshold value threshold the image",
ThrowConvertException(OptionError,"MissingArgument",option);
break;
}
+ if (LocaleCompare("statistic",option+1) == 0)
+ {
+ ssize_t
+ op;
+
+ if (*option == '+')
+ break;
+ i++;
+ if (i == (ssize_t) argc)
+ ThrowConvertException(OptionError,"MissingArgument",option);
+ op=ParseMagickOption(MagickStatisticOptions,MagickFalse,argv[i]);
+ if (op < 0)
+ ThrowConvertException(OptionError,"UnrecognizedStatisticType",
+ argv[i]);
+ i++;
+ if (i == (ssize_t) (argc-1))
+ ThrowConvertException(OptionError,"MissingArgument",option);
+ if (IsGeometry(argv[i]) == MagickFalse)
+ ThrowConvertInvalidArgumentException(option,argv[i]);
+ break;
+ }
if (LocaleCompare("stretch",option+1) == 0)
{
ssize_t
*image=spread_image;
break;
}
+ if (LocaleCompare("statistic",option+1) == 0)
+ {
+ Image
+ *statistic_image;
+
+ StatisticType
+ type;
+
+ (void) SyncImageSettings(mogrify_info,*image);
+ type=(StatisticType) ParseMagickOption(MagickStatisticOptions,
+ MagickFalse,argv[i+1]);
+ (void) ParseGeometry(argv[i+2],&geometry_info);
+ statistic_image=StatisticImageChannel(*image,channel,type,
+ geometry_info.rho,exception);
+ if (statistic_image == (Image *) NULL)
+ break;
+ *image=DestroyImage(*image);
+ *image=statistic_image;
+ break;
+ }
if (LocaleCompare("stretch",option+1) == 0)
{
if (*option == '+')
" fill in a image based on a few color points",
"-splice geometry splice the background color into the image",
"-spread radius displace image pixels by a random amount",
+ "-statistic type radius",
+ " replace each pixel with corresponding statistic from the neighborhood",
"-strip strip image of all profiles and comments",
"-swirl degrees swirl image pixels about the center",
"-threshold value threshold the image",
ThrowMogrifyInvalidArgumentException(option,argv[i]);
break;
}
+ if (LocaleCompare("statistic",option+1) == 0)
+ {
+ ssize_t
+ op;
+
+ if (*option == '+')
+ break;
+ i++;
+ if (i == (ssize_t) argc)
+ ThrowMogrifyException(OptionError,"MissingArgument",option);
+ op=ParseMagickOption(MagickStatisticOptions,MagickFalse,argv[i]);
+ if (op < 0)
+ ThrowMogrifyException(OptionError,"UnrecognizedStatisticType",
+ argv[i]);
+ i++;
+ if (i == (ssize_t) (argc-1))
+ ThrowMogrifyException(OptionError,"MissingArgument",option);
+ if (IsGeometry(argv[i]) == MagickFalse)
+ ThrowMogrifyInvalidArgumentException(option,argv[i]);
+ break;
+ }
if (LocaleCompare("stretch",option+1) == 0)
{
ssize_t