#include "MagickCore/list.h"
#include "MagickCore/magick.h"
#include "MagickCore/memory_.h"
+#include "MagickCore/memory-private.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/morphology.h"
#include "MagickCore/morphology-private.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/prepress.h"
#include "MagickCore/quantize.h"
+#include "MagickCore/resource_.h"
#include "MagickCore/registry.h"
#include "MagickCore/semaphore.h"
#include "MagickCore/splay-tree.h"
#include "MagickCore/statistic.h"
#include "MagickCore/string_.h"
#include "MagickCore/string-private.h"
+#include "MagickCore/thread-private.h"
#include "MagickCore/token.h"
#include "MagickCore/utility.h"
#include "MagickCore/utility-private.h"
** part of the kernel neighbourhood for convolution or morphology processing,
** and thus should be ignored. This allows the use of 'shaped' kernels.
**
-** The special properity that two NaN's are never equal, even if they are from
+** The special property that two NaN's are never equal, even if they are from
** the same variable allow you to test if a value is special NaN value.
**
** This macro IsNaN() is thus is only true if the value given is NaN.
#define Minimize(assign,value) assign=MagickMin(assign,value)
#define Maximize(assign,value) assign=MagickMax(assign,value)
+/* Integer Factorial Function - for a Binomial kernel */
+#if 1
+static inline size_t fact(size_t n)
+{
+ size_t f,l;
+ for(f=1, l=2; l <= n; f=f*l, l++);
+ return(f);
+}
+#elif 1 /* glibc floating point alternatives */
+#define fact(n) ((size_t)tgamma((double)n+1))
+#else
+#define fact(n) ((size_t)lgamma((double)n+1))
+#endif
+
+
/* Currently these are only internal to this module */
static void
CalcKernelMetaData(KernelInfo *),
/* clear flags - for Expanding kernel lists thorugh rotations */
flags = NoValue;
- /* Has a ':' in argument - New user kernel specification */
+ /* Has a ':' in argument - New user kernel specification
+ FUTURE: this split on ':' could be done by StringToken()
+ */
p = strchr(kernel_string, ':');
if ( p != (char *) NULL && p < end)
{
if ( args.xi < 0.0 || args.psi < 0.0 )
return(DestroyKernelInfo(kernel));
kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
- : (ssize_t) (kernel->width-1)/2;
+ : (ssize_t) (kernel->width-1)/2;
kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
- : (ssize_t) (kernel->height-1)/2;
+ : (ssize_t) (kernel->height-1)/2;
if ( kernel->x >= (ssize_t) kernel->width ||
kernel->y >= (ssize_t) kernel->height )
return(DestroyKernelInfo(kernel));
}
/* Read in the kernel values from rest of input string argument */
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
+ kernel->width,kernel->height*sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->minimum = +MagickHuge;
GetMagickToken(p,&p,token);
if ( LocaleCompare("nan",token) == 0
|| LocaleCompare("-",token) == 0 ) {
- kernel->values[i] = nan; /* do not include this value in kernel */
+ kernel->values[i] = nan; /* this value is not part of neighbourhood */
}
else {
- kernel->values[i]=StringToDouble(token,(char **) NULL);
+ kernel->values[i] = StringToDouble(token,(char **) NULL);
( kernel->values[i] < 0)
? ( kernel->negative_range += kernel->values[i] )
: ( kernel->positive_range += kernel->values[i] );
% Note that the first argument is the width of the kernel and not the
% radius of the kernel.
%
+% Binomial:[{radius}]
+% Generate a discrete kernel using a 2 dimentional Pascel's Triangle
+% of values. Used for special forma of image filters.
+%
% # Still to be implemented...
% #
% # Filter2D
case LoGKernel:
case BlurKernel:
case CometKernel:
+ case BinomialKernel:
case DiamondKernel:
case SquareKernel:
case RectangleKernel:
{
kernel->height = kernel->width = (size_t) 1;
kernel->x = kernel->y = (ssize_t) 0;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(1,
- sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(1,sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->maximum = kernel->values[0] = args->rho;
kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
kernel->height = kernel->width;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->x = (ssize_t) (kernel->width-1)/2;
kernel->y = 0;
kernel->negative_range = kernel->positive_range = 0.0;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
else /* special case - generate a unity kernel */
kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
#else
- /* Direct calculation without curve averaging */
+ /* Direct calculation without curve averaging
+ This is equivelent to a KernelRank of 1 */
/* Calculate a Positive Gaussian */
if ( sigma > MagickEpsilon )
#endif
/* Note the above kernel may have been 'clipped' by a user defined
** radius, producing a smaller (darker) kernel. Also for very small
- ** sigma's (> 0.1) the central value becomes larger than one, and thus
- ** producing a very bright kernel.
+ ** sigma's (> 0.1) the central value becomes larger than one, as a
+ ** result of not generating a actual 'discrete' kernel, and thus
+ ** producing a very bright 'impulse'.
**
- ** Normalization will still be needed.
+ ** Becuase of these two factors Normalization is required!
*/
/* Normalize the 1D Gaussian Kernel
kernel->x = kernel->y = 0;
kernel->height = 1;
kernel->negative_range = kernel->positive_range = 0.0;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
/* B = 1.0/(MagickSQ2PI*sigma); */
for ( i=0; i < (ssize_t) kernel->width; i++)
kernel->positive_range +=
- kernel->values[i] =
- exp(-((double)(i*i))*A);
+ kernel->values[i] = exp(-((double)(i*i))*A);
/* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
#endif
}
RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
break;
}
+ case BinomialKernel:
+ {
+ size_t
+ order_f;
+
+ if (args->rho < 1.0)
+ kernel->width = kernel->height = 3; /* default radius = 1 */
+ else
+ kernel->width = kernel->height = ((size_t)args->rho)*2+1;
+ kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
+
+ order_f = fact(kernel->width-1);
+
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
+ if (kernel->values == (MagickRealType *) NULL)
+ return(DestroyKernelInfo(kernel));
+
+ /* set all kernel values within diamond area to scale given */
+ for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
+ { size_t
+ alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
+ for ( u=0; u < (ssize_t)kernel->width; u++, i++)
+ kernel->positive_range += kernel->values[i] = (double)
+ (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
+ }
+ kernel->minimum = 1.0;
+ kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
+ kernel->negative_range = 0.0;
+ break;
+ }
/*
Convolution Kernels - Well Known Named Constant Kernels
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
- kernel->values[3]+=(MagickRealType) MagickSQ2;
- kernel->values[5]-=(MagickRealType) MagickSQ2;
+ kernel->values[3] = +(MagickRealType) MagickSQ2;
+ kernel->values[5] = -(MagickRealType) MagickSQ2;
CalcKernelMetaData(kernel); /* recalculate meta-data */
break;
case 2:
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
- kernel->values[1] = kernel->values[3]+=(MagickRealType) MagickSQ2;
- kernel->values[5] = kernel->values[7]-=(MagickRealType) MagickSQ2;
+ kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
+ kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
CalcKernelMetaData(kernel); /* recalculate meta-data */
ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
break;
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
- kernel->values[3]+=(MagickRealType) MagickSQ2;
- kernel->values[5]-=(MagickRealType) MagickSQ2;
+ kernel->values[3] = +(MagickRealType) MagickSQ2;
+ kernel->values[5] = -(MagickRealType) MagickSQ2;
CalcKernelMetaData(kernel); /* recalculate meta-data */
ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
break;
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
- kernel->values[1]+=(MagickRealType) MagickSQ2;
- kernel->values[7]+=(MagickRealType) MagickSQ2;
+ kernel->values[1] = +(double) MagickSQ2;
+ kernel->values[7] = +(double) MagickSQ2;
CalcKernelMetaData(kernel);
ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
break;
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
- kernel->values[0]+=(MagickRealType) MagickSQ2;
- kernel->values[8]-=(MagickRealType) MagickSQ2;
+ kernel->values[0] = +(MagickRealType) MagickSQ2;
+ kernel->values[8] = -(MagickRealType) MagickSQ2;
CalcKernelMetaData(kernel);
ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
break;
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
- kernel->values[2]-=(MagickRealType) MagickSQ2;
- kernel->values[6]+=(MagickRealType) MagickSQ2;
+ kernel->values[2] = -(MagickRealType) MagickSQ2;
+ kernel->values[6] = +(MagickRealType) MagickSQ2;
CalcKernelMetaData(kernel);
ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
break;
ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
break;
}
- if ( fabs(args->sigma) > MagickEpsilon )
+ if ( fabs(args->sigma) >= MagickEpsilon )
/* Rotate by correctly supplied 'angle' */
RotateKernelInfo(kernel, args->sigma);
else if ( args->rho > 30.0 || args->rho < -30.0 )
kernel->width = kernel->height = ((size_t)args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->y = (ssize_t) args->psi;
scale = 1.0;
}
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->width = kernel->height = ((size_t)args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->width = kernel->height = ((size_t)args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->width = kernel->height = ((size_t)args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->height = kernel->width;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->width = kernel->height = ((size_t)args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->width = kernel->height = ((size_t)args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->width = kernel->height = ((size_t)args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
kernel->width = kernel->height = ((size_t)args->rho)*2+1;
kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
- kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*
+ sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
kernel->positive_range += ( kernel->values[i] =
- args->sigma*sqrt((double)(u*u+v*v)) );
+ args->sigma*sqrt((double)(u*u+v*v)) );
kernel->maximum = kernel->values[0];
break;
}
%
% CloneKernelInfo() creates a new clone of the given Kernel List so that its
% can be modified without effecting the original. The cloned kernel should
-% be destroyed using DestroyKernelInfo() when no longer needed.
+% be destroyed using DestoryKernelInfo() when no longer needed.
%
% The format of the CloneKernelInfo method is:
%
*new_kernel=(*kernel); /* copy values in structure */
/* replace the values with a copy of the values */
- new_kernel->values=(MagickRealType *) AcquireAlignedMemory(kernel->width,
- kernel->height*sizeof(*kernel->values));
+ new_kernel->values=(MagickRealType *) MagickAssumeAligned(
+ AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
if (new_kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(new_kernel));
for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
if ( IsNan(kernel2->values[i]) && !IsNan(kernel1->values[i]) )
return MagickFalse;
/* Test actual values are equivalent */
- if ( fabs(kernel1->values[i] - kernel2->values[i]) > MagickEpsilon )
+ if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
return MagickFalse;
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% MorphologyApply() applies a morphological method, multiple times using
-% a list of multiple kernels.
+% a list of multiple kernels. This is the method that should be called by
+% other 'operators' that internally use morphology operations as part of
+% their processing.
%
% It is basically equivalent to as MorphologyImage() (see below) but
% without any user controls. This allows internel programs to use this
% It is MorphologyImage() task to extract any such user controls, and
% pass them to this function for processing.
%
-% More specifically kernels are not normalized/scaled/blended by the
-% 'convolve:scale' Image Artifact (setting), nor is the convolve bias
-% ('convolve:bias' artifact) looked at, but must be supplied from the
-% function arguments.
+% More specifically all given kernels should already be scaled, normalised,
+% and blended appropriatally before being parred to this routine. The
+% appropriate bias, and compose (typically 'UndefinedComposeOp') given.
%
% The format of the MorphologyApply method is:
%
changed=0;
progress=0;
- image_view=AcquireCacheView(image);
- morphology_view=AcquireCacheView(morphology_image);
+ image_view=AcquireVirtualCacheView(image,exception);
+ morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
virt_width=image->columns+kernel->width-1;
/* Some methods (including convolve) needs use a reflected kernel.
x;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
-#pragma omp parallel for schedule(static,4) shared(progress,status)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ dynamic_number_threads(image,image->columns,image->rows,1)
#endif
for (x=0; x < (ssize_t) image->columns; x++)
{
PixelInfo
result;
- register ssize_t
- v;
-
register const MagickRealType
*restrict k;
register const Quantum
*restrict k_pixels;
+ register ssize_t
+ v;
+
/* Copy input image to the output image for unused channels
* This removes need for 'cloning' a new image every iteration
*/
GetPixelChannels(image)),q);
/* Set the bias of the weighted average output */
- result.red =
- result.green =
- result.blue =
+ result.red =
+ result.green =
+ result.blue =
result.alpha =
- result.black = bias;
+ result.black = bias;
/* Weighted Average of pixels using reflected kernel
*/
k = &kernel->values[ kernel->height-1 ];
k_pixels = p;
- if ( (image->channel_mask != DefaultChannels) || (image->matte == MagickFalse) )
+ if ( (image->channel_mask != DefaultChannels) ||
+ (image->alpha_trait != BlendPixelTrait) )
{ /* No 'Sync' involved.
- ** Convolution is simple greyscale channel operation
+ ** Convolution is just a simple greyscale channel operation
*/
for (v=0; v < (ssize_t) kernel->height; v++) {
if ( IsNan(*k) ) continue;
(image->colorspace == CMYKColorspace))
SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte == MagickTrue))
+ (image->alpha_trait == BlendPixelTrait))
SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
}
else
** Weight the color channels with Alpha Channel so that
** transparent pixels are not part of the results.
*/
- MagickRealType
- alpha, /* alpha weighting of colors : kernel*alpha */
- gamma; /* divisor, sum of color weighting values */
+ double
+ alpha, /* alpha weighting for colors : alpha */
+ gamma; /* divisor, sum of color alpha weighting */
+ size_t
+ count; /* alpha valus collected, number kernel values */
+ count=0;
gamma=0.0;
for (v=0; v < (ssize_t) kernel->height; v++) {
if ( IsNan(*k) ) continue;
- alpha=(*k)*(QuantumScale*GetPixelAlpha(image,k_pixels));
- gamma += alpha;
+ alpha=QuantumScale*GetPixelAlpha(image,k_pixels);
+ gamma += alpha; /* normalize alpha weights only */
+ count++; /* number of alpha values collected */
+ alpha*=(*k); /* include kernel weighting now */
result.red += alpha*GetPixelRed(image,k_pixels);
result.green += alpha*GetPixelGreen(image,k_pixels);
result.blue += alpha*GetPixelBlue(image,k_pixels);
if (image->colorspace == CMYKColorspace)
result.black += alpha*GetPixelBlack(image,k_pixels);
- result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
+ result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
k--;
k_pixels+=GetPixelChannels(image);
}
/* Sync'ed channels, all channels are modified */
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
- SetPixelRed(morphology_image,
- ClampToQuantum(gamma*result.red),q);
- SetPixelGreen(morphology_image,
- ClampToQuantum(gamma*result.green),q);
- SetPixelBlue(morphology_image,
- ClampToQuantum(gamma*result.blue),q);
+ gamma=(double)count/(fabs((double) gamma) < MagickEpsilon ? MagickEpsilon : gamma);
+ SetPixelRed(morphology_image,ClampToQuantum(gamma*result.red),q);
+ SetPixelGreen(morphology_image,ClampToQuantum(gamma*result.green),q);
+ SetPixelBlue(morphology_image,ClampToQuantum(gamma*result.blue),q);
if (image->colorspace == CMYKColorspace)
- SetPixelBlack(morphology_image,
- ClampToQuantum(gamma*result.black),q);
- SetPixelAlpha(morphology_image,
- ClampToQuantum(result.alpha),q);
+ SetPixelBlack(morphology_image,ClampToQuantum(gamma*result.black),q);
+ SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
}
/* Count up changed pixels */
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_MorphologyImage)
+ #pragma omp critical (MagickCore_MorphologyImage)
#endif
proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
if (proceed == MagickFalse)
** Normal handling of horizontal or rectangular kernels (row by row)
*/
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp parallel for schedule(static,4) shared(progress,status)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ dynamic_number_threads(image,image->columns,image->rows,1)
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
for (x=0; x < (ssize_t) image->columns; x++)
{
- ssize_t
- v;
-
- register ssize_t
- u;
+ PixelInfo
+ result,
+ min,
+ max;
register const MagickRealType
*restrict k;
register const Quantum
*restrict k_pixels;
- PixelInfo
- result,
- min,
- max;
+ register ssize_t
+ u;
+
+ ssize_t
+ v;
/* Copy input image to the output image for unused channels
* This removes need for 'cloning' a new image every iteration
min.green =
min.blue =
min.alpha =
- min.black = (MagickRealType) QuantumRange;
+ min.black = (double) QuantumRange;
max.red =
max.green =
max.blue =
max.alpha =
- max.black = (MagickRealType) 0;
+ max.black = (double) 0;
/* default result is the original pixel value */
- result.red = (MagickRealType) GetPixelRed(image,p+r*GetPixelChannels(image));
- result.green = (MagickRealType) GetPixelGreen(image,p+r*GetPixelChannels(image));
- result.blue = (MagickRealType) GetPixelBlue(image,p+r*GetPixelChannels(image));
+ result.red = (double) GetPixelRed(image,p+r*GetPixelChannels(image));
+ result.green = (double) GetPixelGreen(image,p+r*GetPixelChannels(image));
+ result.blue = (double) GetPixelBlue(image,p+r*GetPixelChannels(image));
result.black = 0.0;
if (image->colorspace == CMYKColorspace)
- result.black = (MagickRealType) GetPixelBlack(image,p+r*GetPixelChannels(image));
- result.alpha=(MagickRealType) GetPixelAlpha(image,p+r*GetPixelChannels(image));
+ result.black = (double) GetPixelBlack(image,p+r*GetPixelChannels(image));
+ result.alpha=(double) GetPixelAlpha(image,p+r*GetPixelChannels(image));
switch (method) {
case ConvolveMorphology:
k = &kernel->values[ kernel->width*kernel->height-1 ];
k_pixels = p;
if ( (image->channel_mask != DefaultChannels) ||
- (image->matte == MagickFalse) )
+ (image->alpha_trait != BlendPixelTrait) )
{ /* No 'Sync' involved.
** Convolution is simple greyscale channel operation
*/
SetPixelBlack(morphology_image,ClampToQuantum(result.black),
q);
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte == MagickTrue))
+ (image->alpha_trait == BlendPixelTrait))
SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),
q);
}
** Weight the color channels with Alpha Channel so that
** transparent pixels are not part of the results.
*/
- MagickRealType
- alpha, /* alpha weighting of colors : kernel*alpha */
- gamma; /* divisor, sum of color weighting values */
+ double
+ alpha, /* alpha weighting for colors : alpha */
+ gamma; /* divisor, sum of color alpha weighting */
+ size_t
+ count; /* alpha valus collected, number kernel values */
+ count=0;
gamma=0.0;
for (v=0; v < (ssize_t) kernel->height; v++) {
for (u=0; u < (ssize_t) kernel->width; u++, k--) {
if ( IsNan(*k) ) continue;
- alpha=(*k)*(QuantumScale*GetPixelAlpha(image,k_pixels+u*
- GetPixelChannels(image)));
- gamma += alpha;
+ alpha=QuantumScale*GetPixelAlpha(image,
+ k_pixels+u*GetPixelChannels(image));
+ gamma += alpha; /* normalize alpha weights only */
+ count++; /* number of alpha values collected */
+ alpha=alpha*(*k); /* include kernel weighting now */
result.red += alpha*
GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
result.green += alpha*
result.blue += alpha*
GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
if (image->colorspace == CMYKColorspace)
- result.black+=alpha*
+ result.black += alpha*
GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
- result.alpha += (*k)*
+ result.alpha += (*k)*
GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
}
k_pixels += virt_width*GetPixelChannels(image);
}
/* Sync'ed channels, all channels are modified */
- gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ gamma=(double)count/(fabs((double) gamma) < MagickEpsilon ? MagickEpsilon : gamma);
SetPixelRed(morphology_image,
ClampToQuantum(gamma*result.red),q);
SetPixelGreen(morphology_image,
(image->colorspace == CMYKColorspace))
SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
- (image->matte == MagickTrue))
+ (image->alpha_trait == BlendPixelTrait))
SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
break;
}
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_MorphologyImage)
+ #pragma omp critical (MagickCore_MorphologyImage)
#endif
proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
if (proceed == MagickFalse)
/* DO NOT THREAD THIS CODE! */
/* two views into same image (virtual, and actual) */
- virt_view=AcquireCacheView(image);
- auth_view=AcquireCacheView(image);
+ virt_view=AcquireVirtualCacheView(image,exception);
+ auth_view=AcquireAuthenticCacheView(image,exception);
virt_width=image->columns+kernel->width-1;
for (y=0; y < (ssize_t) image->rows; y++)
for (x=0; x < (ssize_t) image->columns; x++)
{
- ssize_t
- v;
-
- register ssize_t
- u;
+ PixelInfo
+ result;
register const MagickRealType
*restrict k;
register const Quantum
*restrict k_pixels;
- PixelInfo
- result;
+ register ssize_t
+ u;
+
+ ssize_t
+ v;
/* Starting Defaults */
GetPixelInfo(image,&result);
}
break;
case VoronoiMorphology:
- /* Apply Distance to 'Matte' channel, coping the closest color.
+ /* Apply Distance to 'Matte' channel, while coping the color
+ ** values of the closest pixel.
**
** This is experimental, and realy the 'alpha' component should
** be completely separate 'masking' channel so that alpha can
(image->colorspace == CMYKColorspace))
SetPixelBlack(image,ClampToQuantum(result.black),q);
if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
- (image->matte == MagickTrue))
+ (image->alpha_trait == BlendPixelTrait))
SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
break;
}
for (x=(ssize_t)image->columns-1; x >= 0; x--)
{
- ssize_t
- v;
-
- register ssize_t
- u;
+ PixelInfo
+ result;
register const MagickRealType
*restrict k;
register const Quantum
*restrict k_pixels;
- PixelInfo
- result;
+ register ssize_t
+ u;
+
+ ssize_t
+ v;
/* Default - previously modified pixel */
GetPixelInfo(image,&result);
(image->colorspace == CMYKColorspace))
SetPixelBlack(image,ClampToQuantum(result.black),q);
if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
- (image->matte == MagickTrue))
+ (image->alpha_trait == BlendPixelTrait))
SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
break;
}
** composition or re-iteration of results, and compound morphology methods
** that is based on multiple low-level (staged) morphology methods.
**
-** Basically this provides the complex grue between the requested morphology
+** Basically this provides the complex glue between the requested morphology
** method and raw low-level implementation (above).
*/
MagickPrivate Image *MorphologyApply(const Image *image,
changed = MorphologyPrimitiveDirect(rslt_image, method,
kernel, exception);
- if ( IfTrue(verbose) )
+ if ( IfMagickTrue(verbose) )
(void) (void) FormatLocaleFile(stderr,
"%s:%.20g.%.20g #%.20g => Changed %.20g\n",
CommandOptionToMnemonic(MagickMorphologyOptions, method),
assert( this_kernel != (KernelInfo *) NULL );
/* Extra information for debugging compound operations */
- if ( IfTrue(verbose) ) {
+ if ( IfMagickTrue(verbose) ) {
if ( stage_limit > 1 )
(void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
changed = MorphologyPrimitive(curr_image, work_image, primitive,
this_kernel, bias, exception);
- if ( IfTrue(verbose) ) {
+ if ( IfMagickTrue(verbose) ) {
if ( kernel_loop > 1 )
(void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
(void) (void) FormatLocaleFile(stderr,
} /* End Loop 4: Iterate the kernel with primitive */
- if ( IfTrue(verbose) && kernel_changed != (size_t)changed )
+ if ( IfMagickTrue(verbose) && kernel_changed != (size_t)changed )
(void) FormatLocaleFile(stderr, " Total %.20g",(double) kernel_changed);
- if ( IfTrue(verbose) && stage_loop < stage_limit )
+ if ( IfMagickTrue(verbose) && stage_loop < stage_limit )
(void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
#if 0
case EdgeInMorphology:
case TopHatMorphology:
case BottomHatMorphology:
- if ( IfTrue(verbose) )
+ if ( IfMagickTrue(verbose) )
(void) FormatLocaleFile(stderr,
"\n%s: Difference with original image",CommandOptionToMnemonic(
MagickMorphologyOptions, method) );
MagickTrue,0,0,exception);
break;
case EdgeMorphology:
- if ( IfTrue(verbose) )
+ if ( IfMagickTrue(verbose) )
(void) FormatLocaleFile(stderr,
"\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
MagickMorphologyOptions, method) );
if ( kernel->next == (KernelInfo *) NULL )
rslt_image = curr_image; /* just return the resulting image */
else if ( rslt_compose == NoCompositeOp )
- { if ( IfTrue(verbose) ) {
+ { if ( IfMagickTrue(verbose) ) {
if ( this_kernel->next != (KernelInfo *) NULL )
(void) FormatLocaleFile(stderr, " (re-iterate)");
else
rslt_image = curr_image; /* return result, and re-iterate */
}
else if ( rslt_image == (Image *) NULL)
- { if ( IfTrue(verbose) )
+ { if ( IfMagickTrue(verbose) )
(void) FormatLocaleFile(stderr, " (save for compose)");
rslt_image = curr_image;
curr_image = (Image *) image; /* continue with original image */
** purely mathematical way, and only to the selected channels.
** IE: Turn off SVG composition 'alpha blending'.
*/
- if ( IfTrue(verbose) )
+ if ( IfMagickTrue(verbose) )
(void) FormatLocaleFile(stderr, " (compose \"%s\")",
CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
(void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
curr_image = DestroyImage(curr_image);
curr_image = (Image *) image; /* continue with original image */
}
- if ( IfTrue(verbose) )
+ if ( IfMagickTrue(verbose) )
(void) FormatLocaleFile(stderr, "\n");
/* loop to the next kernel in a multi-kernel list */
% the above internal function MorphologyApply().
%
% User defined settings include...
-% * Output Bias for Convolution and correlation ('-define convolve:bias=??")
-% * Kernel Scale/normalize settings ("-define convolve:scale=??")
+% * Output Bias for Convolution and correlation ("-define convolve:bias=??")
+% * Kernel Scale/normalize settings ("-define convolve:scale=??")
% This can also includes the addition of a scaled unity kernel.
-% * Show Kernel being applied ("-define showkernel=1")
+% * Show Kernel being applied ("-define showkernel=1")
+%
+% Other operators that do not want user supplied options interfering,
+% especially "convolve:bias" and "showkernel" should use MorphologyApply()
+% directly.
%
% The format of the MorphologyImage method is:
%
% Image *MorphologyImage(const Image *image,MorphologyMethod method,
% const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
%
-% Image *MorphologyImage(const Image *image, const ChannelType
-% channel,MorphologyMethod method,const ssize_t iterations,
-% KernelInfo *kernel,ExceptionInfo *exception)
-%
% A description of each parameter follows:
%
% o image: the image.
double
bias;
+ curr_kernel = (KernelInfo *) kernel;
+ bias=0.0;
+ compose = UndefinedCompositeOp; /* use default for method */
+
/* Apply Convolve/Correlate Normalization and Scaling Factors.
* This is done BEFORE the ShowKernelInfo() function is called so that
* users can see the results of the 'option:convolve:scale' option.
*/
- curr_kernel = (KernelInfo *) kernel;
- bias=0.0; /* curr_kernel->bias; should we get from kernel */
- if ( method == ConvolveMorphology || method == CorrelateMorphology )
- {
+ if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
const char
*artifact;
+ /* Get the bias value as it will be needed */
+ artifact = GetImageArtifact(image,"convolve:bias");
+ if ( artifact != (const char *) NULL) {
+ if (IfMagickFalse(IsGeometry(artifact)))
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"InvalidSetting","'%s' '%s'",
+ "convolve:bias",artifact);
+ else
+ bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
+ }
+
+ /* Scale kernel according to user wishes */
artifact = GetImageArtifact(image,"convolve:scale");
if ( artifact != (const char *)NULL ) {
- if ( curr_kernel == kernel )
- curr_kernel = CloneKernelInfo(kernel);
- if (curr_kernel == (KernelInfo *) NULL) {
- curr_kernel=DestroyKernelInfo(curr_kernel);
- return((Image *) NULL);
+ if (IfMagickFalse(IsGeometry(artifact)))
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"InvalidSetting","'%s' '%s'",
+ "convolve:scale",artifact);
+ else {
+ if ( curr_kernel == kernel )
+ curr_kernel = CloneKernelInfo(kernel);
+ if (curr_kernel == (KernelInfo *) NULL)
+ return((Image *) NULL);
+ ScaleGeometryKernelInfo(curr_kernel, artifact);
}
- ScaleGeometryKernelInfo(curr_kernel, artifact);
}
-
- artifact = GetImageArtifact(image,"convolve:bias");
- compose = UndefinedCompositeOp; /* use default for method */
- if ( artifact != (const char *) NULL)
- bias=StringToDouble(artifact, (char **) NULL);
}
/* display the (normalized) kernel via stderr */
- if ( IfTrue(IsStringTrue(GetImageArtifact(image,"showkernel")))
- || IfTrue(IsStringTrue(GetImageArtifact(image,"convolve:showkernel")))
- || IfTrue(IsStringTrue(GetImageArtifact(image,"morphology:showkernel"))) )
+ if ( IfStringTrue(GetImageArtifact(image,"showkernel"))
+ || IfStringTrue(GetImageArtifact(image,"convolve:showkernel"))
+ || IfStringTrue(GetImageArtifact(image,"morphology:showkernel")) )
ShowKernelInfo(curr_kernel);
/* Override the default handling of multi-kernel morphology results
*/
{ const char
*artifact;
- compose = UndefinedCompositeOp; /* use default for method */
+ ssize_t
+ parse;
+
artifact = GetImageArtifact(image,"morphology:compose");
- if ( artifact != (const char *) NULL)
- compose=(CompositeOperator) ParseCommandOption(MagickComposeOptions,
+ if ( artifact != (const char *) NULL) {
+ parse=ParseCommandOption(MagickComposeOptions,
MagickFalse,artifact);
+ if ( parse < 0 )
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
+ "morphology:compose",artifact);
+ else
+ compose=(CompositeOperator)parse;
+ }
}
/* Apply the Morphology */
morphology_image = MorphologyApply(image,method,iterations,
{
if ( kernel->width == 3 && kernel->height == 3 )
{ /* Rotate a 3x3 square by 45 degree angle */
- MagickRealType t = kernel->values[0];
+ double t = kernel->values[0];
kernel->values[0] = kernel->values[3];
kernel->values[3] = kernel->values[6];
kernel->values[6] = kernel->values[7];
}
else if ( kernel->width == kernel->height )
{ /* Rotate a square array of values by 90 degrees */
- { register size_t
+ { register ssize_t
i,j,x,y;
+
register MagickRealType
*k,t;
+
k=kernel->values;
- for( i=0, x=kernel->width-1; i<=x; i++, x--)
- for( j=0, y=kernel->height-1; j<y; j++, y--)
+ for( i=0, x=(ssize_t) kernel->width-1; i<=x; i++, x--)
+ for( j=0, y=(ssize_t) kernel->height-1; j<y; j++, y--)
{ t = k[i+j*kernel->width];
k[i+j*kernel->width] = k[j+x*kernel->width];
k[j+x*kernel->width] = k[x+y*kernel->width];
MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
const char *geometry)
{
- GeometryFlags
+ //GeometryFlags
+ MagickStatusType
flags;
+
GeometryInfo
args;
SetGeometryInfo(&args);
- flags = (GeometryFlags) ParseGeometry(geometry, &args);
+ flags = ParseGeometry(geometry, &args);
#if 0
/* For Debugging Geometry Input */
args.sigma = 0.0;
/* Scale/Normalize the input kernel */
- ScaleKernelInfo(kernel, args.rho, flags);
+ ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
/* Add Unity Kernel, for blending with original */
if ( (flags & SigmaValue) != 0 )
/* Normalization of Kernel */
pos_scale = 1.0;
if ( (normalize_flags&NormalizeValue) != 0 ) {
- if ( fabs(kernel->positive_range + kernel->negative_range) > MagickEpsilon )
+ if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
/* non-zero-summing kernel (generally positive) */
pos_scale = fabs(kernel->positive_range + kernel->negative_range);
else
}
/* Force kernel into a normalized zero-summing kernel */
if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
- pos_scale = ( fabs(kernel->positive_range) > MagickEpsilon )
+ pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
? kernel->positive_range : 1.0;
- neg_scale = ( fabs(kernel->negative_range) > MagickEpsilon )
+ neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
? -kernel->negative_range : 1.0;
}
else
(void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
(void) FormatLocaleFile(stderr, " \"%s",
CommandOptionToMnemonic(MagickKernelOptions, k->type) );
- if ( fabs(k->angle) > MagickEpsilon )
+ if ( fabs(k->angle) >= MagickEpsilon )
(void) FormatLocaleFile(stderr, "@%lg", k->angle);
(void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
(void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
else
(void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
- GetMagickPrecision(), k->values[i]);
+ GetMagickPrecision(), (double) k->values[i]);
(void) FormatLocaleFile(stderr,"\n");
}
}