Solace
Solace

Reputation: 9020

Why is the drawContour() in OpenCV generating this strange Mask?

I started by reading in this Mat.

enter image description here

Then I converted it to Greyscale and applied Imgproc.canny() to it, getting the following mask.

enter image description here

Then I used Imgproc.findContours() to find the contours, Imgproc.drawContours(), and Core.putText() to label the contours with numbers:

enter image description here

Then I did Rect boundingRect = Imgproc.boundingRect(contours.get(0)); Mat submatrix = new Mat(); submatrix = originalMat.submat(boundingRect); to get following submatrix:

enter image description here

So far so good. The Problem starts hereafter:

NOW I NEEDED A MASK OF THE submatrix. So I decided to use Imgproc.drawContours() to get the mask:

Mat mask = new Mat(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1);
        List<MatOfPoint> contourList = new ArrayList<>();
        contourList.add(contours.get(0));
        Imgproc.drawContours(mask, contourList, 0, new Scalar(255), -1);

I got the following mask:

enter image description here

WHAT I WAS EXPECTING was a filled (in white color) diamond shape on black background.

WHy am I getting this unexpected result?


EDIT:

  1. When I replaced Mat mask = new Mat(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1); by Mat mask = Mat.zeros(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1);, the last mask with white colored garbage was replaced by an empty black mask withOUT any white color on it. I got the following submat and mask:

    enter image description here enter image description here

  2. I was getting the first contour in the list of contours (named contours) by contours.get(0), and using this first contour to calculate Imgproc.boundingRect() as well as in contourList.add(contours.get(0)); later (where contourList is the list of just one contour which will be used in the last drawContours()).

    Then I went ahead to change contours.get(0) to contours.get(1) in Imgproc.boundingRect() as well as in contourList.add(); (just before Imgproc.drawContours()). That resulted in this submat and mask:

    enter image description here enter image description here

  3. Then I changed back to contours.get(0) in Imgproc.boundingRect(); and let contourList.add(contours.get(1)); be there. Got the following submat and mask:

    enter image description here enter image description here

NOW I am completely Unable to Understand what is happening here.

Upvotes: 0

Views: 578

Answers (1)

api55
api55

Reputation: 11420

I am not sure how this is handle in JAVA (I usually use OpenCV in c++ or python), but there is an error in your code...

The contours list will have a list of list of points. This points will refer to the original image. So, this mean that if the figure one is in lets say, x=300, y= 300, width= 100, height=100 then when you get your submatrix it will try to draw those points in a smaller image... so when it tries to draw point (300,300) in a 100 x 100 image, it will simply fail... probably throws an error or simply doesn't draw anything...

A solution for this is, do a for loop and substract to each point of the contour the initial point of the bounding rect (in my example (300,300)).

As, why there is some garbage drawn... well you never initialize the matrix. Not sure in JAVA, but in c++ you have to set them to 0. I think it should be something like this:

Mat mask = new Mat(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1, new Scalar(0));

I hope this helps :)

EDIT

I think I did not explain myself clearly before.

Your contours are an array of points (x,y). These are the coordinates of the points that represent each contour in the original image. This image has a size, and your submatrix has a smaller size. The points are outside of this small image boundaries....

you should do something like this to fix it:

for (int j = 0; j < contours[0].length; j++) {
    contours[0][j].x -= boundingrect.x;
    contours[0][j].y -= boundingrect.y;
}

and then you can draw the contours, since they will be in boundaries of the submat.

I think in java it is also possible to subtract the opencv points directly:

for (int j = 0; j < contours[0].length; j++) {
    contours[0][j] -= boundingrect.tl();
}

but in this case I am not sure, since I have tried it in c++ only

boundingrect.tl() -> gives you the top left point of the rect

Upvotes: 1

Related Questions