Morphological Operations On Image

3.1k views Asked by At

I am currently doing a project in which I am trying to identify humans based on hands vascular pattern in C# using Emgu CV. The gray-scale image of the hand was first processed using the Adaptive Threshold function. Now I want to create a mask of the image using the morphological operations. The purpose is to remove the noise from the image. This is the adaptive-thresholded image:

Kindly guide me which function should I use and how to use.

1

There are 1 answers

9
Miki On BEST ANSWER

The code here is in C++. It shouldn't be difficult to port to C#, since it's mostly OpenCV functions calls. You can use that as guideline. Sorry about that.


You can apply a open operation with a small kernel to remove most of the noise:

Mat1b opened;
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
morphologyEx(thresholded, opened, MORPH_OPEN, kernel);

enter image description here

As you can see, some noise is still present, and you can't remove it with other morphological operations. You can simply consider the largest blob as the correct one (in green here):

enter image description here

Then you can floodfill the inside of the hand (in gray here):

enter image description here

And set to 0 all values in original image where the corresponding mask in not the same color of the inside of the image:

enter image description here

This is the full code (again, it's C++):

        #include <opencv2/opencv.hpp>
    using namespace cv;

    int main(int, char**)
    {
        // Load grayscale image
        Mat1b thresholded = imread("path_to_image", IMREAD_GRAYSCALE);

        // Get rid of JPEG compression artifacts
        thresholded = thresholded > 100;

        // Needed so findContours handles borders contours correctly
        Mat1b bin;
        copyMakeBorder(thresholded, bin, 1,1,1,1, BORDER_CONSTANT, 0);

        // Apply morphological operation "close"
        Mat1b closed;
        Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
        morphologyEx(bin, closed, MORPH_OPEN, kernel);

        // Find contours
        vector<vector<Point>> contours;
        findContours(bin.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(-1,-1)); // Point(-1,-1) accounts for previous copyMakeBorder

        // Keep largest contour
        int size_largest = 0;
        int idx_largest = -1;
        for (int i = 0; i < contours.size(); ++i)
        {
            if (contours[i].size() > size_largest)
            {
                size_largest = contours[i].size();
                idx_largest = i;
            }
        }

        Mat3b dbg;
        cvtColor(closed, dbg, COLOR_GRAY2BGR);


        // Black initialized mask
        Mat1b mask(thresholded.rows, thresholded.cols, uchar(0));

        if (idx_largest >= 0)
        {
            drawContours(dbg, contours, idx_largest, Scalar(0, 255, 0), CV_FILLED);

            // Draw filled polygin on mask
            drawContours(mask, contours, idx_largest, Scalar(255), 1);
        }

        // Get a point inside the contour
        Moments m = moments(contours[idx_largest]);
        Point2f inside(m.m10 / m.m00, m.m01 / m.m00);

        floodFill(mask, inside, Scalar(127));

        Mat3b result;
        cvtColor(thresholded, result, COLOR_GRAY2BGR);
        result.setTo(Scalar(0), mask != 127);


        imshow("Closed", closed);
        imshow("Contour", dbg);
        imshow("Result", result);
        waitKey();

        return 0;
    }