首页 > 解决方案 > 有没有办法检查LSD的线输出是垂直的还是水平的

问题描述

我在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,但它们表现不佳。

标签: pythonopencvmathcomputer-visionimage-segmentation

解决方案


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

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()

请注意,我们得到了 (0, 0) 和 ( cos, sin) 之间所有线的斜角(参见:笛卡尔到极坐标20 degrees

每 20 度的所有线的斜率

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

水平的

我会说每条斜角在 [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()

应用于不同行的代码


推荐阅读