Getting started
The notebook presents basic usage of the algorithm.
import sn_graph as sn
from skimage import data
import numpy as np
import matplotlib.pyplot as plt
Scikit-image horse example
img = data.horse()
img = img * -1 + 1 # Switch background and foreground
spheres_centers, edges, sdf_array = sn.create_sn_graph(
image=img,
max_num_vertices=300,
edge_threshold=0.95,
max_edge_length=-1,
minimal_sphere_radius=1.5,
return_sdf=True,
)
graph_img = sn.draw_sn_graph(spheres_centers, edges, sdf_array, background_image=img)
# Create figure with two subplots side by side
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 7))
ax1.imshow(img, cmap="binary")
ax1.set_title("Binary Image")
ax2.imshow(graph_img, cmap="gray")
ax2.set_title("SN-Graph")
print(f"""
Graph Statistics:
Number of nodes: {len(spheres_centers)}
Number of edges: {len(edges)}
""")
plt.tight_layout()
plt.show()
Synthetic numpy array example
img = np.zeros((512, 512))
img[0:450, 50:] = 1 # Create a square
img[100:250, 100:250] = 0
spheres_centers, edges, sdf_array = sn.create_sn_graph(
img, max_num_vertices=20, minimal_sphere_radius=6, return_sdf=True
)
graph_img = sn.draw_sn_graph(spheres_centers, edges, sdf_array, background_image=img)
# Create figure with two subplots side by side
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 7))
ax1.imshow(img, cmap="binary")
ax1.set_title("Binary Image")
ax2.imshow(graph_img, cmap="gray")
ax2.set_title("SN-Graph")
print(f"""
Graph Statistics:
Number of nodes: {len(spheres_centers)}
Number of edges: {len(edges)}
""")
plt.tight_layout()
plt.show()
Compare with other skeletonisation approaches
from skimage.morphology import skeletonize, thin, medial_axis
import time
def compare_skeletonization(image):
timings = {}
# Basic skeletonization
start = time.time()
skeleton_basic = skeletonize(image)
timings["basic"] = time.time() - start
# Thinning
start = time.time()
skeleton_thin = thin(image)
timings["thin"] = time.time() - start
# Medial axis
start = time.time()
skeleton_medial = medial_axis(image)
timings["medial"] = time.time() - start
# Sphere-node graph without circles
start = time.time()
spheres_centers, edges, sdf_array = sn.create_sn_graph(
image=image,
max_num_vertices=50,
edge_threshold=0.95,
max_edge_length=-1,
minimal_sphere_radius=3,
return_sdf=True,
)
sn_graph_img = sn.draw_sn_graph(spheres_centers, edges)
timings["sn_graph"] = time.time() - start
# Sphere-node graph with circles
sn_graph_img_circles = sn.draw_sn_graph(spheres_centers, edges, sdf_array)
# Create visualization
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# Original image
axes[0, 0].imshow(image, cmap="binary")
axes[0, 0].set_title("Original")
axes[0, 0].axis("off")
# Basic skeletonization
axes[0, 1].imshow(skeleton_basic, cmap="binary")
axes[0, 1].set_title(f'Basic Skeletonization\n{timings["basic"]:.3f}s')
axes[0, 1].axis("off")
# Medial axis
axes[0, 2].imshow(skeleton_medial, cmap="binary")
axes[0, 2].set_title(f'Medial Axis\n{timings["medial"]:.3f}s')
axes[0, 2].axis("off")
# Thinning
axes[1, 0].imshow(skeleton_thin, cmap="binary")
axes[1, 0].set_title(f'Thinning\n{timings["thin"]:.3f}s')
axes[1, 0].axis("off")
# SN Graph without circles
axes[1, 1].imshow(sn_graph_img * (-1) + 1, cmap="gray")
axes[1, 1].set_title(f'SN-Graph\n{timings["sn_graph"]:.3f}s')
axes[1, 1].axis("off")
# SN Graph with circles
axes[1, 2].imshow(sn_graph_img_circles * (-1) + 1, cmap="gray")
axes[1, 2].set_title("SN-Graph with Circles")
axes[1, 2].axis("off")
plt.tight_layout()
plt.show()
# Print timing statistics
print("\nExecution Times:")
for method, timing in timings.items():
print(f"{method}: {timing:.3f} seconds")
# Print skeleton statistics
print("\nSkeleton Statistics:")
print(f"Basic Skeletonization pixels: {np.sum(skeleton_basic)}")
print(f"Thinning pixels: {np.sum(skeleton_thin)}")
print(f"Medial Axis pixels: {np.sum(skeleton_medial)}")
print(f"SN Graph nodes: {len(spheres_centers)}")
print(f"SN Graph edges: {len(edges)}")
return {
"basic": skeleton_basic,
"thin": skeleton_thin,
"medial": skeleton_medial,
"sn_graph": sn_graph_img,
"sn_graph_circles": sn_graph_img_circles,
"timings": timings,
}
# Load the image and invert it
img = data.horse()
img = img * -1 + 1
# Run the comparison using the image
results = compare_skeletonization(img)