Source code for ctapipe.image.reducer

"""
Algorithms for the data volume reduction.
"""
from abc import abstractmethod

import numpy as np

from ctapipe.containers import DL1CameraContainer
from ctapipe.core import TelescopeComponent
from ctapipe.core.traits import (
    BoolTelescopeParameter,
    ComponentName,
    IntTelescopeParameter,
    TelescopeParameter,
)
from ctapipe.image import TailcutsImageCleaner
from ctapipe.image.cleaning import dilate
from ctapipe.image.extractor import ImageExtractor

__all__ = ["DataVolumeReducer", "NullDataVolumeReducer", "TailCutsDataVolumeReducer"]


[docs]class DataVolumeReducer(TelescopeComponent): """ Base component for data volume reducers. """ def __init__(self, subarray, config=None, parent=None, **kwargs): """ Parameters ---------- subarray: ctapipe.instrument.SubarrayDescription Description of the subarray config: traitlets.loader.Config Configuration specified by config file or cmdline arguments. Used to set traitlet values. Set to None if no configuration to pass. kwargs """ self.subarray = subarray super().__init__(config=config, parent=parent, subarray=subarray, **kwargs)
[docs] def __call__(self, waveforms, tel_id=None, selected_gain_channel=None): """ Call the relevant functions to perform data volume reduction on the waveforms. Parameters ---------- waveforms: ndarray Waveforms stored in a numpy array of shape (n_pix, n_samples). tel_id: int The telescope id. Required for the 'image_extractor' and 'camera.geometry' in 'TailCutsDataVolumeReducer'. selected_gain_channel: ndarray The channel selected in the gain selection, per pixel. Required for the 'image_extractor' in 'TailCutsDataVolumeReducer'. extraction. Returns ------- mask: array Mask of selected pixels. """ mask = self.select_pixels( waveforms, tel_id=tel_id, selected_gain_channel=selected_gain_channel ) return mask
[docs] @abstractmethod def select_pixels(self, waveforms, tel_id=None, selected_gain_channel=None): """ Abstract method to be defined by a DataVolumeReducer subclass. Call the relevant functions for the required pixel selection. Parameters ---------- waveforms: ndarray Waveforms stored in a numpy array of shape (n_pix, n_samples). tel_id: int The telescope id. Required for the 'image_extractor' and 'camera.geometry' in 'TailCutsDataVolumeReducer'. selected_gain_channel: ndarray The channel selected in the gain selection, per pixel. Required for the 'image_extractor' in 'TailCutsDataVolumeReducer'. Returns ------- mask: array Mask of selected pixels. """
[docs]class NullDataVolumeReducer(DataVolumeReducer): """ Perform no data volume reduction """
[docs] def select_pixels(self, waveforms, tel_id=None, selected_gain_channel=None): mask = waveforms != 0 return mask
[docs]class TailCutsDataVolumeReducer(DataVolumeReducer): """ Reduce the time integrated shower image in 3 Steps: 1) Select pixels with tailcuts_clean. 2) Add iteratively all pixels with Signal S >= boundary_thresh with ctapipe module dilate until no new pixels were added. 3) Adding new pixels with dilate to get more conservative. Attributes ---------- image_extractor_type: String Name of the image_extractor to be used. n_end_dilates: IntTelescopeParameter Number of how many times to dilate at the end. do_boundary_dilation: BoolTelescopeParameter If set to 'False', the iteration steps in 2) are skipped and normal TailcutCleaning is used. """ image_extractor_type = TelescopeParameter( trait=ComponentName(ImageExtractor, default_value="NeighborPeakWindowSum"), default_value="NeighborPeakWindowSum", help="Name of the ImageExtractor subclass to be used.", ).tag(config=True) n_end_dilates = IntTelescopeParameter( default_value=1, help="Number of how many times to dilate at the end." ).tag(config=True) do_boundary_dilation = BoolTelescopeParameter( default_value=True, help="If set to 'False', the iteration steps in 2) are skipped and" "normal TailcutCleaning is used.", ).tag(config=True) def __init__( self, subarray, config=None, parent=None, cleaner=None, image_extractor=None, **kwargs, ): """ Parameters ---------- subarray: ctapipe.instrument.SubarrayDescription Description of the subarray config: traitlets.loader.Config Configuration specified by config file or cmdline arguments. Used to set traitlet values. Set to None if no configuration to pass. kwargs """ super().__init__(config=config, parent=parent, subarray=subarray, **kwargs) if cleaner is None: self.cleaner = TailcutsImageCleaner(parent=self, subarray=self.subarray) else: self.cleaner = cleaner self.image_extractors = {} if image_extractor is None: for (_, _, name) in self.image_extractor_type: self.image_extractors[name] = ImageExtractor.from_name( name, subarray=self.subarray, parent=self ) else: name = image_extractor.__class__.__name__ self.image_extractor_type = [("type", "*", name)] self.image_extractors[name] = image_extractor
[docs] def select_pixels(self, waveforms, tel_id=None, selected_gain_channel=None): camera_geom = self.subarray.tel[tel_id].camera.geometry # Pulse-integrate waveforms extractor = self.image_extractors[self.image_extractor_type.tel[tel_id]] # do not treat broken pixels in data volume reduction broken_pixels = np.zeros(camera_geom.n_pixels, dtype=bool) dl1: DL1CameraContainer = extractor( waveforms, tel_id=tel_id, selected_gain_channel=selected_gain_channel, broken_pixels=broken_pixels, ) # 1) Step: TailcutCleaning at first mask = self.cleaner(tel_id, dl1.image) pixels_above_boundary_thresh = ( dl1.image >= self.cleaner.boundary_threshold_pe.tel[tel_id] ) mask_in_loop = np.array([]) # 2) Step: Add iteratively all pixels with Signal # S > boundary_thresh with ctapipe module # 'dilate' until no new pixels were added. while ( not np.array_equal(mask, mask_in_loop) and self.do_boundary_dilation.tel[tel_id] ): mask_in_loop = mask mask = dilate(camera_geom, mask) & pixels_above_boundary_thresh # 3) Step: Adding Pixels with 'dilate' to get more conservative. for _ in range(self.n_end_dilates.tel[tel_id]): mask = dilate(camera_geom, mask) return mask