52#include "MagickCore/studio.h"
53#include "MagickCore/artifact.h"
54#include "MagickCore/cache-view.h"
55#include "MagickCore/channel.h"
56#include "MagickCore/color-private.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/gem.h"
61#include "MagickCore/gem-private.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/linked-list.h"
65#include "MagickCore/list.h"
66#include "MagickCore/magick.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/memory-private.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/morphology.h"
71#include "MagickCore/morphology-private.h"
72#include "MagickCore/option.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/prepress.h"
75#include "MagickCore/quantize.h"
76#include "MagickCore/resource_.h"
77#include "MagickCore/registry.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/splay-tree.h"
80#include "MagickCore/statistic.h"
81#include "MagickCore/string_.h"
82#include "MagickCore/string-private.h"
83#include "MagickCore/thread-private.h"
84#include "MagickCore/token.h"
85#include "MagickCore/utility.h"
86#include "MagickCore/utility-private.h"
91#define Minimize(assign,value) assign=MagickMin(assign,value)
92#define Maximize(assign,value) assign=MagickMax(assign,value)
95static inline size_t fact(
size_t n)
98 for(f=1, l=2; l <= n; f=f*l, l++);
106 ExpandRotateKernelInfo(
KernelInfo *,
const double),
203static inline MagickBooleanType AcquireKernelValues(
KernelInfo *kernel)
208 kernel->values=(MagickRealType *) NULL;
209 if (HeapOverflowSanityCheckGetSize(kernel->width,kernel->height,&elements) != MagickFalse)
211 kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
212 elements,
sizeof(*kernel->values)));
213 return(kernel->values == (MagickRealType *) NULL ? MagickFalse : MagickTrue);
219static KernelInfo *ParseKernelArray(
const char *kernel_string)
225 token[MagickPathExtent];
246 kernel=(
KernelInfo *) AcquireMagickMemory(
sizeof(*kernel));
249 (void) memset(kernel,0,
sizeof(*kernel));
250 kernel->minimum = kernel->maximum = kernel->angle = 0.0;
251 kernel->negative_range = kernel->positive_range = 0.0;
252 kernel->type = UserDefinedKernel;
254 kernel->signature=MagickCoreSignature;
255 if (kernel_string == (
const char *) NULL)
259 end = strchr(kernel_string,
';');
260 if ( end == (
char *) NULL )
261 end = strchr(kernel_string,
'\0');
269 p = strchr(kernel_string,
':');
270 if ( p != (
char *) NULL && p < end)
273 length=MagickMin((
size_t) (p-kernel_string),
sizeof(token)-1);
274 (void) memcpy(token, kernel_string, length);
275 token[length] =
'\0';
276 SetGeometryInfo(&args);
277 flags = ParseGeometry(token, &args);
280 if ( (flags & WidthValue) == 0 )
281 args.rho = args.sigma;
282 if ( args.rho < 1.0 )
284 if ( args.sigma < 1.0 )
285 args.sigma = args.rho;
286 kernel->width = (size_t)args.rho;
287 kernel->height = (size_t)args.sigma;
290 if ( args.xi < 0.0 || args.psi < 0.0 )
291 return(DestroyKernelInfo(kernel));
292 kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
293 : (ssize_t) (kernel->width-1)/2;
294 kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
295 : (ssize_t) (kernel->height-1)/2;
296 if ( kernel->x >= (ssize_t) kernel->width ||
297 kernel->y >= (ssize_t) kernel->height )
298 return(DestroyKernelInfo(kernel));
305 p=(
const char *) kernel_string;
306 while ((isspace((
int) ((
unsigned char) *p)) != 0) || (*p ==
'\''))
308 for (i=0; p < end; i++)
310 (void) GetNextToken(p,&p,MagickPathExtent,token);
312 (void) GetNextToken(p,&p,MagickPathExtent,token);
315 kernel->width = kernel->height= (size_t) sqrt((
double) i+1.0);
316 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
317 p=(
const char *) kernel_string;
318 while ((isspace((
int) ((
unsigned char) *p)) != 0) || (*p ==
'\''))
323 if (AcquireKernelValues(kernel) == MagickFalse)
324 return(DestroyKernelInfo(kernel));
325 kernel->minimum=MagickMaximumValue;
326 kernel->maximum=(-MagickMaximumValue);
327 kernel->negative_range = kernel->positive_range = 0.0;
328 for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
330 (void) GetNextToken(p,&p,MagickPathExtent,token);
332 (void) GetNextToken(p,&p,MagickPathExtent,token);
333 if ( LocaleCompare(
"nan",token) == 0
334 || LocaleCompare(
"-",token) == 0 ) {
335 kernel->values[i] = nan;
338 kernel->values[i] = StringToDouble(token,(
char **) NULL);
339 ( kernel->values[i] < 0)
340 ? ( kernel->negative_range += kernel->values[i] )
341 : ( kernel->positive_range += kernel->values[i] );
342 Minimize(kernel->minimum, kernel->values[i]);
343 Maximize(kernel->maximum, kernel->values[i]);
348 (void) GetNextToken(p,&p,MagickPathExtent,token);
349 if ( *token !=
'\0' && *token !=
';' && *token !=
'\'' )
350 return(DestroyKernelInfo(kernel));
354 if ( i < (ssize_t) (kernel->width*kernel->height) ) {
355 Minimize(kernel->minimum, kernel->values[i]);
356 Maximize(kernel->maximum, kernel->values[i]);
357 for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
358 kernel->values[i]=0.0;
362 if ( i < (ssize_t) (kernel->width*kernel->height) )
363 return(DestroyKernelInfo(kernel));
367 if (kernel->minimum == MagickMaximumValue)
368 return(DestroyKernelInfo(kernel));
370 if ( (flags & AreaValue) != 0 )
371 ExpandRotateKernelInfo(kernel, 45.0);
372 else if ( (flags & GreaterValue) != 0 )
373 ExpandRotateKernelInfo(kernel, 90.0);
374 else if ( (flags & LessValue) != 0 )
375 ExpandMirrorKernelInfo(kernel);
380static KernelInfo *ParseKernelName(
const char *kernel_string,
384 token[MagickPathExtent] =
"";
406 (void) GetNextToken(kernel_string,&p,MagickPathExtent,token);
407 type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
408 if ( type < 0 || type == UserDefinedKernel )
411 while (((isspace((
int) ((
unsigned char) *p)) != 0) ||
412 (*p ==
',') || (*p ==
':' )) && (*p !=
'\0') && (*p !=
';'))
415 end = strchr(p,
';');
416 if ( end == (
char *) NULL )
417 end = strchr(p,
'\0');
420 length=MagickMin((
size_t) (end-p),
sizeof(token)-1);
421 (void) memcpy(token, p, length);
422 token[length] =
'\0';
423 SetGeometryInfo(&args);
424 flags = ParseGeometry(token, &args);
428 (void) FormatLocaleFile(stderr,
"Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
429 flags, args.rho, args.sigma, args.xi, args.psi );
436 if ( (flags & WidthValue) == 0 )
445 if ( (flags & HeightValue) == 0 )
449 if ( (flags & XValue) == 0 )
452 case RectangleKernel:
453 if ( (flags & WidthValue) == 0 )
454 args.rho = args.sigma;
455 if ( args.rho < 1.0 )
457 if ( args.sigma < 1.0 )
458 args.sigma = args.rho;
459 if ( (flags & XValue) == 0 )
460 args.xi = (double)(((ssize_t)args.rho-1)/2);
461 if ( (flags & YValue) == 0 )
462 args.psi = (
double)(((ssize_t)args.sigma-1)/2);
465 case ChebyshevKernel:
466 case ManhattanKernel:
467 case OctagonalKernel:
468 case EuclideanKernel:
469 if ( (flags & HeightValue) == 0 )
471 else if ( (flags & AspectValue ) != 0 )
472 args.sigma = (double) QuantumRange/(args.sigma+1);
473 else if ( (flags & PercentValue ) != 0 )
474 args.sigma *= (
double) QuantumRange/100.0;
480 kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args, exception);
486 if ( (flags & AreaValue) != 0 )
487 ExpandRotateKernelInfo(kernel, 45.0);
488 else if ( (flags & GreaterValue) != 0 )
489 ExpandRotateKernelInfo(kernel, 90.0);
490 else if ( (flags & LessValue) != 0 )
491 ExpandMirrorKernelInfo(kernel);
497MagickExport
KernelInfo *AcquireKernelInfo(
const char *kernel_string,
506 token[MagickPathExtent];
511 if (kernel_string == (
const char *) NULL)
512 return(ParseKernelArray(kernel_string));
514 kernel_cache=(
char *) NULL;
515 if (*kernel_string ==
'@')
517 kernel_cache=FileToString(kernel_string,~0UL,exception);
518 if (kernel_cache == (
char *) NULL)
520 p=(
const char *) kernel_cache;
523 while (GetNextToken(p,(
const char **) NULL,MagickPathExtent,token), *token !=
'\0')
529 if (isalpha((
int) ((
unsigned char) *token)) != 0)
530 new_kernel=ParseKernelName(p,exception);
532 new_kernel=ParseKernelArray(p);
538 kernel=DestroyKernelInfo(kernel);
546 LastKernelInfo(kernel)->next=new_kernel;
551 if (p == (
char *) NULL)
555 if (kernel_cache != (
char *) NULL)
556 kernel_cache=DestroyString(kernel_cache);
962MagickExport
KernelInfo *AcquireKernelBuiltIn(
const KernelInfoType type,
981 case UndefinedKernel:
982 case UserDefinedKernel:
983 ThrowMagickException(exception,GetMagickModule(),OptionWarning,
984 "InvalidOption",
"`%s'",
"Should not call this function");
986 case LaplacianKernel:
995 case DiagonalsKernel:
997 case LineJunctionsKernel:
999 case ConvexHullKernel:
1000 case SkeletonKernel:
1006 case GaussianKernel:
1011 case BinomialKernel:
1014 case RectangleKernel:
1021 case ChebyshevKernel:
1022 case ManhattanKernel:
1023 case OctagonalKernel:
1024 case EuclideanKernel:
1029 kernel=(
KernelInfo *) AcquireMagickMemory(
sizeof(*kernel));
1032 (void) memset(kernel,0,
sizeof(*kernel));
1033 kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1034 kernel->negative_range = kernel->positive_range = 0.0;
1035 kernel->type = type;
1037 kernel->signature=MagickCoreSignature;
1047 kernel->height = kernel->width = (size_t) 1;
1048 kernel->x = kernel->y = (ssize_t) 0;
1049 kernel->values=(MagickRealType *) MagickAssumeAligned(
1050 AcquireAlignedMemory(1,
sizeof(*kernel->values)));
1051 if (kernel->values == (MagickRealType *) NULL)
1052 return(DestroyKernelInfo(kernel));
1053 kernel->maximum = kernel->values[0] = args->rho;
1057 case GaussianKernel:
1061 sigma = fabs(args->sigma),
1062 sigma2 = fabs(args->xi),
1065 if ( args->rho >= 1.0 )
1066 kernel->width = (size_t)args->rho*2+1;
1067 else if ( (type != DoGKernel) || (sigma >= sigma2) )
1068 kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1070 kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1071 kernel->height = kernel->width;
1072 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1073 if (AcquireKernelValues(kernel) == MagickFalse)
1074 return(DestroyKernelInfo(kernel));
1083 if ( type == GaussianKernel || type == DoGKernel )
1085 if ( sigma > MagickEpsilon )
1086 { A = 1.0/(2.0*sigma*sigma);
1087 B = (double) (1.0/(Magick2PI*sigma*sigma));
1088 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1089 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1090 kernel->values[i] = exp(-((
double)(u*u+v*v))*A)*B;
1093 { (void) memset(kernel->values,0, (
size_t)
1094 kernel->width*kernel->height*
sizeof(*kernel->values));
1095 kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width] = 1.0;
1099 if ( type == DoGKernel )
1101 if ( sigma2 > MagickEpsilon )
1103 A = 1.0/(2.0*sigma*sigma);
1104 B = (double) (1.0/(Magick2PI*sigma*sigma));
1105 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1106 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1107 kernel->values[i] -= exp(-((
double)(u*u+v*v))*A)*B;
1110 kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width] -= 1.0;
1113 if ( type == LoGKernel )
1115 if ( sigma > MagickEpsilon )
1116 { A = 1.0/(2.0*sigma*sigma);
1117 B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1118 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1119 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1120 { R = ((double)(u*u+v*v))*A;
1121 kernel->values[i] = (1-R)*exp(-R)*B;
1125 { (void) memset(kernel->values,0, (
size_t)
1126 kernel->width*kernel->height*
sizeof(*kernel->values));
1127 kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width] = 1.0;
1144 CalcKernelMetaData(kernel);
1145 ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1151 sigma = fabs(args->sigma),
1154 if ( args->rho >= 1.0 )
1155 kernel->width = (size_t)args->rho*2+1;
1157 kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1159 kernel->x = (ssize_t) (kernel->width-1)/2;
1161 kernel->negative_range = kernel->positive_range = 0.0;
1162 if (AcquireKernelValues(kernel) == MagickFalse)
1163 return(DestroyKernelInfo(kernel));
1181 v = (ssize_t) (kernel->width*KernelRank-1)/2;
1182 (void) memset(kernel->values,0, (
size_t)
1183 kernel->width*kernel->height*
sizeof(*kernel->values));
1185 if ( sigma > MagickEpsilon )
1186 { sigma *= KernelRank;
1187 alpha = 1.0/(2.0*sigma*sigma);
1188 beta= (double) (1.0/(MagickSQ2PI*sigma ));
1189 for ( u=-v; u <= v; u++) {
1190 kernel->values[(u+v)/KernelRank] +=
1191 exp(-((
double)(u*u))*alpha)*beta;
1195 kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width] = 1.0;
1201 if ( sigma > MagickEpsilon )
1202 { alpha = 1.0/(2.0*sigma*sigma);
1203 beta = 1.0/(MagickSQ2PI*sigma);
1204 for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1205 kernel->values[i] = exp(-((
double)(u*u))*alpha)*beta;
1208 { (void) memset(kernel->values,0, (
size_t)
1209 kernel->width*kernel->height*
sizeof(*kernel->values));
1210 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1227 CalcKernelMetaData(kernel);
1228 ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1231 RotateKernelInfo(kernel, args->xi );
1236 sigma = fabs(args->sigma),
1239 if ( args->rho < 1.0 )
1240 kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1242 kernel->width = (size_t)args->rho;
1243 kernel->x = kernel->y = 0;
1245 kernel->negative_range = kernel->positive_range = 0.0;
1246 if (AcquireKernelValues(kernel) == MagickFalse)
1247 return(DestroyKernelInfo(kernel));
1259 if ( sigma > MagickEpsilon )
1263 v = (ssize_t) kernel->width*KernelRank;
1264 (void) memset(kernel->values,0, (
size_t)
1265 kernel->width*
sizeof(*kernel->values));
1266 sigma *= KernelRank;
1267 A = 1.0/(2.0*sigma*sigma);
1269 for ( u=0; u < v; u++) {
1270 kernel->values[u/KernelRank] +=
1271 exp(-((
double)(u*u))*A);
1274 for (i=0; i < (ssize_t) kernel->width; i++)
1275 kernel->positive_range += kernel->values[i];
1277 A = 1.0/(2.0*sigma*sigma);
1279 for ( i=0; i < (ssize_t) kernel->width; i++)
1280 kernel->positive_range +=
1281 kernel->values[i] = exp(-((
double)(i*i))*A);
1286 { (void) memset(kernel->values,0, (
size_t)
1287 kernel->width*kernel->height*
sizeof(*kernel->values));
1288 kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width] = 1.0;
1289 kernel->positive_range = 1.0;
1292 kernel->minimum = 0.0;
1293 kernel->maximum = kernel->values[0];
1294 kernel->negative_range = 0.0;
1296 ScaleKernelInfo(kernel, 1.0, NormalizeValue);
1297 RotateKernelInfo(kernel, args->xi);
1300 case BinomialKernel:
1303 max_order = (
sizeof(size_t) > 4) ? 20 : 12;
1308 if (args->rho < 1.0)
1309 kernel->width = kernel->height = 3;
1311 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1312 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1315 if ((kernel->width-1) > max_order)
1316 return(DestroyKernelInfo(kernel));
1318 order_f = fact(kernel->width-1);
1320 if (AcquireKernelValues(kernel) == MagickFalse)
1321 return(DestroyKernelInfo(kernel));
1324 for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
1326 alpha = order_f / ( fact((
size_t) v) * fact(kernel->height-(
size_t) v-1) );
1327 for ( u=0; u < (ssize_t)kernel->width; u++, i++)
1328 kernel->positive_range += kernel->values[i] = (
double)
1329 (alpha * order_f / ( fact((
size_t) u) * fact(kernel->height-(
size_t) u-1) ));
1331 kernel->minimum = 1.0;
1332 kernel->maximum = kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width];
1333 kernel->negative_range = 0.0;
1340 case LaplacianKernel:
1341 {
switch ( (
int) args->rho ) {
1344 kernel=ParseKernelArray(
"3: -1,-1,-1 -1,8,-1 -1,-1,-1");
1347 kernel=ParseKernelArray(
"3: 0,-1,0 -1,4,-1 0,-1,0");
1350 kernel=ParseKernelArray(
"3: -2,1,-2 1,4,1 -2,1,-2");
1353 kernel=ParseKernelArray(
"3: 1,-2,1 -2,4,-2 1,-2,1");
1356 kernel=ParseKernelArray(
1357 "5: -4,-1,0,-1,-4 -1,2,3,2,-1 0,3,4,3,0 -1,2,3,2,-1 -4,-1,0,-1,-4");
1360 kernel=ParseKernelArray(
1361 "7:-10,-5,-2,-1,-2,-5,-10 -5,0,3,4,3,0,-5 -2,3,6,7,6,3,-2 -1,4,7,8,7,4,-1 -2,3,6,7,6,3,-2 -5,0,3,4,3,0,-5 -10,-5,-2,-1,-2,-5,-10" );
1364 kernel=ParseKernelArray(
1365 "5: 0,0,-1,0,0 0,-1,-2,-1,0 -1,-2,16,-2,-1 0,-1,-2,-1,0 0,0,-1,0,0");
1369 kernel=ParseKernelArray(
1370 "9: 0,-1,-1,-2,-2,-2,-1,-1,0 -1,-2,-4,-5,-5,-5,-4,-2,-1 -1,-4,-5,-3,-0,-3,-5,-4,-1 -2,-5,-3,12,24,12,-3,-5,-2 -2,-5,-0,24,40,24,-0,-5,-2 -2,-5,-3,12,24,12,-3,-5,-2 -1,-4,-5,-3,-0,-3,-5,-4,-1 -1,-2,-4,-5,-5,-5,-4,-2,-1 0,-1,-1,-2,-2,-2,-1,-1,0");
1375 kernel->type = type;
1380 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1383 kernel->type = type;
1384 RotateKernelInfo(kernel, args->rho);
1389 kernel=ParseKernelArray(
"3: 0,0,0 1,-1,0 0,0,0");
1392 kernel->type = type;
1393 RotateKernelInfo(kernel, args->rho);
1398 kernel=ParseKernelArray(
"3: 1,0,-1 1,0,-1 1,0,-1");
1401 kernel->type = type;
1402 RotateKernelInfo(kernel, args->rho);
1407 kernel=ParseKernelArray(
"3: 1,1,-1 1,-2,-1 1,1,-1");
1410 kernel->type = type;
1411 RotateKernelInfo(kernel, args->rho);
1416 kernel=ParseKernelArray(
"3: 5,-3,-3 5,0,-3 5,-3,-3");
1419 kernel->type = type;
1420 RotateKernelInfo(kernel, args->rho);
1423 case FreiChenKernel:
1427 {
switch ( (
int) args->rho ) {
1430 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1433 kernel->type = type;
1434 kernel->values[3] = +(MagickRealType) MagickSQ2;
1435 kernel->values[5] = -(MagickRealType) MagickSQ2;
1436 CalcKernelMetaData(kernel);
1439 kernel=ParseKernelArray(
"3: 1,2,0 2,0,-2 0,-2,-1");
1442 kernel->type = type;
1443 kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
1444 kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
1445 CalcKernelMetaData(kernel);
1446 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1450 kernel=AcquireKernelInfo(
"FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19",exception);
1457 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1460 kernel->type = type;
1461 kernel->values[3] = +(MagickRealType) MagickSQ2;
1462 kernel->values[5] = -(MagickRealType) MagickSQ2;
1463 CalcKernelMetaData(kernel);
1464 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1467 kernel=ParseKernelArray(
"3: 1,2,1 0,0,0 1,2,1");
1470 kernel->type = type;
1471 kernel->values[1] = +(MagickRealType) MagickSQ2;
1472 kernel->values[7] = +(MagickRealType) MagickSQ2;
1473 CalcKernelMetaData(kernel);
1474 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1477 kernel=ParseKernelArray(
"3: 2,-1,0 -1,0,1 0,1,-2");
1480 kernel->type = type;
1481 kernel->values[0] = +(MagickRealType) MagickSQ2;
1482 kernel->values[8] = -(MagickRealType) MagickSQ2;
1483 CalcKernelMetaData(kernel);
1484 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1487 kernel=ParseKernelArray(
"3: 0,1,-2 -1,0,1 2,-1,0");
1490 kernel->type = type;
1491 kernel->values[2] = -(MagickRealType) MagickSQ2;
1492 kernel->values[6] = +(MagickRealType) MagickSQ2;
1493 CalcKernelMetaData(kernel);
1494 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1497 kernel=ParseKernelArray(
"3: 0,-1,0 1,0,1 0,-1,0");
1500 kernel->type = type;
1501 ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1504 kernel=ParseKernelArray(
"3: 1,0,-1 0,0,0 -1,0,1");
1507 kernel->type = type;
1508 ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1511 kernel=ParseKernelArray(
"3: 1,-2,1 -2,4,-2 -1,-2,1");
1514 kernel->type = type;
1515 ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1518 kernel=ParseKernelArray(
"3: -2,1,-2 1,4,1 -2,1,-2");
1521 kernel->type = type;
1522 ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1525 kernel=ParseKernelArray(
"3: 1,1,1 1,1,1 1,1,1");
1528 kernel->type = type;
1529 ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1532 if ( fabs(args->sigma) >= MagickEpsilon )
1534 RotateKernelInfo(kernel, args->sigma);
1535 else if ( args->rho > 30.0 || args->rho < -30.0 )
1537 RotateKernelInfo(kernel, args->rho);
1546 if (args->rho < 1.0)
1547 kernel->width = kernel->height = 3;
1549 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1550 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1552 if (AcquireKernelValues(kernel) == MagickFalse)
1553 return(DestroyKernelInfo(kernel));
1556 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1557 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1558 if ( (labs((
long) u)+labs((
long) v)) <= (long) kernel->x)
1559 kernel->positive_range += kernel->values[i] = args->sigma;
1561 kernel->values[i] = nan;
1562 kernel->minimum = kernel->maximum = args->sigma;
1566 case RectangleKernel:
1569 if ( type == SquareKernel )
1571 if (args->rho < 1.0)
1572 kernel->width = kernel->height = 3;
1574 kernel->width = kernel->height = (size_t) (2*args->rho+1);
1575 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1576 scale = args->sigma;
1580 if ( args->rho < 1.0 || args->sigma < 1.0 )
1581 return(DestroyKernelInfo(kernel));
1582 kernel->width = CastDoubleToSizeT(args->rho);
1583 kernel->height = CastDoubleToSizeT(args->sigma);
1584 if ((args->xi < 0.0) || (args->xi >= (
double) kernel->width) ||
1585 (args->psi < 0.0) || (args->psi >= (
double) kernel->height))
1586 return(DestroyKernelInfo(kernel));
1587 kernel->x = (ssize_t) args->xi;
1588 kernel->y = (ssize_t) args->psi;
1591 if (AcquireKernelValues(kernel) == MagickFalse)
1592 return(DestroyKernelInfo(kernel));
1595 u=(ssize_t) (kernel->width*kernel->height);
1596 for ( i=0; i < u; i++)
1597 kernel->values[i] = scale;
1598 kernel->minimum = kernel->maximum = scale;
1599 kernel->positive_range = scale*u;
1604 if (args->rho < 1.0)
1605 kernel->width = kernel->height = 5;
1607 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1608 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1610 if (AcquireKernelValues(kernel) == MagickFalse)
1611 return(DestroyKernelInfo(kernel));
1613 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1614 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1615 if ( (labs((
long) u)+labs((
long) v)) <=
1616 ((long)kernel->x + (
long)(kernel->x/2)) )
1617 kernel->positive_range += kernel->values[i] = args->sigma;
1619 kernel->values[i] = nan;
1620 kernel->minimum = kernel->maximum = args->sigma;
1626 limit = (ssize_t)(args->rho*args->rho);
1628 if (args->rho < 0.4)
1629 kernel->width = kernel->height = 9L, limit = 18L;
1631 kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
1632 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1634 if (AcquireKernelValues(kernel) == MagickFalse)
1635 return(DestroyKernelInfo(kernel));
1637 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1638 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1639 if ((u*u+v*v) <= limit)
1640 kernel->positive_range += kernel->values[i] = args->sigma;
1642 kernel->values[i] = nan;
1643 kernel->minimum = kernel->maximum = args->sigma;
1648 if (args->rho < 1.0)
1649 kernel->width = kernel->height = 5;
1651 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1652 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1654 if (AcquireKernelValues(kernel) == MagickFalse)
1655 return(DestroyKernelInfo(kernel));
1658 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1659 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1660 kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
1661 kernel->minimum = kernel->maximum = args->sigma;
1662 kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1667 if (args->rho < 1.0)
1668 kernel->width = kernel->height = 5;
1670 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1671 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1673 if (AcquireKernelValues(kernel) == MagickFalse)
1674 return(DestroyKernelInfo(kernel));
1677 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1678 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1679 kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
1680 kernel->minimum = kernel->maximum = args->sigma;
1681 kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1695 if (args->rho < args->sigma)
1697 kernel->width = ((size_t)args->sigma)*2+1;
1698 limit1 = (ssize_t)(args->rho*args->rho);
1699 limit2 = (ssize_t)(args->sigma*args->sigma);
1703 kernel->width = ((size_t)args->rho)*2+1;
1704 limit1 = (ssize_t)(args->sigma*args->sigma);
1705 limit2 = (ssize_t)(args->rho*args->rho);
1708 kernel->width = 7L, limit1 = 7L, limit2 = 11L;
1710 kernel->height = kernel->width;
1711 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1712 if (AcquireKernelValues(kernel) == MagickFalse)
1713 return(DestroyKernelInfo(kernel));
1716 scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
1717 for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
1718 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1719 { ssize_t radius=u*u+v*v;
1720 if (limit1 < radius && radius <= limit2)
1721 kernel->positive_range += kernel->values[i] = (double) scale;
1723 kernel->values[i] = nan;
1725 kernel->minimum = kernel->maximum = (double) scale;
1726 if ( type == PeaksKernel ) {
1728 kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width] = 1.0;
1729 kernel->positive_range = 1.0;
1730 kernel->maximum = 1.0;
1736 kernel=AcquireKernelInfo(
"ThinSE:482",exception);
1739 kernel->type = type;
1740 ExpandMirrorKernelInfo(kernel);
1745 kernel=AcquireKernelInfo(
"ThinSE:87",exception);
1748 kernel->type = type;
1749 ExpandRotateKernelInfo(kernel, 90.0);
1752 case DiagonalsKernel:
1754 switch ( (
int) args->rho ) {
1759 kernel=ParseKernelArray(
"3: 0,0,0 0,-,1 1,1,-");
1762 kernel->type = type;
1763 new_kernel=ParseKernelArray(
"3: 0,0,1 0,-,1 0,1,-");
1765 return(DestroyKernelInfo(kernel));
1766 new_kernel->type = type;
1767 LastKernelInfo(kernel)->next = new_kernel;
1768 ExpandMirrorKernelInfo(kernel);
1772 kernel=ParseKernelArray(
"3: 0,0,0 0,-,1 1,1,-");
1775 kernel=ParseKernelArray(
"3: 0,0,1 0,-,1 0,1,-");
1780 kernel->type = type;
1781 RotateKernelInfo(kernel, args->sigma);
1784 case LineEndsKernel:
1786 switch ( (
int) args->rho ) {
1790 return(AcquireKernelInfo(
"LineEnds:1>;LineEnds:2>",exception));
1793 kernel=ParseKernelArray(
"3: 0,0,- 0,1,1 0,0,-");
1797 kernel=ParseKernelArray(
"3: 0,0,0 0,1,0 0,0,1");
1801 kernel=ParseKernelArray(
"3: 0,0,0 0,1,1 0,0,0");
1805 kernel=ParseKernelArray(
"3: 0,0,0 0,1,- 0,0,-");
1810 kernel->type = type;
1811 RotateKernelInfo(kernel, args->sigma);
1814 case LineJunctionsKernel:
1816 switch ( (
int) args->rho ) {
1820 return(AcquireKernelInfo(
"LineJunctions:1@;LineJunctions:2>",exception));
1823 kernel=ParseKernelArray(
"3: 1,-,1 -,1,- -,1,-");
1827 kernel=ParseKernelArray(
"3: 1,-,- -,1,- 1,-,1");
1831 kernel=ParseKernelArray(
"3: -,-,- 1,1,1 -,1,-");
1835 kernel=ParseKernelArray(
"3: 1,-,1 -,1,- 1,-,1");
1839 kernel=ParseKernelArray(
"3: -,1,- 1,1,1 -,1,-");
1844 kernel->type = type;
1845 RotateKernelInfo(kernel, args->sigma);
1852 switch ( (
int) args->rho ) {
1855 kernel=ParseKernelArray(
"3x1:0,1,0");
1858 kernel->type = type;
1859 ExpandRotateKernelInfo(kernel, 90.0);
1862 kernel=ParseKernelArray(
"4x1:0,1,1,0");
1865 kernel->type = type;
1866 ExpandRotateKernelInfo(kernel, 90.0);
1871 new_kernel=ParseKernelArray(
"4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
1873 return(DestroyKernelInfo(kernel));
1874 new_kernel->type = type;
1875 LastKernelInfo(kernel)->next = new_kernel;
1876 new_kernel=ParseKernelArray(
"4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
1878 return(DestroyKernelInfo(kernel));
1879 new_kernel->type = type;
1880 LastKernelInfo(kernel)->next = new_kernel;
1881 new_kernel=ParseKernelArray(
"4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
1883 return(DestroyKernelInfo(kernel));
1884 new_kernel->type = type;
1885 LastKernelInfo(kernel)->next = new_kernel;
1886 new_kernel=ParseKernelArray(
"4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
1888 return(DestroyKernelInfo(kernel));
1889 new_kernel->type = type;
1890 LastKernelInfo(kernel)->next = new_kernel;
1891 new_kernel=ParseKernelArray(
"3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
1893 return(DestroyKernelInfo(kernel));
1894 new_kernel->type = type;
1895 LastKernelInfo(kernel)->next = new_kernel;
1896 new_kernel=ParseKernelArray(
"3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
1898 return(DestroyKernelInfo(kernel));
1899 new_kernel->type = type;
1900 LastKernelInfo(kernel)->next = new_kernel;
1901 new_kernel=ParseKernelArray(
"3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
1903 return(DestroyKernelInfo(kernel));
1904 new_kernel->type = type;
1905 LastKernelInfo(kernel)->next = new_kernel;
1906 new_kernel=ParseKernelArray(
"3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
1908 return(DestroyKernelInfo(kernel));
1909 new_kernel->type = type;
1910 LastKernelInfo(kernel)->next = new_kernel;
1915 case ConvexHullKernel:
1920 kernel=ParseKernelArray(
"3: 1,1,- 1,0,- 1,-,0");
1923 kernel->type = type;
1924 ExpandRotateKernelInfo(kernel, 90.0);
1926 new_kernel=ParseKernelArray(
"3: 1,1,1 1,0,- -,-,0");
1928 return(DestroyKernelInfo(kernel));
1929 new_kernel->type = type;
1930 ExpandRotateKernelInfo(new_kernel, 90.0);
1931 LastKernelInfo(kernel)->next = new_kernel;
1934 case SkeletonKernel:
1936 switch ( (
int) args->rho ) {
1942 kernel=AcquireKernelInfo(
"ThinSE:482",exception);
1945 kernel->type = type;
1946 ExpandRotateKernelInfo(kernel, 45.0);
1953 kernel=AcquireKernelInfo(
"ThinSE:482; ThinSE:87x90;",exception);
1957 return(DestroyKernelInfo(kernel));
1958 kernel->type = type;
1959 kernel->next->type = type;
1960 ExpandRotateKernelInfo(kernel, 90.0);
1968 kernel=AcquireKernelInfo(
"ThinSE:41; ThinSE:42; ThinSE:43",
1973 return(DestroyKernelInfo(kernel));
1974 if (kernel->next->next == (
KernelInfo *) NULL)
1975 return(DestroyKernelInfo(kernel));
1976 kernel->type = type;
1977 kernel->next->type = type;
1978 kernel->next->next->type = type;
1979 ExpandMirrorKernelInfo(kernel);
1995 switch ( (
int) args->rho ) {
1998 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 -,-,1");
2001 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 -,0,-");
2004 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,-,1");
2007 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,0,-");
2010 kernel=ParseKernelArray(
"3: -,0,1 0,-,1 -,0,-");
2013 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,0,1");
2016 kernel=ParseKernelArray(
"3: -,1,1 0,-,1 -,0,-");
2019 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 0,-,1");
2022 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 -,-,1");
2026 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 -,1,-");
2029 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,-,-");
2032 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 -,1,-");
2035 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 0,-,-");
2038 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 0,-,-");
2041 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 0,-,1");
2044 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,0,-");
2047 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,1,-");
2050 kernel=ParseKernelArray(
"3: 0,1,- 0,-,1 -,1,-");
2054 kernel=ParseKernelArray(
"3: -,-,1 0,-,- -,0,-");
2057 kernel=ParseKernelArray(
"3: -,1,- -,-,1 0,-,-");
2060 kernel=ParseKernelArray(
"3: -,1,1 0,-,1 0,0,-");
2064 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 0,-,1");
2069 kernel->type = type;
2070 RotateKernelInfo(kernel, args->sigma);
2076 case ChebyshevKernel:
2078 if (args->rho < 1.0)
2079 kernel->width = kernel->height = 3;
2081 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2082 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2084 if (AcquireKernelValues(kernel) == MagickFalse)
2085 return(DestroyKernelInfo(kernel));
2087 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2088 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2089 kernel->positive_range += ( kernel->values[i] =
2090 args->sigma*MagickMax(fabs((
double)u),fabs((
double)v)) );
2091 kernel->maximum = kernel->values[0];
2094 case ManhattanKernel:
2096 if (args->rho < 1.0)
2097 kernel->width = kernel->height = 3;
2099 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2100 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2102 if (AcquireKernelValues(kernel) == MagickFalse)
2103 return(DestroyKernelInfo(kernel));
2105 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2106 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2107 kernel->positive_range += ( kernel->values[i] =
2108 args->sigma*(labs((
long) u)+labs((
long) v)) );
2109 kernel->maximum = kernel->values[0];
2112 case OctagonalKernel:
2114 if (args->rho < 2.0)
2115 kernel->width = kernel->height = 5;
2117 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2118 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2120 if (AcquireKernelValues(kernel) == MagickFalse)
2121 return(DestroyKernelInfo(kernel));
2123 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2124 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2127 r1 = MagickMax(fabs((
double)u),fabs((
double)v)),
2128 r2 = floor((
double)(labs((
long)u)+labs((
long)v)+1)/1.5);
2129 kernel->positive_range += kernel->values[i] =
2130 args->sigma*MagickMax(r1,r2);
2132 kernel->maximum = kernel->values[0];
2135 case EuclideanKernel:
2137 if (args->rho < 1.0)
2138 kernel->width = kernel->height = 3;
2140 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2141 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2143 if (AcquireKernelValues(kernel) == MagickFalse)
2144 return(DestroyKernelInfo(kernel));
2146 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2147 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2148 kernel->positive_range += ( kernel->values[i] =
2149 args->sigma*sqrt((
double) (u*u+v*v)) );
2150 kernel->maximum = kernel->values[0];
2156 kernel=ParseKernelArray(
"1:1");
2159 kernel->type = UndefinedKernel;
2200 new_kernel=(
KernelInfo *) AcquireMagickMemory(
sizeof(*kernel));
2203 *new_kernel=(*kernel);
2206 if (AcquireKernelValues(new_kernel) == MagickFalse)
2207 return(DestroyKernelInfo(new_kernel));
2208 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
2209 new_kernel->values[i]=kernel->values[i];
2213 new_kernel->next = CloneKernelInfo(kernel->next);
2214 if ( new_kernel->next == (
KernelInfo *) NULL )
2215 return(DestroyKernelInfo(new_kernel));
2248 kernel->next=DestroyKernelInfo(kernel->next);
2249 kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
2250 kernel=(
KernelInfo *) RelinquishMagickMemory(kernel);
2286static void FlopKernelInfo(
KernelInfo *kernel)
2295 for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2296 for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2297 t=k[x], k[x]=k[r], k[r]=t;
2299 kernel->x = kernel->width - kernel->x - 1;
2300 angle = fmod(angle+180.0, 360.0);
2304static void ExpandMirrorKernelInfo(
KernelInfo *kernel)
2312 clone = CloneKernelInfo(last);
2315 RotateKernelInfo(clone, 180);
2316 LastKernelInfo(last)->next = clone;
2319 clone = CloneKernelInfo(last);
2322 RotateKernelInfo(clone, 90);
2323 LastKernelInfo(last)->next = clone;
2326 clone = CloneKernelInfo(last);
2329 RotateKernelInfo(clone, 180);
2330 LastKernelInfo(last)->next = clone;
2368static MagickBooleanType SameKernelInfo(
const KernelInfo *kernel1,
2375 if ( kernel1->width != kernel2->width
2376 || kernel1->height != kernel2->height
2377 || kernel1->x != kernel2->x
2378 || kernel1->y != kernel2->y )
2382 for (i=0; i < (kernel1->width*kernel1->height); i++) {
2384 if ( IsNaN(kernel1->values[i]) && !IsNaN(kernel2->values[i]) )
2386 if ( IsNaN(kernel2->values[i]) && !IsNaN(kernel1->values[i]) )
2389 if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
2396static void ExpandRotateKernelInfo(
KernelInfo *kernel,
const double angle)
2404DisableMSCWarning(4127)
2407 clone_info=CloneKernelInfo(last);
2410 RotateKernelInfo(clone_info,angle);
2411 if (SameKernelInfo(kernel,clone_info) != MagickFalse)
2413 LastKernelInfo(last)->next=clone_info;
2417 clone_info=DestroyKernelInfo(clone_info);
2457static void CalcKernelMetaData(
KernelInfo *kernel)
2462 kernel->minimum = kernel->maximum = 0.0;
2463 kernel->negative_range = kernel->positive_range = 0.0;
2464 for (i=0; i < (kernel->width*kernel->height); i++)
2466 if ( fabs(kernel->values[i]) < MagickEpsilon )
2467 kernel->values[i] = 0.0;
2468 ( kernel->values[i] < 0)
2469 ? ( kernel->negative_range += kernel->values[i] )
2470 : ( kernel->positive_range += kernel->values[i] );
2471 Minimize(kernel->minimum, kernel->values[i]);
2472 Maximize(kernel->maximum, kernel->values[i]);
2538static ssize_t MorphologyPrimitive(
const Image *image,
Image *morphology_image,
2539 const MorphologyMethod method,
const KernelInfo *kernel,
const double bias,
2542#define MorphologyTag "Morphology/Image"
2570 assert(image != (
Image *) NULL);
2571 assert(image->signature == MagickCoreSignature);
2572 assert(morphology_image != (
Image *) NULL);
2573 assert(morphology_image->signature == MagickCoreSignature);
2575 assert(kernel->signature == MagickCoreSignature);
2577 assert(exception->signature == MagickCoreSignature);
2580 image_view=AcquireVirtualCacheView(image,exception);
2581 morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
2582 width=image->columns+kernel->width-1;
2587 case ConvolveMorphology:
2588 case DilateMorphology:
2589 case DilateIntensityMorphology:
2590 case IterativeDistanceMorphology:
2595 offset.x=(ssize_t) kernel->width-kernel->x-1;
2596 offset.y=(ssize_t) kernel->height-kernel->y-1;
2599 case ErodeMorphology:
2600 case ErodeIntensityMorphology:
2601 case HitAndMissMorphology:
2602 case ThinningMorphology:
2603 case ThickenMorphology:
2614 ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2615 "InvalidOption",
"`%s'",
"not a primitive morphology method");
2620 changes=(
size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
2622 if (changes == (
size_t *) NULL)
2623 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2624 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
2626 if ((method == ConvolveMorphology) && (kernel->width == 1))
2637#if defined(MAGICKCORE_OPENMP_SUPPORT)
2638 #pragma omp parallel for schedule(static) shared(progress,status) \
2639 magick_number_threads(image,morphology_image,image->columns,1)
2641 for (x=0; x < (ssize_t) image->columns; x++)
2644 id = GetOpenMPThreadId();
2656 if (status == MagickFalse)
2658 p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
2659 kernel->height-1,exception);
2660 q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2661 morphology_image->rows,exception);
2662 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2667 center=(ssize_t) GetPixelChannels(image)*offset.y;
2668 for (r=0; r < (ssize_t) image->rows; r++)
2673 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2687 const MagickRealType
2691 *magick_restrict pixels;
2699 channel=GetPixelChannelChannel(image,i);
2700 traits=GetPixelChannelTraits(image,channel);
2701 morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2702 if ((traits == UndefinedPixelTrait) ||
2703 (morphology_traits == UndefinedPixelTrait))
2705 if ((traits & CopyPixelTrait) != 0)
2707 SetPixelChannel(morphology_image,channel,p[center+i],q);
2710 k=(&kernel->values[kernel->height-1]);
2715 if (((image->alpha_trait & BlendPixelTrait) == 0) ||
2716 ((morphology_traits & BlendPixelTrait) == 0))
2717 for (v=0; v < (ssize_t) kernel->height; v++)
2721 pixel+=(*k)*(double) pixels[i];
2725 pixels+=(ptrdiff_t) GetPixelChannels(image);
2730 for (v=0; v < (ssize_t) kernel->height; v++)
2734 alpha=(double) (QuantumScale*(
double)
2735 GetPixelAlpha(image,pixels));
2736 pixel+=alpha*(*k)*(double) pixels[i];
2741 pixels+=(ptrdiff_t) GetPixelChannels(image);
2744 if (fabs(pixel-(
double) p[center+i]) >= MagickEpsilon)
2746 gamma=PerceptibleReciprocal(gamma);
2748 gamma*=(double) kernel->height/count;
2749 SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
2752 p+=(ptrdiff_t) GetPixelChannels(image);
2753 q+=(ptrdiff_t) GetPixelChannels(morphology_image);
2755 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
2757 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2762#if defined(MAGICKCORE_OPENMP_SUPPORT)
2766 proceed=SetImageProgress(image,MorphologyTag,progress,
2768 if (proceed == MagickFalse)
2772 morphology_image->type=image->type;
2773 morphology_view=DestroyCacheView(morphology_view);
2774 image_view=DestroyCacheView(image_view);
2775 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
2776 changed+=changes[j];
2777 changes=(
size_t *) RelinquishMagickMemory(changes);
2778 return(status ? (ssize_t) (changed/GetImageChannels(image)) : 0);
2783#if defined(MAGICKCORE_OPENMP_SUPPORT)
2784 #pragma omp parallel for schedule(static) shared(progress,status) \
2785 magick_number_threads(image,morphology_image,image->rows,1)
2787 for (y=0; y < (ssize_t) image->rows; y++)
2790 id = GetOpenMPThreadId();
2804 if (status == MagickFalse)
2806 p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
2807 kernel->height,exception);
2808 q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
2810 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2815 center=(ssize_t) ((ssize_t) GetPixelChannels(image)*(ssize_t) width*
2816 offset.y+(ssize_t) GetPixelChannels(image)*offset.x);
2817 for (x=0; x < (ssize_t) image->columns; x++)
2822 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2839 const MagickRealType
2843 *magick_restrict pixels,
2844 *magick_restrict quantum_pixels;
2852 channel=GetPixelChannelChannel(image,i);
2853 traits=GetPixelChannelTraits(image,channel);
2854 morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2855 if ((traits == UndefinedPixelTrait) ||
2856 (morphology_traits == UndefinedPixelTrait))
2858 if ((traits & CopyPixelTrait) != 0)
2860 SetPixelChannel(morphology_image,channel,p[center+i],q);
2864 quantum_pixels=(
const Quantum *) NULL;
2866 minimum=(double) QuantumRange;
2869 case ConvolveMorphology:
2874 case DilateMorphology:
2875 case ErodeIntensityMorphology:
2882 pixel=(double) p[center+i];
2889 case ConvolveMorphology:
2910 k=(&kernel->values[kernel->width*kernel->height-1]);
2911 if (((image->alpha_trait & BlendPixelTrait) == 0) ||
2912 ((morphology_traits & BlendPixelTrait) == 0))
2917 for (v=0; v < (ssize_t) kernel->height; v++)
2919 for (u=0; u < (ssize_t) kernel->width; u++)
2922 pixel+=(*k)*(double) pixels[i];
2924 pixels+=(ptrdiff_t) GetPixelChannels(image);
2926 pixels+=(image->columns-1)*GetPixelChannels(image);
2934 for (v=0; v < (ssize_t) kernel->height; v++)
2936 for (u=0; u < (ssize_t) kernel->width; u++)
2940 alpha=(double) (QuantumScale*(
double)
2941 GetPixelAlpha(image,pixels));
2942 pixel+=alpha*(*k)*(double) pixels[i];
2946 pixels+=(ptrdiff_t) GetPixelChannels(image);
2948 pixels+=(image->columns-1)*GetPixelChannels(image);
2952 case ErodeMorphology:
2963 for (v=0; v < (ssize_t) kernel->height; v++)
2965 for (u=0; u < (ssize_t) kernel->width; u++)
2967 if (!IsNaN(*k) && (*k >= 0.5))
2969 if ((
double) pixels[i] < pixel)
2970 pixel=(
double) pixels[i];
2973 pixels+=(ptrdiff_t) GetPixelChannels(image);
2975 pixels+=(image->columns-1)*GetPixelChannels(image);
2979 case DilateMorphology:
2992 k=(&kernel->values[kernel->width*kernel->height-1]);
2993 for (v=0; v < (ssize_t) kernel->height; v++)
2995 for (u=0; u < (ssize_t) kernel->width; u++)
2997 if (!IsNaN(*k) && (*k > 0.5))
2999 if ((
double) pixels[i] > pixel)
3000 pixel=(
double) pixels[i];
3003 pixels+=(ptrdiff_t) GetPixelChannels(image);
3005 pixels+=(image->columns-1)*GetPixelChannels(image);
3009 case HitAndMissMorphology:
3010 case ThinningMorphology:
3011 case ThickenMorphology:
3026 for (v=0; v < (ssize_t) kernel->height; v++)
3028 for (u=0; u < (ssize_t) kernel->width; u++)
3034 if ((
double) pixels[i] < minimum)
3035 minimum=(
double) pixels[i];
3040 if ((
double) pixels[i] > maximum)
3041 maximum=(
double) pixels[i];
3045 pixels+=(ptrdiff_t) GetPixelChannels(image);
3047 pixels+=(image->columns-1)*GetPixelChannels(image);
3053 if (method == ThinningMorphology)
3054 pixel=(double) p[center+i]-minimum;
3056 if (method == ThickenMorphology)
3057 pixel=(double) p[center+i]+minimum;
3060 case ErodeIntensityMorphology:
3068 for (v=0; v < (ssize_t) kernel->height; v++)
3070 for (u=0; u < (ssize_t) kernel->width; u++)
3072 if (!IsNaN(*k) && (*k >= 0.5))
3074 intensity=(double) GetPixelIntensity(image,pixels);
3075 if (intensity < minimum)
3077 quantum_pixels=pixels;
3078 pixel=(double) pixels[i];
3083 pixels+=(ptrdiff_t) GetPixelChannels(image);
3085 pixels+=(image->columns-1)*GetPixelChannels(image);
3089 case DilateIntensityMorphology:
3096 k=(&kernel->values[kernel->width*kernel->height-1]);
3097 for (v=0; v < (ssize_t) kernel->height; v++)
3099 for (u=0; u < (ssize_t) kernel->width; u++)
3101 if (!IsNaN(*k) && (*k >= 0.5))
3103 intensity=(double) GetPixelIntensity(image,pixels);
3104 if (intensity > maximum)
3106 pixel=(double) pixels[i];
3107 quantum_pixels=pixels;
3112 pixels+=(ptrdiff_t) GetPixelChannels(image);
3114 pixels+=(image->columns-1)*GetPixelChannels(image);
3118 case IterativeDistanceMorphology:
3143 k=(&kernel->values[kernel->width*kernel->height-1]);
3144 for (v=0; v < (ssize_t) kernel->height; v++)
3146 for (u=0; u < (ssize_t) kernel->width; u++)
3150 if (((
double) pixels[i]+(*k)) < pixel)
3151 pixel=(
double) pixels[i]+(*k);
3154 pixels+=(ptrdiff_t) GetPixelChannels(image);
3156 pixels+=(image->columns-1)*GetPixelChannels(image);
3160 case UndefinedMorphology:
3164 if (quantum_pixels != (
const Quantum *) NULL)
3166 SetPixelChannel(morphology_image,channel,quantum_pixels[i],q);
3169 gamma=PerceptibleReciprocal(gamma);
3170 SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*pixel),q);
3171 if (fabs(pixel-(
double) p[center+i]) >= MagickEpsilon)
3174 p+=(ptrdiff_t) GetPixelChannels(image);
3175 q+=(ptrdiff_t) GetPixelChannels(morphology_image);
3177 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3179 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3184#if defined(MAGICKCORE_OPENMP_SUPPORT)
3188 proceed=SetImageProgress(image,MorphologyTag,progress,image->rows);
3189 if (proceed == MagickFalse)
3193 morphology_view=DestroyCacheView(morphology_view);
3194 image_view=DestroyCacheView(image_view);
3195 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
3196 changed+=changes[j];
3197 changes=(
size_t *) RelinquishMagickMemory(changes);
3198 return(status ? (ssize_t) (changed/GetImageChannels(image)) : -1);
3214static ssize_t MorphologyPrimitiveDirect(
Image *image,
3215 const MorphologyMethod method,
const KernelInfo *kernel,
3238 assert(image != (
Image *) NULL);
3239 assert(image->signature == MagickCoreSignature);
3241 assert(kernel->signature == MagickCoreSignature);
3243 assert(exception->signature == MagickCoreSignature);
3249 case DistanceMorphology:
3250 case VoronoiMorphology:
3255 offset.x=(ssize_t) kernel->width-kernel->x-1;
3256 offset.y=(ssize_t) kernel->height-kernel->y-1;
3269 image_view=AcquireVirtualCacheView(image,exception);
3270 morphology_view=AcquireAuthenticCacheView(image,exception);
3271 width=image->columns+kernel->width-1;
3272 for (y=0; y < (ssize_t) image->rows; y++)
3291 if (status == MagickFalse)
3293 p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(
size_t)
3294 offset.y+1,exception);
3295 q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
3297 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3302 for (x=0; x < (ssize_t) image->columns; x++)
3307 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3318 const MagickRealType
3322 *magick_restrict pixels;
3330 channel=GetPixelChannelChannel(image,i);
3331 traits=GetPixelChannelTraits(image,channel);
3332 if (traits == UndefinedPixelTrait)
3334 if ((traits & CopyPixelTrait) != 0)
3337 pixel=(double) QuantumRange;
3340 case DistanceMorphology:
3342 k=(&kernel->values[kernel->width*kernel->height-1]);
3343 for (v=0; v <= offset.y; v++)
3345 for (u=0; u < (ssize_t) kernel->width; u++)
3349 if (((
double) pixels[i]+(*k)) < pixel)
3350 pixel=(
double) pixels[i]+(*k);
3353 pixels+=(ptrdiff_t) GetPixelChannels(image);
3355 pixels+=(image->columns-1)*GetPixelChannels(image);
3357 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3358 pixels=q-offset.x*(ssize_t) GetPixelChannels(image);
3359 for (u=0; u < offset.x; u++)
3361 if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
3363 if (((
double) pixels[i]+(*k)) < pixel)
3364 pixel=(double) pixels[i]+(*k);
3367 pixels+=(ptrdiff_t) GetPixelChannels(image);
3371 case VoronoiMorphology:
3373 k=(&kernel->values[kernel->width*kernel->height-1]);
3374 for (v=0; v < offset.y; v++)
3376 for (u=0; u < (ssize_t) kernel->width; u++)
3380 if (((
double) pixels[i]+(*k)) < pixel)
3381 pixel=(
double) pixels[i]+(*k);
3384 pixels+=(ptrdiff_t) GetPixelChannels(image);
3386 pixels+=(image->columns-1)*GetPixelChannels(image);
3388 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3389 pixels=q-offset.x*(ssize_t) GetPixelChannels(image);
3390 for (u=0; u < offset.x; u++)
3392 if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
3394 if (((
double) pixels[i]+(*k)) < pixel)
3395 pixel=(double) pixels[i]+(*k);
3398 pixels+=(ptrdiff_t) GetPixelChannels(image);
3405 if (fabs(pixel-(
double) q[i]) > MagickEpsilon)
3407 q[i]=ClampToQuantum(pixel);
3409 p+=(ptrdiff_t) GetPixelChannels(image);
3410 q+=(ptrdiff_t) GetPixelChannels(image);
3412 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3414 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3419#if defined(MAGICKCORE_OPENMP_SUPPORT)
3423 proceed=SetImageProgress(image,MorphologyTag,progress,2*image->rows);
3424 if (proceed == MagickFalse)
3428 morphology_view=DestroyCacheView(morphology_view);
3429 image_view=DestroyCacheView(image_view);
3433 image_view=AcquireVirtualCacheView(image,exception);
3434 morphology_view=AcquireAuthenticCacheView(image,exception);
3435 for (y=(ssize_t) image->rows-1; y >= 0; y--)
3453 if (status == MagickFalse)
3455 p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(
size_t)
3456 kernel->y+1,exception);
3457 q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
3459 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3464 p+=(ptrdiff_t) (image->columns-1)*GetPixelChannels(image);
3465 q+=(ptrdiff_t) (image->columns-1)*GetPixelChannels(image);
3466 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3471 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3482 const MagickRealType
3486 *magick_restrict pixels;
3494 channel=GetPixelChannelChannel(image,i);
3495 traits=GetPixelChannelTraits(image,channel);
3496 if (traits == UndefinedPixelTrait)
3498 if ((traits & CopyPixelTrait) != 0)
3501 pixel=(double) QuantumRange;
3504 case DistanceMorphology:
3506 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3507 for (v=offset.y; v < (ssize_t) kernel->height; v++)
3509 for (u=0; u < (ssize_t) kernel->width; u++)
3513 if (((
double) pixels[i]+(*k)) < pixel)
3514 pixel=(
double) pixels[i]+(*k);
3517 pixels+=(ptrdiff_t) GetPixelChannels(image);
3519 pixels+=(image->columns-1)*GetPixelChannels(image);
3521 k=(&kernel->values[(ssize_t) kernel->width*kernel->y+kernel->x-1]);
3523 for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3525 pixels+=(ptrdiff_t) GetPixelChannels(image);
3526 if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
3528 if (((
double) pixels[i]+(*k)) < pixel)
3529 pixel=(double) pixels[i]+(*k);
3535 case VoronoiMorphology:
3537 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3538 for (v=offset.y; v < (ssize_t) kernel->height; v++)
3540 for (u=0; u < (ssize_t) kernel->width; u++)
3544 if (((
double) pixels[i]+(*k)) < pixel)
3545 pixel=(
double) pixels[i]+(*k);
3548 pixels+=(ptrdiff_t) GetPixelChannels(image);
3550 pixels+=(image->columns-1)*GetPixelChannels(image);
3552 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3554 for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3556 pixels+=(ptrdiff_t) GetPixelChannels(image);
3557 if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
3559 if (((
double) pixels[i]+(*k)) < pixel)
3560 pixel=(double) pixels[i]+(*k);
3569 if (fabs(pixel-(
double) q[i]) > MagickEpsilon)
3571 q[i]=ClampToQuantum(pixel);
3573 p-=(ptrdiff_t)GetPixelChannels(image);
3574 q-=GetPixelChannels(image);
3576 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3578 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3583#if defined(MAGICKCORE_OPENMP_SUPPORT)
3587 proceed=SetImageProgress(image,MorphologyTag,progress,2*image->rows);
3588 if (proceed == MagickFalse)
3592 morphology_view=DestroyCacheView(morphology_view);
3593 image_view=DestroyCacheView(image_view);
3594 return(status ? (ssize_t) (changed/GetImageChannels(image)) : -1);
3606MagickPrivate
Image *MorphologyApply(
const Image *image,
3607 const MorphologyMethod method,
const ssize_t iterations,
3608 const KernelInfo *kernel,
const CompositeOperator compose,
const double bias,
3652 v_info[MagickPathExtent];
3654 assert(image != (
Image *) NULL);
3655 assert(image->signature == MagickCoreSignature);
3657 assert(kernel->signature == MagickCoreSignature);
3659 assert(exception->signature == MagickCoreSignature);
3662 if ( iterations == 0 )
3663 return((
Image *) NULL);
3665 kernel_limit = (size_t) iterations;
3666 if ( iterations < 0 )
3667 kernel_limit = image->columns>image->rows ? image->columns : image->rows;
3669 verbose = IsStringTrue(GetImageArtifact(image,
"debug"));
3672 curr_image = (
Image *) image;
3673 curr_compose = image->compose;
3674 (void) curr_compose;
3675 work_image = save_image = rslt_image = (
Image *) NULL;
3685 special = MagickFalse;
3686 rslt_compose = compose;
3688 case SmoothMorphology:
3691 case OpenMorphology:
3692 case OpenIntensityMorphology:
3693 case TopHatMorphology:
3694 case CloseMorphology:
3695 case CloseIntensityMorphology:
3696 case BottomHatMorphology:
3697 case EdgeMorphology:
3700 case HitAndMissMorphology:
3701 rslt_compose = LightenCompositeOp;
3703 case ThinningMorphology:
3704 case ThickenMorphology:
3705 method_limit = kernel_limit;
3708 case DistanceMorphology:
3709 case VoronoiMorphology:
3710 special = MagickTrue;
3719 if ( special != MagickFalse )
3721 rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3722 if (rslt_image == (
Image *) NULL)
3724 if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3727 changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
3729 if (verbose != MagickFalse)
3730 (void) (
void) FormatLocaleFile(stderr,
3731 "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
3732 CommandOptionToMnemonic(MagickMorphologyOptions, method),
3733 1.0,0.0,1.0, (
double) changed);
3738 if ( method == VoronoiMorphology ) {
3740 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3742 (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
3743 MagickTrue,0,0,exception);
3744 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3751 if ( compose != UndefinedCompositeOp )
3752 rslt_compose = compose;
3753 if ( rslt_compose == UndefinedCompositeOp )
3754 rslt_compose = NoCompositeOp;
3759 case CorrelateMorphology:
3760 case CloseMorphology:
3761 case CloseIntensityMorphology:
3762 case BottomHatMorphology:
3763 case SmoothMorphology:
3764 reflected_kernel = CloneKernelInfo(kernel);
3767 RotateKernelInfo(reflected_kernel,180);
3779 while ( method_loop < method_limit && method_changed > 0 ) {
3786 rflt_kernel = reflected_kernel;
3789 while ( norm_kernel != NULL ) {
3793 while ( stage_loop < stage_limit ) {
3797 this_kernel = norm_kernel;
3800 case ErodeMorphology:
3801 case EdgeInMorphology:
3802 primitive = ErodeMorphology;
3804 case DilateMorphology:
3805 case EdgeOutMorphology:
3806 primitive = DilateMorphology;
3808 case OpenMorphology:
3809 case TopHatMorphology:
3810 primitive = ErodeMorphology;
3811 if ( stage_loop == 2 )
3812 primitive = DilateMorphology;
3814 case OpenIntensityMorphology:
3815 primitive = ErodeIntensityMorphology;
3816 if ( stage_loop == 2 )
3817 primitive = DilateIntensityMorphology;
3819 case CloseMorphology:
3820 case BottomHatMorphology:
3821 this_kernel = rflt_kernel;
3822 primitive = DilateMorphology;
3823 if ( stage_loop == 2 )
3824 primitive = ErodeMorphology;
3826 case CloseIntensityMorphology:
3827 this_kernel = rflt_kernel;
3828 primitive = DilateIntensityMorphology;
3829 if ( stage_loop == 2 )
3830 primitive = ErodeIntensityMorphology;
3832 case SmoothMorphology:
3833 switch ( stage_loop ) {
3835 primitive = ErodeMorphology;
3838 primitive = DilateMorphology;
3841 this_kernel = rflt_kernel;
3842 primitive = DilateMorphology;
3845 this_kernel = rflt_kernel;
3846 primitive = ErodeMorphology;
3850 case EdgeMorphology:
3851 primitive = DilateMorphology;
3852 if ( stage_loop == 2 ) {
3853 save_image = curr_image;
3854 curr_image = (
Image *) image;
3855 primitive = ErodeMorphology;
3858 case CorrelateMorphology:
3868 this_kernel = rflt_kernel;
3869 primitive = ConvolveMorphology;
3874 assert( this_kernel != (
KernelInfo *) NULL );
3877 if (verbose != MagickFalse) {
3878 if ( stage_limit > 1 )
3879 (void) FormatLocaleString(v_info,MagickPathExtent,
"%s:%.20g.%.20g -> ",
3880 CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
3881 method_loop,(
double) stage_loop);
3882 else if ( primitive != method )
3883 (void) FormatLocaleString(v_info, MagickPathExtent,
"%s:%.20g -> ",
3884 CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
3894 while ( kernel_loop < kernel_limit && changed > 0 ) {
3898 if ( work_image == (
Image *) NULL )
3900 work_image=CloneImage(image,0,0,MagickTrue,exception);
3901 if (work_image == (
Image *) NULL)
3903 if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
3909 changed = MorphologyPrimitive(curr_image, work_image, primitive,
3910 this_kernel, bias, exception);
3911 if (verbose != MagickFalse) {
3912 if ( kernel_loop > 1 )
3913 (void) FormatLocaleFile(stderr,
"\n");
3914 (void) (
void) FormatLocaleFile(stderr,
3915 "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
3916 v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
3917 primitive),(this_kernel == rflt_kernel ) ?
"*" :
"",
3918 (double) (method_loop+kernel_loop-1),(double) kernel_number,
3919 (double) count,(double) changed);
3923 kernel_changed = (size_t) ((ssize_t) kernel_changed+changed);
3924 method_changed = (size_t) ((ssize_t) method_changed+changed);
3927 {
Image *tmp = work_image;
3928 work_image = curr_image;
3931 if ( work_image == image )
3932 work_image = (
Image *) NULL;
3936 if (verbose != MagickFalse && kernel_changed != (
size_t)changed)
3937 (
void) FormatLocaleFile(stderr,
" Total %.20g",(
double) kernel_changed);
3938 if (verbose != MagickFalse && stage_loop < stage_limit)
3939 (void) FormatLocaleFile(stderr,
"\n");
3942 (void) FormatLocaleFile(stderr,
"--E-- image=0x%lx\n", (
unsigned long)image);
3943 (void) FormatLocaleFile(stderr,
" curr =0x%lx\n", (
unsigned long)curr_image);
3944 (void) FormatLocaleFile(stderr,
" work =0x%lx\n", (
unsigned long)work_image);
3945 (void) FormatLocaleFile(stderr,
" save =0x%lx\n", (
unsigned long)save_image);
3946 (void) FormatLocaleFile(stderr,
" union=0x%lx\n", (
unsigned long)rslt_image);
3959 case EdgeOutMorphology:
3960 case EdgeInMorphology:
3961 case TopHatMorphology:
3962 case BottomHatMorphology:
3963 if (verbose != MagickFalse)
3964 (void) FormatLocaleFile(stderr,
3965 "\n%s: Difference with original image",CommandOptionToMnemonic(
3966 MagickMorphologyOptions, method) );
3967 (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
3968 MagickTrue,0,0,exception);
3970 case EdgeMorphology:
3971 if (verbose != MagickFalse)
3972 (void) FormatLocaleFile(stderr,
3973 "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
3974 MagickMorphologyOptions, method) );
3975 (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
3976 MagickTrue,0,0,exception);
3977 save_image = DestroyImage(save_image);
3985 rslt_image = curr_image;
3986 else if ( rslt_compose == NoCompositeOp )
3987 {
if (verbose != MagickFalse) {
3988 if ( this_kernel->next != (
KernelInfo *) NULL )
3989 (void) FormatLocaleFile(stderr,
" (re-iterate)");
3991 (
void) FormatLocaleFile(stderr,
" (done)");
3993 rslt_image = curr_image;
3995 else if ( rslt_image == (
Image *) NULL)
3996 {
if (verbose != MagickFalse)
3997 (void) FormatLocaleFile(stderr,
" (save for compose)");
3998 rslt_image = curr_image;
3999 curr_image = (
Image *) image;
4009 if (verbose != MagickFalse)
4010 (void) FormatLocaleFile(stderr,
" (compose \"%s\")",
4011 CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
4012 (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
4014 curr_image = DestroyImage(curr_image);
4015 curr_image = (
Image *) image;
4017 if (verbose != MagickFalse)
4018 (void) FormatLocaleFile(stderr,
"\n");
4021 norm_kernel = norm_kernel->next;
4023 rflt_kernel = rflt_kernel->next;
4033 if ( curr_image == rslt_image )
4034 curr_image = (
Image *) NULL;
4035 if ( rslt_image != (
Image *) NULL )
4036 rslt_image = DestroyImage(rslt_image);
4038 if ( curr_image == rslt_image || curr_image == image )
4039 curr_image = (
Image *) NULL;
4040 if ( curr_image != (
Image *) NULL )
4041 curr_image = DestroyImage(curr_image);
4042 if ( work_image != (
Image *) NULL )
4043 work_image = DestroyImage(work_image);
4044 if ( save_image != (
Image *) NULL )
4045 save_image = DestroyImage(save_image);
4046 if ( reflected_kernel != (
KernelInfo *) NULL )
4047 reflected_kernel = DestroyKernelInfo(reflected_kernel);
4101MagickExport
Image *MorphologyImage(
const Image *image,
4102 const MorphologyMethod method,
const ssize_t iterations,
4120 assert(image != (
const Image *) NULL);
4121 assert(image->signature == MagickCoreSignature);
4123 assert(exception->signature == MagickCoreSignature);
4124 if (IsEventLogging() != MagickFalse)
4125 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4128 compose = UndefinedCompositeOp;
4134 if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
4136 artifact = GetImageArtifact(image,
"convolve:bias");
4137 if ( artifact != (
const char *) NULL) {
4138 if (IsGeometry(artifact) == MagickFalse)
4139 (void) ThrowMagickException(exception,GetMagickModule(),
4140 OptionWarning,
"InvalidSetting",
"'%s' '%s'",
4141 "convolve:bias",artifact);
4143 bias=StringToDoubleInterval(artifact,(
double) QuantumRange+1.0);
4147 artifact = GetImageArtifact(image,
"convolve:scale");
4148 if ( artifact != (
const char *) NULL ) {
4149 if (IsGeometry(artifact) == MagickFalse)
4150 (void) ThrowMagickException(exception,GetMagickModule(),
4151 OptionWarning,
"InvalidSetting",
"'%s' '%s'",
4152 "convolve:scale",artifact);
4154 if ( curr_kernel == kernel )
4155 curr_kernel = CloneKernelInfo(kernel);
4157 return((
Image *) NULL);
4158 ScaleGeometryKernelInfo(curr_kernel, artifact);
4164 artifact=GetImageArtifact(image,
"morphology:showKernel");
4165 if (IsStringTrue(artifact) != MagickFalse)
4166 ShowKernelInfo(curr_kernel);
4178 artifact = GetImageArtifact(image,
"morphology:compose");
4179 if ( artifact != (
const char *) NULL) {
4180 parse=ParseCommandOption(MagickComposeOptions,
4181 MagickFalse,artifact);
4183 (void) ThrowMagickException(exception,GetMagickModule(),
4184 OptionWarning,
"UnrecognizedComposeOperator",
"'%s' '%s'",
4185 "morphology:compose",artifact);
4187 compose=(CompositeOperator)parse;
4191 morphology_image = MorphologyApply(image,method,iterations,
4192 curr_kernel,compose,bias,exception);
4195 if ( curr_kernel != kernel )
4196 curr_kernel=DestroyKernelInfo(curr_kernel);
4197 return(morphology_image);
4230static void RotateKernelInfo(
KernelInfo *kernel,
double angle)
4234 RotateKernelInfo(kernel->next, angle);
4242 angle = fmod(angle, 360.0);
4246 if ( 337.5 < angle || angle <= 22.5 )
4250 switch (kernel->type) {
4252 case GaussianKernel:
4257 case LaplacianKernel:
4258 case ChebyshevKernel:
4259 case ManhattanKernel:
4260 case EuclideanKernel:
4274 if ( 135.0 < angle && angle <= 225.0 )
4276 if ( 225.0 < angle && angle <= 315.0 )
4284 if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
4286 if ( kernel->width == 3 && kernel->height == 3 )
4288 double t = kernel->values[0];
4289 kernel->values[0] = kernel->values[3];
4290 kernel->values[3] = kernel->values[6];
4291 kernel->values[6] = kernel->values[7];
4292 kernel->values[7] = kernel->values[8];
4293 kernel->values[8] = kernel->values[5];
4294 kernel->values[5] = kernel->values[2];
4295 kernel->values[2] = kernel->values[1];
4296 kernel->values[1] = t;
4298 if ( kernel->x != 1 || kernel->y != 1 ) {
4300 x = (ssize_t) kernel->x-1;
4301 y = (ssize_t) kernel->y-1;
4302 if ( x == y ) x = 0;
4303 else if ( x == 0 ) x = -y;
4304 else if ( x == -y ) y = 0;
4305 else if ( y == 0 ) y = x;
4306 kernel->x = (ssize_t) x+1;
4307 kernel->y = (ssize_t) y+1;
4309 angle = fmod(angle+315.0, 360.0);
4310 kernel->angle = fmod(kernel->angle+45.0, 360.0);
4313 perror(
"Unable to rotate non-3x3 kernel by 45 degrees");
4315 if ( 45.0 < fmod(angle, 180.0) && fmod(angle,180.0) <= 135.0 )
4317 if ( kernel->width == 1 || kernel->height == 1 )
4323 t = (ssize_t) kernel->width;
4324 kernel->width = kernel->height;
4325 kernel->height = (size_t) t;
4327 kernel->x = kernel->y;
4329 if ( kernel->width == 1 ) {
4330 angle = fmod(angle+270.0, 360.0);
4331 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4333 angle = fmod(angle+90.0, 360.0);
4334 kernel->angle = fmod(kernel->angle+270.0, 360.0);
4337 else if ( kernel->width == kernel->height )
4346 for( i=0, x=(ssize_t) kernel->width-1; i<=x; i++, x--)
4347 for( j=0, y=(ssize_t) kernel->height-1; j<y; j++, y--)
4348 { t = k[i+j*(ssize_t) kernel->width];
4349 k[i+j*(ssize_t) kernel->width] = k[j+x*(ssize_t) kernel->width];
4350 k[j+x*(ssize_t) kernel->width] = k[x+y*(ssize_t) kernel->width];
4351 k[x+y*(ssize_t) kernel->width] = k[y+i*(ssize_t) kernel->width];
4352 k[y+i*(ssize_t) kernel->width] = t;
4357 x = (ssize_t) (kernel->x*2-(ssize_t) kernel->width+1);
4358 y = (ssize_t) (kernel->y*2-(ssize_t) kernel->height+1);
4359 kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4360 kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
4362 angle = fmod(angle+270.0, 360.0);
4363 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4366 perror(
"Unable to rotate a non-square, non-linear kernel 90 degrees");
4368 if ( 135.0 < angle && angle <= 225.0 )
4386 j=(ssize_t) (kernel->width*kernel->height-1);
4387 for (i=0; i < j; i++, j--)
4388 t=k[i], k[i]=k[j], k[j]=t;
4390 kernel->x = (ssize_t) kernel->width - kernel->x - 1;
4391 kernel->y = (ssize_t) kernel->height - kernel->y - 1;
4392 angle = fmod(angle-180.0, 360.0);
4393 kernel->angle = fmod(kernel->angle+180.0, 360.0);
4437MagickExport
void ScaleGeometryKernelInfo (
KernelInfo *kernel,
4438 const char *geometry)
4446 SetGeometryInfo(&args);
4447 flags = ParseGeometry(geometry, &args);
4451 (void) FormatLocaleFile(stderr,
"Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4452 flags, args.rho, args.sigma, args.xi, args.psi );
4455 if ( (flags & PercentValue) != 0 )
4456 args.rho *= 0.01, args.sigma *= 0.01;
4458 if ( (flags & RhoValue) == 0 )
4460 if ( (flags & SigmaValue) == 0 )
4464 ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
4467 if ( (flags & SigmaValue) != 0 )
4468 UnityAddKernelInfo(kernel, args.sigma);
4543MagickExport
void ScaleKernelInfo(
KernelInfo *kernel,
4544 const double scaling_factor,
const GeometryFlags normalize_flags)
4555 ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
4559 if ( (normalize_flags&NormalizeValue) != 0 ) {
4560 if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4562 pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4565 pos_scale = kernel->positive_range;
4568 if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4569 pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4570 ? kernel->positive_range : 1.0;
4571 neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4572 ? -kernel->negative_range : 1.0;
4575 neg_scale = pos_scale;
4578 pos_scale = scaling_factor/pos_scale;
4579 neg_scale = scaling_factor/neg_scale;
4581 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4582 if (!IsNaN(kernel->values[i]))
4583 kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4586 kernel->positive_range *= pos_scale;
4587 kernel->negative_range *= neg_scale;
4589 kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4590 kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4593 if ( scaling_factor < MagickEpsilon ) {
4595 t = kernel->positive_range;
4596 kernel->positive_range = kernel->negative_range;
4597 kernel->negative_range = t;
4598 t = kernel->maximum;
4599 kernel->maximum = kernel->minimum;
4600 kernel->minimum = 1;
4630MagickPrivate
void ShowKernelInfo(
const KernelInfo *kernel)
4638 for (c=0, k=kernel; k != (
KernelInfo *) NULL; c++, k=k->next ) {
4640 (void) FormatLocaleFile(stderr,
"Kernel");
4642 (
void) FormatLocaleFile(stderr,
" #%lu", (
unsigned long) c );
4643 (void) FormatLocaleFile(stderr,
" \"%s",
4644 CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4645 if ( fabs(k->angle) >= MagickEpsilon )
4646 (
void) FormatLocaleFile(stderr,
"@%lg", k->angle);
4647 (void) FormatLocaleFile(stderr,
"\" of size %lux%lu%+ld%+ld",(
unsigned long)
4648 k->width,(
unsigned long) k->height,(
long) k->x,(long) k->y);
4649 (void) FormatLocaleFile(stderr,
4650 " with values from %.*lg to %.*lg\n",
4651 GetMagickPrecision(), k->minimum,
4652 GetMagickPrecision(), k->maximum);
4653 (void) FormatLocaleFile(stderr,
"Forming a output range from %.*lg to %.*lg",
4654 GetMagickPrecision(), k->negative_range,
4655 GetMagickPrecision(), k->positive_range);
4656 if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
4657 (
void) FormatLocaleFile(stderr,
" (Zero-Summing)\n");
4658 else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
4659 (void) FormatLocaleFile(stderr,
" (Normalized)\n");
4661 (
void) FormatLocaleFile(stderr,
" (Sum %.*lg)\n",
4662 GetMagickPrecision(), k->positive_range+k->negative_range);
4663 for (i=v=0; v < k->height; v++) {
4664 (void) FormatLocaleFile(stderr,
"%2lu:", (
unsigned long) v );
4665 for (u=0; u < k->width; u++, i++)
4666 if (IsNaN(k->values[i]))
4667 (
void) FormatLocaleFile(stderr,
" %*s", GetMagickPrecision()+3,
"nan");
4669 (
void) FormatLocaleFile(stderr,
" %*.*lg", GetMagickPrecision()+3,
4670 GetMagickPrecision(), (
double) k->values[i]);
4671 (void) FormatLocaleFile(stderr,
"\n");
4709MagickExport
void UnityAddKernelInfo(
KernelInfo *kernel,
4714 UnityAddKernelInfo(kernel->next, scale);
4717 kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width] += scale;
4718 CalcKernelMetaData(kernel);
4748MagickPrivate
void ZeroKernelNans(
KernelInfo *kernel)
4755 ZeroKernelNans(kernel->next);
4757 for (i=0; i < (kernel->width*kernel->height); i++)
4758 if (IsNaN(kernel->values[i]))
4759 kernel->values[i]=0.0;