391 lines
15 KiB
Matlab
391 lines
15 KiB
Matlab
classdef GiftIcaReport<handle
|
|
%GIFTICAREPORT
|
|
% This Class will be a little tool to print to file images and timecourses
|
|
% to quickly inspect the result of GIFT for a cleanup of fMRI data
|
|
% some guidlines can be found below!
|
|
% https://doi.org/10.1016/j.neuroimage.2016.12.036
|
|
|
|
% to do: auto scale images
|
|
% to do: check slices to be plotted actually exist!!
|
|
% to do: create the output
|
|
properties
|
|
ICAfile = {}; % .nii file containing the ICA images
|
|
TCfile = {}; % .nii file containing the TimeCourse data
|
|
Orientation = 'sag'; % orientation of images to create
|
|
DirOut = '.' %default uses the current directory
|
|
FileOut = 'Report.png'; %default report name
|
|
Layout = 'north'; %define the location of the TC plot
|
|
Colormap=gray; % colormap for images
|
|
|
|
end
|
|
properties (Dependent=true)
|
|
PlotSliceNumbers % slice numbers to plot
|
|
NPlots % number of slices to plot
|
|
ImageLimits %defines the minium and max value for colorbar
|
|
end
|
|
|
|
properties (Access=private)
|
|
SPMPath
|
|
FigureObject
|
|
ImageData
|
|
TimeCourseData
|
|
VData
|
|
VTimeCourse
|
|
PData
|
|
PTimeCourse
|
|
SliceNumbersInternal
|
|
NSlicesInternal = 10;
|
|
TCPlot %subplot containing the time course
|
|
SLPlot %subplot containing the
|
|
CLim = [0,1]; %limits for plotting
|
|
%% the matlab reporting gives an licence error.
|
|
% ReportObj % the place holder for the Report object
|
|
|
|
end
|
|
properties (Constant,Access=private)
|
|
OrientationOptions={'tra','sag','cor','all'}; %option all still needs to be implemented
|
|
end
|
|
|
|
methods %constructor, set and get functions
|
|
function obj = GiftIcaReport()
|
|
%GIFTICAREPORT Construct an instance of this class
|
|
% Detailed explanation goes here
|
|
obj.SPMPath=[]; %check if SPM is in the path
|
|
obj.FigureObject=figure; %create a figure
|
|
%% the matlab reporting gives an licence error at the CNC
|
|
%import mlreportgen.report.*
|
|
%obj.ReportObj = Report(fullfile(obj.DirOut,obj.FileOut));
|
|
end
|
|
|
|
function delete(obj)
|
|
if ~isempty(obj.FigureObject)
|
|
if isa(obj.FigureObject,'matlab.ui.Figure')
|
|
close(obj.FigureObject);
|
|
end
|
|
end
|
|
end
|
|
|
|
function set.SPMPath(obj,~)
|
|
SPMFound=which('spm'); %check if SPM in searchpath
|
|
if isempty(SPMFound) %SPM not found
|
|
id='GiftIcaReport:set_SPMPath:SPM_not_found';
|
|
msg='SPM not found';
|
|
throw(MException(id,msg));
|
|
end
|
|
%to do add a check on spm version
|
|
obj.SPMPath=fileparts(SPMFound);
|
|
end
|
|
|
|
function set.TCfile(obj,val)
|
|
obj.TCfile=obj.setfile(val);
|
|
end
|
|
|
|
function set.ICAfile(obj,val)
|
|
obj.ICAfile=obj.setfile(val);
|
|
end
|
|
|
|
function set.Orientation(obj,val)
|
|
switch lower(val)
|
|
case obj.OrientationOptions
|
|
obj.Orientation=val;
|
|
otherwise
|
|
id = 'GiftIcaReport:set_orientation:invalid_orientation';
|
|
msg = sprintf('%s\n%s\n%s\t%s\t%s\n',...
|
|
'invalid option for orientation',...
|
|
'pick one of the following options',...
|
|
obj.OrientationOptions{:});
|
|
throw(MException(id,msg));
|
|
end
|
|
end
|
|
|
|
function set.PlotSliceNumbers(obj,val)
|
|
if isempty(val)
|
|
if ~isempty(obj.ImageData) && ~isempty(obj.NPlots)
|
|
%autofill the plotslicenumbers
|
|
switch lower(obj.Orientation)
|
|
case 'tra'
|
|
nmax=size(obj.ImageData,3)
|
|
case 'cor'
|
|
nmax=size(obj.ImageData,2)
|
|
case 'sag'
|
|
nmax=size(obj.ImageData,1);
|
|
otherwise
|
|
return;
|
|
end
|
|
obj.SliceNumbersInternal=round(linspace(1,nmax,obj.NPlots));
|
|
end
|
|
else
|
|
if isnumeric(val) && ~any(val<1)
|
|
obj.SliceNumbersInternal=val;
|
|
obj.NSlicesInternal=numel(val);
|
|
else
|
|
id = 'GiftIcaReport:set_slicenumbers:invalid_input';
|
|
msg = sprintf('invalid input for slicenumbers');
|
|
throw(MException(id,msg));
|
|
end
|
|
end
|
|
end
|
|
|
|
function val=get.PlotSliceNumbers(obj)
|
|
val=obj.SliceNumbersInternal;
|
|
end
|
|
|
|
function set.NPlots(obj,val)
|
|
if isnumeric(val) && numel(val)==1 && val>0
|
|
tmp=obj.SliceNumbersInternal;
|
|
if ~isempty(tmp) && val<=numel(tmp)
|
|
obj.PlotSliceNumbers=tmp(1:val);
|
|
else
|
|
obj.NSlicesInternal=val;
|
|
obj.PlotSliceNumbers=[];
|
|
end
|
|
else
|
|
id = 'GiftIcaReport:set_NSlices:invalid_input';
|
|
msg = sprintf('invalid input for slicenumbers');
|
|
throw(MException(id,msg));
|
|
end
|
|
end
|
|
|
|
function val=get.NPlots(obj)
|
|
val=obj.NSlicesInternal;
|
|
end
|
|
|
|
function set.ImageLimits(obj,val)
|
|
if isnumeric(val) && numel(val)==2
|
|
obj.CLim=transpose(sort(val(:))); %guarante it is a row vector
|
|
else
|
|
id = 'GiftIcaReport:setImageLimits:invalid_input';
|
|
msg = sprintf('Invalid input, set limits to [min max]');
|
|
throw(MException(id,msg));
|
|
end
|
|
end
|
|
|
|
function val=get.ImageLimits(obj)
|
|
val=obj.CLim;
|
|
end
|
|
|
|
function set.Layout(obj,val)
|
|
switch lower(val)
|
|
case {'north','south','east','west',''}
|
|
obj.Layout=val;
|
|
otherwise
|
|
id = 'GiftIcaReport:setLayout:invalid_input';
|
|
msg = sprintf('Invalid input for Layout.\n Use north east south, west or empty value');
|
|
throw(MException(id,msg));
|
|
end
|
|
end
|
|
end
|
|
methods %user callable functions
|
|
function LoadData(obj)
|
|
% read ICA file
|
|
[d,f,~]=fileparts(obj.ICAfile{1});
|
|
obj.PData=spm_select('ExtFPList',d,f,1:10000);
|
|
obj.VData=spm_vol(obj.PData);
|
|
obj.ImageData=spm_read_vols(obj.VData);
|
|
% read TimeCourse file
|
|
[d,f,~]=fileparts(obj.TCfile{1});
|
|
obj.PTimeCourse=spm_select('ExtFPList',d,f,1:10000);
|
|
obj.VTimeCourse=spm_vol(obj.PTimeCourse);
|
|
obj.TimeCourseData=spm_read_vols(obj.VTimeCourse);
|
|
if isempty(obj.PlotSliceNumbers) %auto set the slice numbers
|
|
obj.PlotSliceNumbers([]);
|
|
end
|
|
end
|
|
|
|
function BuildFigure(obj)
|
|
if isempty(obj.FigureObject)
|
|
obj.FigureObject=figure;
|
|
end
|
|
figure(obj.FigureObject);
|
|
clf;
|
|
[nrow,ncol] = obj.getrowcol;
|
|
obj.TCPlot={};
|
|
obj.SLPlot=cell(nrow,ncol);
|
|
switch lower(obj.Layout)
|
|
case {'north','n'}
|
|
r=nrow+1;
|
|
c=ncol;
|
|
PlRow=setdiff(1:r,1);
|
|
PlCol=1:c;
|
|
obj.TCPlot={subplot(r,1,1)};
|
|
case {'east','e'}
|
|
r=nrow;
|
|
c=ncol+1;
|
|
PlRow=1:r;
|
|
PlCol=setdiff(1:c,1);
|
|
obj.TCPlot={subplot(1,c,c)};
|
|
case {'south','s'}
|
|
r=nrow+1;
|
|
c=ncol;
|
|
PlRow=setdiff(1:r,r);
|
|
PlCol=1:c;
|
|
obj.TCPlot={subplot(r,1,r)};
|
|
case {'west','w'}
|
|
r=nrow;
|
|
c=ncol+1;
|
|
PlRow=1:r;
|
|
PlCol=setdiff(1:c,1);
|
|
obj.TCPlot={subplot(1,c,1)};
|
|
otherwise %center option
|
|
r=nrow+1;
|
|
c=ncol;
|
|
PlRow=setdiff(1:r,floor(r/2));
|
|
PlCol=1:c;
|
|
obj.TCPlot={subplot(r,1,floor(r/2))};
|
|
end
|
|
[x,y]=ndgrid(PlRow,PlCol);
|
|
x=transpose(x);
|
|
y=transpose(y);
|
|
for k=1:obj.NPlots
|
|
p=(x(k)-1)*c+y(k);
|
|
obj.SLPlot{k}=subplot(r,c,p);
|
|
end
|
|
end
|
|
|
|
function PlotTimeCourse(obj,IC)
|
|
if isempty(obj.TimeCourseData)
|
|
id = 'GiftIcaReport:PlotTC:NoDataToPlot';
|
|
msg = 'No time data to plot';
|
|
throw(MException(id,msg));
|
|
end
|
|
if IC>size(obj.TimeCourseData,2)
|
|
id = 'GiftIcaReport:PlotTC:InvalidComponent';
|
|
msg = 'invalid component';
|
|
throw(MException(id,msg));
|
|
end
|
|
axes(obj.TCPlot{1});
|
|
plot(obj.TimeCourseData(:,IC));
|
|
end
|
|
|
|
function PlotSlices(obj,IC)
|
|
if isempty(obj.ImageData)
|
|
id = 'GiftIcaReport:PlotTC:NoDataToPlot';
|
|
msg = 'No time data to plot';
|
|
throw(MException(id,msg));
|
|
end
|
|
if IC>size(obj.ImageData,4)
|
|
id = 'GiftIcaReport:PlotTC:InvalidComponent';
|
|
msg = 'invalid component';
|
|
throw(MException(id,msg));
|
|
end
|
|
for k=1:obj.NPlots
|
|
axes(obj.SLPlot{k}); %#ok<LAXES>
|
|
switch lower(obj.Orientation)
|
|
case 'cor'
|
|
if obj.PlotSliceNumbers(k)>size(obj.ImageData,2);continue;end
|
|
im=squeeze(obj.ImageData(:,obj.PlotSliceNumbers(k),:,IC));
|
|
case 'tra'
|
|
if obj.PlotSliceNumbers(k)>size(obj.ImageData,3);continue;end
|
|
im=squeeze(obj.ImageData(:,:,obj.PlotSliceNumbers(k),IC));
|
|
case 'sag'
|
|
if obj.PlotSliceNumbers(k)>size(obj.ImageData,1);continue;end
|
|
im=squeeze(obj.ImageData(obj.PlotSliceNumbers(k),:,:,IC));
|
|
case 'all'
|
|
end
|
|
imagesc(im,obj.CLim);
|
|
camroll(90)
|
|
colormap(obj.Colormap);
|
|
axis image
|
|
axis off
|
|
end
|
|
end
|
|
|
|
function GenReport(obj)
|
|
% load all info if not already present
|
|
if isempty(obj.ImageData)||isempty(obj.TimeCourseData)
|
|
obj.LoadData;
|
|
end
|
|
obj.BuildFigure;
|
|
% loop over IC
|
|
for IC=1:size(obj.TimeCourseData,2)
|
|
obj.PlotTimeCourse(IC);
|
|
obj.PlotSlices(IC);
|
|
|
|
obj.TCPlot{1}.Title.String= sprintf('IC:%.4d',IC);
|
|
[~,f,e]=fileparts(obj.FileOut);
|
|
switch lower(e)
|
|
case '.png'
|
|
fout=fullfile(obj.DirOut,sprintf('%s_%.4d.png',f,IC));
|
|
type=-'png';
|
|
otherwise
|
|
id = 'GiftIcaReport:GenReport:invalidFileType';
|
|
msg = sprintf('Filetype not implemented');
|
|
throw(MException(id,msg));
|
|
end
|
|
saveas(gcf,fout,type);
|
|
end
|
|
%% this block of code gives a licence error. weird as I could create the object ?
|
|
% if isempty(obj.ReportObj)
|
|
% id = 'GiftIcaReport:GenReport:EmptyReportObject';
|
|
% msg = 'No report object set in constructor of this object';
|
|
% throw(MException(id,msg));
|
|
% end
|
|
% if ~isa(obj.ReportObj,'mlreportgen.report.Report')
|
|
% id = 'GiftIcaReport:GenReport:WrongClassReportObject';
|
|
% msg = 'No report object set in constructor of this object';
|
|
% throw(MException(id,msg));
|
|
% end
|
|
% import mlreportgen.report.*
|
|
% %open(obj.ReportObj)
|
|
% % generate the figure
|
|
% surf(peaks) %just for testing
|
|
% add(obj.ReportObj,Figure)
|
|
% %close(obj.ReportObj)
|
|
% disp(obj.ReportObj)
|
|
%%
|
|
end
|
|
end
|
|
methods %private methods
|
|
function [nrow,ncol]=getrowcol(obj)
|
|
p=factor(obj.NPlots);
|
|
while numel(p)>2
|
|
p(1)=p(1)*p(end);
|
|
p(end)=[];
|
|
p=sort(p);
|
|
end
|
|
if numel(p)==1 % catch primes
|
|
p(2)=1;
|
|
p=sort(p);
|
|
end
|
|
if 9*p(2)>16*p(1) %very elongated plot
|
|
ncol=ceil(sqrt(obj.NPlots));
|
|
nrow=ceil(obj.NPlots/ncol);
|
|
else
|
|
nrow=p(1);
|
|
ncol=p(2);
|
|
end
|
|
end
|
|
|
|
function out = setfile(~,val)
|
|
if isempty(val)
|
|
out={};
|
|
return
|
|
end
|
|
if strcmpi('ui',val)
|
|
out={spm_select(1,'image','select ICA file, first frame')};
|
|
return
|
|
end
|
|
if iscell(val)
|
|
if ~exist(val{1},'file')
|
|
id='GiftICAReport:setfile:FileNotFound';
|
|
msg='File not Found';
|
|
throw(MException(id,msg));
|
|
else
|
|
out=val(1);
|
|
return
|
|
end
|
|
else
|
|
if ~exist(val,'file')
|
|
id='GiftICAReport:setfile:FileNotFound';
|
|
msg='File not Found';
|
|
throw(MException(id,msg));
|
|
else
|
|
out={val};
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|