53 #include "magick/studio.h"
54 #include "magick/artifact.h"
55 #include "magick/cache-view.h"
56 #include "magick/color-private.h"
57 #include "magick/channel.h"
58 #include "magick/enhance.h"
59 #include "magick/exception.h"
60 #include "magick/exception-private.h"
61 #include "magick/gem.h"
62 #include "magick/hashmap.h"
63 #include "magick/image.h"
64 #include "magick/image-private.h"
65 #include "magick/list.h"
66 #include "magick/magick.h"
67 #include "magick/memory_.h"
68 #include "magick/memory-private.h"
69 #include "magick/monitor-private.h"
70 #include "magick/morphology.h"
71 #include "magick/morphology-private.h"
72 #include "magick/option.h"
73 #include "magick/pixel-private.h"
74 #include "magick/prepress.h"
75 #include "magick/quantize.h"
76 #include "magick/registry.h"
77 #include "magick/resource_.h"
78 #include "magick/semaphore.h"
79 #include "magick/splay-tree.h"
80 #include "magick/statistic.h"
81 #include "magick/string_.h"
82 #include "magick/string-private.h"
83 #include "magick/thread-private.h"
84 #include "magick/token.h"
85 #include "magick/utility.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))
112 ExpandRotateKernelInfo(
KernelInfo *,
const double),
213 static KernelInfo *ParseKernelArray(
const char *kernel_string)
219 token[MaxTextExtent];
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,MaxTextExtent,token);
302 (void) GetNextToken(p,&p,MaxTextExtent,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=(
double *) MagickAssumeAligned(AcquireAlignedMemory(
314 kernel->width,kernel->height*
sizeof(*kernel->values)));
315 if (kernel->values == (
double *) 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,MaxTextExtent,token);
324 (void) GetNextToken(p,&p,MaxTextExtent,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,MaxTextExtent,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)
375 token[MaxTextExtent] =
"";
394 (void) GetNextToken(kernel_string,&p,MaxTextExtent,token);
395 type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
396 if ( type < 0 || type == UserDefinedKernel )
399 while (((isspace((
int) ((
unsigned char) *p)) != 0) ||
400 (*p ==
',') || (*p ==
':' )) && (*p !=
'\0') && (*p !=
';'))
403 end = strchr(p,
';');
404 if ( end == (
char *) NULL )
405 end = strchr(p,
'\0');
408 (void) memcpy(token, p, (
size_t) (end-p));
410 SetGeometryInfo(&args);
411 flags = ParseGeometry(token, &args);
415 (void) FormatLocaleFile(stderr,
"Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
416 flags, args.rho, args.sigma, args.xi, args.psi );
423 if ( (flags & WidthValue) == 0 )
432 if ( (flags & HeightValue) == 0 )
436 if ( (flags & XValue) == 0 )
439 case RectangleKernel:
440 if ( (flags & WidthValue) == 0 )
441 args.rho = args.sigma;
442 if ( args.rho < 1.0 )
444 if ( args.sigma < 1.0 )
445 args.sigma = args.rho;
446 if ( (flags & XValue) == 0 )
447 args.xi = (
double)(((ssize_t)args.rho-1)/2);
448 if ( (flags & YValue) == 0 )
449 args.psi = (
double)(((ssize_t)args.sigma-1)/2);
452 case ChebyshevKernel:
453 case ManhattanKernel:
454 case OctagonalKernel:
455 case EuclideanKernel:
456 if ( (flags & HeightValue) == 0 )
458 else if ( (flags & AspectValue ) != 0 )
459 args.sigma = QuantumRange/(args.sigma+1);
460 else if ( (flags & PercentValue ) != 0 )
461 args.sigma *= QuantumRange/100.0;
467 kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
473 if ( (flags & AreaValue) != 0 )
474 ExpandRotateKernelInfo(kernel, 45.0);
475 else if ( (flags & GreaterValue) != 0 )
476 ExpandRotateKernelInfo(kernel, 90.0);
477 else if ( (flags & LessValue) != 0 )
478 ExpandMirrorKernelInfo(kernel);
484 MagickExport
KernelInfo *AcquireKernelInfo(
const char *kernel_string)
492 token[MaxTextExtent];
497 if (kernel_string == (
const char *) NULL)
498 return(ParseKernelArray(kernel_string));
500 kernel_cache=(
char *) NULL;
501 if (*kernel_string ==
'@')
504 kernel_cache=FileToString(kernel_string+1,~0UL,exception);
505 exception=DestroyExceptionInfo(exception);
506 if (kernel_cache == (
char *) NULL)
508 p=(
const char *) kernel_cache;
512 while (GetNextToken(p,(
const char **) NULL,MaxTextExtent,token), *token !=
'\0')
518 if (isalpha((
int) ((
unsigned char) *token)) != 0)
519 new_kernel=ParseKernelName(p);
521 new_kernel=ParseKernelArray(p);
527 kernel=DestroyKernelInfo(kernel);
535 LastKernelInfo(kernel)->next=new_kernel;
540 if (p == (
char *) NULL)
544 if (kernel_cache != (
char *) NULL)
545 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 assert(
"Should not call this function" != (
char *) NULL);
973 case LaplacianKernel:
982 case DiagonalsKernel:
984 case LineJunctionsKernel:
986 case ConvexHullKernel:
1001 case RectangleKernel:
1008 case ChebyshevKernel:
1009 case ManhattanKernel:
1010 case OctangonalKernel:
1011 case EuclideanKernel:
1016 kernel=(
KernelInfo *) AcquireMagickMemory(
sizeof(*kernel));
1019 (void) memset(kernel,0,
sizeof(*kernel));
1020 kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1021 kernel->negative_range = kernel->positive_range = 0.0;
1022 kernel->type = type;
1024 kernel->signature = MagickCoreSignature;
1034 kernel->height = kernel->width = (size_t) 1;
1035 kernel->x = kernel->y = (ssize_t) 0;
1036 kernel->values=(
double *) MagickAssumeAligned(AcquireAlignedMemory(1,
1037 sizeof(*kernel->values)));
1038 if (kernel->values == (
double *) NULL)
1039 return(DestroyKernelInfo(kernel));
1040 kernel->maximum = kernel->values[0] = args->rho;
1044 case GaussianKernel:
1048 sigma = fabs(args->sigma),
1049 sigma2 = fabs(args->xi),
1052 if ( args->rho >= 1.0 )
1053 kernel->width = (size_t)args->rho*2+1;
1054 else if ( (type != DoGKernel) || (sigma >= sigma2) )
1055 kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1057 kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1058 kernel->height = kernel->width;
1059 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1060 kernel->values=(
double *) MagickAssumeAligned(AcquireAlignedMemory(
1061 kernel->width,kernel->height*
sizeof(*kernel->values)));
1062 if (kernel->values == (
double *) NULL)
1063 return(DestroyKernelInfo(kernel));
1072 if ( type == GaussianKernel || type == DoGKernel )
1074 if ( sigma > MagickEpsilon )
1075 { A = 1.0/(2.0*sigma*sigma);
1076 B = (double) (1.0/(Magick2PI*sigma*sigma));
1077 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1078 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1079 kernel->values[i] = exp(-((
double)(u*u+v*v))*A)*B;
1082 { (void) memset(kernel->values,0, (
size_t)
1083 kernel->width*kernel->height*
sizeof(*kernel->values));
1084 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1088 if ( type == DoGKernel )
1090 if ( sigma2 > MagickEpsilon )
1092 A = 1.0/(2.0*sigma*sigma);
1093 B = (double) (1.0/(Magick2PI*sigma*sigma));
1094 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1095 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1096 kernel->values[i] -= exp(-((
double)(u*u+v*v))*A)*B;
1099 kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1102 if ( type == LoGKernel )
1104 if ( sigma > MagickEpsilon )
1105 { A = 1.0/(2.0*sigma*sigma);
1106 B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1107 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1108 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1109 { R = ((double)(u*u+v*v))*A;
1110 kernel->values[i] = (1-R)*exp(-R)*B;
1114 { (void) memset(kernel->values,0, (
size_t)
1115 kernel->width*kernel->height*
sizeof(*kernel->values));
1116 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1133 CalcKernelMetaData(kernel);
1134 ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1140 sigma = fabs(args->sigma),
1143 if ( args->rho >= 1.0 )
1144 kernel->width = (size_t)args->rho*2+1;
1146 kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1148 kernel->x = (ssize_t) (kernel->width-1)/2;
1150 kernel->negative_range = kernel->positive_range = 0.0;
1151 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1152 kernel->height*
sizeof(*kernel->values));
1153 if (kernel->values == (
double *) NULL)
1154 return(DestroyKernelInfo(kernel));
1157 #define KernelRank 3
1172 v = (ssize_t) (kernel->width*KernelRank-1)/2;
1173 (void) memset(kernel->values,0, (
size_t)
1174 kernel->width*kernel->height*
sizeof(*kernel->values));
1176 if ( sigma > MagickEpsilon )
1177 { sigma *= KernelRank;
1178 alpha = 1.0/(2.0*sigma*sigma);
1179 beta= (double) (1.0/(MagickSQ2PI*sigma ));
1180 for ( u=-v; u <= v; u++) {
1181 kernel->values[(u+v)/KernelRank] +=
1182 exp(-((
double)(u*u))*alpha)*beta;
1186 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1192 if ( sigma > MagickEpsilon )
1193 { alpha = 1.0/(2.0*sigma*sigma);
1194 beta = 1.0/(MagickSQ2PI*sigma);
1195 for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1196 kernel->values[i] = exp(-((
double)(u*u))*alpha)*beta;
1199 { (void) memset(kernel->values,0, (
size_t)
1200 kernel->width*kernel->height*
sizeof(*kernel->values));
1201 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1218 CalcKernelMetaData(kernel);
1219 ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1222 RotateKernelInfo(kernel, args->xi );
1227 sigma = fabs(args->sigma),
1230 if ( args->rho < 1.0 )
1231 kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1233 kernel->width = (size_t)args->rho;
1234 kernel->x = kernel->y = 0;
1236 kernel->negative_range = kernel->positive_range = 0.0;
1237 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1238 kernel->height*
sizeof(*kernel->values));
1239 if (kernel->values == (
double *) NULL)
1240 return(DestroyKernelInfo(kernel));
1252 if ( sigma > MagickEpsilon )
1255 #define KernelRank 3
1256 v = (ssize_t) kernel->width*KernelRank;
1257 (
void) memset(kernel->values,0, (
size_t)
1258 kernel->width*
sizeof(*kernel->values));
1259 sigma *= KernelRank;
1260 A = 1.0/(2.0*sigma*sigma);
1262 for ( u=0; u < v; u++) {
1263 kernel->values[u/KernelRank] +=
1264 exp(-((
double)(u*u))*A);
1267 for (i=0; i < (ssize_t) kernel->width; i++)
1268 kernel->positive_range += kernel->values[i];
1270 A = 1.0/(2.0*sigma*sigma);
1272 for ( i=0; i < (ssize_t) kernel->width; i++)
1273 kernel->positive_range +=
1274 kernel->values[i] = exp(-((
double)(i*i))*A);
1279 { (void) memset(kernel->values,0, (
size_t)
1280 kernel->width*kernel->height*
sizeof(*kernel->values));
1281 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1282 kernel->positive_range = 1.0;
1285 kernel->minimum = 0.0;
1286 kernel->maximum = kernel->values[0];
1287 kernel->negative_range = 0.0;
1289 ScaleKernelInfo(kernel, 1.0, NormalizeValue);
1290 RotateKernelInfo(kernel, args->xi);
1293 case BinomialKernel:
1298 if (args->rho < 1.0)
1299 kernel->width = kernel->height = 3;
1301 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1302 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1304 order_f = fact(kernel->width-1);
1306 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1307 kernel->height*
sizeof(*kernel->values));
1308 if (kernel->values == (
double *) NULL)
1309 return(DestroyKernelInfo(kernel));
1312 for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
1314 alpha = order_f / ( fact((
size_t) v) * fact(kernel->height-v-1) );
1315 for ( u=0; u < (ssize_t)kernel->width; u++, i++)
1316 kernel->positive_range += kernel->values[i] = (double)
1317 (alpha * order_f / ( fact((
size_t) u) * fact(kernel->height-u-1) ));
1319 kernel->minimum = 1.0;
1320 kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
1321 kernel->negative_range = 0.0;
1328 case LaplacianKernel:
1329 {
switch ( (
int) args->rho ) {
1332 kernel=ParseKernelArray(
"3: -1,-1,-1 -1,8,-1 -1,-1,-1");
1335 kernel=ParseKernelArray(
"3: 0,-1,0 -1,4,-1 0,-1,0");
1338 kernel=ParseKernelArray(
"3: -2,1,-2 1,4,1 -2,1,-2");
1341 kernel=ParseKernelArray(
"3: 1,-2,1 -2,4,-2 1,-2,1");
1344 kernel=ParseKernelArray(
1345 "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");
1348 kernel=ParseKernelArray(
1349 "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" );
1352 kernel=ParseKernelArray(
1353 "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");
1357 kernel=ParseKernelArray(
1358 "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");
1363 kernel->type = type;
1368 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1371 kernel->type = type;
1372 RotateKernelInfo(kernel, args->rho);
1377 kernel=ParseKernelArray(
"3: 0,0,0 1,-1,0 0,0,0");
1380 kernel->type = type;
1381 RotateKernelInfo(kernel, args->rho);
1386 kernel=ParseKernelArray(
"3: 1,0,-1 1,0,-1 1,0,-1");
1389 kernel->type = type;
1390 RotateKernelInfo(kernel, args->rho);
1395 kernel=ParseKernelArray(
"3: 1,1,-1 1,-2,-1 1,1,-1");
1398 kernel->type = type;
1399 RotateKernelInfo(kernel, args->rho);
1404 kernel=ParseKernelArray(
"3: 5,-3,-3 5,0,-3 5,-3,-3");
1407 kernel->type = type;
1408 RotateKernelInfo(kernel, args->rho);
1411 case FreiChenKernel:
1415 {
switch ( (
int) args->rho ) {
1418 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1421 kernel->type = type;
1422 kernel->values[3] = +MagickSQ2;
1423 kernel->values[5] = -MagickSQ2;
1424 CalcKernelMetaData(kernel);
1427 kernel=ParseKernelArray(
"3: 1,2,0 2,0,-2 0,-2,-1");
1430 kernel->type = type;
1431 kernel->values[1] = kernel->values[3]= +MagickSQ2;
1432 kernel->values[5] = kernel->values[7]= -MagickSQ2;
1433 CalcKernelMetaData(kernel);
1434 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1437 kernel=AcquireKernelInfo(
"FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
1443 kernel=ParseKernelArray(
"3: 1,0,-1 2,0,-2 1,0,-1");
1446 kernel->type = type;
1447 kernel->values[3] = +MagickSQ2;
1448 kernel->values[5] = -MagickSQ2;
1449 CalcKernelMetaData(kernel);
1450 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1453 kernel=ParseKernelArray(
"3: 1,2,1 0,0,0 1,2,1");
1456 kernel->type = type;
1457 kernel->values[1] = +MagickSQ2;
1458 kernel->values[7] = +MagickSQ2;
1459 CalcKernelMetaData(kernel);
1460 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1463 kernel=ParseKernelArray(
"3: 2,-1,0 -1,0,1 0,1,-2");
1466 kernel->type = type;
1467 kernel->values[0] = +MagickSQ2;
1468 kernel->values[8] = -MagickSQ2;
1469 CalcKernelMetaData(kernel);
1470 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1473 kernel=ParseKernelArray(
"3: 0,1,-2 -1,0,1 2,-1,0");
1476 kernel->type = type;
1477 kernel->values[2] = -MagickSQ2;
1478 kernel->values[6] = +MagickSQ2;
1479 CalcKernelMetaData(kernel);
1480 ScaleKernelInfo(kernel, (
double) (1.0/2.0*MagickSQ2), NoValue);
1483 kernel=ParseKernelArray(
"3: 0,-1,0 1,0,1 0,-1,0");
1486 kernel->type = type;
1487 ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1490 kernel=ParseKernelArray(
"3: 1,0,-1 0,0,0 -1,0,1");
1493 kernel->type = type;
1494 ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1497 kernel=ParseKernelArray(
"3: 1,-2,1 -2,4,-2 -1,-2,1");
1500 kernel->type = type;
1501 ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1504 kernel=ParseKernelArray(
"3: -2,1,-2 1,4,1 -2,1,-2");
1507 kernel->type = type;
1508 ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1511 kernel=ParseKernelArray(
"3: 1,1,1 1,1,1 1,1,1");
1514 kernel->type = type;
1515 ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1518 if ( fabs(args->sigma) >= MagickEpsilon )
1520 RotateKernelInfo(kernel, args->sigma);
1521 else if ( args->rho > 30.0 || args->rho < -30.0 )
1523 RotateKernelInfo(kernel, args->rho);
1532 if (args->rho < 1.0)
1533 kernel->width = kernel->height = 3;
1535 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1536 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1538 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1539 kernel->height*
sizeof(*kernel->values));
1540 if (kernel->values == (
double *) NULL)
1541 return(DestroyKernelInfo(kernel));
1544 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1545 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1546 if ( (labs((
long) u)+labs((
long) v)) <= (long) kernel->x)
1547 kernel->positive_range += kernel->values[i] = args->sigma;
1549 kernel->values[i] = nan;
1550 kernel->minimum = kernel->maximum = args->sigma;
1554 case RectangleKernel:
1557 if ( type == SquareKernel )
1559 if (args->rho < 1.0)
1560 kernel->width = kernel->height = 3;
1562 kernel->width = kernel->height = (size_t) (2*args->rho+1);
1563 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1564 scale = args->sigma;
1568 if ( args->rho < 1.0 || args->sigma < 1.0 )
1569 return(DestroyKernelInfo(kernel));
1570 kernel->width = (size_t)args->rho;
1571 kernel->height = (
size_t)args->sigma;
1572 if ( args->xi < 0.0 || args->xi > (
double)kernel->width ||
1573 args->psi < 0.0 || args->psi > (double)kernel->height )
1574 return(DestroyKernelInfo(kernel));
1575 kernel->x = (ssize_t) args->xi;
1576 kernel->y = (ssize_t) args->psi;
1579 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1580 kernel->height*
sizeof(*kernel->values));
1581 if (kernel->values == (
double *) NULL)
1582 return(DestroyKernelInfo(kernel));
1585 u=(ssize_t) (kernel->width*kernel->height);
1586 for ( i=0; i < u; i++)
1587 kernel->values[i] = scale;
1588 kernel->minimum = kernel->maximum = scale;
1589 kernel->positive_range = scale*u;
1594 if (args->rho < 1.0)
1595 kernel->width = kernel->height = 5;
1597 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1598 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1600 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1601 kernel->height*
sizeof(*kernel->values));
1602 if (kernel->values == (
double *) NULL)
1603 return(DestroyKernelInfo(kernel));
1605 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1606 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1607 if ( (labs((
long) u)+labs((
long) v)) <=
1608 ((long)kernel->x + (
long)(kernel->x/2)) )
1609 kernel->positive_range += kernel->values[i] = args->sigma;
1611 kernel->values[i] = nan;
1612 kernel->minimum = kernel->maximum = args->sigma;
1618 limit = (ssize_t)(args->rho*args->rho);
1620 if (args->rho < 0.4)
1621 kernel->width = kernel->height = 9L, limit = 18L;
1623 kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
1624 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1626 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1627 kernel->height*
sizeof(*kernel->values));
1628 if (kernel->values == (
double *) NULL)
1629 return(DestroyKernelInfo(kernel));
1631 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1632 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1633 if ((u*u+v*v) <= limit)
1634 kernel->positive_range += kernel->values[i] = args->sigma;
1636 kernel->values[i] = nan;
1637 kernel->minimum = kernel->maximum = args->sigma;
1642 if (args->rho < 1.0)
1643 kernel->width = kernel->height = 5;
1645 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1646 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1648 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1649 kernel->height*
sizeof(*kernel->values));
1650 if (kernel->values == (
double *) NULL)
1651 return(DestroyKernelInfo(kernel));
1654 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1655 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1656 kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
1657 kernel->minimum = kernel->maximum = args->sigma;
1658 kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1663 if (args->rho < 1.0)
1664 kernel->width = kernel->height = 5;
1666 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1667 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1669 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1670 kernel->height*
sizeof(*kernel->values));
1671 if (kernel->values == (
double *) NULL)
1672 return(DestroyKernelInfo(kernel));
1675 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1676 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1677 kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
1678 kernel->minimum = kernel->maximum = args->sigma;
1679 kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1693 if (args->rho < args->sigma)
1695 kernel->width = ((size_t)args->sigma)*2+1;
1696 limit1 = (ssize_t)(args->rho*args->rho);
1697 limit2 = (ssize_t)(args->sigma*args->sigma);
1701 kernel->width = ((size_t)args->rho)*2+1;
1702 limit1 = (ssize_t)(args->sigma*args->sigma);
1703 limit2 = (ssize_t)(args->rho*args->rho);
1706 kernel->width = 7L, limit1 = 7L, limit2 = 11L;
1708 kernel->height = kernel->width;
1709 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1710 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
1711 kernel->height*
sizeof(*kernel->values));
1712 if (kernel->values == (
double *) NULL)
1713 return(DestroyKernelInfo(kernel));
1716 scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
1717 for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
1718 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1719 { ssize_t radius=u*u+v*v;
1720 if (limit1 < radius && radius <= limit2)
1721 kernel->positive_range += kernel->values[i] = (double) scale;
1723 kernel->values[i] = nan;
1725 kernel->minimum = kernel->maximum = (double) scale;
1726 if ( type == PeaksKernel ) {
1728 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1729 kernel->positive_range = 1.0;
1730 kernel->maximum = 1.0;
1736 kernel=AcquireKernelInfo(
"ThinSE:482");
1739 kernel->type = type;
1740 ExpandMirrorKernelInfo(kernel);
1745 kernel=AcquireKernelInfo(
"ThinSE:87");
1748 kernel->type = type;
1749 ExpandRotateKernelInfo(kernel, 90.0);
1752 case DiagonalsKernel:
1754 switch ( (
int) args->rho ) {
1759 kernel=ParseKernelArray(
"3: 0,0,0 0,-,1 1,1,-");
1762 kernel->type = type;
1763 new_kernel=ParseKernelArray(
"3: 0,0,1 0,-,1 0,1,-");
1765 return(DestroyKernelInfo(kernel));
1766 new_kernel->type = type;
1767 LastKernelInfo(kernel)->next = new_kernel;
1768 ExpandMirrorKernelInfo(kernel);
1772 kernel=ParseKernelArray(
"3: 0,0,0 0,-,1 1,1,-");
1775 kernel=ParseKernelArray(
"3: 0,0,1 0,-,1 0,1,-");
1780 kernel->type = type;
1781 RotateKernelInfo(kernel, args->sigma);
1784 case LineEndsKernel:
1786 switch ( (
int) args->rho ) {
1790 return(AcquireKernelInfo(
"LineEnds:1>;LineEnds:2>"));
1793 kernel=ParseKernelArray(
"3: 0,0,- 0,1,1 0,0,-");
1797 kernel=ParseKernelArray(
"3: 0,0,0 0,1,0 0,0,1");
1801 kernel=ParseKernelArray(
"3: 0,0,0 0,1,1 0,0,0");
1805 kernel=ParseKernelArray(
"3: 0,0,0 0,1,- 0,0,-");
1810 kernel->type = type;
1811 RotateKernelInfo(kernel, args->sigma);
1814 case LineJunctionsKernel:
1816 switch ( (
int) args->rho ) {
1820 return(AcquireKernelInfo(
"LineJunctions:1@;LineJunctions:2>"));
1823 kernel=ParseKernelArray(
"3: 1,-,1 -,1,- -,1,-");
1827 kernel=ParseKernelArray(
"3: 1,-,- -,1,- 1,-,1");
1831 kernel=ParseKernelArray(
"3: -,-,- 1,1,1 -,1,-");
1835 kernel=ParseKernelArray(
"3: 1,-,1 -,1,- 1,-,1");
1839 kernel=ParseKernelArray(
"3: -,1,- 1,1,1 -,1,-");
1844 kernel->type = type;
1845 RotateKernelInfo(kernel, args->sigma);
1852 switch ( (
int) args->rho ) {
1855 kernel=ParseKernelArray(
"3x1:0,1,0");
1858 kernel->type = type;
1859 ExpandRotateKernelInfo(kernel, 90.0);
1862 kernel=ParseKernelArray(
"4x1:0,1,1,0");
1865 kernel->type = type;
1866 ExpandRotateKernelInfo(kernel, 90.0);
1871 new_kernel=ParseKernelArray(
"4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
1873 return(DestroyKernelInfo(kernel));
1874 new_kernel->type = type;
1875 LastKernelInfo(kernel)->next = new_kernel;
1876 new_kernel=ParseKernelArray(
"4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
1878 return(DestroyKernelInfo(kernel));
1879 new_kernel->type = type;
1880 LastKernelInfo(kernel)->next = new_kernel;
1881 new_kernel=ParseKernelArray(
"4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
1883 return(DestroyKernelInfo(kernel));
1884 new_kernel->type = type;
1885 LastKernelInfo(kernel)->next = new_kernel;
1886 new_kernel=ParseKernelArray(
"4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
1888 return(DestroyKernelInfo(kernel));
1889 new_kernel->type = type;
1890 LastKernelInfo(kernel)->next = new_kernel;
1891 new_kernel=ParseKernelArray(
"3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
1893 return(DestroyKernelInfo(kernel));
1894 new_kernel->type = type;
1895 LastKernelInfo(kernel)->next = new_kernel;
1896 new_kernel=ParseKernelArray(
"3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
1898 return(DestroyKernelInfo(kernel));
1899 new_kernel->type = type;
1900 LastKernelInfo(kernel)->next = new_kernel;
1901 new_kernel=ParseKernelArray(
"3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
1903 return(DestroyKernelInfo(kernel));
1904 new_kernel->type = type;
1905 LastKernelInfo(kernel)->next = new_kernel;
1906 new_kernel=ParseKernelArray(
"3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
1908 return(DestroyKernelInfo(kernel));
1909 new_kernel->type = type;
1910 LastKernelInfo(kernel)->next = new_kernel;
1915 case ConvexHullKernel:
1920 kernel=ParseKernelArray(
"3: 1,1,- 1,0,- 1,-,0");
1923 kernel->type = type;
1924 ExpandRotateKernelInfo(kernel, 90.0);
1926 new_kernel=ParseKernelArray(
"3: 1,1,1 1,0,- -,-,0");
1928 return(DestroyKernelInfo(kernel));
1929 new_kernel->type = type;
1930 ExpandRotateKernelInfo(new_kernel, 90.0);
1931 LastKernelInfo(kernel)->next = new_kernel;
1934 case SkeletonKernel:
1936 switch ( (
int) args->rho ) {
1942 kernel=AcquireKernelInfo(
"ThinSE:482");
1945 kernel->type = type;
1946 ExpandRotateKernelInfo(kernel, 45.0);
1953 kernel=AcquireKernelInfo(
"ThinSE:482; ThinSE:87x90;");
1957 return(DestroyKernelInfo(kernel));
1958 kernel->type = type;
1959 kernel->next->type = type;
1960 ExpandRotateKernelInfo(kernel, 90.0);
1968 kernel=AcquireKernelInfo(
1969 "ThinSE:41; ThinSE:42; ThinSE:43");
1973 return(DestroyKernelInfo(kernel));
1974 if (kernel->next->next == (
KernelInfo *) NULL)
1975 return(DestroyKernelInfo(kernel));
1976 kernel->type = type;
1977 kernel->next->type = type;
1978 kernel->next->next->type = type;
1979 ExpandMirrorKernelInfo(kernel);
1995 switch ( (
int) args->rho ) {
1998 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 -,-,1");
2001 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 -,0,-");
2004 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,-,1");
2007 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,0,-");
2010 kernel=ParseKernelArray(
"3: -,0,1 0,-,1 -,0,-");
2013 kernel=ParseKernelArray(
"3: -,0,- 0,-,1 -,0,1");
2016 kernel=ParseKernelArray(
"3: -,1,1 0,-,1 -,0,-");
2019 kernel=ParseKernelArray(
"3: -,-,1 0,-,1 0,-,1");
2022 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 -,-,1");
2026 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 -,1,-");
2029 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,-,-");
2032 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 -,1,-");
2035 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 0,-,-");
2038 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 0,-,-");
2041 kernel=ParseKernelArray(
"3: 0,-,- 0,-,1 0,-,1");
2044 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,0,-");
2047 kernel=ParseKernelArray(
"3: -,1,- 0,-,1 0,1,-");
2050 kernel=ParseKernelArray(
"3: 0,1,- 0,-,1 -,1,-");
2054 kernel=ParseKernelArray(
"3: -,-,1 0,-,- -,0,-");
2057 kernel=ParseKernelArray(
"3: -,1,- -,-,1 0,-,-");
2060 kernel=ParseKernelArray(
"3: -,1,1 0,-,1 0,0,-");
2064 kernel=ParseKernelArray(
"3: 0,-,1 0,-,1 0,-,1");
2069 kernel->type = type;
2070 RotateKernelInfo(kernel, args->sigma);
2076 case ChebyshevKernel:
2078 if (args->rho < 1.0)
2079 kernel->width = kernel->height = 3;
2081 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2082 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2084 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
2085 kernel->height*
sizeof(*kernel->values));
2086 if (kernel->values == (
double *) NULL)
2087 return(DestroyKernelInfo(kernel));
2089 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2090 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2091 kernel->positive_range += ( kernel->values[i] =
2092 args->sigma*MagickMax(fabs((
double)u),fabs((
double)v)) );
2093 kernel->maximum = kernel->values[0];
2096 case ManhattanKernel:
2098 if (args->rho < 1.0)
2099 kernel->width = kernel->height = 3;
2101 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2102 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2104 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
2105 kernel->height*
sizeof(*kernel->values));
2106 if (kernel->values == (
double *) NULL)
2107 return(DestroyKernelInfo(kernel));
2109 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2110 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2111 kernel->positive_range += ( kernel->values[i] =
2112 args->sigma*(labs((
long) u)+labs((
long) v)) );
2113 kernel->maximum = kernel->values[0];
2116 case OctagonalKernel:
2118 if (args->rho < 2.0)
2119 kernel->width = kernel->height = 5;
2121 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2122 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2124 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
2125 kernel->height*
sizeof(*kernel->values));
2126 if (kernel->values == (
double *) NULL)
2127 return(DestroyKernelInfo(kernel));
2129 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2130 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2133 r1 = MagickMax(fabs((
double)u),fabs((
double)v)),
2134 r2 = floor((
double)(labs((
long)u)+labs((
long)v)+1)/1.5);
2135 kernel->positive_range += kernel->values[i] =
2136 args->sigma*MagickMax(r1,r2);
2138 kernel->maximum = kernel->values[0];
2141 case EuclideanKernel:
2143 if (args->rho < 1.0)
2144 kernel->width = kernel->height = 3;
2146 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2147 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2149 kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
2150 kernel->height*
sizeof(*kernel->values));
2151 if (kernel->values == (
double *) NULL)
2152 return(DestroyKernelInfo(kernel));
2154 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2155 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2156 kernel->positive_range += ( kernel->values[i] =
2157 args->sigma*sqrt((
double)(u*u+v*v)) );
2158 kernel->maximum = kernel->values[0];
2164 kernel=ParseKernelArray(
"1:1");
2167 kernel->type = UndefinedKernel;
2209 new_kernel=(
KernelInfo *) AcquireMagickMemory(
sizeof(*kernel));
2212 *new_kernel=(*kernel);
2215 new_kernel->values=(
double *) AcquireAlignedMemory(kernel->width,
2216 kernel->height*
sizeof(*kernel->values));
2217 if (new_kernel->values == (
double *) NULL)
2218 return(DestroyKernelInfo(new_kernel));
2219 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
2220 new_kernel->values[i]=kernel->values[i];
2224 new_kernel->next = CloneKernelInfo(kernel->next);
2225 if ( new_kernel->next == (
KernelInfo *) NULL )
2226 return(DestroyKernelInfo(new_kernel));
2260 kernel->next=DestroyKernelInfo(kernel->next);
2261 kernel->values=(
double *) RelinquishAlignedMemory(kernel->values);
2262 kernel=(
KernelInfo *) RelinquishMagickMemory(kernel);
2298 static void FlopKernelInfo(
KernelInfo *kernel)
2307 for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2308 for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2309 t=k[x], k[x]=k[r], k[r]=t;
2311 kernel->x = kernel->width - kernel->x - 1;
2312 angle = fmod(angle+180.0, 360.0);
2316 static void ExpandMirrorKernelInfo(
KernelInfo *kernel)
2324 clone = CloneKernelInfo(last);
2327 RotateKernelInfo(clone, 180);
2328 LastKernelInfo(last)->next = clone;
2331 clone = CloneKernelInfo(last);
2334 RotateKernelInfo(clone, 90);
2335 LastKernelInfo(last)->next = clone;
2338 clone = CloneKernelInfo(last);
2341 RotateKernelInfo(clone, 180);
2342 LastKernelInfo(last)->next = clone;
2381 static MagickBooleanType SameKernelInfo(
const KernelInfo *kernel1,
2388 if ( kernel1->width != kernel2->width
2389 || kernel1->height != kernel2->height
2390 || kernel1->x != kernel2->x
2391 || kernel1->y != kernel2->y )
2395 for (i=0; i < (kernel1->width*kernel1->height); i++) {
2397 if ( IsNaN(kernel1->values[i]) && !IsNaN(kernel2->values[i]) )
2399 if ( IsNaN(kernel2->values[i]) && !IsNaN(kernel1->values[i]) )
2402 if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
2409 static void ExpandRotateKernelInfo(
KernelInfo *kernel,
const double angle)
2417 DisableMSCWarning(4127)
2420 clone_info=CloneKernelInfo(last);
2423 RotateKernelInfo(clone_info,angle);
2424 if (SameKernelInfo(kernel,clone_info) != MagickFalse)
2426 LastKernelInfo(last)->next=clone_info;
2430 clone_info=DestroyKernelInfo(clone_info);
2471 static void CalcKernelMetaData(
KernelInfo *kernel)
2476 kernel->minimum = kernel->maximum = 0.0;
2477 kernel->negative_range = kernel->positive_range = 0.0;
2478 for (i=0; i < (kernel->width*kernel->height); i++)
2480 if ( fabs(kernel->values[i]) < MagickEpsilon )
2481 kernel->values[i] = 0.0;
2482 ( kernel->values[i] < 0)
2483 ? ( kernel->negative_range += kernel->values[i] )
2484 : ( kernel->positive_range += kernel->values[i] );
2485 Minimize(kernel->minimum, kernel->values[i]);
2486 Maximize(kernel->maximum, kernel->values[i]);
2563 static ssize_t MorphologyPrimitive(
const Image *image,
Image *result_image,
2564 const MorphologyMethod method,
const ChannelType channel,
2567 #define MorphologyTag "Morphology/Image"
2592 assert(image != (
Image *) NULL);
2593 assert(image->signature == MagickCoreSignature);
2594 assert(result_image != (
Image *) NULL);
2595 assert(result_image->signature == MagickCoreSignature);
2597 assert(kernel->signature == MagickCoreSignature);
2599 assert(exception->signature == MagickCoreSignature);
2604 p_view=AcquireVirtualCacheView(image,exception);
2605 q_view=AcquireAuthenticCacheView(result_image,exception);
2606 virt_width=image->columns+kernel->width-1;
2614 case ConvolveMorphology:
2615 case DilateMorphology:
2616 case DilateIntensityMorphology:
2617 case IterativeDistanceMorphology:
2619 offx = (ssize_t) kernel->width-offx-1;
2620 offy = (ssize_t) kernel->height-offy-1;
2622 case ErodeMorphology:
2623 case ErodeIntensityMorphology:
2624 case HitAndMissMorphology:
2625 case ThinningMorphology:
2626 case ThickenMorphology:
2630 assert(
"Not a Primitive Morphology Method" != (
char *) NULL);
2634 changes=(
size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
2636 if (changes == (
size_t *) NULL)
2637 ThrowFatalException(ResourceLimitFatalError,
"MemoryAllocationFailed");
2638 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2640 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,result_image,image->columns,1)
2663 for (x=0; x < (ssize_t) image->columns; x++)
2666 id = GetOpenMPThreadId();
2672 *magick_restrict p_indexes;
2678 *magick_restrict q_indexes;
2686 if (status == MagickFalse)
2688 p=GetCacheViewVirtualPixels(p_view,x,-offy,1,image->rows+kernel->height-1,
2690 q=GetCacheViewAuthenticPixels(q_view,x,0,1,result_image->rows,exception);
2696 p_indexes=GetCacheViewVirtualIndexQueue(p_view);
2697 q_indexes=GetCacheViewAuthenticIndexQueue(q_view);
2702 for (y=0; y < (ssize_t) image->rows; y++)
2714 *magick_restrict k_pixels;
2717 *magick_restrict k_indexes;
2723 if (image->colorspace == CMYKColorspace)
2724 SetPixelIndex(q_indexes+y,GetPixelIndex(p_indexes+y+r));
2731 result.index = bias;
2740 k = &kernel->values[ kernel->height-1 ];
2742 k_indexes = p_indexes+y;
2743 if ( ((channel & SyncChannels) == 0 ) ||
2744 (image->matte == MagickFalse) )
2748 for (v=0; v < (ssize_t) kernel->height; v++) {
2749 if ( IsNaN(*k) )
continue;
2750 result.red += (*k)*GetPixelRed(k_pixels);
2751 result.green += (*k)*GetPixelGreen(k_pixels);
2752 result.blue += (*k)*GetPixelBlue(k_pixels);
2753 result.opacity += (*k)*GetPixelOpacity(k_pixels);
2754 if ( image->colorspace == CMYKColorspace)
2755 result.index += (*k)*(*k_indexes);
2760 if ((channel & RedChannel) != 0)
2761 SetPixelRed(q,ClampToQuantum(result.red));
2762 if ((channel & GreenChannel) != 0)
2763 SetPixelGreen(q,ClampToQuantum(result.green));
2764 if ((channel & BlueChannel) != 0)
2765 SetPixelBlue(q,ClampToQuantum(result.blue));
2766 if (((channel & OpacityChannel) != 0) &&
2767 (image->matte != MagickFalse))
2768 SetPixelOpacity(q,ClampToQuantum(result.opacity));
2769 if (((channel & IndexChannel) != 0) &&
2770 (image->colorspace == CMYKColorspace))
2771 SetPixelIndex(q_indexes+y,ClampToQuantum(result.index));
2789 for (v=0; v < (ssize_t) kernel->height; v++) {
2790 if ( IsNaN(*k) )
continue;
2791 alpha=QuantumScale*(QuantumRange-GetPixelOpacity(k_pixels));
2795 result.red += alpha*GetPixelRed(k_pixels);
2796 result.green += alpha*GetPixelGreen(k_pixels);
2797 result.blue += alpha*GetPixelBlue(k_pixels);
2798 result.opacity += (*k)*GetPixelOpacity(k_pixels);
2799 if ( image->colorspace == CMYKColorspace)
2800 result.index += alpha*(*k_indexes);
2806 gamma=PerceptibleReciprocal(gamma);
2808 gamma*=(double) kernel->height/count;
2809 SetPixelRed(q,ClampToQuantum(gamma*result.red));
2810 SetPixelGreen(q,ClampToQuantum(gamma*result.green));
2811 SetPixelBlue(q,ClampToQuantum(gamma*result.blue));
2812 SetPixelOpacity(q,ClampToQuantum(result.opacity));
2813 if (image->colorspace == CMYKColorspace)
2814 SetPixelIndex(q_indexes+y,ClampToQuantum(gamma*result.index));
2818 if ( ( p[r].red != GetPixelRed(q))
2819 || ( p[r].green != GetPixelGreen(q))
2820 || ( p[r].blue != GetPixelBlue(q))
2821 || ( (image->matte != MagickFalse) &&
2822 (p[r].opacity != GetPixelOpacity(q)))
2823 || ( (image->colorspace == CMYKColorspace) &&
2824 (GetPixelIndex(p_indexes+y+r) != GetPixelIndex(q_indexes+y))) )
2829 if ( SyncCacheViewAuthenticPixels(q_view,exception) == MagickFalse)
2831 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2836 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2840 proceed=SetImageProgress(image,MorphologyTag,progress,image->rows);
2841 if (proceed == MagickFalse)
2845 result_image->type=image->type;
2846 q_view=DestroyCacheView(q_view);
2847 p_view=DestroyCacheView(p_view);
2848 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2849 changed+=changes[i];
2850 changes=(
size_t *) RelinquishMagickMemory(changes);
2851 return(status ? (ssize_t) changed : 0);
2857 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2858 #pragma omp parallel for schedule(static) shared(progress,status) \
2859 magick_number_threads(image,result_image,image->rows,1)
2861 for (y=0; y < (ssize_t) image->rows; y++)
2864 id = GetOpenMPThreadId();
2870 *magick_restrict p_indexes;
2876 *magick_restrict q_indexes;
2884 if (status == MagickFalse)
2886 p=GetCacheViewVirtualPixels(p_view, -offx, y-offy, virt_width,
2887 kernel->height, exception);
2888 q=GetCacheViewAuthenticPixels(q_view,0,y,result_image->columns,1,
2895 p_indexes=GetCacheViewVirtualIndexQueue(p_view);
2896 q_indexes=GetCacheViewAuthenticIndexQueue(q_view);
2899 r = virt_width*offy + offx;
2901 for (x=0; x < (ssize_t) image->columns; x++)
2913 *magick_restrict k_pixels;
2916 *magick_restrict k_indexes;
2927 if (image->colorspace == CMYKColorspace)
2928 SetPixelIndex(q_indexes+x,GetPixelIndex(p_indexes+x+r));
2935 min.index = (double) QuantumRange;
2942 result.red = (double) p[r].red;
2943 result.green = (double) p[r].green;
2944 result.blue = (double) p[r].blue;
2945 result.opacity = QuantumRange - (double) p[r].opacity;
2947 if ( image->colorspace == CMYKColorspace)
2948 result.index = (double) GetPixelIndex(p_indexes+x+r);
2951 case ConvolveMorphology:
2957 result.index = bias;
2959 case DilateIntensityMorphology:
2960 case ErodeIntensityMorphology:
2969 case ConvolveMorphology:
2988 k = &kernel->values[ kernel->width*kernel->height-1 ];
2990 k_indexes = p_indexes+x;
2991 if ( ((channel & SyncChannels) == 0 ) ||
2992 (image->matte == MagickFalse) )
2996 for (v=0; v < (ssize_t) kernel->height; v++) {
2997 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2998 if ( IsNaN(*k) )
continue;
2999 result.red += (*k)*k_pixels[u].red;
3000 result.green += (*k)*k_pixels[u].green;
3001 result.blue += (*k)*k_pixels[u].blue;
3002 result.opacity += (*k)*k_pixels[u].opacity;
3003 if ( image->colorspace == CMYKColorspace)
3004 result.index += (*k)*GetPixelIndex(k_indexes+u);
3006 k_pixels += virt_width;
3007 k_indexes += virt_width;
3009 if ((channel & RedChannel) != 0)
3010 SetPixelRed(q,ClampToQuantum((MagickRealType) result.red));
3011 if ((channel & GreenChannel) != 0)
3012 SetPixelGreen(q,ClampToQuantum((MagickRealType) result.green));
3013 if ((channel & BlueChannel) != 0)
3014 SetPixelBlue(q,ClampToQuantum((MagickRealType) result.blue));
3015 if (((channel & OpacityChannel) != 0) &&
3016 (image->matte != MagickFalse))
3017 SetPixelOpacity(q,ClampToQuantum((MagickRealType) result.opacity));
3018 if (((channel & IndexChannel) != 0) &&
3019 (image->colorspace == CMYKColorspace))
3020 SetPixelIndex(q_indexes+x,ClampToQuantum(result.index));
3036 for (v=0; v < (ssize_t) kernel->height; v++) {
3037 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3038 if ( IsNaN(*k) )
continue;
3039 alpha=QuantumScale*(QuantumRange-k_pixels[u].opacity);
3043 result.red += alpha*k_pixels[u].red;
3044 result.green += alpha*k_pixels[u].green;
3045 result.blue += alpha*k_pixels[u].blue;
3046 result.opacity += (*k)*k_pixels[u].opacity;
3047 if ( image->colorspace == CMYKColorspace)
3048 result.index+=alpha*GetPixelIndex(k_indexes+u);
3050 k_pixels += virt_width;
3051 k_indexes += virt_width;
3054 gamma=PerceptibleReciprocal(gamma);
3056 gamma*=(double) kernel->height*kernel->width/count;
3057 SetPixelRed(q,ClampToQuantum((MagickRealType) (gamma*result.red)));
3058 SetPixelGreen(q,ClampToQuantum((MagickRealType) (gamma*result.green)));
3059 SetPixelBlue(q,ClampToQuantum((MagickRealType) (gamma*result.blue)));
3060 SetPixelOpacity(q,ClampToQuantum(result.opacity));
3061 if (image->colorspace == CMYKColorspace)
3062 SetPixelIndex(q_indexes+x,ClampToQuantum((MagickRealType) (gamma*
3067 case ErodeMorphology:
3078 k_indexes = p_indexes+x;
3079 for (v=0; v < (ssize_t) kernel->height; v++) {
3080 for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3081 if ( IsNaN(*k) || (*k) < 0.5 )
continue;
3082 Minimize(min.red, (
double) k_pixels[u].red);
3083 Minimize(min.green, (
double) k_pixels[u].green);
3084 Minimize(min.blue, (
double) k_pixels[u].blue);
3085 Minimize(min.opacity,
3086 QuantumRange-(
double) k_pixels[u].opacity);
3087 if ( image->colorspace == CMYKColorspace)
3088 Minimize(min.index,(
double) GetPixelIndex(k_indexes+u));
3090 k_pixels += virt_width;
3091 k_indexes += virt_width;
3095 case DilateMorphology:
3107 k = &kernel->values[ kernel->width*kernel->height-1 ];
3109 k_indexes = p_indexes+x;
3110 for (v=0; v < (ssize_t) kernel->height; v++) {
3111 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3112 if ( IsNaN(*k) || (*k) < 0.5 )
continue;
3113 Maximize(max.red, (
double) k_pixels[u].red);
3114 Maximize(max.green, (
double) k_pixels[u].green);
3115 Maximize(max.blue, (
double) k_pixels[u].blue);
3116 Maximize(max.opacity,
3117 QuantumRange-(
double) k_pixels[u].opacity);
3118 if ( image->colorspace == CMYKColorspace)
3119 Maximize(max.index, (
double) GetPixelIndex(
3122 k_pixels += virt_width;
3123 k_indexes += virt_width;
3127 case HitAndMissMorphology:
3128 case ThinningMorphology:
3129 case ThickenMorphology:
3143 k_indexes = p_indexes+x;
3144 for (v=0; v < (ssize_t) kernel->height; v++) {
3145 for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3146 if ( IsNaN(*k) )
continue;
3149 Minimize(min.red, (
double) k_pixels[u].red);
3150 Minimize(min.green, (
double) k_pixels[u].green);
3151 Minimize(min.blue, (
double) k_pixels[u].blue);
3152 Minimize(min.opacity,
3153 QuantumRange-(
double) k_pixels[u].opacity);
3154 if ( image->colorspace == CMYKColorspace)
3155 Minimize(min.index,(
double) GetPixelIndex(
3158 else if ( (*k) < 0.3 )
3160 Maximize(max.red, (
double) k_pixels[u].red);
3161 Maximize(max.green, (
double) k_pixels[u].green);
3162 Maximize(max.blue, (
double) k_pixels[u].blue);
3163 Maximize(max.opacity,
3164 QuantumRange-(
double) k_pixels[u].opacity);
3165 if ( image->colorspace == CMYKColorspace)
3166 Maximize(max.index, (
double) GetPixelIndex(
3170 k_pixels += virt_width;
3171 k_indexes += virt_width;
3174 min.red -= max.red; Maximize( min.red, 0.0 );
3175 min.green -= max.green; Maximize( min.green, 0.0 );
3176 min.blue -= max.blue; Maximize( min.blue, 0.0 );
3177 min.opacity -= max.opacity; Maximize( min.opacity, 0.0 );
3178 min.index -= max.index; Maximize( min.index, 0.0 );
3181 case ErodeIntensityMorphology:
3192 k_indexes = p_indexes+x;
3193 for (v=0; v < (ssize_t) kernel->height; v++) {
3194 for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3195 if ( IsNaN(*k) || (*k) < 0.5 )
continue;
3196 if ( result.red == 0.0 ||
3197 GetPixelIntensity(image,&(k_pixels[u])) < GetPixelIntensity(result_image,q) ) {
3201 if ( result.red > 0.0 ) changes[id]++;
3205 k_pixels += virt_width;
3206 k_indexes += virt_width;
3210 case DilateIntensityMorphology:
3221 k = &kernel->values[ kernel->width*kernel->height-1 ];
3223 k_indexes = p_indexes+x;
3224 for (v=0; v < (ssize_t) kernel->height; v++) {
3225 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3226 if ( IsNaN(*k) || (*k) < 0.5 )
continue;
3227 if ( result.red == 0.0 ||
3228 GetPixelIntensity(image,&(k_pixels[u])) > GetPixelIntensity(result_image,q) ) {
3231 if ( result.red > 0.0 ) changes[id]++;
3235 k_pixels += virt_width;
3236 k_indexes += virt_width;
3240 case IterativeDistanceMorphology:
3264 k = &kernel->values[ kernel->width*kernel->height-1 ];
3266 k_indexes = p_indexes+x;
3267 for (v=0; v < (ssize_t) kernel->height; v++) {
3268 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3269 if ( IsNaN(*k) )
continue;
3270 Minimize(result.red, (*k)+k_pixels[u].red);
3271 Minimize(result.green, (*k)+k_pixels[u].green);
3272 Minimize(result.blue, (*k)+k_pixels[u].blue);
3273 Minimize(result.opacity, (*k)+QuantumRange-k_pixels[u].opacity);
3274 if ( image->colorspace == CMYKColorspace)
3275 Minimize(result.index,(*k)+GetPixelIndex(k_indexes+u));
3277 k_pixels += virt_width;
3278 k_indexes += virt_width;
3282 case UndefinedMorphology:
3294 case HitAndMissMorphology:
3295 case ErodeMorphology:
3298 case DilateMorphology:
3301 case ThinningMorphology:
3303 result.red -= min.red;
3304 result.green -= min.green;
3305 result.blue -= min.blue;
3306 result.opacity -= min.opacity;
3307 result.index -= min.index;
3309 case ThickenMorphology:
3311 result.red += min.red;
3312 result.green += min.green;
3313 result.blue += min.blue;
3314 result.opacity += min.opacity;
3315 result.index += min.index;
3323 case UndefinedMorphology:
3324 case ConvolveMorphology:
3325 case DilateIntensityMorphology:
3326 case ErodeIntensityMorphology:
3329 if ((channel & RedChannel) != 0)
3330 SetPixelRed(q,ClampToQuantum(result.red));
3331 if ((channel & GreenChannel) != 0)
3332 SetPixelGreen(q,ClampToQuantum(result.green));
3333 if ((channel & BlueChannel) != 0)
3334 SetPixelBlue(q,ClampToQuantum(result.blue));
3335 if ((channel & OpacityChannel) != 0
3336 && image->matte != MagickFalse )
3337 SetPixelAlpha(q,ClampToQuantum(result.opacity));
3338 if (((channel & IndexChannel) != 0) &&
3339 (image->colorspace == CMYKColorspace))
3340 SetPixelIndex(q_indexes+x,ClampToQuantum(result.index));
3344 if ( ( p[r].red != GetPixelRed(q) )
3345 || ( p[r].green != GetPixelGreen(q) )
3346 || ( p[r].blue != GetPixelBlue(q) )
3347 || ( (image->matte != MagickFalse) &&
3348 (p[r].opacity != GetPixelOpacity(q)))
3349 || ( (image->colorspace == CMYKColorspace) &&
3350 (GetPixelIndex(p_indexes+x+r) != GetPixelIndex(q_indexes+x))) )
3355 if ( SyncCacheViewAuthenticPixels(q_view,exception) == MagickFalse)
3357 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3362 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3366 proceed=SetImageProgress(image,MorphologyTag,progress,image->rows);
3367 if (proceed == MagickFalse)
3371 q_view=DestroyCacheView(q_view);
3372 p_view=DestroyCacheView(p_view);
3373 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3374 changed+=changes[i];
3375 changes=(
size_t *) RelinquishMagickMemory(changes);
3376 return(status ? (ssize_t)changed : -1);
3391 static ssize_t MorphologyPrimitiveDirect(
Image *image,
3392 const MorphologyMethod method,
const ChannelType channel,
3416 assert(image != (
Image *) NULL);
3417 assert(image->signature == MagickCoreSignature);
3419 assert(kernel->signature == MagickCoreSignature);
3421 assert(exception->signature == MagickCoreSignature);
3429 case DistanceMorphology:
3430 case VoronoiMorphology:
3432 offx = (ssize_t) kernel->width-offx-1;
3433 offy = (ssize_t) kernel->height-offy-1;
3436 case ?????Morphology:
3441 assert(
"Not a PrimativeDirect Morphology Method" != (
char *) NULL);
3447 virt_view=AcquireVirtualCacheView(image,exception);
3448 auth_view=AcquireAuthenticCacheView(image,exception);
3449 virt_width=image->columns+kernel->width-1;
3451 for (y=0; y < (ssize_t) image->rows; y++)
3457 *magick_restrict p_indexes;
3463 *magick_restrict q_indexes;
3478 if (status == MagickFalse)
3480 p=GetCacheViewVirtualPixels(virt_view, -offx, y-offy, virt_width, (
size_t) offy+1,
3482 q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
3486 if (status == MagickFalse)
3488 p_indexes=GetCacheViewVirtualIndexQueue(virt_view);
3489 q_indexes=GetCacheViewAuthenticIndexQueue(auth_view);
3492 r = (ssize_t) virt_width*offy + offx;
3494 for (x=0; x < (ssize_t) image->columns; x++)
3506 *magick_restrict k_pixels;
3509 *magick_restrict k_indexes;
3515 GetMagickPixelPacket(image,&result);
3516 SetMagickPixelPacket(image,q,q_indexes,&result);
3517 if ( method != VoronoiMorphology )
3518 result.opacity = QuantumRange - result.opacity;
3521 case DistanceMorphology:
3523 k = &kernel->values[ kernel->width*kernel->height-1 ];
3525 k_indexes = p_indexes+x;
3526 for (v=0; v <= (ssize_t) offy; v++) {
3527 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3528 if ( IsNaN(*k) )
continue;
3529 Minimize(result.red, (*k)+k_pixels[u].red);
3530 Minimize(result.green, (*k)+k_pixels[u].green);
3531 Minimize(result.blue, (*k)+k_pixels[u].blue);
3532 Minimize(result.opacity, (*k)+QuantumRange-k_pixels[u].opacity);
3533 if ( image->colorspace == CMYKColorspace)
3534 Minimize(result.index, (*k)+GetPixelIndex(k_indexes+u));
3536 k_pixels += virt_width;
3537 k_indexes += virt_width;
3540 k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3542 k_indexes = q_indexes-offx;
3543 for (u=0; u < (ssize_t) offx; u++, k--) {
3544 if ( x+u-offx < 0 )
continue;
3545 if ( IsNaN(*k) )
continue;
3546 Minimize(result.red, (*k)+k_pixels[u].red);
3547 Minimize(result.green, (*k)+k_pixels[u].green);
3548 Minimize(result.blue, (*k)+k_pixels[u].blue);
3549 Minimize(result.opacity, (*k)+QuantumRange-k_pixels[u].opacity);
3550 if ( image->colorspace == CMYKColorspace)
3551 Minimize(result.index, (*k)+GetPixelIndex(k_indexes+u));
3554 case VoronoiMorphology:
3562 k = &kernel->values[ kernel->width*kernel->height-1 ];
3564 k_indexes = p_indexes+x;
3565 for (v=0; v <= (ssize_t) offy; v++) {
3566 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3567 if ( IsNaN(*k) )
continue;
3568 if( result.opacity > (*k)+k_pixels[u].opacity )
3570 SetMagickPixelPacket(image,&k_pixels[u],&k_indexes[u],
3572 result.opacity += *k;
3575 k_pixels += virt_width;
3576 k_indexes += virt_width;
3579 k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3581 k_indexes = q_indexes-offx;
3582 for (u=0; u < (ssize_t) offx; u++, k--) {
3583 if ( x+u-offx < 0 )
continue;
3584 if ( IsNaN(*k) )
continue;
3585 if( result.opacity > (*k)+k_pixels[u].opacity )
3587 SetMagickPixelPacket(image,&k_pixels[u],&k_indexes[u],
3589 result.opacity += *k;
3599 case VoronoiMorphology:
3600 SetPixelPacket(image,&result,q,q_indexes);
3603 if ((channel & RedChannel) != 0)
3604 SetPixelRed(q,ClampToQuantum(result.red));
3605 if ((channel & GreenChannel) != 0)
3606 SetPixelGreen(q,ClampToQuantum(result.green));
3607 if ((channel & BlueChannel) != 0)
3608 SetPixelBlue(q,ClampToQuantum(result.blue));
3609 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
3610 SetPixelAlpha(q,ClampToQuantum(result.opacity));
3611 if (((channel & IndexChannel) != 0) &&
3612 (image->colorspace == CMYKColorspace))
3613 SetPixelIndex(q_indexes+x,ClampToQuantum(result.index));
3617 if ( ( p[r].red != GetPixelRed(q) )
3618 || ( p[r].green != GetPixelGreen(q) )
3619 || ( p[r].blue != GetPixelBlue(q) )
3620 || ( (image->matte != MagickFalse) &&
3621 (p[r].opacity != GetPixelOpacity(q)))
3622 || ( (image->colorspace == CMYKColorspace) &&
3623 (GetPixelIndex(p_indexes+x+r) != GetPixelIndex(q_indexes+x))) )
3630 if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3632 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3634 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3638 if (SetImageProgress(image,MorphologyTag,progress,image->rows) == MagickFalse )
3645 for (y=(ssize_t)image->rows-1; y >= 0; y--)
3651 *magick_restrict p_indexes;
3657 *magick_restrict q_indexes;
3665 if (status == MagickFalse)
3674 p=GetCacheViewVirtualPixels(virt_view, -offx, y, virt_width, (
size_t) kernel->y+1,
3676 q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
3680 if (status == MagickFalse)
3682 p_indexes=GetCacheViewVirtualIndexQueue(virt_view);
3683 q_indexes=GetCacheViewAuthenticIndexQueue(auth_view);
3686 p += image->columns-1;
3687 q += image->columns-1;
3692 for (x=(ssize_t)image->columns-1; x >= 0; x--)
3704 *magick_restrict k_pixels;
3707 *magick_restrict k_indexes;
3713 GetMagickPixelPacket(image,&result);
3714 SetMagickPixelPacket(image,q,q_indexes,&result);
3715 if ( method != VoronoiMorphology )
3716 result.opacity = QuantumRange - result.opacity;
3719 case DistanceMorphology:
3721 k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3723 k_indexes = p_indexes+x;
3724 for (v=offy; v < (ssize_t) kernel->height; v++) {
3725 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3726 if ( IsNaN(*k) )
continue;
3727 Minimize(result.red, (*k)+k_pixels[u].red);
3728 Minimize(result.green, (*k)+k_pixels[u].green);
3729 Minimize(result.blue, (*k)+k_pixels[u].blue);
3730 Minimize(result.opacity, (*k)+QuantumRange-k_pixels[u].opacity);
3731 if ( image->colorspace == CMYKColorspace)
3732 Minimize(result.index,(*k)+GetPixelIndex(k_indexes+u));
3734 k_pixels += virt_width;
3735 k_indexes += virt_width;
3738 k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3740 k_indexes = q_indexes-offx;
3741 for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3742 if ( (x+u-offx) >= (ssize_t)image->columns )
continue;
3743 if ( IsNaN(*k) )
continue;
3744 Minimize(result.red, (*k)+k_pixels[u].red);
3745 Minimize(result.green, (*k)+k_pixels[u].green);
3746 Minimize(result.blue, (*k)+k_pixels[u].blue);
3747 Minimize(result.opacity, (*k)+QuantumRange-k_pixels[u].opacity);
3748 if ( image->colorspace == CMYKColorspace)
3749 Minimize(result.index, (*k)+GetPixelIndex(k_indexes+u));
3752 case VoronoiMorphology:
3758 k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3760 k_indexes = p_indexes+x;
3761 for (v=offy; v < (ssize_t) kernel->height; v++) {
3762 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3763 if ( IsNaN(*k) )
continue;
3764 if( result.opacity > (*k)+k_pixels[u].opacity )
3766 SetMagickPixelPacket(image,&k_pixels[u],&k_indexes[u],
3768 result.opacity += *k;
3771 k_pixels += virt_width;
3772 k_indexes += virt_width;
3775 k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3777 k_indexes = q_indexes-offx;
3778 for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3779 if ( (x+u-offx) >= (ssize_t)image->columns )
continue;
3780 if ( IsNaN(*k) )
continue;
3781 if( result.opacity > (*k)+k_pixels[u].opacity )
3783 SetMagickPixelPacket(image,&k_pixels[u],&k_indexes[u],
3785 result.opacity += *k;
3795 case VoronoiMorphology:
3796 SetPixelPacket(image,&result,q,q_indexes);
3799 if ((channel & RedChannel) != 0)
3800 SetPixelRed(q,ClampToQuantum(result.red));
3801 if ((channel & GreenChannel) != 0)
3802 SetPixelGreen(q,ClampToQuantum(result.green));
3803 if ((channel & BlueChannel) != 0)
3804 SetPixelBlue(q,ClampToQuantum(result.blue));
3805 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
3806 SetPixelAlpha(q,ClampToQuantum(result.opacity));
3807 if (((channel & IndexChannel) != 0) &&
3808 (image->colorspace == CMYKColorspace))
3809 SetPixelIndex(q_indexes+x,ClampToQuantum(result.index));
3813 if ( ( p[r].red != GetPixelRed(q) )
3814 || ( p[r].green != GetPixelGreen(q) )
3815 || ( p[r].blue != GetPixelBlue(q) )
3816 || ( (image->matte != MagickFalse) &&
3817 (p[r].opacity != GetPixelOpacity(q)))
3818 || ( (image->colorspace == CMYKColorspace) &&
3819 (GetPixelIndex(p_indexes+x+r) != GetPixelIndex(q_indexes+x))) )
3825 if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3827 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3829 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3833 if ( SetImageProgress(image,MorphologyTag,progress,image->rows) == MagickFalse )
3839 auth_view=DestroyCacheView(auth_view);
3840 virt_view=DestroyCacheView(virt_view);
3841 return(status ? (ssize_t) changed : -1);
3852 MagickExport
Image *MorphologyApply(
const Image *image,
const ChannelType
3853 channel,
const MorphologyMethod method,
const ssize_t iterations,
3854 const KernelInfo *kernel,
const CompositeOperator compose,
3898 v_info[MaxTextExtent];
3900 assert(image != (
Image *) NULL);
3901 assert(image->signature == MagickCoreSignature);
3903 assert(kernel->signature == MagickCoreSignature);
3905 assert(exception->signature == MagickCoreSignature);
3908 if ( iterations == 0 )
3909 return((
Image *) NULL);
3911 kernel_limit = (size_t) iterations;
3912 if ( iterations < 0 )
3913 kernel_limit = image->columns>image->rows ? image->columns : image->rows;
3915 verbose = IsMagickTrue(GetImageArtifact(image,
"debug"));
3918 curr_image = (
Image *) image;
3919 curr_compose = image->compose;
3920 (void) curr_compose;
3921 work_image = save_image = rslt_image = (
Image *) NULL;
3931 special = MagickFalse;
3932 rslt_compose = compose;
3934 case SmoothMorphology:
3937 case OpenMorphology:
3938 case OpenIntensityMorphology:
3939 case TopHatMorphology:
3940 case CloseMorphology:
3941 case CloseIntensityMorphology:
3942 case BottomHatMorphology:
3943 case EdgeMorphology:
3946 case HitAndMissMorphology:
3947 rslt_compose = LightenCompositeOp;
3949 case ThinningMorphology:
3950 case ThickenMorphology:
3951 method_limit = kernel_limit;
3954 case DistanceMorphology:
3955 case VoronoiMorphology:
3956 special = MagickTrue;
3965 if ( special != MagickFalse )
3967 rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3968 if (rslt_image == (
Image *) NULL)
3970 if (SetImageStorageClass(rslt_image,DirectClass) == MagickFalse)
3972 InheritException(exception,&rslt_image->exception);
3976 changed = MorphologyPrimitiveDirect(rslt_image, method,
3977 channel, kernel, exception);
3979 if ( verbose != MagickFalse )
3980 (void) (
void) FormatLocaleFile(stderr,
3981 "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
3982 CommandOptionToMnemonic(MagickMorphologyOptions, method),
3983 1.0,0.0,1.0, (
double) changed);
3988 if ( method == VoronoiMorphology ) {
3990 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel);
3991 (void) CompositeImageChannel(rslt_image, DefaultChannels,
3992 CopyOpacityCompositeOp, image, 0, 0);
3993 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel);
3999 if ( compose != UndefinedCompositeOp )
4000 rslt_compose = compose;
4001 if ( rslt_compose == UndefinedCompositeOp )
4002 rslt_compose = NoCompositeOp;
4007 case CorrelateMorphology:
4008 case CloseMorphology:
4009 case CloseIntensityMorphology:
4010 case BottomHatMorphology:
4011 case SmoothMorphology:
4012 reflected_kernel = CloneKernelInfo(kernel);
4015 RotateKernelInfo(reflected_kernel,180);
4027 while ( method_loop < method_limit && method_changed > 0 ) {
4034 rflt_kernel = reflected_kernel;
4037 while ( norm_kernel != NULL ) {
4041 while ( stage_loop < stage_limit ) {
4045 this_kernel = norm_kernel;
4048 case ErodeMorphology:
4049 case EdgeInMorphology:
4050 primitive = ErodeMorphology;
4052 case DilateMorphology:
4053 case EdgeOutMorphology:
4054 primitive = DilateMorphology;
4056 case OpenMorphology:
4057 case TopHatMorphology:
4058 primitive = ErodeMorphology;
4059 if ( stage_loop == 2 )
4060 primitive = DilateMorphology;
4062 case OpenIntensityMorphology:
4063 primitive = ErodeIntensityMorphology;
4064 if ( stage_loop == 2 )
4065 primitive = DilateIntensityMorphology;
4067 case CloseMorphology:
4068 case BottomHatMorphology:
4069 this_kernel = rflt_kernel;
4070 primitive = DilateMorphology;
4071 if ( stage_loop == 2 )
4072 primitive = ErodeMorphology;
4074 case CloseIntensityMorphology:
4075 this_kernel = rflt_kernel;
4076 primitive = DilateIntensityMorphology;
4077 if ( stage_loop == 2 )
4078 primitive = ErodeIntensityMorphology;
4080 case SmoothMorphology:
4081 switch ( stage_loop ) {
4083 primitive = ErodeMorphology;
4086 primitive = DilateMorphology;
4089 this_kernel = rflt_kernel;
4090 primitive = DilateMorphology;
4093 this_kernel = rflt_kernel;
4094 primitive = ErodeMorphology;
4098 case EdgeMorphology:
4099 primitive = DilateMorphology;
4100 if ( stage_loop == 2 ) {
4101 save_image = curr_image;
4102 curr_image = (
Image *) image;
4103 primitive = ErodeMorphology;
4106 case CorrelateMorphology:
4116 this_kernel = rflt_kernel;
4117 primitive = ConvolveMorphology;
4122 assert( this_kernel != (
KernelInfo *) NULL );
4125 if ( verbose != MagickFalse ) {
4126 if ( stage_limit > 1 )
4127 (void) FormatLocaleString(v_info,MaxTextExtent,
"%s:%.20g.%.20g -> ",
4128 CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
4129 method_loop,(
double) stage_loop);
4130 else if ( primitive != method )
4131 (void) FormatLocaleString(v_info, MaxTextExtent,
"%s:%.20g -> ",
4132 CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
4142 while ( kernel_loop < kernel_limit && changed > 0 ) {
4146 if ( work_image == (
Image *) NULL )
4148 work_image=CloneImage(image,0,0,MagickTrue,exception);
4149 if (work_image == (
Image *) NULL)
4151 if (SetImageStorageClass(work_image,DirectClass) == MagickFalse)
4153 InheritException(exception,&work_image->exception);
4161 changed = MorphologyPrimitive(curr_image, work_image, primitive,
4162 channel, this_kernel, bias, exception);
4164 if ( verbose != MagickFalse ) {
4165 if ( kernel_loop > 1 )
4166 (void) FormatLocaleFile(stderr,
"\n");
4167 (void) (
void) FormatLocaleFile(stderr,
4168 "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
4169 v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
4170 primitive),(this_kernel == rflt_kernel ) ?
"*" :
"",
4171 (
double) (method_loop+kernel_loop-1),(
double) kernel_number,
4172 (
double) count,(
double) changed);
4176 kernel_changed += changed;
4177 method_changed += changed;
4180 {
Image *tmp = work_image;
4181 work_image = curr_image;
4184 if ( work_image == image )
4185 work_image = (
Image *) NULL;
4189 if ( verbose != MagickFalse && kernel_changed != (
size_t)changed )
4190 (
void) FormatLocaleFile(stderr,
" Total %.20g",(
double) kernel_changed);
4191 if ( verbose != MagickFalse && stage_loop < stage_limit )
4192 (void) FormatLocaleFile(stderr,
"\n");
4195 (void) FormatLocaleFile(stderr,
"--E-- image=0x%lx\n", (
unsigned long)image);
4196 (void) FormatLocaleFile(stderr,
" curr =0x%lx\n", (
unsigned long)curr_image);
4197 (void) FormatLocaleFile(stderr,
" work =0x%lx\n", (
unsigned long)work_image);
4198 (void) FormatLocaleFile(stderr,
" save =0x%lx\n", (
unsigned long)save_image);
4199 (void) FormatLocaleFile(stderr,
" union=0x%lx\n", (
unsigned long)rslt_image);
4212 case EdgeOutMorphology:
4213 case EdgeInMorphology:
4214 case TopHatMorphology:
4215 case BottomHatMorphology:
4216 if ( verbose != MagickFalse )
4217 (void) FormatLocaleFile(stderr,
4218 "\n%s: Difference with original image",
4219 CommandOptionToMnemonic(MagickMorphologyOptions,method));
4220 (void) CompositeImageChannel(curr_image,(ChannelType)
4221 (channel & ~SyncChannels),DifferenceCompositeOp,image,0,0);
4223 case EdgeMorphology:
4224 if ( verbose != MagickFalse )
4225 (void) FormatLocaleFile(stderr,
4226 "\n%s: Difference of Dilate and Erode",
4227 CommandOptionToMnemonic(MagickMorphologyOptions,method));
4228 (void) CompositeImageChannel(curr_image,(ChannelType)
4229 (channel & ~SyncChannels),DifferenceCompositeOp,save_image,0,0);
4230 save_image = DestroyImage(save_image);
4238 rslt_image = curr_image;
4239 else if ( rslt_compose == NoCompositeOp )
4240 {
if ( verbose != MagickFalse ) {
4241 if ( this_kernel->next != (
KernelInfo *) NULL )
4242 (void) FormatLocaleFile(stderr,
" (re-iterate)");
4244 (
void) FormatLocaleFile(stderr,
" (done)");
4246 rslt_image = curr_image;
4248 else if ( rslt_image == (
Image *) NULL)
4249 {
if ( verbose != MagickFalse )
4250 (void) FormatLocaleFile(stderr,
" (save for compose)");
4251 rslt_image = curr_image;
4252 curr_image = (
Image *) image;
4262 if ( verbose != MagickFalse )
4263 (void) FormatLocaleFile(stderr,
" (compose \"%s\")",
4264 CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
4265 (void) CompositeImageChannel(rslt_image,
4266 (ChannelType) (channel & ~SyncChannels), rslt_compose,
4268 curr_image = DestroyImage(curr_image);
4269 curr_image = (
Image *) image;
4271 if ( verbose != MagickFalse )
4272 (void) FormatLocaleFile(stderr,
"\n");
4275 norm_kernel = norm_kernel->next;
4277 rflt_kernel = rflt_kernel->next;
4287 if ( curr_image == rslt_image )
4288 curr_image = (
Image *) NULL;
4289 if ( rslt_image != (
Image *) NULL )
4290 rslt_image = DestroyImage(rslt_image);
4292 if ( curr_image == rslt_image || curr_image == image )
4293 curr_image = (
Image *) NULL;
4294 if ( curr_image != (
Image *) NULL )
4295 curr_image = DestroyImage(curr_image);
4296 if ( work_image != (
Image *) NULL )
4297 work_image = DestroyImage(work_image);
4298 if ( save_image != (
Image *) NULL )
4299 save_image = DestroyImage(save_image);
4300 if ( reflected_kernel != (
KernelInfo *) NULL )
4301 reflected_kernel = DestroyKernelInfo(reflected_kernel);
4360 MagickExport
Image *MorphologyImage(
const Image *image,
4361 const MorphologyMethod method,
const ssize_t iterations,
4367 morphology_image=MorphologyImageChannel(image,DefaultChannels,method,
4368 iterations,kernel,exception);
4369 return(morphology_image);
4372 MagickExport
Image *MorphologyImageChannel(
const Image *image,
4373 const ChannelType channel,
const MorphologyMethod method,
4392 assert(image != (
const Image *) NULL);
4393 assert(image->signature == MagickCoreSignature);
4395 assert(exception->signature == MagickCoreSignature);
4396 if (IsEventLogging() != MagickFalse)
4397 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4400 if ((method == ConvolveMorphology) || (method == CorrelateMorphology))
4405 artifact = GetImageArtifact(image,
"convolve:bias");
4406 if (artifact != (
const char *) NULL)
4407 bias=StringToDoubleInterval(artifact,(
double) QuantumRange+1.0);
4409 artifact = GetImageArtifact(image,
"convolve:scale");
4410 if ( artifact != (
const char *) NULL ) {
4411 if ( curr_kernel == kernel )
4412 curr_kernel = CloneKernelInfo(kernel);
4414 curr_kernel=DestroyKernelInfo(curr_kernel);
4415 return((
Image *) NULL);
4417 ScaleGeometryKernelInfo(curr_kernel, artifact);
4422 if ( IsMagickTrue(GetImageArtifact(image,
"showKernel"))
4423 || IsMagickTrue(GetImageArtifact(image,
"convolve:showKernel"))
4424 || IsMagickTrue(GetImageArtifact(image,
"morphology:showKernel")) )
4425 ShowKernelInfo(curr_kernel);
4435 compose = UndefinedCompositeOp;
4436 artifact = GetImageArtifact(image,
"morphology:compose");
4437 if ( artifact != (
const char *) NULL)
4438 compose = (CompositeOperator) ParseCommandOption(
4439 MagickComposeOptions,MagickFalse,artifact);
4442 morphology_image = MorphologyApply(image, channel, method, iterations,
4443 curr_kernel, compose, bias, exception);
4446 if ( curr_kernel != kernel )
4447 curr_kernel=DestroyKernelInfo(curr_kernel);
4448 return(morphology_image);
4481 static void RotateKernelInfo(
KernelInfo *kernel,
double angle)
4485 RotateKernelInfo(kernel->next, angle);
4493 angle = fmod(angle, 360.0);
4497 if ( 337.5 < angle || angle <= 22.5 )
4501 switch (kernel->type) {
4503 case GaussianKernel:
4508 case LaplacianKernel:
4509 case ChebyshevKernel:
4510 case ManhattanKernel:
4511 case EuclideanKernel:
4525 if ( 135.0 < angle && angle <= 225.0 )
4527 if ( 225.0 < angle && angle <= 315.0 )
4535 if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
4537 if ( kernel->width == 3 && kernel->height == 3 )
4539 double t = kernel->values[0];
4540 kernel->values[0] = kernel->values[3];
4541 kernel->values[3] = kernel->values[6];
4542 kernel->values[6] = kernel->values[7];
4543 kernel->values[7] = kernel->values[8];
4544 kernel->values[8] = kernel->values[5];
4545 kernel->values[5] = kernel->values[2];
4546 kernel->values[2] = kernel->values[1];
4547 kernel->values[1] = t;
4549 if ( kernel->x != 1 || kernel->y != 1 ) {
4551 x = (ssize_t) kernel->x-1;
4552 y = (ssize_t) kernel->y-1;
4553 if ( x == y ) x = 0;
4554 else if ( x == 0 ) x = -y;
4555 else if ( x == -y ) y = 0;
4556 else if ( y == 0 ) y = x;
4557 kernel->x = (ssize_t) x+1;
4558 kernel->y = (ssize_t) y+1;
4560 angle = fmod(angle+315.0, 360.0);
4561 kernel->angle = fmod(kernel->angle+45.0, 360.0);
4564 perror(
"Unable to rotate non-3x3 kernel by 45 degrees");
4566 if ( 45.0 < fmod(angle, 180.0) && fmod(angle,180.0) <= 135.0 )
4568 if ( kernel->width == 1 || kernel->height == 1 )
4574 t = (ssize_t) kernel->width;
4575 kernel->width = kernel->height;
4576 kernel->height = (
size_t) t;
4578 kernel->x = kernel->y;
4580 if ( kernel->width == 1 ) {
4581 angle = fmod(angle+270.0, 360.0);
4582 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4584 angle = fmod(angle+90.0, 360.0);
4585 kernel->angle = fmod(kernel->angle+270.0, 360.0);
4588 else if ( kernel->width == kernel->height )
4595 for( i=0, x=kernel->width-1; i<=x; i++, x--)
4596 for( j=0, y=kernel->height-1; j<y; j++, y--)
4597 { t = k[i+j*kernel->width];
4598 k[i+j*kernel->width] = k[j+x*kernel->width];
4599 k[j+x*kernel->width] = k[x+y*kernel->width];
4600 k[x+y*kernel->width] = k[y+i*kernel->width];
4601 k[y+i*kernel->width] = t;
4606 x = (ssize_t) (kernel->x*2-kernel->width+1);
4607 y = (ssize_t) (kernel->y*2-kernel->height+1);
4608 kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4609 kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
4611 angle = fmod(angle+270.0, 360.0);
4612 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4615 perror(
"Unable to rotate a non-square, non-linear kernel 90 degrees");
4617 if ( 135.0 < angle && angle <= 225.0 )
4635 for ( i=0, j=kernel->width*kernel->height-1; i<j; i++, j--)
4636 t=k[i], k[i]=k[j], k[j]=t;
4638 kernel->x = (ssize_t) kernel->width - kernel->x - 1;
4639 kernel->y = (ssize_t) kernel->height - kernel->y - 1;
4640 angle = fmod(angle-180.0, 360.0);
4641 kernel->angle = fmod(kernel->angle+180.0, 360.0);
4686 MagickExport
void ScaleGeometryKernelInfo (
KernelInfo *kernel,
4687 const char *geometry)
4694 SetGeometryInfo(&args);
4695 flags = (GeometryFlags) ParseGeometry(geometry, &args);
4699 (void) FormatLocaleFile(stderr,
"Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4700 flags, args.rho, args.sigma, args.xi, args.psi );
4703 if ( (flags & PercentValue) != 0 )
4704 args.rho *= 0.01, args.sigma *= 0.01;
4706 if ( (flags & RhoValue) == 0 )
4708 if ( (flags & SigmaValue) == 0 )
4712 ScaleKernelInfo(kernel, args.rho, flags);
4715 if ( (flags & SigmaValue) != 0 )
4716 UnityAddKernelInfo(kernel, args.sigma);
4791 MagickExport
void ScaleKernelInfo(
KernelInfo *kernel,
4792 const double scaling_factor,
const GeometryFlags normalize_flags)
4803 ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
4807 if ( (normalize_flags&NormalizeValue) != 0 ) {
4808 if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4810 pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4813 pos_scale = kernel->positive_range;
4816 if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4817 pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4818 ? kernel->positive_range : 1.0;
4819 neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4820 ? -kernel->negative_range : 1.0;
4823 neg_scale = pos_scale;
4826 pos_scale = scaling_factor/pos_scale;
4827 neg_scale = scaling_factor/neg_scale;
4829 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4830 if ( ! IsNaN(kernel->values[i]) )
4831 kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4834 kernel->positive_range *= pos_scale;
4835 kernel->negative_range *= neg_scale;
4837 kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4838 kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4841 if ( scaling_factor < MagickEpsilon ) {
4843 t = kernel->positive_range;
4844 kernel->positive_range = kernel->negative_range;
4845 kernel->negative_range = t;
4846 t = kernel->maximum;
4847 kernel->maximum = kernel->minimum;
4848 kernel->minimum = 1;
4878 MagickExport
void ShowKernelInfo(
const KernelInfo *kernel)
4886 for (c=0, k=kernel; k != (
KernelInfo *) NULL; c++, k=k->next ) {
4888 (void) FormatLocaleFile(stderr,
"Kernel");
4890 (
void) FormatLocaleFile(stderr,
" #%lu", (
unsigned long) c );
4891 (void) FormatLocaleFile(stderr,
" \"%s",
4892 CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4893 if ( fabs(k->angle) >= MagickEpsilon )
4894 (
void) FormatLocaleFile(stderr,
"@%lg", k->angle);
4895 (void) FormatLocaleFile(stderr,
"\" of size %lux%lu%+ld%+ld",(
unsigned long)
4896 k->width,(
unsigned long) k->height,(
long) k->x,(long) k->y);
4897 (void) FormatLocaleFile(stderr,
4898 " with values from %.*lg to %.*lg\n",
4899 GetMagickPrecision(), k->minimum,
4900 GetMagickPrecision(), k->maximum);
4901 (void) FormatLocaleFile(stderr,
"Forming a output range from %.*lg to %.*lg",
4902 GetMagickPrecision(), k->negative_range,
4903 GetMagickPrecision(), k->positive_range);
4904 if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
4905 (
void) FormatLocaleFile(stderr,
" (Zero-Summing)\n");
4906 else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
4907 (void) FormatLocaleFile(stderr,
" (Normalized)\n");
4909 (
void) FormatLocaleFile(stderr,
" (Sum %.*lg)\n",
4910 GetMagickPrecision(), k->positive_range+k->negative_range);
4911 for (i=v=0; v < k->height; v++) {
4912 (void) FormatLocaleFile(stderr,
"%2lu:", (
unsigned long) v );
4913 for (u=0; u < k->width; u++, i++)
4914 if ( IsNaN(k->values[i]) )
4915 (
void) FormatLocaleFile(stderr,
" %*s", GetMagickPrecision()+3,
"nan");
4917 (
void) FormatLocaleFile(stderr,
" %*.*lg", GetMagickPrecision()+3,
4918 GetMagickPrecision(), k->values[i]);
4919 (void) FormatLocaleFile(stderr,
"\n");
4958 MagickExport
void UnityAddKernelInfo(
KernelInfo *kernel,
4963 UnityAddKernelInfo(kernel->next, scale);
4966 kernel->values[kernel->x+kernel->y*kernel->width] += scale;
4967 CalcKernelMetaData(kernel);
4998 MagickExport
void ZeroKernelNans(
KernelInfo *kernel)
5005 ZeroKernelNans(kernel->next);
5007 for (i=0; i < (kernel->width*kernel->height); i++)
5008 if ( IsNaN(kernel->values[i]) )
5009 kernel->values[i] = 0.0;