获取在 opencv 中聚集在一起的多条相邻线的单线表示
我检测到图像中的线条,并使用 HoughLinesP 方法在 OpenCv C++ 中将它们绘制到单独的图像文件中.以下是生成的图像的一部分.实际上有数百条细而细的线形成一条大的单线.
I detected lines in an image and drew them in a separate image file in OpenCv C++ using HoughLinesP method. Following is a part of that resulting image. There are actually hundreds of small and thin lines which form a big single line.
但我想要代表所有这些行数的几行.较近的线应合并在一起形成一条线.例如,上面的一组线应该只用 3 条单独的线表示,如下所示.
But I want single few lines that represent all those number of lines. Closer lines should be merged together to form a single line. For example above set of lines should be represented by just 3 separate lines as below.
预期的输出如上.如何完成这项任务.
The expected output is as above. How to accomplish this task.
到目前为止,akarsakov 的回答的进展结果.
Up to now progress result from akarsakov's answer.
(产生的不同类别的线以不同的颜色绘制).请注意,此结果是我正在处理的原始完整图像,而不是我在问题中使用的示例部分
(separate classes of lines resulted are drawn in different colors). Note that this result is the original complete image I am working on, but not the sample section I had used in the question
推荐答案
如果您不知道图像中的行数,您可以使用 cv::partition
函数在等价组上分割线.
If you don't know the number of lines in the image you can use the cv::partition
function to split lines on equivalency group.
我建议您执行以下程序:
I suggest you the following procedure:
使用
cv::partition
拆分您的行.您需要指定一个好的谓词函数.这实际上取决于您从图像中提取的行,但我认为它应该检查以下条件:
Split your lines using
cv::partition
. You need to specify a good predicate function. It really depends on lines which you extract from image, but I think it should check following conditions:
- 线之间的角度应该很小(例如小于 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
.接下来,您应该按如下方式调用 cv::partition
:
Guess you have your lines in vector<Vec4i> lines;
. Next, you should call cv::partition
as follows:
vector<Vec4i> lines;
std::vector<int> labels;
int numberOfLines = cv::partition(lines, labels, isEqual);
您需要调用 cv::partition
一次,它将对所有行进行聚类.Vector labels
将存储它所属集群的每个行标签.请参阅文档了解cv::partition
You need to call cv::partition
once and it will clusterize all lines. Vector labels
will store for each line label of cluster to which it belongs. See documentation for cv::partition
- 获得所有行组后,您应该合并它们.我建议计算组中所有线的平均角度并估计边界"点.例如,如果角度为零(即所有线几乎是水平的),它将是最左侧和最右侧的点.只需在这些点之间划一条线即可.
我注意到您示例中的所有线条都是水平或垂直的.在这种情况下,您可以计算出所有线段中心和边界"点的平均值的点,然后通过中心点绘制由边界"点限制的水平或垂直线.
I noticed that all lines in your examples are horizontal or vertical. In such case you can calculate point which is average of all segment's centers and "border" points, and then just draw horizontal or vertical line limited by "border" points through center point.
请注意,cv::partition
需要 O(N^2) 的时间,因此如果您处理大量行,可能会花费很多时间.
Please note that cv::partition
takes O(N^2) time, so if you process a huge number of lines it may take a lot of time.
我希望它会有所帮助.我将这种方法用于类似的任务.
I hope it will help. I used such approach for similar task.
相关文章