or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

statistics.mddocs/

0

# Statistics and Analysis

1

2

Comprehensive analysis functions for extracting detailed information about connected components, including geometric properties, component extraction, and efficient iteration over individual regions.

3

4

## Capabilities

5

6

### Component Statistics

7

8

Compute detailed statistics about each connected component including voxel counts, bounding boxes, and centroids with options for different output formats.

9

10

```python { .api }

11

def statistics(

12

out_labels: NDArray[Any],

13

no_slice_conversion: bool = False,

14

) -> Union[StatisticsDict, StatisticsSlicesDict]:

15

"""

16

Compute basic statistics on the regions in the image.

17

18

Parameters:

19

- out_labels: A numpy array of connected component labels

20

- no_slice_conversion: If True, return bounding_boxes as numpy array instead of slice tuples

21

22

Returns:

23

- StatisticsDict: With bounding_boxes as list of slice tuples

24

- StatisticsSlicesDict: With bounding_boxes as numpy array (if no_slice_conversion=True)

25

26

Dictionary structure:

27

{

28

'voxel_counts': NDArray[np.uint32], # Index is label, value is voxel count

29

'bounding_boxes': NDArray[np.uint16] | list[tuple[slice, slice, slice]],

30

'centroids': NDArray[np.float64], # Shape (N+1, 3) for x,y,z coordinates

31

}

32

"""

33

```

34

35

Usage examples:

36

37

```python

38

import cc3d

39

import numpy as np

40

41

# Generate connected components

42

labels_in = np.random.randint(0, 10, (100, 100, 100))

43

labels_out = cc3d.connected_components(labels_in)

44

45

# Get statistics (default format with slice tuples)

46

stats = cc3d.statistics(labels_out)

47

48

# Access component information

49

num_components = len(stats['voxel_counts']) - 1 # Subtract 1 for background

50

print(f"Found {num_components} components")

51

52

# Get size of each component (skip background at index 0)

53

component_sizes = stats['voxel_counts'][1:]

54

print(f"Sizes: {component_sizes}")

55

56

# Get centroid coordinates

57

centroids = stats['centroids'][1:] # Skip background

58

for i, centroid in enumerate(centroids):

59

x, y, z = centroid

60

print(f"Component {i+1} centroid: ({x:.2f}, {y:.2f}, {z:.2f})")

61

62

# Extract regions using bounding boxes

63

for label in range(1, len(stats['voxel_counts'])):

64

bbox_slices = stats['bounding_boxes'][label]

65

region = labels_out[bbox_slices]

66

component_mask = (region == label)

67

print(f"Component {label} bounding box shape: {region.shape}")

68

69

# Alternative format with numpy arrays for bounding boxes

70

stats_array = cc3d.statistics(labels_out, no_slice_conversion=True)

71

# bounding_boxes is now NDArray[np.uint16] with shape (N+1, 6)

72

# Structure: [xmin, xmax, ymin, ymax, zmin, zmax] for each component

73

bbox_array = stats_array['bounding_boxes']

74

for label in range(1, len(stats_array['voxel_counts'])):

75

xmin, xmax, ymin, ymax, zmin, zmax = bbox_array[label]

76

print(f"Component {label}: x=[{xmin}:{xmax}], y=[{ymin}:{ymax}], z=[{zmin}:{zmax}]")

77

```

78

79

### Component Iteration

80

81

Efficiently iterate through individual connected components with options for binary or labeled output and memory-optimized processing.

82

83

```python { .api }

84

def each(

85

labels: NDArray[UnsignedIntegerT],

86

binary: bool = False,

87

in_place: bool = False,

88

) -> Iterator[tuple[int, NDArray[UnsignedIntegerT]]]:

89

"""

90

Returns an iterator that extracts each label from a dense labeling.

91

92

Parameters:

93

- labels: Connected component labeled array

94

- binary: Create binary images (True/False) instead of using original label values

95

- in_place: Much faster but resulting images are read-only

96

97

Returns:

98

- Iterator yielding (label, image) tuples

99

100

Yields:

101

- label: int, the component label

102

- image: NDArray, the extracted component (binary or labeled based on 'binary' parameter)

103

"""

104

```

