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)
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))
112 ExpandRotateKernelInfo(
KernelInfo *,
const double),
213static KernelInfo *ParseKernelArray(
const char *kernel_string)
219 token[MaxTextExtent];
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);
372static 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 = (double) QuantumRange/(args.sigma+1);
460 else if ( (flags & PercentValue ) != 0 )
461 args.sigma *= (double) 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);
484MagickExport
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,~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);
950MagickExport
KernelInfo *AcquireKernelBuiltIn(
const KernelInfoType type,
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 OctagonalKernel:
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));
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 )
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);
2298static 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);
2316static 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;
2381static 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 )
2409static void ExpandRotateKernelInfo(
KernelInfo *kernel,
const double angle)
2417DisableMSCWarning(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);
2471static 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]);
2563static 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)*(double) GetPixelRed(k_pixels);
2751 result.green += (*k)*(double) GetPixelGreen(k_pixels);
2752 result.blue += (*k)*(double) GetPixelBlue(k_pixels);
2753 result.opacity += (*k)*(double) GetPixelOpacity(k_pixels);
2754 if ( image->colorspace == CMYKColorspace)
2755 result.index += (*k)*(double) (*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*((double) QuantumRange-(double)
2792 GetPixelOpacity(k_pixels));
2796 result.red += alpha*(double) GetPixelRed(k_pixels);
2797 result.green += alpha*(double) GetPixelGreen(k_pixels);
2798 result.blue += alpha*(double) GetPixelBlue(k_pixels);
2799 result.opacity += (*k)*(double) GetPixelOpacity(k_pixels);
2800 if ( image->colorspace == CMYKColorspace)
2801 result.index += alpha*(double) (*k_indexes);
2807 gamma=PerceptibleReciprocal(gamma);
2809 gamma*=(double) kernel->height/count;
2810 SetPixelRed(q,ClampToQuantum(gamma*result.red));
2811 SetPixelGreen(q,ClampToQuantum(gamma*result.green));
2812 SetPixelBlue(q,ClampToQuantum(gamma*result.blue));
2813 SetPixelOpacity(q,ClampToQuantum(result.opacity));
2814 if (image->colorspace == CMYKColorspace)
2815 SetPixelIndex(q_indexes+y,ClampToQuantum(gamma*result.index));
2819 if ( ( p[r].red != GetPixelRed(q))
2820 || ( p[r].green != GetPixelGreen(q))
2821 || ( p[r].blue != GetPixelBlue(q))
2822 || ( (image->matte != MagickFalse) &&
2823 (p[r].opacity != GetPixelOpacity(q)))
2824 || ( (image->colorspace == CMYKColorspace) &&
2825 (GetPixelIndex(p_indexes+y+r) != GetPixelIndex(q_indexes+y))) )
2830 if ( SyncCacheViewAuthenticPixels(q_view,exception) == MagickFalse)
2832 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2837#if defined(MAGICKCORE_OPENMP_SUPPORT)
2841 proceed=SetImageProgress(image,MorphologyTag,progress,image->columns);
2842 if (proceed == MagickFalse)
2846 result_image->type=image->type;
2847 q_view=DestroyCacheView(q_view);
2848 p_view=DestroyCacheView(p_view);
2849 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2850 changed+=changes[i];
2851 changes=(
size_t *) RelinquishMagickMemory(changes);
2852 return(status ? (ssize_t) changed : 0);
2858#if defined(MAGICKCORE_OPENMP_SUPPORT)
2859 #pragma omp parallel for schedule(static) shared(progress,status) \
2860 magick_number_threads(image,result_image,image->rows,1)
2862 for (y=0; y < (ssize_t) image->rows; y++)
2865 id = GetOpenMPThreadId();
2871 *magick_restrict p_indexes;
2877 *magick_restrict q_indexes;
2885 if (status == MagickFalse)
2887 p=GetCacheViewVirtualPixels(p_view, -offx, y-offy, virt_width,
2888 kernel->height, exception);
2889 q=GetCacheViewAuthenticPixels(q_view,0,y,result_image->columns,1,
2896 p_indexes=GetCacheViewVirtualIndexQueue(p_view);
2897 q_indexes=GetCacheViewAuthenticIndexQueue(q_view);
2900 r = virt_width*offy + offx;
2902 for (x=0; x < (ssize_t) image->columns; x++)
2914 *magick_restrict k_pixels;
2917 *magick_restrict k_indexes;
2928 if (image->colorspace == CMYKColorspace)
2929 SetPixelIndex(q_indexes+x,GetPixelIndex(p_indexes+x+r));
2936 min.index = (double) QuantumRange;
2943 result.red = (double) p[r].red;
2944 result.green = (double) p[r].green;
2945 result.blue = (double) p[r].blue;
2946 result.opacity = (double) QuantumRange - (
double) p[r].opacity;
2948 if ( image->colorspace == CMYKColorspace)
2949 result.index = (double) GetPixelIndex(p_indexes+x+r);
2952 case ConvolveMorphology:
2958 result.index = bias;
2960 case DilateIntensityMorphology:
2961 case ErodeIntensityMorphology:
2970 case ConvolveMorphology:
2989 k = &kernel->values[ kernel->width*kernel->height-1 ];
2991 k_indexes = p_indexes+x;
2992 if ( ((channel & SyncChannels) == 0 ) ||
2993 (image->matte == MagickFalse) )
2997 for (v=0; v < (ssize_t) kernel->height; v++) {
2998 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
2999 if ( IsNaN(*k) )
continue;
3000 result.red += (*k)*(double) k_pixels[u].red;
3001 result.green += (*k)*(double) k_pixels[u].green;
3002 result.blue += (*k)*(double) k_pixels[u].blue;
3003 result.opacity += (*k)*(double) k_pixels[u].opacity;
3004 if ( image->colorspace == CMYKColorspace)
3005 result.index += (*k)*(double) GetPixelIndex(k_indexes+u);
3007 k_pixels += virt_width;
3008 k_indexes += virt_width;
3010 if ((channel & RedChannel) != 0)
3011 SetPixelRed(q,ClampToQuantum((MagickRealType) result.red));
3012 if ((channel & GreenChannel) != 0)
3013 SetPixelGreen(q,ClampToQuantum((MagickRealType) result.green));
3014 if ((channel & BlueChannel) != 0)
3015 SetPixelBlue(q,ClampToQuantum((MagickRealType) result.blue));
3016 if (((channel & OpacityChannel) != 0) &&
3017 (image->matte != MagickFalse))
3018 SetPixelOpacity(q,ClampToQuantum((MagickRealType) result.opacity));
3019 if (((channel & IndexChannel) != 0) &&
3020 (image->colorspace == CMYKColorspace))
3021 SetPixelIndex(q_indexes+x,ClampToQuantum(result.index));
3037 for (v=0; v < (ssize_t) kernel->height; v++) {
3038 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3039 if ( IsNaN(*k) )
continue;
3040 alpha=QuantumScale*((double) QuantumRange-(double)
3041 k_pixels[u].opacity);
3045 result.red += alpha*(double) k_pixels[u].red;
3046 result.green += alpha*(double) k_pixels[u].green;
3047 result.blue += alpha*(double) k_pixels[u].blue;
3048 result.opacity += (*k)*(double) k_pixels[u].opacity;
3049 if ( image->colorspace == CMYKColorspace)
3050 result.index+=alpha*(double) GetPixelIndex(k_indexes+u);
3052 k_pixels += virt_width;
3053 k_indexes += virt_width;
3056 gamma=PerceptibleReciprocal(gamma);
3058 gamma*=(double) kernel->height*kernel->width/count;
3059 SetPixelRed(q,ClampToQuantum((MagickRealType) (gamma*result.red)));
3060 SetPixelGreen(q,ClampToQuantum((MagickRealType) (gamma*result.green)));
3061 SetPixelBlue(q,ClampToQuantum((MagickRealType) (gamma*result.blue)));
3062 SetPixelOpacity(q,ClampToQuantum(result.opacity));
3063 if (image->colorspace == CMYKColorspace)
3064 SetPixelIndex(q_indexes+x,ClampToQuantum((MagickRealType) (gamma*
3069 case ErodeMorphology:
3080 k_indexes = p_indexes+x;
3081 for (v=0; v < (ssize_t) kernel->height; v++) {
3082 for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3083 if ( IsNaN(*k) || (*k) < 0.5 )
continue;
3084 Minimize(min.red, (
double) k_pixels[u].red);
3085 Minimize(min.green, (
double) k_pixels[u].green);
3086 Minimize(min.blue, (
double) k_pixels[u].blue);
3087 Minimize(min.opacity,(
double) QuantumRange-(
double)
3088 k_pixels[u].opacity);
3089 if ( image->colorspace == CMYKColorspace)
3090 Minimize(min.index,(
double) GetPixelIndex(k_indexes+u));
3092 k_pixels += virt_width;
3093 k_indexes += virt_width;
3097 case DilateMorphology:
3109 k = &kernel->values[ kernel->width*kernel->height-1 ];
3111 k_indexes = p_indexes+x;
3112 for (v=0; v < (ssize_t) kernel->height; v++) {
3113 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3114 if ( IsNaN(*k) || (*k) < 0.5 )
continue;
3115 Maximize(max.red, (
double) k_pixels[u].red);
3116 Maximize(max.green, (
double) k_pixels[u].green);
3117 Maximize(max.blue, (
double) k_pixels[u].blue);
3118 Maximize(max.opacity,(
double) QuantumRange-(
double)
3119 k_pixels[u].opacity);
3120 if ( image->colorspace == CMYKColorspace)
3121 Maximize(max.index, (
double) GetPixelIndex(
3124 k_pixels += virt_width;
3125 k_indexes += virt_width;
3129 case HitAndMissMorphology:
3130 case ThinningMorphology:
3131 case ThickenMorphology:
3145 k_indexes = p_indexes+x;
3146 for (v=0; v < (ssize_t) kernel->height; v++) {
3147 for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3148 if ( IsNaN(*k) )
continue;
3151 Minimize(min.red, (
double) k_pixels[u].red);
3152 Minimize(min.green, (
double) k_pixels[u].green);
3153 Minimize(min.blue, (
double) k_pixels[u].blue);
3154 Minimize(min.opacity, (
double) QuantumRange-(
double)
3155 k_pixels[u].opacity);
3156 if ( image->colorspace == CMYKColorspace)
3157 Minimize(min.index,(
double) GetPixelIndex(
3160 else if ( (*k) < 0.3 )
3162 Maximize(max.red, (
double) k_pixels[u].red);
3163 Maximize(max.green, (
double) k_pixels[u].green);
3164 Maximize(max.blue, (
double) k_pixels[u].blue);
3165 Maximize(max.opacity,(
double) QuantumRange-(
double)
3166 k_pixels[u].opacity);
3167 if ( image->colorspace == CMYKColorspace)
3168 Maximize(max.index, (
double) GetPixelIndex(
3172 k_pixels += virt_width;
3173 k_indexes += virt_width;
3176 min.red -= max.red; Maximize( min.red, 0.0 );
3177 min.green -= max.green; Maximize( min.green, 0.0 );
3178 min.blue -= max.blue; Maximize( min.blue, 0.0 );
3179 min.opacity -= max.opacity; Maximize( min.opacity, 0.0 );
3180 min.index -= max.index; Maximize( min.index, 0.0 );
3183 case ErodeIntensityMorphology:
3194 k_indexes = p_indexes+x;
3195 for (v=0; v < (ssize_t) kernel->height; v++) {
3196 for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3197 if ( IsNaN(*k) || (*k) < 0.5 )
continue;
3198 if ( result.red == 0.0 ||
3199 GetPixelIntensity(image,&(k_pixels[u])) < GetPixelIntensity(result_image,q) ) {
3203 if ( result.red > 0.0 ) changes[id]++;
3207 k_pixels += virt_width;
3208 k_indexes += virt_width;
3212 case DilateIntensityMorphology:
3223 k = &kernel->values[ kernel->width*kernel->height-1 ];
3225 k_indexes = p_indexes+x;
3226 for (v=0; v < (ssize_t) kernel->height; v++) {
3227 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3228 if ( IsNaN(*k) || (*k) < 0.5 )
continue;
3229 if ( result.red == 0.0 ||
3230 GetPixelIntensity(image,&(k_pixels[u])) > GetPixelIntensity(result_image,q) ) {
3233 if ( result.red > 0.0 ) changes[id]++;
3237 k_pixels += virt_width;
3238 k_indexes += virt_width;
3242 case IterativeDistanceMorphology:
3266 k = &kernel->values[ kernel->width*kernel->height-1 ];
3268 k_indexes = p_indexes+x;
3269 for (v=0; v < (ssize_t) kernel->height; v++) {
3270 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3271 if ( IsNaN(*k) )
continue;
3272 Minimize(result.red, (*k)+(
double) k_pixels[u].red);
3273 Minimize(result.green, (*k)+(
double) k_pixels[u].green);
3274 Minimize(result.blue, (*k)+(
double) k_pixels[u].blue);
3275 Minimize(result.opacity, (*k)+(
double) QuantumRange-(
double)
3276 k_pixels[u].opacity);
3277 if ( image->colorspace == CMYKColorspace)
3278 Minimize(result.index,(*k)+(
double) GetPixelIndex(k_indexes+u));
3280 k_pixels += virt_width;
3281 k_indexes += virt_width;
3285 case UndefinedMorphology:
3297 case HitAndMissMorphology:
3298 case ErodeMorphology:
3301 case DilateMorphology:
3304 case ThinningMorphology:
3306 result.red -= min.red;
3307 result.green -= min.green;
3308 result.blue -= min.blue;
3309 result.opacity -= min.opacity;
3310 result.index -= min.index;
3312 case ThickenMorphology:
3314 result.red += min.red;
3315 result.green += min.green;
3316 result.blue += min.blue;
3317 result.opacity += min.opacity;
3318 result.index += min.index;
3326 case UndefinedMorphology:
3327 case ConvolveMorphology:
3328 case DilateIntensityMorphology:
3329 case ErodeIntensityMorphology:
3332 if ((channel & RedChannel) != 0)
3333 SetPixelRed(q,ClampToQuantum(result.red));
3334 if ((channel & GreenChannel) != 0)
3335 SetPixelGreen(q,ClampToQuantum(result.green));
3336 if ((channel & BlueChannel) != 0)
3337 SetPixelBlue(q,ClampToQuantum(result.blue));
3338 if ((channel & OpacityChannel) != 0
3339 && image->matte != MagickFalse )
3340 SetPixelAlpha(q,ClampToQuantum(result.opacity));
3341 if (((channel & IndexChannel) != 0) &&
3342 (image->colorspace == CMYKColorspace))
3343 SetPixelIndex(q_indexes+x,ClampToQuantum(result.index));
3347 if ( ( p[r].red != GetPixelRed(q) )
3348 || ( p[r].green != GetPixelGreen(q) )
3349 || ( p[r].blue != GetPixelBlue(q) )
3350 || ( (image->matte != MagickFalse) &&
3351 (p[r].opacity != GetPixelOpacity(q)))
3352 || ( (image->colorspace == CMYKColorspace) &&
3353 (GetPixelIndex(p_indexes+x+r) != GetPixelIndex(q_indexes+x))) )
3358 if ( SyncCacheViewAuthenticPixels(q_view,exception) == MagickFalse)
3360 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3365#if defined(MAGICKCORE_OPENMP_SUPPORT)
3369 proceed=SetImageProgress(image,MorphologyTag,progress,image->rows);
3370 if (proceed == MagickFalse)
3374 q_view=DestroyCacheView(q_view);
3375 p_view=DestroyCacheView(p_view);
3376 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3377 changed+=changes[i];
3378 changes=(
size_t *) RelinquishMagickMemory(changes);
3379 return(status ? (ssize_t)changed : -1);
3394static ssize_t MorphologyPrimitiveDirect(
Image *image,
3395 const MorphologyMethod method,
const ChannelType channel,
3419 assert(image != (
Image *) NULL);
3420 assert(image->signature == MagickCoreSignature);
3422 assert(kernel->signature == MagickCoreSignature);
3424 assert(exception->signature == MagickCoreSignature);
3432 case DistanceMorphology:
3433 case VoronoiMorphology:
3435 offx = (ssize_t) kernel->width-offx-1;
3436 offy = (ssize_t) kernel->height-offy-1;
3439 case ?????Morphology:
3444 assert(
"Not a PrimativeDirect Morphology Method" != (
char *) NULL);
3450 virt_view=AcquireVirtualCacheView(image,exception);
3451 auth_view=AcquireAuthenticCacheView(image,exception);
3452 virt_width=image->columns+kernel->width-1;
3454 for (y=0; y < (ssize_t) image->rows; y++)
3460 *magick_restrict p_indexes;
3466 *magick_restrict q_indexes;
3481 if (status == MagickFalse)
3483 p=GetCacheViewVirtualPixels(virt_view, -offx, y-offy, virt_width, (
size_t) offy+1,
3485 q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
3489 if (status == MagickFalse)
3491 p_indexes=GetCacheViewVirtualIndexQueue(virt_view);
3492 q_indexes=GetCacheViewAuthenticIndexQueue(auth_view);
3495 r = (ssize_t) virt_width*offy + offx;
3497 for (x=0; x < (ssize_t) image->columns; x++)
3509 *magick_restrict k_pixels;
3512 *magick_restrict k_indexes;
3518 GetMagickPixelPacket(image,&result);
3519 SetMagickPixelPacket(image,q,q_indexes,&result);
3520 if ( method != VoronoiMorphology )
3521 result.opacity = (MagickRealType) QuantumRange - (MagickRealType)
3525 case DistanceMorphology:
3527 k = &kernel->values[ kernel->width*kernel->height-1 ];
3529 k_indexes = p_indexes+x;
3530 for (v=0; v <= (ssize_t) offy; v++) {
3531 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3532 if ( IsNaN(*k) )
continue;
3533 Minimize(result.red, (*k)+(
double) k_pixels[u].red);
3534 Minimize(result.green, (*k)+(
double) k_pixels[u].green);
3535 Minimize(result.blue, (*k)+(
double) k_pixels[u].blue);
3536 Minimize(result.opacity, (*k)+(
double) QuantumRange-(
double)
3537 k_pixels[u].opacity);
3538 if ( image->colorspace == CMYKColorspace)
3539 Minimize(result.index, (*k)+(
double)
3540 GetPixelIndex(k_indexes+u));
3542 k_pixels += virt_width;
3543 k_indexes += virt_width;
3546 k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3548 k_indexes = q_indexes-offx;
3549 for (u=0; u < (ssize_t) offx; u++, k--) {
3550 if ( x+u-offx < 0 )
continue;
3551 if ( IsNaN(*k) )
continue;
3552 Minimize(result.red, (*k)+(
double) k_pixels[u].red);
3553 Minimize(result.green, (*k)+(
double) k_pixels[u].green);
3554 Minimize(result.blue, (*k)+(
double) k_pixels[u].blue);
3555 Minimize(result.opacity, (*k)+(
double) QuantumRange-(
double)
3556 k_pixels[u].opacity);
3557 if ( image->colorspace == CMYKColorspace)
3558 Minimize(result.index, (*k)+(
double)
3559 GetPixelIndex(k_indexes+u));
3562 case VoronoiMorphology:
3570 k = &kernel->values[ kernel->width*kernel->height-1 ];
3572 k_indexes = p_indexes+x;
3573 for (v=0; v <= (ssize_t) offy; v++) {
3574 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3575 if ( IsNaN(*k) )
continue;
3576 if( result.opacity > (*k)+(
double) k_pixels[u].opacity )
3578 SetMagickPixelPacket(image,&k_pixels[u],&k_indexes[u],
3580 result.opacity += *k;
3583 k_pixels += virt_width;
3584 k_indexes += virt_width;
3587 k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3589 k_indexes = q_indexes-offx;
3590 for (u=0; u < (ssize_t) offx; u++, k--) {
3591 if ( x+u-offx < 0 )
continue;
3592 if ( IsNaN(*k) )
continue;
3593 if( result.opacity > (*k)+(
double) k_pixels[u].opacity )
3595 SetMagickPixelPacket(image,&k_pixels[u],&k_indexes[u],
3597 result.opacity += *k;
3607 case VoronoiMorphology:
3608 SetPixelPacket(image,&result,q,q_indexes);
3611 if ((channel & RedChannel) != 0)
3612 SetPixelRed(q,ClampToQuantum(result.red));
3613 if ((channel & GreenChannel) != 0)
3614 SetPixelGreen(q,ClampToQuantum(result.green));
3615 if ((channel & BlueChannel) != 0)
3616 SetPixelBlue(q,ClampToQuantum(result.blue));
3617 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
3618 SetPixelAlpha(q,ClampToQuantum(result.opacity));
3619 if (((channel & IndexChannel) != 0) &&
3620 (image->colorspace == CMYKColorspace))
3621 SetPixelIndex(q_indexes+x,ClampToQuantum(result.index));
3625 if ( ( p[r].red != GetPixelRed(q) )
3626 || ( p[r].green != GetPixelGreen(q) )
3627 || ( p[r].blue != GetPixelBlue(q) )
3628 || ( (image->matte != MagickFalse) &&
3629 (p[r].opacity != GetPixelOpacity(q)))
3630 || ( (image->colorspace == CMYKColorspace) &&
3631 (GetPixelIndex(p_indexes+x+r) != GetPixelIndex(q_indexes+x))) )
3638 if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3640 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3642#if defined(MAGICKCORE_OPENMP_SUPPORT)
3646 if (SetImageProgress(image,MorphologyTag,progress,image->rows) == MagickFalse )
3653 for (y=(ssize_t)image->rows-1; y >= 0; y--)
3659 *magick_restrict p_indexes;
3665 *magick_restrict q_indexes;
3673 if (status == MagickFalse)
3682 p=GetCacheViewVirtualPixels(virt_view, -offx, y, virt_width, (
size_t) kernel->y+1,
3684 q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
3688 if (status == MagickFalse)
3690 p_indexes=GetCacheViewVirtualIndexQueue(virt_view);
3691 q_indexes=GetCacheViewAuthenticIndexQueue(auth_view);
3694 p += image->columns-1;
3695 q += image->columns-1;
3700 for (x=(ssize_t)image->columns-1; x >= 0; x--)
3706 *magick_restrict k_pixels;
3709 *magick_restrict k_indexes;
3719 GetMagickPixelPacket(image,&result);
3720 SetMagickPixelPacket(image,q,q_indexes,&result);
3721 if ( method != VoronoiMorphology )
3722 result.opacity = (double) QuantumRange - (
double) result.opacity;
3725 case DistanceMorphology:
3727 k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3729 k_indexes = p_indexes+x;
3730 for (v=offy; v < (ssize_t) kernel->height; v++) {
3731 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3732 if ( IsNaN(*k) )
continue;
3733 Minimize(result.red, (*k)+(
double) k_pixels[u].red);
3734 Minimize(result.green, (*k)+(
double) k_pixels[u].green);
3735 Minimize(result.blue, (*k)+(
double) k_pixels[u].blue);
3736 Minimize(result.opacity, (*k)+(
double) QuantumRange-(
double)
3737 k_pixels[u].opacity);
3738 if ( image->colorspace == CMYKColorspace)
3739 Minimize(result.index,(*k)+(
double)
3740 GetPixelIndex(k_indexes+u));
3742 k_pixels += virt_width;
3743 k_indexes += virt_width;
3746 k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3748 k_indexes = q_indexes-offx;
3749 for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3750 if ( (x+u-offx) >= (ssize_t)image->columns )
continue;
3751 if ( IsNaN(*k) )
continue;
3752 Minimize(result.red, (*k)+(
double) k_pixels[u].red);
3753 Minimize(result.green, (*k)+(
double) k_pixels[u].green);
3754 Minimize(result.blue, (*k)+(
double) k_pixels[u].blue);
3755 Minimize(result.opacity, (*k)+(
double) QuantumRange-(
double)
3756 k_pixels[u].opacity);
3757 if ( image->colorspace == CMYKColorspace)
3758 Minimize(result.index, (*k)+(
double)
3759 GetPixelIndex(k_indexes+u));
3762 case VoronoiMorphology:
3768 k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3770 k_indexes = p_indexes+x;
3771 for (v=offy; v < (ssize_t) kernel->height; v++) {
3772 for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3773 if ( IsNaN(*k) )
continue;
3774 if( result.opacity > (*k)+(
double) k_pixels[u].opacity )
3776 SetMagickPixelPacket(image,&k_pixels[u],&k_indexes[u],
3778 result.opacity += *k;
3781 k_pixels += virt_width;
3782 k_indexes += virt_width;
3785 k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3787 k_indexes = q_indexes-offx;
3788 for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3789 if ( (x+u-offx) >= (ssize_t)image->columns )
continue;
3790 if ( IsNaN(*k) )
continue;
3791 if( result.opacity > (*k)+(
double) k_pixels[u].opacity )
3793 SetMagickPixelPacket(image,&k_pixels[u],&k_indexes[u],
3795 result.opacity += *k;
3805 case VoronoiMorphology:
3806 SetPixelPacket(image,&result,q,q_indexes);
3809 if ((channel & RedChannel) != 0)
3810 SetPixelRed(q,ClampToQuantum(result.red));
3811 if ((channel & GreenChannel) != 0)
3812 SetPixelGreen(q,ClampToQuantum(result.green));
3813 if ((channel & BlueChannel) != 0)
3814 SetPixelBlue(q,ClampToQuantum(result.blue));
3815 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
3816 SetPixelAlpha(q,ClampToQuantum(result.opacity));
3817 if (((channel & IndexChannel) != 0) &&
3818 (image->colorspace == CMYKColorspace))
3819 SetPixelIndex(q_indexes+x,ClampToQuantum(result.index));
3823 if ( ( p[r].red != GetPixelRed(q) )
3824 || ( p[r].green != GetPixelGreen(q) )
3825 || ( p[r].blue != GetPixelBlue(q) )
3826 || ( (image->matte != MagickFalse) &&
3827 (p[r].opacity != GetPixelOpacity(q)))
3828 || ( (image->colorspace == CMYKColorspace) &&
3829 (GetPixelIndex(p_indexes+x+r) != GetPixelIndex(q_indexes+x))) )
3835 if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3837 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3839#if defined(MAGICKCORE_OPENMP_SUPPORT)
3843 if ( SetImageProgress(image,MorphologyTag,progress,image->rows) == MagickFalse )
3849 auth_view=DestroyCacheView(auth_view);
3850 virt_view=DestroyCacheView(virt_view);
3851 return(status ? (ssize_t) changed : -1);
3862MagickExport
Image *MorphologyApply(
const Image *image,
const ChannelType
3863 channel,
const MorphologyMethod method,
const ssize_t iterations,
3864 const KernelInfo *kernel,
const CompositeOperator compose,
3908 v_info[MaxTextExtent];
3910 assert(image != (
Image *) NULL);
3911 assert(image->signature == MagickCoreSignature);
3913 assert(kernel->signature == MagickCoreSignature);
3915 assert(exception->signature == MagickCoreSignature);
3918 if ( iterations == 0 )
3919 return((
Image *) NULL);
3921 kernel_limit = (size_t) iterations;
3922 if ( iterations < 0 )
3923 kernel_limit = image->columns>image->rows ? image->columns : image->rows;
3925 verbose = IsMagickTrue(GetImageArtifact(image,
"debug"));
3928 curr_image = (
Image *) image;
3929 curr_compose = image->compose;
3930 (void) curr_compose;
3931 work_image = save_image = rslt_image = (
Image *) NULL;
3941 special = MagickFalse;
3942 rslt_compose = compose;
3944 case SmoothMorphology:
3947 case OpenMorphology:
3948 case OpenIntensityMorphology:
3949 case TopHatMorphology:
3950 case CloseMorphology:
3951 case CloseIntensityMorphology:
3952 case BottomHatMorphology:
3953 case EdgeMorphology:
3956 case HitAndMissMorphology:
3957 rslt_compose = LightenCompositeOp;
3959 case ThinningMorphology:
3960 case ThickenMorphology:
3961 method_limit = kernel_limit;
3964 case DistanceMorphology:
3965 case VoronoiMorphology:
3966 special = MagickTrue;
3975 if ( special != MagickFalse )
3977 rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3978 if (rslt_image == (
Image *) NULL)
3980 if (SetImageStorageClass(rslt_image,DirectClass) == MagickFalse)
3982 InheritException(exception,&rslt_image->exception);
3986 changed = MorphologyPrimitiveDirect(rslt_image, method,
3987 channel, kernel, exception);
3989 if ( verbose != MagickFalse )
3990 (void) (
void) FormatLocaleFile(stderr,
3991 "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
3992 CommandOptionToMnemonic(MagickMorphologyOptions, method),
3993 1.0,0.0,1.0, (
double) changed);
3998 if ( method == VoronoiMorphology ) {
4000 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel);
4001 (void) CompositeImageChannel(rslt_image, DefaultChannels,
4002 CopyOpacityCompositeOp, image, 0, 0);
4003 (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel);
4009 if ( compose != UndefinedCompositeOp )
4010 rslt_compose = compose;
4011 if ( rslt_compose == UndefinedCompositeOp )
4012 rslt_compose = NoCompositeOp;
4017 case CorrelateMorphology:
4018 case CloseMorphology:
4019 case CloseIntensityMorphology:
4020 case BottomHatMorphology:
4021 case SmoothMorphology:
4022 reflected_kernel = CloneKernelInfo(kernel);
4025 RotateKernelInfo(reflected_kernel,180);
4037 while ( method_loop < method_limit && method_changed > 0 ) {
4044 rflt_kernel = reflected_kernel;
4047 while ( norm_kernel != NULL ) {
4051 while ( stage_loop < stage_limit ) {
4055 this_kernel = norm_kernel;
4058 case ErodeMorphology:
4059 case EdgeInMorphology:
4060 primitive = ErodeMorphology;
4062 case DilateMorphology:
4063 case EdgeOutMorphology:
4064 primitive = DilateMorphology;
4066 case OpenMorphology:
4067 case TopHatMorphology:
4068 primitive = ErodeMorphology;
4069 if ( stage_loop == 2 )
4070 primitive = DilateMorphology;
4072 case OpenIntensityMorphology:
4073 primitive = ErodeIntensityMorphology;
4074 if ( stage_loop == 2 )
4075 primitive = DilateIntensityMorphology;
4077 case CloseMorphology:
4078 case BottomHatMorphology:
4079 this_kernel = rflt_kernel;
4080 primitive = DilateMorphology;
4081 if ( stage_loop == 2 )
4082 primitive = ErodeMorphology;
4084 case CloseIntensityMorphology:
4085 this_kernel = rflt_kernel;
4086 primitive = DilateIntensityMorphology;
4087 if ( stage_loop == 2 )
4088 primitive = ErodeIntensityMorphology;
4090 case SmoothMorphology:
4091 switch ( stage_loop ) {
4093 primitive = ErodeMorphology;
4096 primitive = DilateMorphology;
4099 this_kernel = rflt_kernel;
4100 primitive = DilateMorphology;
4103 this_kernel = rflt_kernel;
4104 primitive = ErodeMorphology;
4108 case EdgeMorphology:
4109 primitive = DilateMorphology;
4110 if ( stage_loop == 2 ) {
4111 save_image = curr_image;
4112 curr_image = (
Image *) image;
4113 primitive = ErodeMorphology;
4116 case CorrelateMorphology:
4126 this_kernel = rflt_kernel;
4127 primitive = ConvolveMorphology;
4132 assert( this_kernel != (
KernelInfo *) NULL );
4135 if ( verbose != MagickFalse ) {
4136 if ( stage_limit > 1 )
4137 (void) FormatLocaleString(v_info,MaxTextExtent,
"%s:%.20g.%.20g -> ",
4138 CommandOptionToMnemonic(MagickMorphologyOptions,method),(
double)
4139 method_loop,(
double) stage_loop);
4140 else if ( primitive != method )
4141 (void) FormatLocaleString(v_info, MaxTextExtent,
"%s:%.20g -> ",
4142 CommandOptionToMnemonic(MagickMorphologyOptions, method),(
double)
4152 while ( kernel_loop < kernel_limit && changed > 0 ) {
4156 if ( work_image == (
Image *) NULL )
4158 work_image=CloneImage(image,0,0,MagickTrue,exception);
4159 if (work_image == (
Image *) NULL)
4161 if (SetImageStorageClass(work_image,DirectClass) == MagickFalse)
4163 InheritException(exception,&work_image->exception);
4171 changed = MorphologyPrimitive(curr_image, work_image, primitive,
4172 channel, this_kernel, bias, exception);
4174 if ( verbose != MagickFalse ) {
4175 if ( kernel_loop > 1 )
4176 (void) FormatLocaleFile(stderr,
"\n");
4177 (void) (
void) FormatLocaleFile(stderr,
4178 "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
4179 v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
4180 primitive),(this_kernel == rflt_kernel ) ?
"*" :
"",
4181 (
double) (method_loop+kernel_loop-1),(
double) kernel_number,
4182 (
double) count,(
double) changed);
4186 kernel_changed += changed;
4187 method_changed += changed;
4190 {
Image *tmp = work_image;
4191 work_image = curr_image;
4194 if ( work_image == image )
4195 work_image = (
Image *) NULL;
4199 if ( verbose != MagickFalse && kernel_changed != (
size_t)changed )
4200 (void) FormatLocaleFile(stderr,
" Total %.20g",(
double) kernel_changed);
4201 if ( verbose != MagickFalse && stage_loop < stage_limit )
4202 (void) FormatLocaleFile(stderr,
"\n");
4205 (void) FormatLocaleFile(stderr,
"--E-- image=0x%lx\n", (
unsigned long)image);
4206 (void) FormatLocaleFile(stderr,
" curr =0x%lx\n", (
unsigned long)curr_image);
4207 (void) FormatLocaleFile(stderr,
" work =0x%lx\n", (
unsigned long)work_image);
4208 (void) FormatLocaleFile(stderr,
" save =0x%lx\n", (
unsigned long)save_image);
4209 (void) FormatLocaleFile(stderr,
" union=0x%lx\n", (
unsigned long)rslt_image);
4222 case EdgeOutMorphology:
4223 case EdgeInMorphology:
4224 case TopHatMorphology:
4225 case BottomHatMorphology:
4226 if ( verbose != MagickFalse )
4227 (void) FormatLocaleFile(stderr,
4228 "\n%s: Difference with original image",
4229 CommandOptionToMnemonic(MagickMorphologyOptions,method));
4230 (void) CompositeImageChannel(curr_image,(ChannelType)
4231 (channel & ~SyncChannels),DifferenceCompositeOp,image,0,0);
4233 case EdgeMorphology:
4234 if ( verbose != MagickFalse )
4235 (void) FormatLocaleFile(stderr,
4236 "\n%s: Difference of Dilate and Erode",
4237 CommandOptionToMnemonic(MagickMorphologyOptions,method));
4238 (void) CompositeImageChannel(curr_image,(ChannelType)
4239 (channel & ~SyncChannels),DifferenceCompositeOp,save_image,0,0);
4240 save_image = DestroyImage(save_image);
4248 rslt_image = curr_image;
4249 else if ( rslt_compose == NoCompositeOp )
4250 {
if ( verbose != MagickFalse ) {
4251 if ( this_kernel->next != (
KernelInfo *) NULL )
4252 (void) FormatLocaleFile(stderr,
" (re-iterate)");
4254 (
void) FormatLocaleFile(stderr,
" (done)");
4256 rslt_image = curr_image;
4258 else if ( rslt_image == (
Image *) NULL)
4259 {
if ( verbose != MagickFalse )
4260 (void) FormatLocaleFile(stderr,
" (save for compose)");
4261 rslt_image = curr_image;
4262 curr_image = (
Image *) image;
4272 if ( verbose != MagickFalse )
4273 (void) FormatLocaleFile(stderr,
" (compose \"%s\")",
4274 CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
4275 (void) CompositeImageChannel(rslt_image,
4276 (ChannelType) (channel & ~SyncChannels), rslt_compose,
4278 curr_image = DestroyImage(curr_image);
4279 curr_image = (
Image *) image;
4281 if ( verbose != MagickFalse )
4282 (void) FormatLocaleFile(stderr,
"\n");
4285 norm_kernel = norm_kernel->next;
4287 rflt_kernel = rflt_kernel->next;
4297 if ( curr_image == rslt_image )
4298 curr_image = (
Image *) NULL;
4299 if ( rslt_image != (
Image *) NULL )
4300 rslt_image = DestroyImage(rslt_image);
4302 if ( curr_image == rslt_image || curr_image == image )
4303 curr_image = (
Image *) NULL;
4304 if ( curr_image != (
Image *) NULL )
4305 curr_image = DestroyImage(curr_image);
4306 if ( work_image != (
Image *) NULL )
4307 work_image = DestroyImage(work_image);
4308 if ( save_image != (
Image *) NULL )
4309 save_image = DestroyImage(save_image);
4310 if ( reflected_kernel != (
KernelInfo *) NULL )
4311 reflected_kernel = DestroyKernelInfo(reflected_kernel);
4370MagickExport
Image *MorphologyImage(
const Image *image,
4371 const MorphologyMethod method,
const ssize_t iterations,
4377 morphology_image=MorphologyImageChannel(image,DefaultChannels,method,
4378 iterations,kernel,exception);
4379 return(morphology_image);
4382MagickExport
Image *MorphologyImageChannel(
const Image *image,
4383 const ChannelType channel,
const MorphologyMethod method,
4402 assert(image != (
const Image *) NULL);
4403 assert(image->signature == MagickCoreSignature);
4405 assert(exception->signature == MagickCoreSignature);
4406 if (IsEventLogging() != MagickFalse)
4407 (void) LogMagickEvent(TraceEvent,GetMagickModule(),
"%s",image->filename);
4410 if ((method == ConvolveMorphology) || (method == CorrelateMorphology))
4415 artifact = GetImageArtifact(image,
"convolve:bias");
4416 if (artifact != (
const char *) NULL)
4417 bias=StringToDoubleInterval(artifact,(
double) QuantumRange+1.0);
4419 artifact = GetImageArtifact(image,
"convolve:scale");
4420 if ( artifact != (
const char *) NULL ) {
4421 if ( curr_kernel == kernel )
4422 curr_kernel = CloneKernelInfo(kernel);
4424 curr_kernel=DestroyKernelInfo(curr_kernel);
4425 return((
Image *) NULL);
4427 ScaleGeometryKernelInfo(curr_kernel, artifact);
4432 if ( IsMagickTrue(GetImageArtifact(image,
"showKernel"))
4433 || IsMagickTrue(GetImageArtifact(image,
"convolve:showKernel"))
4434 || IsMagickTrue(GetImageArtifact(image,
"morphology:showKernel")) )
4435 ShowKernelInfo(curr_kernel);
4445 compose = UndefinedCompositeOp;
4446 artifact = GetImageArtifact(image,
"morphology:compose");
4447 if ( artifact != (
const char *) NULL)
4448 compose = (CompositeOperator) ParseCommandOption(
4449 MagickComposeOptions,MagickFalse,artifact);
4452 morphology_image = MorphologyApply(image, channel, method, iterations,
4453 curr_kernel, compose, bias, exception);
4456 if ( curr_kernel != kernel )
4457 curr_kernel=DestroyKernelInfo(curr_kernel);
4458 return(morphology_image);
4491static void RotateKernelInfo(
KernelInfo *kernel,
double angle)
4495 RotateKernelInfo(kernel->next, angle);
4503 angle = fmod(angle, 360.0);
4507 if ( 337.5 < angle || angle <= 22.5 )
4511 switch (kernel->type) {
4513 case GaussianKernel:
4518 case LaplacianKernel:
4519 case ChebyshevKernel:
4520 case ManhattanKernel:
4521 case EuclideanKernel:
4535 if ( 135.0 < angle && angle <= 225.0 )
4537 if ( 225.0 < angle && angle <= 315.0 )
4545 if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
4547 if ( kernel->width == 3 && kernel->height == 3 )
4549 double t = kernel->values[0];
4550 kernel->values[0] = kernel->values[3];
4551 kernel->values[3] = kernel->values[6];
4552 kernel->values[6] = kernel->values[7];
4553 kernel->values[7] = kernel->values[8];
4554 kernel->values[8] = kernel->values[5];
4555 kernel->values[5] = kernel->values[2];
4556 kernel->values[2] = kernel->values[1];
4557 kernel->values[1] = t;
4559 if ( kernel->x != 1 || kernel->y != 1 ) {
4561 x = (ssize_t) kernel->x-1;
4562 y = (ssize_t) kernel->y-1;
4563 if ( x == y ) x = 0;
4564 else if ( x == 0 ) x = -y;
4565 else if ( x == -y ) y = 0;
4566 else if ( y == 0 ) y = x;
4567 kernel->x = (ssize_t) x+1;
4568 kernel->y = (ssize_t) y+1;
4570 angle = fmod(angle+315.0, 360.0);
4571 kernel->angle = fmod(kernel->angle+45.0, 360.0);
4574 perror(
"Unable to rotate non-3x3 kernel by 45 degrees");
4576 if ( 45.0 < fmod(angle, 180.0) && fmod(angle,180.0) <= 135.0 )
4578 if ( kernel->width == 1 || kernel->height == 1 )
4584 t = (ssize_t) kernel->width;
4585 kernel->width = kernel->height;
4586 kernel->height = (size_t) t;
4588 kernel->x = kernel->y;
4590 if ( kernel->width == 1 ) {
4591 angle = fmod(angle+270.0, 360.0);
4592 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4594 angle = fmod(angle+90.0, 360.0);
4595 kernel->angle = fmod(kernel->angle+270.0, 360.0);
4598 else if ( kernel->width == kernel->height )
4605 for( i=0, x=kernel->width-1; i<=x; i++, x--)
4606 for( j=0, y=kernel->height-1; j<y; j++, y--)
4607 { t = k[i+j*kernel->width];
4608 k[i+j*kernel->width] = k[j+x*kernel->width];
4609 k[j+x*kernel->width] = k[x+y*kernel->width];
4610 k[x+y*kernel->width] = k[y+i*kernel->width];
4611 k[y+i*kernel->width] = t;
4616 x = (ssize_t) (kernel->x*2-kernel->width+1);
4617 y = (ssize_t) (kernel->y*2-kernel->height+1);
4618 kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4619 kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
4621 angle = fmod(angle+270.0, 360.0);
4622 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4625 perror(
"Unable to rotate a non-square, non-linear kernel 90 degrees");
4627 if ( 135.0 < angle && angle <= 225.0 )
4645 for ( i=0, j=kernel->width*kernel->height-1; i<j; i++, j--)
4646 t=k[i], k[i]=k[j], k[j]=t;
4648 kernel->x = (ssize_t) kernel->width - kernel->x - 1;
4649 kernel->y = (ssize_t) kernel->height - kernel->y - 1;
4650 angle = fmod(angle-180.0, 360.0);
4651 kernel->angle = fmod(kernel->angle+180.0, 360.0);
4696MagickExport
void ScaleGeometryKernelInfo (
KernelInfo *kernel,
4697 const char *geometry)
4704 SetGeometryInfo(&args);
4705 flags = (GeometryFlags) ParseGeometry(geometry, &args);
4709 (void) FormatLocaleFile(stderr,
"Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4710 flags, args.rho, args.sigma, args.xi, args.psi );
4713 if ( (flags & PercentValue) != 0 )
4714 args.rho *= 0.01, args.sigma *= 0.01;
4716 if ( (flags & RhoValue) == 0 )
4718 if ( (flags & SigmaValue) == 0 )
4722 ScaleKernelInfo(kernel, args.rho, flags);
4725 if ( (flags & SigmaValue) != 0 )
4726 UnityAddKernelInfo(kernel, args.sigma);
4801MagickExport
void ScaleKernelInfo(
KernelInfo *kernel,
4802 const double scaling_factor,
const GeometryFlags normalize_flags)
4813 ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
4817 if ( (normalize_flags&NormalizeValue) != 0 ) {
4818 if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4820 pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4823 pos_scale = kernel->positive_range;
4826 if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4827 pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4828 ? kernel->positive_range : 1.0;
4829 neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4830 ? -kernel->negative_range : 1.0;
4833 neg_scale = pos_scale;
4836 pos_scale = scaling_factor/pos_scale;
4837 neg_scale = scaling_factor/neg_scale;
4839 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4840 if ( ! IsNaN(kernel->values[i]) )
4841 kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4844 kernel->positive_range *= pos_scale;
4845 kernel->negative_range *= neg_scale;
4847 kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4848 kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4851 if ( scaling_factor < MagickEpsilon ) {
4853 t = kernel->positive_range;
4854 kernel->positive_range = kernel->negative_range;
4855 kernel->negative_range = t;
4856 t = kernel->maximum;
4857 kernel->maximum = kernel->minimum;
4858 kernel->minimum = 1;
4888MagickExport
void ShowKernelInfo(
const KernelInfo *kernel)
4896 for (c=0, k=kernel; k != (
KernelInfo *) NULL; c++, k=k->next ) {
4898 (void) FormatLocaleFile(stderr,
"Kernel");
4900 (void) FormatLocaleFile(stderr,
" #%lu", (
unsigned long) c );
4901 (void) FormatLocaleFile(stderr,
" \"%s",
4902 CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4903 if ( fabs(k->angle) >= MagickEpsilon )
4904 (void) FormatLocaleFile(stderr,
"@%lg", k->angle);
4905 (void) FormatLocaleFile(stderr,
"\" of size %lux%lu%+ld%+ld",(
unsigned long)
4906 k->width,(
unsigned long) k->height,(
long) k->x,(
long) k->y);
4907 (void) FormatLocaleFile(stderr,
4908 " with values from %.*lg to %.*lg\n",
4909 GetMagickPrecision(), k->minimum,
4910 GetMagickPrecision(), k->maximum);
4911 (void) FormatLocaleFile(stderr,
"Forming a output range from %.*lg to %.*lg",
4912 GetMagickPrecision(), k->negative_range,
4913 GetMagickPrecision(), k->positive_range);
4914 if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
4915 (void) FormatLocaleFile(stderr,
" (Zero-Summing)\n");
4916 else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
4917 (void) FormatLocaleFile(stderr,
" (Normalized)\n");
4919 (
void) FormatLocaleFile(stderr,
" (Sum %.*lg)\n",
4920 GetMagickPrecision(), k->positive_range+k->negative_range);
4921 for (i=v=0; v < k->height; v++) {
4922 (void) FormatLocaleFile(stderr,
"%2lu:", (
unsigned long) v );
4923 for (u=0; u < k->width; u++, i++)
4924 if ( IsNaN(k->values[i]) )
4925 (void) FormatLocaleFile(stderr,
" %*s", GetMagickPrecision()+3,
"nan");
4927 (
void) FormatLocaleFile(stderr,
" %*.*lg", GetMagickPrecision()+3,
4928 GetMagickPrecision(), k->values[i]);
4929 (void) FormatLocaleFile(stderr,
"\n");
4968MagickExport
void UnityAddKernelInfo(
KernelInfo *kernel,
4973 UnityAddKernelInfo(kernel->next, scale);
4976 kernel->values[kernel->x+kernel->y*kernel->width] += scale;
4977 CalcKernelMetaData(kernel);
5008MagickExport
void ZeroKernelNans(
KernelInfo *kernel)
5015 ZeroKernelNans(kernel->next);
5017 for (i=0; i < (kernel->width*kernel->height); i++)
5018 if ( IsNaN(kernel->values[i]) )
5019 kernel->values[i] = 0.0;