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)
96static 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),
213static KernelInfo *ParseKernelArray(
const char *kernel_string)
219 token[MagickPathExtent];
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);
372static 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 = (double) QuantumRange/(args.sigma+1);
461 else if ( (flags & PercentValue ) != 0 )
462 args.sigma *= (double) 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);
485MagickExport
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,~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);
950MagickExport
KernelInfo *AcquireKernelBuiltIn(
const KernelInfoType type,
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 OctagonalKernel:
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*(ssize_t) 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*(ssize_t) 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*(ssize_t) 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));
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*(ssize_t) 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 )
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*(ssize_t) 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-(
size_t) 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-(
size_t) u-1) ));
1324 kernel->minimum = 1.0;
1325 kernel->maximum = kernel->values[kernel->x+kernel->y*(ssize_t) 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*(ssize_t) 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);
2314static 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);
2332static 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;
2396static 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 )
2424static void ExpandRotateKernelInfo(
KernelInfo *kernel,
const double angle)
2432DisableMSCWarning(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);
2485static 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]);
2566static ssize_t MorphologyPrimitive(
const Image *image,
Image *morphology_image,
2567 const MorphologyMethod method,
const KernelInfo *kernel,
const double bias,
2570#define MorphologyTag "Morphology/Image"
2598 assert(image != (
Image *) NULL);
2599 assert(image->signature == MagickCoreSignature);
2600 assert(morphology_image != (
Image *) NULL);
2601 assert(morphology_image->signature == MagickCoreSignature);
2603 assert(kernel->signature == MagickCoreSignature);
2605 assert(exception->signature == MagickCoreSignature);
2608 image_view=AcquireVirtualCacheView(image,exception);
2609 morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
2610 width=image->columns+kernel->width-1;
2615 case ConvolveMorphology:
2616 case DilateMorphology:
2617 case DilateIntensityMorphology:
2618 case IterativeDistanceMorphology:
2623 offset.x=(ssize_t) kernel->width-kernel->x-1;
2624 offset.y=(ssize_t) kernel->height-kernel->y-1;
2627 case ErodeMorphology:
2628 case ErodeIntensityMorphology:
2629 case HitAndMissMorphology:
2630 case ThinningMorphology:
2631 case ThickenMorphology:
2642 ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2643 "InvalidOption",
"`%s'",
"not a primitive morphology method");
2648 changes=(
size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
2650 if (changes == (
size_t *) NULL)
2651 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2652 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
2654 if ((method == ConvolveMorphology) && (kernel->width == 1))
2665#if defined(MAGICKCORE_OPENMP_SUPPORT)
2666 #pragma omp parallel for schedule(static) shared(progress,status) \
2667 magick_number_threads(image,morphology_image,image->columns,1)
2669 for (x=0; x < (ssize_t) image->columns; x++)
2672 id = GetOpenMPThreadId();
2684 if (status == MagickFalse)
2686 p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
2687 kernel->height-1,exception);
2688 q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2689 morphology_image->rows,exception);
2690 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2695 center=(ssize_t) GetPixelChannels(image)*offset.y;
2696 for (r=0; r < (ssize_t) image->rows; r++)
2701 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2715 const MagickRealType
2719 *magick_restrict pixels;
2727 channel=GetPixelChannelChannel(image,i);
2728 traits=GetPixelChannelTraits(image,channel);
2729 morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2730 if ((traits == UndefinedPixelTrait) ||
2731 (morphology_traits == UndefinedPixelTrait))
2733 if ((traits & CopyPixelTrait) != 0)
2735 SetPixelChannel(morphology_image,channel,p[center+i],q);
2738 k=(&kernel->values[kernel->height-1]);
2743 if (((image->alpha_trait & BlendPixelTrait) == 0) ||
2744 ((morphology_traits & BlendPixelTrait) == 0))
2745 for (v=0; v < (ssize_t) kernel->height; v++)
2749 pixel+=(*k)*(double) pixels[i];
2753 pixels+=(ptrdiff_t) GetPixelChannels(image);
2758 for (v=0; v < (ssize_t) kernel->height; v++)
2762 alpha=(double) (QuantumScale*(
double)
2763 GetPixelAlpha(image,pixels));
2764 pixel+=alpha*(*k)*(double) pixels[i];
2769 pixels+=(ptrdiff_t) GetPixelChannels(image);
2772 if (fabs(pixel-(
double) p[center+i]) >= MagickEpsilon)
2774 gamma=PerceptibleReciprocal(gamma);
2776 gamma*=(double) kernel->height/count;
2777 SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
2780 p+=(ptrdiff_t) GetPixelChannels(image);
2781 q+=(ptrdiff_t) GetPixelChannels(morphology_image);
2783 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
2785 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2790#if defined(MAGICKCORE_OPENMP_SUPPORT)
2794 proceed=SetImageProgress(image,MorphologyTag,progress,
2796 if (proceed == MagickFalse)
2800 morphology_image->type=image->type;
2801 morphology_view=DestroyCacheView(morphology_view);
2802 image_view=DestroyCacheView(image_view);
2803 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
2804 changed+=changes[j];
2805 changes=(
size_t *) RelinquishMagickMemory(changes);
2806 return(status ? (ssize_t) (changed/GetImageChannels(image)) : 0);
2811#if defined(MAGICKCORE_OPENMP_SUPPORT)
2812 #pragma omp parallel for schedule(static) shared(progress,status) \
2813 magick_number_threads(image,morphology_image,image->rows,1)
2815 for (y=0; y < (ssize_t) image->rows; y++)
2818 id = GetOpenMPThreadId();
2832 if (status == MagickFalse)
2834 p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
2835 kernel->height,exception);
2836 q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
2838 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
2843 center=(ssize_t) ((ssize_t) GetPixelChannels(image)*(ssize_t) width*
2844 offset.y+(ssize_t) GetPixelChannels(image)*offset.x);
2845 for (x=0; x < (ssize_t) image->columns; x++)
2850 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2867 const MagickRealType
2871 *magick_restrict pixels,
2872 *magick_restrict quantum_pixels;
2880 channel=GetPixelChannelChannel(image,i);
2881 traits=GetPixelChannelTraits(image,channel);
2882 morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2883 if ((traits == UndefinedPixelTrait) ||
2884 (morphology_traits == UndefinedPixelTrait))
2886 if ((traits & CopyPixelTrait) != 0)
2888 SetPixelChannel(morphology_image,channel,p[center+i],q);
2892 quantum_pixels=(
const Quantum *) NULL;
2894 minimum=(double) QuantumRange;
2897 case ConvolveMorphology:
2902 case DilateMorphology:
2903 case ErodeIntensityMorphology:
2910 pixel=(double) p[center+i];
2917 case ConvolveMorphology:
2938 k=(&kernel->values[kernel->width*kernel->height-1]);
2939 if (((image->alpha_trait & BlendPixelTrait) == 0) ||
2940 ((morphology_traits & BlendPixelTrait) == 0))
2945 for (v=0; v < (ssize_t) kernel->height; v++)
2947 for (u=0; u < (ssize_t) kernel->width; u++)
2950 pixel+=(*k)*(double) pixels[i];
2952 pixels+=(ptrdiff_t) GetPixelChannels(image);
2954 pixels+=(image->columns-1)*GetPixelChannels(image);
2962 for (v=0; v < (ssize_t) kernel->height; v++)
2964 for (u=0; u < (ssize_t) kernel->width; u++)
2968 alpha=(double) (QuantumScale*(
double)
2969 GetPixelAlpha(image,pixels));
2970 pixel+=alpha*(*k)*(double) pixels[i];
2974 pixels+=(ptrdiff_t) GetPixelChannels(image);
2976 pixels+=(image->columns-1)*GetPixelChannels(image);
2980 case ErodeMorphology:
2991 for (v=0; v < (ssize_t) kernel->height; v++)
2993 for (u=0; u < (ssize_t) kernel->width; u++)
2995 if (!IsNaN(*k) && (*k >= 0.5))
2997 if ((
double) pixels[i] < pixel)
2998 pixel=(double) pixels[i];
3001 pixels+=(ptrdiff_t) GetPixelChannels(image);
3003 pixels+=(image->columns-1)*GetPixelChannels(image);
3007 case DilateMorphology:
3020 k=(&kernel->values[kernel->width*kernel->height-1]);
3021 for (v=0; v < (ssize_t) kernel->height; v++)
3023 for (u=0; u < (ssize_t) kernel->width; u++)
3025 if (!IsNaN(*k) && (*k > 0.5))
3027 if ((
double) pixels[i] > pixel)
3028 pixel=(double) pixels[i];
3031 pixels+=(ptrdiff_t) GetPixelChannels(image);
3033 pixels+=(image->columns-1)*GetPixelChannels(image);
3037 case HitAndMissMorphology:
3038 case ThinningMorphology:
3039 case ThickenMorphology:
3054 for (v=0; v < (ssize_t) kernel->height; v++)
3056 for (u=0; u < (ssize_t) kernel->width; u++)
3062 if ((
double) pixels[i] < minimum)
3063 minimum=(double) pixels[i];
3068 if ((
double) pixels[i] > maximum)
3069 maximum=(double) pixels[i];
3073 pixels+=(ptrdiff_t) GetPixelChannels(image);
3075 pixels+=(image->columns-1)*GetPixelChannels(image);
3081 if (method == ThinningMorphology)
3082 pixel=(double) p[center+i]-minimum;
3084 if (method == ThickenMorphology)
3085 pixel=(double) p[center+i]+minimum;
3088 case ErodeIntensityMorphology:
3096 for (v=0; v < (ssize_t) kernel->height; v++)
3098 for (u=0; u < (ssize_t) kernel->width; u++)
3100 if (!IsNaN(*k) && (*k >= 0.5))
3102 intensity=(double) GetPixelIntensity(image,pixels);
3103 if (intensity < minimum)
3105 quantum_pixels=pixels;
3106 pixel=(double) pixels[i];
3111 pixels+=(ptrdiff_t) GetPixelChannels(image);
3113 pixels+=(image->columns-1)*GetPixelChannels(image);
3117 case DilateIntensityMorphology:
3124 k=(&kernel->values[kernel->width*kernel->height-1]);
3125 for (v=0; v < (ssize_t) kernel->height; v++)
3127 for (u=0; u < (ssize_t) kernel->width; u++)
3129 if (!IsNaN(*k) && (*k >= 0.5))
3131 intensity=(double) GetPixelIntensity(image,pixels);
3132 if (intensity > maximum)
3134 pixel=(double) pixels[i];
3135 quantum_pixels=pixels;
3140 pixels+=(ptrdiff_t) GetPixelChannels(image);
3142 pixels+=(image->columns-1)*GetPixelChannels(image);
3146 case IterativeDistanceMorphology:
3171 k=(&kernel->values[kernel->width*kernel->height-1]);
3172 for (v=0; v < (ssize_t) kernel->height; v++)
3174 for (u=0; u < (ssize_t) kernel->width; u++)
3178 if (((
double) pixels[i]+(*k)) < pixel)
3179 pixel=(double) pixels[i]+(*k);
3182 pixels+=(ptrdiff_t) GetPixelChannels(image);
3184 pixels+=(image->columns-1)*GetPixelChannels(image);
3188 case UndefinedMorphology:
3192 if (quantum_pixels != (
const Quantum *) NULL)
3194 SetPixelChannel(morphology_image,channel,quantum_pixels[i],q);
3197 gamma=PerceptibleReciprocal(gamma);
3198 SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*pixel),q);
3199 if (fabs(pixel-(
double) p[center+i]) >= MagickEpsilon)
3202 p+=(ptrdiff_t) GetPixelChannels(image);
3203 q+=(ptrdiff_t) GetPixelChannels(morphology_image);
3205 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3207 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3212#if defined(MAGICKCORE_OPENMP_SUPPORT)
3216 proceed=SetImageProgress(image,MorphologyTag,progress,image->rows);
3217 if (proceed == MagickFalse)
3221 morphology_view=DestroyCacheView(morphology_view);
3222 image_view=DestroyCacheView(image_view);
3223 for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
3224 changed+=changes[j];
3225 changes=(
size_t *) RelinquishMagickMemory(changes);
3226 return(status ? (ssize_t) (changed/GetImageChannels(image)) : -1);
3242static ssize_t MorphologyPrimitiveDirect(
Image *image,
3243 const MorphologyMethod method,
const KernelInfo *kernel,
3266 assert(image != (
Image *) NULL);
3267 assert(image->signature == MagickCoreSignature);
3269 assert(kernel->signature == MagickCoreSignature);
3271 assert(exception->signature == MagickCoreSignature);
3277 case DistanceMorphology:
3278 case VoronoiMorphology:
3283 offset.x=(ssize_t) kernel->width-kernel->x-1;
3284 offset.y=(ssize_t) kernel->height-kernel->y-1;
3297 image_view=AcquireVirtualCacheView(image,exception);
3298 morphology_view=AcquireAuthenticCacheView(image,exception);
3299 width=image->columns+kernel->width-1;
3300 for (y=0; y < (ssize_t) image->rows; y++)
3319 if (status == MagickFalse)
3321 p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(
size_t)
3322 offset.y+1,exception);
3323 q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
3325 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3330 for (x=0; x < (ssize_t) image->columns; x++)
3335 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3346 const MagickRealType
3350 *magick_restrict pixels;
3358 channel=GetPixelChannelChannel(image,i);
3359 traits=GetPixelChannelTraits(image,channel);
3360 if (traits == UndefinedPixelTrait)
3362 if ((traits & CopyPixelTrait) != 0)
3365 pixel=(double) QuantumRange;
3368 case DistanceMorphology:
3370 k=(&kernel->values[kernel->width*kernel->height-1]);
3371 for (v=0; v <= offset.y; v++)
3373 for (u=0; u < (ssize_t) kernel->width; u++)
3377 if (((
double) pixels[i]+(*k)) < pixel)
3378 pixel=(double) pixels[i]+(*k);
3381 pixels+=(ptrdiff_t) GetPixelChannels(image);
3383 pixels+=(image->columns-1)*GetPixelChannels(image);
3385 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3386 pixels=q-offset.x*(ssize_t) GetPixelChannels(image);
3387 for (u=0; u < offset.x; u++)
3389 if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
3391 if (((
double) pixels[i]+(*k)) < pixel)
3392 pixel=(double) pixels[i]+(*k);
3395 pixels+=(ptrdiff_t) GetPixelChannels(image);
3399 case VoronoiMorphology:
3401 k=(&kernel->values[kernel->width*kernel->height-1]);
3402 for (v=0; v < offset.y; v++)
3404 for (u=0; u < (ssize_t) kernel->width; u++)
3408 if (((
double) pixels[i]+(*k)) < pixel)
3409 pixel=(double) pixels[i]+(*k);
3412 pixels+=(ptrdiff_t) GetPixelChannels(image);
3414 pixels+=(image->columns-1)*GetPixelChannels(image);
3416 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3417 pixels=q-offset.x*(ssize_t) GetPixelChannels(image);
3418 for (u=0; u < offset.x; u++)
3420 if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
3422 if (((
double) pixels[i]+(*k)) < pixel)
3423 pixel=(double) pixels[i]+(*k);
3426 pixels+=(ptrdiff_t) GetPixelChannels(image);
3433 if (fabs(pixel-(
double) q[i]) > MagickEpsilon)
3435 q[i]=ClampToQuantum(pixel);
3437 p+=(ptrdiff_t) GetPixelChannels(image);
3438 q+=(ptrdiff_t) GetPixelChannels(image);
3440 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3442 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3447#if defined(MAGICKCORE_OPENMP_SUPPORT)
3451 proceed=SetImageProgress(image,MorphologyTag,progress,2*image->rows);
3452 if (proceed == MagickFalse)
3456 morphology_view=DestroyCacheView(morphology_view);
3457 image_view=DestroyCacheView(image_view);
3461 image_view=AcquireVirtualCacheView(image,exception);
3462 morphology_view=AcquireAuthenticCacheView(image,exception);
3463 for (y=(ssize_t) image->rows-1; y >= 0; y--)
3481 if (status == MagickFalse)
3483 p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(
size_t)
3484 kernel->y+1,exception);
3485 q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
3487 if ((p == (
const Quantum *) NULL) || (q == (Quantum *) NULL))
3492 p+=(ptrdiff_t) (image->columns-1)*GetPixelChannels(image);
3493 q+=(ptrdiff_t) (image->columns-1)*GetPixelChannels(image);
3494 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3499 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3510 const MagickRealType
3514 *magick_restrict pixels;
3522 channel=GetPixelChannelChannel(image,i);
3523 traits=GetPixelChannelTraits(image,channel);
3524 if (traits == UndefinedPixelTrait)
3526 if ((traits & CopyPixelTrait) != 0)
3529 pixel=(double) QuantumRange;
3532 case DistanceMorphology:
3534 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3535 for (v=offset.y; v < (ssize_t) kernel->height; v++)
3537 for (u=0; u < (ssize_t) kernel->width; u++)
3541 if (((
double) pixels[i]+(*k)) < pixel)
3542 pixel=(double) pixels[i]+(*k);
3545 pixels+=(ptrdiff_t) GetPixelChannels(image);
3547 pixels+=(image->columns-1)*GetPixelChannels(image);
3549 k=(&kernel->values[(ssize_t) kernel->width*kernel->y+kernel->x-1]);
3551 for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3553 pixels+=(ptrdiff_t) GetPixelChannels(image);
3554 if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
3556 if (((
double) pixels[i]+(*k)) < pixel)
3557 pixel=(double) pixels[i]+(*k);
3563 case VoronoiMorphology:
3565 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3566 for (v=offset.y; v < (ssize_t) kernel->height; v++)
3568 for (u=0; u < (ssize_t) kernel->width; u++)
3572 if (((
double) pixels[i]+(*k)) < pixel)
3573 pixel=(double) pixels[i]+(*k);
3576 pixels+=(ptrdiff_t) GetPixelChannels(image);
3578 pixels+=(image->columns-1)*GetPixelChannels(image);
3580 k=(&kernel->values[(ssize_t) kernel->width*(kernel->y+1)-1]);
3582 for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3584 pixels+=(ptrdiff_t) GetPixelChannels(image);
3585 if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
3587 if (((
double) pixels[i]+(*k)) < pixel)
3588 pixel=(double) pixels[i]+(*k);
3597 if (fabs(pixel-(
double) q[i]) > MagickEpsilon)
3599 q[i]=ClampToQuantum(pixel);
3601 p-=(ptrdiff_t)GetPixelChannels(image);
3602 q-=GetPixelChannels(image);
3604 if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3606 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3611#if defined(MAGICKCORE_OPENMP_SUPPORT)
3615 proceed=SetImageProgress(image,MorphologyTag,progress,2*image->rows);
3616 if (proceed == MagickFalse)
3620 morphology_view=DestroyCacheView(morphology_view);
3621 image_view=DestroyCacheView(image_view);
3622 return(status ? (ssize_t) (changed/GetImageChannels(image)) : -1);
3634MagickPrivate
Image *MorphologyApply(
const Image *image,
3635 const MorphologyMethod method,
const ssize_t iterations,
3636 const KernelInfo *kernel,
const CompositeOperator compose,
const double bias,
3680 v_info[MagickPathExtent];
3682 assert(image != (
Image *) NULL);
3683 assert(image->signature == MagickCoreSignature);
3685 assert(kernel->signature == MagickCoreSignature);
3687 assert(exception->signature == MagickCoreSignature);
3690 if ( iterations == 0 )
3691 return((
Image *) NULL);
3693 kernel_limit = (size_t) iterations;
3694 if ( iterations < 0 )
3695 kernel_limit = image->columns>image->rows ? image->columns : image->rows;
3697 verbose = IsStringTrue(GetImageArtifact(image,
"debug"));
3700 curr_image = (
Image *) image;
3701 curr_compose = image->compose;
3702 (void) curr_compose;
3703 work_image = save_image = rslt_image = (
Image *) NULL;
3713 special = MagickFalse;
3714 rslt_compose = compose;
3716 case SmoothMorphology:
3719 case OpenMorphology:
3720 case OpenIntensityMorphology:
3721 case TopHatMorphology:
3722 case CloseMorphology:
3723 case CloseIntensityMorphology:
3724 case BottomHatMorphology:
3725 case EdgeMorphology:
3728 case HitAndMissMorphology:
3729 rslt_compose = LightenCompositeOp;
3731 case ThinningMorphology:
3732 case ThickenMorphology:
3733 method_limit = kernel_limit;
3736 case DistanceMorphology:
3737 case VoronoiMorphology:
3738 special = MagickTrue;
3747 if ( special != MagickFalse )
3749 rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3750 if (rslt_image == (
Image *) NULL)
3752 if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3755 changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
3757 if (verbose != MagickFalse)
3758 (void) (
void) FormatLocaleFile(stderr,
3759 "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
3760 CommandOptionToMnemonic(MagickMorphologyOptions, method),
3761 1.0,0.0,1.0, (
double) changed);
3766 if ( method == VoronoiMorphology ) {
3768 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3770 (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
3771 MagickTrue,0,0,exception);
3772 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
3779 if ( compose != UndefinedCompositeOp )
3780 rslt_compose = compose;
3781 if ( rslt_compose == UndefinedCompositeOp )
3782 rslt_compose = NoCompositeOp;
3787 case CorrelateMorphology:
3788 case CloseMorphology:
3789 case CloseIntensityMorphology:
3790 case BottomHatMorphology:
3791 case SmoothMorphology:
3792 reflected_kernel = CloneKernelInfo(kernel);
3795 RotateKernelInfo(reflected_kernel,180);
3807 while ( method_loop < method_limit && method_changed > 0 ) {
3814 rflt_kernel = reflected_kernel;
3817 while ( norm_kernel != NULL ) {
3821 while ( stage_loop < stage_limit ) {
3825 this_kernel = norm_kernel;
3828 case ErodeMorphology:
3829 case EdgeInMorphology:
3830 primitive = ErodeMorphology;
3832 case DilateMorphology:
3833 case EdgeOutMorphology:
3834 primitive = DilateMorphology;
3836 case OpenMorphology:
3837 case TopHatMorphology:
3838 primitive = ErodeMorphology;
3839 if ( stage_loop == 2 )
3840 primitive = DilateMorphology;
3842 case OpenIntensityMorphology:
3843 primitive = ErodeIntensityMorphology;
3844 if ( stage_loop == 2 )
3845 primitive = DilateIntensityMorphology;
3847 case CloseMorphology:
3848 case BottomHatMorphology:
3849 this_kernel = rflt_kernel;
3850 primitive = DilateMorphology;
3851 if ( stage_loop == 2 )
3852 primitive = ErodeMorphology;
3854 case CloseIntensityMorphology:
3855 this_kernel = rflt_kernel;
3856 primitive = DilateIntensityMorphology;
3857 if ( stage_loop == 2 )
3858 primitive = ErodeIntensityMorphology;
3860 case SmoothMorphology:
3861 switch ( stage_loop ) {
3863 primitive = ErodeMorphology;
3866 primitive = DilateMorphology;
3869 this_kernel = rflt_kernel;
3870 primitive = DilateMorphology;
3873 this_kernel = rflt_kernel;
3874 primitive = ErodeMorphology;
3878 case EdgeMorphology:
3879 primitive = DilateMorphology;
3880 if ( stage_loop == 2 ) {
3881 save_image = curr_image;
3882 curr_image = (
Image *) image;
3883 primitive = ErodeMorphology;
3886 case CorrelateMorphology:
3896 this_kernel = rflt_kernel;
3897 primitive = ConvolveMorphology;
3902 assert( this_kernel != (
KernelInfo *) NULL );
3905 if (verbose != MagickFalse) {
3906 if ( stage_limit > 1 )
3907 (void) FormatLocaleString(v_info,MagickPathExtent,
"%s:%.20g.%.20g -> ",
3908 CommandOptionToMnemonic(MagickMorphologyOptions,method),(
double)
3909 method_loop,(
double) stage_loop);
3910 else if ( primitive != method )
3911 (void) FormatLocaleString(v_info, MagickPathExtent,
"%s:%.20g -> ",
3912 CommandOptionToMnemonic(MagickMorphologyOptions, method),(
double)
3922 while ( kernel_loop < kernel_limit && changed > 0 ) {
3926 if ( work_image == (
Image *) NULL )
3928 work_image=CloneImage(image,0,0,MagickTrue,exception);
3929 if (work_image == (
Image *) NULL)
3931 if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
3937 changed = MorphologyPrimitive(curr_image, work_image, primitive,
3938 this_kernel, bias, exception);
3939 if (verbose != MagickFalse) {
3940 if ( kernel_loop > 1 )
3941 (void) FormatLocaleFile(stderr,
"\n");
3942 (void) (
void) FormatLocaleFile(stderr,
3943 "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
3944 v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
3945 primitive),(this_kernel == rflt_kernel ) ?
"*" :
"",
3946 (
double) (method_loop+kernel_loop-1),(
double) kernel_number,
3947 (
double) count,(
double) changed);
3951 kernel_changed = (size_t) ((ssize_t) kernel_changed+changed);
3952 method_changed = (size_t) ((ssize_t) method_changed+changed);
3955 {
Image *tmp = work_image;
3956 work_image = curr_image;
3959 if ( work_image == image )
3960 work_image = (
Image *) NULL;
3964 if (verbose != MagickFalse && kernel_changed != (
size_t)changed)
3965 (void) FormatLocaleFile(stderr,
" Total %.20g",(
double) kernel_changed);
3966 if (verbose != MagickFalse && stage_loop < stage_limit)
3967 (void) FormatLocaleFile(stderr,
"\n");
3970 (void) FormatLocaleFile(stderr,
"--E-- image=0x%lx\n", (
unsigned long)image);
3971 (void) FormatLocaleFile(stderr,
" curr =0x%lx\n", (
unsigned long)curr_image);
3972 (void) FormatLocaleFile(stderr,
" work =0x%lx\n", (
unsigned long)work_image);
3973 (void) FormatLocaleFile(stderr,
" save =0x%lx\n", (
unsigned long)save_image);
3974 (void) FormatLocaleFile(stderr,
" union=0x%lx\n", (
unsigned long)rslt_image);
3987 case EdgeOutMorphology:
3988 case EdgeInMorphology:
3989 case TopHatMorphology:
3990 case BottomHatMorphology:
3991 if (verbose != MagickFalse)
3992 (void) FormatLocaleFile(stderr,
3993 "\n%s: Difference with original image",CommandOptionToMnemonic(
3994 MagickMorphologyOptions, method) );
3995 (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
3996 MagickTrue,0,0,exception);
3998 case EdgeMorphology:
3999 if (verbose != MagickFalse)
4000 (void) FormatLocaleFile(stderr,
4001 "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
4002 MagickMorphologyOptions, method) );
4003 (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
4004 MagickTrue,0,0,exception);
4005 save_image = DestroyImage(save_image);
4013 rslt_image = curr_image;
4014 else if ( rslt_compose == NoCompositeOp )
4015 {
if (verbose != MagickFalse) {
4016 if ( this_kernel->next != (
KernelInfo *) NULL )
4017 (void) FormatLocaleFile(stderr,
" (re-iterate)");
4019 (
void) FormatLocaleFile(stderr,
" (done)");
4021 rslt_image = curr_image;
4023 else if ( rslt_image == (
Image *) NULL)
4024 {
if (verbose != MagickFalse)
4025 (void) FormatLocaleFile(stderr,
" (save for compose)");
4026 rslt_image = curr_image;
4027 curr_image = (
Image *) image;
4037 if (verbose != MagickFalse)
4038 (void) FormatLocaleFile(stderr,
" (compose \"%s\")",
4039 CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
4040 (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
4042 curr_image = DestroyImage(curr_image);
4043 curr_image = (
Image *) image;
4045 if (verbose != MagickFalse)
4046 (void) FormatLocaleFile(stderr,
"\n");
4049 norm_kernel = norm_kernel->next;
4051 rflt_kernel = rflt_kernel->next;
4061 if ( curr_image == rslt_image )
4062 curr_image = (
Image *) NULL;
4063 if ( rslt_image != (
Image *) NULL )
4064 rslt_image = DestroyImage(rslt_image);
4066 if ( curr_image == rslt_image || curr_image == image )
4067 curr_image = (
Image *) NULL;
4068 if ( curr_image != (
Image *) NULL )
4069 curr_image = DestroyImage(curr_image);
4070 if ( work_image != (
Image *) NULL )
4071 work_image = DestroyImage(work_image);
4072 if ( save_image != (
Image *) NULL )
4073 save_image = DestroyImage(save_image);
4074 if ( reflected_kernel != (
KernelInfo *) NULL )
4075 reflected_kernel = DestroyKernelInfo(reflected_kernel);
4129MagickExport
Image *MorphologyImage(
const Image *image,
4130 const MorphologyMethod method,
const ssize_t iterations,
4148 assert(image != (
const Image *) NULL);
4149 assert(image->signature == MagickCoreSignature);
4151 assert(exception->signature == MagickCoreSignature);
4152 if (IsEventLogging() != MagickFalse)
4153 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4156 compose = UndefinedCompositeOp;
4162 if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
4164 artifact = GetImageArtifact(image,
"convolve:bias");
4165 if ( artifact != (
const char *) NULL) {
4166 if (IsGeometry(artifact) == MagickFalse)
4167 (void) ThrowMagickException(exception,GetMagickModule(),
4168 OptionWarning,
"InvalidSetting",
"'%s' '%s'",
4169 "convolve:bias",artifact);
4171 bias=StringToDoubleInterval(artifact,(
double) QuantumRange+1.0);
4175 artifact = GetImageArtifact(image,
"convolve:scale");
4176 if ( artifact != (
const char *) NULL ) {
4177 if (IsGeometry(artifact) == MagickFalse)
4178 (void) ThrowMagickException(exception,GetMagickModule(),
4179 OptionWarning,
"InvalidSetting",
"'%s' '%s'",
4180 "convolve:scale",artifact);
4182 if ( curr_kernel == kernel )
4183 curr_kernel = CloneKernelInfo(kernel);
4185 return((
Image *) NULL);
4186 ScaleGeometryKernelInfo(curr_kernel, artifact);
4192 artifact=GetImageArtifact(image,
"morphology:showKernel");
4193 if (IsStringTrue(artifact) != MagickFalse)
4194 ShowKernelInfo(curr_kernel);
4206 artifact = GetImageArtifact(image,
"morphology:compose");
4207 if ( artifact != (
const char *) NULL) {
4208 parse=ParseCommandOption(MagickComposeOptions,
4209 MagickFalse,artifact);
4211 (void) ThrowMagickException(exception,GetMagickModule(),
4212 OptionWarning,
"UnrecognizedComposeOperator",
"'%s' '%s'",
4213 "morphology:compose",artifact);
4215 compose=(CompositeOperator)parse;
4219 morphology_image = MorphologyApply(image,method,iterations,
4220 curr_kernel,compose,bias,exception);
4223 if ( curr_kernel != kernel )
4224 curr_kernel=DestroyKernelInfo(curr_kernel);
4225 return(morphology_image);
4258static void RotateKernelInfo(
KernelInfo *kernel,
double angle)
4262 RotateKernelInfo(kernel->next, angle);
4270 angle = fmod(angle, 360.0);
4274 if ( 337.5 < angle || angle <= 22.5 )
4278 switch (kernel->type) {
4280 case GaussianKernel:
4285 case LaplacianKernel:
4286 case ChebyshevKernel:
4287 case ManhattanKernel:
4288 case EuclideanKernel:
4302 if ( 135.0 < angle && angle <= 225.0 )
4304 if ( 225.0 < angle && angle <= 315.0 )
4312 if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
4314 if ( kernel->width == 3 && kernel->height == 3 )
4316 double t = kernel->values[0];
4317 kernel->values[0] = kernel->values[3];
4318 kernel->values[3] = kernel->values[6];
4319 kernel->values[6] = kernel->values[7];
4320 kernel->values[7] = kernel->values[8];
4321 kernel->values[8] = kernel->values[5];
4322 kernel->values[5] = kernel->values[2];
4323 kernel->values[2] = kernel->values[1];
4324 kernel->values[1] = t;
4326 if ( kernel->x != 1 || kernel->y != 1 ) {
4328 x = (ssize_t) kernel->x-1;
4329 y = (ssize_t) kernel->y-1;
4330 if ( x == y ) x = 0;
4331 else if ( x == 0 ) x = -y;
4332 else if ( x == -y ) y = 0;
4333 else if ( y == 0 ) y = x;
4334 kernel->x = (ssize_t) x+1;
4335 kernel->y = (ssize_t) y+1;
4337 angle = fmod(angle+315.0, 360.0);
4338 kernel->angle = fmod(kernel->angle+45.0, 360.0);
4341 perror(
"Unable to rotate non-3x3 kernel by 45 degrees");
4343 if ( 45.0 < fmod(angle, 180.0) && fmod(angle,180.0) <= 135.0 )
4345 if ( kernel->width == 1 || kernel->height == 1 )
4351 t = (ssize_t) kernel->width;
4352 kernel->width = kernel->height;
4353 kernel->height = (size_t) t;
4355 kernel->x = kernel->y;
4357 if ( kernel->width == 1 ) {
4358 angle = fmod(angle+270.0, 360.0);
4359 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4361 angle = fmod(angle+90.0, 360.0);
4362 kernel->angle = fmod(kernel->angle+270.0, 360.0);
4365 else if ( kernel->width == kernel->height )
4374 for( i=0, x=(ssize_t) kernel->width-1; i<=x; i++, x--)
4375 for( j=0, y=(ssize_t) kernel->height-1; j<y; j++, y--)
4376 { t = k[i+j*(ssize_t) kernel->width];
4377 k[i+j*(ssize_t) kernel->width] = k[j+x*(ssize_t) kernel->width];
4378 k[j+x*(ssize_t) kernel->width] = k[x+y*(ssize_t) kernel->width];
4379 k[x+y*(ssize_t) kernel->width] = k[y+i*(ssize_t) kernel->width];
4380 k[y+i*(ssize_t) kernel->width] = t;
4385 x = (ssize_t) (kernel->x*2-(ssize_t) kernel->width+1);
4386 y = (ssize_t) (kernel->y*2-(ssize_t) kernel->height+1);
4387 kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4388 kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
4390 angle = fmod(angle+270.0, 360.0);
4391 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4394 perror(
"Unable to rotate a non-square, non-linear kernel 90 degrees");
4396 if ( 135.0 < angle && angle <= 225.0 )
4414 j=(ssize_t) (kernel->width*kernel->height-1);
4415 for (i=0; i < j; i++, j--)
4416 t=k[i], k[i]=k[j], k[j]=t;
4418 kernel->x = (ssize_t) kernel->width - kernel->x - 1;
4419 kernel->y = (ssize_t) kernel->height - kernel->y - 1;
4420 angle = fmod(angle-180.0, 360.0);
4421 kernel->angle = fmod(kernel->angle+180.0, 360.0);
4465MagickExport
void ScaleGeometryKernelInfo (
KernelInfo *kernel,
4466 const char *geometry)
4474 SetGeometryInfo(&args);
4475 flags = ParseGeometry(geometry, &args);
4479 (void) FormatLocaleFile(stderr,
"Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4480 flags, args.rho, args.sigma, args.xi, args.psi );
4483 if ( (flags & PercentValue) != 0 )
4484 args.rho *= 0.01, args.sigma *= 0.01;
4486 if ( (flags & RhoValue) == 0 )
4488 if ( (flags & SigmaValue) == 0 )
4492 ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
4495 if ( (flags & SigmaValue) != 0 )
4496 UnityAddKernelInfo(kernel, args.sigma);
4571MagickExport
void ScaleKernelInfo(
KernelInfo *kernel,
4572 const double scaling_factor,
const GeometryFlags normalize_flags)
4583 ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
4587 if ( (normalize_flags&NormalizeValue) != 0 ) {
4588 if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4590 pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4593 pos_scale = kernel->positive_range;
4596 if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4597 pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4598 ? kernel->positive_range : 1.0;
4599 neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4600 ? -kernel->negative_range : 1.0;
4603 neg_scale = pos_scale;
4606 pos_scale = scaling_factor/pos_scale;
4607 neg_scale = scaling_factor/neg_scale;
4609 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4610 if (!IsNaN(kernel->values[i]))
4611 kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4614 kernel->positive_range *= pos_scale;
4615 kernel->negative_range *= neg_scale;
4617 kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4618 kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4621 if ( scaling_factor < MagickEpsilon ) {
4623 t = kernel->positive_range;
4624 kernel->positive_range = kernel->negative_range;
4625 kernel->negative_range = t;
4626 t = kernel->maximum;
4627 kernel->maximum = kernel->minimum;
4628 kernel->minimum = 1;
4658MagickPrivate
void ShowKernelInfo(
const KernelInfo *kernel)
4666 for (c=0, k=kernel; k != (
KernelInfo *) NULL; c++, k=k->next ) {
4668 (void) FormatLocaleFile(stderr,
"Kernel");
4670 (void) FormatLocaleFile(stderr,
" #%lu", (
unsigned long) c );
4671 (void) FormatLocaleFile(stderr,
" \"%s",
4672 CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4673 if ( fabs(k->angle) >= MagickEpsilon )
4674 (void) FormatLocaleFile(stderr,
"@%lg", k->angle);
4675 (void) FormatLocaleFile(stderr,
"\" of size %lux%lu%+ld%+ld",(
unsigned long)
4676 k->width,(
unsigned long) k->height,(
long) k->x,(
long) k->y);
4677 (void) FormatLocaleFile(stderr,
4678 " with values from %.*lg to %.*lg\n",
4679 GetMagickPrecision(), k->minimum,
4680 GetMagickPrecision(), k->maximum);
4681 (void) FormatLocaleFile(stderr,
"Forming a output range from %.*lg to %.*lg",
4682 GetMagickPrecision(), k->negative_range,
4683 GetMagickPrecision(), k->positive_range);
4684 if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
4685 (void) FormatLocaleFile(stderr,
" (Zero-Summing)\n");
4686 else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
4687 (void) FormatLocaleFile(stderr,
" (Normalized)\n");
4689 (
void) FormatLocaleFile(stderr,
" (Sum %.*lg)\n",
4690 GetMagickPrecision(), k->positive_range+k->negative_range);
4691 for (i=v=0; v < k->height; v++) {
4692 (void) FormatLocaleFile(stderr,
"%2lu:", (
unsigned long) v );
4693 for (u=0; u < k->width; u++, i++)
4694 if (IsNaN(k->values[i]))
4695 (void) FormatLocaleFile(stderr,
" %*s", GetMagickPrecision()+3,
"nan");
4697 (
void) FormatLocaleFile(stderr,
" %*.*lg", GetMagickPrecision()+3,
4698 GetMagickPrecision(), (
double) k->values[i]);
4699 (void) FormatLocaleFile(stderr,
"\n");
4737MagickExport
void UnityAddKernelInfo(
KernelInfo *kernel,
4742 UnityAddKernelInfo(kernel->next, scale);
4745 kernel->values[kernel->x+kernel->y*(ssize_t) kernel->width] += scale;
4746 CalcKernelMetaData(kernel);
4776MagickPrivate
void ZeroKernelNans(
KernelInfo *kernel)
4783 ZeroKernelNans(kernel->next);
4785 for (i=0; i < (kernel->width*kernel->height); i++)
4786 if (IsNaN(kernel->values[i]))
4787 kernel->values[i]=0.0;