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.
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:
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:
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With