or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

c-extensions.mdcontext-analysis.mdcoordinate-utils.mdindex.mdresampling.md

context-analysis.mddocs/

0

# Context Analysis

1

2

Functions for analyzing and decoding context images to determine which input images contributed to specific output pixels. The context system uses bit-field encoding to efficiently track image contributions across large datasets.

3

4

```python { .api }

5

from typing import Union, List

6

import numpy as np

7

```

8

9

## Capabilities

10

11

### Context Decoding

12

13

Decode context bit fields to determine which input images contributed to specific output pixels.

14

15

```python { .api }

16

def decode_context(

17

context: np.ndarray,

18

x: Union[int, List[int], np.ndarray],

19

y: Union[int, List[int], np.ndarray]

20

) -> List[np.ndarray]:

21

"""

22

Get 0-based indices of input images that contributed to output pixels.

23

24

The context array uses bit-field encoding where each bit represents

25

whether a specific input image contributed to an output pixel.

26

For >32 input images, multiple "planes" are used in the 3rd dimension.

27

28

Parameters:

29

- context: 3D context array of shape (num_planes, height, width)

30

Each plane encodes 32 input images using int32 bit fields

31

- x: X-coordinates of pixels to decode (can be scalar, list, or array)

32

- y: Y-coordinates of pixels to decode (can be scalar, list, or array)

33

Must have same length as x

34

35

Returns:

36

List of numpy arrays, one per coordinate pair.

37

Each array contains 0-based indices of input images that contributed

38

to the corresponding output pixel.

39

40

Raises:

41

- ValueError: If context is not 3D array

42

- ValueError: If x and y have different lengths

43

- ValueError: If coordinates are not integer values

44

- ValueError: If x/y are not scalars or 1D arrays

45

46

Notes:

47

- Context array uses 32-bit integers with bit-field encoding

48

- Plane 0 encodes images 0-31, plane 1 encodes images 32-63, etc.

49

- Bit k in plane p represents input image (32 * p + k)

50

- Empty arrays returned for pixels with no contributions

51

"""

52

```

53

54

## Usage Examples

55

56

### Basic Context Decoding

57

58

```python

59

import numpy as np

60

from drizzle.utils import decode_context

61

62

# Create example context array for 5x6 output image

63

# This example shows results from processing 80 input images

64

context = np.array([

65

# Plane 0: images 0-31

66

[[0, 0, 0, 0, 0, 0],

67

[0, 0, 0, 36196864, 0, 0],

68

[0, 0, 0, 0, 0, 0],

69

[0, 0, 0, 0, 0, 0],

70

[0, 0, 537920000, 0, 0, 0]],

71

# Plane 1: images 32-63

72

[[0, 0, 0, 0, 0, 0],

73

[0, 0, 0, 67125536, 0, 0],

74

[0, 0, 0, 0, 0, 0],

75

[0, 0, 0, 0, 0, 0],

76

[0, 0, 163856, 0, 0, 0]],

77

# Plane 2: images 64-95

78

[[0, 0, 0, 0, 0, 0],

79

[0, 0, 0, 8203, 0, 0],

80

[0, 0, 0, 0, 0, 0],

81

[0, 0, 0, 0, 0, 0],

82

[0, 0, 32865, 0, 0, 0]]

83

], dtype=np.int32)

84

85

# Decode specific pixels

86

contributing_images = decode_context(context, [3, 2], [1, 4])

87

88

print("Pixel (3,1) has contributions from images:", contributing_images[0])

89

print("Pixel (2,4) has contributions from images:", contributing_images[1])

90

```

91

92

### Single Pixel Analysis

93

94

```python

95

import numpy as np

96

from drizzle.utils import decode_context

97

98

# Decode a single pixel

99

x_coord = 3

100

y_coord = 1

101

contributors = decode_context(context, x_coord, y_coord)

102

103

print(f"Pixel ({x_coord},{y_coord}) contributors: {contributors[0]}")

104

105

# Check if specific images contributed

106

target_images = [9, 25, 40]

107

pixel_contributors = contributors[0]

108

109

for img_id in target_images:

110

if img_id in pixel_contributors:

111

print(f"Image {img_id} contributed to this pixel")

112

else:

113

print(f"Image {img_id} did NOT contribute to this pixel")

114

```

