【问题标题】:Finding largest inscribed rectangle in polygon找到多边形中最大的内接矩形
【发布时间】:2022-01-18 13:26:58
【问题描述】:

我有一堆带有标记为红色 (255,0,0) 的多边形的图像:

我想提取边界框,但在多边形内,如蓝色矩形所示:

OpenCV cv.boundingRect 给了我外边界框,但如何提取内边界框?

【问题讨论】:

  • 您有栅格数据还是矢量数据? - “凸多边形中最大的内接矩形” - 几周/几个月前有一个类似的问题允许旋转。解决方案是一些迭代方法。 - 如果您有栅格数据,距离变换可能会起作用。如果您有矢量数据,则可能有更简单的方法。取决于四边形的野性,可能不会简单得多。
  • 你只有凸多边形还是凹多边形?这是一种尝试的方法,它不是最佳的,在某些情况下甚至可能会失败,但对于许多用户来说似乎工作“正常”:stackoverflow.com/a/21479072/2393191 现在有更多更新的答案,也许是更好的答案。
  • 我有 RGB 图像并且多边形是凸的。
  • 我也在考虑提取最外层的 4 个点并从中创建一个矩形

标签: python opencv geometry


【解决方案1】:

也许看看这个largest interior rectancle implementation。它使用this paper 中描述的算法。一个示例图像是

【讨论】:

    【解决方案2】:

    不是一个快速的解决方案,但该算法应该为您提供正确的最大内部矩形:

    1. 将轮廓绘制为强度值为 1 的蒙版
    2. 计算口罩的积分图像https://en.wikipedia.org/wiki/Summed-area_table
    3. 对于掩码值 > 0 的每个像素:对于该像素右侧/底部的每个像素,通过从积分图像中读取 4 个值来测试整个矩形是否被白色像素填充。

    积分图像使其比应有的效率更高,算法本身只是蛮力。

    int main()
    {
        //std::vector<cv::Point> contour = { cv::Point(100,100), cv::Point(200,160), cv::Point(220, 230), cv::Point(90,270) };
        std::vector<cv::Point> contour = { cv::Point(100,100), cv::Point(150,200), cv::Point(200,160), cv::Point(220, 230), cv::Point(90,270) };
        //std::vector<cv::Point> contour = { cv::Point(100,100), cv::Point(200,100), cv::Point(200, 300), cv::Point(100,300) };
    
        cv::Mat img = cv::Mat::zeros(512, 512, CV_8UC3);
        cv::Mat mask = cv::Mat::zeros(img.size(), CV_8UC1);
    
        std::vector<std::vector<cv::Point> > contours = { contour };
        cv::drawContours(mask, contours, 0, cv::Scalar::all(1), -1); // mask values to 1 to make area == sum of pixels
        cv::drawContours(img, contours, 0, cv::Scalar(0, 0, 255), 1);
    
        cv::Mat integral;
        mask = mask;
        cv::integral(mask, integral, CV_32S);
    
        cv::Rect best;
    
        //cv::Mat legal = mask.clone();
        for (int y = 0; y < mask.rows; ++y)
        {
            std::cout << y << std::endl;
            for (int x = 0; x < mask.cols; ++x)
            {
                if (mask.at<uchar>(y, x) == 0) continue;
                cv::Point i1 = cv::Point(x, y);
                int val1 = integral.at<int>(i1);
                for (int y2 = y + 1; y2 < integral.rows; ++y2)
                    for (int x2 = x + 1; x2 < integral.cols; ++x2)
                    {
                        
                        cv::Point i2 = cv::Point(x2, y);
                        cv::Point i3 = cv::Point(x, y2);
                        cv::Point i4 = cv::Point(x2, y2);
                        if (mask.at<uchar>(i4) == 0) continue;
                        int val2 = integral.at<int>(i2);
                        int val3 = integral.at<int>(i3);
                        int val4 = integral.at<int>(i4);
    
                        int area = val1 + val4 - val2 - val3;
                        if (area != (x2 - x) * (y2 - y))
                        {
                            //std::cout << i1 << " to " << i4 << " = w:" << (x2 - x) << " h:" << (y2 - y) << std::endl;
                            //std::cout << area << " vs. " << (x2 - x) * (y2 - y) << std::endl;
                            //legal.at<uchar>(y, x) = 0;
                            //std::cin.get();
                        }
                        else
                        {
                            if (area > best.area()) best = cv::Rect(i1, i4);
                        }
                    }
            }
            
        }
    
        cv::rectangle(img, best, cv::Scalar(255, 0, 0), 1);
    
        cv::imshow("img", img);
        cv::imshow("mask", mask>0);
        cv::waitKey(0);
    }
    

    【讨论】:

      【解决方案3】:

      这个内接矩形问题比外矩形要复杂一些。一个相关的一般情况是多边形内的多边形 - 多边形包含问题this 1983 论文中进行了描述。该算法判断一个有n个点的多边形是否可以拟合另一个有m个点且复杂度为O(n^3 m^3(n+m)log(n+m))的多边形。

      “几何凸集中的最大内接矩形”的受限情况可以更快地完成。看看这篇 2019 年的论文 (DeepAI Link),他们考虑了在紧凑且实心的凸集内找到最大体积的内切框和轴对齐内切框的问题。

      Here 是另一种较老的更易于理解的凸多边形算法,具有良好的可视化效果。

      这是MATLAB implementation。但是代码缺少cmets,有点难理解。

      这是python implementation,但有点过时了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-05-13
        • 2023-04-11
        • 2013-06-25
        • 1970-01-01
        • 2015-02-08
        • 1970-01-01
        • 2013-03-05
        相关资源
        最近更新 更多