% January 2010 %
% %
% %
-% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% Morpology is the the application of various kernels, of any size and even
-% shape, to a image in various ways (typically binary, but not always).
+% Morphology is the application of various kernels, of any size or shape, to an
+% image in various ways (typically binary, but not always).
%
% Convolution (weighted sum or average) is just one specific type of
% morphology. Just one that is very common for image bluring and sharpening
#include "MagickCore/studio.h"
#include "MagickCore/artifact.h"
#include "MagickCore/cache-view.h"
+#include "MagickCore/channel.h"
#include "MagickCore/color-private.h"
#include "MagickCore/enhance.h"
#include "MagickCore/exception.h"
/*
Other global definitions used by module.
*/
-static inline double MagickMin(const double x,const double y)
-{
- return( x < y ? x : y);
-}
-static inline double MagickMax(const double x,const double y)
-{
- return( x > y ? x : y);
-}
#define Minimize(assign,value) assign=MagickMin(assign,value)
#define Maximize(assign,value) assign=MagickMax(assign,value)
static inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
{
while (kernel->next != (KernelInfo *) NULL)
- kernel = kernel->next;
+ kernel=kernel->next;
return(kernel);
}
*kernel;
char
- token[MaxTextExtent];
+ token[MagickPathExtent];
const char
*p,
args;
kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
- if (kernel == (KernelInfo *)NULL)
+ if (kernel == (KernelInfo *) NULL)
return(kernel);
(void) ResetMagickMemory(kernel,0,sizeof(*kernel));
kernel->minimum = kernel->maximum = kernel->angle = 0.0;
kernel->negative_range = kernel->positive_range = 0.0;
kernel->type = UserDefinedKernel;
kernel->next = (KernelInfo *) NULL;
- kernel->signature = MagickSignature;
+ kernel->signature=MagickCoreSignature;
if (kernel_string == (const char *) NULL)
return(kernel);
kernel->width,kernel->height*sizeof(*kernel->values)));
if (kernel->values == (MagickRealType *) NULL)
return(DestroyKernelInfo(kernel));
- kernel->minimum = +MagickHuge;
- kernel->maximum = -MagickHuge;
+ kernel->minimum=MagickMaximumValue;
+ kernel->maximum=(-MagickMaximumValue);
kernel->negative_range = kernel->positive_range = 0.0;
for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
{
#endif
/* check that we recieved at least one real (non-nan) value! */
- if ( kernel->minimum == MagickHuge )
+ if (kernel->minimum == MagickMaximumValue)
return(DestroyKernelInfo(kernel));
if ( (flags & AreaValue) != 0 ) /* '@' symbol in kernel size */
return(kernel);
}
-static KernelInfo *ParseKernelName(const char *kernel_string)
+static KernelInfo *ParseKernelName(const char *kernel_string,
+ ExceptionInfo *exception)
{
char
- token[MaxTextExtent];
+ token[MagickPathExtent];
const char
*p,
GetMagickToken(kernel_string,&p,token);
type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
if ( type < 0 || type == UserDefinedKernel )
- return((KernelInfo *)NULL); /* not a valid named kernel */
+ return((KernelInfo *) NULL); /* not a valid named kernel */
while (((isspace((int) ((unsigned char) *p)) != 0) ||
(*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
break;
}
- kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
+ kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args, exception);
if ( kernel == (KernelInfo *) NULL )
return(kernel);
return(kernel);
}
-MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
+MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string,
+ ExceptionInfo *exception)
{
-
KernelInfo
*kernel,
*new_kernel;
char
- token[MaxTextExtent];
+ *kernel_cache,
+ token[MagickPathExtent];
const char
*p;
- size_t
- kernel_number;
-
if (kernel_string == (const char *) NULL)
return(ParseKernelArray(kernel_string));
- p = kernel_string;
- kernel = NULL;
- kernel_number = 0;
-
- while ( GetMagickToken(p,NULL,token), *token != '\0' ) {
-
- /* ignore extra or multiple ';' kernel separators */
- if ( *token != ';' ) {
-
- /* tokens starting with alpha is a Named kernel */
- if (isalpha((int) *token) != 0)
- new_kernel = ParseKernelName(p);
- else /* otherwise a user defined kernel array */
- new_kernel = ParseKernelArray(p);
-
- /* Error handling -- this is not proper error handling! */
- if ( new_kernel == (KernelInfo *) NULL ) {
- (void) FormatLocaleFile(stderr,"Failed to parse kernel number #%.20g\n",
- (double) kernel_number);
- if ( kernel != (KernelInfo *) NULL )
- kernel=DestroyKernelInfo(kernel);
+ p=kernel_string;
+ kernel_cache=(char *) NULL;
+ if (*kernel_string == '@')
+ {
+ kernel_cache=FileToString(kernel_string+1,~0UL,exception);
+ if (kernel_cache == (char *) NULL)
return((KernelInfo *) NULL);
- }
+ p=(const char *) kernel_cache;
+ }
+ kernel=NULL;
+ while (GetMagickToken(p,NULL,token), *token != '\0')
+ {
+ /* ignore extra or multiple ';' kernel separators */
+ if (*token != ';')
+ {
+ /* tokens starting with alpha is a Named kernel */
+ if (isalpha((int) ((unsigned char) *token)) != 0)
+ new_kernel=ParseKernelName(p,exception);
+ else /* otherwise a user defined kernel array */
+ new_kernel=ParseKernelArray(p);
+
+ /* Error handling -- this is not proper error handling! */
+ if (new_kernel == (KernelInfo *) NULL)
+ {
+ if (kernel != (KernelInfo *) NULL)
+ kernel=DestroyKernelInfo(kernel);
+ return((KernelInfo *) NULL);
+ }
- /* initialise or append the kernel list */
- if ( kernel == (KernelInfo *) NULL )
- kernel = new_kernel;
- else
- LastKernelInfo(kernel)->next = new_kernel;
- }
+ /* initialise or append the kernel list */
+ if (kernel == (KernelInfo *) NULL)
+ kernel=new_kernel;
+ else
+ LastKernelInfo(kernel)->next=new_kernel;
+ }
/* look for the next kernel in list */
- p = strchr(p, ';');
- if ( p == (char *) NULL )
+ p=strchr(p,';');
+ if (p == (char *) NULL)
break;
p++;
-
}
+ if (kernel_cache != (char *) NULL)
+ kernel_cache=DestroyString(kernel_cache);
return(kernel);
}
-
\f
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
*/
MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
- const GeometryInfo *args)
+ const GeometryInfo *args,ExceptionInfo *exception)
{
KernelInfo
*kernel;
switch(type) {
case UndefinedKernel: /* These should not call this function */
case UserDefinedKernel:
- assert("Should not call this function" != (char *)NULL);
+ assert("Should not call this function" != (char *) NULL);
break;
case LaplacianKernel: /* Named Descrete Convolution Kernels */
case SobelKernel: /* these are defined using other kernels */
kernel->negative_range = kernel->positive_range = 0.0;
kernel->type = type;
kernel->next = (KernelInfo *) NULL;
- kernel->signature = MagickSignature;
+ kernel->signature=MagickCoreSignature;
break;
}
ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
break;
case 10:
- kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
+ {
+ kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19",exception);
if (kernel == (KernelInfo *) NULL)
return(kernel);
break;
+ }
case 1:
case 11:
kernel=ParseKernelArray("3: 1,0,-1 2,0,-2 1,0,-1");
}
case EdgesKernel:
{
- kernel=AcquireKernelInfo("ThinSE:482");
+ kernel=AcquireKernelInfo("ThinSE:482",exception);
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
}
case CornersKernel:
{
- kernel=AcquireKernelInfo("ThinSE:87");
+ kernel=AcquireKernelInfo("ThinSE:87",exception);
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
case 0:
default:
/* set of kernels to find all end of lines */
- return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
+ return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>",exception));
case 1:
/* kernel for 4-connected line ends - no rotation */
kernel=ParseKernelArray("3: 0,0,- 0,1,1 0,0,-");
case 0:
default:
/* set of kernels to find all line junctions */
- return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
+ return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>",exception));
case 1:
/* Y Junction */
kernel=ParseKernelArray("3: 1,-,1 -,1,- -,1,-");
/* Traditional Skeleton...
** A cyclically rotated single kernel
*/
- kernel=AcquireKernelInfo("ThinSE:482");
+ kernel=AcquireKernelInfo("ThinSE:482",exception);
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
** Corners of the traditional method made more forgiving,
** but the retain the same cyclic order.
*/
- kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
+ kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;",exception);
if (kernel == (KernelInfo *) NULL)
return(kernel);
if (kernel->next == (KernelInfo *) NULL)
** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
** http://www.leptonica.com/papers/conn.pdf
*/
- kernel=AcquireKernelInfo(
- "ThinSE:41; ThinSE:42; ThinSE:43");
+ kernel=AcquireKernelInfo("ThinSE:41; ThinSE:42; ThinSE:43",
+ exception);
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
{
assert(kernel != (KernelInfo *) NULL);
- if ( kernel->next != (KernelInfo *) NULL )
+ if (kernel->next != (KernelInfo *) NULL)
kernel->next=DestroyKernelInfo(kernel->next);
kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
*last;
last = kernel;
+DisableMSCWarning(4127)
while(1) {
+RestoreMSCWarning
clone = CloneKernelInfo(last);
RotateKernelInfo(clone, angle);
- if ( SameKernelInfo(kernel, clone) == MagickTrue )
+ if ( SameKernelInfo(kernel, clone) != MagickFalse )
break;
LastKernelInfo(last)->next = clone;
last = clone;
OffsetInfo
offset;
+ register ssize_t
+ i;
+
ssize_t
y;
size_t
- width,
- changed;
+ *changes,
+ changed,
+ width;
MagickBooleanType
status;
progress;
assert(image != (Image *) NULL);
- assert(image->signature == MagickSignature);
+ assert(image->signature == MagickCoreSignature);
assert(morphology_image != (Image *) NULL);
- assert(morphology_image->signature == MagickSignature);
+ assert(morphology_image->signature == MagickCoreSignature);
assert(kernel != (KernelInfo *) NULL);
- assert(kernel->signature == MagickSignature);
+ assert(kernel->signature == MagickCoreSignature);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
status=MagickTrue;
- changed=0;
progress=0;
image_view=AcquireVirtualCacheView(image,exception);
morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
width=image->columns+kernel->width-1;
+ offset.x=0;
+ offset.y=0;
switch (method)
{
case ConvolveMorphology:
break;
}
}
+ changed=0;
+ changes=(size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
+ sizeof(*changes));
+ if (changes == (size_t *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
+ changes[i]=0;
if ((method == ConvolveMorphology) && (kernel->width == 1))
{
register ssize_t
#endif
for (x=0; x < (ssize_t) image->columns; x++)
{
+ const int
+ id = GetOpenMPThreadId();
+
register const Quantum
*restrict p;
register ssize_t
u;
+ size_t
+ count;
+
ssize_t
v;
if ((traits == UndefinedPixelTrait) ||
(morphology_traits == UndefinedPixelTrait))
continue;
- if (((morphology_traits & CopyPixelTrait) != 0) ||
- (GetPixelMask(image,p+center) != 0))
+ if (((traits & CopyPixelTrait) != 0) ||
+ (GetPixelReadMask(image,p+center) == 0))
{
SetPixelChannel(morphology_image,channel,p[center+i],q);
continue;
pixels=p;
pixel=bias;
gamma=0.0;
+ count=0;
if ((morphology_traits & BlendPixelTrait) == 0)
+ for (v=0; v < (ssize_t) kernel->height; v++)
{
- /*
- No alpha blending.
- */
- for (v=0; v < (ssize_t) kernel->height; v++)
+ for (u=0; u < (ssize_t) kernel->width; u++)
{
- for (u=0; u < (ssize_t) kernel->width; u++)
- {
- if (IsNaN(*k) != MagickFalse)
- continue;
- pixel+=(*k)*pixels[i];
- gamma+=(*k);
- k--;
- pixels+=GetPixelChannels(image);
- }
+ if (!IsNaN(*k))
+ {
+ pixel+=(*k)*pixels[i];
+ gamma+=(*k);
+ count++;
+ }
+ k--;
+ pixels+=GetPixelChannels(image);
}
- gamma=PerceptibleReciprocal(gamma);
- pixel*=gamma;
- if (fabs(pixel-p[center+i]) > MagickEpsilon)
- changed++;
- SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),
- q);
- continue;
}
- /*
- Alpha blending.
- */
- for (v=0; v < (ssize_t) kernel->width; v++)
- {
- for (u=0; u < (ssize_t) kernel->width; u++)
+ else
+ for (v=0; v < (ssize_t) kernel->height; v++)
{
- if (IsNaN(*k) != MagickFalse)
- continue;
- alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
- pixel+=(*k)*alpha*pixels[i];
- gamma+=(*k)*alpha;
- k--;
- pixels+=GetPixelChannels(image);
+ for (u=0; u < (ssize_t) kernel->width; u++)
+ {
+ if (!IsNaN(*k))
+ {
+ alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
+ pixel+=alpha*(*k)*pixels[i];
+ gamma+=alpha*(*k);
+ count++;
+ }
+ k--;
+ pixels+=GetPixelChannels(image);
+ }
}
- }
- gamma=PerceptibleReciprocal(gamma);
- pixel*=gamma;
if (fabs(pixel-p[center+i]) > MagickEpsilon)
- changed++;
- SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),q);
+ changes[id]++;
+ gamma=PerceptibleReciprocal(gamma);
+ if (count != 0)
+ gamma*=(double) kernel->height*kernel->width/count;
+ SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
+ pixel),q);
}
p+=GetPixelChannels(image);
q+=GetPixelChannels(morphology_image);
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_MorphologyImage)
+ #pragma omp critical (MagickCore_MorphologyPrimitive)
#endif
proceed=SetImageProgress(image,MorphologyTag,progress++,
image->rows);
morphology_image->type=image->type;
morphology_view=DestroyCacheView(morphology_view);
image_view=DestroyCacheView(image_view);
+ for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
+ changed+=changes[i];
+ changes=(size_t *) RelinquishMagickMemory(changes);
return(status ? (ssize_t) changed : 0);
}
/*
#endif
for (y=0; y < (ssize_t) image->rows; y++)
{
+ const int
+ id = GetOpenMPThreadId();
+
register const Quantum
*restrict p;
double
alpha,
gamma,
+ intensity,
maximum,
minimum,
pixel;
register ssize_t
u;
+ size_t
+ count;
+
ssize_t
v;
if ((traits == UndefinedPixelTrait) ||
(morphology_traits == UndefinedPixelTrait))
continue;
- if (((morphology_traits & CopyPixelTrait) != 0) ||
- (GetPixelMask(image,p+center) != 0))
+ if (((traits & CopyPixelTrait) != 0) ||
+ (GetPixelReadMask(image,p+center) == 0))
{
SetPixelChannel(morphology_image,channel,p[center+i],q);
continue;
pixels=p;
maximum=0.0;
minimum=(double) QuantumRange;
+ count=kernel->width*kernel->height;
switch (method)
{
case ConvolveMorphology: pixel=bias; break;
}
default: pixel=0; break;
}
- gamma=0.0;
+ gamma=1.0;
switch (method)
{
case ConvolveMorphology:
/*
Weighted Average of pixels using reflected kernel
- For correct working of this operation for asymetrical
- kernels, the kernel needs to be applied in its reflected form.
- That is its values needs to be reversed.
+ For correct working of this operation for asymetrical kernels,
+ the kernel needs to be applied in its reflected form. That is
+ its values needs to be reversed.
Correlation is actually the same as this but without reflecting
- the kernel, and thus 'lower-level' that Convolution. However
- as Convolution is the more common method used, and it does not
+ the kernel, and thus 'lower-level' that Convolution. However as
+ Convolution is the more common method used, and it does not
really cost us much in terms of processing to use a reflected
kernel, so it is Convolution that is implemented.
- Correlation will have its kernel reflected before calling
- this function to do a Convolve.
+ Correlation will have its kernel reflected before calling this
+ function to do a Convolve.
For more details of Correlation vs Convolution see
http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
*/
k=(&kernel->values[kernel->width*kernel->height-1]);
+ count=0;
if ((morphology_traits & BlendPixelTrait) == 0)
{
/*
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if (IsNaN(*k) == MagickFalse)
- pixel+=(*k)*pixels[i];
+ if (!IsNaN(*k))
+ {
+ pixel+=(*k)*pixels[i];
+ count++;
+ }
k--;
pixels+=GetPixelChannels(image);
}
/*
Alpha blending.
*/
- for (v=0; v < (ssize_t) kernel->width; v++)
+ gamma=0.0;
+ for (v=0; v < (ssize_t) kernel->height; v++)
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if (IsNaN(*k) == MagickFalse)
+ if (!IsNaN(*k))
{
alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
- pixel+=(*k)*alpha*pixels[i];
- gamma+=(*k)*alpha;
+ pixel+=alpha*(*k)*pixels[i];
+ gamma+=alpha*(*k);
+ count++;
}
k--;
pixels+=GetPixelChannels(image);
}
pixels+=(image->columns-1)*GetPixelChannels(image);
}
- gamma=PerceptibleReciprocal(gamma);
- pixel*=gamma;
break;
}
case ErodeMorphology:
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
+ if (!IsNaN(*k) && (*k >= 0.5))
{
if ((double) pixels[i] < pixel)
pixel=(double) pixels[i];
added to the real value, this is currently not done, due to the
nature of the boolean kernels being used.
*/
+ count=0;
k=(&kernel->values[kernel->width*kernel->height-1]);
for (v=0; v < (ssize_t) kernel->height; v++)
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if ((IsNaN(*k) == MagickFalse) && (*k > 0.5))
+ if (!IsNaN(*k) && (*k > 0.5))
{
if ((double) pixels[i] > pixel)
pixel=(double) pixels[i];
cause Thinning/Thicken to not work correctly when used against a
greyscale image.
*/
+ count=0;
k=kernel->values;
for (v=0; v < (ssize_t) kernel->height; v++)
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if (IsNaN(*k) == MagickFalse)
+ if (!IsNaN(*k))
{
if (*k > 0.7)
{
if ((double) pixels[i] > maximum)
maximum=(double) pixels[i];
}
+ count++;
}
k++;
pixels+=GetPixelChannels(image);
The kernel is not reflected for this operation.
*/
+ count=0;
k=kernel->values;
for (v=0; v < (ssize_t) kernel->height; v++)
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
+ if (!IsNaN(*k) && (*k >= 0.5))
{
- if (GetPixelIntensity(image,pixels) < minimum)
+ intensity=(double) GetPixelIntensity(image,pixels);
+ if (intensity < minimum)
{
pixel=(double) pixels[i];
- minimum=GetPixelIntensity(image,pixels);
+ minimum=intensity;
}
+ count++;
}
k++;
pixels+=GetPixelChannels(image);
The kernel is not reflected for this operation.
*/
+ count=0;
k=(&kernel->values[kernel->width*kernel->height-1]);
for (v=0; v < (ssize_t) kernel->height; v++)
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
+ if (!IsNaN(*k) && (*k >= 0.5))
{
- if (GetPixelIntensity(image,pixels) > maximum)
+ intensity=(double) GetPixelIntensity(image,pixels);
+ if (intensity > maximum)
{
pixel=(double) pixels[i];
- maximum=GetPixelIntensity(image,pixels);
+ maximum=intensity;
}
+ count++;
}
k--;
pixels+=GetPixelChannels(image);
GrayErode, but with negative kernel values, and kernel rotation
applied.
*/
+ count=0;
k=(&kernel->values[kernel->width*kernel->height-1]);
for (v=0; v < (ssize_t) kernel->height; v++)
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if (IsNaN(*k) == MagickFalse)
+ if (!IsNaN(*k))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
+ count++;
}
k--;
pixels+=GetPixelChannels(image);
break;
}
if (fabs(pixel-p[center+i]) > MagickEpsilon)
- changed++;
- SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),q);
+ changes[id]++;
+ gamma=PerceptibleReciprocal(gamma);
+ if (count != 0)
+ gamma*=(double) kernel->height*kernel->width/count;
+ SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*pixel),q);
}
p+=GetPixelChannels(image);
q+=GetPixelChannels(morphology_image);
}
- if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
+ if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
status=MagickFalse;
if (image->progress_monitor != (MagickProgressMonitor) NULL)
{
proceed;
#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_MorphologyImage)
+ #pragma omp critical (MagickCore_MorphologyPrimitive)
#endif
proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
if (proceed == MagickFalse)
}
morphology_view=DestroyCacheView(morphology_view);
image_view=DestroyCacheView(image_view);
- return(status ? (ssize_t)changed : -1);
+ for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
+ changed+=changes[i];
+ changes=(size_t *) RelinquishMagickMemory(changes);
+ return(status ? (ssize_t) changed : -1);
}
/*
y;
assert(image != (Image *) NULL);
- assert(image->signature == MagickSignature);
+ assert(image->signature == MagickCoreSignature);
assert(kernel != (KernelInfo *) NULL);
- assert(kernel->signature == MagickSignature);
+ assert(kernel->signature == MagickCoreSignature);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
status=MagickTrue;
changed=0;
progress=0;
register ssize_t
x;
+ ssize_t
+ center;
+
/*
Read virtual pixels, and authentic pixels, from the same image! We read
using virtual to get virtual pixel handling, but write back into the same
through the image iterating the distance function as we go.
*/
if (status == MagickFalse)
- break;
+ continue;
p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(size_t)
offset.y+1,exception);
q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
exception);
if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
- status=MagickFalse;
- if (status == MagickFalse)
- break;
+ {
+ status=MagickFalse;
+ continue;
+ }
+ center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
+ GetPixelChannels(image)*offset.x);
for (x=0; x < (ssize_t) image->columns; x++)
{
register ssize_t
ssize_t
v;
- traits=GetPixelChannelTraits(image,i);
+ traits=GetPixelChannelTraits(image,(PixelChannel) i);
if (traits == UndefinedPixelTrait)
continue;
- if (((traits & CopyPixelTrait) != 0) || (GetPixelMask(image,p) != 0))
+ if (((traits & CopyPixelTrait) != 0) ||
+ (GetPixelReadMask(image,p+center) == 0))
continue;
pixels=p;
- pixel=(double) q[i];
+ pixel=(double) QuantumRange;
switch (method)
{
case DistanceMorphology:
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if (IsNaN(*k) == MagickFalse)
+ if (!IsNaN(*k))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
k--;
pixels+=GetPixelChannels(image);
}
- pixels+=width*GetPixelChannels(image);
+ pixels+=(image->columns-1)*GetPixelChannels(image);
}
k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
pixels=q-offset.x*GetPixelChannels(image);
for (u=0; u < offset.x; u++)
{
- if ((IsNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
+ if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if (IsNaN(*k) == MagickFalse)
+ if (!IsNaN(*k))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
k--;
pixels+=GetPixelChannels(image);
}
- pixels+=width*GetPixelChannels(image);
+ pixels+=(image->columns-1)*GetPixelChannels(image);
}
k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
pixels=q-offset.x*GetPixelChannels(image);
for (u=0; u < offset.x; u++)
{
- if ((IsNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
+ if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
MagickBooleanType
proceed;
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_MorphologyImage)
-#endif
proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
if (proceed == MagickFalse)
status=MagickFalse;
register ssize_t
x;
+ ssize_t
+ center;
+
/*
Read virtual pixels, and authentic pixels, from the same image. We
read using virtual to get virtual pixel handling, but write back
Only the bottom half of the kernel is processed as we up the image.
*/
if (status == MagickFalse)
- break;
+ continue;
p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(size_t)
kernel->y+1,exception);
q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
exception);
if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
- status=MagickFalse;
- if (status == MagickFalse)
- break;
+ {
+ status=MagickFalse;
+ continue;
+ }
p+=(image->columns-1)*GetPixelChannels(image);
q+=(image->columns-1)*GetPixelChannels(image);
+ center=(ssize_t) (offset.x*GetPixelChannels(image));
for (x=(ssize_t) image->columns-1; x >= 0; x--)
{
register ssize_t
ssize_t
v;
- traits=GetPixelChannelTraits(image,i);
+ traits=GetPixelChannelTraits(image,(PixelChannel) i);
if (traits == UndefinedPixelTrait)
continue;
- if (((traits & CopyPixelTrait) != 0) || (GetPixelMask(image,p) != 0))
+ if (((traits & CopyPixelTrait) != 0) ||
+ (GetPixelReadMask(image,p+center) == 0))
continue;
pixels=p;
- pixel=(double) q[i];
+ pixel=(double) QuantumRange;
switch (method)
{
case DistanceMorphology:
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if (IsNaN(*k) == MagickFalse)
+ if (!IsNaN(*k))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
k--;
pixels+=GetPixelChannels(image);
}
- pixels+=width*GetPixelChannels(image);
+ pixels+=(image->columns-1)*GetPixelChannels(image);
}
k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
- pixels=q-offset.x*GetPixelChannels(image);
+ pixels=q;
for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
{
- if ((IsNaN(*k) == MagickFalse) &&
- ((x+u-offset.x) < (ssize_t) image->columns))
+ pixels+=GetPixelChannels(image);
+ if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
}
k--;
- pixels+=GetPixelChannels(image);
}
break;
}
{
for (u=0; u < (ssize_t) kernel->width; u++)
{
- if (IsNaN(*k) == MagickFalse)
+ if (!IsNaN(*k))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
k--;
pixels+=GetPixelChannels(image);
}
- pixels+=width*GetPixelChannels(image);
+ pixels+=(image->columns-1)*GetPixelChannels(image);
}
k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
- pixels=q-offset.x*GetPixelChannels(image);
+ pixels=q;
for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
{
- if ((IsNaN(*k) == MagickFalse) &&
- ((x+u-offset.x) < (ssize_t) image->columns))
+ pixels+=GetPixelChannels(image);
+ if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
{
if ((pixels[i]+(*k)) < pixel)
pixel=(double) pixels[i]+(*k);
}
k--;
- pixels+=GetPixelChannels(image);
}
break;
}
MagickBooleanType
proceed;
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
- #pragma omp critical (MagickCore_MorphologyImage)
-#endif
proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
if (proceed == MagickFalse)
status=MagickFalse;
changed; /* number pixels changed by last primitive operation */
char
- v_info[80];
+ v_info[MagickPathExtent];
assert(image != (Image *) NULL);
- assert(image->signature == MagickSignature);
+ assert(image->signature == MagickCoreSignature);
assert(kernel != (KernelInfo *) NULL);
- assert(kernel->signature == MagickSignature);
+ assert(kernel->signature == MagickCoreSignature);
assert(exception != (ExceptionInfo *) NULL);
- assert(exception->signature == MagickSignature);
+ assert(exception->signature == MagickCoreSignature);
count = 0; /* number of low-level morphology primitives performed */
if ( iterations == 0 )
- return((Image *)NULL); /* null operation - nothing to do! */
+ return((Image *) NULL); /* null operation - nothing to do! */
kernel_limit = (size_t) iterations;
if ( iterations < 0 ) /* negative interations = infinite (well alomst) */
kernel_limit = image->columns>image->rows ? image->columns : image->rows;
- verbose = IsStringTrue(GetImageArtifact(image,"verbose"));
+ verbose = IsStringTrue(GetImageArtifact(image,"debug"));
/* initialise for cleanup */
curr_image = (Image *) image;
/* Apply special methods with special requirments
** For example, single run only, or post-processing requirements
*/
- if ( special == MagickTrue )
+ if ( special != MagickFalse )
{
rslt_image=CloneImage(image,0,0,MagickTrue,exception);
if (rslt_image == (Image *) NULL)
if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
goto error_cleanup;
- changed = MorphologyPrimitiveDirect(rslt_image, method,
- kernel, exception);
+ changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
if ( IfMagickTrue(verbose) )
(void) (void) FormatLocaleFile(stderr,
goto error_cleanup;
if ( method == VoronoiMorphology ) {
- /* Preserve the alpha channel of input image - but turned off */
+ /* Preserve the alpha channel of input image - but turned it off */
(void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
exception);
(void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
/* Extra information for debugging compound operations */
if ( IfMagickTrue(verbose) ) {
if ( stage_limit > 1 )
- (void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
+ (void) FormatLocaleString(v_info,MagickPathExtent,"%s:%.20g.%.20g -> ",
CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
method_loop,(double) stage_loop);
else if ( primitive != method )
- (void) FormatLocaleString(v_info, MaxTextExtent, "%s:%.20g -> ",
+ (void) FormatLocaleString(v_info, MagickPathExtent, "%s:%.20g -> ",
CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
method_loop);
else
goto error_cleanup;
if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
goto error_cleanup;
- /* work_image->type=image->type; ??? */
}
/* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
count++;
changed = MorphologyPrimitive(curr_image, work_image, primitive,
this_kernel, bias, exception);
-
if ( IfMagickTrue(verbose) ) {
if ( kernel_loop > 1 )
(void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% MorphologyImage() applies a user supplied kernel to the image
-% according to the given mophology method.
+% MorphologyImage() applies a user supplied kernel to the image according to
+% the given mophology method.
%
% This function applies any and all user defined settings before calling
% 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=??")
+% * 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 morphology:showkernel=1")
%
% Other operators that do not want user supplied options interfering,
-% especially "convolve:bias" and "showkernel" should use MorphologyApply()
-% directly.
+% especially "convolve:bias" and "morphology:showkernel" should use
+% MorphologyApply() directly.
%
% The format of the MorphologyImage method is:
%
const MorphologyMethod method,const ssize_t iterations,
const KernelInfo *kernel,ExceptionInfo *exception)
{
- KernelInfo
- *curr_kernel;
+ const char
+ *artifact;
CompositeOperator
compose;
+ double
+ bias;
+
Image
*morphology_image;
- double
- bias;
+ KernelInfo
+ *curr_kernel;
curr_kernel = (KernelInfo *) kernel;
bias=0.0;
/* Scale kernel according to user wishes */
artifact = GetImageArtifact(image,"convolve:scale");
- if ( artifact != (const char *)NULL ) {
+ if ( artifact != (const char *) NULL ) {
if (IfMagickFalse(IsGeometry(artifact)))
(void) ThrowMagickException(exception,GetMagickModule(),
OptionWarning,"InvalidSetting","'%s' '%s'",
}
/* display the (normalized) kernel via stderr */
- if ( IfStringTrue(GetImageArtifact(image,"showkernel"))
- || IfStringTrue(GetImageArtifact(image,"convolve:showkernel"))
- || IfStringTrue(GetImageArtifact(image,"morphology:showkernel")) )
+ artifact=GetImageArtifact(image,"morphology:showkernel");
+ if (IsStringTrue(artifact) != MagickFalse)
ShowKernelInfo(curr_kernel);
/* Override the default handling of multi-kernel morphology results
* Otherwise merge resulting images using compose method given.
* Default for 'HitAndMiss' is 'Lighten'.
*/
- { const char
- *artifact;
+ {
ssize_t
parse;
* Basically all that is needed is a reversal of the kernel data!
* And a reflection of the origon
*/
- double
+ MagickRealType
t;
register MagickRealType
%
*/
MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
- const char *geometry)
+ const char *geometry)
{
MagickStatusType
flags;
MagickExport void ScaleKernelInfo(KernelInfo *kernel,
const double scaling_factor,const GeometryFlags normalize_flags)
{
- register ssize_t
- i;
-
register double
pos_scale,
neg_scale;
+ register ssize_t
+ i;
+
/* do the other kernels in a multi-kernel list first */
if ( kernel->next != (KernelInfo *) NULL)
ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
neg_scale = scaling_factor/neg_scale;
for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
- if ( ! IsNaN(kernel->values[i]) )
+ if (!IsNaN(kernel->values[i]))
kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
/* convolution output range */
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ShowKernelInfo() outputs the details of the given kernel defination to
-% standard error, generally due to a users 'showkernel' option request.
+% standard error, generally due to a users 'morphology:showkernel' option
+% request.
%
% The format of the ShowKernel method is:
%
for (i=v=0; v < k->height; v++) {
(void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
for (u=0; u < k->width; u++, i++)
- if ( IsNaN(k->values[i]) )
+ if (IsNaN(k->values[i]))
(void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
else
(void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
i;
/* do the other kernels in a multi-kernel list first */
- if ( kernel->next != (KernelInfo *) NULL)
+ if (kernel->next != (KernelInfo *) NULL)
ZeroKernelNans(kernel->next);
for (i=0; i < (kernel->width*kernel->height); i++)
- if ( IsNaN(kernel->values[i]) )
- kernel->values[i] = 0.0;
+ if (IsNaN(kernel->values[i]))
+ kernel->values[i]=0.0;
return;
}