Skip to content

Commit 5279acf

Browse files
committed
added option to provide mask for selecting subset of voxels
1 parent 198247b commit 5279acf

File tree

1 file changed

+73
-60
lines changed

1 file changed

+73
-60
lines changed

code/pRF.m

Lines changed: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636
% - h_stimulus: height of stimulus images in pixels
3737
%
3838
% optional inputs are
39-
% - hrf : either a column vector containing a single hemodynamic
39+
% - hrf : either a column vector containing a single hemodynamic
4040
% response used for every voxel;
4141
% or a matrix with a unique hemodynamic response along
4242
% its columns for each voxel.
43-
% By default the canonical two-gamma hemodynamic response
43+
% By default the canonical two-gamma hemodynamic response
4444
% function is generated internally based on the scan parameters.
4545
%
4646
% this class has the following functions
@@ -54,7 +54,7 @@
5454
% - pRF.create_timecourses();
5555
% - results = pRF.mapping(data);
5656
%
57-
% use help pRF.function to get more detailed help on any specific function
57+
% use help pRF.function to get more detailed help on any specific function
5858
% (e.g. help pRF.mapping)
5959
%
6060
% typical workflow:
@@ -135,9 +135,9 @@
135135

136136
function hrf = get_hrf(self)
137137
% returns the hemodynamic response used by the class.
138-
% If a single hrf is used for every voxel, this function
138+
% If a single hrf is used for every voxel, this function
139139
% returns a column vector.
140-
% If a unique hrf is used for each voxel, this function returns
140+
% If a unique hrf is used for each voxel, this function returns
141141
% a matrix with columns corresponding to time and the remaining
142142
% dimensions reflecting the spatial dimensions of the data.
143143
if size(self.hrf,2)>1
@@ -149,17 +149,17 @@
149149
end
150150

151151
function stimulus = get_stimulus(self)
152-
% returns the stimulus used by the class as a 3D matrix of
152+
% returns the stimulus used by the class as a 3D matrix of
153153
% dimensions height-by-width-by-time.
154154
stimulus = reshape(self.stimulus,self.h_stimulus,...
155155
self.w_stimulus,self.n_samples);
156156
end
157157

158158
function tc = get_timecourses(self)
159-
% returns the timecourses predicted based on the effective
160-
% stimulus and each combination of pRF model parameters as a
159+
% returns the timecourses predicted based on the effective
160+
% stimulus and each combination of pRF model parameters as a
161161
% time-by-combinations matrix.
162-
% Note that the predicted timecourses have not been convolved
162+
% Note that the predicted timecourses have not been convolved
163163
% with a hemodynamic response.
164164
tc = ifft(self.tc_fft);
165165
end
@@ -173,14 +173,14 @@ function set_hrf(self,hrf)
173173
self.hrf = reshape(hrf,self.l_hrf,self.n_total);
174174
else
175175
self.hrf = hrf;
176-
end
176+
end
177177
end
178178

179179
function set_stimulus(self,stimulus)
180180
% provide a stimulus matrix.
181-
% This is useful if the .png files have already been imported
181+
% This is useful if the .png files have already been imported
182182
% and stored in matrix form.
183-
% Note that the provided stimulus matrix can be either 3D
183+
% Note that the provided stimulus matrix can be either 3D
184184
% (height-by-width-by-time) or 2D (height*width-by-time).
185185
if ndims(stimulus)==3
186186
self.stimulus = reshape(stimulus,...
@@ -192,9 +192,9 @@ function set_stimulus(self,stimulus)
192192
end
193193

194194
function import_stimulus(self)
195-
% imports the series of .png files constituting the stimulation
195+
% imports the series of .png files constituting the stimulation
196196
% protocol of the pRF experiment.
197-
% This series is stored internally in matrix format
197+
% This series is stored internally in matrix format
198198
% (height-by-width-by-time).
199199
% The stimulus is required to generate timecourses.
200200

@@ -232,9 +232,9 @@ function import_stimulus(self)
232232
end
233233

234234
function create_timecourses(self,varargin)
235-
% creates predicted timecourses based on the effective stimulus
236-
% and a range of isotropic receptive fields.
237-
% Isotropic receptive fields are generated for a grid of
235+
% creates predicted timecourses based on the effective stimulus
236+
% and a range of isotropic receptive fields.
237+
% Isotropic receptive fields are generated for a grid of
238238
% location (x,y) and size parameters.
239239
%
240240
% optional inputs are
@@ -276,8 +276,8 @@ function create_timecourses(self,varargin)
276276
mod(floor(i/(n_slope)),n_xy)+1,...
277277
mod(i,n_slope)+1];
278278

