将不规则形状划分为递减的矩形

Posted

技术标签:

【中文标题】将不规则形状划分为递减的矩形【英文标题】:dividing the irregular shape into decreasing rectangles 【发布时间】:2020-05-13 17:52:24 【问题描述】:

我必须,其中一个是最大的矩形,然后是递减的矩形。我只是想知道在编码世界中是否知道这样的问题?怎么做?

该图显示了我想要实现的目标:

【问题讨论】:

我认为这符合packing problem 的条件。它似乎在math.stackexchange.com很受欢迎 有趣的问题,但是“怎么办?”似乎有点太宽泛了。此外,您似乎还处于代码无关紧要的阶段,所以问题不在于 c++ 代码 您可以从设置规则开始。 1) 矩形不得相对于底座“倾斜”。 2) 矩形的最小面积是x。 3) 拟合是通过在尚未占用的空间中最大化矩形的面积来完成的。 4) 重复 3. 直到不能拟合大于x 的矩形。 ...或类似的东西。然后,也许您会弄清楚如何自己编写代码。 :-) 请注意,第一个矩形的线段上至少有 3 个(可能是 4 个)点(因为如果没有,您可以将其变大),类似以下的有 3 个角线段或与前一个矩形相交 我在Miki 的帖子中找到了完美的解决方案,非常感谢@Miki!但它只适用于第一次迭代。保存一个完美的矩形和三个剩余部分。现在我必须对剩下的三张图像做同样的事情,依此类推……你能建议我应该使用递归函数还是其他解决方案。 【参考方案1】:

第一个矩形可以通过这段来自here的代码找到

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;

使用简单

Mat src, src_gray, src_bin;
cv::cvtColor(src, src_gray, COLOR_BGR2GRAY);
src_bin = (src_gray > 1) * 255;
Rect box = findMinRect(~src_bin);
rectangle(src, box, Scalar(0, 0, 255), 2);
cv::imshow("with_rectangle", src);
cv::waitKey();

但这只是第一次迭代。在下一次迭代中,我们必须处理至少 3 个原始图像片段,然后处理 9 个片段,依此类推。我的事情......我将不胜感激任何进一步的建议。

【讨论】:

【参考方案2】:

我接受了 Paul Gray 的回答并编写了一个迭代过程,该过程消耗源以查找纯 c++ 中从最大到最小的所有区域,矩阵的填充部分不需要是连续的形状。

这是我使用的 rect 类,后来我发现 windef.h 中已经有一个 RECT 类,但是我当时已经自己制作了。

class rect

public:
    uint32_t rowStart;
    uint32_t colStart;
    uint32_t height;
    uint32_t width;

    rect(uint32_t _rowStart, uint32_t _colStart, uint32_t _height, uint32_t _width);
    rect();
;

这是 Paul Gray 在他们的答案中发布的算法,在没有使用 openCV 的情况下重做

bool findMinRect(std::vector<std::vector<bool>> *src, rect *retItem)

    bool contFlg = false;
    uint32_t srcRows = src->size();
    uint32_t srcCols = (*src)[0].size();

    std::vector<std::vector<float>> W;
    W.resize(srcRows);

    for (int row = 0; row < srcRows; row++) 
    
        for (int col = 0; col < srcCols; col++)
        
            W[row].push_back(0.f);
        
    

    std::vector<std::vector<float>> H;
    H.resize(srcRows);

    for (int row = 0; row < srcRows; row++) 
    
        for (int col = 0; col < srcCols; col++)
        
            H[row].push_back(0.f);
        
    

    rect maxRect(0, 0, 0, 0);
    float maxArea = 0.f;

    for (int r = 0; r < srcRows; ++r)
    
        for (int c = 0; c < srcCols; ++c)
        
            if ((*src)[r][c] == true)
            
                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(r - h, c - minw + 1, (r + 1 - (r - h)), (c + 1 - (c - minw + 1)));
                    contFlg = true;
                
            
        
    

    *retItem = maxRect;
    return contFlg;

下面的 shapeChk 方法是对源矩阵的完整性检查,以确保它是矩形的,如果不是,它会识别问题所在。

int shapeChk(std::vector<std::vector<bool>> *src)

    uint32_t srcRows = src->size();

    if (srcRows == 0)
    
        return -1;
    

    uint32_t srcCols = (*src)[0].size();

    if (srcCols == 0)
    
        return -2;
    

    for (int row = 0; row < srcRows; row++)
    
        if ((*src)[row].size() != srcCols)
        
            return (-4 - (row + 1));
        
    
    return 0;

以下方法从源矩阵中删除找到的区域,以便重新运行 findMinRect 方法。

void clearclump(std::vector<std::vector<bool>> *src, rect *area)

    for (int r = (area->rowStart); r < (area->rowStart + area->height); r++)
    
        for (int c = (area->colStart); c < (area->colStart + area->width); c++)
        
            (*src)[r][c] = false;
        
    

这是将所有内容组合在一起的主要方法。

int areaClump(std::vector<std::vector<bool>> *src, std::vector<rect> *areas)

    rect retItem(1, 0, 0, 0);
    int err = shapeChk(src);
    if (err != 0)
    
        return err;
    

    while (findMinRect(src, &retItem))
    
        areas->push_back(retItem);
        clearclump(src, &retItem);
    
    return 0;

当您运行 areaClump 时,源矩阵将被完全消耗掉,并且将包含所有 flase 条目,而区域向量包含所有找到的区域。 不确定这是否适合您的目的,或者它是否是处理它的最佳方式,但它对我有用,所以我想我会分享结果,可能会对某人有所帮助。

【讨论】:

以上是关于将不规则形状划分为递减的矩形的主要内容,如果未能解决你的问题,请参考以下文章

Objective c 动态地将图像分割成不规则的拼图形状

题解 SP3734 PERIODNI - Periodni

如何制作非矩形形状的图像

基于形态学处理的不规则形状图像的几何参数统计,包括输出面积,周长,圆度,矩形度,伸长度

AMCL原理概述

不规则可点击形状的图案