Reputation: 11
I am working on a project where I need to compare an initial SVG with its skeleton and decided to work with Trimesh. The goal is to create an output image that combines the initial SVG with skeletonized paths: for the parts where the skeleton is close to the initial on a certain threshold - keep only skeletonized edges, while for the parts where the distance is over the threshold - keep only the initials.
Initial steps were easy
import trimesh
initial_edges = trimesh.load('vectorized_shapes.svg')
skeletonized_edges = initial_edges.medial_axis()
However, I encountered challenges when attempting to compare individual vertices between the initial and skeletonized paths. The approach I tried didn't work well, as vertices are not entities, and I couldn't find a suitable way to compare initial entities with the skeletonized ones.
for i, initial_vertex in enumerate(initial_edges.vertices):
min_distance = float("inf")
# Find the closest skeleton vertex for this initial vertex
for j, skeleton_vertex in enumerate(skeletonized_edges.vertices):
distance_current = np.linalg.norm(initial_vertex - skeleton_vertex)
# If the vertex is closer to inital than any one before -
if distance_current < min_distance:
min_distance = distance_current
closest_skeleton = skeleton_vertex
skeleton_index = j
# If skeleton is closer to initial than threshold - keep skeleton
if min_distance <= threshold:
vertex = closest_skeleton
# If skeleton is not closer to initial than threshold - keep initial
else:
vertex = initial_vertex
final_vertices.append(vertex)
It didn't work very well, since vertices are not entities and I couldn't also find a way to compare initial entities with the skeletonized ones.
Additionally, when I attempted to export the skeletonized edges using the following code:
initial_edges.export(file_obj="svg.svg"))
The output in Jupyter and the exported file differed significantly.
My questions are:
.show()
method?Any insights or suggestions would be greatly appreciated!
Upvotes: 1
Views: 89
Reputation: 11
Managed to save it as svg from matplotlib the following way:
def save_skeleton_as_svg(skeletonized_edges,
min_line_skeleton_threshold: int = 10,
skeleton_smooth_tolerance: int = 5,
skeletonized_svg_name = "skeletonized.svg") -> None: # TODO: skeletonized_svg_name should be dynamic
"""
It's a pretty ugly approach since trimesh has out-of-the-box export and save functions,
but it doesn't play with parameters and representation thus it's much more precise
Parameters:
- skeletonized_edges: trimesh Path2D from skeletonize_svg function
- min_line_skeleton_threshold: Lines smaller than this will be eliminated at the final svg
- skeleton_smooth_tolerance: How smooth the final image should be.
The lower is the number - the smoother is the result
- skeletonized_svg_name: Save skeleton to the file named skeletonized_svg_name
"""
# Assuming skeletonized_edges is an instance of Path2D
fig, ax = plt.subplots()
# Turn off axis ticks and labels from matplotlib
ax.set_xticks([])
ax.set_yticks([])
ax.set_xticklabels([])
ax.set_yticklabels([])
# Remove the box around the plot
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
# Entity is a trimesh entity, can be line, curve, circle etc
for entity in skeletonized_edges.entities:
# Get the discrete representation of the entity
discrete = entity.discrete(skeletonized_edges.vertices)
# Check if the entity length is longer than the threshold
if len(discrete) > min_line_skeleton_threshold:
# Smooth the curve using the Ramer-Douglas-Peucker algorithm
line = LineString(discrete)
# Setting preserve_topology=False simplifies the geometry without
# enforcing strict topological preservation, which can result in
# a smoother and more straightforward simplification of the path.
simplified_line = line.simplify(tolerance=skeleton_smooth_tolerance,
preserve_topology=False)
# Convert the simplified LineString back to discrete representation
simplified_discrete = list(simplified_line.coords)
# if the entity has its own plot method use it
if hasattr(entity, 'plot'):
entity.plot(skeletonized_edges.vertices, ax=ax)
continue
# otherwise plot the simplified discrete curve
ax.plot(*zip(*simplified_discrete))
# Save the plot in SVG format
plt.savefig(skeletonized_svg_name, format='svg')
Will continue comparing skeleton with initial image without trimesh
Upvotes: 0