@@ -359,7 +359,8 @@ def _parse_image(self, image,
359
359
elif mask is not None :
360
360
pass
361
361
else :
362
- mask = ~ np .isfinite (img )
362
+ # if user provides no mask at all, don't mask anywhere
363
+ mask = np .zeros_like (img )
363
364
364
365
if img .shape != mask .shape :
365
366
raise ValueError ('image and mask shapes must match.' )
@@ -484,13 +485,15 @@ def __call__(self, image=None, trace_object=None,
484
485
# parse image and replace optional arguments with updated values
485
486
self .image = self ._parse_image (image , variance , mask , unit , disp_axis )
486
487
variance = self .image .uncertainty .array
488
+ mask = self .image .mask
487
489
unit = self .image .unit
488
490
489
- # mask any previously uncaught invalid values
490
- or_mask = np .logical_or (mask ,
491
+ img = np .ma .masked_array (self .image .data , mask = mask )
492
+
493
+ # create separate mask including any previously uncaught non-finite
494
+ # values for purposes of calculating fit
495
+ or_mask = np .logical_or (img .mask ,
491
496
~ np .isfinite (self .image .data ))
492
- img = np .ma .masked_array (self .image .data , or_mask )
493
- mask = img .mask
494
497
495
498
# If the trace is not flat, shift the rows in each column
496
499
# so the image is aligned along the trace:
@@ -510,26 +513,43 @@ def __call__(self, image=None, trace_object=None,
510
513
)
511
514
512
515
# co-add signal in each image column
513
- ncols = img .shape [crossdisp_axis ]
514
- xd_pixels = np .arange (ncols ) # y plot dir / x spec dir
515
- coadd = img .sum (axis = disp_axis ) / ncols
516
+ nrows = img .shape [crossdisp_axis ]
517
+ xd_pixels = np .arange (nrows ) # counted in y dir on plot (or x in spec)
518
+
519
+ row_mask = np .logical_or .reduce (or_mask , axis = disp_axis )
520
+ coadd = np .ma .masked_array (np .sum (img , axis = disp_axis ) / nrows ,
521
+ mask = row_mask )
522
+ # (mask rows with non-finite sums for fit to work later on)
516
523
517
- # fit source profile, using Gaussian model as a template
524
+ # fit source profile to brightest row , using Gaussian model as template
518
525
# NOTE: could add argument for users to provide their own model
519
526
gauss_prof = models .Gaussian1D (amplitude = coadd .max (),
520
527
mean = coadd .argmax (), stddev = 2 )
521
528
522
- # Fit extraction kernel to column with combined gaussian/bkgrd model
529
+ # Fit extraction kernel to column's finite values with combined model
530
+ # (must exclude masked indices manually; LevMarLSQFitter does not)
523
531
ext_prof = gauss_prof + bkgrd_prof
524
532
fitter = fitting .LevMarLSQFitter ()
525
- fit_ext_kernel = fitter (ext_prof , xd_pixels , coadd )
533
+ fit_ext_kernel = fitter (ext_prof ,
534
+ xd_pixels [~ row_mask ], coadd [~ row_mask ])
526
535
527
- # use compound model to fit a kernel to each image column
536
+ # use compound model to fit a kernel to each fully finite image column
528
537
# NOTE: infers Gaussian1D source profile; needs generalization for others
538
+ col_mask = np .logical_or .reduce (or_mask , axis = crossdisp_axis )
539
+ nonf_col = [np .nan ] * img .shape [crossdisp_axis ]
540
+
529
541
kernel_vals = []
530
542
norms = []
531
543
for col_pix in range (img .shape [disp_axis ]):
532
- # set gaussian model's mean as column's corresponding trace value
544
+ # for now, skip columns with any non-finite values
545
+ # NOTE: fit and other kernel operations should support masking again
546
+ # once a fix is in for renormalizing columns with non-finite values
547
+ if col_mask [col_pix ]:
548
+ kernel_vals .append (nonf_col )
549
+ norms .append (np .nan )
550
+ continue
551
+
552
+ # else, set compound model's mean to column's matching trace value
533
553
fit_ext_kernel .mean_0 = mean_init_guess [col_pix ]
534
554
535
555
# NOTE: support for variable FWHMs forthcoming and would be here
@@ -543,15 +563,15 @@ def __call__(self, image=None, trace_object=None,
543
563
* fit_ext_kernel .stddev_0 * np .sqrt (2 * np .pi ))
544
564
545
565
# transform fit-specific information
546
- kernel_vals = np .array (kernel_vals ).T
566
+ kernel_vals = np .vstack (kernel_vals ).T
547
567
norms = np .array (norms )
548
568
549
- # calculate kernel normalization, masking NaNs
550
- g_x = np .ma . sum (kernel_vals ** 2 / variance , axis = crossdisp_axis )
569
+ # calculate kernel normalization
570
+ g_x = np .sum (kernel_vals ** 2 / variance , axis = crossdisp_axis )
551
571
552
572
# sum by column weights
553
- weighted_img = np .ma . divide (img * kernel_vals , variance )
554
- result = np .ma . sum (weighted_img , axis = crossdisp_axis ) / g_x
573
+ weighted_img = np .divide (img * kernel_vals , variance )
574
+ result = np .sum (weighted_img , axis = crossdisp_axis ) / g_x
555
575
556
576
# multiply kernel normalization into the extracted signal
557
577
extraction = result * norms
0 commit comments