将图像分成包含对象的框

vin*_*001 1 c++ opencv

我遇到了用包含对象的框/子图像分割二值化图像的问题(注意:框可以是不规则的,而对象是任何其他原始形状的圆形)。这可以用下面的图片来解释:

带有物体的二值化图像

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

分区图像

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

因此,有什么意见可以做到吗?

Mik*_*iki 6

既然你提到了:

盒子可以是不规则的

您可以使用 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;
}
Run Code Online (Sandbox Code Playgroud)

更新

如果您需要将框设为矩形,则可以查看递归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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,此算法仅适用于可以沿 X 或 Y 维度进行切割的情况,即所有背景像素都有一条水平线或垂直线。这意味着这不适用于非常混乱的图像。