jeff
jeff

Reputation: 13653

How to segment objects after OpenCV connectedComponents function

I have obtained a labeling with the connectedComponents function of C++ OpenCV, which looks like in the picture :

enter image description here

This is the output of the ccLabels variable, which is a cv::Mat of the same size with the original image.

So what I need to do is :

  1. Count the occurences of each number, and select the ones that occur more than N times, which are the "big" ones.
  2. Segment the areas of the "big" components, and then count the number of 4's and 0's inside that area.

My ultimate aim is to count the number of holes in the image, so I aim to infer number of holes from (number of 0's / number of 4's). This is probably not the prettiest way but the images are very uniform in terms of size and illumination, so it will meet my needs.

But I'm new to OpenCV and I don't have much idea how to accomplish this task.

Here is what I've done so far:

cv::Mat1b outImg;
cv::threshold(grayImg, outImg, 150, 255, 0); // Thresholded -binary- image
cv::Mat ccLabels;
cv::connectedComponents(outImg, ccLabels); // Each non-zero pixel is labeled with their connectedComponent ID's
// write the labels to file:
std::ofstream myfile;
    myfile.open("ccLabels.txt");
    cv::Size s = ccLabels.size();
    myfile << "Size: " << s.height << " , " << s.width <<"\n";
    for (int r1 = 0; r1 < s.height; r1++) {
        for (int c1 = 0; c1 < s.height; c1++) {
            myfile << ccLabels.at<int>(r1,c1);
        }
        myfile << "\n";
    }
    myfile.close();

Since I know how to iterate inside the matrix, counting the numbers should be OK, but first I have to separate(eliminate / ignore) the "background" pixels, which are the 0's outside the connected components. Then counting should be easy.

How can I segment these "big" components? Maybe obtaining a mask, and only consider pixels where mask(x,y) = 1?

Thanks for any help !

Edit

This is the thresholded image:

enter image description here

And this is what I get after Canny edge detection :

enter image description here

This is the actual image (thresholded) : enter image description here

Upvotes: 2

Views: 4526

Answers (3)

Miki
Miki

Reputation: 41765

Here a simple procedure to find the number on the dices, starting from your thresholded image

  1. find external contours
  2. for each contour
    • eventually discard small blobs
    • draw the filled mask
    • use AND and XOR to isolate internal holes
    • find contours, again
    • count contours

Result:

Number: 5
Number: 2

Image:

Code:

#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;

int main(void)
{
    // Grayscale image
    Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);

    // Minimum area of the contour
    double minContourArea = 10;

    // Prepare outpot
    Mat3b result;
    cvtColor(img, result, COLOR_GRAY2BGR);

    // Find contours
    vector<vector<Point>> contours;
    findContours(img.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

    for (int i = 0; i < contours.size(); ++i)
    {
        // Check area
        if (contourArea(contours[i]) < minContourArea) continue;

        // Black mask
        Mat1b mask(img.rows, img.cols, uchar(0));
        // Draw filled contour
        drawContours(mask, contours, i, Scalar(255), CV_FILLED);

        mask = (mask & img) ^ mask;

        vector<vector<Point>> cntrs;
        findContours(mask, cntrs, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

        cout << "Number: " << cntrs.size() << endl;

        // Just for showing results
        drawContours(result, cntrs, -1, Scalar(0,0,255), CV_FILLED);
    }

    imshow("Result", result);
    waitKey();

    return 0;
}

Upvotes: 3

To solve your 1st problem consider you have a set of values in values.Count the occurences of each number that as appeared.

 int m=0;
    for(int n=0;n<256;n++)
    {
        int c=0;
        for(int q=0;q<values.size();q++)
        {
            if(n==values[q])
            {
                //int c;
            c++;
            m++;
            }
        }

        cout<<n<<"= "<< c<<endl;
    }
    cout<<"Total number of elements "<< m<<endl;

To solve your second problem find the largest contour in the image using findcontours, draw bounding rectangle around it and then crop it. Again use the above code to count the pixel value "4" and "0". You can find the link of it here https://stackoverflow.com/a/32998275/3853072

Upvotes: -1

dnit13
dnit13

Reputation: 2496

The easier way is findContours method. You find the inner contours and calculate their area( since the inner contours will be holes) and process this information accordingly.

Upvotes: -1

Related Questions