Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating rectangle within a blob using OpenCV

Input Image:

Output Image:

I have several colored blobs in an image and I'm trying to create rectangles (or squares--which seems to be much easier) inside the largest blob of each color. I've found the answer to how to create a rectangle that bounds a single largest blob, but am unsure as to how to find a square that simply fits inside a blob. It doesn't have to be the largest, it just has to be larger than a certain area otherwise I just won't include it. I've also seen some work done on polygons, but nothing for amorphous shapes.

like image 819
Jacob M. Avatar asked Nov 09 '22 21:11

Jacob M.


1 Answers

For a single blob, the problem can be formulated as: find the largest rectangle containing only zeros in a matrix.

To find the largest axes-oriented rectangle inside a blob, you can refer to the function findMinRect in my other answer. The code is a porting in C++ of the original in Python from here.


Then the second problem is to find all blobs with the same color. This is a little tricky because your image is jpeg, and compression creates a lot artificial colors near to borders. So I created a png image (shown below), just to show that the algorithm works. It's up to you to provide an image without compression artifacts.

Then you just need to create a mask for each color, find connected components for each blob in this mask, and compute the minimum rectangle for each blob.

Initial image:

enter image description here

Here I show the rects found for each blob, divided by color. You can then take only the rectangles you need, either the maximum rectangle for each color, or the rectangle for the largest blob for each color.

Result:

enter image description here

Here the code:

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

// https://stackoverflow.com/a/30418912/5008845
Rect findMinRect(const Mat1b& src)
{
    Mat1f W(src.rows, src.cols, float(0));
    Mat1f H(src.rows, src.cols, float(0));

    Rect maxRect(0, 0, 0, 0);
    float maxArea = 0.f;

    for (int r = 0; r < src.rows; ++r)
    {
        for (int c = 0; c < src.cols; ++c)
        {
            if (src(r, c) == 0)
            {
                H(r, c) = 1.f + ((r>0) ? H(r - 1, c) : 0);
                W(r, c) = 1.f + ((c>0) ? W(r, c - 1) : 0);
            }

            float minw = W(r, c);
            for (int h = 0; h < H(r, c); ++h)
            {
                minw = min(minw, W(r - h, c));
                float area = (h + 1) * minw;
                if (area > maxArea)
                {
                    maxArea = area;
                    maxRect = Rect(Point(c - minw + 1, r - h), Point(c + 1, r + 1));
                }
            }
        }
    }

    return maxRect;
}


struct lessVec3b
{
    bool operator()(const Vec3b& lhs, const Vec3b& rhs) {
        return (lhs[0] != rhs[0]) ? (lhs[0] < rhs[0]) : ((lhs[1] != rhs[1]) ? (lhs[1] < rhs[1]) : (lhs[2] < rhs[2]));
    }
};

int main()
{
    // Load image
    Mat3b img = imread("path_to_image");

    // Find unique colors
    set<Vec3b, lessVec3b> s(img.begin(), img.end());

    // Divide planes of original image
    vector<Mat1b> planes;
    split(img, planes);
    for (auto color : s)
    {
        // Create a mask with only pixels of the given color
        Mat1b mask(img.rows, img.cols, uchar(255));
        for (int i = 0; i < 3; ++i)
        {
            mask &= (planes[i] == color[i]);
        }

        // Find blobs
        vector<vector<Point>> contours;
        findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

        for (int i = 0; i < contours.size(); ++i)
        {
            // Create a mask for each single blob
            Mat1b maskSingleContour(img.rows, img.cols, uchar(0));
            drawContours(maskSingleContour, contours, i, Scalar(255), CV_FILLED);

            // Find minimum rect for each blob
            Rect box = findMinRect(~maskSingleContour);

            // Draw rect
            Scalar rectColor(color[1], color[2], color[0]);
            rectangle(img, box, rectColor, 2);
        }
    }

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

    return 0;
}
like image 76
Miki Avatar answered Nov 15 '22 07:11

Miki