Skip to content

Commit

Permalink
Adding event to __call__ function
Browse files Browse the repository at this point in the history
 - Switching from low-level cleaning method approach to
   just adding event as keyword argument to the
   __call__ function
 - backwards compatible now
  • Loading branch information
Hckjs committed Feb 20, 2024
1 parent d3dd7a1 commit cd4d02b
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 83 deletions.
9 changes: 2 additions & 7 deletions docs/changes/2511.api.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
Change the ``ImageCleaner`` API from __call__(self, tel_id, image, peak_time)
to __call__(self, tel_id: int, event: ArrayEventContainer) so cleaning
algorithms can now access relevant information for methods
Adding event as keyword argument to the ``ImageCleaner`` API
so cleaning algorithms can now access relevant information for methods
that e.g. require monitoring information.

The __call__ function now internally uses an abstract
clean_image(self, tel_id, image, peak_time) method
defined by each ImageCleaner subclass.
115 changes: 46 additions & 69 deletions src/ctapipe/image/cleaning.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@

import numpy as np

from ..containers import ArrayEventContainer
from ctapipe.containers import ArrayEventContainer

from ..core import TelescopeComponent
from ..core.traits import (
BoolTelescopeParameter,
Expand Down Expand Up @@ -467,30 +468,16 @@ class ImageCleaner(TelescopeComponent):
"""

@abstractmethod
def __call__(self, tel_id: int, event: ArrayEventContainer) -> np.ndarray:
"""
Calls the relevant functions to identify pixels with signal
and reject those with pure noise.
Parameters
----------
tel_id: int
which telescope id in the subarray is being used (determines
which cut is used)
event: `ctapipe.containers.ArrayEventContainer`
Returns
-------
np.ndarray
boolean mask of pixels passing cleaning
"""

@abstractmethod
def clean_image(
self, tel_id: int, image: np.ndarray, arrival_times: np.ndarray = None
def __call__(
self,
tel_id: int,
image: np.ndarray,
arrival_times: np.ndarray = None,
*,
event: ArrayEventContainer = None,
) -> np.ndarray:
"""
Abstract cleaning method to be defined by an ImageCleaner subclass.
Identify pixels with signal, and reject those with pure noise.
Parameters
----------
Expand All @@ -501,12 +488,16 @@ def clean_image(
image pixel data corresponding to the camera geometry
arrival_times: np.ndarray
image of arrival time (not used in this method)
event: `ctapipe.containers.ArrayEventContainer`
ArrayEventContainer to make use of additional parameters
e.g. monitoring data.
Returns
-------
np.ndarray
boolean mask of pixels passing cleaning
"""
pass


class TailcutsImageCleaner(ImageCleaner):
Expand All @@ -533,21 +524,18 @@ class TailcutsImageCleaner(ImageCleaner):
"removed.",
).tag(config=True)

def __call__(self, tel_id: int, event: ArrayEventContainer) -> np.ndarray:
def __call__(
self,
tel_id: int,
image: np.ndarray,
arrival_times: np.ndarray = None,
*,
event: ArrayEventContainer = None,
) -> np.ndarray:
"""
Apply standard picture-boundary cleaning. See `ImageCleaner.__call__()`
"""

mask = self.clean_image(
tel_id=tel_id,
image=event.dl1.tel[tel_id].image,
)
return mask

def clean_image(
self, tel_id: int, image: np.ndarray, arrival_times=None
) -> np.ndarray:

return tailcuts_clean(
self.subarray.tel[tel_id].camera.geometry,
image,
Expand All @@ -563,21 +551,18 @@ class MARSImageCleaner(TailcutsImageCleaner):
1st-pass MARS-like Image cleaner (See `ctapipe.image.mars_cleaning_1st_pass`)
"""

def __call__(self, tel_id: int, event: ArrayEventContainer) -> np.ndarray:
def __call__(
self,
tel_id: int,
image: np.ndarray,
arrival_times: np.ndarray = None,
*,
event: ArrayEventContainer = None,
) -> np.ndarray:
"""
Apply MARS-style image cleaning. See `ImageCleaner.__call__()`
"""

mask = self.clean_image(
tel_id=tel_id,
image=event.dl1.tel[tel_id].image,
)
return mask

def clean_image(
self, tel_id: int, image: np.ndarray, arrival_times=None
) -> np.ndarray:

return mars_cleaning_1st_pass(
self.subarray.tel[tel_id].camera.geometry,
image,
Expand All @@ -598,19 +583,15 @@ class FACTImageCleaner(TailcutsImageCleaner):
default_value=5.0, help="arrival time limit for neighboring " "pixels, in ns"
).tag(config=True)

def __call__(self, tel_id: int, event: ArrayEventContainer) -> np.ndarray:
"""Apply FACT-style image cleaning. see ImageCleaner.__call__()"""

mask = self.clean_image(
tel_id=tel_id,
image=event.dl1.tel[tel_id].image,
arrival_times=event.dl1.tel[tel_id].peak_time,
)
return mask

def clean_image(
self, tel_id: int, image: np.ndarray, arrival_times=None
def __call__(
self,
tel_id: int,
image: np.ndarray,
arrival_times: np.ndarray = None,
*,
event: ArrayEventContainer = None,
) -> np.ndarray:
"""Apply FACT-style image cleaning. see ImageCleaner.__call__()"""

return fact_image_cleaning(
geom=self.subarray.tel[tel_id].camera.geometry,
Expand All @@ -637,22 +618,18 @@ class TimeConstrainedImageCleaner(TailcutsImageCleaner):
help="arrival time limit for neighboring " "boundary pixels, in ns",
).tag(config=True)

def __call__(self, tel_id: int, event: ArrayEventContainer) -> np.ndarray:
def __call__(
self,
tel_id: int,
image: np.ndarray,
arrival_times: np.ndarray = None,
*,
event: ArrayEventContainer = None,
) -> np.ndarray:
"""
Apply MAGIC-like image cleaning with timing information. See `ImageCleaner.__call__()`
"""

mask = self.clean_image(
tel_id=tel_id,
image=event.dl1.tel[tel_id].image,
arrival_times=event.dl1.tel[tel_id].peak_time,
)
return mask

def clean_image(
self, tel_id: int, image: np.ndarray, arrival_times=None
) -> np.ndarray:

return time_constrained_clean(
self.subarray.tel[tel_id].camera.geometry,
image,
Expand Down
7 changes: 6 additions & 1 deletion src/ctapipe/image/image_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,12 @@ def _process_telescope_event(self, event):
if self.apply_image_modifier.tel[tel_id]:
dl1_camera.image = self.modify(tel_id=tel_id, image=dl1_camera.image)

dl1_camera.image_mask = self.clean(tel_id=tel_id, event=event)
dl1_camera.image_mask = self.clean(
tel_id=tel_id,
image=dl1_camera.image,
arrival_times=dl1_camera.peak_time,
event=event,
)

dl1_camera.parameters = self._parameterize_image(
tel_id=tel_id,
Expand Down
3 changes: 2 additions & 1 deletion src/ctapipe/image/reducer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Algorithms for the data volume reduction.
"""

from abc import abstractmethod

import numpy as np
Expand Down Expand Up @@ -191,7 +192,7 @@ def select_pixels(self, waveforms, tel_id=None, selected_gain_channel=None):
)

# 1) Step: TailcutCleaning at first
mask = self.cleaner.clean_image(tel_id, dl1.image)
mask = self.cleaner(tel_id, dl1.image)
pixels_above_boundary_thresh = (
dl1.image >= self.cleaner.boundary_threshold_pe.tel[tel_id]
)
Expand Down
7 changes: 2 additions & 5 deletions src/ctapipe/image/tests/test_image_cleaner_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


@pytest.mark.parametrize("method", ImageCleaner.non_abstract_subclasses().keys())
def test_image_cleaner(method, prod5_mst_nectarcam, example_event, reference_location):
def test_image_cleaner(method, prod5_mst_nectarcam, reference_location):
"""Test that we can construct and use a component-based ImageCleaner"""

config = Config(
Expand Down Expand Up @@ -45,10 +45,7 @@ def test_image_cleaner(method, prod5_mst_nectarcam, example_event, reference_loc
image[31:40] = 8.0
times = np.linspace(-5, 10, image.shape[0])

example_event.dl1.tel[1].image = image
example_event.dl1.tel[1].peak_time = times

mask = clean(tel_id=1, event=example_event)
mask = clean(tel_id=1, image=image, arrival_times=times)

# we're not testing the algorithm here, just that it does something (for the
# algorithm tests, see test_cleaning.py
Expand Down

0 comments on commit cd4d02b

Please sign in to comment.