【问题标题】:Python - Map coordinates into list of lines and arcsPython - 将坐标映射到直线和圆弧列表中
【发布时间】:2021-05-31 17:36:02
【问题描述】:

我正在尝试将一组坐标(表示闭合形状)解析为 python 中的一组线和弧(我使用 OpenCV 进行边缘检测)。

简而言之,我想要实现的是使用绘制此示例图像的坐标

Example shape

进入这组线和弧

Set of arcs

显然,弧线的定义并不像图像中那样定义,而是类似于“像素化”弧线。

是否有任何实用程序可以帮助进行这种处理?

【问题讨论】:

  • 你可以从计算每个点的曲率开始,基本上就是角度。也许平滑它,以防你有一个密集的轮廓,其中点要么相邻,要么在最近的对角线像素上......这会给你“嘈杂”的数据。绘制曲率大小。您会看到直线没有(接近 0)并且圆/弧具有恒定的曲率。椭圆很棘手。从该曲率信息中,您可以将轮廓切割成碎片,因为您可以制作出相等曲率的部分。曲率是一阶导数。你可以走得更远,也可以取二阶导数
  • 您是否需要仅针对此形状的解决方案,或者您想要一个可以矢量化任何形状的程序?如果您需要通用方法,您应该查看库 linedraw。如果只是这一种形状,应该比较容易将其分解为水平线和垂直线,将其余部分替换为矩形,并计算 90° 拱形及其边半径。
  • 将图像阈值化为黑白,使用 Canny 边缘检测,然后 Hough 变换找到直线。在单独的二进制图像上绘制找到的线条,其粗细大致等于图像中的线条,然后对该图像和原始阈值图像进行二进制与 - 这将是直线覆盖的像素,每隔一个非黑色像素属于弧线。

标签: python opencv coordinates line automatic-ref-counting


【解决方案1】:

让我们将图像加载为灰度,将其阈值设置为黑白并反转颜色,稍微腐蚀一下,使用 Canny 边缘检测,然后使用霍夫线检测(主要遵循 this tutorial):

import cv2
import numpy as np
import math
import random

src = cv2.imread("s34I0.png", cv2.IMREAD_GRAYSCALE)
thr, bw = cv2.threshold(src, 128, 255, cv2.THRESH_BINARY_INV)
eroded = cv2.erode(bw, np.ones((5, 5), np.uint8))
canny = cv2.Canny(src, 50, 200, None, 3)

lines = cv2.HoughLines(canny, 1, np.pi / 180, 150, None, 0, 0)
lines = [list(x[0]) for x in lines]

def draw_line(img, line, color, thickness):
    rho, the = line
    a   = math.cos(the)
    b   = math.sin(the)
    x0  = a * rho
    y0  = b * rho
    pt1 = (int(x0 + 1000 * (-b)), int(y0 + 1000 * (a)))
    pt2 = (int(x0 - 1000 * (-b)), int(y0 - 1000 * (a)))

    cv2.line(img, pt1, pt2, color, thickness, cv2.LINE_AA)

很遗憾,我们为每个直线段检测到两条平行线。让我们用它们的中线替换每一对这样接近的平行线:

lines_ = []

def midline(line1, line2):
    return [(x + y) / 2 for x, y in zip(line1, line2)]

used = []
for l1 in lines:
    if l1 in used: continue
    for l2 in lines:
        if l2 in used: continue
        if l1 is l2: continue
        if (abs(l1[0] - l2[0]) < 20) and (abs(l1[1] - l2[1]) < 1):
            lines_.append(midline(l1, l2))
            used.append(l1)
            used.append(l2)
            continue
lines = lines_

现在,让我们为直线创建二进制掩码。对于每条直线,我们创建一个临时的二进制黑色图像(所有像素值都为零),然后在其上绘制一条粗白线(与原始图像上的线条相同或略粗)。然后我们对原始阈值图像和临时线条图像进行逻辑与运算,得到两者的共同像素——即线条的二进制掩码。

line_masks = []
for i, line in enumerate(lines):
    line_img = np.zeros(bw.shape)
    draw_line(line_img, line, 255, 10) # 10 pixel thick white line
    common = np.logical_and((bw != 0), (line_img != 0))
    line_masks.append(common)

从原始黑白图像中删除被遮罩的像素,因此只应保留弧线。不幸的是,一些垃圾仍然存在,因为原始图像中的线条并不完美。为了摆脱这种情况,我们可以将霍夫线画得更粗(比如 15 或 20 像素而不是 10 像素),但是它们占用了太多的弧像素。相反,我们可以对生成的图像进行一点腐蚀扩张,以去除垃圾:

for lm in line_masks:
    bw[lm] = 0

bw = cv2.erode(bw, np.ones((5, 5), np.uint8))
bw = cv2.dilate(bw, np.ones((5, 5), np.uint8))

让我们为弧创建二进制掩码。 OpenCV 中没有检测弧的功能,但对于这种情况,我们可以使用连接组件的检测:

arc_masks = []
num, labels = cv2.connectedComponents(bw)
for i in range(1, num):
    arc_masks.append(labels == i)

现在我们有了蒙版,让我们通过在原始图像上绘制来可视化它们。线条将具有随机的绿色阴影,弧线 - 蓝色:

line_colors = [(0, random.randint(127, 256), 0) for _ in line_masks]
arc_colors = [(random.randint(127, 256), 0, 0) for _ in arc_masks]
dst = cv2.imread("s34I0.png")
for color, mask in zip(line_colors, line_masks):
    dst[mask] = color

for color, mask in zip(arc_colors, arc_masks):
    dst[mask] = color

【讨论】:

  • 这是一个有趣的方法,我会尽快尝试,谢谢老兄的帮助!无论如何我可以拆分两个检测到的“弧”(别名不是线)以获得两个单独的点列表?
  • 弧线已经被上面的代码分割(arc_masks 有两个单独的掩码,每个弧线一个,类似于line_masks 有四个单独的掩码,每行一个)。
猜你喜欢
  • 2013-01-17
  • 1970-01-01
  • 2020-08-09
  • 1970-01-01
  • 1970-01-01
  • 2018-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多