Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to version 2.7.0 #774

Merged
merged 24 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1c608ec
Merge pull request #754 from schorschinho/master
HJZollner Jul 15, 2024
0c62d5f
Update compile script
HJZollner Jul 15, 2024
87b32c4
Merge pull request #755 from HJZollner/develop
HJZollner Jul 15, 2024
9646a2e
Allow loading of individual DICOM data files
alexcraven Jul 17, 2024
3261202
Add version number
HJZollner Jul 22, 2024
d452476
Merge pull request #758 from alexcraven/develop
HJZollner Jul 22, 2024
0082e56
[BUG FIX] - VE11 universal loader not working - io_loadspec_twix
HJZollner Jul 22, 2024
11a0b75
Merge pull request #760 from HJZollner/develop
HJZollner Jul 22, 2024
cab108a
Update 05-tutorial_cmd.Rmd
HJZollner Jul 22, 2024
a95f3ec
Merge pull request #761 from schorschinho/HJZollner-fix-typo
HJZollner Jul 22, 2024
ed1f6a3
Add single json for versioning
HJZollner Jul 26, 2024
c08bb16
Merge pull request #762 from HJZollner/develop
HJZollner Jul 26, 2024
ccc0f64
[BUG FIX] - SNR of water spectra not correct - OspreyProcess - Richar…
HJZollner Jul 26, 2024
a9af842
Merge pull request #764 from HJZollner/develop
HJZollner Jul 26, 2024
a71a443
CI caught an error in last commit
HJZollner Jul 26, 2024
bdc7904
Merge pull request #765 from HJZollner/develop
HJZollner Jul 26, 2024
e7c6be9
Add option for atlas-based relaxation correction
CWDAVIESJENKINS Aug 5, 2024
95fd6cd
Merge pull request #767 from CWDAVIESJENKINS/develop
CWDAVIESJENKINS Aug 5, 2024
e9c7ec0
Bugfix Atlas implementation
CWDAVIESJENKINS Aug 8, 2024
a8a5203
Merge pull request #768 from CWDAVIESJENKINS/develop
CWDAVIESJENKINS Aug 8, 2024
a17472d
Bugfix atlas quantify
HJZollner Aug 15, 2024
10242dc
Merge pull request #772 from HJZollner/develop
HJZollner Aug 15, 2024
c843ca9
Prepare release v.2.7.0
HJZollner Aug 15, 2024
92b5308
Merge pull request #773 from HJZollner/develop
HJZollner Aug 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion GUI/Osprey.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
%
% HISTORY:
% 2019-07-11: First version of the code.
%% Get version
VersionStruct = getCurrentVersion;

