From 9673df5aa82f392d403866fecedcf861893f043f Mon Sep 17 00:00:00 2001 From: Kwok-Shing Chan Date: Thu, 8 Dec 2022 22:30:10 +0100 Subject: [PATCH 1/2] provide option for choosing GPU processing for NDI and FANSI --- README.md | 0 addons/qsm/ndi/NDI.m | 4 +++- addons/qsm/ndi/Wrapper_QSM_NDI.m | 5 ++++- addons/qsm/ndi/get_set_qsm_ndi.m | 18 +++++++++++++++--- addons/qsm/ndi/sepia_handle_panel_qsm_NDI.m | 9 +++++++++ gui_func/callback/config/get_set_qsm_fansi.m | 14 +++++++++++++- .../QSM_method/sepia_handle_panel_qsm_FANSI.m | 8 ++++++++ misc/qsm_algorithm/FANSI/FANSI_4sepia_v3.m | 3 +++ sepia_universal_variables.m | 2 +- wrapper/WrapperQSM/Wrapper_QSM_FANSI.m | 4 ++++ wrapper/parser/deprecated/parse_varargin_NDI.m | 6 +++++- 11 files changed, 65 insertions(+), 8 deletions(-) mode change 100644 => 100755 README.md mode change 100644 => 100755 sepia_universal_variables.m diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/addons/qsm/ndi/NDI.m b/addons/qsm/ndi/NDI.m index c3b7d12..f0f7789 100644 --- a/addons/qsm/ndi/NDI.m +++ b/addons/qsm/ndi/NDI.m @@ -42,7 +42,7 @@ matrixSize = size(mask); % check optional input and set default -[tolerance,stepSize,iteration,weight,b0dir] = parse_varargin_NDI(varargin); +[tolerance,stepSize,iteration,weight,b0dir,isGPU] = parse_varargin_NDI(varargin); % verbose disp('Non-linear Dipole Inversion (NDI)'); @@ -67,6 +67,7 @@ chi = zeros(matrixSize, 'like',localField); grad_prev = zeros(matrixSize, 'like',localField); +if isGPU try % add gpu compatibility weight = gpuArray(weight); @@ -79,6 +80,7 @@ catch isGPU = false; end +end % case of one B0 direction tic diff --git a/addons/qsm/ndi/Wrapper_QSM_NDI.m b/addons/qsm/ndi/Wrapper_QSM_NDI.m index 7966393..e40d6cd 100644 --- a/addons/qsm/ndi/Wrapper_QSM_NDI.m +++ b/addons/qsm/ndi/Wrapper_QSM_NDI.m @@ -20,6 +20,7 @@ % Date created: 8 March 2020 % Date modified: 16 August 2021 % Date modified: 20 Feb 2022 (v1.0) +% Date modified: 8 Dec 2022 (v1.2.2) % % function [chi] = Wrapper_QSM_NDI(localField,mask,matrixSize,voxelSize,algorParam, headerAndExtraData) @@ -31,6 +32,7 @@ tol = algorParam.qsm.tol; stepSize = algorParam.qsm.stepSize; maxiter = algorParam.qsm.maxiter; +isGPU = algorParam.qsm.isGPU; % get extra data such as magnitude/weights/B0 direction/TE/etc. headerAndExtraData = check_and_set_SEPIA_header_data(headerAndExtraData); @@ -84,7 +86,7 @@ localField = localField/(b0*gyro); chi = NDI(localField,mask,voxelSize,'b0dir',b0dir,'weight',wmap,... - 'iteration',maxiter,'stepsize',stepSize,'tol',tol); + 'iteration',maxiter,'stepsize',stepSize,'tol',tol,'isGPU',isGPU); end @@ -96,5 +98,6 @@ try algorParam2.qsm.tol = algorParam.qsm.tol; catch; algorParam2.qsm.tol = 1; end try algorParam2.qsm.stepSize = algorParam.qsm.stepSize; catch; algorParam2.qsm.stepSize = 1; end try algorParam2.qsm.maxiter = algorParam.qsm.maxiter; catch; algorParam2.qsm.maxiter = 200; end +try algorParam2.qsm.isGPU = algorParam.qsm.isGPU; catch; algorParam2.qsm.isGPU = false; end end \ No newline at end of file diff --git a/addons/qsm/ndi/get_set_qsm_ndi.m b/addons/qsm/ndi/get_set_qsm_ndi.m index 73b1038..99438f4 100644 --- a/addons/qsm/ndi/get_set_qsm_ndi.m +++ b/addons/qsm/ndi/get_set_qsm_ndi.m @@ -20,21 +20,27 @@ function get_set_qsm_ndi(h,mode,input) str_pattern = {'.qsm.tol',... '.qsm.maxiter',... - '.qsm.stepSize'}; + '.qsm.stepSize',... + '.qsm.isGPU'}; action_handle = {h.qsm.NDI.edit.tol,... h.qsm.NDI.edit.maxIter,... - h.qsm.NDI.edit.stepSize}; + h.qsm.NDI.edit.stepSize,... + h.qsm.NDI.checkbox.isGPU}; switch lower(mode) case 'set' fid = input; - for k = 1:length(action_handle) + for k = 1:3 fprintf(fid,'algorParam%s = %s ;\n' ,str_pattern{k},get(action_handle{k}, 'String')); end + % is GPU + k = k+1; + fprintf(fid,'algorParam%s = %i ;\n' ,str_pattern{k},get(action_handle{k}, 'Value')); + case 'get' config_txt = input; @@ -45,5 +51,11 @@ function get_set_qsm_ndi(h,mode,input) val = get_num_as_string(config_txt, pattern_curr, '=', ';'); set_non_nan_value(action_handle{k},'String',val) end + + % isGPU + k = k+1; + pattern_curr = str_pattern{k}; + val = get_num_as_string(config_txt, pattern_curr, '=', ';'); + set_non_nan_value(action_handle{k}, 'Value', str2double(val)) end \ No newline at end of file diff --git a/addons/qsm/ndi/sepia_handle_panel_qsm_NDI.m b/addons/qsm/ndi/sepia_handle_panel_qsm_NDI.m index 7c59e70..8a88cfb 100644 --- a/addons/qsm/ndi/sepia_handle_panel_qsm_NDI.m +++ b/addons/qsm/ndi/sepia_handle_panel_qsm_NDI.m @@ -24,6 +24,7 @@ defaultTol = 1; defaultMaxIter = 200; defaultStepSize = 1; +defaultIsGPU = 0; %% Tooltips tooltip.qsm.NDI.tol = 'Relative tolerance to stop NDI iteration.'; @@ -65,6 +66,14 @@ % text|edit field pair: gradient step size [h.qsm.NDI.text.stepSize,h.qsm.NDI.edit.stepSize] = sepia_construct_text_edit(... panelParent,'Step size:', defaultStepSize, [left(1) bottom(3) width height], wratio); + + % col 1, row 4 + % checkbox field pair: isGPU + h.qsm.NDI.checkbox.isGPU = uicontrol('Parent',h.qsm.panel.NDI,'Style','checkbox',... + 'String','Enable GPU',... + 'units','normalized','position',[left(1) bottom(4) width height],... + 'HorizontalAlignment','left',... + 'backgroundcolor',get(h.fig,'color')); %% set tooltips diff --git a/gui_func/callback/config/get_set_qsm_fansi.m b/gui_func/callback/config/get_set_qsm_fansi.m index a3900cf..5f00cc3 100644 --- a/gui_func/callback/config/get_set_qsm_fansi.m +++ b/gui_func/callback/config/get_set_qsm_fansi.m @@ -26,6 +26,7 @@ function get_set_qsm_fansi(h,mode,input) '.qsm.solver',... '.qsm.constraint',... '.qsm.gradient_mode',... + '.qsm.isGPU',... '.qsm.isWeakHarmonic',... '.qsm.beta',... '.qsm.muh'}; @@ -38,6 +39,7 @@ function get_set_qsm_fansi(h,mode,input) h.qsm.FANSI.popup.solver,... h.qsm.FANSI.popup.constraints,... h.qsm.FANSI.popup.gradientMode,... + h.qsm.FANSI.checkbox.isGPU,... h.qsm.FANSI.checkbox.isWeakHarmonic,... h.qsm.FANSI.edit.beta,... h.qsm.FANSI.edit.muh}; @@ -54,6 +56,10 @@ function get_set_qsm_fansi(h,mode,input) for k = 6:8 fprintf(fid,'algorParam%s = ''%s'' ;\n' ,str_pattern{k},action_handle{k}.String{action_handle{k}.Value,1}); end + + % is GPU + k = k+1; + fprintf(fid,'algorParam%s = %i ;\n' ,str_pattern{k},get(action_handle{k}, 'Value')); k = k+1; fprintf(fid,'algorParam%s = %i ;\n' ,str_pattern{k},get(action_handle{k}, 'Value')); @@ -110,6 +116,12 @@ function get_set_qsm_fansi(h,mode,input) case 'none' set_non_nan_value(action_handle{k},'Value',4) end + + % isGPU + k = k+1; + pattern_curr = str_pattern{k}; + val = get_num_as_string(config_txt, pattern_curr, '=', ';'); + set_non_nan_value(action_handle{k}, 'Value', str2double(val)) % weak field harmonic k = k+1; @@ -124,5 +136,5 @@ function get_set_qsm_fansi(h,mode,input) val = get_num_as_string(config_txt, pattern_curr, '=', ';'); set_non_nan_value(action_handle{k},'String',val); end - + end \ No newline at end of file diff --git a/gui_func/panel/QSM_method/sepia_handle_panel_qsm_FANSI.m b/gui_func/panel/QSM_method/sepia_handle_panel_qsm_FANSI.m index 16ab5f7..8747541 100644 --- a/gui_func/panel/QSM_method/sepia_handle_panel_qsm_FANSI.m +++ b/gui_func/panel/QSM_method/sepia_handle_panel_qsm_FANSI.m @@ -131,6 +131,14 @@ [h.qsm.FANSI.text.muh,h.qsm.FANSI.edit.muh] = sepia_construct_text_edit(... panelParent,'Harmonic consistency:', defaultMuh, [left(3) bottom(3) width height], wratio); set(h.qsm.FANSI.edit.muh, 'Enable', 'off'); + + % col 3, row 4 + % checkbox field pair: isGPU + h.qsm.FANSI.checkbox.isGPU = uicontrol('Parent',h.qsm.panel.FANSI,'Style','checkbox',... + 'String','Enable GPU',... + 'units','normalized','position',[left(3) bottom(4) width height],... + 'HorizontalAlignment','left',... + 'backgroundcolor',get(h.fig,'color')); %% set tooltips diff --git a/misc/qsm_algorithm/FANSI/FANSI_4sepia_v3.m b/misc/qsm_algorithm/FANSI/FANSI_4sepia_v3.m index 8850b06..3006184 100644 --- a/misc/qsm_algorithm/FANSI/FANSI_4sepia_v3.m +++ b/misc/qsm_algorithm/FANSI/FANSI_4sepia_v3.m @@ -85,6 +85,9 @@ if isfield(options,'update') params.tolUpdate = options.update; % This variable was renamed in release 3.0 end +if isfield(options,'isGPU') + params.isGPU = options.isGPU; % This variable available in release 3.0 +end % Launch the desired solver isNonlinear = true; % This variable was renamed in release 3.0 diff --git a/sepia_universal_variables.m b/sepia_universal_variables.m old mode 100644 new mode 100755 index 4323e1a..a5aef6e --- a/sepia_universal_variables.m +++ b/sepia_universal_variables.m @@ -14,7 +14,7 @@ % DO NOT change the order of the entities, add a new one at the end instead % %% Version -SEPIA_version = 'v1.2.1'; +SEPIA_version = 'v1.2.1 no GPU'; %% PATH SEPIA_HOME = fileparts(mfilename('fullpath')); diff --git a/wrapper/WrapperQSM/Wrapper_QSM_FANSI.m b/wrapper/WrapperQSM/Wrapper_QSM_FANSI.m index 9f32b50..7068a58 100644 --- a/wrapper/WrapperQSM/Wrapper_QSM_FANSI.m +++ b/wrapper/WrapperQSM/Wrapper_QSM_FANSI.m @@ -35,6 +35,7 @@ options.muh = algorParam.qsm.muh; alpha1 = algorParam.qsm.lambda; mu1 = algorParam.qsm.mu1; +isGPU = algorParam.qsm.isGPU; % need further decision gradient_mode = algorParam.qsm.gradient_mode; @@ -77,6 +78,8 @@ options.nonlinear = true; % non-linear end +options.isGPU = logical(isGPU); + % data % if both data are loaded if ~isempty(magn) && ~isempty(wmap) @@ -189,5 +192,6 @@ try algorParam2.qsm.isWeakHarmonic = algorParam.qsm.isWeakHarmonic;catch; algorParam2.qsm.isWeakHarmonic = 0; end try algorParam2.qsm.beta = algorParam.qsm.beta; catch; algorParam2.qsm.beta = 150; end try algorParam2.qsm.muh = algorParam.qsm.muh; catch; algorParam2.qsm.muh = 3; end +try algorParam2.qsm.isGPU = algorParam.qsm.isGPU; catch; algorParam2.qsm.isGPU = false; end end \ No newline at end of file diff --git a/wrapper/parser/deprecated/parse_varargin_NDI.m b/wrapper/parser/deprecated/parse_varargin_NDI.m index d2a2d4f..d3c5f81 100644 --- a/wrapper/parser/deprecated/parse_varargin_NDI.m +++ b/wrapper/parser/deprecated/parse_varargin_NDI.m @@ -7,7 +7,7 @@ % Date created: 5 June 2019 % Date last modified: 27 Feb 2020 (v0.8.0) % -function [tolerance,stepSize,iteration,weight,b0dir] = parse_varargin_NDI(arg) +function [tolerance,stepSize,iteration,weight,b0dir,isGPU] = parse_varargin_NDI(arg) % predefine parameters tolerance = 1; @@ -15,6 +15,7 @@ iteration = 200; b0dir = [0,0,1]; weight = []; +isGPU = false; % use user defined input if any if ~isempty(arg) @@ -34,6 +35,9 @@ if strcmpi(arg{kvar},'b0dir') b0dir = arg{kvar+1}; end + if strcmpi(arg{kvar},'isGPU') + isGPU = arg{kvar+1}; + end end end From 906611f0fac6756875d9feecfe5d8b124a54cb8f Mon Sep 17 00:00:00 2001 From: Kwok-Shing Chan <39571502+kschan0214@users.noreply.github.com> Date: Mon, 12 Dec 2022 14:04:31 +0100 Subject: [PATCH 2/2] update readme and version --- README.md | 9 ++++++--- sepia_universal_variables.m | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0106bca..5befbc1 100755 --- a/README.md +++ b/README.md @@ -41,11 +41,14 @@ If you have a more general question regarding the usage of SEPIA and/or other QS For full update log, please visit https://sepia-documentation.readthedocs.io/en/latest/getting_started/Release-note.html. -### 1.2.1 (current master) +### 1.2.1.1 (current master) +* Enable option of GPU processing for FANSI and NDI + +### 1.2.1 (current 190dd44) * Fix bug for data with odd-number matrix size * Fix bug for missing file when using R2* mapping with NLLS algorithm -### 1.2 (current d2f54a3) +### 1.2 (commit d2f54a3) * Support several deep learning based methods (BFRnet, xQSM, QSMnet+ and LP-CNN) on Linux * Support atlas-based subcortical structure segmentation (CIT168 Reinforcement learning atlas, MuSus-100 and AHEAD) on Linux and Mac * Integrate R2* mapping toolbox into SEPIA @@ -54,7 +57,7 @@ For full update log, please visit https://sepia-documentation.readthedocs.io/en/ Please visit the documentation website for more info regarding the newly supported methods and functions. -### 1.1.1 (current a7680bb) +### 1.1.1 (commit a7680bb) * ROMEO is now packaged together with CLEAR-SWI. To accompany these changes, ROMEO_HOME is renamed to MRITOOLS_HOME * Supported CLEAR-SWI * Fixed bug: bipolar readout correction implementation in full processing pipeline is different from the one in Phase unwrapping standalone diff --git a/sepia_universal_variables.m b/sepia_universal_variables.m index a5aef6e..163202f 100755 --- a/sepia_universal_variables.m +++ b/sepia_universal_variables.m @@ -14,7 +14,7 @@ % DO NOT change the order of the entities, add a new one at the end instead % %% Version -SEPIA_version = 'v1.2.1 no GPU'; +SEPIA_version = 'v1.2.1.1'; %% PATH SEPIA_HOME = fileparts(mfilename('fullpath'));