279-
n_lower = ceil(n_xy/2);
280-
n_upper = floor(n_xy/2);
279+
n_lower = ceil(n_xy/2);
280+
n_upper = floor(n_xy/2);
281281
self.ecc = exp([linspace(log(max_r),log(.1),n_lower),...
282282
linspace(log(.1),log(max_r),n_upper)]);
283283
self.pa = linspace(0,(n_xy-1)/n_xy*2*pi,n_xy);
@@ -292,7 +292,7 @@ function create_timecourses(self,varargin)
292292
W(j,:) = self.gauss(x,y,sigma,X_,Y_)';
293293
waitbar(j/self.n_points*.9,wb);
294294
end
295-
295+
296296
tc = W * self.stimulus;
297297
waitbar(1,wb);
298298
self.tc_fft = fft(tc');
@@ -310,7 +310,7 @@ function create_timecourses(self,varargin)
310310
% - Eccentricity
311311
% - Polar_Angle
312312
%
313-
% the dimension of each field corresponds to the dimensions
313+
% the dimension of each field corresponds to the dimensions
314314
% of the data.
315315
%
316316
% required inputs are
@@ -327,75 +327,88 @@ function create_timecourses(self,varargin)
327327
p = inputParser;
328328
addRequired(p,'data',@isnumeric);
329329
addOptional(p,'threshold',100);
330+
addOptional(p,'mask',[]);
330331
p.parse(data,varargin{:});
331332

332333
data = p.Results.data;
333334
threshold = p.Results.threshold;
335+
mask = p.Results.mask;
334336

335337
data = reshape(data(1:self.n_samples,:,:,:),...
336338
self.n_samples,self.n_total);
337339
mean_signal = mean(data);
338340
data = zscore(data);
339-
mag_d = sqrt(sum(data.^2));
341+
342+
if isempty(mask)
343+
mask = mean_signal>=threshold;
344+
end
345+
mask = mask(:);
346+
voxel_index = find(mask);
347+
n_voxels = numel(voxel_index);
348+
349+
mag_d = sqrt(sum(data(:,mask).^2));
340350

341351
results.corr_fit = zeros(self.n_total,1);
342352
results.mu_x = zeros(self.n_total,1);
343353
results.mu_y = zeros(self.n_total,1);
344354
results.sigma = zeros(self.n_total,1);
345355

356+
346357
if size(self.hrf,2)==1
347358
hrf_fft = fft(repmat([self.hrf;...
348359
zeros(self.n_samples-self.l_hrf,1)],...
349360
[1,self.n_points]));
350361
tc = zscore(ifft(self.tc_fft.*hrf_fft))';
351362
mag_tc = sqrt(sum(tc.^2,2));
352-
for v=1:self.n_total
353-
if mean_signal(v)>threshold
354-
CS = (tc*data(:,v))./...
355-
(mag_tc*mag_d(v));
356-
id = isinf(CS) | isnan(CS);
357-
CS(id) = 0;
358-
[results.corr_fit(v),j] = max(CS);
359-
results.mu_x(v) = cos(self.pa(self.idx(j,1))) * ...
360-
self.ecc(self.idx(j,2));
361-
results.mu_y(v) = sin(self.pa(self.idx(j,1))) * ...
362-
self.ecc(self.idx(j,2));
363-
results.sigma(v) = self.ecc(self.idx(j,2)) * ...
364-
self.slope(self.idx(j,3));
365-
end
366-
waitbar(v/self.n_total,wb)
363+
for m=1:n_voxels
364+
v = voxel_index(m);
365+
366+
CS = (tc*data(:,v))./...
367+
(mag_tc*mag_d(m));
368+
id = isinf(CS) | isnan(CS);
369+
CS(id) = 0;
370+
[results.corr_fit(v),j] = max(CS);
371+
results.mu_x(v) = cos(self.pa(self.idx(j,1))) * ...
372+
self.ecc(self.idx(j,2));
373+
results.mu_y(v) = sin(self.pa(self.idx(j,1))) * ...
374+
self.ecc(self.idx(j,2));
375+
results.sigma(v) = self.ecc(self.idx(j,2)) * ...
376+
self.slope(self.idx(j,3));
377+
378+
waitbar(v/n_voxels,wb)
367379
end
368380
else
369381
hrf_fft_all = fft([self.hrf;...
370-
zeros(self.n_samples-self.l_hrf,self.n_total)]);
371-
for v=1:self.n_total
372-
if mean_signal(v)>threshold
373-
hrf_fft = repmat(hrf_fft_all(:,v),...
374-
[1,self.n_points]);
375-
tc = zscore(ifft(self.tc_fft.*hrf_fft))';
376-
mag_tc = sqrt(sum(tc.^2,2));
377-
378-
CS = (tc*data(:,v))./...
379-
(mag_tc*mag_d(v));
380-
id = isinf(CS) | isnan(CS);
381-
CS(id) = 0;
382-
[results.corr_fit(v),j] = max(CS);
383-
results.mu_x(v) = cos(self.pa(self.idx(j,1))) * ...
384-
self.ecc(self.idx(j,2));
385-
results.mu_y(v) = sin(self.pa(self.idx(j,1))) * ...
386-
self.ecc(self.idx(j,2));
387-
results.sigma(v) = self.ecc(self.idx(j,2)) * ...
388-
self.slope(self.idx(j,3));
389-
end
390-
waitbar(v/self.n_total,wb)
382+
zeros(self.n_samples-self.l_hrf,self.n_total)]);
383+
for m=1:n_voxels
384+
v = voxel_index(m);
385+
386+
hrf_fft = repmat(hrf_fft_all(:,v),...
387+
[1,self.n_points]);
388+
tc = zscore(ifft(self.tc_fft.*hrf_fft))';
389+
mag_tc = sqrt(sum(tc.^2,2));
390+
391+
CS = (tc*data(:,v))./...
392+
(mag_tc*mag_d(m));
393+
id = isinf(CS) | isnan(CS);
394+
CS(id) = 0;
395+
[results.corr_fit(v),j] = max(CS);
396+
results.mu_x(v) = cos(self.pa(self.idx(j,1))) * ...
397+
self.ecc(self.idx(j,2));
398+
results.mu_y(v) = sin(self.pa(self.idx(j,1))) * ...
399+
self.ecc(self.idx(j,2));
400+
results.sigma(v) = self.ecc(self.idx(j,2)) * ...
401+
self.slope(self.idx(j,3));
402+
403+
waitbar(v/n_voxels,wb)
391404
end
392405
end
393406
results.corr_fit = reshape(results.corr_fit,self.n_rows,self.n_cols,self.n_slices);
394407
results.mu_x = reshape(results.mu_x,self.n_rows,self.n_cols,self.n_slices);
395408
results.mu_y = reshape(results.mu_y,self.n_rows,self.n_cols,self.n_slices);
396409
results.sigma = reshape(results.sigma,self.n_rows,self.n_cols,self.n_slices);
397-
results.Eccentricity = abs(results.mu_x+results.mu_y*1i);
398-
results.Polar_Angle = angle(results.mu_x+results.mu_y*1i);
410+
results.eccentricity = abs(results.mu_x+results.mu_y*1i);
411+
results.polar_angle = angle(results.mu_x+results.mu_y*1i);
399412
close(wb)
400413
fprintf('done\n');
401414
end

0 commit comments

Comments
 (0)