Skip to content

Commit

Permalink
..
Browse files Browse the repository at this point in the history
  • Loading branch information
cshanahan1 committed Mar 7, 2024
1 parent 3c80093 commit c20599c
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 129 deletions.
5 changes: 5 additions & 0 deletions specreduce/background.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ def __post_init__(self):
raise ValueError("background regions overlapped")
if np.any(np.sum(bkg_wimage, axis=self.crossdisp_axis) == 0):
raise ValueError("background window does not remain in bounds across entire dispersion axis") # noqa
if np.all(img.mask[bkg_wimage > 0]):
raise ValueError("Image is fully masked within background window determined by `width`.")

# check if image contained within background window is fully-nonfinite and raise an error if so

if self.statistic == 'median':
# make it clear in the expose image that partial pixels are fully-weighted
Expand Down Expand Up @@ -333,6 +337,7 @@ def sub_image(self, image=None):
`~specutils.Spectrum1D` object with same shape as ``image``.
"""
image = self._parse_image(image)
print(image.data)

# a compare_wcs argument is needed for Spectrum1D.subtract() in order to
# avoid a TypeError from SpectralCoord when image's spectral axis is in
Expand Down
4 changes: 4 additions & 0 deletions specreduce/core.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst

from copy import deepcopy
import inspect
from dataclasses import dataclass

Expand Down Expand Up @@ -140,6 +141,9 @@ def _mask_and_nonfinite_data_handling(self, image, mask):


if mask_treatment == 'zero-fill':
# make a copy of the input image since we will be modifying it
image = deepcopy(image)

# if mask_treatment is 'zero_fill', set masked values to zero in
# image data and drop image.mask. note that this is done after
# _combine_mask_with_nonfinite_from_data, so non-finite values in
Expand Down
129 changes: 88 additions & 41 deletions specreduce/tests/test_background.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from astropy.nddata import NDData
import astropy.units as u
import numpy as np
import pytest
Expand Down Expand Up @@ -113,7 +114,11 @@ def test_warnings_errors(mk_test_spec_no_spectral_axis):

def test_trace_inputs(mk_test_img_raw):

# Tests for the input argument 'traces' to `Background`.
"""
Tests for the input argument 'traces' to `Background`. This should accept
a list of or a single Trace object, or a list of or a single (positive)
number to define a FlatTrace.
"""

image = mk_test_img_raw

Expand All @@ -140,60 +145,102 @@ def test_trace_inputs(mk_test_img_raw):

class TestMasksBackground():

def mk_img(self, nrows=4, ncols=5, nan_slices=None, add_noise=True):
"""
Various test functions to test how masked and non-finite data is handled
in `Background.
"""

# make a image to test masking in Background
# image is n columns, each col_i = i + noise
def mk_img(self, nrows=4, ncols=5, nan_slices=None):
"""
Make a simpleimage to test masking in Background.
Optionally add NaNs to data. Returned array is in u.DN.
"""

img = np.tile((np.arange(1., ncols+1)), (nrows, 1))

noise = 0
if add_noise:
np.random.seed(7)
sigma_noise = 0.2
noise = np.random.normal(scale=sigma_noise, size=(nrows, ncols))
img += noise

if nan_slices: # add nans in data
for s in nan_slices:
img[s] = np.nan

return img * u.DN

def test_fully_masked(self):
"""
Test that the appropriate error is raised by `Background` when image
is fully masked/NaN.
"""

with pytest.raises(ValueError, match='Image is fully masked.'):
# fully NaN image
img = np.zeros((4, 5)) * np.nan
Background(img, traces=FlatTrace(self.mk_img(), 2))

with pytest.raises(ValueError, match='Image is fully masked.'):
# fully masked image (should be equivilant)
img = NDData(np.ones((4, 5)), mask=np.ones((4, 5)))
Background(img, traces=FlatTrace(self.mk_img(), 2))

# Now test that an image that isn't fully masked, but is fully masked
# within the window determined by `width`, produces the correct result
msg = 'Image is fully masked within background window determined by `width`.'
with pytest.raises(ValueError, match=msg):
img = self.mk_img(nrows=12, ncols=12, nan_slices=[np.s_[3:10, :]])
Background(img, traces=FlatTrace(img, 6), width=7)

@pytest.mark.filterwarnings("ignore:background window extends beyond image boundaries")
@pytest.mark.parametrize("method,expected",
[("filter", np.array([1., 2., 3., 4., 5., 6., 7.,
8., 9., 10., 11., 12.]))])
8., 9., 10., 11., 12.])),
("omit", np.array([0., 2., 3., 0., 5., 6.,
7., 0., 9., 10., 11., 12.])),
("zero-fill", np.array([ 0.58333333, 2., 3.,
2.33333333, 5., 6., 7.,
7.33333333, 9., 10., 11.,
12.]))])
def test_mask_treatment_bkg_img_spectrum(self, method, expected):

"""This test function tests creating a Background object, and computing
`bkg_image` and `bkg_spectrum` when there is masked data. The test
image has NaNs, which are added to the masked array. This test is
parameterized over all currently implemented mask handling methods
(filter, omit, and zero-fill) to test that all three work as
intended."""
"""
This test function tests `Backgroud.bkg_image` and
`Background.bkg_spectrum` when there is masked data. It also tests
background subtracting the image, and returning the spectrum of the
background subtracted image. This test is parameterized over all
currently implemented mask handling methods (filter, omit, and
zero-fill) to test that all three work as intended. The window size is
set to use the entire image array, so warning about background window
is ignored."""

img_size = 12 # square 12 x 12 image

# make image, set some value to nan so they are included in mask
image = self.mk_img(nrows=img_size, ncols=img_size,
# make image, set some value to nan, which will be masked in the function
image1 = self.mk_img(nrows=img_size, ncols=img_size,
nan_slices=[np.s_[5:10, 0], np.s_[7:12, 3],
np.s_[2, 7]], add_noise=False)

# construct a flat trace in center of image
trace = FlatTrace(image, img_size/2)

# create 'Background' with `mask_treatment` set
background = Background(image, mask_treatment=method,
traces=trace)

# test background image
bk_img = background.bkg_image()
# change this and following assertions to assert_quantity_allclose once
# issue #213 is fixed
np.testing.assert_allclose(bk_img.flux.value,
np.tile(expected, (img_size, 1)))

# test background spectrum
bk_spec = background.bkg_spectrum()
np.testing.assert_allclose(bk_spec.flux.value, expected * img_size)

np.s_[2, 7]])

# also make an image that doesn't have nonf data values, but has
# masked values at the same locations, to make sure they give the same
# results
mask = ~np.isfinite(image1)
dat = self.mk_img(nrows=img_size, ncols=img_size)
image2 = NDData(dat, mask=mask)

for image in [image1, image2]:

# construct a flat trace in center of image
trace = FlatTrace(image, img_size/2)

# create 'Background' object with `mask_treatment` set
# 'width' should be > size of image to use all pix (but warning will
# be raised, which we ignore.)
background = Background(image, mask_treatment=method,
traces=trace, width=img_size+1)

# test background image matches 'expected'
bk_img = background.bkg_image()
# change this and following assertions to assert_quantity_allclose once
# issue #213 is fixed
np.testing.assert_allclose(bk_img.flux.value,
np.tile(expected, (img_size, 1)))

# test background spectrum matches 'expected' times the number of rows
# since this is a sum
bk_spec = background.bkg_spectrum()
np.testing.assert_allclose(bk_spec.flux.value, expected * img_size)
Loading

0 comments on commit c20599c

Please sign in to comment.