def generate_ROIs(ROI_file, stimfunc, noise, scale_percentage, data_dict): # Create the signal in the ROI as specified. print('Loading', ROI_file) nii = nibabel.load(ROI_file) ROI = nii.get_data() # Find all the indices that contain signal idx_list = np.where(ROI == 1) idxs = np.zeros([len(idx_list[0]), 3]) for idx_counter in list(range(0, len(idx_list[0]))): idxs[idx_counter, 0] = int(idx_list[0][idx_counter]) idxs[idx_counter, 1] = int(idx_list[1][idx_counter]) idxs[idx_counter, 2] = int(idx_list[2][idx_counter]) idxs = idxs.astype('int8') # How many voxels per ROI voxels = int(ROI.sum()) # Create a pattern of activity across the two voxels if data_dict['multivariate_pattern'] is True: pattern = np.random.rand(voxels).reshape((voxels, 1)) else: # Just make a univariate increase pattern = np.tile(1, voxels).reshape((voxels, 1)) # Multiply each pattern by each voxel time course weights = np.tile(stimfunc, voxels) * pattern.T # Convolve the onsets with the HRF temporal_res = 1 / data_dict['trDuration'] signal_func = sim.convolve_hrf( stimfunction=weights, tr_duration=data_dict['trDuration'], temporal_resolution=temporal_res, scale_function=1, ) # Change the type of noise noise = noise.astype('double') # Create a noise function (same voxels for signal function as for noise) noise_function = noise[idxs[:, 0], idxs[:, 1], idxs[:, 2], :].T # Compute the signal magnitude for the data sf_scaled = sim.compute_signal_change( signal_function=signal_func, noise_function=noise_function, noise_dict=data_dict['noise_dict'], magnitude=[scale_percentage], method='PSC', ) # Combine the signal time course with the signal volume signal = sim.apply_signal( sf_scaled, ROI, ) # Return signal needed return signal
template=template, mask=mask, noise_dict=noise_dict, ) # Change the type of noise noise = noise.astype('double') # Create a noise function (same voxels for signal function as for noise) noise_function = noise[idxs[:, 0], idxs[:, 1], idxs[:, 2], :].T # Compute the signal magnitude for the data signal_func_scaled = sim.compute_signal_change(signal_function=signal_func, noise_function=noise_function, noise_dict=noise_dict, magnitude=[ scale_percentage], method='PSC', ) # Multiply the voxels with signal by the HRF function brain_signal = sim.apply_signal(signal_function=signal_func_scaled, volume_signal=signal_mask, ) # Convert any nans to 0s brain_signal[np.isnan(brain_signal)] = 0 # Combine the signal and the noise brain = brain_signal + noise
def test_apply_signal(): dimensions = np.array([10, 10, 10]) # What is the size of the brain feature_size = [2] feature_type = ['cube'] feature_coordinates = np.array( [[5, 5, 5]]) signal_magnitude = [30] # Generate a volume representing the location and quality of the signal volume = sim.generate_signal(dimensions=dimensions, feature_coordinates=feature_coordinates, feature_type=feature_type, feature_size=feature_size, signal_magnitude=signal_magnitude, ) # Inputs for generate_stimfunction onsets = [10, 30, 50, 70, 90] event_durations = [6] tr_duration = 2 duration = 100 # Create the time course for the signal to be generated stimfunction = sim.generate_stimfunction(onsets=onsets, event_durations=event_durations, total_time=duration, ) signal_function = sim.convolve_hrf(stimfunction=stimfunction, tr_duration=tr_duration, ) # Check that you can compute signal change appropriately # Preset a bunch of things stimfunction_tr = stimfunction[::int(tr_duration * 100)] mask, template = sim.mask_brain(dimensions, mask_self=False) noise_dict = sim._noise_dict_update({}) noise = sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict=noise_dict, iterations=[0, 0] ) coords = feature_coordinates[0] noise_function_a = noise[coords[0], coords[1], coords[2], :] noise_function_a = noise_function_a.reshape(duration // tr_duration, 1) noise_function_b = noise[coords[0] + 1, coords[1], coords[2], :] noise_function_b = noise_function_b.reshape(duration // tr_duration, 1) # Create the calibrated signal with PSC method = 'PSC' sig_a = sim.compute_signal_change(signal_function, noise_function_a, noise_dict, [0.5], method, ) sig_b = sim.compute_signal_change(signal_function, noise_function_a, noise_dict, [1.0], method, ) assert sig_b.max() / sig_a.max() == 2, 'PSC modulation failed' # Create the calibrated signal with SFNR method = 'SFNR' sig_a = sim.compute_signal_change(signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = sig_a / (noise_function_a.mean() / noise_dict['sfnr']) sig_b = sim.compute_signal_change(signal_function, noise_function_b, noise_dict, [1.0], method, ) scaled_b = sig_b / (noise_function_b.mean() / noise_dict['sfnr']) assert scaled_b.max() / scaled_a.max() == 2, 'SFNR modulation failed' # Create the calibrated signal with CNR_Amp/Noise-SD method = 'CNR_Amp/Noise-SD' sig_a = sim.compute_signal_change(signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = sig_a / noise_function_a.std() sig_b = sim.compute_signal_change(signal_function, noise_function_b, noise_dict, [1.0], method, ) scaled_b = sig_b / noise_function_b.std() assert scaled_b.max() / scaled_a.max() == 2, 'CNR_Amp modulation failed' # Create the calibrated signal with CNR_Amp/Noise-Var_dB method = 'CNR_Amp2/Noise-Var_dB' sig_a = sim.compute_signal_change(signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = np.log(sig_a.max() / noise_function_a.std()) sig_b = sim.compute_signal_change(signal_function, noise_function_b, noise_dict, [1.0], method, ) scaled_b = np.log(sig_b.max() / noise_function_b.std()) assert np.round(scaled_b / scaled_a) == 2, 'CNR_Amp dB modulation failed' # Create the calibrated signal with CNR_Signal-SD/Noise-SD method = 'CNR_Signal-SD/Noise-SD' sig_a = sim.compute_signal_change(signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = sig_a.std() / noise_function_a.std() sig_b = sim.compute_signal_change(signal_function, noise_function_a, noise_dict, [1.0], method, ) scaled_b = sig_b.std() / noise_function_a.std() assert (scaled_b / scaled_a) == 2, 'CNR signal modulation failed' # Create the calibrated signal with CNR_Amp/Noise-Var_dB method = 'CNR_Signal-Var/Noise-Var_dB' sig_a = sim.compute_signal_change(signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = np.log(sig_a.std() / noise_function_a.std()) sig_b = sim.compute_signal_change(signal_function, noise_function_b, noise_dict, [1.0], method, ) scaled_b = np.log(sig_b.std() / noise_function_b.std()) assert np.round(scaled_b / scaled_a) == 2, 'CNR signal dB modulation ' \ 'failed' # Convolve the HRF with the stimulus sequence signal = sim.apply_signal(signal_function=signal_function, volume_signal=volume, ) assert signal.shape == (dimensions[0], dimensions[1], dimensions[2], duration / tr_duration), "The output is the " \ "wrong size" signal = sim.apply_signal(signal_function=stimfunction, volume_signal=volume, ) assert np.any(signal == signal_magnitude), "The stimfunction is not binary" # Check that there is an error if the number of signal voxels doesn't # match the number of non zero brain voxels with pytest.raises(IndexError): sig_vox = (volume > 0).sum() vox_pattern = np.tile(stimfunction, (1, sig_vox - 1)) sim.apply_signal(signal_function=vox_pattern, volume_signal=volume, )
def test_apply_signal(): dimensions = np.array([10, 10, 10]) # What is the size of the brain feature_size = [2] feature_type = ['cube'] feature_coordinates = np.array([[5, 5, 5]]) signal_magnitude = [30] # Generate a volume representing the location and quality of the signal volume = sim.generate_signal( dimensions=dimensions, feature_coordinates=feature_coordinates, feature_type=feature_type, feature_size=feature_size, signal_magnitude=signal_magnitude, ) # Inputs for generate_stimfunction onsets = [10, 30, 50, 70, 90] event_durations = [6] tr_duration = 2 duration = 100 # Create the time course for the signal to be generated stimfunction = sim.generate_stimfunction( onsets=onsets, event_durations=event_durations, total_time=duration, ) signal_function = sim.convolve_hrf( stimfunction=stimfunction, tr_duration=tr_duration, ) # Check that you can compute signal change appropriately # Preset a bunch of things stimfunction_tr = stimfunction[::int(tr_duration * 100)] mask, template = sim.mask_brain(dimensions, mask_self=False) noise_dict = sim._noise_dict_update({}) noise = sim.generate_noise(dimensions=dimensions, stimfunction_tr=stimfunction_tr, tr_duration=tr_duration, template=template, mask=mask, noise_dict=noise_dict, iterations=[0, 0]) coords = feature_coordinates[0] noise_function_a = noise[coords[0], coords[1], coords[2], :] noise_function_a = noise_function_a.reshape(duration // tr_duration, 1) noise_function_b = noise[coords[0] + 1, coords[1], coords[2], :] noise_function_b = noise_function_b.reshape(duration // tr_duration, 1) # Create the calibrated signal with PSC method = 'PSC' sig_a = sim.compute_signal_change( signal_function, noise_function_a, noise_dict, [0.5], method, ) sig_b = sim.compute_signal_change( signal_function, noise_function_a, noise_dict, [1.0], method, ) assert sig_b.max() / sig_a.max() == 2, 'PSC modulation failed' # Create the calibrated signal with SFNR method = 'SFNR' sig_a = sim.compute_signal_change( signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = sig_a / (noise_function_a.mean() / noise_dict['sfnr']) sig_b = sim.compute_signal_change( signal_function, noise_function_b, noise_dict, [1.0], method, ) scaled_b = sig_b / (noise_function_b.mean() / noise_dict['sfnr']) assert scaled_b.max() / scaled_a.max() == 2, 'SFNR modulation failed' # Create the calibrated signal with CNR_Amp/Noise-SD method = 'CNR_Amp/Noise-SD' sig_a = sim.compute_signal_change( signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = sig_a / noise_function_a.std() sig_b = sim.compute_signal_change( signal_function, noise_function_b, noise_dict, [1.0], method, ) scaled_b = sig_b / noise_function_b.std() assert scaled_b.max() / scaled_a.max() == 2, 'CNR_Amp modulation failed' # Create the calibrated signal with CNR_Amp/Noise-Var_dB method = 'CNR_Amp2/Noise-Var_dB' sig_a = sim.compute_signal_change( signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = np.log(sig_a.max() / noise_function_a.std()) sig_b = sim.compute_signal_change( signal_function, noise_function_b, noise_dict, [1.0], method, ) scaled_b = np.log(sig_b.max() / noise_function_b.std()) assert np.round(scaled_b / scaled_a) == 2, 'CNR_Amp dB modulation failed' # Create the calibrated signal with CNR_Signal-SD/Noise-SD method = 'CNR_Signal-SD/Noise-SD' sig_a = sim.compute_signal_change( signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = sig_a.std() / noise_function_a.std() sig_b = sim.compute_signal_change( signal_function, noise_function_a, noise_dict, [1.0], method, ) scaled_b = sig_b.std() / noise_function_a.std() assert (scaled_b / scaled_a) == 2, 'CNR signal modulation failed' # Create the calibrated signal with CNR_Amp/Noise-Var_dB method = 'CNR_Signal-Var/Noise-Var_dB' sig_a = sim.compute_signal_change( signal_function, noise_function_a, noise_dict, [0.5], method, ) scaled_a = np.log(sig_a.std() / noise_function_a.std()) sig_b = sim.compute_signal_change( signal_function, noise_function_b, noise_dict, [1.0], method, ) scaled_b = np.log(sig_b.std() / noise_function_b.std()) assert np.round(scaled_b / scaled_a) == 2, 'CNR signal dB modulation ' \ 'failed' # Convolve the HRF with the stimulus sequence signal = sim.apply_signal( signal_function=signal_function, volume_signal=volume, ) assert signal.shape == (dimensions[0], dimensions[1], dimensions[2], duration / tr_duration), "The output is the " \ "wrong size" signal = sim.apply_signal( signal_function=stimfunction, volume_signal=volume, ) assert np.any(signal == signal_magnitude), "The stimfunction is not binary" # Check that there is an error if the number of signal voxels doesn't # match the number of non zero brain voxels with pytest.raises(IndexError): sig_vox = (volume > 0).sum() vox_pattern = np.tile(stimfunction, (1, sig_vox - 1)) sim.apply_signal( signal_function=vox_pattern, volume_signal=volume, )
def _generate_ROIs(ROI_file, stimfunc, noise, scale_percentage, data_dict): """Make signal activity for an ROI of data Creates the specified evoked response time course, calibrated to the expected signal change, for a given ROI Parameters ---------- ROI_file : str Path to the file of the ROI being loaded in stimfunc : 1 dimensional array Time course of evoked response. Output from fmrisim.generate_stimfunction noise : 4 dimensional array Volume of noise generated from fmrisim.generate_noise. Although this is needed as an input, this is only so that the percent signal change can be calibrated. This is not combined with the signal generated. scale_percentage : float What is the percent signal change for the evoked response data_dict : dict A dictionary to specify the parameters used for making data, specifying the following keys numTRs - int - Specify the number of time points multivariate_patterns - bool - Is the difference between conditions univariate (0) or multivariate (1) different_ROIs - bool - Are there different ROIs for each condition ( 1) or is it in the same ROI (0). If it is the same ROI and you are using univariate differences, the second condition will have a smaller evoked response than the other. event_duration - int - How long, in seconds, is each event scale_percentage - float - What is the percent signal change trDuration - float - How many seconds per volume save_dicom - bool - Save to data as a dicom (1) or numpy (0) save_realtime - bool - Do you want to save the data in real time (1) or as fast as possible (0)? isi - float - What is the time between each event (in seconds) burn_in - int - How long before the first event (in seconds) Returns ---------- signal : 4 dimensional array Volume of signal in the specified ROI (noise has not yet been added) """ # Create the signal in the ROI as specified. # Load in the template data (it may already be loaded if doing a test) if isinstance(ROI_file, str): logger.info('Loading', ROI_file) nii = nibabel.load(ROI_file) ROI = nii.get_data() else: ROI = ROI_file # Find all the indices that contain signal idx_list = np.where(ROI == 1) idxs = np.zeros([len(idx_list[0]), 3]) for idx_counter in list(range(0, len(idx_list[0]))): idxs[idx_counter, 0] = int(idx_list[0][idx_counter]) idxs[idx_counter, 1] = int(idx_list[1][idx_counter]) idxs[idx_counter, 2] = int(idx_list[2][idx_counter]) idxs = idxs.astype('int8') # How many voxels per ROI voxels = int(ROI.sum()) # Create a pattern of activity across the two voxels if data_dict['multivariate_pattern'] is True: pattern = np.random.rand(voxels).reshape((voxels, 1)) else: # Just make a univariate increase pattern = np.tile(1, voxels).reshape((voxels, 1)) # Multiply each pattern by each voxel time course weights = np.tile(stimfunc, voxels) * pattern.T # Convolve the onsets with the HRF temporal_res = 1 / data_dict['trDuration'] signal_func = sim.convolve_hrf( stimfunction=weights, tr_duration=data_dict['trDuration'], temporal_resolution=temporal_res, scale_function=1, ) # Change the type of noise noise = noise.astype('double') # Create a noise function (same voxels for signal function as for noise) noise_function = noise[idxs[:, 0], idxs[:, 1], idxs[:, 2], :].T # Compute the signal magnitude for the data sf_scaled = sim.compute_signal_change( signal_function=signal_func, noise_function=noise_function, noise_dict=data_dict['noise_dict'], magnitude=[scale_percentage], method='PSC', ) # Combine the signal time course with the signal volume signal = sim.apply_signal( sf_scaled, ROI, ) # Return signal needed return signal