33from typing import TYPE_CHECKING
44
55import numpy as np
6- from skan import summarize
6+ from skan import Skeleton
77
88from vesskel .config import ExtractionConfig
9- from vesskel .features import (
10- build_vessel_graph ,
11- compute_tortuosity ,
12- extract_vessel_features ,
13- )
9+ from vesskel .features import compute_tortuosity
1410
1511if TYPE_CHECKING :
1612 from napari .types import LayerDataTuple
1915def extract_skeleton_layers (
2016 skeleton : np .ndarray ,
2117 base_name : str ,
18+ graph : Skeleton ,
19+ branch_data ,
2220 config : ExtractionConfig | None = None ,
2321 features : dict [str , float ] | None = None ,
2422) -> list ["napari.types.LayerDataTuple" ]:
@@ -30,19 +28,22 @@ def extract_skeleton_layers(
3028 Binary 2D or 3D skeleton array.
3129 base_name : str
3230 Base name for layer naming.
31+ graph : Skeleton
32+ Pre-built skan Skeleton graph (e.g. from `build_vessel_graph`).
33+ branch_data : DataFrame
34+ Pre-computed branch summary (e.g. from `skan.summarize(graph, ...)`).
3335 config : ExtractionConfig, optional
3436 Configuration for what to extract. Defaults to all except fractal_dimension.
3537 features : dict, optional
36- Pre-computed feature dictionary to avoid recomputation when caller
37- already has it (e.g. from extract_vessel_features).
38+ Pre-computed summary feature dictionary (e.g. from `extract_vessel_features`).
3839 """
3940 if config is None :
4041 config = ExtractionConfig ()
4142
4243 layers = []
4344
4445 if config .branches :
45- branch_layer = _extract_branch_features_layer (skeleton , base_name )
46+ branch_layer = _extract_branch_features_layer (base_name , graph , branch_data )
4647 if branch_layer is not None :
4748 layers .append (branch_layer )
4849
@@ -54,7 +55,6 @@ def extract_skeleton_layers(
5455 summary_layer = _extract_summary_features_layer (
5556 skeleton ,
5657 base_name ,
57- include_fractal = config .fractal_dimension ,
5858 features = features ,
5959 )
6060 layers .append (summary_layer )
@@ -63,33 +63,40 @@ def extract_skeleton_layers(
6363
6464
6565def _extract_branch_features_layer (
66- skeleton : np .ndarray ,
6766 base_name : str ,
67+ graph : Skeleton ,
68+ branch_data ,
6869) -> "napari.types.LayerDataTuple | None" :
6970 """Extract branch features and generate paths layer.
7071
71- Returns None if skeleton has no branches.
72+ Parameters
73+ ----------
74+ base_name : str
75+ Base name used for layer naming.
76+ graph : Skeleton
77+ Pre-built skan Skeleton graph.
78+ branch_data : DataFrame
79+ Pre-computed branch summary from `skan.summarize`.
80+
81+ Returns
82+ -------
83+ LayerDataTuple or None
84+ Napari shapes layer for branch paths, or None if skeleton has no branches.
7285 """
73- graph = build_vessel_graph (skeleton )
74- branch_data = summarize (graph , separator = "-" )
75-
7686 if branch_data .empty :
7787 return None
7888
7989 branch_data = branch_data .reset_index (drop = True ).copy ()
8090 branch_data ["branch_id" ] = np .arange (len (branch_data ), dtype = np .int64 )
8191
82- # Compute tortuosity
8392 euclidean = branch_data ["euclidean-distance" ].to_numpy (dtype = float )
8493 branch_len = branch_data ["branch-distance" ].to_numpy (dtype = float )
8594 tortuosity = compute_tortuosity (branch_len , euclidean )
8695 tortuosity = np .nan_to_num (tortuosity , nan = 1.0 )
8796 branch_data ["tortuosity" ] = tortuosity
8897
89- # Get branch path coordinates
9098 path_data = [graph .path_coordinates (i ) for i in range (len (branch_data ))]
9199
92- # Determine if tortuosity varies significantly
93100 finite_tortuosity = tortuosity [np .isfinite (tortuosity )]
94101 varied_tortuosity = finite_tortuosity .size > 0 and float (
95102 np .min (finite_tortuosity )
@@ -120,11 +127,9 @@ def _extract_branch_text_layer(
120127 branch_layer : "napari.types.LayerDataTuple" ,
121128 base_name : str ,
122129) -> "napari.types.LayerDataTuple" :
123- """Create text labels for branches."""
124130 path_data = branch_layer [0 ]
125131 branch_data = branch_layer [1 ]["properties" ]
126132
127- # Compute label positions as mean of each path
128133 label_points = []
129134 for coords in path_data :
130135 if len (coords ) == 0 :
@@ -154,24 +159,22 @@ def _extract_branch_text_layer(
154159def _extract_summary_features_layer (
155160 skeleton : np .ndarray ,
156161 base_name : str ,
157- include_fractal : bool = False ,
158- features : dict [str , float ] | None = None ,
162+ features : dict [str , float ],
159163) -> "napari.types.LayerDataTuple" :
160- """Extract global skeleton features and create summary point layer .
164+ """Create a summary point layer displaying global skeleton features .
161165
162166 Parameters
163167 ----------
164- features : dict, optional
165- Pre-computed feature dictionary. If provided, skips computation.
168+ skeleton : ndarray
169+ Binary 2D or 3D skeleton array. Used to position the summary label.
170+ base_name : str
171+ Base name for layer naming.
172+ features : dict[str, float]
173+ Pre-computed summary feature dictionary
174+ (e.g. from `extract_vessel_features`).
166175 """
167- if features is None :
168- features = extract_vessel_features (
169- skeleton ,
170- include_fractal = include_fractal ,
171- )
172176 meta_features = {k : [v ] for k , v in features .items ()}
173177
174- # Find center of foreground
175178 fg = np .argwhere (skeleton > 0 )
176179 if fg .size :
177180 center = fg .mean (axis = 0 , dtype = float )
0 commit comments