def make_model(stimulus): # these define min and max of the edge of the initial brute-force search. x_grid = (-8, 8) y_grid = (-8, 8) s_grid = (1 / stimulus.ppd + 0.25, 5.25) h_grid = (-1.0, 1.0) # these define the boundaries of the final gradient-descent search. x_bound = (-10.0, 10.0) y_bound = (-10.0, 10.0) s_bound = (1 / stimulus.ppd, 12.0) # smallest sigma is a pixel b_bound = (1e-8, None) u_bound = (None, None) h_bound = (-3.0, 3.0) GRIDS = (x_grid, y_grid, s_grid, h_grid) BOUNDS = ( x_bound, y_bound, s_bound, h_bound, b_bound, u_bound, ) model = og.GaussianModel(stimulus, utils.double_gamma_hrf) return model, GRIDS, BOUNDS
def fit_voxel(tup): (ii, vx, js) = tup stdat = js['Stimulus'] if pimms.is_list(stdat): stdat = stdat[0] height = stdat['fieldofviewVert'] width = stdat['fieldofviewHorz'] ### STIMULUS # First get a viewing distance and screen size dist = 100 # 100 cm is arbitrary stim_width = 2 * dist * np.tan(np.pi/180 * width/2) stimulus = VisualStimulus(stim, dist, stim_width, 1.0, float(js['TR']), ctypes.c_int16) if fixed_hrf is not False: model = og_nohrf.GaussianModel(stimulus, utils.double_gamma_hrf) model.hrf_delay = fixed_hrf else: model = og.GaussianModel(stimulus, utils.double_gamma_hrf) ### FIT ## define search grids # these define min and max of the edge of the initial brute-force search. x_grid = (-width/2,width/2) y_grid = (-height/2,height/2) s_grid = (1/stimulus.ppd + 0.25, 5.25) h_grid = (-1.0, 1.0) ## define search bounds # these define the boundaries of the final gradient-descent search. x_bound = (-width, width) y_bound = (-height, height) s_bound = (1/stimulus.ppd, 12.0) # smallest sigma is a pixel b_bound = (1e-8,None) u_bound = (None,None) h_bound = (-3.0,3.0) ## package the grids and bounds if fixed_hrf is not False: grids = (x_grid, y_grid, s_grid) bounds = (x_bound, y_bound, s_bound, b_bound, u_bound,) else: grids = (x_grid, y_grid, s_grid, h_grid) bounds = (x_bound, y_bound, s_bound, h_bound, b_bound, u_bound,) ## fit the response # auto_fit = True fits the model on assignment # verbose = 0 is silent # verbose = 1 is a single print # verbose = 2 is very verbose if fixed_hrf is not False: fit = og_nohrf.GaussianFit(model, vx, grids, bounds, Ns=Ns, voxel_index=(ii, 1, 1), auto_fit=True, verbose=2) else: fit = og.GaussianFit(model, vx, grids, bounds, Ns=Ns, voxel_index=(ii, 1, 1), auto_fit=True, verbose=2) return (ii, vx) + tuple(fit.overloaded_estimate) + (fit.prediction,)
def solver(params): ''' solver(params) accepts a parameter dictionary params and solves the pRF models implied by them. The return value of solver is a dictionary of pRF parameters or statistics; e.g., a dictionary might contain keys 'r2', 'sigma', 'x', and 'y'. ''' tr = params['TR_length'] # go ahead and make the stimulus: stimulus_array = params['stimulus'] stimulus_array = np.round(stimulus_array / np.max(stimulus_array)).astype('short') # if the params tell us to invert y, we should do that now: if params.get('invert_y', False): stimulus_array = np.flip(stimulus_array, axis=0) # figure out screen width from assumed distance of 50 and d2p: stimpx = np.min(stimulus_array.shape[:2]) stimdeg = stimpx / params['pixels_per_degree'] stimcm = 2 * 50 * np.tan(stimdeg / 2 * np.pi / 180) stimulus = popeye.visual_stimulus.VisualStimulus(stimulus_array, 50, stimcm, 0.5, tr, 'short') # A few other optional parameters: grid_n = params.get('grid_n', 5) minmax_x = params.get('range_x', (-stimdeg, stimdeg)) minmax_y = params.get('range_y', (-stimdeg, stimdeg)) minmax_s = params.get('range_sigma', (0.05, stimdeg * 0.75)) minmax_h = params.get('range_hrf', (-6, 6)) grids = [minmax_x, minmax_y, minmax_s, minmax_h] # Get the data and mask ready... data = params['data'] mask = params.get('mask', None) # okay, now go ahead and solve: # note that changes/edits to the HRF should go here: hrf = popeye.utilities.double_gamma_hrf model = og_hrf.GaussianModel(stimulus, hrf) model.hrf_delay = -0.25 args = [(og_hrf.GaussianFit, model, data[i, j, k], grids, grids, (i, j, k), grid_n, True, False) for i in np.arange(data.shape[0]) for j in np.arange(data.shape[1]) for k in np.arange(data.shape[2]) if (mask is None or mask[i, j, k])] # model = og.GaussianModel(stimulus, hrf) # model.hrf_delay = -0.25 # args = [(og.GaussianFit, model, data[i,j,k], grids, grids, (i,j,k), grid_n, True, False) # for i in np.arange(data.shape[0]) # for j in np.arange(data.shape[1]) # for k in np.arange(data.shape[2]) # if (mask is None or mask[i,j,k])] pool = mp.Pool() fits = pool.map(_dofit, args) pool.close() pool.join() # okay, reconstruct these into results volumes # res = {k:np.full(data.shape[:-1], np.nan) res = { k: np.full(data.shape, np.nan) for k in ['x', 'y', 'sigma', 'baseline', 'gain', 'hrf_delay', 'modelpred'] } for (arg, fit) in zip(args, fits): ijk = arg[5] for (k, v) in zip( ['x', 'y', 'sigma', 'baseline', 'gain', 'hrf_delay', 'modelpred'], [ fit.x, fit.y, fit.sigma, fit.baseline, fit.beta, fit.hrf_delay, fit.prediction ]): res[k][ijk] = v return res
def test_og_fit(): # stimulus features viewing_distance = 38 screen_width = 25 thetas = np.arange(0, 360, 90) thetas = np.insert(thetas, 0, -1) thetas = np.append(thetas, -1) num_blank_steps = 30 num_bar_steps = 30 ecc = 12 tr_length = 1.0 frames_per_tr = 1.0 scale_factor = 1.0 pixels_across = 100 pixels_down = 100 dtype = ctypes.c_int16 # create the sweeping bar stimulus in memory bar = simulate_bar_stimulus(pixels_across, pixels_down, viewing_distance, screen_width, thetas, num_bar_steps, num_blank_steps, ecc) # create an instance of the Stimulus class stimulus = VisualStimulus(bar, viewing_distance, screen_width, scale_factor, tr_length, dtype) # initialize the gaussian model model = og.GaussianModel(stimulus, utils.spm_hrf) model.hrf_delay = 0 model.mask_size = 5 # generate a random pRF estimate x = -5.24 y = 2.58 sigma = 1.24 hrf_delay = 0.66 beta = 2.5 baseline = -0.25 # create the "data" data = model.generate_prediction(x, y, sigma, hrf_delay, beta, baseline) # set search grid x_grid = (-10, 10) y_grid = (-10, 10) s_grid = (0.25, 5.25) h_grid = (-1.0, 1.0) # set search bounds x_bound = (-12.0, 12.0) y_bound = (-12.0, 12.0) s_bound = (0.001, 12.0) h_bound = (-1.5, 1.5) b_bound = (1e-8, None) m_bound = (None, None) # loop over each voxel and set up a GaussianFit object grids = ( x_grid, y_grid, s_grid, h_grid, ) bounds = (x_bound, y_bound, s_bound, h_bound, b_bound, m_bound) # fit the response fit = og.GaussianFit(model, data, grids, bounds, Ns=5) # coarse fit npt.assert_almost_equal(fit.x0, -5.0) npt.assert_almost_equal(fit.y0, 5.0) npt.assert_almost_equal(fit.s0, 2.75) npt.assert_almost_equal(fit.hrf0, 0.5) # the baseline/beta should be 0/1 when regressed data vs. estimate (m, b) = np.polyfit(fit.scaled_ballpark_prediction, data, 1) npt.assert_almost_equal(m, 1.0) npt.assert_almost_equal(b, 0.0) # assert equivalence npt.assert_almost_equal(fit.x, x) npt.assert_almost_equal(fit.y, y) npt.assert_almost_equal(fit.hrf_delay, hrf_delay) npt.assert_almost_equal(fit.sigma, sigma) npt.assert_almost_equal(fit.beta, beta) # test receptive field rf = generate_og_receptive_field(x, y, sigma, fit.model.stimulus.deg_x, fit.model.stimulus.deg_y) rf /= (2 * np.pi * sigma**2) * 1 / np.diff(model.stimulus.deg_x[0, 0:2])**2 npt.assert_almost_equal(np.round(rf.sum()), np.round(fit.receptive_field.sum())) # test model == fit RF npt.assert_almost_equal( np.round(fit.model.generate_receptive_field(x, y, sigma).sum()), np.round(fit.receptive_field.sum()))
) fields = ('theta', 'rho', 'sigma', 'hrfdelay', 'beta', 'baseline') res = {k: [] for k in fields} res['pred'] = [] for (ii, vx, js) in zip(range(len(bold)), bold, stim_json): stdat = js['Stimulus'] if pimms.is_list(stdat): stdat = stdat[0] height = stdat['fieldofviewVert'] width = stdat['fieldofviewHorz'] ### STIMULUS # First get a viewing distance and screen size dist = 100 # 100 cm is arbitrary stim_width = 2 * dist * np.tan(np.pi / 180 * width / 2) stimulus = VisualStimulus(stim, dist, stim_width, 1.0, float(js['TR']), ctypes.c_int16) model = og.GaussianModel(stimulus, utils.double_gamma_hrf) ### FIT ## define search grids # these define min and max of the edge of the initial brute-force search. x_grid = (-width / 2, width / 2) y_grid = (-height / 2, height / 2) s_grid = (1 / stimulus.ppd + 0.25, 5.25) h_grid = (-1.0, 1.0) ## define search bounds # these define the boundaries of the final gradient-descent search. x_bound = (-width, width) y_bound = (-height, height) s_bound = (1 / stimulus.ppd, 12.0) # smallest sigma is a pixel b_bound = (1e-8, None) u_bound = (None, None) h_bound = (-3.0, 3.0)
# Stimulus related (we passed it as a nifti, to avoid sending matlab specific things) nii2 = ny.load('~/toolboxes/PRFmodel/data/examples/Exp-103_binary-true_size-20x20.nii.gz', to='image') stim = np.squeeze(nii2.dataobj) # stim = np.roll(stim, -5, axis=2) stimulus = popeye.visual_stimulus.VisualStimulus(stim.astype('int16'), 30, 10.57962, 0.5, 2, 'int16') # %% markdown # #Fit the Models # ## Gaussian Model # % import popeye.og_hrf as og_hrf import popeye.og as og hrf = popeye.utilities.double_gamma_hrf model = og_hrf.GaussianModel(stimulus, hrf) # model.hrf_delay = -0.25 # RSQarray = [] # for ii in np.array([-0.5,-0.25, 1,0.25, 0.5]): model.hrf_delay = -0.25 fit = og_hrf.GaussianFit(model, input_data, ((-10, 10), (-10, 10), (0.25, 6.25), (-3.0, 3.0)), ((-10, 10), (-10, 10), (0.10, 12.0), (-6.0, 6.0)), auto_fit=True, Ns=5, verbose=1) # RSQarray.append((ii,fit.RSQ)) # % # find and show the parameters (x, y, sigma, n, beta, baseline) sol = fit.overloaded_estimate prediction = fit.prediction