W przypadku pojedynczego blobu problem można sformułować jako: find the largest rectangle containing only zeros in a matrix.
Aby znaleźć największy prostokąt zorientowany na osie wewnątrz obiektu typu blob, można skorzystać z funkcji findMinRect
w my other answer. Kod jest portowaniem w C++ oryginału w Pythonie z here.
Następnie drugim problemem jest znalezienie wszystkich obiektów BLOB o tym samym kolorze. Jest to nieco skomplikowane, ponieważ obraz jest w formacie JPEG, a kompresja tworzy wiele sztucznych kolorów w pobliżu krawędzi. Stworzyłem więc obraz png (pokazany poniżej), aby pokazać, że działa algorytm. Od Ciebie zależy dostarczenie obrazu bez artefaktów kompresji.
Następnie wystarczy utworzyć maskę dla każdego koloru, znaleźć połączone elementy dla każdego obiektu typu blob w tej masce i obliczyć minimalny prostokąt dla każdego obiektu typu blob.
obraz początkowa:
Tu pokazać prostokąty, znalezione dla każdego blob, podzielonych według kolorów. Następnie możesz wziąć tylko te prostokąty, których potrzebujesz, albo maksymalny prostokąt dla każdego koloru, albo prostokąt dla największej kropelki dla każdego koloru.
Wynik:
Oto kod:
#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;
}
Jeśli podasz non skompresowany obraz, wyślę na moją odpowiedź również wyniki na obrazie. – Miki