or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

resampling.mddocs/

0

# Image Resampling and Combination

1

2

Core drizzling functionality for combining multiple dithered images onto a common output grid. The drizzling algorithm redistributes flux from input pixels to output pixels based on coordinate transformations, chosen kernels, and weight maps.

3

4

```python { .api }

5

from typing import Optional, Union, Tuple

6

import numpy as np

7

```

8

9

## Capabilities

10

11

### Drizzle Class

12

13

The main class for managing resampling and co-adding of multiple images onto a common output grid. Handles exposure time tracking, context image management, and provides access to output arrays.

14

15

```python { .api }

16

class Drizzle:

17

"""

18

A class for managing resampling and co-adding of multiple images onto a

19

common output grid using the drizzle algorithm.

20

21

The main method is add_image(). Outputs are accessible as properties:

22

out_img, out_wht, out_ctx, and total_exptime.

23

"""

24

25

def __init__(

26

self,

27

kernel: str = "square",

28

fillval: Optional[Union[str, float]] = None,

29

out_shape: Optional[Tuple[int, int]] = None,

30

out_img: Optional[np.ndarray] = None,

31

out_wht: Optional[np.ndarray] = None,

32

out_ctx: Optional[np.ndarray] = None,

33

exptime: float = 0.0,

34

begin_ctx_id: int = 0,

35

max_ctx_id: Optional[int] = None,

36

disable_ctx: bool = False

37

):

38

"""

39

Parameters:

40

- kernel: Kernel for flux distribution ("square", "gaussian", "point", "turbo", "lanczos2", "lanczos3")

41

- fillval: Value for output pixels without input contributions (None, "INDEF", or numeric string)

42

- out_shape: Shape of output images (Ny, Nx). Required if no output arrays provided

43

- out_img: Pre-allocated output image array (float32). If None, will be created

44

- out_wht: Pre-allocated weight array (float32). If None, will be created

45

- out_ctx: Pre-allocated context array (int32). If None, will be created (unless disable_ctx=True)

46

- exptime: Exposure time of previously resampled images (if providing existing arrays)

47

- begin_ctx_id: Starting context ID number (0-based) for first image

48

- max_ctx_id: Maximum expected context ID for pre-allocation

49

- disable_ctx: If True, disables context image creation

50

"""

51

52

def add_image(

53

self,

54

data: np.ndarray,

55

exptime: float,

56

pixmap: np.ndarray,

57

scale: float = 1.0,

58

weight_map: Optional[np.ndarray] = None,

59

wht_scale: float = 1.0,

60

pixfrac: float = 1.0,

61

in_units: str = 'cps',

62

xmin: Optional[int] = None,

63

xmax: Optional[int] = None,

64

ymin: Optional[int] = None,

65

ymax: Optional[int] = None

66

) -> Tuple[float, float]:

67

"""

68

Resample and add an image to the cumulative output image.

69

70

Parameters:

71

- data: Input image to be drizzled (2D numpy array)

72

- exptime: Exposure time of input image (positive number)

73

- pixmap: Coordinate mapping array of shape (Ny, Nx, 2)

74

pixmap[..., 0] = X-coordinates, pixmap[..., 1] = Y-coordinates

75

- scale: Pixel scale of input image (conceptually linear dimension of input pixel)

76

- weight_map: Pixel-by-pixel weighting array. Must match data shape. If None, uniform weights used

77

- wht_scale: Scaling factor applied to pixel weighting

78

- pixfrac: Fraction of pixel flux is confined to (0-1). 1=full pixel, 0.5=half pixel

79

- in_units: Units of input image ("cps" for counts per second, "counts" for total counts)

80

- xmin, xmax, ymin, ymax: Bounding box on input image. Only pixels inside contribute to output

81

82

Returns:

83

Tuple of (nmiss, nskip):

84

- nmiss: Number of pixels that were ignored and did not contribute to output

85

- nskip: Number of lines that were ignored and did not contribute to output

86

"""

87

88

# Properties

89

@property

90

def fillval(self) -> str:

91

"""Fill value for output pixels without contributions from input images."""

92

93

@property

94

def kernel(self) -> str:

95

"""Resampling kernel."""

96

97

@property

98

def ctx_id(self) -> Optional[int]:

99

"""Context image ID (0-based) of the next image to be resampled."""

100

101

@property

102

def out_img(self) -> Optional[np.ndarray]:

103

"""Output resampled image (float32 array)."""

104

105

@property

106

def out_wht(self) -> Optional[np.ndarray]:

107

"""Output weight image (float32 array)."""

108

109

@property

110

def out_ctx(self) -> Optional[np.ndarray]:

111

"""Output context image (int32 array, 2D or 3D)."""

112

113

@property

114

def total_exptime(self) -> float:

115

"""Total exposure time of all resampled images."""

116

```

117

118

### Blot Image Function

119

120

Performs inverse drizzling (blotting) to resample drizzled images back to input coordinate grids. Uses interpolation rather than flux-conserving drizzling, making it suitable for well-sampled images.

