Opencv: Edge detection, Dilation and Mass ceter drawing

c++, image-processing, opencv

Solution

There are a few things you can do to improve your results. To reduce noise in the image, you can apply a median blur before applying the Canny operator. This is a common de-noising 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 significantly reduces the amount of noise in your edge image:

To better retain the original sizes of your dots, you can perform a few iterations of morphological closing with a smaller kernel rather than dilation. This will also reduce joining of the dots with the circle:

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

This will perform two iterations with a 3x3 kernel, indicated by using `cv::noArray()`.

The result is cleaner, and the dots are completely filled:

Leaving the rest of your pipeline unmodified gives the final result. There are still a few spurious mass centers from the circle, but considerably fewer than the original method:

If you wanted to attempt removing the circle from the results entirely, you could try using `cv::HoughCircles()` and adjusting the parameters until you get a good result. This might have some difficulties because the entire circle is not visible in the image, only segments, but I recommend you experiment with it. If you did detect the innermost circle, you could use it as a mask to filter out external mass centers.

Problem

My work is based on images with an array of dots (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 every object, including the dots and a "ring" that I want to delete for better performance. And the result of edge detection is shown in Fig.2. I used Canny edge detector but it didn't work well with some light-gray dots. My first question is how to close the contours of dots and reduce other noise as much as possible? Fig. 2 Edge detection Step 2: Dilate every object. I didn't find a good way to fill holes, so I dilate them directly. As shown in Fig.3, holes seem to be enlarged too much and so does other noise. My second question is how to fill or dilate the holes in order to make them be filled circles in the same/similar size? Fig. 3 Dilation Step 3: Find and draw the mass center of every dot. As shown in Fig. 4, due to the coarse image processing, there exist mark of the "ring" and some of dots are shown in two white pixels. The result wanted should only show the dots and one white pixel for one dot. Fig. 4: Mass centers Here is my code for these 3 steps. Can anyone help to make my work better? ``` #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); } ```

Original source

Related problems