115

116

### Batch Pixel Analysis

117

118

```python

119

import numpy as np

120

from drizzle.utils import decode_context

121

122

# Analyze multiple pixels at once

123

x_coords = np.array([0, 1, 2, 3, 4])

124

y_coords = np.array([0, 1, 2, 3, 4]) # Diagonal pixels

125

126

contributors_list = decode_context(context, x_coords, y_coords)

127

128

for i, (x, y) in enumerate(zip(x_coords, y_coords)):

129

contributors = contributors_list[i]

130

if len(contributors) > 0:

131

print(f"Pixel ({x},{y}): {len(contributors)} contributing images")

132

print(f" Image IDs: {contributors}")

133

else:

134

print(f"Pixel ({x},{y}): No contributing images")

135

```

136

137

### Integration with Drizzle Results

138

139

```python

140

import numpy as np

141

from drizzle.resample import Drizzle

142

from drizzle.utils import decode_context

143

144

# Create drizzle object and process some images

145

drizzler = Drizzle(out_shape=(100, 100))

146

147

# Simulate adding multiple images

148

for i in range(5):

149

data = np.random.random((80, 80)).astype(np.float32)

150

y, x = np.indices((80, 80), dtype=np.float64)

151

x += i * 2 # Shift each image slightly

152

y += i * 1.5

153

pixmap = np.dstack([x, y])

154

155

drizzler.add_image(data, exptime=10.0, pixmap=pixmap)

156

157

# Get the context image

158

context = drizzler.out_ctx

159

print(f"Context shape: {context.shape}")

160

print(f"Processed {drizzler.ctx_id} images")

161

162

# Analyze specific output pixels

163

sample_x = [25, 50, 75]

164

sample_y = [25, 50, 75]

165

contributors = decode_context(context, sample_x, sample_y)

166

167

for i, (x, y) in enumerate(zip(sample_x, sample_y)):

168

imgs = contributors[i]

169

print(f"Pixel ({x},{y}) has {len(imgs)} contributors: {list(imgs)}")

170

```

171

172

### Working with Large Image Sets

173

174

```python

175

import numpy as np

176

from drizzle.utils import decode_context

177

178

# Example with many input images requiring multiple context planes

179

# Simulate context for 100 input images (requires 4 planes: 0-31, 32-63, 64-95, 96-99)

180

181

# Create context array for demonstration

182

height, width = 50, 50

183

num_planes = 4 # For 100 input images

184

context_large = np.random.randint(0, 2**20, (num_planes, height, width), dtype=np.int32)

185

186

# Analyze center pixel

187

center_x, center_y = width // 2, height // 2

188

center_contributors = decode_context(context_large, center_x, center_y)[0]

189

190

print(f"Center pixel ({center_x},{center_y}) contributors:")

191

print(f"Total contributing images: {len(center_contributors)}")

192

print(f"Contributing image IDs: {center_contributors[:10]}...") # Show first 10

193

194

# Count contributions per plane

195

plane_counts = []

196

for plane in range(num_planes):

197

start_img = plane * 32

198

end_img = min(start_img + 32, 100)

199

plane_contributors = center_contributors[

200

(center_contributors >= start_img) & (center_contributors < end_img)

201

]

202

plane_counts.append(len(plane_contributors))

203

print(f"Plane {plane} (images {start_img}-{end_img-1}): {len(plane_contributors)} contributors")

204

```

205

206

### Statistical Analysis of Context

207

208