121

122

```python { .api }

123

def blot_image(

124

data: np.ndarray,

125

pixmap: np.ndarray,

126

pix_ratio: float,

127

exptime: float,

128

output_pixel_shape: Tuple[int, int],

129

interp: str = 'poly5',

130

sinscl: float = 1.0

131

) -> np.ndarray:

132

"""

133

Resample input image onto output grid using interpolation (not flux-conserving).

134

135

Typically used to resample drizzled output back to input coordinate grids.

136

Works best with well-sampled images like drizzled outputs.

137

138

Parameters:

139

- data: Input image in units of 'cps' (2D array)

140

- pixmap: Coordinate mapping array of shape (Ny, Nx, 2)

141

pixmap[..., 0] = X-coordinates, pixmap[..., 1] = Y-coordinates

142

- pix_ratio: Ratio of input image pixel scale to output image pixel scale

143

- exptime: Exposure time of input image

144

- output_pixel_shape: Dimensions of output image (Nx, Ny)

145

- interp: Interpolation method ("nearest", "linear", "poly3", "poly5", "sinc", "lan3", "lan5")

146

- sinscl: Scaling factor for "sinc" interpolation

147

148

Returns:

149

2D numpy array containing the resampled image data (float32)

150

"""

151

```

152

153

## Usage Examples

154

155

### Basic Drizzling

156

157

```python

158

import numpy as np

159

from drizzle.resample import Drizzle

160

161

# Create sample data

162

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

163

164

# Create identity pixel mapping (no transformation)

165

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

166

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

167

168

# Initialize drizzler

169

drizzler = Drizzle(out_shape=(100, 100), kernel="square")

170

171

# Add the image

172

nmiss, nskip = drizzler.add_image(

173

data=data,

174

exptime=30.0,

175

pixmap=pixmap,

176

pixfrac=1.0

177

)

178

179

# Get results

180

output = drizzler.out_img

181

weights = drizzler.out_wht

182

context = drizzler.out_ctx

183

184

print(f"Missed pixels: {nmiss}, Skipped lines: {nskip}")

185

print(f"Total exposure time: {drizzler.total_exptime}")

186

```

187

188

### Multiple Images with Custom Weights

189

190

```python

191

import numpy as np

192

from drizzle.resample import Drizzle

193

194

# Initialize drizzler for combining multiple images

195

drizzler = Drizzle(out_shape=(200, 200), kernel="gaussian")

196

197

# Process multiple input images

198

for i in range(3):

199

# Simulate input data and weights

200

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

201

weights = np.ones_like(data) * (0.8 + 0.1 * i) # Different weights per image

202

203

# Simulate slight shifts in pixel mapping

204

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

205

x += i * 0.5 # Small shift

206

y += i * 0.3

207

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

208

209

# Add each image

210

nmiss, nskip = drizzler.add_image(

211

data=data,

212

exptime=15.0,

213

pixmap=pixmap,

214

weight_map=weights,

215

pixfrac=0.8,

216

scale=1.0

217

)

218

219

print(f"Image {i}: missed={nmiss}, skipped={nskip}")

220

221

# Final combined result

222

combined_image = drizzler.out_img

223

total_weights = drizzler.out_wht

224

print(f"Combined {drizzler.ctx_id} images, total exposure: {drizzler.total_exptime}s")

225

```

226

227

### Blotting Example

228

229

```python

230

import numpy as np

231

from drizzle.resample import blot_image

232

233

# Simulate a well-sampled drizzled image

234

drizzled_data = np.random.random((150, 150)).astype(np.float32)

235

236

# Create pixel mapping from drizzled grid to original input grid

237

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

238

# Transform coordinates (reverse of original drizzling transformation)

239

x_mapped = x * 1.25 # Scale factor

240

y_mapped = y * 1.25

241

pixmap = np.dstack([x_mapped, y_mapped])

242

243

# Blot the drizzled image back to input grid

244

blotted = blot_image(

245

data=drizzled_data,

246

pixmap=pixmap,

247

pix_ratio=1.25,

248

exptime=30.0,

249

output_pixel_shape=(120, 120),

250

interp='poly5'

251

)

252

253

print(f"Blotted image shape: {blotted.shape}")

254

```

255

256

## Error Handling

257

258

```python

259

from drizzle.resample import Drizzle

260

261

try:

262

# Invalid kernel

263

drizzler = Drizzle(kernel="invalid_kernel")

264

except ValueError as e:

265

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

266

267

try:

268

# Invalid exposure time

269

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

270

drizzler.add_image(data, exptime=-1.0, pixmap=pixmap)

271

except ValueError as e:

272

print(f"Exposure time error: {e}")

273

274

try:

275

# Inconsistent array shapes

276

data = np.ones((100, 100))

277

pixmap = np.ones((50, 50, 2)) # Wrong shape

278

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

279

except ValueError as e:

280

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

281

```