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