Home | History | Annotate | Download | only in MagickCore

Lines Matching refs:kernel

45 % generation of many different types of kernel arrays from user supplied
46 % arguments. Prehaps even the generation of a kernel from a small image.
97 /* Integer Factorial Function - for a Binomial kernel */
121 /* Quick function to find last kernel in a kernel list */
122 static inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
124 while (kernel->next != (KernelInfo *) NULL)
125 kernel=kernel->next;
126 return(kernel);
141 % user) and converts it into a Morphology/Convolution Kernel. This allows
142 % users to specify a kernel from a number of pre-defined kernels, or to fully
143 % specify their own kernel for a specific Convolution or Morphology
146 % The kernel so generated can be any rectangular array of floating point
151 % center as origin, this is no longer the case, and any rectangular kernel
155 % The floating point values in the kernel can also include a special value
157 % of the kernel array. This allows you to shaped the kernel within its
158 % rectangular area. That is 'nan' values provide a 'mask' for the kernel
160 % working of a kernel.
162 % The returned kernel should be freed using the DestroyKernelInfo() when you
165 % Input kernel defintion strings can consist of any of three types.
172 % a kernel of size W by H, with W*H floating point numbers following.
180 % square kernel. At least 9 values should be provided for a 3x3
181 % square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
187 % " kernel ; kernel ; kernel ; "
189 % Any extra ';' characters, at start, end or between kernel defintions are
192 % The special flags will expand a single kernel, into a list of rotated
193 % kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
196 % reflected kernel before the +/- 90-degree rotations, which can be important
200 % new kernel specification has a ':' character in its specification string.
202 % numbers generating a odd-sized square kernel has been given.
210 % o kernel_string: the Morphology/Convolution kernel wanted.
220 *kernel;
241 kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
242 if (kernel == (KernelInfo *) NULL)
243 return(kernel);
244 (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
245 kernel->minimum = kernel->maximum = kernel->angle = 0.0;
246 kernel->negative_range = kernel->positive_range = 0.0;
247 kernel->type = UserDefinedKernel;
248 kernel->next = (KernelInfo *) NULL;
249 kernel->signature=MagickCoreSignature;
251 return(kernel);
253 /* find end of this specific kernel definition string */
258 /* clear flags - for Expanding kernel lists thorugh rotations */
261 /* Has a ':' in argument - New user kernel specification
280 kernel->width = (size_t)args.rho;
281 kernel->height = (size_t)args.sigma;
285 return(DestroyKernelInfo(kernel));
286 kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
287 : (ssize_t) (kernel->width-1)/2;
288 kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
289 : (ssize_t) (kernel->height-1)/2;
290 if ( kernel->x >= (ssize_t) kernel->width ||
291 kernel->y >= (ssize_t) kernel->height )
292 return(DestroyKernelInfo(kernel));
297 { /* ELSE - Old old specification, forming odd-square kernel */
308 /* set the size of the kernel - old sized square */
309 kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
310 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
316 /* Read in the kernel values from rest of input string argument */
317 kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
318 kernel->width,kernel->height*sizeof(*kernel->values)));
319 if (kernel->values == (MagickRealType *) NULL)
320 return(DestroyKernelInfo(kernel));
321 kernel->minimum=MagickMaximumValue;
322 kernel->maximum=(-MagickMaximumValue);
323 kernel->negative_range = kernel->positive_range = 0.0;
324 for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
331 kernel->values[i] = nan; /* this value is not part of neighbourhood */
334 kernel->values[i] = StringToDouble(token,(char **) NULL);
335 ( kernel->values[i] < 0)
336 ? ( kernel->negative_range += kernel->values[i] )
337 : ( kernel->positive_range += kernel->values[i] );
338 Minimize(kernel->minimum, kernel->values[i]);
339 Maximize(kernel->maximum, kernel->values[i]);
343 /* sanity check -- no more values in kernel definition */
346 return(DestroyKernelInfo(kernel));
349 /* this was the old method of handling a incomplete kernel */
350 if ( i < (ssize_t) (kernel->width*kernel->height) ) {
351 Minimize(kernel->minimum, kernel->values[i]);
352 Maximize(kernel->maximum, kernel->values[i]);
353 for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
354 kernel->values[i]=0.0;
357 /* Number of values for kernel was not enough - Report Error */
358 if ( i < (ssize_t) (kernel->width*kernel->height) )
359 return(DestroyKernelInfo(kernel));
363 if (kernel->minimum == MagickMaximumValue)
364 return(DestroyKernelInfo(kernel));
366 if ( (flags & AreaValue) != 0 ) /* '@' symbol in kernel size */
367 ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
368 else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
369 ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
370 else if ( (flags & LessValue) != 0 ) /* '<' symbol in kernel args */
371 ExpandMirrorKernelInfo(kernel); /* 90 degree mirror rotate */
373 return(kernel);
390 *kernel;
398 /* Parse special 'named' kernel */
402 return((KernelInfo *) NULL); /* not a valid named kernel */
408 end = strchr(p, ';'); /* end of this kernel defintion */
426 /* Shape Kernel Defaults */
456 /* Distance Kernel Defaults */
472 kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args, exception);
473 if ( kernel == (KernelInfo *) NULL )
474 return(kernel);
476 /* global expand to rotated kernel list - only for single kernels */
477 if ( kernel->next == (KernelInfo *) NULL ) {
478 if ( (flags & AreaValue) != 0 ) /* '@' symbol in kernel args */
479 ExpandRotateKernelInfo(kernel, 45.0);
480 else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
481 ExpandRotateKernelInfo(kernel, 90.0);
482 else if ( (flags & LessValue) != 0 ) /* '<' symbol in kernel args */
483 ExpandMirrorKernelInfo(kernel);
486 return(kernel);
493 *kernel,
514 kernel=NULL;
517 /* ignore extra or multiple ';' kernel separators */
520 /* tokens starting with alpha is a Named kernel */
523 else /* otherwise a user defined kernel array */
529 if (kernel != (KernelInfo *) NULL)
530 kernel=DestroyKernelInfo(kernel);
534 /* initialise or append the kernel list */
535 if (kernel == (KernelInfo *) NULL)
536 kernel=new_kernel;
538 LastKernelInfo(kernel)->next=new_kernel;
541 /* look for the next kernel in list */
549 return(kernel);
579 % o type: the pre-defined type of kernel wanted
581 % o args: arguments defining or modifying the kernel
586 % The a No-Op or Scaling single element kernel.
589 % Generate a two-dimensional gaussian kernel, as used by -gaussian.
590 % The sigma for the curve is required. The resulting kernel is
596 % the final size of the resulting kernel to a square 2*radius+1 in size.
603 % "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
604 % The supposed ideal edge detection, zero-summing kernel.
606 % An alturnative to this kernel is to use a "DoG" with a sigma ratio of
610 % "Difference of Gaussians" Kernel.
613 % The result is a zero-summing kernel.
618 % kernel is clipped to a width of 2*radius+1. Kernel can be rotated
624 % each other, is equivalent to a far larger "Gaussian" kernel with the
630 % a comet like trail. The Kernel is actually half a gaussian curve,
631 % Adding two such blurs in opposite directions produces a Blur Kernel.
634 % Note that the first argument is the width of the kernel and not the
635 % radius of the kernel.
638 % Generate a discrete kernel using a 2 dimentional Pascel's Triangle
645 % # Set kernel values using a resize filter, and given scale (sigma)
671 % Sobel 'Edge' convolution kernel (3x3)
677 % Roberts convolution kernel (3x3)
683 % Prewitt Edge convolution kernel (3x3)
689 % Prewitt's "Compass" convolution kernel (3x3)
695 % Kirsch's "Compass" convolution kernel (3x3)
701 % Frei-Chen Edge Detector is based on a kernel that is similar to
702 % the Sobel Kernel, but is designed to be isotropic. That is it takes
703 % into account the distance of the diagonal in the kernel.
715 % Type 1: Orthogonal Kernel (same as type 11 below)
720 % Type 2: Diagonal form of Kernel...
725 % However this kernel is als at the heart of the FreiChen Edge Detection
726 % Process which uses a set of 9 specially weighted kernel. These 9
776 % as a multi-kernel list, so that you can use them directly (without
781 % the default FreiChen (type 0) kernel. As such FreiChen:45 will look
796 % Generate a diamond shaped kernel with given radius to the points.
797 % Kernel size will again be radius*2+1 square and defaults to radius 1,
798 % generating a 3x3 kernel that is slightly larger than a square.
801 % Generate a square shaped kernel of size radius*2+1, and defaulting
805 % Generate octagonal shaped kernel of given radius and constant scale.
806 % Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
807 % in "Diamond" kernel.
811 % may be a float-point value. Final Kernel size is floor(radius)*2+1
826 % All other Disk shapes are unique to this kernel, but because a "Disk"
839 % These kernel is not a good general morphological kernel, but is used
848 % Generate a kernel in the shape of a 'plus' or a 'cross' with
851 % NOTE: "plus:1" is equivalent to a "Diamond" kernel.
855 % Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
856 % This is the 'edge' pixels of the default "Disk" kernel,
869 % A special kernel to thin the 'outside' of diagonals
874 % Type 1: single kernel for 4-conneected line ends
875 % Type 2: single kernel for simple line ends
879 % Type 1: Y Junction kernel
880 % Type 2: Diagonal T Junction kernel
881 % Type 3: Orthogonal T Junction kernel
882 % Type 4: Diagonal X Junction kernel
883 % Type 5: Orthogonal + Junction kernel
889 % Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
892 % Type 1: Tradional Skeleton kernel (4 connected skeleton)
893 % Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
898 % many other kernel sets use these kernels as source definitions.
930 % the "Octagon" shaped kernel of the same radius. The minimum radius
931 % and default is 2, producing a 5x5 kernel.
935 % However by default the kernel size only has a radius of 1, which
937 % diagonal measurements being correct. As such for the default kernel
943 % Of course a larger kernel is slower to use, and not always needed.
950 % The "Euclidean" Distance Kernel however does generate a non-integer
959 *kernel;
971 /* Generate a new empty kernel if needed */
972 kernel=(KernelInfo *) NULL;
994 break; /* A pre-generated kernel is not needed */
1020 /* Generate the base Kernel Structure */
1021 kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1022 if (kernel == (KernelInfo *) NULL)
1023 return(kernel);
1024 (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
1025 kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1026 kernel->negative_range = kernel->positive_range = 0.0;
1027 kernel->type = type;
1028 kernel->next = (KernelInfo *) NULL;
1029 kernel->signature=MagickCoreSignature;
1039 kernel->height = kernel->width = (size_t) 1;
1040 kernel->x = kernel->y = (ssize_t) 0;
1041 kernel->values=(MagickRealType *) MagickAssumeAligned(
1042 AcquireAlignedMemory(1,sizeof(*kernel->values)));
1043 if (kernel->values == (MagickRealType *) NULL)
1044 return(DestroyKernelInfo(kernel));
1045 kernel->maximum = kernel->values[0] = args->rho;
1058 kernel->width = (size_t)args->rho*2+1;
1060 kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1062 kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1063 kernel->height = kernel->width;
1064 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1065 kernel->values=(MagickRealType *) MagickAssumeAligned(
1066 AcquireAlignedMemory(kernel->width,kernel->height*
1067 sizeof(*kernel->values)));
1068 if (kernel->values == (MagickRealType *) NULL)
1069 return(DestroyKernelInfo(kernel));
1071 /* WARNING: The following generates a 'sampled gaussian' kernel.
1072 * What we really want is a 'discrete gaussian' kernel.
1083 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1084 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1085 kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
1087 else /* limiting case - a unity (normalized Dirac) kernel */
1088 { (void) ResetMagickMemory(kernel->values,0, (size_t)
1089 kernel->width*kernel->height*sizeof(*kernel->values));
1090 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1100 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1101 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1102 kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1104 else /* limiting case - a unity (normalized Dirac) kernel */
1105 kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1113 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1114 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1116 kernel->values[i] = (1-R)*exp(-R)*B;
1119 else /* special case - generate a unity kernel */
1120 { (void) ResetMagickMemory(kernel->values,0, (size_t)
1121 kernel->width*kernel->height*sizeof(*kernel->values));
1122 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1127 ** radius, producing a smaller (darker) kernel. Also for very small
1129 ** producing a very bright kernel.
1134 /* Normalize the 2D Gaussian Kernel
1139 CalcKernelMetaData(kernel); /* the other kernel meta-data */
1140 ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1150 kernel->width = (size_t)args->rho*2+1;
1152 kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1153 kernel->height = 1;
1154 kernel->x = (ssize_t) (kernel->width-1)/2;
1155 kernel->y = 0;
1156 kernel->negative_range = kernel->positive_range = 0.0;
1157 kernel->values=(MagickRealType *) MagickAssumeAligned(
1158 AcquireAlignedMemory(kernel->width,kernel->height*
1159 sizeof(*kernel->values)));
1160 if (kernel->values == (MagickRealType *) NULL)
1161 return(DestroyKernelInfo(kernel));
1168 ** resulting kernel, especially for very low sigma values.
1179 v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
1180 (void) ResetMagickMemory(kernel->values,0, (size_t)
1181 kernel->width*kernel->height*sizeof(*kernel->values));
1188 kernel->values[(u+v)/KernelRank] +=
1192 else /* special case - generate a unity kernel */
1193 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1202 for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1203 kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1205 else /* special case - generate a unity kernel */
1206 { (void) ResetMagickMemory(kernel->values,0, (size_t)
1207 kernel->width*kernel->height*sizeof(*kernel->values));
1208 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1211 /* Note the above kernel may have been 'clipped' by a user defined
1212 ** radius, producing a smaller (darker) kernel. Also for very small
1214 ** result of not generating a actual 'discrete' kernel, and thus
1220 /* Normalize the 1D Gaussian Kernel
1225 CalcKernelMetaData(kernel); /* the other kernel meta-data */
1226 ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1228 /* rotate the 1D kernel by given angle */
1229 RotateKernelInfo(kernel, args->xi );
1238 kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1240 kernel->width = (size_t)args->rho;
1241 kernel->x = kernel->y = 0;
1242 kernel->height = 1;
1243 kernel->negative_range = kernel->positive_range = 0.0;
1244 kernel->values=(MagickRealType *) MagickAssumeAligned(
1245 AcquireAlignedMemory(kernel->width,kernel->height*
1246 sizeof(*kernel->values)));
1247 if (kernel->values == (MagickRealType *) NULL)
1248 return(DestroyKernelInfo(kernel));
1264 v = (ssize_t) kernel->width*KernelRank; /* start/end points */
1265 (void) ResetMagickMemory(kernel->values,0, (size_t)
1266 kernel->width*sizeof(*kernel->values));
1271 kernel->values[u/KernelRank] +=
1275 for (i=0; i < (ssize_t) kernel->width; i++)
1276 kernel->positive_range += kernel->values[i];
1280 for ( i=0; i < (ssize_t) kernel->width; i++)
1281 kernel->positive_range +=
1282 kernel->values[i] = exp(-((double)(i*i))*A);
1286 else /* special case - generate a unity kernel */
1287 { (void) ResetMagickMemory(kernel->values,0, (size_t)
1288 kernel->width*kernel->height*sizeof(*kernel->values));
1289 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1290 kernel->positive_range = 1.0;
1293 kernel->minimum = 0.0;
1294 kernel->maximum = kernel->values[0];
1295 kernel->negative_range = 0.0;
1297 ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
1298 RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
1307 kernel->width = kernel->height = 3; /* default radius = 1 */
1309 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1310 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1312 order_f = fact(kernel->width-1);
1314 kernel->values=(MagickRealType *) MagickAssumeAligned(
1315 AcquireAlignedMemory(kernel->width,kernel->height*
1316 sizeof(*kernel->values)));
1317 if (kernel->values == (MagickRealType *) NULL)
1318 return(DestroyKernelInfo(kernel));
1320 /* set all kernel values within diamond area to scale given */
1321 for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
1323 alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
1324 for ( u=0; u < (ssize_t)kernel->width; u++, i++)
1325 kernel->positive_range += kernel->values[i] = (double)
1326 (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
1328 kernel->minimum = 1.0;
1329 kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
1330 kernel->negative_range = 0.0;
1341 kernel=ParseKernelArray("3: -1,-1,-1 -1,8,-1 -1,-1,-1");
1344 kernel=ParseKernelArray("3: 0,-1,0 -1,4,-1 0,-1,0");
1347 kernel=ParseKernelArray("3: -2,1,-2 1,4,1 -2,1,-2");
1350 kernel=ParseKernelArray("3: 1,-2,1 -2,4,-2 1,-2,1");
1353 kernel=ParseKernelArray(
1357 kernel=ParseKernelArray(
1361 kernel=ParseKernelArray(
1366 kernel=ParseKernelArray(
1370 if (kernel == (KernelInfo *) NULL)
1371 return(kernel);
1372 kernel->type = type;
1376 { /* Simple Sobel Kernel */
1377 kernel=ParseKernelArray("3: 1,0,-1 2,0,-2 1,0,-1");
1378 if (kernel == (KernelInfo *) NULL)
1379 return(kernel);
1380 kernel->type = type;
1381 RotateKernelInfo(kernel, args->rho);
1386 kernel=ParseKernelArray("3: 0,0,0 1,-1,0 0,0,0");
1387 if (kernel == (KernelInfo *) NULL)
1388 return(kernel);
1389 kernel->type = type;
1390 RotateKernelInfo(kernel, args->rho);
1395 kernel=ParseKernelArray("3: 1,0,-1 1,0,-1 1,0,-1");
1396 if (kernel == (KernelInfo *) NULL)
1397 return(kernel);
1398 kernel->type = type;
1399 RotateKernelInfo(kernel, args->rho);
1404 kernel=ParseKernelArray("3: 1,1,-1 1,-2,-1 1,1,-1");
1405 if (kernel == (KernelInfo *) NULL)
1406 return(kernel);
1407 kernel->type = type;
1408 RotateKernelInfo(kernel, args->rho);
1413 kernel=ParseKernelArray("3: 5,-3,-3 5,0,-3 5,-3,-3");
1414 if (kernel == (KernelInfo *) NULL)
1415 return(kernel);
1416 kernel->type = type;
1417 RotateKernelInfo(kernel, args->rho);
1427 kernel=ParseKernelArray("3: 1,0,-1 2,0,-2 1,0,-1");
1428 if (kernel == (KernelInfo *) NULL)
1429 return(kernel);
1430 kernel->type = type;
1431 kernel->values[3] = +(MagickRealType) MagickSQ2;
1432 kernel->values[5] = -(MagickRealType) MagickSQ2;
1433 CalcKernelMetaData(kernel); /* recalculate meta-data */
1436 kernel=ParseKernelArray("3: 1,2,0 2,0,-2 0,-2,-1");
1437 if (kernel == (KernelInfo *) NULL)
1438 return(kernel);
1439 kernel->type = type;
1440 kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
1441 kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
1442 CalcKernelMetaData(kernel); /* recalculate meta-data */
1443 ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1447 kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19",exception);
1448 if (kernel == (KernelInfo *) NULL)
1449 return(kernel);
1454 kernel=ParseKernelArray("3: 1,0,-1 2,0,-2 1,0,-1");
1455 if (kernel == (KernelInfo *) NULL)
1456 return(kernel);
1457 kernel->type = type;
1458 kernel->values[3] = +(MagickRealType) MagickSQ2;
1459 kernel->values[5] = -(MagickRealType) MagickSQ2;
1460 CalcKernelMetaData(kernel); /* recalculate meta-data */
1461 ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1464 kernel=ParseKernelArray("3: 1,2,1 0,0,0 1,2,1");
1465 if (kernel == (KernelInfo *) NULL)
1466 return(kernel);
1467 kernel->type = type;
1468 kernel->values[1] = +(MagickRealType) MagickSQ2;
1469 kernel->values[7] = +(MagickRealType) MagickSQ2;
1470 CalcKernelMetaData(kernel);
1471 ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1474 kernel=ParseKernelArray("3: 2,-1,0 -1,0,1 0,1,-2");
1475 if (kernel == (KernelInfo *) NULL)
1476 return(kernel);
1477 kernel->type = type;
1478 kernel->values[0] = +(MagickRealType) MagickSQ2;
1479 kernel->values[8] = -(MagickRealType) MagickSQ2;
1480 CalcKernelMetaData(kernel);
1481 ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1484 kernel=ParseKernelArray("3: 0,1,-2 -1,0,1 2,-1,0");
1485 if (kernel == (KernelInfo *) NULL)
1486 return(kernel);
1487 kernel->type = type;
1488 kernel->values[2] = -(MagickRealType) MagickSQ2;
1489 kernel->values[6] = +(MagickRealType) MagickSQ2;
1490 CalcKernelMetaData(kernel);
1491 ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1494 kernel=ParseKernelArray("3: 0,-1,0 1,0,1 0,-1,0");
1495 if (kernel == (KernelInfo *) NULL)
1496 return(kernel);
1497 kernel->type = type;
1498 ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1501 kernel=ParseKernelArray("3: 1,0,-1 0,0,0 -1,0,1");
1502 if (kernel == (KernelInfo *) NULL)
1503 return(kernel);
1504 kernel->type = type;
1505 ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1508 kernel=ParseKernelArray("3: 1,-2,1 -2,4,-2 -1,-2,1");
1509 if (kernel == (KernelInfo *) NULL)
1510 return(kernel);
1511 kernel->type = type;
1512 ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1515 kernel=ParseKernelArray("3: -2,1,-2 1,4,1 -2,1,-2");
1516 if (kernel == (KernelInfo *) NULL)
1517 return(kernel);
1518 kernel->type = type;
1519 ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1522 kernel=ParseKernelArray("3: 1,1,1 1,1,1 1,1,1");
1523 if (kernel == (KernelInfo *) NULL)
1524 return(kernel);
1525 kernel->type = type;
1526 ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1531 RotateKernelInfo(kernel, args->sigma);
1534 RotateKernelInfo(kernel, args->rho);
1544 kernel->width = kernel->height = 3; /* default radius = 1 */
1546 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1547 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1549 kernel->values=(MagickRealType *) MagickAssumeAligned(
1550 AcquireAlignedMemory(kernel->width,kernel->height*
1551 sizeof(*kernel->values)));
1552 if (kernel->values == (MagickRealType *) NULL)
1553 return(DestroyKernelInfo(kernel));
1555 /* set all kernel values within diamond area to scale given */
1556 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1557 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1558 if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1559 kernel->positive_range += kernel->values[i] = args->sigma;
1561 kernel->values[i] = nan;
1562 kernel->minimum = kernel->maximum = args->sigma; /* a flat shape */
1572 kernel->width = kernel->height = 3; /* default radius = 1 */
1574 kernel->width = kernel->height = (size_t) (2*args->rho+1);
1575 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1581 return(DestroyKernelInfo(kernel)); /* invalid args given */
1582 kernel->width = (size_t)args->rho;
1583 kernel->height = (size_t)args->sigma;
1584 if ( args->xi < 0.0 || args->xi > (double)kernel->width ||
1585 args->psi < 0.0 || args->psi > (double)kernel->height )
1586 return(DestroyKernelInfo(kernel)); /* invalid args given */
1587 kernel->x = (ssize_t) args->xi;
1588 kernel->y = (ssize_t) args->psi;
1591 kernel->values=(MagickRealType *) MagickAssumeAligned(
1592 AcquireAlignedMemory(kernel->width,kernel->height*
1593 sizeof(*kernel->values)));
1594 if (kernel->values == (MagickRealType *) NULL)
1595 return(DestroyKernelInfo(kernel));
1597 /* set all kernel values to scale given */
1598 u=(ssize_t) (kernel->width*kernel->height);
1600 kernel->values[i] = scale;
1601 kernel->minimum = kernel->maximum = scale; /* a flat shape */
1602 kernel->positive_range = scale*u;
1608 kernel->width = kernel->height = 5; /* default radius = 2 */
1610 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1611 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1613 kernel->values=(MagickRealType *) MagickAssumeAligned(
1614 AcquireAlignedMemory(kernel->width,kernel->height*
1615 sizeof(*kernel->values)));
1616 if (kernel->values == (MagickRealType *) NULL)
1617 return(DestroyKernelInfo(kernel));
1619 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1620 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1622 ((long)kernel->x + (long)(kernel->x/2)) )
1623 kernel->positive_range += kernel->values[i] = args->sigma;
1625 kernel->values[i] = nan;
1626 kernel->minimum = kernel->maximum = args->sigma; /* a flat shape */
1635 kernel->width = kernel->height = 9L, limit = 18L;
1637 kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
1638 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1640 kernel->values=(MagickRealType *) MagickAssumeAligned(
1641 AcquireAlignedMemory(kernel->width,kernel->height*
1642 sizeof(*kernel->values)));
1643 if (kernel->values == (MagickRealType *) NULL)
1644 return(DestroyKernelInfo(kernel));
1646 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1647 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1649 kernel->positive_range += kernel->values[i] = args->sigma;
1651 kernel->values[i] = nan;
1652 kernel->minimum = kernel->maximum = args->sigma; /* a flat shape */
1658 kernel->width = kernel->height = 5; /* default radius 2 */
1660 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1661 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1663 kernel->values=(MagickRealType *) MagickAssumeAligned(
1664 AcquireAlignedMemory(kernel->width,kernel->height*
1665 sizeof(*kernel->values)));
1666 if (kernel->values == (MagickRealType *) NULL)
1667 return(DestroyKernelInfo(kernel));
1669 /* set all kernel values along axises to given scale */
1670 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1671 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1672 kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
1673 kernel->minimum = kernel->maximum = args->sigma; /* a flat shape */
1674 kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1680 kernel->width = kernel->height = 5; /* default radius 2 */
1682 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1683 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1685 kernel->values=(MagickRealType *) MagickAssumeAligned(
1686 AcquireAlignedMemory(kernel->width,kernel->height*
1687 sizeof(*kernel->values)));
1688 if (kernel->values == (MagickRealType *) NULL)
1689 return(DestroyKernelInfo(kernel));
1691 /* set all kernel values along axises to given scale */
1692 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1693 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1694 kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
1695 kernel->minimum = kernel->maximum = args->sigma; /* a flat shape */
1696 kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
1712 kernel->width = ((size_t)args->sigma)*2+1;
1718 kernel->width = ((size_t)args->rho)*2+1;
1723 kernel->width = 7L, limit1 = 7L, limit2 = 11L;
1725 kernel->height = kernel->width;
1726 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1727 kernel->values=(MagickRealType *) MagickAssumeAligned(
1728 AcquireAlignedMemory(kernel->width,kernel->height*
1729 sizeof(*kernel->values)));
1730 if (kernel->values == (MagickRealType *) NULL)
1731 return(DestroyKernelInfo(kernel));
1735 for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
1736 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1739 kernel->positive_range += kernel->values[i] = (double) scale;
1741 kernel->values[i] = nan;
1743 kernel->minimum = kernel->maximum = (double) scale;
1746 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1747 kernel->positive_range = 1.0;
1748 kernel->maximum = 1.0;
1754 kernel=AcquireKernelInfo("ThinSE:482",exception);
1755 if (kernel == (KernelInfo *) NULL)
1756 return(kernel);
1757 kernel->type = type;
1758 ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
1763 kernel=AcquireKernelInfo("ThinSE:87",exception);
1764 if (kernel == (KernelInfo *) NULL)
1765 return(kernel);
1766 kernel->type = type;
1767 ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
1777 kernel=ParseKernelArray("3: 0,0,0 0,-,1 1,1,-");
1778 if (kernel == (KernelInfo *) NULL)
1779 return(kernel);
1780 kernel->type = type;
1783 return(DestroyKernelInfo(kernel));
1785 LastKernelInfo(kernel)->next = new_kernel;
1786 ExpandMirrorKernelInfo(kernel);
1787 return(kernel);
1790 kernel=ParseKernelArray("3: 0,0,0 0,-,1 1,1,-");
1793 kernel=ParseKernelArray("3: 0,0,1 0,-,1 0,1,-");
1796 if (kernel == (KernelInfo *) NULL)
1797 return(kernel);
1798 kernel->type = type;
1799 RotateKernelInfo(kernel, args->sigma);
1810 /* kernel for 4-connected line ends - no rotation */
1811 kernel=ParseKernelArray("3: 0,0,- 0,1,1 0,0,-");
1814 /* kernel to add for 8-connected lines - no rotation */
1815 kernel=ParseKernelArray("3: 0,0,0 0,1,0 0,0,1");
1818 /* kernel to add for orthogonal line ends - does not find corners */
1819 kernel=ParseKernelArray("3: 0,0,0 0,1,1 0,0,0");
1823 kernel=ParseKernelArray("3: 0,0,0 0,1,- 0,0,-");
1826 if (kernel == (KernelInfo *) NULL)
1827 return(kernel);
1828 kernel->type = type;
1829 RotateKernelInfo(kernel, args->sigma);
1841 kernel=ParseKernelArray("3: 1,-,1 -,1,- -,1,-");
1845 kernel=ParseKernelArray("3: 1,-,- -,1,- 1,-,1");
1849 kernel=ParseKernelArray("3: -,-,- 1,1,1 -,1,-");
1853 kernel=ParseKernelArray("3: 1,-,1 -,1,- 1,-,1");
1856 /* Orthogonal X Junctions - minimal diamond kernel */
1857 kernel=ParseKernelArray("3: -,1,- 1,1,1 -,1,-");
1860 if (kernel == (KernelInfo *) NULL)
1861 return(kernel);
1862 kernel->type = type;
1863 RotateKernelInfo(kernel, args->sigma);
1873 kernel=ParseKernelArray("3x1:0,1,0");
1874 if (kernel == (KernelInfo *) NULL)
1875 return(kernel);
1876 kernel->type = type;
1877 ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
1880 kernel=ParseKernelArray("4x1:0,1,1,0");
1881 if (kernel == (KernelInfo *) NULL)
1882 return(kernel);
1883 kernel->type = type;
1884 ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
1887 /* Unfortunatally we can not yet rotate a non-square kernel */
1888 /* But then we can't flip a non-symetrical kernel either */
1891 return(DestroyKernelInfo(kernel));
1893 LastKernelInfo(kernel)->next = new_kernel;
1896 return(DestroyKernelInfo(kernel));
1898 LastKernelInfo(kernel)->next = new_kernel;
1901 return(DestroyKernelInfo(kernel));
1903 LastKernelInfo(kernel)->next = new_kernel;
1906 return(DestroyKernelInfo(kernel));
1908 LastKernelInfo(kernel)->next = new_kernel;
1911 return(DestroyKernelInfo(kernel));
1913 LastKernelInfo(kernel)->next = new_kernel;
1916 return(DestroyKernelInfo(kernel));
1918 LastKernelInfo(kernel)->next = new_kernel;
1921 return(DestroyKernelInfo(kernel));
1923 LastKernelInfo(kernel)->next = new_kernel;
1926 return(DestroyKernelInfo(kernel));
1928 LastKernelInfo(kernel)->next = new_kernel;
1938 kernel=ParseKernelArray("3: 1,1,- 1,0,- 1,-,0");
1939 if (kernel == (KernelInfo *) NULL)
1940 return(kernel);
1941 kernel->type = type;
1942 ExpandRotateKernelInfo(kernel, 90.0);
1946 return(DestroyKernelInfo(kernel));
1949 LastKernelInfo(kernel)->next = new_kernel;
1958 ** A cyclically rotated single kernel
1960 kernel=AcquireKernelInfo("ThinSE:482",exception);
1961 if (kernel == (KernelInfo *) NULL)
1962 return(kernel);
1963 kernel->type = type;
1964 ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
1971 kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;",exception);
1972 if (kernel == (KernelInfo *) NULL)
1973 return(kernel);
1974 if (kernel->next == (KernelInfo *) NULL)
1975 return(DestroyKernelInfo(kernel));
1976 kernel->type = type;
1977 kernel->next->type = type;
1978 ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
1986 kernel=AcquireKernelInfo("ThinSE:41; ThinSE:42; ThinSE:43",
1988 if (kernel == (KernelInfo *) NULL)
1989 return(kernel);
1990 kernel->type = type;
1991 kernel->next->type = type;
1992 kernel->next->next->type = type;
1993 ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
2012 kernel=ParseKernelArray("3: -,-,1 0,-,1 -,-,1");
2015 kernel=ParseKernelArray("3: -,-,1 0,-,1 -,0,-");
2018 kernel=ParseKernelArray("3: -,0,- 0,-,1 -,-,1");
2021 kernel=ParseKernelArray("3: -,0,- 0,-,1 -,0,-");
2024 kernel=ParseKernelArray("3: -,0,1 0,-,1 -,0,-");
2027 kernel=ParseKernelArray("3: -,0,- 0,-,1 -,0,1");
2030 kernel=ParseKernelArray("3: -,1,1 0,-,1 -,0,-");
2033 kernel=ParseKernelArray("3: -,-,1 0,-,1 0,-,1");
2036 kernel=ParseKernelArray("3: 0,-,1 0,-,1 -,-,1");
2040 kernel=ParseKernelArray("3: -,1,- 0,-,1 -,1,-");
2043 kernel=ParseKernelArray("3: -,1,- 0,-,1 0,-,-");
2046 kernel=ParseKernelArray("3: 0,-,- 0,-,1 -,1,-");
2049 kernel=ParseKernelArray("3: 0,-,- 0,-,1 0,-,-");
2052 kernel=ParseKernelArray("3: 0,-,1 0,-,1 0,-,-");
2055 kernel=ParseKernelArray("3: 0,-,- 0,-,1 0,-,1");
2058 kernel=ParseKernelArray("3: -,1,- 0,-,1 0,0,-");
2061 kernel=ParseKernelArray("3: -,1,- 0,-,1 0,1,-");
2064 kernel=ParseKernelArray("3: 0,1,- 0,-,1 -,1,-");
2067 case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
2068 kernel=ParseKernelArray("3: -,-,1 0,-,- -,0,-");
2070 case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
2071 kernel=ParseKernelArray("3: -,1,- -,-,1 0,-,-");
2073 case 481: /* SE_48_1 - General Connected Corner Kernel */
2074 kernel=ParseKernelArray("3: -,1,1 0,-,1 0,0,-");
2077 case 482: /* SE_48_2 - General Edge Kernel */
2078 kernel=ParseKernelArray("3: 0,-,1 0,-,1 0,-,1");
2081 if (kernel == (KernelInfo *) NULL)
2082 return(kernel);
2083 kernel->type = type;
2084 RotateKernelInfo(kernel, args->sigma);
2093 kernel->width = kernel->height = 3; /* default radius = 1 */
2095 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2096 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2098 kernel->values=(MagickRealType *) MagickAssumeAligned(
2099 AcquireAlignedMemory(kernel->width,kernel->height*
2100 sizeof(*kernel->values)));
2101 if (kernel->values == (MagickRealType *) NULL)
2102 return(DestroyKernelInfo(kernel));
2104 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2105 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2106 kernel->positive_range += ( kernel->values[i] =
2108 kernel->maximum = kernel->values[0];
2114 kernel->width = kernel->height = 3; /* default radius = 1 */
2116 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2117 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2119 kernel->values=(MagickRealType *) MagickAssumeAligned(
2120 AcquireAlignedMemory(kernel->width,kernel->height*
2121 sizeof(*kernel->values)));
2122 if (kernel->values == (MagickRealType *) NULL)
2123 return(DestroyKernelInfo(kernel));
2125 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2126 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2127 kernel->positive_range += ( kernel->values[i] =
2129 kernel->maximum = kernel->values[0];
2135 kernel->width = kernel->height = 5; /* default/minimum radius = 2 */
2137 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2138 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2140 kernel->values=(MagickRealType *) MagickAssumeAligned(
2141 AcquireAlignedMemory(kernel->width,kernel->height*
2142 sizeof(*kernel->values)));
2143 if (kernel->values == (MagickRealType *) NULL)
2144 return(DestroyKernelInfo(kernel));
2146 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2147 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2152 kernel->positive_range += kernel->values[i] =
2155 kernel->maximum = kernel->values[0];
2161 kernel->width = kernel->height = 3; /* default radius = 1 */
2163 kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2164 kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2166 kernel->values=(MagickRealType *) MagickAssumeAligned(
2167 AcquireAlignedMemory(kernel->width,kernel->height*
2168 sizeof(*kernel->values)));
2169 if (kernel->values == (MagickRealType *) NULL)
2170 return(DestroyKernelInfo(kernel));
2172 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2173 for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2174 kernel->positive_range += ( kernel->values[i] =
2176 kernel->maximum = kernel->values[0];
2181 /* No-Op Kernel - Basically just a single pixel on its own */
2182 kernel=ParseKernelArray("1:1");
2183 if (kernel == (KernelInfo *) NULL)
2184 return(kernel);
2185 kernel->type = UndefinedKernel;
2190 return(kernel);
2205 % CloneKernelInfo() creates a new clone of the given Kernel List so that its
2206 % can be modified without effecting the original. The cloned kernel should
2211 % KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
2215 % o kernel: the Morphology/Convolution kernel to be cloned
2218 MagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
2226 assert(kernel != (KernelInfo *) NULL);
2227 new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
2230 *new_kernel=(*kernel); /* copy values in structure */
2234 AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
2237 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
2238 new_kernel->values[i]=kernel->values[i];
2240 /* Also clone the next kernel in the kernel list */
2241 if ( kernel->next != (KernelInfo *) NULL ) {
2242 new_kernel->next = CloneKernelInfo(kernel->next);
2263 % kernel.
2267 % KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2271 % o kernel: the Morphology/Convolution kernel to be destroyed
2274 MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2276 assert(kernel != (KernelInfo *) NULL);
2277 if (kernel->next != (KernelInfo *) NULL)
2278 kernel->next=DestroyKernelInfo(kernel->next);
2279 kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
2280 kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
2281 return(kernel);
2296 % ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
2305 % void ExpandMirrorKernelInfo(KernelInfo *kernel)
2309 % o kernel: the Morphology/Convolution kernel
2317 static void FlopKernelInfo(KernelInfo *kernel)
2326 for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2327 for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2330 kernel->x = kernel->width - kernel->x - 1;
2335 static void ExpandMirrorKernelInfo(KernelInfo *kernel)
2341 last = kernel;
2372 % ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2373 % incrementally by the angle given, until the kernel repeats.
2380 % void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
2384 % o kernel: the Morphology/Convolution kernel
2407 /* check actual kernel values */
2422 static void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
2428 last = kernel;
2434 if ( SameKernelInfo(kernel, clone) != MagickFalse )
2439 clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
2455 % CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
2456 % using the kernel values. This should only ne used if it is not possible to
2460 % used to perform kernel normalization.
2464 % void CalcKernelMetaData(KernelInfo *kernel, const double scale )
2468 % o kernel: the Morphology/Convolution kernel to modify
2471 % zero is not part of the kernel (as in Gaussian Derived kernels). This
2474 % WARNING: Only the specific kernel pointed to is modified, not a list of
2480 static void CalcKernelMetaData(KernelInfo *kernel)
2485 kernel->minimum = kernel->maximum = 0.0;
2486 kernel->negative_range = kernel->positive_range = 0.0;
2487 for (i=0; i < (kernel->width*kernel->height); i++)
2489 if ( fabs(kernel->values[i]) < MagickEpsilon )
2490 kernel->values[i] = 0.0;
2491 ( kernel->values[i] < 0)
2492 ? ( kernel->negative_range += kernel->values[i] )
2493 : ( kernel->positive_range += kernel->values[i] );
2494 Minimize(kernel->minimum, kernel->values[i]);
2495 Maximize(kernel->maximum, kernel->values[i]);
2533 % const ssize_t iterations,const KernelInfo *kernel,
2550 % o kernel: An array of double representing the morphology kernel.
2552 % o compose: How to handle or merge multi-kernel results.
2554 % If 'NoCompositeOp' force image to be re-iterated by each kernel.
2563 const MorphologyMethod method,const KernelInfo *kernel,const double bias,
2594 assert(kernel != (KernelInfo *) NULL);
2595 assert(kernel->signature == MagickCoreSignature);
2602 width=image->columns+kernel->width-1;
2613 Kernel needs to used with reflection about origin.
2615 offset.x=(ssize_t) kernel->width-kernel->x-1;
2616 offset.y=(ssize_t) kernel->height-kernel->y-1;
2625 offset.x=kernel->x;
2626 offset.y=kernel->y;
2643 if ((method == ConvolveMorphology) && (kernel->width == 1))
2678 kernel->height-1,exception);
2730 k=(&kernel->values[kernel->height-1]);
2736 for (v=0; v < (ssize_t) kernel->height; v++)
2748 for (v=0; v < (ssize_t) kernel->height; v++)
2764 gamma*=(double) kernel->height/count;
2822 kernel->height,exception);
2884 count=kernel->width*kernel->height;
2908 Weighted Average of pixels using reflected kernel
2911 the kernel needs to be applied in its reflected form. That is
2915 the kernel, and thus 'lower-level' that Convolution. However as
2918 kernel, so it is Convolution that is implemented.
2920 Correlation will have its kernel reflected before calling this
2926 k=(&kernel->values[kernel->width*kernel->height-1]);
2933 for (v=0; v < (ssize_t) kernel->height; v++)
2935 for (u=0; u < (ssize_t) kernel->width; u++)
2953 for (v=0; v < (ssize_t) kernel->height; v++)
2955 for (u=0; u < (ssize_t) kernel->width; u++)
2974 Minimum value within kernel neighbourhood.
2976 The kernel is not reflected for this operation. In normal
2977 Greyscale Morphology, the kernel value should be added
2981 k=kernel->values;
2982 for (v=0; v < (ssize_t) kernel->height; v++)
2984 for (u=0; u < (ssize_t) kernel->width; u++)
3001 Maximum value within kernel neighbourhood.
3004 the kernel needs to be applied in its reflected form. That is
3007 In normal Greyscale Morphology, the kernel value should be
3012 k=(&kernel->values[kernel->width*kernel->height-1]);
3013 for (v=0; v < (ssize_t) kernel->height; v++)
3015 for (u=0; u < (ssize_t) kernel->width; u++)
3036 The kernel is not reflected for this operation, and consists
3046 k=kernel->values;
3047 for (v=0; v < (ssize_t) kernel->height; v++)
3049 for (u=0; u < (ssize_t) kernel->width; u++)
3084 Select pixel with minimum intensity within kernel neighbourhood.
3086 The kernel is not reflected for this operation.
3089 k=kernel->values;
3090 for (v=0; v < (ssize_t) kernel->height; v++)
3092 for (u=0; u < (ssize_t) kernel->width; u++)
3114 Select pixel with maximum intensity within kernel neighbourhood.
3116 The kernel is not reflected for this operation.
3119 k=(&kernel->values[kernel->width*kernel->height-1]);
3120 for (v=0; v < (ssize_t) kernel->height; v++)
3122 for (u=0; u < (ssize_t) kernel->width; u++)
3148 It works by adding kernel values to the neighbourhood, and and
3149 select the minimum value found. The kernel is rotated before
3150 use, so kernel distances match resulting distances, when a user
3151 provided asymmetric kernel is applied.
3156 GreyDilate Kernel values added, maximum value found Kernel is
3159 GrayErode: Kernel values subtracted and minimum value found No
3160 kernel rotation used.
3163 GrayErode, but with negative kernel values, and kernel rotation
3167 k=(&kernel->values[kernel->width*kernel->height-1]);
3168 for (v=0; v < (ssize_t) kernel->height; v++)
3170 for (u=0; u < (ssize_t) kernel->width; u++)
3193 gamma*=(double) kernel->height*kernel->width/count;
3236 const MorphologyMethod method,const KernelInfo *kernel,
3261 assert(kernel != (KernelInfo *) NULL);
3262 assert(kernel->signature == MagickCoreSignature);
3274 Kernel reflected about origin.
3276 offset.x=(ssize_t) kernel->width-kernel->x-1;
3277 offset.y=(ssize_t) kernel->height-kernel->y-1;
3282 offset.x=kernel->x;
3283 offset.y=kernel->y;
3292 width=image->columns+kernel->width-1;
3312 Only top half of kernel is processed as we do a single pass downward
3365 k=(&kernel->values[kernel->width*kernel->height-1]);
3368 for (u=0; u < (ssize_t) kernel->width; u++)
3380 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3396 k=(&kernel->values[kernel->width*kernel->height-1]);
3399 for (u=0; u < (ssize_t) kernel->width; u++)
3411 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3473 Only the bottom half of the kernel is processed as we up the image.
3478 kernel->y+1,exception);
3526 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3527 for (v=offset.y; v < (ssize_t) kernel->height; v++)
3529 for (u=0; u < (ssize_t) kernel->width; u++)
3541 k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
3543 for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3557 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3558 for (v=offset.y; v < (ssize_t) kernel->height; v++)
3560 for (u=0; u < (ssize_t) kernel->width; u++)
3572 k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3574 for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3624 const KernelInfo *kernel, const CompositeOperator compose,const double bias,
3634 *rslt_image; /* resultant image - after multi-kernel handling */
3637 *reflected_kernel, /* A reflected copy of the kernel (if needed) */
3638 *norm_kernel, /* the current normal un-reflected kernel */
3639 *rflt_kernel, /* the current reflected kernel (if needed) */
3640 *this_kernel; /* the kernel being applied */
3646 rslt_compose; /* multi-kernel compose method for results to use */
3655 kernel_number, /* Loop 2: the kernel number being applied */
3658 kernel_loop, /* Loop 4: iterate the kernel over image */
3659 kernel_limit, /* number of times to iterate kernel */
3661 kernel_changed, /* total count of changed using iterated kernel */
3672 assert(kernel != (KernelInfo *) NULL);
3673 assert(kernel->signature == MagickCoreSignature);
3697 * + multi-kernel compose method to use (by default)
3717 rslt_compose = LightenCompositeOp; /* Union of multi-kernel results */
3722 kernel_limit = 1; /* do not do kernel iteration */
3743 changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
3766 /* Handle user (caller) specified multi-kernel composition method */
3772 /* Some methods require a reflected kernel to use with primitives.
3773 * Create the reflected kernel for those methods. */
3780 reflected_kernel = CloneKernelInfo(kernel);
3799 /* Loop 2: iterate over each kernel in a multi-kernel list */
3800 norm_kernel = (KernelInfo *) kernel;
3801 this_kernel = (KernelInfo *) kernel;
3813 this_kernel = norm_kernel; /* default use unreflected kernel */
3837 this_kernel = rflt_kernel; /* use the reflected kernel */
3843 this_kernel = rflt_kernel; /* use the reflected kernel */
3856 case 3: /* Reflect kernel a close */
3857 this_kernel = rflt_kernel; /* use the reflected kernel */
3861 this_kernel = rflt_kernel; /* use the reflected kernel */
3875 /* A Correlation is a Convolution with a reflected kernel.
3877 ** kernel. It may seem stange to convert a Correlation into a
3881 ** kernel when it is not required (which is typically the
3884 this_kernel = rflt_kernel; /* use the reflected kernel */
3906 /* Loop 4: Iterate the kernel with primitive */
3911 kernel_loop++; /* the iteration of this kernel */
3950 } /* End Loop 4: Iterate the kernel with primitive */
3999 /* multi-kernel handling: re-iterate, or compose results */
4000 if ( kernel->next == (KernelInfo *) NULL )
4036 /* loop to the next kernel in a multi-kernel list */
4041 } /* End Loop 2: Loop over each kernel */
4080 % MorphologyImage() applies a user supplied kernel to the image according to
4088 % * Kernel Scale/normalize settings ("-define convolve:scale=??")
4089 % This can also includes the addition of a scaled unity kernel.
4090 % * Show Kernel being applied ("-define morphology:showkernel=1")
4099 % const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
4112 % o kernel: An array of double representing the morphology kernel.
4113 % Warning: kernel may be normalized for the Convolve method.
4120 const KernelInfo *kernel,ExceptionInfo *exception)
4137 curr_kernel = (KernelInfo *) kernel;
4157 /* Scale kernel according to user wishes */
4165 if ( curr_kernel == kernel )
4166 curr_kernel = CloneKernelInfo(kernel);
4174 /* display the (normalized) kernel via stderr */
4179 /* Override the default handling of multi-kernel morphology results
4206 if ( curr_kernel != kernel )
4223 % RotateKernelInfo() rotates the kernel by the angle given.
4231 % void RotateKernelInfo(KernelInfo *kernel, double angle)
4235 % o kernel: the Morphology/Convolution kernel
4242 static void RotateKernelInfo(KernelInfo *kernel, double angle)
4245 if ( kernel->next != (KernelInfo *) NULL)
4246 RotateKernelInfo(kernel->next, angle);
4248 /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
4262 switch (kernel->type) {
4298 if ( kernel->width == 3 && kernel->height == 3 )
4300 double t = kernel->values[0];
4301 kernel->values[0] = kernel->values[3];
4302 kernel->values[3] = kernel->values[6];
4303 kernel->values[6] = kernel->values[7];
4304 kernel->values[7] = kernel->values[8];
4305 kernel->values[8] = kernel->values[5];
4306 kernel->values[5] = kernel->values[2];
4307 kernel->values[2] = kernel->values[1];
4308 kernel->values[1] = t;
4310 if ( kernel->x != 1 || kernel->y != 1 ) {
4312 x = (ssize_t) kernel->x-1;
4313 y = (ssize_t) kernel->y-1;
4318 kernel->x = (ssize_t) x+1;
4319 kernel->y = (ssize_t) y+1;
4322 kernel->angle = fmod(kernel->angle+45.0, 360.0);
4325 perror("Unable to rotate non-3x3 kernel by 45 degrees");
4329 if ( kernel->width == 1 || kernel->height == 1 )
4330 { /* Do a transpose of a 1 dimensional kernel,
4335 t = (ssize_t) kernel->width;
4336 kernel->width = kernel->height;
4337 kernel->height = (size_t) t;
4338 t = kernel->x;
4339 kernel->x = kernel->y;
4340 kernel->y = t;
4341 if ( kernel->width == 1 ) {
4343 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4346 kernel->angle = fmod(kernel->angle+270.0, 360.0);
4349 else if ( kernel->width == kernel->height )
4357 k=kernel->values;
4358 for( i=0, x=(ssize_t) kernel->width-1; i<=x; i++, x--)
4359 for( j=0, y=(ssize_t) kernel->height-1; j<y; j++, y--)
4360 { t = k[i+j*kernel->width];
4361 k[i+j*kernel->width] = k[j+x*kernel->width];
4362 k[j+x*kernel->width] = k[x+y*kernel->width];
4363 k[x+y*kernel->width] = k[y+i*kernel->width];
4364 k[y+i*kernel->width] = t;
4369 x = (ssize_t) (kernel->x*2-kernel->width+1);
4370 y = (ssize_t) (kernel->y*2-kernel->height+1);
4371 kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4372 kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
4375 kernel->angle = fmod(kernel->angle+90.0, 360.0);
4378 perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
4384 * Basically all that is needed is a reversal of the kernel data!
4397 k=kernel->values;
4398 j=(ssize_t) (kernel->width*kernel->height-1);
4402 kernel->x = (ssize_t) kernel->width - kernel->x - 1;
4403 kernel->y = (ssize_t) kernel->height - kernel->y - 1;
4405 kernel->angle = fmod(kernel->angle+180.0, 360.0);
4409 * performed here, posibily with a linear kernel restriction.
4429 % and modifies the kernel according to the parsed arguments of that setting.
4432 % ScaleKernelInfo() to scale/normalize the kernel. The second argument
4433 % is then passed to UnityAddKernelInfo() to add a scled unity kernel
4434 % into the scaled/normalized kernel.
4438 % void ScaleGeometryKernelInfo(KernelInfo *kernel,
4443 % o kernel: the Morphology/Convolution kernel to modify
4450 MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
4476 /* Scale/Normalize the input kernel */
4477 ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
4479 /* Add Unity Kernel, for blending with original */
4481 UnityAddKernelInfo(kernel, args.sigma);
4496 % ScaleKernelInfo() scales the given kernel list by the given amount, with or
4497 % without normalization of the sum of the kernel values (as per given flags).
4499 % By default (no flags given) the values within the kernel is scaled
4502 % If either of the two 'normalize_flags' are given the kernel will first be
4505 % Kernel normalization ('normalize_flags' given) is designed to ensure that
4506 % any use of the kernel scaling factor with 'Convolve' or 'Correlate'
4512 % 'Gaussian' kernel) will be scaled so that those values sum to +1.0,
4516 % the kernel will be scaled by the absolute of the sum of kernel values, so
4519 % For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
4526 % values separately to those of the negative values, so the kernel will be
4527 % forced to become a zero-sum kernel better suited to such searches.
4529 % WARNING: Correct normalization of the kernel assumes that the '*_range'
4530 % attributes within the kernel structure have been correctly set during the
4539 % void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
4544 % o kernel: the Morphology/Convolution kernel
4548 % zero. If the kernel is normalized regardless of any flags.
4556 MagickExport void ScaleKernelInfo(KernelInfo *kernel,
4566 /* do the other kernels in a multi-kernel list first */
4567 if ( kernel->next != (KernelInfo *) NULL)
4568 ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
4570 /* Normalization of Kernel */
4573 if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4574 /* non-zero-summing kernel (generally positive) */
4575 pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4577 /* zero-summing kernel */
4578 pos_scale = kernel->positive_range;
4580 /* Force kernel into a normalized zero-summing kernel */
4582 pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4583 ? kernel->positive_range : 1.0;
4584 neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4585 ? -kernel->negative_range : 1.0;
4594 for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4595 if (!IsNaN(kernel->values[i]))
4596 kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4599 kernel->positive_range *= pos_scale;
4600 kernel->negative_range *= neg_scale;
4601 /* maximum and minimum values in kernel */
4602 kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4603 kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4605 /* swap kernel settings if user's scaling factor is negative */
4608 t = kernel->positive_range;
4609 kernel->positive_range = kernel->negative_range;
4610 kernel->negative_range = t;
4611 t = kernel->maximum;
4612 kernel->maximum = kernel->minimum;
4613 kernel->minimum = 1;
4631 % ShowKernelInfo() outputs the details of the given kernel defination to
4637 % void ShowKernelInfo(const KernelInfo *kernel)
4641 % o kernel: the Morphology/Convolution kernel
4644 MagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
4652 for (c=0, k=kernel; k != (KernelInfo *) NULL; c++, k=k->next ) {
4654 (void) FormatLocaleFile(stderr, "Kernel");
4655 if ( kernel->next != (KernelInfo *) NULL )
4702 % UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
4703 % to the given pre-scaled and normalized Kernel. This in effect adds that
4704 % amount of the original image into the resulting convolution kernel. This
4713 % void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
4717 % o kernel: the Morphology/Convolution kernel
4720 % scaling factor for the unity kernel to be added to
4721 % the given kernel.
4724 MagickExport void UnityAddKernelInfo(KernelInfo *kernel,
4727 /* do the other kernels in a multi-kernel list first */
4728 if ( kernel->next != (KernelInfo *) NULL)
4729 UnityAddKernelInfo(kernel->next, scale);
4731 /* Add the scaled unity kernel to the existing kernel */
4732 kernel->values[kernel->x+kernel->y*kernel->width] += scale;
4733 CalcKernelMetaData(kernel); /* recalculate the meta-data */
4751 % the kernel with a zero value. This is typically done when the kernel will
4757 % void ZeroKernelNans (KernelInfo *kernel)
4761 % o kernel: the Morphology/Convolution kernel
4764 MagickPrivate void ZeroKernelNans(KernelInfo *kernel)
4769 /* do the other kernels in a multi-kernel list first */
4770 if (kernel->next != (KernelInfo *) NULL)
4771 ZeroKernelNans(kernel->next);
4773 for (i=0; i < (kernel->width*kernel->height); i++)
4774 if (IsNaN(kernel->values[i]))
4775 kernel->values[i]=0.0;