drizzle 1.15.2__cp312-cp312-win32.whl → 2.0.0__cp312-cp312-win32.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.

@@ -0,0 +1,1437 @@
1
+ import math
2
+ import os
3
+
4
+ import numpy as np
5
+ import pytest
6
+
7
+ from astropy import wcs
8
+ from astropy.io import fits
9
+ from drizzle import cdrizzle, resample, utils
10
+
11
+ TEST_DIR = os.path.abspath(os.path.dirname(__file__))
12
+ DATA_DIR = os.path.join(TEST_DIR, 'data')
13
+
14
+
15
+ def bound_image(image):
16
+ """
17
+ Compute region where image is non-zero
18
+ """
19
+ coords = np.nonzero(image)
20
+ ymin = coords[0].min()
21
+ ymax = coords[0].max()
22
+ xmin = coords[1].min()
23
+ xmax = coords[1].max()
24
+ return (ymin, ymax, xmin, xmax)
25
+
26
+
27
+ def centroid(image, size, center):
28
+ """
29
+ Compute the centroid of a rectangular area
30
+ """
31
+ ylo = int(center[0] - size / 2)
32
+ yhi = min(ylo + size, image.shape[0])
33
+ xlo = int(center[1] - size / 2)
34
+ xhi = min(xlo + size, image.shape[1])
35
+
36
+ yx1 = np.mgrid[ylo:yhi, xlo:xhi, 1:2]
37
+ center = (yx1[..., 0] * image[ylo:yhi, xlo:xhi]).sum(
38
+ axis=(1, 2),
39
+ dtype=np.float64,
40
+ )
41
+
42
+ if center[2] == 0.0:
43
+ return None
44
+
45
+ center[0] /= center[2]
46
+ center[1] /= center[2]
47
+ return center
48
+
49
+
50
+ def centroid_close(list_of_centroids, size, point):
51
+ """
52
+ Find if any centroid is close to a point
53
+ """
54
+ for i in range(len(list_of_centroids) - 1, -1, -1):
55
+ if (abs(list_of_centroids[i][0] - point[0]) < int(size / 2) and
56
+ abs(list_of_centroids[i][1] - point[1]) < int(size / 2)):
57
+ return 1
58
+
59
+ return 0
60
+
61
+
62
+ def centroid_compare(centroid):
63
+ return centroid[1]
64
+
65
+
66
+ def centroid_distances(image1, image2, amp, size):
67
+ """
68
+ Compute a list of centroids and the distances between them in two images
69
+ """
70
+ distances = []
71
+ list_of_centroids = centroid_list(image2, amp, size)
72
+ for center2 in list_of_centroids:
73
+ center1 = centroid(image1, size, center2)
74
+ if center1 is None:
75
+ continue
76
+
77
+ disty = center2[0] - center1[0]
78
+ distx = center2[1] - center1[1]
79
+ dist = math.sqrt(disty * disty + distx * distx)
80
+ dflux = abs(center2[2] - center1[2])
81
+ distances.append([dist, dflux, center1, center2])
82
+
83
+ distances.sort(key=centroid_compare)
84
+ return distances
85
+
86
+
87
+ def centroid_list(image, amp, size):
88
+ """
89
+ Find the next centroid
90
+ """
91
+ list_of_centroids = []
92
+ points = np.transpose(np.nonzero(image > amp))
93
+ for point in points:
94
+ if not centroid_close(list_of_centroids, size, point):
95
+ center = centroid(image, size, point)
96
+ list_of_centroids.append(center)
97
+
98
+ return list_of_centroids
99
+
100
+
101
+ def centroid_statistics(title, fname, image1, image2, amp, size):
102
+ """
103
+ write centroid statistics to compare differences btw two images
104
+ """
105
+ stats = ("minimum", "median", "maximum")
106
+ images = (None, None, image1, image2)
107
+ im_type = ("", "", "test", "reference")
108
+
109
+ diff = []
110
+ distances = centroid_distances(image1, image2, amp, size)
111
+ indexes = (0, int(len(distances) / 2), len(distances) - 1)
112
+ fd = open(fname, 'w')
113
+ fd.write(f"*** {title:s} ***\n")
114
+
115
+ if len(distances) == 0:
116
+ diff = [0.0, 0.0, 0.0]
117
+ fd.write("No matches!!\n")
118
+
119
+ elif len(distances) == 1:
120
+ diff = [distances[0][0], distances[0][0], distances[0][0]]
121
+
122
+ fd.write("1 match\n")
123
+ fd.write(
124
+ f"distance = {distances[0][0]:f} "
125
+ f"flux difference = {distances[0][1]:f}\n"
126
+ )
127
+
128
+ for j in range(2, 4):
129
+ ylo = int(distances[0][j][0]) - 1
130
+ yhi = int(distances[0][j][0]) + 2
131
+ xlo = int(distances[0][j][1]) - 1
132
+ xhi = int(distances[0][j][1]) + 2
133
+ subimage = images[j][ylo:yhi, xlo:xhi]
134
+ fd.write(
135
+ f"\n{im_type[j]} image centroid = "
136
+ f"({distances[0][j][0]:f}, {distances[0][j][1]:f}) "
137
+ f"image flux = {distances[0][j][2]:f}\n"
138
+ )
139
+ fd.write(str(subimage) + "\n")
140
+
141
+ else:
142
+ fd.write(f"{len(distances)} matches\n")
143
+
144
+ for k in range(3):
145
+ i = indexes[k]
146
+ diff.append(distances[i][0])
147
+ fd.write(
148
+ f"\n{stats[k]} distance = {distances[i][0]:f} "
149
+ f"flux difference = {distances[i][1]:f}\n"
150
+ )
151
+
152
+ for j in range(2, 4):
153
+ ylo = int(distances[i][j][0]) - 1
154
+ yhi = int(distances[i][j][0]) + 2
155
+ xlo = int(distances[i][j][1]) - 1
156
+ xhi = int(distances[i][j][1]) + 2
157
+ subimage = images[j][ylo:yhi, xlo:xhi]
158
+ fd.write(
159
+ f"\n{stats[k]} {im_type[j]} image centroid = "
160
+ f"({distances[i][j][0]:f}, {distances[i][j][1]:f}) "
161
+ f"image flux = {distances[i][j][2]:f}\n"
162
+ )
163
+ fd.write(str(subimage) + "\n")
164
+
165
+ fd.close()
166
+ return tuple(diff)
167
+
168
+
169
+ def make_point_image(input_image, point, value):
170
+ """
171
+ Create an image with a single point set
172
+ """
173
+ output_image = np.zeros(input_image.shape, dtype=input_image.dtype)
174
+ output_image[point] = value
175
+ return output_image
176
+
177
+
178
+ def make_grid_image(input_image, spacing, value):
179
+ """
180
+ Create an image with points on a grid set
181
+ """
182
+ output_image = np.zeros(input_image.shape, dtype=input_image.dtype)
183
+
184
+ shape = output_image.shape
185
+ half_space = int(spacing / 2)
186
+ for y in range(half_space, shape[0], spacing):
187
+ for x in range(half_space, shape[1], spacing):
188
+ output_image[y, x] = value
189
+
190
+ return output_image
191
+
192
+
193
+ def read_image(filename):
194
+ """
195
+ Read the image from a fits file
196
+ """
197
+ path = os.path.join(DATA_DIR, filename)
198
+ hdu = fits.open(path)
199
+
200
+ image = hdu[1].data
201
+ hdu.close()
202
+ return image
203
+
204
+
205
+ def read_wcs(filename):
206
+ """
207
+ Read the wcs of a fits file
208
+ """
209
+ path = os.path.join(DATA_DIR, filename)
210
+ hdu = fits.open(path)
211
+ the_wcs = wcs.WCS(hdu[1].header)
212
+ hdu.close()
213
+ return the_wcs
214
+
215
+
216
+ def test_drizzle_defaults():
217
+ n = 200
218
+ in_shape = (n, n)
219
+
220
+ # input coordinate grid:
221
+ y, x = np.indices(in_shape, dtype=np.float64)
222
+
223
+ # simulate data:
224
+ in_sci = np.ones(in_shape, dtype=np.float32)
225
+ in_wht = np.ones(in_shape, dtype=np.float32)
226
+
227
+ # create a Drizzle object using all default parameters (except for 'kernel')
228
+ driz = resample.Drizzle(
229
+ kernel='square',
230
+ )
231
+
232
+ assert driz.out_img is None
233
+ assert driz.out_wht is None
234
+ assert driz.out_ctx is None
235
+ assert driz.total_exptime == 0.0
236
+
237
+ driz.add_image(
238
+ in_sci,
239
+ exptime=1.0,
240
+ pixmap=np.dstack([x, y]),
241
+ weight_map=in_wht,
242
+ )
243
+
244
+ pixmap = np.dstack([x + 1, y + 2])
245
+ driz.add_image(
246
+ 3 * in_sci,
247
+ exptime=1.0,
248
+ pixmap=pixmap,
249
+ weight_map=in_wht,
250
+ )
251
+
252
+ assert driz.out_img[0, 0] == 1
253
+ assert driz.out_img[1, 0] == 1
254
+ assert driz.out_img[2, 0] == 1
255
+ assert driz.out_img[1, 1] == 1
256
+ assert driz.out_img[1, 2] == 1
257
+ assert (driz.out_img[2, 1] - 2.0) < 1.0e-14
258
+
259
+
260
+ def test_square_with_point(tmpdir):
261
+ """
262
+ Test do_driz square kernel with point
263
+ """
264
+ output_difference = str(tmpdir.join('difference_square_point.txt'))
265
+
266
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
267
+ output_template = os.path.join(DATA_DIR, 'reference_square_point.fits')
268
+
269
+ insci = read_image(input_file)
270
+ inwcs = read_wcs(input_file)
271
+ insci = make_point_image(insci, (500, 200), 100.0)
272
+ inwht = np.ones(insci.shape, dtype=insci.dtype)
273
+ output_wcs = read_wcs(output_template)
274
+
275
+ pixmap = utils.calc_pixmap(
276
+ inwcs,
277
+ output_wcs,
278
+ )
279
+
280
+ # ignore previous pscale and compute it the old way (only to make
281
+ # tests work with old truth files and thus to show that new API gives
282
+ # same results when equal definitions of the pixel scale is used):
283
+ pscale = np.sqrt(
284
+ np.sum(output_wcs.wcs.pc**2, axis=0)[0] /
285
+ np.sum(inwcs.wcs.cd**2, axis=0)[0]
286
+ )
287
+
288
+ driz = resample.Drizzle(
289
+ kernel='square',
290
+ out_shape=output_wcs.array_shape,
291
+ fillval=0.0,
292
+ )
293
+ driz.add_image(
294
+ insci,
295
+ exptime=1.0,
296
+ pixmap=pixmap,
297
+ weight_map=inwht,
298
+ scale=pscale,
299
+ )
300
+
301
+ template_data = read_image(output_template)
302
+
303
+ _, med_diff, max_diff = centroid_statistics(
304
+ "square with point",
305
+ output_difference,
306
+ driz.out_img,
307
+ template_data,
308
+ 20.0,
309
+ 8,
310
+ )
311
+
312
+ assert med_diff < 1.0e-6
313
+ assert max_diff < 1.0e-5
314
+
315
+
316
+ @pytest.mark.parametrize(
317
+ 'kernel,fc',
318
+ [
319
+ ('square', True),
320
+ ('point', True),
321
+ ('turbo', True),
322
+ ('lanczos2', False),
323
+ ('lanczos3', False),
324
+ ('gaussian', False),
325
+ ],
326
+ )
327
+ def test_zero_input_weight(kernel, fc):
328
+ """
329
+ Test do_driz square kernel with grid
330
+ """
331
+ # initialize input:
332
+ insci = np.ones((200, 400), dtype=np.float32)
333
+ inwht = np.ones((200, 400), dtype=np.float32)
334
+ inwht[:, 150:155] = 0
335
+
336
+ # initialize output:
337
+ outsci = np.zeros((210, 410), dtype=np.float32)
338
+ outwht = np.zeros((210, 410), dtype=np.float32)
339
+ outctx = np.zeros((210, 410), dtype=np.int32)
340
+
341
+ # define coordinate mapping:
342
+ pixmap = np.moveaxis(np.mgrid[1:201, 1:401][::-1], 0, -1)
343
+
344
+ # resample:
345
+ if fc:
346
+ cdrizzle.tdriz(
347
+ insci,
348
+ inwht,
349
+ pixmap,
350
+ outsci,
351
+ outwht,
352
+ outctx,
353
+ uniqid=1,
354
+ xmin=0,
355
+ xmax=400,
356
+ ymin=0,
357
+ ymax=200,
358
+ pixfrac=1,
359
+ kernel=kernel,
360
+ in_units='cps',
361
+ expscale=1,
362
+ wtscale=1,
363
+ fillstr='INDEF',
364
+ )
365
+ else:
366
+ with pytest.warns(Warning):
367
+ cdrizzle.tdriz(
368
+ insci,
369
+ inwht,
370
+ pixmap,
371
+ outsci,
372
+ outwht,
373
+ outctx,
374
+ uniqid=1,
375
+ xmin=0,
376
+ xmax=400,
377
+ ymin=0,
378
+ ymax=200,
379
+ pixfrac=1,
380
+ kernel=kernel,
381
+ in_units='cps',
382
+ expscale=1,
383
+ wtscale=1,
384
+ fillstr='INDEF',
385
+ )
386
+ # pytest.xfail("Not a flux-conserving kernel")
387
+
388
+ # check that no pixel with 0 weight has any counts:
389
+ assert np.sum(np.abs(outsci[(outwht == 0)])) == 0.0
390
+
391
+
392
+ def test_square_with_grid(tmpdir):
393
+ """
394
+ Test do_driz square kernel with grid
395
+ """
396
+ output_difference = str(tmpdir.join('difference_square_grid.txt'))
397
+
398
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
399
+ output_template = os.path.join(DATA_DIR, 'reference_square_grid.fits')
400
+
401
+ insci = read_image(input_file)
402
+ inwcs = read_wcs(input_file)
403
+ insci = make_grid_image(insci, 64, 100.0)
404
+
405
+ inwht = np.ones(insci.shape, dtype=insci.dtype)
406
+ output_wcs = read_wcs(output_template)
407
+
408
+ pixmap = utils.calc_pixmap(
409
+ inwcs,
410
+ output_wcs,
411
+ )
412
+ pscale = utils.estimate_pixel_scale_ratio(
413
+ inwcs,
414
+ output_wcs,
415
+ refpix_from=inwcs.wcs.crpix,
416
+ refpix_to=output_wcs.wcs.crpix,
417
+ )
418
+ # ignore previous pscale and compute it the old way (only to make
419
+ # tests work with old truth files and thus to show that new API gives
420
+ # same results when equal definitions of the pixel scale is used):
421
+ pscale = np.sqrt(
422
+ np.sum(output_wcs.wcs.pc**2, axis=0)[0] /
423
+ np.sum(inwcs.wcs.cd**2, axis=0)[0]
424
+ )
425
+
426
+ driz = resample.Drizzle(
427
+ kernel='square',
428
+ out_shape=output_wcs.array_shape,
429
+ fillval=0.0,
430
+ )
431
+ driz.add_image(
432
+ insci,
433
+ exptime=1.0,
434
+ pixmap=pixmap,
435
+ weight_map=inwht,
436
+ scale=pscale,
437
+ )
438
+ template_data = read_image(output_template)
439
+
440
+ _, med_diff, max_diff = centroid_statistics(
441
+ "square with grid",
442
+ output_difference,
443
+ driz.out_img,
444
+ template_data,
445
+ 20.0,
446
+ 8,
447
+ )
448
+ assert med_diff < 1.0e-6
449
+ assert max_diff < 1.0e-5
450
+
451
+
452
+ def test_turbo_with_grid(tmpdir):
453
+ """
454
+ Test do_driz turbo kernel with grid
455
+ """
456
+ output_difference = str(tmpdir.join('difference_turbo_grid.txt'))
457
+
458
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
459
+ output_template = os.path.join(DATA_DIR, 'reference_turbo_grid.fits')
460
+
461
+ insci = read_image(input_file)
462
+ inwcs = read_wcs(input_file)
463
+ insci = make_grid_image(insci, 64, 100.0)
464
+ inwht = np.ones(insci.shape, dtype=insci.dtype)
465
+ output_wcs = read_wcs(output_template)
466
+
467
+ pixmap = utils.calc_pixmap(
468
+ inwcs,
469
+ output_wcs,
470
+ )
471
+ pscale = utils.estimate_pixel_scale_ratio(
472
+ inwcs,
473
+ output_wcs,
474
+ refpix_from=inwcs.wcs.crpix,
475
+ refpix_to=output_wcs.wcs.crpix,
476
+ )
477
+
478
+ # ignore previous pscale and compute it the old way (only to make
479
+ # tests work with old truth files and thus to show that new API gives
480
+ # same results when equal definitions of the pixel scale is used):
481
+ pscale = np.sqrt(
482
+ np.sum(output_wcs.wcs.pc**2, axis=0)[0] /
483
+ np.sum(inwcs.wcs.cd**2, axis=0)[0]
484
+ )
485
+
486
+ driz = resample.Drizzle(
487
+ kernel='turbo',
488
+ out_shape=output_wcs.array_shape,
489
+ fillval=0.0,
490
+ )
491
+ driz.add_image(
492
+ insci,
493
+ exptime=1.0,
494
+ pixmap=pixmap,
495
+ weight_map=inwht,
496
+ scale=pscale,
497
+ )
498
+
499
+ template_data = read_image(output_template)
500
+
501
+ _, med_diff, max_diff = centroid_statistics(
502
+ "turbo with grid",
503
+ output_difference,
504
+ driz.out_img,
505
+ template_data,
506
+ 20.0,
507
+ 8,
508
+ )
509
+
510
+ assert med_diff < 1.0e-6
511
+ assert max_diff < 1.0e-5
512
+
513
+
514
+ def test_gaussian_with_grid(tmpdir):
515
+ """
516
+ Test do_driz gaussian kernel with grid
517
+ """
518
+ output_difference = str(tmpdir.join('difference_gaussian_grid.txt'))
519
+
520
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
521
+ output_template = os.path.join(DATA_DIR, 'reference_gaussian_grid.fits')
522
+
523
+ insci = read_image(input_file)
524
+ inwcs = read_wcs(input_file)
525
+ insci = make_grid_image(insci, 64, 100.0)
526
+ inwht = np.ones(insci.shape, dtype=insci.dtype)
527
+ output_wcs = read_wcs(output_template)
528
+
529
+ pixmap = utils.calc_pixmap(
530
+ inwcs,
531
+ output_wcs,
532
+ )
533
+ pscale = utils.estimate_pixel_scale_ratio(
534
+ inwcs,
535
+ output_wcs,
536
+ refpix_from=inwcs.wcs.crpix,
537
+ refpix_to=output_wcs.wcs.crpix,
538
+ )
539
+
540
+ # ignore previous pscale and compute it the old way (only to make
541
+ # tests work with old truth files and thus to show that new API gives
542
+ # same results when equal definitions of the pixel scale is used):
543
+ pscale = np.sqrt(
544
+ np.sum(output_wcs.wcs.pc**2, axis=0)[0] /
545
+ np.sum(inwcs.wcs.cd**2, axis=0)[0]
546
+ )
547
+
548
+ driz = resample.Drizzle(
549
+ kernel='gaussian',
550
+ out_shape=output_wcs.array_shape,
551
+ fillval=0.0,
552
+ )
553
+ with pytest.warns(Warning):
554
+ driz.add_image(
555
+ insci,
556
+ exptime=1.0,
557
+ pixmap=pixmap,
558
+ weight_map=inwht,
559
+ scale=pscale,
560
+ )
561
+
562
+ template_data = read_image(output_template)
563
+
564
+ _, med_diff, max_diff = centroid_statistics(
565
+ "gaussian with grid",
566
+ output_difference,
567
+ driz.out_img,
568
+ template_data,
569
+ 20.0,
570
+ 8,
571
+ )
572
+
573
+ assert med_diff < 1.0e-6
574
+ assert max_diff < 2.0e-5
575
+
576
+
577
+ def test_lanczos_with_grid(tmpdir):
578
+ """
579
+ Test do_driz lanczos kernel with grid
580
+ """
581
+ output_difference = str(tmpdir.join('difference_lanczos_grid.txt'))
582
+
583
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
584
+ output_template = os.path.join(DATA_DIR, 'reference_lanczos_grid.fits')
585
+
586
+ insci = read_image(input_file)
587
+ inwcs = read_wcs(input_file)
588
+ insci = make_grid_image(insci, 64, 100.0)
589
+ inwht = np.ones(insci.shape, dtype=insci.dtype)
590
+ output_wcs = read_wcs(output_template)
591
+
592
+ pixmap = utils.calc_pixmap(
593
+ inwcs,
594
+ output_wcs,
595
+ )
596
+ pscale = utils.estimate_pixel_scale_ratio(
597
+ inwcs,
598
+ output_wcs,
599
+ refpix_from=inwcs.wcs.crpix,
600
+ refpix_to=output_wcs.wcs.crpix,
601
+ )
602
+
603
+ # ignore previous pscale and compute it the old way (only to make
604
+ # tests work with old truth files and thus to show that new API gives
605
+ # same results when equal definitions of the pixel scale is used):
606
+ pscale = np.sqrt(
607
+ np.sum(output_wcs.wcs.pc**2, axis=0)[0] /
608
+ np.sum(inwcs.wcs.cd**2, axis=0)[0]
609
+ )
610
+
611
+ driz = resample.Drizzle(
612
+ kernel='lanczos3',
613
+ out_shape=output_wcs.array_shape,
614
+ fillval=0.0,
615
+ )
616
+ with pytest.warns(Warning):
617
+ driz.add_image(
618
+ insci,
619
+ exptime=1.0,
620
+ pixmap=pixmap,
621
+ weight_map=inwht,
622
+ scale=pscale,
623
+ )
624
+
625
+ template_data = read_image(output_template)
626
+
627
+ _, med_diff, max_diff = centroid_statistics(
628
+ "lanczos with grid",
629
+ output_difference,
630
+ driz.out_img,
631
+ template_data,
632
+ 20.0,
633
+ 8,
634
+ )
635
+ assert med_diff < 1.0e-6
636
+ assert max_diff < 1.0e-5
637
+
638
+
639
+ def test_point_with_grid(tmpdir):
640
+ """
641
+ Test do_driz point kernel with grid
642
+ """
643
+ output_difference = str(tmpdir.join('difference_point_grid.txt'))
644
+
645
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
646
+ output_template = os.path.join(DATA_DIR, 'reference_point_grid.fits')
647
+
648
+ insci = read_image(input_file)
649
+ inwcs = read_wcs(input_file)
650
+ insci = make_grid_image(insci, 64, 100.0)
651
+ inwht = np.ones(insci.shape, dtype=insci.dtype)
652
+ output_wcs = read_wcs(output_template)
653
+
654
+ pixmap = utils.calc_pixmap(inwcs, output_wcs)
655
+
656
+ driz = resample.Drizzle(kernel='point', out_shape=output_wcs.array_shape, fillval=0.0)
657
+ driz.add_image(insci, exptime=1.0, pixmap=pixmap, weight_map=inwht)
658
+
659
+ template_data = read_image(output_template)
660
+
661
+ _, med_diff, max_diff = centroid_statistics(
662
+ "point with grid",
663
+ output_difference,
664
+ driz.out_img,
665
+ template_data,
666
+ 20.0,
667
+ 8,
668
+ )
669
+ assert med_diff < 1.0e-6
670
+ assert max_diff < 1.0e-5
671
+
672
+
673
+ def test_blot_with_point(tmpdir):
674
+ """
675
+ Test do_blot with point image
676
+ """
677
+ output_difference = str(tmpdir.join('difference_blot_point.txt'))
678
+
679
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
680
+ output_template = os.path.join(DATA_DIR, 'reference_blot_point.fits')
681
+
682
+ outsci = read_image(input_file)
683
+ outwcs = read_wcs(input_file)
684
+ outsci = make_point_image(outsci, (500, 200), 40.0)
685
+ inwcs = read_wcs(output_template)
686
+
687
+ pixmap = utils.calc_pixmap(inwcs, outwcs)
688
+
689
+ # compute pscale the old way (only to make
690
+ # tests work with old truth files and thus to show that new API gives
691
+ # same results when equal definitions of the pixel scale is used):
692
+ pscale = np.sqrt(
693
+ np.sum(inwcs.wcs.pc**2, axis=0)[0] /
694
+ np.sum(outwcs.wcs.cd**2, axis=0)[0]
695
+ )
696
+
697
+ blotted_image = resample.blot_image(
698
+ outsci,
699
+ pixmap=pixmap,
700
+ pix_ratio=pscale,
701
+ exptime=1.0,
702
+ output_pixel_shape=inwcs.pixel_shape,
703
+ )
704
+
705
+ template_data = read_image(output_template)
706
+
707
+ _, med_diff, max_diff = centroid_statistics(
708
+ "blot with point",
709
+ output_difference,
710
+ blotted_image,
711
+ template_data,
712
+ 20.0,
713
+ 16,
714
+ )
715
+ assert med_diff < 1.0e-6
716
+ assert max_diff < 1.0e-5
717
+
718
+
719
+ def test_blot_with_default(tmpdir):
720
+ """
721
+ Test do_blot with default grid image
722
+ """
723
+ output_difference = str(tmpdir.join('difference_blot_default.txt'))
724
+
725
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
726
+ output_template = os.path.join(DATA_DIR, 'reference_blot_default.fits')
727
+
728
+ outsci = read_image(input_file)
729
+ outsci = make_grid_image(outsci, 64, 100.0)
730
+ outwcs = read_wcs(input_file)
731
+ inwcs = read_wcs(output_template)
732
+
733
+ pixmap = utils.calc_pixmap(inwcs, outwcs)
734
+
735
+ # compute pscale the old way (only to make
736
+ # tests work with old truth files and thus to show that new API gives
737
+ # same results when equal definitions of the pixel scale is used):
738
+ pscale = np.sqrt(
739
+ np.sum(inwcs.wcs.pc**2, axis=0)[0] /
740
+ np.sum(outwcs.wcs.cd**2, axis=0)[0]
741
+ )
742
+
743
+ blotted_image = resample.blot_image(
744
+ outsci,
745
+ pixmap=pixmap,
746
+ pix_ratio=pscale,
747
+ exptime=1.0,
748
+ output_pixel_shape=inwcs.pixel_shape,
749
+ )
750
+
751
+ template_data = read_image(output_template)
752
+
753
+ _, med_diff, max_diff = centroid_statistics(
754
+ "blot with defaults",
755
+ output_difference,
756
+ blotted_image,
757
+ template_data,
758
+ 20.0,
759
+ 16,
760
+ )
761
+
762
+ assert med_diff < 1.0e-6
763
+ assert max_diff < 1.0e-5
764
+
765
+
766
+ def test_blot_with_lan3(tmpdir):
767
+ """
768
+ Test do_blot with lan3 grid image
769
+ """
770
+ output_difference = str(tmpdir.join('difference_blot_lan3.txt'))
771
+
772
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
773
+ output_template = os.path.join(DATA_DIR, 'reference_blot_lan3.fits')
774
+
775
+ outsci = read_image(input_file)
776
+ outsci = make_grid_image(outsci, 64, 100.0)
777
+ outwcs = read_wcs(input_file)
778
+ inwcs = read_wcs(output_template)
779
+
780
+ pixmap = utils.calc_pixmap(inwcs, outwcs)
781
+
782
+ # compute pscale the old way (only to make
783
+ # tests work with old truth files and thus to show that new API gives
784
+ # same results when equal definitions of the pixel scale is used):
785
+ pscale = np.sqrt(
786
+ np.sum(inwcs.wcs.pc**2, axis=0)[0] /
787
+ np.sum(outwcs.wcs.cd**2, axis=0)[0]
788
+ )
789
+
790
+ blotted_image = resample.blot_image(
791
+ outsci,
792
+ pixmap=pixmap,
793
+ pix_ratio=pscale,
794
+ exptime=1.0,
795
+ output_pixel_shape=inwcs.pixel_shape,
796
+ interp="lan3",
797
+ )
798
+
799
+ template_data = read_image(output_template)
800
+
801
+ _, med_diff, max_diff = centroid_statistics(
802
+ "blot with lan3",
803
+ output_difference,
804
+ blotted_image,
805
+ template_data,
806
+ 20.0,
807
+ 16,
808
+ )
809
+
810
+ assert med_diff < 1.0e-6
811
+ assert max_diff < 1.0e-5
812
+
813
+
814
+ def test_blot_with_lan5(tmpdir):
815
+ """
816
+ Test do_blot with lan5 grid image
817
+ """
818
+ output_difference = str(tmpdir.join('difference_blot_lan5.txt'))
819
+
820
+ input_file = os.path.join(DATA_DIR, 'j8bt06nyq_flt.fits')
821
+ output_template = os.path.join(DATA_DIR, 'reference_blot_lan5.fits')
822
+
823
+ outsci = read_image(input_file)
824
+ outsci = make_grid_image(outsci, 64, 100.0)
825
+ outwcs = read_wcs(input_file)
826
+ inwcs = read_wcs(output_template)
827
+
828
+ pixmap = utils.calc_pixmap(inwcs, outwcs)
829
+
830
+ # compute pscale the old way (only to make
831
+ # tests work with old truth files and thus to show that new API gives
832
+ # same results when equal definitions of the pixel scale is used):
833
+ pscale = np.sqrt(
834
+ np.sum(inwcs.wcs.pc**2, axis=0)[0] /
835
+ np.sum(outwcs.wcs.cd**2, axis=0)[0]
836
+ )
837
+
838
+ blotted_image = resample.blot_image(
839
+ outsci,
840
+ pixmap=pixmap,
841
+ pix_ratio=pscale,
842
+ exptime=1.0,
843
+ output_pixel_shape=inwcs.pixel_shape,
844
+ interp="lan5",
845
+ )
846
+
847
+ template_data = read_image(output_template)
848
+
849
+ _, med_diff, max_diff = centroid_statistics(
850
+ "blot with lan5",
851
+ output_difference,
852
+ blotted_image,
853
+ template_data,
854
+ 20.0,
855
+ 16,
856
+ )
857
+
858
+ assert med_diff < 1.0e-6
859
+ assert max_diff < 1.0e-5
860
+
861
+
862
+ def test_context_planes():
863
+ """Reproduce error seen in issue #50"""
864
+ shape = (10, 10)
865
+ output_wcs = wcs.WCS()
866
+ output_wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN']
867
+ output_wcs.wcs.pc = [[1, 0], [0, 1]]
868
+ output_wcs.pixel_shape = shape
869
+ driz = resample.Drizzle(out_shape=tuple(shape))
870
+
871
+ image = np.ones(shape)
872
+ inwcs = wcs.WCS()
873
+ inwcs.wcs.ctype = ['RA---TAN', 'DEC--TAN']
874
+ inwcs.wcs.cd = [[1, 0], [0, 1]]
875
+ inwcs.pixel_shape = shape
876
+
877
+ pixmap = utils.calc_pixmap(inwcs, output_wcs)
878
+
879
+ # context image must be 2D or 3D:
880
+ with pytest.raises(ValueError) as err_info:
881
+ resample.Drizzle(
882
+ kernel='point',
883
+ exptime=0.0,
884
+ out_shape=shape,
885
+ out_ctx=[0, 0, 0],
886
+ )
887
+ assert str(err_info.value).startswith(
888
+ "'out_ctx' must be either a 2D or 3D array."
889
+ )
890
+
891
+ driz = resample.Drizzle(
892
+ kernel='square',
893
+ out_shape=output_wcs.array_shape,
894
+ fillval=0.0,
895
+ )
896
+
897
+ for i in range(32):
898
+ assert driz.ctx_id == i
899
+ driz.add_image(image, exptime=1.0, pixmap=pixmap)
900
+ assert driz.out_ctx.shape == (1, 10, 10)
901
+
902
+ driz.add_image(image, exptime=1.0, pixmap=pixmap)
903
+ assert driz.out_ctx.shape == (2, 10, 10)
904
+
905
+
906
+ def test_no_context_image():
907
+ """Reproduce error seen in issue #50"""
908
+ shape = (10, 10)
909
+ output_wcs = wcs.WCS()
910
+ output_wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN']
911
+ output_wcs.wcs.pc = [[1, 0], [0, 1]]
912
+ output_wcs.pixel_shape = shape
913
+ driz = resample.Drizzle(
914
+ out_shape=tuple(shape),
915
+ begin_ctx_id=-1,
916
+ disable_ctx=True
917
+ )
918
+ assert driz.out_ctx is None
919
+ assert driz.ctx_id is None
920
+
921
+ image = np.ones(shape)
922
+ inwcs = wcs.WCS()
923
+ inwcs.wcs.ctype = ['RA---TAN', 'DEC--TAN']
924
+ inwcs.wcs.cd = [[1, 0], [0, 1]]
925
+ inwcs.pixel_shape = shape
926
+
927
+ pixmap = utils.calc_pixmap(inwcs, output_wcs)
928
+
929
+ for i in range(33):
930
+ driz.add_image(image, exptime=1.0, pixmap=pixmap)
931
+ assert driz.out_ctx is None
932
+ assert driz.ctx_id is None
933
+
934
+
935
+ def test_init_ctx_id():
936
+ # starting context ID must be positive
937
+ with pytest.raises(ValueError) as err_info:
938
+ resample.Drizzle(
939
+ kernel='square',
940
+ exptime=0.0,
941
+ begin_ctx_id=-1,
942
+ out_shape=(10, 10),
943
+ )
944
+ assert str(err_info.value).startswith(
945
+ "Invalid context image ID"
946
+ )
947
+
948
+ with pytest.raises(ValueError) as err_info:
949
+ resample.Drizzle(
950
+ kernel='square',
951
+ exptime=0.0,
952
+ out_shape=(10, 10),
953
+ begin_ctx_id=1,
954
+ max_ctx_id=0,
955
+ )
956
+ assert str(err_info.value).startswith(
957
+ "'max_ctx_id' cannot be smaller than 'begin_ctx_id'."
958
+ )
959
+
960
+
961
+ def test_context_agrees_with_weight():
962
+ n = 200
963
+ out_shape = (n, n)
964
+
965
+ # allocate output arrays:
966
+ out_img = np.zeros(out_shape, dtype=np.float32)
967
+ out_ctx = np.zeros(out_shape, dtype=np.int32)
968
+ out_wht = np.zeros(out_shape, dtype=np.float32)
969
+
970
+ # previous data in weight and context must agree:
971
+ with pytest.raises(ValueError) as err_info:
972
+ out_ctx[0, 0] = 1
973
+ out_ctx[0, 1] = 1
974
+ out_wht[0, 0] = 0.1
975
+ resample.Drizzle(
976
+ kernel='square',
977
+ out_shape=out_shape,
978
+ out_img=out_img,
979
+ out_ctx=out_ctx,
980
+ out_wht=out_wht,
981
+ exptime=1.0,
982
+ )
983
+ assert str(err_info.value).startswith(
984
+ "Inconsistent values of supplied 'out_wht' and 'out_ctx' "
985
+ )
986
+
987
+
988
+ @pytest.mark.parametrize(
989
+ 'kernel,fc',
990
+ [
991
+ ('square', True),
992
+ ('point', True),
993
+ ('turbo', True),
994
+ ('lanczos2', False),
995
+ ('lanczos3', False),
996
+ ('gaussian', False),
997
+ ],
998
+ )
999
+ def test_flux_conservation_nondistorted(kernel, fc):
1000
+ n = 200
1001
+ in_shape = (n, n)
1002
+
1003
+ # input coordinate grid:
1004
+ y, x = np.indices(in_shape, dtype=np.float64)
1005
+
1006
+ # simulate a gaussian "star":
1007
+ fwhm = 2.9
1008
+ x0 = 50.0
1009
+ y0 = 68.0
1010
+ sig = fwhm / (2.0 * np.sqrt(2.0 * np.log(2.0 * fwhm)))
1011
+ sig2 = sig * sig
1012
+ star = np.exp(-0.5 / sig2 * ((x.astype(np.float32) - x0)**2 + (y.astype(np.float32) - y0)**2))
1013
+ in_sci = (star / np.sum(star)).astype(np.float32) # normalize to 1
1014
+ in_wht = np.ones(in_shape, dtype=np.float32)
1015
+
1016
+ # linear shift:
1017
+ xp = x + 0.5
1018
+ yp = y + 0.2
1019
+
1020
+ pixmap = np.dstack([xp, yp])
1021
+
1022
+ out_shape = (int(yp.max()) + 1, int(xp.max()) + 1)
1023
+ # make sure distorion is not moving flux out of the image towards negative
1024
+ # coordinates (just because of the simple way of how we account for output
1025
+ # image size)
1026
+ assert np.min(xp) > -0.5 and np.min(yp) > -0.5
1027
+
1028
+ out_img = np.zeros(out_shape, dtype=np.float32)
1029
+ out_ctx = np.zeros(out_shape, dtype=np.int32)
1030
+ out_wht = np.zeros(out_shape, dtype=np.float32)
1031
+
1032
+ if fc:
1033
+ cdrizzle.tdriz(
1034
+ in_sci,
1035
+ in_wht,
1036
+ pixmap,
1037
+ out_img,
1038
+ out_wht,
1039
+ out_ctx,
1040
+ pixfrac=1.0,
1041
+ scale=1.0,
1042
+ kernel=kernel,
1043
+ in_units="cps",
1044
+ expscale=1.0,
1045
+ wtscale=1.0,
1046
+ )
1047
+ else:
1048
+ with pytest.warns(Warning):
1049
+ cdrizzle.tdriz(
1050
+ in_sci,
1051
+ in_wht,
1052
+ pixmap,
1053
+ out_img,
1054
+ out_wht,
1055
+ out_ctx,
1056
+ pixfrac=1.0,
1057
+ scale=1.0,
1058
+ kernel=kernel,
1059
+ in_units="cps",
1060
+ expscale=1.0,
1061
+ wtscale=1.0,
1062
+ )
1063
+ pytest.xfail("Not a flux-conserving kernel")
1064
+
1065
+ assert np.allclose(
1066
+ np.sum(out_img * out_wht),
1067
+ np.sum(in_sci),
1068
+ atol=0.0,
1069
+ rtol=0.0001,
1070
+ )
1071
+
1072
+
1073
+ @pytest.mark.parametrize(
1074
+ 'kernel,fc',
1075
+ [
1076
+ ('square', True),
1077
+ ('point', True),
1078
+ ('turbo', True),
1079
+ ('lanczos2', False),
1080
+ ('lanczos3', False),
1081
+ ('gaussian', False),
1082
+ ],
1083
+ )
1084
+ def test_flux_conservation_distorted(kernel, fc):
1085
+ n = 200
1086
+ in_shape = (n, n)
1087
+
1088
+ # input coordinate grid:
1089
+ y, x = np.indices(in_shape, dtype=np.float64)
1090
+
1091
+ # simulate a gaussian "star":
1092
+ fwhm = 2.9
1093
+ x0 = 50.0
1094
+ y0 = 68.0
1095
+ sig = fwhm / (2.0 * np.sqrt(2.0 * np.log(2.0 * fwhm)))
1096
+ sig2 = sig * sig
1097
+ star = np.exp(-0.5 / sig2 * ((x.astype(np.float32) - x0)**2 + (y.astype(np.float32) - y0)**2))
1098
+ in_sci = (star / np.sum(star)).astype(np.float32) # normalize to 1
1099
+ in_wht = np.ones(in_shape, dtype=np.float32)
1100
+
1101
+ # linear shift:
1102
+ xp = x + 0.5
1103
+ yp = y + 0.2
1104
+ # add distortion:
1105
+ xp += 1e-4 * x**2 + 1e-5 * x * y
1106
+ yp += 1e-3 * y**2 - 2e-5 * x * y
1107
+
1108
+ pixmap = np.dstack([xp, yp])
1109
+
1110
+ out_shape = (int(yp.max()) + 1, int(xp.max()) + 1)
1111
+ # make sure distorion is not moving (pixels with) flux out of the image
1112
+ # towards negative coordinates (just because of the simple way of how we
1113
+ # account for output image size):
1114
+ assert np.min(xp) > -0.5 and np.min(yp) > -0.5
1115
+
1116
+ out_img = np.zeros(out_shape, dtype=np.float32)
1117
+ out_ctx = np.zeros(out_shape, dtype=np.int32)
1118
+ out_wht = np.zeros(out_shape, dtype=np.float32)
1119
+
1120
+ if fc:
1121
+ cdrizzle.tdriz(
1122
+ in_sci,
1123
+ in_wht,
1124
+ pixmap,
1125
+ out_img,
1126
+ out_wht,
1127
+ out_ctx,
1128
+ pixfrac=1.0,
1129
+ scale=1.0,
1130
+ kernel=kernel,
1131
+ in_units="cps",
1132
+ expscale=1.0,
1133
+ wtscale=1.0,
1134
+ )
1135
+ else:
1136
+ with pytest.warns(Warning):
1137
+ cdrizzle.tdriz(
1138
+ in_sci,
1139
+ in_wht,
1140
+ pixmap,
1141
+ out_img,
1142
+ out_wht,
1143
+ out_ctx,
1144
+ pixfrac=1.0,
1145
+ scale=1.0,
1146
+ kernel=kernel,
1147
+ in_units="cps",
1148
+ expscale=1.0,
1149
+ wtscale=1.0,
1150
+ )
1151
+ pytest.xfail("Not a flux-conserving kernel")
1152
+
1153
+ assert np.allclose(
1154
+ np.sum(out_img * out_wht),
1155
+ np.sum(in_sci),
1156
+ atol=0.0,
1157
+ rtol=0.0001,
1158
+ )
1159
+
1160
+
1161
+ def test_drizzle_exptime():
1162
+ n = 200
1163
+ in_shape = (n, n)
1164
+
1165
+ # input coordinate grid:
1166
+ y, x = np.indices(in_shape, dtype=np.float64)
1167
+
1168
+ # simulate data:
1169
+ in_sci = np.ones(in_shape, dtype=np.float32)
1170
+ in_wht = np.ones(in_shape, dtype=np.float32)
1171
+
1172
+ pixmap = np.dstack([x, y])
1173
+
1174
+ # allocate output arrays:
1175
+ out_shape = (int(y.max()) + 1, int(x.max()) + 1)
1176
+ out_img = np.zeros(out_shape, dtype=np.float32)
1177
+ out_ctx = np.zeros(out_shape, dtype=np.int32)
1178
+ out_wht = np.zeros(out_shape, dtype=np.float32)
1179
+
1180
+ # starting exposure time must be non-negative:
1181
+ with pytest.raises(ValueError) as err_info:
1182
+ driz = resample.Drizzle(
1183
+ kernel='square',
1184
+ out_shape=out_shape,
1185
+ fillval="indef",
1186
+ exptime=-1.0,
1187
+ )
1188
+ assert str(err_info.value) == "Exposure time must be non-negative."
1189
+
1190
+ driz = resample.Drizzle(
1191
+ kernel='turbo',
1192
+ out_shape=out_shape,
1193
+ fillval="",
1194
+ out_img=out_img,
1195
+ out_ctx=out_ctx,
1196
+ out_wht=out_wht,
1197
+ exptime=1.0,
1198
+ )
1199
+ assert driz.kernel == 'turbo'
1200
+
1201
+ driz.add_image(in_sci, weight_map=in_wht, exptime=1.03456, pixmap=pixmap)
1202
+ assert np.allclose(driz.total_exptime, 2.03456, rtol=0, atol=1.0e-14)
1203
+
1204
+ driz.add_image(in_sci, weight_map=in_wht, exptime=3.1415926, pixmap=pixmap)
1205
+ assert np.allclose(driz.total_exptime, 5.1761526, rtol=0, atol=1.0e-14)
1206
+
1207
+ with pytest.raises(ValueError) as err_info:
1208
+ driz.add_image(in_sci, weight_map=in_wht, exptime=-1, pixmap=pixmap)
1209
+ assert str(err_info.value) == "'exptime' *must* be a strictly positive number."
1210
+
1211
+ # exptime cannot be 0 when output data has data:
1212
+ with pytest.raises(ValueError) as err_info:
1213
+ out_ctx[0, 0] = 1
1214
+ driz = resample.Drizzle(
1215
+ kernel='square',
1216
+ out_shape=out_shape,
1217
+ fillval="indef",
1218
+ out_img=out_img,
1219
+ out_ctx=out_ctx,
1220
+ out_wht=out_wht,
1221
+ exptime=0.0,
1222
+ )
1223
+ assert str(err_info.value).startswith(
1224
+ "Inconsistent exposure time and context and/or weight images:"
1225
+ )
1226
+
1227
+ # exptime must be 0 when output arrays are not provided:
1228
+ with pytest.raises(ValueError) as err_info:
1229
+ driz = resample.Drizzle(
1230
+ kernel='square',
1231
+ out_shape=out_shape,
1232
+ exptime=1.0,
1233
+ )
1234
+ assert str(err_info.value).startswith(
1235
+ "Exposure time must be 0.0 for the first resampling"
1236
+ )
1237
+
1238
+
1239
+ def test_drizzle_unsupported_kernel():
1240
+ with pytest.raises(ValueError) as err_info:
1241
+ resample.Drizzle(
1242
+ kernel='magic_image_improver',
1243
+ out_shape=(10, 10),
1244
+ exptime=0.0,
1245
+ )
1246
+ assert str(err_info.value) == "Kernel 'magic_image_improver' is not supported."
1247
+
1248
+
1249
+ def test_pixmap_shape_matches_image():
1250
+ n = 200
1251
+ in_shape = (n, n)
1252
+
1253
+ # input coordinate grid:
1254
+ y, x = np.indices((n + 1, n), dtype=np.float64)
1255
+
1256
+ # simulate data:
1257
+ in_sci = np.ones(in_shape, dtype=np.float32)
1258
+ in_wht = np.ones(in_shape, dtype=np.float32)
1259
+
1260
+ pixmap = np.dstack([x, y])
1261
+
1262
+ driz = resample.Drizzle(
1263
+ kernel='square',
1264
+ fillval=0.0,
1265
+ exptime=0.0,
1266
+ )
1267
+
1268
+ # last two sizes of the pixelmap must match those of input images:
1269
+ with pytest.raises(ValueError) as err_info:
1270
+ driz.add_image(
1271
+ in_sci,
1272
+ exptime=1.0,
1273
+ pixmap=pixmap,
1274
+ weight_map=in_wht,
1275
+ )
1276
+ assert str(err_info.value) == "'pixmap' shape is not consistent with 'data' shape."
1277
+
1278
+
1279
+ def test_drizzle_fillval():
1280
+ n = 200
1281
+ in_shape = (n, n)
1282
+
1283
+ # input coordinate grid:
1284
+ y, x = np.indices(in_shape, dtype=np.float64)
1285
+
1286
+ # simulate a gaussian "star":
1287
+ fwhm = 2.9
1288
+ x0 = 50.0
1289
+ y0 = 68.0
1290
+ sig = fwhm / (2.0 * np.sqrt(2.0 * np.log(2.0 * fwhm)))
1291
+ sig2 = sig * sig
1292
+ star = np.exp(-0.5 / sig2 * ((x.astype(np.float32) - x0)**2 + (y.astype(np.float32) - y0)**2))
1293
+ in_sci = (star / np.sum(star)).astype(np.float32) # normalize to 1
1294
+ in_wht = np.zeros(in_shape, dtype=np.float32)
1295
+ mask = np.where((x.astype(np.float32) - x0)**2 + (y.astype(np.float32) - y0)**2 <= 10)
1296
+ in_wht[mask] = 1.0
1297
+
1298
+ # linear shift:
1299
+ xp = x + 50
1300
+ yp = y + 50
1301
+
1302
+ pixmap = np.dstack([xp, yp])
1303
+
1304
+ out_shape = (int(yp.max()) + 1, int(xp.max()) + 1)
1305
+ # make sure distorion is not moving flux out of the image towards negative
1306
+ # coordinates (just because of the simple way of how we account for output
1307
+ # image size)
1308
+ assert np.min(xp) > -0.5 and np.min(yp) > -0.5
1309
+
1310
+ out_img = np.zeros(out_shape, dtype=np.float32) - 1.11
1311
+ out_ctx = np.zeros((1, ) + out_shape, dtype=np.int32)
1312
+ out_wht = np.zeros(out_shape, dtype=np.float32)
1313
+
1314
+ driz = resample.Drizzle(
1315
+ kernel='square',
1316
+ out_shape=out_shape,
1317
+ fillval="indef",
1318
+ exptime=0.0,
1319
+ )
1320
+
1321
+ driz.add_image(in_sci, weight_map=in_wht, exptime=1.0, pixmap=pixmap)
1322
+ assert np.isnan(driz.out_img[0, 0])
1323
+ assert driz.out_img[int(y0) + 50, int(x0) + 50] > 0.0
1324
+
1325
+ driz = resample.Drizzle(
1326
+ kernel='square',
1327
+ out_shape=out_shape,
1328
+ fillval="-1.11",
1329
+ out_img=out_img.copy(),
1330
+ out_ctx=out_ctx.copy(),
1331
+ out_wht=out_wht.copy(),
1332
+ exptime=0.0,
1333
+ )
1334
+ driz.add_image(in_sci, weight_map=in_wht, exptime=1.0, pixmap=pixmap)
1335
+ assert np.allclose(driz.out_img[0, 0], -1.11, rtol=0.0, atol=1.0e-7)
1336
+ assert driz.out_img[int(y0) + 50, int(x0) + 50] > 0.0
1337
+ assert set(driz.out_ctx.ravel().tolist()) == {0, 1}
1338
+
1339
+ # test same with numeric fillval:
1340
+ driz = resample.Drizzle(
1341
+ kernel='square',
1342
+ out_shape=out_shape,
1343
+ fillval=-1.11,
1344
+ out_img=out_img.copy(),
1345
+ out_ctx=out_ctx.copy(),
1346
+ out_wht=out_wht.copy(),
1347
+ exptime=0.0,
1348
+ )
1349
+ driz.add_image(in_sci, weight_map=in_wht, exptime=1.0, pixmap=pixmap)
1350
+ assert np.allclose(driz.out_img[0, 0], -1.11, rtol=0.0, atol=1.0e-7)
1351
+ assert np.allclose(float(driz.fillval), -1.11, rtol=0.0, atol=np.finfo(float).eps)
1352
+
1353
+ # make sure code raises exception for unsuported fillval:
1354
+ with pytest.raises(ValueError) as err_info:
1355
+ resample.Drizzle(
1356
+ kernel='square',
1357
+ out_shape=out_shape,
1358
+ fillval="fillval",
1359
+ exptime=0.0,
1360
+ )
1361
+ assert str(err_info.value) == "could not convert string to float: 'fillval'"
1362
+
1363
+
1364
+ def test_resample_get_shape_from_pixmap():
1365
+ n = 200
1366
+ in_shape = (n, n)
1367
+
1368
+ # input coordinate grid:
1369
+ y, x = np.indices(in_shape, dtype=np.float64)
1370
+
1371
+ # simulate constant data:
1372
+ in_sci = np.ones(in_shape, dtype=np.float32)
1373
+ in_wht = np.ones(in_shape, dtype=np.float32)
1374
+
1375
+ pixmap = np.dstack([x, y])
1376
+
1377
+ driz = resample.Drizzle(
1378
+ kernel='point',
1379
+ exptime=0.0,
1380
+ )
1381
+
1382
+ driz.add_image(in_sci, weight_map=in_wht, exptime=1.0, pixmap=pixmap)
1383
+ assert driz.out_img.shape == in_shape
1384
+
1385
+
1386
+ def test_resample_counts_units():
1387
+ n = 200
1388
+ in_shape = (n, n)
1389
+
1390
+ # input coordinate grid:
1391
+ y, x = np.indices(in_shape, dtype=np.float64)
1392
+ pixmap = np.dstack([x, y])
1393
+
1394
+ # simulate constant data:
1395
+ in_sci = np.ones(in_shape, dtype=np.float32)
1396
+ in_wht = np.ones(in_shape, dtype=np.float32)
1397
+
1398
+ driz = resample.Drizzle()
1399
+ driz.add_image(in_sci, weight_map=in_wht, exptime=1.0, pixmap=pixmap, in_units='cps')
1400
+ cps_max_val = driz.out_img.max()
1401
+
1402
+ driz = resample.Drizzle()
1403
+ driz.add_image(in_sci, weight_map=in_wht, exptime=2.0, pixmap=pixmap, in_units='counts')
1404
+ counts_max_val = driz.out_img.max()
1405
+
1406
+ assert abs(counts_max_val - cps_max_val / 2.0) < 1.0e-14
1407
+
1408
+
1409
+ def test_resample_inconsistent_output():
1410
+ n = 200
1411
+ out_shape = (n, n)
1412
+
1413
+ # different shapes:
1414
+ out_img = np.zeros((n, n), dtype=np.float32)
1415
+ out_ctx = np.zeros((1, n, n + 1), dtype=np.int32)
1416
+ out_wht = np.zeros((n + 1, n + 1), dtype=np.float32)
1417
+
1418
+ # shape from out_img:
1419
+ driz = resample.Drizzle(
1420
+ kernel='point',
1421
+ exptime=0.0,
1422
+ out_img=out_img,
1423
+ )
1424
+ assert driz.out_img.shape == out_shape
1425
+
1426
+ # inconsistent shapes:
1427
+ out_shape = (n + 1, n)
1428
+ with pytest.raises(ValueError) as err_info:
1429
+ resample.Drizzle(
1430
+ kernel='point',
1431
+ exptime=0.0,
1432
+ out_shape=out_shape,
1433
+ out_img=out_img,
1434
+ out_ctx=out_ctx,
1435
+ out_wht=out_wht,
1436
+ )
1437
+ assert str(err_info.value).startswith("Inconsistent data shapes specified")