【问题标题】:is there any way to check if a line output of LSD is vertical or horizontal有什么方法可以检查LSD的线输出是垂直的还是水平的
【发布时间】:2021-12-26 11:31:07
【问题描述】:

我在 python 和 OpenCV 中使用 LSD: LineSegmentDetector,现在的问题是我想计算检测到的水平线数和检测到的垂直线数。

img = cv2.imread("test/images.jpg")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 100, 200, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
linesL = lsd(gray)
for line in linesL:
    x1, y1, x2, y2, width = map(int,line)
    length = line_length(x1,y1,x2,y2)
    if line[-1]<3:
        lines_img = cv2.line(img, (x1,y1), (x2,y2), (0,0,0),1)
show_img(lines_img,"FLD")

线数组 [[x1,y1,x2,y2,width],....]

我也尝试过形态学运算和 houghlinesP,但它们表现不佳。

【问题讨论】:

  • 可以获取每条线的斜率并设置阈值。如果斜率满足阈值,则它是垂直的。否则它是水平的。
  • 我尝试了这种方法,但对我没有效果
  • 让我们假设我们有 2 个坐标 (20,40),(22.5,10.5) 然后 m = -11.8,它是一条垂直线,但符合以下逻辑它的水平 desmos.com/calculator/md6buy4efz
  • 线段与向量 (1,0) 之间的角度。阅读 aran2 或玩简单的余弦角测量和 pi 的倍数

标签: python opencv math computer-vision image-segmentation


【解决方案1】:

我们可以使用斜率来解决这个问题。我们知道一条线的斜率是arctanys 和xs 之比的xs

slope_as_angle = atan((y1 - y2) / (x1 - x2))

最好使用atan2 而不是atan。为简单起见,让我们使用度数而不是弧度:

slope_as_angle = math.degrees(math.atan2((y1 - y2), (x1 - x2)))

现在让我们看看不同倾斜角度下线条的外观:

for i in range(0, 360, 20):
    x = 10 * math.cos(math.radians(i))
    y = 10 * math.sin(math.radians(i))
    spl = math.degrees(math.atan2(y - 0, x - 0)) % 360

    plt.plot([0, x], [0, y], label=str(i))

plt.legend()
plt.show()

请注意,对于每个 20 degrees,我们正在获取 (0, 0) 和 (cos, sin) 之间的所有线的倾斜角(请参阅:Cartesian to Polar)。

现在我们需要一个逻辑来理解一条线在给定的倾斜角度下是垂直还是水平:

水平

我会说每条斜角在 [160, 200] 或大于 340 或小于 20 之间的线是水平的。

if 160 < angle < 200 or 340 < angle or angle < 20

更好的方法:

if 160 < angle < 200 or not 20 < angle < 340

垂直

我会说斜角在 [60, 120] 或 [240, 300] 之间的每条线都是垂直的。

if 60< angle < 120 or 240 < angle < 300.

让我们指定一个限制变量作为阈值,以便我们可以随意更改它:

对于水平线:

if (not limit < spl <= 360 - limit) or (180 - limit <= spl < 180 + limit):

垂直线:

if (90 - limit < spl < 90 + limit) or (270 - limit < spl < 270 + limit):

要检查的代码是:

def check_the_line(slope, limit=10):
    if (not limit < spl <= 360 - limit) or (180 - limit <= spl < 180 + limit):
        return "h"
    elif (90 - limit < spl < 90 + limit) or (270 - limit < spl < 270 + limit):
        return "v"
    
    return "o"

让我们验证一下:

import math
from matplotlib import pyplot as plt

fig, (ax1, ax2, ax3) = plt.subplots(3, 1)

limit = 10


def check_the_line(slope, limit=10):
    if (not limit < spl <= 360 - limit) or (180 - limit <= spl < 180 + limit):
        return "h"
    elif (90 - limit < spl < 90 + limit) or (270 - limit < spl < 270 + limit):
        return "v"

    return "o"


for i in range(0, 360, 1):
    x = 10 * math.cos(math.radians(i))
    y = 10 * math.sin(math.radians(i))
    spl = math.degrees(math.atan2(y, x)) % 360

    ax1.plot([0, x], [0, y])
    if check_the_line(spl, limit=limit) == "h":
        ax2.plot([0, x], [0, y])
    elif check_the_line(spl, limit=limit) == "v":
        ax3.plot([0, x], [0, y])

ax1.set_title("All lines")
ax1.set_xlim([-10, 10])
ax1.set_ylim([-10, 10])

ax2.set_title("Horizontal Lines")
ax2.set_xlim([-10, 10])
ax2.set_ylim([-10, 10])

ax3.set_title("Vertical Lines")
ax3.set_xlim([-10, 10])
ax3.set_ylim([-10, 10])
plt.show()

【讨论】:

  • 您的图具有欺骗性,因为 x 轴和 y 轴的单位像素数不同。在最后一个情节中尤其明显,由于这种差异,它看起来像一个沙漏。
  • 每个具有“如何阅读图表”基本知识的人都会知道查看轴上的值。这不是一张纸。很抱歉,但我不会花更多时间/精力来制作矩形且具有视觉吸引力的图形。我希望 OP 明白其中的逻辑。
  • 此外,您选择的阈值意味着您对垂直线有 30° 的容差(允许范围 60、120 围绕角度 90),但对于水平线只有 20° 的容差(允许范围 160、 200 围绕角度 180)。
  • 这些数字是占位符。如果您可以继续阅读,您会看到阈值是函数的固定值(limit)。你看我为每个20 degrees画了线,不幸的是90不能被20整除。
【解决方案2】:

你知道端点的坐标,你可以简单地计算直线的斜率

slope = (y2 - y1) / (x2 - x1)

如果斜率为0,则直线是水平的,如果是无穷大,那么直线是垂直的。 实际上,您很少有等于 0 或无穷大的斜率。所以简单地设置一个阈值:

if abs(slope) < 1:
    print("It's an horizontal line!")
elif abs(slope) > 100:
    print("It's a vertical line!")
else:
    print("It's... a line!")

如果您真的只关心水平线和垂直线,另一个简单的解决方案是比较 x 值和 y 值:

if abs(x1 - x2) < 5:
    print("It's a vertical line!")
elif abs(y1 - y2) < 5:
    print("It's an horizontal line!")
else:
    print("It's... a line!")

编辑:我在斜率比较中添加了绝对值。

【讨论】:

  • 假设我们有坐标 (5,5) 和 (50,10),我们知道它的垂直线但斜率为 0.11...
  • (x1 = 5, y1 = 5) 和 (x2 = 50, y2 = 10) 给出一条几乎平坦的线(水平)。 x 值是“水平”坐标,y 值是“垂直”坐标。如果你交换了坐标顺序,只需交换水平/垂直分类
  • 如果 (x1 = 20, y1 = 40),(x2 = 22.5, y= 10.5) 那么 m = -11.8,但几乎垂直
  • 您选择的阈值非常奇怪。斜率为 1 对应于与水平线夹角为 pi/4 (45°) 的线;所以,根本不是一条水平线。斜率为 100 对应于与水平面成 89.4° 角的线;所以,一条非常垂直的线。
  • 阈值任意设置,例如。它们的值取决于您想要的精度。此外,除以 0 问题也是已知的,您可以在计算斜率之前测试 x1 和 x2 是否相等。由于这个问题,我添加了替代方案。我没有为他的问题提供完整的工作代码,只是暗示他可能会尝试。
猜你喜欢
  • 2012-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-08
  • 1970-01-01
  • 2011-04-23
  • 2015-05-11
  • 2014-06-03
相关资源
最近更新 更多