or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-ccl.mdfiltering.mdgraphs.mdindex.mdstatistics.md

graphs.mddocs/

0

# Graph Analysis

1

2

Advanced graph-based analysis functions for extracting connectivity relationships between regions and voxels, including contact surface area calculations, region adjacency graphs, and voxel-level connectivity patterns for network analysis and advanced image processing.

3

4

## Capabilities

5

6

### Contact Analysis

7

8

Calculate contact surface areas and adjacency relationships between connected components with support for anisotropic voxel spacing and different connectivity patterns.

9

10

```python { .api }

11

def contacts(

12

labels: NDArray[Any],

13

connectivity: Literal[4, 6, 8, 18, 26] = 26,

14

surface_area: bool = True,

15

anisotropy: tuple[Union[int, float], Union[int, float], Union[int, float]] = (1, 1, 1),

16

) -> dict[tuple[int, int], Union[int, float]]:

17

"""

18

Get the N-connected region adjacency graph and contact area between regions.

19

20

Parameters:

21

- labels: 3D numpy array of integer segmentation labels

22

- connectivity: 6, 18, or 26 (default) for 3D; 4 or 8 for 2D

23

- surface_area: Return contact surface area (True) or simple voxel count (False)

24

- anisotropy: Weights for x, y, and z dimensions for computing surface area

25

26

Returns:

27

- dict: Mapping {(label_1, label_2): contact_value, ...}

28

contact_value is surface area (float) or voxel count (int)

29

"""

30

```

31

32

Usage examples:

33

34

```python

35

import cc3d

36

import numpy as np

37

38

# Create test segmentation

39

labels = np.zeros((100, 100, 100), dtype=np.int32)

40

labels[20:40, 20:40, 20:40] = 1 # Component 1

41

labels[35:55, 35:55, 35:55] = 2 # Component 2 (overlapping)

42

labels[60:80, 60:80, 60:80] = 3 # Component 3 (separate)

43

44

# Get contact surface areas between all pairs

45

surface_contacts = cc3d.contacts(labels, connectivity=26, surface_area=True)

46

47

# Print all contacts

48

for (label1, label2), area in surface_contacts.items():

49

print(f"Contact between regions {label1} and {label2}: {area:.2f} surface area")

50

51

# Get simple voxel contact counts instead of surface area

52

voxel_contacts = cc3d.contacts(labels, connectivity=6, surface_area=False)

53

for (label1, label2), count in voxel_contacts.items():

54

print(f"Regions {label1} and {label2} share {count} face-adjacent voxels")

55

56

# Handle anisotropic voxel spacing (e.g., microscopy data)

57

# Voxel dimensions: 0.5μm x 0.5μm x 2μm (z is thicker)

58

anisotropic_contacts = cc3d.contacts(

59

labels,

60

connectivity=26,

61

surface_area=True,

62

anisotropy=(0.5, 0.5, 2.0)

63

)

64

65

# Different connectivity patterns

66

face_contacts = cc3d.contacts(labels, connectivity=6) # Face-adjacent only

67

edge_contacts = cc3d.contacts(labels, connectivity=18) # Faces + edges

68

corner_contacts = cc3d.contacts(labels, connectivity=26) # Faces + edges + corners

69

70

# Check if specific regions are adjacent

71

if (1, 2) in surface_contacts:

72

contact_area = surface_contacts[(1, 2)]

73

print(f"Regions 1 and 2 are adjacent with area {contact_area}")

74

else:

75

print("Regions 1 and 2 are not adjacent")

76

77

# Find all neighbors of a specific region

78

region_of_interest = 2

79

neighbors = []

80

for (label1, label2) in surface_contacts.keys():

81

if label1 == region_of_interest:

82

neighbors.append(label2)

83

elif label2 == region_of_interest:

84

neighbors.append(label1)

85

print(f"Region {region_of_interest} neighbors: {neighbors}")

86

```

87

88

### Region Adjacency Graph

89

90

Extract the topological adjacency graph between regions as a set of edges, providing a simplified view of spatial relationships.

91

92

```python { .api }

93

def region_graph(

94

labels: NDArray[np.integer],

95

connectivity: Literal[4, 6, 8, 18, 26] = 26,

96

) -> set[tuple[int, int]]:

97

"""

98

Get the N-connected region adjacency graph of a 3D image.

99

100

Parameters:

101

- labels: 3D numpy array of integer segmentation labels

102

- connectivity: 6, 18, or 26 (default) for 3D; 4 or 8 for 2D

103

104

Returns:

105

- set: Set of edges between labels as (label1, label2) tuples

106

"""

107

```

108

109

Usage examples:

110

111

