From d643c9e2cc1bf5784138a0c2239fdb03a09a77f6 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 12:40:24 +0100 Subject: [PATCH 01/25] Creation of WaveformReducer class - Replaces ChargeExtractor - Renamed charge_extractor.py to waveform_reducer.py - WaveformReducer uses `__call__` in place of `extract_charge` - Created extract_pulse_time_weighted_average - WaveformReducer returns charge and pulse_time - Updated names for classes to be more descriptive --- ...extraction.py => test_waveform_reducer.py} | 0 ...arge_extractors.py => waveform_reducer.py} | 157 ++++++++---------- 2 files changed, 69 insertions(+), 88 deletions(-) rename ctapipe/image/tests/{test_charge_extraction.py => test_waveform_reducer.py} (100%) rename ctapipe/image/{charge_extractors.py => waveform_reducer.py} (63%) diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_waveform_reducer.py similarity index 100% rename from ctapipe/image/tests/test_charge_extraction.py rename to ctapipe/image/tests/test_waveform_reducer.py diff --git a/ctapipe/image/charge_extractors.py b/ctapipe/image/waveform_reducer.py similarity index 63% rename from ctapipe/image/charge_extractors.py rename to ctapipe/image/waveform_reducer.py index 56562781b3b..8c617501a9e 100644 --- a/ctapipe/image/charge_extractors.py +++ b/ctapipe/image/waveform_reducer.py @@ -3,13 +3,14 @@ """ __all__ = [ - 'ChargeExtractor', - 'FullIntegrator', - 'SimpleIntegrator', - 'GlobalPeakIntegrator', - 'LocalPeakIntegrator', - 'NeighbourPeakIntegrator', - 'AverageWfPeakIntegrator', + 'WaveformReducer', + 'FullWaveformSum', + 'UserWindowSum', + 'GlobalWindowSum', + 'LocalWindowSum', + 'NeighbourWindowSum', + 'extract_charge_from_peakpos_array', + 'extract_pulse_time_weighted_average', ] @@ -46,12 +47,6 @@ def extract_charge_from_peakpos_array(waveforms, peakpos, width, shift): Shape: (n_chan, n_pix) """ - # Ensure window is within waveform - # width[width > n_samples] = n_samples - # start[start < 0] = 0 - # sum_check = start + width > n_samples - # start[sum_check] = n_samples - width[sum_check] - start = peakpos - shift end = start + width ind = np.indices(waveforms.shape)[2] @@ -59,15 +54,38 @@ def extract_charge_from_peakpos_array(waveforms, peakpos, width, shift): windowed = np.ma.array(waveforms, mask=~integration_window) charge = windowed.sum(2).data - # TODO: remove integration window return - return charge, integration_window + return charge -class ChargeExtractor(Component): +def extract_pulse_time_weighted_average(waveforms): + """ + Use the weighted average of the waveforms to extract the time of the pulse + in each pixel + + Parameters + ---------- + waveforms : ndarray + Waveforms stored in a numpy array. + Shape: (n_chan, n_pix, n_samples) + + Returns + ------- + pulse_time : ndarray + Floating point pulse time in each pixel + Shape: (n_chan, n_pix) + + """ + samples_i = np.indices(waveforms.shape)[2] + pulse_time = np.average(samples_i, weights=waveforms, axis=2) + return pulse_time + + +class WaveformReducer(Component): def __init__(self, config=None, parent=None, **kwargs): """ - Base component to handle the extraction of charge from an image cube. + Base component to handle the extraction of charge and pulse time + from an image cube. Attributes ---------- @@ -97,7 +115,7 @@ def __init__(self, config=None, parent=None, **kwargs): @staticmethod def requires_neighbours(): """ - Method used for callers of the ChargeExtractor to know if the extractor + Method used for callers of the WaveformReducer to know if the extractor requires knowledge of the pixel neighbours Returns @@ -108,7 +126,7 @@ def requires_neighbours(): def check_neighbour_set(self): """ - Check if the pixel neighbours has been set for the extractor + Check if the pixel neighbours has been set for the reducer Raises ------- @@ -121,10 +139,10 @@ def check_neighbour_set(self): raise ValueError() @abstractmethod - def extract_charge(self, waveforms): + def __call__(self, waveforms): """ - Call the relevant functions to fully extract the charge for the - particular extractor. + Call the relevant functions to fully extract the charge and time + for the particular extractor. Parameters ---------- @@ -147,21 +165,20 @@ def extract_charge(self, waveforms): """ -class FullIntegrator(ChargeExtractor): +class FullWaveformSum(WaveformReducer): """ - Charge extractor that integrates the entire waveform. + Waveform reducer that integrates the entire waveform. """ - def extract_charge(self, waveforms): - # TODO: remove integration window return - peakpos = np.zeros(waveforms.shape[:2], dtype=np.intp) - window = np.ones(waveforms.shape, dtype=np.bool) - return waveforms.sum(2), peakpos, window + def __call__(self, waveforms): + charge = waveforms.sum(2) + pulse_time = extract_pulse_time_weighted_average(waveforms) + return charge, pulse_time -class SimpleIntegrator(ChargeExtractor): +class UserWindowSum(WaveformReducer): """ - Charge extractor that integrates within a window defined by the user. + Waveform reducer that integrates within a window defined by the user. """ window_start = Int( 0, help='Define the start position for the integration window' @@ -170,19 +187,18 @@ class SimpleIntegrator(ChargeExtractor): 7, help='Define the width of the integration window' ).tag(config=True) - def extract_charge(self, waveforms): + def __call__(self, waveforms): start = self.window_start end = self.window_start + self.window_width - # TODO: remove integration window return - peakpos = np.zeros(waveforms.shape[:2], dtype=np.intp) - window = np.ones(waveforms.shape, dtype=np.bool) - return waveforms[..., start:end].sum(2), peakpos, window + charge = waveforms[..., start:end].sum(2) + pulse_time = extract_pulse_time_weighted_average(waveforms) + return charge, pulse_time -class GlobalPeakIntegrator(ChargeExtractor): +class GlobalWindowSum(WaveformReducer): """ - Charge extractor that defines an integration window about the global - peak in the image. + Waveform reducer that defines an integration window defined by the + average waveform across all pixels. """ window_shift = Int( 3, help='Define the shift of the integration window ' @@ -192,29 +208,21 @@ class GlobalPeakIntegrator(ChargeExtractor): 7, help='Define the width of the integration window' ).tag(config=True) - def extract_charge(self, waveforms): - max_t = waveforms.argmax(2) - max_s = waveforms.max(2) - peakpos = np.round( - np.average(max_t, weights=max_s, axis=1) - ).astype(np.int) + def __call__(self, waveforms): + peakpos = waveforms.mean(1).argmax(1) start = peakpos - self.window_shift end = start + self.window_width charge = np.stack([ waveforms[0, :, start[0]:end[0]].sum(1), # HI channel waveforms[1, :, start[1]:end[1]].sum(1), # LO channel ]) + pulse_time = extract_pulse_time_weighted_average(waveforms) + return charge, pulse_time - # TODO: remove integration window return - ind = np.indices(waveforms.shape)[2] - window = (ind >= start[..., None, None]) & (ind < end[..., None, None]) - - return charge, peakpos, window - -class LocalPeakIntegrator(ChargeExtractor): +class LocalWindowSum(WaveformReducer): """ - Charge extractor that defines an integration window about the local + Waveform reducer that defines an integration window about the local peak in each pixel. """ window_shift = Int( @@ -225,17 +233,18 @@ class LocalPeakIntegrator(ChargeExtractor): 7, help='Define the width of the integration window' ).tag(config=True) - def extract_charge(self, waveforms): + def __call__(self, waveforms): peakpos = waveforms.argmax(2).astype(np.int) charge, window = extract_charge_from_peakpos_array( waveforms, peakpos, self.window_width, self.window_shift ) - return charge, peakpos, window + pulse_time = extract_pulse_time_weighted_average(waveforms) + return charge, pulse_time -class NeighbourPeakIntegrator(ChargeExtractor): +class NeighbourWindowSum(WaveformReducer): """ - Charge extractor that defines an integration window defined by the + Waveform reducer that defines an integration window defined by the peaks in the neighbouring pixels. """ window_shift = Int( @@ -253,7 +262,7 @@ class NeighbourPeakIntegrator(ChargeExtractor): def requires_neighbours(self): return True - def extract_charge(self, waveforms): + def __call__(self, waveforms): shape = waveforms.shape waveforms_32 = waveforms.astype(np.float32) sum_data = np.zeros_like(waveforms_32) @@ -263,33 +272,5 @@ def extract_charge(self, waveforms): charge, window = extract_charge_from_peakpos_array( waveforms, peakpos, self.window_width, self.window_shift ) - return charge, peakpos, window - - -class AverageWfPeakIntegrator(ChargeExtractor): - """ - Charge extractor that defines an integration window defined by the - average waveform across all pixels. - """ - window_shift = Int( - 3, help='Define the shift of the integration window ' - 'from the peakpos (peakpos - shift)' - ).tag(config=True) - window_width = Int( - 7, help='Define the width of the integration window' - ).tag(config=True) - - def extract_charge(self, waveforms): - peakpos = waveforms.mean(1).argmax(1) - start = peakpos - self.window_shift - end = start + self.window_width - charge = np.stack([ - waveforms[0, :, start[0]:end[0]].sum(1), # HI channel - waveforms[1, :, start[1]:end[1]].sum(1), # LO channel - ]) - - # TODO: remove integration window return - ind = np.indices(waveforms.shape)[2] - window = (ind >= start[..., None, None]) & (ind < end[..., None, None]) - - return charge, peakpos, window + pulse_time = extract_pulse_time_weighted_average(waveforms) + return charge, pulse_time From d07a800178d4d53385c1691bb62aa7c03e6d2b04 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 15:56:30 +0100 Subject: [PATCH 02/25] Replace WaveformReducer with WaveformExtractor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - This is done to avoid confusion with the DataVolumeReducer - Rename waveform_reducer to waveform_extractor - Create subtract_baseline and BaselineSubtractedNeighborWindowSum - Replace “neighbours” with “neighbors” --- ...eform_reducer.py => waveform_extractor.py} | 112 +++++++++++++----- 1 file changed, 80 insertions(+), 32 deletions(-) rename ctapipe/image/{waveform_reducer.py => waveform_extractor.py} (70%) diff --git a/ctapipe/image/waveform_reducer.py b/ctapipe/image/waveform_extractor.py similarity index 70% rename from ctapipe/image/waveform_reducer.py rename to ctapipe/image/waveform_extractor.py index 8c617501a9e..7ac373ef4fa 100644 --- a/ctapipe/image/waveform_reducer.py +++ b/ctapipe/image/waveform_extractor.py @@ -3,14 +3,16 @@ """ __all__ = [ - 'WaveformReducer', + 'WaveformExtractor', 'FullWaveformSum', 'UserWindowSum', 'GlobalWindowSum', 'LocalWindowSum', - 'NeighbourWindowSum', + 'NeighborWindowSum', + 'BaselineSubtractedNeighborWindowSum', 'extract_charge_from_peakpos_array', 'extract_pulse_time_weighted_average', + 'subtract_baseline', ] @@ -80,7 +82,34 @@ def extract_pulse_time_weighted_average(waveforms): return pulse_time -class WaveformReducer(Component): +def subtract_baseline(waveforms, baseline_start, baseline_end): + """ + Subtracts the waveform baseline, estimated as the mean waveform value + in the interval [baseline_start:baseline_end] + + Parameters + ---------- + waveforms : ndarray + Waveforms stored in a numpy array. + Shape: (n_chan, n_pix, n_samples) + baseline_start : int + Sample where the baseline window starts + baseline_end : int + Sample where the baseline window ends + + Returns + ------- + baseline_corrected : ndarray + Waveform with the baseline subtracted + """ + baseline_corrected = waveforms - np.mean( + waveforms[..., baseline_start:baseline_end], axis=2 + )[..., None] + + return baseline_corrected + + +class WaveformExtractor(Component): def __init__(self, config=None, parent=None, **kwargs): """ @@ -89,8 +118,8 @@ def __init__(self, config=None, parent=None, **kwargs): Attributes ---------- - neighbours : ndarray - 2D array where each row is [pixel index, one neighbour + neighbors : ndarray + 2D array where each row is [pixel index, one neighbor of that pixel]. Changes per telescope. Can be obtained from @@ -110,13 +139,13 @@ def __init__(self, config=None, parent=None, **kwargs): """ super().__init__(config=config, parent=parent, **kwargs) - self.neighbours = None + self.neighbors = None @staticmethod - def requires_neighbours(): + def requires_neighbors(): """ - Method used for callers of the WaveformReducer to know if the extractor - requires knowledge of the pixel neighbours + Method used for callers of the WaveformExtractor to know if the + extractor requires knowledge of the pixel neighbors Returns ------- @@ -124,18 +153,18 @@ def requires_neighbours(): """ return False - def check_neighbour_set(self): + def check_neighbor_set(self): """ - Check if the pixel neighbours has been set for the reducer + Check if the pixel neighbors has been set for the extractor Raises ------- ValueError - If neighbours has not been set + If neighbors has not been set """ - if self.requires_neighbours(): - if self.neighbours is None: - self.log.exception("neighbours attribute must be set") + if self.requires_neighbors(): + if self.neighbors is None: + self.log.exception("neighbors attribute must be set") raise ValueError() @abstractmethod @@ -165,9 +194,9 @@ def __call__(self, waveforms): """ -class FullWaveformSum(WaveformReducer): +class FullWaveformSum(WaveformExtractor): """ - Waveform reducer that integrates the entire waveform. + Waveform extractor that integrates the entire waveform. """ def __call__(self, waveforms): @@ -176,9 +205,9 @@ def __call__(self, waveforms): return charge, pulse_time -class UserWindowSum(WaveformReducer): +class UserWindowSum(WaveformExtractor): """ - Waveform reducer that integrates within a window defined by the user. + Waveform extractor that integrates within a window defined by the user. """ window_start = Int( 0, help='Define the start position for the integration window' @@ -195,9 +224,9 @@ def __call__(self, waveforms): return charge, pulse_time -class GlobalWindowSum(WaveformReducer): +class GlobalWindowSum(WaveformExtractor): """ - Waveform reducer that defines an integration window defined by the + Waveform extractor that defines an integration window defined by the average waveform across all pixels. """ window_shift = Int( @@ -220,9 +249,9 @@ def __call__(self, waveforms): return charge, pulse_time -class LocalWindowSum(WaveformReducer): +class LocalWindowSum(WaveformExtractor): """ - Waveform reducer that defines an integration window about the local + Waveform extractor that defines an integration window about the local peak in each pixel. """ window_shift = Int( @@ -235,17 +264,17 @@ class LocalWindowSum(WaveformReducer): def __call__(self, waveforms): peakpos = waveforms.argmax(2).astype(np.int) - charge, window = extract_charge_from_peakpos_array( + charge = extract_charge_from_peakpos_array( waveforms, peakpos, self.window_width, self.window_shift ) pulse_time = extract_pulse_time_weighted_average(waveforms) return charge, pulse_time -class NeighbourWindowSum(WaveformReducer): +class NeighborWindowSum(WaveformExtractor): """ - Waveform reducer that defines an integration window defined by the - peaks in the neighbouring pixels. + Waveform extractor that defines an integration window defined by the + peaks in the neighboring pixels. """ window_shift = Int( 3, help='Define the shift of the integration window ' @@ -255,22 +284,41 @@ class NeighbourWindowSum(WaveformReducer): 7, help='Define the width of the integration window' ).tag(config=True) lwt = Int( - 0, help='Weight of the local pixel (0: peak from neighbours only, ' - '1: local pixel counts as much as any neighbour)' + 0, help='Weight of the local pixel (0: peak from neighbors only, ' + '1: local pixel counts as much as any neighbor)' ).tag(config=True) - def requires_neighbours(self): + def requires_neighbors(self): return True def __call__(self, waveforms): shape = waveforms.shape waveforms_32 = waveforms.astype(np.float32) sum_data = np.zeros_like(waveforms_32) - n = self.neighbours.astype(np.uint16) + n = self.neighbors.astype(np.uint16) get_sum_array(waveforms_32, sum_data, *shape, n, n.shape[0], self.lwt) peakpos = sum_data.argmax(2).astype(np.int) - charge, window = extract_charge_from_peakpos_array( + charge = extract_charge_from_peakpos_array( waveforms, peakpos, self.window_width, self.window_shift ) pulse_time = extract_pulse_time_weighted_average(waveforms) return charge, pulse_time + + +class BaselineSubtractedNeighborWindowSum(NeighborWindowSum): + """ + Waveform extractor that first subtracts the baseline before integrating in + a window defined by the peaks in neighboring pixels. + """ + baseline_start = Int( + 0, help='Start sample for baseline estimation' + ).tag(config=True) + baseline_end = Int( + 10, help='End sample for baseline estimation' + ).tag(config=True) + + def __call__(self, waveforms): + baseline_corrected = subtract_baseline( + waveforms, self.baseline_start, self.baseline_end + ) + return super().__call__(baseline_corrected) From 199e0f8aedd5a0c9a8a6a8a3f90d6ebc7639f1d1 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 15:57:06 +0100 Subject: [PATCH 03/25] Created tests for waveform_extractor.py --- .../image/tests/test_waveform_extractor.py | 181 ++++++++++++++++++ ctapipe/image/tests/test_waveform_reducer.py | 132 ------------- 2 files changed, 181 insertions(+), 132 deletions(-) create mode 100644 ctapipe/image/tests/test_waveform_extractor.py delete mode 100644 ctapipe/image/tests/test_waveform_reducer.py diff --git a/ctapipe/image/tests/test_waveform_extractor.py b/ctapipe/image/tests/test_waveform_extractor.py new file mode 100644 index 00000000000..3b607762b93 --- /dev/null +++ b/ctapipe/image/tests/test_waveform_extractor.py @@ -0,0 +1,181 @@ +import pytest +import numpy as np +from scipy.stats import norm +from numpy.testing import assert_allclose +from ctapipe.instrument import CameraGeometry +from ctapipe.image.waveform_extractor import ( + extract_charge_from_peakpos_array, + extract_pulse_time_weighted_average, + subtract_baseline, + WaveformExtractor, + FullWaveformSum, + UserWindowSum, + GlobalWindowSum, + LocalWindowSum, + NeighborWindowSum, + BaselineSubtractedNeighborWindowSum, +) + + +@pytest.fixture(scope='module') +def camera_waveforms(): + camera = CameraGeometry.from_name("CHEC") + + n_pixels = camera.n_pixels + n_samples = 96 + mid = n_samples // 2 + pulse_sigma = 6 + r_hi = np.random.RandomState(1) + r_lo = np.random.RandomState(2) + + x = np.arange(n_samples) + + # Randomize times + t_pulse_hi = r_hi.uniform(mid - 10, mid + 10, n_pixels)[:, None] + t_pulse_lo = r_lo.uniform(mid + 10, mid + 20, n_pixels)[:, None] + + # Create pulses + y_hi = norm.pdf(x, t_pulse_hi, pulse_sigma) + y_lo = norm.pdf(x, t_pulse_lo, pulse_sigma) + + # Randomize amplitudes + y_hi *= r_hi.uniform(100, 1000, n_pixels)[:, None] + y_lo *= r_lo.uniform(100, 1000, n_pixels)[:, None] + + y = np.stack([y_hi, y_lo]) + + return y, camera + + +def test_extract_charge_from_peakpos_array(camera_waveforms): + waveforms, _ = camera_waveforms + _, n_pixels, n_samples = waveforms.shape + rand = np.random.RandomState(1) + peakpos = rand.uniform(0, n_samples, (2, n_pixels)).astype(np.int) + charge = extract_charge_from_peakpos_array(waveforms, peakpos, 7, 3) + + assert_allclose(charge[0][0], 146.022991, rtol=1e-3) + assert_allclose(charge[1][0], 22.393974, rtol=1e-3) + + +def test_extract_pulse_time_weighted_average(camera_waveforms): + waveforms, _ = camera_waveforms + pulse_time = extract_pulse_time_weighted_average(waveforms) + + assert_allclose(pulse_time[0][0], 46.34044, rtol=1e-3) + assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) + + +def test_baseline_subtractor(camera_waveforms): + waveforms, _ = camera_waveforms + n_chan, n_pixels, n_samples = waveforms.shape + rand = np.random.RandomState(1) + offset = np.arange(n_pixels)[None, :, None] + waveforms = rand.normal(0, 0.1, waveforms.shape) + offset + assert_allclose(waveforms[0, 3].mean(), 3, rtol=1e-2) + baseline_subtracted = subtract_baseline(waveforms, 0, 10) + assert_allclose(baseline_subtracted.mean(), 0, atol=1e-3) + + +def test_full_waveform_sum(camera_waveforms): + waveforms, _ = camera_waveforms + extractor = FullWaveformSum() + charge, pulse_time = extractor(waveforms) + + assert_allclose(charge[0][0], 545.945, rtol=1e-3) + assert_allclose(charge[1][0], 970.025, rtol=1e-3) + assert_allclose(pulse_time[0][0], 46.34044, rtol=1e-3) + assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) + + +def test_user_window_sum(camera_waveforms): + waveforms, _ = camera_waveforms + extractor = UserWindowSum(window_start=45) + charge, pulse_time = extractor(waveforms) + + assert_allclose(charge[0][0], 232.559, rtol=1e-3) + assert_allclose(charge[1][0], 32.539, rtol=1e-3) + assert_allclose(pulse_time[0][0], 46.34044, rtol=1e-3) + assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) + + +def test_global_window_sum(camera_waveforms): + waveforms, _ = camera_waveforms + extractor = GlobalWindowSum() + charge, pulse_time = extractor(waveforms) + + assert_allclose(charge[0][0], 232.559, rtol=1e-3) + assert_allclose(charge[1][0], 425.406, rtol=1e-3) + assert_allclose(pulse_time[0][0], 46.34044, rtol=1e-3) + assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) + + +def test_local_window_sum(camera_waveforms): + waveforms, _ = camera_waveforms + extractor = LocalWindowSum() + charge, pulse_time = extractor(waveforms) + + assert_allclose(charge[0][0], 240.3, rtol=1e-3) + assert_allclose(charge[1][0], 427.158, rtol=1e-3) + assert_allclose(pulse_time[0][0], 46.34044, rtol=1e-3) + assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) + + +def test_neighbor_window_sum(camera_waveforms): + waveforms, camera = camera_waveforms + nei = camera.neighbor_matrix_where + extractor = NeighborWindowSum() + extractor.neighbors = nei + charge, pulse_time = extractor(waveforms) + + assert_allclose(charge[0][0], 94.671, rtol=1e-3) + assert_allclose(charge[1][0], 426.887, rtol=1e-3) + assert_allclose(pulse_time[0][0], 46.34044, rtol=1e-3) + assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) + + +def test_baseline_subtracted_neighbor_window_sum(camera_waveforms): + waveforms, camera = camera_waveforms + nei = camera.neighbor_matrix_where + extractor = BaselineSubtractedNeighborWindowSum() + extractor.neighbors = nei + charge, pulse_time = extractor(waveforms) + + assert_allclose(charge[0][0], 94.671, rtol=1e-3) + assert_allclose(charge[1][0], 426.887, rtol=1e-3) + assert_allclose(pulse_time[0][0], 46.34044, rtol=1e-3) + assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) + + +def test_charge_extractor_factory(camera_waveforms): + waveforms, _ = camera_waveforms + extractor = WaveformExtractor.from_name('LocalWindowSum') + extractor(waveforms) + + +def test_charge_extractor_factory_args(): + """ + Config is supposed to be created by a `Tool` + """ + from traitlets.config.loader import Config + config = Config( + { + 'WaveformExtractor': { + 'window_width': 20, + 'window_shift': 3, + } + } + ) + + extractor = WaveformExtractor.from_name( + 'LocalWindowSum', + config=config, + ) + assert extractor.window_width == 20 + assert extractor.window_shift == 3 + + with pytest.warns(UserWarning): + WaveformExtractor.from_name( + 'FullWaveformSum', + config=config, + ) diff --git a/ctapipe/image/tests/test_waveform_reducer.py b/ctapipe/image/tests/test_waveform_reducer.py deleted file mode 100644 index a2aa8e28baa..00000000000 --- a/ctapipe/image/tests/test_waveform_reducer.py +++ /dev/null @@ -1,132 +0,0 @@ -import pytest -import numpy as np -from scipy.stats import norm -from numpy.testing import assert_allclose -from ctapipe.instrument import CameraGeometry -from ctapipe.image.charge_extractors import ( - ChargeExtractor, - FullIntegrator, - SimpleIntegrator, - GlobalPeakIntegrator, - LocalPeakIntegrator, - NeighbourPeakIntegrator, - AverageWfPeakIntegrator, -) - - -@pytest.fixture(scope='module') -def camera_waveforms(): - camera = CameraGeometry.from_name("CHEC") - - n_pixels = camera.n_pixels - n_samples = 96 - mid = n_samples // 2 - pulse_sigma = 6 - r_hi = np.random.RandomState(1) - r_lo = np.random.RandomState(2) - - x = np.arange(n_samples) - - # Randomize times - t_pulse_hi = r_hi.uniform(mid - 10, mid + 10, n_pixels)[:, None] - t_pulse_lo = r_lo.uniform(mid + 10, mid + 20, n_pixels)[:, None] - - # Create pulses - y_hi = norm.pdf(x, t_pulse_hi, pulse_sigma) - y_lo = norm.pdf(x, t_pulse_lo, pulse_sigma) - - # Randomize amplitudes - y_hi *= r_hi.uniform(100, 1000, n_pixels)[:, None] - y_lo *= r_lo.uniform(100, 1000, n_pixels)[:, None] - - y = np.stack([y_hi, y_lo]) - - return y, camera - - -def test_full_integration(camera_waveforms): - waveforms, _ = camera_waveforms - integrator = FullIntegrator() - integration, _, _ = integrator.extract_charge(waveforms) - - assert_allclose(integration[0][0], 545.945, rtol=1e-3) - assert_allclose(integration[1][0], 970.025, rtol=1e-3) - - -def test_simple_integration(camera_waveforms): - waveforms, _ = camera_waveforms - integrator = SimpleIntegrator(window_start=45) - integration, _, _ = integrator.extract_charge(waveforms) - - assert_allclose(integration[0][0], 232.559, rtol=1e-3) - assert_allclose(integration[1][0], 32.539, rtol=1e-3) - - -def test_global_peak_integration(camera_waveforms): - waveforms, _ = camera_waveforms - integrator = GlobalPeakIntegrator() - integration, _, _ = integrator.extract_charge(waveforms) - - assert_allclose(integration[0][0], 232.559, rtol=1e-3) - assert_allclose(integration[1][0], 425.406, rtol=1e-3) - - -def test_local_peak_integration(camera_waveforms): - waveforms, _ = camera_waveforms - integrator = LocalPeakIntegrator() - integration, _, _ = integrator.extract_charge(waveforms) - - assert_allclose(integration[0][0], 240.3, rtol=1e-3) - assert_allclose(integration[1][0], 427.158, rtol=1e-3) - - -def test_nb_peak_integration(camera_waveforms): - waveforms, camera = camera_waveforms - nei = camera.neighbor_matrix_where - integrator = NeighbourPeakIntegrator() - integrator.neighbours = nei - integration, _, _ = integrator.extract_charge(waveforms) - - assert_allclose(integration[0][0], 94.671, rtol=1e-3) - assert_allclose(integration[1][0], 426.887, rtol=1e-3) - - -def test_averagewf_peak_integration(camera_waveforms): - waveforms, _ = camera_waveforms - integrator = AverageWfPeakIntegrator() - integration, _, _ = integrator.extract_charge(waveforms) - - assert_allclose(integration[0][0], 232.559, rtol=1e-3) - assert_allclose(integration[1][0], 425.406, rtol=1e-3) - - -def test_charge_extractor_factory(camera_waveforms): - waveforms, _ = camera_waveforms - extractor = ChargeExtractor.from_name('LocalPeakIntegrator') - extractor.extract_charge(waveforms) - - -def test_charge_extractor_factory_args(): - '''config is supposed to be created by a `Tool` - ''' - from traitlets.config.loader import Config - config = Config( - { - 'ChargeExtractor': { - 'window_width': 20, - 'window_shift': 3, - } - } - ) - - local_peak_integrator = ChargeExtractor.from_name( - 'LocalPeakIntegrator', - config=config, - ) - assert local_peak_integrator.window_width == 20 - assert local_peak_integrator.window_shift == 3 - - ChargeExtractor.from_name( - 'FullIntegrator', - config=config, - ) From f5ec3b2cdf89ae69c75461202939488357a526bf Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 15:57:18 +0100 Subject: [PATCH 04/25] Removed waveform_cleaning.py --- ctapipe/image/tests/test_waveform_cleaning.py | 64 ----- ctapipe/image/waveform_cleaning.py | 238 ------------------ 2 files changed, 302 deletions(-) delete mode 100644 ctapipe/image/tests/test_waveform_cleaning.py delete mode 100644 ctapipe/image/waveform_cleaning.py diff --git a/ctapipe/image/tests/test_waveform_cleaning.py b/ctapipe/image/tests/test_waveform_cleaning.py deleted file mode 100644 index 6de83f3bc1b..00000000000 --- a/ctapipe/image/tests/test_waveform_cleaning.py +++ /dev/null @@ -1,64 +0,0 @@ -import numpy as np -from numpy.testing import assert_almost_equal - -from ctapipe.image.waveform_cleaning import (NullWaveformCleaner, - CHECMWaveformCleanerAverage, - CHECMWaveformCleanerLocal, - BaselineWaveformCleaner) - - -def test_null_cleaner(example_event): - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality - - cleaner = NullWaveformCleaner() - cleaned = cleaner.apply(data_ped) - - assert (np.array_equal(data_ped, cleaned)) - - -def test_checm_cleaner_average(example_event): - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality - - cleaner = CHECMWaveformCleanerAverage() - cleaner.apply(data_ped) - - -def test_checm_cleaner_local(example_event): - telid = list(example_event.r0.tel)[0] - data = example_event.r0.tel[telid].waveform - nsamples = data.shape[2] - ped = example_event.mc.tel[telid].pedestal - data_ped = data - np.atleast_3d(ped / nsamples) - data_ped = np.array([data_ped[0], data_ped[0]]) # Test LG functionality - - cleaner = CHECMWaveformCleanerLocal() - cleaner.apply(data_ped) - - -def test_baseline_cleaner(): - - # waveform : first 20 samples = 0, second 20 samples = 10 - waveform = np.full((2, 1855, 40), 10) - waveform[:, :, 0:20] = 0 - - cleaner = BaselineWaveformCleaner() - - cleaner.baseline_start = 0 - cleaner.baseline_end = 20 - cleaned = cleaner.apply(waveform) - assert (cleaned.mean() == 5) - - cleaner.baseline_start = 20 - cleaner.baseline_end = 40 - cleaned = cleaner.apply(waveform) - assert (cleaned.mean() == -5) diff --git a/ctapipe/image/waveform_cleaning.py b/ctapipe/image/waveform_cleaning.py deleted file mode 100644 index ea8b864d42e..00000000000 --- a/ctapipe/image/waveform_cleaning.py +++ /dev/null @@ -1,238 +0,0 @@ -""" -Waveform cleaning algorithms (smoothing, filtering, baseline subtraction) -""" - -from abc import abstractmethod - -import numpy as np -from scipy.signal import general_gaussian -from traitlets import Int - -from ctapipe.core import Component -from ctapipe.image.charge_extractors import (AverageWfPeakIntegrator, - LocalPeakIntegrator) - -__all__ = ['WaveformCleaner', 'CHECMWaveformCleanerAverage', - 'CHECMWaveformCleanerLocal', 'BaselineWaveformCleaner', - 'NullWaveformCleaner'] - - -class WaveformCleaner(Component): - """ - Base component to handle the cleaning of the waveforms in an image. - - Parameters - ---------- - 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. - tool : ctapipe.core.Tool or None - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - kwargs - """ - def __init__(self, config=None, parent=None, **kwargs): - super().__init__(config=config, parent=parent, **kwargs) - - @abstractmethod - def apply(self, waveforms): - """ - Apply the cleaning method to the waveforms - - Parameters - ---------- - waveforms : ndarray - Waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - - Returns - ------- - cleaned : ndarray - Cleaned waveforms stored in a numpy array of shape - (n_chan, n_pix, n_samples). - - """ - pass - - -class NullWaveformCleaner(WaveformCleaner): - """ - Dummy waveform cleaner that simply returns its input - """ - - def apply(self, waveforms): - return waveforms - - -class BaselineWaveformCleaner(WaveformCleaner): - """ - Basic waveform cleaner that subtracts the waveform baseline - estimated as the mean waveform value in the interval [baseline_start,baseline_end] - """ - baseline_start = Int(0, help='Start sample for baseline estimation').tag(config=True) - - baseline_end = Int(10, help='End sample for baseline estimation').tag(config=True) - - def apply(self, waveforms): - # Subtract baseline - baseline_corrected = waveforms - np.mean( - waveforms[:, :, self.baseline_start:self.baseline_end], axis=2 - )[:, :, None] - - return baseline_corrected - - -class CHECMWaveformCleaner(WaveformCleaner): - """ - Waveform cleaner used by CHEC-M. - - This cleaner performs 2 basline subtractions: a simple subtraction - using the average of the first 32 samples in the waveforms, then a - convolved baseline subtraction to remove and low frequency drifts in - the baseline. - - Parameters - ---------- - 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. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - - """ - window_width = Int(16, help='Define the width of the pulse ' - 'window').tag(config=True) - window_shift = Int(8, help='Define the shift of the pulse window from the ' - 'peakpos (peakpos - shift).').tag(config=True) - - def __init__(self, config=None, parent=None, **kwargs): - super().__init__(config=config, parent=parent, **kwargs) - - # Cleaning steps for plotting - self.stages = {} - self.stage_names = ['0: raw', - '1: baseline_sub', - '2: no_pulse', - '3: smooth_baseline', - '4: smooth_wf', - '5: cleaned'] - - self.kernel = general_gaussian(10, p=1.0, sig=32) - - self.extractor = self.get_extractor() - - @abstractmethod - def get_extractor(self): - """ - Get the extractor to be used to define a window used to mask out the - pulse. - - Returns - ------- - `ChargeExtractor` - - """ - - def apply(self, waveforms): - samples = waveforms[0] - - # Subtract initial baseline - baseline_sub = samples - np.mean(samples[:, :32], axis=1)[:, None] - - # Obtain waveform with pulse masked - baseline_sub_b = baseline_sub[None, ...] - _, _, window = self.extractor.extract_charge(waveforms) - windowed = np.ma.array(baseline_sub_b, mask=window[0]) - no_pulse = np.ma.filled(windowed, 0)[0] - - # Get smooth baseline (no pulse) - smooth_flat = np.convolve(no_pulse.ravel(), self.kernel, "same") - smooth_baseline = np.reshape(smooth_flat, samples.shape) - no_pulse_std = np.std(no_pulse, axis=1) - smooth_baseline_std = np.std(smooth_baseline, axis=1) - with np.errstate(divide='ignore', invalid='ignore'): - smooth_baseline *= (no_pulse_std / smooth_baseline_std)[:, None] - smooth_baseline[~np.isfinite(smooth_baseline)] = 0 - - # Get smooth waveform - smooth_wf = baseline_sub # self.wf_smoother.apply(baseline_sub) - - # Subtract smooth baseline - cleaned = smooth_wf - smooth_baseline - - self.stages['0: raw'] = samples - self.stages['1: baseline_sub'] = baseline_sub - self.stages['window'] = window - self.stages['2: no_pulse'] = no_pulse - self.stages['3: smooth_baseline'] = smooth_baseline - self.stages['4: smooth_wf'] = smooth_wf - self.stages['5: cleaned'] = cleaned - - return cleaned[None, :] - - -class CHECMWaveformCleanerAverage(CHECMWaveformCleaner): - """ - Waveform cleaner used by CHEC-M. - - This cleaner performs 2 basline subtractions: a simple subtraction - using the average of the first 32 samples in the waveforms, then a - convolved baseline subtraction to remove and low frequency drifts in - the baseline. - - This particular cleaner obtains the peak position using an - `AverageWfPeakIntegrator`. - - Parameters - ---------- - 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. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - - """ - - def get_extractor(self): - return AverageWfPeakIntegrator(None, self.parent, - window_width=self.window_width, - window_shift=self.window_shift) - - -class CHECMWaveformCleanerLocal(CHECMWaveformCleaner): - """ - Waveform cleaner used by CHEC-M. - - This cleaner performs 2 basline subtractions: a simple subtraction - using the average of the first 32 samples in the waveforms, then a - convolved baseline subtraction to remove and low frequency drifts in - the baseline. - - This particular cleaner obtains the peak position using an - `LocalPeakIntegrator`. - - Parameters - ---------- - 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. - tool : ctapipe.core.Tool - Tool executable that is calling this component. - Passes the correct logger to the component. - Set to None if no Tool to pass. - - """ - - def get_extractor(self): - return LocalPeakIntegrator(None, self.parent, - window_width=self.window_width, - window_shift=self.window_shift) From e8357693f4ea1e7e4c91af49586dd74ecc5ae17b Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 15:57:31 +0100 Subject: [PATCH 05/25] Updated containers --- ctapipe/io/containers.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ctapipe/io/containers.py b/ctapipe/io/containers.py index bad00cc5471..28333ceefd0 100644 --- a/ctapipe/io/containers.py +++ b/ctapipe/io/containers.py @@ -80,32 +80,28 @@ class InstrumentContainer(Container): class DL1CameraContainer(Container): - """Storage of output of camera calibration e.g the final calibrated - image in intensity units and other per-event calculated - calibration information. + """ + Storage of output of camera calibration e.g the final calibrated + image in intensity units and the pulse time. """ image = Field( None, - "np array of camera image, after waveform integration (N_pix)" + "Numpy array of camera image, after waveform extraction." + "Shape: (n_chan, n_pixel)" ) - gain_channel = Field(None, "boolean numpy array of which gain channel was " - "used for each pixel in the image ") - extracted_samples = Field( + pulse_time = Field( None, - "numpy array of bools indicating which samples were included in the " - "charge extraction as a result of the charge extractor chosen. " - "Shape=(nchan, npix, nsamples)." + "Numpy array containing position of the pulse as determined by " + "the waveform extractor." + "Shape: (n_chan, n_pixel, n_samples)" ) - peakpos = Field( + #TODO: Remove when gain selection added? + gain_channel = Field( None, - "numpy array containing position of the peak as determined by " - "the peak-finding algorithm for each pixel" - ) - cleaned = Field( - None, "numpy array containing the waveform after cleaning" + "boolean numpy array of which gain channel was used for each pixel " + "in the image " ) - class CameraCalibrationContainer(Container): """ Storage of externally calculated calibration parameters (not per-event) From c6c2d2be96f714e6e2ec1ce8c6e231331ad1ecfb Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 15:57:41 +0100 Subject: [PATCH 06/25] Updated __init__.py --- ctapipe/image/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ctapipe/image/__init__.py b/ctapipe/image/__init__.py index b2142be9855..7e43d00e060 100644 --- a/ctapipe/image/__init__.py +++ b/ctapipe/image/__init__.py @@ -1,8 +1,7 @@ from .hillas import * from .cleaning import * from .pixel_likelihood import * -from .charge_extractors import * -from .waveform_cleaning import * +from .waveform_extractor import * from .reducers import * from .muon import * from .geometry_converter import * From 7f59bef019056c81ab1f9d5fb1eb41204fa3d1bf Mon Sep 17 00:00:00 2001 From: watsonjj Date: Mon, 25 Mar 2019 15:58:03 +0100 Subject: [PATCH 07/25] Updated calibration to use new WaveformExtractors --- ctapipe/calib/camera/calibrator.py | 21 +++------- ctapipe/calib/camera/dl1.py | 41 ++++++------------- ctapipe/calib/camera/tests/test_calibrator.py | 6 +-- 3 files changed, 22 insertions(+), 46 deletions(-) diff --git a/ctapipe/calib/camera/calibrator.py b/ctapipe/calib/camera/calibrator.py index 08a09e9323c..4ec14ccf1ed 100644 --- a/ctapipe/calib/camera/calibrator.py +++ b/ctapipe/calib/camera/calibrator.py @@ -10,7 +10,7 @@ CameraDL0Reducer, CameraDL1Calibrator, ) -from ctapipe.image import ChargeExtractor, WaveformCleaner +from ctapipe.image import WaveformExtractor __all__ = ['CameraCalibrator'] @@ -48,8 +48,7 @@ class CameraCalibrator(Component): """ def __init__(self, config=None, parent=None, r1_product=None, - extractor_product='NeighbourPeakIntegrator', - cleaner_product='NullWaveformCleaner', + extractor_name='NeighborWindowSum', eventsource=None, **kwargs): """ @@ -65,10 +64,8 @@ def __init__(self, config=None, parent=None, Set to None if no Tool to pass. r1_product : str The R1 calibrator to use. Manually overrides the Factory. - extractor_product : str - The ChargeExtractor to use. Manually overrides the Factory. - cleaner_product : str - The WaveformCleaner to use. Manually overrides the Factory. + extractor_name : str + The name of the WaveformExtractor to use. eventsource : ctapipe.io.eventsource.EventSource EventSource that is being used to read the events. The EventSource contains information (such as metadata or inst) which indicates @@ -77,13 +74,8 @@ def __init__(self, config=None, parent=None, """ super().__init__(config=config, parent=parent, **kwargs) - extractor = ChargeExtractor.from_name( - extractor_product, - parent=self, - ) - - cleaner = WaveformCleaner.from_name( - cleaner_product, + extractor = WaveformExtractor.from_name( + extractor_name, parent=self, ) @@ -103,7 +95,6 @@ def __init__(self, config=None, parent=None, self.dl1 = CameraDL1Calibrator( parent=self, extractor=extractor, - cleaner=cleaner, ) def calibrate(self, event): diff --git a/ctapipe/calib/camera/dl1.py b/ctapipe/calib/camera/dl1.py index b0fe84954d0..269fdb3ca85 100644 --- a/ctapipe/calib/camera/dl1.py +++ b/ctapipe/calib/camera/dl1.py @@ -11,7 +11,7 @@ from ...core import Component from ...core.traits import Float -from ...image import NeighbourPeakIntegrator, NullWaveformCleaner +from ...image import NeighborWindowSum __all__ = ['CameraDL1Calibrator'] @@ -91,13 +91,11 @@ class CameraDL1Calibrator(Component): Tool executable that is calling this component. Passes the correct logger to the component. Set to None if no Tool to pass. - extractor : ctapipe.calib.camera.charge_extractors.ChargeExtractor - The extractor to use to extract the charge from the waveforms. - By default the NeighbourPeakIntegrator with default configuration + extractor : ctapipe.calib.camera.waveform_reducer.WaveformExtractor + The reducer to use to extract the charge and pulse time from + the waveforms. + By default the NeighborWindowSum with default configuration is used. - cleaner : ctapipe.calib.camera.waveform_cleaners.Cleaner - The waveform cleaner to use. By default no cleaning is - applied to the waveforms. kwargs """ radius = Float(None, allow_none=True, @@ -109,15 +107,11 @@ class CameraDL1Calibrator(Component): 'clipped. Set to None for no ' 'clipping.').tag(config=True) - def __init__(self, config=None, parent=None, extractor=None, cleaner=None, - **kwargs): + def __init__(self, config=None, parent=None, extractor=None, **kwargs): super().__init__(config=config, parent=parent, **kwargs) self.extractor = extractor if self.extractor is None: - self.extractor = NeighbourPeakIntegrator(config, parent) - self.cleaner = cleaner - if self.cleaner is None: - self.cleaner = NullWaveformCleaner(config, parent) + self.extractor = NeighborWindowSum(config, parent) self._dl0_empty_warn = False def check_dl0_exists(self, event, telid): @@ -199,20 +193,13 @@ def calibrate(self, event): if n_samples == 1: # To handle ASTRI and dst corrected = waveforms[..., 0] - window = np.ones(waveforms.shape) - peakpos = np.zeros(waveforms.shape[0:2]) - cleaned = waveforms + pulse_time = np.zeros(waveforms.shape[0:2]) else: - # Clean waveforms - cleaned = self.cleaner.apply(waveforms) - - # Extract charge - if self.extractor.requires_neighbours(): - e = self.extractor + # Extract charge and pulse time + if self.extractor.requires_neighbors(): g = event.inst.subarray.tel[telid].camera - e.neighbours = g.neighbor_matrix_where - extract = self.extractor.extract_charge - charge, peakpos, window = extract(cleaned) + self.extractor.neighbors = g.neighbor_matrix_where + charge, pulse_time = self.extractor(waveforms) # Apply integration correction correction = self.get_correction(event, telid)[:, None] @@ -225,6 +212,4 @@ def calibrate(self, event): # Store into event container event.dl1.tel[telid].image = corrected - event.dl1.tel[telid].extracted_samples = window - event.dl1.tel[telid].peakpos = peakpos - event.dl1.tel[telid].cleaned = cleaned + event.dl1.tel[telid].pulse_time = pulse_time diff --git a/ctapipe/calib/camera/tests/test_calibrator.py b/ctapipe/calib/camera/tests/test_calibrator.py index 38120811b87..67418434b1b 100644 --- a/ctapipe/calib/camera/tests/test_calibrator.py +++ b/ctapipe/calib/camera/tests/test_calibrator.py @@ -5,7 +5,7 @@ HESSIOR1Calibrator, NullR1Calibrator ) -from ctapipe.image.charge_extractors import LocalPeakIntegrator +from ctapipe.image.waveform_extractor import LocalWindowSum from ctapipe.io import SimTelEventSource from ctapipe.utils import get_dataset_path @@ -26,8 +26,8 @@ def test_manual_r1(): def test_manual_extractor(): - calibrator = CameraCalibrator(extractor_product="LocalPeakIntegrator") - assert isinstance(calibrator.dl1.extractor, LocalPeakIntegrator) + calibrator = CameraCalibrator(extractor_name="LocalWindowSum") + assert isinstance(calibrator.dl1.extractor, LocalWindowSum) def test_eventsource_r1(): From 91a10c8314394e0aec161706ea7365909b0d6d9f Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 11:17:18 +0100 Subject: [PATCH 08/25] Fix name mixup after merge --- ctapipe/image/waveform_extractor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ctapipe/image/waveform_extractor.py b/ctapipe/image/waveform_extractor.py index 2fd8f530dda..80f02be7acb 100644 --- a/ctapipe/image/waveform_extractor.py +++ b/ctapipe/image/waveform_extractor.py @@ -204,7 +204,7 @@ def __call__(self, waveforms): return charge, pulse_time -class SimpleIntegrator(WaveformExtractor): +class UserWindowSum(WaveformExtractor): """ Waveform extractor that integrates within a window defined by the user. """ @@ -223,9 +223,9 @@ def __call__(self, waveforms): return charge, pulse_time -class GlobalPeakIntegrator(WaveformExtractor): +class GlobalWindowSum(WaveformExtractor): """ - Charge extractor that defines an integration window about the global + Waveform extractor that defines an integration window about the global peak in the image. """ window_width = Int( @@ -236,7 +236,7 @@ class GlobalPeakIntegrator(WaveformExtractor): 'from the peakpos (peakpos - shift)' ).tag(config=True) - def extract_charge(self, waveforms): + def __call__(self, waveforms): max_t = waveforms.argmax(2) max_s = waveforms.max(2) peakpos = np.round( From 55324f068721a05df17d71df5094c5981dbb8175 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 12:27:12 +0100 Subject: [PATCH 09/25] Limit range for pulse_time calculation --- ctapipe/image/waveform_extractor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ctapipe/image/waveform_extractor.py b/ctapipe/image/waveform_extractor.py index 80f02be7acb..f1f6ddb2d81 100644 --- a/ctapipe/image/waveform_extractor.py +++ b/ctapipe/image/waveform_extractor.py @@ -78,6 +78,8 @@ def extract_pulse_time_weighted_average(waveforms): """ samples_i = np.indices(waveforms.shape)[2] pulse_time = np.average(samples_i, weights=waveforms, axis=2) + pulse_time[pulse_time < 0] = 0 + pulse_time[pulse_time > waveforms.shape[2]] = waveforms.shape[2] return pulse_time From 431f23f50339549a5ff5fe0a9bde06f4a27e715e Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 12:27:40 +0100 Subject: [PATCH 10/25] Update bokeh event plotter to handle new WaveformExtractor --- ctapipe/plotting/bokeh_event_viewer.py | 36 +---------------- ctapipe/tools/bokeh/file_viewer.py | 56 +++++--------------------- 2 files changed, 12 insertions(+), 80 deletions(-) diff --git a/ctapipe/plotting/bokeh_event_viewer.py b/ctapipe/plotting/bokeh_event_viewer.py index f54e5350a7f..877bffaa4fa 100644 --- a/ctapipe/plotting/bokeh_event_viewer.py +++ b/ctapipe/plotting/bokeh_event_viewer.py @@ -30,8 +30,7 @@ def __init__(self, event_viewer, fig=None): 'r1': lambda e, t, c, time: e.r1.tel[t].waveform[c, :, time], 'dl0': lambda e, t, c, time: e.dl0.tel[t].waveform[c, :, time], 'dl1': lambda e, t, c, time: e.dl1.tel[t].image[c, :], - 'peakpos': lambda e, t, c, time: e.dl1.tel[t].peakpos[c, :], - 'cleaned': lambda e, t, c, time: e.dl1.tel[t].cleaned[c, :, time], + 'pulse_time': lambda e, t, c, time: e.dl1.tel[t].pulse_time[c, :], } self.w_view = None @@ -174,13 +173,11 @@ def __init__(self, event_viewer, fig=None): self._channel = 0 self._pixel = 0 super().__init__(fig=fig) - self._draw_integration_window() self._view_options = { 'r0': lambda e, t, c, p: e.r0.tel[t].waveform[c, p], 'r1': lambda e, t, c, p: e.r1.tel[t].waveform[c, p], 'dl0': lambda e, t, c, p: e.dl0.tel[t].waveform[c, p], - 'cleaned': lambda e, t, c, p: e.dl1.tel[t].cleaned[c, p], } self.w_view = None @@ -213,34 +210,8 @@ def _set_waveform(self): except TypeError: self.waveform = None - def _draw_integration_window(self): - self.intwin1 = Span(location=0, dimension='height', - line_color='green', line_dash='dotted') - self.intwin2 = Span(location=0, dimension='height', - line_color='green', line_dash='dotted') - self.fig.add_layout(self.intwin1) - self.fig.add_layout(self.intwin2) - - def _set_integration_window(self): - e = self.event - t = self.telid - c = self.channel - p = self.pixel - if e: - if e.dl1.tel[t].extracted_samples is not None: - # Get Windows - windows = e.dl1.tel[t].extracted_samples[c, p] - length = np.sum(windows) - start = np.argmax(windows) - end = start + length - 1 - self.intwin1.location = start - self.intwin2.location = end - else: - self.event_viewer.log.warning("No event has been provided") - def refresh(self): self._set_waveform() - self._set_integration_window() @property def event(self): @@ -250,7 +221,6 @@ def event(self): def event(self, val): self._event = val self._set_waveform() - self._set_integration_window() def change_event(self, event, telid): if self.event: # Only reset when an event exists @@ -268,7 +238,6 @@ def view(self, val): raise ValueError(f"View is not valid: {val}") self._view = val self._set_waveform() - self._set_integration_window() @property def telid(self): @@ -280,7 +249,6 @@ def telid(self, val): self._reset() self._telid = val self._set_waveform() - self._set_integration_window() @property def channel(self): @@ -290,7 +258,6 @@ def channel(self): def channel(self, val): self._channel = val self._set_waveform() - self._set_integration_window() @property def pixel(self): @@ -300,7 +267,6 @@ def pixel(self): def pixel(self, val): self._pixel = val self._set_waveform() - self._set_integration_window() def _on_waveform_click(self, time): super()._on_waveform_click(time) diff --git a/ctapipe/tools/bokeh/file_viewer.py b/ctapipe/tools/bokeh/file_viewer.py index acb5aa16791..5a362d573f0 100644 --- a/ctapipe/tools/bokeh/file_viewer.py +++ b/ctapipe/tools/bokeh/file_viewer.py @@ -9,8 +9,7 @@ from ctapipe.calib.camera.dl1 import CameraDL1Calibrator from ctapipe.calib.camera.r1 import CameraR1Calibrator from ctapipe.core import Tool -from ctapipe.image.charge_extractors import ChargeExtractor -from ctapipe.image.waveform_cleaning import WaveformCleaner +from ctapipe.image.waveform_extractor import WaveformExtractor from ctapipe.io import EventSource from ctapipe.io.eventseeker import EventSeeker from ctapipe.plotting.bokeh_event_viewer import BokehEventViewer @@ -30,13 +29,9 @@ class BokehFileViewer(Tool): default_url = get_dataset_path("gamma_test_large.simtel.gz") EventSource.input_url.default_value = default_url - cleaner_product = tool_utils.enum_trait( - WaveformCleaner, - default='NullWaveformCleaner' - ) extractor_product = tool_utils.enum_trait( - ChargeExtractor, - default='NeighbourPeakIntegrator' + WaveformExtractor, + default='NeighborWindowSum' ) aliases = Dict(dict( @@ -45,15 +40,13 @@ class BokehFileViewer(Tool): f='EventSource.input_url', max_events='EventSource.max_events', extractor='BokehFileViewer.extractor_product', - cleaner='BokehFileViewer.cleaner_product', )) classes = List( [ EventSource, CameraDL1Calibrator, - ] + tool_utils.classes_with_traits(WaveformCleaner) - + tool_utils.classes_with_traits(ChargeExtractor) + ] + tool_utils.classes_with_traits(WaveformExtractor) + tool_utils.classes_with_traits(CameraR1Calibrator) ) @@ -80,7 +73,6 @@ def __init__(self, **kwargs): self.reader = None self.seeker = None self.extractor = None - self.cleaner = None self.r1 = None self.dl0 = None self.dl1 = None @@ -94,14 +86,10 @@ def setup(self): self.reader = EventSource.from_config(parent=self) self.seeker = EventSeeker(self.reader, parent=self) - self.extractor = ChargeExtractor.from_name( + self.extractor = WaveformExtractor.from_name( self.extractor_product, parent=self ) - self.cleaner = WaveformCleaner.from_name( - self.cleaner_product, - parent=self - ) self.r1 = CameraR1Calibrator.from_eventsource( eventsource=self.reader, parent=self @@ -109,7 +97,6 @@ def setup(self): self.dl0 = CameraDL0Reducer(parent=self) self.dl1 = CameraDL1Calibrator( extractor=self.extractor, - cleaner=self.cleaner, parent=self ) @@ -241,26 +228,21 @@ def event(self, val): self._channel = self.viewer.channel self.update_channel_widget() - def update_dl1_calibrator(self, extractor=None, cleaner=None): + def update_dl1_calibrator(self, extractor=None): """ Recreate the dl1 calibrator with the specified extractor and cleaner Parameters ---------- extractor : ctapipe.image.charge_extractors.ChargeExtractor - cleaner : ctapipe.image.waveform_cleaning.WaveformCleaner """ if extractor is None: extractor = self.dl1.extractor - if cleaner is None: - cleaner = self.dl1.cleaner self.extractor = extractor - self.cleaner = cleaner self.dl1 = CameraDL1Calibrator( extractor=self.extractor, - cleaner=self.cleaner, parent=self ) self.dl1.calibrate(self.event) @@ -354,11 +336,9 @@ def on_channel_widget_change(self, _, __, ___): def create_dl1_widgets(self): self.w_dl1_dict = dict( - cleaner=Select(title="Cleaner:", value='', width=5, - options=BokehFileViewer.cleaner_product.values), extractor=Select(title="Extractor:", value='', width=5, options=BokehFileViewer.extractor_product.values), - extractor_t0=TextInput(title="T0:", value=''), + extractor_window_start=TextInput(title="Window Start:", value=''), extractor_window_width=TextInput(title="Window Width:", value=''), extractor_window_shift=TextInput(title="Window Shift:", value=''), extractor_lwt=TextInput(title="Local Pixel Weight:", value='')) @@ -368,9 +348,8 @@ def create_dl1_widgets(self): self.wb_extractor = widgetbox( PreText(text="Charge Extractor Configuration"), - self.w_dl1_dict['cleaner'], self.w_dl1_dict['extractor'], - self.w_dl1_dict['extractor_t0'], + self.w_dl1_dict['extractor_window_start'], self.w_dl1_dict['extractor_window_width'], self.w_dl1_dict['extractor_window_shift'], self.w_dl1_dict['extractor_lwt']) @@ -387,15 +366,6 @@ def update_dl1_widget_values(self): val.value = str(getattr(self.extractor, key)) except AttributeError: val.value = '' - elif 'cleaner' in key: - if key == 'cleaner': - val.value = self.cleaner.__class__.__name__ - else: - key = key.replace("cleaner_", "") - try: - val.value = str(getattr(self.cleaner, key)) - except AttributeError: - val.value = '' def on_dl1_widget_change(self, _, __, ___): if self.event: @@ -403,18 +373,14 @@ def on_dl1_widget_change(self, _, __, ___): self._updating_dl1 = True cmdline = [] for key, val in self.w_dl1_dict.items(): - k = key.replace("extractor_", "ChargeExtractor.") - k = k.replace("cleaner_", "WaveformCleaner.") + k = key.replace("extractor_", "WaveformExtractor.") if val.value: cmdline.append(f'--{k}={val.value}') self.parse_command_line(cmdline) - extractor = ChargeExtractor.from_name( + extractor = WaveformExtractor.from_name( self.extractor_product, parent=self) - cleaner = WaveformCleaner.from_name( - self.cleaner_product, - parent=self) - self.update_dl1_calibrator(extractor, cleaner) + self.update_dl1_calibrator(extractor) self.update_dl1_widget_values() self._updating_dl1 = False From 1981a30b05528bcb2ef36f582644ade5991a692f Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 12:32:38 +0100 Subject: [PATCH 11/25] Update tools --- ctapipe/tools/display_dl1.py | 8 ++--- ctapipe/tools/display_integrator.py | 42 +++------------------- ctapipe/tools/extract_charge_resolution.py | 8 ++--- 3 files changed, 13 insertions(+), 45 deletions(-) diff --git a/ctapipe/tools/display_dl1.py b/ctapipe/tools/display_dl1.py index b0006f01c32..3ff85147557 100644 --- a/ctapipe/tools/display_dl1.py +++ b/ctapipe/tools/display_dl1.py @@ -9,7 +9,7 @@ from ctapipe.visualization import CameraDisplay from ctapipe.core import Tool, Component from ctapipe.utils import get_dataset_path -from ctapipe.image.charge_extractors import ChargeExtractor +from ctapipe.image.waveform_extractor import WaveformExtractor from ctapipe.io import EventSource import ctapipe.utils.tools as tool_utils @@ -142,8 +142,8 @@ class DisplayDL1Calib(Tool): ).tag(config=True) extractor_product = tool_utils.enum_trait( - ChargeExtractor, - default='NeighbourPeakIntegrator' + WaveformExtractor, + default='NeighborWindowSum' ) aliases = Dict( @@ -172,7 +172,7 @@ class DisplayDL1Calib(Tool): EventSource, CameraDL1Calibrator, ImagePlotter - ] + tool_utils.classes_with_traits(ChargeExtractor) + ] + tool_utils.classes_with_traits(WaveformExtractor) ) def __init__(self, **kwargs): diff --git a/ctapipe/tools/display_integrator.py b/ctapipe/tools/display_integrator.py index acb5ec9ebef..5b01225be73 100644 --- a/ctapipe/tools/display_integrator.py +++ b/ctapipe/tools/display_integrator.py @@ -11,7 +11,7 @@ from ctapipe.calib.camera import CameraR1Calibrator, CameraDL0Reducer, \ CameraDL1Calibrator from ctapipe.core import Tool -from ctapipe.image.charge_extractors import ChargeExtractor +from ctapipe.image.waveform_extractor import WaveformExtractor from ctapipe.io.eventseeker import EventSeeker from ctapipe.io import EventSource from ctapipe.visualization import CameraDisplay @@ -35,12 +35,6 @@ def plot(event, telid, chan, extractor_name): max_pixel_nei = nei[max_pix] min_pixel_nei = nei[min_pix] - # Get Windows - windows = event.dl1.tel[telid].extracted_samples[chan] - length = np.sum(windows, axis=1) - start = np.argmax(windows, axis=1) - end = start + length - 1 - # Draw figures ax_max_nei = {} ax_min_nei = {} @@ -71,14 +65,6 @@ def plot(event, telid, chan, extractor_name): f'Measured = {dl1[max_pix]:.3f}' ) max_ylim = ax_max_pix.get_ylim() - ax_max_pix.plot([start[max_pix], start[max_pix]], - ax_max_pix.get_ylim(), - color='r', - alpha=1) - ax_max_pix.plot([end[max_pix], end[max_pix]], - ax_max_pix.get_ylim(), - color='r', - alpha=1) for i, ax in ax_max_nei.items(): if len(max_pixel_nei) > i: pix = max_pixel_nei[i] @@ -90,11 +76,6 @@ def plot(event, telid, chan, extractor_name): .format(pix, t_pe[pix], dl1[pix]) ) ax.set_ylim(max_ylim) - ax.plot([start[pix], start[pix]], - ax.get_ylim(), - color='r', - alpha=1) - ax.plot([end[pix], end[pix]], ax.get_ylim(), color='r', alpha=1) # Draw min pixel traces ax_min_pix.plot(dl0[min_pix]) @@ -105,14 +86,6 @@ def plot(event, telid, chan, extractor_name): f'Measured = {dl1[min_pix]:.3f}' ) ax_min_pix.set_ylim(max_ylim) - ax_min_pix.plot([start[min_pix], start[min_pix]], - ax_min_pix.get_ylim(), - color='r', - alpha=1) - ax_min_pix.plot([end[min_pix], end[min_pix]], - ax_min_pix.get_ylim(), - color='r', - alpha=1) for i, ax in ax_min_nei.items(): if len(min_pixel_nei) > i: pix = min_pixel_nei[i] @@ -124,11 +97,6 @@ def plot(event, telid, chan, extractor_name): f'Measured = {dl1[pix]:.3f}' ) ax.set_ylim(max_ylim) - ax.plot([start[pix], start[pix]], - ax.get_ylim(), - color='r', - alpha=1) - ax.plot([end[pix], end[pix]], ax.get_ylim(), color='r', alpha=1) # Draw cameras nei_camera = np.zeros_like(max_charges, dtype=np.int) @@ -259,8 +227,8 @@ class DisplayIntegrator(Tool): channel = Enum([0, 1], 0, help='Channel to view').tag(config=True) extractor_product = tool_utils.enum_trait( - ChargeExtractor, - default='NeighbourPeakIntegrator' + WaveformExtractor, + default='NeighborWindowSum' ) aliases = Dict( @@ -288,7 +256,7 @@ class DisplayIntegrator(Tool): [ EventSource, CameraDL1Calibrator, - ] + tool_utils.classes_with_traits(ChargeExtractor) + ] + tool_utils.classes_with_traits(WaveformExtractor) ) def __init__(self, **kwargs): @@ -304,7 +272,7 @@ def setup(self): event_source = EventSource.from_config(parent=self) self.eventseeker = EventSeeker(event_source, parent=self) - self.extractor = ChargeExtractor.from_name( + self.extractor = WaveformExtractor.from_name( self.extractor_product, parent=self, ) diff --git a/ctapipe/tools/extract_charge_resolution.py b/ctapipe/tools/extract_charge_resolution.py index ac766bb6ab9..5ac18ac4f21 100644 --- a/ctapipe/tools/extract_charge_resolution.py +++ b/ctapipe/tools/extract_charge_resolution.py @@ -18,7 +18,7 @@ from ctapipe.calib.camera.dl1 import CameraDL1Calibrator from ctapipe.calib.camera.r1 import HESSIOR1Calibrator from ctapipe.core import Tool, Provenance -from ctapipe.image.charge_extractors import ChargeExtractor +from ctapipe.image.waveform_extractor import WaveformExtractor from ctapipe.io.simteleventsource import SimTelEventSource @@ -36,7 +36,7 @@ class ChargeResolutionGenerator(Tool): help='Path to store the output HDF5 file' ).tag(config=True) extractor_product = tool_utils.enum_trait( - ChargeExtractor, + WaveformExtractor, default='NeighbourPeakIntegrator' ) @@ -54,7 +54,7 @@ class ChargeResolutionGenerator(Tool): SimTelEventSource, CameraDL1Calibrator, ChargeResolutionCalculator - ] + tool_utils.classes_with_traits(ChargeExtractor) + ] + tool_utils.classes_with_traits(WaveformExtractor) ) def __init__(self, **kwargs): @@ -71,7 +71,7 @@ def setup(self): self.eventsource = SimTelEventSource(**kwargs) - extractor = ChargeExtractor.from_name( + extractor = WaveformExtractor.from_name( self.extractor_product, **kwargs ) From 54a9f8a0c67082d88f2017f342896b6b31a437eb Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 15:47:44 +0100 Subject: [PATCH 12/25] Correct references to ChargeExtractor --- ctapipe/calib/camera/dl1.py | 6 ++---- ctapipe/image/tests/test_waveform_extractor.py | 4 ++-- ctapipe/tools/bokeh/file_viewer.py | 2 +- ctapipe/utils/tests/test_tools.py | 8 ++++---- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/ctapipe/calib/camera/dl1.py b/ctapipe/calib/camera/dl1.py index 269fdb3ca85..ec5f5a2e62e 100644 --- a/ctapipe/calib/camera/dl1.py +++ b/ctapipe/calib/camera/dl1.py @@ -2,10 +2,8 @@ Calibrator for the DL0 -> DL1 data level transition. This module handles the calibration from the DL0 data level to DL1. This -transition involves the waveform cleaning (such as filtering, smoothing, -or basline subtraction) performed by a cleaner from -`ctapipe.image.waveform_cleaning`, and the charge extraction technique -from `ctapipe.image.charge_extractors`. +transition involves the extraction of parameters from the waveform using a +`ctapipe.image.waveform_extractor.WaveformExtractor`. """ import numpy as np diff --git a/ctapipe/image/tests/test_waveform_extractor.py b/ctapipe/image/tests/test_waveform_extractor.py index 3b607762b93..06e26027781 100644 --- a/ctapipe/image/tests/test_waveform_extractor.py +++ b/ctapipe/image/tests/test_waveform_extractor.py @@ -147,13 +147,13 @@ def test_baseline_subtracted_neighbor_window_sum(camera_waveforms): assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) -def test_charge_extractor_factory(camera_waveforms): +def test_waveform_extractor_factory(camera_waveforms): waveforms, _ = camera_waveforms extractor = WaveformExtractor.from_name('LocalWindowSum') extractor(waveforms) -def test_charge_extractor_factory_args(): +def test_waveform_extractor_factory_args(): """ Config is supposed to be created by a `Tool` """ diff --git a/ctapipe/tools/bokeh/file_viewer.py b/ctapipe/tools/bokeh/file_viewer.py index 5a362d573f0..de97a12e405 100644 --- a/ctapipe/tools/bokeh/file_viewer.py +++ b/ctapipe/tools/bokeh/file_viewer.py @@ -234,7 +234,7 @@ def update_dl1_calibrator(self, extractor=None): Parameters ---------- - extractor : ctapipe.image.charge_extractors.ChargeExtractor + extractor : ctapipe.image.waveform_extractors.WaveformExtractor """ if extractor is None: extractor = self.dl1.extractor diff --git a/ctapipe/utils/tests/test_tools.py b/ctapipe/utils/tests/test_tools.py index 21921996c97..d545b3b7826 100644 --- a/ctapipe/utils/tests/test_tools.py +++ b/ctapipe/utils/tests/test_tools.py @@ -3,7 +3,7 @@ from traitlets import HasTraits from traitlets import Int # using this class as test input -from ctapipe.image.charge_extractors import ChargeExtractor +from ctapipe.image.waveform_extractor import WaveformExtractor def test_enum_trait_default_is_right(): @@ -11,14 +11,14 @@ def test_enum_trait_default_is_right(): from ctapipe.utils.tools import enum_trait with pytest.raises(ValueError): - enum_trait(ChargeExtractor, default='name_of_default_choice') + enum_trait(WaveformExtractor, default='name_of_default_choice') def test_enum_trait(): # function under test from ctapipe.utils.tools import enum_trait - trait = enum_trait(ChargeExtractor, default='NeighbourPeakIntegrator') + trait = enum_trait(WaveformExtractor, default='NeighborWindowSum') assert isinstance(trait, traitlets.traitlets.CaselessStrEnum) @@ -26,7 +26,7 @@ def test_enum_classes_with_traits(): # function under test from ctapipe.utils.tools import classes_with_traits - list_of_classes = classes_with_traits(ChargeExtractor) + list_of_classes = classes_with_traits(WaveformExtractor) assert list_of_classes # should not be empty From a3419b3d9691134056d8252f73fe63e2f8957db8 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 15:49:09 +0100 Subject: [PATCH 13/25] Remove further references --- ctapipe/calib/camera/calibrator.py | 22 ---------------------- ctapipe/image/reducers.py | 4 ++-- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/ctapipe/calib/camera/calibrator.py b/ctapipe/calib/camera/calibrator.py index 4ec14ccf1ed..eab01a9873c 100644 --- a/ctapipe/calib/camera/calibrator.py +++ b/ctapipe/calib/camera/calibrator.py @@ -23,28 +23,6 @@ class CameraCalibrator(Component): This calibrator will apply the calibrations found in r1.py, dl0.py and dl1.py. - The following traitlet alias configuration is suggestion for configuring - the calibration inside a `ctapipe.core.Tool`: - - .. code-block:: python - - aliases = Dict(dict( - ped='CameraR1CalibratorFactory.pedestal_path', - tf='CameraR1CalibratorFactory.tf_path', - pe='CameraR1CalibratorFactory.adc2pe_path', - extractor='ChargeExtractorFactory.extractor', - extractor_t0='ChargeExtractorFactory.t0', - window_width='ChargeExtractorFactory.window_width', - window_shift='ChargeExtractorFactory.window_shift', - sig_amp_cut_HG='ChargeExtractorFactory.sig_amp_cut_HG', - sig_amp_cut_LG='ChargeExtractorFactory.sig_amp_cut_LG', - lwt='ChargeExtractorFactory.lwt', - clip_amplitude='CameraDL1Calibrator.clip_amplitude', - radius='CameraDL1Calibrator.radius', - cleaner='WaveformCleanerFactory.cleaner', - cleaner_t0='WaveformCleanerFactory.t0', - )) - """ def __init__(self, config=None, parent=None, r1_product=None, diff --git a/ctapipe/image/reducers.py b/ctapipe/image/reducers.py index e9f2b3aefd1..2e642780cb7 100644 --- a/ctapipe/image/reducers.py +++ b/ctapipe/image/reducers.py @@ -55,8 +55,8 @@ def __init__(self, config=None, parent=None, **kwargs): @staticmethod def requires_neighbours(): """ - Method used for callers of the ChargeExtractor to know if the extractor - requires knowledge of the pixel neighbours + Method used for callers of the WaveformExtractor to know if the + extractor requires knowledge of the pixel neighbours Returns ------- From d70c0dc89fc47e92b2f794c456b0508c7feb5c54 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 15:53:37 +0100 Subject: [PATCH 14/25] Correct tools --- ctapipe/tools/display_dl1.py | 32 +++++++++++----------- ctapipe/tools/extract_charge_resolution.py | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ctapipe/tools/display_dl1.py b/ctapipe/tools/display_dl1.py index 3ff85147557..fde7688664b 100644 --- a/ctapipe/tools/display_dl1.py +++ b/ctapipe/tools/display_dl1.py @@ -47,9 +47,9 @@ def __init__(self, config=None, parent=None, **kwargs): super().__init__(config=config, parent=parent, **kwargs) self._current_tel = None self.c_intensity = None - self.c_peakpos = None + self.c_pulse_time = None self.cb_intensity = None - self.cb_peakpos = None + self.cb_pulse_time = None self.pdf = None self._init_figure() @@ -57,7 +57,7 @@ def __init__(self, config=None, parent=None, **kwargs): def _init_figure(self): self.fig = plt.figure(figsize=(16, 7)) self.ax_intensity = self.fig.add_subplot(1, 2, 1) - self.ax_peakpos = self.fig.add_subplot(1, 2, 2) + self.ax_pulse_time = self.fig.add_subplot(1, 2, 2) if self.output_path: self.log.info(f"Creating PDF: {self.output_path}") self.pdf = PdfPages(self.output_path) @@ -69,21 +69,21 @@ def get_geometry(event, telid): def plot(self, event, telid): chan = 0 image = event.dl1.tel[telid].image[chan] - peakpos = event.dl1.tel[telid].peakpos[chan] + pulse_time = event.dl1.tel[telid].pulse_time[chan] if self._current_tel != telid: self._current_tel = telid self.ax_intensity.cla() - self.ax_peakpos.cla() + self.ax_pulse_time.cla() # Redraw camera geom = self.get_geometry(event, telid) self.c_intensity = CameraDisplay(geom, ax=self.ax_intensity) - self.c_peakpos = CameraDisplay(geom, ax=self.ax_peakpos) + self.c_pulse_time = CameraDisplay(geom, ax=self.ax_pulse_time) tmaxmin = event.dl0.tel[telid].waveform.shape[2] - t_chargemax = peakpos[image.argmax()] + t_chargemax = pulse_time[image.argmax()] cmap_time = colors.LinearSegmentedColormap.from_list( 'cmap_t', [(0 / tmaxmin, 'darkgreen'), @@ -91,7 +91,7 @@ def plot(self, event, telid): (t_chargemax / tmaxmin, 'yellow'), (1.4 * t_chargemax / tmaxmin, 'blue'), (1, 'darkblue')] ) - self.c_peakpos.pixels.set_cmap(cmap_time) + self.c_pulse_time.pixels.set_cmap(cmap_time) if not self.cb_intensity: self.c_intensity.add_colorbar( @@ -101,18 +101,18 @@ def plot(self, event, telid): else: self.c_intensity.colorbar = self.cb_intensity self.c_intensity.update(True) - if not self.cb_peakpos: - self.c_peakpos.add_colorbar( - ax=self.ax_peakpos, label='Peakpos (ns)' + if not self.cb_pulse_time: + self.c_pulse_time.add_colorbar( + ax=self.ax_pulse_time, label='Pulse Time (ns)' ) - self.cb_peakpos = self.c_peakpos.colorbar + self.cb_pulse_time = self.c_pulse_time.colorbar else: - self.c_peakpos.colorbar = self.cb_peakpos - self.c_peakpos.update(True) + self.c_pulse_time.colorbar = self.cb_pulse_time + self.c_pulse_time.update(True) self.c_intensity.image = image - if peakpos is not None: - self.c_peakpos.image = peakpos + if pulse_time is not None: + self.c_pulse_time.image = pulse_time self.fig.suptitle( "Event_index={} Event_id={} Telescope={}" diff --git a/ctapipe/tools/extract_charge_resolution.py b/ctapipe/tools/extract_charge_resolution.py index 5ac18ac4f21..f546c272d18 100644 --- a/ctapipe/tools/extract_charge_resolution.py +++ b/ctapipe/tools/extract_charge_resolution.py @@ -37,7 +37,7 @@ class ChargeResolutionGenerator(Tool): ).tag(config=True) extractor_product = tool_utils.enum_trait( WaveformExtractor, - default='NeighbourPeakIntegrator' + default='NeighborWindowSum' ) aliases = Dict(dict( From 23bc11012913073f92a28ab68c86afa21cfd3387 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Tue, 26 Mar 2019 15:56:04 +0100 Subject: [PATCH 15/25] Improved names of WaveformExtractors --- ctapipe/calib/camera/calibrator.py | 2 +- ctapipe/calib/camera/dl1.py | 6 ++-- ctapipe/calib/camera/tests/test_calibrator.py | 6 ++-- .../image/tests/test_waveform_extractor.py | 28 +++++++++---------- ctapipe/image/waveform_extractor.py | 16 +++++------ ctapipe/tools/bokeh/file_viewer.py | 2 +- ctapipe/tools/display_dl1.py | 2 +- ctapipe/tools/display_integrator.py | 2 +- ctapipe/tools/extract_charge_resolution.py | 2 +- ctapipe/utils/tests/test_tools.py | 2 +- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/ctapipe/calib/camera/calibrator.py b/ctapipe/calib/camera/calibrator.py index eab01a9873c..8762800ef1d 100644 --- a/ctapipe/calib/camera/calibrator.py +++ b/ctapipe/calib/camera/calibrator.py @@ -26,7 +26,7 @@ class CameraCalibrator(Component): """ def __init__(self, config=None, parent=None, r1_product=None, - extractor_name='NeighborWindowSum', + extractor_name='NeighborPeakWindowSum', eventsource=None, **kwargs): """ diff --git a/ctapipe/calib/camera/dl1.py b/ctapipe/calib/camera/dl1.py index ec5f5a2e62e..4c58bcbe06a 100644 --- a/ctapipe/calib/camera/dl1.py +++ b/ctapipe/calib/camera/dl1.py @@ -9,7 +9,7 @@ from ...core import Component from ...core.traits import Float -from ...image import NeighborWindowSum +from ...image import NeighborPeakWindowSum __all__ = ['CameraDL1Calibrator'] @@ -92,7 +92,7 @@ class CameraDL1Calibrator(Component): extractor : ctapipe.calib.camera.waveform_reducer.WaveformExtractor The reducer to use to extract the charge and pulse time from the waveforms. - By default the NeighborWindowSum with default configuration + By default the NeighborPeakWindowSum with default configuration is used. kwargs """ @@ -109,7 +109,7 @@ def __init__(self, config=None, parent=None, extractor=None, **kwargs): super().__init__(config=config, parent=parent, **kwargs) self.extractor = extractor if self.extractor is None: - self.extractor = NeighborWindowSum(config, parent) + self.extractor = NeighborPeakWindowSum(config, parent) self._dl0_empty_warn = False def check_dl0_exists(self, event, telid): diff --git a/ctapipe/calib/camera/tests/test_calibrator.py b/ctapipe/calib/camera/tests/test_calibrator.py index 67418434b1b..9913a6091e1 100644 --- a/ctapipe/calib/camera/tests/test_calibrator.py +++ b/ctapipe/calib/camera/tests/test_calibrator.py @@ -5,7 +5,7 @@ HESSIOR1Calibrator, NullR1Calibrator ) -from ctapipe.image.waveform_extractor import LocalWindowSum +from ctapipe.image.waveform_extractor import LocalPeakWindowSum from ctapipe.io import SimTelEventSource from ctapipe.utils import get_dataset_path @@ -26,8 +26,8 @@ def test_manual_r1(): def test_manual_extractor(): - calibrator = CameraCalibrator(extractor_name="LocalWindowSum") - assert isinstance(calibrator.dl1.extractor, LocalWindowSum) + calibrator = CameraCalibrator(extractor_name="LocalPeakWindowSum") + assert isinstance(calibrator.dl1.extractor, LocalPeakWindowSum) def test_eventsource_r1(): diff --git a/ctapipe/image/tests/test_waveform_extractor.py b/ctapipe/image/tests/test_waveform_extractor.py index 06e26027781..0bdd34db7df 100644 --- a/ctapipe/image/tests/test_waveform_extractor.py +++ b/ctapipe/image/tests/test_waveform_extractor.py @@ -10,10 +10,10 @@ WaveformExtractor, FullWaveformSum, UserWindowSum, - GlobalWindowSum, - LocalWindowSum, - NeighborWindowSum, - BaselineSubtractedNeighborWindowSum, + GlobalPeakWindowSum, + LocalPeakWindowSum, + NeighborPeakWindowSum, + BaselineSubtractedNeighborPeakWindowSum, ) @@ -99,9 +99,9 @@ def test_user_window_sum(camera_waveforms): assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) -def test_global_window_sum(camera_waveforms): +def test_global_peak_window_sum(camera_waveforms): waveforms, _ = camera_waveforms - extractor = GlobalWindowSum() + extractor = GlobalPeakWindowSum() charge, pulse_time = extractor(waveforms) assert_allclose(charge[0][0], 232.559, rtol=1e-3) @@ -110,9 +110,9 @@ def test_global_window_sum(camera_waveforms): assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) -def test_local_window_sum(camera_waveforms): +def test_local_peak_window_sum(camera_waveforms): waveforms, _ = camera_waveforms - extractor = LocalWindowSum() + extractor = LocalPeakWindowSum() charge, pulse_time = extractor(waveforms) assert_allclose(charge[0][0], 240.3, rtol=1e-3) @@ -121,10 +121,10 @@ def test_local_window_sum(camera_waveforms): assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) -def test_neighbor_window_sum(camera_waveforms): +def test_neighbor_peak_window_sum(camera_waveforms): waveforms, camera = camera_waveforms nei = camera.neighbor_matrix_where - extractor = NeighborWindowSum() + extractor = NeighborPeakWindowSum() extractor.neighbors = nei charge, pulse_time = extractor(waveforms) @@ -134,10 +134,10 @@ def test_neighbor_window_sum(camera_waveforms): assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) -def test_baseline_subtracted_neighbor_window_sum(camera_waveforms): +def test_baseline_subtracted_neighbor_peak_window_sum(camera_waveforms): waveforms, camera = camera_waveforms nei = camera.neighbor_matrix_where - extractor = BaselineSubtractedNeighborWindowSum() + extractor = BaselineSubtractedNeighborPeakWindowSum() extractor.neighbors = nei charge, pulse_time = extractor(waveforms) @@ -149,7 +149,7 @@ def test_baseline_subtracted_neighbor_window_sum(camera_waveforms): def test_waveform_extractor_factory(camera_waveforms): waveforms, _ = camera_waveforms - extractor = WaveformExtractor.from_name('LocalWindowSum') + extractor = WaveformExtractor.from_name('LocalPeakWindowSum') extractor(waveforms) @@ -168,7 +168,7 @@ def test_waveform_extractor_factory_args(): ) extractor = WaveformExtractor.from_name( - 'LocalWindowSum', + 'LocalPeakWindowSum', config=config, ) assert extractor.window_width == 20 diff --git a/ctapipe/image/waveform_extractor.py b/ctapipe/image/waveform_extractor.py index f1f6ddb2d81..546c6971e40 100644 --- a/ctapipe/image/waveform_extractor.py +++ b/ctapipe/image/waveform_extractor.py @@ -6,10 +6,10 @@ 'WaveformExtractor', 'FullWaveformSum', 'UserWindowSum', - 'GlobalWindowSum', - 'LocalWindowSum', - 'NeighborWindowSum', - 'BaselineSubtractedNeighborWindowSum', + 'GlobalPeakWindowSum', + 'LocalPeakWindowSum', + 'NeighborPeakWindowSum', + 'BaselineSubtractedNeighborPeakWindowSum', 'extract_charge_from_peakpos_array', 'extract_pulse_time_weighted_average', 'subtract_baseline', @@ -225,7 +225,7 @@ def __call__(self, waveforms): return charge, pulse_time -class GlobalWindowSum(WaveformExtractor): +class GlobalPeakWindowSum(WaveformExtractor): """ Waveform extractor that defines an integration window about the global peak in the image. @@ -254,7 +254,7 @@ def __call__(self, waveforms): return charge, pulse_time -class LocalWindowSum(WaveformExtractor): +class LocalPeakWindowSum(WaveformExtractor): """ Waveform extractor that defines an integration window about the local peak in each pixel. @@ -276,7 +276,7 @@ def __call__(self, waveforms): return charge, pulse_time -class NeighborWindowSum(WaveformExtractor): +class NeighborPeakWindowSum(WaveformExtractor): """ Waveform extractor that defines an integration window defined by the peaks in the neighboring pixels. @@ -310,7 +310,7 @@ def __call__(self, waveforms): return charge, pulse_time -class BaselineSubtractedNeighborWindowSum(NeighborWindowSum): +class BaselineSubtractedNeighborPeakWindowSum(NeighborPeakWindowSum): """ Waveform extractor that first subtracts the baseline before integrating in a window defined by the peaks in neighboring pixels. diff --git a/ctapipe/tools/bokeh/file_viewer.py b/ctapipe/tools/bokeh/file_viewer.py index de97a12e405..11f8d0a9e3a 100644 --- a/ctapipe/tools/bokeh/file_viewer.py +++ b/ctapipe/tools/bokeh/file_viewer.py @@ -31,7 +31,7 @@ class BokehFileViewer(Tool): extractor_product = tool_utils.enum_trait( WaveformExtractor, - default='NeighborWindowSum' + default='NeighborPeakWindowSum' ) aliases = Dict(dict( diff --git a/ctapipe/tools/display_dl1.py b/ctapipe/tools/display_dl1.py index fde7688664b..d6d20130e9d 100644 --- a/ctapipe/tools/display_dl1.py +++ b/ctapipe/tools/display_dl1.py @@ -143,7 +143,7 @@ class DisplayDL1Calib(Tool): extractor_product = tool_utils.enum_trait( WaveformExtractor, - default='NeighborWindowSum' + default='NeighborPeakWindowSum' ) aliases = Dict( diff --git a/ctapipe/tools/display_integrator.py b/ctapipe/tools/display_integrator.py index 5b01225be73..3261f15bc99 100644 --- a/ctapipe/tools/display_integrator.py +++ b/ctapipe/tools/display_integrator.py @@ -228,7 +228,7 @@ class DisplayIntegrator(Tool): extractor_product = tool_utils.enum_trait( WaveformExtractor, - default='NeighborWindowSum' + default='NeighborPeakWindowSum' ) aliases = Dict( diff --git a/ctapipe/tools/extract_charge_resolution.py b/ctapipe/tools/extract_charge_resolution.py index f546c272d18..51eb540dbd2 100644 --- a/ctapipe/tools/extract_charge_resolution.py +++ b/ctapipe/tools/extract_charge_resolution.py @@ -37,7 +37,7 @@ class ChargeResolutionGenerator(Tool): ).tag(config=True) extractor_product = tool_utils.enum_trait( WaveformExtractor, - default='NeighborWindowSum' + default='NeighborPeakWindowSum' ) aliases = Dict(dict( diff --git a/ctapipe/utils/tests/test_tools.py b/ctapipe/utils/tests/test_tools.py index d545b3b7826..070eea2f59c 100644 --- a/ctapipe/utils/tests/test_tools.py +++ b/ctapipe/utils/tests/test_tools.py @@ -18,7 +18,7 @@ def test_enum_trait(): # function under test from ctapipe.utils.tools import enum_trait - trait = enum_trait(WaveformExtractor, default='NeighborWindowSum') + trait = enum_trait(WaveformExtractor, default='NeighborPeakWindowSum') assert isinstance(trait, traitlets.traitlets.CaselessStrEnum) From cabe80efcb671a51181dbe60f3523ed134ca1356 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 29 Mar 2019 13:22:10 +0100 Subject: [PATCH 16/25] Deleted empty file --- ctapipe/image/tests/test_charge_extraction.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ctapipe/image/tests/test_charge_extraction.py diff --git a/ctapipe/image/tests/test_charge_extraction.py b/ctapipe/image/tests/test_charge_extraction.py deleted file mode 100644 index e69de29bb2d..00000000000 From 79b927603b0488d88af632bb8da1f33a8fb16399 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 29 Mar 2019 13:41:41 +0100 Subject: [PATCH 17/25] Rename WaveformExtractor to ImageExtractor - Rename waveform_extractor.py to extractor.py --- ctapipe/calib/camera/calibrator.py | 6 ++-- ctapipe/calib/camera/dl1.py | 4 +-- ctapipe/calib/camera/tests/test_calibrator.py | 2 +- ctapipe/image/__init__.py | 2 +- .../{waveform_extractor.py => extractor.py} | 36 +++++++++---------- ctapipe/image/reducers.py | 2 +- ...aveform_extractor.py => test_extractor.py} | 12 +++---- ctapipe/io/containers.py | 2 +- ctapipe/tools/bokeh/file_viewer.py | 14 ++++---- ctapipe/tools/display_dl1.py | 6 ++-- ctapipe/tools/display_integrator.py | 8 ++--- ctapipe/tools/extract_charge_resolution.py | 8 ++--- ctapipe/utils/tests/test_tools.py | 8 ++--- 13 files changed, 55 insertions(+), 55 deletions(-) rename ctapipe/image/{waveform_extractor.py => extractor.py} (91%) rename ctapipe/image/tests/{test_waveform_extractor.py => test_extractor.py} (96%) diff --git a/ctapipe/calib/camera/calibrator.py b/ctapipe/calib/camera/calibrator.py index 8762800ef1d..492e831f667 100644 --- a/ctapipe/calib/camera/calibrator.py +++ b/ctapipe/calib/camera/calibrator.py @@ -10,7 +10,7 @@ CameraDL0Reducer, CameraDL1Calibrator, ) -from ctapipe.image import WaveformExtractor +from ctapipe.image import ImageExtractor __all__ = ['CameraCalibrator'] @@ -43,7 +43,7 @@ def __init__(self, config=None, parent=None, r1_product : str The R1 calibrator to use. Manually overrides the Factory. extractor_name : str - The name of the WaveformExtractor to use. + The name of the ImageExtractor to use. eventsource : ctapipe.io.eventsource.EventSource EventSource that is being used to read the events. The EventSource contains information (such as metadata or inst) which indicates @@ -52,7 +52,7 @@ def __init__(self, config=None, parent=None, """ super().__init__(config=config, parent=parent, **kwargs) - extractor = WaveformExtractor.from_name( + extractor = ImageExtractor.from_name( extractor_name, parent=self, ) diff --git a/ctapipe/calib/camera/dl1.py b/ctapipe/calib/camera/dl1.py index 4c58bcbe06a..2bdba6a6311 100644 --- a/ctapipe/calib/camera/dl1.py +++ b/ctapipe/calib/camera/dl1.py @@ -3,7 +3,7 @@ This module handles the calibration from the DL0 data level to DL1. This transition involves the extraction of parameters from the waveform using a -`ctapipe.image.waveform_extractor.WaveformExtractor`. +`ctapipe.image.extractor.ImageExtractor`. """ import numpy as np @@ -89,7 +89,7 @@ class CameraDL1Calibrator(Component): Tool executable that is calling this component. Passes the correct logger to the component. Set to None if no Tool to pass. - extractor : ctapipe.calib.camera.waveform_reducer.WaveformExtractor + extractor : ctapipe.calib.camera.waveform_reducer.ImageExtractor The reducer to use to extract the charge and pulse time from the waveforms. By default the NeighborPeakWindowSum with default configuration diff --git a/ctapipe/calib/camera/tests/test_calibrator.py b/ctapipe/calib/camera/tests/test_calibrator.py index 9913a6091e1..aa3f4d30d5c 100644 --- a/ctapipe/calib/camera/tests/test_calibrator.py +++ b/ctapipe/calib/camera/tests/test_calibrator.py @@ -5,7 +5,7 @@ HESSIOR1Calibrator, NullR1Calibrator ) -from ctapipe.image.waveform_extractor import LocalPeakWindowSum +from ctapipe.image.extractor import LocalPeakWindowSum from ctapipe.io import SimTelEventSource from ctapipe.utils import get_dataset_path diff --git a/ctapipe/image/__init__.py b/ctapipe/image/__init__.py index 7e43d00e060..5eba28989b6 100644 --- a/ctapipe/image/__init__.py +++ b/ctapipe/image/__init__.py @@ -1,7 +1,7 @@ from .hillas import * from .cleaning import * from .pixel_likelihood import * -from .waveform_extractor import * +from .extractor import * from .reducers import * from .muon import * from .geometry_converter import * diff --git a/ctapipe/image/waveform_extractor.py b/ctapipe/image/extractor.py similarity index 91% rename from ctapipe/image/waveform_extractor.py rename to ctapipe/image/extractor.py index 6eba8582b8e..97836877e7e 100644 --- a/ctapipe/image/waveform_extractor.py +++ b/ctapipe/image/extractor.py @@ -3,7 +3,7 @@ """ __all__ = [ - 'WaveformExtractor', + 'ImageExtractor', 'FullWaveformSum', 'UserWindowSum', 'GlobalPeakWindowSum', @@ -158,7 +158,7 @@ def subtract_baseline(waveforms, baseline_start, baseline_end): return baseline_corrected -class WaveformExtractor(Component): +class ImageExtractor(Component): def __init__(self, config=None, parent=None, **kwargs): """ @@ -193,7 +193,7 @@ def __init__(self, config=None, parent=None, **kwargs): @staticmethod def requires_neighbors(): """ - Method used for callers of the WaveformExtractor to know if the + Method used for callers of the ImageExtractor to know if the extractor requires knowledge of the pixel neighbors Returns @@ -243,9 +243,9 @@ def __call__(self, waveforms): """ -class FullWaveformSum(WaveformExtractor): +class FullWaveformSum(ImageExtractor): """ - Waveform extractor that integrates the entire waveform. + Extractor that sums the entire waveform. """ def __call__(self, waveforms): @@ -254,9 +254,9 @@ def __call__(self, waveforms): return charge, pulse_time -class UserWindowSum(WaveformExtractor): +class UserWindowSum(ImageExtractor): """ - Waveform extractor that integrates within a window defined by the user. + Extractor that sums within a window defined by the user. """ window_start = Int( 0, help='Define the start position for the integration window' @@ -273,10 +273,10 @@ def __call__(self, waveforms): return charge, pulse_time -class GlobalPeakWindowSum(WaveformExtractor): +class GlobalPeakWindowSum(ImageExtractor): """ - Waveform extractor that defines an integration window about the global - peak in the image. + Extractor which sums in a window about the + peak from the global average waveform. """ window_width = Int( 7, help='Define the width of the integration window' @@ -298,10 +298,10 @@ def __call__(self, waveforms): return charge, pulse_time -class LocalPeakWindowSum(WaveformExtractor): +class LocalPeakWindowSum(ImageExtractor): """ - Waveform extractor that defines an integration window about the local - peak in each pixel. + Extractor which sums in a window about the + peak in each pixel's waveform. """ window_width = Int( 7, help='Define the width of the integration window' @@ -320,10 +320,10 @@ def __call__(self, waveforms): return charge, pulse_time -class NeighborPeakWindowSum(WaveformExtractor): +class NeighborPeakWindowSum(ImageExtractor): """ - Waveform extractor that defines an integration window defined by the - peaks in the neighboring pixels. + Extractor which sums in a window about the + peak defined by the wavefroms in neighboring pixels. """ window_width = Int( 7, help='Define the width of the integration window' @@ -354,8 +354,8 @@ def __call__(self, waveforms): class BaselineSubtractedNeighborPeakWindowSum(NeighborPeakWindowSum): """ - Waveform extractor that first subtracts the baseline before integrating in - a window defined by the peaks in neighboring pixels. + Extractor that first subtracts the baseline before summing in a + window about the peak defined by the wavefroms in neighboring pixels. """ baseline_start = Int( 0, help='Start sample for baseline estimation' diff --git a/ctapipe/image/reducers.py b/ctapipe/image/reducers.py index 2e642780cb7..76977a8492f 100644 --- a/ctapipe/image/reducers.py +++ b/ctapipe/image/reducers.py @@ -55,7 +55,7 @@ def __init__(self, config=None, parent=None, **kwargs): @staticmethod def requires_neighbours(): """ - Method used for callers of the WaveformExtractor to know if the + Method used for callers of the ImageExtractor to know if the extractor requires knowledge of the pixel neighbours Returns diff --git a/ctapipe/image/tests/test_waveform_extractor.py b/ctapipe/image/tests/test_extractor.py similarity index 96% rename from ctapipe/image/tests/test_waveform_extractor.py rename to ctapipe/image/tests/test_extractor.py index 35ec3106752..702be94c0a8 100644 --- a/ctapipe/image/tests/test_waveform_extractor.py +++ b/ctapipe/image/tests/test_extractor.py @@ -3,12 +3,12 @@ from scipy.stats import norm from numpy.testing import assert_allclose from ctapipe.instrument import CameraGeometry -from ctapipe.image.waveform_extractor import ( +from ctapipe.image.extractor import ( extract_charge_from_peakpos_array, neighbor_average_waveform, extract_pulse_time_weighted_average, subtract_baseline, - WaveformExtractor, + ImageExtractor, FullWaveformSum, UserWindowSum, GlobalPeakWindowSum, @@ -172,7 +172,7 @@ def test_baseline_subtracted_neighbor_peak_window_sum(camera_waveforms): def test_waveform_extractor_factory(camera_waveforms): waveforms, _ = camera_waveforms - extractor = WaveformExtractor.from_name('LocalPeakWindowSum') + extractor = ImageExtractor.from_name('LocalPeakWindowSum') extractor(waveforms) @@ -183,14 +183,14 @@ def test_waveform_extractor_factory_args(): from traitlets.config.loader import Config config = Config( { - 'WaveformExtractor': { + 'ImageExtractor': { 'window_width': 20, 'window_shift': 3, } } ) - extractor = WaveformExtractor.from_name( + extractor = ImageExtractor.from_name( 'LocalPeakWindowSum', config=config, ) @@ -198,7 +198,7 @@ def test_waveform_extractor_factory_args(): assert extractor.window_shift == 3 with pytest.warns(UserWarning): - WaveformExtractor.from_name( + ImageExtractor.from_name( 'FullWaveformSum', config=config, ) diff --git a/ctapipe/io/containers.py b/ctapipe/io/containers.py index 28333ceefd0..c4d664b4873 100644 --- a/ctapipe/io/containers.py +++ b/ctapipe/io/containers.py @@ -92,7 +92,7 @@ class DL1CameraContainer(Container): pulse_time = Field( None, "Numpy array containing position of the pulse as determined by " - "the waveform extractor." + "the extractor." "Shape: (n_chan, n_pixel, n_samples)" ) #TODO: Remove when gain selection added? diff --git a/ctapipe/tools/bokeh/file_viewer.py b/ctapipe/tools/bokeh/file_viewer.py index 11f8d0a9e3a..20819b8a084 100644 --- a/ctapipe/tools/bokeh/file_viewer.py +++ b/ctapipe/tools/bokeh/file_viewer.py @@ -9,7 +9,7 @@ from ctapipe.calib.camera.dl1 import CameraDL1Calibrator from ctapipe.calib.camera.r1 import CameraR1Calibrator from ctapipe.core import Tool -from ctapipe.image.waveform_extractor import WaveformExtractor +from ctapipe.image.extractor import ImageExtractor from ctapipe.io import EventSource from ctapipe.io.eventseeker import EventSeeker from ctapipe.plotting.bokeh_event_viewer import BokehEventViewer @@ -30,7 +30,7 @@ class BokehFileViewer(Tool): EventSource.input_url.default_value = default_url extractor_product = tool_utils.enum_trait( - WaveformExtractor, + ImageExtractor, default='NeighborPeakWindowSum' ) @@ -46,7 +46,7 @@ class BokehFileViewer(Tool): [ EventSource, CameraDL1Calibrator, - ] + tool_utils.classes_with_traits(WaveformExtractor) + ] + tool_utils.classes_with_traits(ImageExtractor) + tool_utils.classes_with_traits(CameraR1Calibrator) ) @@ -86,7 +86,7 @@ def setup(self): self.reader = EventSource.from_config(parent=self) self.seeker = EventSeeker(self.reader, parent=self) - self.extractor = WaveformExtractor.from_name( + self.extractor = ImageExtractor.from_name( self.extractor_product, parent=self ) @@ -234,7 +234,7 @@ def update_dl1_calibrator(self, extractor=None): Parameters ---------- - extractor : ctapipe.image.waveform_extractors.WaveformExtractor + extractor : ctapipe.image.extractor.ImageExtractor """ if extractor is None: extractor = self.dl1.extractor @@ -373,11 +373,11 @@ def on_dl1_widget_change(self, _, __, ___): self._updating_dl1 = True cmdline = [] for key, val in self.w_dl1_dict.items(): - k = key.replace("extractor_", "WaveformExtractor.") + k = key.replace("extractor_", "ImageExtractor.") if val.value: cmdline.append(f'--{k}={val.value}') self.parse_command_line(cmdline) - extractor = WaveformExtractor.from_name( + extractor = ImageExtractor.from_name( self.extractor_product, parent=self) self.update_dl1_calibrator(extractor) diff --git a/ctapipe/tools/display_dl1.py b/ctapipe/tools/display_dl1.py index d6d20130e9d..99df19c4758 100644 --- a/ctapipe/tools/display_dl1.py +++ b/ctapipe/tools/display_dl1.py @@ -9,7 +9,7 @@ from ctapipe.visualization import CameraDisplay from ctapipe.core import Tool, Component from ctapipe.utils import get_dataset_path -from ctapipe.image.waveform_extractor import WaveformExtractor +from ctapipe.image.extractor import ImageExtractor from ctapipe.io import EventSource import ctapipe.utils.tools as tool_utils @@ -142,7 +142,7 @@ class DisplayDL1Calib(Tool): ).tag(config=True) extractor_product = tool_utils.enum_trait( - WaveformExtractor, + ImageExtractor, default='NeighborPeakWindowSum' ) @@ -172,7 +172,7 @@ class DisplayDL1Calib(Tool): EventSource, CameraDL1Calibrator, ImagePlotter - ] + tool_utils.classes_with_traits(WaveformExtractor) + ] + tool_utils.classes_with_traits(ImageExtractor) ) def __init__(self, **kwargs): diff --git a/ctapipe/tools/display_integrator.py b/ctapipe/tools/display_integrator.py index 3261f15bc99..42429364ce8 100644 --- a/ctapipe/tools/display_integrator.py +++ b/ctapipe/tools/display_integrator.py @@ -11,7 +11,7 @@ from ctapipe.calib.camera import CameraR1Calibrator, CameraDL0Reducer, \ CameraDL1Calibrator from ctapipe.core import Tool -from ctapipe.image.waveform_extractor import WaveformExtractor +from ctapipe.image.extractor import ImageExtractor from ctapipe.io.eventseeker import EventSeeker from ctapipe.io import EventSource from ctapipe.visualization import CameraDisplay @@ -227,7 +227,7 @@ class DisplayIntegrator(Tool): channel = Enum([0, 1], 0, help='Channel to view').tag(config=True) extractor_product = tool_utils.enum_trait( - WaveformExtractor, + ImageExtractor, default='NeighborPeakWindowSum' ) @@ -256,7 +256,7 @@ class DisplayIntegrator(Tool): [ EventSource, CameraDL1Calibrator, - ] + tool_utils.classes_with_traits(WaveformExtractor) + ] + tool_utils.classes_with_traits(ImageExtractor) ) def __init__(self, **kwargs): @@ -272,7 +272,7 @@ def setup(self): event_source = EventSource.from_config(parent=self) self.eventseeker = EventSeeker(event_source, parent=self) - self.extractor = WaveformExtractor.from_name( + self.extractor = ImageExtractor.from_name( self.extractor_product, parent=self, ) diff --git a/ctapipe/tools/extract_charge_resolution.py b/ctapipe/tools/extract_charge_resolution.py index 8f829ec7cea..1117558eae3 100644 --- a/ctapipe/tools/extract_charge_resolution.py +++ b/ctapipe/tools/extract_charge_resolution.py @@ -18,7 +18,7 @@ from ctapipe.calib.camera.dl1 import CameraDL1Calibrator from ctapipe.calib.camera.r1 import HESSIOR1Calibrator from ctapipe.core import Tool, Provenance -from ctapipe.image.waveform_extractor import WaveformExtractor +from ctapipe.image.extractor import ImageExtractor from ctapipe.io.simteleventsource import SimTelEventSource @@ -36,7 +36,7 @@ class ChargeResolutionGenerator(Tool): help='Path to store the output HDF5 file' ).tag(config=True) extractor_product = tool_utils.enum_trait( - WaveformExtractor, + ImageExtractor, default='NeighborPeakWindowSum' ) @@ -52,7 +52,7 @@ class ChargeResolutionGenerator(Tool): [ SimTelEventSource, CameraDL1Calibrator, - ] + tool_utils.classes_with_traits(WaveformExtractor) + ] + tool_utils.classes_with_traits(ImageExtractor) ) def __init__(self, **kwargs): @@ -68,7 +68,7 @@ def setup(self): self.eventsource = SimTelEventSource(parent=self) - extractor = WaveformExtractor.from_name( + extractor = ImageExtractor.from_name( self.extractor_product, parent=self ) diff --git a/ctapipe/utils/tests/test_tools.py b/ctapipe/utils/tests/test_tools.py index 070eea2f59c..da1be9d80df 100644 --- a/ctapipe/utils/tests/test_tools.py +++ b/ctapipe/utils/tests/test_tools.py @@ -3,7 +3,7 @@ from traitlets import HasTraits from traitlets import Int # using this class as test input -from ctapipe.image.waveform_extractor import WaveformExtractor +from ctapipe.image.extractor import ImageExtractor def test_enum_trait_default_is_right(): @@ -11,14 +11,14 @@ def test_enum_trait_default_is_right(): from ctapipe.utils.tools import enum_trait with pytest.raises(ValueError): - enum_trait(WaveformExtractor, default='name_of_default_choice') + enum_trait(ImageExtractor, default='name_of_default_choice') def test_enum_trait(): # function under test from ctapipe.utils.tools import enum_trait - trait = enum_trait(WaveformExtractor, default='NeighborPeakWindowSum') + trait = enum_trait(ImageExtractor, default='NeighborPeakWindowSum') assert isinstance(trait, traitlets.traitlets.CaselessStrEnum) @@ -26,7 +26,7 @@ def test_enum_classes_with_traits(): # function under test from ctapipe.utils.tools import classes_with_traits - list_of_classes = classes_with_traits(WaveformExtractor) + list_of_classes = classes_with_traits(ImageExtractor) assert list_of_classes # should not be empty From 000dbc7f08ed2866efd685d75f6a623245506b74 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 29 Mar 2019 13:43:49 +0100 Subject: [PATCH 18/25] Update doc --- ctapipe/image/extractor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctapipe/image/extractor.py b/ctapipe/image/extractor.py index 97836877e7e..4ccae191f10 100644 --- a/ctapipe/image/extractor.py +++ b/ctapipe/image/extractor.py @@ -163,7 +163,7 @@ class ImageExtractor(Component): def __init__(self, config=None, parent=None, **kwargs): """ Base component to handle the extraction of charge and pulse time - from an image cube. + from an image cube (waveforms). Attributes ---------- From 875307d33b4154aeb40c1dcf25692ef16419ee30 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 29 Mar 2019 13:58:55 +0100 Subject: [PATCH 19/25] Update docs --- docs/ctapipe_api/image/charge_extractors.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ctapipe_api/image/charge_extractors.rst b/docs/ctapipe_api/image/charge_extractors.rst index 5016b5f7525..71c70606ba0 100644 --- a/docs/ctapipe_api/image/charge_extractors.rst +++ b/docs/ctapipe_api/image/charge_extractors.rst @@ -4,8 +4,8 @@ Charge Extraction (cube to image) ================================== -extracts charge from the waveform, resulting in a single number per pixel +Extracts charge and time per pixel from the waveform. -.. automodapi:: ctapipe.image.charge_extractors +.. automodapi:: ctapipe.image.extractor From a27269b19d1a3078029e1101ecf2809571de7905 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 29 Mar 2019 14:21:59 +0100 Subject: [PATCH 20/25] Update docs --- docs/ctapipe_api/image/cleaning.rst | 7 ++----- .../image/{charge_extractors.rst => extractor.rst} | 0 2 files changed, 2 insertions(+), 5 deletions(-) rename docs/ctapipe_api/image/{charge_extractors.rst => extractor.rst} (100%) diff --git a/docs/ctapipe_api/image/cleaning.rst b/docs/ctapipe_api/image/cleaning.rst index 0b04279a238..445139968b4 100644 --- a/docs/ctapipe_api/image/cleaning.rst +++ b/docs/ctapipe_api/image/cleaning.rst @@ -1,11 +1,10 @@ .. _image_cleaning: =========================== -Image and Waveform Cleaning +Image Cleaning =========================== -Cleaning/denoising of images in time (applying filters, convolutions, -or baseline subtractions) and space (tailcuts cleaning, dilation, +Cleaning/denoising of images (tailcuts cleaning, dilation, filtering). An example of image cleaning and dilation: @@ -19,6 +18,4 @@ API Reference .. automodapi:: ctapipe.image.cleaning -.. automodapi:: ctapipe.image.waveform_cleaning - diff --git a/docs/ctapipe_api/image/charge_extractors.rst b/docs/ctapipe_api/image/extractor.rst similarity index 100% rename from docs/ctapipe_api/image/charge_extractors.rst rename to docs/ctapipe_api/image/extractor.rst From 71f47668b7ce3437f817a6ebd93494b2da9ed486 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 29 Mar 2019 14:53:23 +0100 Subject: [PATCH 21/25] Update docs --- docs/ctapipe_api/image/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ctapipe_api/image/index.rst b/docs/ctapipe_api/image/index.rst index c6ea35acc7f..24d1b27b36f 100644 --- a/docs/ctapipe_api/image/index.rst +++ b/docs/ctapipe_api/image/index.rst @@ -25,7 +25,7 @@ This module contains the following sub-modules, but the most important functions muon toymodel pixel_likelihood - charge_extractors + extractor reducers geometry_converter From 2861d0ffce60aa877b6785987995148b179db9b8 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 29 Mar 2019 16:06:56 +0100 Subject: [PATCH 22/25] Update notebooks --- docs/tutorials/calibrated_data_exploration.ipynb | 2 +- docs/tutorials/ctapipe_handson.ipynb | 4 ++-- docs/tutorials/lst_analysis_bootcamp_2018.ipynb | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/tutorials/calibrated_data_exploration.ipynb b/docs/tutorials/calibrated_data_exploration.ipynb index 1e2076240f8..1d51f520895 100644 --- a/docs/tutorials/calibrated_data_exploration.ipynb +++ b/docs/tutorials/calibrated_data_exploration.ipynb @@ -125,7 +125,7 @@ "\n", "* **r1.tel[x]**: contains the \"r1-calibrated\" waveforms, after gain-selection, pedestal subtraciton, and gain-correction\n", "* **dl0.tel[x]**: is the same but with optional data volume reduction (some pixels not filled), in this case this is not performed by default, so it is the same as r1\n", - "* **dl1.tel[x]**: contains the (possibly re-calibrated) waveforms as dl0, but also the time-integrated *image* that has been calculated using a `ChargeExtractor` (a `NeighborPeakIntegrator` by default)" + "* **dl1.tel[x]**: contains the (possibly re-calibrated) waveforms as dl0, but also the time-integrated *image* that has been calculated using a `ImageExtractor` (a `NeighborPeakWindowSum` by default)" ] }, { diff --git a/docs/tutorials/ctapipe_handson.ipynb b/docs/tutorials/ctapipe_handson.ipynb index d860d9b6299..a162ec42a1c 100644 --- a/docs/tutorials/ctapipe_handson.ipynb +++ b/docs/tutorials/ctapipe_handson.ipynb @@ -390,7 +390,7 @@ "metadata": {}, "outputs": [], "source": [ - "dl1tel.peakpos" + "dl1tel.pulse_time" ] }, { @@ -408,7 +408,7 @@ "metadata": {}, "outputs": [], "source": [ - "CameraDisplay(tel.camera, image=dl1tel.peakpos[0])" + "CameraDisplay(tel.camera, image=dl1tel.pulse_time[0])" ] }, { diff --git a/docs/tutorials/lst_analysis_bootcamp_2018.ipynb b/docs/tutorials/lst_analysis_bootcamp_2018.ipynb index ec9fd7d8483..0165fd6ef85 100644 --- a/docs/tutorials/lst_analysis_bootcamp_2018.ipynb +++ b/docs/tutorials/lst_analysis_bootcamp_2018.ipynb @@ -440,8 +440,8 @@ "d1.image = dl1.image[0]\n", "d1.add_colorbar(ax=ax1)\n", "\n", - "ax2.set_title('PeakPos')\n", - "d2.image = dl1.peakpos[0] - np.average(dl1.peakpos[0], weights=dl1.image[0])\n", + "ax2.set_title('Pulse Time')\n", + "d2.image = dl1.pulse_time[0] - np.average(dl1.pulse_time[0], weights=dl1.image[0])\n", "d2.cmap = 'RdBu_r'\n", "d2.add_colorbar(ax=ax2)\n", "\n", @@ -505,7 +505,7 @@ "timing = timing_parameters(\n", " camera[clean],\n", " dl1.image[0][clean],\n", - " dl1.peakpos[0][clean],\n", + " dl1.pulse_time[0][clean],\n", " hillas,\n", ")\n", "\n", @@ -522,7 +522,7 @@ " camera.pix_x, camera.pix_y,hillas.x, hillas.y, hillas.psi\n", ")\n", "\n", - "plt.plot(long[clean], dl1.peakpos[0][clean], 'o')\n", + "plt.plot(long[clean], dl1.pulse_time[0][clean], 'o')\n", "plt.plot(long[clean], timing.slope * long[clean] + timing.intercept)" ] }, @@ -633,7 +633,7 @@ " for telescope_id, dl1 in event.dl1.tel.items():\n", " camera = event.inst.subarray.tels[telescope_id].camera\n", " image = dl1.image[0]\n", - " peakpos = dl1.peakpos[0]\n", + " pulse_time = dl1.pulse_time[0]\n", "\n", " boundary, picture, min_neighbors = cleaning_level[camera.cam_id]\n", "\n", @@ -657,7 +657,7 @@ " if leakage_c.leakage2_intensity > 0.2:\n", " continue\n", "\n", - " timing_c = timing_parameters(camera[clean], image[clean], peakpos[clean], hillas_c)\n", + " timing_c = timing_parameters(camera[clean], image[clean], pulse_time[clean], hillas_c)\n", "\n", " hillas_containers[telescope_id] = hillas_c\n", "\n", @@ -807,7 +807,7 @@ "\n", " camera = event.inst.subarray.tels[telescope_id].camera\n", " image = dl1.image[0]\n", - " peakpos = dl1.peakpos[0]\n", + " pulse_time = dl1.pulse_time[0]\n", "\n", " boundary, picture, min_neighbors = cleaning_level[camera.cam_id]\n", "\n", @@ -825,7 +825,7 @@ " event_info = EventInfo(event_id=event.r0.event_id, obs_id=event.r0.obs_id, telescope_id=telescope_id)\n", " hillas_c = hillas_parameters(camera[clean], image[clean])\n", " leakage_c = leakage(camera, image, clean)\n", - " timing_c = timing_parameters(camera[clean], image[clean], peakpos[clean], hillas_c)\n", + " timing_c = timing_parameters(camera[clean], image[clean], pulse_time[clean], hillas_c)\n", "\n", " writer.write('events', [event_info, event.mc, hillas_c, leakage_c, timing_c])\n", " \n" From e510fd967fcdcd27c379ab548651591d5dd60246 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Thu, 4 Apr 2019 17:55:36 +0200 Subject: [PATCH 23/25] Rename UserWindowSum to FixedWindowSum --- ctapipe/image/extractor.py | 6 +++--- ctapipe/image/tests/test_extractor.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ctapipe/image/extractor.py b/ctapipe/image/extractor.py index 4ccae191f10..f7563de5c79 100644 --- a/ctapipe/image/extractor.py +++ b/ctapipe/image/extractor.py @@ -5,7 +5,7 @@ __all__ = [ 'ImageExtractor', 'FullWaveformSum', - 'UserWindowSum', + 'FixedWindowSum', 'GlobalPeakWindowSum', 'LocalPeakWindowSum', 'NeighborPeakWindowSum', @@ -254,9 +254,9 @@ def __call__(self, waveforms): return charge, pulse_time -class UserWindowSum(ImageExtractor): +class FixedWindowSum(ImageExtractor): """ - Extractor that sums within a window defined by the user. + Extractor that sums within a fixed window defined by the user. """ window_start = Int( 0, help='Define the start position for the integration window' diff --git a/ctapipe/image/tests/test_extractor.py b/ctapipe/image/tests/test_extractor.py index 702be94c0a8..5a30575ddee 100644 --- a/ctapipe/image/tests/test_extractor.py +++ b/ctapipe/image/tests/test_extractor.py @@ -10,7 +10,7 @@ subtract_baseline, ImageExtractor, FullWaveformSum, - UserWindowSum, + FixedWindowSum, GlobalPeakWindowSum, LocalPeakWindowSum, NeighborPeakWindowSum, @@ -103,9 +103,9 @@ def test_full_waveform_sum(camera_waveforms): assert_allclose(pulse_time[1][0], 62.359948, rtol=1e-3) -def test_user_window_sum(camera_waveforms): +def test_fixed_window_sum(camera_waveforms): waveforms, _ = camera_waveforms - extractor = UserWindowSum(window_start=45) + extractor = FixedWindowSum(window_start=45) charge, pulse_time = extractor(waveforms) assert_allclose(charge[0][0], 232.559, rtol=1e-3) From ca6c2a3c575d7f2fda3cb47f87e93ccef2ce88e4 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Thu, 4 Apr 2019 17:56:05 +0200 Subject: [PATCH 24/25] Set to nan if out of range --- ctapipe/image/extractor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctapipe/image/extractor.py b/ctapipe/image/extractor.py index f7563de5c79..60d12484c25 100644 --- a/ctapipe/image/extractor.py +++ b/ctapipe/image/extractor.py @@ -126,8 +126,8 @@ def extract_pulse_time_weighted_average(waveforms): """ samples_i = np.indices(waveforms.shape)[2] pulse_time = np.average(samples_i, weights=waveforms, axis=2) - pulse_time[pulse_time < 0] = 0 - pulse_time[pulse_time > waveforms.shape[2]] = waveforms.shape[2] + outside = np.logical_or(pulse_time < 0, pulse_time >= waveforms.shape[2]) + pulse_time[outside] = np.nan return pulse_time From 06783461913d2c96733d7a4ead1de398be4cb055 Mon Sep 17 00:00:00 2001 From: watsonjj Date: Fri, 5 Apr 2019 10:05:23 +0200 Subject: [PATCH 25/25] Set out-of-bounds pulse time to -1 --- ctapipe/image/extractor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctapipe/image/extractor.py b/ctapipe/image/extractor.py index 60d12484c25..bd3596f8fdb 100644 --- a/ctapipe/image/extractor.py +++ b/ctapipe/image/extractor.py @@ -127,7 +127,7 @@ def extract_pulse_time_weighted_average(waveforms): samples_i = np.indices(waveforms.shape)[2] pulse_time = np.average(samples_i, weights=waveforms, axis=2) outside = np.logical_or(pulse_time < 0, pulse_time >= waveforms.shape[2]) - pulse_time[outside] = np.nan + pulse_time[outside] = -1 return pulse_time