def main(): std_config = get_standard_config() if args.config_file is not None: config = replace_config(std_config, read_configuration_file(args.config_file)) else: config = std_config print(config['tailcut']) geom = CameraGeometry.from_name('LSTCam-002') foclen = OpticsDescription.from_name('LST').equivalent_focal_length dl1_container = DL1ParametersContainer() parameters_to_update = list(HillasParametersContainer().keys()) parameters_to_update.extend(['wl', 'r', 'leakage', 'n_islands', 'intercept', 'time_gradient']) nodes_keys = get_dataset_keys(args.input_file) if args.noimage: nodes_keys.remove(dl1_images_lstcam_key) auto_merge_h5files([args.input_file], args.output_file, nodes_keys=nodes_keys) with tables.open_file(args.input_file, mode='r') as input: image_table = input.root[dl1_images_lstcam_key] with tables.open_file(args.output_file, mode='a') as output: params = output.root[dl1_params_lstcam_key].read() for ii, row in enumerate(image_table): if ii%10000 == 0: print(ii) image = row['image'] pulse_time = row['pulse_time'] signal_pixels = tailcuts_clean(geom, image, **config['tailcut']) if image[signal_pixels].shape[0] > 0: num_islands, island_labels = number_of_islands(geom, signal_pixels) hillas = hillas_parameters(geom[signal_pixels], image[signal_pixels]) dl1_container.fill_hillas(hillas) dl1_container.set_timing_features(geom[signal_pixels], image[signal_pixels], pulse_time[signal_pixels], hillas) dl1_container.set_leakage(geom, image, signal_pixels) dl1_container.n_islands = num_islands dl1_container.wl = dl1_container.width / dl1_container.length width = np.rad2deg(np.arctan2(dl1_container.width, foclen)) length = np.rad2deg(np.arctan2(dl1_container.length, foclen)) dl1_container.width = width.value dl1_container.length = length.value dl1_container.r = np.sqrt(dl1_container.x**2 + dl1_container.y**2) for p in parameters_to_update: params[ii][p] = Quantity(dl1_container[p]).value else: for p in parameters_to_update: params[ii][p] = 0 output.root[dl1_params_lstcam_key][:] = params
def test_largest_island(): """Test selection of largest island in imagea with given cleaning masks.""" # Create a simple rectangular camera made of 17 pixels camera = CameraGeometry.make_rectangular(17, 1) # Assume a simple image (flattened array) made of 0, 1 or 2 photoelectrons # [2, 1, 1, 1, 1, 2, 2, 1, 0, 2, 1, 1, 1, 0, 2, 2, 2] # Assume a virtual tailcut cleaning that requires: # - picture_threshold = 2 photoelectrons, # - boundary_threshold = 1 photoelectron, # - min_number_picture_neighbors = 0 # There will be 4 islands left after cleaning: clean_mask = np.zeros(camera.n_pixels).astype("bool") # initialization clean_mask[[0, 1]] = 1 clean_mask[[4, 5, 6, 7]] = 2 # this is the biggest clean_mask[[9, 10]] = 3 clean_mask[[14, 15, 16]] = 4 # Label islands (their number is not important here) _, islands_labels = cleaning.number_of_islands(camera, clean_mask) # Create the true mask which takes into account only the biggest island # Pixels with no signal are labelled with a 0 true_mask_largest = np.zeros(camera.n_pixels).astype("bool") true_mask_largest[[4, 5, 6, 7]] = 1 # Apply the function to test mask_largest = cleaning.largest_island(islands_labels) # Now the degenerate case of only one island surviving # Same process as before clean_mask_one = np.zeros(camera.n_pixels).astype("bool") clean_mask_one[[0, 1]] = 1 _, islands_labels_one = cleaning.number_of_islands(camera, clean_mask_one) true_mask_largest_one = np.zeros(camera.n_pixels).astype("bool") true_mask_largest_one[[0, 1]] = 1 mask_largest_one = cleaning.largest_island(islands_labels_one) # Last the case of no islands surviving clean_mask_0 = np.zeros(camera.n_pixels).astype("bool") _, islands_labels_0 = cleaning.number_of_islands(camera, clean_mask_0) true_mask_largest_0 = np.zeros(camera.n_pixels).astype("bool") mask_largest_0 = cleaning.largest_island(islands_labels_0) # Check if the function recovers the ground truth in all of the three cases assert (mask_largest_one == true_mask_largest_one).all() assert (mask_largest_0 == true_mask_largest_0).all() assert_allclose(mask_largest, true_mask_largest)
def test_number_of_islands(): # test with LST geometry (1855 pixels) geom = CameraGeometry.from_name("LSTCam") # create 18 triggered pixels grouped to 5 clusters island_mask_true = np.zeros(geom.n_pixels) mask = np.zeros(geom.n_pixels).astype('bool') triggered_pixels = np.array( [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 37, 38, 111, 222]) island_mask_true[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]] = 1 island_mask_true[14] = 2 island_mask_true[[37, 38]] = 3 island_mask_true[111] = 4 island_mask_true[222] = 5 mask[triggered_pixels] = 1 n_islands, island_mask = cleaning.number_of_islands(geom, mask) n_islands_true = 5 assert n_islands == n_islands_true assert_allclose(island_mask, island_mask_true)
def test_number_of_islands(): # test with LST geometry (1855 pixels) geom = CameraGeometry.from_name("LSTCam") # create 18 triggered pixels grouped to 5 clusters island_mask_true = np.zeros(geom.n_pixels) mask = np.zeros(geom.n_pixels).astype('bool') triggered_pixels = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 37, 38, 111, 222]) island_mask_true[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]] = 1 island_mask_true[14] = 2 island_mask_true[[37, 38]] = 3 island_mask_true[111] = 4 island_mask_true[222] = 5 mask[triggered_pixels] = 1 n_islands, island_mask = cleaning.number_of_islands(geom, mask) n_islands_true = 5 assert n_islands == n_islands_true assert_allclose(island_mask, island_mask_true)
def set_n_islands(self, geom, clean): n_islands, islands_mask = number_of_islands(geom, clean) self.n_islands = n_islands
def get_dl1( calibrated_event, telescope_id, dl1_container=None, custom_config={}, use_main_island=True, ): """ Return a DL1ParametersContainer of extracted features from a calibrated event. The DL1ParametersContainer can be passed to be filled if created outside the function (faster for multiple event processing) Parameters ---------- calibrated_event: ctapipe event container telescope_id: `int` dl1_container: DL1ParametersContainer custom_config: path to a configuration file configuration used for tailcut cleaning superseeds the standard configuration use_main_island: `bool` Use only the main island to calculate DL1 parameters Returns ------- DL1ParametersContainer """ config = replace_config(standard_config, custom_config) cleaning_parameters = config["tailcut"] dl1_container = DL1ParametersContainer() if dl1_container is None else dl1_container tel = calibrated_event.inst.subarray.tels[telescope_id] dl1 = calibrated_event.dl1.tel[telescope_id] camera = tel.camera image = dl1.image pulse_time = dl1.pulse_time signal_pixels = cleaning_method(camera, image, **cleaning_parameters) n_pixels = np.count_nonzero(signal_pixels) if n_pixels > 0: # check the number of islands num_islands, island_labels = number_of_islands(camera, signal_pixels) if use_main_island: n_pixels_on_island = np.bincount(island_labels.astype(np.int)) n_pixels_on_island[0] = 0 # first island is no-island and should not be considered max_island_label = np.argmax(n_pixels_on_island) signal_pixels[island_labels != max_island_label] = False hillas = hillas_parameters(camera[signal_pixels], image[signal_pixels]) # Fill container dl1_container.fill_hillas(hillas) dl1_container.fill_event_info(calibrated_event) dl1_container.set_mc_core_distance(calibrated_event, telescope_id) dl1_container.set_mc_type(calibrated_event) dl1_container.set_timing_features(camera[signal_pixels], image[signal_pixels], pulse_time[signal_pixels], hillas) dl1_container.set_leakage(camera, image, signal_pixels) dl1_container.set_concentration(camera, image, hillas) dl1_container.n_pixels = n_pixels dl1_container.n_islands = num_islands dl1_container.set_telescope_info(calibrated_event, telescope_id) return dl1_container else: return None
def process_event(event, config): ''' Processes ''' reco_algorithm = config.reco_algorithm features = {} params = {} pointing_azimuth = {} pointing_altitude = {} tel_x = {} tel_y = {} tel_focal_lengths = {} cleaning_method = config.cleaning_method valid_cleaning_methods = ['tailcuts_clean', 'fact_image_cleaning'] if cleaning_method not in valid_cleaning_methods: print('Cleaning Method not implemented') print('Please use one of ', valid_cleaning_methods) return None for telescope_id, dl1 in event.dl1.tel.items(): camera = event.inst.subarray.tels[telescope_id].camera if camera.cam_id not in config.allowed_cameras: continue telescope_type_name = event.inst.subarray.tels[ telescope_id].optics.tel_type if cleaning_method == 'tailcuts_clean': boundary_thresh, picture_thresh, min_number_picture_neighbors = config.cleaning_level[ camera.cam_id] mask = tailcuts_clean(camera, dl1.image[0], *config.cleaning_level[camera.cam_id]) elif cleaning_method == 'fact_image_cleaning': mask = fact_image_cleaning( camera, dl1.image[0], *config.cleaning_level_fact[camera.cam_id]) try: cleaned = dl1.image[0].copy() cleaned[~mask] = 0 hillas_container = hillas_parameters( camera, cleaned, ) params[telescope_id] = hillas_container except HillasParameterizationError: continue # probably wise to add try...except blocks here as well # Add more Features here (look what ctapipe can do, timing?) num_islands, island_labels = number_of_islands(camera, mask) island_dict = { 'num_islands': num_islands, 'island_labels': island_labels } leakage_container = leakage(camera, dl1.image[0], mask) timing_container = timing_parameters(camera, dl1.image[0], dl1.peakpos[0], hillas_container) pointing_azimuth[ telescope_id] = event.mc.tel[telescope_id].azimuth_raw * u.rad pointing_altitude[ telescope_id] = event.mc.tel[telescope_id].altitude_raw * u.rad tel_x[telescope_id] = event.inst.subarray.positions[telescope_id][0] tel_y[telescope_id] = event.inst.subarray.positions[telescope_id][1] telescope_description = event.inst.subarray.tel[telescope_id] tel_focal_lengths[ telescope_id] = telescope_description.optics.equivalent_focal_length d = { 'array_event_id': event.dl0.event_id, 'telescope_id': int(telescope_id), 'camera_name': camera.cam_id, 'camera_id': config.names_to_id[camera.cam_id], 'run_id': event.r0.obs_id, 'telescope_type_name': telescope_type_name, 'telescope_type_id': config.types_to_id[telescope_type_name], 'pointing_azimuth': event.mc.tel[telescope_id].azimuth_raw, 'pointing_altitude': event.mc.tel[telescope_id].altitude_raw, 'mirror_area': telescope_description.optics.mirror_area, 'focal_length': telescope_description.optics.equivalent_focal_length, } d.update(hillas_container.as_dict()) d.update(leakage_container.as_dict()) d.update(island_dict) d.update(timing_container.as_dict()) features[telescope_id] = ({k: strip_unit(v) for k, v in d.items()}) if reco_algorithm == 'intersection': reco = HillasIntersection() array_direction = SkyCoord(alt=event.mcheader.run_array_direction[1], az=event.mcheader.run_array_direction[0], frame='altaz') reconstruction = reco.predict(params, tel_x, tel_y, tel_focal_lengths, array_direction) elif reco_algorithm == 'planes': reco = HillasReconstructor() reconstruction = reco.predict(params, event.inst, pointing_altitude, pointing_azimuth) for telescope_id in event.dl1.tel.keys(): if telescope_id not in params: continue camera = event.inst.subarray.tels[telescope_id].camera if camera.cam_id not in config.allowed_cameras: continue pos = event.inst.subarray.positions[telescope_id] x, y = pos[0], pos[1] core_x = reconstruction.core_x core_y = reconstruction.core_y d = np.sqrt((core_x - x)**2 + (core_y - y)**2) features[telescope_id]['distance_to_core'] = d.value return pd.DataFrame(list(features.values())), reconstruction, params
# cleaning boundary, picture, min_neighbors = cleaning_level[geom.camera_name] clean = tailcuts_clean(geom, image, boundary_thresh=boundary, picture_thresh=picture, min_number_picture_neighbors=min_neighbors) # ignore images with less than 5 pixels after cleaning if clean.sum() < 5: continue # image parameters hillas_c = hillas_parameters(geom[clean], image[clean]) leakage_c = leakage(geom, image, clean) n_islands, island_ids = number_of_islands(geom, clean) timing_c = timing_parameters( geom[clean], image[clean], peakpos[clean], hillas_c, ) # store parameters for stereo reconstruction hillas_containers[telescope_id] = hillas_c # store timegradients for plotting # ASTRI has no timing in PROD3b, so we use skewness instead if geom.camera_name != 'ASTRICam': time_gradients[telescope_id] = timing_c.slope.value
def main(): std_config = get_standard_config() if args.config_file is not None: config = replace_config(std_config, read_configuration_file(args.config_file)) else: config = std_config print(config['tailcut']) foclen = OpticsDescription.from_name('LST').equivalent_focal_length cam_table = Table.read(args.input_file, path="instrument/telescope/camera/LSTCam") camera_geom = CameraGeometry.from_table(cam_table) dl1_container = DL1ParametersContainer() parameters_to_update = list(HillasParametersContainer().keys()) parameters_to_update.extend([ 'wl', 'r', 'leakage1_intensity', 'leakage2_intensity', 'leakage1_pixel', 'leakage2_pixel', 'concentration_cog', 'concentration_core', 'concentration_pixel', 'n_pixels', 'n_islands', 'intercept', 'time_gradient' ]) nodes_keys = get_dataset_keys(args.input_file) if args.noimage: nodes_keys.remove(dl1_images_lstcam_key) auto_merge_h5files([args.input_file], args.output_file, nodes_keys=nodes_keys) with tables.open_file(args.input_file, mode='r') as input: image_table = input.root[dl1_images_lstcam_key] with tables.open_file(args.output_file, mode='a') as output: params = output.root[dl1_params_lstcam_key].read() for ii, row in enumerate(image_table): if ii % 10000 == 0: print(ii) image = row['image'] pulse_time = row['pulse_time'] signal_pixels = tailcuts_clean(camera_geom, image, **config['tailcut']) n_pixels = np.count_nonzero(signal_pixels) if n_pixels > 0: num_islands, island_labels = number_of_islands( camera_geom, signal_pixels) n_pixels_on_island = np.bincount( island_labels.astype(np.int)) n_pixels_on_island[ 0] = 0 # first island is no-island and should not be considered max_island_label = np.argmax(n_pixels_on_island) signal_pixels[island_labels != max_island_label] = False hillas = hillas_parameters(camera_geom[signal_pixels], image[signal_pixels]) dl1_container.fill_hillas(hillas) dl1_container.set_timing_features( camera_geom[signal_pixels], image[signal_pixels], pulse_time[signal_pixels], hillas) dl1_container.set_leakage(camera_geom, image, signal_pixels) dl1_container.set_concentration(camera_geom, image, hillas) dl1_container.n_islands = num_islands dl1_container.wl = dl1_container.width / dl1_container.length dl1_container.n_pixels = n_pixels width = np.rad2deg(np.arctan2(dl1_container.width, foclen)) length = np.rad2deg( np.arctan2(dl1_container.length, foclen)) dl1_container.width = width.value dl1_container.length = length.value dl1_container.r = np.sqrt(dl1_container.x**2 + dl1_container.y**2) for p in parameters_to_update: params[ii][p] = Quantity(dl1_container[p]).value else: for p in parameters_to_update: params[ii][p] = 0 output.root[dl1_params_lstcam_key][:] = params
clean = tailcuts_clean( camera, image, boundary_thresh=boundary, picture_thresh=picture, min_number_picture_neighbors=min_neighbors ) # ignore images with less than 5 pixels after cleaning if clean.sum() < 5: continue # image parameters hillas_c = hillas_parameters(camera[clean], image[clean]) leakage_c = leakage(camera, image, clean) n_islands, island_ids = number_of_islands(camera, clean) timing_c = timing_parameters( camera[clean], image[clean], peakpos[clean], hillas_c ) # store parameters for stereo reconstruction hillas_containers[telescope_id] = hillas_c pointing_azimuth[telescope_id] = event.mc.tel[telescope_id].azimuth_raw * u.rad pointing_altitude[telescope_id] = event.mc.tel[telescope_id].altitude_raw * u.rad # store timegradients for plotting # ASTRI has no timing in PROD3b, so we use skewness instead if camera.cam_id != 'ASTRICam': time_gradients[telescope_id] = timing_c.slope.value else:
def main_pipeline( files, aux_basepath, max_events, dark_filename, integral_width, debug, hillas_filename, parameters_filename, picture_threshold, boundary_threshold, template_filename, saturation_threshold, threshold_pulse, nevent_plot=12, event_plot_filename=None, bad_pixels=None, disable_bar=False, wdw_number=1, apply_corr_factor=False, ): # get configuration with open(parameters_filename) as file: calibration_parameters = yaml.load(file) if bad_pixels is None: bad_pixels = get_bad_pixels(calib_file=parameters_filename, dark_histo=dark_filename, plot=None) pulse_template = NormalizedPulseTemplate.load(template_filename) pulse_area = pulse_template.integral() * u.ns ratio = pulse_template.compute_charge_amplitude_ratio( integral_width=integral_width, dt_sampling=4) # ~ 0.24 gain = np.array(calibration_parameters['gain']) # ~ 20 LSB / p.e. gain_amplitude = gain * ratio crosstalk = np.array(calibration_parameters['mu_xt']) bias_resistance = 10 * 1E3 * u.Ohm # 10 kOhm cell_capacitance = 50 * 1E-15 * u.Farad # 50 fF geom = DigiCam.geometry dark_histo = Histogram1D.load(dark_filename) dark_baseline = dark_histo.mean() # define pipeline events = calibration_event_stream(files, max_events=max_events, disable_bar=disable_bar) if aux_basepath is not None: events = add_slow_data_calibration( events, basepath=aux_basepath, aux_services=('DriveSystem', 'DigicamSlowControl', 'MasterSST1M', 'SafetyPLC', 'PDPSlowControl')) events = baseline.fill_dark_baseline(events, dark_baseline) events = baseline.fill_digicam_baseline(events) events = tagging.tag_burst_from_moving_average_baseline(events) events = baseline.compute_baseline_shift(events) events = baseline.subtract_baseline(events) events = filters.filter_clocked_trigger(events) events = baseline.compute_nsb_rate(events, gain_amplitude, pulse_area, crosstalk, bias_resistance, cell_capacitance) events = baseline.compute_gain_drop(events, bias_resistance, cell_capacitance) events = peak.find_pulse_with_max(events) events = charge.compute_dynamic_charge( events, integral_width=integral_width, saturation_threshold=saturation_threshold, threshold_pulse=threshold_pulse, debug=debug, pulse_tail=False, ) events = charge.compute_photo_electron(events, gains=gain) events = charge.apply_wdw_transmittance_correction_factor( events, wdw_number, apply_corr_factor) events = charge.interpolate_bad_pixels(events, geom, bad_pixels) events = cleaning.compute_tailcuts_clean( events, geom=geom, overwrite=True, picture_thresh=picture_threshold, boundary_thresh=boundary_threshold, keep_isolated_pixels=False) events = cleaning.compute_boarder_cleaning(events, geom, boundary_threshold) events = cleaning.compute_dilate(events, geom) events = image.compute_hillas_parameters(events, geom) if event_plot_filename is not None: events = plot_nevent(events, nevent_plot, filename=event_plot_filename, bad_pixels=bad_pixels, norm="lin") events = charge.compute_sample_photo_electron(events, gain_amplitude) events = cleaning.compute_3d_cleaning(events, geom, n_sample=50, threshold_sample_pe=20, threshold_time=2.1 * u.ns, threshold_size=0.005 * u.mm) # create pipeline output file output_file = Serializer(hillas_filename, mode='w', format='fits') data_to_store = PipelineOutputContainer() for event in events: if debug: print(event.hillas) print(event.data.nsb_rate) print(event.data.gain_drop) print(event.data.baseline_shift) print(event.data.border) plot_array_camera(np.max(event.data.adc_samples, axis=-1)) plot_array_camera(event.data.reconstructed_charge) plot_array_camera(event.data.cleaning_mask.astype(float)) plot_array_camera(event.data.reconstructed_number_of_pe) plt.show() # fill container data_to_store.local_time = event.data.local_time data_to_store.event_type = event.event_type data_to_store.event_id = event.event_id r = event.hillas.r phi = event.hillas.phi psi = event.hillas.psi alpha = compute_alpha(phi.value, psi.value) * u.rad data_to_store.alpha = alpha data_to_store.miss = compute_miss(r=r.value, alpha=alpha.value) data_to_store.miss = data_to_store.miss * r.unit data_to_store.baseline = np.mean(event.data.digicam_baseline) data_to_store.nsb_rate = np.mean(event.data.nsb_rate) data_to_store.shower = bool(event.data.shower) data_to_store.border = bool(event.data.border) data_to_store.burst = bool(event.data.burst) data_to_store.saturated = bool(event.data.saturated) num_islands, island_labels = number_of_islands( geom, event.data.cleaning_mask) data_to_store.number_of_island = num_islands if aux_basepath is not None: data_to_store.az = event.slow_data.DriveSystem.current_position_az data_to_store.el = event.slow_data.DriveSystem.current_position_el temp_crate1 = event.slow_data.DigicamSlowControl.Crate1_T temp_crate2 = event.slow_data.DigicamSlowControl.Crate2_T temp_crate3 = event.slow_data.DigicamSlowControl.Crate3_T temp_digicam = np.array( np.hstack([temp_crate1, temp_crate2, temp_crate3])) temp_digicam_mean = np.mean(temp_digicam[np.logical_and( temp_digicam > 0, temp_digicam < 60)]) data_to_store.digicam_temperature = temp_digicam_mean temp_sector1 = event.slow_data.PDPSlowControl.Sector1_T temp_sector2 = event.slow_data.PDPSlowControl.Sector2_T temp_sector3 = event.slow_data.PDPSlowControl.Sector3_T temp_pdp = np.array( np.hstack([temp_sector1, temp_sector2, temp_sector3])) temp_pdp_mean = np.mean(temp_pdp[np.logical_and( temp_pdp > 0, temp_pdp < 60)]) data_to_store.pdp_temperature = temp_pdp_mean target_radec = event.slow_data.MasterSST1M.target_radec data_to_store.target_ra = target_radec[0] data_to_store.target_dec = target_radec[1] status_leds = event.slow_data.SafetyPLC.SPLC_CAM_Status # bit 8 of status_LEDs is about on/off, bit 9 about blinking data_to_store.pointing_leds_on = bool((status_leds & 1 << 8) >> 8) pointing_leds_blink = bool((status_leds & 1 << 9) >> 9) data_to_store.pointing_leds_blink = pointing_leds_blink hv_sector1 = event.slow_data.PDPSlowControl.Sector1_HV hv_sector2 = event.slow_data.PDPSlowControl.Sector2_HV hv_sector3 = event.slow_data.PDPSlowControl.Sector3_HV hv_pdp = np.array(np.hstack([hv_sector1, hv_sector2, hv_sector3]), dtype=bool) data_to_store.all_hv_on = np.all(hv_pdp) ghv_sector1 = event.slow_data.PDPSlowControl.Sector1_GHV ghv_sector2 = event.slow_data.PDPSlowControl.Sector2_GHV ghv_sector3 = event.slow_data.PDPSlowControl.Sector3_GHV ghv_pdp = np.array(np.hstack( [ghv_sector1, ghv_sector2, ghv_sector3]), dtype=bool) data_to_store.all_ghv_on = np.all(ghv_pdp) is_on_source = bool(event.slow_data.DriveSystem.is_on_source) data_to_store.is_on_source = is_on_source is_tracking = bool(event.slow_data.DriveSystem.is_tracking) data_to_store.is_tracking = is_tracking for key, val in event.hillas.items(): data_to_store[key] = val output_file.add_container(data_to_store) try: output_file.close() print(hillas_filename, 'created.') except ValueError: print('WARNING: no data to save,', hillas_filename, 'not created.')
def calculate_image_features(telescope_id, event, dl1, config): ''' Performs cleaning and adds the following image parameters: - hillas - leakage - concentration - timing - number of islands Make sure to adapt cleaning levels to the used algorithm (-> config) - tailcuts: picture_thresh, picture_thresh, min_number_picture_neighbors - fact_image_cleaning: picture_threshold, boundary_threshold, min_number_neighbors, time_limit Returns: -------- TelescopeParameterContainer ''' array_event_id = event.dl0.event_id run_id = event.r0.obs_id telescope = event.inst.subarray.tels[telescope_id] image = dl1.image # might want to make the parameter names more consistent between methods if config.cleaning_method == 'tailcuts_clean': boundary_thresh, picture_thresh, min_number_picture_neighbors = config.cleaning_level[ telescope.camera.cam_id ] mask = tailcuts_clean( telescope.camera, image, boundary_thresh=boundary_thresh, picture_thresh=picture_thresh, min_number_picture_neighbors=min_number_picture_neighbors, ) elif config.cleaning_method == 'fact_image_cleaning': boundary_threshold, picture_threshold, time_limit, min_number_neighbors = config.cleaning_level[ telescope.camera.cam_id ] mask = fact_image_cleaning( telescope.camera, image, dl1.pulse_time, boundary_threshhold=boundary_threshold, picture_threshold=picture_threshold, min_number_neighbors=min_number_neighbors, time_limit=time_limit, ) cleaned = image.copy() cleaned[~mask] = 0 logging.debug(f'calculating hillas for event {array_event_id, telescope_id}') hillas_container = hillas_parameters(telescope.camera, cleaned) hillas_container.prefix = '' logging.debug(f'calculating leakage for event {array_event_id, telescope_id}') leakage_container = leakage(telescope.camera, image, mask) leakage_container.prefix = '' logging.debug(f'calculating concentration for event {array_event_id, telescope_id}') concentration_container = concentration(telescope.camera, image, hillas_container) concentration_container.prefix = '' logging.debug(f'getting timing information for event {array_event_id, telescope_id}') timing_container = timing_parameters(telescope.camera, image, dl1.pulse_time, hillas_container) timing_container.prefix = '' # membership missing for now as it causes problems with the hdf5tablewriter # right now i dont need this anyway logging.debug(f'calculating num_islands for event {array_event_id, telescope_id}') num_islands, membership = number_of_islands(telescope.camera, mask) island_container = IslandContainer(num_islands=num_islands) island_container.prefix = '' num_pixel_in_shower = mask.sum() logging.debug(f'getting pointing container for event {array_event_id, telescope_id}') # ctapipe requires this to be rad pointing_container = TelescopePointingContainer( azimuth=event.mc.tel[telescope_id].azimuth_raw * u.rad, altitude=event.mc.tel[telescope_id].altitude_raw * u.rad, prefix='pointing', ) return TelescopeParameterContainer( telescope_id=telescope_id, run_id=run_id, array_event_id=array_event_id, leakage=leakage_container, hillas=hillas_container, concentration=concentration_container, pointing=pointing_container, timing=timing_container, islands=island_container, telescope_type_id=config.types_to_id[telescope.type], camera_type_id=config.names_to_id[telescope.camera.cam_id], focal_length=telescope.optics.equivalent_focal_length, mirror_area=telescope.optics.mirror_area, num_pixel_in_shower=num_pixel_in_shower, )
def get_dl1(calibrated_event, telescope_id, dl1_container=None, custom_config={}, use_main_island=True): """ Return a DL1ParametersContainer of extracted features from a calibrated event. The DL1ParametersContainer can be passed to be filled if created outside the function (faster for multiple event processing) Parameters ---------- calibrated_event: ctapipe event container telescope_id: int dl1_container: DL1ParametersContainer config_file: path to a configuration file configuration used for tailcut cleaning superseeds the standard configuration Returns ------- DL1ParametersContainer """ config = replace_config(standard_config, custom_config) cleaning_parameters = config["tailcut"] dl1_container = DL1ParametersContainer( ) if dl1_container is None else dl1_container tel = calibrated_event.inst.subarray.tels[telescope_id] dl1 = calibrated_event.dl1.tel[telescope_id] camera = tel.camera image = dl1.image pulse_time = dl1.pulse_time signal_pixels = cleaning_method(camera, image, **cleaning_parameters) if image[signal_pixels].sum() > 0: # check the number of islands num_islands, island_labels = number_of_islands(camera, signal_pixels) if use_main_island: n_pixels_on_island = np.zeros(num_islands + 1) for iisland in range(1, num_islands + 1): n_pixels_on_island[iisland] = np.sum(island_labels == iisland) max_island_label = np.argmax(n_pixels_on_island) signal_pixels[island_labels != max_island_label] = False hillas = hillas_parameters(camera[signal_pixels], image[signal_pixels]) # Fill container dl1_container.fill_hillas(hillas) dl1_container.fill_event_info(calibrated_event) dl1_container.set_mc_core_distance(calibrated_event, telescope_id) dl1_container.set_mc_type(calibrated_event) dl1_container.set_timing_features(camera[signal_pixels], image[signal_pixels], pulse_time[signal_pixels], hillas) dl1_container.set_leakage(camera, image, signal_pixels) dl1_container.n_islands = num_islands dl1_container.set_telescope_info(calibrated_event, telescope_id) return dl1_container else: return None