From c0e4c07a000c0053040a4032d8987f0ac4ecaef4 Mon Sep 17 00:00:00 2001 From: Helge <47348963+HJZollner@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:03:09 -0400 Subject: [PATCH 1/7] [BUG FIX] - XA50 HERCULES loader not working - io_loadspec_twix - Pavi -Added new sequence tag for XA50 HERCULES --- detectOS.m | 166 ++++++++++++++++++ .../FID-A/inputOutput/io_loadspec_twix.m | 3 +- 2 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 detectOS.m diff --git a/detectOS.m b/detectOS.m new file mode 100644 index 00000000..123b5c41 --- /dev/null +++ b/detectOS.m @@ -0,0 +1,166 @@ +function [OS, OSVersion] = detectOS +%DETECTOS Name and version number of the operating system. +% OS = DETECTOS returns the name of the operating system as one of the +% following character vectors: 'windows', 'macos' (which includes OS X), +% 'solaris', 'aix', or another Unix/Linux distro in all lowercase +% characters (such as 'ubuntu' or 'centos'). An error is thrown if the +% operating system cannot be determined. +% +% [~, OSVERSION] = DETECTOS returns the operating system version number +% as a numeric row vector. For example, version 6.1.7601 is reported as +% OSVERSION = [6, 1, 7601]. If the OS version cannot be determined, a +% warning is issued and the empty numeric array is returned. +% +%See also COMPUTER, ISMAC, ISPC, ISUNIX. + +% Created 2016-01-05 by Jorg C. Woehl +% 2016-10-10 (JCW): Converted to standalone function, comments added (v1.0.1). +% 2018-04-20 (JCW): Used the recommended “replace” instead of “strrep”. +% 2021-04-22 (JCW): Version information added (v1.1). + +if ismac + % Mac + % see https://support.apple.com/en-us/HT201260 for version numbers + OS = 'macos'; + [status, OSVersion] = system('sw_vers -productVersion'); + OSVersion = strtrim(OSVersion); + if (~(status == 0) || isempty(OSVersion)) + warning('detectOS:UnknownMacOSVersion',... + 'Unable to determine macOS/OS X version.'); + OSVersion = ''; + end +elseif ispc + % Windows + % see https://en.wikipedia.org/wiki/Ver_(command) for version numbers + OS = 'windows'; + [status, OSVersion] = system('ver'); + OSVersion = regexp(OSVersion, '\d[.\d]*', 'match'); + if (~(status == 0) || isempty(OSVersion)) + warning('detectOS:UnknownWindowsVersion',... + 'Unable to determine Windows version.'); + OSVersion = ''; + end +elseif isunix + % Unix/Linux + % inspired in part by + % http://linuxmafia.com/faq/Admin/release-files.html and + % http://unix.stackexchange.com/questions/92199/how-can-i-reliably-get-the-operating-systems-name/92218#92218 + [status, OS] = system('uname -s'); % results in 'SunOS', 'AIX', or 'Linux' + OS = strtrim(OS); + assert((status == 0), 'detectOS:UnknownUnixDistro',... + 'Unable to determine Unix distribution.'); + if strcmpi(OS, 'SunOS') + OS = 'solaris'; % newer name + [status, OSVersion] = system('uname -v'); % example: + OSVersion = regexp(OSVersion, '\d[.\d]*', 'match'); + if (~(status == 0) || isempty(OSVersion)) + warning('detectOS:UnknownSolarisVersion',... + 'Unable to determine Solaris version.'); + OSVersion = ''; + end + elseif strcmpi(OS, 'AIX') + OS = 'aix'; + [status, OSVersion] = system('oslevel'); % example: 6.1.0.0 + OSVersion = regexp(OSVersion, '\d[.\d]*', 'match'); + if (~(status == 0) || isempty(OSVersion)) + warning('detectOS:UnknownAIXVersion',... + 'Unable to determine AIX version.'); + OSVersion = ''; + end + elseif strcmpi(OS, 'Linux') + OS = ''; + OSVersion = ''; + % first check if /etc/os-release exists and read it + [status, result] = system('cat /etc/os-release'); + if (status == 0) + % add newline to beginning and end of output character vector (makes parsing easier) + result = sprintf('\n%s\n', result); + % determine OS + OS = regexpi(result, '(?<=\nID=).*?(?=\n)', 'match'); % ID=... (shortest match) + OS = lower(strtrim(replace(OS, '"', ''))); % remove quotes, leading/trailing spaces, and make lowercase + if ~isempty(OS) + % convert to character vector + OS = OS{1}; + end + % determine OS version + OSVersion = regexpi(result, '(?<=\nVERSION_ID=)"*\d[.\d]*"*(?=\n)', 'match'); % VERSION_ID=... (longest match) + OSVersion = replace(OSVersion, '"', ''); % remove quotes + else + % check for output from lsb_release (more standardized than /etc/lsb-release itself) + [status, result] = system('lsb_release -a'); + if (status == 0) + % add newline to beginning and end of output character vector (makes parsing easier) + result = sprintf('\n%s\n', result); + % determine OS + OS = regexpi(result, '(?<=\nDistributor ID:\t).*?(?=\n)', 'match'); % Distributor ID: ... (shortest match) + OS = lower(strtrim(OS)); % remove leading/trailing spaces, and convert to lowercase + if ~isempty(OS) + % convert to character vector + OS = OS{1}; + end + % determine OS version + OSVersion = regexpi(result, '(?<=\nRelease:\t)\d[.\d]*(?=\n)', 'match'); % Release: ... (longest match) + else + % extract information from /etc/*release or /etc/*version filename + [status, result] = system('ls -m /etc/*version'); % comma-delimited file listing + fileList = ''; + if (status == 0) + fileList = result; + end + [status, result] = system('ls -m /etc/*release'); % comma-delimited file listing + if (status == 0) + fileList = [fileList ', ' result]; + end + fileList = replace(fileList, ',', ' '); + % remove spaces and trailing newline + fileList = strtrim(fileList); + OSList = regexpi(fileList, '(?<=/etc/).*?(?=[-_][rv])', 'match'); % /etc/ ... -release/version or _release/version + fileList = strtrim(strsplit(fileList)); + % find the first entry that's different from 'os', 'lsb', 'system', '', or 'debian'/'redhat' (unless it's the only one) + ii = 1; + while (ii <= numel(OSList)) + if ~(strcmpi(OSList{ii}, 'os') || strcmpi(OSList{ii}, 'lsb') || strcmpi(OSList{ii}, 'system') || ... + isempty(OSList{ii}) || strcmpi(OSList{ii}, 'redhat') || strcmpi(OSList{ii}, 'debian')) + OS = OSList{ii}; + OSFile = fileList{ii}; + break; + elseif (strcmpi(OSList{ii}, 'redhat') || strcmpi(OSList{ii}, 'debian')) + OS = OSList{ii}; % assign temporarily, but keep going + OSFile = fileList{ii}; + end + ii = ii+1; + end + % determine OS version + if ~isempty(OSFile) + [status, OSVersion] = system(['cat ' OSFile]); + if (status == 0) + OSVersion = regexp(OSVersion, '\d[.\d]*', 'match'); + else + OSVersion = ''; + end + end + end + end + assert(~isempty(OS), 'detectOS:UnknownLinuxDistro',... + 'Unable to determine Linux distribution.'); + if isempty(OSVersion) + warning('detectOS:UnknownLinuxVersion',... + 'Unable to determine Linux version.'); + OSVersion = ''; + end + else + error('detectOS:UnknownUnixDistro',... + 'Unknown Unix distribution.') + end +else + error('detectOS:PlatformNotSupported',... + 'Platform not supported.'); +end + +if iscell(OSVersion) + % convert to character vector + OSVersion = OSVersion{1}; +end +OSVersion = round(str2double(strsplit(OSVersion, '.'))); + +end \ No newline at end of file diff --git a/libraries/FID-A/inputOutput/io_loadspec_twix.m b/libraries/FID-A/inputOutput/io_loadspec_twix.m index 375b5e4f..079dff69 100644 --- a/libraries/FID-A/inputOutput/io_loadspec_twix.m +++ b/libraries/FID-A/inputOutput/io_loadspec_twix.m @@ -91,7 +91,8 @@ ~isempty(strfind(sequence,'svs_st'))) && ... % or the Siemens STEAM sequence? isempty(strfind(sequence,'eja_svs')); %And make sure it's not 'eja_svs_steam'. isUniversal = ~isempty(strfind(sequence,'univ')) ||... %Is JHU universal editing sequence - ~isempty(strfind(sequence,'smm_svs_herc')); % Is Pavi's HERCULES sequence + ~isempty(strfind(sequence,'smm_svs_herc')) ||... % Is Pavi's HERCULES sequence + ~isempty(strfind(sequence,'svs_herc_gls')); % Is Gize's HERCULES sequence isDondersMRSfMRI = contains(sequence,'moco_nav_set'); %Is combined fMRI-MRS sequence implemented at Donders Institute NL isConnectom = contains(twix_obj.hdr.Dicom.ManufacturersModelName,'Connectom'); %Is from Connectom scanner (Apparently svs_se Dims are not as expected for vd) From 938cb57a19c6f59e5b767d84e0c93b977161a4d3 Mon Sep 17 00:00:00 2001 From: Helge <47348963+HJZollner@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:04:21 -0400 Subject: [PATCH 2/7] Add version to latest fix Add version --- settings/version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings/version.json b/settings/version.json index ef09467c..38c45d13 100644 --- a/settings/version.json +++ b/settings/version.json @@ -1,3 +1,3 @@ -{"Version": "2.7.0", - "Date": "August 15, 2024", +{"Version": "2.7.1", + "Date": "August 20, 2024", "Description": "First digit for major releases, second digit for regular compiled releases, third digit for each commit to the develop branch. "} From 39dd9d67e3fb5834ded1e4a0832d545a83e6b877 Mon Sep 17 00:00:00 2001 From: Helge <47348963+HJZollner@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:06:48 -0400 Subject: [PATCH 3/7] Update for HBCD - Added SubSpecOrder to json field for external ordering of subspectra - Modified load nii for HBCD loading to apply 180 phase to FIDs if necessary instead of first-point phasing prior to coil-combination. Discussed with Richard - Fixed a random bug in the combineCoils function --- job/OspreyJob.m | 5 +++++ libraries/FID-A/inputOutput/io_loadspec_niimrs.m | 16 +++++++++++++--- process/osp_combineCoils.m | 2 +- settings/version.json | 4 ++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/job/OspreyJob.m b/job/OspreyJob.m index 8aedca03..f94ec9ae 100755 --- a/job/OspreyJob.m +++ b/job/OspreyJob.m @@ -435,6 +435,11 @@ else opts.load.undoPhaseCycle = 1; end + if isfield(jobStruct,'SubSpecOrder') + MRSCont.opts.Order = jobStruct.SubSpecOrder'; + else + MRSCont.opts.Order = []; + end debug = '11'; if isfield(jobStruct,'exportParams') MRSCont.opts.exportParams.flag = 1; diff --git a/libraries/FID-A/inputOutput/io_loadspec_niimrs.m b/libraries/FID-A/inputOutput/io_loadspec_niimrs.m index 65f3d54f..d68cedcd 100644 --- a/libraries/FID-A/inputOutput/io_loadspec_niimrs.m +++ b/libraries/FID-A/inputOutput/io_loadspec_niimrs.m @@ -294,7 +294,7 @@ sqzDims{end+1} = dimsFieldNames{rr}; end end - subspecs = 0; + subspecs = 1; end if length(sqzDims)==5 @@ -348,12 +348,22 @@ undoPhaseCycle = 1; end if isfield(hdr_ext, 'Manufacturer') && strcmp(hdr_ext.Manufacturer,'Philips') && undoPhaseCycle - fids = fids .* repmat(conj(fids(1,:,:,:))./abs(fids(1,:,:,:)),[size(fids,1) 1]); + if hdr_ext.WaterSuppressed && hdr_ext.EchoTime == 0.080 + fids(:,:,:,1:2:end) = -fids(:,:,:,1:2:end); + end + if hdr_ext.WaterSuppressed && hdr_ext.EchoTime == 0.035 + fids(:,:,2:2:end,:) = -fids(:,:,2:2:end,:); + end end - if isfield(hdr_ext, 'Manufacturer') && strcmp(hdr_ext.Manufacturer,'GE') && undoPhaseCycle + if isfield(hdr_ext, 'Manufacturer') && strcmp(hdr_ext.Manufacturer,'GE') && ~strcmp(hdr_ext.SequenceName,'hbcd2') && undoPhaseCycle if hdr_ext.WaterSuppressed && hdr_ext.EchoTime == 0.080 fids(:,:,2:2:end,:) = -fids(:,:,2:2:end,:); end + else if isfield(hdr_ext, 'Manufacturer') && strcmp(hdr_ext.Manufacturer,'GE') && strcmp(hdr_ext.SequenceName,'hbcd2') && undoPhaseCycle + if ~hdr_ext.WaterSuppressed && hdr_ext.EchoTime == 0.080 + fids = -fids; + end + end end %Compared to NIfTI MRS, FID-A needs the conjugate diff --git a/process/osp_combineCoils.m b/process/osp_combineCoils.m index d964104b..21181123 100644 --- a/process/osp_combineCoils.m +++ b/process/osp_combineCoils.m @@ -44,7 +44,7 @@ metab_ll = MRSCont.opts.MultipleSpectra.metab(ll); % For SPECIAL acquisitions, some of the sub-spectra need to be combined % prior to determining the CC coefficients. We'll set a flag here. - isSpecial = strcmpi(MRSCont.raw_uncomb{metab_ll,kk}.seq, 'special'); + isSpecial = strcmpi(MRSCont.raw_uncomb{metab_ll,kk}.seq(1), 'special'); MRSCont.flags.isSPECIAL = isSpecial; % Check if reference scans exist, if so, get CC coefficients from there if MRSCont.flags.hasRef diff --git a/settings/version.json b/settings/version.json index 38c45d13..c1c3e0c8 100644 --- a/settings/version.json +++ b/settings/version.json @@ -1,3 +1,3 @@ -{"Version": "2.7.1", - "Date": "August 20, 2024", +{"Version": "2.7.2", + "Date": "September 10, 2024", "Description": "First digit for major releases, second digit for regular compiled releases, third digit for each commit to the develop branch. "} From d6face2c8620fed33c7054cddb29611e74e13876 Mon Sep 17 00:00:00 2001 From: Christopher Davies-Jenkins Date: Thu, 19 Sep 2024 19:12:48 -0400 Subject: [PATCH 4/7] Fixes #778 Added handling of interleaved water in Siemens product Twix data --- .../FID-A/inputOutput/io_loadspec_twix.m | 64 +++++++++++++++++++ settings/version.json | 4 +- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/libraries/FID-A/inputOutput/io_loadspec_twix.m b/libraries/FID-A/inputOutput/io_loadspec_twix.m index 079dff69..b39635de 100644 --- a/libraries/FID-A/inputOutput/io_loadspec_twix.m +++ b/libraries/FID-A/inputOutput/io_loadspec_twix.m @@ -271,6 +271,43 @@ end end +% Product Siemens PRESS/STEAM can also include water reference scans. +if isSiemens && (matches(seq,'PRESS')||matches(seq,'STEAM')) && length(sqzSize)>3 + wRefs=true; + + % First, separate out the reference scans: + Ind = find(matches(sqzDims,'Phs')); + if ~(sqzSize(Ind)==2) + error('Expecting 2 entries in the "Phs" dimension, we found %i!',Ind); + end + fids_w = squeeze(GetSlice(fids,Ind,1)); + fids = squeeze(GetSlice(fids,Ind,2)); + sqzDims(Ind) = []; + sqzSize(Ind) = []; + + % Then, concat the averages and repetitions (if they exist) + if sqzSize(matches(sqzDims,'Rep'))>1 + DimAv = find(matches(sqzDims,'Ave')); + DimRep = find(matches(sqzDims,'Rep')); + + % Reshape the fids: + NewSize = sqzSize; + NewSize(DimAv) = NewSize(DimAv) * NewSize(DimRep); + NewSize(DimRep) = []; + fids = reshape(fids,NewSize); + + sqzSize = size(fids); + sqzDims(DimRep)=[]; + + % Reshape the water fids, throwing out the zero entries + NZ_Avs = find(any(~(squeeze(sum(abs(fids_w),[1,2]))==0),DimRep-2)); % Finds water fids in "averages" which are non-zero + fids_w =fids_w(:,:,NZ_Avs,:); + Sz = size(fids_w); + fids_w = reshape(fids_w,Sz(1),Sz(2),[]); + end + +end + % Extract voxel dimensions if (strcmp(version,'vd') || strcmp(version,'vb') || contains(version,'XA')) TwixHeader.VoI_RoFOV = twix_obj.hdr.Config.VoI_RoFOV; % Voxel size in readout direction [mm] @@ -852,4 +889,31 @@ out_w=[]; end +end %DONE + +function[SlicedArray] = GetSlice(Array, Dim, Idx) +%% function[SlicedArray] = GetSlice(Array, Dim, Idx) +% +% Description: Takes a slice of multi-dimensional array in dimension "Dim" +% at the specified index: Idx. +% +% Input: Array = Aray to be sliced +% Dim = Dimension in which to slice +% Idx = Index (or indices) to extract from dimension "Dim" +% Output: SlicedArray = Subset of array +% +% Example usage: +% For a 7D array, "Data": +% +% SlicedData = GetSlice(Data, 5, 1:5); +% returns 7D array but with only the 1st 5 entries in 5th dim +% +% C.W. Davies-Jenkins, Johns Hopkins University 2024 + +Specification = repmat({':'}, 1, ndims(Array)); % Create an vector whose length matches the dimensionality of the Array +Specification{Dim} = Idx; % At the specified Dim, add the indices we want to extract + +SlicedArray = Array(Specification{:}); % Apply the slicing! + +end diff --git a/settings/version.json b/settings/version.json index c1c3e0c8..125b18cd 100644 --- a/settings/version.json +++ b/settings/version.json @@ -1,3 +1,3 @@ -{"Version": "2.7.2", - "Date": "September 10, 2024", +{"Version": "2.7.3", + "Date": "September 19, 2024", "Description": "First digit for major releases, second digit for regular compiled releases, third digit for each commit to the develop branch. "} From f3f4a852c813829cfc7919c3aee17a01ad5020db Mon Sep 17 00:00:00 2001 From: Helge <47348963+HJZollner@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:02:36 -0400 Subject: [PATCH 5/7] [BUG FIX] - Crash during (non-HBCD) HTML report - OspreyHTMLReport Added additional conditions to make non-HBCD pipeline HTML reports work --- plot/OspreyHTMLReport.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plot/OspreyHTMLReport.m b/plot/OspreyHTMLReport.m index ffc5dd71..4c527fa3 100644 --- a/plot/OspreyHTMLReport.m +++ b/plot/OspreyHTMLReport.m @@ -1012,7 +1012,7 @@ end fprintf(fid,'\n\t'); fprintf(fid,'\n\t
'); -if MRSCont.flags.didSeg && isfield(MRSCont.seg.img, 'vol_Tha_CoM') % HBCD thalamus overlap +if MRSCont.flags.didSeg && isfield(MRSCont.seg,'img') && isfield(MRSCont.seg.img, 'vol_Tha_CoM') % HBCD thalamus overlap if table2array(MRSCont.seg.tables_Voxel_1(kk,4)) < 0.4 fprintf(fid,'\n

