diff --git a/api-matlab/rtQC_display.m b/api-matlab/rtQC_display.m index a85544e..1b39dc8 100644 --- a/api-matlab/rtQC_display.m +++ b/api-matlab/rtQC_display.m @@ -59,6 +59,7 @@ function rtQC_display() % Setup all UIcontrols and GUI components rtQC_display_setup; + % Set callbacks gui_data.tgroup.SelectionChangedFcn = @tabChangedCallback; gui_data.pb_help.Callback = @linkToGithub; @@ -91,6 +92,7 @@ function rtQC_display() gui_data.pb_startRT.Callback = @startRT; gui_data.pb_stopRT.Callback = @stopRT; gui_data.popup_setDim.Callback = @setDim; +gui_data.popup_setVendor.Callback = @setVendor; gui_data.sld_slice.Callback = @changeSlice; gui_data.popup_setImg.Callback = @setImg; gui_data.edit_structural_pre.Callback = @editStructural; @@ -113,10 +115,39 @@ function rtQC_display() gui_data.QCtgroup.SelectionChangedFcn = @QCtabChangedCallback; gui_data.pb_open_qc_post.Callback = @generateReport; - +gui_data.pb_get_physio_batch.Callback = @physioBatch; + +% settings callback +gui_data.edit_physio_save_dir.Callback = @editphysio_save_dir ; +gui_data.edit_log_files_sampling_interval.Callback = @editlog_files_sampling_interval ; +gui_data.edit_log_files_relative_start_acquisition.Callback = @editlog_files_relative_start_acquisition ; +gui_data.edit_log_files_align_scan.Callback = @editlog_files_align_scan ; +gui_data.edit_sqpar_NslicesPerBeat.Callback = @editsqpar_NslicesPerBeat ; +gui_data.edit_sqpar_time_slice_to_slice.Callback = @editsqpar_time_slice_to_slice ; +gui_data.edit_sqpar_Nprep.Callback = @editsqpar_Nprep ; +gui_data.edit_sync_nominal.Callback = @editsync_nominal ; +gui_data.edit_auto_matched_min.Callback = @editauto_matched_min ; +gui_data.edit_auto_matched_file.Callback = @editauto_matched_file ; +gui_data.edit_posthoc_cpulse_select_off.Callback = @editposthoc_cpulse_select_off ; +gui_data.edit_model_output_multiple_regressors.Callback = @editmodel_output_multiple_regressors ; +gui_data.edit_model_output_physio.Callback = @editmodel_output_physio ; +gui_data.edit_model_orthogonalise.Callback = @editmodel_orthogonalise ; +gui_data.edit_model_censor_unreliable_recording_intervals.Callback = @editmodel_censor_unreliable_recording_intervals ; +gui_data.edit_order_c.Callback = @editorder_c; +gui_data.edit_order_r.Callback = @editorder_r ; +gui_data.edit_order_cr.Callback = @editorder_cr ; +gui_data.edit_rvt_no.Callback = @editrvt_no ; +gui_data.edit_hrv_no.Callback = @edithrv_no ; +gui_data.edit_noise_rois_no.Callback = @editnoise_rois_no ; +gui_data.edit_movement_no.Callback = @editmovement_no ; +gui_data.edit_other_no.Callback = @editother_no ; +gui_data.edit_verbose_level.Callback = @editverbose_level ; +gui_data.edit_verbose_fig_output_file.Callback = @editverbose_fig_output_file ; +gui_data.edit_verbose_use_tabs.Callback = @editverbose_use_tabs ; + +gui_data.export_settings.Callback = @export_settings; set(findall(fig, '-property', 'Interruptible'), 'Interruptible', 'on') - % Make figure visible after normalizing units set(findall(fig, '-property', 'Units'), 'Units', 'Normalized') fig.Visible = 'on'; @@ -205,6 +236,147 @@ function gotoTab2(hObject,eventdata) guidata(fig,gui_data) end +%%%% start settings tab functions + +function export_settings(hObject,eventdata) + fig = ancestor(hObject,'figure'); + gui_data = guidata(fig); + panels = {'general', 'physio', 'new'}; + labels = {}; + values = {}; + counter = 1; + for i=1:length(panels) + children = eval(['gui_data.panel_set_' panels{i} '.Children']); + for j=1:length(children) + if strcmp(children(j).Style, 'text') + name = children(j).String; + name = strrep(name, '.', '_'); + matching_edit = eval(['gui_data.edit_' name '.String']); + labels{counter} = [panels{i} '_' name]; + values{counter} = matching_edit; + counter = counter+1; + end + end + end + % save to json file + enc = jsonencode(containers.Map(labels, values)); + fid = fopen([gui_data.qc_out_dir filesep 'settings.json'], 'wt'); + fprintf(fid, '%s', enc); + fclose(fid); + msgbox(['Saved settings in: ' gui_data.qc_out_dir], 'Success'); +end + +function editphysio_save_dir (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editlog_files_sampling_interval (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editlog_files_relative_start_acquisition (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editlog_files_align_scan (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editsqpar_NslicesPerBeat (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editsqpar_time_slice_to_slice (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editsqpar_Nprep (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editsync_nominal (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editauto_matched_min (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editauto_matched_file (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editposthoc_cpulse_select_off (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editmodel_output_multiple_regressors (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editmodel_output_physio (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editmodel_orthogonalise (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editmodel_censor_unreliable_recording_intervals (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editorder_c (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editorder_r (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editorder_cr (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editrvt_no (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function edithrv_no (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editnoise_rois_no (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editmovement_no (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editother_no (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editverbose_level (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end +function editverbose_fig_output_file(hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +txt = get(hObject, 'String'); +gui_data.editverbose_fig_output_file.String = txt; +% assignin('base', 'gui_data', gui_data) +guidata(fig,gui_data) +end +function editverbose_use_tabs (hObject,eventdata) +fig = ancestor(hObject,'figure'); +gui_data = guidata(fig); +end + +%%%%% end setting tab functions + function editFDthreshold(hObject,eventdata) fig = ancestor(hObject,'figure'); @@ -226,7 +398,6 @@ function editFDthreshold(hObject,eventdata) guidata(fig,gui_data) end - function editSPMdir(hObject,eventdata) fig = ancestor(hObject,'figure'); gui_data = guidata(fig); @@ -457,6 +628,7 @@ function calcAndDispMI(hObject,eventdata) return; end + msgbox('Functionality to be added: plot 2D histogram and display mutual information measure'); % slice_nr = 20; % im1 = spm_read_vols(spm_vol(gui_data.im1_dqchecks_fn)); @@ -492,6 +664,8 @@ function runPreProc(hObject,eventdata) gui_data.functional0_fn = [gui_data.functional4D_fn ',1']; % Preprocess structural and f0 images [d, f, e] = fileparts(gui_data.structural_fn); +disp(d); +disp(f); if exist([d filesep 'rc1' f e], 'file') % 1) If preprocessing has already been done, assign variables gui_data.preProc_status = 1; @@ -1099,6 +1273,9 @@ function drawTHEPLOT(fig) % guidata(fig, gui_data); end +function setVendor(hObject,eventdata) + +end function setDim(hObject,eventdata) @@ -1215,7 +1392,6 @@ function drawBrains(fig) end - % Offline tab functions function runOfflineQC(hObject,eventdata) @@ -1424,7 +1600,7 @@ function generateReport(hObject,eventdata) dt_str = [num2str(Y) num2str(MO) num2str(D) num2str(H) num2str(MI) num2str(round(S))]; t = datestr(dt); log_name = [gui_data.subject_id '_' dt_str '.html']; -fid = fopen(log_name,'a'); +fid = fopen([gui_data.qc_out_dir filesep log_name],'a'); fprintf(fid, '

Log

'); fprintf(fid, ['\n
Subject: ' gui_data.subject_id]); fprintf(fid, ['\n
Date/time: ' t]); @@ -1450,10 +1626,43 @@ function generateReport(hObject,eventdata) fprintf(fid, '\n
no picture
' ); fprintf(fid, '\n
no picture
' ); fprintf(fid, '\n
no picture
' ); +% add physio plots +phys_dir = gui_data.edit_physio_save_dir.String; +if exist([gui_data.qc_out_dir filesep phys_dir]) + fprintf(fid, '

Physio processing plots

'); + figure_file_gen = split(gui_data.edit_verbose_fig_output_file.String, '.'); + figure_file = cell2mat(join([figure_file_gen(1) '_05.' figure_file_gen(2)], '')); + img_to_plot = [phys_dir '/' figure_file]; % possible option (this works if in settings tab just allow input of directory name + % add other figures + fprintf(fid, sprintf('\n
no picture
', img_to_plot)); +end fclose(fid); url = ['file:///' gui_data.qc_out_dir filesep log_name]; -web(url, '-browser') +web(url, '-browser'); + +end +function physioBatch(hObject,eventdata) + + % get guidata + fig = ancestor(hObject,'figure'); + gui_data = guidata(fig); + + vendor = gui_data.popup_setVendor.String(gui_data.popup_setVendor.Value); + if strcmp(vendor, 'select vendor...') + errordlg('First set vendor in drop down menu to the right of the button.'); + end + + %% read nr slices and nr scans from nifti header: + % hdr = spm_vol('C:\Users\nwiedemann\Downloads\rtQC_sample_data\rtQC_sample_data\sub-opennft\fanon-0007.nii') + % nr_scans = length(hdr) + % nr_slices = hdr(1).dim(3) + %% + % default path for cardiac and respiratory inputs: + default_path = gui_data.data_dir; + % default_path = 'C:\Users\nwiedemann\Downloads\tapas\tapas\misc\example\PhysIO'; + + wrapper_physiobatch(char(vendor), gui_data.qc_out_dir, default_path, gui_data); end function showFDoutliers(hObject,eventdata) diff --git a/api-matlab/rtQC_display_setup.m b/api-matlab/rtQC_display_setup.m index 184bfe7..1f53e4b 100644 --- a/api-matlab/rtQC_display_setup.m +++ b/api-matlab/rtQC_display_setup.m @@ -34,6 +34,7 @@ gui_data.tab_pre = uitab('Parent', gui_data.tgroup, 'Title', 'Pre QC'); gui_data.tab_online = uitab('Parent', gui_data.tgroup, 'Title', 'Online QC'); gui_data.tab_post = uitab('Parent', gui_data.tgroup, 'Title', 'Post QC'); +gui_data.tab_settings = uitab('Parent', gui_data.tgroup, 'Title', 'Settings'); % Set variable values gui_data.current_dir = mfilename('fullpath'); @@ -964,7 +965,7 @@ 'Style', 'push',... 'String', 'Show FD outliers', ... 'Units', 'Normalized',... - 'Position', [0.8 0.7 0.18 0.3],... + 'Position', [0.8 0.795 0.18 0.225],... 'CallBack', @showFDoutliers, ... 'UserData', 0,... 'fontsize', gui_data.button_font_size); @@ -972,7 +973,7 @@ 'Style', 'push',... 'String', 'Run Offline QC', ... 'Units', 'Normalized',... - 'Position', [0.8 0.38 0.18 0.3],... + 'Position', [0.8 0.55 0.18 0.225],... 'CallBack', @runOfflineQC, ... 'UserData', 0,... 'fontsize', gui_data.button_font_size); @@ -980,10 +981,26 @@ 'Style', 'push',... 'String', 'Generate report', ... 'Units', 'Normalized',... - 'Position', [0.8 0.06 0.18 0.3],... + 'Position', [0.8 0.06 0.18 0.225],... 'CallBack', @generateReport, ... 'UserData', 0,... 'fontsize', gui_data.button_font_size); +gui_data.pb_get_physio_batch = uicontrol('Parent', gui_data.panel_rtsummary,... + 'Style', 'push',... + 'String', 'Get physio batch', ... + 'Units', 'Normalized',... + 'Position', [0.8 0.305 0.1 0.225],... + 'CallBack', @physioBatch, ... + 'UserData', 0,... + 'fontsize', gui_data.button_font_size); +gui_data.popup_setVendor = uicontrol('Parent', gui_data.panel_rtsummary,... + 'Style', 'popup',... + 'String', {'select vendor...', 'BIDS','Philips','Biopac_Txt','GE','Siemens','Siemens_HCP','Siemens_Tics'},... + 'Units', 'Normalized',... + 'Position', [0.91 0.305 0.07 0.225],... + 'Value', 1,... + 'Callback', @setVendor,... + 'fontsize', gui_data.button_font_size); % Offline summary panel UIcontrols @@ -1072,7 +1089,9 @@ % set(get(gui_data.panel_qcsummaryR,'Children'),'Enable','off') % set(children(strcmpi ( get (children,'Type'),'UIControl')),'enable','off') - +%% --------------- Create UIcontrols for PRE QC tab -------------------- %% +settings_tab; +%% % Save GUI data guidata(fig, gui_data) diff --git a/api-matlab/settings_tab.m b/api-matlab/settings_tab.m new file mode 100644 index 0000000..8f236b1 --- /dev/null +++ b/api-matlab/settings_tab.m @@ -0,0 +1,470 @@ +%% --------------- Create UIcontrols for Settings tab -------------------- %% +% Create panels +gui_data.panel_set_general = uipanel('Parent', gui_data.tab_settings,... + 'Title','1. General parameters',... + 'Position',[.02 .8 .96 .18],... + 'fontweight', 'bold',... + 'fontsize', gui_data.standard_font_size); +gui_data.panel_set_physio = uipanel('Parent', gui_data.tab_settings,... + 'Title','2. Physio processing parameters',... + 'Position',[.02 .01 .47 .77],... + 'fontweight', 'bold',... + 'fontsize', gui_data.standard_font_size); +gui_data.panel_set_new = uipanel('Parent', gui_data.tab_settings,... + 'Title','Other parameters',... + 'Position',[.51 .01 .47 .77],... + 'fontweight', 'bold',... + 'fontsize', gui_data.standard_font_size); + +% Export Settings button: +gui_data.export_settings = uicontrol('Parent', gui_data.panel_set_general,... + 'Style', 'push',... + 'String', 'Export Settings', ... + 'Units', 'Normalized',... + 'Position', [0.8 0.06 0.18 0.225],... + 'CallBack', @export_settings, ... + 'UserData', 0,... + 'fontsize', gui_data.button_font_size); + +%% PHYSIO PANEL SETTINGS FIELDS +gui_data.physio_save_dir = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','physio.save_dir ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.05 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_physio_save_dir = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','physio_out',... +'Units', 'Normalized',... +'Position', [ 0.25 0.05 0.2 0.025 ], ... +'CallBack', @editphysio_save_dir , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.log_files_sampling_interval = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','log_files.sampling_interval ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.085 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_log_files_sampling_interval = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.085 0.2 0.025 ], ... +'CallBack', @editlog_files_sampling_interval , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.log_files_relative_start_acquisition = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','log_files.relative_start_acquisition ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.12000000000000001 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_log_files_relative_start_acquisition = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','0',... +'Units', 'Normalized',... +'Position', [ 0.25 0.12000000000000001 0.2 0.025 ], ... +'CallBack', @editlog_files_relative_start_acquisition , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.log_files_align_scan = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','log_files.align_scan ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.155 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_log_files_align_scan = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','last',... +'Units', 'Normalized',... +'Position', [ 0.25 0.155 0.2 0.025 ], ... +'CallBack', @editlog_files_align_scan , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.sqpar_NslicesPerBeat = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','sqpar.NslicesPerBeat ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.19 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_sqpar_NslicesPerBeat = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.19 0.2 0.025 ], ... +'CallBack', @editsqpar_NslicesPerBeat , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.sqpar_time_slice_to_slice = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','sqpar.time_slice_to_slice ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.22499999999999998 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_sqpar_time_slice_to_slice = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.22499999999999998 0.2 0.025 ], ... +'CallBack', @editsqpar_time_slice_to_slice , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.sqpar_Nprep = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','sqpar.Nprep ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.26 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_sqpar_Nprep = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.26 0.2 0.025 ], ... +'CallBack', @editsqpar_Nprep , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.sync_nominal = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','sync.nominal ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.295 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_sync_nominal = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.295 0.2 0.025 ], ... +'CallBack', @editsync_nominal , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.auto_matched_min = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','auto_matched.min ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.33 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_auto_matched_min = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','0.4',... +'Units', 'Normalized',... +'Position', [ 0.25 0.33 0.2 0.025 ], ... +'CallBack', @editauto_matched_min , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.auto_matched_file = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','auto_matched.file ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.365 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_auto_matched_file = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','initial_cpulse_kRpeakfile.mat',... +'Units', 'Normalized',... +'Position', [ 0.25 0.365 0.2 0.025 ], ... +'CallBack', @editauto_matched_file , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.posthoc_cpulse_select_off = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','posthoc_cpulse_select.off ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.39999999999999997 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_posthoc_cpulse_select_off = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.39999999999999997 0.2 0.025 ], ... +'CallBack', @editposthoc_cpulse_select_off , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.model_output_multiple_regressors = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','model.output_multiple_regressors ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.435 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_model_output_multiple_regressors = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','retroicor_regressors.txt',... +'Units', 'Normalized',... +'Position', [ 0.25 0.435 0.2 0.025 ], ... +'CallBack', @editmodel_output_multiple_regressors , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.model_output_physio = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','model.output_physio ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.47 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_model_output_physio = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','physio.mat',... +'Units', 'Normalized',... +'Position', [ 0.25 0.47 0.2 0.025 ], ... +'CallBack', @editmodel_output_physio , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.model_orthogonalise = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','model.orthogonalise ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.505 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_model_orthogonalise = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','none',... +'Units', 'Normalized',... +'Position', [ 0.25 0.505 0.2 0.025 ], ... +'CallBack', @editmodel_orthogonalise , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.model_censor_unreliable_recording_intervals = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','model.censor_unreliable_recording_intervals ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.54 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_model_censor_unreliable_recording_intervals = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','false',... +'Units', 'Normalized',... +'Position', [ 0.25 0.54 0.2 0.025 ], ... +'CallBack', @editmodel_censor_unreliable_recording_intervals , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.order_c = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','order.c ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.5750000000000001 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_order_c = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','3',... +'Units', 'Normalized',... +'Position', [ 0.25 0.5750000000000001 0.2 0.025 ], ... +'CallBack', @editorder_c , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.order_r = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','order.r ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.6100000000000001 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_order_r = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','4',... +'Units', 'Normalized',... +'Position', [ 0.25 0.6100000000000001 0.2 0.025 ], ... +'CallBack', @editorder_r , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.order_cr = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','order.cr ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.645 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_order_cr = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','1',... +'Units', 'Normalized',... +'Position', [ 0.25 0.645 0.2 0.025 ], ... +'CallBack', @editorder_cr , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.rvt_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','rvt.no ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.68 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_rvt_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.68 0.2 0.025 ], ... +'CallBack', @editrvt_no , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.hrv_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','hrv.no ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.7150000000000001 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_hrv_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.7150000000000001 0.2 0.025 ], ... +'CallBack', @edithrv_no , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.noise_rois_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','noise_rois.no ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.75 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_noise_rois_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.75 0.2 0.025 ], ... +'CallBack', @editnoise_rois_no , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.movement_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','movement.no ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.785 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_movement_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.785 0.2 0.025 ], ... +'CallBack', @editmovement_no , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.other_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','other.no ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.8200000000000001 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_other_no = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','[]',... +'Units', 'Normalized',... +'Position', [ 0.25 0.8200000000000001 0.2 0.025 ], ... +'CallBack', @editother_no , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.verbose_level = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','verbose.level ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.8550000000000001 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_verbose_level = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','2',... +'Units', 'Normalized',... +'Position', [ 0.25 0.8550000000000001 0.2 0.025 ], ... +'CallBack', @editverbose_level , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.verbose_fig_output_file = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','verbose.fig_output_file ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.89 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_verbose_fig_output_file = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','physIO_figures.png',... +'Units', 'Normalized',... +'Position', [ 0.25 0.89 0.2 0.025 ], ... +'CallBack', @editverbose_fig_output_file , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.verbose_use_tabs = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','text',... +'String','verbose.use_tabs ',... +'Units', 'Normalized',... +'Position', [ 0.02 0.925 0.2 0.025 ], ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); + +gui_data.edit_verbose_use_tabs = uicontrol('Parent', gui_data.panel_set_physio,... +'Style','edit',... +'String','false',... +'Units', 'Normalized',... +'Position', [ 0.25 0.925 0.2 0.025 ], ... +'CallBack', @editverbose_use_tabs , ... +'HorizontalAlignment', 'left',... +'fontsize', gui_data.small_font_size); \ No newline at end of file diff --git a/api-matlab/wrapper_physiobatch.m b/api-matlab/wrapper_physiobatch.m new file mode 100644 index 0000000..66e9200 --- /dev/null +++ b/api-matlab/wrapper_physiobatch.m @@ -0,0 +1,145 @@ +function matlabbatch = wrapper_physiobatch(data_vendor, out_dir, default_path, gui_data) + spm_jobman('initcfg'); + global defaults + defaults.stats.maxmem = 2^30; + matlabbatch = {}; + + matlabbatch{1}.spm.tools.physio.log_files.vendor = data_vendor; + +% FOR TESTING: +% EXAMPLES IN C:\Users\nwiedemann\Downloads\tapas\tapas\misc\example\PhysIO +% matlabbatch{1}.spm.tools.physio.log_files.cardiac = {'C:\Users\nwiedemann\Downloads\tapas\tapas\misc\example\PhysIO\BIDS\CPULSE3T\sub-s998_task-random_run-99_physio.tsv'}; +% matlabbatch{1}.spm.tools.physio.log_files.respiration = {''}; +% matlabbatch{1}.spm.tools.physio.log_files.scan_timing = {''}; + % different vendor options require different file input + if strcmp(data_vendor, 'BIDS') + [tsv_filename, file_path] = uigetfile('*.tsv*','Specify tsv/tsv.gz file with ECG data',default_path); + matlabbatch{1}.spm.tools.physio.log_files.cardiac = {fullfile(file_path, tsv_filename)}; + matlabbatch{1}.spm.tools.physio.log_files.respiration = {''}; + matlabbatch{1}.spm.tools.physio.log_files.scan_timing = {''}; + elseif strcmp(data_vendor, 'Philips') + [scanphyslog, file_path] = uigetfile('*.log','Specify SCANPHYSLOG.log file', default_path); + matlabbatch{1}.spm.tools.physio.log_files.cardiac = {fullfile(file_path, scanphyslog)}; + matlabbatch{1}.spm.tools.physio.log_files.respiration = {fullfile(file_path, scanphyslog)}; + matlabbatch{1}.spm.tools.physio.log_files.scan_timing = {fullfile(file_path, scanphyslog)}; % or empty? + elseif strcmp(data_vendor, 'Biopac_Txt') + [scanphyslog, file_path] = uigetfile('*.txt','Specify biopac data export txt file', default_path); + % scanphyslog = input('Specify file path to the biopac data export txt file', 's'); + matlabbatch{1}.spm.tools.physio.log_files.cardiac = {fullfile(file_path, scanphyslog)}; + matlabbatch{1}.spm.tools.physio.log_files.respiration = {fullfile(file_path, scanphyslog)}; + matlabbatch{1}.spm.tools.physio.log_files.scan_timing = {fullfile(file_path, scanphyslog)}; + elseif strcmp(data_vendor, 'GE') + [cardiac, file_path1] = uigetfile('*.*','Specify cardiac file (starting with ECGData_)', default_path); + % cardiac = input('Specify file path to the cardiac file (in template starting with ECGData_)', 's'); + [respiration, file_path2] = uigetfile('*.*','Specify respiration file (starting with RespData_)', default_path); + % respiration = input('Specify file path to the respiration file (in template starting with RespData_)', 's'); + matlabbatch{1}.spm.tools.physio.log_files.cardiac = {fullfile(file_path1, cardiac)}; + matlabbatch{1}.spm.tools.physio.log_files.respiration = {fullfile(file_path2, respiration)}; + matlabbatch{1}.spm.tools.physio.log_files.scan_timing = {''}; + elseif strcmp(data_vendor, 'Siemens_HCP') + [physiolog, file_path] = uigetfile('*.*','Specify physio data file (sth like tfMRI_MOTOR_LR_Physio_log.txt)', default_path); + matlabbatch{1}.spm.tools.physio.log_files.cardiac = {fullfile(file_path, physiolog)}; + matlabbatch{1}.spm.tools.physio.log_files.respiration = {fullfile(file_path, physiolog)}; + matlabbatch{1}.spm.tools.physio.log_files.scan_timing = {''}; + elseif strcmp(data_vendor, 'Siemens') + [physiolog, file_path] = uigetfile('*.ecg','Specify physio data file (siemens_PAV.ecg)', default_path); + matlabbatch{1}.spm.tools.physio.log_files.cardiac = {fullfile(file_path, physiolog)}; + matlabbatch{1}.spm.tools.physio.log_files.respiration = {''}; + matlabbatch{1}.spm.tools.physio.log_files.scan_timing = {''}; + elseif strcmp(data_vendor, 'Siemens_Tics') + [cardiac, file_path1] = uigetfile('*.log','Specify cardiac file (..._PULS.log)', default_path); + [respiration, file_path2] = uigetfile('*.log','Specify respiration file (..._RESP.log)', default_path); + [timing, file_path3] = uigetfile('*.log','Specify scan timing file (..._Info.log)', default_path); + matlabbatch{1}.spm.tools.physio.log_files.cardiac = {fullfile(file_path1, cardiac)}; + matlabbatch{1}.spm.tools.physio.log_files.respiration = {fullfile(file_path2, respiration)}; + matlabbatch{1}.spm.tools.physio.log_files.scan_timing = {fullfile(file_path3, timing)}; + else + ME = MException('error:wronginput', 'Wrong vendor specified. Please specify one of BIDS, Philips, Biopac_Txt, GE, Siemens, Siemens_HCP, Siemens_Tics'); + throw(ME); + end + + % read scan parameters + input_params = inputdlg({'Number of scans:','Number of slices:','Number of dummies:', 'TR:', 'Onset slice:', 'ECG/PPU:'}, 'Parameters for physio', 1, {'30', '40', '0', '2', '1', 'ECG'}); + + % scan parameters --> input + matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.Nslices = str2double(input_params{2}); + matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.TR = str2double(input_params{4}); + matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.Ndummies = str2double(input_params{3}); + matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.Nscans = str2double(input_params{1}); + matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.onset_slice = str2double(input_params{5}); + % input modality + matlabbatch{1}.spm.tools.physio.preproc.cardiac.modality = input_params{6}; + + % parameters from settings tab + matlabbatch{1}.spm.tools.physio.save_dir = {[out_dir filesep gui_data.edit_physio_save_dir.String]}; + matlabbatch{1}.spm.tools.physio.log_files.sampling_interval = eval(gui_data.edit_log_files_sampling_interval.String); + matlabbatch{1}.spm.tools.physio.log_files.relative_start_acquisition = str2double(gui_data.edit_log_files_relative_start_acquisition.String); + matlabbatch{1}.spm.tools.physio.log_files.align_scan = gui_data.edit_log_files_align_scan.String; + matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.NslicesPerBeat = eval(gui_data.edit_sqpar_NslicesPerBeat.String); + matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.time_slice_to_slice = eval(gui_data.edit_sqpar_time_slice_to_slice.String); + matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.Nprep = eval(gui_data.edit_sqpar_Nprep.String); + matlabbatch{1}.spm.tools.physio.scan_timing.sync.nominal = struct(eval(gui_data.edit_sync_nominal.String)); + matlabbatch{1}.spm.tools.physio.preproc.cardiac.initial_cpulse_select.auto_matched.min = str2double(gui_data.edit_auto_matched_min.String); + matlabbatch{1}.spm.tools.physio.preproc.cardiac.initial_cpulse_select.auto_matched.file = gui_data.edit_auto_matched_file.String; + matlabbatch{1}.spm.tools.physio.preproc.cardiac.posthoc_cpulse_select.off = struct(eval(gui_data.edit_posthoc_cpulse_select_off.String)); + matlabbatch{1}.spm.tools.physio.model.output_multiple_regressors = gui_data.edit_model_output_multiple_regressors.String; + matlabbatch{1}.spm.tools.physio.model.output_physio = gui_data.edit_model_output_physio.String; + matlabbatch{1}.spm.tools.physio.model.orthogonalise = gui_data.edit_model_orthogonalise.String; + matlabbatch{1}.spm.tools.physio.model.censor_unreliable_recording_intervals = strcmpi(gui_data.edit_model_censor_unreliable_recording_intervals.String, 'true'); + matlabbatch{1}.spm.tools.physio.model.retroicor.yes.order.c = str2double(gui_data.edit_order_c.String); + matlabbatch{1}.spm.tools.physio.model.retroicor.yes.order.r = str2double(gui_data.edit_order_r.String); + matlabbatch{1}.spm.tools.physio.model.retroicor.yes.order.cr = str2double(gui_data.edit_order_cr.String); + matlabbatch{1}.spm.tools.physio.model.rvt.no = struct(eval(gui_data.edit_rvt_no.String)); + matlabbatch{1}.spm.tools.physio.model.hrv.no = struct(eval(gui_data.edit_hrv_no.String)); + matlabbatch{1}.spm.tools.physio.model.noise_rois.no = struct(eval(gui_data.edit_noise_rois_no.String)); + matlabbatch{1}.spm.tools.physio.model.movement.no = struct(eval(gui_data.edit_movement_no.String)); + matlabbatch{1}.spm.tools.physio.model.other.no = struct(eval(gui_data.edit_other_no.String)); + matlabbatch{1}.spm.tools.physio.verbose.level = str2double(gui_data.edit_verbose_level.String); + matlabbatch{1}.spm.tools.physio.verbose.fig_output_file = gui_data.edit_verbose_fig_output_file.String; + matlabbatch{1}.spm.tools.physio.verbose.use_tabs = strcmpi(gui_data.edit_verbose_use_tabs.String, 'true'); + + % check if physio is installed: + pathCell = regexp(path, pathsep, 'split'); + onPath = any(contains(pathCell, 'PhysIO')); + if onPath + spm_jobman('run',matlabbatch); + else + % save batch if physio is not installed + save([out_dir filesep 'physio_batch'], 'matlabbatch'); + % current out_dir: C:\Users\nwiedemann\Downloads\rtQC_sample_data\rtQC_sample_data\sub-hcp\rtQC_output + % msgbox(['Saved matlabbatch in: ' out_dir], 'Success'); + errordlg('Batch was saved to rtQC_output directory. \n \n Plots could not be created because the Physio toolbox is not installed or not added to path. Follow installation instructions on https://github.com/translationalneuromodeling/tapas.'); + end +end + +% matlabbatch{1}.spm.tools.physio.save_dir = {[out_dir filesep 'physio_out']}; +% +% scan parameters - defaults +% matlabbatch{1}.spm.tools.physio.log_files.sampling_interval = []; +% matlabbatch{1}.spm.tools.physio.log_files.relative_start_acquisition = 0; +% matlabbatch{1}.spm.tools.physio.log_files.align_scan = 'last'; % sometimes first, sometimes last +% matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.NslicesPerBeat = []; +% matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.time_slice_to_slice = []; % eval(gui_data.edit_sqpar_time_slice_to_slice.String); +% +% template +% matlabbatch{1}.spm.tools.physio.scan_timing.sqpar.Nprep = []; +% matlabbatch{1}.spm.tools.physio.scan_timing.sync.nominal = struct([]); +% matlabbatch{1}.spm.tools.physio.preproc.cardiac.initial_cpulse_select.auto_matched.min = 0.4; +% matlabbatch{1}.spm.tools.physio.preproc.cardiac.initial_cpulse_select.auto_matched.file = 'initial_cpulse_kRpeakfile.mat'; +% matlabbatch{1}.spm.tools.physio.preproc.cardiac.posthoc_cpulse_select.off = struct([]); +% matlabbatch{1}.spm.tools.physio.model.output_multiple_regressors = 'retroicor_regressors.txt'; +% matlabbatch{1}.spm.tools.physio.model.output_physio = 'physio.mat'; +% matlabbatch{1}.spm.tools.physio.model.orthogonalise = 'none'; +% matlabbatch{1}.spm.tools.physio.model.censor_unreliable_recording_intervals = false; +% matlabbatch{1}.spm.tools.physio.model.retroicor.yes.order.c = 3; +% matlabbatch{1}.spm.tools.physio.model.retroicor.yes.order.r = 4; % str2double(gui_data.edit_order_r.String); +% matlabbatch{1}.spm.tools.physio.model.retroicor.yes.order.cr = 1; +% matlabbatch{1}.spm.tools.physio.model.rvt.no = struct([]); +% matlabbatch{1}.spm.tools.physio.model.hrv.no = struct([]); +% matlabbatch{1}.spm.tools.physio.model.noise_rois.no = struct([]); +% matlabbatch{1}.spm.tools.physio.model.movement.no = struct([]); +% matlabbatch{1}.spm.tools.physio.model.other.no = struct([]); +% matlabbatch{1}.spm.tools.physio.verbose.level = 2; +% matlabbatch{1}.spm.tools.physio.verbose.fig_output_file = ''; % gui_data.edit_verbose_fig_output_file.String; +% matlabbatch{1}.spm.tools.physio.verbose.use_tabs = false; +% \ No newline at end of file