or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analysis-utilities.mdcli-interface.mdcore-skeletonization.mdindex.mdpoint-connection.mdpost-processing.md

post-processing.mddocs/

0

# Post-processing and Quality Enhancement

1

2

Post-processing functions improve skeleton quality by removing artifacts, joining disconnected components, eliminating loops, and trimming small branches. These functions are essential for preparing skeletons for visualization and analysis.

3

4

## Imports

5

6

```python

7

from typing import Sequence

8

from osteoid import Skeleton

9

```

10

11

## Capabilities

12

13

### Comprehensive Post-processing

14

15

Applies a complete post-processing pipeline to clean and improve skeleton quality.

16

17

```python { .api }

18

def postprocess(

19

skeleton: Skeleton,

20

dust_threshold: float = 1500.0,

21

tick_threshold: float = 3000.0

22

) -> Skeleton:

23

"""

24

Apply comprehensive post-processing to improve skeleton quality.

25

26

The following steps are applied in order:

27

1. Remove disconnected components smaller than dust_threshold

28

2. Remove loops (skeletons should be trees)

29

3. Join close components within radius threshold

30

4. Remove small branches (ticks) smaller than tick_threshold

31

32

Parameters:

33

- skeleton (Skeleton): Input skeleton to process

34

- dust_threshold (float): Remove components smaller than this (physical units)

35

- tick_threshold (float): Remove branches smaller than this (physical units)

36

37

Returns:

38

Skeleton: Cleaned and improved skeleton

39

"""

40

```

41

42

#### Usage Example

43

44

```python

45

import kimimaro

46

47

# Generate initial skeletons

48

skeletons = kimimaro.skeletonize(labels, anisotropy=(16, 16, 40))

49

50

# Post-process each skeleton

51

cleaned_skeletons = {}

52

for label_id, skeleton in skeletons.items():

53

cleaned_skeletons[label_id] = kimimaro.postprocess(

54

skeleton,

55

dust_threshold=2000, # Remove components < 2000 nm

56

tick_threshold=5000 # Remove branches < 5000 nm

57

)

58

59

# The cleaned skeletons have:

60

# - No small disconnected pieces

61

# - No loops (proper tree structure)

62

# - Connected nearby components

63

# - No tiny branches/artifacts

64

```

65

66

### Component Joining

67

68

Connects nearby skeleton components by finding the closest vertices and linking them, useful for joining skeletons from adjacent image chunks or reconnecting artificially separated components.

69

70

```python { .api }

71

def join_close_components(

72

skeletons: Sequence[Skeleton],

73

radius: float = float('inf'),

74

restrict_by_radius: bool = False

75

) -> Skeleton:

76

"""

77

Join nearby skeleton components within specified distance.

78

79

Given a set of skeletons which may contain multiple connected components,

80

attempts to connect each component to the nearest other component via

81

the nearest two vertices. Repeats until no components remain or no

82

points closer than radius are available.

83

84

Parameters:

85

- skeletons (list or Skeleton): Input skeleton(s) to process

86

- radius (float): Maximum distance for joining components (default: infinity)

87

- restrict_by_radius (bool): If True, only join within boundary distance radius

88

89

Returns:

90

Skeleton: Single connected skeleton with components joined

91

"""

92

```

93

94

#### Usage Example

95

96

```python

97

import kimimaro

98

from osteoid import Skeleton

99

100

# Join multiple skeletons from the same neuron

101

skeleton_chunks = [skel1, skel2, skel3] # From adjacent image volumes

102

103

# Join all components without distance restriction

104

merged_skeleton = kimimaro.join_close_components(skeleton_chunks)

105

106

# Join only components within 1500 nm of each other

107

selective_merge = kimimaro.join_close_components(

108

skeleton_chunks,

109

radius=1500,

110

restrict_by_radius=True

111

)

112

113

# Join components of a single fragmented skeleton

114

fragmented_skeleton = skeletons[neuron_id]

115

connected_skeleton = kimimaro.join_close_components([fragmented_skeleton])

116

```

117

118

## Advanced Post-processing Workflow

119

120

For complex skeletonization tasks, you may want to apply post-processing steps individually:

