def main(): # Generate a json.product to display messages on Brainlife UI dict_json_product = {'brainlife': []} # Load inputs from config.json with open('config.json') as config_json: config = json.load(config_json) # Read the raw file data_file = config.pop('fif') raw = mne.io.read_raw_fif(data_file, allow_maxshield=True) # Read and save optional files config, cross_talk_file, calibration_file, events_file, head_pos_file, channels_file, destination = helper.read_optional_files(config, 'out_dir_bad_channels') # Convert empty strings values to None config = helper.convert_parameters_to_None(config) # Channels.tsv must be BIDS compliant if channels_file is not None: user_warning_message_channels = f'The channels file provided must be ' \ f'BIDS compliant and the column "status" must be present. ' warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({'type': 'warning', 'msg': user_warning_message_channels}) # Udpate raw.info['bads'] with info contained in channels.tsv raw, user_warning_message_channels = helper.update_data_info_bads(raw, channels_file) if user_warning_message_channels is not None: warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({'type': 'warning', 'msg': user_warning_message_channels}) else: # Create a BIDSPath bids_path = BIDSPath(subject='subject', session=None, task='task', run='01', acquisition=None, processing=None, recording=None, space=None, suffix=None, datatype='meg', root='bids') # Write BIDS to create channels.tsv BIDS compliant write_raw_bids(raw, bids_path, overwrite=True) # Extract channels.tsv from bids path channels_file = 'bids/sub-subject/meg/sub-subject_task-task_run-01_channels.tsv' # Check if param_extended_proj parameter is an empty list string if config['param_extended_proj'] == '[]': config['param_extended_proj'] = [] # required to run a pipeline on BL # Display a warning if h_freq is None if config['param_h_freq'] is None: user_warning_message = f'No low-pass filter will be applied to the data. ' \ f'Make sure line noise and cHPI artifacts were removed before finding ' \ f'bad channels.' warnings.warn(user_warning_message) dict_json_product['brainlife'].append({'type': 'warning', 'msg': user_warning_message}) # Check if config['param_return_scores'] is True if config['param_return_scores'] is not True: value_error_message = f'param_return_scores must be True.' raise ValueError(value_error_message) ## Convert parameters ## # Deal with param_origin parameter # # Convert origin parameter into array when the app is run locally if isinstance(config['param_origin'], list): config['param_origin'] = np.array(config['param_origin']) # Convert origin parameter into array when the app is run on BL if isinstance(config['param_origin'], str) and config['param_origin'] != "auto": param_origin = list(map(float, config['param_origin'].split(', '))) config['param_origin'] = np.array(param_origin) # Raise an error if param origin is not an array of shape 3 if config['param_origin'] != "auto" and config['param_origin'].shape[0] != 3: value_error_message = f"Origin parameter must contain three elements." raise ValueError(value_error_message) # Deal with param_mag_scale parameter # # Convert param_mag_scale into a float when not "auto" when the app runs on BL if isinstance(config['param_mag_scale'], str) and config['param_mag_scale'] != "auto": config['param_mag_scale'] = float(config['param_mag_scale']) # Deal with skip_by_annotation parameter # # Convert param_mag_scale into a list of strings when the app runs on BL skip_by_an = config['param_skip_by_annotation'] if skip_by_an == "[]": skip_by_an = [] elif isinstance(skip_by_an, str) and skip_by_an.find("[") != -1 and skip_by_an != "[]": skip_by_an = skip_by_an.replace('[', '') skip_by_an = skip_by_an.replace(']', '') skip_by_an = list(map(str, skip_by_an.split(', '))) config['param_skip_by_annotation'] = skip_by_an # Delete keys values in config.json when this app is executed on Brainlife kwargs = helper.define_kwargs(config) # Apply find bad channels raw_copy = raw.copy() raw_bad_channels, auto_noisy_chs, auto_flat_chs, auto_scores = find_bad_channels(raw_copy, cross_talk_file, calibration_file, head_pos_file, **kwargs) # Delete the copy del raw_copy ## Update channels.tsv with bad channels ## # Read it as a dataframe df_channels = pd.read_csv(channels_file, sep='\t') # Update df_channels with bad channels bads = raw_bad_channels.info['bads'] for bad in bads: index_bad_channel = df_channels[df_channels['name'] == bad].index df_channels.loc[index_bad_channel, 'status'] = 'bad' # Save channels.tsv df_channels.to_csv('out_dir_bad_channels/channels.tsv', sep = '\t', index=False) # Write a success message in product.json dict_json_product['brainlife'].append({'type': 'success', 'msg': 'Bad channels were successfully detected and written in channels.tsv.'}) # Write an info message in product.json dict_json_product['brainlife'].append({'type': 'info', 'msg': f'This algorithm is not fully reliable. ' f"Don't hesitate to check all of the " f"signals visually " f"before performing an another preprocessing step."}) ## Generate html report ## # Get messages to add to the html report report_calibration_file, report_cross_talk_file, report_head_pos_file, report_destination = helper.message_optional_files_in_reports(calibration_file, cross_talk_file, head_pos_file, destination) # Generate html report _generate_report(raw, raw_bad_channels, auto_scores, auto_noisy_chs, auto_flat_chs, data_file, report_cross_talk_file, report_calibration_file, report_head_pos_file, **kwargs) # Save the dict_json_product in a json file with open('product.json', 'w') as outfile: json.dump(dict_json_product, outfile)
def main(): # Generate a json.product to display messages on Brainlife UI dict_json_product = {'brainlife': []} # Load inputs from config.json with open('config.json') as config_json: config = json.load(config_json) # Read the files data_file = config.pop('fif') raw = mne.io.read_raw_fif(data_file, allow_maxshield=True) # Convert empty strings values to None config = helper.convert_parameters_to_None(config) # Check if param_extended_proj parameter is empty if config['param_extended_proj'] == '[]': config['param_extended_proj'] = [] # Read and save optional files config, cross_talk_file, calibration_file, events_file, head_pos_file, channels_file, destination = helper.read_optional_files( config, 'out_dir_maxwell_filter') # Raise a value error if the user provides both the destination file and the destination parameter if config['param_destination'] is not None and destination is not None: value_error_message = f"You can't provide both a destination file and a " \ f"destination parameter. One of them must be None." raise ValueError(value_error_message) # Channels.tsv must be BIDS compliant if channels_file is not None: user_warning_message_channels = f'The channels file provided must be ' \ f'BIDS compliant and the column "status" must be present. ' warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Update raw.info['bads'] with info contained in channels.tsv raw, user_warning_message_channels = helper.update_data_info_bads( raw, channels_file) if user_warning_message_channels is not None: warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) ## Convert parameters ## # Deal with param_origin parameter # # Convert origin parameter into array when the app is run locally if isinstance(config['param_origin'], list): config['param_origin'] = np.array(config['param_origin']) # Convert origin parameter into array when the app is run on BL if isinstance(config['param_origin'], str) and config['param_origin'] != "auto": param_origin = list(map(float, config['param_origin'].split(', '))) config['param_origin'] = np.array(param_origin) # Raise an error if param origin is not an array of shape 3 if config['param_origin'] != "auto" and config['param_origin'].shape[ 0] != 3: value_error_message = f"Origin parameter must contain three elements." raise ValueError(value_error_message) # Deal with param_destination parameter # if config['param_destination'] is not None: destination = config['param_destination'] report_param_destination = destination # Convert destination parameter into array when the app is run locally if isinstance(destination, list): destination = np.array(destination) # Convert destination parameter into array when the app is run on BL if isinstance(destination, str): report_param_destination = destination destination = list(map(float, destination.split(', '))) destination = np.array(destination) # Raise an error if param destination is not an array of shape 3 if isinstance(destination, np.ndarray) and destination.shape[0] != 3: value_error_message = f"Destination parameter must contain three elements." raise ValueError(value_error_message) else: report_param_destination = None # Deal with param_mag_scale parameter # # Convert param_mag_scale parameter into float when the app is run on BL if isinstance(config['param_mag_scale'], str) and config['param_mag_scale'] != "auto": config['param_mag_scale'] = float(config['param_mag_scale']) # Deal with skip_by_annotation parameter # # Convert param_mag_scale into a list of strings when the app runs on BL skip_by_an = config['param_skip_by_annotation'] if skip_by_an == "[]": skip_by_an = [] elif isinstance(skip_by_an, str) and skip_by_an.find("[") != -1 and skip_by_an != "[]": skip_by_an = skip_by_an.replace('[', '') skip_by_an = skip_by_an.replace(']', '') skip_by_an = list(map(str, skip_by_an.split(', '))) config['param_skip_by_annotation'] = skip_by_an # Display a warning if bad channels are empty if not raw.info['bads']: user_warning_message = f'No channels are marked as bad. ' \ f'Make sure to check (automatically or visually) for bad channels before ' \ f'applying Maxwell Filtering.' warnings.warn(user_warning_message) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message }) ## Define kwargs ## # Delete keys values in config.json when this app is executed on Brainlife config = helper.define_kwargs(config) # Delete the param_destination key del config['param_destination'] # Define kwargs kwargs = config # Keep bad channels in memory bad_channels = raw.info['bads'] # Apply Maxwell Filter raw_maxwell_filter = apply_maxwell_filter(raw, calibration_file, cross_talk_file, head_pos_file, destination, **kwargs) # Update channels.tsv if it exists because bad channels were interpolated if channels_file is not None: df_channels = pd.read_csv(channels_file, sep='\t') for bad in bad_channels: index_bad_channel = df_channels[df_channels['name'] == bad].index df_channels.loc[index_bad_channel, 'status'] = 'good' # Save channels.tsv df_channels.to_csv('out_dir_maxwell_filter/channels.tsv', sep='\t', index=False) # Write a success message in product.json dict_json_product['brainlife'].append({ 'type': 'success', 'msg': 'Maxwell Filter was applied successfully.' }) # Compute SNR on magnetometers # snr_before_mag = _compute_snr(raw, meg_channels_type='mag') # snr_after_mag = _compute_snr(raw_maxfilter, meg_channels_type='mag') # Compute SNR on gradiometers # snr_before_mag = _compute_snr(raw, meg_channels_type='grad') # snr_after_mag = _compute_snr(raw_maxfilter, meg_channels_type='grad') ## Generate html report ## # Get messages to add to the html report report_calibration_file, report_cross_talk_file, report_head_pos_file, report_destination_file = helper.message_optional_files_in_reports( calibration_file, cross_talk_file, head_pos_file, destination) # Generate report _generate_report(data_file, raw, raw_maxwell_filter, bad_channels, report_cross_talk_file, report_calibration_file, report_head_pos_file, report_destination_file, report_param_destination, **kwargs) # Save the dict_json_product in a json file with open('product.json', 'w') as outfile: json.dump(dict_json_product, outfile)
def main(): # Generate a json.product to display messages on Brainlife UI dict_json_product = {'brainlife': []} # Load inputs from config.json with open('config.json') as config_json: config = json.load(config_json) # Read the MEG file data_file = config.pop('fif') raw = mne.io.read_raw_fif(data_file, allow_maxshield=True) # Read and save optional files config, cross_talk_file, calibration_file, events_file, head_pos_file, channels_file, destination = helper.read_optional_files(config, 'out_dir_make_epochs') # Convert empty strings values to None config = helper.convert_parameters_to_None(config) # Channels.tsv must be BIDS compliant if channels_file is not None: user_warning_message_channels = f'The channels file provided must be ' \ f'BIDS compliant and the column "status" must be present. ' warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({'type': 'warning', 'msg': user_warning_message_channels}) raw, user_warning_message_channels = helper.update_data_info_bads(raw, channels_file) if user_warning_message_channels is not None: warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({'type': 'warning', 'msg': user_warning_message_channels}) ## Extract the matrix of events ## # Test if an events file is given if events_file is None: value_error_message = f'You need to provide events.tsv to make epochs. ' \ f'Please use the app-get-events to create such file.' # Raise exception raise ValueError(value_error_message) # Get the matrix of events elif events_file is not None: # Warning: events file must be BIDS compliant user_warning_message_events = f'The events file provided must be ' \ f'BIDS compliant.' warnings.warn(user_warning_message_events) dict_json_product['brainlife'].append({'type': 'warning', 'msg': user_warning_message_events}) ############### TO BE TESTED ON NO RESTING STATE DATA # Compute the events matrix # df_events = pd.read_csv(events_file, sep='\t') # Extract relevant info from df_events samples = df_events['sample'].values event_id = df_events['value'].values # Compute the values for events matrix events_time_in_sample = [raw.first_samp + sample for sample in samples] values_of_trigger_channels = [0]*len(events_time_in_sample) # Create a dataframe df_events_matrix = pd.DataFrame([events_time_in_sample, values_of_trigger_channels, event_id]) df_events_matrix = df_events_matrix.transpose() # Convert dataframe to numpy array events_matrix = df_events_matrix.to_numpy() ## Convert parameters ## # Deal with param_baseline parameter # # Convert baseline parameter into tuple when the app runs locally if isinstance(config['param_baseline'], list): config['param_baseline'] = tuple(config['param_baseline']) # Convert baseline parameter into tuple when the App runs on BL if isinstance(config['param_baseline'], str): param_baseline = list(map(str, config['param_baseline'].split(', '))) param_baseline = [None if i=='None' else i for i in param_baseline] param_baseline = [float(i) if isinstance(i, str) else i for i in param_baseline] config['param_baseline'] = tuple(param_baseline) # Deal with param_proj parameter # # Convert string into boolean when app runs on BL if config['param_proj'] == "True": config['param_proj'] = True elif config['param_proj'] == "False": config['param_proj'] = False # Deal with param_picks_by_channel_indices parameter # # Convert it into a slice When the App is run locally and on BL picks = config['param_picks_by_channel_indices'] if isinstance(picks, str) and picks.find(",") != -1 and picks.find("[") == -1 and picks is not None: picks = list(map(int, picks.split(', '))) if len(picks) == 2: config['param_picks_by_channel_indices'] = slice(picks[0], picks[1]) elif len(picks) == 3: config['param_picks_by_channel_indices'] = slice(picks[0], picks[1], picks[2]) else: value_error_message = f"If you want to select channels using a slice, you must give two or three elements." raise ValueError(value_error_message) # Convert it into a list of integers when the App is run on BL if isinstance(picks, str) and picks.find(",") != -1 and picks.find("[") != -1 and picks is not None: picks = picks.replace('[', '') picks = picks.replace(']', '') config['param_picks_by_channel_indices'] = list(map(int, picks.split(', '))) # Deal with param_picks_by_channel_types_or_name parameter # # Convert it into a list of str when the App is run on BL picks = config['param_picks_by_channel_types_or_names'] if isinstance(picks, str) and picks.find("[") != -1 and picks is not None: picks = picks.replace('[', '') picks = picks.replace(']', '') config['param_picks_by_channel_types_or_names'] = list(map(str, picks.split(', '))) # Deal with event id # # Convert it into a list of int or an int When it is run on BL if config['param_event_id'] is not None: if config['param_event_id'].find("[") != -1: config['param_event_id'] = config['param_event_id'].replace('[', '') config['param_event_id'] = config['param_event_id'].replace(']', '') config['param_event_id'] = list(map(int, config['param_event_id'].split(', '))) else: config['param_event_id'] = int(config['param_event_id']) # Deal with param metadata # # Convert it into a pd.Dataframe when the App runs locally if isinstance(config['param_metadata'], str): config['param_metadata'] = pd.read_csv(config['param_metadata']) elif isinstance(config['param_metadata'], dict): config['param_metadata'] = pd.DataFrame(list(config['param_metadata'].items())) # Deal with param detrend # # Convert it into an int if not None if isinstance(config['param_detrend'], str): config['param_detrend'] = int(config['param_detrend']) # Delete keys values in config.json when this app is executed on Brainlife kwargs = helper.define_kwargs(config) # Epoch data epoched_data = make_epochs(raw, events_matrix, **kwargs) # Success message in product.json dict_json_product['brainlife'].append({'type': 'success', 'msg': 'Data was successfully epoched.'}) # Save the dict_json_product in a json file with open('product.json', 'w') as outfile: json.dump(dict_json_product, outfile)
def main(): # Generate a json.product to display messages on Brainlife UI dict_json_product = {'brainlife': []} # Load inputs from config.json with open('config.json') as config_json: config = json.load(config_json) # Read the files data_file = config.pop('fif') if config['param_epoched_data'] is False: data = mne.io.read_raw_fif(data_file, allow_maxshield=True) else: data = mne.read_epochs(data_file) # Read and save optional files config, cross_talk_file, calibration_file, events_file, head_pos_file, channels_file, destination = helper.read_optional_files( config, 'out_dir_resampling') # Convert empty strings values to None config = helper.convert_parameters_to_None(config) # Channels.tsv must be BIDS compliant if channels_file is not None: user_warning_message_channels = f'The channels file provided must be ' \ f'BIDS compliant and the column "status" must be present. ' warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Udpate data.info['bads'] with info contained in channels.tsv data, user_warning_message_channels = helper.update_data_info_bads( data, channels_file) if user_warning_message_channels is not None: warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Extract the matrix of events # if config['param_epoched_data'] is False: if events_file is not None: # Warning: events file must be BIDS compliant user_warning_message_events = f'The events file provided must be ' \ f'BIDS compliant.' warnings.warn(user_warning_message_events) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_events }) ############### TO BE TESTED ON NO RESTING STATE DATA # Compute the events matrix # df_events = pd.read_csv(events_file, sep='\t') # Extract relevant info from df_events samples = df_events['sample'].values event_id = df_events['value'].values # Compute the values for events matrix events_time_in_sample = [ data.first_samp + sample for sample in samples ] values_of_trigger_channels = [0] * len(events_time_in_sample) # Create a dataframe df_events_matrix = pd.DataFrame( [events_time_in_sample, values_of_trigger_channels, event_id]) df_events_matrix = df_events_matrix.transpose() # Convert dataframe to numpy array events_matrix = df_events_matrix.to_numpy() else: events_matrix = None else: events_matrix = None # Info message about resampling if applied if config['param_epoched_data'] is False: dict_json_product['brainlife'].append({ 'type': 'info', 'msg': f'Data was resampled at ' f'{config["param_sfreq"]}. ' f'Please bear in mind that it is generally ' f'recommended not to epoch ' f'downsampled data, but instead epoch ' f'and then downsample.' }) # Comment about resampling comments_resample_freq = f'{config["param_sfreq"]}Hz' # Check if the user will save an empty events file if events_file is None and config[ 'param_save_jointly_resampled_events'] is True: value_error_message = f'You cannot save en empty events file. ' \ f"If you haven't an events file, please set " \ f"'param_save_jointly_resampled_event' to False." # Raise exception raise ValueError(value_error_message) ## Convert parameters ## # Deal with param_npad parameter # # Convert param_npad into int if not "auto" when the App is run on BL if config['param_npad'] != "auto": config['param_npad'] = int(config['param_npad']) # Deal with param_n_jobs parameter # # Convert n jobs into int when the App is run on BL if config['param_n_jobs'] != 'cuda': config['param_n_jobs'] = int(config['param_n_jobs']) # Deal with stim picks parameter # # Convert stim picks into a list of int when the App is run on BL if isinstance(config['param_stim_picks'], str) and config['param_stim_picks'] is not None: config['param_stim_picks'] = config['param_stim_picks'].replace( '[', '') config['param_stim_picks'] = config['param_stim_picks'].replace( ']', '') config['param_stim_picks'] = list( map(int, config['param_stim_picks'].split(', '))) # Keep bad channels in memory bad_channels = data.info['bads'] # Define the type of data data = data.pick(picks=config['param_pick_type']) # Delete keys values in config.json when this app is executed on Brainlife del config['param_pick_type'] kwargs = helper.define_kwargs(config) # Apply resampling data_copy = data.copy() data_resampled, events_resampled = resampling(data_copy, events_matrix, **kwargs) del data_copy ## Create BIDS compliant events file if existed ## if events_resampled is not None and config['param_epoched_data'] is False: # Create a BIDSPath bids_path = BIDSPath(subject='subject', session=None, task='task', run='01', acquisition=None, processing=None, recording=None, space=None, suffix=None, datatype='meg', root='bids') # Extract event_id value # # to be tested when events are extracted from data event_id_value = list( events_resampled[:, 2] ) # the third column of events corresponds to the value column of BIDS events.tsv id_values_occurrences = Counter( event_id_value) # number of different events id_values_occurrences = list(id_values_occurrences.keys()) trials_type = [ f"events_{i}" for i in range(1, len(id_values_occurrences) + 1) ] # for trial type column of BIDS events.tsv dict_event_id = dict( (k, v) for k, v in zip(trials_type, id_values_occurrences)) # Write BIDS to create events.tsv BIDS compliant write_raw_bids(data, bids_path, events_data=events_resampled, event_id=dict_event_id, overwrite=True) # Extract events.tsv from bids path events_file = 'bids/sub-subject/meg/sub-subject_task-task_run-01_events.tsv' # Copy events.tsv in outdir shutil.copy2(events_file, 'out_dir_resampling/events.tsv') # Info message in product.json dict_json_product['brainlife'].append({ 'type': 'info', 'msg': 'Jointly resampled events are saved in events.tsv.' }) # Success message in product.json dict_json_product['brainlife'].append({ 'type': 'success', 'msg': 'Data was successfully resampled.' }) # Save the dict_json_product in a json file with open('product.json', 'w') as outfile: json.dump(dict_json_product, outfile)
def main(): # Generate a json.product to display messages on Brainlife UI dict_json_product = {'brainlife': []} # Load inputs from config.json with open('config.json') as config_json: config = json.load(config_json) # Read the files data_file = config.pop('fif') raw = mne.io.read_raw_fif(data_file, allow_maxshield=True) # Read and save optional files config, cross_talk_file, calibration_file, events_file, head_pos_file, channels_file, destination = helper.read_optional_files( config, 'out_dir_notch_filter') # Convert empty strings values to None config = helper.convert_parameters_to_None(config) # Raise error if no start parameter and method is wrong if config['param_freqs_specific_or_start'] is None and config[ 'param_method'] != 'spectrum_fit': value_error_message = f'This frequency can only be None when method is spectrum_fit.' # Raise exception raise ValueError(value_error_message) ## Convert parameters ## # Deal with param_picks_by_channel_indices parameter # # Convert it into a slice When the App is run locally and on BL picks = config['param_picks_by_channel_indices'] if isinstance(picks, str) and picks.find(",") != -1 and picks.find( "[") == -1 and picks is not None: picks = list(map(int, picks.split(', '))) if len(picks) == 2: config['param_picks_by_channel_indices'] = slice( picks[0], picks[1]) elif len(picks) == 3: config['param_picks_by_channel_indices'] = slice( picks[0], picks[1], picks[2]) else: value_error_message = f"If you want to select channels using a slice, you must give two or three elements." raise ValueError(value_error_message) # Convert it into a list of int when the app is run on BL if isinstance(picks, str) and picks.find(",") != -1 and picks.find( "[") != -1 and picks is not None: picks = picks.replace('[', '') picks = picks.replace(']', '') config['param_picks_by_channel_indices'] = list( map(int, picks.split(', '))) # Deal with param_picks_by_channel_types_or_name parameter # # Convert it into a list of str when the App is run on BL picks = config['param_picks_by_channel_types_or_names'] if isinstance(picks, str) and picks.find("[") != -1 and picks is not None: picks = picks.replace('[', '') picks = picks.replace(']', '') config['param_picks_by_channel_types_or_names'] = list( map(str, picks.split(', '))) # Deal with filter_length parameter # # Convert it into int if not "auto" when the App runs on BL if config['param_filter_length'] != "auto" and config[ 'param_filter_length'].find("s") == -1: config['param_filter_length'] = int(config['param_filter_length']) # Deal with param_notch_widths parameter # # Convert notch widths parameter into array when the app is run locally if isinstance(config['param_notch_widths'], list): config['param_notch_widths'] = np.array(config['param_notch_widths']) # Convert notch widths parameter into array when the app is run on BL if isinstance(config['param_notch_widths'], str): config['param_notch_widths'] = list( map(float, config['param_notch_widths'].split(', '))) if len(config['param_notch_widths']) == 1: config['param_notch_widths'] = float( config['param_notch_widths'][0]) else: config['param_notch_widths'] = np.array( config['param_notch_widths']) # Deal with param_n_jobs parameter # # Convert it into into if not "cuda" When the App is run on BL if config['param_n_jobs'] != 'cuda': config['param_n_jobs'] = int(config['param_n_jobs']) # Comments messages about filtering # if config['param_freqs_specific_or_start'] is not None and config[ 'param_freqs_end'] is None: comments_notch = f"{config['param_freqs_specific_or_start']}Hz" elif config['param_freqs_specific_or_start'] is not None and config[ 'param_freqs_end'] is not None: comments_notch = f"Between {config['param_freqs_specific_or_start']} and {config['param_freqs_end']}Hz" if config['param_freqs_step'] is not None: comments_notch = f"Between {config['param_freqs_specific_or_start']} and " \ f"{config['param_freqs_end']}Hz every {config['param_freqs_step']}Hz" # Channels.tsv must be BIDS compliant if channels_file is not None: user_warning_message_channels = f'The channels file provided must be ' \ f'BIDS compliant and the column "status" must be present. ' warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Udpate raw.info['bads'] with info contained in channels.tsv raw, user_warning_message_channels = helper.update_data_info_bads( raw, channels_file) if user_warning_message_channels is not None: warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Keep bad channels in memory # bad_channels = raw.info['bads'] # Delete keys values in config.json when this app is executed on Brainlife kwargs = helper.define_kwargs(config) # Apply temporal filtering raw_copy = raw.copy() raw_notch_filtered = notch_filter(raw_copy, **kwargs) del raw_copy # Success message in product.json dict_json_product['brainlife'].append({ 'type': 'success', 'msg': 'Notch filter was applied successfully.' }) # Compute SNR # snr_before = _compute_snr(raw) # snr_after = _compute_snr(raw_filtered) # Generate a report _generate_report(data_file, raw, raw_notch_filtered, bad_channels, comments_notch, **kwargs) # Save the dict_json_product in a json file with open('product.json', 'w') as outfile: json.dump(dict_json_product, outfile)
def main(): # Generate a json.product to display messages on Brainlife UI dict_json_product = {'brainlife': []} # Load inputs from config.json with open('config.json') as config_json: config = json.load(config_json) # Read the MEG file data_file = config.pop('fif') raw = mne.io.read_raw_fif(data_file, allow_maxshield=True) # Read and save optional files config, cross_talk_file, calibration_file, events_file, head_pos_file, channels_file, destination = helper.read_optional_files( config, 'out_dir_get_events') # Convert empty strings values to None config = helper.convert_parameters_to_None(config) # Channels.tsv must be BIDS compliant if channels_file is not None: user_warning_message_channels = f'The channels file provided must be ' \ f'BIDS compliant and the column "status" must be present. ' warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Udpate raw.info['bads'] with info contained in channels.tsv raw, user_warning_message_channels = helper.update_data_info_bads( raw, channels_file) if user_warning_message_channels is not None: warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Events file if events_file is not None: user_warning_message_events = f'The events.tsv file provided will be ' \ f'overwritten with the new events obtained by this App.' warnings.warn(user_warning_message_events) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_events }) ## Convert parameters ## # Deal with stim channels # # Convert it into a list of strings it is run on BL stim_channels = config['param_find_events_stim_channels'] if isinstance(stim_channels, str) and stim_channels.find( "[") != -1 and stim_channels is not None: stim_channels = stim_channels.replace('[', '') stim_channels = stim_channels.replace(']', '') config['param_find_events_stim_channels'] = list( map(str, stim_channels.split(', '))) # Deal with param consecutive # # Convert it into a bool if necessary if config['param_find_events_consecutive'] == "True": config['param_find_events_consecutive'] = True elif config['param_find_events_consecutive'] == "False": config['param_find_events_consecutive'] = False # Test if the data contains events if raw.info['events'] and config['param_make_events'] is True: user_warning_message = f'Events already exist in this raw file. ' \ f'You are going to create an events.tsv file with events ' \ f'different from those contained in the raw file.' warnings.warn(user_warning_message) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message }) elif not raw.info['events'] and config['param_make_events'] is False: error_value_message = f"You can't extract events from this raw file because it doesn't contain " \ f"any events. Please set param_make_events to 'True' so that fixed " \ f"length events will be created." raise ValueError(error_value_message) # Delete keys values in config.json when this app is executed on Brainlife kwargs = helper.define_kwargs(config) # Create or extract events events = get_events(raw, **kwargs) ## Create BIDS compliant events file ## # Create a BIDSPath bids_path = BIDSPath(subject='subject', session=None, task='task', run='01', acquisition=None, processing=None, recording=None, space=None, suffix=None, datatype='meg', root='bids') # Extract event_id value # # When fixed length events were created if config['param_make_events'] is True: dict_event_id = {'event': config['param_make_events_id']} # When existing events were extracted else: # to be tested event_id_value = list( events[:, 2] ) # the third column of events corresponds to the value column of BIDS events.tsv id_values_occurrences = Counter( event_id_value) # number of different events id_values_occurrences = list(id_values_occurrences.keys()) trials_type = [ f"events_{i}" for i in range(1, len(id_values_occurrences) + 1) ] # for trial type column of BIDS events.tsv dict_event_id = dict( (k, v) for k, v in zip(trials_type, id_values_occurrences)) # Write BIDS to create events.tsv BIDS compliant write_raw_bids(raw, bids_path, events_data=events, event_id=dict_event_id, overwrite=True) # Extract events.tsv from bids path events_file = 'bids/sub-subject/meg/sub-subject_task-task_run-01_events.tsv' # Copy events.tsv in outdir shutil.copy2(events_file, 'out_dir_get_events/events.tsv') # Success message in product.json if config['param_make_events'] is True: dict_json_product['brainlife'].append({ 'type': 'success', 'msg': 'Events were successfully created.' }) else: dict_json_product['brainlife'].append({ 'type': 'success', 'msg': 'Events were successfully extracted.' }) # Save the dict_json_product in a json file with open('product.json', 'w') as outfile: json.dump(dict_json_product, outfile)
def main(): # Generate a json.product to display messages on Brainlife UI dict_json_product = {'brainlife': []} # Load inputs from config.json with open('config.json') as config_json: config = json.load(config_json) # Read the files data_file = config.pop('fif') if config['param_epoched_data'] is False: data = mne.io.read_raw_fif(data_file, allow_maxshield=True) else: data = mne.read_epochs(data_file) # Read and save optional files config, cross_talk_file, calibration_file, events_file, head_pos_file, channels_file, destination = helper.read_optional_files( config, 'out_dir_temporal_filtering') # Convert empty strings values to None config = helper.convert_parameters_to_None(config) ## Convert parameters ## # Deal with param_picks_by_channel_indices parameter # # Convert param_picks_by_channel_indices into slice when the App is run locally and on BL picks = config['param_picks_by_channel_indices'] if isinstance(picks, str) and picks.find(",") != -1 and picks.find( "[") == -1 and picks is not None: picks = list(map(int, picks.split(', '))) if len(picks) == 2: config['param_picks_by_channel_indices'] = slice( picks[0], picks[1]) elif len(picks) == 3: config['param_picks_by_channel_indices'] = slice( picks[0], picks[1], picks[2]) else: value_error_message = f"If you want to select channels using a slice, you must give two or three elements." raise ValueError(value_error_message) # Convert param_picks_by_channel_indices into a list of integers when the App is run on BL if isinstance(picks, str) and picks.find(",") != -1 and picks.find( "[") != -1 and picks is not None: picks = picks.replace('[', '') picks = picks.replace(']', '') config['param_picks_by_channel_indices'] = list( map(int, picks.split(', '))) # Deal with param_picks_by_channel_types_or_name parameter # # Convert param_picks_by_channel_types_or_names into a list of string when the App is run on BL picks = config['param_picks_by_channel_types_or_names'] if isinstance(picks, str) and picks.find("[") != -1 and picks is not None: picks = picks.replace('[', '') picks = picks.replace(']', '') config['param_picks_by_channel_types_or_names'] = list( map(str, picks.split(', '))) # Deal with filter_length parameter on BL # # Convert param_filter_length into int if not auto and not a length in time when the App is run on BL if config['param_filter_length'] != "auto" and config[ 'param_filter_length'].find("s") == -1: config['param_filter_length'] = int(config['param_filter_length']) # Deal with param_l_trans_bandwidth parameter on BL # # Convert param_l_trans_bandwidth into a float if not auto when the App is run on BL if isinstance(config['param_l_trans_bandwidth'], str) and config['param_l_trans_bandwidth'] != "auto": config['param_l_trans_bandwidth'] = float( config['param_l_trans_bandwidth']) # Deal with param_h_trans_bandwidth parameter on BL # # Convert param_h_trans_bandwidth into a float if not auto when the App is run on BL if isinstance(config['param_h_trans_bandwidth'], str) and config['param_h_trans_bandwidth'] != "auto": config['param_h_trans_bandwidth'] = float( config['param_h_trans_bandwidth']) # Deal with param_n_jobs parameter on BL # # Convert param n jobs into an int if not cuda if config['param_n_jobs'] != 'cuda': config['param_n_jobs'] = int(config['param_n_jobs']) # Deal with skip_by_annotation parameter # # Convert param_mag_scale into a list of strings when the app runs on BL skip_by_an = config['param_skip_by_annotation'] if skip_by_an == "[]": skip_by_an = [] elif isinstance(skip_by_an, str) and skip_by_an.find("[") != -1 and skip_by_an != "[]": skip_by_an = skip_by_an.replace('[', '') skip_by_an = skip_by_an.replace(']', '') skip_by_an = list(map(str, skip_by_an.split(', '))) config['param_skip_by_annotation'] = skip_by_an ## Info message about filtering ## # Band pass filter if config['param_l_freq'] is not None and config[ 'param_h_freq'] is not None: comments_about_filtering = f'Data was filtered between ' \ f'{config["param_l_freq"]} ' \ f'and {config["param_h_freq"]}Hz.' dict_json_product['brainlife'].append({ 'type': 'info', 'msg': comments_about_filtering }) filter_type = "band-pass" # Lowpass filter elif config['param_l_freq'] is None and config['param_h_freq'] is not None: comments_about_filtering = f'Lowpass filter was applied at {config["param_h_freq"]}Hz.' dict_json_product['brainlife'].append({ 'type': 'info', 'msg': comments_about_filtering }) filter_type = "low-pass" # Highpass filter elif config['param_l_freq'] is not None and config['param_h_freq'] is None: comments_about_filtering = f'Highpass filter was applied at {config["param_l_freq"]}Hz.' dict_json_product['brainlife'].append({ 'type': 'info', 'msg': comments_about_filtering }) filter_type = "high-pass" # Raise an exception if both param_filter_l_freq and param_filter_h_freq are None elif config['param_l_freq'] is None and config["param_h_freq"] is None: value_error_message = f'You must specify a value for param_l_freq or param_h_freq, ' \ f"they can't both be set to None." # Raise exception raise ValueError(value_error_message) # Channels.tsv must be BIDS compliant if channels_file is not None: user_warning_message_channels = f'The channels file provided must be ' \ f'BIDS compliant and the column "status" must be present. ' warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Udpate data.info['bads'] with info contained in channels.tsv data, user_warning_message_channels = helper.update_data_info_bads( data, channels_file) if user_warning_message_channels is not None: warnings.warn(user_warning_message_channels) dict_json_product['brainlife'].append({ 'type': 'warning', 'msg': user_warning_message_channels }) # Keep bad channels in memory bad_channels = data.info['bads'] # Delete keys values in config.json when this app is executed on Brainlife kwargs = helper.define_kwargs(config) # Apply temporal filtering data_copy = data.copy() data_filtered = temporal_filtering(data_copy, **kwargs) del data_copy # Success message in product.json dict_json_product['brainlife'].append({ 'type': 'success', 'msg': 'Filtering was applied successfully.' }) # Compute SNR #snr_before = _compute_snr(data) #snr_after = _compute_snr(data_filtered) # Generate a report _generate_report(data_file, data, data_filtered, bad_channels, comments_about_filtering, **kwargs) # Save the dict_json_product in a json file with open('product.json', 'w') as outfile: json.dump(dict_json_product, outfile)