Repository navigation
DOT additions #392
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
Merged
tiborauer
merged 12 commits into
automaticanalysis:master
from
jones-michael-s:dot_additions
Jan 5, 2024
Merged
DOT additions #392
Changes from 5 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
d1b1f33
modifying to handle dot data in addition to epi'
48f5d28
add outlier filtering
bbbc369
new lightweight QA plots of functional data w/ outlier flagging
52bf876
tie stream rename to input instead of output
84a561c
tweak the output delete warning to include a bit more information
6316828
use aas_getsettings and aas_getstreams
0a0fc8a
remove unnecessary isrenameable tags
a5baa45
apply heuristics to outstream names
b0d0df2
cosmetic cleanup
64772c0
fix residual desc
e246ba8
minor improvements
97ef5d0
add GVTD outlier thresholding
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,257 @@ | ||
| function [aap,resp] = aamod_QA(aap, task) | ||
| % | ||
| % generate a QA summary of functional data | ||
| % | ||
| % Change History | ||
| % | ||
| % fall 2023 [MSJ] -- new | ||
| % | ||
|
|
||
| resp=''; | ||
|
|
||
| switch task | ||
|
|
||
| case 'report' | ||
|
|
||
| case 'doit' | ||
|
|
||
|
|
||
| nvoxels = aap.tasklist.currenttask.settings.nvoxels; | ||
| carpetscale = aap.tasklist.currenttask.settings.carpetscale; | ||
|
|
||
| outlier_filter = aap.tasklist.currenttask.settings.outlier_filter; | ||
| outlier_threshold = aap.tasklist.currenttask.settings.outlier_threshold; | ||
|
|
||
| savedir = fullfile(aas_getstudypath(aap),'QA_images'); | ||
| if ~exist(savedir,'dir');mkdir(savedir);end | ||
|
|
||
| [ ~,subindex_list ] = aas_getN_bydomain(aap,'subject'); | ||
|
|
||
| summary_fname = fullfile(aas_getstudypath(aap),'data_summary.txt'); | ||
|
|
||
| if exist(summary_fname,'file');delete(summary_fname);end | ||
|
|
||
| fid = fopen(summary_fname,'w'); % plaintext stats summary.... | ||
|
|
||
| if (fid < 0) | ||
| aas_log(aap, true, 'Cannot open summary file for writing. Halting...'); | ||
| end | ||
|
|
||
| group_outliers = []; % for group outlier plot... | ||
|
|
||
| for subject_index = subindex_list | ||
|
|
||
| [ ~,all_sess ] = aas_getN_bydomain(aap, 'session', subject_index); | ||
| session_list = intersect(all_sess, aap.acq_details.selected_sessions); | ||
|
|
||
| for session_index = session_list | ||
|
|
||
| data_instream_struct = aap.tasklist.currenttask.inputstreams(1).stream{1}; | ||
| fname = aas_getfiles_bystream(aap, subject_index, session_index, data_instream_struct.CONTENT); | ||
|
tiborauer marked this conversation as resolved.
Resolved
|
||
|
|
||
| header = spm_vol(fname); | ||
| data = spm_read_vols(header); | ||
|
|
||
| [ r,c,s,f ] = size(data); | ||
| data = reshape(data,r*c*s,f); % nvoxels x nframes | ||
|
|
||
| % stats | ||
|
|
||
| nancount = sum(isnan(data(:))); | ||
| zerocount = sum(data(:)==0); | ||
| voxsize = diag(header(1).mat); | ||
| voxsize = voxsize(1:3); | ||
|
|
||
| % swap nan for zeros -- so masking can strip nan | ||
|
|
||
| if (nancount > 0) | ||
| data(isnan(data)) = 0; | ||
| end | ||
|
|
||
| datamax = max(data(:)); | ||
| datamin = min(data(:)); | ||
|
|
||
| % extract voxel subset for processing / plotting | ||
|
|
||
| selector = floor(linspace(1,r*c*s,nvoxels)); | ||
| data = data(selector,:); | ||
|
|
||
| % we don't have a brainmask, so instead do a | ||
| % quick and dirty implicit mask: we assume any | ||
| % identically zero rows are outside of brain | ||
| % this results in a reasonably good selection | ||
| % especially if nvoxels is large-ish | ||
|
|
||
| data = data(all(data~=0,2),:); | ||
|
|
||
| % style points - open window in center of display | ||
| % (this trick only works if not running cluster) | ||
|
|
||
| if (strcmp(aap.options.wheretoprocess, 'localsingle')) | ||
| h = figure('Position',[0 0 1500 1000], 'Color', [1 1 1],'NumberTitle','off', 'Visible', 'off', 'MenuBar', 'none'); | ||
| movegui(h, 'center'); | ||
| set(h, 'Visible', 'on'); | ||
| else | ||
| h = figure('Position',[0 0 1500 1000],'Color', [1 1 1], 'NumberTitle','off','MenuBar','none'); | ||
| end | ||
|
|
||
| subplot(3,3,[5 6 8 9]); | ||
|
|
||
| % joy plot | ||
|
|
||
| surf(data,log(abs(data))); | ||
| axis tight | ||
| xlabel('frame','FontSize',18); | ||
| ylabel('voxel','FontSize',18); | ||
| set(gca,'FontSize',18); | ||
| shading interp; | ||
| colormap gray; | ||
| cm = colormap; | ||
| colormap(cm.*cm.*cm); % suppresses midrange | ||
| view(-25,60); | ||
|
|
||
| % traditional carpet plot for comparison | ||
|
|
||
| subplot(3,3,[4 7]); | ||
| imagesc(data,[carpetscale(1)*max(data(:)) carpetscale(2)*max(data(:))]); | ||
| xlabel('frame','FontSize',14); | ||
| ylabel('voxel','FontSize',14); | ||
| title('grayplot','FontSize',14); | ||
|
|
||
| % sum over voxels plot and outliers | ||
|
|
||
| subplot(3,3,[1 2 3]); | ||
|
|
||
| sumovervoxels = sum(data,1,'omitnan'); | ||
| plot(sumovervoxels,'k','LineWidth',2); | ||
| a = axis; | ||
| hold on; | ||
|
|
||
| switch outlier_filter | ||
|
|
||
| case 'anymedian' | ||
|
|
||
| outliers = isoutlier(data,'median',2, 'ThresholdFactor',outlier_threshold); | ||
| outliers = any(outliers,1); | ||
|
|
||
| case 'anymean' | ||
|
|
||
| outliers = isoutlier(data,'mean',2, 'ThresholdFactor',outlier_threshold); | ||
| outliers = any(outliers,1); | ||
|
|
||
| case 'mediansum' | ||
|
|
||
| outliers = isoutlier(sumovervoxels,'median', 'ThresholdFactor',outlier_threshold); | ||
|
|
||
| case 'meansum' | ||
|
|
||
| outliers = isoutlier(sumovervoxels,'mean', 'ThresholdFactor',outlier_threshold); | ||
|
|
||
| case 'none' | ||
|
|
||
| outliers = []; | ||
|
|
||
| end | ||
|
|
||
| outlier_index = find(outliers); | ||
| if ~isempty(outlier_index) | ||
| plot(outlier_index,0.5*(a(3)+a(4)),'r+','LineWidth',2,'MarkerSize',12); | ||
| end | ||
| axis tight; | ||
|
|
||
| % include stats in title | ||
|
|
||
| [~,n,~]=fileparts(fname); | ||
| n = strrep(n,'_','-'); | ||
| titlestring = [n ' dim: ' num2str([r c s f]) ' voxsize: ' num2str(voxsize') ' max: ' num2str(datamax,3) ' min: ' num2str(datamin,3) ' nan: ' num2str(nancount) ' zeros: ' num2str(zerocount)]; | ||
| title(titlestring,'FontSize',18); | ||
|
|
||
| if ~isempty(outlier_index) | ||
| odescriptor = sprintf('outliers (%s > %g)', outlier_filter, outlier_threshold); | ||
| legend({'sum over voxels', odescriptor},'FontSize',16); | ||
| else | ||
| legend('sum over voxels','FontSize',16); | ||
| end | ||
| xlabel('frame','FontSize',18); | ||
| ylabel('intensity','FontSize',18); | ||
|
|
||
| fname_out = fullfile(savedir,sprintf('%s.jpg', n)); | ||
|
|
||
| set(h,'Renderer','opengl'); | ||
| set(findall(h,'Type','text'),'FontUnits','normalized'); | ||
| h.InvertHardcopy = 'off'; % otherwise matlab changes our nice black bg to white | ||
| print(h, '-djpeg', '-r150', fname_out); | ||
|
|
||
| close(h); | ||
|
|
||
| % collect outliers for group outlier plot | ||
|
|
||
| if (isempty(group_outliers)) | ||
| group_outliers(1,:) = outliers; | ||
| else | ||
| % must protect against unequal number-of-frames | ||
| [~,ncols] = size(group_outliers); | ||
| if (ncols < length(outliers)) | ||
| % this will zero extend ALL rows of group_outliers | ||
| % to new width so we can add this data to bottom | ||
| group_outliers(1,length(outliers)) = 0; | ||
| end | ||
| if (ncols > length(outliers)) | ||
| % this will extend current outliers to fit | ||
| % group_outliers (we assume frame(1) are aligned) | ||
| outliers(ncols) = 0; | ||
| end | ||
| group_outliers(end+1,:) = outliers; | ||
| end | ||
|
|
||
| % add stats to summary file | ||
|
|
||
| fprintf(fid,'%s outliers: %d\n', titlestring, sum(outliers)); | ||
|
|
||
| end % session | ||
|
|
||
| end % subject | ||
|
|
||
| fclose(fid); % summary_fname | ||
|
|
||
| % group outlier plot | ||
| % this is useful for detecting correlated artifacts across subjects | ||
|
|
||
| % get a little better appearance if we size the window to fit data | ||
|
|
||
| [nrows,ncols] = size(group_outliers); | ||
|
|
||
| if (strcmp(aap.options.wheretoprocess, 'localsingle')) | ||
| h2 = figure('Position',[0 0 6*ncols 20*nrows], 'Color', [1 1 1],'NumberTitle','off', 'Visible', 'off', 'MenuBar', 'none'); | ||
| movegui(h2, 'center'); | ||
| set(h2, 'Visible', 'on'); | ||
| else | ||
| h2 = figure('Position',[0 0 6*ncols 20*nrows],'Color', [1 1 1], 'NumberTitle','off','MenuBar','none'); | ||
| end | ||
|
|
||
| group_outliers(end+1,end+1) = 0; % pcolor needs a dummy row/col | ||
| pcolor(group_outliers); | ||
| title('Outliers','FontSize',16); | ||
| xlabel('frame','FontSize',14); | ||
| ylabel('subject','FontSize',14); | ||
| colormap gray; | ||
|
|
||
| fname_out = fullfile(aas_getstudypath(aap),'group_outliers.jpg'); | ||
| set(h2,'Renderer','opengl'); | ||
| set(findall(h2,'Type','text'),'FontUnits','normalized'); | ||
| h2.InvertHardcopy = 'off'; | ||
| print(h2, '-djpeg', '-r150', fname_out); | ||
| close(h2); | ||
|
|
||
| aap = aas_desc_outputs(aap, 'data_summary', summary_fname); | ||
|
|
||
| % done! | ||
|
|
||
| case 'checkrequirements' | ||
|
|
||
| otherwise | ||
|
|
||
| aas_log(aap,true,sprintf('Unknown task %s',task)); | ||
|
|
||
| end % switch | ||
| end % function | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <aap> | ||
| <tasklist> | ||
| <currenttask domain='study' desc='make QA summary of functional data' modality='MRI'> | ||
|
|
||
| <!-- generate a collection of epi QA maps --> | ||
|
|
||
| <qsub> | ||
| <timeBase>1</timeBase> | ||
| <memoryBase>1</memoryBase> | ||
| </qsub> | ||
|
|
||
| <!-- number of voxels to include in QA plots --> | ||
|
|
||
| <!-- actual number will probably be less, as any NaN or --> | ||
| <!-- zero timeseries are asssumed outside brain and skipped --> | ||
|
|
||
| <nvoxels>1000</nvoxels> | ||
|
|
||
| <!-- min/max color scaling for the carpet plot --> | ||
|
|
||
| <carpetscale>[-2 0.8]</carpetscale> | ||
|
|
||
| <!-- outlier flagging --> | ||
|
|
||
| <outlier_filter options='anymedian|anymean|mediansum|meansum|none'>meansum</outlier_filter> | ||
| <outlier_threshold>3</outlier_threshold> | ||
|
|
||
| <inputstreams> | ||
| <stream isrenameable='1'>epi</stream> | ||
| </inputstreams> | ||
|
|
||
| <outputstreams> | ||
| <stream>QA_summary</stream> | ||
| </outputstreams> | ||
|
|
||
| </currenttask> | ||
| </tasklist> | ||
| </aap> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.