我遇到了使用包含对象的box / subimage对二值化图像进行分区的问题(注意:当对象处于任何其他原始形状的圆圈时,框可以是不规则的)。这可以通过以下图像解释:
图1:将圆圈作为感兴趣对象的图像
图2:包含任意大小的包含感兴趣对象的框的图像
那么,有什么意见可以做到这一点吗?
答案 0 :(得分:2)
既然你提到过:
盒子可以是不规则的
您可以使用Voronoi图(由distanceTransform计算):
代码:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
Mat1f dist;
Mat1i labels;
distanceTransform(img, dist, labels, CV_DIST_L2, 3, DIST_LABEL_CCOMP);
// Show result
Mat1b labels1b;
labels.convertTo(labels1b, CV_8U);
normalize(labels1b, labels1b, 0, 255, NORM_MINMAX);
Mat3b res;
applyColorMap(labels1b, res, COLORMAP_JET);
res.setTo(Scalar(0,0,0), ~img);
imshow("Result", res);
waitKey();
return 0;
}
<强>更新强>
如果您需要这些框矩形,您可以查看递归XY Cut算法。这是XY Cut算法的修改版本,它使矩形不接触前景对象,因此所有矩形的总和覆盖整个图像区域。在这里我颠倒了图像,因为通常黑色是背景,白色是前景。
代码:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
vector<Rect> XYCut_projH(const Mat1b& src, Rect roi)
{
Mat1b projH;
reduce(src(roi), projH, 1, CV_REDUCE_MAX);
vector<Rect> rects;
bool bOut = true;
vector<int> coords;
coords.push_back(0);
for (int i = 0; i < projH.rows; ++i)
{
if (bOut && projH(i) > 0)
{
coords.back() = (coords.back() + i) / 2;
bOut = false;
}
else if (!bOut && projH(i) == 0)
{
coords.push_back(i);
bOut = true;
}
}
coords.front() = 0;
coords.back() = projH.rows;
if (coords.size() <= 1) return rects;
for (int i = 0; i < coords.size() - 1; ++i)
{
Rect r(0, coords[i], src.cols, coords[i + 1] - coords[i]);
r = (r + roi.tl()) & roi;
rects.push_back(r);
}
return rects;
}
vector<Rect> XYCut_projV(const Mat1b& src, Rect roi)
{
Mat1b projV;
reduce(src(roi), projV, 0, CV_REDUCE_MAX);
vector<Rect> rects;
bool bOut = true;
vector<int> coords;
coords.push_back(0);
for (int i = 0; i < projV.cols; ++i)
{
if (bOut && projV(i) > 0)
{
coords.back() = (coords.back() + i) / 2;
bOut = false;
}
else if (!bOut && projV(i) == 0)
{
coords.push_back(i);
bOut = true;
}
}
coords.front() = 0;
coords.back() = projV.cols;
if (coords.size() <= 1) return rects;
for (int i = 0; i < coords.size() - 1; ++i)
{
Rect r(coords[i], 0, coords[i + 1] - coords[i], src.rows);
r = (r + roi.tl()) & roi;
rects.push_back(r);
}
return rects;
}
void XYCut_step(const Mat1b& src, Rect roi, vector<Rect>& rects, bool bAlternate)
{
vector<Rect> step;
if (bAlternate)
{
step = XYCut_projH(src, roi);
if ((step.size() == 1) && (step[0] == roi) && (XYCut_projV(src, roi).size() == 1))
{
rects.push_back(roi);
return;
}
}
else
{
step = XYCut_projV(src, roi);
if ((step.size() == 1) && (step[0] == roi) && (XYCut_projH(src, roi).size() == 1))
{
rects.push_back(roi);
return;
}
}
for (int i = 0; i < step.size(); ++i)
{
XYCut_step(src, step[i], rects, !bAlternate);
}
}
void XYCut(const Mat1b& src, vector<Rect>& rects)
{
bool bAlternate = true;
Rect roi(0, 0, src.cols, src.rows);
XYCut_step(src, roi, rects, bAlternate);
}
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
// invert image, if needed
img = ~img;
// Apply (modified) XY Cut
vector<Rect> rects;
XYCut(img, rects);
// Show results
Mat3b res;
cvtColor(img, res, COLOR_GRAY2BGR);
for (int i = 0; i < rects.size(); ++i)
{
rectangle(res, rects[i], Scalar(0,255,0));
}
imshow("Result", res);
waitKey();
return 0;
}
请注意,此算法仅在可以沿X或Y维度进行切割时才有效,即存在包含所有背景像素的水平或垂直线。这意味着这不会在非常混乱的图像中起作用。