skeleton_graph3d — 3D medial op

• 資料種類:voxel → table

• 呼叫: import fullseye as fs; fs.ledger.skeleton_graph3d(vol, distance=None, spacing=(1.0, 1.0, 1.0), min_branch_len=0.0)(要直接呼叫實作,import medial; medial.skeleton_graph3d(vol, distance=None, spacing=(1.0, 1.0, 1.0), min_branch_len=0.0);從台帳取用則 ops3d.get("skeleton_graph3d"))

用法

把 3D 骨架組裝成由節點(連接點、端點)和分支(長度、半徑)構成的圖。

skeleton_junctions3d / skeleton_endpoints3d / skeleton_branches3d 只以 遮罩 回傳「哪些 voxel 是節點/分支」,並不回傳 哪條分支連接哪個節點與哪個節點。要構成電路就需要這種連接 —— 在神經形態的電纜理論中,區室的軸向電導由 直徑與長度 決定,區室之間的 連接方式 本身就是電路。本 op 填補這 1 個環節。

引數:

vol: 3-D 骨架(bool 或 {0, 1}。skeletonize_vol 的輸出)。若內容

是塊(存在 6 鄰域全為前景的 interior voxel),則與同族的其他 op

一樣,在內部先套用 skeletonize_vol。

distance: 選用。同形狀的距離轉換體(vol_distance_transform 的

輸出)。傳入後各節點、各分支會附帶半徑。單位遵循傳入的距離場

—— 需要物理單位時傳 vol_distance_transform(mask, spacing)。

spacing: (sz, sy, sx)。以 實際距離 量測分支長度(預設設想 EM 的非等向

體素)。也接受 VolumeMeta。

min_branch_len: 剪掉短於此值的 末端分支(毛刺)(預設 0 = 不剪)。

單位是 spacing 下的實際距離。被剪的是「一端為端點(度 1)、另

一端度為 2 以上」的分支 —— 兩端都是端點 的分支不剪。這種分支

本身就是 1 個連通元件(短的孤立管),剪掉會讓整個結構

消失。剪掉的數量回傳到 n_pruned_branches。

回傳值: dict(台帳宣告的 out 型別 = table)。

• nodes: 節點表。id / z,y,x(以 voxel 索引表示的質心。實際座標需乘以 spacing)/ kind / degree / n_voxels / radius (傳入 distance 時,該節點各 voxel 上的最大值 = 內切半徑)/ component。

• edges: 分支表。u / v(節點 id 對)/ length(沿骨架的實際距離,含 spacing)/ radius_mean / radius_min / n_points (路徑上的 voxel 數,含兩端節點 voxel)/ component。

• n_nodes / n_edges / n_components / n_cycles / n_pruned_branches / n_skeleton_voxels / spacing / has_radius。

約定(這裡決定拓撲):

• 26 鄰域度為 2 的 voxel 是分支的中途,不作為節點。度 1 以下 為端點(含孤立 voxel),3 以上 為接合。

• 接合 voxel 不一定只有 1 個(離散骨架中分支會成為數個 voxel 的塊)。依 26 連通歸併成塊並計為 1 個節點,座標為其質心。

• 有多個連通元件時不會擅自連接。 在 n_components 中回傳個數, 並給各節點、各分支加上 component。

• 只有閉環的元件(沒有 1 個會成為節點的 voxel 的環),只在該元件的首個 voxel 上立 1 個種子節點,形成 1 條自環分支。這樣 尤拉關係 n_cycles = n_edges - n_nodes + n_components 對環也成立(不假定為樹)。

• kind 由 剪枝後的 度決定: 0 = isolated / 1 = endpoint / 2 = chain(環的種子節點,或剪枝後變成這樣的節點)/ 3 以上 = junction。剪枝可能使接合降為度 2,這種節點會保留(作為區室邊界是正確的,但請注意上面「度 2 不作為節點」的約定說的是 剪枝前 的情況)。

• 接合塊之間直接相鄰(中間沒有度 2 的路徑)時,為該對產生 1 條 分支(長度 = 相鄰 voxel 對的最短值)。

檢驗(全部以 ValueError fail-closed): 不是 3-D/空陣列/NaN、Inf/ 既不是 bool 也不是 {0,1} 的值(不會把中間值的「大概是前景」擅自取整)/ 前景為零/distance 形狀不同、為負、非有限/spacing 不是 3 個正的有限值/min_branch_len 為負。需要細化的輸入在沒有 scikit-image 時為 ImportError。

注意(honest,均為實測):

• 長度是 26 鄰域折線 之和,因此彎曲的分支會比連續曲線長。半徑 14 voxel 的閉合管(真實周長 87.96)得到 94.7 = +7.7 %。

• 粗的輸入端部會收縮。 自由端的細化會停在端蓋之前。在長 35 的

直圓柱上實測: 半徑 1、2 為 35.00(零收縮),半徑 3 為 33,

半徑 4 為 31 —— 半徑達到 3 以上時每端向內偏移 1~2 voxel。

要與真值比較長度,請傳入 不經過細化的 1 voxel 寬骨架

(此時嚴格一致: 31 voxel 的直線得到 30.0)。

• 在分支處,分支會縮短被接合塊(度 3 以上依 26 連通歸併而成)吞掉的部分。比接合短的毛刺不會成為分支,而是計入節點的 n_voxels 而消失(甚至不會成為 min_branch_len 的剪枝對象)。

• 半徑就是 distance 的值本身。端點由到端蓋的距離決定,因此 會小於管的半徑(半徑 3 的圓柱: 端點 2.0,分支平均 3.07)。看分支粗細時請用 radius_mean / radius_min。

• 90 度旋轉: 輸入 1 voxel 寬的骨架時,圖同構且長度也嚴格一致(實測 9 種)。傳入粗塊時節點數、分支數一致,但長度最多相差 1.41(= 對角 1 步)—— 產生偏差的不是本 op,而是 細化輔助函式(skimage Lee)在旋轉下並不嚴格產生相同的骨架。

參考(範例資料、文獻)

• 範例資料目錄(下載 URL / 授權) —— 2-D 用 skimage.data(BSD/公有領域)加合成圖,3-D 給出真實資料源(Stanford/PDS 等)的下載 URL。

• 運算子來歷與參考文獻 —— 該運算子族所依據的研究/方法出處。

可執行的範例(實際呼叫該運算子並已驗證的樣例)

• medial_topology — py -3.11 examples_3d/medial_topology.py

型別可銜接的下一個運算子(可接受 table 作為輸入)

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 運算子登記表。本條目由 tools/opdocs.py md 自動產生(請勿手動編輯)。*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.