```python

112

import cc3d

113

import numpy as np

114

115

# Create complex segmentation

116

labels = create_complex_segmentation() # Your segmentation function

117

118

# Get adjacency graph

119

edges = cc3d.region_graph(labels, connectivity=26)

120

121

# Print graph structure

122

print(f"Found {len(edges)} adjacency relationships")

123

for label1, label2 in sorted(edges):

124

print(f"Regions {label1} and {label2} are adjacent")

125

126

# Convert to other graph representations

127

# NetworkX graph

128

import networkx as nx

129

G = nx.Graph()

130

G.add_edges_from(edges)

131

132

# Adjacency list

133

from collections import defaultdict

134

adjacency_list = defaultdict(list)

135

for label1, label2 in edges:

136

adjacency_list[label1].append(label2)

137

adjacency_list[label2].append(label1)

138

139

# Find connected components in the adjacency graph

140

connected_groups = list(nx.connected_components(G))

141

print(f"Found {len(connected_groups)} connected groups of regions")

142

143

# Find regions with most neighbors

144

degree_centrality = nx.degree_centrality(G)

145

most_connected = max(degree_centrality.items(), key=lambda x: x[1])

146

print(f"Most connected region: {most_connected[0]} with {most_connected[1]:.2f} centrality")

147

148

# Compare different connectivity patterns

149

edges_6 = cc3d.region_graph(labels, connectivity=6)

150

edges_26 = cc3d.region_graph(labels, connectivity=26)

151

print(f"6-connected: {len(edges_6)} edges")

152

print(f"26-connected: {len(edges_26)} edges")

153

```

154

155

### Voxel Connectivity Graph

156

157

Generate detailed voxel-level connectivity information as bitfields, enabling fine-grained analysis of spatial relationships and custom connectivity processing.

158

159

```python { .api }

160

def voxel_connectivity_graph(

161

data: NDArray[IntegerT],

162

connectivity: Literal[4, 6, 8, 18, 26] = 26,

163

) -> NDArray[IntegerT]:

164

"""

165

Extracts the voxel connectivity graph from a multi-label image.

166

167

A voxel is considered connected if the adjacent voxel has the same label.

168

The output is a bitfield representing allowed directions for transit between voxels.

169

170

For 2D connectivity (4,8): returns uint8 with bits 1-8 encoding directions

171

For 3D connectivity (6): returns uint8 with bits 1-6 encoding face directions

172

For 3D connectivity (18,26): returns uint32 with bits 1-26 encoding all directions

173

174

Bit encoding for 3D (26-connected):

175

Bits 1-6: faces (+x,-x,+y,-y,+z,-z)

176

Bits 7-18: edges

177

Bits 19-26: corners

178

Bits 27-32: unused (zero)

179

180

Parameters:

181

- data: Input labeled array

182

- connectivity: Connectivity pattern to analyze

183

184

Returns:

185

- NDArray: Same shape as input, with connectivity bitfields

186

"""

187

```

188

189

Usage examples:

190

191

```python

192

import cc3d

193

import numpy as np

194

195

# Create test segmentation

196

labels = np.random.randint(0, 5, (50, 50, 50), dtype=np.int32)

197

198

# Generate voxel connectivity graph

199

vcg = cc3d.voxel_connectivity_graph(labels, connectivity=26)

200

201

# Analyze connectivity patterns

202

print(f"VCG dtype: {vcg.dtype}") # uint32 for 26-connected

203

print(f"VCG shape: {vcg.shape}") # Same as input

204

205

# Check connectivity at specific voxel

206

x, y, z = 25, 25, 25

207

connectivity_bits = vcg[x, y, z]

208

print(f"Voxel ({x},{y},{z}) connectivity: {connectivity_bits:032b}")

209

210

# Count total connections per voxel

211

total_connections = np.zeros_like(vcg, dtype=np.int32)

212

for bit in range(26): # For 26-connected

213

bit_mask = 1 << bit

214

total_connections += ((vcg & bit_mask) > 0).astype(np.int32)

215

216

# Find highly connected voxels

217

highly_connected = np.where(total_connections > 20)

218

print(f"Found {len(highly_connected[0])} highly connected voxels")

219

220

# Different connectivity patterns

221

vcg_6 = cc3d.voxel_connectivity_graph(labels, connectivity=6) # uint8

222

vcg_18 = cc3d.voxel_connectivity_graph(labels, connectivity=18) # uint32

223

vcg_26 = cc3d.voxel_connectivity_graph(labels, connectivity=26) # uint32

224

225

# Analyze 2D connectivity (using 2D slice)

226

labels_2d = labels[:, :, 25]

227

vcg_2d_4 = cc3d.voxel_connectivity_graph(labels_2d, connectivity=4) # uint8

228

vcg_2d_8 = cc3d.voxel_connectivity_graph(labels_2d, connectivity=8) # uint8

229

230

# Extract specific directional connections

231

# For 3D 26-connected, bit positions:

232

# 1: +x, 2: -x, 3: +y, 4: -y, 5: +z, 6: -z

233

plus_x_connections = (vcg & 1) > 0 # Can move in +x direction

234

minus_z_connections = (vcg & (1 << 5)) > 0 # Can move in -z direction

235

236

print(f"Voxels with +x connectivity: {np.sum(plus_x_connections)}")

237

print(f"Voxels with -z connectivity: {np.sum(minus_z_connections)}")

238

```

