Source code for irispy.utils.utils

"""
This module provides general utility functions.
"""

import numbers

import numpy as np
from scipy import ndimage

import astropy.units as u
from astropy.modeling.models import custom_model

from .constants import BAD_PIXEL_VALUE_SCALED

__all__ = [
    "calculate_dust_mask",
    "calculate_uncertainty",
    "gaussian1d_on_linear_bg",
    "get_detector_type",
    "image_clipping",
    "record_to_dict",
]


[docs] def record_to_dict(arr: np.ndarray): """ Convert a structured numpy array to a regular dictionary. """ new_arr = arr.copy() for name in arr.dtype.names: if isinstance(arr[name], np.ndarray) and arr[name].dtype == np.object_: new_arr[name] = arr[name].tolist() return {name: new_arr[name] for name in new_arr.dtype.names}
[docs] def image_clipping(image, cutoff=1.5e-3, gamma=1.0): """ Computes and returns the min and max values of the input (image), clipping brightest and darkest pixels. Parameters ---------- image : `numpy.ndarray` The input image. cutoff : float, optional The cutoff value for the histogram. Defaults to 1.5e-3 gamma : float, optional The gamma value for the histogram. Defaults to 1.0 References ---------- Based on original IDL routine by P.Suetterlin (06 Jul 1993) Ported by V.Hansteen (15 Apr 2020) """ hmin = np.min(image) hmax = np.max(image) if issubclass(image.dtype.type, numbers.Integral): nbins = np.abs(np.max(image) - np.min(image)) hist = np.histogram(image, bins=nbins) fak = 1 else: nbins = 10000 fak = nbins / (hmax - hmin) hist = np.histogram((image - hmin) * fak, range=(0.0, float(nbins)), bins=nbins) h = hist[0] bins = hist[1] nh = np.size(h) # Integrate the histogram so that h(i) holds the number of points # with equal or lower intensity. for i in range(1, nh - 1): h[i] = h[i] + h[i - 1] h = h / float(h[nh - 2]) h[nh - 1] = 1 # As cutoff is in percent and h is normalized to unity, # vmin/vmax are the indices of the point where the number of pixels # with lower/higher intensity reach the given limit. This has to be # converted to a real image value by dividing by the scalefactor # fak and adding the min value of the image # Note that the bottom value is taken off (addition of h[0] to cutoff), # there are often very many points in IRIS images that are set to zero, this # removes them from calculation... and seems to work. vmin = (np.max(np.where(h <= (cutoff + h[0]), bins[1:] - bins[0], 0)) / fak + hmin) ** gamma vmax = (np.min(np.where(h >= (1.0 - cutoff), bins[1:] - bins[0], nh - 2)) / fak + hmin) ** gamma return vmin, vmax
@custom_model def gaussian1d_on_linear_bg( x, amplitude=None, mean=None, standard_deviation=None, constant_term=None, linear_term=None, ): return amplitude * np.exp(-(((x - mean) / standard_deviation) ** 2)) + constant_term + linear_term * x
[docs] def get_detector_type(meta): """ Gets the IRIS detector type from a meta dictionary. In this function, FUV1 and FUV2 are just assigned as FUV. Parameters ---------- meta: `dict` Dictionary-like object containing entry for "detector type" Returns ------- `str` Detector type. """ return "FUV" if "FUV" in meta["detector type"] else meta["detector type"]
[docs] def calculate_dust_mask(data_array): """ Calculate a mask with the dust positions in a given array. Parameters ---------- data_array : `numpy.ndarray` This array contains some dust position that will be calculated. The array must have scaled values. Returns ------- `numpy.ndarray` of `bool` This array has the same shape than data_array and contains the dust positions when the value is True. """ # Creating a mask with the same shape than the inputted data array. mask = np.zeros_like(data_array, dtype=bool) # Set the pixel value to True is the pixel is recognized as a dust pixel. mask[(data_array < 0.5) & (data_array > BAD_PIXEL_VALUE_SCALED)] = True # Extending the mask to avoid the neighbours pixel influenced by the dust pixels. if mask.ndim == 2: struct = np.ones((3, 3), dtype=bool) elif mask.ndim == 3: struct = np.array([np.zeros((3, 3)), np.ones((3, 3)), np.zeros((3, 3))], dtype=bool) else: msg = "data_array must be 2D or 3D." raise ValueError(msg) return ndimage.binary_dilation(mask, structure=struct).astype(mask.dtype)
[docs] def calculate_uncertainty(data: np.ndarray, readout_noise: u.Quantity, unit: u.Quantity) -> float: """ Calculates the uncertainty of a given data array. Parameters ---------- data : numpy.ndarray The data array. readout_noise : astropy.units.Quantity The readout noise, needs to be a unit that is convertible to photon. unit : astropy.units.Quantity The final unit that the value should be converted to. Returns ------- float The readout noise with no unit. """ return ( u.Quantity( np.sqrt((data * unit).to(u.photon).value + readout_noise.to(u.photon).value ** 2), unit=u.photon, ) .to(unit) .value )