drizzle 2.0.0__cp311-cp311-macosx_10_9_x86_64.whl → 2.1.0__cp311-cp311-macosx_10_9_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of drizzle might be problematic. Click here for more details.
- drizzle/cdrizzle.cpython-311-darwin.so +0 -0
- drizzle/resample.py +23 -18
- drizzle/tests/helpers.py +221 -0
- drizzle/tests/test_resample.py +277 -489
- drizzle/tests/test_utils.py +92 -48
- drizzle/utils.py +56 -10
- {drizzle-2.0.0.dist-info → drizzle-2.1.0.dist-info}/METADATA +18 -48
- drizzle-2.1.0.dist-info/RECORD +16 -0
- {drizzle-2.0.0.dist-info → drizzle-2.1.0.dist-info}/WHEEL +2 -1
- drizzle-2.0.0.dist-info/RECORD +0 -15
- {drizzle-2.0.0.dist-info → drizzle-2.1.0.dist-info/licenses}/LICENSE.rst +0 -0
- {drizzle-2.0.0.dist-info → drizzle-2.1.0.dist-info}/top_level.txt +0 -0
|
Binary file
|
drizzle/resample.py
CHANGED
|
@@ -6,7 +6,7 @@ import numpy as np
|
|
|
6
6
|
|
|
7
7
|
from drizzle import cdrizzle
|
|
8
8
|
|
|
9
|
-
__all__ = ["
|
|
9
|
+
__all__ = ["Drizzle", "blot_image"]
|
|
10
10
|
|
|
11
11
|
SUPPORTED_DRIZZLE_KERNELS = [
|
|
12
12
|
"square",
|
|
@@ -76,23 +76,24 @@ class Drizzle:
|
|
|
76
76
|
information about which input image has contributed to the corresponding
|
|
77
77
|
pixel in the resampled data array. Context image uses 32 bit integers to
|
|
78
78
|
encode this information and hence it can keep track of only 32 input images.
|
|
79
|
-
|
|
80
|
-
second input image, and so on.
|
|
81
|
-
|
|
79
|
+
The first bit corresponds to the first input image, the second bit
|
|
80
|
+
corresponds to the second input image, and so on.
|
|
81
|
+
We call this (0-indexed) order "context ID" which is represented by
|
|
82
|
+
the ``ctx_id`` parameter/property. If the number of
|
|
82
83
|
input images exceeds 32, then it is necessary to have multiple context
|
|
83
|
-
images ("planes") to hold information about all input images with the first
|
|
84
|
+
images ("planes") to hold information about all input images, with the first
|
|
84
85
|
plane encoding which of the first 32 images contributed to the output data
|
|
85
|
-
pixel, second plane representing next 32 input images (number 33-64),
|
|
86
|
-
For this reason, context array is either a 2D array (if the total
|
|
87
|
-
of resampled images is less than 33) of the type `numpy.int32` and
|
|
88
|
-
``(ny, nx)`` or a a 3D array of shape ``(np, ny, nx)`` where ``nx``
|
|
89
|
-
``ny`` are dimensions of image
|
|
90
|
-
|
|
91
|
-
``k`` in a pixel with coordinates ``(p, y, x)`` is 0 then input
|
|
92
|
-
``32 * p + k`` (0-indexed) did not contribute to the output
|
|
93
|
-
with array coordinates ``(y, x)`` and if that bit is 1 then
|
|
94
|
-
number ``32 * p + k`` did contribute to the pixel ``(y, x)``
|
|
95
|
-
resampled image.
|
|
86
|
+
pixel, the second plane representing next 32 input images (number 33-64),
|
|
87
|
+
etc. For this reason, context array is either a 2D array (if the total
|
|
88
|
+
number of resampled images is less than 33) of the type `numpy.int32` and
|
|
89
|
+
shape ``(ny, nx)`` or a a 3D array of shape ``(np, ny, nx)`` where ``nx``
|
|
90
|
+
and ``ny`` are dimensions of the image data. ``np`` is the number of
|
|
91
|
+
"planes" computed as ``(number of input images - 1) // 32 + 1``. If a bit at
|
|
92
|
+
position ``k`` in a pixel with coordinates ``(p, y, x)`` is 0, then input
|
|
93
|
+
image number ``32 * p + k`` (0-indexed) did not contribute to the output
|
|
94
|
+
data pixel with array coordinates ``(y, x)`` and if that bit is 1, then
|
|
95
|
+
input image number ``32 * p + k`` did contribute to the pixel ``(y, x)``
|
|
96
|
+
in the resampled image.
|
|
96
97
|
|
|
97
98
|
As an example, let's assume we have 8 input images. Then, when ``out_ctx``
|
|
98
99
|
pixel values are displayed using binary representation (and decimal in
|
|
@@ -413,7 +414,11 @@ class Drizzle:
|
|
|
413
414
|
)
|
|
414
415
|
|
|
415
416
|
if out_img is None:
|
|
416
|
-
self.
|
|
417
|
+
if self._fillval.upper() in ["INDEF", "NAN"]:
|
|
418
|
+
fillval = np.nan
|
|
419
|
+
else:
|
|
420
|
+
fillval = float(self._fillval)
|
|
421
|
+
self._out_img = np.full(out_shape, fillval, dtype=np.float32)
|
|
417
422
|
else:
|
|
418
423
|
self._out_img = out_img
|
|
419
424
|
|
|
@@ -548,7 +553,7 @@ class Drizzle:
|
|
|
548
553
|
|
|
549
554
|
self._alloc_output_arrays(
|
|
550
555
|
out_shape=self._out_shape,
|
|
551
|
-
max_ctx_id=
|
|
556
|
+
max_ctx_id=self._max_ctx_id,
|
|
552
557
|
out_img=None,
|
|
553
558
|
out_wht=None,
|
|
554
559
|
out_ctx=None,
|
drizzle/tests/helpers.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import gwcs
|
|
4
|
+
import numpy as np
|
|
5
|
+
from gwcs.coordinate_frames import CelestialFrame, Frame2D
|
|
6
|
+
|
|
7
|
+
from astropy import coordinates as coord
|
|
8
|
+
from astropy import units
|
|
9
|
+
from astropy import wcs as fits_wcs
|
|
10
|
+
from astropy.io import fits
|
|
11
|
+
from astropy.modeling.models import (
|
|
12
|
+
Mapping,
|
|
13
|
+
Pix2Sky_TAN,
|
|
14
|
+
Polynomial2D,
|
|
15
|
+
RotateNative2Celestial,
|
|
16
|
+
Shift,
|
|
17
|
+
)
|
|
18
|
+
from astropy.modeling.projections import AffineTransformation2D
|
|
19
|
+
|
|
20
|
+
__all__ = ["wcs_from_file"]
|
|
21
|
+
|
|
22
|
+
TEST_DIR = os.path.abspath(os.path.dirname(__file__))
|
|
23
|
+
DATA_DIR = os.path.join(TEST_DIR, 'data')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def wcs_from_file(filename, ext=None, return_data=False, crpix_shift=None,
|
|
27
|
+
wcs_type="fits"):
|
|
28
|
+
"""
|
|
29
|
+
Read the WCS from a ".fits" file.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
filename : str
|
|
34
|
+
Name of the file to load WCS from.
|
|
35
|
+
|
|
36
|
+
ext : int, None, optional
|
|
37
|
+
Extension number to load the WCS from. When `None`, the WCS will be
|
|
38
|
+
loaded from the first extension containing a WCS.
|
|
39
|
+
|
|
40
|
+
return_data : bool, optional
|
|
41
|
+
When `True`, this function will return a tuple with first item
|
|
42
|
+
being the WCS and the second item being the image data array.
|
|
43
|
+
|
|
44
|
+
crpix_shift : tuple, None, optional
|
|
45
|
+
A tuple of two values to be added to header CRPIX values before
|
|
46
|
+
creating the WCS. This effectively introduces a constant shift
|
|
47
|
+
in the image coordinate system.
|
|
48
|
+
|
|
49
|
+
wcs_type : {"fits", "gwcs"}, optional
|
|
50
|
+
Return either a FITS WCS or a gwcs.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
WCS or tuple of WCS and image data
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
full_file_name = os.path.join(DATA_DIR, filename)
|
|
58
|
+
path = os.path.join(DATA_DIR, full_file_name)
|
|
59
|
+
|
|
60
|
+
def get_shape(hdr):
|
|
61
|
+
naxis1 = hdr.get("WCSNAX1", hdr.get("NAXIS1"))
|
|
62
|
+
naxis2 = hdr.get("WCSNAX2", hdr.get("NAXIS2"))
|
|
63
|
+
if naxis1 is None or naxis2 is None:
|
|
64
|
+
return None
|
|
65
|
+
return (naxis2, naxis1)
|
|
66
|
+
|
|
67
|
+
def data_from_hdr(hdr, data=None, shape=None):
|
|
68
|
+
if data is not None:
|
|
69
|
+
return data
|
|
70
|
+
bitpix = hdr.get("BITPIX", -32)
|
|
71
|
+
dtype = fits.hdu.BITPIX2DTYPE[bitpix]
|
|
72
|
+
shape = get_shape(hdr) or shape
|
|
73
|
+
if shape is None:
|
|
74
|
+
return None
|
|
75
|
+
return np.zeros(shape, dtype=dtype)
|
|
76
|
+
|
|
77
|
+
if os.path.splitext(filename)[1] in [".hdr", ".txt"]:
|
|
78
|
+
hdul = None
|
|
79
|
+
hdr = fits.Header.fromfile(
|
|
80
|
+
path,
|
|
81
|
+
sep='\n',
|
|
82
|
+
endcard=False,
|
|
83
|
+
padding=False
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
with fits.open(path) as fits_hdul:
|
|
88
|
+
hdul = fits.HDUList([hdu.copy() for hdu in fits_hdul])
|
|
89
|
+
|
|
90
|
+
if ext is None and hdul is not None:
|
|
91
|
+
for k, u in enumerate(hdul):
|
|
92
|
+
if "CTYPE1" in u.header:
|
|
93
|
+
ext = k
|
|
94
|
+
break
|
|
95
|
+
|
|
96
|
+
hdr = hdul[ext].header
|
|
97
|
+
|
|
98
|
+
if crpix_shift is not None and "CRPIX1" in hdr:
|
|
99
|
+
hdr["CRPIX1"] += crpix_shift[0]
|
|
100
|
+
hdr["CRPIX2"] += crpix_shift[1]
|
|
101
|
+
|
|
102
|
+
result = fits_wcs.WCS(hdr, hdul)
|
|
103
|
+
shape = get_shape(hdr)
|
|
104
|
+
result.array_shape = shape
|
|
105
|
+
|
|
106
|
+
if wcs_type == "gwcs":
|
|
107
|
+
result = _gwcs_from_hst_fits_wcs(result)
|
|
108
|
+
|
|
109
|
+
if return_data:
|
|
110
|
+
if hdul is None:
|
|
111
|
+
data = data_from_hdr(hdr, data=None, shape=shape)
|
|
112
|
+
return (result, data)
|
|
113
|
+
|
|
114
|
+
result = (result, )
|
|
115
|
+
if not isinstance(return_data, (list, tuple)):
|
|
116
|
+
return_data = [ext]
|
|
117
|
+
for ext in return_data:
|
|
118
|
+
data = data_from_hdr(
|
|
119
|
+
hdul[ext].header,
|
|
120
|
+
data=hdul[ext].data,
|
|
121
|
+
shape=shape
|
|
122
|
+
)
|
|
123
|
+
result = result + (data, )
|
|
124
|
+
|
|
125
|
+
return result
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _gwcs_from_hst_fits_wcs(w):
|
|
129
|
+
# NOTE: this function ignores table distortions
|
|
130
|
+
def coeffs_to_poly(mat, degree):
|
|
131
|
+
pol = Polynomial2D(degree=degree)
|
|
132
|
+
for i in range(mat.shape[0]):
|
|
133
|
+
for j in range(mat.shape[1]):
|
|
134
|
+
if 0 < i + j <= degree:
|
|
135
|
+
setattr(pol, f'c{i}_{j}', mat[i, j])
|
|
136
|
+
return pol
|
|
137
|
+
|
|
138
|
+
nx, ny = w.pixel_shape
|
|
139
|
+
x0, y0 = w.wcs.crpix - 1
|
|
140
|
+
|
|
141
|
+
cd = w.wcs.piximg_matrix
|
|
142
|
+
|
|
143
|
+
if w.sip is None:
|
|
144
|
+
# construct GWCS:
|
|
145
|
+
det2sky = (
|
|
146
|
+
(Shift(-x0) & Shift(-y0)) |
|
|
147
|
+
Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
|
|
148
|
+
)
|
|
149
|
+
else:
|
|
150
|
+
cfx, cfy = np.dot(cd, [w.sip.a.ravel(), w.sip.b.ravel()])
|
|
151
|
+
a = np.reshape(cfx, w.sip.a.shape)
|
|
152
|
+
b = np.reshape(cfy, w.sip.b.shape)
|
|
153
|
+
a[1, 0] = cd[0, 0]
|
|
154
|
+
a[0, 1] = cd[0, 1]
|
|
155
|
+
b[1, 0] = cd[1, 0]
|
|
156
|
+
b[0, 1] = cd[1, 1]
|
|
157
|
+
|
|
158
|
+
polx = coeffs_to_poly(a, w.sip.a_order)
|
|
159
|
+
poly = coeffs_to_poly(b, w.sip.b_order)
|
|
160
|
+
|
|
161
|
+
sip = Mapping((0, 1, 0, 1)) | (polx & poly)
|
|
162
|
+
|
|
163
|
+
# construct GWCS:
|
|
164
|
+
det2sky = (
|
|
165
|
+
(Shift(-x0) & Shift(-y0)) | sip |
|
|
166
|
+
Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
detector_frame = Frame2D(
|
|
170
|
+
name="detector",
|
|
171
|
+
axes_names=("x", "y"),
|
|
172
|
+
unit=(units.pix, units.pix)
|
|
173
|
+
)
|
|
174
|
+
sky_frame = CelestialFrame(
|
|
175
|
+
reference_frame=getattr(coord, w.wcs.radesys).__call__(),
|
|
176
|
+
name=w.wcs.radesys,
|
|
177
|
+
unit=(units.deg, units.deg)
|
|
178
|
+
)
|
|
179
|
+
pipeline = [(detector_frame, det2sky), (sky_frame, None)]
|
|
180
|
+
gw = gwcs.wcs.WCS(pipeline)
|
|
181
|
+
gw.array_shape = w.array_shape
|
|
182
|
+
gw.bounding_box = ((-0.5, nx - 0.5), (-0.5, ny - 0.5))
|
|
183
|
+
|
|
184
|
+
if w.sip is not None:
|
|
185
|
+
# compute inverse SIP and re-create output GWCS
|
|
186
|
+
|
|
187
|
+
# compute inverse SIP:
|
|
188
|
+
hdr = gw.to_fits_sip(
|
|
189
|
+
max_inv_pix_error=1e-5,
|
|
190
|
+
inv_degree=None,
|
|
191
|
+
npoints=64,
|
|
192
|
+
crpix=w.wcs.crpix,
|
|
193
|
+
projection='TAN',
|
|
194
|
+
verbose=False
|
|
195
|
+
)
|
|
196
|
+
winv = fits_wcs.WCS(hdr)
|
|
197
|
+
ap = winv.sip.ap.copy()
|
|
198
|
+
bp = winv.sip.bp.copy()
|
|
199
|
+
ap[1, 0] += 1
|
|
200
|
+
bp[0, 1] += 1
|
|
201
|
+
polx_inv = coeffs_to_poly(ap, winv.sip.ap_order)
|
|
202
|
+
poly_inv = coeffs_to_poly(bp, winv.sip.bp_order)
|
|
203
|
+
af = AffineTransformation2D(
|
|
204
|
+
matrix=np.linalg.inv(winv.wcs.piximg_matrix)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# set analytical inverses:
|
|
208
|
+
sip.inverse = af | Mapping((0, 1, 0, 1)) | (polx_inv & poly_inv)
|
|
209
|
+
|
|
210
|
+
# construct GWCS:
|
|
211
|
+
det2sky = (
|
|
212
|
+
(Shift(-x0) & Shift(-y0)) | sip |
|
|
213
|
+
Pix2Sky_TAN() | RotateNative2Celestial(*w.wcs.crval, 180)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
pipeline = [(detector_frame, det2sky), (sky_frame, None)]
|
|
217
|
+
gw = gwcs.wcs.WCS(pipeline)
|
|
218
|
+
gw.array_shape = w.array_shape
|
|
219
|
+
gw.bounding_box = ((-0.5, nx - 0.5), (-0.5, ny - 0.5))
|
|
220
|
+
|
|
221
|
+
return gw
|