%% Check for available add-ons
[~,~] = osp_Toolbox_Check ('OspreyGUI',0);
%% Set up Layout
Expand All @@ -30,7 +33,7 @@
logoFcn = @()imread('osprey.png', 'BackgroundColor', gui.colormap.Background);
logoBanner = uiw.utility.loadIcon(logoFcn);
% Here the intro banner is created
gui.d = uiw.dialog.About('Name', 'Osprey','Version','2.6.0','Date', 'July 15, 2024',...
gui.d = uiw.dialog.About('Name', 'Osprey','Version',VersionStruct.Version,'Date', VersionStruct.Date,...
'Timeout', 3,'CustomText', 'Osprey is provided by Johns Hopkins University.',...
'ContactInfo', '[email protected]','LogoCData', logoBanner);

Expand Down
4 changes: 2 additions & 2 deletions GUI/osp_Toolbox_Check.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
% HISTORY:
% 2020-05-15: First version of the code.
%% % 1. SAVE OSPREY VERSION%%%
%%% 1. SAVE OSPREY VERSION%%%
OspreyVersion = 'Osprey 2.6.0';
VersionStruct = getCurrentVersion;
OspreyVersion = ['Osprey ' VersionStruct.Version];
fprintf(['Timestamp %s ' OspreyVersion ' ' Module '\n'], datestr(now,'mmmm dd, yyyy HH:MM:SS'));
hasSPM = 1; % For the compiled GUI
%% % 2. GET SPMPATH AND TOOLBOXES%%%
Expand Down
4 changes: 2 additions & 2 deletions docs_source/05-tutorial_cmd.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ MRSCont = OspreyJob('Path/To/Job/File');
MRSCont will now contain the options and file locations for your data, as specified in the job file. Next, the data can be loaded:

```octave
MRSCont = OspreyLoad(MRScont);
MRSCont = OspreyLoad(MRSCont);
```

The substructure, MRSCont.raw, now contains the raw MRS data, as extracted from their native format. We may now preprocess these data:
Expand Down Expand Up @@ -56,4 +56,4 @@ At any point during a command-line analysis, it is possible to visualize the dat

```octave
OspreyGUI(MRSCont);
```
```
23 changes: 22 additions & 1 deletion job/OspreyJob.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

%%% 1. INITIALISE DATA CONTAINER WITH DEFAULT SETTINGS
[MRSCont] = OspreySettings;
VersionStruct = getCurrentVersion;

%%% 2. CHECK JOB INPUT FILE FORMAT %%%
[~,~,ext] = fileparts(jobFile);
Expand Down Expand Up @@ -210,6 +211,16 @@
fprintf('Adding macromolecule and lipid basis functions to the fit (default). Please indicate otherwise in the csv-file or the GUI \n');
MRSCont.opts.fit.fitMM = 1;
end
if isfield(jobStruct,'RelaxationAtlas')
MRSCont.opts.quantify.RelaxationAtlas = jobStruct(1).RelaxationAtlas;
else
MRSCont.opts.quantify.RelaxationAtlas = 0;
end
if isfield(jobStruct,'RelaxationAtlasAge')
MRSCont.opts.quantify.RelaxationAtlasAge = jobStruct(1).RelaxationAtlasAge;
else
MRSCont.opts.quantify.RelaxationAtlasAge = 0;
end
if isfield(jobStruct,'deface')
MRSCont.opts.img.deface = jobStruct.deface;
else
Expand Down Expand Up @@ -397,6 +408,16 @@
else
MRSCont.opts.fit.fitMM = 1;
end
if isfield(jobStruct,'RelaxationAtlas')
MRSCont.opts.quantify.RelaxationAtlas = jobStruct.RelaxationAtlas;
else
MRSCont.opts.quantify.RelaxationAtlas = 0;
end
if isfield(jobStruct,'RelaxationAtlasAge')
MRSCont.opts.quantify.RelaxationAtlasAge = jobStruct.RelaxationAtlasAge;
else
MRSCont.opts.quantify.RelaxationAtlasAge = 0;
end
if isfield(jobStruct,'basisSet')
if isfolder(jobStruct.basisSet)
opts.fit.basissetFolder = jobStruct.basisSet;
Expand Down Expand Up @@ -781,7 +802,7 @@
%%% 7. SET FLAGS AND VERSION %%%
MRSCont.flags.didJob = 1;
MRSCont.loadedJob = jobFile;
MRSCont.ver.Osp = 'Osprey 2.6.0';
MRSCont.ver.Osp = ['Osprey ' VersionStruct.Version];


%%% 8. CHECK IF OUTPUT STRUCTURE ALREADY EXISTS IN OUTPUT FOLDER %%%
Expand Down
108 changes: 85 additions & 23 deletions libraries/FID-A/inputOutput/io_loadspec_dicom.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,97 @@
%Georg Oeltzschner, Johns Hopkins University 2019.
%
% USAGE:
% out=io_loadspec_dicom(folder);
%
% out=io_loadspec_dicom(source);
%
% DESCRIPTION:
% Loads a DICOM (.dcm, .ima) file into matlab structure format.
%
%
% INPUTS:
% filename = Name of a folder with DICOM (.dcm, .ima) data to load.
% source = Name of a folder (or file) with DICOM (.dcm, .ima) data to load.
%
% OUTPUTS:
% out = Input dataset in FID-A structure format.

function out=io_loadspec_dicom(folder);
function out=io_loadspec_dicom(source);

% If path to a .dcm is provided, then extract the folder location:
if ~isfolder(folder) && isfile(folder)
folder =fileparts(folder);
% ARC170724 : Source may be a folder, or an individual file. We later determine
% how this should be interpreted.
if ~isfolder(source) && isfile(source)
[folder,filename,ext] =fileparts(source);
requested_file=[filename ext];
else
folder=source;
requested_file=[];
end

% Create list of complete filenames (incl. path) in the folder
dirFolder = dir(folder);
filesInFolder = dirFolder(~[dirFolder.isdir]);
hidden = logical(ones(1,length(filesInFolder)));
for jj = 1:length(filesInFolder)
for jj = 1:length(filesInFolder)
if strcmp(filesInFolder(jj).name(1),'.')
hidden(jj) = 0;
end
end
filesInFolder = filesInFolder(hidden);%delete hidden files
filesInFolder = fullfile(folder, {filesInFolder.name});
filesInFolder = filesInFolder(hidden);%delete hidden files

if length(filesInFolder)==0
% No files survived. This will not end well.
error([ 'io_loadspec_dicom did not find any files in ' folder ])
end

requested_file_ix=find(strcmp(requested_file,{filesInFolder.name})); % NB, may be empty

if isfolder(folder)
filesInFolder = fullfile(folder, {filesInFolder.name});
else
% this can happen if the function is called with a wildcard, eg /path/to/abc*dcm
filesInFolder = fullfile(dirname(folder), {filesInFolder.name});
end

% ARC170724 : If a single file is specified (rather than a directory), check
% whether this file should be considered in isolation (pre-averaged data) or
% if it should be combined with other items in the folder. We do this by
% inspecting the Series Number header.

if ~isempty(requested_file_ix)
% check header of the first, last and requested items; if Series Number
% does not match, don't try to combine
check_header_ix = unique([1, requested_file_ix, length(filesInFolder) ]);
encountered_series_numbers = [];
encountered_problems = [];

for ix = check_header_ix
try
dh = dicominfo(filesInFolder{ix});
encountered_series_numbers(end+1) = dh.SeriesNumber;
catch
encountered_problems(end+1) = ix;
end
end

encountered_series_numbers=unique(encountered_series_numbers);

if any(encountered_problems == requested_file_ix)

warning([ 'io_loadspec_dicom could not determine SeriesNumber of the requested file: ' source ]);
% in this case, fall back on default behaviour

elseif length(encountered_series_numbers)>1 || length(encountered_problems)>0

% if we found more than one series, OR we found some unreadable files
% (probably non-DICOM), then just retain that one specific file the
% caller originally asked for

if length(encountered_problems)>0
warning([ 'io_loadspec_dicom encountered some non-DICOM data in ' source ]);
end

filesInFolder={source};
end

% in any other scenario, full back on the default behaviour
end

% Get the header of the first file to make some decisions.
DicomHeader = read_dcm_header(filesInFolder{1});
Expand All @@ -41,7 +103,7 @@
seqorig = DicomHeader.seqorig;
else
% deal with missing seqorig field for dicom data load on Siemens Minnesota
% sequences (Auerbach and Deelchand versions, VB17A & VE11C)
% sequences (Auerbach and Deelchand versions, VB17A & VE11C)
if contains(DicomHeader.sequenceFileName, 'slaser_dkd') %Deelchand/Oz
seqorig = 'CMRR';
elseif contains(DicomHeader.sequenceFileName, 'eja_svs') %Auerbach/Marjanska
Expand Down Expand Up @@ -80,7 +142,7 @@
end
% Collect all FIDs and sort them into fids array
for kk = 1:length(filesInFolder)

% First, attempt to retrieve the FID from the DICOM header:
infoDicom = dicominfo(filesInFolder{kk});
if isfield(infoDicom, 'SpectroscopyData')
Expand All @@ -95,7 +157,7 @@
fids(:,kk) = dicom_get_spectrum_siemens(fd);
fclose(fd);
end

end


Expand All @@ -115,16 +177,16 @@
else
out.flags.averaged = 0;
end

% Currently, the DICOM recon of the universal sequence is flawed.
% Kick out empty lines here and see if data can be reconstructed.
if strcmp(seqorig, 'Universal') && out.flags.averaged == 0
fids(:,size(fids,2)/2+1:end) = [];
end

% Rearrange into subspecs
fids = reshape(fids,[size(fids,1) size(fids,2)/2 2]);

elseif strcmp(seqtype,'PRESS') || strcmp(seqtype,'STEAM') || strcmp(seqtype,'sLASER')
% If the number of stored FIDs does not match the number of averages
% stored in the DICOM header, the data are averaged.
Expand All @@ -137,9 +199,9 @@

elseif strcmp(seqtype,'HERMES')

out.flags.averaged = 0;
out.flags.averaged = 0;
% Currently, the DICOM recon of the universal sequence is flawed.
% Kick out empty lines here and see if data can be reconstructed.
% Kick out empty lines here and see if data can be reconstructed.
if strcmp(seqorig, 'Universal') && out.flags.averaged == 0
fids(:,size(fids,2)/2+1:end) = [];
end
Expand Down Expand Up @@ -222,7 +284,7 @@

%Find the number of averages. 'averages' will specify the current number
%of averages in the dataset as it is processed, which may be subject to
%change. 'rawAverages' will specify the original number of acquired
%change. 'rawAverages' will specify the original number of acquired
%averages in the dataset, which is unchangeable.
if dims.subSpecs ~=0
if dims.averages~=0
Expand All @@ -244,7 +306,7 @@

%Find the number of subspecs. 'subspecs' will specify the current number
%of subspectra in the dataset as it is processed, which may be subject to
%change. 'rawSubspecs' will specify the original number of acquired
%change. 'rawSubspecs' will specify the original number of acquired
%subspectra in the dataset, which is unchangeable.
if dims.subSpecs ~=0
subspecs=sz(dims.subSpecs);
Expand Down Expand Up @@ -288,8 +350,8 @@
out.fids=fids;
out.specs=specs;
out.sz=sz;
out.ppm=ppm;
out.t=t;
out.ppm=ppm;
out.t=t;
out.spectralwidth=spectralwidth;
out.dwelltime=dwelltime;
out.txfrq=txfrq;
Expand Down
8 changes: 4 additions & 4 deletions libraries/FID-A/inputOutput/io_loadspec_twix.m
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
isjnseq=~isempty(strfind(sequence,'jn_')); %Is this another one of Jamie Near's sequences?
isWIP529=~isempty(strfind(sequence,'edit_529'));%Is this WIP 529 (MEGA-PRESS)?
ismodWIP=((~isempty(strfind(sequence,'\svs_edit'))||...
~isempty(strfind(sequence,'\wip_svs_edit'))) && isempty(strfind(sequence,'edit_859'))); %Modified WIP
~isempty(strfind(sequence,'\wip_svs_edit'))) && ...
(isempty(strfind(sequence,'edit_859')) && isempty(strfind(sequence,'univ')))); %Modified WIP
isWIP859=~isempty(strfind(sequence,'edit_859'))||...;%Is this WIP 859 (MEGA-PRESS)?
~isempty(strfind(sequence,'WIP_859'));%Is this WIP 859 (MEGA-PRESS)? Other naming
isTLFrei=~isempty(strfind(sequence,'md_svs_edit')) ||... %Is Thomas Lange's MEGA-PRESS sequence
Expand Down Expand Up @@ -353,9 +354,8 @@
else
% GO 6/2024: Encountered a VD13 Siemens product sequence that had
% transients in Set, not in Ave...
if strcmp(sqzDims,'Ave')
dims.averages=find(strcmp(sqzDims,'Ave'));
else
dims.averages=find(strcmp(sqzDims,'Ave'));
if isempty(dims.averages)
dims.averages=find(strcmp(sqzDims,'Set'));
end
end
Expand Down
12 changes: 6 additions & 6 deletions process/OspreyProcess.m
Original file line number Diff line number Diff line change
Expand Up @@ -745,18 +745,18 @@
end
end
if MRSCont.flags.hasRef
MRSCont.QM.SNR.ref(ref_ll,kk) = op_getSNR(MRSCont.processed.ref{kk});
MRSCont.QM.FWHM.ref(ref_ll,kk) = op_getLW(MRSCont.processed.ref{kk},4.2,5.2);
MRSCont.QM.SNR.ref(ref_ll,kk) = op_getSNR(MRSCont.processed.ref{kk},3.7,5.7);
MRSCont.QM.FWHM.ref(ref_ll,kk) = op_getLW(MRSCont.processed.ref{kk},3.7,5.7);
MRSCont.processed.ref{kk}.QC_names = {'water'};
end
if MRSCont.flags.hasWater
MRSCont.QM.SNR.w(w_ll,kk) = op_getSNR(MRSCont.processed.w{kk});
MRSCont.QM.FWHM.w(w_ll,kk) = op_getLW(MRSCont.processed.w{kk},4.2,5.2);
MRSCont.QM.SNR.w(w_ll,kk) = op_getSNR(MRSCont.processed.w{kk},3.7,5.7);
MRSCont.QM.FWHM.w(w_ll,kk) = op_getLW(MRSCont.processed.w{kk},3.7,5.7);
MRSCont.processed.w{kk}.QC_names = {'water'};
end
if MRSCont.flags.hasMMRef
MRSCont.QM.SNR.mm_ref(mm_ref_ll,kk) = op_getSNR(MRSCont.processed.mm_ref{kk});
MRSCont.QM.FWHM.mm_ref(mm_ref_ll,kk) = op_getLW(MRSCont.processed.mm_ref{kk},4.2,5.2);
MRSCont.QM.SNR.mm_ref(mm_ref_ll,kk) = op_getSNR(MRSCont.processed.mm_ref{kk},3.7,5.7);
MRSCont.QM.FWHM.mm_ref(mm_ref_ll,kk) = op_getLW(MRSCont.processed.mm_ref{kk},3.7,5.7);
MRSCont.processed.mm_ref{kk}.QC_names = {'water'};
end
end
Expand Down
Loading
Loading