medial op• Datenarten: voxel → table
• Aufruf: import fullseye as fs; fs.ledger.skeleton_graph3d(vol, distance=None, spacing=(1.0, 1.0, 1.0), min_branch_len=0.0) (die Implementierung direkt: import medial; medial.skeleton_graph3d(vol, distance=None, spacing=(1.0, 1.0, 1.0), min_branch_len=0.0); aus dem Register: ops3d.get("skeleton_graph3d"))
Setzt ein 3D-Skelett zu einem Graphen aus Knoten (Verzweigungs- und Endpunkte) und Ästen (Länge, Radius) zusammen.
skeleton_junctions3d / skeleton_endpoints3d / skeleton_branches3d geben nur "welche Voxel Knoten / Äste sind" als Maske zurück; sie liefern nicht, welcher Ast welchen Knoten mit welchem Knoten verbindet. Für einen Schaltkreis wird genau diese Verbindung gebraucht — in der Kabeltheorie der Neuronenmorphologie wird die axiale Leitfähigkeit eines Kompartiments durch Durchmesser und Länge bestimmt, und die Art der Verbindung der Kompartimente ist der Schaltkreis selbst. Dieser Op füllt diesen 1 Schritt.
Argumente:
vol: ein 3-D-Skelett (bool oder {0, 1}; die Ausgabe von skeletonize_vol). Ist der Inhalt
ein Klumpen (es gibt ein Inneres-Voxel, dessen 6-Nachbarschaft vollständig Vordergrund ist), wird wie bei den anderen Ops der Familie
intern zuerst skeletonize_vol angewendet.
distance: optional. Ein Distanztransformations-Volumen derselben Form (Ausgabe von
vol_distance_transform). Wird es übergeben, erhält jeder Knoten und jeder Ast einen Radius. Die Einheit folgt dem übergebenen Distanzfeld
— werden physische Einheiten gebraucht, vol_distance_transform(mask, spacing) übergeben.
spacing: (sz, sy, sx). Astlängen werden in realer Distanz gemessen (anisotrope EM-Voxel sind
die Standardannahme). Akzeptiert auch VolumeMeta.
min_branch_len: kappt Endäste (Sporne), die kürzer sind (Standard 0 = nicht kappen).
Die Einheit ist die reale Distanz von spacing. Gekappt wird ein Ast, bei dem "ein Ende ein Endpunkt (Grad 1) und das andere
Ende vom Grad 2 oder mehr" ist — ein Ast mit beiden Enden als Endpunkte wird nicht gekappt. Er
ist für sich eine ganze Zusammenhangskomponente (1 kurzes isoliertes Rohr), Kappen würde also die ganze Struktur
verschwinden lassen. Die Zahl der gekappten Äste wird in n_pruned_branches zurückgegeben.
Rückgabewert: dict (deklarierter out-Typ im Register = table).
• nodes: Knotentabelle. id / z,y,x (Schwerpunkt in Voxelindizes; für reale Koordinaten mit spacing multiplizieren) / kind / degree / n_voxels / radius (wenn distance übergeben wurde, das Maximum über die Voxel des Knotens = Inkreisradius) / component.
• edges: Asttabelle. u / v (Paar von Knoten-ids) / length (reale Distanz entlang des Skeletts, inklusive spacing) / radius_mean / radius_min / n_points (Anzahl der Voxel auf dem Pfad, inklusive der Knoten-Voxel an beiden Enden) / component.
• n_nodes / n_edges / n_components / n_cycles / n_pruned_branches / n_skeleton_voxels / spacing / has_radius.
Konventionen (sie bestimmen die Topologie):
• Ein Voxel mit Grad 2 in der 26-Nachbarschaft liegt mitten in einem Ast und wird kein Knoten. Grad 1 oder weniger ist ein Endpunkt (inklusive isolierter Voxel), 3 oder mehr eine Verzweigung.
• Eine Verzweigung ist nicht unbedingt 1 Voxel (in einem diskreten Skelett wird eine Verzweigung zu einem Klumpen aus mehreren Voxeln). Klumpen werden per 26-Zusammenhang gruppiert und als 1 Knoten gezählt, mit ihrem Schwerpunkt als Koordinate.
• Mehrere Zusammenhangskomponenten werden nie stillschweigend verbunden. Ihre Anzahl steht in n_components, und jeder Knoten und jeder Ast erhält eine component.
• Eine Komponente nur aus einer geschlossenen Schleife (ein Ring ohne auch nur 1 Voxel, das ein Knoten würde) erhält genau 1 Saatknoten am ersten Voxel dieser Komponente und wird zu 1 Ast als Selbstschleife. So gilt die Euler-Relation n_cycles = n_edges - n_nodes + n_components auch für Ringe (es wird kein Baum angenommen).
• kind richtet sich nach dem Grad nach dem Kappen: 0 = isolated / 1 = endpoint / 2 = chain (Saatknoten eines Rings oder ein Knoten, der durch Kappen so wurde) / 3 oder mehr = junction. Durch Kappen kann eine Verzweigung auf Grad 2 fallen, und dieser Knoten bleibt (als Kompartimentgrenze korrekt, aber beachten: Die obige Regel "Grad 2 wird kein Knoten" gilt vor dem Kappen).
• Grenzen Verzweigungsklumpen direkt aneinander (kein Pfad vom Grad 2 dazwischen), wird für dieses Paar 1 Ast erzeugt (Länge = das kürzeste benachbarte Voxelpaar).
Prüfung (alles fail-closed mit ValueError): kein 3-D / leeres Array / NaN, Inf / Werte, die weder bool noch {0,1} sind (Zwischenwerte als "wahrscheinlich Vordergrund" werden nicht stillschweigend gerundet) / kein Vordergrund / distance mit anderer Form, negativ oder nicht endlich / spacing nicht 3 positive endliche Werte / negatives min_branch_len. Braucht die Eingabe eine Ausdünnung und fehlt scikit-image, ImportError.
Hinweise (honest, alles gemessen):
• Die Länge ist die Summe eines Polygonzugs in der 26-Nachbarschaft, gekrümmte Äste fallen daher länger aus als die stetige Kurve. Ein geschlossenes Rohr mit Radius 14 Voxel (wahrer Umfang 87.96) ergibt 94.7 = +7.7 %.
• Dicke Eingaben schrumpfen an den Enden. Die Ausdünnung eines freien Endes stoppt vor dem Enddeckel. Gemessen an einem geraden Zylinder der Länge 35: Radius 1 und 2 ergeben 35.00 (kein Schrumpfen), Radius 3 ergibt 33, Radius 4 ergibt 31 — ab Radius 3 rückt jedes Ende um 1 bis 2 Voxel nach innen. Für den Vergleich von Längen mit dem Ground Truth ein 1 Voxel breites Skelett, das keine Ausdünnung durchläuft, übergeben (dann stimmen sie exakt überein: 30.0 für eine Gerade aus 31 Voxeln).
• An Verzweigungen verliert ein Ast den Teil, den der Verzweigungsklumpen (Grad 3 oder mehr, per 26-Zusammenhang gruppiert) verschluckt. Sporne, die kürzer als die Verzweigung sind, werden keine Äste, sondern gehen in n_voxels des Knotens auf und verschwinden (sie sind nicht einmal Kandidaten zum Kappen mit min_branch_len).
• Der Radius ist der Wert von distance selbst. An einem Endpunkt bestimmt ihn der Abstand zum Enddeckel, daher fällt er kleiner als der Rohrradius aus (Zylinder mit Radius 3: Endpunkt 2.0, Astmittel 3.07). Für die Astdicke radius_mean / radius_min verwenden.
• Rotation um 90 Grad: Mit einem 1 Voxel breiten Skelett als Eingabe sind die Graphen isomorph und die Längen stimmen exakt überein (9 Fälle gemessen). Bei einem dicken Klumpen stimmen Knoten- und Astzahl überein, aber die Längen weichen um bis zu 1.41 (= 1 diagonaler Schritt) ab — die Abweichung stammt nicht von diesem Op, sondern davon, dass der Ausdünnungshelfer (skimage Lee) unter Rotation nicht exakt dasselbe Skelett erzeugt.
• Katalog der Beispieldaten (Download-URLs / Lizenzen) — 2-D nutzt skimage.data (BSD/Public Domain) plus synthetische Bilder, 3-D nennt Download-URLs echter Datenquellen (Stanford, PDS, …).
• Herkunft und Literatur der Operatoren — die Quellen der Forschung/Verfahren, auf denen diese Operatorfamilie beruht.
• medial_topology — py -3.11 examples_3d/medial_topology.py
table als Eingabe)fuse_to_voxel · mesh_select_lod
medial)distance_ridge · skeletonize_vol · medial_axis_points · topology_signature · medial_match · skeleton_junctions3d · skeleton_endpoints3d · skeleton_prune3d
*Provenance: medial.py — 3D Operator-Registry. Diese Notiz wird von tools/opdocs.py md erzeugt (nicht von Hand bearbeiten).*
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.