imutil.pixels.fixpix

imutil.pixels.fixpix(
    ccd,
    mask=None,
    maskpath=None,
    extension=None,
    mask_extension=None,
    priority=None,
    update_header=True,
    verbose=True,
)

Interpolate the masked location (N-D generalization of IRAF PROTO.FIXPIX)

Parameters

ccd : ~astropy.nddata.CCDData-like (e.g., ~astropy.io.fits.PrimaryHDU, ~astropy.io.fits.ImageHDU, ~astropy.io.fits.HDUList), ~numpy.ndarray, path-like, or number-like

The CCD data to be “fixed”.

mask : ~astropy.nddata.CCDData-like (e.g., ~astropy.io.fits.PrimaryHDU, ~astropy.io.fits.ImageHDU, ~astropy.io.fits.HDUList), ~numpy.ndarray, path-like, optional. = None

The mask to be used for fixing pixels (pixels to be fixed are where mask is True). If None, nothing will happen and ccd is returned.

extension : HDUExt = None

The extension of FITS to be used. It can be given as integer (0-indexing) of the extension, EXTNAME (single str), or a tuple of str and int: (EXTNAME, EXTVER). If None (default), the first extension with data will be used. Default: None.

mask_extension : HDUExt = None

The extension of FITS to be used. It can be given as integer (0-indexing) of the extension, EXTNAME (single str), or a tuple of str and int: (EXTNAME, EXTVER). If None (default), the first extension with data will be used. Default: None.

priority : tuple[int, …] | None = None

The priority of axis as a tuple of non-repeating int from 0 to ccd.ndim. It will be used if the mask has the same size along two or more of the directions. To specify, use the integers for axis directions, descending priority. For example, (2, 1, 0) will be identical to priority=None (default) for 3-D images. Default is None to follow IRAF’s PROTO.FIXPIX: Priority is higher for larger axis number (e.g., in 2-D, x-axis (axis=1) has higher priority than y-axis (axis=0)).

Notes

Run boundaries are computed from the original mask with storage proportional to the number of masked pixels times the dimension. Interpolation retains C-order traversal and repeated writes where runs cross. Precomputation adds index storage and sorting overhead, including for isolated bad pixels.

Examples

Timing test: MBP 15” [2018, macOS 11.4, i7-8850H (2.6 GHz; 6-core), RAM 16 GB (2400MHz DDR4), Radeon Pro 560X (4GB)], 2021-11-05 11:14:04 (KST: GMT+09:00)

>>> np.random.RandomState(123)  # RandomState(MT19937) at 0x7FAECA768D40
>>> data = np.random.normal(size=(1000, 1000))
>>> mask = np.zeros_like(data).astype(bool)
>>> mask[10, 10] = True
>>> %timeit air.fixpix(data, mask)
19.7 ms +- 1.53 ms per loop (mean +- std. dev. of 7 runs, 100 loops each)

Same benchmark on MBP 14” [2024, macOS 26.4.1, M4Pro(8P+4E/G20c/N16c/48G)], 2026-05-27:

>>> rng = np.random.default_rng(123)
>>> data = rng.normal(size=(1000, 1000))
>>> mask = np.zeros_like(data, dtype=bool)
>>> mask[10, 10] = True
>>> %timeit air.fixpix(data, mask)
8.55 ms ± 124 µs per loop (7 runs, 20 loops each)

Same M4Pro benchmark after run-span interpolation was added:

>>> %timeit air.fixpix(data, mask, update_header=False, verbose=False)
166 µs ± 8 µs per loop (5 runs, 200 loops each)
>>> mask100 = np.zeros_like(data, dtype=bool)
>>> mask100.ravel()[::10000] = True
>>> %timeit air.fixpix(data, mask100, update_header=False, verbose=False)
620 µs ± 25 µs per loop (5 runs, 100 loops each)
>>> print(data[9:12, 9:12], air.fixpix(data, mask)[9:12, 9:12])
# [[ 1.64164502 -1.00385046 -1.24748504]
#  [-1.31877621  1.37965928  0.66008966]
#  [-0.7960262  -0.14613834 -1.34513327]]
# [[ 1.64164502 -1.00385046 -1.24748504]
#  [-1.31877621 -0.32934328  0.66008966]
#  [-0.7960262  -0.14613834 -1.34513327]] adu