```python

209

import numpy as np

210

from drizzle.utils import decode_context

211

212

def analyze_context_statistics(context):

213

"""Analyze context image to get contribution statistics."""

214

height, width = context.shape[1], context.shape[2]

215

216

# Sample grid of pixels for analysis

217

x_sample = np.arange(0, width, width // 10)

218

y_sample = np.arange(0, height, height // 10)

219

220

# Create meshgrid for all combinations

221

x_grid, y_grid = np.meshgrid(x_sample, y_sample)

222

x_flat = x_grid.flatten()

223

y_flat = y_grid.flatten()

224

225

# Decode all sample pixels

226

contributors_list = decode_context(context, x_flat, y_flat)

227

228

# Calculate statistics

229

contribution_counts = [len(contributors) for contributors in contributors_list]

230

231

return {

232

'mean_contributors': np.mean(contribution_counts),

233

'max_contributors': np.max(contribution_counts),

234

'min_contributors': np.min(contribution_counts),

235

'std_contributors': np.std(contribution_counts),

236

'pixels_with_no_contributions': sum(1 for c in contribution_counts if c == 0),

237

'total_sampled_pixels': len(contribution_counts)

238

}

239

240

# Use with drizzle results

241

stats = analyze_context_statistics(context)

242

print("Context Statistics:")

243

for key, value in stats.items():

244

print(f" {key}: {value:.2f}")

245

```

246

247

### Manual Bit Field Analysis

248

249

```python

250

import numpy as np

251

252

def manual_bit_decode(context_value, plane_index):

253

"""

254

Manually decode a context value to understand bit field encoding.

255

256

Parameters:

257

- context_value: int32 value from context array

258

- plane_index: which plane this value came from (0, 1, 2, ...)

259

260

Returns:

261

List of contributing image indices

262

"""

263

contributors = []

264

base_image_id = plane_index * 32

265

266

# Check each bit

267

for bit_pos in range(32):

268

if context_value & (1 << bit_pos):

269

contributors.append(base_image_id + bit_pos)

270

271

return contributors

272

273

# Example: decode the bit pattern manually

274

example_context_value = 36196864 # From our example above

275

plane = 0

276

277

manual_result = manual_bit_decode(example_context_value, plane)

278

print(f"Manual decode of value {example_context_value} (binary: {bin(example_context_value)}):")

279

print(f"Contributing images: {manual_result}")

280

281

# Compare with decode_context function

282

# (This would match the result from decode_context for the same pixel)

283

```

284

285

## Error Handling

286

287

```python

288

import numpy as np

289

from drizzle.utils import decode_context

290

291

# Handle invalid context array dimensions

292

try:

293

bad_context_2d = np.ones((50, 50), dtype=np.int32) # 2D instead of 3D

294

decode_context(bad_context_2d, [10], [10])

295

except ValueError as e:

296

print(f"Dimension error: {e}")

297

298

# Handle mismatched coordinate arrays

299

try:

300

context = np.ones((1, 50, 50), dtype=np.int32)

301

decode_context(context, [10, 20], [10]) # Different lengths

302

except ValueError as e:

303

print(f"Coordinate length error: {e}")

304

305

# Handle non-integer coordinates

306

try:

307

decode_context(context, [10.5], [10.7]) # Float coordinates

308

except ValueError as e:

309

print(f"Coordinate type error: {e}")

310

311

# Handle invalid coordinate dimensions

312

try:

313

coords_2d = np.array([[10, 20], [15, 25]]) # 2D coordinate array

314

decode_context(context, coords_2d, [10, 20])

315

except ValueError as e:

316

print(f"Coordinate dimension error: {e}")

317

```

318

319

## Notes

320

321

- Context arrays use 32-bit integer bit fields for efficient storage

322

- Each context plane can track up to 32 input images

323

- Multiple planes are automatically created for >32 input images

324

- Bit position k in plane p represents input image (32 * p + k)

325

- Context tracking can be disabled in Drizzle constructor with `disable_ctx=True`

326

- Context information is essential for understanding data quality and provenance in combined images

327

- The decode_context function is the primary interface for context analysis

328

- Manual bit manipulation can provide additional insights but decode_context is recommended