diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..f2c1332f Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index c8de7899..d9282a28 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,3 @@ lib *.so *.mexw64 *.asv -examples/ diff --git a/PhysIO/.gitmodules b/.gitmodules similarity index 81% rename from PhysIO/.gitmodules rename to .gitmodules index e87034b3..daea895e 100644 --- a/PhysIO/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "wikidocs"] - path = wikidocs + path = PhysIO/wikidocs url = git@tnurepository.ethz.ch:physio/physio-public.wiki.git branch = master diff --git a/CHANGELOG.md b/CHANGELOG.md index 205bfa90..c73983ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,35 @@ # Changelog TAPAS toolbox +## [3.1.0] 2019-03-26 + +### Added +- Get revision info from Matlab. +- PhysIO R2018.1.2: BioPac txt-file reader (for single file, resp/cardiac/trigger data in different columns) +- SERIA: Automatic plotting of the seria model. +- SERIA: Example for single subject. + +### Fixed +- Huge: minor bugs. + +### Changed +- Huge: Improved documentation. +- New version of the HGF toolbox (v5.3). Details in tapas/HGF/README.md +- New version of the rDCM toolbox (v1.1). Details in tapas/rDCM/CHANGELOG.md. +- New version of the PhysIO Toolbox (R2019a-v7.1.0) + - BIDS and BioPac readers; code sorted in modules (`readin`, `preproc` etc.), + also reflected in figure names + - Updated and extended all examples, and introduced unit testing + - Full details in tapas/PhysIO/CHANGELOG.md +- Improved the documentation of SERIA. + +## [3.0.1] 2018-10-17 + +### Fixed +- PhysIO R2018.1.2: fixed bug for 3D nifti array read-in in tapas_physio_create_noise_rois_regressors (github issue #24, gitlab #52) + +### Added + ## [3.0.0] 2018-09-09 ### Added diff --git a/Contributor-License-Agreement.md b/Contributor-License-Agreement.md index f9864999..b8da061f 100644 --- a/Contributor-License-Agreement.md +++ b/Contributor-License-Agreement.md @@ -11,11 +11,13 @@ and each GitHub User listed in the following table: Name | Company/Institution/Address | City | Country | E-Mail/GitHub Username ------------------------- | ----------------------------| --------- | ------- | ---------------------- +------------------------ | --------------------------- | --------- | ------- | ---------------------- Lars Kasper | TNU, University of Zurich | Zurich | CH | mrikasper Eduardo Aponte | TNU, University of Zurich | Zurich | CH | tnutapas - | | | | -**-> Add Entry here <-** | | | | +Daniel Hoffmann Ayala | Technical University | Munich | D | DanielHoffmannAyala +Benoît Béranger | CENIR, ICM | Paris | FR | benoitberanger +**- Add Entry here -** | **- Add Entry here -** | **Add** | **Add** | **Add** + (hereinafter referred to as "Contributor") relating to the Contribution (as defined below) that Contributor makes to the following Project: diff --git a/HGF/README.md b/HGF/README.md index e5f30743..8f323f8e 100644 --- a/HGF/README.md +++ b/HGF/README.md @@ -3,7 +3,7 @@ Release ID: $Format:%h %d$ --- -Copyright (C) 2012-2018 Christoph Mathys +Copyright (C) 2012-2019 Christoph Mathys Translational Neuromodeling Unit (TNU) University of Zurich and ETH Zurich @@ -46,6 +46,16 @@ hgf_demo.pdf. ## Release notes +### v5.3 +- Enabled setting and storing of seed for random number generator in simulations +- Debugged reading of response model configuration in simModel +- Reduced default maxStep from 2 to 1 in quasinewton_oqptim_config +- Improved readability of siem files for unitsq_sgm and softmax_binary +- Added simulation capability for softmax_wld and softmax_mu3_wld +- Added softmax_wld response model +- Improved readability of softmax_mu3_wld code +- Improved readability of softmax and softmax_mu3 code + ### v5.2 - Brought hgf_demo.pdf up to date - Added gaussian_obs_offset response model diff --git a/HGF/hgf_demo.m b/HGF/hgf_demo.m index 970e1049..16000d2a 100644 --- a/HGF/hgf_demo.m +++ b/HGF/hgf_demo.m @@ -6,7 +6,7 @@ %% % The inputs are simply a time series of 320 0s and 1s. This is the input % sequence used in the task of Iglesias et al. (2013), _Neuron_, *80*(2), 519-530. -%% + scrsz = get(0,'ScreenSize'); outerpos = [0.2*scrsz(3),0.7*scrsz(4),0.8*scrsz(3),0.3*scrsz(4)]; figure('OuterPosition', outerpos) @@ -31,17 +31,18 @@ % % * The first argument, which would normally be the observed responses, is empty % (ie, []) here because the optimal parameter values are independent of any responses. -% * The second argument is the perceptual model, _hgf_binary_ here. We need -% to use the prefix 'tapas_' and the suffix '_config' in order to find the correct +% * The second argument is the inputs _u._ +% * The third argument is the perceptual model, _hgf_binary_ here. We need to +% use the prefix 'tapas_' and the suffix '_config' in order to find the correct % configuration file -% * The third argument is the response model, _bayes_optimal_binary_ here. Again -% we need to use the same prefix and suffix. In fact, bayes_optimal_binary is -% a kind of pseudo-response model because instead of providing response probabilities +% * The fourth argument is the response model, _bayes_optimal_binary_ here. +% Again we need to use the same prefix and suffix. In fact, bayes_optimal_binary +% is a kind of pseudo-response model because instead of providing response probabilities % it simply calculates the Shannon surprise elicited by each new input given the % current perceptual state. -% * The fourth argument is the optimization algorithm to be used, _quasinewton_optim_ +% * The fifth argument is the optimization algorithm to be used, _quasinewton_optim_ % here, which is a variant of the Broyden-Fletcher-Goldfarb-Shanno (BFGS) algorithm. -%% + bopars = tapas_fitModel([],... u,... 'tapas_hgf_binary_config',... @@ -74,13 +75,15 @@ % that, we simply choose values for $\omega$. Here, we take $\omega_2=-2.5$ and % $\omega_3=-6$. But in addition to the perceptual model _hgf_binary_, we now % need a response model. Here, we take the unit square sigmoid model, _unitsq_sgm_, -% with parameter $\zeta=5$. -%% +% with parameter $\zeta=5$. The last argument is an optional seed for the random +% number generator. + sim = tapas_simModel(u,... 'tapas_hgf_binary',... [NaN 0 1 NaN 1 1 NaN 0 0 1 1 NaN -2.5 -6],... 'tapas_unitsq_sgm',... - 5); + 5,... + 12345); %% % The general meaning of the arguments supplied to simModel is explained % in the manual and in the file _tapas_simModel.m_. The specific meaning of each @@ -89,12 +92,12 @@ %% Plot simulated responses % We can plot our simulated responses $y$ using the plotting function for _hgf_binary_ % models. -%% + tapas_hgf_binary_plotTraj(sim) %% Recover parameter values from simulated responses % We can now try to recover the parameters we put into the simulation ($\omega_2=-2.5$ % and $\omega_3=-6$) using fitModel. -%% + est = tapas_fitModel(sim.y,... sim.u,... 'tapas_hgf_binary_config',... @@ -121,7 +124,7 @@ % -1). In these cases the two parameters cannot be identified independently and % one of them needs to be fixed. The other parameter can then be estimated conditional % on the value of the one that has been fixed. -%% + tapas_fit_plotCorr(est) %% % In this case, there is nothing to worry about. Unless their correlation @@ -129,7 +132,7 @@ % describe distinct aspects of the data. % % The posterior parameter correlation matrix is stored in est.optim.Corr, -%% + disp(est.optim.Corr) %% % while the posterior parameter covariance matrix is stored in est.optim.Sigma @@ -139,7 +142,7 @@ % The posterior means of the estimated as well as the fixed parameters can be % found in est.p_prc for the perceptual model and in est.p_obs for the observation % model: -%% + disp(est.p_prc) disp(est.p_obs) %% @@ -150,16 +153,16 @@ %% Inferred belief trajectories % As with the simulated trajectories, we can plot the inferred belief trajectories % implied by the estimated parameters. -%% + tapas_hgf_binary_plotTraj(est) %% % These trajectories can be found in est.traj: -%% + disp(est.traj) %% Changing the perceptual model % Next, let's try to fit the same data using a different perceptual model while % keeping the same response model. We will take the Rescorla-Wagner model _rw_binary_. -%% + est1a = tapas_fitModel(sim.y,... sim.u,... 'tapas_rw_binary_config',... @@ -171,7 +174,7 @@ % % Just as for _hgf_binary_, we can plot posterior correlations and inferred % trajectories for _rw_binary_. -%% + tapas_fit_plotCorr(est1a) tapas_rw_binary_plotTraj(est1a) %% Input on a continuous scale @@ -179,12 +182,12 @@ % interesting time series are on a continuous scale. As an example, we'll use % the exchange rate of the US Dollar to the Swiss Franc during much of 2010 and % 2011. -%% + usdchf = load('example_usdchf.txt'); %% % As before, we'll first estimate the Bayes optimal parameter values. This % time, we'll take a 2-level HGF for continuous-scaled inputs. -%% + bopars2 = tapas_fitModel([],... usdchf,... 'tapas_hgf_config',... @@ -192,17 +195,18 @@ 'tapas_quasinewton_optim_config'); %% % And again, let's check the posterior correlation and the trajectories: -%% + tapas_fit_plotCorr(bopars2) tapas_hgf_plotTraj(bopars2) %% % Now, let's simulate an agent and plot the resulting trajectories: -%% + sim2 = tapas_simModel(usdchf,... 'tapas_hgf',... [1.04 1 0.0001 0.1 0 0 1 -13 -2 1e4],... 'tapas_gaussian_obs',... - 0.00002); + 0.00002,... + 12345); tapas_hgf_plotTraj(sim2) %% % Looking at the volatility (ie, the second) level, we see that there are @@ -217,12 +221,13 @@ % shows up as another spike in volatitlity. %% Adding levels % Let's see what happens if we add another level: -%% + sim2a = tapas_simModel(usdchf,... 'tapas_hgf',... [1.04 1 1 0.0001 0.1 0.1 0 0 0 1 1 -13 -2 -2 1e4],... 'tapas_gaussian_obs',... - 0.00005); + 0.00005,... + 12345); tapas_hgf_plotTraj(sim2a) %% % Owing to the presence of the third level, the second level is a lot smoother @@ -234,7 +239,7 @@ % While the third level is very smooth overall, the two salient events discussed % above are still visible. Let's see how these events are reflected in the precision % weighting of the updates at each level: -%% + figure plot(sim2a.traj.wt) xlim([1, length(sim2a.traj.wt)]) @@ -252,7 +257,7 @@ %% Parameter recovery % Now, let's try to recover the parameters we put into the simulation by fitting % the HGF to our simulated responses: -%% + est2 = tapas_fitModel(sim2.y,... usdchf,... 'tapas_hgf_config',... @@ -260,7 +265,7 @@ 'tapas_quasinewton_optim_config'); %% % Again, we fit the posterior correlation and the estimated trajectories: -%% + tapas_fit_plotCorr(est2) tapas_hgf_plotTraj(est2) %% Plotting residual diagnostics @@ -268,7 +273,7 @@ % and actual responses) of a model. If the residual show any obvious patterns, % that's an indication that your model fails to capture aspects of the data that % should in princple be predictable. -%% + tapas_fit_plotResidualDiagnostics(est2) %% % Everything looks fine here - no obvious patterns to be seen. @@ -283,7 +288,7 @@ % % $$r^{(k)} =\frac{y^{(k)} - \hat{\mu}_1^{(k)}}{\sqrt{\hat{\mu}_1^{(k)} \left(1-\hat{\mu}_1^{(k)}\right) % }}$$ -%% + tapas_fit_plotResidualDiagnostics(est) %% % In the case of our binary response example, we see some patterns in the @@ -301,12 +306,13 @@ % % We begin by simulating responses from another fictive agent and estimating % the parameters behind the simulated responses: -%% + sim2b = tapas_simModel(usdchf,... 'tapas_hgf',... [1.04 1 0.0001 0.1 0 0 1 -14.5 -2.5 1e4],... 'tapas_gaussian_obs',... - 0.00002); + 0.00002,... + 12345); tapas_hgf_plotTraj(sim2b) est2b = tapas_fitModel(sim2b.y,... usdchf,... @@ -317,7 +323,7 @@ tapas_hgf_plotTraj(est2b) %% % Now we can take the Bayesian parameter average of our two: -%% + bpa = tapas_bayesian_parameter_average(est2, est2b); tapas_fit_plotCorr(bpa) tapas_hgf_plotTraj(bpa) \ No newline at end of file diff --git a/HGF/hgf_demo.mlx b/HGF/hgf_demo.mlx index e3f9cbd6..1df3b1a9 100644 Binary files a/HGF/hgf_demo.mlx and b/HGF/hgf_demo.mlx differ diff --git a/HGF/hgf_demo.pdf b/HGF/hgf_demo.pdf index 2f025eaa..293ad558 100644 Binary files a/HGF/hgf_demo.pdf and b/HGF/hgf_demo.pdf differ diff --git a/HGF/tapas_beta_obs_sim.m b/HGF/tapas_beta_obs_sim.m index ea07d76a..b7b4f047 100644 --- a/HGF/tapas_beta_obs_sim.m +++ b/HGF/tapas_beta_obs_sim.m @@ -30,7 +30,11 @@ be = nu - al; % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = betarnd(al, be); diff --git a/HGF/tapas_condhalluc_obs2_sim.m b/HGF/tapas_condhalluc_obs2_sim.m index 42307fa4..45d54c7b 100644 --- a/HGF/tapas_condhalluc_obs2_sim.m +++ b/HGF/tapas_condhalluc_obs2_sim.m @@ -27,7 +27,11 @@ prob = tapas_sgm(be.*(2.*x-1),1); % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = binornd(1, prob); diff --git a/HGF/tapas_condhalluc_obs_sim.m b/HGF/tapas_condhalluc_obs_sim.m index 131332a4..ebed024a 100644 --- a/HGF/tapas_condhalluc_obs_sim.m +++ b/HGF/tapas_condhalluc_obs_sim.m @@ -28,7 +28,11 @@ prob = tapas_sgm(be.*(2.*x-1),1); % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = binornd(1, prob); diff --git a/HGF/tapas_gaussian_obs_offset_sim.m b/HGF/tapas_gaussian_obs_offset_sim.m index bd26b430..e1e3fc81 100644 --- a/HGF/tapas_gaussian_obs_offset_sim.m +++ b/HGF/tapas_gaussian_obs_offset_sim.m @@ -20,7 +20,11 @@ n = length(yhat); % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = yhat +sqrt(ze)*randn(n, 1); diff --git a/HGF/tapas_gaussian_obs_sim.m b/HGF/tapas_gaussian_obs_sim.m index 2c0fb195..58c86e88 100644 --- a/HGF/tapas_gaussian_obs_sim.m +++ b/HGF/tapas_gaussian_obs_sim.m @@ -19,7 +19,11 @@ n = length(muhat); % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = muhat +sqrt(ze)*randn(n, 1); diff --git a/HGF/tapas_logrt_linear_binary_sim.m b/HGF/tapas_logrt_linear_binary_sim.m index 27f17e0a..52396ab4 100644 --- a/HGF/tapas_logrt_linear_binary_sim.m +++ b/HGF/tapas_logrt_linear_binary_sim.m @@ -52,7 +52,11 @@ logrt = be0 +be1.*surp +be2.*bernv +be3.*inferv +be4.*pv; % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = logrt+sqrt(ze)*randn(n, 1); diff --git a/HGF/tapas_quasinewton_optim_config.m b/HGF/tapas_quasinewton_optim_config.m index 42a67fa1..732cb648 100644 --- a/HGF/tapas_quasinewton_optim_config.m +++ b/HGF/tapas_quasinewton_optim_config.m @@ -25,7 +25,7 @@ % Options for optimization c.tolGrad = 1e-3; c.tolArg = 1e-3; -c.maxStep = 2; +c.maxStep = 1; c.maxIter = 100; c.maxRegu = 16; c.maxRst = 10; diff --git a/HGF/tapas_simModel.m b/HGF/tapas_simModel.m index 55eb2a88..9178c3f8 100644 --- a/HGF/tapas_simModel.m +++ b/HGF/tapas_simModel.m @@ -176,18 +176,23 @@ % Read configuration of observation model try - obs_config_fun = str2func([obs_model, '_config']); + obs_config_fun = str2func([r.c_sim.obs_model, '_config']); r.c_obs = obs_config_fun(); catch r.c_obs = []; end + + % Set seed for random number generator + r.c_sim.seed = NaN; + if nargin > 5 + r.c_sim.seed = varargin{3}; + end % Get function handle to observation model obs_fun = str2func([r.c_sim.obs_model, '_sim']); % Simulate decisions r.y = obs_fun(r, infStates, r.p_obs.p); - end return; diff --git a/HGF/tapas_softmax.m b/HGF/tapas_softmax.m index 0f61e03a..0eef4e4f 100644 --- a/HGF/tapas_softmax.m +++ b/HGF/tapas_softmax.m @@ -2,7 +2,7 @@ % Calculates the log-probability of responses under the softmax model % % -------------------------------------------------------------------------------------------------- -% Copyright (C) 2013 Christoph Mathys, TNU, UZH & ETHZ +% Copyright (C) 2013-2019 Christoph Mathys, TNU, UZH & ETHZ % % This file is part of the HGF toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL @@ -26,20 +26,25 @@ yhat = NaN(n,1); res = NaN(n,1); -% Weed irregular trials out from inferred states and responses -states = squeeze(infStates(:,1,:,pop)); % Assumed structure of infStates: % dim 1: time (ie, input sequence number) % dim 2: HGF level % dim 3: choice number % dim 4: 1: muhat, 2: sahat, 3: mu, 4: sa -states(r.irr,:) = []; -y = r.y(:,1); -y(r.irr) = []; % Number of choices nc = size(infStates,3); +% Belief trajectories at 1st level +states = squeeze(infStates(:,1,:,pop)); + +% Responses +y = r.y(:,1); + +% Weed irregular trials out from inferred states and responses +states(r.irr,:) = []; +y(r.irr) = []; + % Partition functions Z = sum(exp(be*states),2); Z = repmat(Z,1,nc); @@ -56,4 +61,4 @@ yhat(reg) = probc; res(reg) = -log(probc); -return; +end diff --git a/HGF/tapas_softmax_binary_sim.m b/HGF/tapas_softmax_binary_sim.m index e7d04c70..bde0f08f 100644 --- a/HGF/tapas_softmax_binary_sim.m +++ b/HGF/tapas_softmax_binary_sim.m @@ -9,25 +9,34 @@ % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -mu1hat = infStates(:,1,1); +% Predictions or posteriors? +pop = 1; % Default: predictions +if r.c_obs.predorpost == 2 + pop = 3; % Alternative: posteriors +end + +% Inverse decision temperature beta be = p; -if size(mu1hat,2) == 1 - if ~any(mu1hat<0) && ~any(mu1hat>1) - % Apply the logistic sigmoid to the inferred states - prob = tapas_sgm(be.*(2.*mu1hat-1),1); - else - error('tapas:hgf:SoftMaxBinary:InfStatesIncompatible', 'infStates incompatible with tapas_softmax_binary observation model.') - end -else - % Apply the logistic sigmoid to the inferred states - prob = tapas_sgm(be.*(mu1hat(:,1)-mu1hat(:,2)),1); -end +% Assumed structure of infStates: +% dim 1: time (ie, input sequence number) +% dim 2: HGF level +% dim 3: 1: muhat, 2: sahat, 3: mu, 4: sa + +% Belief trajectories at 1st level +states = squeeze(infStates(:,1,pop)); + +% Apply the logistic sigmoid to the inferred states +prob = tapas_sgm(be.*(2.*states-1),1); % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = binornd(1, prob); -return; +end diff --git a/HGF/tapas_softmax_mu3.m b/HGF/tapas_softmax_mu3.m index 4b8e6fa1..13b73983 100644 --- a/HGF/tapas_softmax_mu3.m +++ b/HGF/tapas_softmax_mu3.m @@ -2,7 +2,7 @@ % Calculates the log-probability of responses under the softmax model % % -------------------------------------------------------------------------------------------------- -% Copyright (C) 2017 Christoph Mathys, TNU, UZH & ETHZ +% Copyright (C) 2017-2019 Christoph Mathys, TNU, UZH & ETHZ % % This file is part of the HGF toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL @@ -29,18 +29,24 @@ % dim 3: choice number % dim 4: 1: muhat, 2: sahat, 3: mu, 4: sa -% Weed irregular trials out from inferred states and responses +% Number of choices +nc = size(infStates,3); + +% Belief trajectories at 1st level states = squeeze(infStates(:,1,:,pop)); -states(r.irr,:) = []; + +% Log-volatility trajectory mu3 = squeeze(infStates(:,3,1,3)); -mu3(r.irr) = []; + +% Responses y = r.y(:,1); -y(r.irr) = []; -% Number of choices -nc = size(infStates,3); +% Weed irregular trials out from inferred states and responses +states(r.irr,:) = []; +mu3(r.irr) = []; +y(r.irr) = []; -% Inveyrse decision temperature +% Inverse decision temperature be = exp(-mu3); be = repmat(be,1,nc); @@ -60,4 +66,4 @@ yhat(reg) = probc; res(reg) = -log(probc); -return; +end diff --git a/HGF/tapas_softmax_mu3_sim.m b/HGF/tapas_softmax_mu3_sim.m index 38783eaa..05d632d7 100644 --- a/HGF/tapas_softmax_mu3_sim.m +++ b/HGF/tapas_softmax_mu3_sim.m @@ -2,20 +2,33 @@ % Simulates observations from a Boltzmann distribution with volatility as temperature % % -------------------------------------------------------------------------------------------------- -% Copyright (C) 2017 Christoph Mathys, TNU, UZH & ETHZ +% Copyright (C) 2017-2019 Christoph Mathys, TNU, UZH & ETHZ % % This file is part of the HGF toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . +% Predictions or posteriors? +pop = 1; % Default: predictions +if r.c_obs.predorpost == 2 + pop = 3; % Alternative: posteriors +end + +% Assumed structure of infStates: +% dim 1: time (ie, input sequence number) +% dim 2: HGF level +% dim 3: choice number +% dim 4: 1: muhat, 2: sahat, 3: mu, 4: sa + % Number of choices nc = size(infStates,3); -% The value of the last dimension of infStates determines whether responses are -% based on: 1 = predictions, 2 = posteriors. -states = squeeze(infStates(:,1,:,1)); -mu3 = infStates(:,3,1,3); +% Belief trajectories at 1st level +states = squeeze(infStates(:,1,:,pop)); + +% Log-volatility trajectory +mu3 = squeeze(infStates(:,3,1,3)); % Inverse decision temperature be = exp(-mu3); @@ -29,7 +42,11 @@ prob = exp(be.*states)./Z; % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Draw responses n = size(infStates,1); @@ -39,4 +56,4 @@ y(j) = find(mnrnd(1, prob(j,:))); end -return; +end diff --git a/HGF/tapas_softmax_mu3_wld.m b/HGF/tapas_softmax_mu3_wld.m index 87ea97a8..bf0859cb 100644 --- a/HGF/tapas_softmax_mu3_wld.m +++ b/HGF/tapas_softmax_mu3_wld.m @@ -4,7 +4,7 @@ % accounting for win- and loss-distortion of state values. % % -------------------------------------------------------------------------------------------------- -% Copyright (C) 2018 Christoph Mathys, TNU, UZH & ETHZ +% Copyright (C) 2018-2019 Christoph Mathys, TNU, UZH & ETHZ % % This file is part of the HGF toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL @@ -35,18 +35,26 @@ % dim 3: choice number % dim 4: 1: muhat, 2: sahat, 3: mu, 4: sa -% Weed irregular trials out from inferred states, inputs, and responses +% Number of choices +nc = size(infStates,3); + +% Belief trajectories at 1st level states = squeeze(infStates(:,1,:,pop)); -states(r.irr,:) = []; + +% Log-volatility trajectory mu3 = squeeze(infStates(:,3,1,3)); -mu3(r.irr) = []; + +% Inputs u = r.u(:,1); -u(r.irr) = []; + +% Responses y = r.y(:,1); -y(r.irr) = []; -% Number of choices -nc = size(infStates,3); +% Weed irregular trials out from inferred states, inputs, and responses +states(r.irr,:) = []; +mu3(r.irr) = []; +u(r.irr) = []; +y(r.irr) = []; % Choice matrix Y corresponding to states Y = zeros(size(states)); @@ -95,4 +103,4 @@ yhat(reg) = probc; res(reg) = -log(probc); -return; +end diff --git a/HGF/tapas_softmax_mu3_wld_sim.m b/HGF/tapas_softmax_mu3_wld_sim.m new file mode 100644 index 00000000..00c70067 --- /dev/null +++ b/HGF/tapas_softmax_mu3_wld_sim.m @@ -0,0 +1,96 @@ +function y = tapas_softmax_mu3_wld_sim(r, infStates, p) +% Simulates observations from a Boltzmann distribution with volatility as temperature +% +% -------------------------------------------------------------------------------------------------- +% Copyright (C) 2017-2019 Christoph Mathys, TNU, UZH & ETHZ +% +% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +% Predictions or posteriors? +pop = 1; % Default: predictions +if r.c_obs.predorpost == 2 + pop = 3; % Alternative: posteriors +end + +% Win- and loss-distortion parameters +la_wd = p(1); +la_ld = p(2); + +% Assumed structure of infStates: +% dim 1: time (ie, input sequence number) +% dim 2: HGF level +% dim 3: choice number +% dim 4: 1: muhat, 2: sahat, 3: mu, 4: sa + +% Number of choices +nc = size(infStates,3); + +% Belief trajectories at 1st level +states = squeeze(infStates(:,1,:,pop)); + +% Log-volatility trajectory +mu3 = squeeze(infStates(:,3,1,3)); + +% Inputs +u = r.u(:,1); + +% True responses underlying belief trajectories +y_true = r.u(:,2); + +% Choice matrix Y corresponding to states +Y = zeros(size(states)); +Y(sub2ind(size(Y), 1:size(Y,1), y_true')) = 1; + +% Choice on previous trial +Yprev = Y; +Yprev = [zeros(1,size(Yprev,2)); Yprev]; +Yprev(end,:) = []; + +% Wins on previous trial +wprev = u; +wprev = [0; wprev]; +wprev(end) = []; + +% Losses on previous trial +lprev = 1 - wprev; +lprev(1) = 0; + +% In matrix form corresponding to states +Wprev = Yprev; +Wprev(find(lprev),:) = 0; +Lprev = Yprev; +Lprev(find(wprev),:) = 0; + +% Win- and loss-distortion +states = states + la_wd*Wprev + la_ld*Lprev; + +% Inverse decision temperature +be = exp(-mu3); +be = repmat(be,1,nc); + +% Partition functions +Z = sum(exp(be.*states),2); +Z = repmat(Z,1,nc); + +% Softmax probabilities +prob = exp(be.*states)./Z; + +% Initialize random number generator +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end + +% Draw responses +n = size(infStates,1); +y = NaN(n,1); + +for j=1:n + y(j) = find(mnrnd(1, prob(j,:))); +end + +end diff --git a/HGF/tapas_softmax_sim.m b/HGF/tapas_softmax_sim.m index b7f2b378..df879a45 100644 --- a/HGF/tapas_softmax_sim.m +++ b/HGF/tapas_softmax_sim.m @@ -2,30 +2,47 @@ % Simulates observations from a Bernoulli distribution % % -------------------------------------------------------------------------------------------------- -% Copyright (C) 2012-2013 Christoph Mathys, TNU, UZH & ETHZ +% Copyright (C) 2012-2019 Christoph Mathys, TNU, UZH & ETHZ % % This file is part of the HGF toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% Number of response options -no = size(infStates,3); +% Predictions or posteriors? +pop = 1; % Default: predictions +if r.c_obs.predorpost == 2 + pop = 3; % Alternative: posteriors +end -% The value of the last dimension of infStates determines whether responses are -% based on: 1 = predictions, 2 = posteriors. -states = squeeze(infStates(:,1,:,1)); +% Inverse decision temperature beta be = p; +% Assumed structure of infStates: +% dim 1: time (ie, input sequence number) +% dim 2: HGF level +% dim 3: choice number +% dim 4: 1: muhat, 2: sahat, 3: mu, 4: sa + +% Number of choices +nc = size(infStates,3); + +% Belief trajectories at 1st level +states = squeeze(infStates(:,1,:,pop)); + % Partition functions Z = sum(exp(be*states),2); -Z = repmat(Z,1,no); +Z = repmat(Z,1,nc); % Softmax probabilities prob = exp(be*states)./Z; % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Draw responses n = size(infStates,1); @@ -35,4 +52,4 @@ y(j) = find(mnrnd(1, prob(j,:))); end -return; +end diff --git a/HGF/tapas_softmax_wld.m b/HGF/tapas_softmax_wld.m new file mode 100644 index 00000000..81cb80b8 --- /dev/null +++ b/HGF/tapas_softmax_wld.m @@ -0,0 +1,101 @@ +function [logp, yhat, res] = tapas_softmax_wld(r, infStates, ptrans) +% Calculates the log-probability of responses under the softmax model +% with phasic volatility exp(mu3) as the decision temperature and parameters +% accounting for win- and loss-distortion of state values. +% +% -------------------------------------------------------------------------------------------------- +% Copyright (C) 2019 Christoph Mathys, TNU, UZH & ETHZ +% +% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +% Predictions or posteriors? +pop = 1; % Default: predictions +if r.c_obs.predorpost == 2 + pop = 3; % Alternative: posteriors +end + +% Transform beta to its native space +be = exp(ptrans(1)); + +% Win- and loss-distortion parameters +la_wd = ptrans(1); +la_ld = ptrans(2); + +% Initialize returned log-probabilities, predictions, +% and residuals as NaNs so that NaN is returned for all +% irregualar trials +n = size(infStates,1); +logp = NaN(n,1); +yhat = NaN(n,1); +res = NaN(n,1); + +% Assumed structure of infStates: +% dim 1: time (ie, input sequence number) +% dim 2: HGF level +% dim 3: choice number +% dim 4: 1: muhat, 2: sahat, 3: mu, 4: sa + +% Number of choices +nc = size(infStates,3); + +% Belief trajectories at 1st level +states = squeeze(infStates(:,1,:,pop)); + +% Inputs +u = r.u(:,1); + +% Responses +y = r.y(:,1); + +% Weed irregular trials out from inferred states, inputs, and responses +states(r.irr,:) = []; +u(r.irr) = []; +y(r.irr) = []; + +% Choice matrix Y corresponding to states +Y = zeros(size(states)); +Y(sub2ind(size(Y), 1:size(Y,1), y')) = 1; + +% Choice on previous trial +Yprev = Y; +Yprev = [zeros(1,size(Yprev,2)); Yprev]; +Yprev(end,:) = []; + +% Wins on previous trial +wprev = u; +wprev = [0; wprev]; +wprev(end) = []; + +% Losses on previous trial +lprev = 1 - wprev; +lprev(1) = 0; + +% In matrix form corresponding to states +Wprev = Yprev; +Wprev(find(lprev),:) = 0; +Lprev = Yprev; +Lprev(find(wprev),:) = 0; + +% Win- and loss-distortion +states = states + la_wd*Wprev + la_ld*Lprev; + +% Partition functions +Z = sum(exp(be.*states),2); +Z = repmat(Z,1,nc); + +% Softmax probabilities +prob = exp(be.*states)./Z; + +% Extract probabilities of chosen options +probc = prob(sub2ind(size(prob), 1:length(y), y')); + +% Calculate log-probabilities for non-irregular trials +reg = ~ismember(1:n,r.irr); +logp(reg) = log(probc); +yhat(reg) = probc; +res(reg) = -log(probc); + +end diff --git a/HGF/tapas_softmax_wld_config.m b/HGF/tapas_softmax_wld_config.m new file mode 100644 index 00000000..1b7599e0 --- /dev/null +++ b/HGF/tapas_softmax_wld_config.m @@ -0,0 +1,63 @@ +function c = tapas_softmax_wld_config +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Contains the configuration for the softmax observation model for multinomial responses with phasic +% volatility exp(mu3) as the decision temperature and parameters accounting for win- and +% loss-distortion of state values. +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% -------------------------------------------------------------------------------------------------- +% Copyright (C) 2019 Christoph Mathys, TNU, UZH & ETHZ +% +% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +% Config structure +c = struct; + +% Is the decision based on predictions or posteriors? Comment as appropriate. +c.predorpost = 1; % Predictions +%c.predorpost = 2; % Posteriors + +% Model name +c.model = 'softmax_wld'; + +% Sufficient statistics of Gaussian parameter priors + +% Beta +c.logbemu = log(1); +c.logbesa = 4^2; + +% Win-distortion +c.la_wdmu = 0; +c.la_wdsa = 2^-2; + +% Loss-distortion +c.la_ldmu = 0; +c.la_ldsa = 2^-2; + +% Gather prior settings in vectors +c.priormus = [ + c.logbemu,... + c.la_wdmu,... + c.la_ldmu,... + ]; + +c.priorsas = [ + c.logbesa,... + c.la_wdsa,... + c.la_ldsa,... + ]; + +% Model filehandle +c.obs_fun = @tapas_softmax_wld; + +% Handle to function that transforms observation parameters to their native space +% from the space they are estimated in +c.transp_obs_fun = @tapas_softmax_wld_transp; + +return; diff --git a/HGF/tapas_softmax_wld_namep.m b/HGF/tapas_softmax_wld_namep.m new file mode 100644 index 00000000..41a0e3a8 --- /dev/null +++ b/HGF/tapas_softmax_wld_namep.m @@ -0,0 +1,16 @@ +function pstruct = tapas_softmax_wld_namep(pvec) +% -------------------------------------------------------------------------------------------------- +% Copyright (C) 2019 Christoph Mathys, TNU, UZH & ETHZ +% +% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +pstruct = struct; + +pstruct.be = pvec(1); +pstruct.la_wd = pvec(2); +pstruct.la_ld = pvec(3); + +return; diff --git a/HGF/tapas_softmax_wld_sim.m b/HGF/tapas_softmax_wld_sim.m new file mode 100644 index 00000000..f941b804 --- /dev/null +++ b/HGF/tapas_softmax_wld_sim.m @@ -0,0 +1,95 @@ +function y = tapas_softmax_wld_sim(r, infStates, p) +% Simulates observations from a Boltzmann distribution with volatility as temperature +% +% -------------------------------------------------------------------------------------------------- +% Copyright (C) 2019 Christoph Mathys, TNU, UZH & ETHZ +% +% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +% Predictions or posteriors? +pop = 1; % Default: predictions +if r.c_obs.predorpost == 2 + pop = 3; % Alternative: posteriors +end + +% Inverse decision temperature beta +be = p; + +% Win- and loss-distortion parameters +la_wd = p(1); +la_ld = p(2); + +% Assumed structure of infStates: +% dim 1: time (ie, input sequence number) +% dim 2: HGF level +% dim 3: choice number +% dim 4: 1: muhat, 2: sahat, 3: mu, 4: sa + +% Number of choices +nc = size(infStates,3); + +% Belief trajectories at 1st level +states = squeeze(infStates(:,1,:,pop)); + +% Log-volatility trajectory +mu3 = squeeze(infStates(:,3,1,3)); + +% Inputs +u = r.u(:,1); + +% True responses underlying belief trajectories +y_true = r.u(:,2); + +% Choice matrix Y corresponding to states +Y = zeros(size(states)); +Y(sub2ind(size(Y), 1:size(Y,1), y_true')) = 1; + +% Choice on previous trial +Yprev = Y; +Yprev = [zeros(1,size(Yprev,2)); Yprev]; +Yprev(end,:) = []; + +% Wins on previous trial +wprev = u; +wprev = [0; wprev]; +wprev(end) = []; + +% Losses on previous trial +lprev = 1 - wprev; +lprev(1) = 0; + +% In matrix form corresponding to states +Wprev = Yprev; +Wprev(find(lprev),:) = 0; +Lprev = Yprev; +Lprev(find(wprev),:) = 0; + +% Win- and loss-distortion +states = states + la_wd*Wprev + la_ld*Lprev; + +% Partition functions +Z = sum(exp(be.*states),2); +Z = repmat(Z,1,nc); + +% Softmax probabilities +prob = exp(be.*states)./Z; + +% Initialize random number generator +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end + +% Draw responses +n = size(infStates,1); +y = NaN(n,1); + +for j=1:n + y(j) = find(mnrnd(1, prob(j,:))); +end + +end diff --git a/HGF/tapas_softmax_wld_transp.m b/HGF/tapas_softmax_wld_transp.m new file mode 100644 index 00000000..2c8161d6 --- /dev/null +++ b/HGF/tapas_softmax_wld_transp.m @@ -0,0 +1,20 @@ +function [pvec, pstruct] = tapas_softmax_wld_transp(r, ptrans) +% -------------------------------------------------------------------------------------------------- +% Copyright (C) 2019 Christoph Mathys, TNU, UZH & ETHZ +% +% This file is part of the HGF toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +pvec = NaN(1,length(ptrans)); +pstruct = struct; + +pvec(1) = exp(ptrans(1)); % be +pstruct.be = pvec(1); +pvec(2) = ptrans(2); % la_wd +pstruct.la_wd = pvec(2); +pvec(3) = ptrans(3); % la_ld +pstruct.la_ld = pvec(3); + +return; diff --git a/HGF/tapas_unitsq_sgm_config.m b/HGF/tapas_unitsq_sgm_config.m index b42ee4dc..a41faf4a 100644 --- a/HGF/tapas_unitsq_sgm_config.m +++ b/HGF/tapas_unitsq_sgm_config.m @@ -37,6 +37,10 @@ % Config structure c = struct; +% Is the decision based on predictions or posteriors? Comment as appropriate. +c.predorpost = 1; % Predictions +%c.predorpost = 2; % Posteriors + % Model name c.model = 'tapas_unitsq_sgm'; diff --git a/HGF/tapas_unitsq_sgm_mu3_sim.m b/HGF/tapas_unitsq_sgm_mu3_sim.m index 8a186397..7391636f 100644 --- a/HGF/tapas_unitsq_sgm_mu3_sim.m +++ b/HGF/tapas_unitsq_sgm_mu3_sim.m @@ -17,7 +17,11 @@ prob = mu1hat.^ze./(mu1hat.^ze+(1-mu1hat).^ze); % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = binornd(1, prob); diff --git a/HGF/tapas_unitsq_sgm_sim.m b/HGF/tapas_unitsq_sgm_sim.m index d182180c..a7811af0 100644 --- a/HGF/tapas_unitsq_sgm_sim.m +++ b/HGF/tapas_unitsq_sgm_sim.m @@ -9,16 +9,34 @@ % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -mu1hat = infStates(:,1,1); +% Predictions or posteriors? +pop = 1; % Default: predictions +if r.c_obs.predorpost == 2 + pop = 3; % Alternative: posteriors +end + +% Inverse decision temperature zeta ze = p; +% Assumed structure of infStates: +% dim 1: time (ie, input sequence number) +% dim 2: HGF level +% dim 3: 1: muhat, 2: sahat, 3: mu, 4: sa + +% Belief trajectories at 1st level +states = squeeze(infStates(:,1,pop)); + % Apply the unit-square sigmoid to the inferred states -prob = mu1hat.^ze./(mu1hat.^ze+(1-mu1hat).^ze); +prob = states.^ze./(states.^ze+(1-states).^ze); % Initialize random number generator -rng('shuffle'); +if isnan(r.c_sim.seed) + rng('shuffle'); +else + rng(r.c_sim.seed); +end % Simulate y = binornd(1, prob); -return; +end diff --git a/PhysIO/CHANGELOG.md b/PhysIO/CHANGELOG.md index 44faed0a..6ec3c29f 100644 --- a/PhysIO/CHANGELOG.md +++ b/PhysIO/CHANGELOG.md @@ -4,18 +4,75 @@ RELEASE INFORMATION Current Release --------------- -PhysIO_Toolbox_R2018.1.1 +*Current version: PhysIO Toolbox Release R2019a, v7.1.0* -September 05, 2018 +March 19, 2019 -Bugfix Release Notes (R2018.1.1) -------------------------------- + +Minor Release Notes (R2019a, v7.1.0) +------------------------------------ + +### Added +- BIDS reader and example (Brain Imaging Data Structure, +http://bids.neuroimaging.io/bids_spec.pdf) for `*_physio.tsv[.gz]/.json` files +- Added BioPac txt-File read-in and example +- Template example with all physio-fields for matlab script and settings as in default SPM batch +- Started unit testing framework in folder `tests` + - example functions for findpeaks and BIDS readin + - reference data saved with example data in subfolder `TestReferenceResults` + - reference data reflects physio structure after running example scripts + with PhysIO R2019a + +### Changed +- put all functions in `code` into subfolders relating to different modules: + `readin`, `sync`, `preproc`, `model`, `assess`, `utils` (gitlab-issue #58) + - updated deployment `tapas_physio_init` because of that + - updated figure names to reflect respective code module +- matlab-script examples now contain more comments + - fixed internal bug that prepended absolute paths to input logfiles in automatic example generation +- `tapas_physio_create_noise_rois_regressors` with more flexible ROI reslicing + options (speed-up) and uses `spm_erode` (no Matlab image processing toolbox needed), + thanks to a [contribution by Benoît Béranger](https://github.com/translationalneuromodeling/tapas/pull/34) +- introduced semantic version numbers for all previous releases, and changed +Release numbering to R style +- extended documentation (FAQ, new read-in BIDS) +- several bugfixes (Sep 18 - Mar 19), see + [GitHub Issues](https://github.com/translationalneuromodeling/tapas/issues?q=label:physio) + +### Removed +- `tapas_physio_findpeaks` now refers to current Matlab signal processing +toolbox implementation, instead of copy of older version +- some Matlab toolbox dependencies by custom simplified functions (e.g., +`suptitle`) + + +Bugfix Release Notes (R2018.1.3, v7.0.3) +---------------------------------------- + +### Changed +- fixed bug for matching of Philips SCANPHYSLOG-files (Gitlab #62), if + physlogs were acquired on different days, but similar times + + +Bugfix Release Notes (R2018.1.2, v7.0.2) +---------------------------------------- + +### Added +- BioPac txt-file reader (for single file, resp/cardiac/trigger data in different columns) + +### Changed +- fixed bug for 3D nifti array read-in in tapas_physio_create_noise_rois_regressors (github issue #24, gitlab #52) + + +Bugfix Release Notes (R2018.1.1, v7.0.1) +---------------------------------------- ### Changed - documentation.{html,pdf} export nicer with different FAQ numbering -Major Release Notes (R2018.1) ------------------------------ + +Major Release Notes (R2018.1, v7.0.0) +------------------------------------- ### Added - initialization function `tapas_physio_init()` to check Matlab paths, including SPM for batch processing @@ -29,8 +86,9 @@ Major Release Notes (R2018.1) - Updated `README.md` to reflect changes to example download, new references - Extended Wiki documentation, in particular examples and read-in formats -Minor Release Notes (R2017.3) ------------------------------ + +Minor Release Notes (R2017.3, v6.3.0) +------------------------------------- - Included references to external [ETH gitlab physio-doc repo and wiki](https://gitlab.ethz.ch/physio/physio-doc) - New Human Connectome Project reader for preprocessed Siemens 3-column logfiles (`*Physio_log.txt`) @@ -43,8 +101,9 @@ Minor Release Notes (R2017.3) - updated README about documentation, new support policy and [TAPAS on GitHub](https://translationalneuromodeling.github.io/tapas) - extended FAQ -Minor Release Notes (R2017.2) ------------------------------ + +Minor Release Notes (R2017.2, v6.2.0) +------------------------------------- - Included Markdown-based documentation via Wiki (also CITATION, LICENSE, CHANGELOG.md) - Included FAQ in Wiki @@ -52,8 +111,9 @@ Minor Release Notes (R2017.2) - Bugfix and Typo correction - Philips SCANPYHSLOG for their software release 5.1.7. -Minor Release Notes (R2017.1) ------------------------------ + +Minor Release Notes (R2017.1, v6.1.0) +------------------------------------- - Substantially improved Siemens interface, both for VB/VD and 3T/7T releases - several bugfixes @@ -61,8 +121,9 @@ Minor Release Notes (R2017.1) - New functionality tapas_physio_overlay_contrasts.m to display non-physio contrasts automatically as well -Major Release Notes (r904 / R2016.1) ------------------------------------- + +Major Release Notes (r904 / R2016.1, v6.0.0) +-------------------------------------------- - Software version for accepted PhysIO Toolbox Paper: doi:10.1016/j.jneumeth.2016.10.019 - Tested and expanded versions of examples @@ -73,8 +134,9 @@ Major Release Notes (r904 / R2016.1) - Improved Read-in capabilities (Siemens respiration data, BioPac .mat) - Migration from svn (r904) to git (tnurepository) for version control -Major Release Notes (r835) --------------------------- + +Major Release Notes (r835, v5.0.0) +---------------------------------- - Software version for Toolbox Paper submission - Noise ROIs modeling @@ -85,8 +147,9 @@ Major Release Notes (r835) - consistent module naming (scan_timing, preproc) - Visualisation improvement (color schemes, legends) -Minor Release Notes (r666) --------------------------- + +Minor Release Notes (r666, v4.1.0) +---------------------------------- - Compatibility tested for SPM12, small bugfixes Batch Dependencies - Cleaner Batch Interface with grouped sub-menus (cfg_choice) @@ -97,16 +160,19 @@ Minor Release Notes (r666) - All peak detections (cardiac/respiratory) now via auto_matched algorithm - Adapt plots/saving for Matlab R2014b -Major Release Notes (r534) --------------------------- + +Major Release Notes (r534, v4.0.0) +---------------------------------- - Read-in of Siemens plain text log files; new example dataset for Siemens -- Speed up and debugging of auto-detection method for noisy cardiac data => new method thresh.cardiac.initial_cpulse_select.method = ???auto_matched??? +- Speed up and debugging of auto-detection method for noisy cardiac data => new +method thresh.cardiac.initial_cpulse_select.method = 'auto_matched' - Error handling for temporary breathing belt failures (Eduardo Aponte, TNU Zurich) - slice-wise regressors can be created by setting sqpar.onset_slice to a index vector of slices -Major Release Notes (r497) --------------------------- + +Major Release Notes (r497, v3.0.0) +---------------------------------- - SPM matlabbatch GUI implemented (Call via Batch -> SPM -> Tools -> TAPAS PhysIO Toolbox) - improved, automatic heartbeat detection for noisy ECG now standard for ECG and Pulse oximetry (courtesy of Steffen Bollmann) @@ -114,8 +180,9 @@ Major Release Notes (r497) - job .m/.mat-files created for all example datasets - bugfixes cpulse-initial-select method-handling (auto/manual/load) -Major Release Notes (r429) --------------------------- + +Major Release Notes (r429, v2.0.0) +---------------------------------- - Cardiac and Respiratory response function regressors integrated in workflow (heart rate and breathing volume computation) - Handling of Cardiac and Respiratory Logfiles only @@ -123,14 +190,16 @@ Major Release Notes (r429) - read-in of custom log files, e.g. for BrainVoyager peripheral data - more informative plots and commenting (especially in tapas_physio_new). -Minor Release Notes (r354) --------------------------- + +Minor Release Notes (r354, v1.1.0) +---------------------------------- - computation of heart and breathing rate in Philips/PPU/main_PPU.m - prefix of functions with tapas_* -Major Release Notes (r241) --------------------------- + +Major Release Notes (r241, v1.0.0) +---------------------------------- - complete modularization of reading/preprocessing/regressor creation for peripheral physiological data - manual selection of missed heartbeats in ECG/pulse oximetry (courtesy of Jakob Heinzle) @@ -140,4 +209,3 @@ Major Release Notes (r241) - consistent function names (prefixed by "physio_") NOTE: Your main_ECG/PPU.m etc. scripts from previous versions (<=r159) will not work with this one any more. Please adapt one of the example scripts for your needs (~5 min of work). The main benefit of this version is a complete new variable structure that is more sustainable and makes the code more readable. - diff --git a/PhysIO/README.md b/PhysIO/README.md index d8363daf..76502a41 100644 --- a/PhysIO/README.md +++ b/PhysIO/README.md @@ -1,15 +1,15 @@ TAPAS PhysIO Toolbox ==================== -*Current version: 2018.1* +*Current version: Release 2019a, v7.1.0* -> Copyright (C) 2012-2018 Lars Kasper - -> Translational Neuromodeling Unit (TNU) - -> Institute for Biomedical Engineering - -> University of Zurich and ETH Zurich +> Copyright (C) 2012-2019 +> Lars Kasper +> +> +> Translational Neuromodeling Unit (TNU) +> Institute for Biomedical Engineering +> University of Zurich and ETH Zurich Download @@ -242,9 +242,12 @@ References ### Main Toolbox Reference -1. Kasper, L., Bollmann, S., Diaconescu, A.O., Hutton, C., Heinzle, J., Iglesias, S., Hauser, T.U., Sebold, M., Manjaly, Z.-M., Pruessmann, K.P., Stephan, K.E., 2017. The PhysIO Toolbox for Modeling Physiological Noise in fMRI Data. Journal of Neuroscience Methods 276, 56–72. doi:10.1016/j.jneumeth.2016.10.019 +1. Kasper, L., Bollmann, S., Diaconescu, A.O., Hutton, C., Heinzle, J., Iglesias, +S., Hauser, T.U., Sebold, M., Manjaly, Z.-M., Pruessmann, K.P., Stephan, K.E., 2017. +The PhysIO Toolbox for Modeling Physiological Noise in fMRI Data. +Journal of Neuroscience Methods 276, 56–72. doi:10.1016/j.jneumeth.2016.10.019 -### Related Papers (Implemented noise correction algorithms) +### Related Papers (Implemented noise correction algorithms and optimal parameter choices) #### RETROICOR 2. Glover, G.H., Li, T.Q. & Ress, D. Image‐based method for retrospective correction @@ -261,24 +264,33 @@ Imaging 28, 1337‐1344 (2008). 5. Behzadi, Y., Restom, K., Liau, J., Liu, T.T., 2007. A component based noise correction method (CompCor) for BOLD and perfusion based fMRI. NeuroImage 37, 90–101. doi:10.1016/j.neuroimage.2007.04.042 - + #### RVT 6. Birn, R.M., Smith, M.A., Jones, T.B., Bandettini, P.A., 2008. The respiration response function: The temporal dynamics of fMRI s ignal fluctuations related to changes in respiration. NeuroImage 40, 644–654. doi:10.1016/j.neuroimage.2007.11.059 - +7. Jo, H.J., Saad, Z.S., Simmons, W.K., Milbury, L.A., Cox, R.W., 2010. +Mapping sources of correlation in resting state FMRI, with artifact detection +and removal. NeuroImage 52, 571–582. https://doi.org/10.1016/j.neuroimage.2010.04.246 +*regressor delay suggestions* + #### HRV -7. Chang, C., Cunningham, J.P., Glover, G.H., 2009. Influence of heart rate on the +8. Chang, C., Cunningham, J.P., Glover, G.H., 2009. Influence of heart rate on the BOLD signal: The cardiac response function. NeuroImage 44, 857–869. doi:10.1016/j.neuroimage.2008.09.029 - +9. Shmueli, K., van Gelderen, P., de Zwart, J.A., Horovitz, S.G., Fukunaga, M., +Jansma, J.M., Duyn, J.H., 2007. Low-frequency fluctuations in the cardiac rate +as a source of variance in the resting-state fMRI BOLD signal. +NeuroImage 38, 306–320. https://doi.org/10.1016/j.neuroimage.2007.07.037 +*regressor delay suggestions* + #### Motion (Censoring, Framewise Displacement) -8. Siegel, J.S., Power, J.D., Dubis, J.W., Vogel, A.C., Church, J.A., Schlaggar, B.L., +10. Siegel, J.S., Power, J.D., Dubis, J.W., Vogel, A.C., Church, J.A., Schlaggar, B.L., Petersen, S.E., 2014. Statistical improvements in functional magnetic resonance imaging analyses produced by censoring high-motion data points. Hum. Brain Mapp. 35, 1981–1996. doi:10.1002/hbm.22307 -9. Power, J.D., Barnes, K.A., Snyder, A.Z., Schlaggar, B.L., Petersen, S.E., 2012. Spurious but systematic correlations in functional connectivity MRI networks arise from subject motion. NeuroImage 59, 2142–2154. https://doi.org/10.1016/j.neuroimage.2011.10.018 +11. Power, J.D., Barnes, K.A., Snyder, A.Z., Schlaggar, B.L., Petersen, S.E., 2012. Spurious but systematic correlations in functional connectivity MRI networks arise from subject motion. NeuroImage 59, 2142–2154. https://doi.org/10.1016/j.neuroimage.2011.10.018 Copying/License diff --git a/PhysIO/code/tapas_physio_check_efficacy.m b/PhysIO/code/assess/tapas_physio_check_efficacy.m similarity index 95% rename from PhysIO/code/tapas_physio_check_efficacy.m rename to PhysIO/code/assess/tapas_physio_check_efficacy.m index 55434c98..9daa0748 100644 --- a/PhysIO/code/tapas_physio_check_efficacy.m +++ b/PhysIO/code/assess/tapas_physio_check_efficacy.m @@ -1,179 +1,178 @@ -% This script reports all relevant F-contrast-maps for physIO-created regressors -% for the specified subjects -% -% Author: Lars Kasper -% Created: 2014-01-21 -% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. -% -% This file is part of the TNU CheckPhysRETROICOR toolbox, which is released under the terms of the GNU General Public -% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . -% -% $Id$ - -%% ======================================================================== -% START #MOD - -% general paths study -pathSPM = '~/Documents/code/matlab/spm12b'; -pathPhysIO = '~/Documents/code/matlab/spm12b/toolbox/PhysIO'; -fileReport = ['~/Dropbox/Andreiuta/physio_rest_ioio_pharm/' ... - 'physio_IOIO_pharm/results/PhysIOTest_cardiac_overview_inferior_sliceParallel.ps']; % where contrast maps are saved - -% logfile names sorted per session -nSess = 1; - -% subject directories to be included into analysis - -% root directory holding all subject directories -pathDataRoot = '/Users/kasperla/Dropbox/Andreiuta/physio_rest_ioio_pharm/physio_IOIO_pharm/glmAnalysis'; - -% prefix of all subject directories -maskSubjects = 'DMPAD_*'; - -% GLM analysis subdirectory of subject folder -dirGLM = ''; - -% includes subdirectory of subject folder and file name mask -maskStructural = '^meanfunct_rest.nii'; - -% names of physiological contrasts to be reported -% namesPhysContrasts = { -% 'All Phys Regressors' -% 'Cardiac Regressors' -% 'Respiratory Regressors' -% 'Cardiac X Respiratory Interaction' -% 'Movement Regressors' -% }; - -namesPhysContrasts = { - 'All Phys' - 'Cardiac' - 'Respiratory' - 'Card X Resp Interation' - 'Movement' - }; - -% selection of physiological contrasts to be reported, corresponding to -% namesPhysContrasts order -indReportPhysContrasts = 2; - - -reportContrastThreshold = 0.001; % 0.05; 0.001; -reportContrastCorrection = 'none'; % 'FWE'; 'none'; -%reportContrastPosition = [0 -15 -2*16]; 'max'; % 'max' to jump to max; or [x,y,z] in mm -%fovMillimeter = 50; %mm; choose 0 to plot whole FOV (bounding box) -reportContrastPosition = [0 0 -30]; 'max'; % 'max' to jump to max; or [x,y,z] in mm -fovMillimeter = 0; %mm; choose 0 to plot whole FOV (bounding box) - -% if true, voxel space (parallel to slices), not world space (with interpolation) is used -doPlotSliceParallel = true; - -physio = tapas_physio_new('RETROICOR'); -model = physio.model; % holding number of physiological regressors - -% END #MOD -%% ======================================================================== - -scans = dir(fullfile(pathDataRoot,maskSubjects)); -scans = {scans.name}; -subjectIndices = 1:length(scans); - -delete(fileReport); -addpath(pathPhysIO); -addpath(pathSPM); -spm('defaults', 'fMRI'); -spm_jobman('initcfg'); - -for s = subjectIndices - - try - dirSubject = scans{s}; - pathSubject = fullfile(pathDataRoot,dirSubject); %dirSubject = scan - pathAnalysis = fullfile(pathDataRoot,dirSubject,dirGLM); - fileSPM = fullfile(pathAnalysis, 'SPM.mat'); - fileStruct = spm_select('ExtFPList', pathSubject, maskStructural); - - - - %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % Create and plot phys regressors F-contrasts - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - if exist(fileSPM, 'file') - load(fileSPM); - nContrasts = numel(indReportPhysContrasts); - - - % Check whether contrasts already exist in SPM.mat - indContrasts = zeros(nContrasts,1); - for c = 1:nContrasts - iC = indReportPhysContrasts(c); - indContrasts(c) = tapas_physio_check_get_xcon_index(SPM, ... - namesPhysContrasts{iC}); - end - - % Generate contrasts only if not already existing - matlabbatch = tapas_physio_check_prepare_job_contrasts(fileSPM, ... - model, SPM, indReportPhysContrasts, pathPhysIO); - matlabbatch{1}.spm.stats.con.consess(find(indContrasts)) = []; - if ~isempty(matlabbatch{1}.spm.stats.con.consess) - spm_jobman('run', matlabbatch); - load(fileSPM); - end - - % report contrasts - for c = 1:nContrasts - iC = indReportPhysContrasts(c); - indContrasts(c) = tapas_physio_check_get_xcon_index(SPM, ... - namesPhysContrasts{iC}); - load(fullfile(pathPhysIO, 'tapas_physio_check_job_report')); - matlabbatch{1}.spm.stats.results.spmmat = cellstr(fileSPM); - matlabbatch{1}.spm.stats.results.conspec.titlestr = [dirSubject ' - ' namesPhysContrasts{iC}]; - matlabbatch{1}.spm.stats.results.conspec.contrasts = indContrasts(c); - - % contrast report correction - matlabbatch{1}.spm.stats.results.conspec.thresh = reportContrastThreshold; - matlabbatch{1}.spm.stats.results.conspec.threshdesc = reportContrastCorrection; - - spm_jobman('run', matlabbatch); % report result - % spm_print(fileReport) - spm_sections(xSPM,hReg, fileStruct); % overlay structural - - % voxel, not world space - if doPlotSliceParallel - spm_orthviews('Space',1) - end - - spm_orthviews('Zoom', fovMillimeter); % zoom to FOV*2 view - spm_orthviews('Interp', 0); % next neighbour interpolation plot - - - if isequal(reportContrastPosition, 'max'); - spm_mip_ui('Jump',spm_mip_ui('FindMIPax'),'glmax'); % goto global max - else - spm_mip_ui('SetCoords', reportContrastPosition, ... - spm_mip_ui('FindMIPax')); % goto global max - end - - - % spm_print always prepend current directory to print-file - % name :-( - [pathReport, filenameReport] = fileparts(fileReport); - pathTmp = pwd; - cd(pathReport); - spm_print(filenameReport); - cd(pathTmp); - end - - titstr = [dirSubject, ' - SPM.xX.X']; - title(regexprep(titstr,'_','\\_')); - set(gcf,'Name', titstr); - fprintf('good SPM: %s\n', dirSubject); - else % no file, report that - fprintf('no SPM.mat: %s\n', dirSubject); - end - catch - warning(sprintf('Subject ID %d: %s did not run through', s, dirSubject)); - end +% This script reports all relevant F-contrast-maps for physIO-created regressors +% for the specified subjects + +% Author: Lars Kasper +% Created: 2014-01-21 +% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. +% +% This file is part of the TNU CheckPhysRETROICOR toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + + +%% ======================================================================== +% START #MOD + +% general paths study +pathSPM = '~/Documents/code/matlab/spm12b'; +pathPhysIO = '~/Documents/code/matlab/spm12b/toolbox/PhysIO'; +fileReport = ['~/Dropbox/Andreiuta/physio_rest_ioio_pharm/' ... + 'physio_IOIO_pharm/results/PhysIOTest_cardiac_overview_inferior_sliceParallel.ps']; % where contrast maps are saved + +% logfile names sorted per session +nSess = 1; + +% subject directories to be included into analysis + +% root directory holding all subject directories +pathDataRoot = '/Users/kasperla/Dropbox/Andreiuta/physio_rest_ioio_pharm/physio_IOIO_pharm/glmAnalysis'; + +% prefix of all subject directories +maskSubjects = 'DMPAD_*'; + +% GLM analysis subdirectory of subject folder +dirGLM = ''; + +% includes subdirectory of subject folder and file name mask +maskStructural = '^meanfunct_rest.nii'; + +% names of physiological contrasts to be reported +% namesPhysContrasts = { +% 'All Phys Regressors' +% 'Cardiac Regressors' +% 'Respiratory Regressors' +% 'Cardiac X Respiratory Interaction' +% 'Movement Regressors' +% }; + +namesPhysContrasts = { + 'All Phys' + 'Cardiac' + 'Respiratory' + 'Card X Resp Interation' + 'Movement' + }; + +% selection of physiological contrasts to be reported, corresponding to +% namesPhysContrasts order +indReportPhysContrasts = 2; + + +reportContrastThreshold = 0.001; % 0.05; 0.001; +reportContrastCorrection = 'none'; % 'FWE'; 'none'; +%reportContrastPosition = [0 -15 -2*16]; 'max'; % 'max' to jump to max; or [x,y,z] in mm +%fovMillimeter = 50; %mm; choose 0 to plot whole FOV (bounding box) +reportContrastPosition = [0 0 -30]; 'max'; % 'max' to jump to max; or [x,y,z] in mm +fovMillimeter = 0; %mm; choose 0 to plot whole FOV (bounding box) + +% if true, voxel space (parallel to slices), not world space (with interpolation) is used +doPlotSliceParallel = true; + +physio = tapas_physio_new('RETROICOR'); +model = physio.model; % holding number of physiological regressors + +% END #MOD +%% ======================================================================== + +scans = dir(fullfile(pathDataRoot,maskSubjects)); +scans = {scans.name}; +subjectIndices = 1:length(scans); + +delete(fileReport); +addpath(pathPhysIO); +addpath(pathSPM); +spm('defaults', 'fMRI'); +spm_jobman('initcfg'); + +for s = subjectIndices + + try + dirSubject = scans{s}; + pathSubject = fullfile(pathDataRoot,dirSubject); %dirSubject = scan + pathAnalysis = fullfile(pathDataRoot,dirSubject,dirGLM); + fileSPM = fullfile(pathAnalysis, 'SPM.mat'); + fileStruct = spm_select('ExtFPList', pathSubject, maskStructural); + + + + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Create and plot phys regressors F-contrasts + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if exist(fileSPM, 'file') + load(fileSPM); + nContrasts = numel(indReportPhysContrasts); + + + % Check whether contrasts already exist in SPM.mat + indContrasts = zeros(nContrasts,1); + for c = 1:nContrasts + iC = indReportPhysContrasts(c); + indContrasts(c) = tapas_physio_check_get_xcon_index(SPM, ... + namesPhysContrasts{iC}); + end + + % Generate contrasts only if not already existing + matlabbatch = tapas_physio_check_prepare_job_contrasts(fileSPM, ... + model, SPM, indReportPhysContrasts, pathPhysIO); + matlabbatch{1}.spm.stats.con.consess(find(indContrasts)) = []; + if ~isempty(matlabbatch{1}.spm.stats.con.consess) + spm_jobman('run', matlabbatch); + load(fileSPM); + end + + % report contrasts + for c = 1:nContrasts + iC = indReportPhysContrasts(c); + indContrasts(c) = tapas_physio_check_get_xcon_index(SPM, ... + namesPhysContrasts{iC}); + load(fullfile(pathPhysIO, 'tapas_physio_check_job_report')); + matlabbatch{1}.spm.stats.results.spmmat = cellstr(fileSPM); + matlabbatch{1}.spm.stats.results.conspec.titlestr = [dirSubject ' - ' namesPhysContrasts{iC}]; + matlabbatch{1}.spm.stats.results.conspec.contrasts = indContrasts(c); + + % contrast report correction + matlabbatch{1}.spm.stats.results.conspec.thresh = reportContrastThreshold; + matlabbatch{1}.spm.stats.results.conspec.threshdesc = reportContrastCorrection; + + spm_jobman('run', matlabbatch); % report result + % spm_print(fileReport) + spm_sections(xSPM,hReg, fileStruct); % overlay structural + + % voxel, not world space + if doPlotSliceParallel + spm_orthviews('Space',1) + end + + spm_orthviews('Zoom', fovMillimeter); % zoom to FOV*2 view + spm_orthviews('Interp', 0); % next neighbour interpolation plot + + + if isequal(reportContrastPosition, 'max'); + spm_mip_ui('Jump',spm_mip_ui('FindMIPax'),'glmax'); % goto global max + else + spm_mip_ui('SetCoords', reportContrastPosition, ... + spm_mip_ui('FindMIPax')); % goto global max + end + + + % spm_print always prepend current directory to print-file + % name :-( + [pathReport, filenameReport] = fileparts(fileReport); + pathTmp = pwd; + cd(pathReport); + spm_print(filenameReport); + cd(pathTmp); + end + + stringTitle = ['Assess: ' dirSubject, ' - SPM.xX.X']; + title(regexprep(stringTitle,'_','\\_')); + set(gcf,'Name', stringTitle); + fprintf('good SPM: %s\n', dirSubject); + else % no file, report that + fprintf('no SPM.mat: %s\n', dirSubject); + end + catch + warning(sprintf('Subject ID %d: %s did not run through', s, dirSubject)); + end end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_check_get_regressor_columns.m b/PhysIO/code/assess/tapas_physio_check_get_regressor_columns.m similarity index 96% rename from PhysIO/code/tapas_physio_check_get_regressor_columns.m rename to PhysIO/code/assess/tapas_physio_check_get_regressor_columns.m index 4efe9dcf..2882ac4d 100644 --- a/PhysIO/code/tapas_physio_check_get_regressor_columns.m +++ b/PhysIO/code/assess/tapas_physio_check_get_regressor_columns.m @@ -1,109 +1,108 @@ -function [colPhys, colCard, colResp, colMult, colHRV, colRVT, colRois, colMove, colAll] = ... - tapas_physio_check_get_regressor_columns(SPM, model) -% -% returns indices of physiological regressors in an SPM design-matrix, -% pooled over all sessions for later creation of an F-contrast -% -% INPUT: -% SPM SPM.mat -% model physIO.model-structure -% -% -% OUTPUT: -% colPhys - index vector of all physiological regressor columns in design matrix (from SPM.xX.names) -% colCard - index vector of cardiac regressor columns in design matrix (from SPM.xX.names) -% colResp - index vector of respiratory regressor columns in design matrix (from SPM.xX.names) -% colMult - index vector of interaction cardiac X respiration regressor columns in design matrix (from SPM.xX.names) -% colHRV - index vector of heart rate variability column in design matrix (from SPM.xX.names) -% colRVT - index vector of respiratory volume per time column in design matrix (from SPM.xX.names) -% colRois - index vector of noise rois regressors (from SPM.xX.names) -% colMove - index vector of movement regressor columns in design matrix (from SPM.xX.names) -% colAll - colPhys and colMove (all nuisance regressors!) -% -% Author: Lars Kasper -% Created: 2014-01-21 -% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. -% -% This file is part of the TNU CheckPhysRETROICOR toolbox, which is released under the terms of the GNU General Public -% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . -% -% $Id$ - -nSess = length(SPM.Sess); - -iCard = 1; - -if nargin < 2 - nMove = 6; - nCard = 6; - nResp = 8; - nMult = 4; - nHRV = 0; - nRVT = 0; - nRois = 0; -else - - if model.retroicor.include - - nCard = ~isempty(model.retroicor.order.c); - if nCard - nCard = model.retroicor.order.c*2; - end - - nResp = ~isempty(model.retroicor.order.r); - if nResp - nResp = model.retroicor.order.r*2; - end - - nMult = ~isempty(model.retroicor.order.cr); - if nMult - nMult = model.retroicor.order.cr*4; - end - - - else - nCard = 0; - nResp = 0; - nMult = 0; - end - - - % only if following models were calculated - nRois = model.noise_rois.include.*sum(model.noise_rois.n_components); - nMove = model.movement.include.*model.movement.order; - nHRV = model.hrv.include.*numel(model.hrv.delays); - nRVT = model.rvt.include.*numel(model.rvt.delays); - -end - -cnames = SPM.xX.name'; - -colCard = []; -colResp = []; -colMult = []; -colRois = []; -colMove = []; -colHRV = []; -colRVT = []; -for s = 1:nSess - - cname = ['Sn(' int2str(s) ') R' int2str(iCard)]; - indC = find(strcmp(cnames, cname)); - - colCard = [colCard, indC:(indC+nCard - 1)]; - colResp = [colResp, (indC+nCard):(indC+nCard+nResp - 1)]; - colMult = [colMult, (indC+nCard+nResp):(indC+nCard+nResp+nMult - 1)]; - colHRV = [colHRV, (indC+nCard+nResp+nMult):... - (indC+nCard+nResp+nMult+nHRV-1)]; - colRVT = [colRVT, (indC+nCard+nResp+nMult+nHRV):... - (indC+nCard+nResp+nMult+nHRV+nRVT-1)]; - colRois = [colRois, (indC+nCard+nResp+nMult+nHRV+nRVT):... - (indC+nCard+nResp+nMult+nHRV+nRVT+nRois-1)]; - colMove = [colMove, (indC+nCard+nResp+nMult+nHRV+nRVT+nRois):... - (indC+nCard+nResp+nMult+nHRV+nRVT+nRois+nMove-1)]; -end - -colPhys = [colCard colResp colMult colHRV colRVT colRois]; +function [colPhys, colCard, colResp, colMult, colHRV, colRVT, colRois, colMove, colAll] = ... + tapas_physio_check_get_regressor_columns(SPM, model) +% +% returns indices of physiological regressors in an SPM design-matrix, +% pooled over all sessions for later creation of an F-contrast +% +% INPUT: +% SPM SPM.mat +% model physIO.model-structure +% +% +% OUTPUT: +% colPhys - index vector of all physiological regressor columns in design matrix (from SPM.xX.names) +% colCard - index vector of cardiac regressor columns in design matrix (from SPM.xX.names) +% colResp - index vector of respiratory regressor columns in design matrix (from SPM.xX.names) +% colMult - index vector of interaction cardiac X respiration regressor columns in design matrix (from SPM.xX.names) +% colHRV - index vector of heart rate variability column in design matrix (from SPM.xX.names) +% colRVT - index vector of respiratory volume per time column in design matrix (from SPM.xX.names) +% colRois - index vector of noise rois regressors (from SPM.xX.names) +% colMove - index vector of movement regressor columns in design matrix (from SPM.xX.names) +% colAll - colPhys and colMove (all nuisance regressors!) + +% Author: Lars Kasper +% Created: 2014-01-21 +% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. +% +% This file is part of the TNU CheckPhysRETROICOR toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + + +nSess = length(SPM.Sess); + +iCard = 1; + +if nargin < 2 + nMove = 6; + nCard = 6; + nResp = 8; + nMult = 4; + nHRV = 0; + nRVT = 0; + nRois = 0; +else + + if model.retroicor.include + + nCard = ~isempty(model.retroicor.order.c); + if nCard + nCard = model.retroicor.order.c*2; + end + + nResp = ~isempty(model.retroicor.order.r); + if nResp + nResp = model.retroicor.order.r*2; + end + + nMult = ~isempty(model.retroicor.order.cr); + if nMult + nMult = model.retroicor.order.cr*4; + end + + + else + nCard = 0; + nResp = 0; + nMult = 0; + end + + + % only if following models were calculated + nRois = model.noise_rois.include.*sum(model.noise_rois.n_components); + nMove = model.movement.include.*model.movement.order; + nHRV = model.hrv.include.*numel(model.hrv.delays); + nRVT = model.rvt.include.*numel(model.rvt.delays); + +end + +cnames = SPM.xX.name'; + +colCard = []; +colResp = []; +colMult = []; +colRois = []; +colMove = []; +colHRV = []; +colRVT = []; +for s = 1:nSess + + cname = ['Sn(' int2str(s) ') R' int2str(iCard)]; + indC = find(strcmp(cnames, cname)); + + colCard = [colCard, indC:(indC+nCard - 1)]; + colResp = [colResp, (indC+nCard):(indC+nCard+nResp - 1)]; + colMult = [colMult, (indC+nCard+nResp):(indC+nCard+nResp+nMult - 1)]; + colHRV = [colHRV, (indC+nCard+nResp+nMult):... + (indC+nCard+nResp+nMult+nHRV-1)]; + colRVT = [colRVT, (indC+nCard+nResp+nMult+nHRV):... + (indC+nCard+nResp+nMult+nHRV+nRVT-1)]; + colRois = [colRois, (indC+nCard+nResp+nMult+nHRV+nRVT):... + (indC+nCard+nResp+nMult+nHRV+nRVT+nRois-1)]; + colMove = [colMove, (indC+nCard+nResp+nMult+nHRV+nRVT+nRois):... + (indC+nCard+nResp+nMult+nHRV+nRVT+nRois+nMove-1)]; +end + +colPhys = [colCard colResp colMult colHRV colRVT colRois]; colAll = [colPhys colMove]; \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_check_get_xcon_index.m b/PhysIO/code/assess/tapas_physio_check_get_xcon_index.m similarity index 96% rename from PhysIO/code/tapas_physio_check_get_xcon_index.m rename to PhysIO/code/assess/tapas_physio_check_get_xcon_index.m index 56c4b376..a0f9853a 100644 --- a/PhysIO/code/tapas_physio_check_get_xcon_index.m +++ b/PhysIO/code/assess/tapas_physio_check_get_xcon_index.m @@ -1,37 +1,36 @@ -function indC = tapas_physio_check_get_xcon_index(SPM, cname) -% returns index of contrasts in SPM.xCon, whose name matches cname -% used to find existing contrasts in an SPM.mat -% -% INPUT: -% SPM - SPM.mat-file -% cname - name of contrast to be searched for (string) -% (works for incomplete names/patterns as well, e.g. to find all F-contrasts) -% -% OUTPUT: -% indC - vector of contrast indices in SPM.xCon with matching name -% returns 0, if none found -% -% Author: Lars Kasper -% Created: 2014-01-21 -% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. -% -% This file is part of the TNU CheckPhysRETROICOR toolbox, which is released under the terms of the GNU General Public -% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . -% -% $Id$ -nContrasts = length(SPM.xCon); -cnames = cell(nContrasts,1); -for c = 1:nContrasts - cnames{c} = SPM.xCon(c).name; -end -indC = find(cell2mat(cellfun(@(x) ~isempty(x), strfind(cnames, cname), 'UniformOutput', false))); - -if isempty(indC) - indC = 0; -else - % if multiple regressors of same name found, take first one - indC = indC(1); -end +function indC = tapas_physio_check_get_xcon_index(SPM, cname) +% returns index of contrasts in SPM.xCon, whose name matches cname +% used to find existing contrasts in an SPM.mat +% +% INPUT: +% SPM - SPM.mat-file +% cname - name of contrast to be searched for (string) +% (works for incomplete names/patterns as well, e.g. to find all F-contrasts) +% +% OUTPUT: +% indC - vector of contrast indices in SPM.xCon with matching name +% returns 0, if none found + +% Author: Lars Kasper +% Created: 2014-01-21 +% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. +% +% This file is part of the TNU CheckPhysRETROICOR toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +nContrasts = length(SPM.xCon); +cnames = cell(nContrasts,1); +for c = 1:nContrasts + cnames{c} = SPM.xCon(c).name; +end +indC = find(cell2mat(cellfun(@(x) ~isempty(x), strfind(cnames, cname), 'UniformOutput', false))); + +if isempty(indC) + indC = 0; +else + % if multiple regressors of same name found, take first one + indC = indC(1); +end end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_check_job_contrasts.mat b/PhysIO/code/assess/tapas_physio_check_job_contrasts.mat similarity index 100% rename from PhysIO/code/tapas_physio_check_job_contrasts.mat rename to PhysIO/code/assess/tapas_physio_check_job_contrasts.mat diff --git a/PhysIO/code/tapas_physio_check_job_report.mat b/PhysIO/code/assess/tapas_physio_check_job_report.mat similarity index 100% rename from PhysIO/code/tapas_physio_check_job_report.mat rename to PhysIO/code/assess/tapas_physio_check_job_report.mat diff --git a/PhysIO/code/tapas_physio_check_prepare_job_contrasts.m b/PhysIO/code/assess/tapas_physio_check_prepare_job_contrasts.m similarity index 97% rename from PhysIO/code/tapas_physio_check_prepare_job_contrasts.m rename to PhysIO/code/assess/tapas_physio_check_prepare_job_contrasts.m index 1f538b3b..a95d88f0 100644 --- a/PhysIO/code/tapas_physio_check_prepare_job_contrasts.m +++ b/PhysIO/code/assess/tapas_physio_check_prepare_job_contrasts.m @@ -1,65 +1,64 @@ -function [matlabbatch, indValidContrasts] = tapas_physio_check_prepare_job_contrasts(fileSPM, ... - model, SPM, indReportPhysContrasts, namesPhysContrasts) -% adapts contrast generator for physiogical regressors to actual SPM-file -% (directory & design matrix columns) -% -% -% IN -% fileSpm -% model -% SPM -% indReportPhysContrasts [1, nContrasts] vector of phys contrast ids to be -% reported, in their default order -% indValidContrasts contrasts ids that could be queried with existing -% physIO model -% namesPhysContrasts if contrasts shall be named differently, -% entered here -% Author: Lars Kasper -% Created: 2014-01-21 -% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. -% -% This file is part of the PhysIO toolbox, which is released under the terms of the GNU General Public -% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . -% -% $Id$ - -if nargin < 6 - namesPhysContrasts = tapas_physio_get_contrast_names_default(); -end - -if ~exist('SPM', 'var'), load(fileSPM); end - -[colPhys, colCard, colResp, colMult, colHRV, colRVT, colRois, colMove, colAll] = ... - tapas_physio_check_get_regressor_columns(SPM, model); -con{1} = colPhys; -con{2} = colCard; -con{3} = colResp; -con{4} = colMult; -con{5} = colHRV; -con{6} = colRVT; -con{7} = colRois; -con{8} = colMove; -con{9} = colAll; - -pathCodePhysIO = fileparts(mfilename('fullpath')); -load(fullfile(pathCodePhysIO,'tapas_physio_check_job_contrasts.mat')); -matlabbatch{1}.spm.stats.con.spmmat{1} = fileSPM; - -% execute computation only for non-empty contrasts; -indValidContrasts = intersect(indReportPhysContrasts, ... - find(~cellfun(@isempty, con))); -nContrasts = numel(indValidContrasts); -for c = 1:nContrasts - iC = indValidContrasts(c); - F{c} = zeros(max(con{iC})); - F{c}(sub2ind(size(F{c}),con{iC}, con{iC})) = 1; - matlabbatch{1}.spm.stats.con.consess{c}.fcon.convec{1} = F{c}; - - matlabbatch{1}.spm.stats.con.consess{c}.fcon.name = ... - namesPhysContrasts{indValidContrasts(c)}; -end - -% remove additional contrasts in matlabbatch +function [matlabbatch, indValidContrasts] = tapas_physio_check_prepare_job_contrasts(fileSPM, ... + model, SPM, indReportPhysContrasts, namesPhysContrasts) +% adapts contrast generator for physiogical regressors to actual SPM-file +% (directory & design matrix columns) +% +% +% IN +% fileSpm +% model +% SPM +% indReportPhysContrasts [1, nContrasts] vector of phys contrast ids to be +% reported, in their default order +% indValidContrasts contrasts ids that could be queried with existing +% physIO model +% namesPhysContrasts if contrasts shall be named differently, +% entered here +% Author: Lars Kasper +% Created: 2014-01-21 +% Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. +% +% This file is part of the PhysIO toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + + +if nargin < 6 + namesPhysContrasts = tapas_physio_get_contrast_names_default(); +end + +if ~exist('SPM', 'var'), load(fileSPM); end + +[colPhys, colCard, colResp, colMult, colHRV, colRVT, colRois, colMove, colAll] = ... + tapas_physio_check_get_regressor_columns(SPM, model); +con{1} = colPhys; +con{2} = colCard; +con{3} = colResp; +con{4} = colMult; +con{5} = colHRV; +con{6} = colRVT; +con{7} = colRois; +con{8} = colMove; +con{9} = colAll; + +pathCodePhysIO = fileparts(mfilename('fullpath')); +load(fullfile(pathCodePhysIO,'tapas_physio_check_job_contrasts.mat')); +matlabbatch{1}.spm.stats.con.spmmat{1} = fileSPM; + +% execute computation only for non-empty contrasts; +indValidContrasts = intersect(indReportPhysContrasts, ... + find(~cellfun(@isempty, con))); +nContrasts = numel(indValidContrasts); +for c = 1:nContrasts + iC = indValidContrasts(c); + F{c} = zeros(max(con{iC})); + F{c}(sub2ind(size(F{c}),con{iC}, con{iC})) = 1; + matlabbatch{1}.spm.stats.con.consess{c}.fcon.convec{1} = F{c}; + + matlabbatch{1}.spm.stats.con.consess{c}.fcon.name = ... + namesPhysContrasts{indValidContrasts(c)}; +end + +% remove additional contrasts in matlabbatch matlabbatch{1}.spm.stats.con.consess(nContrasts+1:end) = []; \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_compute_tsnr_gains.m b/PhysIO/code/assess/tapas_physio_compute_tsnr_gains.m similarity index 99% rename from PhysIO/code/tapas_physio_compute_tsnr_gains.m rename to PhysIO/code/assess/tapas_physio_compute_tsnr_gains.m index 66199584..3e829f19 100644 --- a/PhysIO/code/tapas_physio_compute_tsnr_gains.m +++ b/PhysIO/code/assess/tapas_physio_compute_tsnr_gains.m @@ -29,7 +29,7 @@ % tapas_physio_compute_tsnr_gains % % See also tapas_physio_compute_tsnr_spm spm_write_residuals -% + % Author: Lars Kasper % Created: 2015-07-03 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, @@ -39,8 +39,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + diff --git a/PhysIO/code/tapas_physio_compute_tsnr_spm.m b/PhysIO/code/assess/tapas_physio_compute_tsnr_spm.m similarity index 99% rename from PhysIO/code/tapas_physio_compute_tsnr_spm.m rename to PhysIO/code/assess/tapas_physio_compute_tsnr_spm.m index 47382bc6..71f8a863 100644 --- a/PhysIO/code/tapas_physio_compute_tsnr_spm.m +++ b/PhysIO/code/assess/tapas_physio_compute_tsnr_spm.m @@ -74,11 +74,11 @@ % compute_tsnr_spm % % See also spm_write_residuals spm_FcUtil -% + % Author: Lars Kasper % Created: 2014-12-10 % Copyright (C) 2014 Institute for Biomedical Engineering, ETH/Uni Zurich. -% $Id$ + if nargin < 2 iC = 0; diff --git a/PhysIO/code/tapas_physio_count_physio_regressors.m b/PhysIO/code/assess/tapas_physio_count_physio_regressors.m similarity index 97% rename from PhysIO/code/tapas_physio_count_physio_regressors.m rename to PhysIO/code/assess/tapas_physio_count_physio_regressors.m index 15fd3a9c..175bc610 100644 --- a/PhysIO/code/tapas_physio_count_physio_regressors.m +++ b/PhysIO/code/assess/tapas_physio_count_physio_regressors.m @@ -17,7 +17,7 @@ % tapas_physio_report_contrasts % % See also -% + % Author: Lars Kasper % Created: 2014-10-16 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -26,8 +26,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ + + model = physio.model; diff --git a/PhysIO/code/tapas_physio_create_missing_physio_contrasts.m b/PhysIO/code/assess/tapas_physio_create_missing_physio_contrasts.m similarity index 99% rename from PhysIO/code/tapas_physio_create_missing_physio_contrasts.m rename to PhysIO/code/assess/tapas_physio_create_missing_physio_contrasts.m index 71e4609c..d544e94a 100644 --- a/PhysIO/code/tapas_physio_create_missing_physio_contrasts.m +++ b/PhysIO/code/assess/tapas_physio_create_missing_physio_contrasts.m @@ -30,7 +30,7 @@ % % See also tapas_physio_check_prepare_job_contrasts % See also tapas_physio_report_contrasts -% + % Author: Lars Kasper % Created: 2016-10-03 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -40,8 +40,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 3 namesPhysContrasts = tapas_physio_get_contrast_names_default(); diff --git a/PhysIO/code/tapas_physio_get_contrast_names_default.m b/PhysIO/code/assess/tapas_physio_get_contrast_names_default.m similarity index 99% rename from PhysIO/code/tapas_physio_get_contrast_names_default.m rename to PhysIO/code/assess/tapas_physio_get_contrast_names_default.m index b669a110..c90b14ff 100644 --- a/PhysIO/code/tapas_physio_get_contrast_names_default.m +++ b/PhysIO/code/assess/tapas_physio_get_contrast_names_default.m @@ -11,7 +11,7 @@ % tapas_physio_get_contrast_names_default % % See also tapas_physio_report_contrasts tapas_physio_compute_tsnr_gains -% + % Author: Lars Kasper % Created: 2016-10-03 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -21,8 +21,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + namesPhysContrasts = { 'All Phys' diff --git a/PhysIO/code/tapas_physio_overlay_contrasts.m b/PhysIO/code/assess/tapas_physio_overlay_contrasts.m similarity index 97% rename from PhysIO/code/tapas_physio_overlay_contrasts.m rename to PhysIO/code/assess/tapas_physio_overlay_contrasts.m index 3690d870..286a1973 100644 --- a/PhysIO/code/tapas_physio_overlay_contrasts.m +++ b/PhysIO/code/assess/tapas_physio_overlay_contrasts.m @@ -53,7 +53,7 @@ % function % % See also tapas_physio_report_contrasts -% + % Author: Lars Kasper % Created: 2017-01-05 % Copyright (C) 2017 Institute for Biomedical Engineering @@ -65,8 +65,8 @@ % (either version 3 or, at your option, any later version). % For further details, see the file COPYING or % . -% -% $Id: new_function2.m 354 2013-12-02 22:21:41Z kasperla $ + + %% START #MOD ============================================================= @@ -186,9 +186,9 @@ end cd(pathBeforeReport); -titstr = [titleGraphicsWindow, ' - SPM.xX.X']; -title(regexprep(titstr,'_','\\_')); -set(gcf,'Name', titstr); +stringTitle = ['Assess: ' titleGraphicsWindow, ' - SPM.xX.X']; +title(regexprep(stringTitle,'_','\\_')); +set(gcf,'Name', stringTitle); set(0, 'DefaultFigureWindowStyle', tmpWindowStyle); diff --git a/PhysIO/code/tapas_physio_report_contrasts.m b/PhysIO/code/assess/tapas_physio_report_contrasts.m similarity index 99% rename from PhysIO/code/tapas_physio_report_contrasts.m rename to PhysIO/code/assess/tapas_physio_report_contrasts.m index 5cb67758..b13045a7 100644 --- a/PhysIO/code/tapas_physio_report_contrasts.m +++ b/PhysIO/code/assess/tapas_physio_report_contrasts.m @@ -70,7 +70,7 @@ % tapas_physio_report_contrasts % % See also tapas_physio_overlay_contrasts -% + % Author: Lars Kasper % Created: 2014-10-16 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -79,8 +79,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ + + %% START #MOD ============================================================= diff --git a/PhysIO/code/tapas_physio_review.m b/PhysIO/code/assess/tapas_physio_review.m similarity index 98% rename from PhysIO/code/tapas_physio_review.m rename to PhysIO/code/assess/tapas_physio_review.m index 7058d21d..759c4fcf 100644 --- a/PhysIO/code/tapas_physio_review.m +++ b/PhysIO/code/assess/tapas_physio_review.m @@ -28,7 +28,7 @@ % tapas_physio_review % % See also -% + % Author: Lars Kasper % Created: 2016-10-27 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -38,8 +38,8 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 775 2015-07-17 10:52:58Z kasperla $ + + if ischar(physio) % file name load(physio); diff --git a/PhysIO/code/tapas_physio_sort_images_by_cardiac_phase.m b/PhysIO/code/assess/tapas_physio_sort_images_by_cardiac_phase.m similarity index 97% rename from PhysIO/code/tapas_physio_sort_images_by_cardiac_phase.m rename to PhysIO/code/assess/tapas_physio_sort_images_by_cardiac_phase.m index dbd536a7..fbf01832 100644 --- a/PhysIO/code/tapas_physio_sort_images_by_cardiac_phase.m +++ b/PhysIO/code/assess/tapas_physio_sort_images_by_cardiac_phase.m @@ -61,7 +61,7 @@ % tapas_physio_sort_images_by_cardiac_phase % % See also -% + % Author: Lars Kasper % Created: 2014-06-15 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -70,8 +70,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ + + if nargin < 5 verbose = false; end @@ -116,7 +116,8 @@ [h, cardiacPhaseArray] = hist(c_phase, nCardiacPhases); if verbose - stringTitle = 'Number of slice/volume occurences of each cardiac phase'; + stringTitle = 'Assess: Number of slice/volume occurences of each cardiac + phase'; fh(1) = tapas_physio_get_default_fig_params(); set(gcf, 'Name', stringTitle); bar(cardiacPhaseArray,h); xlabel('cardiac phase'); ylabel('counts'); @@ -155,7 +156,7 @@ %% Plot cardiac phase per slice and volume if verbose - stringTitle = 'Cardiac phase per slice and volume'; + stringTitle = 'Assess: Cardiac phase per slice and volume'; fh(2) = tapas_physio_get_default_fig_params(); set(gcf, 'Name', stringTitle); imagesc(iPhaseVolSliceArray); @@ -186,7 +187,7 @@ end if verbose - stringTitle = 'Count of volumes falling into phase/slice bin'; + stringTitle = 'Assess: Count of volumes falling into phase/slice bin'; fh(3) = tapas_physio_get_default_fig_params(); set(fh(3), 'Name', stringTitle); imagesc(nVolPerPhaseSlice); @@ -260,7 +261,7 @@ % spm_img_load % % See also -% + % Author: Lars Kasper % Created: 2013-08-01 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -316,7 +317,7 @@ % reuse_nifti_hdr % % See also -% + % Author: Lars Kasper % Created: 2013-11-12 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. diff --git a/PhysIO/code/tapas_physio_sort_images_by_respiratory_phase.m b/PhysIO/code/assess/tapas_physio_sort_images_by_respiratory_phase.m similarity index 96% rename from PhysIO/code/tapas_physio_sort_images_by_respiratory_phase.m rename to PhysIO/code/assess/tapas_physio_sort_images_by_respiratory_phase.m index 0e38b587..6e0bdc85 100644 --- a/PhysIO/code/tapas_physio_sort_images_by_respiratory_phase.m +++ b/PhysIO/code/assess/tapas_physio_sort_images_by_respiratory_phase.m @@ -47,7 +47,7 @@ % tapas_physio_sort_images_by_respiratory_phase % % See also -% + % Author: Lars Kasper % Created: 2014-06-15 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -56,8 +56,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 5 verbose = false; end @@ -97,7 +96,9 @@ [h, respPhaseArray] = hist(r_phase, nRespiratoryPhases); if verbose + stringTitle = 'Assess: Number of slice/volume occurences of each respiratory phase'; fh(1) = tapas_physio_get_default_fig_params(); + set(gcf, 'Name', stringTitle); bar(respPhaseArray,h); xlabel('respiratory phase'); ylabel('counts'); end @@ -133,7 +134,9 @@ %% Plot respiratory phase per slice and volume if verbose + stringTitle = 'Assess: Respiratory phase per slice and volume'; fh(2) = tapas_physio_get_default_fig_params(); + set(gcf, 'Name', stringTitle); imagesc(iPhaseVolSliceArray); xlabel('Volumes'); ylabel('slice'); title('respiratory phase per slice and volume'); @@ -157,7 +160,7 @@ end if verbose - stringTitle = 'Count of volumes falling into phase/slice bin'; + stringTitle = 'Assess: Count of volumes falling into phase/slice bin'; fh(3) = tapas_physio_get_default_fig_params(); set(fh(3), 'Name', stringTitle); imagesc(nVolPerPhaseSlice); @@ -230,11 +233,11 @@ % spm_img_load % % See also -% + % Author: Lars Kasper % Created: 2013-08-01 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. -% $Id$ + if nargin < 2 verbose = false; end @@ -286,11 +289,11 @@ % reuse_nifti_hdr % % See also -% + % Author: Lars Kasper % Created: 2013-11-12 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. -% $Id$ + W = spm_vol(fnIn); nVols = length(W); if nargin < 4 diff --git a/PhysIO/code/tapas_physio_censor_unreliable_regressor_parts_retroicor.m b/PhysIO/code/model/tapas_physio_censor_unreliable_regressor_parts_retroicor.m similarity index 97% rename from PhysIO/code/tapas_physio_censor_unreliable_regressor_parts_retroicor.m rename to PhysIO/code/model/tapas_physio_censor_unreliable_regressor_parts_retroicor.m index 57d07a4e..5b125f62 100644 --- a/PhysIO/code/tapas_physio_censor_unreliable_regressor_parts_retroicor.m +++ b/PhysIO/code/model/tapas_physio_censor_unreliable_regressor_parts_retroicor.m @@ -14,7 +14,7 @@ % tapas_physio_censor_unreliable_regressor_parts_retroicor % % See also -% + % Author: Lars Kasper, after fruitful exchange with % [Daniel Hoffmann Ayala](https://github.com/DanielHoffmannAyala) % @@ -65,7 +65,7 @@ end if doPlot - stringTitle = ['Censoring of RETROICOR regressors in intervals of ' ... + stringTitle = ['Model: Censoring of RETROICOR regressors in intervals of ' ... 'unreliable physiological recordings']; verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); set(gcf, 'Name', stringTitle); @@ -90,7 +90,7 @@ end xlabel('time (s)') - suptitle(stringTitle); + tapas_physio_suptitle(stringTitle); end end diff --git a/PhysIO/code/tapas_physio_create_hrv_regressors.m b/PhysIO/code/model/tapas_physio_create_hrv_regressors.m similarity index 98% rename from PhysIO/code/tapas_physio_create_hrv_regressors.m rename to PhysIO/code/model/tapas_physio_create_hrv_regressors.m index 4efb2b8c..fb5d777e 100644 --- a/PhysIO/code/tapas_physio_create_hrv_regressors.m +++ b/PhysIO/code/model/tapas_physio_create_hrv_regressors.m @@ -25,7 +25,7 @@ % physio_out.sqpar); % % See also tapas_physio_hr tapas_physio_crf -% + % Author: Lars Kasper % Created: 2013-07-26 % Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -34,8 +34,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 3 physio = tapas_physio_new; model_hrv = physio.model.hrv; @@ -55,7 +54,7 @@ if verbose.level>=2 verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf, 'Name', 'Regressors Heart Rate: HRV X CRF'); + set(gcf, 'Name', 'Model: Regressors Heart Rate: HRV X CRF'); subplot(2,2,1) plot(sample_points,hr,'r');xlabel('time (seconds)'); title('Heart Rate'); diff --git a/PhysIO/code/tapas_physio_create_movement_regressors.m b/PhysIO/code/model/tapas_physio_create_movement_regressors.m similarity index 99% rename from PhysIO/code/tapas_physio_create_movement_regressors.m rename to PhysIO/code/model/tapas_physio_create_movement_regressors.m index 2894ca75..4f10a58f 100644 --- a/PhysIO/code/tapas_physio_create_movement_regressors.m +++ b/PhysIO/code/model/tapas_physio_create_movement_regressors.m @@ -68,7 +68,7 @@ % tapas_physio_create_movement_regressors % % See also tapas_physio_new tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2015-07-10 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, diff --git a/PhysIO/code/model/tapas_physio_create_noise_rois_regressors.m b/PhysIO/code/model/tapas_physio_create_noise_rois_regressors.m new file mode 100644 index 00000000..afca9898 --- /dev/null +++ b/PhysIO/code/model/tapas_physio_create_noise_rois_regressors.m @@ -0,0 +1,270 @@ +function [R_noise_rois, noise_rois, verbose] = tapas_physio_create_noise_rois_regressors(... + noise_rois, verbose) +% Compute physiological regressors by extracting principal components of +% the preprocessed fMRI time series from anatomically defined noise ROIS (e.g. CSF) +% +% [R_noise_rois, verbose] = tapas_physio_create_noise_rois_regressors(... +% noise_rois, verbose) +% +% NOTE: The mean of all time series in each ROI will also be added as a regressor +% automatically +% +% Approach similar to the one described as aCompCor: +% Behzadi, Y., Restom, K., Liau, J., Liu, T.T., 2007. +% A component based noise correction method (CompCor) for BOLD and +% perfusion based fMRI. NeuroImage 37, 90?101. +% doi:10.1016/j.neuroimage.2007.04.042 +% +% IN +% physio.model.noise_rois +% OUT +% +% EXAMPLE +% tapas_physio_create_noise_rois_regressors +% +% See also spm_ov_roi + +% Author: Lars Kasper +% Created: 2015-07-22 +% Copyright (C) 2015 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public +% License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + + +% TODO: components/mean and spatial loads in ROIs + +global st % to overlay the final ROIs, using spm_orthviews + +tapas_physio_strip_fields(noise_rois); + +if isempty(fmri_files) || isempty(roi_files) + R_noise_rois = []; + return +end + +if ~iscell(fmri_files) + fmri_files = cellstr(fmri_files); +end + +if ~iscell(roi_files) + roi_files = cellstr(roi_files); +end + +nRois = numel(roi_files); + +if numel(thresholds) == 1 + thresholds = repmat(thresholds, 1, nRois); +end + +if numel(n_voxel_crop) == 1 + n_voxel_crop = repmat(n_voxel_crop, 1, nRois); +end + +if numel(n_components) == 1 + n_components = repmat(n_components, 1, nRois); +end + +% Show the noise ROIs before reslice, threshold and erosion +if verbose.level >= 2 + spm_check_registration( roi_files{:} ) +end + +Vimg = []; for iFile = 1:numel(fmri_files), Vimg = [Vimg; spm_vol(fmri_files{iFile})];end +Yimg = spm_read_vols(Vimg); + +nVolumes = size(Yimg, 4); +Yimg = reshape(Yimg, [], nVolumes); + +R_noise_rois = []; +for r = 1:nRois + + nComponents = n_components(r); + threshold = thresholds(r); + fileRoi = roi_files{r}; + + Vroi = spm_vol(fileRoi); + + hasRoiDifferentGeometry = any(any(abs(Vroi.mat - Vimg(1).mat) > 1e-5)) | ... + any(Vroi.dim-Vimg(1).dim(1:3)); + perform_coreg = strcmpi(force_coregister,'Yes') || isequal(force_coregister, 1); + + % Force coregistration ? + if ~perform_coreg && hasRoiDifferentGeometry % still check the geometry !! very important !! + perform_coreg = true; + warning(sprintf('fMRI volume and noise ROI have different orientation : \n %s \n %s \n', Vimg(1).fname, Vroi.fname)); %#ok + end + + if perform_coreg + + % estimate & reslice to same geometry + matlabbatch{1}.spm.spatial.coreg.estwrite.ref = { sprintf('%s,1',fmri_files{1}) }; % select the first volume + matlabbatch{1}.spm.spatial.coreg.estwrite.source = roi_files(r); + matlabbatch{1}.spm.spatial.coreg.estwrite.other = {''}; + matlabbatch{1}.spm.spatial.coreg.estwrite.eoptions.cost_fun = 'nmi'; + matlabbatch{1}.spm.spatial.coreg.estwrite.eoptions.sep = [4 2]; + matlabbatch{1}.spm.spatial.coreg.estwrite.eoptions.tol = [0.02 0.02 0.02 0.001 0.001 0.001 0.01 0.01 0.01 0.001 0.001 0.001]; + matlabbatch{1}.spm.spatial.coreg.estwrite.eoptions.fwhm = [7 7]; + matlabbatch{1}.spm.spatial.coreg.estwrite.roptions.interp = 4; + matlabbatch{1}.spm.spatial.coreg.estwrite.roptions.wrap = [0 0 0]; + matlabbatch{1}.spm.spatial.coreg.estwrite.roptions.mask = 0; + matlabbatch{1}.spm.spatial.coreg.estwrite.roptions.prefix = 'r'; + + spm_jobman('run', matlabbatch); + + % update header link to new reslice mask-file + Vroi = spm_vol(spm_file(fileRoi, 'prefix', 'r')); + end + + roi = spm_read_vols(Vroi); % 3D matrix of the ROI + roi(roi < threshold) = 0; + roi(roi >= threshold) = 1; + + nVoxelsInRoi = sum(roi(:)>0); + + if nVoxelsInRoi == 0 + verbose = tapas_physio_log(sprintf(['No voxels in Noise ROI mask no. %d.\n' ... + 'Please reduce threshold %f!'], ... + r, threshold), verbose, 2); + elseif (nComponents >= 1 && nVoxelsInRoi < (nComponents + 1)) % less voxels in roi than PCA components (and mean) requested + verbose = tapas_physio_log(sprintf(['Not enough voxels in Noise ROI mask no. %d\n' ... + '%d voxels remain, but %d (+1 for mean) components requested.\n' ... + 'Please reduce threshold %f!'], r, nVoxelsInRoi, ... + nComponents, threshold), verbose, 2); + end + + % crop pixel, if desired + for iter = 1 : n_voxel_crop(r) + roi = spm_erode(roi); % using spm_erode, a compiled mex file + % roi= imerode(roi, strel('sphere', 1)); % using imerode (+ strel) from Image Processing Toolbox + % NB : the result is exactly the same with spm_erode or imerode + nVoxelsInRoi = sum(roi(:)>0); + if nVoxelsInRoi == 0 + verbose = tapas_physio_log(sprintf(['No voxels in Noise ROI mask no. %d\n' ... + 'after eroding %d pixel(s); Please reduce nVoxels for cropping!'], ... + r, iter), verbose, 2); + elseif (nComponents >= 1 && nVoxelsInRoi < (nComponents + 1)) % less voxels in roi than PCA components (and mean) requested + verbose = tapas_physio_log(sprintf(['Not enough voxels in Noise ROI mask no. %d\n' ... + 'after eroding %d pixel(s); %d voxels remain, but %d (+1 for mean) ' ... + 'components requested.\nPlease reduce nVoxels for cropping!'], ... + r, iter, nVoxelsInRoi, nComponents), verbose, 2); + end + end + + % Write the final noise ROIs in a volume, after relice, threshold and erosion + [fpRoi,fnRoi] = fileparts(Vroi.fname); + Vroi.fname = fullfile(fpRoi, sprintf('noiseROI_%s.nii', fnRoi)); + spm_write_vol(Vroi,roi); + + % Overlay the final noise ROI (code from spm_orthviews:add_c_image) + if verbose.level >= 2 + spm_check_registration( roi_files{:} ); + spm_orthviews('addcolouredimage',r,Vroi.fname ,[1 0 0]); + hlabel = sprintf('%s (%s)',Vroi.fname ,'Red'); + c_handle = findobj(findobj(st.vols{r}.ax{1}.cm,'label','Overlay'),'Label','Remove coloured blobs'); + ch_c_handle = get(c_handle,'Children'); + set(c_handle,'Visible','on'); + uimenu(ch_c_handle(2),'Label',hlabel,'ForegroundColor',[1 0 0],... + 'Callback','c = get(gcbo,''UserData'');spm_orthviews(''context_menu'',''remove_c_blobs'',2,c);'); + spm_orthviews('redraw') + end + + Yroi = Yimg(roi(:)==1, :); % Time series of the fMRI volume in the noise ROIs + + %% mean and linear trend removal according to CompCor pub + % design matrix + X = ones(nVolumes,1); + X(:,2) = 1:nVolumes; + % fit 1st order polynomial to time series data in each voxel + for n_roi_voxel = 1:size(Yroi,1) + % extract data + raw_Y = Yroi(n_roi_voxel,:)'; + % estimate betas + beta = X\raw_Y; + % fitted data + fit_Y = X*beta; + % detrend data + detrend_Y = raw_Y - fit_Y; + + % overwrite Yroi + Yroi(n_roi_voxel,:) = detrend_Y; + end + + + + + + %% + + + % COEFF = [nVolumes, nPCs] principal components (PCs) ordered by variance + % explained + % SCORE = [nVoxel, nPCs] loads of each component in each voxel, i.e. + % specific contribution of each component in + % a voxel's variance + % LATENT = [nPCs, 1] eigenvalues of data covariance matrix, + % stating how much variance was explained by + % each PC overall + % TSQUARED = [nVoxels,1] Hotelling's T-Squared test whether PC + % explained significant variance in a voxel + % EXPLAINED = [nPCs, 1] relative amount of variance explained (in + % percent) by each component + % MU = [1, nVolumes] mean of all time series + [COEFF, SCORE, LATENT, TSQUARED, EXPLAINED, MU] = pca(Yroi); + + % components defined via threshold of variance explained + if nComponents < 1 + nComponents = find(cumsum(EXPLAINED)/100 > nComponents, 1, 'first'); + end + + % save to return + noise_rois.n_components(r) = nComponents + 1; % + 1 for mean + + % Take mean and some components into noise regressor + R = MU'; + R = [R, COEFF(:,1:nComponents)]; + + nRegressors = size(R,2); + + % z-transform + stringLegend = cell(nRegressors,1); + for c = 1:nRegressors + R(:,c) = (R(:,c) - mean(R(:,c)))/std(R(:,c)); + + if c > 1 + stringLegend{c} = ... + sprintf('Principal component %d (%7.4f %% variance explained)', ... + c-1, EXPLAINED(c-1)); + else + stringLegend{c} = 'Mean time series of all voxels'; + end + end + + if verbose.level >=2 + stringFig = sprintf('Model: Noise\\_rois: Extracted principal components for ROI %d', r); + verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); + set(gcf, 'Name', stringFig); + plot(R); + legend(stringLegend); + title(stringFig); + xlabel('scans'); + ylabel('PCs, mean-centred and std-scaled'); + end + + % write away extracted PC-loads & roi of extraction + [fpRoi,fnRoi] = fileparts(Vroi(1).fname); + fpFmri = fileparts(Vimg(1).fname); + for c = 1:nComponents + Vpc = Vroi; + Vpc.fname = fullfile(fpFmri, sprintf('pc%02d_scores_%s.nii',c, fnRoi)); + % saved as float, since was masked before + Vpc.dt = [spm_type('float32') 1]; + pcScores = zeros(Vpc.dim); + pcScores(roi(:)==1) = SCORE(:, c); + spm_write_vol(Vpc, pcScores); + end + R_noise_rois = [R_noise_rois, R]; +end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_create_retroicor_regressors.m b/PhysIO/code/model/tapas_physio_create_retroicor_regressors.m similarity index 99% rename from PhysIO/code/tapas_physio_create_retroicor_regressors.m rename to PhysIO/code/model/tapas_physio_create_retroicor_regressors.m index 899d634c..23b5b711 100644 --- a/PhysIO/code/tapas_physio_create_retroicor_regressors.m +++ b/PhysIO/code/model/tapas_physio_create_retroicor_regressors.m @@ -38,8 +38,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% variable renaming if ~exist('verbose', 'var') diff --git a/PhysIO/code/tapas_physio_create_rvt_regressors.m b/PhysIO/code/model/tapas_physio_create_rvt_regressors.m similarity index 98% rename from PhysIO/code/tapas_physio_create_rvt_regressors.m rename to PhysIO/code/model/tapas_physio_create_rvt_regressors.m index fddfceac..cc33150b 100644 --- a/PhysIO/code/tapas_physio_create_rvt_regressors.m +++ b/PhysIO/code/model/tapas_physio_create_rvt_regressors.m @@ -25,7 +25,7 @@ % [convHRV, hr] = tapas_physio_create_hrv_regressor(physio_out.ons_secs, physio_out.sqpar); % % See also tapas_physio_rvt tapas_physio_rrf -% + % Author: Lars Kasper % Created: 2014-01-20 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -34,8 +34,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 3 physio = tapas_physio_new; model_rvt = physio.model.rvt; @@ -57,7 +56,7 @@ if verbose.level >=2 verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf, 'Name', 'Convolution Respiration RVT X RRF'); + set(gcf, 'Name', 'Model: Convolution Respiration RVT X RRF'); subplot(2,2,1) plot(sample_points,rvt, 'g');xlabel('time (seconds)'); title('Respiratory volume per time'); diff --git a/PhysIO/code/tapas_physio_crf.m b/PhysIO/code/model/tapas_physio_crf.m similarity index 99% rename from PhysIO/code/tapas_physio_crf.m rename to PhysIO/code/model/tapas_physio_crf.m index 0b7d7913..aa67dd0f 100644 --- a/PhysIO/code/tapas_physio_crf.m +++ b/PhysIO/code/model/tapas_physio_crf.m @@ -23,7 +23,7 @@ % figure;plot(t,crf); % % See also tapas_physio_rrf -% + % Author: Lars Kasper % Created: 2013-07-26 % Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -32,6 +32,5 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + crf = 0.6*t.^2.7.*exp(-t/1.6) - 16/sqrt(2*pi*9).*exp(-1/2.*(t-12).^2/9); diff --git a/PhysIO/code/tapas_physio_downsample_phase.m b/PhysIO/code/model/tapas_physio_downsample_phase.m similarity index 98% rename from PhysIO/code/tapas_physio_downsample_phase.m rename to PhysIO/code/model/tapas_physio_downsample_phase.m index e944a374..4f04bfca 100644 --- a/PhysIO/code/tapas_physio_downsample_phase.m +++ b/PhysIO/code/model/tapas_physio_downsample_phase.m @@ -7,8 +7,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + n = zeros(size(tsample)); for t=1:length(tsample) diff --git a/PhysIO/code/tapas_physio_get_fourier_expansion.m b/PhysIO/code/model/tapas_physio_get_fourier_expansion.m similarity index 99% rename from PhysIO/code/tapas_physio_get_fourier_expansion.m rename to PhysIO/code/model/tapas_physio_get_fourier_expansion.m index c5c21468..85dc843b 100644 --- a/PhysIO/code/tapas_physio_get_fourier_expansion.m +++ b/PhysIO/code/model/tapas_physio_get_fourier_expansion.m @@ -23,8 +23,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % if (order < 1) %well, not correct in a strict sense, but more convenient than returning a constant phase_expansion = [] diff --git a/PhysIO/code/tapas_physio_get_movement_quality_measures.m b/PhysIO/code/model/tapas_physio_get_movement_quality_measures.m similarity index 99% rename from PhysIO/code/tapas_physio_get_movement_quality_measures.m rename to PhysIO/code/model/tapas_physio_get_movement_quality_measures.m index 38465afa..9735723f 100644 --- a/PhysIO/code/tapas_physio_get_movement_quality_measures.m +++ b/PhysIO/code/model/tapas_physio_get_movement_quality_measures.m @@ -46,7 +46,7 @@ % tapas_physio_get_movement_quality_measures % % See also -% + % Author: Lars Kasper % Created: 2018-02-21 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, diff --git a/PhysIO/code/tapas_physio_hr.m b/PhysIO/code/model/tapas_physio_hr.m similarity index 99% rename from PhysIO/code/tapas_physio_hr.m rename to PhysIO/code/model/tapas_physio_hr.m index a56df0f7..c88caf6f 100644 --- a/PhysIO/code/tapas_physio_hr.m +++ b/PhysIO/code/model/tapas_physio_hr.m @@ -21,7 +21,7 @@ % hr = tapas_physio_hr(physio_out.ons_secs.cpulse,physio_out.ons_secs.svolpulse) % % See also tapas_physio_create_hrv_regressor -% + % Author: Lars Kasper % Created: 2013-07-26 % Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -30,8 +30,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + n = length(t); diff --git a/PhysIO/code/tapas_physio_load_other_multiple_regressors.m b/PhysIO/code/model/tapas_physio_load_other_multiple_regressors.m similarity index 99% rename from PhysIO/code/tapas_physio_load_other_multiple_regressors.m rename to PhysIO/code/model/tapas_physio_load_other_multiple_regressors.m index cc088810..ec29de9d 100644 --- a/PhysIO/code/tapas_physio_load_other_multiple_regressors.m +++ b/PhysIO/code/model/tapas_physio_load_other_multiple_regressors.m @@ -14,7 +14,7 @@ % tapas_physio_load_other_multiple_regressors % % See also tapas_physio_orthogonalise_physiological_regressors tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2013-02-21 % @@ -24,8 +24,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + [fp, fn, fs] = fileparts(filename_other_regressors); if ~exist(filename_other_regressors, 'file') diff --git a/PhysIO/code/tapas_physio_orthogonalise_physiological_regressors.m b/PhysIO/code/model/tapas_physio_orthogonalise_physiological_regressors.m similarity index 94% rename from PhysIO/code/tapas_physio_orthogonalise_physiological_regressors.m rename to PhysIO/code/model/tapas_physio_orthogonalise_physiological_regressors.m index 5bc7a3d3..d0ff0b03 100644 --- a/PhysIO/code/tapas_physio_orthogonalise_physiological_regressors.m +++ b/PhysIO/code/model/tapas_physio_orthogonalise_physiological_regressors.m @@ -36,7 +36,7 @@ % tapas_physio_orthogonalise_physiological_regressors % % See also -% + % Author: Lars Kasper % Created: 2013-02-21 % Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -45,8 +45,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + R_non_orth = [cardiac_sess, respire_sess, mult_sess input_R]; if isempty(R_non_orth) @@ -71,17 +70,17 @@ %% Plot orthogonalisation if verbose.level verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf, 'Name', 'RETROICOR GLM regressors'); + set(gcf, 'Name', 'Model: RETROICOR GLM regressors'); switch lower(orthogonalise) case {'n', 'none'} imagesc(R); - title({'Physiological regressor matrix for GLM', ... + title({'Model: Physiological regressor matrix for GLM', ... '- including input confound regressors -'}); colormap gray; xlabel('regressor');ylabel('scan volume'); otherwise - subplot(1,3,1); imagesc(R); title({'Physiological regressor matrix for GLM'... + subplot(1,3,1); imagesc(R); title({'Model: Physiological regressor matrix for GLM'... ' - specified regressors orthogonalized - '}); colormap gray; xlabel('regressor');ylabel('scan volume'); subplot(1,3,2); diff --git a/PhysIO/code/tapas_physio_rrf.m b/PhysIO/code/model/tapas_physio_rrf.m similarity index 99% rename from PhysIO/code/tapas_physio_rrf.m rename to PhysIO/code/model/tapas_physio_rrf.m index b78f87ac..9e5b3102 100644 --- a/PhysIO/code/tapas_physio_rrf.m +++ b/PhysIO/code/model/tapas_physio_rrf.m @@ -25,7 +25,7 @@ % figure;plot(t,rrf); % % See also tapas_physio_crf -% + % Author: Lars Kasper % Created: 2013-07-26 % Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -34,6 +34,5 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + rrf = 0.6*t.^2.1.*exp(-t/1.6) - 0.0023*t.^3.54.*exp(-t/4.25); diff --git a/PhysIO/code/tapas_physio_rvt.m b/PhysIO/code/model/tapas_physio_rvt.m similarity index 98% rename from PhysIO/code/tapas_physio_rvt.m rename to PhysIO/code/model/tapas_physio_rvt.m index 28d6a46c..1dd61759 100644 --- a/PhysIO/code/tapas_physio_rvt.m +++ b/PhysIO/code/model/tapas_physio_rvt.m @@ -36,7 +36,7 @@ % [rvt, rpulse] = tapas_physio_rvt(fr, t) % % See also tapas_physio_create_rvt_regressor -% + % Author: Lars Kasper % Created: 2014-01-20 % Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -45,8 +45,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + dt = t(2)-t(1); @@ -86,7 +85,7 @@ if verbose.level>=2 verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf, 'Name', 'Respiratory Volume per Time'); + set(gcf, 'Name', 'Model: Respiratory Volume per Time'); hp(1) = plot(t,fr, 'g'); hold all; stem(timeRpulseMax, maxFr*ones(nMax,1),'b'); stem(timeRpulseMin, -maxFr*ones(nMin,1),'r'); diff --git a/PhysIO/code/tapas_physio_scaleorthmean_regressors.m b/PhysIO/code/model/tapas_physio_scaleorthmean_regressors.m similarity index 99% rename from PhysIO/code/tapas_physio_scaleorthmean_regressors.m rename to PhysIO/code/model/tapas_physio_scaleorthmean_regressors.m index 1e9a151d..b0bca862 100644 --- a/PhysIO/code/tapas_physio_scaleorthmean_regressors.m +++ b/PhysIO/code/model/tapas_physio_scaleorthmean_regressors.m @@ -23,8 +23,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if isempty(cardiac_sess) R = []; diff --git a/PhysIO/code/tapas_physio_split_regressor_slices.m b/PhysIO/code/model/tapas_physio_split_regressor_slices.m similarity index 99% rename from PhysIO/code/tapas_physio_split_regressor_slices.m rename to PhysIO/code/model/tapas_physio_split_regressor_slices.m index 5285d05a..257854b9 100644 --- a/PhysIO/code/tapas_physio_split_regressor_slices.m +++ b/PhysIO/code/model/tapas_physio_split_regressor_slices.m @@ -22,7 +22,7 @@ % tapas_physio_split_regressor_slices % % See also -% + % Author: Lars Kasper % Created: 2014-08-14 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -31,8 +31,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % make multiple columns for multiple slices nRegressors = size(slice_regressors_concat,2); slice_regressors = []; diff --git a/PhysIO/code/tapas_physio_get_default_fig_params.m b/PhysIO/code/plot/tapas_physio_get_default_fig_params.m similarity index 99% rename from PhysIO/code/tapas_physio_get_default_fig_params.m rename to PhysIO/code/plot/tapas_physio_get_default_fig_params.m index 29f11d10..16847ba8 100644 --- a/PhysIO/code/tapas_physio_get_default_fig_params.m +++ b/PhysIO/code/plot/tapas_physio_get_default_fig_params.m @@ -14,8 +14,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % if nargin < 1 convfac = 2; % conversion factor for figure scaling; for laptop display diff --git a/PhysIO/code/tapas_physio_plot_cropped_phys_to_acqwindow.m b/PhysIO/code/plot/tapas_physio_plot_cropped_phys_to_acqwindow.m similarity index 98% rename from PhysIO/code/tapas_physio_plot_cropped_phys_to_acqwindow.m rename to PhysIO/code/plot/tapas_physio_plot_cropped_phys_to_acqwindow.m index 25a7e443..e7ab6303 100644 --- a/PhysIO/code/tapas_physio_plot_cropped_phys_to_acqwindow.m +++ b/PhysIO/code/plot/tapas_physio_plot_cropped_phys_to_acqwindow.m @@ -10,7 +10,7 @@ % % OUTPUT % fh figure handle of output figure -% + % Author: Lars Kasper % % Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -19,10 +19,9 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + fh = tapas_physio_get_default_fig_params(); -set(fh,'Name','Cutout actual scans - all events and gradients'); +set(fh,'Name','Preproc: Cutout actual scans - all events and gradients'); Ndummies = sqpar.Ndummies; Nslices = sqpar.Nslices; diff --git a/PhysIO/code/tapas_physio_plot_gradient.m b/PhysIO/code/plot/tapas_physio_plot_gradient.m similarity index 94% rename from PhysIO/code/tapas_physio_plot_gradient.m rename to PhysIO/code/plot/tapas_physio_plot_gradient.m index 6302507c..faa06f85 100644 --- a/PhysIO/code/tapas_physio_plot_gradient.m +++ b/PhysIO/code/plot/tapas_physio_plot_gradient.m @@ -13,7 +13,7 @@ % plot_gradient % % See also -% + % Author: Lars Kasper % Created: 2015-01-10 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -22,10 +22,9 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ -stringTitle = 'Gradient subplots'; + +stringTitle = 'Sync: Gradient subplots'; nSamples = size(G,1); @@ -61,4 +60,4 @@ end linkaxes(hs, 'x'); -suptitle(stringTitle); \ No newline at end of file +tapas_physio_suptitle(stringTitle); \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_plot_movement_outliers_fd.m b/PhysIO/code/plot/tapas_physio_plot_movement_outliers_fd.m similarity index 95% rename from PhysIO/code/tapas_physio_plot_movement_outliers_fd.m rename to PhysIO/code/plot/tapas_physio_plot_movement_outliers_fd.m index 9de170f6..8dc20d6e 100644 --- a/PhysIO/code/tapas_physio_plot_movement_outliers_fd.m +++ b/PhysIO/code/plot/tapas_physio_plot_movement_outliers_fd.m @@ -15,7 +15,7 @@ % tapas_physio_plot_movement_outliers_fd % % See also tapas_physio_get_movement_quality_measures -% + % Author: Lars Kasper % Created: 2018-02-21 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, @@ -28,7 +28,7 @@ % fh = tapas_physio_get_default_fig_params(); -stringTitle = 'Motion Quality Control - Framewise Displacement'; +stringTitle = 'Model: Motion Quality Control - Framewise Displacement'; set(fh, 'Name', stringTitle); colors = [ @@ -79,6 +79,6 @@ xlabel('Volume #'); title('Outlier Mask of Stick (Spike) Regressors for censored volumes'); -suptitle(stringTitle); +tapas_physio_suptitle(stringTitle); linkaxes(hs, 'x'); \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_plot_movement_outliers_maxval.m b/PhysIO/code/plot/tapas_physio_plot_movement_outliers_maxval.m similarity index 97% rename from PhysIO/code/tapas_physio_plot_movement_outliers_maxval.m rename to PhysIO/code/plot/tapas_physio_plot_movement_outliers_maxval.m index 45d2ee4d..c7353f13 100644 --- a/PhysIO/code/tapas_physio_plot_movement_outliers_maxval.m +++ b/PhysIO/code/plot/tapas_physio_plot_movement_outliers_maxval.m @@ -34,7 +34,7 @@ % tapas_physio_plot_movement_outliers_maxval % % See also -% + % Author: Lars Kasper % Created: 2018-02-21 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, @@ -64,7 +64,7 @@ nOutlierTrans = numel(iOutlierTrans); nOutlierRot = numel(iOutlierRot); -stringTitle = 'Motion Quality Control - MAXVAL thresholds per direction'; +stringTitle = 'Model: Motion Quality Control - MAXVAL thresholds per direction'; fh = tapas_physio_get_default_fig_params(); set(fh, 'Name', stringTitle); @@ -103,4 +103,4 @@ xlabel('scans'); ylabel('rotation (deg)'); -suptitle(stringTitle); \ No newline at end of file +tapas_physio_suptitle(stringTitle); \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_plot_raw_physdata.m b/PhysIO/code/plot/tapas_physio_plot_raw_physdata.m similarity index 97% rename from PhysIO/code/tapas_physio_plot_raw_physdata.m rename to PhysIO/code/plot/tapas_physio_plot_raw_physdata.m index 4c61464e..f6469e94 100644 --- a/PhysIO/code/tapas_physio_plot_raw_physdata.m +++ b/PhysIO/code/plot/tapas_physio_plot_raw_physdata.m @@ -17,7 +17,7 @@ % tapas_physio_plot_raw_physdata % % See also -% + % Author: Lars Kasper % Created: 2013-02-21 % Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -30,7 +30,7 @@ if verbose.level >= 2 fh = tapas_physio_get_default_fig_params(); - set(fh, 'Name', 'Raw Physiological Logfile Data'); + set(fh, 'Name', 'Read-In: Raw Physiological Logfile Data'); has_cardiac_triggers = isfield(ons_secs, 'cpulse') && ~isempty(ons_secs.cpulse); has_scan_triggers = isfield(ons_secs, 'acq_codes') && ~isempty(ons_secs.acq_codes); has_cardiac = isfield(ons_secs, 'c') && ~isempty(ons_secs.c); diff --git a/PhysIO/code/tapas_physio_plot_raw_physdata_diagnostics.m b/PhysIO/code/plot/tapas_physio_plot_raw_physdata_diagnostics.m similarity index 96% rename from PhysIO/code/tapas_physio_plot_raw_physdata_diagnostics.m rename to PhysIO/code/plot/tapas_physio_plot_raw_physdata_diagnostics.m index b004e502..21328d02 100644 --- a/PhysIO/code/tapas_physio_plot_raw_physdata_diagnostics.m +++ b/PhysIO/code/plot/tapas_physio_plot_raw_physdata_diagnostics.m @@ -2,7 +2,7 @@ thresh_cardiac, verbose, t, c) % plots diagnostics for raw physiological time series as monitoried by the % MR scanner breathing belt/ECG -% + % Author: Lars Kasper % % Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -11,8 +11,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% Cardiac analysis of heartbeat rates @@ -25,7 +24,7 @@ if isVerbose fh = tapas_physio_get_default_fig_params(); verbose.fig_handles(end+1) = fh; - set(fh, 'Name','Diagnostics for raw physiological time series'); + set(fh, 'Name','Preproc: Diagnostics for raw physiological time series'); ah = subplot(2,1,1); if hasCardiacData diff --git a/PhysIO/code/tapas_physio_plot_raw_physdata_siemens.m b/PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens.m similarity index 97% rename from PhysIO/code/tapas_physio_plot_raw_physdata_siemens.m rename to PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens.m index 29a4239f..564d6299 100644 --- a/PhysIO/code/tapas_physio_plot_raw_physdata_siemens.m +++ b/PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens.m @@ -12,7 +12,7 @@ % % See also tapas_physio_read_physlogfiles_siemens % See also tapas_physio_siemens_table2cardiac -% + % Author: Lars Kasper % Created: 2016-02-29 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -25,7 +25,7 @@ tapas_physio_strip_fields(dataCardiac); -stringTitle = 'Raw Siemens physlog data'; +stringTitle = 'Read-In: Raw Siemens physlog data'; fh = tapas_physio_get_default_fig_params(); set(gcf, 'Name', stringTitle); stem(cpulse_on, ampl*ones(size(cpulse_on)), 'g'); hold all; diff --git a/PhysIO/code/tapas_physio_plot_raw_physdata_siemens_hcp.m b/PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens_hcp.m similarity index 89% rename from PhysIO/code/tapas_physio_plot_raw_physdata_siemens_hcp.m rename to PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens_hcp.m index 47df95ce..fd5afd4d 100644 --- a/PhysIO/code/tapas_physio_plot_raw_physdata_siemens_hcp.m +++ b/PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens_hcp.m @@ -1,8 +1,10 @@ -function fh = tapas_physio_plot_raw_physdata_siemens_hcp(t, c, r, acq_codes) +function fh = tapas_physio_plot_raw_physdata_siemens_hcp(t, c, r, acq_codes, ... + stringTitle) % plots cardiac data as extracted from Human Connectome Phys log file % (=preprocessed Siemens log file) % -% fh = tapas_physio_plot_raw_physdata_siemens_hcp(t, c, r, acq_codes) +% fh = tapas_physio_plot_raw_physdata_siemens_hcp(t, c, r, acq_codes, ... +% stringTitle) % % IN % @@ -12,7 +14,7 @@ % tapas_physio_plot_raw_physdata_siemens_hcp % % See also tapas_physio_read_physlogfiles_siemens_hcp -% + % Author: Lars Kasper % Created: 2018-01-23 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, @@ -22,8 +24,10 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . +if nargin < 5 + stringTitle = 'Read-In: Raw Human Connectome Project physlog data (preprocessed Siemens Data)'; +end -stringTitle = 'Raw Human Connectome Project physlog data (preprocessed Siemens Data)'; volpulse_on = find(acq_codes == 8); volpulse_off = find(acq_codes == 16); diff --git a/PhysIO/code/tapas_physio_plot_raw_physdata_siemens_tics.m b/PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens_tics.m similarity index 98% rename from PhysIO/code/tapas_physio_plot_raw_physdata_siemens_tics.m rename to PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens_tics.m index ed6970bb..572c5956 100644 --- a/PhysIO/code/tapas_physio_plot_raw_physdata_siemens_tics.m +++ b/PhysIO/code/plot/tapas_physio_plot_raw_physdata_siemens_tics.m @@ -13,7 +13,7 @@ % tapas_physio_plot_raw_physdata_siemens_tics % % See also -% + % Author: Lars Kasper % Created: 2017-09-10 % Copyright (C) 2017 TNU, Institute for Biomedical Engineering, @@ -26,7 +26,7 @@ % fh = tapas_physio_get_default_fig_params(); -stringTitle = 'Siemens Tics - Read-in cardiac and respiratory logfiles'; +stringTitle = 'Read-In: Siemens Tics - Read-in cardiac and respiratory logfiles'; set(gcf, 'Name', stringTitle); stringLegend = {}; tOffset = min([tRespiration; tCardiac]); diff --git a/PhysIO/code/tapas_physio_plot_retroicor_regressors.m b/PhysIO/code/plot/tapas_physio_plot_retroicor_regressors.m similarity index 97% rename from PhysIO/code/tapas_physio_plot_retroicor_regressors.m rename to PhysIO/code/plot/tapas_physio_plot_retroicor_regressors.m index 89713399..5a9034b8 100644 --- a/PhysIO/code/tapas_physio_plot_retroicor_regressors.m +++ b/PhysIO/code/plot/tapas_physio_plot_retroicor_regressors.m @@ -16,7 +16,7 @@ % tapas_physio_plot_retroicor_regressors % % See also -% + % Author: Lars Kasper % Created: 2015-08-03 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, @@ -26,8 +26,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 3 hasCardiacData = 1; @@ -61,7 +60,7 @@ end fh = tapas_physio_get_default_fig_params(); -set(gcf,'Name','RETROICOR timecourse physiological regressors'); +set(gcf,'Name','Model: RETROICOR timecourse physiological regressors'); orders = {order.c, order.r, order.cr}; yData = {cardiac_sess, respire_sess, mult_sess}; diff --git a/PhysIO/code/tapas_physio_print_figs_to_file.m b/PhysIO/code/plot/tapas_physio_print_figs_to_file.m similarity index 99% rename from PhysIO/code/tapas_physio_print_figs_to_file.m rename to PhysIO/code/plot/tapas_physio_print_figs_to_file.m index ba232c09..b0b0db88 100644 --- a/PhysIO/code/tapas_physio_print_figs_to_file.m +++ b/PhysIO/code/plot/tapas_physio_print_figs_to_file.m @@ -13,7 +13,7 @@ % physio_print_figs_to_ps % % See also -% + % Author: Lars Kasper % based on code by Jakob Heinzle, TNU % @@ -24,8 +24,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin > 1 verbose.fig_output_file = fullfile(save_dir, verbose.fig_output_file); end diff --git a/PhysIO/code/tapas_physio_cardiac_detect_outliers.m b/PhysIO/code/preproc/tapas_physio_cardiac_detect_outliers.m similarity index 98% rename from PhysIO/code/tapas_physio_cardiac_detect_outliers.m rename to PhysIO/code/preproc/tapas_physio_cardiac_detect_outliers.m index 0b1e64a0..08599946 100644 --- a/PhysIO/code/tapas_physio_cardiac_detect_outliers.m +++ b/PhysIO/code/preproc/tapas_physio_cardiac_detect_outliers.m @@ -22,7 +22,7 @@ % tapas_physio_cardiac_detect_outliers % % See also tapas_physio_correct_cardiac_pulses_manually -% + % Author: Lars Kasper % Created: 2013-04-25 % Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -31,8 +31,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + dt = diff(tCardiac); @@ -46,7 +45,7 @@ if nargin < 6 fh = tapas_physio_get_default_fig_params(); verbose.fig_handles(end+1,1) = fh; - set(fh, 'Name','Diagnostics raw phys time series'); + set(fh, 'Name','Preproc: Diagnostics raw phys time series'); else fh = get(ah, 'Parent'); figure(fh); diff --git a/PhysIO/code/tapas_physio_correct_cardiac_pulses_manually.m b/PhysIO/code/preproc/tapas_physio_correct_cardiac_pulses_manually.m similarity index 74% rename from PhysIO/code/tapas_physio_correct_cardiac_pulses_manually.m rename to PhysIO/code/preproc/tapas_physio_correct_cardiac_pulses_manually.m index f77f2fe6..d61e8a74 100644 --- a/PhysIO/code/tapas_physio_correct_cardiac_pulses_manually.m +++ b/PhysIO/code/preproc/tapas_physio_correct_cardiac_pulses_manually.m @@ -4,7 +4,7 @@ % this function takes the onsets from ECG measure and controls for % outliers (more or less than a threshold given by a percentile increased % or decreased by upperTresh or lowerThresh percent respectively. -% + % Author: Jakob Heinzle, TNU, % adaptation: Lars Kasper % @@ -14,8 +14,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if verbose.level < 1 error('Manual picking of cardiac pulses requires verbose.level >= 1'); @@ -25,23 +24,26 @@ upperThresh = thresh_cardiac.upper_thresh; lowerThresh = thresh_cardiac.lower_thresh; +%% Select additional pulses [outliersHigh,outliersLow, verbose] = tapas_physio_cardiac_detect_outliers(... ons_secs.cpulse, percentile, upperThresh, lowerThresh, verbose); if any(outliersHigh) - %disp('Press Enter to proceed to manual peak selection!'); - %pause; additionalPulse=[]; - fh2=figure; - for outk=1:length(outliersHigh) - s=0; - while ~(s==1) + fh2 = figure('Name', 'Preproc: Manual Pulse Selection Window'); + outk = 0; + doQuitSelection = false; + while (outk < length(outliersHigh)) && ~doQuitSelection + s = 0; + outk = outk + 1; + while s==0 indStart = outliersHigh(outk)-1; indEnd = outliersHigh(outk)+2; ind=find(ons_secs.t>=ons_secs.cpulse(indStart), 1, 'first')-100:find(ons_secs.t<=ons_secs.cpulse(indEnd), 1, 'last')+100; figure(fh2); clf; plot(ons_secs.t(ind),ons_secs.c(ind),'r') hold on; plot(ons_secs.cpulse(indStart:indEnd),ones(4,1)*max(ons_secs.c(ind)),'ok') - inpNum=input('How many triggers do you want to set? Enter a number between 0 and 10 : '); + inpNum=input(... + 'How many triggers do you want to set? Enter a number between 0 and 10 : '); I1=[]; for ii=1:inpNum figure(fh2); @@ -50,7 +52,11 @@ end - s=input('If you agree with the selected triggers, press 1 (then enter) : '); + fprintf('If you agree with the selected triggers, press 1 (then enter);\n'); + fprintf('If you want to repeat, press 0;\n'); + s=input('If you want to quit the manual selection,, press -1 : '); + doQuitSelection = s==-1; + if isempty(s) s=0; end @@ -60,12 +66,18 @@ ons_secs.cpulse = sort([ons_secs.cpulse;additionalPulse]); close(fh2); end + +% closes detected outlier figure, since no longer up to date after +% selection close(verbose.fig_handles(end)); verbose.fig_handles(end) = []; + +%% Remove superfluous pulses [outliersHigh,outliersLow, verbose] = tapas_physio_cardiac_detect_outliers(... ons_secs.cpulse, percentile, upperThresh, lowerThresh, verbose); +doQuitSelection = false; nPulses = length(ons_secs.cpulse); finalIndex=1:nPulses; @@ -74,9 +86,7 @@ %show windows with suspicious outliers only for indStart = round(nWindow/2):nWindow-2:nPulses indEnd = indStart+nWindow-1; - if any(ismember(outliersLow, indStart:indEnd)) - %disp('Press Enter to proceed to manual peak deletion!'); - %pause; + if any(ismember(outliersLow, indStart:indEnd)) && ~doQuitSelection fh3=figure('Position', [500 500 1000 500]); ind=find(ons_secs.t>=ons_secs.cpulse(indStart), 1, 'first')-100:find(ons_secs.t<=ons_secs.cpulse(indEnd), 1, 'last')+100; figure(fh3); clf; @@ -91,7 +101,8 @@ delInd= []; - delInd=input('Enter the indices of pulses you want to delete (0 if none, put multiple in [], e.g. [19,20]): '); + fprintf('Enter the indices of pulses you want to delete.\n'); + delInd=input('(0 if none, -1 to quit, multiple indices in [], e.g. [19,20]): '); if ~isempty(delInd) && any(find(delInd~=0)) plot(ons_secs.cpulse(delInd),max(ons_secs.c(ind))*ones(size(delInd)), 'rx', 'MarkerSize',20); @@ -103,19 +114,26 @@ end ons_secs.cpulse = sort(ons_secs.cpulse(finalIndex)); +% closes detected outlier figure, since no longer up to date after +% selection close(verbose.fig_handles(end)); verbose.fig_handles(end) = []; + +%% Check for outliers again and continue recursively, if wanted [outliersHigh,outliersLow, verbose] = tapas_physio_cardiac_detect_outliers(... ons_secs.cpulse, percentile, upperThresh, lowerThresh, verbose); -close(verbose.fig_handles(end)); -verbose.fig_handles(end) = []; - % recursively determine outliers if ~isempty(outliersHigh) || ~isempty(outliersLow) - doManualCorrectionAgain = input('More outliers detected after correction. Do you want to remove them? (1=yes, 0=no; ENTER)'); + + doManualCorrectionAgain = input('More outliers detected after correction. Do you want to remove them? (1=yes, 0=no; ENTER)'); if doManualCorrectionAgain + + % will be recalculated later + close(verbose.fig_handles(end)); + verbose.fig_handles(end) = []; + [ons_secs, outliersHigh, outliersLow] = ... tapas_physio_correct_cardiac_pulses_manually(ons_secs, ... thresh_cardiac, verbose); diff --git a/PhysIO/code/tapas_physio_detect_constants.m b/PhysIO/code/preproc/tapas_physio_detect_constants.m similarity index 97% rename from PhysIO/code/tapas_physio_detect_constants.m rename to PhysIO/code/preproc/tapas_physio_detect_constants.m index a6126339..f36baed8 100644 --- a/PhysIO/code/tapas_physio_detect_constants.m +++ b/PhysIO/code/preproc/tapas_physio_detect_constants.m @@ -23,7 +23,7 @@ % tapas_physio_detect_constants % % See also tapas_physio_new tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2016-09-29 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -33,8 +33,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 2 nMinConstantSamples = 10; @@ -109,7 +108,7 @@ if DEBUG fh = tapas_physio_get_default_fig_params(); verbose.fig_handles(end+1) = fh; - stringTitle = 'Detection of suspicious constant values in physiological time series'; + stringTitle = 'Preproc: Detection of suspicious constant values in physiological time series'; set(fh, 'Name', stringTitle); plot(dy/max(dy)); hold all; diff --git a/PhysIO/code/tapas_physio_filter_respiratory.m b/PhysIO/code/preproc/tapas_physio_filter_respiratory.m similarity index 99% rename from PhysIO/code/tapas_physio_filter_respiratory.m rename to PhysIO/code/preproc/tapas_physio_filter_respiratory.m index fac289a1..3a70277e 100644 --- a/PhysIO/code/tapas_physio_filter_respiratory.m +++ b/PhysIO/code/preproc/tapas_physio_filter_respiratory.m @@ -9,7 +9,7 @@ % rsamping % doNormalize default:false % Optionally, data is normalized to be in -1...+1 range -% + % Author: Lars Kasper, 2011; heavily based on an earlier implementation of % Chloe Hutton (FIL, UCL London) % @@ -19,8 +19,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if isempty(rpulset) rpulset = []; return; diff --git a/PhysIO/code/tapas_physio_find_ecg_r_peaks.m b/PhysIO/code/preproc/tapas_physio_find_ecg_r_peaks.m similarity index 98% rename from PhysIO/code/tapas_physio_find_ecg_r_peaks.m rename to PhysIO/code/preproc/tapas_physio_find_ecg_r_peaks.m index a8901adb..c9c3c8a6 100644 --- a/PhysIO/code/tapas_physio_find_ecg_r_peaks.m +++ b/PhysIO/code/preproc/tapas_physio_find_ecg_r_peaks.m @@ -39,8 +39,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % manual_mode = ~exist('kRpeak', 'var') || isempty(kRpeak); @@ -55,7 +54,7 @@ if manual_mode %% Plot ECG curve, central part and already detected input events fh = tapas_physio_get_default_fig_params(); - set(fh, 'Name', 'Detection of R-wave from measured ECG-timecourse'); + set(fh, 'Name', 'Preproc: Detection of R-wave from measured ECG-timecourse'); subplot(3,1,1); hold off; plot(t(iSamples), y(iSamples)); hold all; diff --git a/PhysIO/code/tapas_physio_get_cardiac_phase.m b/PhysIO/code/preproc/tapas_physio_get_cardiac_phase.m similarity index 93% rename from PhysIO/code/tapas_physio_get_cardiac_phase.m rename to PhysIO/code/preproc/tapas_physio_get_cardiac_phase.m index dfdac445..463e7afe 100644 --- a/PhysIO/code/tapas_physio_get_cardiac_phase.m +++ b/PhysIO/code/preproc/tapas_physio_get_cardiac_phase.m @@ -30,8 +30,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % @@ -46,12 +45,12 @@ % add pulses in beginning, if first onset time of scan before recorded % pulses - use guess based on average heart rate -if scannert(1) < pulset(1) +if scannert(1) <= pulset(1) verbose = tapas_physio_log(... 'Guessed additional cardiac pulse at time series start for phase estimation', ... verbose, 1); meanCycleDur = mean(diff(pulset(1:nAverage))); - nAddPulses = ceil((pulset(1) - scannert(1))/meanCycleDur); + nAddPulses = max(1, ceil((pulset(1) - scannert(1))/meanCycleDur)); pulset = [pulset(1) - meanCycleDur*(1:nAddPulses)';pulset]; end @@ -86,9 +85,9 @@ % and line of phases) % 3. plot all detected cardiac r-wave peaks % 4. plot volume start event - titstr = 'tapas_physio_get_cardiac_phase: scanner and R-wave pulses - output phase'; + stringTitle = 'Preproc: tapas_physio_get_cardiac_phase: scanner and R-wave pulses - output phase'; fh = tapas_physio_get_default_fig_params(); - set(fh, 'Name', titstr); + set(fh, 'Name', stringTitle); stem(scannert, cardiac_phase, 'k'); hold on; plot(scannert, cardiac_phase, 'k'); stem(pulset,3*ones(size(pulset)),'r', 'LineWidth',2); @@ -97,7 +96,7 @@ '', ... 'heart beat R-peak', ... 'scan volume start'); - title(regexprep(titstr,'_', '\\_')); + title(regexprep(stringTitle,'_', '\\_')); xlabel('t (seconds)'); %stem(scannertpriorpulse,ones(size(scannertpriorpulse))*2,'g'); %stem(scannertafterpulse,ones(size(scannertafterpulse))*2,'b'); diff --git a/PhysIO/code/tapas_physio_get_cardiac_pulse_template.m b/PhysIO/code/preproc/tapas_physio_get_cardiac_pulse_template.m similarity index 55% rename from PhysIO/code/tapas_physio_get_cardiac_pulse_template.m rename to PhysIO/code/preproc/tapas_physio_get_cardiac_pulse_template.m index 21675ee4..93ebd619 100644 --- a/PhysIO/code/tapas_physio_get_cardiac_pulse_template.m +++ b/PhysIO/code/preproc/tapas_physio_get_cardiac_pulse_template.m @@ -16,7 +16,7 @@ % tapas_physio_get_cardiac_pulse_template % % See also -% + % Author: Lars Kasper % Created: 2014-08-05 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -25,24 +25,25 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % template should only be length of a fraction of average heartbeat length defaults.shortenTemplateFactor = 0.5; defaults.minCycleSamples = ceil(0.5/2e-3); defaults.thresh_min = 0.4; - +defaults.doNormalizeTemplate = true; +% outliers below that correlation will be removed for averaging when retrieving final template +defaults.thresholdHighQualityCorrelation = 0.95; args = tapas_physio_propval(varargin, defaults); tapas_physio_strip_fields(args); -debug = verbose.level >= 3; +doDebug = verbose.level >= 3; dt = t(2) - t(1); -%% Guess peaks in two steps with updated avereage heartrate +%% Guess peaks in two steps with updated average heartrate % First step [tmp, cpulse1stGuess] = tapas_physio_findpeaks( ... @@ -53,7 +54,7 @@ %% Second step, refined heart rate estimate -stringTitle = 'Iterative Template Creation Single Cycle'; +stringTitle = 'Preproc: Iterative Template Creation Single Cycle'; if hasFirstGuessPeaks @@ -63,7 +64,7 @@ 'minpeakdistance', round(shortenTemplateFactor*... averageHeartRateInSamples)); - if debug + if doDebug nPulses1 = length(cpulse1stGuess); nPulses2 = length(cpulse2ndGuess); @@ -110,10 +111,11 @@ legend(stringLegend); end else - if debug - fh = tapas_physio_get_default_fig_params; + if doDebug + fh = tapas_physio_get_default_fig_params(); + verbose.fig_handles(end+1,1) = fh; subplot(3,1,1); - plot(t, c, 'k'); title('Finding first peak of cycle, backwards') + plot(t, c, 'k'); title('Preproc: Finding first peak of cycle, backwards') end verbose = tapas_physio_log(['No peaks found in raw cardiac time series. Check raw ' ... 'physiological recordings figure whether there is any non-constant' ... @@ -126,87 +128,13 @@ % cut out all data around the detected (presumed) R-peaks % => these are the representative "QRS"-waves + halfTemplateWidthInSamples = round(shortenTemplateFactor * ... (averageHeartRateInSamples / 2)); -% z-transform to have all templates (for averaging) have -% same norm & be mean-corrected -doNormalizeTemplate = true; -nSamplesTemplate = halfTemplateWidthInSamples * 2 + 1; -nPulses = numel(cpulse2ndGuess); -template = zeros(nPulses-3,nSamplesTemplate); -for n=2:nPulses-2 - startTemplate = cpulse2ndGuess(n)-halfTemplateWidthInSamples; - endTemplate = cpulse2ndGuess(n)+halfTemplateWidthInSamples; - - template(n,:) = c(startTemplate:endTemplate); - - if doNormalizeTemplate - template(n,:) = template(n,:) - mean(template(n,:),2); - - % std-normalized... - %template(n,:) = template(n,:)./std(template(n,:),0,2); - % max-norm: - template(n,:) = template(n,:)./max(abs(template(n,:))); - end - -end - -%delete first zero-elements of the template -template(1,:) = []; - -% template as average of the found representative waves -pulseTemplate = mean(template); - -if debug - figure(fh); - subplot(3,1,3); - tTemplate = dt*(0:2*halfTemplateWidthInSamples); - plot(tTemplate, template'); - hold all; - hp(1) = plot(tTemplate, pulseTemplate', '.--g', 'LineWidth', 3, 'Marker', ... - 'o'); - xlabel('t (seconds)'); - title('Templates of cycle time course and mean template'); -end - -% delete the peaks deviating from the mean too -% much before building the final template -[~, pulseTemplate] = tapas_physio_corrcoef12(pulseTemplate, pulseTemplate); -isZtransformed = [0 1]; - -nTemplates = size(template,1); -similarityToTemplate = zeros(nTemplates,1); -for n=1:nTemplates - similarityToTemplate(n) = tapas_physio_corrcoef12(template(n,:),pulseTemplate, ... - isZtransformed); -end - -%% Final template for peak search from best-matching templates - -thresholdHighQualityCorrelation = 0.95; - -% minimal number of high quality templates to be achieved, otherwise -% enforced -nMinHighQualityTemplates = ceil(0.1 * nPulses); -indHighQualityTemplates = find(similarityToTemplate > ... - thresholdHighQualityCorrelation); - -% if threshold to restrictive, try with new one: -% best nMinHighQualityTemplates / nPulses of all found templates used for -% averaging -if numel(indHighQualityTemplates) < nMinHighQualityTemplates - thresholdHighQualityCorrelation = tapas_physio_prctile(similarityToTemplate, ... - 1 - nMinHighQualityTemplates/nPulses); - indHighQualityTemplates = find(similarityToTemplate > ... - thresholdHighQualityCorrelation); -end -pulseCleanedTemplate = mean(template(indHighQualityTemplates, :)); - -if debug - hp(2) = plot(tTemplate, pulseCleanedTemplate, '.-g', 'LineWidth', 4, ... - 'Marker', 'x'); - legend(hp, 'mean of templates', 'mean of most similar, chosen templates'); - suptitle(stringTitle); -end \ No newline at end of file +[pulseCleanedTemplate, pulseTemplate] = tapas_physio_get_template_from_pulses(... + c, cpulse2ndGuess, halfTemplateWidthInSamples, ... + verbose, 'doNormalizeTemplate', doNormalizeTemplate, ... + 'thresholdHighQualityCorrelation', thresholdHighQualityCorrelation, ... + 'dt', dt); \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_get_cardiac_pulses.m b/PhysIO/code/preproc/tapas_physio_get_cardiac_pulses.m similarity index 99% rename from PhysIO/code/tapas_physio_get_cardiac_pulses.m rename to PhysIO/code/preproc/tapas_physio_get_cardiac_pulses.m index c5802c1c..8475dc78 100644 --- a/PhysIO/code/tapas_physio_get_cardiac_pulses.m +++ b/PhysIO/code/preproc/tapas_physio_get_cardiac_pulses.m @@ -50,7 +50,7 @@ % thresh.cardiac, cardiac_modality, cardiac_peak_file); % % See also tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2013-02-16 % @@ -60,8 +60,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% detection of cardiac R-peaks diff --git a/PhysIO/code/tapas_physio_get_cardiac_pulses_auto_matched.m b/PhysIO/code/preproc/tapas_physio_get_cardiac_pulses_auto_matched.m similarity index 96% rename from PhysIO/code/tapas_physio_get_cardiac_pulses_auto_matched.m rename to PhysIO/code/preproc/tapas_physio_get_cardiac_pulses_auto_matched.m index 19c35d63..f7b4a58b 100644 --- a/PhysIO/code/tapas_physio_get_cardiac_pulses_auto_matched.m +++ b/PhysIO/code/preproc/tapas_physio_get_cardiac_pulses_auto_matched.m @@ -29,7 +29,7 @@ % tapas_physio_get_cardiac_pulses_auto_matched % % See also -% + % Author: Steffen Bollmann, Kinderspital Zurich % Created: 2014-03-20 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -38,8 +38,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 5 verbose.level = 0; verbose.fig_handles = []; @@ -91,14 +90,14 @@ if verbose.level >=2 verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - titstr = 'Peak Detection from Automatically Generated Template'; - set(gcf, 'Name', titstr); + stringTitle = 'Preproc: Peak Detection from Automatically Generated Template'; + set(gcf, 'Name', stringTitle); plot(t, c, 'k'); hold all; stem(cpulse,4*ones(size(cpulse)), 'r'); legend('Raw time course',... 'Detected maxima (cardiac pulses / max inhalations)'); - title(titstr); + title(stringTitle); end end diff --git a/PhysIO/code/tapas_physio_get_cardiac_pulses_manual_template.m b/PhysIO/code/preproc/tapas_physio_get_cardiac_pulses_manual_template.m similarity index 99% rename from PhysIO/code/tapas_physio_get_cardiac_pulses_manual_template.m rename to PhysIO/code/preproc/tapas_physio_get_cardiac_pulses_manual_template.m index e5263363..38f59092 100644 --- a/PhysIO/code/tapas_physio_get_cardiac_pulses_manual_template.m +++ b/PhysIO/code/preproc/tapas_physio_get_cardiac_pulses_manual_template.m @@ -27,7 +27,7 @@ % tapas_physio_get_cardiac_pulses_manual_template % % See also tapa_physio_new -% + % Author: Lars Kasper % Created: 2012-02-20 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -36,8 +36,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 5 verbose.level = 0; verbose.fig_handles = []; diff --git a/PhysIO/code/tapas_physio_get_oxy_pulses_filtered.m b/PhysIO/code/preproc/tapas_physio_get_oxy_pulses_filtered.m similarity index 97% rename from PhysIO/code/tapas_physio_get_oxy_pulses_filtered.m rename to PhysIO/code/preproc/tapas_physio_get_oxy_pulses_filtered.m index 24f4b75e..336c13c3 100644 --- a/PhysIO/code/tapas_physio_get_oxy_pulses_filtered.m +++ b/PhysIO/code/preproc/tapas_physio_get_oxy_pulses_filtered.m @@ -26,7 +26,7 @@ % tapas_physio_get_oxy_pulses_filtered % % See also -% + % Author: Lars Kasper % Created: 2014-08-03 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -35,8 +35,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + dt = t(2) - t(1); c = c-mean(c); c = c./max(c); % normalize time series @@ -57,7 +56,7 @@ if verbose.level >=2 % visualise influence of smoothing on peak detection verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf, 'Name', 'PPU-OXY: Tresholding Maxima for Heart Beat Detection'); + set(gcf, 'Name', 'Preproc: PPU-OXY: Tresholding Maxima for Heart Beat Detection'); [tmp, cpulse2] = tapas_physio_findpeaks(c,'minpeakheight',thresh_cardiac.min,'minpeakdistance', dt120); plot(t, c, 'k'); hold all; diff --git a/PhysIO/code/tapas_physio_get_respiratory_phase.m b/PhysIO/code/preproc/tapas_physio_get_respiratory_phase.m similarity index 98% rename from PhysIO/code/tapas_physio_get_respiratory_phase.m rename to PhysIO/code/preproc/tapas_physio_get_respiratory_phase.m index a409e092..75fed38b 100644 --- a/PhysIO/code/tapas_physio_get_respiratory_phase.m +++ b/PhysIO/code/preproc/tapas_physio_get_respiratory_phase.m @@ -27,7 +27,7 @@ % 4) Calculate the running integral of the histogram for the bin % corresponding to each respiratory amplitude %_______________________________________________________________________ -% + % Author: Lars Kasper % Error handling for temporary breathing belt failures: % Eduardo Aponte, TNU Zurich @@ -38,8 +38,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% get histogram of amplitudes of breathing belt signal if nargin < 3 @@ -117,7 +116,7 @@ fh = tapas_physio_get_default_fig_params(); set(fh, 'Name', ... - 'get_respiratory_phase: histogram for respiratory phase estimation'); + 'Preproc: get_respiratory_phase: histogram for respiratory phase estimation'); hs(1) = subplot(2,2,1); plot(t,pulset); diff --git a/PhysIO/code/tapas_physio_get_sample_points.m b/PhysIO/code/preproc/tapas_physio_get_sample_points.m similarity index 99% rename from PhysIO/code/tapas_physio_get_sample_points.m rename to PhysIO/code/preproc/tapas_physio_get_sample_points.m index 49b0f83c..b0e01743 100644 --- a/PhysIO/code/tapas_physio_get_sample_points.m +++ b/PhysIO/code/preproc/tapas_physio_get_sample_points.m @@ -14,7 +14,7 @@ % OUTPUT: % sample_points - absolute time (in seconds) where the specified slice was % aquired for every volume -% + % Author: Lars Kasper % % Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -23,8 +23,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % slicenum should be field of onset_slice if nargin<3 diff --git a/PhysIO/code/preproc/tapas_physio_get_template_from_pulses.m b/PhysIO/code/preproc/tapas_physio_get_template_from_pulses.m new file mode 100644 index 00000000..86170dde --- /dev/null +++ b/PhysIO/code/preproc/tapas_physio_get_template_from_pulses.m @@ -0,0 +1,148 @@ +function [pulseCleanedTemplate, pulseTemplate] = tapas_physio_get_template_from_pulses(... + c, cpulse, halfTemplateWidthInSamples, verbose, varargin) +% Computes mean pulse template given time stamp of detected pulses and raw +% time series; removes outlier pulses with correlation to initial guess of +% mean template +% +% [pulseCleanedTemplate, pulseTemplate] = tapas_physio_get_template_from_pulses(... +% c, cpulse2ndGuess, halfTemplateWidthInSamples, ... +% doNormalizeTemplate, dt) +% IN +% c cardiac time series (raw physiological recording) +% cpulse index vector (wrt c) of detected pulse events (1= event onset/max) +% halfTemplateWidthInSamples +% half the width in samples of template to be +% generated, i.e. numel(pulseTemplate) = 2*this+1 +% verbose verbose substructure of physio, holds all figure +% handles created and verbose.level to trigger visualization +% +% optional as propertyName/Value pairs: +% +% thresholdHighQualityCorrelation +% default: 0.95 +% outliers pulses below that correlation will be +% removed for averaging when retrieving final template +% minFractionIncludedPulses +% default: 0.1 +% defines minimum fraction of total number of +% found pulses to be included in final template +% (after removing outliers). If there are not +% enough clean pulses, this value is enforced by +% lowering quality thresholds. +% doNormalizeTemplate default: true; if true, subtracts mean and +% scales max to 1 for each pulse, before +% averaging +% dt default: 1; defines time vector only used for +% output plot +% +% +% OUT +% pulseCleanedTemplate pulseTemplate, mean after removing outliers for +% correlation quality +% pulseTemplate average of all detected pulse shapes +% EXAMPLE +% tapas_physio_get_template_from_pulses +% +% See also tapas_physio_get_cardiac_pulse_template tapas_physio_get_cardiac_pulses_auto_matched + +% Author: Lars Kasper +% Created: 2019-01-29 +% Copyright (C) 2019 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under +% the terms of the GNU General Public License (GPL), version 3. You can +% redistribute it and/or modify it under the terms of the GPL (either +% version 3 or, at your option, any later version). For further details, +% see the file COPYING or . + +% z-transform to have all templates (for averaging) have +% same norm & be mean-corrected +defaults.doNormalizeTemplate = true; +defaults.minFractionIncludedPulses = 0.1; +defaults.thresholdHighQualityCorrelation = 0.95; +defaults.dt = 1; % for visualization only + +args = tapas_physio_propval(varargin, defaults); +tapas_physio_strip_fields(args); + +doDebug = verbose.level >= 3; + +nSamplesTemplate = halfTemplateWidthInSamples * 2 + 1; +nPulses = numel(cpulse); +template = zeros(nPulses-3,nSamplesTemplate); + +for n=2:nPulses-2 + startTemplate = cpulse(n)-halfTemplateWidthInSamples; + endTemplate = cpulse(n)+halfTemplateWidthInSamples; + + template(n,:) = c(startTemplate:endTemplate); + + if doNormalizeTemplate + template(n,:) = template(n,:) - mean(template(n,:),2); + + % std-normalized... + %template(n,:) = template(n,:)./std(template(n,:),0,2); + % max-norm: + template(n,:) = template(n,:)./max(abs(template(n,:))); + end + +end + +%delete first zero-elements of the template +template(1,:) = []; + +% template as average of the found representative waves +pulseTemplate = mean(template); + +if doDebug + fh = verbose.fig_handles(end); + figure(fh); + subplot(3,1,3); + tTemplate = dt*(0:2*halfTemplateWidthInSamples); + plot(tTemplate, template'); + hold all; + hp(1) = plot(tTemplate, pulseTemplate', '.--g', 'LineWidth', 3, 'Marker', ... + 'o'); + xlabel('t (seconds)'); + title('Templates of cycle time course and mean template'); +end + +%% Final template for peak search from best-matching templates +% delete the peaks deviating from the mean too +% much before building the final template +[~, pulseTemplate] = tapas_physio_corrcoef12(pulseTemplate, pulseTemplate); +isZtransformed = [0 1]; + +nTemplates = size(template,1); +similarityToTemplate = zeros(nTemplates,1); +for n=1:nTemplates + similarityToTemplate(n) = tapas_physio_corrcoef12(template(n,:),pulseTemplate, ... + isZtransformed); +end + + +% minimal number of high quality templates to be achieved, otherwise +% enforced +nMinHighQualityTemplates = ceil(minFractionIncludedPulses * nPulses); +indHighQualityTemplates = find(similarityToTemplate > ... + thresholdHighQualityCorrelation); + +% if threshold to restrictive, try with new one: +% best nMinHighQualityTemplates / nPulses of all found templates used for +% averaging +if numel(indHighQualityTemplates) < nMinHighQualityTemplates + thresholdHighQualityCorrelation = tapas_physio_prctile(similarityToTemplate, ... + 1 - nMinHighQualityTemplates/nPulses); + indHighQualityTemplates = find(similarityToTemplate > ... + thresholdHighQualityCorrelation); +end +pulseCleanedTemplate = mean(template(indHighQualityTemplates, :)); + +if doDebug + stringTitle = 'Preproc: Iterative Template Creation Single Cycle'; + hp(2) = plot(tTemplate, pulseCleanedTemplate, '.-g', 'LineWidth', 4, ... + 'Marker', 'x'); + legend(hp, 'mean of templates', 'mean of most similar, chosen templates'); + tapas_physio_suptitle(stringTitle); +end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_preprocess_phys_timeseries.m b/PhysIO/code/preproc/tapas_physio_preprocess_phys_timeseries.m similarity index 91% rename from PhysIO/code/tapas_physio_preprocess_phys_timeseries.m rename to PhysIO/code/preproc/tapas_physio_preprocess_phys_timeseries.m index 5e6ec393..571fbadd 100644 --- a/PhysIO/code/tapas_physio_preprocess_phys_timeseries.m +++ b/PhysIO/code/preproc/tapas_physio_preprocess_phys_timeseries.m @@ -15,7 +15,7 @@ % tapas_physio_preprocess_phys_timeseries % % See also -% + % Author: Lars Kasper % Created: 2016-02-28 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -33,6 +33,7 @@ hasCardiacData = ~isempty(ons_secs.c); hasRespData = ~isempty(ons_secs.r); hasDetectedCardiacPulses = ~isempty(ons_secs.cpulse); +hasAcquisitionCodes = ~isempty(ons_secs.acq_codes); %% Normalize cardiac/respiratory time series to max 1 @@ -75,6 +76,10 @@ if hasRespData ons_secs.r = [paddingStart; ons_secs.r]; end + + if hasAcquisitionCodes + ons_secs.acq_codes = [paddingStart; ons_secs.acq_codes]; + end end t = ons_secs.t; @@ -93,6 +98,10 @@ if hasRespData ons_secs.r = [ons_secs.r; paddingEnd]; end + + if hasAcquisitionCodes + ons_secs.acq_codes = [ons_secs.acq_codes; paddingEnd]; + end end diff --git a/PhysIO/code/preproc/tapas_physio_simulate_pulse_samples.m b/PhysIO/code/preproc/tapas_physio_simulate_pulse_samples.m new file mode 100644 index 00000000..32f5e9da --- /dev/null +++ b/PhysIO/code/preproc/tapas_physio_simulate_pulse_samples.m @@ -0,0 +1,106 @@ +function simulatedSamples = tapas_physio_simulate_pulse_samples(t, c, ... + nSimulatedSamples, positionString, verbose) +% Simulates samples at start/end of physiological recording by estimating +% pulse template and average pulse rate, and continuing those +% +% simulatedSamples = tapas_physio_simulate_pulse_samples(t, c, ... +% nSimulatedSamples, positionString, verbose)% +% IN +% +% OUT +% +% EXAMPLE +% tapas_physio_simulate_pulse_samples +% +% See also tapas_physio_get_cardiac_pulse_template tapas_physio_read_physlogfiles + +% Author: Lars Kasper +% Created: 2019-01-26 +% Copyright (C) 2019 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under +% the terms of the GNU General Public License (GPL), version 3. You can +% redistribute it and/or modify it under the terms of the GPL (either +% version 3 or, at your option, any later version). For further details, +% see the file COPYING or . + +doDebug = verbose.level >=2; + +% add artificial time series before +simulatedPulses = zeros(nSimulatedSamples, 1); +[pulseTemplate, idxPulses, meanPulseRateInSamples] = ... + tapas_physio_get_cardiac_pulse_template(t, c, verbose, ... + 'doNormalizeTemplate', false, 'shortenTemplateFactor', .99); + +doPrepend = false; +doAppend = false; +switch positionString + case {'pre', 'before'} + doPrepend = true; + % Create last simulated pulse: + % if first detected pulse in orig time series is further from start + % than meanPulseRate, assume a regular heart beat also all the way + % to the start, i.e. spacing by a multiple of meanPulseRateInSamples + % to last simulated pulse + if mod(idxPulses(1), meanPulseRateInSamples) == 0 + % if earliest pulse at multiple of pulse rate, + % then next pulse would be just before start of orig time + % series + idxLastSimulatedPulse = nSimulatedPulses; + else + idxLastSimulatedPulse = nSimulatedSamples - meanPulseRateInSamples ... + + mod(idxPulses(1), meanPulseRateInSamples); + end + % put 1 where pulses should occur before start of time series, + % given earliest pulse and mean pulse rate, fill backwards... + simulatedPulses(idxLastSimulatedPulse:-meanPulseRateInSamples:1) = 1; + case {'post', 'after'} + doAppend = true; + % Create first simulated pulse: + % if last detected pulse in orig time series is further from end + % than meanPulseRate, assume a regular heart beat also all the way + % to the end, i.e. spacing by a multiple of meanPulseRateInSamples + % to first simulated pulse + nSamplesOrig = numel(c); + idxFirstSimulatedPulse = meanPulseRateInSamples ... + - mod(nSamplesOrig - idxPulses(end), meanPulseRateInSamples); + + % put 1 where pulses should occur after end of time series + % given last pulse event in orig time series and mean pulse rate... + simulatedPulses(idxFirstSimulatedPulse:meanPulseRateInSamples:end) = 1; + otherwise + tapas_physio_log(... + sprintf('Unknown positionString ''%s'' for simulating samples; Use ''pre'' or ''post''', positionString),... + verbose, 2) +end + +% ...and convolve with pulse template +simulatedSamples = conv(simulatedPulses, pulseTemplate, 'same'); + +if doDebug + dt = t(2) - t(1); + if doPrepend + tNew = -flip(1:nSimulatedSamples)'*dt+t(1); + elseif doAppend + tNew = (1:nSimulatedSamples)'*dt+t(end); + end + verbose.fig_handles(end+1,1) = tapas_physio_get_default_fig_params(); + stringTitle = sprintf('Preproc: Added simulated samples %s time series', positionString); + + % plot orig time series and extension by simulated samples + plot(t,c); hold all; + plot(tNew, simulatedSamples); + + % plot time events of actual and simulated pulses as stems + idxSimulatedPulses = find(simulatedPulses); + stem(t(idxPulses), ones(numel(idxPulses))); + stem(tNew(idxSimulatedPulses), ones(numel(idxSimulatedPulses))); + + % plot template pulse centered on one simulated pulse + tStart = tNew(idxSimulatedPulses(1)); + nSamplesTemplate = numel(pulseTemplate); + tTemplate = tStart+dt*(-ceil(pulseTemplate/2)+(0:(nSamplesTemplate-1)))'; + plot(tTemplate, pulseTemplate); + title(stringTitle); +end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_find_matching_scanphyslog_philips.m b/PhysIO/code/readin/tapas_physio_find_matching_scanphyslog_philips.m similarity index 80% rename from PhysIO/code/tapas_physio_find_matching_scanphyslog_philips.m rename to PhysIO/code/readin/tapas_physio_find_matching_scanphyslog_philips.m index 0dbb8fd4..842fc2a3 100644 --- a/PhysIO/code/tapas_physio_find_matching_scanphyslog_philips.m +++ b/PhysIO/code/readin/tapas_physio_find_matching_scanphyslog_philips.m @@ -19,7 +19,7 @@ % tapas_physio_find_matching_scanphyslog_philips % % See also tapas_physio_get_filename_from_id_philips -% + % Author: Lars Kasper % Created: 2014-06-19 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -28,12 +28,13 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ + + x = dir(fullfile(pathLogFiles, 'SCANPHYSLOG*')); nPhys = length(x); for i = 1:nPhys tPhys(i) = str2num(x(i).name(20:25)); + datePhys(i) = str2num(x(i).name(12:19)); end; isCellfnImage = iscell(fnImageArray); @@ -52,10 +53,14 @@ % Philips par/rec files always have a ddmmyyyy_HHMMSST_ formatting part, from % which we extract the time - tFunString = regexp(fnImageArray{d}, '_\d{8}_(\d{6})\d_', 'tokens'); - tFun(d) = str2num(tFunString{1}{1}); - [tmp, iFun] = min(abs(tPhys-tFun(d))); - fnPhysLogArray{d} = x(iFun).name; + dateTimeFunString = regexp(fnImageArray{d}, '_(\d{8})_(\d{6})\d_', 'tokens'); + tFun(d) = str2num(dateTimeFunString{1}{2}); + dateFun(d) = str2num(dateTimeFunString{1}{1}([5:8, 3 4 1 2])); % 26082017 -> 20170826 + + iDateMatching = find(datePhys == dateFun(d)); % consider only logfiles from same day + [tmp, iFun] = min(abs(tPhys(iDateMatching)-tFun(d))); + + fnPhysLogArray{d} = x(iDateMatching(iFun)).name; fprintf(1,'matched %s \n \t --> %s\n\n', fnImageArray{d}, ... fnPhysLogArray{d}); end diff --git a/PhysIO/code/tapas_physio_get_filename_from_id_philips.m b/PhysIO/code/readin/tapas_physio_get_filename_from_id_philips.m similarity index 91% rename from PhysIO/code/tapas_physio_get_filename_from_id_philips.m rename to PhysIO/code/readin/tapas_physio_get_filename_from_id_philips.m index 132b0cbc..1d25aaf3 100644 --- a/PhysIO/code/tapas_physio_get_filename_from_id_philips.m +++ b/PhysIO/code/readin/tapas_physio_get_filename_from_id_philips.m @@ -13,11 +13,11 @@ % get_filename_from_id % % See also -% + % Author: Lars Kasper % Created: 2013-11-04 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. -% $Id: get_filename_from_id.m 320 2013-11-12 10:42:05Z kasperla $ + if nargin < 3 ext = '.nii'; end diff --git a/PhysIO/code/tapas_physio_read_files_siemens_tics.m b/PhysIO/code/readin/tapas_physio_read_columnar_textfiles.m similarity index 72% rename from PhysIO/code/tapas_physio_read_files_siemens_tics.m rename to PhysIO/code/readin/tapas_physio_read_columnar_textfiles.m index f614c3f2..c46bd02c 100644 --- a/PhysIO/code/tapas_physio_read_files_siemens_tics.m +++ b/PhysIO/code/readin/tapas_physio_read_columnar_textfiles.m @@ -1,12 +1,12 @@ -function [C, columnNames] = tapas_physio_read_files_siemens_tics(fileName, fileType) +function [C, columnNames] = tapas_physio_read_columnar_textfiles(fileName, fileType) % Reads _PULS, _RESP, _ECG, _Info-files from Siemens tics format with % multiple numbers of columns and different column headers % -% output = tapas_physio_read_files_siemens_tics(input) +% output = tapas_physio_read_columnar_textfiles(input) % % IN % fileName *.log from Siemens VD/VE tics file format -% fileType 'ECG', 'PULS', 'RESP', 'Info' +% fileType 'ECG', 'PULS', 'RESP', 'Info', 'BIOPAC_TXT' % If not specified, this is read from the last part of the % filename after the last underscore, e.g. % Physio_*_ECG.log -> log @@ -15,12 +15,12 @@ % columnNames cell(1, nColumns) of column names % % EXAMPLE -% tapas_physio_read_files_siemens_tics('Physio_RESP.log', 'RESP') +% tapas_physio_read_columnar_textfiles('Physio_RESP.log', 'RESP') % % equivalent to (since file name unique!) -% tapas_physio_read_files_siemens_tics('Physio_RESP.log') +% tapas_physio_read_columnar_textfiles('Physio_RESP.log') % % See also -% + % Author: Lars Kasper % Created: 2017-11-16 % Copyright (C) 2017 TNU, Institute for Biomedical Engineering, @@ -30,8 +30,8 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 775 2015-07-17 10:52:58Z kasperla $ + + %_AcquisitionInfo @@ -55,7 +55,15 @@ end -switch fileType +switch upper(fileType) + case 'BIDS' + strColumnHeader = ''; + parsePatternPerNColumns{3} = '%f %f %f'; + nEmptyLinesAfterHeader(3) = 0; + case 'BIOPAC_TXT' + strColumnHeader = '.*RESP.*'; + parsePatternPerNColumns{4} = '%f %f %f %d'; + nEmptyLinesAfterHeader(4) = 0; case 'INFO' % header is a subset of % Cologne: % Volume_ID Slice_ID AcqTime_Tics @@ -91,7 +99,7 @@ % Determine number of header lines by searching for the column header line, % which has both Volume and Slice as a keyword in it -haveFoundColumnHeader = false; +haveFoundColumnHeader = isempty(strColumnHeader); % for empty column header search string, don't search (e.g. BIDS no column header) nHeaderLines = 0; while ~haveFoundColumnHeader nHeaderLines = nHeaderLines + 1; @@ -99,22 +107,30 @@ haveFoundColumnHeader = any(regexp(upper(strLine), strColumnHeader)); end -columnNames = regexp(strLine, '([\w]*)', 'tokens'); -columnNames = [columnNames{:}]; % cell of cell into cell of strings +switch upper(fileType) + case 'BIDS' % will be in separate json-file + columnNames = {}; + nColumns = 3; + case 'BIOPAC_TXT' % bad column names with spaces...e.g. 'RESP - RSP100C' + columnNames = regexp(strLine, '([\t])', 'split'); + nColumns = numel(columnNames); + otherwise + columnNames = regexp(strLine, '([\w]*)', 'tokens'); + columnNames = [columnNames{:}]; % cell of cell into cell of strings + nColumns = numel(regexp(strLine, ' *')) + 1; % columns are separated by arbitrary number of spaces +end fclose(fid); -nColumns = numel(regexp(strLine, ' *')) + 1; % columns are separated by arbitrary number of spaces nHeaderLines = nHeaderLines + nEmptyLinesAfterHeader(nColumns); % since empty line after header for CMRR files (not in Cologne!) +% now read the rest of the file fid = fopen(fileName); - -switch fileType - case 'INFO' - C = textscan(fid, parsePatternPerNColumns{nColumns}, 'HeaderLines', nHeaderLines); - case {'PULS', 'RESP'} +switch upper(fileType) + case {'BIDS', 'BIOPAC_TXT', 'INFO', 'PULS', 'RESP'} C = textscan(fid, parsePatternPerNColumns{nColumns}, 'HeaderLines', nHeaderLines); case {'ECG'} C = textscan(fid, parsePatternPerNColumns{nColumns}, 'HeaderLines', nHeaderLines); if nColumns == 4 % CMRR, different ECG channels possible! end end +fclose(fid); diff --git a/PhysIO/code/tapas_physio_read_physlogfiles.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles.m similarity index 85% rename from PhysIO/code/tapas_physio_read_physlogfiles.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles.m index 08063003..359e91c4 100755 --- a/PhysIO/code/tapas_physio_read_physlogfiles.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles.m @@ -29,8 +29,8 @@ % cpulse time events of R-wave peak in cardiac time series (seconds) % acq_codes slice/volume start events marked by number <> 0 % for time points in t -% 10/20 = scan start/end; -% 1 = ECG pulse; 2 = OXY max; 3 = Resp trigger; +% 10/20 = scan start/end; +% 1 = ECG pulse; 2 = OXY max; 3 = Resp trigger; % 8 = scan volume trigger % % EXAMPLE @@ -38,7 +38,7 @@ % tapas_physio_read_physlogfiles(logfile, vendor, cardiac_modality); % % See physio_also main_create_regressors -% + % Author: Lars Kasper % Created: 2013-02-16 % Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -57,9 +57,15 @@ end switch lower(log_files.vendor) + case 'bids' + [c, r, t, cpulse, acq_codes] = ... + tapas_physio_read_physlogfiles_bids(log_files, cardiac_modality, verbose); case 'biopac_mat' [c, r, t, cpulse, acq_codes] = ... tapas_physio_read_physlogfiles_biopac_mat(log_files, cardiac_modality, verbose); + case 'biopac_txt' + [c, r, t, cpulse, acq_codes, verbose, gsr] = ... + tapas_physio_read_physlogfiles_biopac_txt(log_files, cardiac_modality, verbose); case 'custom' [c, r, t, cpulse] = ... tapas_physio_read_physlogfiles_custom(log_files, verbose); @@ -94,15 +100,18 @@ if ~isempty(t) && t(1) > 0 && ~isSiemensTics dt = t(2) - t(1); nPrependSamples = ceil(t(1)/dt); - t = [(0:nPrependSamples-1)'*dt;t]; - if ~isempty(log_files.cardiac) - c = [zeros(nPrependSamples, 1);c]; + if ~isempty(c) + prependSamples = tapas_physio_simulate_pulse_samples(t, c, nPrependSamples, 'pre', verbose); + c = [prependSamples;c]; end - if ~isempty(log_files.respiration) - r = [zeros(nPrependSamples,1);r]; + if ~isempty(r) + prependSamples = tapas_physio_simulate_pulse_samples(t, r, nPrependSamples, 'pre', verbose); + r = [prependSamples;r]; end if ~isempty(acq_codes) acq_codes = [zeros(nPrependSamples,1);acq_codes]; end + + t = [(0:nPrependSamples-1)'*dt;t]; end end diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_GE.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_GE.m similarity index 98% rename from PhysIO/code/tapas_physio_read_physlogfiles_GE.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_GE.m index 6b6bc94d..384d09d4 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_GE.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_GE.m @@ -37,7 +37,7 @@ % tapas_physio_read_physlogfiles_GE(logfiles); % % See also tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2013-02-16 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -122,7 +122,7 @@ function fh = plot_interpolation(tOrig, yOrig, tInterp, yInterp, ... stringOrigInterp) fh = tapas_physio_get_default_fig_params; -stringTitle = sprintf('Interpolation of %s signal', stringOrigInterp{1}); +stringTitle = sprintf('Read-In: Interpolation of %s signal', stringOrigInterp{1}); set(fh, 'Name', stringTitle); plot(tOrig, yOrig, 'go--'); hold all; plot(tInterp, yInterp,'r.'); diff --git a/PhysIO/code/readin/tapas_physio_read_physlogfiles_bids.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_bids.m new file mode 100644 index 00000000..be15fa9a --- /dev/null +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_bids.m @@ -0,0 +1,222 @@ +function [c, r, t, cpulse, acq_codes, verbose] = ... + tapas_physio_read_physlogfiles_bids(log_files, cardiac_modality, ... + verbose, varargin) +% Reads in 3-column tsv-file from BIDS Data (cardiac, respiratory, trigger), +% assuming log_files-meta information to be in an accompanying .json-file +% Note: if a JSON file of the same file name exists (but .json instead of .tsv) +% column order of physiological recordings will be read from there as well +% as values for sampling_interval and relative_start_acquisition, if they were +% empty before +% +% Details of the Brain Imaging Data Structure (BIDS) standard for peripheral +% physiological recordings can be found here: +% +% https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/ +% 06-physiological-and-other-continous-recordings.html +% +% [c, r, t, cpulse, acq_codes, verbose] = tapas_physio_read_physlogfiles_biopac_txt(... +% log_files, cardiac_modality, verbose, varargin) +% +% IN log_files +% .log_cardiac *.tsv[.gz] file ([zipped] tab separated file, +% contains 3 columns of the form +% cardiac respiratory trigger +% -0.949402 -0.00610382 0 +% -0.949402 -0.00610382 0 +% -0.951233 -0.00915558 0 +% -0.951233 -0.00915558 0 +% -0.953064 -0.0122073 0 +% -0.953064 -0.0122073 0 +% -0.95459 -0.0076297 1 +% -0.95459 -0.0076297 0 +% - cardiac and respiratory column contain the raw +% physiological traces +% - for cardiac, alternatively, one can set the +% cardiac triggers (cpulse), e.g. detected +% R-peaks, as 0/1 time series, as for scan trigger +% - trigger is 0 everywhere but at +% start of a scanned volume (=1) +% .log_respiration same as .log_cardiac +% .sampling_interval sampling interval (in seconds) +% default: 1 ms (1000 Hz) +% cardiac_modality 'ECG' or 'PULS'/'PPU'/'OXY' to determine +% which channel data to be returned +% UNUSED, is always column labeled 'cardiac' +% verbose +% .level debugging plots are created if level >=3 +% .fig_handles appended by handle to output figure +% +% OUT +% cpulse time events of R-wave peak in cardiac time series (seconds) +% , if raw cardiac trace is given... +% r respiratory time series +% t vector of time points (in seconds) +% c cardiac time series (PPU) +% acq_codes slice/volume start events marked by number <> 0 +% for time points in t +% 10/20 = scan start/end; +% 1 = ECG pulse; 2 = OXY max; 4 = Resp trigger; +% 8 = scan volume trigger (on) +% 16 = scan volume trigger (off) +% +% EXAMPLE +% tapas_physio_read_physlogfiles_biopac_txt +% +% See also tapas_physio_read_physlogfiles_siemens tapas_physio_plot_raw_physdata_siemens_hcp + +% Author: Lars Kasper +% Created: 2018-12-14 +% Copyright (C) 2018 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. + +% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public +% License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +%% read out values +DEBUG = verbose.level >= 2; + +hasRespirationFile = ~isempty(log_files.respiration); +hasCardiacFile = ~isempty(log_files.cardiac); + +if hasCardiacFile + fileName = log_files.cardiac; +elseif hasRespirationFile + fileName = log_files.respiration; +end + +%% check for .gz files and unzip to temporary file +[fp, fn, ext] = fileparts(fileName); + +isZipped = strcmpi(ext, '.gz'); + +if isZipped + fileJson = regexprep(fileName, '\.tsv\.gz', '\.json'); + tempFilePath = tempname; % tempname is matlab inbuilt + fileName = gunzip(fileName, tempFilePath); + fileName = fileName{1}; +else + fileJson = regexprep(fileName, '\.tsv', '\.json'); +end + + + +hasJsonFile = isfile(fileJson); + +if hasJsonFile + val = jsondecode(fileread(fileJson)); +else + verbose = tapas_physio_log(... + ['No .json file found. Please specify log_files.sampling_interval' ... + ' and log_files.relative_start_acquisition explicitly.'], verbose, 1); +end + +dt = log_files.sampling_interval; +if isempty(dt) + if hasJsonFile + dt = 1/val.SamplingFrequency; + else + verbose = tapas_physio_log(... + ['No .json file found and empty log_files.sampling_interval. ' ... + 'Please specify explicitly.'], verbose, 2); + end +end + +tRelStartScan = log_files.relative_start_acquisition; +if isempty(tRelStartScan) + if hasJsonFile + % in BIDS, start of the phys logging is stated relative to the first volume scan start. + % PhysIO defines the scan acquisiton relative to the phys log start + tRelStartScan = -val.StartTime; + else + tRelStartScan = 0; + end +end + +% default columns in text file for phys recordings; overruled by JSON file +% 1 = cardiac, 2 = resp, 3 = trigger +bidsColumnNames = {'cardiac', 'respiratory', 'trigger'}; +idxCol = 1:3; %set default values for columns from BIDS +for iCol = 1:3 + if hasJsonFile + idxCurrCol = find(cellfun(@(x) isequal(lower(x), bidsColumnNames{iCol}), val.Columns)); + if ~isempty(idxCurrCol) + idxCol(iCol) = idxCurrCol; + end + end +end + +C = tapas_physio_read_columnar_textfiles(fileName, 'BIDS'); +c = double(C{idxCol(1)}); +r = double(C{idxCol(2)}); +iAcqStart = (double(C{idxCol(3)})~=0); % trigger has 1, rest is 0; + + +%% Create timing vector from samples + +nSamples = max(numel(c), numel(r)); +t = -tRelStartScan + ((0:(nSamples-1))*dt)'; + +%% Recompute acq_codes as for Siemens (volume on/volume off) +acq_codes = []; + +if ~isempty(iAcqStart) % otherwise, nothing to read ... + % iAcqStart is a columns of 0 and 1, 1 for the trigger event of a new + % volume start + + % sometimes, trigger is on for several samples; ignore these extended + % phases of "on-triggers" as duplicate values, if trigger distance is + % very different + % + % fraction of mean trigger distance; if trigger time difference below that, will be removed + outlierThreshold = 0.2; + + idxAcqStart = find(iAcqStart); + dAcqStart = diff(idxAcqStart); + + % + 1 because of diff + iAcqOutlier = 1 + find(dAcqStart < outlierThreshold*mean(dAcqStart)); + iAcqStart(idxAcqStart(iAcqOutlier)) = 0; + + acq_codes = zeros(nSamples,1); + acq_codes(iAcqStart) = 8; % to match Philips etc. format + + nAcqs = numel(find(iAcqStart)); + + if nAcqs >= 1 + % report time of acquisition, as defined in SPM + meanTR = mean(diff(t(iAcqStart))); + stdTR = std(diff(t(iAcqStart))); + verbose = tapas_physio_log(... + sprintf('TR = %.3f +/- %.3f s (Estimated mean +/- std time of repetition for one volume; nTriggers = %d)', ... + meanTR, stdTR, nAcqs), verbose, 0); + end +end + + +%% Plot, if wanted + +if DEBUG + stringTitle = 'Read-In: Raw BIDS physlog data (TSV file)'; + verbose.fig_handles(end+1) = ... + tapas_physio_plot_raw_physdata_siemens_hcp(t, c, r, acq_codes, ... + stringTitle); +end + +%% occasionally, cardiac time course is instead containing 0/1 cardiac triggers, +% and not raw trace; check this and populate cpulse accordingly +if all(ismember(unique(c), [1 0])) + cpulse = t(c==1); +else + cpulse = []; +end + +%% delete temporary unzipped file +if isZipped + [status,message,messageId] = rmdir(tempFilePath, 's'); + % warning if deletion failed + if status == 0 + tapas_physio_log(sprintf('%s: %s', messageId, message), verbose, 1) + end +end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_biopac_mat.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_biopac_mat.m similarity index 97% rename from PhysIO/code/tapas_physio_read_physlogfiles_biopac_mat.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_biopac_mat.m index 3891d4f9..5c0fa43e 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_biopac_mat.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_biopac_mat.m @@ -41,17 +41,16 @@ % tapas_physio_read_physlogfiles_GE(logfiles); % % See also tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2016-08-15 -% Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. +% Copyright (C) 2016 Institute for Biomedical Engineering, ETH/Uni Zurich. % % This file is part of the PhysIO toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% read out parameters DEBUG = verbose.level >= 3; @@ -149,7 +148,7 @@ if DEBUG verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - stringTitle = 'Extracted time courses from Biopac mat file'; + stringTitle = 'Read-In: Extracted time courses from Biopac mat file'; set(gcf, 'Name', stringTitle); plot(t, data); legend(labels); diff --git a/PhysIO/code/readin/tapas_physio_read_physlogfiles_biopac_txt.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_biopac_txt.m new file mode 100644 index 00000000..febe24d2 --- /dev/null +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_biopac_txt.m @@ -0,0 +1,140 @@ +function [c, r, t, cpulse, acq_codes, verbose, gsr] = tapas_physio_read_physlogfiles_biopac_txt(... + log_files, cardiac_modality, verbose, varargin) +% Reads in 4-column txt-export from BioPac Data (resp, ppu, gsr, trigger) +% +% [c, r, t, cpulse, acq_codes, verbose] = tapas_physio_read_physlogfiles_biopac_txt(... +% log_files, cardiac_modality, verbose, varargin) +% +% IN log_files +% .log_cardiac *.txt file, contains 4 columns of the form +% RESP - RSP100C GSR - EDA100C-MRI PPG - PPG100C Marker +% -0.949402 -0.00610382 0.0134277 0 +% -0.949402 -0.00610382 0.0134277 0 +% -0.951233 -0.00915558 0.0204468 0 +% -0.951233 -0.00915558 0.0204468 0 +% -0.953064 -0.0122073 0.0259399 0 +% -0.953064 -0.0122073 0.0259399 0 +% -0.95459 -0.0076297 0.0296021 0 +% -0.95459 -0.0076297 0.0296021 0 +% .log_respiration same as .log_cardiac +% .sampling_interval sampling interval (in seconds) +% default: 1 ms (1000 Hz) +% cardiac_modality 'ECG' or 'PULS'/'PPU'/'OXY' to determine +% which channel data to be returned +% UNUSED, is always pulse plethysmographic unit +% for BioPac +% verbose +% .level debugging plots are created if level >=3 +% .fig_handles appended by handle to output figure +% +% OUT +% cpulse time events of R-wave peak in cardiac time series (seconds) +% , since not written to logfile +% r respiratory time series +% t vector of time points (in seconds) +% c cardiac time series (PPU) +% acq_codes slice/volume start events marked by number <> 0 +% for time points in t +% 10/20 = scan start/end; +% 1 = ECG pulse; 2 = OXY max; 4 = Resp trigger; +% 8 = scan volume trigger (on) +% 16 = scan volume trigger (off) +% gsr galvanic skin response (not used) +% +% EXAMPLE +% tapas_physio_read_physlogfiles_biopac_txt +% +% See also tapas_physio_read_physlogfiles_siemens tapas_physio_plot_raw_physdata_siemens_hcp + +% Author: Lars Kasper +% Created: 2018-09-27 +% Copyright (C) 2018 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. + +% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public +% License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + +%% read out values +DEBUG = verbose.level >= 2; + +hasRespirationFile = ~isempty(log_files.respiration); +hasCardiacFile = ~isempty(log_files.cardiac); + +hasRespirationFile = ~isempty(log_files.respiration); +hasCardiacFile = ~isempty(log_files.cardiac); + +if hasCardiacFile + fileName = log_files.cardiac; +elseif hasRespirationFile + fileName = log_files.respiration; +end + + +[C, columnNames] = tapas_physio_read_columnar_textfiles(fileName, 'BIOPAC_TXT'); +c = double(C{3}); +r = double(C{1}); +gsr = double(C{2}); +iAcqOn = (double(C{4})~=0); % trigger has 11, rest is 0; + +%% Create timing vector from samples + +dt = log_files.sampling_interval; + +if isempty(dt) + dt = 1/1000; % 1000 Hz sampling interval +end + +nSamples = max(numel(c), numel(r)); +t = -log_files.relative_start_acquisition + ((0:(nSamples-1))*dt)'; + +%% Recompute acq_codes as for Siemens (volume on/volume off) +acq_codes = []; + +if ~isempty(iAcqOn) % otherwise, nothing to read ... + % iAcqOn is a column of 1s and 0s, 1 whenever scan acquisition is on + % Determine 1st start and last stop directly via first/last 1 + % Determine everything else in between via difference (go 1->0 or 0->1) + iAcqStart = find(iAcqOn, 1, 'first'); + iAcqEnd = find(iAcqOn, 1, 'last'); + d_iAcqOn = diff(iAcqOn); + + % index shift + 1, since diff vector has index of differences i_(n+1) - i_n, + % and the latter of the two operands (i_(n+1)) has sought value +1 + iAcqStart = [iAcqStart; find(d_iAcqOn == 1) + 1]; + % no index shift, for the same reason + iAcqEnd = [find(d_iAcqOn == -1); iAcqEnd]; + + acq_codes = zeros(nSamples,1); + acq_codes(iAcqStart) = 8; % to match Philips etc. format + acq_codes(iAcqEnd) = 16; % don't know... + + % report estimated onset gap between last slice of volume_n and 1st slice of + % volume_(n+1) + nAcqStarts = numel(iAcqStart); + nAcqEnds = numel(iAcqEnd); + nAcqs = min(nAcqStarts, nAcqEnds); + + if nAcqs >= 1 + % report time of acquisition, as defined in SPM + TA = mean(t(iAcqEnd(1:nAcqs)) - t(iAcqStart(1:nAcqs))); + verbose = tapas_physio_log(... + sprintf('TA = %.4f s (Estimated time of acquisition during one volume TR)', ... + TA), verbose, 0); + end +end + + +%% Plot, if wanted + +if DEBUG + stringTitle = 'Read-In: Raw BioPac physlog data (TXT Export)'; + verbose.fig_handles(end+1) = ... + tapas_physio_plot_raw_physdata_siemens_hcp(t, c, r, acq_codes, ... + stringTitle); +end + +%% Undefined output parameters + +cpulse = []; diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_brainproducts.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_brainproducts.m similarity index 97% rename from PhysIO/code/tapas_physio_read_physlogfiles_brainproducts.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_brainproducts.m index cdb275c4..f993ce5a 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_brainproducts.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_brainproducts.m @@ -1,183 +1,183 @@ -function [c, r, t, cpulse, acq_codes] = tapas_physio_read_physlogfiles_brainproducts(log_files, ... - cardiac_modality, verbose) -% reads out physiological time series (ECG, PMU, resp belt) and timing vector for BrainProducts .eeg file -% -% [cpulse, rpulse, t, c, acq_codes] = tapas_physio_read_physlogfiles_brainproducts(logfiles, ... -% verbose) -% -% -% IN log_files -% .cardiac contains ECG or pulse oximeter time course -% for BrainProducts: usually the same as respiration -% .respiration contains breathing belt amplitude time course -% for BrainProducts: usually the same as cardiac -% .sampling_interval is ignored here, read from logfile -% -% cardiac_modality -% 'ecg1_filtered' filtered 1st ECG channel signal -% (Default) -% 'ecg2_filtered' filteered 2nd ECG channel -% (sometimes less gradient artifacts) -% 'ecg1_raw' raw 1st ECG channel -% -% verbose -% .level debugging plots are created if level >=3 -% .fig_handles appended by handle to output figure -% -% OUT -% r respiratory time series -% c cardiac time series (ECG or pulse oximetry) -% t vector of time points (in seconds) -% cpulse time events of R-wave peak in cardiac time series (seconds) -% for Biopac: usually empty, kept for compatibility -% acq_codes slice/volume start events marked by number <> 0 -% for time points in t -% EXAMPLE -% [ons_secs.cpulse, ons_secs.rpulse, ons_secs.t, ons_secs.c] = -% tapas_physio_read_physlogfiles_GE(logfiles); -% -% See also tapas_physio_main_create_regressors -% -% Author: Lars Kasper -% Created: 2017-01-27 -% Copyright (C) 2017 Institute for Biomedical Engineering, ETH/Uni Zurich. -% -% This file is part of the PhysIO toolbox, which is released under the terms of the GNU General Public -% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . - - -%% user input - -% volume trigger event -event_type = 'Response'; -event_value = 'R128'; - -switch lower(cardiac_modality) - case {'ecg','ecg1_filtered'} - % chose one ecg channel (only for visualisation) - ecg_ch = 1; - case 'ecg2_filtered' - % chose one ecg channel (only for visualisation) - ecg_ch = 2; -end - -% is ecg data flipped? -ecg_is_flipped = 1; - -%% data - -% read data and header info using field trip (included in SPM) -if ~exist('ft_read_header', 'file') - pathSpm = fileparts(which('spm')); - addpath(genpath(fullfile(pathSpm, 'external', 'fieldtrip'))); -end - -hdr = ft_read_header(log_files.cardiac); -data = ft_read_data(log_files.cardiac); - -fs = hdr.Fs; % sampling frequency in Hz -N = hdr.nSamples; % number of samples -dt = 1/fs; % sampling interval in seconds -t = linspace(0,dt*(N-1), N); % time vector in seconds - -fh = []; - -% plot first 10 seconds of the raw data -fh(end+1,1) = tapas_physio_get_default_fig_params(); -plot_end = 10 * fs; -plot(t(1:plot_end), data(:,(1:plot_end))); -xlabel('time in seconds'); -ylabel(hdr.chanunit); -legend(hdr.label); - -% extract ECG data -if ecg_is_flipped - s = -data(ecg_ch,:); -else - s = data(ecg_ch,:); -end - -% plot first 10 seconds again for check -fh(end+1,1) = tapas_physio_get_default_fig_params(); -plot_end = 10 * fs; -plot(t(1:plot_end), s(:,(1:plot_end))); -xlabel('time in seconds'); -ylabel(hdr.chanunit{ecg_ch}); -legend(hdr.label{ecg_ch}); -%% events -% display all events in the data on the command line -cfg = []; -cfg.dataset = log_files.cardiac; -cfg.trialdef.eventtype = '?'; -ft_definetrial(cfg); - -% define volume trigger -cfg.trialdef.eventtype = event_type; -cfg.trialdef.eventvalue = event_value; -cfg = ft_definetrial(cfg); -trigger_pos = cfg.trl(:,1); -n_trigger = length(trigger_pos); - -% plot raw data and events -fh(end+1,1) = tapas_physio_get_default_fig_params(); -max_value = max(s); -plot(t, s); -hold all; -stem(t(trigger_pos), max_value * ones(1,n_trigger)); - -% find segments -% compute the number of samples between each trigger (pos-1) -diff_trigger_pos = diff(trigger_pos); -% find the positons where a change is happening -diff_diff_trigger_pos = find(diff(diff_trigger_pos)); -% add first and last event -start_segment = [1; diff_diff_trigger_pos(2:2:end)+1]; -end_segment = [diff_diff_trigger_pos(1:2:end)+1; n_trigger]; -% number of samples per trials -n_trial_samples = diff_trigger_pos(start_segment); -% position of start segment in samples -sample_start_segment = trigger_pos(start_segment); -% position of end segment in samples (not including the whole trial) -sample_end_segment = trigger_pos(end_segment); -% number of segments -n_segments = length(start_segment); -% number of trials per segment -n_trials = end_segment - start_segment + 1; -% TR per trial -TR_trials = n_trial_samples*dt; - -% plot segments -stem(t(sample_start_segment), max_value * ones(1,n_segments), '--', 'LineWidth', 5); -stem(t(sample_end_segment), max_value * ones(1,n_segments), '--', 'LineWidth', 5); -legend('signal', 'all trigger', 'start segment', 'end segment'); - -%% load only first segment for sanity check -cfg = []; -cfg.dataset = log_files.cardiac; -cfg.trialdef.eventtype = event_type; -cfg.trialdef.eventvalue = event_value; -cfg.trialfun = 'ft_trialfun_segment'; -cfg.trialdef.prestim = 0; -cfg.trialdef.poststim = TR_trials(1); -cfg.trialdef.segment_start = start_segment(1); -cfg.trialdef.segment_end = end_segment(1); -cfg = ft_definetrial(cfg); - -% plot segments -fh(end+1,1) = tapas_physio_get_default_fig_params(); -max_value = max(s); -plot(t, s); -hold all; -stem(t(cfg.trl(:,1)), max_value * ones(1,n_trials(1))); -stem(t(cfg.trl(:,2)), max_value * ones(1,n_trials(1))); -legend('signal', 'start trial', 'end trial'); - -%% save results -[~,name] = fileparts(file_name); -save_name = fullfile(file_path, [name, '_segments.mat']); -save(save_name, 'sample_start_segment', 'sample_end_segment', ... - 'n_segments', 'n_trials', 'TR_trials', 'start_segment', 'end_segment', ... - 'event_type', 'event_value', 'n_trial_samples', 'fs', ... +function [c, r, t, cpulse, acq_codes] = tapas_physio_read_physlogfiles_brainproducts(log_files, ... + cardiac_modality, verbose) +% reads out physiological time series (ECG, PMU, resp belt) and timing vector for BrainProducts .eeg file +% +% [cpulse, rpulse, t, c, acq_codes] = tapas_physio_read_physlogfiles_brainproducts(logfiles, ... +% verbose) +% +% +% IN log_files +% .cardiac contains ECG or pulse oximeter time course +% for BrainProducts: usually the same as respiration +% .respiration contains breathing belt amplitude time course +% for BrainProducts: usually the same as cardiac +% .sampling_interval is ignored here, read from logfile +% +% cardiac_modality +% 'ecg1_filtered' filtered 1st ECG channel signal +% (Default) +% 'ecg2_filtered' filteered 2nd ECG channel +% (sometimes less gradient artifacts) +% 'ecg1_raw' raw 1st ECG channel +% +% verbose +% .level debugging plots are created if level >=3 +% .fig_handles appended by handle to output figure +% +% OUT +% r respiratory time series +% c cardiac time series (ECG or pulse oximetry) +% t vector of time points (in seconds) +% cpulse time events of R-wave peak in cardiac time series (seconds) +% for Biopac: usually empty, kept for compatibility +% acq_codes slice/volume start events marked by number <> 0 +% for time points in t +% EXAMPLE +% [ons_secs.cpulse, ons_secs.rpulse, ons_secs.t, ons_secs.c] = +% tapas_physio_read_physlogfiles_GE(logfiles); +% +% See also tapas_physio_main_create_regressors + +% Author: Lars Kasper +% Created: 2017-01-27 +% Copyright (C) 2017 Institute for Biomedical Engineering, ETH/Uni Zurich. +% +% This file is part of the PhysIO toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + + +%% user input + +% volume trigger event +event_type = 'Response'; +event_value = 'R128'; + +switch lower(cardiac_modality) + case {'ecg','ecg1_filtered'} + % chose one ecg channel (only for visualisation) + ecg_ch = 1; + case 'ecg2_filtered' + % chose one ecg channel (only for visualisation) + ecg_ch = 2; +end + +% is ecg data flipped? +ecg_is_flipped = 1; + +%% data + +% read data and header info using field trip (included in SPM) +if ~exist('ft_read_header', 'file') + pathSpm = fileparts(which('spm')); + addpath(genpath(fullfile(pathSpm, 'external', 'fieldtrip'))); +end + +hdr = ft_read_header(log_files.cardiac); +data = ft_read_data(log_files.cardiac); + +fs = hdr.Fs; % sampling frequency in Hz +N = hdr.nSamples; % number of samples +dt = 1/fs; % sampling interval in seconds +t = linspace(0,dt*(N-1), N); % time vector in seconds + +fh = []; + +% plot first 10 seconds of the raw data +fh(end+1,1) = tapas_physio_get_default_fig_params(); +plot_end = 10 * fs; +plot(t(1:plot_end), data(:,(1:plot_end))); +xlabel('time in seconds'); +ylabel(hdr.chanunit); +legend(hdr.label); + +% extract ECG data +if ecg_is_flipped + s = -data(ecg_ch,:); +else + s = data(ecg_ch,:); +end + +% plot first 10 seconds again for check +fh(end+1,1) = tapas_physio_get_default_fig_params(); +plot_end = 10 * fs; +plot(t(1:plot_end), s(:,(1:plot_end))); +xlabel('time in seconds'); +ylabel(hdr.chanunit{ecg_ch}); +legend(hdr.label{ecg_ch}); +%% events +% display all events in the data on the command line +cfg = []; +cfg.dataset = log_files.cardiac; +cfg.trialdef.eventtype = '?'; +ft_definetrial(cfg); + +% define volume trigger +cfg.trialdef.eventtype = event_type; +cfg.trialdef.eventvalue = event_value; +cfg = ft_definetrial(cfg); +trigger_pos = cfg.trl(:,1); +n_trigger = length(trigger_pos); + +% plot raw data and events +fh(end+1,1) = tapas_physio_get_default_fig_params(); +max_value = max(s); +plot(t, s); +hold all; +stem(t(trigger_pos), max_value * ones(1,n_trigger)); + +% find segments +% compute the number of samples between each trigger (pos-1) +diff_trigger_pos = diff(trigger_pos); +% find the positons where a change is happening +diff_diff_trigger_pos = find(diff(diff_trigger_pos)); +% add first and last event +start_segment = [1; diff_diff_trigger_pos(2:2:end)+1]; +end_segment = [diff_diff_trigger_pos(1:2:end)+1; n_trigger]; +% number of samples per trials +n_trial_samples = diff_trigger_pos(start_segment); +% position of start segment in samples +sample_start_segment = trigger_pos(start_segment); +% position of end segment in samples (not including the whole trial) +sample_end_segment = trigger_pos(end_segment); +% number of segments +n_segments = length(start_segment); +% number of trials per segment +n_trials = end_segment - start_segment + 1; +% TR per trial +TR_trials = n_trial_samples*dt; + +% plot segments +stem(t(sample_start_segment), max_value * ones(1,n_segments), '--', 'LineWidth', 5); +stem(t(sample_end_segment), max_value * ones(1,n_segments), '--', 'LineWidth', 5); +legend('signal', 'all trigger', 'start segment', 'end segment'); + +%% load only first segment for sanity check +cfg = []; +cfg.dataset = log_files.cardiac; +cfg.trialdef.eventtype = event_type; +cfg.trialdef.eventvalue = event_value; +cfg.trialfun = 'ft_trialfun_segment'; +cfg.trialdef.prestim = 0; +cfg.trialdef.poststim = TR_trials(1); +cfg.trialdef.segment_start = start_segment(1); +cfg.trialdef.segment_end = end_segment(1); +cfg = ft_definetrial(cfg); + +% plot segments +fh(end+1,1) = tapas_physio_get_default_fig_params(); +max_value = max(s); +plot(t, s); +hold all; +stem(t(cfg.trl(:,1)), max_value * ones(1,n_trials(1))); +stem(t(cfg.trl(:,2)), max_value * ones(1,n_trials(1))); +legend('signal', 'start trial', 'end trial'); + +%% save results +[~,name] = fileparts(file_name); +save_name = fullfile(file_path, [name, '_segments.mat']); +save(save_name, 'sample_start_segment', 'sample_end_segment', ... + 'n_segments', 'n_trials', 'TR_trials', 'start_segment', 'end_segment', ... + 'event_type', 'event_value', 'n_trial_samples', 'fs', ... 'ecg_is_flipped', 'ecg_ch', 'file_path', 'file_name'); \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_custom.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_custom.m similarity index 97% rename from PhysIO/code/tapas_physio_read_physlogfiles_custom.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_custom.m index f95116c7..28997444 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_custom.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_custom.m @@ -23,7 +23,7 @@ % tapas_physio_read_physlogfiles(logfile, vendor, cardiac_modality); % % See also tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2013-02-16 % Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -32,8 +32,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% read out values DEBUG = verbose.level >=3; @@ -118,7 +117,7 @@ function fh = plot_interpolation(tOrig, yOrig, tInterp, yInterp, ... stringOrigInterp) fh = tapas_physio_get_default_fig_params; -stringTitle = sprintf('Interpolation of %s signal', stringOrigInterp{1}); +stringTitle = sprintf('Read-In: Interpolation of %s signal', stringOrigInterp{1}); set(fh, 'Name', stringTitle); plot(tOrig, yOrig, 'go--'); hold all; plot(tInterp, yInterp,'r.'); diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_philips.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_philips.m similarity index 99% rename from PhysIO/code/tapas_physio_read_physlogfiles_philips.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_philips.m index d70257f4..861e1545 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_philips.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_philips.m @@ -42,7 +42,7 @@ % tapas_physio_read_physlogfiles(logfile, vendor, cardiac_modality); % % See also tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2013-02-16 % Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -51,8 +51,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% read out values hasCardiac = ~isempty(log_files.cardiac); diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_philips_matrix.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_philips_matrix.m similarity index 96% rename from PhysIO/code/tapas_physio_read_physlogfiles_philips_matrix.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_philips_matrix.m index 454f93f6..250cac3b 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_philips_matrix.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_philips_matrix.m @@ -21,7 +21,7 @@ % See also tapas_physio_read_physlogfiles_philips % See also tapas_physio_create_scan_timing_from_gradients_philips % See also tapas_physio_create_scan_timing_from_gradients_auto_philips -% + % Author: Lars Kasper % Created: 2015-01-11 % Copyright (C) 2015, Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -30,8 +30,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: tapas_physio_read_physlogfiles_philips_matrix.m 632 2015-01-09 12:36:12Z kasperla $ + + % use textread as long as it exists, for it is much faster (factor 4) than % textscan; TODO: use fread and sscanf to make it even faster... diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_siemens.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens.m similarity index 99% rename from PhysIO/code/tapas_physio_read_physlogfiles_siemens.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens.m index 6a9f8c7f..0843e141 100755 --- a/PhysIO/code/tapas_physio_read_physlogfiles_siemens.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens.m @@ -37,7 +37,7 @@ % tapas_physio_read_physlogfiles_siemens(logfile, vendor, cardiac_modality); % % See also tapas_physio_main_create_regressors -% + % Author: Lars Kasper % file structure information from PhLeM Toolbox, T. Verstynen (November 2007); % and Deshpande and J. Grinstead, Siemens Medical Solutions (March 2009) diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_siemens_hcp.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_hcp.m similarity index 99% rename from PhysIO/code/tapas_physio_read_physlogfiles_siemens_hcp.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_hcp.m index 5231ad17..e9c4519b 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_siemens_hcp.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_hcp.m @@ -50,7 +50,7 @@ % tapas_physio_read_physlogfiles_siemens_hcp % % See also tapas_physio_read_physlogfiles_siemens tapas_physio_plot_raw_physdata_siemens_hcp -% + % Author: Lars Kasper % Created: 2018-01-23 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_siemens_raw.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_raw.m similarity index 99% rename from PhysIO/code/tapas_physio_read_physlogfiles_siemens_raw.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_raw.m index 987b5e9a..4e69458e 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_siemens_raw.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_raw.m @@ -22,7 +22,7 @@ % tapas_physio_read_physlogfiles_siemens_raw % % See also -% + % Author: Lars Kasper % Created: 2016-02-29 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -32,8 +32,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + fid = fopen(fileNameLog); diff --git a/PhysIO/code/tapas_physio_read_physlogfiles_siemens_tics.m b/PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_tics.m similarity index 72% rename from PhysIO/code/tapas_physio_read_physlogfiles_siemens_tics.m rename to PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_tics.m index ecd62a05..906d2d7d 100644 --- a/PhysIO/code/tapas_physio_read_physlogfiles_siemens_tics.m +++ b/PhysIO/code/readin/tapas_physio_read_physlogfiles_siemens_tics.m @@ -49,7 +49,7 @@ % tapas_physio_read_physlogfiles_siemens_tics(logfiles); % % See also tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2014-09-08 % Copyright (C) 2014 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -58,8 +58,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% read out values DEBUG = verbose.level >= 3; @@ -82,25 +81,32 @@ % Cardiac and respiratory sampling intervals are ignored, since Tics are % assumed to be counted in files -dt = log_files.sampling_interval; - -switch numel(dt) - case 3 - dtTics = dt(3); - case 1 - dtTics = dt; - otherwise - dtTics = 2.5e-3; +if ~isempty(log_files.sampling_interval) + verbose = tapas_physio_log( ... + 'Ignoring given sampling intervals, using dt = 2.5 ms (tics) instead', ... + verbose, 1); end +dtTics = 2.5e-3; dtCardiac = dtTics; dtRespiration = dtTics; -acq_codes = []; +%% Define outputs +t = []; +acq_codes = []; +tCardiac = []; +cacq_codes = []; +c = []; +cpulse = []; +tRespiration = []; +racq_codes = []; +r = []; +rpulse = []; +%% Read resp. file if hasRespirationFile - C = tapas_physio_read_files_siemens_tics(log_files.respiration, 'RESP'); + C = tapas_physio_read_columnar_textfiles(log_files.respiration, 'RESP'); nColumns = numel(C); % different file formats indicated by diff number of columns extTriggerSignals = []; @@ -136,18 +142,12 @@ if ~isempty(acqpulse) racq_codes(acqpulse) = racq_codes(acqpulse) + 8; end - - -else - rpulse = []; - r = []; - tRespiration = []; - racq_codes = []; end +%% Read cardiac file if hasCardiacFile - C = tapas_physio_read_files_siemens_tics(log_files.cardiac); + C = tapas_physio_read_columnar_textfiles(log_files.cardiac); nColumns = numel(C); @@ -186,19 +186,11 @@ if ~isempty(acqpulse) cacq_codes(acqpulse) = cacq_codes(acqpulse) + 8; end - - -else - c = []; - tCardiac = []; - cpulse = []; - cacq_codes = []; end -%% interpolate to greater precision, if 2 different sampling rates are given - +%% plot raw data so far if DEBUG fh = tapas_physio_plot_raw_physdata_siemens_tics(tCardiac, c, tRespiration, r, ... hasCardiacFile, hasRespirationFile, cpulse, rpulse, cacq_codes, ... @@ -207,72 +199,71 @@ end -hasDifferentSampling = ~isequal(tCardiac, tRespiration); +%% If only one file exists, take t and acq_codes from that accordingly +hasOnlyCardiacFile = hasCardiacFile && ~hasRespirationFile; +if hasOnlyCardiacFile + t = tCardiac; + acq_codes = cacq_codes; +end -if hasDifferentSampling && hasCardiacFile && hasRespirationFile - %TODO: interpolate acq_codes - - nSamplesRespiration = size(r,1); - nSamplesCardiac = size(c,1); - dtCardiac = tCardiac(2)-tCardiac(1); - dtRespiration = tRespiration(2) - tRespiration(1); +hasOnlyRespirationFile = ~hasCardiacFile && hasRespirationFile; +if hasOnlyRespirationFile + t = tRespiration; + acq_codes = racq_codes; +end + +%% Merge acquisition codes, if both files exist, but on same sampling grid +if hasCardiacFile && hasRespirationFile - isHigherSamplingCardiac = dtCardiac < dtRespiration; - if isHigherSamplingCardiac - t = tCardiac; - rInterp = interp1(tRespiration, r, t); - racq_codesInterp = interp1(tRespiration, racq_codes, t, 'nearest'); - acq_codes = cacq_codes + racq_codesInterp; - - if DEBUG - fh = plot_interpolation(tRespiration, r, t, rInterp, ... - {'respiratory', 'cardiac'}); - verbose.fig_handles(end+1) = fh; - end - r = rInterp; - + haveSameSampling = isequal(tRespiration, tCardiac); + if haveSameSampling + acq_codes = cacq_codes + racq_codes; else - t = tRespiration; - cInterp = interp1(tCardiac, c, t); - cacq_codesInterp = interp1(tCardiac, cacq_codes, t, 'nearest'); - acq_codes = racq_codes + cacq_codesInterp; + %% interpolate to greater precision, if both files exist and + % 2 different sampling rates are given + %interpolate acq_codes and trace with lower sampling rate to higher + %rate - if DEBUG - fh = plot_interpolation(tCardiac, c, t, cInterp, ... - {'cardiac', 'respiratory'}); - verbose.fig_handles(end+1) = fh; - end - c = cInterp; + dtCardiac = tCardiac(2)-tCardiac(1); + dtRespiration = tRespiration(2) - tRespiration(1); - end - -else - - % merge acq codes - if hasCardiacFile - if hasRespirationFile - acq_codes = cacq_codes + racq_codes; + isHigherSamplingCardiac = dtCardiac < dtRespiration; + if isHigherSamplingCardiac + t = tCardiac; + rInterp = interp1(tRespiration, r, t); + racq_codesInterp = interp1(tRespiration, racq_codes, t, 'nearest'); + acq_codes = cacq_codes + racq_codesInterp; + + if DEBUG + fh = plot_interpolation(tRespiration, r, t, rInterp, ... + {'respiratory', 'cardiac'}); + verbose.fig_handles(end+1) = fh; + end + r = rInterp; + else - acq_codes = cacq_codes; + t = tRespiration; + cInterp = interp1(tCardiac, c, t); + cacq_codesInterp = interp1(tCardiac, cacq_codes, t, 'nearest'); + acq_codes = racq_codes + cacq_codesInterp; + + if DEBUG + fh = plot_interpolation(tCardiac, c, t, cInterp, ... + {'cardiac', 'respiratory'}); + verbose.fig_handles(end+1) = fh; + end + c = cInterp; + end - elseif hasRespirationFile - acq_codes = racq_codes; end - - nSamples = max(size(c,1), size(r,1)); - t = -log_files.relative_start_acquisition + ((0:(nSamples-1))*... - min(dtCardiac, dtRespiration))'; end - end - - %% Local function to plot interpolation result function fh = plot_interpolation(tOrig, yOrig, tInterp, yInterp, ... stringOrigInterp) fh = tapas_physio_get_default_fig_params(); -stringTitle = sprintf('Interpolation of %s signal', stringOrigInterp{1}); +stringTitle = sprintf('Read-In: Interpolation of %s signal', stringOrigInterp{1}); set(fh, 'Name', stringTitle); plot(tOrig, yOrig, 'go--'); hold all; plot(tInterp, yInterp,'r.'); diff --git a/PhysIO/code/tapas_physio_repair_scan_events_PHILIPS.m b/PhysIO/code/readin/tapas_physio_repair_scan_events_PHILIPS.m similarity index 98% rename from PhysIO/code/tapas_physio_repair_scan_events_PHILIPS.m rename to PhysIO/code/readin/tapas_physio_repair_scan_events_PHILIPS.m index d5d8c602..73342fe9 100644 --- a/PhysIO/code/tapas_physio_repair_scan_events_PHILIPS.m +++ b/PhysIO/code/readin/tapas_physio_repair_scan_events_PHILIPS.m @@ -40,8 +40,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + thrmin = 1.5; % gap between slices, if spacing >thrmin*min(slicegap) thrmax = 0.5; % gap is large, if gap > thrmax*max(slicegap) @@ -132,7 +131,7 @@ function fh = plot_fix_missing_scan_events(ons, dur, index) fh = tapas_physio_get_default_fig_params(); -set(fh,'Name','Time-difference view between events: Fix missing scan events'); +set(fh,'Name','Read-In: Time-difference view between events: Fix missing scan events'); ax(1) = subplot(3,1,1); hold off; plot(1:length(dur.acq_slice),dur.acq_slice); diff --git a/PhysIO/code/tapas_physio_siemens_line2table.m b/PhysIO/code/readin/tapas_physio_siemens_line2table.m similarity index 99% rename from PhysIO/code/tapas_physio_siemens_line2table.m rename to PhysIO/code/readin/tapas_physio_siemens_line2table.m index b4ba9bb7..1109884d 100755 --- a/PhysIO/code/tapas_physio_siemens_line2table.m +++ b/PhysIO/code/readin/tapas_physio_siemens_line2table.m @@ -20,7 +20,7 @@ % tapas_physio_siemens_line2table % % See also -% + % Author: Lars Kasper % Created: 2016-02-29 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -30,8 +30,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % signals start of data logging iTrigger = regexpi(lineData, '6002'); diff --git a/PhysIO/code/tapas_physio_siemens_table2cardiac.m b/PhysIO/code/readin/tapas_physio_siemens_table2cardiac.m similarity index 99% rename from PhysIO/code/tapas_physio_siemens_table2cardiac.m rename to PhysIO/code/readin/tapas_physio_siemens_table2cardiac.m index 6b8cf0d6..32b0f13d 100644 --- a/PhysIO/code/tapas_physio_siemens_table2cardiac.m +++ b/PhysIO/code/readin/tapas_physio_siemens_table2cardiac.m @@ -35,7 +35,7 @@ % % See also tapas_physio_siemens_line2table % See also tapas_physio_read_physlogfiles_siemens -% + % Author: Lars Kasper % Created: 2016-02-29 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -45,8 +45,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % set new indices to actual cpulse_on = find(data_table(:,3) == 5000); diff --git a/PhysIO/code/tapas_physio_split_data_per_channel_siemens_tics.m b/PhysIO/code/readin/tapas_physio_split_data_per_channel_siemens_tics.m similarity index 98% rename from PhysIO/code/tapas_physio_split_data_per_channel_siemens_tics.m rename to PhysIO/code/readin/tapas_physio_split_data_per_channel_siemens_tics.m index f3281382..ef61beba 100644 --- a/PhysIO/code/tapas_physio_split_data_per_channel_siemens_tics.m +++ b/PhysIO/code/readin/tapas_physio_split_data_per_channel_siemens_tics.m @@ -34,7 +34,7 @@ % tapas_physio_split_data_per_channel_siemens_tics % % See also -% + % Author: Lars Kasper % Created: 2017-11-17 % Copyright (C) 2017 TNU, Institute for Biomedical Engineering, @@ -44,8 +44,8 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 775 2015-07-17 10:52:58Z kasperla $ + + DEBUG = verbose.level >= 3; @@ -178,7 +178,7 @@ %% Show some plots of (interpolated) channel data if DEBUG fh = tapas_physio_get_default_fig_params(); - set(fh, 'Name', 'Raw ECG Siemens Tics data, split into channels') + set(fh, 'Name', 'Read-In: Raw ECG Siemens Tics data, split into channels') verbose.fig_handles(end+1) = fh; diff --git a/PhysIO/code/tapas_physio_create_LOCS_from_VOLLOCS.m b/PhysIO/code/sync/tapas_physio_create_LOCS_from_VOLLOCS.m similarity index 99% rename from PhysIO/code/tapas_physio_create_LOCS_from_VOLLOCS.m rename to PhysIO/code/sync/tapas_physio_create_LOCS_from_VOLLOCS.m index 531eee08..7d8d6c99 100644 --- a/PhysIO/code/tapas_physio_create_LOCS_from_VOLLOCS.m +++ b/PhysIO/code/sync/tapas_physio_create_LOCS_from_VOLLOCS.m @@ -27,7 +27,7 @@ % events started % % See also tapas_physio_new tapas_physio_main_create_regresssors -% + % Author: Lars Kasper % Created: 2013-08-23 % Copyright (C) 2016 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -36,8 +36,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + NallVols = sqpar.Ndummies+sqpar.Nscans; Nslices = sqpar.Nslices; diff --git a/PhysIO/code/tapas_physio_create_scan_timing.m b/PhysIO/code/sync/tapas_physio_create_scan_timing.m similarity index 96% rename from PhysIO/code/tapas_physio_create_scan_timing.m rename to PhysIO/code/sync/tapas_physio_create_scan_timing.m index 95c6b4a6..32da2d9b 100644 --- a/PhysIO/code/tapas_physio_create_scan_timing.m +++ b/PhysIO/code/sync/tapas_physio_create_scan_timing.m @@ -58,7 +58,7 @@ % events started % % See also tapas_physio_new tapas_physio_main_create_regresssors -% + % Author: Lars Kasper % Created: 2013-08-23 % Copyright (C) 2016 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -67,8 +67,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + sqpar = scan_timing.sqpar; @@ -97,12 +96,16 @@ [VOLLOCS, LOCS, verbose] = ... tapas_physio_create_scan_timing_from_tics_siemens( ... ons_secs.t, ons_secs.t_start, log_files, verbose); - case 'biopac_mat' + case {'biopac_mat', 'biopac_txt', 'bids'} [VOLLOCS, LOCS, verbose] = ... tapas_physio_create_scan_timing_from_acq_codes( ... ons_secs.t, ons_secs.acq_codes, sqpar, ... log_files.align_scan, verbose); end + otherwise + verbose = tapas_physio_log(... + sprintf('unknown scan_timing.sync.method: %s', ... + scan_timing.sync.method), verbose, 2); end diff --git a/PhysIO/code/tapas_physio_create_scan_timing_from_acq_codes.m b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_acq_codes.m similarity index 94% rename from PhysIO/code/tapas_physio_create_scan_timing_from_acq_codes.m rename to PhysIO/code/sync/tapas_physio_create_scan_timing_from_acq_codes.m index 9bb063cf..e1c57d1b 100644 --- a/PhysIO/code/tapas_physio_create_scan_timing_from_acq_codes.m +++ b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_acq_codes.m @@ -27,7 +27,7 @@ % events started % % See also tapas_physio_create_scan_timing tapas_physio_read_physlogfiles -% + % Author: Lars Kasper % Created: 2016-08-20 % Copyright (C) 2014 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -36,8 +36,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + DEBUG = verbose.level >=3; nVolumes = sqpar.Nscans; @@ -53,6 +52,10 @@ LOCS = find(acq_codes == 1); VOLLOCS = find(acq_codes == 10); +if isempty(VOLLOCS) % try Philips scan trigger onset code instead + VOLLOCS = find(acq_codes == 8); +end + isValidVOLLOCS = numel(VOLLOCS) >= nTotalVolumes; isValidLOCS = numel(LOCS) >= nTotalSlices; @@ -69,7 +72,7 @@ if doCountFromStart VOLLOCS = VOLLOCS(1:nTotalVolumes); else - VOLLOCS = VOLLOCS((end-(nTotalVolumes+1)):end); + VOLLOCS = VOLLOCS((end-nTotalVolumes+1):end); end end @@ -94,7 +97,7 @@ if DEBUG verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - stringTitle = 'Extracted Scan Slice/Volume Onsets from Acq Codes'; + stringTitle = 'Sync: Extracted Scan Slice/Volume Onsets from Acq Codes'; set(gcf, 'Name', stringTitle); stem(t(LOCS), ones(size(LOCS))); hold all; stem(t(VOLLOCS), 1.5*ones(size(VOLLOCS))); diff --git a/PhysIO/code/tapas_physio_create_scan_timing_from_end_marker_philips.m b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_end_marker_philips.m similarity index 98% rename from PhysIO/code/tapas_physio_create_scan_timing_from_end_marker_philips.m rename to PhysIO/code/sync/tapas_physio_create_scan_timing_from_end_marker_philips.m index faa898c8..17680a45 100644 --- a/PhysIO/code/tapas_physio_create_scan_timing_from_end_marker_philips.m +++ b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_end_marker_philips.m @@ -65,7 +65,7 @@ % thresh.scan_timing); % % See also -% + % Author: Lars Kasper % Created: 2013-02-16 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -74,8 +74,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % everything stored in 1 logfile if ~isfield(log_files, 'cardiac') || isempty(log_files.cardiac) @@ -146,7 +145,7 @@ if verbose.level>=1 verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf,'Name', 'Thresholding Gradient for slice acq start detection'); + set(gcf,'Name', 'Sync: Thresholding Gradient for slice acq start detection'); fs(1) = subplot(1,1,1); plot(t, y(:,7:9)); legend('gradient x', 'gradient y', 'gradient z'); diff --git a/PhysIO/code/tapas_physio_create_scan_timing_from_gradients_auto_philips.m b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_gradients_auto_philips.m similarity index 97% rename from PhysIO/code/tapas_physio_create_scan_timing_from_gradients_auto_philips.m rename to PhysIO/code/sync/tapas_physio_create_scan_timing_from_gradients_auto_philips.m index be034a20..2e71d748 100644 --- a/PhysIO/code/tapas_physio_create_scan_timing_from_gradients_auto_philips.m +++ b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_gradients_auto_philips.m @@ -94,7 +94,7 @@ % scan_timing, verbose); % % See also tapas_physio_create_scan_timing_from_gradients_philips -% + % Author: Lars Kasper % Created: 2015-01-09 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -103,8 +103,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: tapas_physio_create_scan_timing_from_gradients_philips.m 632 2015-01-09 12:36:12Z kasperla $ + + % smaller than typical single shot EPI slice duration (including waiting @@ -346,7 +346,7 @@ %% Plot template for volume repetition function fh = plot_template(t, templateGradientVolume) -stringTitle = 'Template Gradient Timecourse during 1 Slice'; +stringTitle = 'Sync: Template Gradient Timecourse during 1 Slice'; fh = tapas_physio_get_default_fig_params(); set(gcf, 'Name', stringTitle); @@ -361,7 +361,7 @@ function fh = plot_volume_events(VOLLOCS, t, G, ... templateGradientVolume, secondGuessVOLLOCS) -stringTitle = 'Template Gradient Timecourse during 1 Volume'; +stringTitle = 'Sync: Template Gradient Timecourse during 1 Volume'; fh = tapas_physio_get_default_fig_params(); set(gcf, 'Name', stringTitle); @@ -391,7 +391,7 @@ function fh = plot_slice_events(LOCS, t, G, ... templateGradientSlice, secondGuessLOCS) -stringTitle = 'Template Gradient Timecourse during 1 Slice'; +stringTitle = 'Sync: Template Gradient Timecourse during 1 Slice'; fh = tapas_physio_get_default_fig_params(); set(gcf, 'Name', stringTitle); @@ -437,7 +437,7 @@ function plot_diff_LOCS(t, LOCS, dt) %% Plot all raw gradient time-courses function [fh, fs] = plot_raw_gradients(t, y, acq_codes) fh = tapas_physio_get_default_fig_params(); -set(gcf,'Name', 'Thresholding Gradient for slice acq start detection'); +set(gcf,'Name', 'Sync: Thresholding Gradient for slice acq start detection'); for i=1:3, fs(i) = subplot(3,1,i); end axes(fs(1)); @@ -454,7 +454,7 @@ function plot_diff_LOCS(t, LOCS, dt) legend('abs(G_x^2+G_y^2+G_z^2)', 'gradient x', 'gradient y', 'gradient z'); -title('Raw Gradient Time-courses'); +title('Sync: Raw Gradient Time-courses'); end diff --git a/PhysIO/code/tapas_physio_create_scan_timing_from_gradients_philips.m b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_gradients_philips.m similarity index 98% rename from PhysIO/code/tapas_physio_create_scan_timing_from_gradients_philips.m rename to PhysIO/code/sync/tapas_physio_create_scan_timing_from_gradients_philips.m index bd0e7900..61502d18 100644 --- a/PhysIO/code/tapas_physio_create_scan_timing_from_gradients_philips.m +++ b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_gradients_philips.m @@ -77,7 +77,7 @@ % scan_timing, verbose); % % See also -% + % Author: Lars Kasper % Created: 2013-02-16 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -86,8 +86,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + sqpar = scan_timing.sqpar; sync = scan_timing.sync; @@ -222,8 +221,8 @@ if verbose.level>=1 % Depict all gradients, raw - verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf,'Name', 'Thresholding Gradient for slice acq start detection'); + verbose.fig_handles(end+1,1) = tapas_physio_get_default_fig_params(); + set(gcf,'Name', 'Sync: Thresholding Gradient for slice acq start detection'); fs(1) = subplot(3,1,1); plot(t, sqrt(sum(y(:,7:9).^2,2)), '--k'); @@ -239,7 +238,7 @@ legend('abs(G_x^2+G_y^2+G_z^2)', 'gradient x', 'gradient y', 'gradient z'); - title('Raw Gradient Time-courses'); + title('Sync: Raw Gradient Time-courses'); % Plot gradient thresholding for slice timing determination fs(2) = subplot(3,1,2); diff --git a/PhysIO/code/tapas_physio_create_scan_timing_from_tics_siemens.m b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_tics_siemens.m similarity index 97% rename from PhysIO/code/tapas_physio_create_scan_timing_from_tics_siemens.m rename to PhysIO/code/sync/tapas_physio_create_scan_timing_from_tics_siemens.m index 131120f3..6fca5ed4 100644 --- a/PhysIO/code/tapas_physio_create_scan_timing_from_tics_siemens.m +++ b/PhysIO/code/sync/tapas_physio_create_scan_timing_from_tics_siemens.m @@ -29,7 +29,7 @@ % [VOLLOCS, LOCS] = tapas_physio_create_scan_timing_from_tics_siemens(t, sqpar); % % See also -% + % Author: Lars Kasper % Created: 2014-09-08 % Copyright (C) 2014 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -44,7 +44,7 @@ %% Extract relevant columns slices/volumes/tic timing/echoes -[C, columnNames] = tapas_physio_read_files_siemens_tics(log_files.scan_timing, 'INFO'); +[C, columnNames] = tapas_physio_read_columnar_textfiles(log_files.scan_timing, 'INFO'); dtTicSeconds = 2.5e-3; @@ -98,7 +98,7 @@ if DEBUG verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - stringTitle = 'Extracted Sequence Timing Siemens'; + stringTitle = 'Sync: Extracted Sequence Timing Siemens'; set(gcf, 'Name', stringTitle); stem(t(LOCS), ones(size(LOCS))); hold all; stem(t(VOLLOCS), 1.5*ones(size(VOLLOCS))); diff --git a/PhysIO/code/tapas_physio_create_scan_timing_nominal.m b/PhysIO/code/sync/tapas_physio_create_scan_timing_nominal.m similarity index 99% rename from PhysIO/code/tapas_physio_create_scan_timing_nominal.m rename to PhysIO/code/sync/tapas_physio_create_scan_timing_nominal.m index 224387e4..09aae693 100644 --- a/PhysIO/code/tapas_physio_create_scan_timing_nominal.m +++ b/PhysIO/code/sync/tapas_physio_create_scan_timing_nominal.m @@ -44,7 +44,7 @@ % [VOLLOCS, LOCS] = tapas_physio_create_scan_timing_nominal(t, sqpar); % % See also -% + % Author: Lars Kasper % Created: 2013-02-07 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. @@ -53,8 +53,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 3 align_scan = 'last'; diff --git a/PhysIO/code/tapas_physio_crop_scanphysevents_to_acq_window.m b/PhysIO/code/sync/tapas_physio_crop_scanphysevents_to_acq_window.m similarity index 95% rename from PhysIO/code/tapas_physio_crop_scanphysevents_to_acq_window.m rename to PhysIO/code/sync/tapas_physio_crop_scanphysevents_to_acq_window.m index 865a0068..0ac336af 100644 --- a/PhysIO/code/tapas_physio_crop_scanphysevents_to_acq_window.m +++ b/PhysIO/code/sync/tapas_physio_crop_scanphysevents_to_acq_window.m @@ -5,8 +5,8 @@ % USAGE % function [ons_secs, sqpar] = ... % tapas_physio_crop_scanphysevents_to_acq_window(ons_secs, sqpar) -%------------------------------------------------------------------------- -% INPUT: +% +% IN % ons_secs - onsets of all physlog events in seconds % .spulse = onsets of slice scan acquisition % .cpulse = onsets of cardiac R-wave peaks @@ -25,8 +25,7 @@ % onset_slice - slice whose scan onset determines the adjustment of the % regressor timing to a particular slice for the whole volume % -%------------------------------------------------------------------------- -% OUTPUT: +% OUT % ons_secs - input ons_secs cropped to acquisition window % .raw - uncropped ons_secs-structure as input into this % function @@ -35,7 +34,6 @@ % .Nvols_paradigm - acquired volumes during paradigm running % .meanTR - mean repetition time (secs) over session % -%------------------------------------------------------------------------- % Lars Kasper, August 2011 % Copyright (C) 2013 Institute for Biomedical Engineering, ETH/Uni Zurich. % @@ -43,8 +41,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + %% parameter settings Nscans = sqpar.Nscans; diff --git a/PhysIO/code/tapas_physio_get_onsets_from_locs.m b/PhysIO/code/sync/tapas_physio_get_onsets_from_locs.m similarity index 97% rename from PhysIO/code/tapas_physio_get_onsets_from_locs.m rename to PhysIO/code/sync/tapas_physio_get_onsets_from_locs.m index e9ee6205..24f0f7da 100644 --- a/PhysIO/code/tapas_physio_get_onsets_from_locs.m +++ b/PhysIO/code/sync/tapas_physio_get_onsets_from_locs.m @@ -34,7 +34,7 @@ % [ons_secs.svolpulse, ons_secs.spulse] = tapas_physio_get_onsets_from_locs(ons_secs.t, VOLLOCS, LOCS); % % See also tapas_physio_main_create_regressors tapas_physio_read_physlogfiles_philips -% + % Author: Lars Kasper % Created: 2013-02-16 % @@ -44,8 +44,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + Nscans = sqpar.Nscans; Ndummies = sqpar.Ndummies; @@ -89,11 +88,11 @@ end if verbose.level >= 3 - titstr = 'Slice bundles belonging to 1 volume'; + stringTitle = 'Sync: Slice bundles belonging to 1 volume'; verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf, 'Name', titstr); + set(gcf, 'Name', stringTitle); for v=1:Nallvols-1, stem(t(SLICELOCS{v}),ones(size(SLICELOCS{v})));hold all;end - title(titstr); + title(stringTitle); xlabel('t (seconds since SCANPHYSLOG-start)'); end diff --git a/PhysIO/code/tapas_physio_rescale_gradient_gain_fluctuations.m b/PhysIO/code/sync/tapas_physio_rescale_gradient_gain_fluctuations.m similarity index 98% rename from PhysIO/code/tapas_physio_rescale_gradient_gain_fluctuations.m rename to PhysIO/code/sync/tapas_physio_rescale_gradient_gain_fluctuations.m index 019202c2..51efbd13 100644 --- a/PhysIO/code/tapas_physio_rescale_gradient_gain_fluctuations.m +++ b/PhysIO/code/sync/tapas_physio_rescale_gradient_gain_fluctuations.m @@ -23,7 +23,7 @@ % tapas_physio_rescale_gradient_gain_fluctuations % % See also -% + % Author: Lars Kasper % Created: 2015-01-11 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -32,8 +32,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % Determine gain fluctuations via steps in sliding-window-maximum @@ -100,7 +99,7 @@ if nGainSwitches > 0 if doPlot - stringTitle = 'Detected Gradient Gain Fluctuations'; + stringTitle = 'Sync: Detected Gradient Gain Fluctuations'; verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); set(gcf, 'Name', stringTitle); hs(1) = subplot(2,1,1); diff --git a/PhysIO/code/tapas_physio_cfg_matlabbatch.m b/PhysIO/code/tapas_physio_cfg_matlabbatch.m index 67a0fa64..90bb87de 100644 --- a/PhysIO/code/tapas_physio_cfg_matlabbatch.m +++ b/PhysIO/code/tapas_physio_cfg_matlabbatch.m @@ -1,16 +1,18 @@ function physio = tapas_physio_cfg_matlabbatch -% Lars Kasper, March 2013 -% -% Copyright (C) 2013, Institute for Biomedical Engineering, ETH/Uni Zurich. +% This file needs to be in a sub-folder of spm/toolbox in order for the +% Batch Editor to recognize PhysIO as an SPM toolbox. + +% Author: Lars Kasper +% Created: 2013-04-23 +% Copyright (C) 2013-2018 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. % % This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . - pathThis = fileparts(mfilename('fullpath')); % TODO: more elegant via SPM! -addpath(pathThis); +addpath(genpath(pathThis)); % to include sub-folders of code as well %-------------------------------------------------------------------------- @@ -37,17 +39,8 @@ vendor.tag = 'vendor'; vendor.name = 'vendor'; vendor.help = {' Vendor Name depending on your MR Scanner/Physiological recording system' - ' ''Philips''' - ' ''GE''' - ' ''Siemens''' - ' ''Siemens_Tics'' - new Siemens physiological' - ' Logging with time stamps in tics' - ' (= steps of 2.5 ms since midnight) and' - ' extra acquisition (scan_timing) logfile with' - ' time stamps of all volumes and slices' - ' ''Siemens_HCP'' - Human Connectome Project (HCP) Physiology Data' - ' HCP-downloaded files of name format *_Physio_log.txt ' - ' are already preprocessed into this simple 3-colum text format' + ' ''BIDS'' - Brain Imaging Data Structure (https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/06-physiological-and-other-continous-recordings.html)' + ' ''Biopac_Txt'' - exported txt files from Biopac system (4 columns, [Resp PPU GSR Trigger]' ' ''Biopac_Mat'' - exported mat files from Biopac system' ' ''BrainProducts'' - .eeg files from BrainProducts EEG system' ' ''Custom''' @@ -65,13 +58,28 @@ ' -0.3 0' ' NOTE: the sampling interval has to be specified for these files as' ' well (s.b.)' + ' ''GE''' + ' ''Philips''' + ' ''Siemens''' + ' ''Siemens_Tics'' - new Siemens physiological' + ' Logging with time stamps in tics' + ' (= steps of 2.5 ms since midnight) and' + ' extra acquisition (scan_timing) logfile with' + ' time stamps of all volumes and slices' + ' ''Siemens_HCP'' - Human Connectome Project (HCP) Physiology Data' + ' HCP-downloaded files of name format *_Physio_log.txt ' + ' are already preprocessed into this simple 3-column text format' }; -vendor.labels = {'Philips', 'GE', 'Siemens (VB, *.puls/*.ecg/*.resp)', ... +vendor.labels = {'BIDS (Brain Imaging Data Structure)', 'Biopac_Txt', 'Biopac_Mat', ... +'BrainProducts', 'Custom', ... +'GE', 'Philips', ... + 'Siemens (VB, *.puls/*.ecg/*.resp)', ... 'Siemens_Tics (VD: *_PULS.log/*_ECG1.log/*_RESP.log/*_AcquisitionInfo*.log)', ... 'Siemens_HCP (Human Connectome Project, *Physio_log.txt, 3 column format', ... - 'Biopac_Mat', 'BrainProducts', 'Custom'}; -vendor.values = {'Philips', 'GE', 'Siemens', 'Siemens_Tics', 'Siemens_HCP', ... - 'Biopac_Mat', 'BrainProducts', 'Custom'}; + }; +vendor.values = {'BIDS', 'Biopac_Txt','Biopac_Mat', 'BrainProducts', 'Custom', ... +'GE', 'Philips', 'Siemens', 'Siemens_Tics', 'Siemens_HCP', ... + }; vendor.val = {'Philips'}; %-------------------------------------------------------------------------- @@ -80,10 +88,9 @@ cardiac = cfg_files; cardiac.tag = 'cardiac'; cardiac.name = 'log_cardiac'; -%cardiac.val = {{'/Users/kasperla/Documents/code/matlab/smoothing_trunk/tSNR_fMRI_SPM/CheckPhysRETROICOR/PhysIOToolbox/examples/Philips/ECG3T/SCANPHYSLOG.log'}}; cardiac.help = {'logfile with cardiac, i.e. ECG/PPU (pulse oximetry) data' 'Select 0 files, if only respiratory data is available' - 'For Philips, same as respiratory logfile.' + 'For Philips and BIDS, same as respiratory logfile.' }; cardiac.filter = 'any'; cardiac.ufilter = '.*'; @@ -95,10 +102,9 @@ respiration = cfg_files; respiration.tag = 'respiration'; respiration.name = 'log_respiration'; -% respiration.val = {{'/Users/kasperla/Documents/code/matlab/smoothing_trunk/tSNR_fMRI_SPM/CheckPhysRETROICOR/PhysIOToolbox/examples/Philips/ECG3T/SCANPHYSLOG.log'}}; respiration.help = {'logfile with respiratory, i.e. breathing belt amplitude data' 'Select 0 files, if only cardiac data available' - 'For Philips, same as cardiac logfile.' + 'For Philips and BIDS, same as cardiac logfile.' }; respiration.filter = 'any'; respiration.ufilter = '.*'; @@ -115,8 +121,8 @@ ' MRI scans.' '' ' Currently implemented for 2 cases:' - ' Siemens: Enter the first or last Dicom volume of your session here,' - ' The time stamp in the dicom header is on the same time' + ' Siemens: Enter the first or last DICOM volume of your session here,' + ' The time stamp in the DICOM header is on the same time' ' axis as the time stamp in the physiological log file' ' Siemens_Tics: log-file which holds table conversion for tics axis to' ' time conversion' @@ -136,6 +142,7 @@ 'sampling interval of phys log files (in seconds)' ' If empty, default values are used: 2 ms for Philips, 25 ms for GE, 2.5 ms for Siemens Tics and HCP' ' For Biopac and Siemens, sampling rate is read directly from logfile' + ' For BIDS, sampling interval is read from accompanying json-file, if existing' ' If cardiac and respiratory sampling rate differ, enter them as vector' ' [sampling_interval_cardiac, sampling_interval_respiratory]' ' ' @@ -173,6 +180,8 @@ ' at 0 (e.g., for Siemens_Tics) since physiological recordings' ' and acquisition timing are already synchronized by this' ' information, and you would introduce another shift.' + ' 3. For BIDS, relative_start_acquisition is read as -StartTime from' + ' accompanying json-file, if existing' }; relative_start_acquisition.strtype = 'e'; relative_start_acquisition.num = [Inf Inf]; @@ -192,7 +201,7 @@ ' to pre-scans' '' ' NOTE: In all cases, log_files.relative_start_acquisition is' - ' added to timing after the initial alignmnent to first/last scan' + ' added to timing after the initial alignment to first/last scan' '' ' ''first'' start of logfile will be aligned to first scan volume' ' ''last'' end of logfile will be aligned to last scan volume' @@ -284,7 +293,6 @@ Nslices.help = {'Number of slices in one volume'}; Nslices.strtype = 'e'; Nslices.num = [Inf Inf]; -%Nslices.val = {37}; @@ -310,7 +318,7 @@ Nprep.help = { ' Count of preparation pulses BEFORE 1st dummy scan.' ' Only important, if log_files.scan_align = ''first'', since then' - ' preparation pulses and dummiy triggers are counted and discarded ' + ' preparation pulses and dummy triggers are counted and discarded ' ' as first scan onset' }; Nprep.strtype = 'e'; @@ -464,9 +472,16 @@ sync_method_scan_timing_log.tag = 'scan_timing_log'; sync_method_scan_timing_log.name = 'scan_timing_log'; sync_method_scan_timing_log.val = {}; -sync_method_scan_timing_log.help = { ... +sync_method_scan_timing_log.help = { ' Derive scan-timing from individual scan timing logfile with time ' - ' stamps ("tics") for each slice and volume (e.g. Siemens_Cologne)'}; + ' stamps ("tics") or triggers for each slice and volume' + ' file types differ depending on the physlog file format:' + ' *_INFO.log for ''Siemens_Tics'' (time stamps for' + ' every slice and volume) (e.g., Siemens VD, CMRR sequence)' + ' *.dcm (DICOM) for Siemens, is first volume (non-dummy) used' + ' in GLM analysis' + ' *.tsv (3rd column) for BIDS, using the scanner volume trigger onset events' + }; %-------------------------------------------------------------------------- @@ -615,7 +630,7 @@ initial_cpulse_select_file }; initial_cpulse_select_method_load_template.help = { ... - 'Load template from previous manual/auto run to perform autocorrelation detection of hearbeats' + 'Load template from previous manual/auto run to perform autocorrelation detection of heartbeats' }; @@ -770,7 +785,7 @@ posthoc_cpulse_select.help = { - 'The posthoc cardiac pulse selection structure: If only few (<20)' + 'The post-hoc cardiac pulse selection structure: If only few (<20)' 'cardiac pulses are missing in a session due to bad signal quality, a' 'manual selection after visual inspection is possible using the' 'following parameters. The results are saved for reproducibility.' @@ -830,7 +845,7 @@ orthog.tag = 'orthogonalise'; orthog.name = 'orthogonalise'; orthog.help = { - 'Orthogonalize physiological regressors with respect to each other.' + 'Orthogonalise physiological regressors with respect to each other.' 'Note: This is only recommended for triggered/gated acquisition sequences.' }; orthog.labels = {'none' 'cardiac' 'resp' 'mult' 'RETROCOR', 'HRV', 'RVT', 'Noise_ROIs'}; @@ -849,7 +864,7 @@ 'Output file for physiological regressors' 'Choose file name with extension:' '.txt for ASCII files with 1 regressor per column' - '.mat for matlab variable file' + '.mat for Matlab variable file' }; output_multiple_regressors.strtype = 's'; output_multiple_regressors.num = [1 Inf]; @@ -1006,7 +1021,7 @@ rvt_yes.val = {rvt_delays}; rvt_yes.help = { 'Include Respiratory Volume per Time (RVT) Model, ' - 'as described in Birn, R.M., et al. NeuroImage 40, 644?654. doi:10.1016/j.neuroimage.2007.11.059' + 'as described in Birn et al. NeuroImage 40, 644?654. doi:10.1016/j.neuroimage.2007.11.059' }; @@ -1022,7 +1037,7 @@ rvt.values = {rvt_no, rvt_yes}; rvt.help = { 'Respiratory Volume per Time (RVT) Model, ' - 'as described in Birn, R.M., et al. NeuroImage 40, 644-654. doi:10.1016/j.neuroimage.2007.11.059' + 'as described in Birn et al. NeuroImage 40, 644-654. doi:10.1016/j.neuroimage.2007.11.059' }; @@ -1064,7 +1079,7 @@ hrv_yes.val = {hrv_delays}; hrv_yes.help = { 'Include Heart Rate Variability (HRV) Model, ' - 'as described in Chang, C. et al., NeuroImage 44, 857-869. doi:10.1016/j.neuroimage.2008.09.029' + 'as described in Chang et al., NeuroImage 44, 857-869. doi:10.1016/j.neuroimage.2008.09.029' }; @@ -1080,7 +1095,7 @@ hrv.values = {hrv_no, hrv_yes}; hrv.help = { 'Heart Rate Variability (HRV) Model, as described in ' - 'Chang, C. et al., NeuroImage 44, 857-869. doi:10.1016/j.neuroimage.2008.09.029' + 'Chang et al., NeuroImage 44, 857-869. doi:10.1016/j.neuroimage.2008.09.029' }; @@ -1088,13 +1103,37 @@ %% Sub-substructure Noise_Rois Model %-------------------------------------------------------------------------- +%-------------------------------------------------------------------------- +% force_coregister +%-------------------------------------------------------------------------- + +force_coregister = cfg_menu; +force_coregister.tag = 'force_coregister'; +force_coregister.name = 'Force Coregister : Estimate & Reslice of the noise ROIs'; +force_coregister.labels = {'Yes', 'No'}; +force_coregister.values = {'Yes', 'No'}; +force_coregister.val = {'Yes'}; % default value, discussion in https://github.com/translationalneuromodeling/tapas/pull/34 +force_coregister.help = { + 'Noise ROIs volumes must have the same geometry as the functional time series.' + 'It means same affine transformation(space) and same matrix(voxel size)' + '' + 'Yes - Coregister : Estimate & Reslice will be performed on the noise NOIs,' + 'so their geometry (space + voxel size) will match the fMRI volume.' + '' + 'No - Geometry will be tested :' + '1) If they match, continue' + '2) If they don''t match, perform a Coregister : Estimate & Reslice as fallback' + '' + }; + + %-------------------------------------------------------------------------- % fmri_files %-------------------------------------------------------------------------- fmri_files = cfg_files; fmri_files.tag = 'fmri_files'; -fmri_files.name = 'FMRI Time Series File(s)'; +fmri_files.name = 'fMRI Time Series File(s)'; fmri_files.val = {{''}}; fmri_files.help = { 'Preprocessed fmri nifti/analyze files, from which time series ' @@ -1111,7 +1150,11 @@ roi_files.tag = 'roi_files'; roi_files.name = 'Noise ROI Image File(s)'; roi_files.val = {{''}}; -roi_files.help = {'Masks/tissue probability maps characterizing where noise resides'}; +roi_files.help = { + 'Masks/tissue probability maps characterizing where noise resides' + 'Theses volumes must be in the same space as the functional volume,' + 'where the time series will be extracted.' + }; roi_files.filter = '.*'; roi_files.ufilter = '.nii$|.img$'; roi_files.num = [0 Inf]; @@ -1186,11 +1229,12 @@ noise_rois_yes = cfg_branch; noise_rois_yes.tag = 'yes'; noise_rois_yes.name = 'Yes'; -noise_rois_yes.val = {fmri_files, roi_files, roi_thresholds, n_voxel_crop, ... - n_components}; +noise_rois_yes.val = {fmri_files, roi_files, force_coregister, roi_thresholds,... + n_voxel_crop, n_components}; noise_rois_yes.help = { 'Include Noise ROIs model' '(Principal components of anatomical regions), similar to aCompCor, Behzadi et al. 2007' + 'Noise ROIs will be shown in SPM ''Graphics'' window' }; @@ -1255,7 +1299,7 @@ ' 1 value -> used for translation and rotation' ' 2 values -> 1st = translation (mm), 2nd = rotation (deg)' ' 6 values -> individual threshold for each axis (x,y,z,pitch,roll,yaw)' - ' ''FD'' - framewise displacement (in mm)' + ' ''FD'' - frame-wise displacement (in mm)' ' recommended for subject rejection: 0.5 (Power et al., 2012)' ' recommended for censoring: 0.2 ((Power et al., 2015)' ' ''DVARS'' - in percent BOLD signal change' @@ -1284,11 +1328,11 @@ movement_censoring_method.name = 'Censoring Method for Thresholding'; movement_censoring_method.help = {'Censoring method used for thresholding' ' ''None'' - no motion censoring performed' - ' ''MAXVAL'' - tresholding (max. translation/rotation)' - ' ''FD'' - framewise displacement (as defined by Power et al., 2012)' + ' ''MAXVAL'' - thresholding (max. translation/rotation)' + ' ''FD'' - frame-wise displacement (as defined by Power et al., 2012)' ' i.e., |rp_x(n+1) - rp_x(n)| + |rp_y(n+1) - rp_y(n)| + |rp_z(n+1) - rp_z(n)|' - ' + 50mm *(|rp_pitch(n+1) - rp_pitch(n)| + |rp_roll(n+1) - rp_roll(n)| + |rp_yaw(n+1) - rp_yaw(n)|' - ' where 50mm is an average head radius mapping a rotation into a translation of head surface' + ' + 50 mm *(|rp_pitch(n+1) - rp_pitch(n)| + |rp_roll(n+1) - rp_roll(n)| + |rp_yaw(n+1) - rp_yaw(n)|' + ' where 50 mm is an average head radius mapping a rotation into a translation of head surface' ' ''DVARS'' - root mean square over brain voxels of ' ' difference in voxel intensity between consecutive volumes' ' (Power et al., 2012)' @@ -1310,7 +1354,7 @@ movement_yes.help = {'Motion Assessment and Regression Models used' '- Motion 6/12/24, and as described in Friston et al., 1996' '- Motion Censoring (''spike'' regressors for motion-corrupted volumes)' - ' - by different thresholding (max. translation/rotation, framewise ' + ' - by different thresholding (max. translation/rotation, frame-wise ' ' displacement and DVARS (Power et al., 2012))' '- Motion Scrubbing (linear interpolation of censored volumes by nearest neighbours)' }; @@ -1328,7 +1372,7 @@ movement.help = {'Motion Assessment and Regression Models' '- Motion 6/12/24 regressors from realignment as described in Friston et al., 1996' '- Motion Censoring (''spike'' regressors for motion-corrupted volumes)' - ' - by different thresholding (max. translation/rotation, framewise ' + ' - by different thresholding (max. translation/rotation, frame-wise ' ' displacement and DVARS (Power et al., 2012))' '- Motion Scrubbing (linear interpolation of censored volumes by nearest neighbours)' }; @@ -1469,6 +1513,8 @@ ' Fig 5: time course of all sampled RETROICOR' ' regressors' ' Fig 6: final multiple_regressors matrix' + ' SPM Graphics : noise ROI before VS after' + ' (reslice) + threshold + erosion' '' ' 3 = all plots' ' Fig 1: raw phys logfile data' @@ -1484,6 +1530,8 @@ ' Fig 8: time course of all sampled RETROICOR' ' regressors' ' Fig 9: final multiple_regressors matrix' + ' SPM Graphics : noise ROI before VS after' + ' (reslice) + threshold + erosion' }; verbose.val = {level fig_output_file use_tabs}; @@ -1529,7 +1577,7 @@ %========================================================================== function dep = vout_physio(job) dep(1) = cfg_dep; -dep(1).sname = 'physiological noise regressors file (Multiple Regresssors)'; +dep(1).sname = 'physiological noise regressors file (Multiple Regressors)'; dep(1).src_output = substruct('.','physnoisereg'); dep(1).tgt_spec = cfg_findspec({{'filter','mat','strtype','e'}}); diff --git a/PhysIO/code/tapas_physio_create_noise_rois_regressors.m b/PhysIO/code/tapas_physio_create_noise_rois_regressors.m deleted file mode 100644 index daf53660..00000000 --- a/PhysIO/code/tapas_physio_create_noise_rois_regressors.m +++ /dev/null @@ -1,213 +0,0 @@ -function [R_noise_rois, noise_rois, verbose] = tapas_physio_create_noise_rois_regressors(... - noise_rois, verbose) -% Compute physiological regressors by extracting principal components of -% the preprocessed fMRI time series from anatomically defined noise ROIS (e.g. CSF) -% -% [R_noise_rois, verbose] = tapas_physio_create_noise_rois_regressors(... -% noise_rois, verbose) -% -% NOTE: The mean of all time series in each ROI will also be added as a regressor -% automatically -% -% Approach similar to the one described as aCompCor: -% Behzadi, Y., Restom, K., Liau, J., Liu, T.T., 2007. -% A component based noise correction method (CompCor) for BOLD and -% perfusion based fMRI. NeuroImage 37, 90?101. -% doi:10.1016/j.neuroimage.2007.04.042 -% -% IN -% physio.model.noise_rois -% OUT -% -% EXAMPLE -% tapas_physio_create_noise_rois_regressors -% -% See also spm_ov_roi -% -% Author: Lars Kasper -% Created: 2015-07-22 -% Copyright (C) 2015 TNU, Institute for Biomedical Engineering, -% University of Zurich and ETH Zurich. -% -% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public -% License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . -% -% $Id$ - -% TODO: visualization of Rois, components/mean and spatial loads in ROIs - -tapas_physio_strip_fields(noise_rois); - -if isempty(fmri_files) || isempty(roi_files) - R_noise_rois = []; - return -end - -if ~iscell(fmri_files) - fmri_files = cellstr(fmri_files); -end - -if ~iscell(roi_files) - roi_files = cellstr(roi_files); -end - -nRois = numel(roi_files); - -if numel(thresholds) == 1 - thresholds = repmat(thresholds, 1, nRois); -end - -if numel(n_voxel_crop) == 1 - n_voxel_crop = repmat(n_voxel_crop, 1, nRois); -end - -if numel(n_components) == 1 - n_components = repmat(n_components, 1, nRois); -end - -% TODO: what if different geometry of mask and fmri data? -% or several fmri files given? -Vimg = spm_vol(fmri_files{1}); -Yimg = spm_read_vols(Vimg); - -nVolumes = size(Yimg, 4); -Yimg = reshape(Yimg, [], nVolumes); - -R_noise_rois = []; -for r = 1:nRois - - Vroi = spm_vol(roi_files{r}); - - hasRoiDifferentGeometry = any(any(abs(Vroi.mat - Vimg(1).mat) > 1e-5)) | ... - any(Vroi.dim-Vimg(1).dim(1:3)); - hasRoiDifferentGeometry = true; - - if hasRoiDifferentGeometry - - % reslice to same geometry - matlabbatch{1}.spm.spatial.coreg.write.ref = fmri_files(1); - matlabbatch{1}.spm.spatial.coreg.write.source = roi_files(r); - matlabbatch{1}.spm.spatial.coreg.write.roptions.interp = 4; - matlabbatch{1}.spm.spatial.coreg.write.roptions.wrap = [0 0 0]; - matlabbatch{1}.spm.spatial.coreg.write.roptions.mask = 0; - matlabbatch{1}.spm.spatial.coreg.write.roptions.prefix = 'r'; - - spm_jobman('run', matlabbatch); - - % update header link to new reslice mask-file - Vroi = spm_vol(spm_file(roi_files{r}, 'prefix', 'r')); - end - - - roi = spm_read_vols(Vroi); - roi(roi < thresholds(r)) = 0; - roi(roi >= thresholds(r)) = 1; - - % crop pixel, if desired - if n_voxel_crop(r) - nSlices = size(roi,3); - for s = 1:nSlices - roi(:,:,s) = imerode(roi(:,:,s), strel('disk', n_voxel_crop(r))); - end - end - - Yroi = Yimg(roi(:)==1, :); - - %% mean and linear trend removal according to CompCor pub - % design matrix - X = ones(nVolumes,1); - X(:,2) = 1:nVolumes; - % fit 1st order polynomial to time series data in each voxel - for n_roi_voxel = 1:size(Yroi,1) - % extract data - raw_Y = Yroi(n_roi_voxel,:)'; - % estimate betas - beta = X\raw_Y; - % fitted data - fit_Y = X*beta; - % detrend data - detrend_Y = raw_Y - fit_Y; - - % overwrite Yroi - Yroi(n_roi_voxel,:) = detrend_Y; - end - - - - - - %% - - - % COEFF = [nVolumes, nPCs] principal components (PCs) ordered by variance - % explained - % SCORE = [nVoxel, nPCs] loads of each component in each voxel, i.e. - % specific contribution of each component in - % a voxel's variance - % LATENT = [nPCs, 1] eigenvalues of data covariance matrix, - % stating how much variance was explained by - % each PC overall - % TSQUARED = [nVoxels,1] Hotelling's T-Squared test whether PC - % explained significant variance in a voxel - % EXPLAINED = [nPCs, 1] relative amount of variance explained (in - % percent) by each component - % MU = [1, nVolumes] mean of all time series - [COEFF, SCORE, LATENT, TSQUARED, EXPLAINED, MU] = pca(Yroi); - - % components either via number or threshold of variance explained - if n_components(r) >= 1 - nComponents = n_components(r); - else - nComponents = find(cumsum(EXPLAINED)/100 > n_components(r), 1, 'first'); - end - - % save to return - noise_rois.n_components(r) = nComponents + 1; % + 1 for mean - - % Take mean and some components into noise regressor - R = MU'; - R = [R, COEFF(:,1:nComponents)]; - - nRegressors = size(R,2); - - % z-transform - stringLegend = cell(nRegressors,1); - for c = 1:nRegressors - R(:,c) = (R(:,c) - mean(R(:,c)))/std(R(:,c)); - - if c > 1 - stringLegend{c} = ... - sprintf('Principal component %d (%7.4f %% variance explained)', ... - c-1, EXPLAINED(c-1)); - else - stringLegend{c} = 'Mean time series of all voxels'; - end - end - - if verbose.level >=2 - stringFig = sprintf('Noise_rois: Extracted principal components for ROI %d', r); - verbose.fig_handles(end+1) = tapas_physio_get_default_fig_params(); - set(gcf, 'Name', stringFig); - plot(R); - legend(stringLegend); - title(stringFig); - xlabel('scans'); - ylabel('PCs, mean-centred and std-scaled'); - end - - % write away extracted PC-loads & roi of extraction - [tmp,fnRoi] = fileparts(Vroi(1).fname); - fpFmri = fileparts(Vimg(1).fname); - for c = 1:nComponents - Vpc = Vroi; - Vpc.fname = fullfile(fpFmri, sprintf('pc%02d_scores_%s.nii',c, fnRoi)); - % saved as float, since was masked before - Vpc.dt = [spm_type('float32') 1]; - pcScores = zeros(Vpc.dim); - pcScores(roi(:)==1) = SCORE(:, c); - spm_write_vol(Vpc, pcScores); - end - R_noise_rois = [R_noise_rois, R]; -end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_findpeaks.m b/PhysIO/code/tapas_physio_findpeaks.m deleted file mode 100644 index c8cc5e7c..00000000 --- a/PhysIO/code/tapas_physio_findpeaks.m +++ /dev/null @@ -1,218 +0,0 @@ -function [pks,locs] = tapas_physio_findpeaks(X,varargin) -%tapas_physio_findpeaks Find local peaks in data -% -% NOTE: This copy of function findpeaks is included in the TAPAS PhysIO -% Toolbox to make the dependency on Matlab's signal processing toolbox -% explicit. Please do not use this function if you haven't purchased -% the signal processing toolbox. -% -% PKS = tapas_physio_findpeaks(X) finds local peaks in the data vector X. A local peak -% is defined as a data sample which is either larger than the two -% neighboring samples or is equal to Inf. -% -% [PKS,LOCS]= tapas_physio_findpeaks(X) also returns the indices LOCS at which the -% peaks occur. -% -% [...] = tapas_physio_findpeaks(X,'MINPEAKHEIGHT',MPH) finds only those peaks that -% are greater than MINPEAKHEIGHT MPH. Specifying a minimum peak height -% may help in reducing the processing time. MPH is a real valued scalar. -% The default value of MPH is -Inf. -% -% [...] = tapas_physio_findpeaks(X,'MINPEAKDISTANCE',MPD) finds peaks that are at -% least separated by MINPEAKDISTANCE MPD. MPD is a positive integer -% valued scalar. This parameter may be specified to ignore smaller peaks -% that may occur in close proximity to a large local peak. For example, -% if a large local peak occurs at index N, then all smaller peaks in the -% range (N-MPD, N+MPD) are ignored. If not specified, MPD is assigned a -% value of one. -% -% [...] = tapas_physio_findpeaks(X,'THRESHOLD',TH)finds peaks that are at least -% greater than their neighbors by the THRESHOLD TH. TH is real valued -% scalar greater than or equal to zero. The default value of TH is zero. -% -% [...] = tapas_physio_findpeaks(X,'NPEAKS',NP) specifies the maximum number of peaks -% to be found. NP is an integer greater than zero. If not specified, all -% peaks are returned. -% -% [...] = tapas_physio_findpeaks(X,'SORTSTR',STR) specifies the direction of sorting -% of peaks. STR can take values of 'ascend','descend' or 'none'. If not -% specified, STR takes the value of 'none' and the peaks are returned in -% the order of their occurrence. -% -% See also DSPDATA/FINDPEAKS - -% Copyright 2007-2010 The MathWorks, Inc. -% $Revision: 235 $ $Date: 2013-08-19 18:28:07 +0200 (Mon, 19 Aug 2013) $ - -if verLessThan('matlab','8.4') - error(nargchk(1,11,nargin,'struct')); -else - narginchk(1,11); -end - -[X,Ph,Pd,Th,Np,Str,infIdx] = parse_inputs(X,varargin{:}); -[pks,locs] = getPeaksAboveMinPeakHeight(X,Ph); -[pks,locs] = removePeaksBelowThreshold(X,pks,locs,Th,infIdx); -[pks,locs] = removePeaksSeparatedByLessThanMinPeakDistance(pks,locs,Pd); -[pks,locs] = orderPeaks(pks,locs,Str); -[pks,locs] = keepAtMostNpPeaks(pks,locs,Np); - -%-------------------------------------------------------------------------- -function [X,Ph,Pd,Th,Np,Str,infIdx] = parse_inputs(X,varargin) - -% Validate input signal -validateattributes(X,{'numeric'},{'nonempty','real','vector'},... - 'findpeaks','X'); -M = numel(X); -if (M < 3) - datamsgid = generatemsgid('emptyDataSet'); - error(datamsgid,'Data set must contain at least 3 samples.'); -end - -%#function dspopts.findpeaks -%hopts = tapas_physio_uddpvparse('dspopts.findpeaks',varargin{:}); -if nargin - varargin(1:2:end) = lower(varargin(1:2:end)); -end - -defaults.minpeakheight = -Inf; -defaults.minpeakdistance = []; -defaults.threshold = 0; -defaults.npeaks = []; -defaults.sortstr = 'none'; - -hopts = tapas_physio_propval(varargin, defaults); - -Ph = hopts.minpeakheight; -Pd = hopts.minpeakdistance; -Th = hopts.threshold; -Np = hopts.npeaks; -Str = hopts.sortstr; - -% Validate MinPeakDistance -if ~isempty(Pd) && (~isnumeric(Pd) || ~isscalar(Pd) ||any(rem(Pd,1)) || (Pd < 1)) - Nmsgid = generatemsgid('invalidMinPeakDistance'); - error(Nmsgid,'MinPeakDistance should be an integer greater than 0.'); -end - -% Set default values for MinPeakDistance and NPeaks -if(isempty(Pd)), Pd = 1; end -if(isempty(Np)), Np = M; end - -if(Pd >= M) - pdmsgid = generatemsgid('largeMinPeakDistance'); - error(pdmsgid,'Invalid MinPeakDistance. Set MinPeakDistance as an integer in the range between 1 and %s.',... - num2str(M)); -end - -% Replace Inf by realmax because the diff of two Infs is not a number -infIdx = isinf(X); -if any(infIdx), - X(infIdx) = sign(X(infIdx))*realmax; -end -infIdx = infIdx & X>0; % Keep only track of +Inf - -%-------------------------------------------------------------------------- -function [pks,locs] = getPeaksAboveMinPeakHeight(X,Ph) - -pks = []; -locs = []; - -if all(isnan(X)), - return, -end - -Indx = find(X > Ph); -if(isempty(Indx)) - mphmsgid = generatemsgid('largeMinPeakHeight'); - warning(mphmsgid,'Invalid MinPeakHeight. There are no data points greater than MinPeakHeight.'); - return -end - -% Peaks cannot be easily solved by comparing the sample values. Instead, we -% use first order difference information to identify the peak. A peak -% happens when the trend change from upward to downward, i.e., a peak is -% where the difference changed from a streak of positives and zeros to -% negative. This means that for flat peak we'll keep only the rising -% edge. -trend = sign(diff(X)); -idx = find(trend==0); % Find flats -N = length(trend); -for i=length(idx):-1:1, - % Back-propagate trend for flats - if trend(min(idx(i)+1,N))>=0, - trend(idx(i)) = 1; - else - trend(idx(i)) = -1; % Flat peak - end -end - -idx = find(diff(trend)==-2)+1; % Get all the peaks -locs = intersect(Indx,idx); % Keep peaks above MinPeakHeight -pks = X(locs); - -%-------------------------------------------------------------------------- -function [pks,locs] = removePeaksBelowThreshold(X,pks,locs,Th,infIdx) - -idelete = []; -for i = 1:length(pks), - delta = min(pks(i)-X(locs(i)-1),pks(i)-X(locs(i)+1)); - if delta - end -end -if ~isempty(idelete), - locs(idelete) = []; -end - -X(infIdx) = Inf; % Restore +Inf -locs = union(locs,find(infIdx)); % Make sure we find peaks like [realmax Inf realmax] -pks = X(locs); - -%-------------------------------------------------------------------------- -function [pks,locs] = removePeaksSeparatedByLessThanMinPeakDistance(pks,locs,Pd) -% Start with the larger peaks to make sure we don't accidentally keep a -% small peak and remove a large peak in its neighborhood. - -if isempty(pks) || Pd==1, - return -end - -% Order peaks from large to small -[pks, idx] = sort(pks,'descend'); -locs = locs(idx); - -idelete = ones(size(locs))<0; -for i = 1:length(locs), - if ~idelete(i), - % If the peak is not in the neighborhood of a larger peak, find - % secondary peaks to eliminate. - idelete = idelete | (locs>=locs(i)-Pd)&(locs<=locs(i)+Pd); - idelete(i) = 0; % Keep current peak - end -end -pks(idelete) = []; -locs(idelete) = []; - -%-------------------------------------------------------------------------- -function [pks,locs] = orderPeaks(pks,locs,Str) - -if isempty(pks), return; end - -if strcmp(Str,'none') - [locs idx] = sort(locs); - pks = pks(idx); -else - [pks,s] = sort(pks,Str); - locs = locs(s); -end - -%-------------------------------------------------------------------------- -function [pks,locs] = keepAtMostNpPeaks(pks,locs,Np) - -if length(pks)>Np, - locs = locs(1:Np); - pks = pks(1:Np); -end - -% [EOF] diff --git a/PhysIO/code/tapas_physio_gausswin.m b/PhysIO/code/tapas_physio_gausswin.m deleted file mode 100644 index 1a552c58..00000000 --- a/PhysIO/code/tapas_physio_gausswin.m +++ /dev/null @@ -1,47 +0,0 @@ -function w = tapas_physio_gausswin(L, a) -% Fast version of gausswin, without all checks -% -% w = tapas_physio_gausswin(L, a) -% -% Matlab's "GAUSSWIN(N) returns an N-point Gaussian window. -% -% GAUSSWIN(N, ALPHA) returns the ALPHA-valued N-point Gaussian -% window. ALPHA is defined as the reciprocal of the standard -% deviation and is a measure of the width of its Fourier Transform. -% As ALPHA increases, the width of the window will decrease. If omitted, -% ALPHA is 2.5." -% -% Computed according to -% [1] fredric j. harris [sic], On the Use of Windows for Harmonic -% Analysis with the Discrete Fourier Transform, Proceedings of -% the IEEE, Vol. 66, No. 1, January 1978 - -% IN -% -% OUT -% -% EXAMPLE -% tapas_physio_gausswin -% -% See also -% -% Author: Lars Kasper -% Created: 2017-11-16 -% Copyright (C) 2017 TNU, Institute for Biomedical Engineering, -% University of Zurich and ETH Zurich. -% -% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public -% License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . -% -% $Id: teditRETRO.m 775 2015-07-17 10:52:58Z kasperla $ - -if nargin < 2 - a = 2.5; -end - -% Compute window according to [1] -N = L-1; -n = (0:N)'-N/2; -w = exp(-(1/2)*(a*n/(N/2)).^2); diff --git a/PhysIO/code/tapas_physio_init.m b/PhysIO/code/tapas_physio_init.m index b641b162..0e0a990b 100644 --- a/PhysIO/code/tapas_physio_init.m +++ b/PhysIO/code/tapas_physio_init.m @@ -11,7 +11,7 @@ % tapas_physio_init() % % See also -% + % Author: Lars Kasper % Created: 2018-02-17 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, @@ -72,7 +72,7 @@ if ~isPhysioVisibleForSpmBatchEditor fprintf('No link found. Trying to create one...'); - cmdString = tapas_physio_create_spm_toolbox_link(); + cmdString = tapas_physio_create_spm_toolbox_link(pathPhysIO); % try again... [isPhysioVisibleForSpmBatchEditor, pathSpm, pathPhysIO] = ... diff --git a/PhysIO/code/tapas_physio_main_create_regressors.m b/PhysIO/code/tapas_physio_main_create_regressors.m index bda9a8d8..35750e11 100644 --- a/PhysIO/code/tapas_physio_main_create_regressors.m +++ b/PhysIO/code/tapas_physio_main_create_regressors.m @@ -36,25 +36,25 @@ % Chang et al2009, NI 44 % % See also tapas_physio_new -% + % Author: Lars Kasper % Created: 2011-08-01 -% Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. +% Copyright (C) 2011-2018 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. % -% This file is part of the PhysIO toolbox, which is released under the terms of the GNU General Public +% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ -% - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 0. Set Default parameters %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% include subfolders of code to path as well +pathThis = fileparts(mfilename('fullpath')); +addpath(genpath(pathThis)); + % These parameters could become toolbox inputs... minConstantIntervalAlertSeconds = 0.2; maxHeartRateBpm = 90; diff --git a/PhysIO/code/tapas_physio_new.m b/PhysIO/code/tapas_physio_new.m index 056f6bcf..e1756f19 100644 --- a/PhysIO/code/tapas_physio_new.m +++ b/PhysIO/code/tapas_physio_new.m @@ -28,7 +28,7 @@ % are overwritten, the others are kept as in physio_in % % OUT -% physio - the complete physio structure, which can be unsed in +% physio - the complete physio structure, which can be used in % tapas_physio_main_create_regressors % % NOTE @@ -44,23 +44,24 @@ % physio = tapas_physio_new('manual_peak_select', physio); % % See also tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2013-04-23 -% Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. +% Copyright (C) 2013-2018 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. % % This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ % if not specified differently, create everything empty if ~nargin default_scheme = 'empty'; end +% include sub-folders of code to path as well +pathThis = fileparts(mfilename('fullpath')); +addpath(genpath(pathThis)); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Modules (Overview) @@ -101,35 +102,36 @@ log_files = []; % vendor Name depending on your MR Scanner system - % 'Philips' (default) - % 'GE', - % 'Siemens' - % 'Siemens_Tics' - new Siemens physiological - % logging with time stamps in tics - % (= steps of 2.5 ms since midnight) and - % extra acquisition (scan_timing) logfile with - % time stamps of all volumes and slices - % - % or + % 'BIDS' - Brain Imaging Data Structure (http://bids.neuroimaging.io/bids_spec.pdf, section 8.6)' + % 'Biopac_Txt' - exported txt files from Biopac system (4 columns, [Resp PPU GSR Trigger]' + % 'Biopac_Mat' - exported mat files from Biopac system' + % 'BrainProducts' - .eeg files from BrainProducts EEG system' % 'Custom' - % - % 'Custom' expects the logfiles (separate files for cardiac and respiratory) - % to be plain text, with one cardiac (or - % respiratory) sample per row; - % If heartbeat (R-wave peak) events are - % recorded as well, they have to be put - % as a 2nd column in the cardiac logfile - % by specifying a 1; 0 in all other rows - % e.g.: - % 0.2 0 - % 0.4 1 <- cardiac pulse event - % 0.2 0 - % -0.3 0 - % - % - % NOTE: the sampling interval has to be specified for these files as - % well (s.b.) - + % 'Custom' expects the logfiles (separate files for cardiac and respiratory)' + % to be plain text, with one cardiac (or' + % respiratory) sample per row;' + % If heartbeat (R-wave peak) events are' + % recorded as well, they have to be put' + % as a 2nd column in the cardiac logfile' + % by specifying a 1; 0 in all other rows' + % e.g.:' + % 0.2 0' + % 0.4 1 <- cardiac pulse event' + % 0.2 0' + % -0.3 0' + % NOTE: the sampling interval has to be specified for these files as' + % well (s.b.)' + % 'GE' + % 'Philips' + % 'Siemens' + % 'Siemens_Tics' - new Siemens physiological' + % Logging with time stamps in tics' + % (= steps of 2.5 ms since midnight) and' + % extra acquisition (scan_timing) logfile with' + % time stamps of all volumes and slices' + % 'Siemens_HCP' - Human Connectome Project (HCP) Physiology Data' + % HCP-downloaded files of name format *_Physio_log.txt ' + % are already preprocessed into this simple 3-column text format' log_files.vendor = 'Philips'; % Logfile with cardiac data, e.g. @@ -146,8 +148,8 @@ % additional file for relative timing information between logfiles and % MRI scans. % Currently implemented for 2 cases - % Siemens: Enter the first or last Dicom volume of your session here, - % The time stamp in the dicom header is on the same time + % Siemens: Enter the first or last DICOM volume of your session here, + % The time stamp in the DICOM header is on the same time % axis as the time stamp in the physiological log file % Siemens_Tics: log-file which holds table conversion for tics axis to % time conversion @@ -186,7 +188,7 @@ % to pre-scans % % NOTE: In all cases, log_files.relative_start_acquisition is - % added to timing after the initial alignmnent to first/last scan + % added to timing after the initial alignment to first/last scan % % 'first' start of logfile will be aligned to first scan volume % 'last' end of logfile will be aligned to last scan volume @@ -217,7 +219,7 @@ % Count of preparation pulses % BEFORE 1st dummy scan. % Only important, if log_files.scan_align = 'first', since then - % preparation pulses and dummiy triggers are counted and discarded + % preparation pulses and dummy triggers are counted and discarded % as first scan onset scan_timing.sqpar.Nprep = []; @@ -248,15 +250,17 @@ % e.g., % *_INFO.log for 'Siemens_Tics' (time stamps for % every slice and volume) - % *.dcm (DICOM) of first volume (non-dummy) used + % *.dcm (DICOM) for Siemens, is first volume (non-dummy) used % in GLM analysis + % *.tsv (3rd column) for BIDS, using the scanner + % volume trigger onset events % NOTE: This setting needs a valid filename to % entered in log_files.scan_timing scan_timing.sync.method = 'gradient_log'; scan_timing.sync.grad_direction = ''; % 'x', 'y', or 'z'; % if set, sequence timing is calculated - % from logged gradient timecourse along + % from logged gradient time-course along % this coordinate axis; scan_timing.sync.zero = []; % gradient values below this value are set to zero; @@ -291,27 +295,33 @@ % The initial cardiac pulse selection structure: Determines how the % majority of cardiac pulses is detected - % 'auto' - auto generation of representative QRS-wave; detection via - % maximising auto-correlation with it - % 'load_from_logfile' - from phys logfile, detected R-peaks of scanner - % 'manual' - via manually selected QRS-wave for autocoreelations + % default: auto + % + % 'auto_matched' + % - auto generation of representative QRS-wave; detection via + % maximizing auto-correlation with it + % 'load_from_logfile' + % - from phys logfile, detected R-peaks of scanner + % 'manual' - via manually selected QRS-wave for autocorrelations % 'load' - from previous manual/auto run - preproc.cardiac.initial_cpulse_select.method = 'load_from_logfile'; + preproc.cardiac.initial_cpulse_select.method = 'auto_matched'; % file containing reference ECG-peak (variable kRpeak) % used for method 'manual' or 'load' [default: not set] % if method == 'manual', this file is saved after picking the QRS-wave % such that results are reproducible - preproc.cardiac.initial_cpulse_select.file = ''; + % default: initial_cpulse_kRpeakfile.mat + preproc.cardiac.initial_cpulse_select.file = 'initial_cpulse_kRpeakfile.mat'; % threshold for correlation with QRS-wave to find cardiac pulses - preproc.cardiac.initial_cpulse_select.min = []; + % default: 0.4 + preproc.cardiac.initial_cpulse_select.min = 0.4; % variable saving an example cardiac QRS-wave to correlate with % ECG time series preproc.cardiac.initial_cpulse_select.kRpeak = []; - % The posthoc cardiac pulse selection structure: If only few (<20) + % The post-hoc cardiac pulse selection structure: If only few (<20) % cardiac pulses are missing in a session due to bad signal quality, a % manual selection after visual inspection is possible using the % following parameters. The results are saved for reproducibility @@ -340,9 +350,9 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 'none' no physiological model is computed; only the read-out % logfile data is read out and saved in physio.ons_secs - % 'RETROICOR' as in Glover el al, MRM 44, 2000l - % order of expansion: See Harvey et al, JMRI 28, 2008 - % 'HRV' heart rate variability, as in Chang et al, 2009 + % 'RETROICOR' as in Glover et al., MRM 44, 2000 + % order of expansion: See Harvey et al., JMRI 28, 2008 + % 'HRV' heart rate variability, as in Chang et al., 2009 % 'RVT' respiratory volume time, as in Birn et al., 2006/8 % 'movement' realignment parameters, derivatives, % + squared (parameters+derivatives), @@ -368,8 +378,8 @@ % 'RETROICOR' % 'HRV' % 'RVT' - % 'movement - % noise_rois + % 'movement' + % 'noise_rois' model.orthogonalise = 'none'; % true or false (default) @@ -403,7 +413,7 @@ %% RETROICOR (Model): Glover et al. 2000 % Retrospective image correction method, based on Fourier expansion of % cardiac and respiratory phase, plus multiplicative interaction terms - % (Harvey et al, 2008) + % (Harvey et al., 2008) model.retroicor.include = 1; % 1 = included; 0 = not used % natural number, order of cardiac phase Fourier expansion @@ -416,22 +426,20 @@ model.retroicor.order.cr = 1; - %% RVT (Model): Respiratory Volume per time model , Birn et al, 2006/8 + %% RVT (Model): Respiratory Volume per time model , Birn et al., 2006/8 model.rvt.include = 0; % one or multiple delays (in seconds) can be specified to shift - % canonical RVT response function from Birn et al, 2006 paper + % canonical RVT response function from Birn et al., 2006 paper % Delays e.g. 0, 5, 10, 15, and 20s (Jo et al., 2010 NeuroImage 52) - model.rvt.delays = 0; - %% HRV (Model): Heart Rate variability, Chang et al, 2009 - + %% HRV (Model): Heart Rate variability, Chang et al., 2009 model.hrv.include = 0; % one or multiple delays (in seconds) can be specified to shift - % canonical HRV response function from Chang et al, 2009 paper + % canonical HRV response function from Chang et al., 2009 paper % Delays e.g. 0:6:24s (Shmueli et al, 2007, NeuroImage 38) model.hrv.delays = 0; @@ -444,13 +452,15 @@ % T.T., 2007. A component based noise correction method (CompCor) % for BOLD and perfusion based fMRI. NeuroImage 37, 90-101. % doi:10.1016/j.neuroimage.2007.04.042 - model.noise_rois.include = 0; - % cell of preprocessed fmri nifti/analyze files, from which time series + + % cell of preprocessed fMRI nifti/analyze files, from which time series % shall be extracted model.noise_rois.fmri_files = {}; + % cell of Masks/tissue probability maps characterizing where noise resides model.noise_rois.roi_files = {}; + % Single threshold or vector [1, nRois] of thresholds to be applied to mask files to decide % which voxels to include (e.g. a probability like 0.99, if roi_files % are tissue probability maps) @@ -470,6 +480,17 @@ % extracted model.noise_rois.n_components = 1; + % Noise ROIs volumes must have the same geometry as the functional time series. + % It means same affine transformation(space) and same matrix(voxel size) + % Possible values: + % 'Yes' or 1/true (default) + % Coregister : Estimate & Reslice will be performed on the noise NOIs, + % so their geometry (space + voxel size) will match the fMRI volume. + % 'No' or 0 or false + % Geometry will be tested: + % 1) If they match, continue + % 2) If they don't match, perform a Coregister : Estimate & Reslice as fallback + model.noise_rois.force_coregister = 1; %% movement (Model): Regressor model 6/12/24, Friston et al. 1996 % Also: sudden movement exceedance regressors @@ -484,7 +505,7 @@ % Volterra expansion V_t, V_t^2, V_(t-1), V_(t-1)^2 model.movement.order = 6; - % Censoring Outlier Threshold; + % Censoring outlier threshold; % Threshold, above which a stick (''spike'') regressor is created for % corresponding outlier volume exceeding threshold' % @@ -509,11 +530,11 @@ % quality criterion will be created, using one of these methods: % % 'None' - no motion censoring performed - % 'MAXVAL' - tresholding (max. translation/rotation) - % 'FD'' - framewise displacement (as defined by Power et al., 2012) + % 'MAXVAL' - thresholding (max. translation/rotation) + % 'FD'' - frame-wise displacement (as defined by Power et al., 2012) % i.e., |rp_x(n+1) - rp_x(n)| + |rp_y(n+1) - rp_y(n)| + |rp_z(n+1) - rp_z(n)| - % + 50mm *(|rp_pitch(n+1) - rp_pitch(n)| + |rp_roll(n+1) - rp_roll(n)| + |rp_yaw(n+1) - rp_yaw(n)| - % where 50mm is an average head radius mapping a rotation into a translation of head surface + % + 50 mm *(|rp_pitch(n+1) - rp_pitch(n)| + |rp_roll(n+1) - rp_roll(n)| + |rp_yaw(n+1) - rp_yaw(n)| + % where 50 mm is an average head radius mapping a rotation into a translation of head surface % 'DVARS' - root mean square over brain voxels of % difference in voxel intensity between consecutive volumes % (Power et al., 2012)) @@ -569,11 +590,13 @@ % regressors % Fig 9: final multiple_regressors matrix verbose.level = 1; + + % stores text outputs of PhysIO Toolbox processing, e.g. warnings about missed + % slice triggers, peak height etc. verbose.process_log = cell(0,1); - % stores text outputs of PhysIO Toolbox - % processing, e.g. warnings about missed - % slice triggers, peak height etc. - verbose.fig_handles = zeros(0,1); % [nFigs,1] vector; collecting of all generated figure handles during a run of tapas_physio_main_create_regressors + + % [nFigs,1] vector; collecting of all generated figure handles during a run of tapas_physio_main_create_regressors + verbose.fig_handles = zeros(0,1); % file name (including extension) where to print all physIO output % figures to. @@ -586,8 +609,8 @@ % index, e.g. 'PhysIO_output_fig01.jpg' verbose.fig_output_file = ''; - - % If true, plots are performed in tabs of SPM graphics window + % NOT IMPLEMENTED YET + % If true, plots are performed in tabs of SPM graphics window % TODO: implement via [handles] = spm_uitab(hparent,labels,callbacks,... % tag,active,height,tab_height) % @@ -618,7 +641,7 @@ ons_secs.c_is_reliable = []; % 1 for all time points where cardiac recording is reliable, 0 elsewhere (e.g. high noise, too low/high heartrates) ons_secs.r_is_reliable = []; % 1 for all time points, where respiratory recording is reliable; 0 elsewhere (e.g. constant amplitude through detachment/clipping) - % processed elements cardiac pulse detecion and phase estimations + % processed elements cardiac pulse detection and phase estimations ons_secs.cpulse = []; % onset times of cardiac pulse events (e.g. R-peaks) ons_secs.fr = []; % filtered respiration amplitude time series ons_secs.c_sample_phase = []; % phase in heart-cycle when each slice of each volume was acquired @@ -681,5 +704,5 @@ % Call functions for specific initial value settings (e.g. 3T Philips system) switch default_scheme case 'Philips' - physio = tapas_physio_init_philips(physio); + physio = tapas_physio_new_philips(physio); end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_version.m b/PhysIO/code/tapas_physio_version.m new file mode 100644 index 00000000..9b01b8cf --- /dev/null +++ b/PhysIO/code/tapas_physio_version.m @@ -0,0 +1,26 @@ +function versionPhysio = tapas_physio_version() +% returns version number of this copy of PhysIO +% +% output = tapas_physio_version(input) +% +% IN +% +% OUT +% +% EXAMPLE +% tapas_physio_version +% +% See also + +% Author: Lars Kasper +% Created: 2018-10-19 +% Copyright (C) 2018 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under +% the terms of the GNU General Public License (GPL), version 3. You can +% redistribute it and/or modify it under the terms of the GPL (either +% version 3 or, at your option, any later version). For further details, +% see the file COPYING or . +% +versionPhysio = 'R2019a-v7.1.0'; \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_cell2char.m b/PhysIO/code/utils/tapas_physio_cell2char.m similarity index 96% rename from PhysIO/code/tapas_physio_cell2char.m rename to PhysIO/code/utils/tapas_physio_cell2char.m index 9176a792..7a644995 100644 --- a/PhysIO/code/tapas_physio_cell2char.m +++ b/PhysIO/code/utils/tapas_physio_cell2char.m @@ -12,7 +12,7 @@ % tapas_physio_cell2char % % See also -% + % Author: Lars Kasper % Created: 2014-04-28 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -21,8 +21,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ + + physio.save_dir = char(physio.save_dir); physio.log_files.cardiac = char(physio.log_files.cardiac); physio.log_files.respiration = char(physio.log_files.respiration); diff --git a/PhysIO/code/tapas_physio_check_path.m b/PhysIO/code/utils/tapas_physio_check_path.m similarity index 88% rename from PhysIO/code/tapas_physio_check_path.m rename to PhysIO/code/utils/tapas_physio_check_path.m index 6224fed2..b392dba3 100644 --- a/PhysIO/code/tapas_physio_check_path.m +++ b/PhysIO/code/utils/tapas_physio_check_path.m @@ -11,7 +11,7 @@ % tapas_physio_check_path % % See also -% + % Author: Lars Kasper % Created: 2018-02-17 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, @@ -22,7 +22,9 @@ % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . % -pathPhysIO = fileparts(mfilename('fullpath')); + +% 2x fileparts, since it is the parent folder of where check_path resides +pathPhysIO = fileparts(fileparts(mfilename('fullpath'))); pathCell = regexp(path, pathsep, 'split'); if ispc || ismac % Windows/Mac is not case-sensitive diff --git a/PhysIO/code/tapas_physio_check_spm.m b/PhysIO/code/utils/tapas_physio_check_spm.m similarity index 99% rename from PhysIO/code/tapas_physio_check_spm.m rename to PhysIO/code/utils/tapas_physio_check_spm.m index 62a2fa4f..ea9d8a81 100644 --- a/PhysIO/code/tapas_physio_check_spm.m +++ b/PhysIO/code/utils/tapas_physio_check_spm.m @@ -12,7 +12,7 @@ % tapas_physio_check_spm % % See also spm_check_installation tapas_physio_check_spm_batch_editor_integration -% + % Author: Lars Kasper % Created: 2018-02-17 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, diff --git a/PhysIO/code/tapas_physio_check_spm_batch_editor_integration.m b/PhysIO/code/utils/tapas_physio_check_spm_batch_editor_integration.m similarity index 99% rename from PhysIO/code/tapas_physio_check_spm_batch_editor_integration.m rename to PhysIO/code/utils/tapas_physio_check_spm_batch_editor_integration.m index 585b09af..a5496f0e 100644 --- a/PhysIO/code/tapas_physio_check_spm_batch_editor_integration.m +++ b/PhysIO/code/utils/tapas_physio_check_spm_batch_editor_integration.m @@ -14,7 +14,7 @@ % tapas_physio_check_spm_batch_editor_integration % % See also -% + % Author: Lars Kasper % Created: 2018-02-17 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, diff --git a/PhysIO/code/tapas_physio_corrcoef12.m b/PhysIO/code/utils/tapas_physio_corrcoef12.m similarity index 99% rename from PhysIO/code/tapas_physio_corrcoef12.m rename to PhysIO/code/utils/tapas_physio_corrcoef12.m index 4ca9ec38..8fa0eaa3 100644 --- a/PhysIO/code/tapas_physio_corrcoef12.m +++ b/PhysIO/code/utils/tapas_physio_corrcoef12.m @@ -25,7 +25,7 @@ % tapas_physio_corrcoef12 % % See also -% + % Author: Lars Kasper % Created: 2014-08-14 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -34,8 +34,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 3 isZtransformed = [0 0]; end diff --git a/PhysIO/code/tapas_physio_create_spm_toolbox_link.m b/PhysIO/code/utils/tapas_physio_create_spm_toolbox_link.m similarity index 79% rename from PhysIO/code/tapas_physio_create_spm_toolbox_link.m rename to PhysIO/code/utils/tapas_physio_create_spm_toolbox_link.m index d110c8f3..ac83d49b 100644 --- a/PhysIO/code/tapas_physio_create_spm_toolbox_link.m +++ b/PhysIO/code/utils/tapas_physio_create_spm_toolbox_link.m @@ -1,4 +1,4 @@ -function cmdString = tapas_physio_create_spm_toolbox_link() +function cmdString = tapas_physio_create_spm_toolbox_link(pathPhysIO) % Creates a symbolik link of PhysIO/code folder to subfolder SPM/toolbox/PhysIO % to make toolbox visible to SPM Batch editor % @@ -13,7 +13,7 @@ % tapas_physio_create_spm_toolbox_link % % See also -% + % Author: Lars Kasper % Created: 2018-02-17 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, @@ -26,12 +26,15 @@ % cmdString = ''; -pathPhysIO = fileparts(mfilename('fullpath')); +if nargin < 1 + % 2x fileparts, because parent folder + pathPhysIO = fileparts(fileparts(mfilename('fullpath'))); +end pathSpm = fileparts(which('spm')); if isempty(pathSpm) - warning('SPM folder not found. Could not create symbolink link to PhysIO Toolbox'); + warning('SPM folder not found. Could not create symbolic link to PhysIO Toolbox'); else pathLinkPhysIOSPM = fullfile(pathSpm, 'toolbox', 'PhysIO'); if ~exist(pathLinkPhysIOSPM, 'dir') @@ -40,13 +43,14 @@ % unfortunately, system link does not work for SPM, has to be % hard copy fprintf('Copying %s to %s, because symlink not sufficient on Windows...\n', pathPhysIO, pathLinkPhysIOSPM); - cmdString = sprintf('xcopy /I /Y %s %s', pathPhysIO, pathLinkPhysIOSPM); + cmdString = sprintf('copyfile(''%s'', ''%s'');',pathPhysIO, pathLinkPhysIOSPM); + eval(cmdString); % linking indeed the other way around than in Linux/Mac % cmdString = sprintf('mklink /D %s %s', pathLinkPhysIOSPM, pathPhysIO); else %unix/Mac cmdString = sprintf('ln -s %s %s', pathPhysIO, pathLinkPhysIOSPM); + system(cmdString); end - system(cmdString); else warning('Destination spm/toolbox folder %s already found. Will not overwrite or re-link', pathLinkPhysIOSPM); end diff --git a/PhysIO/code/tapas_physio_fieldnamesr.m b/PhysIO/code/utils/tapas_physio_fieldnamesr.m similarity index 100% rename from PhysIO/code/tapas_physio_fieldnamesr.m rename to PhysIO/code/utils/tapas_physio_fieldnamesr.m diff --git a/PhysIO/code/tapas_physio_filename2path.m b/PhysIO/code/utils/tapas_physio_filename2path.m similarity index 99% rename from PhysIO/code/tapas_physio_filename2path.m rename to PhysIO/code/utils/tapas_physio_filename2path.m index 69a4a148..57334bc1 100644 --- a/PhysIO/code/tapas_physio_filename2path.m +++ b/PhysIO/code/utils/tapas_physio_filename2path.m @@ -13,7 +13,7 @@ % tapas_physio_filename2path % % See also -% + % Author: Lars Kasper % Created: 2016-10-03 % Copyright (C) 2016 TNU, Institute for Biomedical Engineering, @@ -23,8 +23,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + % strip cell if iscell(filename) diff --git a/PhysIO/code/tapas_physio_fill_empty_parameters.m b/PhysIO/code/utils/tapas_physio_fill_empty_parameters.m similarity index 93% rename from PhysIO/code/tapas_physio_fill_empty_parameters.m rename to PhysIO/code/utils/tapas_physio_fill_empty_parameters.m index 8f1ddd5a..3ad5be5d 100644 --- a/PhysIO/code/tapas_physio_fill_empty_parameters.m +++ b/PhysIO/code/utils/tapas_physio_fill_empty_parameters.m @@ -15,7 +15,7 @@ % tapas_physio_fill_empty_parameters % % See also tapas_physio_new tapas_physio_main_create_regressors -% + % Author: Lars Kasper % Created: 2014-04-27 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -24,8 +24,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ + + if isempty(physio.scan_timing.sqpar.NslicesPerBeat) physio.scan_timing.sqpar.NslicesPerBeat = physio.scan_timing.sqpar.Nslices; @@ -50,6 +50,12 @@ if isempty(physio.log_files.sampling_interval) switch lower(physio.log_files.vendor) + case {'bids', 'biopac_mat', 'brainproducts', 'siemens'} % will be read from file later + physio.log_files.sampling_interval = []; + case 'biopac_txt' + physio.log_files.sampling_interval = 1/1000; + case 'ge' + physio.log_files.sampling_interval = 25e-3; case 'philips' isWifi = regexpi(physio.preproc.cardiac.modality, '_wifi'); % different sampling interval for Wifi devices @@ -58,12 +64,8 @@ else physio.log_files.sampling_interval = 1/500; end - case 'ge' - physio.log_files.sampling_interval = 25e-3; case {'siemens_tics', 'siemens_hcp'} physio.log_files.sampling_interval = 2.5e-3; - case {'biopac_mat', 'siemens', 'brainproducts'} % will be read from file later - physio.log_files.sampling_interval = []; otherwise % e.g. custom error('Please specify sampling interval for custom text data'); end diff --git a/PhysIO/code/tapas_physio_find_string.m b/PhysIO/code/utils/tapas_physio_find_string.m similarity index 97% rename from PhysIO/code/tapas_physio_find_string.m rename to PhysIO/code/utils/tapas_physio_find_string.m index 6ff69ece..83e801c0 100644 --- a/PhysIO/code/tapas_physio_find_string.m +++ b/PhysIO/code/utils/tapas_physio_find_string.m @@ -23,11 +23,14 @@ % find_string % % See also -% + % Author: Lars Kasper % Created: 2014-11-05 % Copyright (C) 2014 Institute for Biomedical Engineering, ETH/Uni Zurich. -% $Id$ + +if ~iscell(stringArray) + stringArray = {stringArray}; +end if nargin < 3 isExact = false; diff --git a/PhysIO/code/utils/tapas_physio_findpeaks.m b/PhysIO/code/utils/tapas_physio_findpeaks.m new file mode 100644 index 00000000..92a3f843 --- /dev/null +++ b/PhysIO/code/utils/tapas_physio_findpeaks.m @@ -0,0 +1,47 @@ +function [pks,locs] = tapas_physio_findpeaks(X,varargin) +% Finds local peaks in the data. Wrapper for Matlab's findpeaks, +% with compatibility alternative, if no signal processing toolbox available +% +% [pks,locs] = tapas_physio_findpeaks(X,varargin) +% +% IN +% X input data vector +% varargin property name/value pairs, the following options are available +% +% 'minPeakHeight' +% 'minPeakDistance' +% 'threshold' local elevation compared to neighbours +% 'nPeaks' +% 'sorstr' 'ascend' or 'descend'; default: no sorting, order of +% occurence +% +% OUT +% pks height of local peaks (maxima) +% locs location indices of peaks +% +% EXAMPLE +% tapas_physio_findpeaks +% +% See also + +% Author: Lars Kasper +% Created: 2019-02-01 +% Copyright (C) 2019 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under +% the terms of the GNU General Public License (GPL), version 3. You can +% redistribute it and/or modify it under the terms of the GPL (either +% version 3 or, at your option, any later version). For further details, +% see the file COPYING or . + +if exist('findpeaks', 'file') + [pks,locs] = findpeaks(X,varargin{:}); +else + % previously: + % [pks,locs] = tapas_physio_findpeaks_compatible(X,varargin{:}); + tapas_physio_log(sprintf(... + ['tapas_physio_findpeaks_compatible is no longer\n' ... + 'distributed with this toolbox. Instead, we rely on the "findpeaks"\n' ... + 'function included in Matlab''s Signal Processing Toolbox.']), [], 2) +end \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_findpeaks_template_correlation.m b/PhysIO/code/utils/tapas_physio_findpeaks_template_correlation.m similarity index 84% rename from PhysIO/code/tapas_physio_findpeaks_template_correlation.m rename to PhysIO/code/utils/tapas_physio_findpeaks_template_correlation.m index a68536ae..6d59a9e6 100644 --- a/PhysIO/code/tapas_physio_findpeaks_template_correlation.m +++ b/PhysIO/code/utils/tapas_physio_findpeaks_template_correlation.m @@ -28,7 +28,7 @@ % tapas_physio_findpeaks_template_correlation % % See also -% + % Author: Steffen Bollmann, merged in this function: Lars Kasper % Created: 2014-08-05 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, @@ -38,8 +38,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: tapas_physio_findpeaks_template_correlation.m 787 2015-07-31 18:35:24Z kasperla $ + + % Determine starting peak for the search: % search for a representative R-peak a range of peaks @@ -191,39 +191,43 @@ while n <= nLimit %search around peak for searchPosition = -searchStepsTotal:1:searchStepsTotal - startSignalIndex = ... - max(1, n-halfTemplateWidthInSamples+searchPosition); - endSignalIndex = n+halfTemplateWidthInSamples+searchPosition; - - signalPart = cPadded(startSignalIndex:endSignalIndex); - - if debug - figure(997); hold all; - plot(startSignalIndex:endSignalIndex, cPadded(startSignalIndex:endSignalIndex)); - xlim([0 1000]); - end - - correlation = tapas_physio_corrcoef12(signalPart, ... - zTransformedTemplate, isZTransformed); - - locationWeight = gaussianWindow(searchPosition+searchStepsTotal+1); - amplitudeWeight = abs(cPadded(n+searchPosition+1)); - correlationWeighted = locationWeight .* amplitudeWeight .* correlation; - similarityToTemplate(n+searchPosition) = correlationWeighted; - % collect plot Data - plotData.locationWeight(n+searchPosition) = locationWeight; - plotData.amplitudeWeight(n+searchPosition) = amplitudeWeight; - - if searchPosition==0 - plotData.searchedAt(n+searchPosition) = 1; + % check only, if search indices within bounds of time series + if (n+searchPosition) >= 1 && (n+searchPosition+1) <= nLimit + + startSignalIndex = ... + max(1, n-halfTemplateWidthInSamples+searchPosition); + endSignalIndex = n+halfTemplateWidthInSamples+searchPosition; + + signalPart = cPadded(startSignalIndex:endSignalIndex); + + if debug + figure(997); hold all; + plot(startSignalIndex:endSignalIndex, cPadded(startSignalIndex:endSignalIndex)); + xlim([0 1000]); + end + + correlation = tapas_physio_corrcoef12(signalPart, ... + zTransformedTemplate, isZTransformed); + + locationWeight = gaussianWindow(searchPosition+searchStepsTotal+1); + amplitudeWeight = abs(cPadded(n+searchPosition+1)); + correlationWeighted = locationWeight .* amplitudeWeight .* correlation; + similarityToTemplate(n+searchPosition) = correlationWeighted; + + % collect plot Data + plotData.locationWeight(n+searchPosition) = locationWeight; + plotData.amplitudeWeight(n+searchPosition) = amplitudeWeight; + + if searchPosition==0 + plotData.searchedAt(n+searchPosition) = 1; + end end - end %find largest correlation-peak from all the different search positions - indexSearchStart=n-searchStepsTotal; - indexSearchEnd=n+searchStepsTotal; + indexSearchStart = max(1, n-searchStepsTotal); + indexSearchEnd = min(n+searchStepsTotal, nLimit - 1); % -1 because of index shift similarityToTemplate and cPadded indexSearchRange=indexSearchStart:indexSearchEnd; searchRangeValues=similarityToTemplate(indexSearchRange); diff --git a/PhysIO/code/tapas_physio_findpeaks_template_xcorr.m b/PhysIO/code/utils/tapas_physio_findpeaks_template_xcorr.m similarity index 98% rename from PhysIO/code/tapas_physio_findpeaks_template_xcorr.m rename to PhysIO/code/utils/tapas_physio_findpeaks_template_xcorr.m index e912ddd3..40a296c9 100644 --- a/PhysIO/code/tapas_physio_findpeaks_template_xcorr.m +++ b/PhysIO/code/utils/tapas_physio_findpeaks_template_xcorr.m @@ -19,7 +19,7 @@ % tapas_physio_findpeaks_template_correlation % % See also -% + % Author: Steffen Bollmann, cleanup, xcorr: Lars Kasper % Created: 2014-08-05 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, @@ -29,8 +29,8 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: tapas_physio_findpeaks_template_correlation.m 748 2015-06-19 11:29:27Z kasperla $ + + % Determine starting peak for the search: % search for a representative R-peak a range of peaks diff --git a/PhysIO/code/utils/tapas_physio_gausswin.m b/PhysIO/code/utils/tapas_physio_gausswin.m new file mode 100644 index 00000000..58999b51 --- /dev/null +++ b/PhysIO/code/utils/tapas_physio_gausswin.m @@ -0,0 +1,36 @@ +function w = tapas_physio_gausswin(L, a) +% vector of discrete Gaussian window +% +% w = tapas_physio_gausswin(L, a) +% +% IN +% L number of sampling points of gaussian window +% a alpha parameter: 1/std of gaussian, measures width of +% fourier transform of gausswin +% The larger a, the narrower the window +% default: 2.5 +% OUT +% w [L,1] vector of Gaussian window +% EXAMPLE +% tapas_physio_suptitle +% +% See also + +% Author: Lars Kasper +% Created: 2019-02-01 +% Copyright (C) 2019 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under +% the terms of the GNU General Public License (GPL), version 3. You can +% redistribute it and/or modify it under the terms of the GPL (either +% version 3 or, at your option, any later version). For further details, +% see the file COPYING or . + +if nargin < 2 + a = 2.5; +end + +N = L-1; +n = (0:N)'-N/2; +w = exp(-2*(a*n/N).^2); diff --git a/PhysIO/code/tapas_physio_job2physio.m b/PhysIO/code/utils/tapas_physio_job2physio.m similarity index 94% rename from PhysIO/code/tapas_physio_job2physio.m rename to PhysIO/code/utils/tapas_physio_job2physio.m index c62ec4e4..0a696e86 100644 --- a/PhysIO/code/tapas_physio_job2physio.m +++ b/PhysIO/code/utils/tapas_physio_job2physio.m @@ -1,68 +1,68 @@ -function physio = tapas_physio_job2physio(job) -% Converts job from SPM batch editor to physio-structure -% -% physio = tapas_physio_job2physio(job) -% -% IN -% -% OUT -% -% EXAMPLE -% physio = tapas_physio_job2physio(job) -% -% See also spm_physio_cfg_matlabbatch -% -% Author: Lars Kasper -% Created: 2015-01-05 -% Copyright (C) 2015 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. -% -% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public -% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ - - -physio = tapas_physio_new(); - -%% Use existing properties that are cfg_choices in job to overwrite -% properties of physio and set corresponding method - -physio = tapas_physio_update_from_job(physio, job, ... - {'preproc.cardiac.posthoc_cpulse_select', ... - 'preproc.cardiac.initial_cpulse_select', 'scan_timing.sync'}, ... - {'preproc.cardiac.posthoc_cpulse_select', ... - 'preproc.cardiac.initial_cpulse_select', 'scan_timing.sync'}, ... - true, ... - 'method'); - -%% Take over model substructs as is -modelArray = ... - {'movement', 'retroicor', 'rvt', 'hrv', ... - 'noise_rois', 'other'}; - -physio = tapas_physio_update_from_job(physio, job, ... - strcat('model.', modelArray), strcat('model.', modelArray), ... - true, 'include'); - -%% Convert yes => true (=1) and no => false (=0) -nModels = numel(modelArray); -for iModel = 1:nModels - physio.model.(modelArray{iModel}).include = strcmpi(... - physio.model.(modelArray{iModel}).include, 'yes'); -end - -%% Use existing properties in job to overwrite properties of physio -physio = tapas_physio_update_from_job(physio, job, ... - {'preproc.cardiac.modality', 'scan_timing.sqpar', ... - 'log_files', 'verbose', 'save_dir',... - 'model.orthogonalise', 'model.censor_unreliable_recording_intervals', ... - 'model.output_multiple_regressors', ... - 'model.output_physio'}, ... - {'preproc.cardiac.modality', 'scan_timing.sqpar', ... - 'log_files', 'verbose', 'save_dir',... - 'model.orthogonalise', 'model.censor_unreliable_recording_intervals', ... - 'model.output_multiple_regressors', ... - 'model.output_physio'}, ... - false); +function physio = tapas_physio_job2physio(job) +% Converts job from SPM batch editor to physio-structure +% +% physio = tapas_physio_job2physio(job) +% +% IN +% +% OUT +% +% EXAMPLE +% physio = tapas_physio_job2physio(job) +% +% See also spm_physio_cfg_matlabbatch + +% Author: Lars Kasper +% Created: 2015-01-05 +% Copyright (C) 2015 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + + + + +physio = tapas_physio_new(); + +%% Use existing properties that are cfg_choices in job to overwrite +% properties of physio and set corresponding method + +physio = tapas_physio_update_from_job(physio, job, ... + {'preproc.cardiac.posthoc_cpulse_select', ... + 'preproc.cardiac.initial_cpulse_select', 'scan_timing.sync'}, ... + {'preproc.cardiac.posthoc_cpulse_select', ... + 'preproc.cardiac.initial_cpulse_select', 'scan_timing.sync'}, ... + true, ... + 'method'); + +%% Take over model substructs as is +modelArray = ... + {'movement', 'retroicor', 'rvt', 'hrv', ... + 'noise_rois', 'other'}; + +physio = tapas_physio_update_from_job(physio, job, ... + strcat('model.', modelArray), strcat('model.', modelArray), ... + true, 'include'); + +%% Convert yes => true (=1) and no => false (=0) +nModels = numel(modelArray); +for iModel = 1:nModels + physio.model.(modelArray{iModel}).include = strcmpi(... + physio.model.(modelArray{iModel}).include, 'yes'); +end + +%% Use existing properties in job to overwrite properties of physio +physio = tapas_physio_update_from_job(physio, job, ... + {'preproc.cardiac.modality', 'scan_timing.sqpar', ... + 'log_files', 'verbose', 'save_dir',... + 'model.orthogonalise', 'model.censor_unreliable_recording_intervals', ... + 'model.output_multiple_regressors', ... + 'model.output_physio'}, ... + {'preproc.cardiac.modality', 'scan_timing.sqpar', ... + 'log_files', 'verbose', 'save_dir',... + 'model.orthogonalise', 'model.censor_unreliable_recording_intervals', ... + 'model.output_multiple_regressors', ... + 'model.output_physio'}, ... + false); diff --git a/PhysIO/code/tapas_physio_log.m b/PhysIO/code/utils/tapas_physio_log.m similarity index 86% rename from PhysIO/code/tapas_physio_log.m rename to PhysIO/code/utils/tapas_physio_log.m index edfb5a21..ef1709a3 100644 --- a/PhysIO/code/tapas_physio_log.m +++ b/PhysIO/code/utils/tapas_physio_log.m @@ -19,7 +19,7 @@ % tapas_physio_log % % See also -% + % Author: Lars Kasper % Created: 2015-07-05 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -28,8 +28,11 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 672 2015-02-08 17:46:28Z kasperla $ + +if nargin < 2 || isempty(verbose) + tmpPhysio = tapas_physio_new(); + verbose = tmpPhysio.verbose; +end if nargin < 3 warningLevel = 0; @@ -47,12 +50,12 @@ try save(filenameProcessLog, 'verbose'); catch - warning(['Could not save verbose info in %s, ' ... + warning('tapas:physio', ['Could not save verbose info in %s, ' ... 'throwing original error now:'], filenameProcessLog); end - error(msg); + error('tapas:physio', msg); case 1 - warning(msg); + warning('tapas:physio', msg); case 0 fprintf('%s\n',msg); end diff --git a/PhysIO/code/tapas_physio_logo.m b/PhysIO/code/utils/tapas_physio_logo.m similarity index 89% rename from PhysIO/code/tapas_physio_logo.m rename to PhysIO/code/utils/tapas_physio_logo.m index 17e6fb96..71c62ff2 100644 --- a/PhysIO/code/tapas_physio_logo.m +++ b/PhysIO/code/utils/tapas_physio_logo.m @@ -11,7 +11,7 @@ function tapas_physio_logo() % tapas_physio_logo % % See also -% + % Author: Lars Kasper % Created: 2018-02-17 % Copyright (C) 2018 TNU, Institute for Biomedical Engineering, @@ -26,7 +26,7 @@ function tapas_physio_logo() disp(' '); disp('====================================================================') disp(' '); -disp(' This is the TAPAS PhysIO Toolbox Version R2017.3'); +fprintf(' This is the TAPAS PhysIO Toolbox Version %s \n', tapas_physio_version()); disp(' '); disp('====================================================================') -disp(' '); +disp(' '); \ No newline at end of file diff --git a/PhysIO/code/tapas_physio_maxfilter.m b/PhysIO/code/utils/tapas_physio_maxfilter.m similarity index 99% rename from PhysIO/code/tapas_physio_maxfilter.m rename to PhysIO/code/utils/tapas_physio_maxfilter.m index a043a9e5..dfd55cec 100644 --- a/PhysIO/code/tapas_physio_maxfilter.m +++ b/PhysIO/code/utils/tapas_physio_maxfilter.m @@ -14,7 +14,7 @@ % tapas_physio_maxfilter % % See also -% + % Author: Lars Kasper % Created: 2015-01-11 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -23,8 +23,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + nSamples = numel(x); if nargin < 2 diff --git a/PhysIO/code/tapas_physio_init_philips.m b/PhysIO/code/utils/tapas_physio_new_philips.m similarity index 92% rename from PhysIO/code/tapas_physio_init_philips.m rename to PhysIO/code/utils/tapas_physio_new_philips.m index 7f61d1fc..42732b33 100644 --- a/PhysIO/code/tapas_physio_init_philips.m +++ b/PhysIO/code/utils/tapas_physio_new_philips.m @@ -1,18 +1,18 @@ -function physio = tapas_physio_init_philips(physio) +function physio = tapas_physio_new_philips(physio) % Initializes physio-properties for Philips 3T Achieva system with good % ECG-data % -% physio = tapas_physio_init_philips(physio) +% physio = tapas_physio_new_philips(physio) % % IN % % OUT % % EXAMPLE -% tapas_physio_init_philips +% tapas_physio_new_philips % % See also -% + % Author: Lars Kasper % Created: 2014-10-09 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -21,8 +21,6 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ log_files = physio.log_files; scan_timing = physio.scan_timing; diff --git a/PhysIO/code/tapas_physio_prctile.m b/PhysIO/code/utils/tapas_physio_prctile.m similarity index 99% rename from PhysIO/code/tapas_physio_prctile.m rename to PhysIO/code/utils/tapas_physio_prctile.m index 2372d522..59cf481b 100644 --- a/PhysIO/code/tapas_physio_prctile.m +++ b/PhysIO/code/utils/tapas_physio_prctile.m @@ -17,7 +17,7 @@ % figure;hist(x,100);yl = ylim;hold all;stem(p, yl(2)); % % See also prctile tapas_physio_plot_raw_physdata_diagnostics tapas_physio_create_scan_timing_from_gradients_philips -% + % Author: Lars Kasper % Created: 2013-03-13 % Copyright (C) 2013 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -26,8 +26,7 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + sx = sort(x); N = length(x); diff --git a/PhysIO/code/tapas_physio_prepend_absolute_paths.m b/PhysIO/code/utils/tapas_physio_prepend_absolute_paths.m similarity index 89% rename from PhysIO/code/tapas_physio_prepend_absolute_paths.m rename to PhysIO/code/utils/tapas_physio_prepend_absolute_paths.m index dc7272e8..a55ff6e1 100644 --- a/PhysIO/code/tapas_physio_prepend_absolute_paths.m +++ b/PhysIO/code/utils/tapas_physio_prepend_absolute_paths.m @@ -12,7 +12,7 @@ % tapas_physio_prepend_absolute_paths % % See also -% + % Author: Lars Kasper % Created: 2014-05-03 % Copyright (C) 2014 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. @@ -21,8 +21,14 @@ % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ + + +[parentPath, currentFolder] = fileparts(physio.save_dir); + +% only relative folder specified, make absolute +if isempty(parentPath) && ~isempty(currentFolder) + physio.save_dir = fullfile(pwd, physio.save_dir); +end save_dir = physio.save_dir; diff --git a/PhysIO/code/tapas_physio_propval.m b/PhysIO/code/utils/tapas_physio_propval.m similarity index 100% rename from PhysIO/code/tapas_physio_propval.m rename to PhysIO/code/utils/tapas_physio_propval.m diff --git a/PhysIO/code/tapas_physio_replace_absolute_paths.m b/PhysIO/code/utils/tapas_physio_replace_absolute_paths.m similarity index 71% rename from PhysIO/code/tapas_physio_replace_absolute_paths.m rename to PhysIO/code/utils/tapas_physio_replace_absolute_paths.m index 738ca28f..e4b2015a 100644 --- a/PhysIO/code/tapas_physio_replace_absolute_paths.m +++ b/PhysIO/code/utils/tapas_physio_replace_absolute_paths.m @@ -19,7 +19,7 @@ % tapas_physio_replace_absolute_paths % % See also tapas_physio_fieldnamesr -% + % Author: Lars Kasper % Created: 2015-08-10 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, @@ -29,8 +29,7 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + jobFields = tapas_physio_fieldnamesr(job); @@ -41,11 +40,29 @@ nFields = numel(jobFields); nPaths = numel(pathArray); +% replace \ by / in search paths first (will be done in fields of job +% later) +for p = 1:nPaths + pathArray{p} = regexprep(pathArray{p}, '\\', '/'); +end + for f = 1:nFields for p = 1:nPaths if ~isempty(pathArray{p}) strField = ['job.' jobFields{f}]; - % replace strings + + if ispc % replace \ by / first + % replace strings + if eval(['isstr(' strField ')']) + % to lazy for not using eval :-( + eval([ strField ' = regexprep(' strField ', ''\\'', ''/'');']); + % replace cellstrings + elseif eval(['iscell(' strField ')']) && eval(['isstr(' strField '{1})']) + eval([ strField '{1} = regexprep(' strField '{1}, ''\\'', ''/'');']); + end + end + + % remove all prepending */ (paths) before (filename) strings if eval(['isstr(' strField ')']) % to lazy for not using eval :-( eval([ strField ' = regexprep(' strField ', ''' pathArray{p} '[/]*'', '''');']); diff --git a/PhysIO/code/tapas_physio_save_batch_mat_script.m b/PhysIO/code/utils/tapas_physio_save_batch_mat_script.m similarity index 58% rename from PhysIO/code/tapas_physio_save_batch_mat_script.m rename to PhysIO/code/utils/tapas_physio_save_batch_mat_script.m index 99818d85..8471c72a 100644 --- a/PhysIO/code/tapas_physio_save_batch_mat_script.m +++ b/PhysIO/code/utils/tapas_physio_save_batch_mat_script.m @@ -1,4 +1,5 @@ -function physio = tapas_physio_save_batch_mat_script(fileBatchM, pathOutput) +function physio = tapas_physio_save_batch_mat_script(fileBatchM, pathOutput, ... + doRemoveEmptyDefaults) % Saves .m-matlabbatch-file as .mat and as spm-independent matlab-script % % physio = tapas_physio_save_batch_mat_script(fileBatchM) @@ -8,6 +9,10 @@ % variable % pathOutput path where new .mat and matlab-script file are saved % default: same as fileBatchM +% doRemoveEmptyDefaults +% If true, matlab batch lines with [] '' {} are not +% printed in output .m files +% default: true % % OUT % physio physio-structure, see also tapas_physio_new @@ -22,7 +27,7 @@ % % % See also cfg_util gencode -% + % Author: Lars Kasper % Created: 2015-07-19 % Copyright (C) 2015 TNU, Institute for Biomedical Engineering, @@ -32,22 +37,31 @@ % License (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id$ + if nargin < 1 fileBatchM = 'example_spm_job_ECG7T.m'; end % file given run it! +doSaveBatchM = true; if ~ischar(fileBatchM) matlabbatch = fileBatchM; fileBatchM = fullfile(pwd, 'physio_job.m'); else - run(fileBatchM); + [fp,fn,ext] = fileparts(fileBatchM); + switch ext + case '.mat' % indeed a .mat file + fileBatchMat = fileBatchM; + fileBatchM = fullfile(fp,[fn '.m']); + load(fileBatchMat); + case '.m' + doSaveBatchM = false; % does exist already + fileBatchMat = fullfile(fp,[fn '.mat']); + run(fileBatchM); + end end -fileBatchMat = regexprep(fileBatchM, '\.m', '\.mat'); fileScript = regexprep(fileBatchM, {'spm_job', 'job'}, 'matlab_script'); if isequal(fileScript,fileBatchM) @@ -57,11 +71,22 @@ if nargin >=2 %% replace paths in output file pathBatchM = fileparts(fileBatchM); - fileBatchMat = regexprep(fileBatchMat, pathBatchM, pathOutput); - fileScript = regexprep(fileScript, pathBatchM, pathOutput); + if isempty(pathBatchM) + % no path originally + fileBatchMOut = fullfile(pathOutput, fileBatchM); + fileBatchMat = fullfile(pathOutput, fileBatchMat); + fileScript = fullfile(pathOutput, fileScript); + else + fileBatchMOut = regexprep(fileBatchM, pathBatchM, pathOutput); + fileBatchMat = regexprep(fileBatchMat, pathBatchM, pathOutput); + fileScript = regexprep(fileScript, pathBatchM, pathOutput); + end + [~,~] = mkdir(pathOutput); end - +if nargin < 3 + doRemoveEmptyDefaults = true; +end if ~exist('cfg_files', 'file') spm_jobman('initcfg'); @@ -84,13 +109,17 @@ % delete temporary files -delete([fileTemp '.m'], fileTempJob); - +if doSaveBatchM + delete([fileTemp '.m']); + movefile(fileTempJob, fileBatchMOut); +else + delete([fileTemp '.m'], fileTempJob); +end % remove newly introduced absolute paths :-( pathJob = fileparts(fileBatchM); -% pathNow, since cfg-stuff adds the paths of the directory it was executed +% also pathNow, since cfg-stuff adds the paths of the directory it was executed % in :-( pathNow = pwd; matlabbatch{1}.spm.tools.physio = tapas_physio_replace_absolute_paths(... @@ -104,24 +133,33 @@ % convert to script variable and save to file physio = tapas_physio_job2physio(matlabbatch{1}.spm.tools.physio); - % write out variable strings and remove lines that set empty values str = gencode(physio)'; -indLineRemove = tapas_physio_find_string(str, ... - {'= \[\];', '= {};', '= {''''};', '= '''';'}); -indLineRemove = cell2mat(indLineRemove); -str(indLineRemove) = []; -% add generating line for physio-structure, and save to matlab_script-file -str = [{'physio = tapas_physio_new();'} +if doRemoveEmptyDefaults + indLineRemove = tapas_physio_find_string(str, ... + {'= \[\];', '= {};', '= {''''};', '= '''';'}); + indLineRemove = cell2mat(indLineRemove); + str(indLineRemove) = []; +end + +% add comments to write and generating line for physio-structure, +% and save to matlab_script-file +str = [ + {'%% Example script using PhysIO with Matlab only (no SPM needed)'} + {'% For documentation of the parameters, see also tapas_physio_new (e.g., via edit tapas_physio_new)'} + {''} + {'%% Create default parameter structure with all fields'} + {'physio = tapas_physio_new();'} {''} + {'%% Individual Parameter settings. Modify to your need and remove default settings'} str {''}; + {'%% Run physiological recording preprocessing and noise modeling'} {'physio = tapas_physio_main_create_regressors(physio);'} ]; - nLines = numel(str); fid = fopen(fileScript, 'w+'); diff --git a/PhysIO/code/tapas_physio_strip_fields.m b/PhysIO/code/utils/tapas_physio_strip_fields.m similarity index 95% rename from PhysIO/code/tapas_physio_strip_fields.m rename to PhysIO/code/utils/tapas_physio_strip_fields.m index 4048d11a..2fed119a 100644 --- a/PhysIO/code/tapas_physio_strip_fields.m +++ b/PhysIO/code/utils/tapas_physio_strip_fields.m @@ -24,7 +24,7 @@ function tapas_physio_strip_fields(opts) % coolArray 100x100 double % % See also propval -% + % Author: Lars Kasper % Created: 2013-11-13 % @@ -34,8 +34,8 @@ function tapas_physio_strip_fields(opts) % Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL % (either version 3 or, at your option, any later version). For further details, see the file % COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ + + optsArray = fields(opts); nOpts = length(optsArray); diff --git a/PhysIO/code/utils/tapas_physio_suptitle.m b/PhysIO/code/utils/tapas_physio_suptitle.m new file mode 100644 index 00000000..c0a1caa5 --- /dev/null +++ b/PhysIO/code/utils/tapas_physio_suptitle.m @@ -0,0 +1,31 @@ +function tapas_physio_suptitle(titleString) +% if Biodemo toolbox not installed, use simple title instead of suptitle +% +% tapas_physio_suptitle(titleString) +% +% IN +% titleString that should be displayed on top of figure with multiple +% subplots +% OUT +% +% EXAMPLE +% tapas_physio_suptitle +% +% See also + +% Author: Lars Kasper +% Created: 2019-01-31 +% Copyright (C) 2019 TNU, Institute for Biomedical Engineering, +% University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under +% the terms of the GNU General Public License (GPL), version 3. You can +% redistribute it and/or modify it under the terms of the GPL (either +% version 3 or, at your option, any later version). For further details, +% see the file COPYING or . + +if exist('suptitle', 'file') + suptitle(titleString); +else + title(titleString); +end diff --git a/PhysIO/code/tapas_physio_update_from_job.m b/PhysIO/code/utils/tapas_physio_update_from_job.m similarity index 95% rename from PhysIO/code/tapas_physio_update_from_job.m rename to PhysIO/code/utils/tapas_physio_update_from_job.m index 051dcf1e..e6460dc5 100644 --- a/PhysIO/code/tapas_physio_update_from_job.m +++ b/PhysIO/code/utils/tapas_physio_update_from_job.m @@ -1,116 +1,116 @@ -function physio = tapas_physio_update_from_job(physio, job, ... - jobPropertyArray, physioPropertyArray, isBranch, branchProperty) -% Updates properties of physio-structure from a job-structure -% -% physio = tapas_physio_update_from_job(physio, job, ... -% jobPropertyArray, physioPropertyArray, isBranch) -% -% IN -% physio physio-structure. See also tapas_physio_new -% job spm_jobman job, as created by See also -% tapas_physio_cfg_matlabbatch -% jobPropertyArray cell(nProperties,1) -% (sub-)properties of job-input that shall update -% physio-structure -% e.g. thresh.cardiac.posthoc_cpulse_select -% physioPropertyArray cell(nProperties,1) -% sub-properties of physio-structure that shall be updated by -% job -% e.g. thresh.cardiac.posthoc_cpulse_select -% isBranch true or false or cell(nProperties,1) of true/false -% If true, a branch is assumed as job-property, and the field -% name of the job-branch will be used as a separate -% (sub-) property of physio -% branchProperty string or cell(nProperties,1) of strings that -% jobProperty-field name itself shall be transferred to -% default: 'method' -% -% OUT -% physio physio-structure with updated properties according to job -% EXAMPLE -% physio = tapas_physio_update_from_job(physio, job, ... -% 'thresh.cardiac.posthoc_cpulse_select', ... -% 'thresh.cardiac.posthoc_cpulse_select, true, 'method') -% -% will set all properties set in -% job.thresh.cardiac.posthoc_cpulse_select.manual (or load or off) -% also in physio.thresh.cardiac.posthoc_cpulse_select and will also set -% physio.thresh.cardiac.posthoc_cpulse_select.method = 'manual' (or load -% or off -% -% physio = tapas_physio_update_from_job(physio, job, ... -% 'thresh.cardiac.posthoc_cpulse_select', ... -% 'thresh.cardiac.posthoc_cpulse_select, false) -% -% will just take all existing fields of job.thresh.cardiac.posthoc_cpulse_select -% and overwrite with this the corresponding fields of -% physio.thresh.cardiac.posthoc_cpulse_select -% -% See also -% -% Author: Lars Kasper -% Created: 2015-01-05 -% Copyright (C) 2015 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. -% -% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public -% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL -% (either version 3 or, at your option, any later version). For further details, see the file -% COPYING or . -% -% $Id: teditRETRO.m 464 2014-04-27 11:58:09Z kasperla $ - -jobPropertyArray = cellstr(jobPropertyArray); -physioPropertyArray = cellstr(physioPropertyArray); - -if nargin < 5 - isBranch = true; -end - -if nargin < 6 - branchProperty = 'method'; -end - -nProperties = numel(jobPropertyArray); - - -if ~iscell(isBranch) - isBranch = repmat({isBranch}, nProperties,1); -end - -if ~iscell(branchProperty) - branchProperty = repmat({branchProperty}, nProperties,1); -end - - -for p = 1:nProperties - currentProperty = eval(sprintf('job.%s', jobPropertyArray{p})); - - % overwrite properties of physio with sub-properties of job, also - % set value of branchProperty to name of current property - if isBranch{p} - valueBranchArray = fields(currentProperty); - valueBranch = valueBranchArray{1}; - eval(sprintf('physio.%s.%s = valueBranch;', ... - physioPropertyArray{p}, branchProperty{p})); - - currentProperty = currentProperty.(valueBranch); - end - - - % update property itself, if it has no sub-properties - if ~isstruct(currentProperty) - eval(sprintf('physio.%s = currentProperty;', ... - physioPropertyArray{p})); - else - - % update all existing sub-properties in job to physio - - subPropArray = fields(currentProperty); - nFields = numel(subPropArray); - - for f = 1:nFields - eval(sprintf('physio.%s.%s = currentProperty.%s;', ... - physioPropertyArray{p},subPropArray{f}, subPropArray{f})); - end - end +function physio = tapas_physio_update_from_job(physio, job, ... + jobPropertyArray, physioPropertyArray, isBranch, branchProperty) +% Updates properties of physio-structure from a job-structure +% +% physio = tapas_physio_update_from_job(physio, job, ... +% jobPropertyArray, physioPropertyArray, isBranch) +% +% IN +% physio physio-structure. See also tapas_physio_new +% job spm_jobman job, as created by See also +% tapas_physio_cfg_matlabbatch +% jobPropertyArray cell(nProperties,1) +% (sub-)properties of job-input that shall update +% physio-structure +% e.g. thresh.cardiac.posthoc_cpulse_select +% physioPropertyArray cell(nProperties,1) +% sub-properties of physio-structure that shall be updated by +% job +% e.g. thresh.cardiac.posthoc_cpulse_select +% isBranch true or false or cell(nProperties,1) of true/false +% If true, a branch is assumed as job-property, and the field +% name of the job-branch will be used as a separate +% (sub-) property of physio +% branchProperty string or cell(nProperties,1) of strings that +% jobProperty-field name itself shall be transferred to +% default: 'method' +% +% OUT +% physio physio-structure with updated properties according to job +% EXAMPLE +% physio = tapas_physio_update_from_job(physio, job, ... +% 'thresh.cardiac.posthoc_cpulse_select', ... +% 'thresh.cardiac.posthoc_cpulse_select, true, 'method') +% +% will set all properties set in +% job.thresh.cardiac.posthoc_cpulse_select.manual (or load or off) +% also in physio.thresh.cardiac.posthoc_cpulse_select and will also set +% physio.thresh.cardiac.posthoc_cpulse_select.method = 'manual' (or load +% or off +% +% physio = tapas_physio_update_from_job(physio, job, ... +% 'thresh.cardiac.posthoc_cpulse_select', ... +% 'thresh.cardiac.posthoc_cpulse_select, false) +% +% will just take all existing fields of job.thresh.cardiac.posthoc_cpulse_select +% and overwrite with this the corresponding fields of +% physio.thresh.cardiac.posthoc_cpulse_select +% +% See also + +% Author: Lars Kasper +% Created: 2015-01-05 +% Copyright (C) 2015 TNU, Institute for Biomedical Engineering, University of Zurich and ETH Zurich. +% +% This file is part of the TAPAS PhysIO Toolbox, which is released under the terms of the GNU General Public +% Licence (GPL), version 3. You can redistribute it and/or modify it under the terms of the GPL +% (either version 3 or, at your option, any later version). For further details, see the file +% COPYING or . + + + +jobPropertyArray = cellstr(jobPropertyArray); +physioPropertyArray = cellstr(physioPropertyArray); + +if nargin < 5 + isBranch = true; +end + +if nargin < 6 + branchProperty = 'method'; +end + +nProperties = numel(jobPropertyArray); + + +if ~iscell(isBranch) + isBranch = repmat({isBranch}, nProperties,1); +end + +if ~iscell(branchProperty) + branchProperty = repmat({branchProperty}, nProperties,1); +end + + +for p = 1:nProperties + currentProperty = eval(sprintf('job.%s', jobPropertyArray{p})); + + % overwrite properties of physio with sub-properties of job, also + % set value of branchProperty to name of current property + if isBranch{p} + valueBranchArray = fields(currentProperty); + valueBranch = valueBranchArray{1}; + eval(sprintf('physio.%s.%s = valueBranch;', ... + physioPropertyArray{p}, branchProperty{p})); + + currentProperty = currentProperty.(valueBranch); + end + + + % update property itself, if it has no sub-properties + if ~isstruct(currentProperty) + eval(sprintf('physio.%s = currentProperty;', ... + physioPropertyArray{p})); + else + + % update all existing sub-properties in job to physio + + subPropArray = fields(currentProperty); + nFields = numel(subPropArray); + + for f = 1:nFields + eval(sprintf('physio.%s.%s = currentProperty.%s;', ... + physioPropertyArray{p},subPropArray{f}, subPropArray{f})); + end + end end \ No newline at end of file diff --git a/PhysIO/docs/documentation.html b/PhysIO/docs/documentation.html index b0d99a46..fde9bd5f 100644 --- a/PhysIO/docs/documentation.html +++ b/PhysIO/docs/documentation.html @@ -6,10 +6,10 @@ PhysIO Toolbox Documentation