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)
96 static inline size_t fact(
size_t n)
99 for(f=1, l=2; l <= n; f=f*l, l++);
103 #define fact(n) ((size_t)tgamma((double)n+1))
105 #define fact(n) ((size_t)lgamma((double)n+1))
113 ExpandRotateKernelInfo(
KernelInfo *,
const double),
213 static KernelInfo *ParseKernelArray(
const char *kernel_string)
219 token[MagickPathExtent];
229 nan = sqrt((
double)-1.0);
237 kernel=(
KernelInfo *) AcquireMagickMemory(
sizeof(*kernel));
240 (void) memset(kernel,0,
sizeof(*kernel));
241 kernel->minimum = kernel->maximum = kernel->angle = 0.0;
242 kernel->negative_range = kernel->positive_range = 0.0;
243 kernel->type = UserDefinedKernel;
245 kernel->signature=MagickCoreSignature;
246 if (kernel_string == (
const char *) NULL)
250 end = strchr(kernel_string,
';');
251 if ( end == (
char *) NULL )
252 end = strchr(kernel_string,
'\0');
260 p = strchr(kernel_string,
':');
261 if ( p != (
char *) NULL && p < end)
264 (void) memcpy(token, kernel_string, (
size_t) (p-kernel_string));
265 token[p-kernel_string] =
'\0';
266 SetGeometryInfo(&args);
267 flags = ParseGeometry(token, &args);
270 if ( (flags & WidthValue) == 0 )
271 args.rho = args.sigma;
272 if ( args.rho < 1.0 )
274 if ( args.sigma < 1.0 )
275 args.sigma = args.rho;
276 kernel->width = (size_t)args.rho;
277 kernel->height = (
size_t)args.sigma;
280 if ( args.xi < 0.0 || args.psi < 0.0 )
281 return(DestroyKernelInfo(kernel));
282 kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
283 : (ssize_t) (kernel->width-1)/2;
284 kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
285 : (ssize_t) (kernel->height-1)/2;
286 if ( kernel->x >= (ssize_t) kernel->width ||
287 kernel->y >= (ssize_t) kernel->height )
288 return(DestroyKernelInfo(kernel));
295 p=(
const char *) kernel_string;
296 while ((isspace((
int) ((
unsigned char) *p)) != 0) || (*p ==
'\''))
298 for (i=0; p < end; i++)
300 (void) GetNextToken(p,&p,MagickPathExtent,token);
302 (void) GetNextToken(p,&p,MagickPathExtent,token);
305 kernel->width = kernel->height= (size_t) sqrt((
double) i+1.0);
306 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
307 p=(
const char *) kernel_string;
308 while ((isspace((
int) ((
unsigned char) *p)) != 0) || (*p ==
'\''))
313 kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
314 kernel->width,kernel->height*
sizeof(*kernel->values)));
315 if (kernel->values == (MagickRealType *) NULL)
316 return(DestroyKernelInfo(kernel));
317 kernel->minimum=MagickMaximumValue;
318 kernel->maximum=(-MagickMaximumValue);
319 kernel->negative_range = kernel->positive_range = 0.0;
320 for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
322 (void) GetNextToken(p,&p,MagickPathExtent,token);
324 (void) GetNextToken(p,&p,MagickPathExtent,token);
325 if ( LocaleCompare(
"nan",token) == 0
326 || LocaleCompare(
"-",token) == 0 ) {
327 kernel->values[i] = nan;
330 kernel->values[i] = StringToDouble(token,(
char **) NULL);
331 ( kernel->values[i] < 0)
332 ? ( kernel->negative_range += kernel->values[i] )
333 : ( kernel->positive_range += kernel->values[i] );
334 Minimize(kernel->minimum, kernel->values[i]);
335 Maximize(kernel->maximum, kernel->values[i]);
340 (void) GetNextToken(p,&p,MagickPathExtent,token);
341 if ( *token !=
'\0' && *token !=
';' && *token !=
'\'' )
342 return(DestroyKernelInfo(kernel));
346 if ( i < (ssize_t) (kernel->width*kernel->height) ) {
347 Minimize(kernel->minimum, kernel->values[i]);
348 Maximize(kernel->maximum, kernel->values[i]);
349 for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
350 kernel->values[i]=0.0;
354 if ( i < (ssize_t) (kernel->width*kernel->height) )
355 return(DestroyKernelInfo(kernel));
359 if (kernel->minimum == MagickMaximumValue)
360 return(DestroyKernelInfo(kernel));
362 if ( (flags & AreaValue) != 0 )
363 ExpandRotateKernelInfo(kernel, 45.0);
364 else if ( (flags & GreaterValue) != 0 )
365 ExpandRotateKernelInfo(kernel, 90.0);
366 else if ( (flags & LessValue) != 0 )
367 ExpandMirrorKernelInfo(kernel);
372 static KernelInfo *ParseKernelName(
const char *kernel_string,
376 token[MagickPathExtent];
395 (void) GetNextToken(kernel_string,&p,MagickPathExtent,token);
396 type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
397 if ( type < 0 || type == UserDefinedKernel )
400 while (((isspace((
int) ((
unsigned char) *p)) != 0) ||
401 (*p ==
',') || (*p ==
':' )) && (*p !=
'\0') && (*p !=
';'))
404 end = strchr(p,
';');
405 if ( end == (
char *) NULL )
406 end = strchr(p,
'\0');
409 (void) memcpy(token, p, (
size_t) (end-p));
411 SetGeometryInfo(&args);
412 flags = ParseGeometry(token, &args);
416 (void) FormatLocaleFile(stderr,
"Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
417 flags, args.rho, args.sigma, args.xi, args.psi );
424 if ( (flags & WidthValue) == 0 )
433 if ( (flags & HeightValue) == 0 )
437 if ( (flags & XValue) == 0 )
440 case RectangleKernel:
441 if ( (flags & WidthValue) == 0 )
442 args.rho = args.sigma;
443 if ( args.rho < 1.0 )
445 if ( args.sigma < 1.0 )
446 args.sigma = args.rho;
447 if ( (flags & XValue) == 0 )
448 args.xi = (
double)(((ssize_t)args.rho-1)/2);
449 if ( (flags & YValue) == 0 )
450 args.psi = (
double)(((ssize_t)args.sigma-1)/2);
453 case ChebyshevKernel:
454 case ManhattanKernel:
455 case OctagonalKernel:
456 case EuclideanKernel:
457 if ( (flags & HeightValue) == 0 )
459 else if ( (flags & AspectValue ) != 0 )
460 args.sigma = QuantumRange/(args.sigma+1);
461 else if ( (flags & PercentValue ) != 0 )
462 args.sigma *= QuantumRange/100.0;
468 kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args, exception);
474 if ( (flags & AreaValue) != 0 )
475 ExpandRotateKernelInfo(kernel, 45.0);
476 else if ( (flags & GreaterValue) != 0 )
477 ExpandRotateKernelInfo(kernel, 90.0);
478 else if ( (flags & LessValue) != 0 )
479 ExpandMirrorKernelInfo(kernel);
485 MagickExport
KernelInfo *AcquireKernelInfo(
const char *kernel_string,
494 token[MagickPathExtent];
499 if (kernel_string == (
const char *) NULL)
500 return(ParseKernelArray(kernel_string));
502 kernel_cache=(
char *) NULL;
503 if (*kernel_string ==
'@')
505 kernel_cache=FileToString(kernel_string+1,~0UL,exception);
506 if (kernel_cache == (
char *) NULL)
508 p=(
const char *) kernel_cache;
511 while (GetNextToken(p,(
const char **) NULL,MagickPathExtent,token), *token !=
'\0')
517 if (isalpha((
int) ((
unsigned char) *token)) != 0)
518 new_kernel=ParseKernelName(p,exception);
520 new_kernel=ParseKernelArray(p);
526 kernel=DestroyKernelInfo(kernel);
534 LastKernelInfo(kernel)->next=new_kernel;
539 if (p == (
char *) NULL)
543 if (kernel_cache != (
char *) NULL)
544 kernel_cache=DestroyString(kernel_cache);
950 MagickExport
KernelInfo *AcquireKernelBuiltIn(
const KernelInfoType type,
964 nan = sqrt((
double)-1.0);
969 case UndefinedKernel:
970 case UserDefinedKernel:
971 ThrowMagickException(exception,GetMagickModule(),OptionWarning,
972 "InvalidOption",
"`%s'",
"Should not call this function");
974 case LaplacianKernel:
983 case DiagonalsKernel:
985 case LineJunctionsKernel:
987 case ConvexHullKernel:
1002 case RectangleKernel:
1009 case ChebyshevKernel:
1010 case ManhattanKernel:
1011 case OctangonalKernel:
1012 case EuclideanKernel:
1017 kernel=(
KernelInfo *) AcquireMagickMemory(
sizeof(*kernel));
1020 (void) memset(kernel,0,
sizeof(*kernel));
1021 kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1022 kernel->negative_range = kernel->positive_range = 0.0;
1023 kernel->type = type;
1025 kernel->signature=MagickCoreSignature;
1035 kernel->height = kernel->width = (size_t) 1;
1036 kernel->x = kernel->y = (ssize_t) 0;
1037 kernel->values=(MagickRealType *) MagickAssumeAligned(
1038 AcquireAlignedMemory(1,
sizeof(*kernel->values)));
1039 if (kernel->values == (MagickRealType *) NULL)
1040 return(DestroyKernelInfo(kernel));
1041 kernel->maximum = kernel->values[0] = args->rho;
1045 case GaussianKernel:
1049 sigma = fabs(args->sigma),
1050 sigma2 = fabs(args->xi),
1053 if ( args->rho >= 1.0 )
1054 kernel->width = (size_t)args->rho*2+1;
1055 else if ( (type != DoGKernel) || (sigma >= sigma2) )
1056 kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1058 kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1059 kernel->height = kernel->width;
1060 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1061 kernel->values=(MagickRealType *) MagickAssumeAligned(
1062 AcquireAlignedMemory(kernel->width,kernel->height*
1063 sizeof(*kernel->values)));
1064 if (kernel->values == (MagickRealType *) NULL)
1065 return(DestroyKernelInfo(kernel));
1074 if ( type == GaussianKernel || type == DoGKernel )
1076 if ( sigma > MagickEpsilon )
1077 { A = 1.0/(2.0*sigma*sigma);
1078 B = (double) (1.0/(Magick2PI*sigma*sigma));
1079 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1080 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1081 kernel->values[i] = exp(-((
double)(u*u+v*v))*A)*B;
1084 { (void) memset(kernel->values,0, (
size_t)
1085 kernel->width*kernel->height*
sizeof(*kernel->values));
1086 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1090 if ( type == DoGKernel )
1092 if ( sigma2 > MagickEpsilon )
1094 A = 1.0/(2.0*sigma*sigma);
1095 B = (double) (1.0/(Magick2PI*sigma*sigma));
1096 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1097 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1098 kernel->values[i] -= exp(-((
double)(u*u+v*v))*A)*B;
1101 kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1104 if ( type == LoGKernel )
1106 if ( sigma > MagickEpsilon )
1107 { A = 1.0/(2.0*sigma*sigma);
1108 B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1109 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1110 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1111 { R = ((double)(u*u+v*v))*A;
1112 kernel->values[i] = (1-R)*exp(-R)*B;
1116 { (void) memset(kernel->values,0, (
size_t)
1117 kernel->width*kernel->height*
sizeof(*kernel->values));
1118 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1135 CalcKernelMetaData(kernel);
1136 ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1142 sigma = fabs(args->sigma),
1145 if ( args->rho >= 1.0 )
1146 kernel->width = (size_t)args->rho*2+1;
1148 kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1150 kernel->x = (ssize_t) (kernel->width-1)/2;
1152 kernel->negative_range = kernel->positive_range = 0.0;
1153 kernel->values=(MagickRealType *) MagickAssumeAligned(
1154 AcquireAlignedMemory(kernel->width,kernel->height*
1155 sizeof(*kernel->values)));
1156 if (kernel->values == (MagickRealType *) NULL)
1157 return(DestroyKernelInfo(kernel));
1160 #define KernelRank 3
1175 v = (ssize_t) (kernel->width*KernelRank-1)/2;
1176 (void) memset(kernel->values,0, (
size_t)
1177 kernel->width*kernel->height*
sizeof(*kernel->values));
1179 if ( sigma > MagickEpsilon )
1180 { sigma *= KernelRank;
1181 alpha = 1.0/(2.0*sigma*sigma);
1182 beta= (double) (1.0/(MagickSQ2PI*sigma ));
1183 for ( u=-v; u <= v; u++) {
1184 kernel->values[(u+v)/KernelRank] +=
1185 exp(-((
double)(u*u))*alpha)*beta;
1189 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1195 if ( sigma > MagickEpsilon )
1196 { alpha = 1.0/(2.0*sigma*sigma);
1197 beta = 1.0/(MagickSQ2PI*sigma);
1198 for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1199 kernel->values[i] = exp(-((
double)(u*u))*alpha)*beta;
1202 { (void) memset(kernel->values,0, (
size_t)
1203 kernel->width*kernel->height*
sizeof(*kernel->values));
1204 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1221 CalcKernelMetaData(kernel);
1222 ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1225 RotateKernelInfo(kernel, args->xi );
1230 sigma = fabs(args->sigma),
1233 if ( args->rho < 1.0 )
1234 kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1236 kernel->width = (size_t)args->rho;
1237 kernel->x = kernel->y = 0;
1239 kernel->negative_range = kernel->positive_range = 0.0;
1240 kernel->values=(MagickRealType *) MagickAssumeAligned(
1241 AcquireAlignedMemory(kernel->width,kernel->height*
1242 sizeof(*kernel->values)));
1243 if (kernel->values == (MagickRealType *) NULL)
1244 return(DestroyKernelInfo(kernel));
1256 if ( sigma > MagickEpsilon )
1259 #define KernelRank 3
1260 v = (ssize_t) kernel->width*KernelRank;
1261 (
void) memset(kernel->values,0, (
size_t)
1262 kernel->width*
sizeof(*kernel->values));
1263 sigma *= KernelRank;
1264 A = 1.0/(2.0*sigma*sigma);
1266 for ( u=0; u < v; u++) {
1267 kernel->values[u/KernelRank] +=
1268 exp(-((
double)(u*u))*A);
1271 for (i=0; i < (ssize_t) kernel->width; i++)
1272 kernel->positive_range += kernel->values[i];
1274 A = 1.0/(2.0*sigma*sigma);
1276 for ( i=0; i < (ssize_t) kernel->width; i++)
1277 kernel->positive_range +=
1278 kernel->values[i] = exp(-((
double)(i*i))*A);
1283 { (void) memset(kernel->values,0, (
size_t)
1284 kernel->width*kernel->height*
sizeof(*kernel->values));
1285 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1286 kernel->positive_range = 1.0;
1289 kernel->minimum = 0.0;
1290 kernel->maximum = kernel->values[0];
1291 kernel->negative_range = 0.0;
1293 ScaleKernelInfo(kernel, 1.0, NormalizeValue);
1294 RotateKernelInfo(kernel, args->xi);
1297 case BinomialKernel:
1302 if (args->rho < 1.0)
1303 kernel->width = kernel->height = 3;
1305 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1306 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1308 order_f = fact(kernel->width-1);
1310 kernel->values=(MagickRealType *) MagickAssumeAligned(
1311 AcquireAlignedMemory(kernel->width,kernel->height*
1312 sizeof(*kernel->values)));
1313 if (kernel->values == (MagickRealType *) NULL)
1314 return(DestroyKernelInfo(kernel));
1317 for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
1319 alpha = order_f / ( fact((
size_t) v) * fact(kernel->height-v-1) );
1320 for ( u=0; u < (ssize_t)kernel->width; u++, i++)
1321 kernel->positive_range += kernel->values[i] = (double)
1322 (alpha * order_f / ( fact((
size_t) u) * fact(kernel->height-u-1) ));
1324 kernel->minimum = 1.0;
1325 kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
1326 kernel->negative_range = 0.0;
1333 case LaplacianKernel:
1334 {
switch ( (
int) args->rho ) {
1337 kernel=ParseKernelArray(
"3: -1,-1,-1 -1,8,-1 -1,-1,-1");
1340 kernel=ParseKernelArray(
"3: 0,-1,0 -1,4,-1 0,-1,0");
1343 kernel=ParseKernelArray(
"3: -2,1,-2 1,4,1 -2,1,-2");
1346 kernel=ParseKernelArray(
"3: 1,-2,1 -2,4,-2 1,-2,1");
1349 kernel=ParseKernelArray(
1350 "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");
1353 kernel=ParseKernelArray(
1354 "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" );
1357 kernel=ParseKernelArray(
1358 "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");
1362 kernel=ParseKernelArray(
1363 "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");
1368 kernel->type = type;
1373 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1376 kernel->type = type;
1377 RotateKernelInfo(kernel, args->rho);
1382 kernel=ParseKernelArray(
"3: 0,0,0 1,-1,0 0,0,0");
1385 kernel->type = type;
1386 RotateKernelInfo(kernel, args->rho);
1391 kernel=ParseKernelArray(
"3: 1,0,-1 1,0,-1 1,0,-1");
1394 kernel->type = type;
1395 RotateKernelInfo(kernel, args->rho);
1400 kernel=ParseKernelArray(
"3: 1,1,-1 1,-2,-1 1,1,-1");
1403 kernel->type = type;
1404 RotateKernelInfo(kernel, args->rho);
1409 kernel=ParseKernelArray(
"3: 5,-3,-3 5,0,-3 5,-3,-3");
1412 kernel->type = type;
1413 RotateKernelInfo(kernel, args->rho);
1416 case FreiChenKernel:
1420 {
switch ( (
int) args->rho ) {
1423 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1426 kernel->type = type;
1427 kernel->values[3] = +(MagickRealType) MagickSQ2;
1428 kernel->values[5] = -(MagickRealType) MagickSQ2;
1429 CalcKernelMetaData(kernel);
1432 kernel=ParseKernelArray(
"3: 1,2,0 2,0,-2 0,-2,-1");
1435 kernel->type = type;
1436 kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
1437 kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
1438 CalcKernelMetaData(kernel);
1439 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1443 kernel=AcquireKernelInfo(
"FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19",exception);
1450 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1453 kernel->type = type;
1454 kernel->values[3] = +(MagickRealType) MagickSQ2;
1455 kernel->values[5] = -(MagickRealType) MagickSQ2;
1456 CalcKernelMetaData(kernel);
1457 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1460 kernel=ParseKernelArray(
"3: 1,2,1 0,0,0 1,2,1");
1463 kernel->type = type;
1464 kernel->values[1] = +(MagickRealType) MagickSQ2;
1465 kernel->values[7] = +(MagickRealType) MagickSQ2;
1466 CalcKernelMetaData(kernel);
1467 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1470 kernel=ParseKernelArray(
"3: 2,-1,0 -1,0,1 0,1,-2");
1473 kernel->type = type;
1474 kernel->values[0] = +(MagickRealType) MagickSQ2;
1475 kernel->values[8] = -(MagickRealType) MagickSQ2;
1476 CalcKernelMetaData(kernel);
1477 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1480 kernel=ParseKernelArray(
"3: 0,1,-2 -1,0,1 2,-1,0");
1483 kernel->type = type;
1484 kernel->values[2] = -(MagickRealType) MagickSQ2;
1485 kernel->values[6] = +(MagickRealType) MagickSQ2;
1486 CalcKernelMetaData(kernel);
1487 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1490 kernel=ParseKernelArray(
"3: 0,-1,0 1,0,1 0,-1,0");
1493 kernel->type = type;
1494 ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1497 kernel=ParseKernelArray(
"3: 1,0,-1 0,0,0 -1,0,1");
1500 kernel->type = type;
1501 ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1504 kernel=ParseKernelArray(
"3: 1,-2,1 -2,4,-2 -1,-2,1");
1507 kernel->type = type;
1508 ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1511 kernel=ParseKernelArray(
"3: -2,1,-2 1,4,1 -2,1,-2");
1514 kernel->type = type;
1515 ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1518 kernel=ParseKernelArray(
"3: 1,1,1 1,1,1 1,1,1");
1521 kernel->type = type;
1522 ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1525 if ( fabs(args->sigma) >= MagickEpsilon )
1527 RotateKernelInfo(kernel, args->sigma);
1528 else if ( args->rho > 30.0 || args->rho < -30.0 )
1530 RotateKernelInfo(kernel, args->rho);
1539 if (args->rho < 1.0)
1540 kernel->width = kernel->height = 3;
1542 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1543 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1545 kernel->values=(MagickRealType *) MagickAssumeAligned(
1546 AcquireAlignedMemory(kernel->width,kernel->height*
1547 sizeof(*kernel->values)));
1548 if (kernel->values == (MagickRealType *) NULL)
1549 return(DestroyKernelInfo(kernel));
1552 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1553 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1554 if ( (labs((
long) u)+labs((
long) v)) <= (long) kernel->x)
1555 kernel->positive_range += kernel->values[i] = args->sigma;
1557 kernel->values[i] = nan;
1558 kernel->minimum = kernel->maximum = args->sigma;
1562 case RectangleKernel:
1565 if ( type == SquareKernel )
1567 if (args->rho < 1.0)
1568 kernel->width = kernel->height = 3;
1570 kernel->width = kernel->height = (size_t) (2*args->rho+1);
1571 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1572 scale = args->sigma;
1576 if ( args->rho < 1.0 || args->sigma < 1.0 )
1577 return(DestroyKernelInfo(kernel));
1578 kernel->width = (size_t)args->rho;
1579 kernel->height = (
size_t)args->sigma;
1580 if ( args->xi < 0.0 || args->xi > (
double)kernel->width ||
1581 args->psi < 0.0 || args->psi > (double)kernel->height )
1582 return(DestroyKernelInfo(kernel));
1583 kernel->x = (ssize_t) args->xi;
1584 kernel->y = (ssize_t) args->psi;
1587 kernel->values=(MagickRealType *) MagickAssumeAligned(
1588 AcquireAlignedMemory(kernel->width,kernel->height*
1589 sizeof(*kernel->values)));
1590 if (kernel->values == (MagickRealType *) NULL)
1591 return(DestroyKernelInfo(kernel));
1594 u=(ssize_t) (kernel->width*kernel->height);
1595 for ( i=0; i < u; i++)
1596 kernel->values[i] = scale;
1597 kernel->minimum = kernel->maximum = scale;
1598 kernel->positive_range = scale*u;
1603 if (args->rho < 1.0)
1604 kernel->width = kernel->height = 5;
1606 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1607 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1609 kernel->values=(MagickRealType *) MagickAssumeAligned(
1610 AcquireAlignedMemory(kernel->width,kernel->height*
1611 sizeof(*kernel->values)));
1612 if (kernel->values == (MagickRealType *) NULL)
1613 return(DestroyKernelInfo(kernel));
1615 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1616 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1617 if ( (labs((
long) u)+labs((
long) v)) <=
1618 ((long)kernel->x + (
long)(kernel->x/2)) )
1619 kernel->positive_range += kernel->values[i] = args->sigma;
1621 kernel->values[i] = nan;
1622 kernel->minimum = kernel->maximum = args->sigma;
1628 limit = (ssize_t)(args->rho*args->rho);
1630 if (args->rho < 0.4)
1631 kernel->width = kernel->height = 9L, limit = 18L;
1633 kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
1634 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1636 kernel->values=(MagickRealType *) MagickAssumeAligned(
1637 AcquireAlignedMemory(kernel->width,kernel->height*
1638 sizeof(*kernel->values)));
1639 if (kernel->values == (MagickRealType *) NULL)
1640 return(DestroyKernelInfo(kernel));
1642 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1643 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1644 if ((u*u+v*v) <= limit)
1645 kernel->positive_range += kernel->values[i] = args->sigma;
1647 kernel->values[i] = nan;
1648 kernel->minimum = kernel->maximum = args->sigma;
1653 if (args->rho < 1.0)
1654 kernel->width = kernel->height = 5;
1656 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1657 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1659 kernel->values=(MagickRealType *) MagickAssumeAligned(
1660 AcquireAlignedMemory(kernel->width,kernel->height*
1661 sizeof(*kernel->values)));
1662 if (kernel->values == (MagickRealType *) NULL)
1663 return(DestroyKernelInfo(kernel));
1666 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1667 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1668 kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
1669 kernel->minimum = kernel->maximum = args->sigma;
1670 kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1675 if (args->rho < 1.0)
1676 kernel->width = kernel->height = 5;
1678 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1679 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1681 kernel->values=(MagickRealType *) MagickAssumeAligned(
1682 AcquireAlignedMemory(kernel->width,kernel->height*
1683 sizeof(*kernel->values)));
1684 if (kernel->values == (MagickRealType *) NULL)
1685 return(DestroyKernelInfo(kernel));
1688 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1689 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1690 kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
1691 kernel->minimum = kernel->maximum = args->sigma;
1692 kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1706 if (args->rho < args->sigma)
1708 kernel->width = ((size_t)args->sigma)*2+1;
1709 limit1 = (ssize_t)(args->rho*args->rho);
1710 limit2 = (ssize_t)(args->sigma*args->sigma);
1714 kernel->width = ((size_t)args->rho)*2+1;
1715 limit1 = (ssize_t)(args->sigma*args->sigma);
1716 limit2 = (ssize_t)(args->rho*args->rho);
1719 kernel->width = 7L, limit1 = 7L, limit2 = 11L;
1721 kernel->height = kernel->width;
1722 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1723 kernel->values=(MagickRealType *) MagickAssumeAligned(
1724 AcquireAlignedMemory(kernel->width,kernel->height*
1725 sizeof(*kernel->values)));
1726 if (kernel->values == (MagickRealType *) NULL)
1727 return(DestroyKernelInfo(kernel));
1730 scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
1731 for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
1732 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1733 { ssize_t radius=u*u+v*v;
1734 if (limit1 < radius && radius <= limit2)
1735 kernel->positive_range += kernel->values[i] = (double) scale;
1737 kernel->values[i] = nan;
1739 kernel->minimum = kernel->maximum = (double) scale;
1740 if ( type == PeaksKernel ) {
1742 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1743 kernel->positive_range = 1.0;
1744 kernel->maximum = 1.0;
1750 kernel=AcquireKernelInfo(
"ThinSE:482",exception);
1753 kernel->type = type;
1754 ExpandMirrorKernelInfo(kernel);
1759 kernel=AcquireKernelInfo(
"ThinSE:87",exception);
1762 kernel->type = type;
1763 ExpandRotateKernelInfo(kernel, 90.0);
1766 case DiagonalsKernel:
1768 switch ( (
int) args->rho ) {
1773 kernel=ParseKernelArray(
"3: 0,0,0 0,-,1 1,1,-");
1776 kernel->type = type;
1777 new_kernel=ParseKernelArray(
"3: 0,0,1 0,-,1 0,1,-");
1779 return(DestroyKernelInfo(kernel));
1780 new_kernel->type = type;
1781 LastKernelInfo(kernel)->next = new_kernel;
1782 ExpandMirrorKernelInfo(kernel);
1786 kernel=ParseKernelArray(
"3: 0,0,0 0,-,1 1,1,-");
1789 kernel=ParseKernelArray(
"3: 0,0,1 0,-,1 0,1,-");
1794 kernel->type = type;
1795 RotateKernelInfo(kernel, args->sigma);
1798 case LineEndsKernel:
1800 switch ( (
int) args->rho ) {
1804 return(AcquireKernelInfo(
"LineEnds:1>;LineEnds:2>",exception));
1807 kernel=ParseKernelArray(
"3: 0,0,- 0,1,1 0,0,-");
1811 kernel=ParseKernelArray(
"3: 0,0,0 0,1,0 0,0,1");
1815 kernel=ParseKernelArray(
"3: 0,0,0 0,1,1 0,0,0");
1819 kernel=ParseKernelArray(
"3: 0,0,0 0,1,- 0,0,-");
1824 kernel->type = type;
1825 RotateKernelInfo(kernel, args->sigma);
1828 case LineJunctionsKernel:
1830 switch ( (
int) args->rho ) {
1834 return(AcquireKernelInfo(
"LineJunctions:1@;LineJunctions:2>",exception));
1837 kernel=ParseKernelArray(
"3: 1,-,1 -,1,- -,1,-");
1841 kernel=ParseKernelArray(
"3: 1,-,- -,1,- 1,-,1");
1845 kernel=ParseKernelArray(
"3: -,-,- 1,1,1 -,1,-");
1849 kernel=ParseKernelArray(
"3: 1,-,1 -,1,- 1,-,1");
1853 kernel=ParseKernelArray(
"3: -,1,- 1,1,1 -,1,-");
1858 kernel->type = type;
1859 RotateKernelInfo(kernel, args->sigma);
1866 switch ( (
int) args->rho ) {
1869 kernel=ParseKernelArray(
"3x1:0,1,0");
1872 kernel->type = type;
1873 ExpandRotateKernelInfo(kernel, 90.0);
1876 kernel=ParseKernelArray(
"4x1:0,1,1,0");
1879 kernel->type = type;
1880 ExpandRotateKernelInfo(kernel, 90.0);
1885 new_kernel=ParseKernelArray(
"4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
1887 return(DestroyKernelInfo(kernel));
1888 new_kernel->type = type;
1889 LastKernelInfo(kernel)->next = new_kernel;
1890 new_kernel=ParseKernelArray(
"4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
1892 return(DestroyKernelInfo(kernel));
1893 new_kernel->type = type;
1894 LastKernelInfo(kernel)->next = new_kernel;
1895 new_kernel=ParseKernelArray(
"4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
1897 return(DestroyKernelInfo(kernel));
1898 new_kernel->type = type;
1899 LastKernelInfo(kernel)->next = new_kernel;
1900 new_kernel=ParseKernelArray(
"4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
1902 return(DestroyKernelInfo(kernel));
1903 new_kernel->type = type;
1904 LastKernelInfo(kernel)->next = new_kernel;
1905 new_kernel=ParseKernelArray(
"3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
1907 return(DestroyKernelInfo(kernel));
1908 new_kernel->type = type;
1909 LastKernelInfo(kernel)->next = new_kernel;
1910 new_kernel=ParseKernelArray(
"3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
1912 return(DestroyKernelInfo(kernel));
1913 new_kernel->type = type;
1914 LastKernelInfo(kernel)->next = new_kernel;
1915 new_kernel=ParseKernelArray(
"3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
1917 return(DestroyKernelInfo(kernel));
1918 new_kernel->type = type;
1919 LastKernelInfo(kernel)->next = new_kernel;
1920 new_kernel=ParseKernelArray(
"3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
1922 return(DestroyKernelInfo(kernel));
1923 new_kernel->type = type;
1924 LastKernelInfo(kernel)->next = new_kernel;
1929 case ConvexHullKernel:
1934 kernel=ParseKernelArray(
"3: 1,1,- 1,0,- 1,-,0");
1937 kernel->type = type;
1938 ExpandRotateKernelInfo(kernel, 90.0);
1940 new_kernel=ParseKernelArray(
"3: 1,1,1 1,0,- -,-,0");
1942 return(DestroyKernelInfo(kernel));
1943 new_kernel->type = type;
1944 ExpandRotateKernelInfo(new_kernel, 90.0);
1945 LastKernelInfo(kernel)->next = new_kernel;
1948 case SkeletonKernel:
1950 switch ( (
int) args->rho ) {
1956 kernel=AcquireKernelInfo(
"ThinSE:482",exception);
1959 kernel->type = type;
1960 ExpandRotateKernelInfo(kernel, 45.0);
1967 kernel=AcquireKernelInfo(
"ThinSE:482; ThinSE:87x90;",exception);
1971 return(DestroyKernelInfo(kernel));
1972 kernel->type = type;
1973 kernel->next->type = type;
1974 ExpandRotateKernelInfo(kernel, 90.0);
1982 kernel=AcquireKernelInfo(
"ThinSE:41; ThinSE:42; ThinSE:43",
1987 return(DestroyKernelInfo(kernel));
1988 if (kernel->next->next == (
KernelInfo *) NULL)
1989 return(DestroyKernelInfo(kernel));
1990 kernel->type = type;
1991 kernel->next->type = type;
1992 kernel->next->next->type = type;
1993 ExpandMirrorKernelInfo(kernel);
2009 switch ( (
int) args->rho ) {
2012 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 -,-,1");
2015 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 -,0,-");
2018 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,-,1");
2021 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,0,-");
2024 kernel=ParseKernelArray(
"3: -,0,1 0,-,1 -,0,-");
2027 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,0,1");
2030 kernel=ParseKernelArray(
"3: -,1,1 0,-,1 -,0,-");
2033 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 0,-,1");
2036 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 -,-,1");
2040 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 -,1,-");
2043 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,-,-");
2046 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 -,1,-");
2049 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 0,-,-");
2052 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 0,-,-");
2055 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 0,-,1");
2058 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,0,-");
2061 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,1,-");
2064 kernel=ParseKernelArray(
"3: 0,1,- 0,-,1 -,1,-");
2068 kernel=ParseKernelArray(
"3: -,-,1 0,-,- -,0,-");
2071 kernel=ParseKernelArray(
"3: -,1,- -,-,1 0,-,-");
2074 kernel=ParseKernelArray(
"3: -,1,1 0,-,1 0,0,-");
2078 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 0,-,1");
2083 kernel->type = type;
2084 RotateKernelInfo(kernel, args->sigma);
2090 case ChebyshevKernel:
2092 if (args->rho < 1.0)
2093 kernel->width = kernel->height = 3;
2095 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2096 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2098 kernel->values=(MagickRealType *) MagickAssumeAligned(
2099 AcquireAlignedMemory(kernel->width,kernel->height*
2100 sizeof(*kernel->values)));
2101 if (kernel->values == (MagickRealType *) NULL)
2102 return(DestroyKernelInfo(kernel));
2104 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2105 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2106 kernel->positive_range += ( kernel->values[i] =
2107 args->sigma*MagickMax(fabs((
double)u),fabs((
double)v)) );
2108 kernel->maximum = kernel->values[0];
2111 case ManhattanKernel:
2113 if (args->rho < 1.0)
2114 kernel->width = kernel->height = 3;
2116 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2117 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2119 kernel->values=(MagickRealType *) MagickAssumeAligned(
2120 AcquireAlignedMemory(kernel->width,kernel->height*
2121 sizeof(*kernel->values)));
2122 if (kernel->values == (MagickRealType *) NULL)
2123 return(DestroyKernelInfo(kernel));
2125 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2126 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2127 kernel->positive_range += ( kernel->values[i] =
2128 args->sigma*(labs((
long) u)+labs((
long) v)) );
2129 kernel->maximum = kernel->values[0];
2132 case OctagonalKernel:
2134 if (args->rho < 2.0)
2135 kernel->width = kernel->height = 5;
2137 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2138 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2140 kernel->values=(MagickRealType *) MagickAssumeAligned(
2141 AcquireAlignedMemory(kernel->width,kernel->height*
2142 sizeof(*kernel->values)));
2143 if (kernel->values == (MagickRealType *) NULL)
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++)
2150 r1 = MagickMax(fabs((
double)u),fabs((
double)v)),
2151 r2 = floor((
double)(labs((
long)u)+labs((
long)v)+1)/1.5);
2152 kernel->positive_range += kernel->values[i] =
2153 args->sigma*MagickMax(r1,r2);
2155 kernel->maximum = kernel->values[0];
2158 case EuclideanKernel:
2160 if (args->rho < 1.0)
2161 kernel->width = kernel->height = 3;
2163 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2164 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2166 kernel->values=(MagickRealType *) MagickAssumeAligned(
2167 AcquireAlignedMemory(kernel->width,kernel->height*
2168 sizeof(*kernel->values)));
2169 if (kernel->values == (MagickRealType *) NULL)
2170 return(DestroyKernelInfo(kernel));
2172 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2173 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2174 kernel->positive_range += ( kernel->values[i] =
2175 args->sigma*sqrt((
double)(u*u+v*v)) );
2176 kernel->maximum = kernel->values[0];
2182 kernel=ParseKernelArray(
"1:1");
2185 kernel->type = UndefinedKernel;
2226 new_kernel=(
KernelInfo *) AcquireMagickMemory(
sizeof(*kernel));
2229 *new_kernel=(*kernel);
2232 new_kernel->values=(MagickRealType *) MagickAssumeAligned(
2233 AcquireAlignedMemory(kernel->width,kernel->height*
sizeof(*kernel->values)));
2234 if (new_kernel->values == (MagickRealType *) NULL)
2235 return(DestroyKernelInfo(new_kernel));
2236 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
2237 new_kernel->values[i]=kernel->values[i];
2241 new_kernel->next = CloneKernelInfo(kernel->next);
2242 if ( new_kernel->next == (
KernelInfo *) NULL )
2243 return(DestroyKernelInfo(new_kernel));
2276 kernel->next=DestroyKernelInfo(kernel->next);
2277 kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
2278 kernel=(
KernelInfo *) RelinquishMagickMemory(kernel);
2314 static void FlopKernelInfo(
KernelInfo *kernel)
2323 for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2324 for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2325 t=k[x], k[x]=k[r], k[r]=t;
2327 kernel->x = kernel->width - kernel->x - 1;
2328 angle = fmod(angle+180.0, 360.0);
2332 static void ExpandMirrorKernelInfo(
KernelInfo *kernel)
2340 clone = CloneKernelInfo(last);
2343 RotateKernelInfo(clone, 180);
2344 LastKernelInfo(last)->next = clone;
2347 clone = CloneKernelInfo(last);
2350 RotateKernelInfo(clone, 90);
2351 LastKernelInfo(last)->next = clone;
2354 clone = CloneKernelInfo(last);
2357 RotateKernelInfo(clone, 180);
2358 LastKernelInfo(last)->next = clone;
2396 static MagickBooleanType SameKernelInfo(
const KernelInfo *kernel1,
2403 if ( kernel1->width != kernel2->width
2404 || kernel1->height != kernel2->height
2405 || kernel1->x != kernel2->x
2406 || kernel1->y != kernel2->y )
2410 for (i=0; i < (kernel1->width*kernel1->height); i++) {
2412 if ( IsNaN(kernel1->values[i]) && !IsNaN(kernel2->values[i]) )
2414 if ( IsNaN(kernel2->values[i]) && !IsNaN(kernel1->values[i]) )
2417 if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
2424 static void ExpandRotateKernelInfo(
KernelInfo *kernel,
const double angle)
2432 DisableMSCWarning(4127)
2435 clone_info=CloneKernelInfo(last);
2438 RotateKernelInfo(clone_info,angle);
2439 if (SameKernelInfo(kernel,clone_info) != MagickFalse)
2441 LastKernelInfo(last)->next=clone_info;
2445 clone_info=DestroyKernelInfo(clone_info);
2485 static void CalcKernelMetaData(
KernelInfo *kernel)
2490 kernel->minimum = kernel->maximum = 0.0;
2491 kernel->negative_range = kernel->positive_range = 0.0;
2492 for (i=0; i < (kernel->width*kernel->height); i++)
2494 if ( fabs(kernel->values[i]) < MagickEpsilon )
2495 kernel->values[i] = 0.0;
2496 ( kernel->values[i] < 0)
2497 ? ( kernel->negative_range += kernel->values[i] )
2498 : ( kernel->positive_range += kernel->values[i] );
2499 Minimize(kernel->minimum, kernel->values[i]);
2500 Maximize(kernel->maximum, kernel->values[i]);
2566 static ssize_t MorphologyPrimitive(
const Image *image,
Image *morphology_image,
2567 const MorphologyMethod method,
const KernelInfo *kernel,
const double bias,
2570 #define MorphologyTag "Morphology/Image"
2594 assert(image != (
Image *) NULL);
2595 assert(image->signature == MagickCoreSignature);
2596 assert(morphology_image != (
Image *) NULL);
2597 assert(morphology_image->signature == MagickCoreSignature);
2599 assert(kernel->signature == MagickCoreSignature);
2601 assert(exception->signature == MagickCoreSignature);
2604 image_view=AcquireVirtualCacheView(image,exception);
2605 morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
2606 width=image->columns+kernel->width-1;
2611 case ConvolveMorphology:
2612 case DilateMorphology:
2613 case DilateIntensityMorphology:
2614 case IterativeDistanceMorphology:
2619 offset.x=(ssize_t) kernel->width-kernel->x-1;
2620 offset.y=(ssize_t) kernel->height-kernel->y-1;
2623 case ErodeMorphology:
2624 case ErodeIntensityMorphology:
2625 case HitAndMissMorphology:
2626 case ThinningMorphology:
2627 case ThickenMorphology:
2635 ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2636 "InvalidOption",
"`%s'",
"Not a Primitive Morphology Method");
2641 changes=(
size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
2643 if (changes == (
size_t *) NULL)
2644 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2645 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
2648 if ((method == ConvolveMorphology) && (kernel->width == 1))
2659 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2660 #pragma omp parallel for schedule(static) shared(progress,status) \
2661 magick_number_threads(image,morphology_image,image->columns,1)
2663 for (x=0; x < (ssize_t) image->columns; x++)
2666 id = GetOpenMPThreadId();
2680 if (status == MagickFalse)
2682 p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
2683 kernel->height-1,exception);
2684 q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2685 morphology_image->rows,exception);
2686 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2691 center=(ssize_t) GetPixelChannels(image)*offset.y;
2692 for (r=0; r < (ssize_t) image->rows; r++)
2697 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2711 const MagickRealType
2715 *magick_restrict pixels;
2723 channel=GetPixelChannelChannel(image,i);
2724 traits=GetPixelChannelTraits(image,channel);
2725 morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2726 if ((traits == UndefinedPixelTrait) ||
2727 (morphology_traits == UndefinedPixelTrait))
2729 if ((traits & CopyPixelTrait) != 0)
2731 SetPixelChannel(morphology_image,channel,p[center+i],q);
2734 k=(&kernel->values[kernel->height-1]);
2739 if (((image->alpha_trait & BlendPixelTrait) == 0) ||
2740 ((morphology_traits & BlendPixelTrait) == 0))
2741 for (v=0; v < (ssize_t) kernel->height; v++)
2745 pixel+=(*k)*pixels[i];
2749 pixels+=GetPixelChannels(image);
2754 for (v=0; v < (ssize_t) kernel->height; v++)
2758 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2759 pixel+=alpha*(*k)*pixels[i];
2764 pixels+=GetPixelChannels(image);
2767 if (fabs(pixel-p[center+i]) > MagickEpsilon)
2769 gamma=PerceptibleReciprocal(gamma);
2771 gamma*=(double) kernel->height/count;
2772 SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
2775 p+=GetPixelChannels(image);
2776 q+=GetPixelChannels(morphology_image);
2778 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
2780 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2785 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2789 proceed=SetImageProgress(image,MorphologyTag,progress,image->rows);
2790 if (proceed == MagickFalse)
2794 morphology_image->type=image->type;
2795 morphology_view=DestroyCacheView(morphology_view);
2796 image_view=DestroyCacheView(image_view);
2797 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
2798 changed+=changes[j];
2799 changes=(
size_t *) RelinquishMagickMemory(changes);
2800 return(status ? (ssize_t) changed : 0);
2805 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2806 #pragma omp parallel for schedule(static) shared(progress,status) \
2807 magick_number_threads(image,morphology_image,image->rows,1)
2809 for (y=0; y < (ssize_t) image->rows; y++)
2812 id = GetOpenMPThreadId();
2826 if (status == MagickFalse)
2828 p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
2829 kernel->height,exception);
2830 q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
2832 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2837 center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
2838 GetPixelChannels(image)*offset.x);
2839 for (x=0; x < (ssize_t) image->columns; x++)
2844 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2861 const MagickRealType
2865 *magick_restrict pixels,
2866 *magick_restrict quantum_pixels;
2877 channel=GetPixelChannelChannel(image,i);
2878 traits=GetPixelChannelTraits(image,channel);
2879 morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2880 if ((traits == UndefinedPixelTrait) ||
2881 (morphology_traits == UndefinedPixelTrait))
2883 if ((traits & CopyPixelTrait) != 0)
2885 SetPixelChannel(morphology_image,channel,p[center+i],q);
2889 quantum_pixels=(
const Quantum *) NULL;
2891 minimum=(double) QuantumRange;
2894 case ConvolveMorphology:
2899 case DilateMorphology:
2900 case ErodeIntensityMorphology:
2905 case HitAndMissMorphology:
2906 case ErodeMorphology:
2913 pixel=(double) p[center+i];
2921 case ConvolveMorphology:
2942 k=(&kernel->values[kernel->width*kernel->height-1]);
2943 if (((image->alpha_trait & BlendPixelTrait) == 0) ||
2944 ((morphology_traits & BlendPixelTrait) == 0))
2949 for (v=0; v < (ssize_t) kernel->height; v++)
2951 for (u=0; u < (ssize_t) kernel->width; u++)
2955 pixel+=(*k)*pixels[i];
2959 pixels+=GetPixelChannels(image);
2961 pixels+=(image->columns-1)*GetPixelChannels(image);
2969 for (v=0; v < (ssize_t) kernel->height; v++)
2971 for (u=0; u < (ssize_t) kernel->width; u++)
2975 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2976 pixel+=alpha*(*k)*pixels[i];
2981 pixels+=GetPixelChannels(image);
2983 pixels+=(image->columns-1)*GetPixelChannels(image);
2987 case ErodeMorphology:
2998 for (v=0; v < (ssize_t) kernel->height; v++)
3000 for (u=0; u < (ssize_t) kernel->width; u++)
3002 if (!IsNaN(*k) && (*k >= 0.5))
3004 if ((
double) pixels[i] < pixel)
3005 pixel=(
double) pixels[i];
3008 pixels+=GetPixelChannels(image);
3010 pixels+=(image->columns-1)*GetPixelChannels(image);
3014 case DilateMorphology:
3027 k=(&kernel->values[kernel->width*kernel->height-1]);
3028 for (v=0; v < (ssize_t) kernel->height; v++)
3030 for (u=0; u < (ssize_t) kernel->width; u++)
3032 if (!IsNaN(*k) && (*k > 0.5))
3034 if ((
double) pixels[i] > pixel)
3035 pixel=(
double) pixels[i];
3038 pixels+=GetPixelChannels(image);
3040 pixels+=(image->columns-1)*GetPixelChannels(image);
3044 case HitAndMissMorphology:
3045 case ThinningMorphology:
3046 case ThickenMorphology:
3061 for (v=0; v < (ssize_t) kernel->height; v++)
3063 for (u=0; u < (ssize_t) kernel->width; u++)
3069 if ((
double) pixels[i] < pixel)
3070 pixel=(
double) pixels[i];
3075 if ((
double) pixels[i] > maximum)
3076 maximum=(
double) pixels[i];
3081 pixels+=GetPixelChannels(image);
3083 pixels+=(image->columns-1)*GetPixelChannels(image);
3088 if (method == ThinningMorphology)
3089 pixel=(double) p[center+i]-pixel;
3091 if (method == ThickenMorphology)
3092 pixel+=(double) p[center+i]+pixel;
3095 case ErodeIntensityMorphology:
3103 for (v=0; v < (ssize_t) kernel->height; v++)
3105 for (u=0; u < (ssize_t) kernel->width; u++)
3107 if (!IsNaN(*k) && (*k >= 0.5))
3109 intensity=(double) GetPixelIntensity(image,pixels);
3110 if (intensity < minimum)
3112 quantum_pixels=pixels;
3113 pixel=(double) pixels[i];
3119 pixels+=GetPixelChannels(image);
3121 pixels+=(image->columns-1)*GetPixelChannels(image);
3125 case DilateIntensityMorphology:
3132 k=(&kernel->values[kernel->width*kernel->height-1]);
3133 for (v=0; v < (ssize_t) kernel->height; v++)
3135 for (u=0; u < (ssize_t) kernel->width; u++)
3137 if (!IsNaN(*k) && (*k >= 0.5))
3139 intensity=(double) GetPixelIntensity(image,pixels);
3140 if (intensity > maximum)
3142 pixel=(double) pixels[i];
3143 quantum_pixels=pixels;
3149 pixels+=GetPixelChannels(image);
3151 pixels+=(image->columns-1)*GetPixelChannels(image);
3155 case IterativeDistanceMorphology:
3180 k=(&kernel->values[kernel->width*kernel->height-1]);
3181 for (v=0; v < (ssize_t) kernel->height; v++)
3183 for (u=0; u < (ssize_t) kernel->width; u++)
3187 if ((pixels[i]+(*k)) < pixel)
3188 pixel=(double) pixels[i]+(*k);
3192 pixels+=GetPixelChannels(image);
3194 pixels+=(image->columns-1)*GetPixelChannels(image);
3198 case UndefinedMorphology:
3202 if (fabs(pixel-p[center+i]) > MagickEpsilon)
3204 if (quantum_pixels != (
const Quantum *) NULL)
3206 SetPixelChannel(morphology_image,channel,quantum_pixels[i],q);
3209 gamma=PerceptibleReciprocal(gamma);
3211 gamma*=(double) kernel->height*kernel->width/count;
3212 SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*pixel),q);
3214 p+=GetPixelChannels(image);
3215 q+=GetPixelChannels(morphology_image);
3217 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3219 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3224 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3228 proceed=SetImageProgress(image,MorphologyTag,progress,image->rows);
3229 if (proceed == MagickFalse)
3233 morphology_view=DestroyCacheView(morphology_view);
3234 image_view=DestroyCacheView(image_view);
3235 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
3236 changed+=changes[j];
3237 changes=(
size_t *) RelinquishMagickMemory(changes);
3238 return(status ? (ssize_t) changed : -1);
3254 static ssize_t MorphologyPrimitiveDirect(
Image *image,
3255 const MorphologyMethod method,
const KernelInfo *kernel,
3278 assert(image != (
Image *) NULL);
3279 assert(image->signature == MagickCoreSignature);
3281 assert(kernel->signature == MagickCoreSignature);
3283 assert(exception->signature == MagickCoreSignature);
3289 case DistanceMorphology:
3290 case VoronoiMorphology:
3295 offset.x=(ssize_t) kernel->width-kernel->x-1;
3296 offset.y=(ssize_t) kernel->height-kernel->y-1;
3309 image_view=AcquireVirtualCacheView(image,exception);
3310 morphology_view=AcquireAuthenticCacheView(image,exception);
3311 width=image->columns+kernel->width-1;
3312 for (y=0; y < (ssize_t) image->rows; y++)
3331 if (status == MagickFalse)
3333 p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(
size_t)
3334 offset.y+1,exception);
3335 q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
3337 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3342 for (x=0; x < (ssize_t) image->columns; x++)
3347 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3358 const MagickRealType
3362 *magick_restrict pixels;
3370 channel=GetPixelChannelChannel(image,i);
3371 traits=GetPixelChannelTraits(image,channel);
3372 if (traits == UndefinedPixelTrait)
3374 if ((traits & CopyPixelTrait) != 0)
3377 pixel=(double) QuantumRange;
3380 case DistanceMorphology:
3382 k=(&kernel->values[kernel->width*kernel->height-1]);
3383 for (v=0; v <= offset.y; v++)
3385 for (u=0; u < (ssize_t) kernel->width; u++)
3389 if ((pixels[i]+(*k)) < pixel)
3390 pixel=(double) pixels[i]+(*k);
3393 pixels+=GetPixelChannels(image);
3395 pixels+=(image->columns-1)*GetPixelChannels(image);
3397 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3398 pixels=q-offset.x*GetPixelChannels(image);
3399 for (u=0; u < offset.x; u++)
3401 if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
3403 if ((pixels[i]+(*k)) < pixel)
3404 pixel=(double) pixels[i]+(*k);
3407 pixels+=GetPixelChannels(image);
3411 case VoronoiMorphology:
3413 k=(&kernel->values[kernel->width*kernel->height-1]);
3414 for (v=0; v < offset.y; v++)
3416 for (u=0; u < (ssize_t) kernel->width; u++)
3420 if ((pixels[i]+(*k)) < pixel)
3421 pixel=(double) pixels[i]+(*k);
3424 pixels+=GetPixelChannels(image);
3426 pixels+=(image->columns-1)*GetPixelChannels(image);
3428 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3429 pixels=q-offset.x*GetPixelChannels(image);
3430 for (u=0; u < offset.x; u++)
3432 if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
3434 if ((pixels[i]+(*k)) < pixel)
3435 pixel=(double) pixels[i]+(*k);
3438 pixels+=GetPixelChannels(image);
3445 if (fabs(pixel-q[i]) > MagickEpsilon)
3447 q[i]=ClampToQuantum(pixel);
3449 p+=GetPixelChannels(image);
3450 q+=GetPixelChannels(image);
3452 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3454 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3459 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3463 proceed=SetImageProgress(image,MorphologyTag,progress,2*image->rows);
3464 if (proceed == MagickFalse)
3468 morphology_view=DestroyCacheView(morphology_view);
3469 image_view=DestroyCacheView(image_view);
3473 image_view=AcquireVirtualCacheView(image,exception);
3474 morphology_view=AcquireAuthenticCacheView(image,exception);
3475 for (y=(ssize_t) image->rows-1; y >= 0; y--)
3493 if (status == MagickFalse)
3495 p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(
size_t)
3496 kernel->y+1,exception);
3497 q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
3499 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3504 p+=(image->columns-1)*GetPixelChannels(image);
3505 q+=(image->columns-1)*GetPixelChannels(image);
3506 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3511 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3522 const MagickRealType
3526 *magick_restrict pixels;
3534 channel=GetPixelChannelChannel(image,i);
3535 traits=GetPixelChannelTraits(image,channel);
3536 if (traits == UndefinedPixelTrait)
3538 if ((traits & CopyPixelTrait) != 0)
3541 pixel=(double) QuantumRange;
3544 case DistanceMorphology:
3546 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3547 for (v=offset.y; v < (ssize_t) kernel->height; v++)
3549 for (u=0; u < (ssize_t) kernel->width; u++)
3553 if ((pixels[i]+(*k)) < pixel)
3554 pixel=(double) pixels[i]+(*k);
3557 pixels+=GetPixelChannels(image);
3559 pixels+=(image->columns-1)*GetPixelChannels(image);
3561 k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
3563 for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3565 pixels+=GetPixelChannels(image);
3566 if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
3568 if ((pixels[i]+(*k)) < pixel)
3569 pixel=(double) pixels[i]+(*k);
3575 case VoronoiMorphology:
3577 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3578 for (v=offset.y; v < (ssize_t) kernel->height; v++)
3580 for (u=0; u < (ssize_t) kernel->width; u++)
3584 if ((pixels[i]+(*k)) < pixel)
3585 pixel=(double) pixels[i]+(*k);
3588 pixels+=GetPixelChannels(image);
3590 pixels+=(image->columns-1)*GetPixelChannels(image);
3592 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3594 for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3596 pixels+=GetPixelChannels(image);
3597 if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
3599 if ((pixels[i]+(*k)) < pixel)
3600 pixel=(double) pixels[i]+(*k);
3609 if (fabs(pixel-q[i]) > MagickEpsilon)
3611 q[i]=ClampToQuantum(pixel);
3613 p-=GetPixelChannels(image);
3614 q-=GetPixelChannels(image);
3616 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3618 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3623 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3627 proceed=SetImageProgress(image,MorphologyTag,progress,2*image->rows);
3628 if (proceed == MagickFalse)
3632 morphology_view=DestroyCacheView(morphology_view);
3633 image_view=DestroyCacheView(image_view);
3634 return(status ? (ssize_t) changed : -1);
3646 MagickPrivate
Image *MorphologyApply(
const Image *image,
3647 const MorphologyMethod method,
const ssize_t iterations,
3648 const KernelInfo *kernel,
const CompositeOperator compose,
const double bias,
3692 v_info[MagickPathExtent];
3694 assert(image != (
Image *) NULL);
3695 assert(image->signature == MagickCoreSignature);
3697 assert(kernel->signature == MagickCoreSignature);
3699 assert(exception->signature == MagickCoreSignature);
3702 if ( iterations == 0 )
3703 return((
Image *) NULL);
3705 kernel_limit = (size_t) iterations;
3706 if ( iterations < 0 )
3707 kernel_limit = image->columns>image->rows ? image->columns : image->rows;
3709 verbose = IsStringTrue(GetImageArtifact(image,
"debug"));
3712 curr_image = (
Image *) image;
3713 curr_compose = image->compose;
3714 (void) curr_compose;
3715 work_image = save_image = rslt_image = (
Image *) NULL;
3725 special = MagickFalse;
3726 rslt_compose = compose;
3728 case SmoothMorphology:
3731 case OpenMorphology:
3732 case OpenIntensityMorphology:
3733 case TopHatMorphology:
3734 case CloseMorphology:
3735 case CloseIntensityMorphology:
3736 case BottomHatMorphology:
3737 case EdgeMorphology:
3740 case HitAndMissMorphology:
3741 rslt_compose = LightenCompositeOp;
3743 case ThinningMorphology:
3744 case ThickenMorphology:
3745 method_limit = kernel_limit;
3748 case DistanceMorphology:
3749 case VoronoiMorphology:
3750 special = MagickTrue;
3759 if ( special != MagickFalse )
3761 rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3762 if (rslt_image == (
Image *) NULL)
3764 if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3767 changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
3769 if (verbose != MagickFalse)
3770 (void) (
void) FormatLocaleFile(stderr,
3771 "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
3772 CommandOptionToMnemonic(MagickMorphologyOptions, method),
3773 1.0,0.0,1.0, (
double) changed);
3778 if ( method == VoronoiMorphology ) {
3780 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3782 (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
3783 MagickTrue,0,0,exception);
3784 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3791 if ( compose != UndefinedCompositeOp )
3792 rslt_compose = compose;
3793 if ( rslt_compose == UndefinedCompositeOp )
3794 rslt_compose = NoCompositeOp;
3799 case CorrelateMorphology:
3800 case CloseMorphology:
3801 case CloseIntensityMorphology:
3802 case BottomHatMorphology:
3803 case SmoothMorphology:
3804 reflected_kernel = CloneKernelInfo(kernel);
3807 RotateKernelInfo(reflected_kernel,180);
3819 while ( method_loop < method_limit && method_changed > 0 ) {
3826 rflt_kernel = reflected_kernel;
3829 while ( norm_kernel != NULL ) {
3833 while ( stage_loop < stage_limit ) {
3837 this_kernel = norm_kernel;
3840 case ErodeMorphology:
3841 case EdgeInMorphology:
3842 primitive = ErodeMorphology;
3844 case DilateMorphology:
3845 case EdgeOutMorphology:
3846 primitive = DilateMorphology;
3848 case OpenMorphology:
3849 case TopHatMorphology:
3850 primitive = ErodeMorphology;
3851 if ( stage_loop == 2 )
3852 primitive = DilateMorphology;
3854 case OpenIntensityMorphology:
3855 primitive = ErodeIntensityMorphology;
3856 if ( stage_loop == 2 )
3857 primitive = DilateIntensityMorphology;
3859 case CloseMorphology:
3860 case BottomHatMorphology:
3861 this_kernel = rflt_kernel;
3862 primitive = DilateMorphology;
3863 if ( stage_loop == 2 )
3864 primitive = ErodeMorphology;
3866 case CloseIntensityMorphology:
3867 this_kernel = rflt_kernel;
3868 primitive = DilateIntensityMorphology;
3869 if ( stage_loop == 2 )
3870 primitive = ErodeIntensityMorphology;
3872 case SmoothMorphology:
3873 switch ( stage_loop ) {
3875 primitive = ErodeMorphology;
3878 primitive = DilateMorphology;
3881 this_kernel = rflt_kernel;
3882 primitive = DilateMorphology;
3885 this_kernel = rflt_kernel;
3886 primitive = ErodeMorphology;
3890 case EdgeMorphology:
3891 primitive = DilateMorphology;
3892 if ( stage_loop == 2 ) {
3893 save_image = curr_image;
3894 curr_image = (
Image *) image;
3895 primitive = ErodeMorphology;
3898 case CorrelateMorphology:
3908 this_kernel = rflt_kernel;
3909 primitive = ConvolveMorphology;
3914 assert( this_kernel != (
KernelInfo *) NULL );
3917 if (verbose != MagickFalse) {
3918 if ( stage_limit > 1 )
3919 (void) FormatLocaleString(v_info,MagickPathExtent,
"%s:%.20g.%.20g -> ",
3920 CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
3921 method_loop,(
double) stage_loop);
3922 else if ( primitive != method )
3923 (void) FormatLocaleString(v_info, MagickPathExtent,
"%s:%.20g -> ",
3924 CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
3934 while ( kernel_loop < kernel_limit && changed > 0 ) {
3938 if ( work_image == (
Image *) NULL )
3940 work_image=CloneImage(image,0,0,MagickTrue,exception);
3941 if (work_image == (
Image *) NULL)
3943 if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
3949 changed = MorphologyPrimitive(curr_image, work_image, primitive,
3950 this_kernel, bias, exception);
3951 if (verbose != MagickFalse) {
3952 if ( kernel_loop > 1 )
3953 (void) FormatLocaleFile(stderr,
"\n");
3954 (void) (
void) FormatLocaleFile(stderr,
3955 "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
3956 v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
3957 primitive),(this_kernel == rflt_kernel ) ?
"*" :
"",
3958 (
double) (method_loop+kernel_loop-1),(
double) kernel_number,
3959 (
double) count,(
double) changed);
3963 kernel_changed += changed;
3964 method_changed += changed;
3967 {
Image *tmp = work_image;
3968 work_image = curr_image;
3971 if ( work_image == image )
3972 work_image = (
Image *) NULL;
3976 if (verbose != MagickFalse && kernel_changed != (
size_t)changed)
3977 (
void) FormatLocaleFile(stderr,
" Total %.20g",(
double) kernel_changed);
3978 if (verbose != MagickFalse && stage_loop < stage_limit)
3979 (void) FormatLocaleFile(stderr,
"\n");
3982 (void) FormatLocaleFile(stderr,
"--E-- image=0x%lx\n", (
unsigned long)image);
3983 (void) FormatLocaleFile(stderr,
" curr =0x%lx\n", (
unsigned long)curr_image);
3984 (void) FormatLocaleFile(stderr,
" work =0x%lx\n", (
unsigned long)work_image);
3985 (void) FormatLocaleFile(stderr,
" save =0x%lx\n", (
unsigned long)save_image);
3986 (void) FormatLocaleFile(stderr,
" union=0x%lx\n", (
unsigned long)rslt_image);
3999 case EdgeOutMorphology:
4000 case EdgeInMorphology:
4001 case TopHatMorphology:
4002 case BottomHatMorphology:
4003 if (verbose != MagickFalse)
4004 (void) FormatLocaleFile(stderr,
4005 "\n%s: Difference with original image",CommandOptionToMnemonic(
4006 MagickMorphologyOptions, method) );
4007 (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
4008 MagickTrue,0,0,exception);
4010 case EdgeMorphology:
4011 if (verbose != MagickFalse)
4012 (void) FormatLocaleFile(stderr,
4013 "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
4014 MagickMorphologyOptions, method) );
4015 (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
4016 MagickTrue,0,0,exception);
4017 save_image = DestroyImage(save_image);
4025 rslt_image = curr_image;
4026 else if ( rslt_compose == NoCompositeOp )
4027 {
if (verbose != MagickFalse) {
4028 if ( this_kernel->next != (
KernelInfo *) NULL )
4029 (void) FormatLocaleFile(stderr,
" (re-iterate)");
4031 (
void) FormatLocaleFile(stderr,
" (done)");
4033 rslt_image = curr_image;
4035 else if ( rslt_image == (
Image *) NULL)
4036 {
if (verbose != MagickFalse)
4037 (void) FormatLocaleFile(stderr,
" (save for compose)");
4038 rslt_image = curr_image;
4039 curr_image = (
Image *) image;
4049 if (verbose != MagickFalse)
4050 (void) FormatLocaleFile(stderr,
" (compose \"%s\")",
4051 CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
4052 (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
4054 curr_image = DestroyImage(curr_image);
4055 curr_image = (
Image *) image;
4057 if (verbose != MagickFalse)
4058 (void) FormatLocaleFile(stderr,
"\n");
4061 norm_kernel = norm_kernel->next;
4063 rflt_kernel = rflt_kernel->next;
4073 if ( curr_image == rslt_image )
4074 curr_image = (
Image *) NULL;
4075 if ( rslt_image != (
Image *) NULL )
4076 rslt_image = DestroyImage(rslt_image);
4078 if ( curr_image == rslt_image || curr_image == image )
4079 curr_image = (
Image *) NULL;
4080 if ( curr_image != (
Image *) NULL )
4081 curr_image = DestroyImage(curr_image);
4082 if ( work_image != (
Image *) NULL )
4083 work_image = DestroyImage(work_image);
4084 if ( save_image != (
Image *) NULL )
4085 save_image = DestroyImage(save_image);
4086 if ( reflected_kernel != (
KernelInfo *) NULL )
4087 reflected_kernel = DestroyKernelInfo(reflected_kernel);
4141 MagickExport
Image *MorphologyImage(
const Image *image,
4142 const MorphologyMethod method,
const ssize_t iterations,
4160 assert(image != (
const Image *) NULL);
4161 assert(image->signature == MagickCoreSignature);
4163 assert(exception->signature == MagickCoreSignature);
4164 if (IsEventLogging() != MagickFalse)
4165 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4168 compose = UndefinedCompositeOp;
4174 if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
4176 artifact = GetImageArtifact(image,
"convolve:bias");
4177 if ( artifact != (
const char *) NULL) {
4178 if (IsGeometry(artifact) == MagickFalse)
4179 (void) ThrowMagickException(exception,GetMagickModule(),
4180 OptionWarning,
"InvalidSetting",
"'%s' '%s'",
4181 "convolve:bias",artifact);
4183 bias=StringToDoubleInterval(artifact,(
double) QuantumRange+1.0);
4187 artifact = GetImageArtifact(image,
"convolve:scale");
4188 if ( artifact != (
const char *) NULL ) {
4189 if (IsGeometry(artifact) == MagickFalse)
4190 (void) ThrowMagickException(exception,GetMagickModule(),
4191 OptionWarning,
"InvalidSetting",
"'%s' '%s'",
4192 "convolve:scale",artifact);
4194 if ( curr_kernel == kernel )
4195 curr_kernel = CloneKernelInfo(kernel);
4197 return((
Image *) NULL);
4198 ScaleGeometryKernelInfo(curr_kernel, artifact);
4204 artifact=GetImageArtifact(image,
"morphology:showKernel");
4205 if (IsStringTrue(artifact) != MagickFalse)
4206 ShowKernelInfo(curr_kernel);
4218 artifact = GetImageArtifact(image,
"morphology:compose");
4219 if ( artifact != (
const char *) NULL) {
4220 parse=ParseCommandOption(MagickComposeOptions,
4221 MagickFalse,artifact);
4223 (void) ThrowMagickException(exception,GetMagickModule(),
4224 OptionWarning,
"UnrecognizedComposeOperator",
"'%s' '%s'",
4225 "morphology:compose",artifact);
4227 compose=(CompositeOperator)parse;
4231 morphology_image = MorphologyApply(image,method,iterations,
4232 curr_kernel,compose,bias,exception);
4235 if ( curr_kernel != kernel )
4236 curr_kernel=DestroyKernelInfo(curr_kernel);
4237 return(morphology_image);
4270 static void RotateKernelInfo(
KernelInfo *kernel,
double angle)
4274 RotateKernelInfo(kernel->next, angle);
4282 angle = fmod(angle, 360.0);
4286 if ( 337.5 < angle || angle <= 22.5 )
4290 switch (kernel->type) {
4292 case GaussianKernel:
4297 case LaplacianKernel:
4298 case ChebyshevKernel:
4299 case ManhattanKernel:
4300 case EuclideanKernel:
4314 if ( 135.0 < angle && angle <= 225.0 )
4316 if ( 225.0 < angle && angle <= 315.0 )
4324 if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
4326 if ( kernel->width == 3 && kernel->height == 3 )
4328 double t = kernel->values[0];
4329 kernel->values[0] = kernel->values[3];
4330 kernel->values[3] = kernel->values[6];
4331 kernel->values[6] = kernel->values[7];
4332 kernel->values[7] = kernel->values[8];
4333 kernel->values[8] = kernel->values[5];
4334 kernel->values[5] = kernel->values[2];
4335 kernel->values[2] = kernel->values[1];
4336 kernel->values[1] = t;
4338 if ( kernel->x != 1 || kernel->y != 1 ) {
4340 x = (ssize_t) kernel->x-1;
4341 y = (ssize_t) kernel->y-1;
4342 if ( x == y ) x = 0;
4343 else if ( x == 0 ) x = -y;
4344 else if ( x == -y ) y = 0;
4345 else if ( y == 0 ) y = x;
4346 kernel->x = (ssize_t) x+1;
4347 kernel->y = (ssize_t) y+1;
4349 angle = fmod(angle+315.0, 360.0);
4350 kernel->angle = fmod(kernel->angle+45.0, 360.0);
4353 perror(
"Unable to rotate non-3x3 kernel by 45 degrees");
4355 if ( 45.0 < fmod(angle, 180.0) && fmod(angle,180.0) <= 135.0 )
4357 if ( kernel->width == 1 || kernel->height == 1 )
4363 t = (ssize_t) kernel->width;
4364 kernel->width = kernel->height;
4365 kernel->height = (
size_t) t;
4367 kernel->x = kernel->y;
4369 if ( kernel->width == 1 ) {
4370 angle = fmod(angle+270.0, 360.0);
4371 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4373 angle = fmod(angle+90.0, 360.0);
4374 kernel->angle = fmod(kernel->angle+270.0, 360.0);
4377 else if ( kernel->width == kernel->height )
4386 for( i=0, x=(ssize_t) kernel->width-1; i<=x; i++, x--)
4387 for( j=0, y=(ssize_t) kernel->height-1; j<y; j++, y--)
4388 { t = k[i+j*kernel->width];
4389 k[i+j*kernel->width] = k[j+x*kernel->width];
4390 k[j+x*kernel->width] = k[x+y*kernel->width];
4391 k[x+y*kernel->width] = k[y+i*kernel->width];
4392 k[y+i*kernel->width] = t;
4397 x = (ssize_t) (kernel->x*2-kernel->width+1);
4398 y = (ssize_t) (kernel->y*2-kernel->height+1);
4399 kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4400 kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
4402 angle = fmod(angle+270.0, 360.0);
4403 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4406 perror(
"Unable to rotate a non-square, non-linear kernel 90 degrees");
4408 if ( 135.0 < angle && angle <= 225.0 )
4426 j=(ssize_t) (kernel->width*kernel->height-1);
4427 for (i=0; i < j; i++, j--)
4428 t=k[i], k[i]=k[j], k[j]=t;
4430 kernel->x = (ssize_t) kernel->width - kernel->x - 1;
4431 kernel->y = (ssize_t) kernel->height - kernel->y - 1;
4432 angle = fmod(angle-180.0, 360.0);
4433 kernel->angle = fmod(kernel->angle+180.0, 360.0);
4477 MagickExport
void ScaleGeometryKernelInfo (
KernelInfo *kernel,
4478 const char *geometry)
4486 SetGeometryInfo(&args);
4487 flags = ParseGeometry(geometry, &args);
4491 (void) FormatLocaleFile(stderr,
"Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4492 flags, args.rho, args.sigma, args.xi, args.psi );
4495 if ( (flags & PercentValue) != 0 )
4496 args.rho *= 0.01, args.sigma *= 0.01;
4498 if ( (flags & RhoValue) == 0 )
4500 if ( (flags & SigmaValue) == 0 )
4504 ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
4507 if ( (flags & SigmaValue) != 0 )
4508 UnityAddKernelInfo(kernel, args.sigma);
4583 MagickExport
void ScaleKernelInfo(
KernelInfo *kernel,
4584 const double scaling_factor,
const GeometryFlags normalize_flags)
4595 ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
4599 if ( (normalize_flags&NormalizeValue) != 0 ) {
4600 if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4602 pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4605 pos_scale = kernel->positive_range;
4608 if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4609 pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4610 ? kernel->positive_range : 1.0;
4611 neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4612 ? -kernel->negative_range : 1.0;
4615 neg_scale = pos_scale;
4618 pos_scale = scaling_factor/pos_scale;
4619 neg_scale = scaling_factor/neg_scale;
4621 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4622 if (!IsNaN(kernel->values[i]))
4623 kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4626 kernel->positive_range *= pos_scale;
4627 kernel->negative_range *= neg_scale;
4629 kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4630 kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4633 if ( scaling_factor < MagickEpsilon ) {
4635 t = kernel->positive_range;
4636 kernel->positive_range = kernel->negative_range;
4637 kernel->negative_range = t;
4638 t = kernel->maximum;
4639 kernel->maximum = kernel->minimum;
4640 kernel->minimum = 1;
4670 MagickPrivate
void ShowKernelInfo(
const KernelInfo *kernel)
4678 for (c=0, k=kernel; k != (
KernelInfo *) NULL; c++, k=k->next ) {
4680 (void) FormatLocaleFile(stderr,
"Kernel");
4682 (
void) FormatLocaleFile(stderr,
" #%lu", (
unsigned long) c );
4683 (void) FormatLocaleFile(stderr,
" \"%s",
4684 CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4685 if ( fabs(k->angle) >= MagickEpsilon )
4686 (
void) FormatLocaleFile(stderr,
"@%lg", k->angle);
4687 (void) FormatLocaleFile(stderr,
"\" of size %lux%lu%+ld%+ld",(
unsigned long)
4688 k->width,(
unsigned long) k->height,(
long) k->x,(long) k->y);
4689 (void) FormatLocaleFile(stderr,
4690 " with values from %.*lg to %.*lg\n",
4691 GetMagickPrecision(), k->minimum,
4692 GetMagickPrecision(), k->maximum);
4693 (void) FormatLocaleFile(stderr,
"Forming a output range from %.*lg to %.*lg",
4694 GetMagickPrecision(), k->negative_range,
4695 GetMagickPrecision(), k->positive_range);
4696 if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
4697 (
void) FormatLocaleFile(stderr,
" (Zero-Summing)\n");
4698 else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
4699 (void) FormatLocaleFile(stderr,
" (Normalized)\n");
4701 (
void) FormatLocaleFile(stderr,
" (Sum %.*lg)\n",
4702 GetMagickPrecision(), k->positive_range+k->negative_range);
4703 for (i=v=0; v < k->height; v++) {
4704 (void) FormatLocaleFile(stderr,
"%2lu:", (
unsigned long) v );
4705 for (u=0; u < k->width; u++, i++)
4706 if (IsNaN(k->values[i]))
4707 (
void) FormatLocaleFile(stderr,
" %*s", GetMagickPrecision()+3,
"nan");
4709 (
void) FormatLocaleFile(stderr,
" %*.*lg", GetMagickPrecision()+3,
4710 GetMagickPrecision(), (
double) k->values[i]);
4711 (void) FormatLocaleFile(stderr,
"\n");
4749 MagickExport
void UnityAddKernelInfo(
KernelInfo *kernel,
4754 UnityAddKernelInfo(kernel->next, scale);
4757 kernel->values[kernel->x+kernel->y*kernel->width] += scale;
4758 CalcKernelMetaData(kernel);
4788 MagickPrivate
void ZeroKernelNans(
KernelInfo *kernel)
4795 ZeroKernelNans(kernel->next);
4797 for (i=0; i < (kernel->width*kernel->height); i++)
4798 if (IsNaN(kernel->values[i]))
4799 kernel->values[i]=0.0;