python - 如何将 SVG 折线转换为 Quickdraw Stroke-3 numpy 格式?

标签 python numpy svg

我想将包含折线的基本 SVG 文件转换为 sketch-rnn 使用的描边 3 格式。 (以及 quickdraw dataset )。

据我了解,笔画 3 格式的每个折线点将是:

  • 存储为[delta_x, delta_y, pen_up],其中
  • delta_xdelta_y表示相对于 上一点和
  • pen_up 是一个位,当笔抬起时为 1 (例如 move_to 操作 a-la 海龟图形)或 0(当笔处于 向下(例如 line_to 操作 a-la 海龟图形)。

我尝试编写该函数并转换 SVG,但是当我渲染笔划 3 格式的测试时,我得到了一条额外的行。

我的输入 SVG 如下所示:

box svg

<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 900 900" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#00c000" stroke-linecap="round"><path d="m324.56 326.77h62.721 62.721 62.721 62.721"/><path d="m575.44 326.77 0.1772 62.891 0.1771 62.891 0.1772 62.891 0.1771 62.891"/><path d="m576.15 578.33h-63.075-63.075l-63.075-1e-4h-63.075"/><path d="m323.85 578.33 0.1772-62.891 0.1772-62.891 0.1772-62.891 0.1771-62.891"/><path d="m575.44 326.77 29.765-32.469 29.765-32.469" stroke="#c00000"/><path d="m634.97 261.83h-92.486-92.486l-92.486-1e-4h-92.486" stroke="#c00000"/><path d="m265.03 261.83 44.647 48.704 14.882 16.235" stroke="#c00000"/><path d="m323.85 578.33-15.092 13.725-30.183 27.45-15.092 13.725" stroke="#c0c000"/><path d="m263.48 633.23h93.258 93.258l93.258 1e-4h93.258" stroke="#c0c000"/><path d="m636.52 633.23-60.366-54.9" stroke="#c0c000"/><path d="m634.97 261.83 0.3863 92.851 0.3862 92.851 0.3863 92.851 0.3863 92.851" stroke="#0000c0"/><path d="m636.52 633.23h-93.258l-93.258-1e-4h-93.258-93.258" stroke="#0000c0"/><path d="m263.48 633.23 0.3863-92.851 0.3863-92.851 0.3863-92.851 0.3862-92.851" stroke="#0000c0"/></g></svg>

这是一个可视化效果,其中从 SVG 文件解析的线条以粗绿色呈现,从转换后的描边 3 格式绘制的线条以较细的红色呈现: box line and strokes

请注意右侧面上的对角线,它在原始 SVG 中不存在。

我一定是在某处标记了行操作而不是移动操作,但我已经盯着代码太久了,我无法发现错误。

这是一个最小的示例,展示了我使用 svg.path 的尝试:

import xml.etree.ElementTree as ET
import numpy as np
from svg.path import parse_path
from svg.path.path import Line, Move

cubeSVG = '<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 900 900" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#00c000" stroke-linecap="round"><path d="m324.56 326.77h62.721 62.721 62.721 62.721"/><path d="m575.44 326.77 0.1772 62.891 0.1771 62.891 0.1772 62.891 0.1771 62.891"/><path d="m576.15 578.33h-63.075-63.075l-63.075-1e-4h-63.075"/><path d="m323.85 578.33 0.1772-62.891 0.1772-62.891 0.1772-62.891 0.1771-62.891"/><path d="m575.44 326.77 29.765-32.469 29.765-32.469" stroke="#c00000"/><path d="m634.97 261.83h-92.486-92.486l-92.486-1e-4h-92.486" stroke="#c00000"/><path d="m265.03 261.83 44.647 48.704 14.882 16.235" stroke="#c00000"/><path d="m323.85 578.33-15.092 13.725-30.183 27.45-15.092 13.725" stroke="#c0c000"/><path d="m263.48 633.23h93.258 93.258l93.258 1e-4h93.258" stroke="#c0c000"/><path d="m636.52 633.23-60.366-54.9" stroke="#c0c000"/><path d="m634.97 261.83 0.3863 92.851 0.3862 92.851 0.3863 92.851 0.3863 92.851" stroke="#0000c0"/><path d="m636.52 633.23h-93.258l-93.258-1e-4h-93.258-93.258" stroke="#0000c0"/><path d="m263.48 633.23 0.3863-92.851 0.3863-92.851 0.3863-92.851 0.3862-92.851" stroke="#0000c0"/></g></svg>'

def svg_to_stroke3(svg_string):
    # parse the doc
    doc = ET.fromstring(svg_string)
    # get paths
    paths = doc.findall('.//{http://www.w3.org/2000/svg}path')

    strokes = []
    # previous x, y
    px, py = 0, 0

    for path_index, path in enumerate(paths):
        stroke = parse_path(path.attrib['d'])
        was_moving = False
        for operation_index, operation in enumerate(stroke):
            if isinstance(operation, Move):
                mx  = int(operation.start.real)
                my  = int(operation.start.imag)
                # prep this end point for check as next line first point
                was_moving = True

                strokes.append([mx-px, my-py, 1])
                # update previous (absolute) coordinates
                px = mx
                py = my

            if isinstance(operation, Line):
                sx  = int(operation.start.real)
                sy  = int(operation.start.imag)
                ex  = int(operation.end.real)
                ey  = int(operation.end.imag)
                if was_moving:
                    # append delta x, y relative to previous move operation
                    strokes.append([sx-px, sy-py, 0])
                    was_moving = False
                # append delta x,y (line end relative to line start)
                strokes.append([ex-sx, ey-sy, 0])
                # update previous (absolute) coordinates
                px = ex
                py = ey

                # update previous end point
    
    strokes_np = np.array(strokes, dtype=np.int16)
    return strokes_np

print(svg_to_stroke3(cubeSVG))

此外,我已将上述内容作为易于运行的内容提供 Google Colab Notebook .

最佳答案

您的转换是正确的,错误在于渲染代码中。它必须是 is_down = data[i][2] == 0 而不是 draw_lines3 中的 is_down = data[i-1][2] == 0 .

enter image description here

此错误不会在其他路径中出现,除了两种情况外,新路径从前一个路径的末尾开始。在另一种情况下,您确实移动到新的起点,附加线与已绘制的线重合。


更新和更正:
我注意到我误解了提笔位的含义:事实上,它表明在绘制当前笔画之后要抬起笔,而不是为了当前的中风正如我一开始所想的那样。因此,您的渲染代码似乎没问题,并且错误存在于中风3文件生成中。
我想您可以通过记录每个操作的终点以及当前操作的操作码(1 = move,0 = draw)来做到更简单。转换为 numpy 数组后,我们可以通过前两列的差异轻松地将这些绝对位置转换为相对位移,然后将带有操作码的第三列向后移动一个位置:

def svg_to_stroke3(svg_string):
    doc = ET.fromstring(svg_string)
    paths = doc.findall('.//{http://www.w3.org/2000/svg}path')
    strokes = [[0,0,0]]
    for path in paths:
        stroke = parse_path(path.attrib['d'])
        for op in stroke:
            if isinstance(op, Move):
                strokes.append([op.start.real, op.start.imag, 1])
            if isinstance(op, Line):
                strokes.append([op.end.real, op.end.imag, 0])
    strokes_np = np.array(strokes, dtype=np.int16)
    # convert x,y columns to relative displacements
    strokes_np[1:,:2] -= strokes_np[:-1,:2]
    # shift back op codes
    strokes_np[:-1,2] = strokes_np[1:,2]
    # remove [0,0,0]s and set previous op to 0
    m = (strokes_np == 0).all(1)
    strokes_np[np.argwhere(m)-1,2] = 0
    return strokes_np[~m]

这提供了一个相当紧凑的表示(示例立方体为 49 行),并使用您的原始代码和 David Ha 的 draw_lines() 正确渲染(如果您通过约定从笔开始,如 draw_lines() 所做的那样)。

关于python - 如何将 SVG 折线转换为 Quickdraw Stroke-3 numpy 格式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65985435/

相关文章:

Buildroot 中的 Python - PyDrive 错误

Python Pandas - 按空行拆分 Excel 电子表格

python - Python/Pandas 中的 R/ifelse 等价物?比较字符串列?

javascript - 顺利回流大量图像(600+)

java - SVG 到 Java Graphics2D

python - 无法理解 Matplotlib 中的代码

python - 按值查找嵌套字典路径

javascript - outerHtml 在 IE 中为 SVG 元素返回 undefined

python - .py 文件的 Sublime 文本粉红色突出显示

Python:如何减少运行时间?