fThalamus in voxel [%%] \t%5.2f

',table2array(MRSCont.seg.tables_Voxel_1(kk,4))*100); else @@ -1254,7 +1254,7 @@ fprintf(fid,'\n\t
'); fprintf(fid,'\n\t\t',fullfile(outputFigures,[sub_str '_seg_svs_space-scanner_mask.jpg'])); fprintf(fid,'\n\t
'); - if isfield(MRSCont.seg.img, 'vol_Tha_CoM') % HBCD thalamus overlap + if isfield(MRSCont.seg,'img') && isfield(MRSCont.seg.img, 'vol_Tha_CoM') % HBCD thalamus overlap fprintf(fid,'\n\t
'); fprintf(fid,'\n\t\t',fullfile(outputFigures,[sub_str '_seg_svs_space-scanner_CoM.jpg'])); fprintf(fid,'\n\t
'); From 2dfa76550836c20dd61f43e3d44a4e70fe2d21d8 Mon Sep 17 00:00:00 2001 From: Helge <47348963+HJZollner@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:03:22 -0400 Subject: [PATCH 6/7] Update version Updated version --- settings/version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings/version.json b/settings/version.json index 125b18cd..0ba78deb 100644 --- a/settings/version.json +++ b/settings/version.json @@ -1,3 +1,3 @@ -{"Version": "2.7.3", - "Date": "September 19, 2024", +{"Version": "2.7.5", + "Date": "September 23, 2024", "Description": "First digit for major releases, second digit for regular compiled releases, third digit for each commit to the develop branch. "} From abf68f63cf79a4fa3b28c43fee590b51d11d073e Mon Sep 17 00:00:00 2001 From: Helge <47348963+HJZollner@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:14:08 -0400 Subject: [PATCH 7/7] Prepare release 2.8.0 Update version number for release --- settings/version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/version.json b/settings/version.json index 0ba78deb..fe88eae1 100644 --- a/settings/version.json +++ b/settings/version.json @@ -1,3 +1,3 @@ -{"Version": "2.7.5", +{"Version": "2.8.0", "Date": "September 23, 2024", "Description": "First digit for major releases, second digit for regular compiled releases, third digit for each commit to the develop branch. "}