239

240

### Connectivity Graph Coloring

241

242

Convert voxel connectivity graphs back to labeled images, useful for reconstructing components from connectivity information or creating alternative labelings.

243

244

```python { .api }

245

def color_connectivity_graph(

246

vcg: NDArray[VcgT],

247

connectivity: Literal[4, 6, 8, 18, 26] = 26,

248

return_N: bool = False,

249

) -> Union[NDArray[VcgT], tuple[NDArray[VcgT], int]]:

250

"""

251

Color the connectivity graph back into connected components.

252

253

Given a voxel connectivity graph, this function treats it as an undirected

254

graph and returns connected components based on the connectivity patterns

255

encoded in the bitfield.

256

257

Parameters:

258

- vcg: Voxel connectivity graph from voxel_connectivity_graph()

259

- connectivity: Must match the connectivity used to generate vcg

260

- return_N: If True, also return the number of components

261

262

Returns:

263

- NDArray: Labeled image with connected components

264

- tuple[NDArray, int]: If return_N=True, includes component count

265

"""

266

```

267

268

Usage examples:

269

270

```python

271

import cc3d

272

import numpy as np

273

274

# Start with original labels

275

original_labels = np.random.randint(0, 10, (100, 100, 100), dtype=np.int32)

276

277

# Extract voxel connectivity graph

278

vcg = cc3d.voxel_connectivity_graph(original_labels, connectivity=26)

279

280

# Reconstruct connected components from connectivity graph

281

reconstructed_labels = cc3d.color_connectivity_graph(vcg, connectivity=26)

282

283

# Get component count

284

reconstructed_labels, N = cc3d.color_connectivity_graph(

285

vcg, connectivity=26, return_N=True

286

)

287

print(f"Reconstructed {N} components")

288

289

# Compare original vs reconstructed

290

print(f"Original max label: {np.max(original_labels)}")

291

print(f"Reconstructed max label: {np.max(reconstructed_labels)}")

292

293

# Note: Labels will be different but connectivity should be preserved

294

# Verify connectivity is preserved by checking region adjacency

295

original_graph = cc3d.region_graph(original_labels, connectivity=26)

296

reconstructed_graph = cc3d.region_graph(reconstructed_labels, connectivity=26)

297

298

print(f"Original graph edges: {len(original_graph)}")

299

print(f"Reconstructed graph edges: {len(reconstructed_graph)}")

300

301

# Modify connectivity graph before reconstruction

302

modified_vcg = vcg.copy()

303

304

# Remove some connections (set bits to 0)

305

# For example, remove all +x connections (bit 0)

306

modified_vcg = modified_vcg & ~1 # Clear bit 0

307

308

# Reconstruct with modified connectivity

309

modified_labels = cc3d.color_connectivity_graph(modified_vcg, connectivity=26)

310

311

# This should result in more components since we removed connections

312

original_N = len(np.unique(reconstructed_labels)) - 1 # Subtract background

313

modified_N = len(np.unique(modified_labels)) - 1

314

print(f"Original: {original_N} components")

315

print(f"After removing +x connections: {modified_N} components")

316

317

# Use with different connectivity patterns

318

vcg_6 = cc3d.voxel_connectivity_graph(original_labels, connectivity=6)

319

labels_6 = cc3d.color_connectivity_graph(vcg_6, connectivity=6)

320

321

# 2D example

322

labels_2d = original_labels[:, :, 50]

323

vcg_2d = cc3d.voxel_connectivity_graph(labels_2d, connectivity=8)

324

reconstructed_2d = cc3d.color_connectivity_graph(vcg_2d, connectivity=8)

325

```

326

327

## Advanced Graph Analysis Patterns

328

329

### Combining Graph Functions

330

331

```python

332

# Complete region analysis pipeline

333

labels = your_segmentation_image()

334

335

# Get adjacency relationships

336

edges = cc3d.region_graph(labels)

337

contacts = cc3d.contacts(labels, surface_area=True)

338

339

# Analyze voxel-level connectivity

340

vcg = cc3d.voxel_connectivity_graph(labels)

341

342

# Create connectivity-based alternative labeling

343

alternative_labels = cc3d.color_connectivity_graph(vcg)

344

345

# Compare region structures

346

print(f"Original regions: {len(np.unique(labels))}")

347

print(f"Alternative regions: {len(np.unique(alternative_labels))}")

348

```

349

350

### Network Analysis Integration

351

352

```python

353

# Convert to NetworkX for advanced analysis

354

import networkx as nx

355

356

contacts = cc3d.contacts(labels, surface_area=True)

357

G = nx.Graph()

358

for (u, v), weight in contacts.items():

359

G.add_edge(u, v, weight=weight)

360

361

# Network metrics

362

centrality = nx.betweenness_centrality(G, weight='weight')

363

clustering = nx.clustering(G, weight='weight')

364

communities = nx.community.greedy_modularity_communities(G)

365

366

print(f"Found {len(communities)} communities in the region graph")

367

```