Opencv: edge detection, stretching and mass drawing

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

enter image description here

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

enter image description here

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

enter image description here

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

enter image description here

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); } 
+7
c ++ image-processing opencv
source share
2 answers

There are several things you can do to improve your results. To reduce image noise, you can apply median blur before applying the Canny operator. This is a common noise reduction technique. Also try to avoid using the C API and IplImage .

  cv::Mat img = cv::imread("c:\\dot1.bmp", 0); // dot1.bmp = Fig. 1 cv::medianBlur(img, img, 7); // Perform canny edge detection cv::Canny(img, img, 33, 100); 

This greatly reduces the amount of noise in your edge image: Canny result

To better preserve the original sizes of your points, you can perform several iterations of morphological closure with a smaller core, rather than with an extension. This will also reduce the union of points with the circle:

 // This replaces the call to dilate() cv::morphologyEx(src, dilation, MORPH_CLOSE, cv::noArray(),cv::Point(-1,-1),2); 

This will do two iterations with the 3x3 kernel denoted by cv::noArray() .

The result is clean and the points are completely filled:

Closing result

Leaving the rest of your pipeline unmodified gives the final result. There are several more stray mass centers from the circle, but much smaller than the original method:

Mass centers

If you want to completely remove the circle from the results, you can try cv::HoughCircles() and adjust the parameters until you get a good result. This may have some difficulties, because the whole circle is not displayed on the image, but only segments, but I recommend that you experiment with it. If you find the innermost circle, you can use it as a mask to filter the outer centers of mass.

+9
source share

How to close the contours of points? use the drawContours method with a filled drawing parameter (CV_FILLED or thickness = -1)

reduce noise? use one of the blurring (low pass filtering) methods.

similar size? use erosion after expansion = morphological closure .

one point for one circle, exit without an outer ring? find the average value of all contour areas . erase contours that have a big difference with this value. print the remaining centers.

Aurelius has already mentioned most of them, but since this problem is interesting, I will probably try to post a complete solution when I have enough time. Good luck.

+5
source share

All Articles