将不规则形状划分为递减的矩形
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 条目,而区域向量包含所有找到的区域。 不确定这是否适合您的目的,或者它是否是处理它的最佳方式,但它对我有用,所以我想我会分享结果,可能会对某人有所帮助。
【讨论】:
以上是关于将不规则形状划分为递减的矩形的主要内容,如果未能解决你的问题,请参考以下文章