Commit c7e910e7 authored by Ole Streicher's avatar Ole Streicher

New upstream version 1.2.0

parent d5082707
1.2.0 (Unreleased)
1.2.0 (2016-12-13)
------------------
ccdproc has now the following additional dependency:
- scikit-image.
New Features
^^^^^^^^^^^^
- Add an optional attribute named ``filenames`` to ``ImageFileCollection``,
so that users can pass a list of FITS files to the collection. [#374, #403]
- Added ``block_replicate``, ``block_reduce`` and ``block_average`` functions.
[#402]
- Added ``median_filter`` function. [#420]
- ``combine`` now takes an additional ``combine_uncertainty_function`` argument
which is passed as ``uncertainty_func`` parameter to
``Combiner.median_combine`` or ``Combiner.average_combine``. [#416]
- Added ``ccdmask`` function. [#414, #432]
Other Changes and Additions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- ccdprocs core functions now explicitly add HIERARCH cards. [#359, #399, #413]
- ``combine`` now accepts a ``dtype`` argument which is passed to
``Combiner.__init__``. [#391, #392]
- Removed ``CaseInsensitiveOrderedDict`` because it is not used in the current
code base. [#428]
Bug Fixes
^^^^^^^^^
- The default dtype of the ``combine``-result doesn't depend on the dtype
of the first CCDData anymore. This also corrects the memory consumption
calculation. [#391, #392]
- ``ccd_process`` now copies the meta of the input when subtracting the
master bias. [#404]
- Fixed ``combine`` with ``CCDData`` objects using ``StdDevUncertainty`` as
uncertainty. [#416, #424]
- ``ccds`` generator from ``ImageFileCollection`` now uses the full path to the
file when calling ``fits_ccddata_reader``. [#421 #422]
1.1.0 (2016-08-01)
------------------
......@@ -145,6 +186,8 @@ There was no 0.3.2 release because of a packaging error.
New Features
^^^^^^^^^^^^
- Add CCDData generator for ImageCollection [#405]
Other Changes and Additions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
......
Metadata-Version: 1.1
Name: ccdproc
Version: 1.1.0
Version: 1.2.0
Summary: Astropy affiliated package
Home-page: http://ccdproc.readthedocs.io/
Author: Steve Crawford and Matt Craig
......
......@@ -10,8 +10,8 @@ from collections import OrderedDict
import numpy as np
from astropy.nddata import NDDataArray
from astropy.nddata.nduncertainty import StdDevUncertainty, NDUncertainty, MissingDataAssociationException
from astropy.nddata import (NDDataArray, StdDevUncertainty, NDUncertainty,
MissingDataAssociationException)
from astropy.io import fits, registry
from astropy import units as u
from astropy import log
......@@ -22,7 +22,10 @@ from astropy.wcs import WCS
# described in https://github.com/astropy/astropy/issues/4825
import astropy
from distutils.version import LooseVersion
if LooseVersion(astropy.__version__) < LooseVersion('1.2'): # pragma: no cover
_ASTROPY_LT_1_2 = LooseVersion(astropy.__version__) < LooseVersion('1.2')
if _ASTROPY_LT_1_2:
class ParentNDDataDescriptor(object):
def __get__(self, obj, objtype=None):
......@@ -59,6 +62,49 @@ if LooseVersion(astropy.__version__) < LooseVersion('1.2'): # pragma: no cover
__all__ = ['CCDData', 'fits_ccddata_reader', 'fits_ccddata_writer']
# Global value which can turn on/off the unit requirements when creating a
# CCDData. Should be used with care because several functions actually break
# if the unit is None!
_config_ccd_requires_unit = True
if not _ASTROPY_LT_1_2:
from astropy.utils.decorators import sharedmethod
def _arithmetic(op):
"""Decorator factory which temporarly disables the need for a unit when
creating a new CCDData instance. The final result must have a unit.
Parameters
----------
op : function
The function to apply. Supported are:
- ``np.add``
- ``np.subtract``
- ``np.multiply``
- ``np.true_divide``
Notes
-----
Should only be used on CCDData ``add``, ``subtract``, ``divide`` or
``multiply`` because only these methods from NDArithmeticMixin are
overwritten.
"""
def decorator(func):
def inner(self, operand, operand2=None, **kwargs):
global _config_ccd_requires_unit
_config_ccd_requires_unit = False
result = self._prepare_then_do_arithmetic(op, operand,
operand2, **kwargs)
# Wrap it again as CCDData so it checks the final unit.
_config_ccd_requires_unit = True
return result.__class__(result)
inner.__doc__ = ("See `astropy.nddata.NDArithmeticMixin.{}`."
"".format(func.__name__))
return sharedmethod(inner)
return decorator
class CCDData(NDDataArray):
"""A class describing basic CCD data.
......@@ -161,7 +207,10 @@ class CCDData(NDDataArray):
raise ValueError("can't have both header and meta.")
super(CCDData, self).__init__(*args, **kwd)
if self.unit is None:
# Check if a unit is set. This can be temporarly disabled by the
# _CCDDataUnit contextmanager.
if _config_ccd_requires_unit and self.unit is None:
raise ValueError("a unit for CCDData must be specified.")
@property
......@@ -516,6 +565,15 @@ class CCDData(NDDataArray):
return self._ccddata_arithmetic(other, np.subtract,
scale_uncertainty=False)
# Use NDDataArithmetic methods if astropy version is 1.2 or greater
if not _ASTROPY_LT_1_2:
del add, subtract, divide, multiply, _ccddata_arithmetic
add = _arithmetic(np.add)(NDDataArray.add)
subtract = _arithmetic(np.subtract)(NDDataArray.subtract)
multiply = _arithmetic(np.multiply)(NDDataArray.multiply)
divide = _arithmetic(np.true_divide)(NDDataArray.divide)
def _insert_in_metadata_fits_safe(self, key, value):
"""
Insert key/value pair into metadata in a way that FITS can serialize.
......@@ -550,7 +608,8 @@ class CCDData(NDDataArray):
# problems...
# Shorten, sort of...
short_name = _short_names[key]
self.meta[key] = (short_name, "Shortened name for ccdproc command")
self.meta['HIERARCH {0}'.format(key.upper())] = (
short_name, "Shortened name for ccdproc command")
self.meta[short_name] = value
else:
self.meta[key] = value
......@@ -614,18 +673,18 @@ def fits_ccddata_reader(filename, hdu=0, unit=None, hdu_uncertainty='UNCERT',
with fits.open(filename, **kwd) as hdus:
hdr = hdus[hdu].header
if hdu_uncertainty in hdus:
if hdu_uncertainty is not None and hdu_uncertainty in hdus:
uncertainty = StdDevUncertainty(hdus[hdu_uncertainty].data)
else:
uncertainty = None
if hdu_mask in hdus:
if hdu_mask is not None and hdu_mask in hdus:
# Mask is saved as uint but we want it to be boolean.
mask = hdus[hdu_mask].data.astype(np.bool_)
else:
mask = None
if hdu_flags in hdus:
if hdu_flags is not None and hdu_flags in hdus:
raise NotImplementedError('loading flags is currently not '
'supported.')
......
This diff is collapsed.
This diff is collapsed.
......@@ -15,6 +15,8 @@ from astropy.extern import six
import warnings
from astropy.utils.exceptions import AstropyUserWarning
from .ccddata import fits_ccddata_reader
logger = logging.getLogger(__name__)
__all__ = ['ImageFileCollection']
......@@ -34,7 +36,7 @@ class ImageFileCollection(object):
Parameters
----------
location : str or None, optional
path to directory containing FITS files.
Path to directory containing FITS files.
Default is ``None``.
keywords : list of str, '*' or None, optional
......@@ -51,17 +53,25 @@ class ImageFileCollection(object):
list.
Default is ``None``.
filenames: str, list of str, or None, optional
List of the names of FITS files which will be added to the collection.
The filenames are assumed to be in ``location``.
Default is ``None``.
Raises
------
ValueError
Raised if keywords are set to a combination of '*' and any other
value.
"""
def __init__(self, location=None, keywords=None, info_file=None):
def __init__(self, location=None, keywords=None, info_file=None,
filenames=None):
self._location = location
self._filenames = filenames
self._files = []
if location:
self._files = self._fits_files_in_directory()
self._files = self._get_files()
if self._files == []:
warnings.warn("no FITS files in the collection.",
AstropyUserWarning)
......@@ -262,7 +272,8 @@ class ImageFileCollection(object):
filtered_files = self.summary_info['file'].compressed()
self.summary_info['file'].mask = current_file_mask
if include_path:
filtered_files = [path.join(self._location, f) for f in filtered_files]
filtered_files = [path.join(self._location, f)
for f in filtered_files]
return filtered_files
def refresh(self):
......@@ -271,7 +282,7 @@ class ImageFileCollection(object):
"""
keywords = '*' if self._all_keywords else self.keywords
# Re-load list of files
self._files = self._fits_files_in_directory()
self._files = self._get_files()
self._summary_info = self._fits_summary(header_keywords=keywords)
def sort(self, keys=None):
......@@ -290,6 +301,26 @@ class ImageFileCollection(object):
self._summary_info.sort(keys)
self._files = list(self.summary_info['file'])
def _get_files(self):
""" Helper method which checks whether ``files`` should be set
to a subset of file names or to all file names in a directory.
Returns
-------
files : list or str
List of file names which will be added to the collection.
"""
files = []
if self._filenames:
if isinstance(self._filenames, six.string_types):
files.append(self._filenames)
else:
files = self._filenames
else:
files = self._fits_files_in_directory()
return files
def _dict_from_fits_header(self, file_name, input_summary=None,
missing_marker=None):
"""
......@@ -573,6 +604,7 @@ class ImageFileCollection(object):
overwrite=False,
do_not_scale_image_data=True,
return_fname=False,
ccd_kwargs=None,
**kwd):
"""
Generator that yields each {name} in the collection.
......@@ -619,6 +651,14 @@ class ImageFileCollection(object):
not the full path to the file.
Default is ``False``.
ccd_kwargs : dict, optional
Dict with parameters for `~ccdproc.fits_ccddata_reader`.
For instance, the key ``'unit'`` can be used to specify the unit
of the data. If ``'unit'`` is not given then ``'adu'`` is used as
the default unit.
See `~ccdproc.fits_ccddata_reader` for a complete list of
parameters that can be passed through ``ccd_kwargs``.
kwd :
Any additional keywords are used to filter the items returned; see
Examples for details.
......@@ -645,6 +685,8 @@ class ImageFileCollection(object):
if kwd:
self._find_keywords_by_values(**kwd)
ccd_kwargs = ccd_kwargs or {}
for full_path in self._paths():
no_scale = do_not_scale_image_data
hdulist = fits.open(full_path,
......@@ -652,14 +694,16 @@ class ImageFileCollection(object):
file_name = path.basename(full_path)
return_options = {'header': hdulist[0].header,
'hdu': hdulist[0],
'data': hdulist[0].data}
return_options = {
'header': lambda: hdulist[0].header,
'hdu': lambda: hdulist[0],
'data': lambda: hdulist[0].data,
'ccd': lambda: fits_ccddata_reader(full_path, **ccd_kwargs)
}
try:
yield (return_options[return_type] # pragma: no branch
yield (return_options[return_type]() # pragma: no branch
if (not return_fname) else
(return_options[return_type], file_name))
(return_options[return_type](), file_name))
except KeyError:
raise ValueError('no generator for {}'.format(return_type))
......@@ -701,22 +745,25 @@ class ImageFileCollection(object):
return self._generator('header',
do_not_scale_image_data=do_not_scale_image_data,
**kwd)
headers.__doc__ = _generator.__doc__.format(name='header',
default_scaling='True',
return_type='astropy.io.fits.Header')
headers.__doc__ = _generator.__doc__.format(
name='header', default_scaling='True',
return_type='astropy.io.fits.Header')
def hdus(self, do_not_scale_image_data=False, **kwd):
return self._generator('hdu',
do_not_scale_image_data=do_not_scale_image_data,
**kwd)
hdus.__doc__ = _generator.__doc__.format(name='HDU',
default_scaling='False',
return_type='astropy.io.fits.HDU')
hdus.__doc__ = _generator.__doc__.format(
name='HDU', default_scaling='False', return_type='astropy.io.fits.HDU')
def data(self, do_not_scale_image_data=False, **kwd):
return self._generator('data',
do_not_scale_image_data=do_not_scale_image_data,
**kwd)
data.__doc__ = _generator.__doc__.format(name='image',
default_scaling='False',
return_type='numpy.ndarray')
data.__doc__ = _generator.__doc__.format(
name='image', default_scaling='False', return_type='numpy.ndarray')
def ccds(self, ccd_kwargs=None, **kwd):
return self._generator('ccd', ccd_kwargs=ccd_kwargs, **kwd)
ccds.__doc__ = _generator.__doc__.format(
name='CCDData', default_scaling='True', return_type='ccdproc.CCDData')
......@@ -3,6 +3,7 @@ from __future__ import (absolute_import, division, print_function,
from functools import wraps
import inspect
from itertools import chain
import numpy as np
......@@ -77,14 +78,12 @@ def log_to_metadata(func):
# Logging is not turned off, but user did not provide a value
# so construct one.
key = func.__name__
pos_args = ["{0}={1}".format(arg_name,
_replace_array_with_placeholder(arg_value))
for arg_name, arg_value
in zip(original_positional_args, args)]
kwd_args = ["{0}={1}".format(k, _replace_array_with_placeholder(v))
for k, v in six.iteritems(kwd)]
pos_args.extend(kwd_args)
log_val = ", ".join(pos_args)
all_args = chain(zip(original_positional_args, args),
six.iteritems(kwd))
all_args = ["{0}={1}".format(name,
_replace_array_with_placeholder(val))
for name, val in all_args]
log_val = ", ".join(all_args)
log_val = log_val.replace("\n", "")
meta_dict = {key: log_val}
......
This diff is collapsed.
......@@ -19,6 +19,13 @@ from ..ccddata import CCDData
from ..core import *
from ..core import _blkavg
try:
from ..core import block_reduce
HAS_BLOCK_X_FUNCS = True
except:
HAS_BLOCK_X_FUNCS = False
# test creating deviation
# success expected if u_image * u_gain = u_readnoise
......@@ -612,6 +619,77 @@ def test_rebin_ccddata(ccd_data, mask_data, uncertainty):
assert b.uncertainty.array.shape == (20, 20)
# test block_reduce and block_replicate wrapper
@pytest.mark.skipif(not HAS_BLOCK_X_FUNCS, reason="needs astropy >= 1.1.x")
def test_block_reduce():
ccd = CCDData(np.ones((4, 4)), unit='adu', meta={'testkw': 1},
mask=np.zeros((4, 4), dtype=bool),
uncertainty=StdDevUncertainty(np.ones((4, 4))),
wcs=np.zeros((4, 4)))
ccd_summed = block_reduce(ccd, (2, 2))
assert isinstance(ccd_summed, CCDData)
assert np.all(ccd_summed.data == 4)
assert ccd_summed.data.shape == (2, 2)
assert ccd_summed.unit == u.adu
# Other attributes are set to None. In case the function is modified to
# work on these attributes correctly those tests need to be updated!
assert ccd_summed.meta == {'testkw': 1}
assert ccd_summed.mask is None
assert ccd_summed.wcs is None
assert ccd_summed.uncertainty is None
# Make sure meta is copied
ccd_summed.meta['testkw2'] = 10
assert 'testkw2' not in ccd.meta
@pytest.mark.skipif(not HAS_BLOCK_X_FUNCS, reason="needs astropy >= 1.1.x")
def test_block_average():
ccd = CCDData(np.ones((4, 4)), unit='adu', meta={'testkw': 1},
mask=np.zeros((4, 4), dtype=bool),
uncertainty=StdDevUncertainty(np.ones((4, 4))),
wcs=np.zeros((4, 4)))
ccd.data[::2, ::2] = 2
ccd_avgd = block_average(ccd, (2, 2))
assert isinstance(ccd_avgd, CCDData)
assert np.all(ccd_avgd.data == 1.25)
assert ccd_avgd.data.shape == (2, 2)
assert ccd_avgd.unit == u.adu
# Other attributes are set to None. In case the function is modified to
# work on these attributes correctly those tests need to be updated!
assert ccd_avgd.meta == {'testkw': 1}
assert ccd_avgd.mask is None
assert ccd_avgd.wcs is None
assert ccd_avgd.uncertainty is None
# Make sure meta is copied
ccd_avgd.meta['testkw2'] = 10
assert 'testkw2' not in ccd.meta
@pytest.mark.skipif(not HAS_BLOCK_X_FUNCS, reason="needs astropy >= 1.1.x")
def test_block_replicate():
ccd = CCDData(np.ones((4, 4)), unit='adu', meta={'testkw': 1},
mask=np.zeros((4, 4), dtype=bool),
uncertainty=StdDevUncertainty(np.ones((4, 4))),
wcs=np.zeros((4, 4)))
ccd_repl = block_replicate(ccd, (2, 2))
assert isinstance(ccd_repl, CCDData)
assert np.all(ccd_repl.data == 0.25)
assert ccd_repl.data.shape == (8, 8)
assert ccd_repl.unit == u.adu
# Other attributes are set to None. In case the function is modified to
# work on these attributes correctly those tests need to be updated!
assert ccd_repl.meta == {'testkw': 1}
assert ccd_repl.mask is None
assert ccd_repl.wcs is None
assert ccd_repl.uncertainty is None
# Make sure meta is copied
ccd_repl.meta['testkw2'] = 10
assert 'testkw2' not in ccd.meta
#test blockaveraging ndarray
def test__blkavg_ndarray(ccd_data):
with pytest.raises(TypeError):
......@@ -809,10 +887,8 @@ def test_wcs_project_onto_scale_wcs(ccd_data):
assert new_ccd.wcs.wcs.compare(target_wcs.wcs)
# Define a cutout from the new array that should match the old.
new_lower_bound = (np.array(new_ccd.shape) -
np.array(ccd_data.shape)) / 2
new_upper_bound = (np.array(new_ccd.shape) +
np.array(ccd_data.shape)) / 2
new_lower_bound = (np.array(new_ccd.shape) - np.array(ccd_data.shape)) // 2
new_upper_bound = (np.array(new_ccd.shape) + np.array(ccd_data.shape)) // 2
data_cutout = new_ccd.data[new_lower_bound[0]:new_upper_bound[0],
new_lower_bound[1]:new_upper_bound[1]]
......@@ -824,7 +900,7 @@ def test_wcs_project_onto_scale_wcs(ccd_data):
# Mask should be true for four pixels (all nearest neighbors)
# of the single pixel we masked initially.
new_center = new_ccd.wcs.wcs.crpix
new_center = np.array(new_ccd.wcs.wcs.crpix, dtype=int, copy=False)
assert np.all(new_ccd.mask[new_center[0]:new_center[0]+2,
new_center[1]:new_center[1]+2])
......@@ -877,6 +953,7 @@ def test_ccd_process():
# test the through ccd_process
ccd_data = CCDData(10.0 * np.ones((100, 100)), unit=u.adu)
ccd_data.data[:, -10:] = 2
ccd_data.meta['testkw'] = 100
mask = np.zeros((100, 90))
......@@ -905,3 +982,5 @@ def test_ccd_process():
occd.uncertainty.array)
np.testing.assert_array_equal(mask, occd.mask)
assert(occd.unit == u.electron)
# Make sure the original keyword is still present. Regression test for #401
assert occd.meta['testkw'] == 100
......@@ -285,6 +285,25 @@ def test_combine_average_fitsimages():
np.testing.assert_array_almost_equal(avgccd.data, ccd_by_combiner.data)
def test_combiner_result_dtype():
"""Regression test: #391
The result should have the appropriate dtype not the dtype of the first
input."""
ccd = CCDData(np.ones((3, 3), dtype=np.uint16), unit='adu')
res = combine([ccd, ccd.multiply(2)])
# The default dtype of Combiner is float64
assert res.data.dtype == np.float64
ref = np.ones((3, 3)) * 1.5
np.testing.assert_array_almost_equal(res.data, ref)
res = combine([ccd, ccd.multiply(2), ccd.multiply(3)], dtype=int)
# The result dtype should be integer:
assert res.data.dtype == np.int_
ref = np.ones((3, 3)) * 2
np.testing.assert_array_almost_equal(res.data, ref)
#test combiner convenience function works with list of ccddata objects
def test_combine_average_ccddata():
fitsfile = get_pkg_data_filename('data/a8280271.fits')
......@@ -343,6 +362,11 @@ def test_average_combine_uncertainty(ccd_data):
uncert_ref = np.sum(c.data_arr, 0) / np.sqrt(3)
np.testing.assert_array_equal(ccd.uncertainty.array, uncert_ref)
# Compare this also to the "combine" call
ccd2 = combine(ccd_list, method='average', combine_uncertainty_function=np.sum)
np.testing.assert_array_equal(ccd.data, ccd2.data)
np.testing.assert_array_equal(ccd.uncertainty.array, ccd2.uncertainty.array)
#test the optional uncertainty function in median_combine
def test_median_combine_uncertainty(ccd_data):
ccd_list = [ccd_data, ccd_data, ccd_data]
......@@ -351,6 +375,11 @@ def test_median_combine_uncertainty(ccd_data):
uncert_ref = np.sum(c.data_arr, 0) / np.sqrt(3)
np.testing.assert_array_equal(ccd.uncertainty.array, uncert_ref)
# Compare this also to the "combine" call
ccd2 = combine(ccd_list, method='median', combine_uncertainty_function=np.sum)
np.testing.assert_array_equal(ccd.data, ccd2.data)
np.testing.assert_array_equal(ccd.uncertainty.array, ccd2.uncertainty.array)
# test resulting uncertainty is corrected for the number of images
def test_combiner_uncertainty_average():
......
......@@ -14,6 +14,8 @@ import numpy as np
from astropy.tests.helper import pytest, catch_warnings
from astropy.utils.exceptions import AstropyUserWarning
from ccdproc import CCDData
from .. import image_collection
_filters = []
......@@ -85,6 +87,20 @@ class TestImageFileCollection(object):
location=triage_setup.test_dir, keywords=['imagetyp', 'filter'])
assert img_collection.summary is img_collection.summary_info
def test_filenames_are_set_properly(self, triage_setup):
fn = ['filter_no_object_bias.fit', 'filter_object_light_foo.fit']
img_collection = image_collection.ImageFileCollection(
location=triage_setup.test_dir, filenames=fn, keywords=['filter'])
assert img_collection.files == fn
img_collection.refresh()
assert img_collection.files == fn
fn = 'filter_no_object_bias.fit'
img_collection = image_collection.ImageFileCollection(
location=triage_setup.test_dir, filenames=fn, keywords=['filter'])
assert img_collection.files == [fn]
def test_files_with_compressed(self, triage_setup):
collection = image_collection.ImageFileCollection(
location=triage_setup.test_dir)
......@@ -217,6 +233,20 @@ class TestImageFileCollection(object):
for img in collection.data():
assert isinstance(img, np.ndarray)
def test_generator_ccds_without_unit(self, triage_setup):
collection = image_collection.ImageFileCollection(
location=triage_setup.test_dir, keywords=['imagetyp'])
with pytest.raises(ValueError):
ccd = next(collection.ccds())
def test_generator_ccds(self, triage_setup):
collection = image_collection.ImageFileCollection(
location=triage_setup.test_dir, keywords=['imagetyp'])
ccd_kwargs = {'unit': 'adu'}
for ccd in collection.ccds(ccd_kwargs=ccd_kwargs):
assert isinstance(ccd, CCDData)
def test_consecutive_fiilters(self, triage_setup):
collection = image_collection.ImageFileCollection(location=triage_setup.test_dir,
keywords=['imagetyp',
......@@ -627,3 +657,23 @@ class TestImageFileCollection(object):
assert((str(hdu.header), fname) in all_elements)
for i in range(len(collection.summary)):
assert(collection.summary['file'][i] == collection.files[i])
def test_ccds_generator_in_different_directory(self, triage_setup, tmpdir):
"""
Regression test for https://github.com/astropy/ccdproc/issues/421 in
which the ccds generator fails if the current working directory is
not the location of the ImageFileCollection.
"""
coll = image_collection.ImageFileCollection(triage_setup.test_dir)
# The temporary directory below should be different that the collection
# location.
os.chdir(tmpdir.strpath)
# Let's make sure it is.
assert not os.path.samefile(os.getcwd(), coll.location)
# This generated an IOError before the issue was fixed.
for _ in coll.ccds(ccd_kwargs={'unit': 'adu'}):
pass
# Licensed under a 3-clause BSD style license - see LICENSE.rst
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as np
from astropy.nddata import StdDevUncertainty
from scipy import ndimage
from ..ccddata import CCDData
from .. import core
def test_medianfilter_correct():
ccd = CCDData([[2, 6, 6, 1, 7, 2, 4, 5, 9, 1],
[10, 10, 9, 0, 2, 10, 8, 3, 9, 7],
[2, 4, 0, 4, 4, 10, 0, 5, 6, 5],
[7, 10, 8, 7, 7, 0, 5, 3, 5, 9],
[9, 6, 3, 8, 6, 9, 2, 8, 10, 10],
[6, 5, 1, 7, 8, 0, 8, 2, 9, 3],
[0, 6, 0, 6, 3, 10, 8, 9, 7, 8],
[5, 8, 3, 2, 3, 0, 2, 0, 3, 5],
[9, 6, 3, 7, 1, 0, 5, 4, 8, 3],
[5, 6, 9, 9, 0, 4, 9, 1, 7, 8]], unit='adu')
result = core.median_filter(ccd, 3)
assert isinstance(result, CCDData)
assert np.all(result.data == [[6, 6, 6, 6, 2, 4, 4, 5, 5, 7],
[4, 6, 4, 4, 4, 4, 5, 5, 5, 6],
[7, 8, 7, 4, 4, 5, 5, 5, 5, 7],
[7, 6, 6, 6, 7, 5, 5, 5, 6, 9],
[7, 6, 7, 7, 7, 6, 3, 5, 8, 9],
[6, 5, 6, 6, 7, 8, 8, 8, 8, 8],
[5, 5, 5, 3, 3, 3, 2, 7, 5, 5],
[6, 5, 6, 3, 3, 3, 4, 5, 5, 5],
[6, 6, 6, 3, 2, 2, 2, 4, 4, 5],
[6, 6, 7, 7, 4, 4, 4, 7, 7, 8]])
assert result.unit == 'adu'
assert all(getattr(result, attr) is None
for attr in ['mask', 'uncertainty', 'wcs', 'flags'])
# The following test could be deleted if log_to_metadata is also applied.
assert not result.meta
def test_medianfilter_unusued():
ccd = CCDData(np.ones((3, 3)), unit='adu',
mask=np.ones((3, 3)),
uncertainty=StdDevUncertainty(np.ones((3, 3))),