【问题标题】:OpenCV C++ how to know the number of contours per row for sorting?OpenCV C++如何知道每行的轮廓数进行排序?
【发布时间】:2023-03-10 19:48:02
【问题描述】:

我有一个binary image: 在这张图片中,我可以使用重载的std::sort 轻松地从上到下和从左到右对找到的轮廓进行排序。

我首先通过以下方式从上到下排序:

sort(contours.begin(), contours.end(), top_to_bottom_contour_sorter());

然后我从左到右排序:

for (int i = 0; i < contours.size(); i = i + no_of_contours_horizontally)
    {
        sort(i, i + no_of_contours_horizontally, left_to_right_contour_sorter);
    }

top_to_bottomleft_to_right 是我传递给排序函数的单独函数。而no_of_contours_horizontally 相对于第一张图片是三 (3)。

但是,这只有在我知道水平轮廓的数量时才有效。如果我使用的图像将具有不同数量的水平轮廓,就像这张图片一样。 contours_sample。程序失败。我可以蛮力并定义特定索引以更改找到的轮廓数。但是,它会限制程序对特定输入进行操作,而不是灵活。我正在考虑创建可以覆盖在图像顶部的矩形或线条,并计算内部轮廓的数量,以便获得水平轮廓数量的值。如果有更优雅的解决方案,我将不胜感激。

这是我的排序功能

bool top_to_bottom_contour_sorter(const std::vector<Point> &lhs, const std::vector<Point> &rhs)
{
    Rect rectLhs = boundingRect(Mat(lhs));
    Rect rectRhs = boundingRect(Mat(rhs));

    return rectLhs.y < rectRhs.y;
}

bool left_to_right_contour_sorter(const std::vector<Point> &lhs, const std::vector<Point> &rhs)
{
    Rect rectLhs = boundingRect(Mat(lhs));
    Rect rectRhs = boundingRect(Mat(rhs));

    return rectLhs.x < rectRhs.x;
}

编辑 这是我当前的输出和每个图像的期望输出。 使用第一张图片和我当前的工作代码。 Current_Output

第二张图片我想要的输出。 Desired_Output

【问题讨论】:

  • 就在我的脑海中。我会做类似线扫描算法的事情。您从上到下开始“扫描”线并找到该线与先前找到的轮廓的交点,然后您从左到右对它们进行排序,因为您现在知道它们中有哪些以及有多少。
  • 对于每个轮廓,计算boundingRect,然后按相应矩形的xy 值排序。我们是否很快完成了您的调查评估程序? ;-)
  • @HansHirse,差不多了 ;-)。这就是我目前正在做的事情。
  • 好的,那么,您能否在第二个示例中提供所需的轮廓排序顺序?如果您一次考虑所有轮廓,则必须定义 - 例如 - 如果我们先从左到右还是先从上到下。而且,这意味着,我们需要一个分拣机来完成这项任务!或者,您可以单独考虑轮廓组(列、行)。
  • 我做的是先从上到下再从左到右排序。尝试同时使用两个坐标但失败了。

标签: c++ sorting opencv


【解决方案1】:

我猜,您唯一的问题是不尊重其中一个坐标的相等性!?

我们开始吧:

// Custom sorter.
bool sortContour(std::vector<cv::Point> a, std::vector<cv::Point> b)
{
    cv::Rect rectA = cv::boundingRect(a);
    cv::Rect rectB = cv::boundingRect(b);

    if (rectA.y == rectB.y)
        return (rectA.x < rectB.x);

    return (rectA.y < rectB.y);
}

int main()
{
    // Load image.
    cv::Mat image = cv::imread("contours.jpg", cv::IMREAD_GRAYSCALE);

    // There are some artifacts in the JPG...
    cv::threshold(image, image, 128, 255, cv::THRESH_BINARY);

    // Find contours.
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(image, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);

    // Output unsorted contours.
    cv::Mat imageUnsorted = image.clone();
    for (int i = 0; i < contours.size(); i++)
    {
        cv::Rect rect = cv::boundingRect(contours[i]);
        cv::putText(imageUnsorted, std::to_string(i), cv::Point(rect.x - 10, rect.y - 10), cv::FONT_HERSHEY_COMPLEX, 0.5, cv::Scalar(255));
    }
    cv::imwrite("unsorted.png", imageUnsorted);

    // Sort using custom sorter.
    std::sort(contours.begin(), contours.end(), sortContour);

    // Output sorted contours.
    cv::Mat imageSorted = image.clone();
    for (int i = 0; i < contours.size(); i++)
    {
        cv::Rect rect = cv::boundingRect(contours[i]);
        cv::putText(imageSorted, std::to_string(i), cv::Point(rect.x - 10, rect.y - 10), cv::FONT_HERSHEY_COMPLEX, 0.5, cv::Scalar(255));
    }
    cv::imwrite("sorted.png", imageSorted);
}

未排序的轮廓:

排序后的轮廓:

如您所见,也可以将原始顺序颠倒过来,因为cv::findContours 只是朝相反的方向前进。 ;-)

一个重要的警告:如果扫描(或您获得调查)甚至稍微逆时针旋转,此例程将失败。因此,应事先检查整个扫描(或...)的角度。

【讨论】:

  • @Hanshire 再次感谢您的帮助。我只是做了一些小的调整,而不是检查是否相等,我设置了大约 5 的范围。abs(rectLhs.y - rectRhs.y) &lt;= 5
  • @Fer-de-lance 啊,是的 - 简单但好主意!很高兴,我能帮上忙。
【解决方案2】:

一个简单实用的解决方案是排序

y*100 + x

在旋转输入的情况下也可以工作的更复杂的东西是

  1. 选择两个 blob 之间的最小距离
  2. 让我们将连接这两个点的向量称为(dx, dy)
  3. 根据(x*dx + y*dy)*100 + (x*dy - y*dx)排序

输出将按“网格”顺序排列(可能是您想要的一种或旋转 90 度的一种,但旋转输入时问题是不适定的,您应该使用一些规则在两者之间进行选择)。

【讨论】:

    猜你喜欢
    • 2012-11-09
    • 2022-07-20
    • 1970-01-01
    • 1970-01-01
    • 2021-09-14
    • 1970-01-01
    • 2013-05-29
    • 2017-01-17
    • 2015-01-24
    相关资源
    最近更新 更多