121

122

```python

123

import kimimaro

124

125

# Get initial skeleton

126

skeleton = skeletons[target_neuron_id]

127

128

# Step 1: Remove small artifacts first

129

clean_skeleton = kimimaro.postprocess(

130

skeleton,

131

dust_threshold=1000, # Conservative dust removal

132

tick_threshold=0 # Skip tick removal for now

133

)

134

135

# Step 2: Join components from different image chunks

136

if len(skeleton_chunks) > 1:

137

joined_skeleton = kimimaro.join_close_components(

138

skeleton_chunks,

139

radius=2000, # 2 micron joining threshold

140

restrict_by_radius=True

141

)

142

else:

143

joined_skeleton = clean_skeleton

144

145

# Step 3: Final cleanup with aggressive tick removal

146

final_skeleton = kimimaro.postprocess(

147

joined_skeleton,

148

dust_threshold=2000, # More aggressive dust removal

149

tick_threshold=3000 # Remove small branches

150

)

151

152

# Step 4: Validate result

153

print(f"Original vertices: {len(skeleton.vertices)}")

154

print(f"Final vertices: {len(final_skeleton.vertices)}")

155

print(f"Connected components: {final_skeleton.n_components}")

156

```

157

158

## Quality Assessment

159

160

After post-processing, you can assess skeleton quality:

161

162

```python

163

# Check connectivity

164

n_components = skeleton.n_components

165

if n_components == 1:

166

print("Skeleton is fully connected")

167

else:

168

print(f"Skeleton has {n_components} disconnected components")

169

170

# Check for loops (should be 0 for proper tree structure)

171

n_cycles = len(skeleton.cycles())

172

if n_cycles == 0:

173

print("Skeleton is a proper tree")

174

else:

175

print(f"Warning: Skeleton has {n_cycles} loops")

176

177

# Measure total cable length

178

total_length = skeleton.cable_length()

179

print(f"Total cable length: {total_length:.1f} physical units")

180

181

# Check branch complexity

182

branch_points = len([v for v in skeleton.vertices if len(skeleton.edges[v]) > 2])

183

endpoints = len([v for v in skeleton.vertices if len(skeleton.edges[v]) == 1])

184

print(f"Branch points: {branch_points}, Endpoints: {endpoints}")

185

```

186

187

## Integration with Analysis Pipeline

188

189

Post-processing typically fits into a larger analysis workflow:

190

191

```python

192

import kimimaro

193

import numpy as np

194

195

# 1. Skeletonization

196

labels = np.load("segmentation.npy")

197

skeletons = kimimaro.skeletonize(

198

labels,

199

anisotropy=(16, 16, 40),

200

parallel=4,

201

progress=True

202

)

203

204

# 2. Post-processing

205

cleaned_skeletons = {}

206

for label_id, skeleton in skeletons.items():

207

cleaned_skeletons[label_id] = kimimaro.postprocess(

208

skeleton,

209

dust_threshold=1500,

210

tick_threshold=3000

211

)

212

213

# 3. Cross-sectional analysis

214

analyzed_skeletons = kimimaro.cross_sectional_area(

215

labels,

216

cleaned_skeletons,

217

anisotropy=(16, 16, 40),

218

smoothing_window=5

219

)

220

221

# 4. Export results

222

for label_id, skeleton in analyzed_skeletons.items():

223

with open(f"neuron_{label_id}.swc", "w") as f:

224

f.write(skeleton.to_swc())

225

```

226

227

## Common Post-processing Issues

228

229

### Over-aggressive Dust Removal

230

- **Problem**: Important small structures removed

231

- **Solution**: Use lower dust_threshold or inspect removed components

232

233

### Incomplete Component Joining

234

- **Problem**: Components remain disconnected despite proximity

235

- **Solution**: Check radius parameter, verify component spacing

236

237

### Loop Preservation

238

- **Problem**: Biological loops incorrectly removed

239

- **Solution**: Use custom post-processing instead of full `postprocess()`

240

241

### Branch Over-trimming

242

- **Problem**: Important dendrites removed as "ticks"

243

- **Solution**: Lower tick_threshold or use length-based filtering