获取在opencv中聚集在一起的多个关闭行的单行表示

新手上路,请多包涵

我检测到图像中的线条,并使用 HoughLinesP 方法将它们绘制在 OpenCv C++ 中的单独图像文件中。以下是生成的图像的一部分。实际上有数百条细小的线形成一条大的单线。

在此处输入图像描述

但我想要代表所有这些行数的几行。较近的线应合并在一起形成一条线。例如,上面的一组线应该由下面的 3 条单独的线表示。

在此处输入图像描述

预期输出如上。如何完成这个任务。



到目前为止,阿卡萨科夫的回答取得了进展。


(产生的不同类别的线用不同的颜色绘制)。请注意,此结果是我正在处理的原始完整图像,而不是我在问题中使用的示例部分

在此处输入图像描述

原文由 Samitha Chathuranga 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 578
1 个回答

如果您不知道图像中的行数,您可以使用 cv::partition 函数在等价组上拆分行。

我建议您执行以下程序:

  1. 使用 cv::partition 拆分行。您需要指定一个好的谓词函数。这实际上取决于您从图像中提取的行,但我认为它应该检查以下条件:

    • 线之间的角度应该非常小(例如,小于 3 度)。使用 点积 计算角度的余弦。
    • 线段中心之间的距离应小于两个线段最大长度的一半。

例如,它可以实现如下:

 bool isEqual(const Vec4i& _l1, const Vec4i& _l2)
{
    Vec4i l1(_l1), l2(_l2);

    float length1 = sqrtf((l1[2] - l1[0])*(l1[2] - l1[0]) + (l1[3] - l1[1])*(l1[3] - l1[1]));
    float length2 = sqrtf((l2[2] - l2[0])*(l2[2] - l2[0]) + (l2[3] - l2[1])*(l2[3] - l2[1]));

    float product = (l1[2] - l1[0])*(l2[2] - l2[0]) + (l1[3] - l1[1])*(l2[3] - l2[1]);

    if (fabs(product / (length1 * length2)) < cos(CV_PI / 30))
        return false;

    float mx1 = (l1[0] + l1[2]) * 0.5f;
    float mx2 = (l2[0] + l2[2]) * 0.5f;

    float my1 = (l1[1] + l1[3]) * 0.5f;
    float my2 = (l2[1] + l2[3]) * 0.5f;
    float dist = sqrtf((mx1 - mx2)*(mx1 - mx2) + (my1 - my2)*(my1 - my2));

    if (dist > std::max(length1, length2) * 0.5f)
        return false;

    return true;
}

猜猜你的台词在 vector<Vec4i> lines; 。接下来,您应该调用 cv::partition 如下:

 vector<Vec4i> lines;
std::vector<int> labels;
int numberOfLines = cv::partition(lines, labels, isEqual);

您需要调用 cv::partition 一次,它将聚集所有行。向量 labels 将存储它所属的簇的每个行标签。参见 文档 cv::partition

  1. 获得所有行组后,您应该合并它们。我建议计算组中所有线的平均角度并估计“边界”点。例如,如果角度为零(即所有线几乎水平),它将是最左边和最右边的点。剩下的只是在这些点之间画一条线。

我注意到您示例中的所有线条都是水平或垂直的。在这种情况下,您可以计算所有线段中心和“边界”点的平均值,然后通过中心点绘制由“边界”点限制的水平或垂直线。

请注意 cv::partition 需要 O(N^2) 时间,因此如果您处理大量行,可能需要很长时间。

我希望它会有所帮助。我将这种方法用于类似的任务。

原文由 akarsakov 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题