分区图像到包含对象的框中

时间:2015-11-12 06:15:57

标签: c++ opencv

我遇到了使用包含对象的box / subimage对二值化图像进行分区的问题(注意:当对象处于任何其他原始形状的圆圈时,框可以是不规则的)。这可以通过以下图像解释:

binarized image with objects

图1:将圆圈作为感兴趣对象的图像

Partitioned Image

图2:包含任意大小的包含感兴趣对象的框的图像

那么,有什么意见可以做到这一点吗?

1 个答案:

答案 0 :(得分:2)

既然你提到过:

  

盒子可以是不规则的

您可以使用Voronoi图(由distanceTransform计算):

enter image description here

代码:

#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算法的修改版本,它使矩形不接触前景对象,因此所有矩形的总和覆盖整个图像区域。在这里我颠倒了图像,因为通常黑色是背景,白色是前景。

enter image description here

代码:

#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维度进行切割时才有效,即存在包含所有背景像素的水平或垂直线。这意味着这不会在非常混乱的图像中起作用。