pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
- pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
+ pixel.index+=(*k)*alpha*GetIndexPixelComponent(indexes+x+(width-i)*
+ v+u);
gamma+=(*k)*alpha;
k++;
p++;
}
gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
if ((channel & OpacityChannel) != 0)
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
- blur_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(gamma*
+ pixel.index));
q++;
r++;
}
pixel.opacity+=(*k)*GetOpacityPixelComponent(p);
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
- pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
+ pixel.index+=(*k)*alpha*GetIndexPixelComponent(indexes+x+(width-i)*
+ v+u);
gamma+=(*k)*alpha;
k++;
p++;
}
gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
if ((channel & OpacityChannel) != 0)
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
- sharp_indexes[x]=ClampToQuantum(gamma*GetIndexPixelComponent(&pixel));
+ SetIndexPixelComponent(sharp_indexes+x,ClampToQuantum(gamma*
+ pixel.index));
q++;
r++;
}
{
for (i=0; i < (ssize_t) width; i++)
{
- pixel.red+=(*k)*kernel_pixels->red;
- pixel.green+=(*k)*kernel_pixels->green;
- pixel.blue+=(*k)*kernel_pixels->blue;
+ pixel.red+=(*k)*GetRedPixelComponent(kernel_pixels);
+ pixel.green+=(*k)*GetGreenPixelComponent(kernel_pixels);
+ pixel.blue+=(*k)*GetBluePixelComponent(kernel_pixels);
k++;
kernel_pixels++;
}
if ((channel & RedChannel) != 0)
- SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(pixel.red));
if ((channel & GreenChannel) != 0)
- SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(pixel.green));
if ((channel & BlueChannel) != 0)
- SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(pixel.blue));
if ((channel & OpacityChannel) != 0)
{
k=kernel;
kernel_pixels=p;
for (i=0; i < (ssize_t) width; i++)
{
- pixel.opacity+=(*k)*kernel_pixels->opacity;
+ pixel.opacity+=(*k)*GetOpacityPixelComponent(kernel_pixels);
k++;
kernel_pixels++;
}
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
kernel_indexes=indexes;
for (i=0; i < (ssize_t) width; i++)
{
- pixel.index+=(*k)*(*kernel_indexes);
+ pixel.index+=(*k)*GetIndexPixelComponent(kernel_indexes);
k++;
kernel_indexes++;
}
- blur_indexes[x]=ClampToQuantum(pixel.index);
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(
+ pixel.index));
}
}
else
{
alpha=(MagickRealType) (QuantumScale*
GetAlphaPixelComponent(kernel_pixels));
- pixel.red+=(*k)*alpha*kernel_pixels->red;
- pixel.green+=(*k)*alpha*kernel_pixels->green;
- pixel.blue+=(*k)*alpha*kernel_pixels->blue;
+ pixel.red+=(*k)*alpha*GetRedPixelComponent(kernel_pixels);
+ pixel.green+=(*k)*alpha*GetGreenPixelComponent(kernel_pixels);
+ pixel.blue+=(*k)*alpha*GetBluePixelComponent(kernel_pixels);
gamma+=(*k)*alpha;
k++;
kernel_pixels++;
}
gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
if ((channel & OpacityChannel) != 0)
{
k=kernel;
kernel_pixels=p;
for (i=0; i < (ssize_t) width; i++)
{
- pixel.opacity+=(*k)*kernel_pixels->opacity;
+ pixel.opacity+=(*k)*GetOpacityPixelComponent(kernel_pixels);
k++;
kernel_pixels++;
}
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
kernel_pixels++;
kernel_indexes++;
}
- blur_indexes[x]=ClampToQuantum(gamma*
- GetIndexPixelComponent(&pixel));
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(gamma*
+ pixel.index));
}
}
indexes++;
{
for (i=0; i < (ssize_t) width; i++)
{
- pixel.red+=(*k)*kernel_pixels->red;
- pixel.green+=(*k)*kernel_pixels->green;
- pixel.blue+=(*k)*kernel_pixels->blue;
+ pixel.red+=(*k)*GetRedPixelComponent(kernel_pixels);
+ pixel.green+=(*k)*GetGreenPixelComponent(kernel_pixels);
+ pixel.blue+=(*k)*GetBluePixelComponent(kernel_pixels);
k++;
kernel_pixels++;
}
if ((channel & RedChannel) != 0)
- SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(pixel.red));
if ((channel & GreenChannel) != 0)
- SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(pixel.green));
if ((channel & BlueChannel) != 0)
- SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(pixel.blue));
if ((channel & OpacityChannel) != 0)
{
k=kernel;
kernel_pixels=p;
for (i=0; i < (ssize_t) width; i++)
{
- pixel.opacity+=(*k)*kernel_pixels->opacity;
+ pixel.opacity+=(*k)*GetOpacityPixelComponent(kernel_pixels);
k++;
kernel_pixels++;
}
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
kernel_indexes=indexes;
for (i=0; i < (ssize_t) width; i++)
{
- pixel.index+=(*k)*(*kernel_indexes);
+ pixel.index+=(*k)*GetIndexPixelComponent(kernel_indexes);
k++;
kernel_indexes++;
}
- blur_indexes[y]=ClampToQuantum(pixel.index);
+ SetIndexPixelComponent(blur_indexes+y,ClampToQuantum(
+ pixel.index));
}
}
else
{
alpha=(MagickRealType) (QuantumScale*
GetAlphaPixelComponent(kernel_pixels));
- pixel.red+=(*k)*alpha*kernel_pixels->red;
- pixel.green+=(*k)*alpha*kernel_pixels->green;
- pixel.blue+=(*k)*alpha*kernel_pixels->blue;
+ pixel.red+=(*k)*alpha*GetRedPixelComponent(kernel_pixels);
+ pixel.green+=(*k)*alpha*GetGreenPixelComponent(kernel_pixels);
+ pixel.blue+=(*k)*alpha*GetBluePixelComponent(kernel_pixels);
gamma+=(*k)*alpha;
k++;
kernel_pixels++;
}
gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
if ((channel & OpacityChannel) != 0)
{
k=kernel;
kernel_pixels=p;
for (i=0; i < (ssize_t) width; i++)
{
- pixel.opacity+=(*k)*kernel_pixels->opacity;
+ pixel.opacity+=(*k)*GetOpacityPixelComponent(kernel_pixels);
k++;
kernel_pixels++;
}
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
kernel_pixels++;
kernel_indexes++;
}
- blur_indexes[y]=ClampToQuantum(gamma*
- GetIndexPixelComponent(&pixel));
+ SetIndexPixelComponent(blur_indexes+y,ClampToQuantum(gamma*
+ pixel.index));
}
}
indexes++;
kernel_pixels+=image->columns+width;
}
if ((channel & RedChannel) != 0)
- SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(pixel.red));
if ((channel & GreenChannel) != 0)
- SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(pixel.green));
if ((channel & BlueChannel) != 0)
- SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(pixel.blue));
if ((channel & OpacityChannel) != 0)
{
k=normal_kernel;
}
kernel_pixels+=image->columns+width;
}
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
{
for (u=0; u < (ssize_t) width; u++)
{
- pixel.index+=(*k)*kernel_indexes[u];
+ pixel.index+=(*k)*GetIndexPixelComponent(kernel_indexes+u);
k++;
}
kernel_indexes+=image->columns+width;
}
- convolve_indexes[x]=ClampToQuantum(pixel.index);
+ SetIndexPixelComponent(convolve_indexes+x,ClampToQuantum(
+ pixel.index));
}
}
else
}
gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
if ((channel & OpacityChannel) != 0)
{
k=normal_kernel;
{
for (u=0; u < (ssize_t) width; u++)
{
- pixel.opacity+=(*k)*kernel_pixels[u].opacity;
+ pixel.opacity+=(*k)*GetOpacityPixelComponent(kernel_pixels+u);
k++;
}
kernel_pixels+=image->columns+width;
}
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
{
alpha=(MagickRealType) (QuantumScale*(QuantumRange-
kernel_pixels[u].opacity));
- pixel.index+=(*k)*alpha*kernel_indexes[u];
+ pixel.index+=(*k)*alpha*GetIndexPixelComponent(
+ kernel_indexes+u);
k++;
}
kernel_pixels+=image->columns+width;
kernel_indexes+=image->columns+width;
}
- convolve_indexes[x]=ClampToQuantum(gamma*
- GetIndexPixelComponent(&pixel));
+ SetIndexPixelComponent(convolve_indexes+x,ClampToQuantum(gamma*
+ pixel.index));
}
}
indexes++;
case 1: pixel[j]=GetGreenPixelComponent(p); break;
case 2: pixel[j]=GetBluePixelComponent(p); break;
case 3: pixel[j]=GetOpacityPixelComponent(p); break;
- case 4: pixel[j]=GetBlackPixelComponent(indexes,x); break;
+ case 4: pixel[j]=GetBlackPixelComponent(indexes+x); break;
default: break;
}
p++;
{
switch (i)
{
- case 0: q->red=pixel[j]; break;
- case 1: q->green=pixel[j]; break;
- case 2: q->blue=pixel[j]; break;
- case 3: q->opacity=pixel[j]; break;
- case 4: indexes[x]=pixel[j]; break;
+ case 0: SetRedPixelComponent(q,pixel[j]); break;
+ case 1: SetGreenPixelComponent(q,pixel[j]); break;
+ case 2: SetBluePixelComponent(q,pixel[j]); break;
+ case 3: SetOpacityPixelComponent(q,pixel[j]); break;
+ case 4: SetIndexPixelComponent(indexes+x,pixel[j]); break;
default: break;
}
q++;
kernel_pixels+=image->columns+kernel->width;
}
if ((channel & RedChannel) != 0)
- SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(pixel.red));
if ((channel & GreenChannel) != 0)
- SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(pixel.green));
if ((channel & BlueChannel) != 0)
- SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(pixel.blue));
if ((channel & OpacityChannel) != 0)
{
k=kernel->values;
}
kernel_pixels+=image->columns+kernel->width;
}
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
{
for (u=0; u < (ssize_t) kernel->height; u++)
{
- pixel.index+=(*k)*kernel_indexes[u];
+ pixel.index+=(*k)*GetIndexPixelComponent(kernel_indexes+u);
k++;
}
kernel_indexes+=image->columns+kernel->width;
}
- filter_indexes[x]=ClampToQuantum(pixel.index);
+ SetIndexPixelComponent(filter_indexes+x,ClampToQuantum(
+ pixel.index));
}
}
else
for (u=0; u < (ssize_t) kernel->height; u++)
{
alpha=(MagickRealType) (QuantumScale*(QuantumRange-
- kernel_pixels[u].opacity));
- pixel.red+=(*k)*alpha*kernel_pixels[u].red;
- pixel.green+=(*k)*alpha*kernel_pixels[u].green;
- pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
+ GetOpacityPixelComponent(kernel_pixels+u)));
+ pixel.red+=(*k)*alpha*GetRedPixelComponent(kernel_pixels+u);
+ pixel.green+=(*k)*alpha*GetGreenPixelComponent(kernel_pixels+u);
+ pixel.blue+=(*k)*alpha*GetBluePixelComponent(kernel_pixels+u);
gamma+=(*k)*alpha;
k++;
}
}
gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
+ SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
+ SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
if ((channel & OpacityChannel) != 0)
{
k=kernel->values;
{
for (u=0; u < (ssize_t) kernel->height; u++)
{
- pixel.opacity+=(*k)*kernel_pixels[u].opacity;
+ pixel.opacity+=(*k)*GetOpacityPixelComponent(kernel_pixels+u);
k++;
}
kernel_pixels+=image->columns+kernel->width;
}
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
{
alpha=(MagickRealType) (QuantumScale*(QuantumRange-
kernel_pixels[u].opacity));
- pixel.index+=(*k)*alpha*kernel_indexes[u];
+ pixel.index+=(*k)*alpha*GetIndexPixelComponent(
+ kernel_indexes+u);
k++;
}
kernel_pixels+=image->columns+kernel->width;
kernel_indexes+=image->columns+kernel->width;
}
- filter_indexes[x]=ClampToQuantum(gamma*
- GetIndexPixelComponent(&pixel));
+ SetIndexPixelComponent(filter_indexes+x,ClampToQuantum(gamma*
+ pixel.index));
}
}
indexes++;
% %
% %
% %
-% M e d i a n F i l t e r I m a g e %
+% M o t i o n B l u r I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% MedianFilterImage() applies a digital filter that improves the quality
-% of a noisy image. Each pixel is replaced by the median in a set of
-% neighboring pixels as defined by radius.
+% MotionBlurImage() simulates motion blur. We convolve the image with a
+% Gaussian operator of the given radius and standard deviation (sigma).
+% For reasonable results, radius should be larger than sigma. Use a
+% radius of 0 and MotionBlurImage() selects a suitable radius for you.
+% Angle gives the angle of the blurring motion.
%
-% The algorithm was contributed by Mike Edmonds and implements an insertion
-% sort for selecting median color-channel values. For more on this algorithm
-% see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
-% Pugh in the June 1990 of Communications of the ACM.
+% Andrew Protano contributed this effect.
%
-% The format of the MedianFilterImage method is:
+% The format of the MotionBlurImage method is:
%
-% Image *MedianFilterImage(const Image *image,const double radius,
-% ExceptionInfo *exception)
+% Image *MotionBlurImage(const Image *image,const double radius,
+% const double sigma,const double angle,ExceptionInfo *exception)
+% Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
+% const double radius,const double sigma,const double angle,
+% ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the image.
%
-% o radius: the radius of the pixel neighborhood.
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% o radius: the radius of the Gaussian, in pixels, not counting
+% the center pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o angle: Apply the effect along this angle.
%
% o exception: return any errors or warnings in this structure.
%
*/
-#define ListChannels 5
-
-typedef struct _ListNode
-{
- size_t
- next[9],
- count,
- signature;
-} ListNode;
-
-typedef struct _SkipList
-{
- ssize_t
- level;
-
- ListNode
- *nodes;
-} SkipList;
-
-typedef struct _PixelList
+static double *GetMotionBlurKernel(const size_t width,const double sigma)
{
- size_t
- center,
- seed,
- signature;
-
- SkipList
- lists[ListChannels];
-} PixelList;
+ double
+ *kernel,
+ normalize;
-static PixelList *DestroyPixelList(PixelList *pixel_list)
-{
register ssize_t
i;
- if (pixel_list == (PixelList *) NULL)
- return((PixelList *) NULL);
- for (i=0; i < ListChannels; i++)
- if (pixel_list->lists[i].nodes != (ListNode *) NULL)
- pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
- pixel_list->lists[i].nodes);
- pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
- return(pixel_list);
+ /*
+ Generate a 1-D convolution kernel.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ return(kernel);
+ normalize=0.0;
+ for (i=0; i < (ssize_t) width; i++)
+ {
+ kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
+ MagickSigma)))/(MagickSQ2PI*MagickSigma));
+ normalize+=kernel[i];
+ }
+ for (i=0; i < (ssize_t) width; i++)
+ kernel[i]/=normalize;
+ return(kernel);
}
-static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
+MagickExport Image *MotionBlurImage(const Image *image,const double radius,
+ const double sigma,const double angle,ExceptionInfo *exception)
{
- register ssize_t
- i;
+ Image
+ *motion_blur;
- assert(pixel_list != (PixelList **) NULL);
- for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
- if (pixel_list[i] != (PixelList *) NULL)
- pixel_list[i]=DestroyPixelList(pixel_list[i]);
- pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
- return(pixel_list);
+ motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
+ exception);
+ return(motion_blur);
}
-static PixelList *AcquirePixelList(const size_t width)
+MagickExport Image *MotionBlurImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ const double angle,ExceptionInfo *exception)
{
- PixelList
- *pixel_list;
+ CacheView
+ *blur_view,
+ *image_view;
- register ssize_t
- i;
+ double
+ *kernel;
- pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
- if (pixel_list == (PixelList *) NULL)
- return(pixel_list);
- (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
- pixel_list->center=width*width/2;
- for (i=0; i < ListChannels; i++)
- {
- pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
- sizeof(*pixel_list->lists[i].nodes));
- if (pixel_list->lists[i].nodes == (ListNode *) NULL)
- return(DestroyPixelList(pixel_list));
- (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
- sizeof(*pixel_list->lists[i].nodes));
- }
- pixel_list->signature=MagickSignature;
- return(pixel_list);
-}
+ Image
+ *blur_image;
-static PixelList **AcquirePixelListThreadSet(const size_t width)
-{
- PixelList
- **pixel_list;
+ MagickBooleanType
+ status;
- register ssize_t
- i;
+ MagickOffsetType
+ progress;
- size_t
- number_threads;
+ MagickPixelPacket
+ bias;
- number_threads=GetOpenMPMaximumThreads();
- pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
- sizeof(*pixel_list));
- if (pixel_list == (PixelList **) NULL)
- return((PixelList **) NULL);
- (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
- for (i=0; i < (ssize_t) number_threads; i++)
- {
- pixel_list[i]=AcquirePixelList(width);
- if (pixel_list[i] == (PixelList *) NULL)
- return(DestroyPixelListThreadSet(pixel_list));
- }
- return(pixel_list);
-}
+ OffsetInfo
+ *offset;
-static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
- const size_t color)
-{
- register SkipList
- *list;
+ PointInfo
+ point;
register ssize_t
- level;
+ i;
size_t
- search,
- update[9];
+ width;
- /*
- Initialize the node.
- */
- list=pixel_list->lists+channel;
- list->nodes[color].signature=pixel_list->signature;
- list->nodes[color].count=1;
- /*
- Determine where it belongs in the list.
- */
- search=65536UL;
- for (level=list->level; level >= 0; level--)
- {
- while (list->nodes[search].next[level] < color)
- search=list->nodes[search].next[level];
- update[level]=search;
- }
- /*
- Generate a pseudo-random level for this node.
- */
- for (level=0; ; level++)
+ ssize_t
+ y;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ width=GetOptimalKernelWidth1D(radius,sigma);
+ kernel=GetMotionBlurKernel(width,sigma);
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
+ if (offset == (OffsetInfo *) NULL)
+ {
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ blur_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (blur_image == (Image *) NULL)
+ {
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ offset=(OffsetInfo *) RelinquishMagickMemory(offset);
+ return((Image *) NULL);
+ }
+ if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ {
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ offset=(OffsetInfo *) RelinquishMagickMemory(offset);
+ InheritException(exception,&blur_image->exception);
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ point.x=(double) width*sin(DegreesToRadians(angle));
+ point.y=(double) width*cos(DegreesToRadians(angle));
+ for (i=0; i < (ssize_t) width; i++)
{
- pixel_list->seed=(pixel_list->seed*42893621L)+1L;
- if ((pixel_list->seed & 0x300) != 0x300)
- break;
+ offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
+ offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
}
- if (level > 8)
- level=8;
- if (level > (list->level+2))
- level=list->level+2;
/*
- If we're raising the list's level, link back to the root node.
+ Motion blur image.
*/
- while (level > list->level)
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&bias);
+ image_view=AcquireCacheView(image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
{
- list->level++;
- update[list->level]=65536UL;
- }
- /*
- Link the node into the skip-list.
- */
- do
- {
- list->nodes[color].next[level]=list->nodes[update[level]].next[level];
- list->nodes[update[level]].next[level]=color;
- }
- while (level-- > 0);
-}
-
-static MagickPixelPacket GetPixelList(PixelList *pixel_list)
-{
- MagickPixelPacket
- pixel;
-
- register SkipList
- *list;
-
- register ssize_t
- channel;
-
- size_t
- center,
- color,
- count;
-
- unsigned short
- channels[ListChannels];
-
- /*
- Find the median value for each of the color.
- */
- center=pixel_list->center;
- for (channel=0; channel < 5; channel++)
- {
- list=pixel_list->lists+channel;
- color=65536UL;
- count=0;
- do
- {
- color=list->nodes[color].next[0];
- count+=list->nodes[color].count;
- }
- while (count <= center);
- channels[channel]=(unsigned short) color;
- }
- GetMagickPixelPacket((const Image *) NULL,&pixel);
- pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
- pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
- pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
- pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
- pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
- return(pixel);
-}
-
-static inline void InsertPixelList(const Image *image,const PixelPacket *pixel,
- const IndexPacket *indexes,PixelList *pixel_list)
-{
- size_t
- signature;
-
- unsigned short
- index;
-
- index=ScaleQuantumToShort(pixel->red);
- signature=pixel_list->lists[0].nodes[index].signature;
- if (signature == pixel_list->signature)
- pixel_list->lists[0].nodes[index].count++;
- else
- AddNodePixelList(pixel_list,0,index);
- index=ScaleQuantumToShort(pixel->green);
- signature=pixel_list->lists[1].nodes[index].signature;
- if (signature == pixel_list->signature)
- pixel_list->lists[1].nodes[index].count++;
- else
- AddNodePixelList(pixel_list,1,index);
- index=ScaleQuantumToShort(pixel->blue);
- signature=pixel_list->lists[2].nodes[index].signature;
- if (signature == pixel_list->signature)
- pixel_list->lists[2].nodes[index].count++;
- else
- AddNodePixelList(pixel_list,2,index);
- index=ScaleQuantumToShort(pixel->opacity);
- signature=pixel_list->lists[3].nodes[index].signature;
- if (signature == pixel_list->signature)
- pixel_list->lists[3].nodes[index].count++;
- else
- AddNodePixelList(pixel_list,3,index);
- if (image->colorspace == CMYKColorspace)
- index=ScaleQuantumToShort(*indexes);
- signature=pixel_list->lists[4].nodes[index].signature;
- if (signature == pixel_list->signature)
- pixel_list->lists[4].nodes[index].count++;
- else
- AddNodePixelList(pixel_list,4,index);
-}
-
-static void ResetPixelList(PixelList *pixel_list)
-{
- int
- level;
-
- register ListNode
- *root;
-
- register SkipList
- *list;
-
- register ssize_t
- channel;
-
- /*
- Reset the skip-list.
- */
- for (channel=0; channel < 5; channel++)
- {
- list=pixel_list->lists+channel;
- root=list->nodes+65536UL;
- list->level=0;
- for (level=0; level < 9; level++)
- root->next[level]=65536UL;
- }
- pixel_list->seed=pixel_list->signature++;
-}
-
-MagickExport Image *MedianFilterImage(const Image *image,const double radius,
- ExceptionInfo *exception)
-{
-#define MedianFilterImageTag "MedianFilter/Image"
-
- CacheView
- *image_view,
- *median_view;
-
- Image
- *median_image;
-
- MagickBooleanType
- status;
-
- MagickOffsetType
- progress;
-
- PixelList
- **restrict pixel_list;
-
- size_t
- width;
-
- ssize_t
- y;
-
- /*
- Initialize median 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);
- median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
- exception);
- if (median_image == (Image *) NULL)
- return((Image *) NULL);
- if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
- {
- InheritException(exception,&median_image->exception);
- median_image=DestroyImage(median_image);
- return((Image *) NULL);
- }
- pixel_list=AcquirePixelListThreadSet(width);
- if (pixel_list == (PixelList **) NULL)
- {
- median_image=DestroyImage(median_image);
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- }
- /*
- Median filter each image row.
- */
- status=MagickTrue;
- progress=0;
- image_view=AcquireCacheView(image);
- median_view=AcquireCacheView(median_image);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
-#endif
- for (y=0; y < (ssize_t) median_image->rows; y++)
- {
- const int
- id = GetOpenMPThreadId();
-
- register const IndexPacket
- *restrict indexes;
-
- register const PixelPacket
- *restrict p;
-
register IndexPacket
- *restrict median_indexes;
+ *restrict blur_indexes;
register PixelPacket
*restrict q;
if (status == MagickFalse)
continue;
- p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
- (width/2L),image->columns+width,width,exception);
- q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
+ q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
exception);
- if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ if (q == (PixelPacket *) NULL)
{
status=MagickFalse;
continue;
}
- indexes=GetCacheViewVirtualIndexQueue(image_view);
- median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
- for (x=0; x < (ssize_t) median_image->columns; x++)
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (x=0; x < (ssize_t) image->columns; x++)
{
MagickPixelPacket
+ qixel;
+
+ PixelPacket
pixel;
register const IndexPacket
- *restrict s;
+ *restrict indexes;
- register const PixelPacket
- *restrict r;
+ register double
+ *restrict k;
register ssize_t
- u,
- v;
+ i;
- 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;
- }
- pixel=GetPixelList(pixel_list[id]);
- SetPixelPacket(median_image,&pixel,q,median_indexes+x);
- p++;
- q++;
- }
- if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
- status=MagickFalse;
- if (image->progress_monitor != (MagickProgressMonitor) NULL)
- {
- MagickBooleanType
- proceed;
+ k=kernel;
+ qixel=bias;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (i=0; i < (ssize_t) width; i++)
+ {
+ (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
+ offset[i].y,&pixel,exception);
+ qixel.red+=(*k)*pixel.red;
+ qixel.green+=(*k)*pixel.green;
+ qixel.blue+=(*k)*pixel.blue;
+ qixel.opacity+=(*k)*pixel.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ qixel.index+=(*k)*(*indexes);
+ }
+ k++;
+ }
+ if ((channel & RedChannel) != 0)
+ SetRedPixelComponent(q,ClampToQuantum(qixel.red));
+ if ((channel & GreenChannel) != 0)
+ SetGreenPixelComponent(q,ClampToQuantum(qixel.green));
+ if ((channel & BlueChannel) != 0)
+ SetBluePixelComponent(q,ClampToQuantum(qixel.blue));
+ if ((channel & OpacityChannel) != 0)
+ SetOpacityPixelComponent(q,ClampToQuantum(qixel.opacity));
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(qixel.index));
+ }
+ else
+ {
+ MagickRealType
+ alpha,
+ gamma;
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_MedianFilterImage)
-#endif
- proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
- image->rows);
- if (proceed == MagickFalse)
- status=MagickFalse;
- }
- }
- median_view=DestroyCacheView(median_view);
- image_view=DestroyCacheView(image_view);
- pixel_list=DestroyPixelListThreadSet(pixel_list);
- return(median_image);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% %
-% %
-% %
-% M o d e I m a g e %
+ alpha=0.0;
+ gamma=0.0;
+ for (i=0; i < (ssize_t) width; i++)
+ {
+ (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
+ offset[i].y,&pixel,exception);
+ alpha=(MagickRealType) (QuantumScale*
+ GetAlphaPixelComponent(&pixel));
+ qixel.red+=(*k)*alpha*pixel.red;
+ qixel.green+=(*k)*alpha*pixel.green;
+ qixel.blue+=(*k)*alpha*pixel.blue;
+ qixel.opacity+=(*k)*pixel.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ qixel.index+=(*k)*alpha*GetIndexPixelComponent(indexes);
+ }
+ gamma+=(*k)*alpha;
+ k++;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ SetRedPixelComponent(q,ClampToQuantum(gamma*qixel.red));
+ if ((channel & GreenChannel) != 0)
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*qixel.green));
+ if ((channel & BlueChannel) != 0)
+ SetBluePixelComponent(q,ClampToQuantum(gamma*qixel.blue));
+ if ((channel & OpacityChannel) != 0)
+ SetOpacityPixelComponent(q,ClampToQuantum(qixel.opacity));
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(gamma*
+ qixel.index));
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_MotionBlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ blur_view=DestroyCacheView(blur_view);
+ image_view=DestroyCacheView(image_view);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ offset=(OffsetInfo *) RelinquishMagickMemory(offset);
+ if (status == MagickFalse)
+ blur_image=DestroyImage(blur_image);
+ return(blur_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P r e v i e w I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% ModeImage() makes each pixel the 'predominate color' of the neighborhood
-% of the specified radius.
+% PreviewImage() tiles 9 thumbnails of the specified image with an image
+% processing operation applied with varying parameters. This may be helpful
+% pin-pointing an appropriate parameter for a particular image processing
+% operation.
%
-% The format of the ModeImage method is:
+% The format of the PreviewImages method is:
%
-% Image *ModeImage(const Image *image,const double radius,
+% Image *PreviewImages(const Image *image,const PreviewType preview,
% ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the image.
%
-% o radius: the radius of the pixel neighborhood.
+% o preview: the image processing operation.
%
% o exception: return any errors or warnings in this structure.
%
*/
-
-static MagickPixelPacket GetModePixelList(PixelList *pixel_list)
+MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
+ ExceptionInfo *exception)
{
- MagickPixelPacket
- pixel;
-
- register SkipList
- *list;
-
- register ssize_t
- channel;
+#define NumberTiles 9
+#define PreviewImageTag "Preview/Image"
+#define DefaultPreviewGeometry "204x204+10+10"
- size_t
- color,
- count,
- max_count,
- mode,
- width;
+ char
+ factor[MaxTextExtent],
+ label[MaxTextExtent];
- unsigned short
- channels[5];
+ double
+ degrees,
+ gamma,
+ percentage,
+ radius,
+ sigma,
+ threshold;
- /*
- Make each pixel the 'predominate color' of the specified neighborhood.
- */
- width=pixel_list->center << 1;
- for (channel=0; channel < 5; channel++)
- {
- list=pixel_list->lists+channel;
- color=65536UL;
- mode=color;
- max_count=list->nodes[mode].count;
- count=0;
- do
- {
- color=list->nodes[color].next[0];
- if (list->nodes[color].count > max_count)
- {
- mode=color;
- max_count=list->nodes[mode].count;
- }
- count+=list->nodes[color].count;
- }
- while (count <= width);
- channels[channel]=(unsigned short) mode;
- }
- GetMagickPixelPacket((const Image *) NULL,&pixel);
- pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
- pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
- pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
- pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
- pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
- return(pixel);
-}
+ Image
+ *images,
+ *montage_image,
+ *preview_image,
+ *thumbnail;
-MagickExport Image *ModeImage(const Image *image,const double radius,
- ExceptionInfo *exception)
-{
-#define ModeImageTag "Mode/Image"
+ ImageInfo
+ *preview_info;
- CacheView
- *image_view,
- *mode_view;
+ MagickBooleanType
+ proceed;
- Image
- *mode_image;
+ MontageInfo
+ *montage_info;
- MagickBooleanType
- status;
+ QuantizeInfo
+ quantize_info;
- MagickOffsetType
- progress;
+ RectangleInfo
+ geometry;
- PixelList
- **restrict pixel_list;
+ register ssize_t
+ i,
+ x;
size_t
- width;
+ colors;
ssize_t
y;
/*
- Initialize mode image attributes.
+ Open output image file.
*/
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);
- mode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
- exception);
- if (mode_image == (Image *) NULL)
- return((Image *) NULL);
- if (SetImageStorageClass(mode_image,DirectClass) == MagickFalse)
- {
- InheritException(exception,&mode_image->exception);
- mode_image=DestroyImage(mode_image);
- return((Image *) NULL);
- }
- pixel_list=AcquirePixelListThreadSet(width);
- if (pixel_list == (PixelList **) NULL)
- {
- mode_image=DestroyImage(mode_image);
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- }
- /*
- Reduce mode image.
- */
- status=MagickTrue;
- progress=0;
- image_view=AcquireCacheView(image);
- mode_view=AcquireCacheView(mode_image);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
-#endif
- for (y=0; y < (ssize_t) mode_image->rows; y++)
+ colors=2;
+ degrees=0.0;
+ gamma=(-0.2f);
+ preview_info=AcquireImageInfo();
+ SetGeometry(image,&geometry);
+ (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
+ &geometry.width,&geometry.height);
+ images=NewImageList();
+ percentage=12.5;
+ GetQuantizeInfo(&quantize_info);
+ radius=0.0;
+ sigma=1.0;
+ threshold=0.0;
+ x=0;
+ y=0;
+ for (i=0; i < NumberTiles; i++)
{
- const int
- id = GetOpenMPThreadId();
-
- register const IndexPacket
- *restrict indexes;
-
- register const PixelPacket
- *restrict p;
-
- register IndexPacket
- *restrict mode_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(mode_view,0,y,mode_image->columns,1,
- exception);
- if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
+ if (thumbnail == (Image *) NULL)
+ break;
+ (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
+ (void *) NULL);
+ (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
+ if (i == (NumberTiles/2))
{
- status=MagickFalse;
+ (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
+ AppendImageToList(&images,thumbnail);
continue;
}
- indexes=GetCacheViewVirtualIndexQueue(image_view);
- mode_indexes=GetCacheViewAuthenticIndexQueue(mode_view);
- for (x=0; x < (ssize_t) mode_image->columns; x++)
+ switch (preview)
{
- 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++)
+ case RotatePreview:
{
- 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;
+ degrees+=45.0;
+ preview_image=RotateImage(thumbnail,degrees,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
+ break;
}
- pixel=GetModePixelList(pixel_list[id]);
- SetPixelPacket(mode_image,&pixel,q,mode_indexes+x);
- p++;
- q++;
- }
- if (SyncCacheViewAuthenticPixels(mode_view,exception) == MagickFalse)
- status=MagickFalse;
- if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ case ShearPreview:
{
- MagickBooleanType
- proceed;
-
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_ModeImage)
-#endif
- proceed=SetImageProgress(image,ModeImageTag,progress++,image->rows);
- if (proceed == MagickFalse)
- status=MagickFalse;
+ degrees+=5.0;
+ preview_image=ShearImage(thumbnail,degrees,degrees,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
+ degrees,2.0*degrees);
+ break;
}
- }
- mode_view=DestroyCacheView(mode_view);
- image_view=DestroyCacheView(image_view);
- pixel_list=DestroyPixelListThreadSet(pixel_list);
- return(mode_image);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% %
-% %
-% %
-% M o t i o n B l u r I m a g e %
-% %
-% %
-% %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% MotionBlurImage() simulates motion blur. We convolve the image with a
-% Gaussian operator of the given radius and standard deviation (sigma).
-% For reasonable results, radius should be larger than sigma. Use a
-% radius of 0 and MotionBlurImage() selects a suitable radius for you.
-% Angle gives the angle of the blurring motion.
-%
-% Andrew Protano contributed this effect.
-%
-% The format of the MotionBlurImage method is:
-%
-% Image *MotionBlurImage(const Image *image,const double radius,
-% const double sigma,const double angle,ExceptionInfo *exception)
-% Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
-% const double radius,const double sigma,const double angle,
-% ExceptionInfo *exception)
-%
-% A description of each parameter follows:
-%
-% o image: the image.
-%
-% o channel: the channel type.
-%
-% o radius: the radius of the Gaussian, in pixels, not counting the center
-% o radius: the radius of the Gaussian, in pixels, not counting
-% the center pixel.
-%
-% o sigma: the standard deviation of the Gaussian, in pixels.
-%
-% o angle: Apply the effect along this angle.
-%
-% o exception: return any errors or warnings in this structure.
-%
-*/
-
-static double *GetMotionBlurKernel(const size_t width,const double sigma)
-{
- double
- *kernel,
- normalize;
-
- register ssize_t
- i;
-
- /*
- Generate a 1-D convolution kernel.
- */
- (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
- kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
- if (kernel == (double *) NULL)
- return(kernel);
- normalize=0.0;
- for (i=0; i < (ssize_t) width; i++)
- {
- kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
- MagickSigma)))/(MagickSQ2PI*MagickSigma));
- normalize+=kernel[i];
- }
- for (i=0; i < (ssize_t) width; i++)
- kernel[i]/=normalize;
- return(kernel);
-}
-
-MagickExport Image *MotionBlurImage(const Image *image,const double radius,
- const double sigma,const double angle,ExceptionInfo *exception)
-{
- Image
- *motion_blur;
-
- motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
- exception);
- return(motion_blur);
-}
-
-MagickExport Image *MotionBlurImageChannel(const Image *image,
- const ChannelType channel,const double radius,const double sigma,
- const double angle,ExceptionInfo *exception)
-{
- CacheView
- *blur_view,
- *image_view;
-
- double
- *kernel;
-
- Image
- *blur_image;
-
- MagickBooleanType
- status;
-
- MagickOffsetType
- progress;
-
- MagickPixelPacket
- bias;
-
- OffsetInfo
- *offset;
-
- PointInfo
- point;
-
- register ssize_t
- i;
-
- size_t
- width;
-
- ssize_t
- y;
-
- assert(image != (Image *) NULL);
- assert(image->signature == MagickSignature);
- if (image->debug != MagickFalse)
- (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
- assert(exception != (ExceptionInfo *) NULL);
- width=GetOptimalKernelWidth1D(radius,sigma);
- kernel=GetMotionBlurKernel(width,sigma);
- if (kernel == (double *) NULL)
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
- if (offset == (OffsetInfo *) NULL)
- {
- kernel=(double *) RelinquishMagickMemory(kernel);
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- }
- blur_image=CloneImage(image,0,0,MagickTrue,exception);
- if (blur_image == (Image *) NULL)
- {
- kernel=(double *) RelinquishMagickMemory(kernel);
- offset=(OffsetInfo *) RelinquishMagickMemory(offset);
- return((Image *) NULL);
- }
- if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
- {
- kernel=(double *) RelinquishMagickMemory(kernel);
- offset=(OffsetInfo *) RelinquishMagickMemory(offset);
- InheritException(exception,&blur_image->exception);
- blur_image=DestroyImage(blur_image);
- return((Image *) NULL);
- }
- point.x=(double) width*sin(DegreesToRadians(angle));
- point.y=(double) width*cos(DegreesToRadians(angle));
- for (i=0; i < (ssize_t) width; i++)
- {
- offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
- offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
- }
- /*
- Motion blur image.
- */
- status=MagickTrue;
- progress=0;
- GetMagickPixelPacket(image,&bias);
- image_view=AcquireCacheView(image);
- blur_view=AcquireCacheView(blur_image);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
-#endif
- for (y=0; y < (ssize_t) image->rows; y++)
- {
- register IndexPacket
- *restrict blur_indexes;
-
- register PixelPacket
- *restrict q;
-
- register ssize_t
- x;
-
- if (status == MagickFalse)
- continue;
- q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
- exception);
- if (q == (PixelPacket *) NULL)
+ case RollPreview:
{
- status=MagickFalse;
- continue;
- }
- blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
- for (x=0; x < (ssize_t) image->columns; x++)
- {
- MagickPixelPacket
- qixel;
-
- PixelPacket
- pixel;
-
- register const IndexPacket
- *restrict indexes;
-
- register double
- *restrict k;
-
- register ssize_t
- i;
-
- k=kernel;
- qixel=bias;
- if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
- {
- for (i=0; i < (ssize_t) width; i++)
- {
- (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
- offset[i].y,&pixel,exception);
- qixel.red+=(*k)*pixel.red;
- qixel.green+=(*k)*pixel.green;
- qixel.blue+=(*k)*pixel.blue;
- qixel.opacity+=(*k)*pixel.opacity;
- if (image->colorspace == CMYKColorspace)
- {
- indexes=GetCacheViewVirtualIndexQueue(image_view);
- qixel.index+=(*k)*(*indexes);
- }
- k++;
- }
- if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(qixel.red);
- if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(qixel.green);
- if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(qixel.blue);
- if ((channel & OpacityChannel) != 0)
- q->opacity=ClampToQuantum(qixel.opacity);
- if (((channel & IndexChannel) != 0) &&
- (image->colorspace == CMYKColorspace))
- blur_indexes[x]=(IndexPacket) ClampToQuantum(qixel.index);
- }
- else
- {
- MagickRealType
- alpha,
- gamma;
-
- alpha=0.0;
- gamma=0.0;
- for (i=0; i < (ssize_t) width; i++)
- {
- (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
- offset[i].y,&pixel,exception);
- alpha=(MagickRealType) (QuantumScale*
- GetAlphaPixelComponent(&pixel));
- qixel.red+=(*k)*alpha*pixel.red;
- qixel.green+=(*k)*alpha*pixel.green;
- qixel.blue+=(*k)*alpha*pixel.blue;
- qixel.opacity+=(*k)*pixel.opacity;
- if (image->colorspace == CMYKColorspace)
- {
- indexes=GetCacheViewVirtualIndexQueue(image_view);
- qixel.index+=(*k)*alpha*(*indexes);
- }
- gamma+=(*k)*alpha;
- k++;
- }
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
- if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*qixel.red);
- if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*qixel.green);
- if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*qixel.blue);
- if ((channel & OpacityChannel) != 0)
- q->opacity=ClampToQuantum(qixel.opacity);
- if (((channel & IndexChannel) != 0) &&
- (image->colorspace == CMYKColorspace))
- blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
- }
- q++;
- }
- if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
- status=MagickFalse;
- if (image->progress_monitor != (MagickProgressMonitor) NULL)
- {
- MagickBooleanType
- proceed;
-
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_MotionBlurImageChannel)
-#endif
- proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
- if (proceed == MagickFalse)
- status=MagickFalse;
- }
- }
- blur_view=DestroyCacheView(blur_view);
- image_view=DestroyCacheView(image_view);
- kernel=(double *) RelinquishMagickMemory(kernel);
- offset=(OffsetInfo *) RelinquishMagickMemory(offset);
- if (status == MagickFalse)
- blur_image=DestroyImage(blur_image);
- return(blur_image);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% %
-% %
-% %
-% P r e v i e w I m a g e %
-% %
-% %
-% %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% PreviewImage() tiles 9 thumbnails of the specified image with an image
-% processing operation applied with varying parameters. This may be helpful
-% pin-pointing an appropriate parameter for a particular image processing
-% operation.
-%
-% The format of the PreviewImages method is:
-%
-% Image *PreviewImages(const Image *image,const PreviewType preview,
-% ExceptionInfo *exception)
-%
-% A description of each parameter follows:
-%
-% o image: the image.
-%
-% o preview: the image processing operation.
-%
-% o exception: return any errors or warnings in this structure.
-%
-*/
-MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
- ExceptionInfo *exception)
-{
-#define NumberTiles 9
-#define PreviewImageTag "Preview/Image"
-#define DefaultPreviewGeometry "204x204+10+10"
-
- char
- factor[MaxTextExtent],
- label[MaxTextExtent];
-
- double
- degrees,
- gamma,
- percentage,
- radius,
- sigma,
- threshold;
-
- Image
- *images,
- *montage_image,
- *preview_image,
- *thumbnail;
-
- ImageInfo
- *preview_info;
-
- MagickBooleanType
- proceed;
-
- MontageInfo
- *montage_info;
-
- QuantizeInfo
- quantize_info;
-
- RectangleInfo
- geometry;
-
- register ssize_t
- i,
- x;
-
- size_t
- colors;
-
- ssize_t
- y;
-
- /*
- Open output image file.
- */
- assert(image != (Image *) NULL);
- assert(image->signature == MagickSignature);
- if (image->debug != MagickFalse)
- (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
- colors=2;
- degrees=0.0;
- gamma=(-0.2f);
- preview_info=AcquireImageInfo();
- SetGeometry(image,&geometry);
- (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
- &geometry.width,&geometry.height);
- images=NewImageList();
- percentage=12.5;
- GetQuantizeInfo(&quantize_info);
- radius=0.0;
- sigma=1.0;
- threshold=0.0;
- x=0;
- y=0;
- for (i=0; i < NumberTiles; i++)
- {
- thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
- if (thumbnail == (Image *) NULL)
- break;
- (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
- (void *) NULL);
- (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
- if (i == (NumberTiles/2))
- {
- (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
- AppendImageToList(&images,thumbnail);
- continue;
- }
- switch (preview)
- {
- case RotatePreview:
- {
- degrees+=45.0;
- preview_image=RotateImage(thumbnail,degrees,exception);
- (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
- break;
- }
- case ShearPreview:
- {
- degrees+=5.0;
- preview_image=ShearImage(thumbnail,degrees,degrees,exception);
- (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
- degrees,2.0*degrees);
- break;
- }
- case RollPreview:
- {
- x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
- y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
- preview_image=RollImage(thumbnail,x,y,exception);
- (void) FormatMagickString(label,MaxTextExtent,"roll %+.20gx%+.20g",
- (double) x,(double) y);
- break;
+ x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
+ y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
+ preview_image=RollImage(thumbnail,x,y,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"roll %+.20gx%+.20g",
+ (double) x,(double) y);
+ break;
}
case HuePreview:
{
}
case ReduceNoisePreview:
{
- preview_image=ReduceNoiseImage(thumbnail,radius,exception);
+ preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
+ (size_t) radius,exception);
(void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
break;
}
break;
}
}
- preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
+ preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
+ (size_t) i,exception);
(void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
break;
}
}
case OilPaintPreview:
{
- preview_image=OilPaintImage(thumbnail,(double) radius,exception);
- (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
- break;
+ preview_image=OilPaintImage(thumbnail,(double) radius,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
+ break;
+ }
+ case CharcoalDrawingPreview:
+ {
+ preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
+ exception);
+ (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",
+ radius,sigma);
+ break;
+ }
+ case JPEGPreview:
+ {
+ char
+ filename[MaxTextExtent];
+
+ int
+ file;
+
+ MagickBooleanType
+ status;
+
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ preview_info->quality=(size_t) percentage;
+ (void) FormatMagickString(factor,MaxTextExtent,"%.20g",(double)
+ preview_info->quality);
+ file=AcquireUniqueFileResource(filename);
+ if (file != -1)
+ file=close(file)-1;
+ (void) FormatMagickString(preview_image->filename,MaxTextExtent,
+ "jpeg:%s",filename);
+ status=WriteImage(preview_info,preview_image);
+ if (status != MagickFalse)
+ {
+ Image
+ *quality_image;
+
+ (void) CopyMagickString(preview_info->filename,
+ preview_image->filename,MaxTextExtent);
+ quality_image=ReadImage(preview_info,exception);
+ if (quality_image != (Image *) NULL)
+ {
+ preview_image=DestroyImage(preview_image);
+ preview_image=quality_image;
+ }
+ }
+ (void) RelinquishUniqueFileResource(preview_image->filename);
+ if ((GetBlobSize(preview_image)/1024) >= 1024)
+ (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
+ factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
+ 1024.0/1024.0);
+ else
+ if (GetBlobSize(preview_image) >= 1024)
+ (void) FormatMagickString(label,MaxTextExtent,
+ "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
+ GetBlobSize(preview_image))/1024.0);
+ else
+ (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%.20gb ",
+ factor,(double) GetBlobSize(thumbnail));
+ break;
+ }
+ }
+ thumbnail=DestroyImage(thumbnail);
+ percentage+=12.5;
+ radius+=0.5;
+ sigma+=0.25;
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) DeleteImageProperty(preview_image,"label");
+ (void) SetImageProperty(preview_image,"label",label);
+ AppendImageToList(&images,preview_image);
+ proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
+ NumberTiles);
+ if (proceed == MagickFalse)
+ break;
+ }
+ if (images == (Image *) NULL)
+ {
+ preview_info=DestroyImageInfo(preview_info);
+ return((Image *) NULL);
+ }
+ /*
+ Create the montage.
+ */
+ montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
+ (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
+ montage_info->shadow=MagickTrue;
+ (void) CloneString(&montage_info->tile,"3x3");
+ (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
+ (void) CloneString(&montage_info->frame,DefaultTileFrame);
+ montage_image=MontageImages(images,montage_info,exception);
+ montage_info=DestroyMontageInfo(montage_info);
+ images=DestroyImageList(images);
+ if (montage_image == (Image *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ if (montage_image->montage != (char *) NULL)
+ {
+ /*
+ Free image directory.
+ */
+ montage_image->montage=(char *) RelinquishMagickMemory(
+ montage_image->montage);
+ if (image->directory != (char *) NULL)
+ montage_image->directory=(char *) RelinquishMagickMemory(
+ montage_image->directory);
+ }
+ preview_info=DestroyImageInfo(preview_info);
+ return(montage_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R a d i a l B l u r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RadialBlurImage() applies a radial blur to the image.
+%
+% Andrew Protano contributed this effect.
+%
+% The format of the RadialBlurImage method is:
+%
+% Image *RadialBlurImage(const Image *image,const double angle,
+% ExceptionInfo *exception)
+% Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
+% const double angle,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o angle: the angle of the radial blur.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *RadialBlurImage(const Image *image,const double angle,
+ ExceptionInfo *exception)
+{
+ Image
+ *blur_image;
+
+ blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
+ return(blur_image);
+}
+
+MagickExport Image *RadialBlurImageChannel(const Image *image,
+ const ChannelType channel,const double angle,ExceptionInfo *exception)
+{
+ CacheView
+ *blur_view,
+ *image_view;
+
+ Image
+ *blur_image;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ progress;
+
+ MagickPixelPacket
+ bias;
+
+ MagickRealType
+ blur_radius,
+ *cos_theta,
+ offset,
+ *sin_theta,
+ theta;
+
+ PointInfo
+ blur_center;
+
+ register ssize_t
+ i;
+
+ size_t
+ n;
+
+ ssize_t
+ y;
+
+ /*
+ Allocate blur image.
+ */
+ 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);
+ blur_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (blur_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&blur_image->exception);
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ blur_center.x=(double) image->columns/2.0;
+ blur_center.y=(double) image->rows/2.0;
+ blur_radius=hypot(blur_center.x,blur_center.y);
+ n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
+ theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
+ cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
+ sizeof(*cos_theta));
+ sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
+ sizeof(*sin_theta));
+ if ((cos_theta == (MagickRealType *) NULL) ||
+ (sin_theta == (MagickRealType *) NULL))
+ {
+ blur_image=DestroyImage(blur_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ offset=theta*(MagickRealType) (n-1)/2.0;
+ for (i=0; i < (ssize_t) n; i++)
+ {
+ cos_theta[i]=cos((double) (theta*i-offset));
+ sin_theta[i]=sin((double) (theta*i-offset));
+ }
+ /*
+ Radial blur image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&bias);
+ image_view=AcquireCacheView(image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (ssize_t) blur_image->rows; y++)
+ {
+ register const IndexPacket
+ *restrict indexes;
+
+ register IndexPacket
+ *restrict blur_indexes;
+
+ register PixelPacket
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (x=0; x < (ssize_t) blur_image->columns; x++)
+ {
+ MagickPixelPacket
+ qixel;
+
+ MagickRealType
+ normalize,
+ radius;
+
+ PixelPacket
+ pixel;
+
+ PointInfo
+ center;
+
+ register ssize_t
+ i;
+
+ size_t
+ step;
+
+ center.x=(double) x-blur_center.x;
+ center.y=(double) y-blur_center.y;
+ radius=hypot((double) center.x,center.y);
+ if (radius == 0)
+ step=1;
+ else
+ {
+ step=(size_t) (blur_radius/radius);
+ if (step == 0)
+ step=1;
+ else
+ if (step >= n)
+ step=n-1;
+ }
+ normalize=0.0;
+ qixel=bias;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
+ {
+ (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
+ (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
+ (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
+ cos_theta[i]+0.5),&pixel,exception);
+ qixel.red+=pixel.red;
+ qixel.green+=pixel.green;
+ qixel.blue+=pixel.blue;
+ qixel.opacity+=pixel.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ qixel.index+=(*indexes);
+ }
+ normalize+=1.0;
+ }
+ normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
+ normalize);
+ if ((channel & RedChannel) != 0)
+ SetRedPixelComponent(q,ClampToQuantum(normalize*qixel.red));
+ if ((channel & GreenChannel) != 0)
+ SetGreenPixelComponent(q,ClampToQuantum(normalize*qixel.green));
+ if ((channel & BlueChannel) != 0)
+ SetBluePixelComponent(q,ClampToQuantum(normalize*qixel.blue));
+ if ((channel & OpacityChannel) != 0)
+ SetOpacityPixelComponent(q,ClampToQuantum(normalize*qixel.opacity));
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(normalize*
+ qixel.index));
+ }
+ else
+ {
+ MagickRealType
+ alpha,
+ gamma;
+
+ alpha=1.0;
+ gamma=0.0;
+ for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
+ {
+ (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
+ (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
+ (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
+ cos_theta[i]+0.5),&pixel,exception);
+ alpha=(MagickRealType) (QuantumScale*
+ GetAlphaPixelComponent(&pixel));
+ qixel.red+=alpha*pixel.red;
+ qixel.green+=alpha*pixel.green;
+ qixel.blue+=alpha*pixel.blue;
+ qixel.opacity+=pixel.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ qixel.index+=alpha*(*indexes);
+ }
+ gamma+=alpha;
+ normalize+=1.0;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
+ normalize);
+ if ((channel & RedChannel) != 0)
+ SetRedPixelComponent(q,ClampToQuantum(gamma*qixel.red));
+ if ((channel & GreenChannel) != 0)
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*qixel.green));
+ if ((channel & BlueChannel) != 0)
+ SetBluePixelComponent(q,ClampToQuantum(gamma*qixel.blue));
+ if ((channel & OpacityChannel) != 0)
+ SetOpacityPixelComponent(q,ClampToQuantum(normalize*qixel.opacity));
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(gamma*
+ qixel.index));
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_RadialBlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ blur_view=DestroyCacheView(blur_view);
+ image_view=DestroyCacheView(image_view);
+ cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
+ sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
+ if (status == MagickFalse)
+ blur_image=DestroyImage(blur_image);
+ return(blur_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e l e c t i v e B l u r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
+% It is similar to the unsharpen mask that sharpens everything with contrast
+% above a certain threshold.
+%
+% The format of the SelectiveBlurImage method is:
+%
+% Image *SelectiveBlurImage(const Image *image,const double radius,
+% const double sigma,const double threshold,ExceptionInfo *exception)
+% Image *SelectiveBlurImageChannel(const Image *image,
+% const ChannelType channel,const double radius,const double sigma,
+% const double threshold,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o threshold: only pixels within this contrast threshold are included
+% in the blur operation.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
+ const PixelPacket *q,const double threshold)
+{
+ if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
+ const double sigma,const double threshold,ExceptionInfo *exception)
+{
+ Image
+ *blur_image;
+
+ blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
+ threshold,exception);
+ return(blur_image);
+}
+
+MagickExport Image *SelectiveBlurImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ const double threshold,ExceptionInfo *exception)
+{
+#define SelectiveBlurImageTag "SelectiveBlur/Image"
+
+ CacheView
+ *blur_view,
+ *image_view;
+
+ double
+ *kernel;
+
+ Image
+ *blur_image;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ progress;
+
+ MagickPixelPacket
+ bias;
+
+ register ssize_t
+ i;
+
+ size_t
+ width;
+
+ ssize_t
+ j,
+ u,
+ v,
+ y;
+
+ /*
+ Initialize blur 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=GetOptimalKernelWidth1D(radius,sigma);
+ kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ j=(ssize_t) width/2;
+ i=0;
+ for (v=(-j); v <= j; v++)
+ {
+ for (u=(-j); u <= j; u++)
+ kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
+ MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
+ }
+ if (image->debug != MagickFalse)
+ {
+ char
+ format[MaxTextExtent],
+ *message;
+
+ register const double
+ *k;
+
+ ssize_t
+ u,
+ v;
+
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
+ width);
+ message=AcquireString("");
+ k=kernel;
+ for (v=0; v < (ssize_t) width; v++)
+ {
+ *message='\0';
+ (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
+ (void) ConcatenateString(&message,format);
+ for (u=0; u < (ssize_t) width; u++)
+ {
+ (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
+ (void) ConcatenateString(&message,format);
+ }
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
}
- case CharcoalDrawingPreview:
+ message=DestroyString(message);
+ }
+ blur_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (blur_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&blur_image->exception);
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ /*
+ Threshold blur image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&bias);
+ SetMagickPixelPacketBias(image,&bias);
+ image_view=AcquireCacheView(image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ MagickRealType
+ gamma;
+
+ register const IndexPacket
+ *restrict indexes;
+
+ register const PixelPacket
+ *restrict p;
+
+ register IndexPacket
+ *restrict blur_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=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
{
- preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
- exception);
- (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",
- radius,sigma);
- break;
+ status=MagickFalse;
+ continue;
}
- case JPEGPreview:
- {
- char
- filename[MaxTextExtent];
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ MagickPixelPacket
+ pixel;
- int
- file;
+ register const double
+ *restrict k;
- MagickBooleanType
- status;
+ register ssize_t
+ u;
- preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
- if (preview_image == (Image *) NULL)
- break;
- preview_info->quality=(size_t) percentage;
- (void) FormatMagickString(factor,MaxTextExtent,"%.20g",(double)
- preview_info->quality);
- file=AcquireUniqueFileResource(filename);
- if (file != -1)
- file=close(file)-1;
- (void) FormatMagickString(preview_image->filename,MaxTextExtent,
- "jpeg:%s",filename);
- status=WriteImage(preview_info,preview_image);
- if (status != MagickFalse)
+ ssize_t
+ j,
+ v;
+
+ pixel=bias;
+ k=kernel;
+ gamma=0.0;
+ j=0;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (v=0; v < (ssize_t) width; v++)
{
- Image
- *quality_image;
+ for (u=0; u < (ssize_t) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ pixel.red+=(*k)*GetRedPixelComponent(p+u+j);
+ pixel.green+=(*k)*GetGreenPixelComponent(p+u+j);
+ pixel.blue+=(*k)*GetBluePixelComponent(p+u+j);
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=(ssize_t) (image->columns+width);
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
+ if ((channel & GreenChannel) != 0)
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
+ if ((channel & BlueChannel) != 0)
+ SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ gamma=0.0;
+ j=0;
+ for (v=0; v < (ssize_t) width; v++)
+ {
+ for (u=0; u < (ssize_t) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ pixel.opacity+=(*k)*(p+u+j)->opacity;
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=(ssize_t) (image->columns+width);
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
+ gamma);
+ SetOpacityPixelComponent(q,ClampToQuantum(gamma*
+ pixel.opacity));
+ }
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ gamma=0.0;
+ j=0;
+ for (v=0; v < (ssize_t) width; v++)
+ {
+ for (u=0; u < (ssize_t) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ pixel.index+=(*k)*GetIndexPixelComponent(indexes+x+u+j);
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=(ssize_t) (image->columns+width);
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
+ gamma);
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(gamma*
+ pixel.index));
+ }
+ }
+ }
+ else
+ {
+ MagickRealType
+ alpha;
- (void) CopyMagickString(preview_info->filename,
- preview_image->filename,MaxTextExtent);
- quality_image=ReadImage(preview_info,exception);
- if (quality_image != (Image *) NULL)
+ for (v=0; v < (ssize_t) width; v++)
+ {
+ for (u=0; u < (ssize_t) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ alpha=(MagickRealType) (QuantumScale*
+ GetAlphaPixelComponent(p+u+j));
+ pixel.red+=(*k)*alpha*GetRedPixelComponent(p+u+j);
+ pixel.green+=(*k)*alpha*GetGreenPixelComponent(p+u+j);
+ pixel.blue+=(*k)*alpha*GetBluePixelComponent(p+u+j);
+ pixel.opacity+=(*k)*GetOpacityPixelComponent(p+u+j);
+ gamma+=(*k)*alpha;
+ k++;
+ }
+ }
+ j+=(ssize_t) (image->columns+width);
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
+ if ((channel & GreenChannel) != 0)
+ SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
+ if ((channel & BlueChannel) != 0)
+ SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ gamma=0.0;
+ j=0;
+ for (v=0; v < (ssize_t) width; v++)
{
- preview_image=DestroyImage(preview_image);
- preview_image=quality_image;
+ for (u=0; u < (ssize_t) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ pixel.opacity+=(*k)*GetOpacityPixelComponent(p+u+j);
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=(ssize_t) (image->columns+width);
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
+ gamma);
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
+ }
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ gamma=0.0;
+ j=0;
+ for (v=0; v < (ssize_t) width; v++)
+ {
+ for (u=0; u < (ssize_t) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ alpha=(MagickRealType) (QuantumScale*
+ GetAlphaPixelComponent(p+u+j));
+ pixel.index+=(*k)*alpha*GetIndexPixelComponent(indexes+x+
+ u+j);
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=(ssize_t) (image->columns+width);
}
- }
- (void) RelinquishUniqueFileResource(preview_image->filename);
- if ((GetBlobSize(preview_image)/1024) >= 1024)
- (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
- factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
- 1024.0/1024.0);
- else
- if (GetBlobSize(preview_image) >= 1024)
- (void) FormatMagickString(label,MaxTextExtent,
- "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
- GetBlobSize(preview_image))/1024.0);
- else
- (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%.20gb ",
- factor,(double) GetBlobSize(thumbnail));
- break;
- }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
+ gamma);
+ SetIndexPixelComponent(blur_indexes+x,ClampToQuantum(gamma*
+ pixel.index));
+ }
+ }
+ }
+ p++;
+ q++;
}
- thumbnail=DestroyImage(thumbnail);
- percentage+=12.5;
- radius+=0.5;
- sigma+=0.25;
- if (preview_image == (Image *) NULL)
- break;
- (void) DeleteImageProperty(preview_image,"label");
- (void) SetImageProperty(preview_image,"label",label);
- AppendImageToList(&images,preview_image);
- proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
- NumberTiles);
- if (proceed == MagickFalse)
- break;
+ sync=SyncCacheViewAuthenticPixels(blur_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
}
- if (images == (Image *) NULL)
- {
- preview_info=DestroyImageInfo(preview_info);
- return((Image *) NULL);
- }
- /*
- Create the montage.
- */
- montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
- (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
- montage_info->shadow=MagickTrue;
- (void) CloneString(&montage_info->tile,"3x3");
- (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
- (void) CloneString(&montage_info->frame,DefaultTileFrame);
- montage_image=MontageImages(images,montage_info,exception);
- montage_info=DestroyMontageInfo(montage_info);
- images=DestroyImageList(images);
- if (montage_image == (Image *) NULL)
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- if (montage_image->montage != (char *) NULL)
- {
- /*
- Free image directory.
- */
- montage_image->montage=(char *) RelinquishMagickMemory(
- montage_image->montage);
- if (image->directory != (char *) NULL)
- montage_image->directory=(char *) RelinquishMagickMemory(
- montage_image->directory);
- }
- preview_info=DestroyImageInfo(preview_info);
- return(montage_image);
+ blur_image->type=image->type;
+ blur_view=DestroyCacheView(blur_view);
+ image_view=DestroyCacheView(image_view);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ if (status == MagickFalse)
+ blur_image=DestroyImage(blur_image);
+ return(blur_image);
}
\f
/*
% %
% %
% %
-% R a d i a l B l u r I m a g e %
+% S h a d e I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% RadialBlurImage() applies a radial blur to the image.
-%
-% Andrew Protano contributed this effect.
+% ShadeImage() shines a distant light on an image to create a
+% three-dimensional effect. You control the positioning of the light with
+% azimuth and elevation; azimuth is measured in degrees off the x axis
+% and elevation is measured in pixels above the Z axis.
%
-% The format of the RadialBlurImage method is:
+% The format of the ShadeImage method is:
%
-% Image *RadialBlurImage(const Image *image,const double angle,
-% ExceptionInfo *exception)
-% Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
-% const double angle,ExceptionInfo *exception)
+% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
+% const double azimuth,const double elevation,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the image.
%
-% o channel: the channel type.
+% o gray: A value other than zero shades the intensity of each pixel.
%
-% o angle: the angle of the radial blur.
+% o azimuth, elevation: Define the light source direction.
%
% o exception: return any errors or warnings in this structure.
%
*/
-
-MagickExport Image *RadialBlurImage(const Image *image,const double angle,
- ExceptionInfo *exception)
+MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
+ const double azimuth,const double elevation,ExceptionInfo *exception)
{
- Image
- *blur_image;
-
- blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
- return(blur_image);
-}
+#define ShadeImageTag "Shade/Image"
-MagickExport Image *RadialBlurImageChannel(const Image *image,
- const ChannelType channel,const double angle,ExceptionInfo *exception)
-{
CacheView
- *blur_view,
- *image_view;
+ *image_view,
+ *shade_view;
Image
- *blur_image;
+ *shade_image;
MagickBooleanType
status;
MagickOffsetType
progress;
- MagickPixelPacket
- bias;
-
- MagickRealType
- blur_radius,
- *cos_theta,
- offset,
- *sin_theta,
- theta;
-
- PointInfo
- blur_center;
-
- register ssize_t
- i;
-
- size_t
- n;
+ PrimaryInfo
+ light;
ssize_t
y;
/*
- Allocate blur image.
+ Initialize shaded image attributes.
*/
- assert(image != (Image *) NULL);
+ 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);
- blur_image=CloneImage(image,0,0,MagickTrue,exception);
- if (blur_image == (Image *) NULL)
+ shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (shade_image == (Image *) NULL)
return((Image *) NULL);
- if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
{
- InheritException(exception,&blur_image->exception);
- blur_image=DestroyImage(blur_image);
+ InheritException(exception,&shade_image->exception);
+ shade_image=DestroyImage(shade_image);
return((Image *) NULL);
}
- blur_center.x=(double) image->columns/2.0;
- blur_center.y=(double) image->rows/2.0;
- blur_radius=hypot(blur_center.x,blur_center.y);
- n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
- theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
- cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
- sizeof(*cos_theta));
- sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
- sizeof(*sin_theta));
- if ((cos_theta == (MagickRealType *) NULL) ||
- (sin_theta == (MagickRealType *) NULL))
- {
- blur_image=DestroyImage(blur_image);
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- }
- offset=theta*(MagickRealType) (n-1)/2.0;
- for (i=0; i < (ssize_t) n; i++)
- {
- cos_theta[i]=cos((double) (theta*i-offset));
- sin_theta[i]=sin((double) (theta*i-offset));
- }
/*
- Radial blur image.
+ Compute the light vector.
+ */
+ light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
+ cos(DegreesToRadians(elevation));
+ light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
+ cos(DegreesToRadians(elevation));
+ light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
+ /*
+ Shade image.
*/
status=MagickTrue;
progress=0;
- GetMagickPixelPacket(image,&bias);
image_view=AcquireCacheView(image);
- blur_view=AcquireCacheView(blur_image);
+ shade_view=AcquireCacheView(shade_image);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
#pragma omp parallel for schedule(dynamic,4) shared(progress,status)
#endif
- for (y=0; y < (ssize_t) blur_image->rows; y++)
+ for (y=0; y < (ssize_t) image->rows; y++)
{
- register const IndexPacket
- *restrict indexes;
+ MagickRealType
+ distance,
+ normal_distance,
+ shade;
- register IndexPacket
- *restrict blur_indexes;
+ PrimaryInfo
+ normal;
+
+ register const PixelPacket
+ *restrict p,
+ *restrict s0,
+ *restrict s1,
+ *restrict s2;
register PixelPacket
*restrict q;
register ssize_t
x;
- if (status == MagickFalse)
- continue;
- q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
- exception);
- if (q == (PixelPacket *) NULL)
- {
- status=MagickFalse;
- continue;
- }
- blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
- for (x=0; x < (ssize_t) blur_image->columns; x++)
- {
- MagickPixelPacket
- qixel;
-
- MagickRealType
- normalize,
- radius;
-
- PixelPacket
- pixel;
-
- PointInfo
- center;
-
- register ssize_t
- i;
-
- size_t
- step;
-
- center.x=(double) x-blur_center.x;
- center.y=(double) y-blur_center.y;
- radius=hypot((double) center.x,center.y);
- if (radius == 0)
- step=1;
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
+ q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
+ exception);
+ if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ /*
+ Shade this row of pixels.
+ */
+ normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
+ s0=p+1;
+ s1=s0+image->columns+2;
+ s2=s1+image->columns+2;
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ /*
+ Determine the surface normal and compute shading.
+ */
+ normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
+ PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
+ PixelIntensity(s2+1));
+ normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
+ PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
+ PixelIntensity(s0+1));
+ if ((normal.x == 0.0) && (normal.y == 0.0))
+ shade=light.z;
else
{
- step=(size_t) (blur_radius/radius);
- if (step == 0)
- step=1;
- else
- if (step >= n)
- step=n-1;
+ shade=0.0;
+ distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
+ if (distance > MagickEpsilon)
+ {
+ normal_distance=
+ normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
+ if (normal_distance > (MagickEpsilon*MagickEpsilon))
+ shade=distance/sqrt((double) normal_distance);
+ }
}
- normalize=0.0;
- qixel=bias;
- if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ if (gray != MagickFalse)
{
- for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
- {
- (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
- (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
- (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
- cos_theta[i]+0.5),&pixel,exception);
- qixel.red+=pixel.red;
- qixel.green+=pixel.green;
- qixel.blue+=pixel.blue;
- qixel.opacity+=pixel.opacity;
- if (image->colorspace == CMYKColorspace)
- {
- indexes=GetCacheViewVirtualIndexQueue(image_view);
- qixel.index+=(*indexes);
- }
- normalize+=1.0;
- }
- normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
- normalize);
- if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(normalize*qixel.red);
- if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(normalize*qixel.green);
- if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(normalize*qixel.blue);
- if ((channel & OpacityChannel) != 0)
- q->opacity=ClampToQuantum(normalize*qixel.opacity);
- if (((channel & IndexChannel) != 0) &&
- (image->colorspace == CMYKColorspace))
- blur_indexes[x]=(IndexPacket) ClampToQuantum(normalize*qixel.index);
+ SetRedPixelComponent(q,shade);
+ SetGreenPixelComponent(q,shade);
+ SetBluePixelComponent(q,shade);
}
else
{
- MagickRealType
- alpha,
- gamma;
-
- alpha=1.0;
- gamma=0.0;
- for (i=0; i < (ssize_t) n; i+=(ssize_t) step)
- {
- (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
- (blur_center.x+center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),
- (ssize_t) (blur_center.y+center.x*sin_theta[i]+center.y*
- cos_theta[i]+0.5),&pixel,exception);
- alpha=(MagickRealType) (QuantumScale*
- GetAlphaPixelComponent(&pixel));
- qixel.red+=alpha*pixel.red;
- qixel.green+=alpha*pixel.green;
- qixel.blue+=alpha*pixel.blue;
- qixel.opacity+=pixel.opacity;
- if (image->colorspace == CMYKColorspace)
- {
- indexes=GetCacheViewVirtualIndexQueue(image_view);
- qixel.index+=alpha*(*indexes);
- }
- gamma+=alpha;
- normalize+=1.0;
- }
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
- normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
- normalize);
- if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*qixel.red);
- if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*qixel.green);
- if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*qixel.blue);
- if ((channel & OpacityChannel) != 0)
- q->opacity=ClampToQuantum(normalize*qixel.opacity);
- if (((channel & IndexChannel) != 0) &&
- (image->colorspace == CMYKColorspace))
- blur_indexes[x]=(IndexPacket) ClampToQuantum(gamma*qixel.index);
+ SetRedPixelComponent(q,ClampToQuantum(QuantumScale*shade*
+ GetRedPixelComponent(s1)));
+ SetGreenPixelComponent(q,ClampToQuantum(QuantumScale*shade*
+ GetGreenPixelComponent(s1)));
+ SetBluePixelComponent(q,ClampToQuantum(QuantumScale*shade*
+ GetBluePixelComponent(s1)));
}
+ q->opacity=s1->opacity;
+ s0++;
+ s1++;
+ s2++;
q++;
}
- if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
+ if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
status=MagickFalse;
if (image->progress_monitor != (MagickProgressMonitor) NULL)
{
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_RadialBlurImageChannel)
+ #pragma omp critical (MagickCore_ShadeImage)
#endif
- proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
+ proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
if (proceed == MagickFalse)
status=MagickFalse;
}
}
- blur_view=DestroyCacheView(blur_view);
+ shade_view=DestroyCacheView(shade_view);
image_view=DestroyCacheView(image_view);
- cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
- sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
if (status == MagickFalse)
- blur_image=DestroyImage(blur_image);
- return(blur_image);
+ shade_image=DestroyImage(shade_image);
+ return(shade_image);
}
\f
/*
% %
% %
% %
-% R e d u c e N o i s e I m a g e %
+% S h a r p e n I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% ReduceNoiseImage() smooths the contours of an image while still preserving
-% edge information. The algorithm works by replacing each pixel with its
-% neighbor closest in value. A neighbor is defined by radius. Use a radius
-% of 0 and ReduceNoise() selects a suitable radius for you.
+% SharpenImage() sharpens the image. We convolve the image with a Gaussian
+% operator of the given radius and standard deviation (sigma). For
+% reasonable results, radius should be larger than sigma. Use a radius of 0
+% and SharpenImage() selects a suitable radius for you.
+%
+% Using a separable kernel would be faster, but the negative weights cancel
+% out on the corners of the kernel producing often undesirable ringing in the
+% filtered result; this can be avoided by using a 2D gaussian shaped image
+% sharpening kernel instead.
%
-% The format of the ReduceNoiseImage method is:
+% The format of the SharpenImage method is:
%
-% Image *ReduceNoiseImage(const Image *image,const double radius,
-% ExceptionInfo *exception)
+% Image *SharpenImage(const Image *image,const double radius,
+% const double sigma,ExceptionInfo *exception)
+% Image *SharpenImageChannel(const Image *image,const ChannelType channel,
+% const double radius,const double sigma,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the image.
%
-% o radius: the radius of the pixel neighborhood.
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Laplacian, in pixels.
%
% o exception: return any errors or warnings in this structure.
%
*/
-static MagickPixelPacket GetNonpeakPixelList(PixelList *pixel_list)
+MagickExport Image *SharpenImage(const Image *image,const double radius,
+ const double sigma,ExceptionInfo *exception)
{
- MagickPixelPacket
- pixel;
+ Image
+ *sharp_image;
- register SkipList
- *list;
+ sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
+ return(sharp_image);
+}
+
+MagickExport Image *SharpenImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ ExceptionInfo *exception)
+{
+ double
+ *kernel,
+ normalize;
+
+ Image
+ *sharp_image;
register ssize_t
- channel;
+ i;
size_t
- center,
- color,
- count,
- next,
- previous;
+ width;
- unsigned short
- channels[5];
+ ssize_t
+ j,
+ u,
+ v;
- /*
- Finds the median value for each of the color.
- */
- center=pixel_list->center;
- for (channel=0; channel < 5; channel++)
+ 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);
+ width=GetOptimalKernelWidth2D(radius,sigma);
+ kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ normalize=0.0;
+ j=(ssize_t) width/2;
+ i=0;
+ for (v=(-j); v <= j; v++)
{
- list=pixel_list->lists+channel;
- color=65536UL;
- next=list->nodes[color].next[0];
- count=0;
- do
+ for (u=(-j); u <= j; u++)
{
- previous=color;
- color=next;
- next=list->nodes[color].next[0];
- count+=list->nodes[color].count;
+ kernel[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
+ MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
+ normalize+=kernel[i];
+ i++;
}
- while (count <= center);
- if ((previous == 65536UL) && (next != 65536UL))
- color=next;
- else
- if ((previous != 65536UL) && (next == 65536UL))
- color=previous;
- channels[channel]=(unsigned short) color;
}
- GetMagickPixelPacket((const Image *) NULL,&pixel);
- pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
- pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
- pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
- pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
- pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
- return(pixel);
+ kernel[i/2]=(double) ((-2.0)*normalize);
+ sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ return(sharp_image);
}
-
-MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p r e a d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SpreadImage() is a special effects method that randomly displaces each
+% pixel in a block defined by the radius parameter.
+%
+% The format of the SpreadImage method is:
+%
+% Image *SpreadImage(const Image *image,const double radius,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: Choose a random pixel in a neighborhood of this extent.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SpreadImage(const Image *image,const double radius,
ExceptionInfo *exception)
{
-#define ReduceNoiseImageTag "ReduceNoise/Image"
+#define SpreadImageTag "Spread/Image"
CacheView
*image_view,
- *noise_view;
+ *spread_view;
Image
- *noise_image;
+ *spread_image;
MagickBooleanType
status;
MagickOffsetType
progress;
- PixelList
- **restrict pixel_list;
+ MagickPixelPacket
+ bias;
+
+ RandomInfo
+ **restrict random_info;
size_t
width;
y;
/*
- Initialize noise image attributes.
+ Initialize spread image attributes.
*/
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
- width=GetOptimalKernelWidth2D(radius,0.5);
- noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
exception);
- if (noise_image == (Image *) NULL)
+ if (spread_image == (Image *) NULL)
return((Image *) NULL);
- if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
+ if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
{
- InheritException(exception,&noise_image->exception);
- noise_image=DestroyImage(noise_image);
+ InheritException(exception,&spread_image->exception);
+ spread_image=DestroyImage(spread_image);
return((Image *) NULL);
}
- pixel_list=AcquirePixelListThreadSet(width);
- if (pixel_list == (PixelList **) NULL)
- {
- noise_image=DestroyImage(noise_image);
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- }
/*
- Reduce noise image.
+ Spread image.
*/
status=MagickTrue;
progress=0;
+ GetMagickPixelPacket(spread_image,&bias);
+ width=GetOptimalKernelWidth1D(radius,0.5);
+ random_info=AcquireRandomInfoThreadSet();
image_view=AcquireCacheView(image);
- noise_view=AcquireCacheView(noise_image);
+ spread_view=AcquireCacheView(spread_image);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
#endif
- for (y=0; y < (ssize_t) noise_image->rows; y++)
+ for (y=0; y < (ssize_t) spread_image->rows; y++)
{
const int
id = GetOpenMPThreadId();
- register const IndexPacket
- *restrict indexes;
-
- register const PixelPacket
- *restrict p;
+ MagickPixelPacket
+ pixel;
register IndexPacket
- *restrict noise_indexes;
+ *restrict indexes;
register PixelPacket
*restrict q;
if (status == MagickFalse)
continue;
- p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
- (width/2L),image->columns+width,width,exception);
- q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
+ q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
exception);
- if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ if (q == (PixelPacket *) NULL)
{
status=MagickFalse;
continue;
}
- indexes=GetCacheViewVirtualIndexQueue(image_view);
- noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
- for (x=0; x < (ssize_t) noise_image->columns; x++)
+ indexes=GetCacheViewAuthenticIndexQueue(spread_view);
+ pixel=bias;
+ for (x=0; x < (ssize_t) spread_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;
- }
- pixel=GetNonpeakPixelList(pixel_list[id]);
- SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
- p++;
+ (void) InterpolateMagickPixelPacket(image,image_view,
+ UndefinedInterpolatePixel,(double) x+width*(GetPseudoRandomValue(
+ random_info[id])-0.5),(double) y+width*(GetPseudoRandomValue(
+ random_info[id])-0.5),&pixel,exception);
+ SetPixelPacket(spread_image,&pixel,q,indexes+x);
q++;
}
- if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
+ if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
status=MagickFalse;
if (image->progress_monitor != (MagickProgressMonitor) NULL)
{
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_ReduceNoiseImage)
+ #pragma omp critical (MagickCore_SpreadImage)
#endif
- proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
- image->rows);
+ proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
if (proceed == MagickFalse)
status=MagickFalse;
}
}
- noise_view=DestroyCacheView(noise_view);
+ spread_view=DestroyCacheView(spread_view);
image_view=DestroyCacheView(image_view);
- pixel_list=DestroyPixelListThreadSet(pixel_list);
- return(noise_image);
+ random_info=DestroyRandomInfoThreadSet(random_info);
+ return(spread_image);
}
\f
/*
% %
% %
% %
-% S e l e c t i v e B l u r I m a g e %
+% S t a t i s t i c I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
-% It is similar to the unsharpen mask that sharpens everything with contrast
-% above a certain threshold.
+% StatisticImage() makes each pixel the min / max / median / mode / etc. of
+% the neighborhood of the specified width and height.
%
-% The format of the SelectiveBlurImage method is:
+% The format of the StatisticImage method is:
%
-% Image *SelectiveBlurImage(const Image *image,const double radius,
-% const double sigma,const double threshold,ExceptionInfo *exception)
-% Image *SelectiveBlurImageChannel(const Image *image,
-% const ChannelType channel,const double radius,const double sigma,
-% const double threshold,ExceptionInfo *exception)
+% Image *StatisticImage(const Image *image,const StatisticType type,
+% const size_t width,const size_t height,ExceptionInfo *exception)
+% Image *StatisticImageChannel(const Image *image,
+% const ChannelType channel,const StatisticType type,
+% const size_t width,const size_t height,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: the image.
%
-% o channel: the channel type.
+% o channel: the image channel.
%
-% o radius: the radius of the Gaussian, in pixels, not counting the center
-% pixel.
+% o type: the statistic type (median, mode, etc.).
%
-% o sigma: the standard deviation of the Gaussian, in pixels.
+% o width: the width of the pixel neighborhood.
%
-% o threshold: only pixels within this contrast threshold are included
-% in the blur operation.
+% o height: the height of the pixel neighborhood.
%
% o exception: return any errors or warnings in this structure.
%
*/
-static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
- const PixelPacket *q,const double threshold)
+#define ListChannels 5
+
+typedef struct _ListNode
{
- if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
- return(MagickTrue);
- return(MagickFalse);
+ size_t
+ next[9],
+ count,
+ signature;
+} ListNode;
+
+typedef struct _SkipList
+{
+ ssize_t
+ level;
+
+ ListNode
+ *nodes;
+} SkipList;
+
+typedef struct _PixelList
+{
+ size_t
+ length,
+ seed,
+ signature;
+
+ SkipList
+ lists[ListChannels];
+} PixelList;
+
+static PixelList *DestroyPixelList(PixelList *pixel_list)
+{
+ register ssize_t
+ i;
+
+ if (pixel_list == (PixelList *) NULL)
+ return((PixelList *) NULL);
+ for (i=0; i < ListChannels; i++)
+ if (pixel_list->lists[i].nodes != (ListNode *) NULL)
+ pixel_list->lists[i].nodes=(ListNode *) RelinquishMagickMemory(
+ pixel_list->lists[i].nodes);
+ pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
+ return(pixel_list);
+}
+
+static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
+{
+ register ssize_t
+ i;
+
+ assert(pixel_list != (PixelList **) NULL);
+ for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
+ if (pixel_list[i] != (PixelList *) NULL)
+ pixel_list[i]=DestroyPixelList(pixel_list[i]);
+ pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
+ return(pixel_list);
+}
+
+static PixelList *AcquirePixelList(const size_t width,const size_t height)
+{
+ PixelList
+ *pixel_list;
+
+ register ssize_t
+ i;
+
+ pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
+ if (pixel_list == (PixelList *) NULL)
+ return(pixel_list);
+ (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
+ pixel_list->length=width*height;
+ for (i=0; i < ListChannels; i++)
+ {
+ pixel_list->lists[i].nodes=(ListNode *) AcquireQuantumMemory(65537UL,
+ sizeof(*pixel_list->lists[i].nodes));
+ if (pixel_list->lists[i].nodes == (ListNode *) NULL)
+ return(DestroyPixelList(pixel_list));
+ (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
+ sizeof(*pixel_list->lists[i].nodes));
+ }
+ pixel_list->signature=MagickSignature;
+ return(pixel_list);
+}
+
+static PixelList **AcquirePixelListThreadSet(const size_t width,
+ const size_t height)
+{
+ PixelList
+ **pixel_list;
+
+ register ssize_t
+ i;
+
+ size_t
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
+ sizeof(*pixel_list));
+ if (pixel_list == (PixelList **) NULL)
+ return((PixelList **) NULL);
+ (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
+ for (i=0; i < (ssize_t) number_threads; i++)
+ {
+ pixel_list[i]=AcquirePixelList(width,height);
+ if (pixel_list[i] == (PixelList *) NULL)
+ return(DestroyPixelListThreadSet(pixel_list));
+ }
+ return(pixel_list);
+}
+
+static void AddNodePixelList(PixelList *pixel_list,const ssize_t channel,
+ const size_t color)
+{
+ register SkipList
+ *list;
+
+ register ssize_t
+ level;
+
+ size_t
+ search,
+ update[9];
+
+ /*
+ Initialize the node.
+ */
+ list=pixel_list->lists+channel;
+ list->nodes[color].signature=pixel_list->signature;
+ list->nodes[color].count=1;
+ /*
+ Determine where it belongs in the list.
+ */
+ search=65536UL;
+ for (level=list->level; level >= 0; level--)
+ {
+ while (list->nodes[search].next[level] < color)
+ search=list->nodes[search].next[level];
+ update[level]=search;
+ }
+ /*
+ Generate a pseudo-random level for this node.
+ */
+ for (level=0; ; level++)
+ {
+ pixel_list->seed=(pixel_list->seed*42893621L)+1L;
+ if ((pixel_list->seed & 0x300) != 0x300)
+ break;
+ }
+ if (level > 8)
+ level=8;
+ if (level > (list->level+2))
+ level=list->level+2;
+ /*
+ If we're raising the list's level, link back to the root node.
+ */
+ while (level > list->level)
+ {
+ list->level++;
+ update[list->level]=65536UL;
+ }
+ /*
+ Link the node into the skip-list.
+ */
+ do
+ {
+ list->nodes[color].next[level]=list->nodes[update[level]].next[level];
+ list->nodes[update[level]].next[level]=color;
+ } while (level-- > 0);
}
-MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
- const double sigma,const double threshold,ExceptionInfo *exception)
+static MagickPixelPacket GetMaximumPixelList(PixelList *pixel_list)
{
- Image
- *blur_image;
-
- blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
- threshold,exception);
- return(blur_image);
-}
+ MagickPixelPacket
+ pixel;
-MagickExport Image *SelectiveBlurImageChannel(const Image *image,
- const ChannelType channel,const double radius,const double sigma,
- const double threshold,ExceptionInfo *exception)
-{
-#define SelectiveBlurImageTag "SelectiveBlur/Image"
+ register SkipList
+ *list;
- CacheView
- *blur_view,
- *image_view;
+ register ssize_t
+ channel;
- double
- *kernel;
+ size_t
+ color,
+ maximum;
- Image
- *blur_image;
+ ssize_t
+ count;
- MagickBooleanType
- status;
+ unsigned short
+ channels[ListChannels];
- MagickOffsetType
- progress;
+ /*
+ Find the maximum value for each of the color.
+ */
+ for (channel=0; channel < 5; channel++)
+ {
+ list=pixel_list->lists+channel;
+ color=65536L;
+ count=0;
+ maximum=list->nodes[color].next[0];
+ do
+ {
+ color=list->nodes[color].next[0];
+ if (color > maximum)
+ maximum=color;
+ count+=list->nodes[color].count;
+ } while (count < (ssize_t) pixel_list->length);
+ channels[channel]=(unsigned short) maximum;
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
+static MagickPixelPacket GetMeanPixelList(PixelList *pixel_list)
+{
MagickPixelPacket
- bias;
+ pixel;
+
+ MagickRealType
+ sum;
+
+ register SkipList
+ *list;
register ssize_t
- i;
+ channel;
size_t
- width;
+ color;
ssize_t
- j,
- u,
- v,
- y;
+ count;
+
+ unsigned short
+ channels[ListChannels];
/*
- Initialize blur image attributes.
+ Find the mean value for each of the color.
*/
- 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=GetOptimalKernelWidth1D(radius,sigma);
- kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
- if (kernel == (double *) NULL)
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- j=(ssize_t) width/2;
- i=0;
- for (v=(-j); v <= j; v++)
+ for (channel=0; channel < 5; channel++)
{
- for (u=(-j); u <= j; u++)
- kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
- MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
- }
- if (image->debug != MagickFalse)
+ list=pixel_list->lists+channel;
+ color=65536L;
+ count=0;
+ sum=0.0;
+ do
{
- char
- format[MaxTextExtent],
- *message;
+ color=list->nodes[color].next[0];
+ sum+=(MagickRealType) list->nodes[color].count*color;
+ count+=list->nodes[color].count;
+ } while (count < (ssize_t) pixel_list->length);
+ sum/=pixel_list->length;
+ channels[channel]=(unsigned short) sum;
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
- register const double
- *k;
+static MagickPixelPacket GetMedianPixelList(PixelList *pixel_list)
+{
+ MagickPixelPacket
+ pixel;
- ssize_t
- u,
- v;
+ register SkipList
+ *list;
+
+ register ssize_t
+ channel;
+
+ size_t
+ color;
+
+ ssize_t
+ count;
+
+ unsigned short
+ channels[ListChannels];
- (void) LogMagickEvent(TransformEvent,GetMagickModule(),
- " SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
- width);
- message=AcquireString("");
- k=kernel;
- for (v=0; v < (ssize_t) width; v++)
- {
- *message='\0';
- (void) FormatMagickString(format,MaxTextExtent,"%.20g: ",(double) v);
- (void) ConcatenateString(&message,format);
- for (u=0; u < (ssize_t) width; u++)
- {
- (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
- (void) ConcatenateString(&message,format);
- }
- (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
- }
- message=DestroyString(message);
- }
- blur_image=CloneImage(image,0,0,MagickTrue,exception);
- if (blur_image == (Image *) NULL)
- return((Image *) NULL);
- if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
- {
- InheritException(exception,&blur_image->exception);
- blur_image=DestroyImage(blur_image);
- return((Image *) NULL);
- }
/*
- Threshold blur image.
+ Find the median value for each of the color.
*/
- status=MagickTrue;
- progress=0;
- GetMagickPixelPacket(image,&bias);
- SetMagickPixelPacketBias(image,&bias);
- image_view=AcquireCacheView(image);
- blur_view=AcquireCacheView(blur_image);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
-#endif
- for (y=0; y < (ssize_t) image->rows; y++)
+ for (channel=0; channel < 5; channel++)
{
- MagickBooleanType
- sync;
+ list=pixel_list->lists+channel;
+ color=65536L;
+ count=0;
+ do
+ {
+ color=list->nodes[color].next[0];
+ count+=list->nodes[color].count;
+ } while (count <= (ssize_t) (pixel_list->length >> 1));
+ channels[channel]=(unsigned short) color;
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
- MagickRealType
- gamma;
+static MagickPixelPacket GetMinimumPixelList(PixelList *pixel_list)
+{
+ MagickPixelPacket
+ pixel;
- register const IndexPacket
- *restrict indexes;
+ register SkipList
+ *list;
- register const PixelPacket
- *restrict p;
+ register ssize_t
+ channel;
- register IndexPacket
- *restrict blur_indexes;
+ size_t
+ color,
+ minimum;
- register PixelPacket
- *restrict q;
+ ssize_t
+ count;
- register ssize_t
- x;
+ unsigned short
+ channels[ListChannels];
- if (status == MagickFalse)
- continue;
- p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
- (width/2L),image->columns+width,width,exception);
- q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
- exception);
- if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
- {
- status=MagickFalse;
- continue;
- }
- indexes=GetCacheViewVirtualIndexQueue(image_view);
- blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
- for (x=0; x < (ssize_t) image->columns; x++)
+ /*
+ Find the minimum value for each of the color.
+ */
+ for (channel=0; channel < 5; channel++)
+ {
+ list=pixel_list->lists+channel;
+ count=0;
+ color=65536UL;
+ minimum=list->nodes[color].next[0];
+ do
{
- MagickPixelPacket
- pixel;
+ color=list->nodes[color].next[0];
+ if (color < minimum)
+ minimum=color;
+ count+=list->nodes[color].count;
+ } while (count < (ssize_t) pixel_list->length);
+ channels[channel]=(unsigned short) minimum;
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
- register const double
- *restrict k;
+static MagickPixelPacket GetModePixelList(PixelList *pixel_list)
+{
+ MagickPixelPacket
+ pixel;
- register ssize_t
- u;
+ register SkipList
+ *list;
- ssize_t
- j,
- v;
+ register ssize_t
+ channel;
- pixel=bias;
- k=kernel;
- gamma=0.0;
- j=0;
- if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
- {
- for (v=0; v < (ssize_t) width; v++)
- {
- for (u=0; u < (ssize_t) width; u++)
- {
- if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
- {
- pixel.red+=(*k)*(p+u+j)->red;
- pixel.green+=(*k)*(p+u+j)->green;
- pixel.blue+=(*k)*(p+u+j)->blue;
- gamma+=(*k);
- k++;
- }
- }
- j+=(ssize_t) (image->columns+width);
- }
- if (gamma != 0.0)
- {
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
- if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
- if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
- if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
- }
- if ((channel & OpacityChannel) != 0)
- {
- gamma=0.0;
- j=0;
- for (v=0; v < (ssize_t) width; v++)
- {
- for (u=0; u < (ssize_t) width; u++)
- {
- if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
- {
- pixel.opacity+=(*k)*(p+u+j)->opacity;
- gamma+=(*k);
- k++;
- }
- }
- j+=(ssize_t) (image->columns+width);
- }
- if (gamma != 0.0)
- {
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
- gamma);
- SetOpacityPixelComponent(q,ClampToQuantum(gamma*
- GetOpacityPixelComponent(&pixel)));
- }
- }
- if (((channel & IndexChannel) != 0) &&
- (image->colorspace == CMYKColorspace))
- {
- gamma=0.0;
- j=0;
- for (v=0; v < (ssize_t) width; v++)
- {
- for (u=0; u < (ssize_t) width; u++)
- {
- if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
- {
- pixel.index+=(*k)*indexes[x+u+j];
- gamma+=(*k);
- k++;
- }
- }
- j+=(ssize_t) (image->columns+width);
- }
- if (gamma != 0.0)
- {
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
- gamma);
- blur_indexes[x]=ClampToQuantum(gamma*
- GetIndexPixelComponent(&pixel));
- }
- }
- }
- else
- {
- MagickRealType
- alpha;
+ size_t
+ color,
+ max_count,
+ mode;
- for (v=0; v < (ssize_t) width; v++)
- {
- for (u=0; u < (ssize_t) width; u++)
- {
- if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
- {
- alpha=(MagickRealType) (QuantumScale*
- GetAlphaPixelComponent(p+u+j));
- pixel.red+=(*k)*alpha*(p+u+j)->red;
- pixel.green+=(*k)*alpha*(p+u+j)->green;
- pixel.blue+=(*k)*alpha*(p+u+j)->blue;
- pixel.opacity+=(*k)*(p+u+j)->opacity;
- gamma+=(*k)*alpha;
- k++;
- }
- }
- j+=(ssize_t) (image->columns+width);
- }
- if (gamma != 0.0)
- {
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
- if ((channel & RedChannel) != 0)
- q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
- if ((channel & GreenChannel) != 0)
- q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
- if ((channel & BlueChannel) != 0)
- q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
- }
- if ((channel & OpacityChannel) != 0)
- {
- gamma=0.0;
- j=0;
- for (v=0; v < (ssize_t) width; v++)
- {
- for (u=0; u < (ssize_t) width; u++)
- {
- if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
- {
- pixel.opacity+=(*k)*(p+u+j)->opacity;
- gamma+=(*k);
- k++;
- }
- }
- j+=(ssize_t) (image->columns+width);
- }
- if (gamma != 0.0)
- {
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
- gamma);
- SetOpacityPixelComponent(q,
- ClampOpacityPixelComponent(&pixel));
- }
- }
- if (((channel & IndexChannel) != 0) &&
- (image->colorspace == CMYKColorspace))
- {
- gamma=0.0;
- j=0;
- for (v=0; v < (ssize_t) width; v++)
- {
- for (u=0; u < (ssize_t) width; u++)
- {
- if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
- {
- alpha=(MagickRealType) (QuantumScale*
- GetAlphaPixelComponent(p+u+j));
- pixel.index+=(*k)*alpha*indexes[x+u+j];
- gamma+=(*k);
- k++;
- }
- }
- j+=(ssize_t) (image->columns+width);
- }
- if (gamma != 0.0)
- {
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
- gamma);
- blur_indexes[x]=ClampToQuantum(gamma*
- GetIndexPixelComponent(&pixel));
- }
- }
- }
- p++;
- q++;
- }
- sync=SyncCacheViewAuthenticPixels(blur_view,exception);
- if (sync == MagickFalse)
- status=MagickFalse;
- if (image->progress_monitor != (MagickProgressMonitor) NULL)
- {
- MagickBooleanType
- proceed;
+ ssize_t
+ count;
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
-#endif
- proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
- image->rows);
- if (proceed == MagickFalse)
- status=MagickFalse;
- }
- }
- blur_image->type=image->type;
- blur_view=DestroyCacheView(blur_view);
- image_view=DestroyCacheView(image_view);
- kernel=(double *) RelinquishMagickMemory(kernel);
- if (status == MagickFalse)
- blur_image=DestroyImage(blur_image);
- return(blur_image);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% %
-% %
-% %
-% S h a d e I m a g e %
-% %
-% %
-% %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% ShadeImage() shines a distant light on an image to create a
-% three-dimensional effect. You control the positioning of the light with
-% azimuth and elevation; azimuth is measured in degrees off the x axis
-% and elevation is measured in pixels above the Z axis.
-%
-% The format of the ShadeImage method is:
-%
-% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
-% const double azimuth,const double elevation,ExceptionInfo *exception)
-%
-% A description of each parameter follows:
-%
-% o image: the image.
-%
-% o gray: A value other than zero shades the intensity of each pixel.
-%
-% o azimuth, elevation: Define the light source direction.
-%
-% o exception: return any errors or warnings in this structure.
-%
-*/
-MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
- const double azimuth,const double elevation,ExceptionInfo *exception)
-{
-#define ShadeImageTag "Shade/Image"
+ unsigned short
+ channels[5];
- CacheView
- *image_view,
- *shade_view;
+ /*
+ Make each pixel the 'predominate color' of the specified neighborhood.
+ */
+ for (channel=0; channel < 5; channel++)
+ {
+ list=pixel_list->lists+channel;
+ color=65536L;
+ mode=color;
+ max_count=list->nodes[mode].count;
+ count=0;
+ do
+ {
+ color=list->nodes[color].next[0];
+ if (list->nodes[color].count > max_count)
+ {
+ mode=color;
+ max_count=list->nodes[mode].count;
+ }
+ count+=list->nodes[color].count;
+ } while (count < (ssize_t) pixel_list->length);
+ channels[channel]=(unsigned short) mode;
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
- Image
- *shade_image;
+static MagickPixelPacket GetNonpeakPixelList(PixelList *pixel_list)
+{
+ MagickPixelPacket
+ pixel;
- MagickBooleanType
- status;
+ register SkipList
+ *list;
- MagickOffsetType
- progress;
+ register ssize_t
+ channel;
- PrimaryInfo
- light;
+ size_t
+ color,
+ next,
+ previous;
ssize_t
- y;
+ count;
+
+ unsigned short
+ channels[5];
/*
- Initialize shaded image attributes.
- */
- 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);
- shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
- if (shade_image == (Image *) NULL)
- return((Image *) NULL);
- if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
- {
- InheritException(exception,&shade_image->exception);
- shade_image=DestroyImage(shade_image);
- return((Image *) NULL);
- }
- /*
- Compute the light vector.
- */
- light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
- cos(DegreesToRadians(elevation));
- light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
- cos(DegreesToRadians(elevation));
- light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
- /*
- Shade image.
+ Finds the non peak value for each of the colors.
*/
- status=MagickTrue;
- progress=0;
- image_view=AcquireCacheView(image);
- shade_view=AcquireCacheView(shade_image);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
-#endif
- for (y=0; y < (ssize_t) image->rows; y++)
+ for (channel=0; channel < 5; channel++)
{
- MagickRealType
- distance,
- normal_distance,
- shade;
+ list=pixel_list->lists+channel;
+ color=65536L;
+ next=list->nodes[color].next[0];
+ count=0;
+ do
+ {
+ previous=color;
+ color=next;
+ next=list->nodes[color].next[0];
+ count+=list->nodes[color].count;
+ } while (count <= (ssize_t) (pixel_list->length >> 1));
+ if ((previous == 65536UL) && (next != 65536UL))
+ color=next;
+ else
+ if ((previous != 65536UL) && (next == 65536UL))
+ color=previous;
+ channels[channel]=(unsigned short) color;
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
- PrimaryInfo
- normal;
+static MagickPixelPacket GetStandardDeviationPixelList(PixelList *pixel_list)
+{
+ MagickPixelPacket
+ pixel;
- register const PixelPacket
- *restrict p,
- *restrict s0,
- *restrict s1,
- *restrict s2;
+ MagickRealType
+ sum,
+ sum_squared;
- register PixelPacket
- *restrict q;
+ register SkipList
+ *list;
- register ssize_t
- x;
+ register ssize_t
+ channel;
- if (status == MagickFalse)
- continue;
- p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
- q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
- exception);
- if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
- {
- status=MagickFalse;
- continue;
- }
- /*
- Shade this row of pixels.
- */
- normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
- s0=p+1;
- s1=s0+image->columns+2;
- s2=s1+image->columns+2;
- for (x=0; x < (ssize_t) image->columns; x++)
- {
- /*
- Determine the surface normal and compute shading.
- */
- normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
- PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
- PixelIntensity(s2+1));
- normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
- PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
- PixelIntensity(s0+1));
- if ((normal.x == 0.0) && (normal.y == 0.0))
- shade=light.z;
- else
- {
- shade=0.0;
- distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
- if (distance > MagickEpsilon)
- {
- normal_distance=
- normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
- if (normal_distance > (MagickEpsilon*MagickEpsilon))
- shade=distance/sqrt((double) normal_distance);
- }
- }
- if (gray != MagickFalse)
- {
- q->red=(Quantum) shade;
- q->green=(Quantum) shade;
- q->blue=(Quantum) shade;
- }
- else
- {
- q->red=ClampToQuantum(QuantumScale*shade*s1->red);
- q->green=ClampToQuantum(QuantumScale*shade*s1->green);
- q->blue=ClampToQuantum(QuantumScale*shade*s1->blue);
- }
- q->opacity=s1->opacity;
- s0++;
- s1++;
- s2++;
- q++;
- }
- if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
- status=MagickFalse;
- if (image->progress_monitor != (MagickProgressMonitor) NULL)
- {
- MagickBooleanType
- proceed;
+ size_t
+ color;
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_ShadeImage)
-#endif
- proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
- if (proceed == MagickFalse)
- status=MagickFalse;
- }
- }
- shade_view=DestroyCacheView(shade_view);
- image_view=DestroyCacheView(image_view);
- if (status == MagickFalse)
- shade_image=DestroyImage(shade_image);
- return(shade_image);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% %
-% %
-% %
-% S h a r p e n I m a g e %
-% %
-% %
-% %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% SharpenImage() sharpens the image. We convolve the image with a Gaussian
-% operator of the given radius and standard deviation (sigma). For
-% reasonable results, radius should be larger than sigma. Use a radius of 0
-% and SharpenImage() selects a suitable radius for you.
-%
-% Using a separable kernel would be faster, but the negative weights cancel
-% out on the corners of the kernel producing often undesirable ringing in the
-% filtered result; this can be avoided by using a 2D gaussian shaped image
-% sharpening kernel instead.
-%
-% The format of the SharpenImage method is:
-%
-% Image *SharpenImage(const Image *image,const double radius,
-% const double sigma,ExceptionInfo *exception)
-% Image *SharpenImageChannel(const Image *image,const ChannelType channel,
-% const double radius,const double sigma,ExceptionInfo *exception)
-%
-% A description of each parameter follows:
-%
-% o image: the image.
-%
-% o channel: the channel type.
-%
-% o radius: the radius of the Gaussian, in pixels, not counting the center
-% pixel.
-%
-% o sigma: the standard deviation of the Laplacian, in pixels.
-%
-% o exception: return any errors or warnings in this structure.
-%
-*/
+ ssize_t
+ count;
+
+ unsigned short
+ channels[ListChannels];
+
+ /*
+ Find the standard-deviation value for each of the color.
+ */
+ for (channel=0; channel < 5; channel++)
+ {
+ list=pixel_list->lists+channel;
+ color=65536L;
+ count=0;
+ sum=0.0;
+ sum_squared=0.0;
+ do
+ {
+ register ssize_t
+ i;
-MagickExport Image *SharpenImage(const Image *image,const double radius,
- const double sigma,ExceptionInfo *exception)
+ color=list->nodes[color].next[0];
+ sum+=(MagickRealType) list->nodes[color].count*color;
+ for (i=0; i < (ssize_t) list->nodes[color].count; i++)
+ sum_squared+=((MagickRealType) color)*((MagickRealType) color);
+ count+=list->nodes[color].count;
+ } while (count < (ssize_t) pixel_list->length);
+ sum/=pixel_list->length;
+ sum_squared/=pixel_list->length;
+ channels[channel]=(unsigned short) sqrt(sum_squared-(sum*sum));
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
+
+static inline void InsertPixelList(const Image *image,const PixelPacket *pixel,
+ const IndexPacket *indexes,PixelList *pixel_list)
{
- Image
- *sharp_image;
+ size_t
+ signature;
- sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
- return(sharp_image);
+ unsigned short
+ index;
+
+ index=ScaleQuantumToShort(GetRedPixelComponent(pixel));
+ signature=pixel_list->lists[0].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[0].nodes[index].count++;
+ else
+ AddNodePixelList(pixel_list,0,index);
+ index=ScaleQuantumToShort(GetGreenPixelComponent(pixel));
+ signature=pixel_list->lists[1].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[1].nodes[index].count++;
+ else
+ AddNodePixelList(pixel_list,1,index);
+ index=ScaleQuantumToShort(GetBluePixelComponent(pixel));
+ signature=pixel_list->lists[2].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[2].nodes[index].count++;
+ else
+ AddNodePixelList(pixel_list,2,index);
+ index=ScaleQuantumToShort(GetOpacityPixelComponent(pixel));
+ signature=pixel_list->lists[3].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[3].nodes[index].count++;
+ else
+ AddNodePixelList(pixel_list,3,index);
+ if (image->colorspace == CMYKColorspace)
+ index=ScaleQuantumToShort(GetIndexPixelComponent(indexes));
+ signature=pixel_list->lists[4].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[4].nodes[index].count++;
+ else
+ AddNodePixelList(pixel_list,4,index);
}
-MagickExport Image *SharpenImageChannel(const Image *image,
- const ChannelType channel,const double radius,const double sigma,
- ExceptionInfo *exception)
+static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
{
- double
- *kernel,
- normalize;
+ if (x < 0)
+ return(-x);
+ return(x);
+}
- Image
- *sharp_image;
+static void ResetPixelList(PixelList *pixel_list)
+{
+ int
+ level;
- register ssize_t
- i;
+ register ListNode
+ *root;
- size_t
- width;
+ register SkipList
+ *list;
- ssize_t
- j,
- u,
- v;
+ register ssize_t
+ channel;
- 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);
- width=GetOptimalKernelWidth2D(radius,sigma);
- kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
- if (kernel == (double *) NULL)
- ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
- normalize=0.0;
- j=(ssize_t) width/2;
- i=0;
- for (v=(-j); v <= j; v++)
+ /*
+ Reset the skip-list.
+ */
+ for (channel=0; channel < 5; channel++)
{
- for (u=(-j); u <= j; u++)
- {
- kernel[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
- MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
- normalize+=kernel[i];
- i++;
- }
+ list=pixel_list->lists+channel;
+ root=list->nodes+65536UL;
+ list->level=0;
+ for (level=0; level < 9; level++)
+ root->next[level]=65536UL;
}
- kernel[i/2]=(double) ((-2.0)*normalize);
- sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
- kernel=(double *) RelinquishMagickMemory(kernel);
- return(sharp_image);
+ pixel_list->seed=pixel_list->signature++;
}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% %
-% %
-% %
-% S p r e a d I m a g e %
-% %
-% %
-% %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% SpreadImage() is a special effects method that randomly displaces each
-% pixel in a block defined by the radius parameter.
-%
-% The format of the SpreadImage method is:
-%
-% Image *SpreadImage(const Image *image,const double radius,
-% ExceptionInfo *exception)
-%
-% A description of each parameter follows:
-%
-% o image: the image.
-%
-% o radius: Choose a random pixel in a neighborhood of this extent.
-%
-% o exception: return any errors or warnings in this structure.
-%
-*/
-MagickExport Image *SpreadImage(const Image *image,const double radius,
- ExceptionInfo *exception)
+
+MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
+ const size_t width,const size_t height,ExceptionInfo *exception)
{
-#define SpreadImageTag "Spread/Image"
+ Image
+ *statistic_image;
+
+ statistic_image=StatisticImageChannel(image,DefaultChannels,type,width,
+ height,exception);
+ return(statistic_image);
+}
+
+MagickExport Image *StatisticImageChannel(const Image *image,
+ const ChannelType channel,const StatisticType type,const size_t width,
+ const size_t height,ExceptionInfo *exception)
+{
+#define StatisticWidth \
+ (width == 0 ? GetOptimalKernelWidth2D((double) width,0.5) : width)
+#define StatisticHeight \
+ (height == 0 ? GetOptimalKernelWidth2D((double) height,0.5) : height)
+#define StatisticImageTag "Statistic/Image"
CacheView
- *image_view;
+ *image_view,
+ *statistic_view;
Image
- *spread_image;
+ *statistic_image;
MagickBooleanType
status;
MagickOffsetType
progress;
- MagickPixelPacket
- bias;
-
- RandomInfo
- **restrict random_info;
-
- ResampleFilter
- **restrict resample_filter;
-
- size_t
- width;
+ PixelList
+ **restrict pixel_list;
ssize_t
y;
/*
- Initialize spread image attributes.
+ Initialize statistics image attributes.
*/
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
- spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
exception);
- if (spread_image == (Image *) NULL)
+ if (statistic_image == (Image *) NULL)
return((Image *) NULL);
- if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
+ if (SetImageStorageClass(statistic_image,DirectClass) == MagickFalse)
{
- InheritException(exception,&spread_image->exception);
- spread_image=DestroyImage(spread_image);
+ InheritException(exception,&statistic_image->exception);
+ statistic_image=DestroyImage(statistic_image);
return((Image *) NULL);
}
+ pixel_list=AcquirePixelListThreadSet(StatisticWidth,StatisticHeight);
+ if (pixel_list == (PixelList **) NULL)
+ {
+ statistic_image=DestroyImage(statistic_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
/*
- Spread image.
+ Make each pixel the min / max / median / mode / etc. of the neighborhood.
*/
status=MagickTrue;
progress=0;
- GetMagickPixelPacket(spread_image,&bias);
- width=GetOptimalKernelWidth1D(radius,0.5);
- resample_filter=AcquireResampleFilterThreadSet(image,
- UndefinedVirtualPixelMethod,MagickTrue,exception);
- random_info=AcquireRandomInfoThreadSet();
- image_view=AcquireCacheView(spread_image);
+ image_view=AcquireCacheView(image);
+ statistic_view=AcquireCacheView(statistic_image);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
#endif
- for (y=0; y < (ssize_t) spread_image->rows; y++)
+ for (y=0; y < (ssize_t) statistic_image->rows; y++)
{
const int
id = GetOpenMPThreadId();
- MagickPixelPacket
- pixel;
+ register const IndexPacket
+ *restrict indexes;
+
+ register const PixelPacket
+ *restrict p;
register IndexPacket
- *restrict indexes;
+ *restrict statistic_indexes;
register PixelPacket
*restrict q;
if (status == MagickFalse)
continue;
- q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
- exception);
- if (q == (PixelPacket *) NULL)
+ p=GetCacheViewVirtualPixels(image_view,-((ssize_t) StatisticWidth/2L),y-
+ (ssize_t) (StatisticHeight/2L),image->columns+StatisticWidth,
+ StatisticHeight,exception);
+ q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns, 1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
{
status=MagickFalse;
continue;
}
- indexes=GetCacheViewAuthenticIndexQueue(image_view);
- pixel=bias;
- for (x=0; x < (ssize_t) spread_image->columns; x++)
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ statistic_indexes=GetCacheViewAuthenticIndexQueue(statistic_view);
+ for (x=0; x < (ssize_t) statistic_image->columns; x++)
{
- (void) ResamplePixelColor(resample_filter[id],(double) x+width*
- (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
- (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
- SetPixelPacket(spread_image,&pixel,q,indexes+x);
+ MagickPixelPacket
+ pixel;
+
+ register const IndexPacket
+ *restrict s;
+
+ register const PixelPacket
+ *restrict r;
+
+ register ssize_t
+ u,
+ v;
+
+ r=p;
+ s=indexes+x;
+ ResetPixelList(pixel_list[id]);
+ for (v=0; v < (ssize_t) StatisticHeight; v++)
+ {
+ for (u=0; u < (ssize_t) StatisticWidth; u++)
+ InsertPixelList(image,r+u,s+u,pixel_list[id]);
+ r+=image->columns+StatisticWidth;
+ s+=image->columns+StatisticWidth;
+ }
+ GetMagickPixelPacket(image,&pixel);
+ SetMagickPixelPacket(image,p+StatisticWidth*StatisticHeight/2,indexes+
+ StatisticWidth*StatisticHeight/2+x,&pixel);
+ switch (type)
+ {
+ case GradientStatistic:
+ {
+ MagickPixelPacket
+ maximum,
+ minimum;
+
+ minimum=GetMinimumPixelList(pixel_list[id]);
+ maximum=GetMaximumPixelList(pixel_list[id]);
+ pixel.red=MagickAbsoluteValue(maximum.red-minimum.red);
+ pixel.green=MagickAbsoluteValue(maximum.green-minimum.green);
+ pixel.blue=MagickAbsoluteValue(maximum.blue-minimum.blue);
+ pixel.opacity=MagickAbsoluteValue(maximum.opacity-minimum.opacity);
+ if (image->colorspace == CMYKColorspace)
+ pixel.index=MagickAbsoluteValue(maximum.index-minimum.index);
+ break;
+ }
+ case MaximumStatistic:
+ {
+ pixel=GetMaximumPixelList(pixel_list[id]);
+ break;
+ }
+ case MeanStatistic:
+ {
+ pixel=GetMeanPixelList(pixel_list[id]);
+ break;
+ }
+ case MedianStatistic:
+ default:
+ {
+ pixel=GetMedianPixelList(pixel_list[id]);
+ break;
+ }
+ case MinimumStatistic:
+ {
+ pixel=GetMinimumPixelList(pixel_list[id]);
+ break;
+ }
+ case ModeStatistic:
+ {
+ pixel=GetModePixelList(pixel_list[id]);
+ break;
+ }
+ case NonpeakStatistic:
+ {
+ pixel=GetNonpeakPixelList(pixel_list[id]);
+ break;
+ }
+ case StandardDeviationStatistic:
+ {
+ pixel=GetStandardDeviationPixelList(pixel_list[id]);
+ break;
+ }
+ }
+ if ((channel & RedChannel) != 0)
+ SetRedPixelComponent(q,ClampToQuantum(pixel.red));
+ if ((channel & GreenChannel) != 0)
+ SetGreenPixelComponent(q,ClampToQuantum(pixel.green));
+ if ((channel & BlueChannel) != 0)
+ SetBluePixelComponent(q,ClampToQuantum(pixel.blue));
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ SetIndexPixelComponent(statistic_indexes+x,ClampToQuantum(pixel.index));
+ p++;
q++;
}
- if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
status=MagickFalse;
if (image->progress_monitor != (MagickProgressMonitor) NULL)
{
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_SpreadImage)
+ #pragma omp critical (MagickCore_StatisticImage)
#endif
- proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
+ proceed=SetImageProgress(image,StatisticImageTag,progress++,
+ image->rows);
if (proceed == MagickFalse)
status=MagickFalse;
}
}
+ statistic_view=DestroyCacheView(statistic_view);
image_view=DestroyCacheView(image_view);
- random_info=DestroyRandomInfoThreadSet(random_info);
- resample_filter=DestroyResampleFilterThreadSet(resample_filter);
- return(spread_image);
+ pixel_list=DestroyPixelListThreadSet(pixel_list);
+ return(statistic_image);
}
\f
/*
{
if ((channel & RedChannel) != 0)
{
- pixel.red=p->red-(MagickRealType) q->red;
+ pixel.red=GetRedPixelComponent(p)-(MagickRealType)
+ GetRedPixelComponent(q);
if (fabs(2.0*pixel.red) < quantum_threshold)
pixel.red=(MagickRealType) GetRedPixelComponent(p);
else
- pixel.red=(MagickRealType) p->red+(pixel.red*amount);
- SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
+ pixel.red=(MagickRealType) GetRedPixelComponent(p)+
+ (pixel.red*amount);
+ SetRedPixelComponent(q,ClampToQuantum(pixel.red));
}
if ((channel & GreenChannel) != 0)
{
- pixel.green=p->green-(MagickRealType) q->green;
+ pixel.green=GetGreenPixelComponent(p)-(MagickRealType) q->green;
if (fabs(2.0*pixel.green) < quantum_threshold)
pixel.green=(MagickRealType) GetGreenPixelComponent(p);
else
- pixel.green=(MagickRealType) p->green+(pixel.green*amount);
- SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
+ pixel.green=(MagickRealType) GetGreenPixelComponent(p)+(pixel.green*amount);
+ SetGreenPixelComponent(q,ClampToQuantum(pixel.green));
}
if ((channel & BlueChannel) != 0)
{
- pixel.blue=p->blue-(MagickRealType) q->blue;
+ pixel.blue=GetBluePixelComponent(p)-(MagickRealType) q->blue;
if (fabs(2.0*pixel.blue) < quantum_threshold)
pixel.blue=(MagickRealType) GetBluePixelComponent(p);
else
- pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
- SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
+ pixel.blue=(MagickRealType) GetBluePixelComponent(p)+(pixel.blue*amount);
+ SetBluePixelComponent(q,ClampToQuantum(pixel.blue));
}
if ((channel & OpacityChannel) != 0)
{
- pixel.opacity=p->opacity-(MagickRealType) q->opacity;
+ pixel.opacity=GetOpacityPixelComponent(p)-(MagickRealType) q->opacity;
if (fabs(2.0*pixel.opacity) < quantum_threshold)
pixel.opacity=(MagickRealType) GetOpacityPixelComponent(p);
else
- pixel.opacity=p->opacity+(pixel.opacity*amount);
- SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
+ pixel.opacity=GetOpacityPixelComponent(p)+(pixel.opacity*amount);
+ SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
}
if (((channel & IndexChannel) != 0) &&
(image->colorspace == CMYKColorspace))
{
- pixel.index=indexes[x]-(MagickRealType) unsharp_indexes[x];
+ pixel.index=GetIndexPixelComponent(indexes+x)-(MagickRealType)
+ GetIndexPixelComponent(unsharp_indexes+x);
if (fabs(2.0*pixel.index) < quantum_threshold)
- pixel.index=(MagickRealType) indexes[x];
+ pixel.index=(MagickRealType) GetIndexPixelComponent(indexes+x);
else
- pixel.index=(MagickRealType) indexes[x]+(pixel.index*amount);
- unsharp_indexes[x]=ClampToQuantum(pixel.index);
+ pixel.index=(MagickRealType) GetIndexPixelComponent(indexes+x)+
+ (pixel.index*amount);
+ SetIndexPixelComponent(unsharp_indexes+x,ClampToQuantum(pixel.index));
}
p++;
q++;