+ if (args->rho < args->sigma)
+ {
+ kernel->width = ((size_t)args->sigma)*2+1;
+ limit1 = (ssize_t)(args->rho*args->rho);
+ limit2 = (ssize_t)(args->sigma*args->sigma);
+ }
+ else
+ {
+ kernel->width = ((size_t)args->rho)*2+1;
+ limit1 = (ssize_t)(args->sigma*args->sigma);
+ limit2 = (ssize_t)(args->rho*args->rho);
+ }
+ if ( limit2 <= 0 )
+ kernel->width = 7L, limit1 = 7L, limit2 = 11L;
+
+ kernel->height = kernel->width;
+ kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
+ kernel->values=(double *) AcquireQuantumMemory(kernel->width,
+ kernel->height*sizeof(double));
+ if (kernel->values == (double *) NULL)
+ return(DestroyKernelInfo(kernel));
+
+ /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
+ scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
+ for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
+ for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
+ { ssize_t radius=u*u+v*v;
+ if (limit1 < radius && radius <= limit2)
+ kernel->positive_range += kernel->values[i] = (double) scale;
+ else
+ kernel->values[i] = nan;
+ }
+ kernel->minimum = kernel->maximum = (double) scale;
+ if ( type == PeaksKernel ) {
+ /* set the central point in the middle */
+ kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
+ kernel->positive_range = 1.0;
+ kernel->maximum = 1.0;
+ }
+ break;
+ }
+ case EdgesKernel:
+ {
+ kernel=AcquireKernelInfo("ThinSE:482");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
+ break;
+ }
+ case CornersKernel:
+ {
+ kernel=AcquireKernelInfo("ThinSE:87");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
+ break;
+ }
+ case DiagonalsKernel:
+ {
+ switch ( (int) args->rho ) {
+ case 0:
+ default:
+ { KernelInfo
+ *new_kernel;
+ kernel=ParseKernelArray("3: 0,0,0 0,-,1 1,1,-");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ new_kernel=ParseKernelArray("3: 0,0,1 0,-,1 0,1,-");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ ExpandMirrorKernelInfo(kernel);
+ return(kernel);
+ }
+ case 1:
+ kernel=ParseKernelArray("3: 0,0,0 0,-,1 1,1,-");
+ break;
+ case 2:
+ kernel=ParseKernelArray("3: 0,0,1 0,-,1 0,1,-");
+ break;
+ }
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ RotateKernelInfo(kernel, args->sigma);
+ break;
+ }
+ case LineEndsKernel:
+ { /* Kernels for finding the end of thin lines */
+ switch ( (int) args->rho ) {
+ case 0:
+ default:
+ /* set of kernels to find all end of lines */
+ return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
+ case 1:
+ /* kernel for 4-connected line ends - no rotation */
+ kernel=ParseKernelArray("3: 0,0,- 0,1,1 0,0,-");
+ break;
+ case 2:
+ /* kernel to add for 8-connected lines - no rotation */
+ kernel=ParseKernelArray("3: 0,0,0 0,1,0 0,0,1");
+ break;
+ case 3:
+ /* kernel to add for orthogonal line ends - does not find corners */
+ kernel=ParseKernelArray("3: 0,0,0 0,1,1 0,0,0");
+ break;
+ case 4:
+ /* traditional line end - fails on last T end */
+ kernel=ParseKernelArray("3: 0,0,0 0,1,- 0,0,-");
+ break;
+ }
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ RotateKernelInfo(kernel, args->sigma);
+ break;
+ }
+ case LineJunctionsKernel:
+ { /* kernels for finding the junctions of multiple lines */
+ switch ( (int) args->rho ) {
+ case 0:
+ default:
+ /* set of kernels to find all line junctions */
+ return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
+ case 1:
+ /* Y Junction */
+ kernel=ParseKernelArray("3: 1,-,1 -,1,- -,1,-");
+ break;
+ case 2:
+ /* Diagonal T Junctions */
+ kernel=ParseKernelArray("3: 1,-,- -,1,- 1,-,1");
+ break;
+ case 3:
+ /* Orthogonal T Junctions */
+ kernel=ParseKernelArray("3: -,-,- 1,1,1 -,1,-");
+ break;
+ case 4:
+ /* Diagonal X Junctions */
+ kernel=ParseKernelArray("3: 1,-,1 -,1,- 1,-,1");
+ break;
+ case 5:
+ /* Orthogonal X Junctions - minimal diamond kernel */
+ kernel=ParseKernelArray("3: -,1,- 1,1,1 -,1,-");
+ break;
+ }
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ RotateKernelInfo(kernel, args->sigma);
+ break;
+ }
+ case RidgesKernel:
+ { /* Ridges - Ridge finding kernels */
+ KernelInfo
+ *new_kernel;
+ switch ( (int) args->rho ) {
+ case 1:
+ default:
+ kernel=ParseKernelArray("3x1:0,1,0");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
+ break;
+ case 2:
+ kernel=ParseKernelArray("4x1:0,1,1,0");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
+
+ /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
+ /* Unfortunatally we can not yet rotate a non-square kernel */
+ /* But then we can't flip a non-symetrical kernel either */
+ new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ LastKernelInfo(kernel)->next = new_kernel;
+ break;
+ }
+ break;
+ }
+ case ConvexHullKernel:
+ {
+ KernelInfo
+ *new_kernel;
+ /* first set of 8 kernels */
+ kernel=ParseKernelArray("3: 1,1,- 1,0,- 1,-,0");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ ExpandRotateKernelInfo(kernel, 90.0);
+ /* append the mirror versions too - no flip function yet */
+ new_kernel=ParseKernelArray("3: 1,1,1 1,0,- -,-,0");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ ExpandRotateKernelInfo(new_kernel, 90.0);
+ LastKernelInfo(kernel)->next = new_kernel;
+ break;
+ }
+ case SkeletonKernel:
+ {
+ switch ( (int) args->rho ) {
+ case 1:
+ default:
+ /* Traditional Skeleton...
+ ** A cyclically rotated single kernel
+ */
+ kernel=AcquireKernelInfo("ThinSE:482");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
+ break;
+ case 2:
+ /* HIPR Variation of the cyclic skeleton
+ ** Corners of the traditional method made more forgiving,
+ ** but the retain the same cyclic order.
+ */
+ kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ if (kernel->next == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ kernel->type = type;
+ kernel->next->type = type;
+ ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
+ break;
+ case 3:
+ /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
+ ** "Connectivity-Preserving Morphological Image Thransformations"
+ ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
+ ** http://www.leptonica.com/papers/conn.pdf
+ */
+ kernel=AcquireKernelInfo(
+ "ThinSE:41; ThinSE:42; ThinSE:43");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ kernel->next->type = type;
+ kernel->next->next->type = type;
+ ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
+ break;
+ }
+ break;
+ }
+ case ThinSEKernel:
+ { /* Special kernels for general thinning, while preserving connections
+ ** "Connectivity-Preserving Morphological Image Thransformations"
+ ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
+ ** http://www.leptonica.com/papers/conn.pdf
+ ** And
+ ** http://tpgit.github.com/Leptonica/ccthin_8c_source.html
+ **
+ ** Note kernels do not specify the origin pixel, allowing them
+ ** to be used for both thickening and thinning operations.
+ */
+ switch ( (int) args->rho ) {
+ /* SE for 4-connected thinning */
+ case 41: /* SE_4_1 */
+ kernel=ParseKernelArray("3: -,-,1 0,-,1 -,-,1");
+ break;
+ case 42: /* SE_4_2 */
+ kernel=ParseKernelArray("3: -,-,1 0,-,1 -,0,-");
+ break;
+ case 43: /* SE_4_3 */
+ kernel=ParseKernelArray("3: -,0,- 0,-,1 -,-,1");
+ break;
+ case 44: /* SE_4_4 */
+ kernel=ParseKernelArray("3: -,0,- 0,-,1 -,0,-");
+ break;
+ case 45: /* SE_4_5 */
+ kernel=ParseKernelArray("3: -,0,1 0,-,1 -,0,-");
+ break;
+ case 46: /* SE_4_6 */
+ kernel=ParseKernelArray("3: -,0,- 0,-,1 -,0,1");
+ break;
+ case 47: /* SE_4_7 */
+ kernel=ParseKernelArray("3: -,1,1 0,-,1 -,0,-");
+ break;
+ case 48: /* SE_4_8 */
+ kernel=ParseKernelArray("3: -,-,1 0,-,1 0,-,1");
+ break;
+ case 49: /* SE_4_9 */
+ kernel=ParseKernelArray("3: 0,-,1 0,-,1 -,-,1");
+ break;
+ /* SE for 8-connected thinning - negatives of the above */
+ case 81: /* SE_8_0 */
+ kernel=ParseKernelArray("3: -,1,- 0,-,1 -,1,-");
+ break;
+ case 82: /* SE_8_2 */
+ kernel=ParseKernelArray("3: -,1,- 0,-,1 0,-,-");
+ break;
+ case 83: /* SE_8_3 */
+ kernel=ParseKernelArray("3: 0,-,- 0,-,1 -,1,-");
+ break;
+ case 84: /* SE_8_4 */
+ kernel=ParseKernelArray("3: 0,-,- 0,-,1 0,-,-");
+ break;
+ case 85: /* SE_8_5 */
+ kernel=ParseKernelArray("3: 0,-,1 0,-,1 0,-,-");
+ break;
+ case 86: /* SE_8_6 */
+ kernel=ParseKernelArray("3: 0,-,- 0,-,1 0,-,1");
+ break;
+ case 87: /* SE_8_7 */
+ kernel=ParseKernelArray("3: -,1,- 0,-,1 0,0,-");
+ break;
+ case 88: /* SE_8_8 */
+ kernel=ParseKernelArray("3: -,1,- 0,-,1 0,1,-");
+ break;
+ case 89: /* SE_8_9 */
+ kernel=ParseKernelArray("3: 0,1,- 0,-,1 -,1,-");
+ break;
+ /* Special combined SE kernels */
+ case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
+ kernel=ParseKernelArray("3: -,-,1 0,-,- -,0,-");
+ break;
+ case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
+ kernel=ParseKernelArray("3: -,1,- -,-,1 0,-,-");
+ break;
+ case 481: /* SE_48_1 - General Connected Corner Kernel */
+ kernel=ParseKernelArray("3: -,1,1 0,-,1 0,0,-");
+ break;
+ default:
+ case 482: /* SE_48_2 - General Edge Kernel */
+ kernel=ParseKernelArray("3: 0,-,1 0,-,1 0,-,1");
+ break;
+ }
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ RotateKernelInfo(kernel, args->sigma);
+ break;
+ }
+ /*
+ Distance Measuring Kernels
+ */
+ case ChebyshevKernel:
+ {
+ if (args->rho < 1.0)
+ kernel->width = kernel->height = 3; /* default radius = 1 */
+ else
+ kernel->width = kernel->height = ((size_t)args->rho)*2+1;
+ kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
+
+ kernel->values=(double *) AcquireQuantumMemory(kernel->width,
+ kernel->height*sizeof(double));
+ if (kernel->values == (double *) NULL)
+ return(DestroyKernelInfo(kernel));
+
+ for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
+ for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
+ kernel->positive_range += ( kernel->values[i] =
+ args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
+ kernel->maximum = kernel->values[0];
+ break;
+ }
+ case ManhattanKernel:
+ {
+ if (args->rho < 1.0)
+ kernel->width = kernel->height = 3; /* default radius = 1 */
+ else
+ kernel->width = kernel->height = ((size_t)args->rho)*2+1;
+ kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
+
+ kernel->values=(double *) AcquireQuantumMemory(kernel->width,
+ kernel->height*sizeof(double));
+ if (kernel->values == (double *) NULL)
+ return(DestroyKernelInfo(kernel));
+
+ for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
+ for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
+ kernel->positive_range += ( kernel->values[i] =
+ args->sigma*(labs((long) u)+labs((long) v)) );
+ kernel->maximum = kernel->values[0];
+ break;
+ }
+ case OctagonalKernel: