I am working on an inspection process that requires me to precisely locate features in the field of view.
Specifically I am finding features on a circuit board which could include finding tooling holes and solder pads. Our vision setup gives us a per-pixel size of ~100 microns.
At that resolution there is a great deal of material clutter as solder pads leak beyond their intended circumference, and tooling holes have microscopic burrs which can distort.
The question is about a tooling hole where a couple of burrs are causing my centroid calculation to result in a center point that does not correspond with the center of mass of the object. I have attached the raw image and a processed image that shows the detected contour, the calculated centroid and the circle based on the area of the contour centered at the centroid.
Looking at the output I would expect the circle to be shifted much further to the left as the omitted pieces of the contour look much larger than the extra pieces to right.
Raw Capture
Processed Image
static void Process( string filename ) {
Mat raw;
raw = Cv2.ImRead( filename, ImreadModes.GrayScale );
Mat threshold = new Mat();
Cv2.Threshold( raw, threshold, 190, 255, ThresholdTypes.Otsu );
Point[][] contours;
HierarchyIndex[] hierarchyIndexes;
Cv2.FindContours( threshold, out contours, out hierarchyIndexes, RetrievalModes.List, ContourApproximationModes.ApproxSimple );
Mat rawColor = new Mat();
Cv2.CvtColor( raw, rawColor, ColorConversionCodes.GRAY2RGB );
if ( contours.Length != 0 ) {
var contourIndex = 0;
while ( contourIndex >= 0 ) {
var area = Cv2.ContourArea( contours[ contourIndex ] );
Debug.Print( $"{contourIndex} points: {contours[ contourIndex ].Length}, area: {area}" );
//only look for features that have a given area
if ( area < 1100000 && area > 400000.0 ) {
//if ( area < 1100000 && area > 60000.0 ) {
Cv2.DrawContours( rawColor, contours, contourIndex, new Scalar( 255, 0, 0 ), 10, LineTypes.Link8, hierarchyIndexes, int.MaxValue );
Point centroid = Centroid( contours[ contourIndex ] );
Cv2.Circle( rawColor, centroid, 20, new Scalar( 0, 0, 255 ), 3 );
var radius = Math.Sqrt( area / Math.PI );
Cv2.Circle( rawColor, centroid, (int)radius, new Scalar( 0, 0, 255 ), thickness: 3 );
}
contourIndex = hierarchyIndexes[ contourIndex ].Next;
}
}
}
static Point Centroid ( Point[] knots ) {
Point center = new Point();
int sumofx = 0, sumofy = 0;
for ( int i = 0; i < knots.Length; i++ ) {
sumofx = sumofx + knots[ i ].X;
sumofy = sumofy + knots[ i ].Y;
}
center.X = sumofx / knots.Length;
center.Y = sumofy / knots.Length;
return center;
}