My work is based on images with an array of points (Fig. 1), and the final result is shown in Fig. 4. I will explain my work step by step.
Fig. 1 Original Image

Step 1: Detect the edge of each object, including the points and the βringβ that I want to delete, for better performance. And the result of edge detection is shown in Fig. 2. I used a Canny sneaker detector, but it didnβt work well with some light gray dots. My first question: how to close the contours of points and reduce as much noise as possible?
Fig. 2 Edge Detection

Step 2: Dilute each object. I did not find a good way to fill the holes, so I expand them straight. As shown in FIG. 3, the openings appear to be oversized, as well as other noises. My second question is how to fill or expand the holes so that they are filled with circles in the same / similar size?
Fig. 3 Dilation

Step 3: Find and draw the center of mass of each point. As shown in Figure 4, due to the processing of the coarse image, there is a βringβ mark, and some points are shown by two white pixels. The required result should show only dots and one white pixel for one point.
Fig. 4: Mass centers

Here is my code for these three steps. Can anyone help improve my work?
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <stdlib.h> #include <stdio.h> #include <cv.h> #include <highgui.h> using namespace std; using namespace cv; // Global variables Mat src, edge, dilation; int dilation_size = 2; // Function header void thresh_callback(int, void*); int main(int argc, char* argv) { IplImage* img = cvLoadImage("c:\\dot1.bmp", 0); // dot1.bmp = Fig. 1 // Perform canny edge detection cvCanny(img, img, 33, 100, 3); // IplImage to Mat Mat imgMat(img); src = img; namedWindow("Step 1: Edge", CV_WINDOW_AUTOSIZE); imshow("Step 1: Edge", src); // Apply the dilation operation Mat element = getStructuringElement(2, Size(2 * dilation_size + 1, 2 * dilation_size + 1), Point(dilation_size, dilation_size)); // dilation_type = MORPH_ELLIPSE dilate(src, dilation, element); // imwrite("c:\\dot1_dilate.bmp", dilation); namedWindow("Step 2: Dilation", CV_WINDOW_AUTOSIZE); imshow("Step 2: Dilation", dilation); thresh_callback( 0, 0 ); waitKey(0); return 0; } /* function thresh_callback */ void thresh_callback(int, void*) { vector<vector<Point>> contours; vector<Vec4i> hierarchy; // Find contours findContours(dilation, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); // Get the moments vector<Moments> mu(contours.size()); for(int i = 0; i < contours.size(); i++) { mu[i] = moments(contours[i], false); } // Get the mass centers vector<Point2f> mc(contours.size()); for(int i = 0; i < contours.size(); i++) { mc[i] = Point2f(mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00); } // Draw mass centers Mat drawing = Mat::zeros(dilation.size(), CV_8UC1); for( int i = 0; i< contours.size(); i++ ) { Scalar color = Scalar(255, 255, 255); line(drawing, mc[i], mc[i], color, 1, 8, 0); } namedWindow("Step 3: Mass Centers", CV_WINDOW_AUTOSIZE); imshow("Step 3: Mass Centers", drawing); }