% Find any peak larger than the pixels the fall between the two radii.
% The default ring of pixels is as per "Ring".
% Edges
-% Find edges of a binary shape
+% Find flat orthogonal edges of a binary shape
% Corners
-% Find corners of a binary shape
-% Ridges:type
-% Find single pixel ridges or thin lines
-% LineEnds
+% Find 90 degree corners of a binary shape
+% LineEnds:type
% Find end points of lines (for pruning a skeletion)
+% Two types of lines ends (default to both) can be searched for
+% Type 0: All line ends
+% Type 1: single kernel for 4-conneected line ends
+% Type 2: single kernel for simple line ends
% LineJunctions
% Find three line junctions (within a skeletion)
+% Type 0: all line junctions
+% Type 1: Y Junction kernel
+% Type 2: Diagonal T Junction kernel
+% Type 3: Orthogonal T Junction kernel
+% Type 4: Diagonal X Junction kernel
+% Type 5: Orthogonal + Junction kernel
+% Ridges:type
+% Find single pixel ridges or thin lines
+% Type 1: Fine single pixel thick lines and ridges
+% Type 2: Find two pixel thick lines and ridges
% ConvexHull
% Octagonal thicken kernel, to generate convex hulls of 45 degrees
% Skeleton:type
% Traditional skeleton generating kernels.
+% Type 1: Tradional Skeleton kernel (4 connected skeleton)
+% Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
+% Type 3: Experimental Variation to try to present left-right symmetry
+% Type 4: Experimental Variation to preserve left-right symmetry
%
% Distance Measuring Kernels
%
case CompassKernel:
case KirschKernel:
case FreiChenKernel:
- case CornersKernel: /* Hit and Miss kernels */
+ case EdgesKernel: /* Hit and Miss kernels */
+ case CornersKernel:
case LineEndsKernel:
case LineJunctionsKernel:
- case EdgesKernel:
case RidgesKernel:
case ConvexHullKernel:
case SkeletonKernel:
break;
}
case SobelKernel:
- { switch ( (int) args->rho ) {
+#if 0
+ { /* Sobel with optional 'sub-types' */
+ switch ( (int) args->rho ) {
default:
case 0:
kernel=ParseKernelArray("3: 1,0,-1 2,0,-2 1,0,-1");
ScaleKernelInfo(kernel, 0.25, NoValue);
break;
}
+ if ( fabs(args->sigma) > MagickEpsilon )
+ /* Rotate by correctly supplied 'angle' */
+ RotateKernelInfo(kernel, args->sigma);
+ else if ( args->rho > 30.0 || args->rho < -30.0 )
+ /* Rotate by out of bounds 'type' */
+ RotateKernelInfo(kernel, args->rho);
+ break;
+ }
+#else
+ { /* Simple Sobel Kernel */
+ kernel=ParseKernelArray("3: 1,0,-1 2,0,-2 1,0,-1");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ RotateKernelInfo(kernel, args->rho);
break;
}
+#endif
case RobertsKernel:
{
kernel=ParseKernelArray("3: 0,0,0 1,-1,0 0,0,0");
break;
}
case LineEndsKernel:
- {
- KernelInfo
- *new_kernel;
- kernel=ParseKernelArray("3: 0,0,0 0,1,0 -,1,-");
- if (kernel == (KernelInfo *) NULL)
- return(kernel);
- kernel->type = type;
- ExpandRotateKernelInfo(kernel, 90.0);
- /* append second set of 4 kernels */
- new_kernel=ParseKernelArray("3: 0,0,0 0,1,0 0,0,1");
- if (new_kernel == (KernelInfo *) NULL)
- return(DestroyKernelInfo(kernel));
- new_kernel->type = type;
- ExpandRotateKernelInfo(new_kernel, 90.0);
- LastKernelInfo(kernel)->next = new_kernel;
+ { /* Kernels for finding the end of thin lines */
+ switch ( (int) args->rho ) {
+ case 0:
+ default:
+ /* set of kernels to find all end of lines */
+ kernel=AcquireKernelInfo("LineEnds:1>;LineEnds:2>");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ break;
+ case 1:
+ /* kernel for 4-connected line ends - no rotation */
+ kernel=ParseKernelArray("3: 0,0,0 0,1,0 -,1,-");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ break;
+ case 2:
+ /* kernel to add for 8-connected lines - no rotation */
+ kernel=ParseKernelArray("3: 0,0,0 0,1,0 0,0,1");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ break;
+ }
break;
}
case LineJunctionsKernel:
- {
- KernelInfo
- *new_kernel;
- /* first set of 4 kernels */
- kernel=ParseKernelArray("3: -,1,- -,1,- 1,-,1");
- if (kernel == (KernelInfo *) NULL)
- return(kernel);
- kernel->type = type;
- ExpandRotateKernelInfo(kernel, 45.0);
- /* append second set of 4 kernels */
- new_kernel=ParseKernelArray("3: 1,-,- -,1,- 1,-,1");
- if (new_kernel == (KernelInfo *) NULL)
- return(DestroyKernelInfo(kernel));
- new_kernel->type = type;
- ExpandRotateKernelInfo(new_kernel, 90.0);
- LastKernelInfo(kernel)->next = new_kernel;
+ { /* kernels for finding the junctions of multiple lines */
+ switch ( (int) args->rho ) {
+ case 0:
+ default:
+ /* set of kernels to find all line junctions */
+ kernel=AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ break;
+ case 1:
+ /* Y Junction */
+ kernel=ParseKernelArray("3: 1,-,1 -,1,- -,1,-");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ break;
+ case 2:
+ /* Diagonal T Junctions */
+ kernel=ParseKernelArray("3: 1,-,- -,1,- 1,-,1");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ break;
+ case 3:
+ /* Orthogonal T Junctions */
+ kernel=ParseKernelArray("3: -,-,- 1,1,1 -,1,-");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ break;
+ case 4:
+ /* Diagonal X Junctions */
+ kernel=ParseKernelArray("3: 1,-,1 -,1,- 1,-,1");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ break;
+ case 5:
+ /* Orthogonal X Junctions - minimal diamond kernel */
+ kernel=ParseKernelArray("3: -,1,- 1,1,1 -,1,-");
+ if (kernel == (KernelInfo *) NULL)
+ return(kernel);
+ kernel->type = type;
+ break;
+ }
break;
}
case RidgesKernel:
return(kernel);
kernel->type = type;
ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
-#if 0
- /* 2 pixel diagonaly thick - 4 rotates - not needed? */
- new_kernel=ParseKernelArray("4x4>:0,-,-,- -,1,-,- -,-,1,- -,-,-,0'");
- if (new_kernel == (KernelInfo *) NULL)
- return(DestroyKernelInfo(kernel));
- new_kernel->type = type;
- ExpandRotateKernelInfo(new_kernel, 90.0); /* 4 rotated kernels */
- LastKernelInfo(kernel)->next = new_kernel;
-#endif
- /* kernels to find a stepped 'thick' line, 4 rotates + mirrors */
+
+ /* 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 (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = type;
- ExpandRotateKernelInfo(kernel, 45.0);
- /* append the mirror versions too */
+ 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, 45.0);
+ ExpandRotateKernelInfo(new_kernel, 90.0);
LastKernelInfo(kernel)->next = new_kernel;
break;
}
new_kernel->type = type;
LastKernelInfo(kernel)->next = new_kernel;
ExpandMirrorKernelInfo(kernel);
+ /* Append a set of corner kernels */
+ new_kernel=ParseKernelArray("3: 0,0,- 0,1,1 -,1,-");
+ if (new_kernel == (KernelInfo *) NULL)
+ return(DestroyKernelInfo(kernel));
+ new_kernel->type = type;
+ ExpandRotateKernelInfo(new_kernel, 90.0);
+ LastKernelInfo(kernel)->next = new_kernel;
break;
}
break;
case UnityKernel:
default:
{
- /* Unity or No-Op Kernel - 3x3 with 1 in center */
- kernel=ParseKernelArray("3:0,0,0,0,1,0,0,0,0");
+ /* Unity or No-Op Kernel - Basically just a single pixel on its own */
+ kernel=ParseKernelArray("1:1");
if (kernel == (KernelInfo *) NULL)
return(kernel);
kernel->type = ( type == UnityKernel ) ? UnityKernel : UndefinedKernel;
case HitAndMissMorphology:
case ThinningMorphology:
case ThickenMorphology:
- /* kernel is user as is, without reflection */
+ /* kernel is used as is, without reflection */
break;
default:
assert("Not a Primitive Morphology Method" != (char *) NULL);
stage_limit = 2;
break;
case HitAndMissMorphology:
- kernel_limit = 1; /* no method or kernel iteration */
rslt_compose = LightenCompositeOp; /* Union of multi-kernel results */
- break;
+ /* FALL THUR */
case ThinningMorphology:
case ThickenMorphology:
- method_limit = kernel_limit; /* iterate method with each kernel */
+ method_limit = kernel_limit; /* iterate the whole method */
kernel_limit = 1; /* do not do kernel iteration */
- case DistanceMorphology:
- rslt_compose = NoCompositeOp; /* Re-iterate with multiple kernels */
break;
default:
break;
** below ensures the methematical compose method is applied in a
** purely mathematical way, and only to the selected channels.
** Turn off SVG composition 'alpha blending'.
+ **
+ ** The compose image order is specifically so that the new image can
+ ** be subtarcted 'Minus' from the collected result, to allow you to
+ ** convert a HitAndMiss methd into a Thinning method.
*/
if ( verbose == MagickTrue )
fprintf(stderr, " (compose \"%s\")",
MagickOptionToMnemonic(MagickComposeOptions, rslt_compose) );
- (void) CompositeImageChannel(rslt_image,
+ (void) CompositeImageChannel(curr_image,
(ChannelType) (channel & ~SyncChannels), rslt_compose,
- curr_image, 0, 0);
+ rslt_image, 0, 0);
+ rslt_image = DestroyImage(rslt_image);
+ rslt_image = curr_image;
curr_image = (Image *) image; /* continue with original image */
}
if ( verbose == MagickTrue )
if ( artifact != (const char *) NULL)
ShowKernelInfo(curr_kernel);
- /* override the default handling of multi-kernel morphology results
- * if 'Undefined' use the default method
- * if 'None' (default for 'Convolve') re-iterate previous result
- * otherwise merge resulting images using compose method given
+ /* Override the default handling of multi-kernel morphology results
+ * If 'Undefined' use the default method
+ * If 'None' (default for 'Convolve') re-iterate previous result
+ * Otherwise merge resulting images using compose method given.
+ * Default for 'HitAndMiss' is 'Lighten'.
*/
compose = UndefinedCompositeOp; /* use default for method */
artifact = GetImageArtifact(image,"morphology:compose");