105

106

Usage examples:

107

108

```python

109

import cc3d

110

import numpy as np

111

112

# Create test data

113

labels_in = np.random.randint(0, 5, (50, 50, 50))

114

labels_out = cc3d.connected_components(labels_in)

115

116

# Iterate through components (memory-efficient, read-only)

117

for label, component_image in cc3d.each(labels_out, in_place=True):

118

component_volume = np.sum(component_image > 0)

119

print(f"Component {label}: {component_volume} voxels")

120

121

# Process the component (read-only access)

122

max_value = np.max(component_image)

123

print(f" Max value: {max_value}")

124

125

# Create binary masks for each component

126

for label, binary_mask in cc3d.each(labels_out, binary=True, in_place=False):

127

# binary_mask contains only True/False values

128

print(f"Component {label} binary mask sum: {np.sum(binary_mask)}")

129

130

# Since in_place=False, we can modify the mask

131

binary_mask[binary_mask] = 255 # Convert to grayscale

132

133

# Create labeled images for each component (mutable copies)

134

for label, labeled_image in cc3d.each(labels_out, binary=False, in_place=False):

135

# labeled_image contains the original label values for this component

136

# Can be modified since in_place=False

137

labeled_image[labeled_image == label] = 1000 # Relabel component

138

139

# Compare performance approaches

140

# Fast but read-only (recommended for analysis)

141

for label, img in cc3d.each(labels_out, in_place=True):

142

analyze_component(img) # Read-only analysis function

143

144

# Slower but mutable (for modification)

145

for label, img in cc3d.each(labels_out, in_place=False):

146

modify_component(img) # Function that modifies the component

147

```

148

149

### Low-Level Component Access

150

151

Direct access to component location data for advanced processing and custom rendering operations.

152

153

```python { .api }

154

def runs(labels: NDArray[np.unsignedinteger]) -> dict[int, list[tuple[int, int]]]:

155

"""

156

Returns a dictionary describing where each label is located.

157

158

Parameters:

159

- labels: Connected component labeled array

160

161

Returns:

162

- dict mapping label -> list of (start, end) run positions

163

"""

164

165

def draw(

166

label: ArrayLike,

167

runs: list[tuple[int, int]],

168

image: NDArray[UnsignedIntegerT],

169

) -> NDArray[UnsignedIntegerT]:

170

"""

171

Draws label onto the provided image according to runs.

172

173

Parameters:

174

- label: The label value to draw

175

- runs: List of (start, end) positions from runs() function

176

- image: Target image to draw into

177

178

Returns:

179

- Modified image with label drawn

180

"""

181

```

182

183

Usage examples:

184

185

```python

186

import cc3d

187

import numpy as np

188

189

# Get component locations as run-length encoded data

190

labels_out = cc3d.connected_components(input_image)

191

component_runs = cc3d.runs(labels_out)

192

193

# Examine structure

194

for label, run_list in component_runs.items():

195

if label == 0: # Skip background

196

continue

197

print(f"Component {label} has {len(run_list)} runs")

198

print(f" First run: positions {run_list[0][0]} to {run_list[0][1]}")

199

200

# Reconstruct individual components using runs

201

empty_image = np.zeros_like(labels_out)

202

for component_label in range(1, 6): # Components 1-5

203

if component_label in component_runs:

204

cc3d.draw(component_label, component_runs[component_label], empty_image)

205

206

# Create custom visualization by selectively drawing components

207

visualization = np.zeros_like(labels_out)

208

large_components = [label for label, runs in component_runs.items()

209

if len(runs) > 100] # Components with many runs

210

for label in large_components:

211

cc3d.draw(255, component_runs[label], visualization) # Draw in white

212

```